チャネル ランタイム ライフサイクル
チャンネルは ZeroClaw のエッジに位置します。チャットプラットフォーム、ウェブフック、エディター、イベントソースと通信し、正規化された作業をエージェントランタイムに渡します。
チャンネルリスナー、ゲートウェイの Webhook、メッセージのディスパッチ、返信の意図、ストリーミングドラフト、チャンネル単位のリロード、ヘルス/バックオフの動作、またはプラットフォーム固有のアダプターとランタイム所有のターン処理との境界に変更が及ぶ場合は、このページを参照してください。
対象境界と現在の遷移
対象の境界はシンプルです:
- チャネルアダプターはプラットフォーム固有のI/Oを担当します。
- ランタイム所有のコードがエージェントターンのライフサイクルを所有します。
- Gateway webhook ハンドラーは汎用的な HTTP トランスポートの詳細を担当し、その後、長時間実行リスナーと同じチャネルターンのライフサイクルに入ります。
現在のコードはまだ移行中です。zeroclaw-channels には、ChannelRuntimeContext、run_message_dispatch_loop、process_channel_message を含む大きな orchestrator モジュールが含まれています。このコードは現在、メッセージルーティング、フック、セルフループガード、パッシブコンテキスト、メディア/リンクのエンリッチメント、オートセーブ、メモリ想起、返信インテント、ツールループ呼び出し、ドラフト更新、キャンセル、受信確認、コスト追跡、最終配信といったランタイム規模の処理を実行しています。
それは動作するコードであり、すべてのチャネル変更をブロックする理由にはなりません。レビュールールはより限定的です。新しいチャネル、webhook、またはストリーミングの作業は、可能な限りこの共有ライフサイクルを再利用すべきであり、別のローカルなミニオーケストレーターを追加すべきではありません。
何が何を所有するか
| Surface | 所有者 | ルールを確認 |
|---|---|---|
| プラットフォームリスナーまたはチャネルインバウンドアダプター | チャンネルモジュールまたはチャンネルプラグイン | 署名チェック、ペイロードのデコード、プラットフォームのリトライ、プロバイダー検証、チャレンジ処理、および ChannelMessage の構築は、トランスポートアダプターにローカルに保つ。 |
| ゲートウェイ Webhook ルート | ゲートウェイハンドラー | ルートホスティング、プロキシ、タイムアウト動作、高速確認応答、および汎用 HTTP レスポンスポリシーをゲートウェイにローカルに保ってください。ドキュメント化された移行負債を除き、そこに新しいプラットフォーム固有の解析を増やさないでください。 |
| 正規化されたインバウンドメッセージ | zeroclaw-api の ChannelMessage | 送信者、返信先、チャネル、エイリアス、スレッド、添付ファイル、件名、パッシブコンテキスト、および会話スコープを保持します。ルーティングシグナルをユーザーに見えるテキストに隠すのではなく、構造化されたメタデータを追加してください。 |
| チャネルエイリアスのエージェント所有権 | start_channels / AgentRouter およびアクティブなチャネルバインディング | チャネルの所有エージェントを、設定されたバインディングから解決します。チャネルが未所有または無効の場合に、無関係なエージェントへ暗黙的にフォールバックしないでください。 |
| メッセージのディスパッチとキャンセル | 共有チャネルのディスパッチループ | 進行中のトラッキング、/stop、送信者/スレッドのキャンセル、最大進行数の制限、ワーカーの同時実行を再利用します。 |
| ターン処理 | 共有ランタイム/チャネルのライフサイクル | フック、自己ループガード、パッシブコンテキスト、メディア/リンクのエンリッチメント、ランタイムコマンド、モデルルーティング、オートセーブ、メモリリコール、返信インテント、ツール実行、レシート、コスト、および配信は、1つのパスに存在すべきです。 |
| ゲートウェイ Webhook 確認応答 | ゲートウェイハンドラー | Fast-ack トランスポートはモデルが完了する前に HTTP 200 を返す場合がありますが、バックグラウンドの処理は依然として共有チャネルのライフサイクルに入る必要があります。 |
| チャンネルの健全性と再接続 | リスナー スーパーバイザー | 再試行可能なリスナーの障害には、上限付きの指数バックオフとキャンセルを考慮したシャットダウンを使用します。再試行できない障害は、停止させるか明確に表面化させる必要があります。 |
| ランタイムの再読み込み | デーモンリロードとチャネル再起動パス | 設定の保存だけでは不十分です。長時間実行中のリスナーは、デーモンがリロードするかプロセスが再起動したときにのみ、channel/provider/scheduler の変更を反映します。 |
インバウンド形状
長時間実行チャネルと Webhook ベースのチャネルは、トランスポートのエントリポイントは異なりますが、同じメッセージ形状に収束する必要があります:
flowchart LR
A["Platform event"] --> B["Transport adapter"]
B --> C["ChannelMessage"]
C --> D["Channel dispatch loop"]
D --> E["Agent turn lifecycle"]
E --> F["Channel send / draft / reply"]
アダプターは、プラットフォームのみが理解できる作業を保持すべきです:
- ルートとエイリアスの解決;
- ボディサイズの制限とデコード。
- 署名またはトークンの検証。
- プラットフォーム固有の解析ルール。
- ペアリング、許可リスト、または送信者ID抽出。
- プロバイダーのチャレンジまたは検証エンドポイント。
- 即時確認ポリシー。
その後、正規化された ChannelMessage をハンドオフします。例外が限定的で、文書化され、テストされている場合を除き、ライフサイクルの残りをアダプターにコピーしないでください。
ランタイムターンの責務
共有ライフサイクルは、チャネル間で一貫性を保つ必要がある動作を管理する必要があります。
- message-received や message-sent などのフック。
Channel::self_handle()とdrop_self_messagesによるセルフループ保護。- モデル/プロバイダーへの副作用なしでパッシブコンテキストを記録する。
- 早期確認リアクションと返信なしのクリーンアップ。
- プロバイダー呼び出し前のメディアおよびリンクの前処理。
/new、/model、/models、/config、/stopなどのランタイムコマンド。- 自動保存とセッション履歴キー。
- メモリの想起と履歴のトリミング。
- グループチャンネルとアンビエントチャンネルにおける返信意図の分類。
- ドラフトのストリーミング更新とマルチメッセージ動作。
- ツールの承認、実行、レシート、オブザーバーイベント、コスト追跡。
- キャンセル、タイムアウト、ロールバック、そして最終応答の配信。
PR がそれらの責務の 1 つを 1 つのチャネルのみに対して変更する場合、レビュアーはそれが共有ライフサイクルに属するか、型付きチャネルのケイパビリティメタデータに属するかを問うべきです。
ゲートウェイのウェブフック
Gateway の Webhook には正当な特別要件が 1 つあります。エージェントのターンが遅い場合でも、HTTP リクエストをすばやく返す必要がある場合があることです。Nextcloud Talk が最も明確な例です。遅いローカルモデルはプロバイダーの Webhook タイムアウトを超える可能性があるためです。
その迅速な確認応答要件によって、ゲートウェイが別個のエージェント ライフサイクルを担うことになるべきではありません。現在のゲートウェイベースのハンドラーは、引き続きゲートウェイ固有の検証後ディスパッチ パスを使用しています。これを移行上の負債および移行期の事情として扱い、新しい Webhook ベースのチャネル実装における目標パターンとはしないでください。Webhook ハンドラーは固定された順序に従います:
- リクエストを検証してください。
- ペイロードをデコードします。
- 1 つ以上の
ChannelMessage値を解析します。 - 同期またはバックグラウンドディスパッチを選択します。
- トランスポートに応じた HTTP レスポンスを返します。
メッセージをディスパッチするチャネル webhook では、手順 1 と 4 は慣習的なものではなく、構造上のものです。ゲートウェイの webhook_ingress モジュールが、認証済みイングレスのコントラクトを担います:
- 各メッセージディスパッチ用 Webhook アダプターは、単一のレジストリ(
MESSAGE_DISPATCHING_WEBHOOKS)で認証モードを宣言し、ドリフトガードテストはゲートウェイのルートテーブルとそのレジストリを照合する; authenticateはフェイルクローズの認証情報ポリシーを適用します。必須シークレットが欠落している、空白である、または解決されていない場合、ペイロードのバイトが1つでも解析される前に、リクエストを401で拒否します。プロバイダー固有の署名アルゴリズムとヘッダー形式はトランスポートハンドラーにクロージャとして保持され、認証情報の解決後にのみ実行されます;- チェックに成功すると、検証済みのバイト列を保持する
VerifiedWebhookIngress証明が生成されます。parse_messagesを消費すると、パーサーにその正確なバイト列が渡され、非公開のVerifiedWebhookMessages値が返されます。いずれの証明も他の場所で構築したり複製したりすることはできません。 dispatch_verified_webhookは、現在の受信ログ、セッションキー、自動保存、エージェントディスパッチ、クイックスタートのフォールバック、返信/エラー配信、および同期または fast-ack 実行で共有される gateway-webhook ヘルパーです。解析済みの proof を使用するため、同じリクエストにバインドされた検証結果と解析ステップなしに、webhook の内容がエージェントディスパッチに渡ることはありません。
そのヘルパーは、重複していたゲートウェイの parse -> autosave -> chat -> send チェーンをなくし、認証済みイングレスに強制適用される単一のチョークポイントを提供します。ただし、引き続きゲートウェイのチャットパスを呼び出すため、上述した共有チャネルのターンライフサイクルではありません。ゲートウェイの Webhook は、フック、自己ループ制御、パッシブコンテキスト、メディアとリンクの処理、ランタイムコマンド、キャンセル、返信インテント、レシート、コスト追跡のために、引き続きそのライフサイクルに収束する必要があります。将来的にこの収束を実現する際は、認証済みイングレスの証明と、各トランスポートの同期応答または高速 ACK 応答の動作を維持する必要があります。
レジストリ内のメッセージディスパッチを行うすべての webhook アダプターは、エイリアスごとに必須の認証情報を宣言しており、その認証情報が存在しない、空白である、または解決できない場合は、解析前に拒否されます。レジストリには検証を任意にするモードはありません。暗黙のフォールスルーは認証モードではありません。したがって、受信側の認証情報メカニズムを持たないアダプターは、現時点ではメッセージディスパッチ用として登録できません。追加するには、検証を緩和するのではなく、明示的な「拒否のみ」のポリシーをレジストリに拡張する必要があります。
Webhook の変更をレビューする際は、同期ハンドラーと fast-ack ハンドラーを別々に比較してください。
- 同期ハンドラーは既存のステータスコード、無効な署名の動作、autosaveキー、および返信配信を維持する必要があります。
- fast-ack ハンドラーは、モデル呼び出しがプロバイダータイムアウトをブロックする前に HTTP 応答確認が発生することを保証する必要があります。
- ゲートウェイ固有のパスを残したまま、どちらの形式も、別の
parse -> autosave -> chat -> sendチェーンを追加するのではなく、dispatch_verified_webhookを介してディスパッチに入らなければならない。固定された呼び出し箇所一覧により、ハンドラーが認証済みのファネルを迂回するとビルドが失敗する。この要件は、ヘルパーが対象チャネルのライフサイクルを担うという意味ではない。
リロードとリスナーのライフサイクル
チャネル設定は、実行中のリスナーがそれを認識する前に保存できます。デーモンは長寿命のサブシステムグラフを所有しているため、チャネルリスナーの変更は、デーモンが関連するサブシステムをリロードまたは再起動したときに適用されます。スタンドアロンゲートウェイの起動では、チャネルリスナーの変更にプロセスの再起動が必要になる場合があります。
以下の項目を確認してリロードの影響を受ける変更をレビューしてください:
- 変更した値が
config.tomlに保存されるかどうか。 - 実行中のチャンネルコンテキストが新しい値を即座に読み込むか、リロード時に読み込むか、または再起動後にのみ読み込むか。
- 古い接続を放置するのではなく、キャンセルによってリスナータスクが停止するかどうか。
- アクティブなチャネルバインディングが、どのエージェントがどのチャネルエイリアスを所有するかについての信頼できる情報源であり続けるかどうか。
ストリーミング、ドラフト、キャンセル
ストリーミングは機能の境界です。チャネルによっては、下書き編集、複数メッセージのストリーミング、入力中インジケーター、または最終送信のみの動作をサポートする場合があります。共有ライフサイクルが、ターン中にこれらの機能をどのように使用するかを決定します。
ストリーミングの変更を確認するには、次を確認してください。
- チャネルはターンループに動作をハードコーディングする代わりに、ケイパビリティを宣言していますか?
- 下書きメッセージは、すべての成功、無応答、失敗、キャンセルの経路において、確定、キャンセル、または置換されていますか?
/stopは正しい送信者/スレッドのスコープをキャンセルしますか?- 中断されたターンは、部分的なアシスタントの応答を完全なものであるかのように永続化することを回避しますか?
- ユーザーに見える動作は、ダイレクトメッセージ、グループチャット、スレッド返信の間で一貫していますか?
ヘルスとバックオフ
長時間実行リスナーは、オペレーターが理解できる方法で失敗しなければなりません。再試行可能なプラットフォーム障害はバックオフして再試行し、再試行不可能な設定または認証の障害は永久にループするのではなく明確に表面化すべきです。
リスナーの変更については、関連するパスを証明してください:
- キャンセル時のリスナーの正常なシャットダウン。
- 再試行可能な API 障害はバックオフして再開されます。
- 再試行不可能な失敗は、恒久的な構成の問題を停止または報告します。
- 1 つのチャネルが停止しても、兄弟のリスナーやオブザーバーへの配信が妨げられることはありません。
レビュー担当者向けチェックリスト
チャネル、Webhook、または channel-runtime の変更については、レビュアーのサインオフ前にこれらに回答してください:
- アダプターに残っているトランスポート固有の作業とは何か、またその理由は何ですか?
- コードは最初に
ChannelMessageをどこで作成または受信しますか? - このメッセージのチャネルエイリアスを所有しているエージェントはどれですか?
- 変更は共有のディスパッチとターンのライフサイクルを再利用していますか?
- チャネル固有のライフサイクル動作を追加する場合、検討された共有フックまたは機能は何で、なぜそれでは不十分なのか?
- self-loop、addressedness、passive context、および reply intent はどのように運ばれますか?
- メディア、リンク、添付ファイル、およびツール出力は、プロバイダー可視コンテキストに入る前に制限されていますか?
- 高速確認応答がある場合でも、同期ディスパッチと同じバックグラウンドターン動作を引き続き保持しますか?
- リロード、リスナーのキャンセル、プロバイダーのタイムアウト、
/stop、応答なし、送信失敗が発生した場合はどうなりますか? - 変更された境界を検証するのは、どのフォーカステストまたは手動スモークテストですか?
ソースポインタ
正規ドキュメント:
- リクエストのライフサイクル
- ランタイム状態と永続化
- メモリとペイロードのライフサイクル
- 設定のライフサイクル
- チャンネルの概要
- ゲートウェイ HTTP API
- FND-001: 意図的なアーキテクチャ
- プラグインプロトコル
主要なコードエントリポイント:
- Channel トレイトとメッセージ形状:
crates/zeroclaw-api/src/channel.rs - イングレスコンテキスト ABI:
crates/zeroclaw-api/src/ingress.rs - チャネルディスパッチとターンライフサイクル:
crates/zeroclaw-channels/src/orchestrator/mod.rs - 実行時ターンループ:
crates/zeroclaw-runtime/src/agent/turn/ - ランタイムの汎用プロセスエントリポイント:
crates/zeroclaw-runtime/src/agent/loop_.rs - Gateway webhook/chat パス:
crates/zeroclaw-gateway/src/lib.rs