メインコンテンツへスキップ
OpenClaw は、複数のエージェント実行が衝突しないように、すべてのチャネルの受信自動返信実行を小さなインプロセスキューで直列化しつつ、セッション間では安全な並列処理を許可します。

理由

  • 自動返信実行は高コストになることがあり(LLM 呼び出し)、複数の受信メッセージが短い間隔で届くと衝突する可能性があります。
  • 直列化により、共有リソース(セッションファイル、ログ、CLI 標準入力)の競合を避け、上流のレート制限に達する可能性を下げます。

仕組み

  • レーン対応の FIFO キューが、設定可能な並行数上限で各レーンを処理します(未設定レーンのデフォルトは 1、main のデフォルトは 4、subagent は 8)。
  • runEmbeddedAgentセッションキー(レーン session:<key>)でエンキューし、セッションごとにアクティブな実行が 1 つだけになることを保証します。
  • 各セッション実行はその後 グローバルレーン(デフォルトでは main)にキュー投入されるため、全体の並列度は agents.defaults.maxConcurrent によって制限されます。
  • 詳細ログが有効な場合、キュー内の実行は開始までに約 2 秒を超えて待機すると短い通知を出力します。
  • 入力中インジケーターは、チャネルが対応している場合、エンキュー時に即座に送信されるため、実行が順番待ちしている間もユーザー体験は変わりません。

デフォルト

未設定の場合、すべての受信チャネルサーフェスは次を使用します。
  • mode: "steer"
  • debounceMs: 500
  • cap: 20
  • drop: "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" },
    },
  },
}

キューオプション

オプションはキュー配信に適用されます。debounceMssteer モードで Codex ステアリングの静穏ウィンドウも設定します。
  • debounceMs: キュー内のフォローアップまたは collect バッチを処理する前の静穏ウィンドウです。Codex の steer モードでは、バッチ化された turn/steer を送信する前の静穏ウィンドウです。裸の数値はミリ秒です。単位 mssmhd/queue オプションで受け付けられます。
  • cap: セッションごとのキュー内メッセージの最大数です。1 未満の値は無視されます。
  • drop: "summarize"(デフォルト): 必要に応じて最も古いキュー項目を削除し、コンパクトな要約を保持して、合成フォローアッププロンプトとして注入します。
  • drop: "old": 必要に応じて最も古いキュー項目を削除します。要約は保持しません。
  • drop: "new": キューがすでに満杯の場合、最新のメッセージを拒否します。
デフォルト: debounceMs: 500cap: 20drop: summarize

ステアリングとストリーミング

チャネルストリーミングが partial または block の場合、アクティブな実行がランタイム境界に到達するまで、ステアリングは複数の短い可視返信のように見えることがあります。
  • partial: プレビューが早めに確定し、その後ステアリングが受け付けられると新しいプレビューが開始されることがあります。
  • block: 下書きサイズのブロックでも、同じような順次表示になることがあります。
  • ストリーミングがない場合、ランタイムが同一ターンのステアリングを受け付けられないと、ステアリングはアクティブな実行後のフォローアップにフォールバックします。
steer は実行中のツールを中止しません。最新のメッセージで現在の実行を中止する必要がある場合は、/queue interrupt を使用してください。

優先順位

モード選択では、OpenClaw は次の順に解決します。
  1. インラインまたは保存済みのセッションごとの /queue オーバーライド。
  2. messages.queue.byChannel.<channel>
  3. messages.queue.mode
  4. デフォルトの steer
オプションでは、インラインまたは保存済みの /queue オプションが設定より優先されます。その後、チャネル固有のデバウンス(messages.queue.debounceMsByChannel)、Plugin のデバウンスデフォルト、グローバルな messages.queue オプション、組み込みデフォルトの順に適用されます。capdrop はグローバルまたはセッションのオプションであり、チャネルごとの設定キーではありません。

セッションごとのオーバーライド

  • /queue <steer|followup|collect|interrupt> を単独のコマンドとして送信すると、現在のセッションのキューモードが保存されます。
  • オプションは組み合わせられます: /queue collect debounce:0.5s cap:25 drop:summarize
  • /queue default または /queue reset はセッションオーバーライドをクリアします。

キュー内ターンのキャンセル

プロンプトがフォローアップまたは collect キュー内にある間(たとえば、別のターンがアクティブな間に TUI や ウェブチャットの chat.send が到着した場合)、Gateway はキュー内の コンテンツが実行されるか削除されるまで、そのクライアント runIdGateway 所有のキャンセル ID を保持します。この ID は、オーバーフロー要約に折りたたまれたコンテンツにも追従します。
  • 特定の runId を指定した chat.abort は、リクエスターが認可されている場合(アクティブな実行と同じ所有権ルール)、そのターンがまだ キュー内にある間にキャンセルします。
  • runId なしでセッションに対する chat.abort は、まず 認可されたキュー内ターン をキャンセルし、その後、認可されたアクティブな実行を中止します。この順序により、キュー処理が作業を半停止状態のセッションへ昇格させることを防ぎます。
  • リクエスターごとのチェックなしにセッションキュー全体をクリアすることは、複数所有者セッションの 停止経路ではありません。
  • キュー待機は sessions.list でアクティブなエージェント実行として投影されず、 アクティブ実行のタイムアウトセマンティクスも所有しません。所有するのはアクティブフェーズのみです。
クライアント(TUI を含む)は実行中のプロンプトを転送し、Gateway にキューモードを適用させます。Esc//stop はセッションスコープの中止を使用するため、失われたローカルハンドルによって、まだキュー内にあるプロンプトが実行され続けることはありません。

スコープと保証

  • Gateway 返信パイプラインを使用するすべての受信チャネル(WhatsApp web、Telegram、Slack、Discord、Signal、iMessage、ウェブチャットなど)にまたがる自動返信エージェント実行に適用されます。
  • デフォルトレーン(main)は、受信 + main Heartbeat に対してプロセス全体で共有されます。複数セッションを並列に許可するには、agents.defaults.maxConcurrent を設定してください。
  • 追加レーン(例: croncron-nestednestedsubagent)が存在する場合があり、バックグラウンドジョブは受信返信をブロックせずに並列実行できます。分離された Cron エージェントターンは、内側のエージェント実行が cron-nested を使用している間、cron スロットを保持します。どちらも cron.maxConcurrentRuns を使用します。共有された非 Cron の nested フローは独自のレーン動作を維持します。これらの切り離された実行はバックグラウンドタスクとして追跡されます。
  • セッションごとのレーンにより、特定のセッションに同時に触れるエージェント実行が 1 つだけであることを保証します。
  • 外部依存関係やバックグラウンドワーカースレッドはありません。純粋な TypeScript + promises です。

トラブルシューティング

  • コマンドが停止しているように見える場合は、詳細ログを有効にし、キューが処理されていることを確認するために “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.stucksession.long_running の警告ログ行は、セッションが変わらない間は指数バックオフします。回復試行は、そのバックオフに関係なく、すべての Heartbeat tick で引き続き実行されます。

関連