Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

ランタイム構成契約(提案)

ステータス: 提案段階、未実装。本ページは、いかなる呼び出し元も移行する前に、#10993 が求めているコンポジション API の提案です。同 Issue の最初の受け入れ項目と、#7432 R1 の設計部分を実現するものです。これに付随する唯一のコードは crates/zeroclaw-runtime/src/composition.rs の型スケルトンであり、現時点ではどこからも利用されていません。

目標は、RFC #5574 が Phase 2 D1 に対して定めたもので、エンベッダーが自ら提供する機能を用いて実行できるエージェントランタイムを作ることです。そのランタイムは「Telegram、Discord、Anthropic、あるいは特定のツール実装についての知識を一切持たない」ものであり、バイナリは薄い配線レイヤーになります。

現在のランタイムの状況

c44909b1ed の master で、cargo metadata と、#[cfg(test)] モジュールおよびテストファイルを除外した本番コード用の grep を使って計測しました。

クレートのエッジ

クレート依存関係(ワークスペースのクレート)
zeroclaw-runtimeapi、コマンド、設定、インフラ、ログ、マクロ、メモリ、プロバイダー、relay-proto、sop-graph、spawn、tls、tool-call-parser、ツール;プラグイン(任意)
zeroclaw-channelsapi, 設定, インフラ, ログ, メモリ, プロバイダー, ランタイム, spawn, tool-call-parser; ツール (任意)
zeroclaw-gatewayAPI、チャンネル、設定、ハードウェア、インフラ、ログ、メモリ、プロバイダー、ランタイム、起動、TLS、ツール;プラグイン(任意)

ランタイムは、すべての具体的なプロバイダー、メモリバックエンド、オプションのツールを無条件にリンクしています。channels クレートはランタイムの上位に位置しており、これは #6864 で述べられている依存関係の逆転です。

Where concrete capabilities are constructed 具体的な機能が構築される箇所

ランタイム内では、すべての ケイパビリティ の構築が少数のエントリ関数に集約されています:

関数ビルド
agent::loop_::runプロバイダー, メモリ, オブザーバー, ツールレジストリ
agent::loop_::process_message_sharedプロバイダー, メモリ, オブザーバー, ツールレジストリ
Agent::from_config_with_session_cwd_and_mcp_approval_modeプロバイダー, メモリ, オブザーバー
Agent::try_apply_model_switch, build_session_model_providerプロバイダー
agent::turn::assemble_owned_executionメモリ, ツールレジストリ
tools::delegate (build_target_provider、memory_for_target_agent、independent_agentic_tools_for_target)プロバイダー、メモリ、ツールレジストリ(委任先のターゲット用)
daemon::run, daemon::run_heartbeat_workerメモリ, オブザーバー
cron::scheduler::run_agent_jobメモリ
tools::all_tools_with_runtime と default_toolsツールレジストリ自体
機能生産現場の建設現場ファイル
プロバイダー136
メモリ148
Observer84
ツールレジストリの組み立て106
zeroclaw_tools:: を使用98 (tools/mod.rs 内: 78)10

構築はランタイムの外側でも、ランタイムと並行して行われます。これは依存関係の問題だけでなく、2つ目の信頼できる情報源(真実の源)でもあります。

  • Gateway(zeroclaw-gateway/src/lib.rs)は、独自に耐障害性のあるプロバイダー、メモリバックエンド、オブザーバー、そして2つのツールレジストリを構築し、その後 agent::process_message を呼び出します。
  • Channels orchestratorは送信者ごとのプロバイダーキャッシュ(get_or_create_provider)を保持しており、それはcreate_resilient_model_provider_from_refで構築され、all_tools_with_runtimeを直接呼び出します。
  • ACP server は Agent::from_config_with_session_cwd_and_mcp_backchannel* を使ってエージェントを構築します。
  • src/main.rs は複数のコマンド向けにプロバイダーを構築し、SOP エンジン向けにメモリを構築したうえで、agent::run と daemon::run を呼び出します。

