ACP (Agent Client Protocol) (日本語)
ACPとは?
Section titled “ACPとは?”ACP(Agent Client Protocol)は、OmniRoute向けの**「バックエンドとしてのCLI」トランスポートです。AIプロバイダーへのHTTP API呼び出しをインターセプトする代わりに、ACPはCLIエージェントを子プロセスとして起動**し、それぞれのネイティブインターフェースを介してプロンプトを送信します。
ACPを使用する理由
Section titled “ACPを使用する理由”| メリット | 説明 |
|---|---|
| APIキーが不要 | 既存のCLI認証を使用します |
| ネイティブプロトコル | 各CLIのネイティブな入出力形式を使用します |
| 自動検出 | システムにインストールされているCLIを検出します |
| 15個の組み込みエージェント | 一般的なCLIツール向けに事前設定されています |
| カスタムエージェント | 設定から独自のCLIツールを追加できます |
| プロセス管理 | ライフサイクル(起動、送信、終了)を処理します |
対応しているCLIエージェント
Section titled “対応しているCLIエージェント”ACPは、標準で15個の組み込みCLIエージェントをサポートしています。
| エージェントID | 表示名 | バイナリ | プロトコル |
|---|---|---|---|
codex |
OpenAI Codex CLI | codex |
stdio |
claude |
Claude Code CLI | claude |
stdio |
goose |
Goose CLI | goose |
stdio |
openclaw |
OpenClaw | openclaw |
stdio |
aider |
Aider | aider |
stdio |
opencode |
OpenCode | opencode |
stdio |
cline |
Cline | cline |
stdio |
qwen |
Qwen Code | qwen --acp |
stdio |
forge |
ForgeCode | forge |
stdio |
amazon-q |
Amazon Q Developer | q |
stdio |
interpreter |
Open Interpreter | interpreter |
stdio |
cursor-cli |
Cursor CLI | cursor |
stdio |
warp |
Warp AI | warp |
stdio |
gemini |
Gemini CLI | gemini |
stdio |
zcode |
ZCode | zcode |
stdio |
カスタムエージェント
Section titled “カスタムエージェント”設定から独自のCLIエージェントを追加できます。カスタムエージェントは、組み込みエージェントと同じ機能をサポートします。
クイックスタート
Section titled “クイックスタート”ステップ1:CLIエージェントをインストールする
Section titled “ステップ1:CLIエージェントをインストールする”# 例:Claude Code CLIをインストールnpm install -g @anthropic-ai/claude-code
# インストールを確認claude --versionステップ2:ACPによる自動検出
Section titled “ステップ2:ACPによる自動検出”ACPは、システムにインストールされているCLIエージェントを自動的に検出します。設定は不要です!
ステップ3:ACPトランスポートを使用する
Section titled “ステップ3:ACPトランスポートを使用する”検出後、ACPをサポート対象の任意のプロバイダーのトランスポートとして使用できます。CLIが利用可能な場合、OmniRouteはACPを自動的に使用します。
ACPの仕組み
Section titled “ACPの仕組み”アーキテクチャ
Section titled “アーキテクチャ”┌─────────────────┐│ OmniRoute ││ (HTTPプロキシ) │└────────┬────────┘ │ │ spawn() ▼┌─────────────────┐│ 子プロセス ││ (CLIエージェント) ││ ││ stdin ◄──────┤ プロンプトを送信│ stdout ──────►│ 応答を受信│ stderr ──────►│ エラーを受信└─────────────────┘プロセスのライフサイクル
Section titled “プロセスのライフサイクル”- 起動 — ACPがCLIエージェントの子プロセスを作成します
- 送信 — ACPがプロンプトをプロセスのstdinに書き込みます
- 受信 — ACPがstdout/stderrから応答を読み取ります
- アイドル検出 — ACPは、応答が完了したと判断する前に2秒間出力がないことを待ちます
- 終了 — ACPがプロセスを終了します(SIGTERMを送信し、5秒後にSIGKILLを送信)
通信プロトコル
Section titled “通信プロトコル”ACPは、CLIエージェントとの通信にstdio(標準入出力)を使用します。プロトコルは次のとおりです。
- プロンプトを送信 — 改行を付けてstdinに書き込みます
- 応答を待機 — アイドル状態になるまでstdoutから読み取ります(2秒間出力なし)
- タイムアウト — デフォルトは120秒です(設定可能)
APIリファレンス
Section titled “APIリファレンス”レジストリ関数
Section titled “レジストリ関数”detectInstalledAgents()
Section titled “detectInstalledAgents()”システムにインストールされているすべてのCLIエージェントを検出します。結果は60秒間キャッシュされます。
import { detectInstalledAgents } from "@/lib/acp";
const agents = detectInstalledAgents();// 戻り値: CliAgentInfo[]
interface CliAgentInfo { id: string; // 例: "codex", "claude" name: string; // 表示名 binary: string; // 起動するバイナリ名 versionCommand: string; // バージョン検出コマンド version: string | null; // 検出されたバージョン(インストールされていない場合はnull) installed: boolean; // エージェントがインストールされているかどうか providerAlias: string; // OmniRoute内のプロバイダーID spawnArgs: string[]; // 起動時に渡す引数 protocol: "stdio" | "http"; // 通信プロトコル isCustom?: boolean; // ユーザー定義のカスタムエージェントかどうか}getAvailableAgents()
Section titled “getAvailableAgents()”インストール済みでACPに使用可能なエージェントのみを取得します。
import { getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();// 戻り値: CliAgentInfo[](インストール済みのエージェントのみ)getAgentById(id)
Section titled “getAgentById(id)”IDを指定して特定のエージェントを取得します。
import { getAgentById } from "@/lib/acp";
const agent = getAgentById("claude");// 戻り値: CliAgentInfo | undefinedsetCustomAgents(agents)
Section titled “setCustomAgents(agents)”設定からカスタムエージェント定義を設定します。
import { setCustomAgents } from "@/lib/acp";
setCustomAgents([ { id: "my-custom-cli", name: "My Custom CLI", binary: "mycli", versionCommand: "mycli --version", providerAlias: "my-provider", spawnArgs: [], protocol: "stdio", },]);マネージャー関数
Section titled “マネージャー関数”acpManager.spawn(agentId, binary, args, env)
Section titled “acpManager.spawn(agentId, binary, args, env)”新しいCLIエージェントプロセスを起動します。
import { acpManager } from "@/lib/acp";
const session = acpManager.spawn("claude", "claude", ["--print", "--output-format", "json"], { /* カスタム環境変数 */});// 戻り値: AcpSession許可されているエージェントID: ["claude", "codex", "gemini", "qwen"]
acpManager.sendPrompt(sessionId, prompt, timeoutMs)
Section titled “acpManager.sendPrompt(sessionId, prompt, timeoutMs)”CLIエージェントにプロンプトを送信し、レスポンスを収集します。
import { acpManager } from "@/lib/acp";
const response = await acpManager.sendPrompt( "acp-claude-1234567890-abc123", "What is 2+2?", 120000 // 2分のタイムアウト);// 戻り値: Promise<string>acpManager.kill(sessionId)
Section titled “acpManager.kill(sessionId)”セッションを終了し、クリーンアップします。
import { acpManager } from "@/lib/acp";
const killed = acpManager.kill("acp-claude-1234567890-abc123");// 戻り値: booleanacpManager.getActiveSessions()
Section titled “acpManager.getActiveSessions()”すべてのアクティブなセッションを取得します。
import { acpManager } from "@/lib/acp";
const sessions = acpManager.getActiveSessions();// 戻り値: AcpSession[]acpManager.killAll()
Section titled “acpManager.killAll()”すべてのセッションを終了します。
import { acpManager } from "@/lib/acp";
acpManager.killAll();セッションインターフェース
Section titled “セッションインターフェース”interface AcpSession { id: string; // 一意のセッションID agentId: string; // エージェントID(例: "claude") process: ChildProcess; // 子プロセスのハンドル alive: boolean; // プロセスが実行中かどうか stdoutBuffer: string; // 累積されたstdoutバッファ stderrBuffer: string; // 累積されたstderrバッファ createdAt: Date; // 作成日時}AcpManagerはEventEmitterを拡張し、以下のイベントを発行します。
stdout
Section titled “stdout”CLIエージェントがstdoutに書き込んだときに発行されます。
acpManager.on("stdout", ({ sessionId, data }) => { console.log(`[${sessionId}] stdout: ${data}`);});stderr
Section titled “stderr”CLIエージェントがstderrに書き込んだときに発行されます。
acpManager.on("stderr", ({ sessionId, data }) => { console.error(`[${sessionId}] stderr: ${data}`);});CLIエージェントプロセスが終了したときに発行されます。
acpManager.on("exit", ({ sessionId, code, signal }) => { console.log(`[${sessionId}] exited with code ${code}, signal ${signal}`);});CLIエージェントプロセスでエラーが発生したときに発行されます。
acpManager.on("error", ({ sessionId, error }) => { console.error(`[${sessionId}] error: ${error}`);});ACP は親プロセスからすべての環境変数を継承し、カスタム環境変数で拡張できます。
acpManager.spawn("claude", "claude", [], { ANTHROPIC_API_KEY: "sk-...", DEBUG: "true",});各エージェントには、レジストリで定義されたデフォルトの起動引数があります。これらは上書きできます。
acpManager.spawn("claude", "claude", ["--print", "--verbose"], {});タイムアウト
Section titled “タイムアウト”デフォルトのプロンプトタイムアウトは 120 秒(2 分)です。次のように上書きできます。
await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 分検出キャッシュ
Section titled “検出キャッシュ”負荷の高いファイルシステムスキャンを避けるため、エージェントの検出結果は 60 秒間キャッシュされます。強制的に更新するには、次のようにします。
import { refreshAgentCache } from "@/lib/acp";
refreshAgentCache();セキュリティ
Section titled “セキュリティ”コマンドインジェクションの防止
Section titled “コマンドインジェクションの防止”ACP は、コマンドインジェクション攻撃を防止するためにバージョンコマンドを検証します。
const DISALLOWED_VERSION_COMMAND_CHARS = /[;&|<>`$\r\n]/;以下の文字を含むバージョンコマンドは拒否されます。
;— コマンド区切り文字&— バックグラウンドプロセス|— パイプ<,>— リダイレクト`— コマンド置換$— 変数展開\r,\n— 改行
バイナリ名の検証
Section titled “バイナリ名の検証”ACP は、バージョンコマンドのバイナリが想定されるバイナリ名と一致することを検証します(カスタムエージェントの場合を除く)。
プロセスの分離
Section titled “プロセスの分離”各 ACP セッションは、それぞれ独立した子プロセスで実行されます。セッションが終了するかタイムアウトすると、プロセスは強制終了されます。
パフォーマンス
Section titled “パフォーマンス”検出パフォーマンス
Section titled “検出パフォーマンス”- 初回呼び出し: 約 50~200ms(各エージェントの
versionコマンドを実行) - キャッシュ済みの呼び出し: 1ms 未満(キャッシュから返却)
- キャッシュ TTL: 60 秒
プロンプトのパフォーマンス
Section titled “プロンプトのパフォーマンス”- 起動: 約 50~100ms
- プロンプト送信: 約 10~50ms
- 応答待機: CLI エージェントに依存(通常は 1~30 秒)
- 強制終了: 約 5 秒(SIGTERM)+ 即時(SIGKILL)
リソース使用量
Section titled “リソース使用量”- セッションあたりのメモリ: 約 10~50MB(CLI エージェントに依存)
- CPU: 最小限(I/O バウンド)
- ディスク: なし
トラブルシューティング
Section titled “トラブルシューティング”「Unknown agent」エラー
Section titled “「Unknown agent」エラー”問題: acpManager.spawn() が Unknown agent: <id> をスローする
解決策: spawn() で使用できるのは、次のエージェントのみです。
claudecodexgeminiqwen
その他のエージェントは、手動またはカスタムエージェント定義を使用して起動する必要があります。
「Session not alive」エラー
Section titled “「Session not alive」エラー”問題: acpManager.sendPrompt() が Session ${sessionId} is not alive をスローする
解決策: セッションが終了したか、強制終了された可能性があります。セッションの状態を確認してください。
const session = acpManager.getSession(sessionId);if (!session?.alive) { // セッションを再起動 acpManager.spawn("claude", "claude", [], {});}「ACP timeout」エラー
Section titled “「ACP timeout」エラー”問題: acpManager.sendPrompt() が ACP timeout after 120000ms をスローする
解決策: タイムアウトを延長してください。
await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 分CLI が検出されない
Section titled “CLI が検出されない”問題: detectInstalledAgents() が CLI を検出しない
解決策:
- PATH を確認: CLI がシステムの PATH に含まれていることを確認する
- バージョンコマンドを確認:
claude --versionを手動で実行する - 権限を確認: CLI が実行可能であることを確認する
- カスタムエージェント: 非標準の CLI 用にカスタムエージェント定義を追加する
問題: ACP が CLI を実行できない
解決策:
- ファイル権限を確認:
chmod +x /usr/local/bin/claude - 所有権を確認: OmniRoute に読み取り/実行権限があることを確認する
- SELinux/AppArmor を確認: プロセスの起動がブロックされる場合があります
例 1: Claude Code の起動と使用
Section titled “例 1: Claude Code の起動と使用”import { acpManager, detectInstalledAgents } from "@/lib/acp";
// インストール済みのエージェントを検出const agents = detectInstalledAgents();const claude = agents.find((a) => a.id === "claude");
if (claude?.installed) { // 新しいセッションを起動 const session = acpManager.spawn("claude", claude.binary, ["--print", "--output-format", "json"]);
// プロンプトを送信 const response = await acpManager.sendPrompt( session.id, "量子コンピューティングについて100語で説明してください" );
console.log("Claudeの応答:", response);
// クリーンアップ acpManager.kill(session.id);}例 2: フォールバック付き自動検出
Section titled “例 2: フォールバック付き自動検出”import { acpManager, getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// まずClaudeを試し、見つからない場合はCodexにフォールバックlet agentId = "claude";if (!available.find((a) => a.id === "claude")) { if (available.find((a) => a.id === "codex")) { agentId = "codex"; } else { throw new Error("ACP互換のCLIエージェントが見つかりません"); }}
const agent = available.find((a) => a.id === agentId)!;const session = acpManager.spawn(agentId, agent.binary, agent.spawnArgs);
const response = await acpManager.sendPrompt(session.id, "こんにちは!");
acpManager.kill(session.id);例 3: カスタムエージェント
Section titled “例 3: カスタムエージェント”import { setCustomAgents, detectInstalledAgents } from "@/lib/acp";
// カスタムCLIエージェントを登録setCustomAgents([ { id: "my-llm-cli", name: "My LLM CLI", binary: "myllm", versionCommand: "myllm --version", providerAlias: "my-llm-provider", spawnArgs: ["--format", "json"], protocol: "stdio", },]);
// detectInstalledAgents()に「my-llm-cli」が含まれるようになりますconst agents = detectInstalledAgents();次のステップ
Section titled “次のステップ”- APIリファレンス — REST APIエンドポイント
- プロバイダーリファレンス — 全352プロバイダー
- MCPサーバー — Model Context Protocolとの統合
- A2Aサーバー — エージェント間プロトコル
- クラウドエージェント — クラウドベースのエージェント
- AionUiプロジェクト — ACP自動検出の着想元
- ACPソースコード — 実装の詳細
manager.ts— プロセスのライフサイクル管理registry.ts— エージェントの検出と登録index.ts— 公開APIのエクスポート
HagiCode
HagiCode は構造化ワークフロー、マルチエージェント実行、Hero Dungeon ビューを備えたエージェント型コーディングワークスペースです。
よりスマートで速く、楽しいエージェント型ワークフローで、使いやすいソフトウェアを形にします。

- Smart構造化ワークフローは意図をアイデアから変更のリリースまで実行可能な道筋にします。
- Efficientマルチエージェントのワークフローで調査、実装、レビューを並行して進めます。
- FunHero Dungeon により長時間のコーディングを視覚的で協力的な体験にします。