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を使用すると、セッションのオーバーライドがクリアされます。
キュー内ターンのキャンセル
プロンプトが followup/collect キュー内で待機している間(たとえば、別のターンがアクティブなときに TUI またはウェブチャットの chat.send が届いた場合)、Gateway は、キュー内の内容が実行または破棄されるまで、そのクライアントの runId に対するGateway 所有のキャンセル IDを保持します。この ID は、オーバーフロー要約にまとめられた内容にも引き継がれます。
- 特定の
runIdを指定したchat.abortは、そのターンがまだキュー内にあり、要求元に権限がある場合(アクティブな実行と同じ所有権ルール)、そのターンをキャンセルします。 runIdを指定せずセッションに対して実行するchat.abortは、まず権限のあるキュー内ターンをキャンセルし、その後、権限のあるアクティブな実行を中止します。この順序により、キューの処理によって作業が不完全に停止したセッションへ昇格することを防ぎます。- 要求元ごとの確認を行わずにセッションキュー全体をクリアする方法は、複数所有者セッションの停止経路として使用されません。
- キュー内での待機は、
sessions.listではアクティブなエージェント実行として投影されず、アクティブ実行のタイムアウトのセマンティクスも持ちません。これはアクティブフェーズだけが持ちます。
クライアント(TUI を含む)は実行中に届いたプロンプトを転送し、Gateway にキューモードを適用させます。Esc//stop はセッション単位の中止を使用するため、ローカルハンドルが失われても、キュー内に残ったプロンプトが実行されることはありません。
適用範囲と保証
- Gateway の返信パイプラインを使用するすべての受信チャネル(WhatsApp web、Telegram、Slack、Discord、Signal、iMessage、ウェブチャットなど)の自動返信エージェント実行に適用されます。
- デフォルトレーン(
main)は、受信処理とメインの Heartbeat に対してプロセス全体で共有されます。複数のセッションを並列実行できるようにするには、agents.defaults.maxConcurrentを設定します。 - 追加のレーン(
cron、cron-nested、nested、subagentなど)が存在する場合があり、バックグラウンドジョブは受信返信をブロックせずに並列実行できます。分離された Cron エージェントターンは、内部のエージェント実行がcron-nestedを使用している間、cronスロットを保持します。どちらもcron.maxConcurrentRunsを使用します。共有される Cron 以外のnestedフローでは、独自のレーン動作が維持されます。これらの切り離された実行は、バックグラウンドタスクとして追跡されます。 - セッション単位のレーンにより、特定のセッションに同時にアクセスするエージェント実行が 1 つだけであることが保証されます。
- 外部依存関係やバックグラウンドワーカースレッドはなく、純粋な TypeScript と Promise のみで構成されています。
トラブルシューティング
- コマンドが停止しているように見える場合は、詳細ログを有効にし、キューが処理されていることを確認するために「queued for ...ms」という行を探してください。
- ターンを受け入れた後に進行状況の出力を停止した Codex app-server の実行は、外側の実行タイムアウトを待つ代わりにアクティブなセッションレーンを解放できるよう、Codex アダプターによって中断されます。
- 診断が有効な場合、
diagnostics.stuckSessionWarnMsを超えてprocessingのままになっており、返信、ツール、ステータス、ブロック、ACP の進行が観測されないセッションは、現在のアクティビティに基づいて分類されます。- 最近の進行状況があるアクティブな作業は、
session.long_runningとしてログに記録されます。所有者のいる無出力のモデル呼び出しも、低速または非ストリーミングのプロバイダーが早すぎる段階で停止と報告されないよう、diagnostics.stuckSessionAbortMsまではsession.long_runningのままです。 - 最近の進行状況がないアクティブな作業は
session.stalledとしてログに記録されます。所有者のいるモデル呼び出し、ブロックされたツール呼び出し、停止した埋め込み実行は、中止しきい値以降にsession.stalledへ切り替わります。所有者のいない古いモデルやツールのアクティビティが、長時間実行中として隠されることはありません。 session.stuckは、所有者のいない古いモデルやツールのアクティビティを伴う、アイドル状態のキュー内セッションを含む、復旧可能な古いセッション管理情報のために予約されています。session.stuckは常に、影響を受けたセッションレーンを解放できる復旧処理をトリガーします。diagnostics.stuckSessionAbortMsを超えたsession.stalledの分類(ブロックされたツール呼び出し、停止したモデル呼び出し、停止した埋め込み実行)も、アクティブ中止による復旧をトリガーできるため、キューの停止を解除できるのはsession.stuckだけではありません。- セッションの状態が変わらない間、
session.stuckとsession.long_runningの警告ログが繰り返し出力される間隔は指数関数的に延長されます。そのバックオフに関係なく、復旧処理は Heartbeat の各ティックで引き続き実行されます。
- 最近の進行状況があるアクティブな作業は、