DaemonRegistry が既に行っていること

daemon::DaemonRegistry(#7430 で導入)はサブシステムのスターターを登録します。対象は gateway、channels orchestrator、local socket、WSS、relay、enrollment、MQTT です。また、共有の SOP エンジンと監査ロガーも保持します。providers、memory、tools、observers は登録しません。これはプロセスの構成を扱うものであり、機能の構成を扱うものではありません。

Proposal

二つのレジストリ — ライフタイムが二種類あるため

RFC #5574 では、register_channel、register_tool、set_provider、set_memory、set_observer を持つ単一の Registry を提案していました。現在のコードには寿命の異なる2つのものがあり、それを1つの型にまとめると、その違いが見えなくなってしまいます。

機能サブシステム
何Providers、メモリ、ツール、送信チャネル、オブザーバーゲートウェイ, チャネルオーケストレーター, ソケット, WSS, リレー, 登録, MQTT
タイプRuntimeCapabilities (新規)DaemonRegistry (既存)
ライフタイム設定生成 1 回1回のデーモン実行またはリロードの反復処理
消費元エージェントのターンごとdaemon::run の監視

ランタイムは両方を保持します。サブシステムのスターターは、独自に構築する代わりに世代の capabilities を受け取ります。これにより channels オーケストレーターにおける並行構築が取り除かれます。ゲートウェイは例外で、そのスターターは v0.9.0 のゲートウェイ分割で削除されるため、このコントラクトへの移行は行われません(ステップ 4)。

ソースであり、インスタンスではない

The RFC のスケッチでは、完成済みのインスタンス(set_provider(Arc<dyn Provider>))を渡しています。これは現在のランタイムでは扱えません:

  • プロバイダーとメモリーは、マルチエージェントモデル(ADR-011)の下でエージェントごとに解決されます。

  • A session can モデルを切り替える mid-conversation (try_apply_model_switch)…

    Let me translate carefully: “A session can switch model mid-conversation (try_apply_model_switch), and a delegate resolves a different agent’s provider and memory.”

    セッションは会話の途中でモデルを切り替えることができ(try_apply_model_switch)、デリゲートは別のエージェントのプロバイダーとメモリを解決します。

  • A live config apply produces a new generation (ADR-012), and turns already running must keep the capabilities they started with.

    Wait, that’s the source text itself. Let me restate what I’m producing: the Japanese translation.

    generation → 世代 (established term for config/state versions in this context)

    ライブ設定の適用により新しい世代(ADR-012)が生成され、実行中のターンは開始時の機能を維持し続ける必要があります。

ランタイムは、sources に対して、エージェントと世代を指定して必要なものを要求します。ソースは、どのようにビルドしてキャッシュするかを決定します。ランタイムは、いつ要求するか、どのエージェントに対して、解決済みのどのポリシーのもとで要求するかを決定します。

Public types

これらはスケルトン内にあります、zeroclaw_runtime::composition:

#![allow(unused)]
fn main() {
#[derive(Clone)]
pub struct RuntimeCapabilities {
    pub providers: Arc<dyn ProviderSource>,
    pub memory: Arc<dyn MemorySource>,
    pub tools: Arc<dyn ToolSource>,
    pub channels: Arc<dyn ChannelSource>,
    pub observer: Arc<dyn Observer>,
}

pub trait ProviderSource: Send + Sync {
    fn model_provider(&self, request: &ProviderRequest<'_>)
        -> anyhow::Result<Arc<dyn ModelProvider>>;
}
pub trait MemorySource: Send + Sync {
    fn memory(&self, request: &MemoryRequest<'_>) -> anyhow::Result<Arc<dyn Memory>>;
}
pub trait ToolSource: Send + Sync {
    fn tools(&self, request: &ToolRequest<'_>) -> anyhow::Result<Vec<Box<dyn Tool>>>;
}
pub trait ChannelSource: Send + Sync {
    fn channel(&self, alias: &str) -> Option<Arc<dyn Channel>>;
}
}

ProviderRequest は config、エージェントのエイリアス、および任意の明示的なプロバイダー参照を保持します。MemoryRequest は config とエージェントのエイリアスを保持します。ToolRequest は config、エージェントのエイリアス、解決済みの SecurityPolicy、選択された RuntimeAdapter、およびエージェントのメモリハンドルを保持します。

The runtime entry point these feed is proposed as follows. It is not in the skeleton, because it cannot exist without an implementation:

Wait—this input is already in English and I’m instructed to translate to ja. Translating it gives:

実行時のエントリポイントは、これらが供給する形で次のように提案されています。実装なしには存在し得ないため、スケルトンには含まれていません:

#![allow(unused)]
fn main() {
pub struct Runtime { /* config, Arc<RuntimeCapabilities>, DaemonRegistry */ }

impl Runtime {
    pub fn new(config: Config, capabilities: RuntimeCapabilities) -> Self;
    pub fn with_daemon(self, subsystems: DaemonRegistry) -> Self;
    /// デーモンなしで行うエージェントの1ターン: 独立したコンシューマーのパス。
    pub async fn run_turn(&self, agent_alias: &str, message: &str) -> anyhow::Result<String>;
}

デーモンモード: 登録されたサブシステムを監視します。
pub async fn run(runtime: Runtime) -> anyhow::Result<DaemonExit>;
}

run_turn は意図的に最小限の実装となっています。セッションの直列化、受付制御、キャンセル、承認を含む、受信コマンドの完全な処理経路は、#11012 で追跡されている RuntimeIngress コントラクトの対象です。run_turn は、そのコントラクトが導入されるまでの間、独立コンシューマの受け入れ基準に対して安定したインターフェースを提供するために存在しており、RuntimeIngress が導入された後は、その薄いクライアントになる予定です。

所有権とライフタイムのルール

  1. ランタイムは解決を担い、ソースは構築を担う。 ランタイムは、リクエストがどのエージェント、どのプロバイダー参照、どの生成を対象とするかを決定します。ソースがその判断のためにアンビエントな状態を読み取ることはありません。
  2. 機能は世代ごとに不変です。 配線を変更する設定適用では新しい RuntimeCapabilities が構築されます。ターンは開始時に受け取った Arc を完了まで保持するため、リロードによって実行中のターンの下からプロバイダーが入れ替わることはありません。
  3. 返される handle は、ある scope の間だけ借用される。 provider や memory の handle は、それを要求した session または turn に属する。ソース側は instance をキャッシュして共有してよいが、呼び出し元は session の範囲を超えて handle を保持してはならない。
  4. エージェントごと・世代ごとに1つのストア。 同じ世代内で同じエージェントに対する2つのメモリ要求は、必ず同じ基盤ストアに到達しなければなりません。現在のコードは同じ設定から構築することで、これを暗黙的に保証しています。本契約は、これをソースの義務として明文化するものです。
  5. オブザーバーは世代ごとにプロセス単位で存在します。 ソースではなくインスタンスであり、エージェントごとに解決されるものではないためです。
  6. アウトバウンドのみ。 ChannelSource は、ランタイムが送信するチャネル(エイリアス指定の返信、スケジュール配信、承認プロンプト)を対象とします。受信ターンは RuntimeIngress を介して到着し、このコントラクトは対象外です。

セキュリティポリシーはランタイムが管理します

この問題では、解決済みのセキュリティポリシーを境界をまたいで明示的に保つ必要があります。ルールは次のとおりです:

  • ランタイムは各エージェントの SecurityPolicy と RuntimeAdapter を解決し、それらを ToolRequest に渡します。ネイティブソースは、渡された値に基づいてツールを構築することが求められる信頼された実装です。これらを Rust のトレイトに渡しても、プロセス内のコードが機械的に制限されるわけではありません。ソースは任意のネイティブツールを返せるため、ランタイムへの受け入れ審査、ポリシーによるフィルタリング、呼び出しごとのゲート制御と承認は、それぞれ独立した要件として残ります。
  • ポリシーに対してツールを構築しても、何かが許可されるわけではありません。構築後も、ランタイムは従来どおりすべての呼び出しを制御し、承認を行います。
  • ランタイムは、コアツールを追加することも、ポリシーで除外されたツールを削除することもあります。ソースが返したからといって、ポリシーで禁止されたツールを追加することは決してありません。

最初の実装スライスにおけるパリティテストでは、代表的な設定内のすべてのエージェントについて、有効なツールセットと承認動作が変更されていないことを示す必要があります。

DaemonRegistry の位置づけ

DaemonRegistry は置き換えられるのではなく、組み込まれています:

  • その役割と現在の登録方法は維持されます。
  • channels、cron、SOP、RpcContext のスターターは generation の RuntimeCapabilities にアクセスできるようになり、独自に providers、memory、tools を構築しなくなります。このシグネチャ変更は独立したスライスとして扱います(下記のステップ4)。gateway スターターは capabilities を受け取りません。v0.9.0 のカットで削除され、その routes が RPC へ移行するため、gateway 自体の構築も不要になります。
  • 共有SOPエンジンと、現在それが保持している監査ロガーは、機能形状の状態です。本提案ではこれらはそのままの場所に残し、機能世代の背後へ移動する作業はフォローアップとして挙げています。

関連する他の作業との関係

  • #6864 は 2026-08-23 に機械的な依存関係ゲーティングへと範囲を絞り込まれました。当初提案されていたオーケストレーターの移動は、ランタイムが所有するセッション作業に移りました。本提案はそのいずれとも重複しません。移動後のオーケストレーターが利用する機能コントラクトを提供するものです。
  • #11012 およびその RFC #9487 は受信コマンド経路(RuntimeIngress / InboundTurn)を担当します。本提案は、ターンが 何から構築されるか を担当します。両者は、受信ハンドラが保持する Runtime ハンドルで接続します。
  • #10998 (デフォルトランタイムではコアツールのみ) は ToolSource を利用しており、オプションのツールはアプリケーションの ToolSource の背後に移動され、ランタイムにはコアツールのみが残ります。

契約の所在

この契約には Config と SecurityPolicy が必要ですが、これらは zeroclaw-config にあります。zeroclaw-api は設計上ワークスペースの依存関係を持たないため、候補から外れます。残りの配置先は次のとおりです:

オプション対象に対して
A. zeroclaw-runtime(スケルトンの現在の配置)これは_まさにランタイムの公開インターフェース_です。新しいクレートは追加しません。呼び出し元はすでにこれに依存しています。|The crate is the transitional holding crate. Placing new public API there needs a Core Team exception under ADR-016, which this proposal cannot grant itself





















| |B. api と config に依存する新しい contract crate|クリーンな境界を保ち、ランタイムに依存せずにアプリケーションの配線を行えるようにする||Translate to Japanese: “A new crate ahead of the planned kernel extraction; the ADR-016 record already notes the risk of establishing boundaries early” 計画されているカーネル抽出に先立つ新しいクレート。ADR-016 の記録には、境界を早期に確立することのリスクがすでに記載されている| |-|| |C. 計画中の zeroclaw-kernel|両方のアクティブなランタイム例外が既に指定している宛先|存在しないため、先にそれを抽出すると agent-loop の抽出作業がブロックされてしまいます。|

決定: オプション A。 コントラクトは zeroclaw-runtime に置かれ、#11092 で別途記録された、範囲を限定した holding-crate の例外の下にあります。この PR は Core Team の承認 を受け、2026-09-28 にマージされました。対象範囲は src/composition.rs、src/agent/{loop_.rs,agent.rs,turn/mod.rs} 内でそれを利用する *_with_capabilities エントリポイント・アダプタ、そして src/daemon/{registry.rs,mod.rs} 内の capability を保持する DaemonRegistry starter シグネチャです。ただし #10993 composition API と本ページの移行手順に限定されます。具体的な provider、memory、tool の構築処理をこのクレートへ移すことは対象外です。

意図された移管先は、計画中のzeroclaw-kernel抽出における構成の所有者です。この例外は、エージェントループ抽出の設計レビュー時、または契約がproviders、memory、tools、outbound channels、observerを超える機能の種類を追加する前にレビューに付されます。配置に関する前提条件は、記録された範囲内で満たされています。ただし、これはより広範なランタイムの追加を承認するものではなく、実装の受け入れを確定するものでもありません。

移行の順序(呼び出し元向け)

すべての手順で既存の設定と実効パーミッションを維持します。issue のリスクセクションで求められているとおり、古いエントリポイントは最後の呼び出し元が移行するまで互換アダプターとして残します。

ステップ変更証拠が必要です
0この提案とスケルトンです。呼び出し元はありません。API境界のレビュー
1DefaultCapabilities::from_config: 現在のcreate_*関数をそのままラップするソースProvider・メモリ・ツールセット解決のエージェントごとのパリティテスト
2ランタイムのエントリポイントに *_with_capabilities 形式が追加されます: agent::loop_::run、process_message_shared、Agent::from_config*、assemble_owned_execution。従来のシグネチャは DefaultCapabilities を経由して委譲されます。|Existing agent tests pass unchanged; policy-propagation regression tests 既存のエージェントテストは変更なしで合格;ポリシー伝播の回帰テスト| |-|
3src/main.rs は DefaultCapabilities を一度ビルドし、agent と daemon に対して新しいフォームを呼び出しますCLIとデーモンの起動、キャンセル、シャットダウンに関する回帰テストのカバレッジ
4channels starter、cron scheduler、SOP drivers、およびRpcContextは、世代のRuntimeCapabilitiesを受け取ります。gateway starterは v0.9.0 のカットで削除され、RuntimeCapabilitiesは受け取りません。そのルートがRPCに移行すると、gatewayの構築自体も不要になります。移行済みの各スターターでパリティを確保する。移行済みスターターに create_* の呼び出しを残さないこと。ゲートウェイはその場では移行しない。再結合できないことを確認する手段はルールではなく、ステップ7の依存関係ラチェットである。
5Channels オーケストレーターの送信者ごとのプロバイダーキャッシュは ProviderSource の実装になり、ACP は新しい形式へ移行します。#11012 と順序を合わせて実施します。zeroclaw-channels 内に残っている create_* 呼び出しはありません
6内部呼び出し元: delegate, cron, heartbeat, サブエージェント, SOP ツール仕様DefaultCapabilities 以外のランタイムに、create_* の呼び出しは残っていません
7DefaultCapabilities と tier-3(オプション)のツール組み立ては、ToolSource の背後にあるアプリケーション層へ移動します。tier-1(コア)と tier-2(ホスト依存)のツールは引き続きランタイムで構築され、ランタイムは v0.9.0 まで(下記参照)zeroclaw-tools を説明付きの依存関係として保持します。これを削除するのは v1.0.0 の作業です。ツールは 3 つの粗い Cargo フィーチャーにグループ化されます。tools-core(常に有効)、tools-host、tools-extra(いずれもデフォルトで有効)です。加えて、重い依存関係向けに統合ごとのフィーチャーがあります(#10998 と併せて)。依存関係のラチェットテストです。cargo metadata を対象とし、3つのルールがあります。ランタイムは zeroclaw-tools への依存辺を説明します。ゲートウェイには runtime、channels、tools、providers、memory、plugins、config、hardware、sop-graph のいずれも含めません。rpc-proto は api、config、sop-graph のみに依存し、tokio による I/O は行いません。独立したコンシューマが、tier 3 を空で返す ToolSource を用いて、Runtime::run_turn 経由で実際のエージェントターンを実行します。--no-default-features --features agent-runtime,tools-core を指定したビルドと、ターゲットごとの dist のサイズを比較したサイズの証跡です(#10998 AC5)。
8互換性アダプターを削除する削除は、呼び出し元が残っていなければ機械的に行えます。

残っている依存関係と、その理由

この Issue は、保持される依存関係について、未使用の機能の背後に隠すのではなく、その理由を説明することを求めています。期待される最終状態は次のとおりです:

  • zeroclaw-tools: v0.9.0まで保持する、説明された例外。 Tier-1のコアツールは現在2つのクレートに分かれています。shellとfile_readはランタイムにあり、file_write、file_edit、glob_search、content_search、メモリツール、git_operations、web_fetchはzeroclaw-toolsにあります。tier-2のツールには、ToolRequestが保持していないランタイム内部(スケジューラ、キャンバスストア、ask-userハンドル、SOPエンジン、セッションバックエンド、ライブ設定)が必要です。どちらかを アプリケーションのToolSourceの背後に移すと、これらの内部が公開契約に漏れ出してしまいます。そのためステップ7ではtier 3のみを移動し、ratchetでこの例外を明記します。依存関係の削除はv1.0.0の作業です。
  • zeroclaw-providersとzeroclaw-memory: 当面は残存させる想定です。理由はターン構成以外にあります。doctor、quickstart、migration、およびvision routingは、プロバイダーやメモリを直接構築しており、現時点でこのクレートにホストされたままです。それぞれがホールディングクレート計画における個別の抽出候補です。ステップ7のratchetでは、これらの依存関係のみを許可して名前を明記し、新たな利用が発生した場合はチェックで失敗するようにすべきです。

Acceptance mapping

#10993 受け入れ項目提供元
公開コンポジションAPI、所有権とライフタイムのルール、DaemonRegistry、#6864の関連部分をドキュメント化するこのページ(ステップ 0)
|Independent consumer runs a real agent turn with supplied capabilities, without concrete provider/channel/tool construction inside the runtime 独立したコンシューマーが、ランタイム内で具体的な provider/channel/tool を構築せずに、提供された capabilities を使って実際のエージェントターンを実行する| |-|提案されたステップ7の下では部分的な対応です。Runtime::run_turn は供給された capability を実行しますが、tier-1 のコアツールと tier-2 のホスト結合ツールは依然としてランタイムで構築されます。この段階的な retained-tool 設計は基準を完全には満たしません。残りの構築境界を実装して検証するか、#10993 で基準を明示的に調整する必要があり、受け入れを完了する前にいずれかを行ってください。
依存関係チェックで境界を示し、保持される依存関係を説明するステップ7のラチェットと上のセクション
CLIとデーモンは共有コントラクトを使用し、ポリシー、起動、キャンセル、シャットダウンのカバレッジを備えています翻訳する文字列が含まれていません。「Steps 2 and 3」の本文を貼り付けてください。
#7432 R1 に関連する実装 PR と証跡各ステップの PR

Settled decisions

これらはこのページが最初に提案された時点では未解決でした。v0.9.0 のアーキテクチャレビューで両方とも決着しました。

  1. run_turn と RuntimeIngress の待機の比較。 run_turn は最小限の形で出荷され、session/prompt が使うものと同じセッションアクターのクライアントとして動作します。#11012 を待つことはありません。
  2. ProviderSource がリクエストの主体(principal)を受け取るかどうか。 受け取ります。最初の実装スライスから、capability を受け取るコンストラクタが導入される時点で、ProviderRequest に principal: Option<&PrincipalId> が追加されます。埋め込み側が実装するリクエスト型にフィールドを後から追加すると破壊的変更になるためです。このページのスケルトンにはまだこのフィールドは含まれていません。