Messages and delivery
コマンドキュー
OpenClaw は、複数のエージェント実行の衝突を防ぎながら、セッション間では安全な並列処理を可能にするため、受信した自動返信の実行(すべてのチャンネル)を小規模なインプロセスキューで直列化します。
理由
- 自動返信の実行には(LLM 呼び出しなどの)大きなコストがかかる場合があり、複数の受信メッセージが短時間に到着すると衝突する可能性があります。
- 直列化することで、共有リソース(セッションファイル、ログ、CLI 標準入力)の競合を避け、上流のレート制限に達する可能性を低減できます。
仕組み
- レーン対応の FIFO キューが、設定可能な同時実行数の上限に従って各レーンを処理します(未設定のレーンではデフォルト 1、
mainのデフォルトは 4、subagentは 8)。 runEmbeddedAgentは、セッションごとにアクティブな実行が 1 つだけになることを保証するため、セッションキー(レーンsession:<key>)単位でキューに追加します。- その後、各セッションの実行はグローバルレーン(デフォルトでは
main)に追加され、全体の並列数がagents.defaults.maxConcurrentによって制限されます。 - 詳細ログが有効な場合、キュー内の実行が開始までに約 2 秒を超えて待機すると、短い通知が出力されます。
- 入力中インジケーターはキューへの追加直後に引き続き表示されるため(チャンネルが対応している場合)、実行が順番を待っている間もユーザー体験は変わりません。
デフォルト
未設定の場合、すべての受信チャンネルサーフェスで以下が使用されます。
mode: "steer"debounceMs: 500cap: 20drop: "summarize"
同一ターンのステアリングがデフォルトです。実行中に到着したプロンプトは、実行がステアリングを受け入れられる場合、アクティブなランタイムに注入されるため、2 つ目のセッション実行は開始されません。アクティブな実行がステアリングを受け入れられない場合、OpenClaw はその実行が終了してからプロンプトを開始します。
キューモード
/queue は、セッションにすでにアクティブな実行がある間、通常の受信メッセージをどのように処理するかを制御します。
steer: メッセージをアクティブなランタイムに注入します。OpenClaw は、現在のアシスタントターンがツール呼び出しの実行を完了した後、次の LLM 呼び出しの前に、保留中のすべてのステアリングメッセージを配信します。Codex app-server は、バッチ化された 1 つのturn/steerを受信します。実行がアクティブにストリーミングしていない場合、またはステアリングを利用できない場合、OpenClaw はアクティブな実行が終了してからプロンプトを開始します。followup: ステアリングしません。現在の実行が終了した後のエージェントターン用に、各メッセージをキューに追加します。collect: ステアリングしません。待機時間枠の後、キュー内のメッセージを単一のフォローアップターンにまとめます。メッセージの送信先が異なるチャンネルやスレッドの場合、ルーティングを維持するため個別に処理されます。interrupt: そのセッションのアクティブな実行を中止してから、最新のメッセージを実行します。
ランタイム固有のタイミングと依存関係の動作については、ステアリングキューを参照してください。明示的な /steer <message> コマンドについては、ステアリングを参照してください。
messages.queue を使用して、グローバルまたはチャンネルごとに設定します。
{ messages: { queue: { mode: "steer", debounceMs: 500, cap: 20, drop: "summarize", byChannel: { discord: "collect" }, }, },}キューオプション
オプションはキューに追加された配信に適用されます。debounceMs は、steer モードにおける Codex ステアリングの待機時間枠も設定します。
debounceMs: キューに追加されたフォローアップまたは収集バッチを処理する前の待機時間枠です。Codex のsteerモードでは、バッチ化されたturn/steerを送信する前の待機時間枠です。単位のない数値はミリ秒です。/queueオプションでは、単位ms、s、m、h、dを使用できます。cap: セッションごとのキュー内メッセージの最大数です。1未満の値は無視されます。drop: "summarize"(デフォルト): 必要に応じてキュー内の最も古いエントリを破棄し、簡潔な要約を保持して、合成されたフォローアッププロンプトとして注入します。drop: "old": 必要に応じてキュー内の最も古いエントリを破棄し、要約は保持しません。drop: "new": キューがすでに上限に達している場合、最新のメッセージを拒否します。
デフォルト: debounceMs: 500、cap: 20、drop: summarize。
ステアリングとストリーミング
チャンネルストリーミングが partial または block の場合、アクティブな実行がランタイム境界に到達する過程で、ステアリングが複数の短い表示返信のように見えることがあります。
partial: プレビューが早期に確定し、ステアリングの受け入れ後に新しいプレビューが開始される場合があります。block: 下書きサイズのブロックでも、同様に順次表示されることがあります。- ストリーミングを使用しない場合、ランタイムが同一ターンのステアリングを受け入れられなければ、アクティブな実行後のフォローアップにフォールバックします。
steer は実行中のツールを中止しません。最新のメッセージで現在の実行を中止する必要がある場合は、/queue interrupt を使用してください。
優先順位
モードを選択する際、OpenClaw は次の順序で解決します。
- インラインまたは保存済みのセッション単位の
/queueオーバーライド。 messages.queue.byChannel.<channel>。messages.queue.mode。- デフォルトの
steer。
オプションについては、インラインまたは保存済みの /queue オプションが設定より優先されます。続いて、チャンネル固有のデバウンス(messages.queue.debounceMsByChannel)、Plugin のデバウンスのデフォルト、グローバルな messages.queue オプション、組み込みのデフォルトの順に適用されます。cap と drop はグローバルまたはセッション用のオプションであり、チャンネル単位の設定キーではありません。
セッション単位のオーバーライド
- 現在のセッションのキューモードを保存するには、
/queue <steer|followup|collect|interrupt>を単独のコマンドとして送信します。 - オプションは組み合わせられます:
/queue collect debounce:0.5s cap:25 drop:summarize /queue defaultまたは/queue resetは、セッションのオーバーライドを解除します。
キュー内ターンのキャンセル
プロンプトがフォローアップまたは収集キューにある間(たとえば、別のターンがアクティブな間に TUI や
webchat の chat.send が到着した場合)、Gateway はキュー内の
コンテンツが実行または破棄されるまで、そのクライアントの runId に対する
Gateway 所有のキャンセル識別子を保持します。この識別子は、オーバーフロー要約に
まとめられたコンテンツにも引き継がれます。
- 特定の
runIdを指定したchat.abortは、リクエスト元が認可されている場合(アクティブな実行と同じ所有権ルール)、そのターンがまだ キュー内にある間にキャンセルします。 runIdを指定しないセッションのchat.abortは、認可されたキュー内のターンを 先にキャンセルしてから、認可されたアクティブな実行を中止します。この順序により、キューの処理によって 作業が中途半端に停止したセッションへ昇格することを防ぎます。- リクエスト元ごとの確認を行わずにセッションキュー全体を消去する方法は、 複数所有者のセッションにおける停止経路ではありません。
- キュー内の待機は、
sessions.listに対するアクティブなエージェント実行として投影されず、 アクティブ実行のタイムアウトセマンティクスも持ちません。これを持つのはアクティブフェーズだけです。
Gateway を使用するクライアント(openclaw tui を含む)は、実行中に受信したプロンプトを転送し、
Gateway にキューモードを適用させます。Esc//stop はセッション単位の中止を使用するため、
ローカルハンドルが失われても、キュー内のプロンプトが実行されたままになることはありません。
openclaw chat と openclaw tui --local は、埋め込みランタイムで同じ 4 つのモードを適用します。
ローカルの steer は、埋め込みランタイムがステアリングを受け入れる場合はアクティブな実行に注入され、
それ以外の場合はフォローアップになります。followup と
collect はローカルの保留中作業として残ります。interrupt は最新のメッセージを
開始する前に、アクティブなローカル実行を中止します。明示的な /steer <message> コマンドは
ローカルモード用のコマンドではありません。
適用範囲と保証
- Gateway の返信パイプラインを使用するすべての受信チャンネル(WhatsApp web、Telegram、Slack、Discord、Signal、iMessage、webchat など)の自動返信エージェント実行に適用されます。
- デフォルトレーン(
main)は、受信処理とメイン Heartbeat に対してプロセス全体で共有されます。複数のセッションを並列に実行できるようにするには、agents.defaults.maxConcurrentを設定します。 - 追加のレーン(
cron、cron-nested、nested、subagentなど)が存在する場合があり、バックグラウンドジョブは受信返信をブロックせずに並列実行できます。分離された Cron エージェントターンはcronスロットを保持し、その内部のエージェント実行はcron-nestedを使用します。Cron 以外の共有nestedフローは、それぞれ独自のレーン動作を維持します。これらの分離された実行は、バックグラウンドタスクとして追跡されます。 - セッション単位のレーンにより、特定のセッションに同時にアクセスするエージェント実行は 1 つだけであることが保証されます。
- 外部依存関係やバックグラウンドワーカースレッドはありません。純粋な TypeScript と Promise のみを使用します。
トラブルシューティング
- コマンドが停止しているように見える場合は、詳細ログを有効にし、「queued for ...ms」という行を探して、キューが処理されていることを確認してください。
- ターンを受け入れた後に進行状況の出力が停止した Codex app-server の実行は、外側の実行タイムアウトを待たずにアクティブなセッションレーンを解放できるよう、Codex アダプターによって中断されます。
- 診断が有効な場合、組み込みの警告しきい値を超えて
processingのままであり、返信、ツール、ステータス、ブロック、ACP の進行が確認されないセッションは、現在のアクティビティに基づいて分類されます。- 最近の進行状況があるアクティブな作業は、
session.long_runningとしてログに記録されます。所有者のある無出力のモデル呼び出しも、低速または非ストリーミングのプロバイダーが早すぎる段階で停止状態と報告されないよう、組み込みの中止しきい値まではsession.long_runningのままです。 - 最近の進行状況がないアクティブな作業は、
session.stalledとしてログに記録されます。所有者のあるモデル呼び出し、ブロックされたツール呼び出し、停止した埋め込み実行は、中止しきい値以降にsession.stalledに切り替わります。所有者のない古いモデルまたはツールのアクティビティが長時間実行として隠されることはありません。 session.stuckは、所有者のない古いモデルまたはツールのアクティビティを含む、アイドル状態でキューに入ったセッションなど、復旧可能な古いセッション管理情報にのみ使用されます。session.stuckは常に、影響を受けたセッションレーンを解放できる復旧処理を開始します。中止しきい値を超えたsession.stalledの分類(ブロックされたツール呼び出し、停止したモデル呼び出し、または停止した埋め込み実行)でもアクティブ中止による復旧を開始できるため、session.stuckだけでなく、どちらの分類でもキューの停止を解除できます。- セッションが変化しない間、繰り返される
session.stuckとsession.long_runningの警告ログ行は指数関数的にバックオフします。そのバックオフにかかわらず、復旧処理は Heartbeat の各ティックで引き続き実行されます。
- 最近の進行状況があるアクティブな作業は、