ランタイム
ランタイムアダプタ、AgentProvider 内部構造、ランタイムごとの差分を整理します。
各ランタイムは共通の AgentProvider インタフェースを実装するクラスとして
接続されます。アダプタは core/providers/{claude-code,codex,opencode,grok,cursor}.ts、
ACP を話すランタイム (Grok Build と Cursor) は core/providers/acp/base.ts
を共有します。
対応ランタイム概要
SNA は、product が 1 つの CLI に固定されず、workflow に合う runtime を 選べるように設計されています。次の表は product 観点の概要です。細かな 機能対応は 機能 × ランタイムマトリクス で確認できます。
| ランタイム | 向いている用途 | 強み | Tradeoff |
|---|---|---|---|
| Claude Code | Anthropic model と安定した local coding session、豊かな permission behavior が必要な場合。 | CLI behavior が成熟していて、tool-use UX が良いです。Thinking/tool input delta streaming と token/cost metadata が詳しいです。 | 呼び出し/セッションごとに stateless なので daemon pooling はありません。Permission approval は SNA との双方向通信ではなく hook ベースです。 |
| Codex | 多数の session、one-shot job、頻繁な runtime reuse が必要な low-latency product integration。 | RuntimePool で daemon を再利用し、thread ごとの cwd 変更をサポートします。Item lifecycle event と shell output delta が豊富です。 | Codex native config shape が別にあり、Claude Code の stream-json のような tool input delta は露出しません。 |
| OpenCode | OpenCode をすでに使っている app、または OpenCode model/provider 設定を HTTP daemon として使いたい app。 | Pooled daemon と SDK-backed HTTP path を使えます。1 つの cwd で repeated prompt を処理しやすいです。 | Daemon は single-cwd で、一部 runtime mutation は respawn が必要です。Event detail は OpenCode stream が出す範囲に依存します。 |
| Grok Build | Grok Build と公開 protocol surface である ACP を使いたい session。 | SNA の shared ACP adapter を使い、bidirectional permission request と単純な stateless session lifecycle を持ちます。 | Runtime pooling はなく、model/cwd 変更には respawn が必要です。Token/tool detail metadata は Claude/Codex より薄めです。 |
| Cursor | Cursor を主な作業環境として使い、Cursor subscription-backed headless CLI を SNA から動かしたい場合。 | cursor-agent と既存の Cursor login を使います。ACP permission、Cursor model selection、model-id reasoning variant を利用できます。 | Daemon pooling はなく、model/cwd 変更には respawn が必要です。Setup はユーザーの Cursor CLI 認証状態に依存します。 |
以下では、この product-level choice の裏側にある adapter 内部構造を説明します。
AgentProvider
interface AgentProvider {
name: string;
isAvailable(): Promise<boolean>;
spawn(opts: SpawnOptions): AgentProcess;
complete(opts: CompleteOptions): Promise<CompletionResult>;
// ...
}
interface AgentProcess {
send(message: string, opts?): void;
interrupt(): void;
setModel(model: string): void;
setPermissionMode(mode: PermissionMode): void;
applyPatch(patch: SessionPatch): PatchResult;
respondToPermission(approved: boolean): void;
kill(): void;
on(event: string, handler): void; // AgentEvent を emit
}applyPatch() は PATCH /agent/session が使うフィールドごとの
ディスパッチフックです。各ランタイムアダプタがどのフィールドをインプレースで
適用でき、どれが respawn-with-history-replay を要するかを決めます。
ランタイムごとの戦略
| ランタイム | 転送 | 状態 | プール? |
|---|---|---|---|
| claude-code | claude -p (one-shot) + claude --resume (replay) | 呼び出しごとに stateless | いいえ |
| codex | codex app-server デーモン、stdio 上の JSON-RPC | 永続デーモン、マルチスレッド | はい |
| opencode | opencode serve デーモン + SDK HTTP | 永続デーモン、single-cwd | はい |
| grok | grok agent stdio (stdio 上の Agent Client Protocol — Grok Build CLI) | セッションごとに stateless | いいえ |
| cursor | cursor-agent acp (stdio 上の Agent Client Protocol — Cursor CLI) | セッションごとに stateless | いいえ |
Codex と OpenCode ランタイムアダプタは RuntimePool 経由でデーモンを共有
します。セッションを開くとき、プールが (provider, cwd, config) で
照会され、互換のデーモンがあれば再利用してコールドスタートコスト
を節約します(Codex 約 2 秒、OpenCode 約 3 秒)。
Grok Build は、SNA で初めてワイヤプロトコルに公開標準
(ACP)を採用したランタイムでした。
2 番目の ACP-speaking ランタイムとして Cursor が加わったことで、
共有ロジックは core/providers/acp/base.ts に抽出されました — JSON-RPC
pump、initialize/session/new ハンドシェイク、session/update →
AgentEvent 変換、双方向 session/request_permission
(+bypassPermissions 自動承認)、ランタイム間 resource block history
インジェクションまで。各サブクラスには実際に異なる部分だけを残します:
CLI パス探索、spawn 引数、任意の認証ステップ (Cursor の
authenticate({methodId:"cursor_login"}))、ベンダ拡張通知の prefix
(_x.ai/* Grok、cursor/* Cursor)、ランタイム固有の tool-call unwrap。
ランタイム間の history replay は、ACP セッションの最初の
session/prompt に正規化された会話履歴を 1 個の ACP resource
content block として埋め込む方式で扱います — 機能マトリクスページの
ランタイム間 history replay
にまとめています。
Cursor はユーザーがすでに済ませた cursor-agent login をそのまま使います
(macOS Keychain の cursor-access-token / cursor-refresh-token →
Cursor サブスクリプションを利用)。CURSOR_API_KEY を別途発行する必要は
ありません。SNA は spawn の環境変数をそのまま渡し、子プロセスは
ユーザーがインタラクティブに使うときと同じ資格情報を使います。Reasoning
level はモデル ID 自体にエンコードされます (gpt-5.3-codex →
level 4 なら gpt-5.3-codex-high に変換)。reasoning-level.ts の
applyCursorReasoning がその変換を行い、effort family に属さないモデル
(composer-*、auto など) はそのまま通過します。
RuntimePool
prepare(opts, provider): 既知の cwd に対してデーモンを事前 warm-up。findExisting(spawnOpts): セッション spawn 用の strict マッチ (設定が揃っている必要)。findCompatible(provider, cwd):completion()/runOnce()の 単発呼び出し用の loose マッチ (MCP、hooks、permission mode は 無視)。shutdown(handle): 参照カウントが 0 になったら graceful 終了。
Claude Code はプーリングしません。CLI 自体が stateless なので、典型的 なセッション粒度では呼び出しごとの spawn がデーモン resume と 十分に競争力を持ちます。
SNA が ACP だけをランタイム層にせず、高忠実度のネイティブランタイムアダプタを 維持する理由は SNA と ACP で扱います。
クロスランタイム設定
すべてのランタイムで使える 2 つのレイテンシ設定:
reasoningLevel: 0..5: ランタイム非依存、最軽量から最重量 まで。reasoning-level.ts が Claude Code の--effort、Codex のmodel_reasoning_effort、 Grok Build の--effort、Cursor のモデル ID サフィックス (gpt-5.3-codex-high等) に翻訳します。OpenCode はこのフィールドを 無視します。providerOptions.serviceTier: Codex 専用。Codex の/fastスラッシュコマンドをミラーする (値:"priority","flex","batch")。Claude Code には意図的に自動マッピングしません。 Claude Code の/fastは別の課金プールを持つ別の MODEL バリアント だからです。
全ランタイムにわたる機能ごとの互換性については 機能 × ランタイムマトリクス で確認できます。