- Introduced multiple new markdown files detailing the Agent Client Protocol (ACP), AI agent orchestration landscape, and various tools for managing multi-agent systems. - Included in-depth analysis of protocol standards, governance structures, and emerging frameworks relevant to AI agent integration. - Documented key features, architecture, and integration potential of various desktop and CLI orchestrators, enhancing understanding of the current ecosystem. - Provided insights into best practices for integrating multi-provider agent support within the Electron framework. This documentation aims to serve as a foundational resource for developers and stakeholders involved in AI agent orchestration and integration.
39 KiB
Agent Client Protocol (ACP) — Deep Technical Analysis
Дата исследования: 2026-03-24 Контекст: интеграция ACP в Claude Agent Teams UI (Electron 40.x)
1. Что такое ACP?
Agent Client Protocol (ACP) — это открытый стандарт коммуникации между редакторами кода (IDE) и AI-агентами. Создан Zed Industries, поддерживается JetBrains с октября 2025.
Аналогия: LSP (Language Server Protocol) стандартизировал интеграцию языковых серверов с редакторами. ACP делает то же самое для AI coding agents.
Проблема, которую решает:
- Каждый редактор делал кастомную интеграцию для каждого агента (M x N)
- Агенты были привязаны к конкретным IDE
- ACP сводит M x N → M + N (агент реализует ACP один раз, работает во всех IDE)
Лицензия: Apache 2.0 Governance: Lead Maintainers — Ben Brandt (Zed Industries), Sergey Ignatov (JetBrains)
Источники:
- Спецификация: https://agentclientprotocol.com/
- GitHub: https://github.com/agentclientprotocol/agent-client-protocol
- Zed ACP: https://zed.dev/acp
ВАЖНО: Существует ТРИ разных протокола с аббревиатурой ACP:
- Agent Client Protocol (Zed/JetBrains) — редактор ↔ агент. Это наш фокус.
- Agent Communication Protocol (IBM BeeAI) — агент ↔ агент. Сливается с A2A (Linux Foundation). Не релевантно.
- Agent Connect Protocol (Agntcy Collective) — REST API для remote agents. Не релевантно.
2. Архитектура протокола
2.1 Транспорт
| Режим | Транспорт | Формат | Статус |
|---|---|---|---|
| Локальный | stdio (stdin/stdout) | NDJSON (newline-delimited JSON) | Стабильный |
| TCP | TCP socket (порт) | NDJSON | Стабильный (Copilot CLI: --acp --port 8080) |
| Remote | HTTP / WebSocket | JSON-RPC | Work in progress |
Основной режим: JSON-RPC 2.0 поверх NDJSON через stdio. Клиент (IDE) spawn'ит агента как subprocess, stdin/stdout становятся транспортом.
2.2 Типы сообщений
Два типа (JSON-RPC 2.0):
- Methods — request-response пары, ожидают result или error
- Notifications — односторонние сообщения, без ответа
2.3 Lifecycle
Client Agent
| |
|------- initialize --------------->| (версия протокола + capabilities)
|<------ InitializeResponse --------| (agent capabilities)
| |
|------- authenticate ------------->| (если требуется)
|<------ AuthenticateResponse ------|
| |
|------- session/new --------------->| (cwd, mcpServers[])
|<------ NewSessionResponse ---------| (sessionId)
| |
|------- session/prompt ------------->| (prompt content)
|<~~~~~~ session/update (notification)| (streaming chunks, tool calls, plans)
|<~~~~~~ session/update |
|<--request_permission --------------| (tool approval)
|------- permission response ------->|
|<~~~~~~ session/update |
|<------ PromptResponse -------------| (stopReason)
| |
|------- session/prompt (next) ----->|
| ... |
2.4 Session Update Events (стриминг)
Во время prompt агент шлёт session/update notifications:
| Event | Описание |
|---|---|
agent_message_chunk |
Текстовый чанк от агента (streaming) |
agent_thought_chunk |
Мысли агента (thinking) |
user_message_chunk |
Эхо пользовательского ввода |
tool_call |
Новый вызов инструмента (pending/completed) |
tool_call_update |
Обновление статуса вызова инструмента |
plan |
План действий с приоритетами и статусами |
available_commands_update |
Обновление доступных команд |
config_option_update |
Изменение конфигурации |
current_mode_update |
Смена режима сессии |
session_info_update |
Метаданные сессии (title, activity) |
usage_update |
Потребление токенов (draft) |
2.5 Client-Provided Methods
Клиент (IDE) предоставляет агенту доступ к:
| Метод | Описание | Required? |
|---|---|---|
session/request_permission |
Запрос разрешения на выполнение инструмента | Required |
fs/read_text_file |
Чтение файла | Optional |
fs/write_text_file |
Запись файла | Optional |
terminal/create |
Создание терминала | Optional |
terminal/output |
Получение вывода терминала | Optional |
terminal/wait_for_exit |
Ожидание завершения | Optional |
terminal/kill |
Завершение процесса | Optional |
terminal/release |
Освобождение ресурсов | Optional |
2.6 MCP Integration
ACP переиспользует JSON-представления из MCP где возможно. Агент может принимать MCP сервера при создании сессии:
connection.newSession({
cwd: '/path/to/project',
mcpServers: [
{ type: 'stdio', command: 'node', args: ['mcp-server.js'] },
{ type: 'http', url: 'https://mcp.example.com', headers: {} },
{ type: 'sse', url: 'https://mcp.example.com/sse', headers: {} },
],
});
3. TypeScript SDK — API Surface
3.1 Package Info
| Характеристика | Значение |
|---|---|
| npm | @agentclientprotocol/sdk |
| Версия | 0.16.1 (март 2026) |
| Размер | 863 kB |
| Dependencies | 0 (zero dependencies!) |
| Лицензия | Apache-2.0 |
| Dependents | 245+ пакетов |
| GitHub stars | 122 |
| Contributors | 31 |
| Commits | 544 |
| Used by | 823+ проектов |
Факт: ранее публиковался как @zed-industries/agent-client-protocol, переименован.
3.2 Exported Classes (4)
import {
ClientSideConnection, // Для клиентов (IDE) — наш интерес
AgentSideConnection, // Для агентов (сервер)
TerminalHandle, // Управление терминалом
RequestError, // Типизированная ошибка
} from '@agentclientprotocol/sdk';
3.3 Exported Interfaces (2)
interface Client {
requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
sessionUpdate(params: SessionNotification): Promise<void>;
writeTextFile?(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
readTextFile?(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
// terminal methods...
}
interface Agent {
initialize(params: InitializeRequest): Promise<InitializeResponse>;
newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
authenticate?(params: AuthenticateRequest): Promise<AuthenticateResponse>;
prompt(params: PromptRequest): Promise<PromptResponse>;
cancel?(params: CancelNotification): Promise<void>;
setSessionMode?(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
// ...
}
3.4 Exported Functions (1) + Variables (3)
// Единственная утилитарная функция — создаёт NDJSON stream
function ndJsonStream(input: WritableStream, output: ReadableStream): Stream;
// Константы
const PROTOCOL_VERSION: string; // Текущая версия протокола
const AGENT_METHODS: string[]; // Список методов агента
const CLIENT_METHODS: string[]; // Список методов клиента
3.5 Type Aliases (~180+)
Полный список категорий типов:
- Content:
TextContent,ImageContent,AudioContent,Content,ContentBlock,ContentChunk - Authentication:
AuthMethod,AuthCapabilities,AuthenticateRequest/Response - Sessions:
SessionId,SessionInfo,SessionCapabilities,SessionUpdate,SessionMode - Tools:
ToolCall,ToolCallUpdate,ToolCallId,ToolCallStatus,ToolKind - Permissions:
RequestPermissionRequest/Response,PermissionOption,PermissionOptionKind - Plans:
Plan,PlanEntry,PlanEntryStatus,PlanEntryPriority - Diffs:
Diff(path,oldText,newText) - File System:
ReadTextFileRequest/Response,WriteTextFileRequest/Response - Terminals:
Terminal,CreateTerminalRequest/Response,TerminalExitStatus - MCP:
McpCapabilities,McpServer,McpServerStdio,McpServerHttp,McpServerSse - Protocol:
InitializeRequest/Response,PromptRequest/Response,StopReason,Cost,Usage - Elicitation (draft):
ElicitationRequest/Response,ElicitationSchema— формы ввода от агента - Config:
SessionConfigOption,SessionConfigBoolean,SessionConfigSelect - Models:
ModelId,ModelInfo
3.6 ClientSideConnection — Full API
class ClientSideConnection {
constructor(toClient: (agent: Agent) => Client, stream: Stream);
// Properties
signal: AbortSignal; // Aborts when connection closes
closed: Promise<void>; // Resolves when connection ends
// Core methods
initialize(params: InitializeRequest): Promise<InitializeResponse>;
newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
prompt(params: PromptRequest): Promise<PromptResponse>;
authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse>;
// Session management
loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>; // Resume previous
listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse>; // List available
setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse>;
// Unstable/experimental
unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
unstable_resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse>;
unstable_closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse>;
unstable_setSessionModel(params: SetSessionModelRequest): Promise<SetSessionModelResponse>;
unstable_logout(params: LogoutRequest): Promise<LogoutResponse>;
// Notifications
cancel(params: CancelNotification): Promise<void>; // Cancel ongoing prompt
// Extensibility
extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
extNotification(method: string, params: Record<string, unknown>): Promise<void>;
}
4. Какие агенты поддерживают ACP?
Подтверждённые (с доказательствами)
| Агент | Поддержка ACP | Как реализовано | Источник |
|---|---|---|---|
| Gemini CLI | Нативная (reference implementation) | Встроенный ACP-сервер | zed.dev/acp |
| Claude Code | Через адаптер | @zed-industries/claude-code-acp (npm, Apache 2.0) |
GitHub |
| Codex CLI | Через community adapter | Zed adapter | zed.dev/docs/ai/external-agents |
| GitHub Copilot CLI | Нативная (public preview) | copilot --acp / copilot --acp --port 8080 |
GitHub Blog |
| Goose (Block) | Нативная | Встроенный ACP-сервер | goose blog |
| Junie (JetBrains) | Нативная | Встроена в JetBrains AI Assistant | JetBrains |
| Cline | Нативная | Встроенный ACP-сервер | DeepWiki |
| Kiro CLI | Нативная | Встроенный ACP-сервер | Kiro docs |
| OpenCode | Нативная | Встроенный ACP-сервер | opencode.ai |
| Augment Code | Нативная | ACP Registry | Registry |
| Qwen Code | Нативная | ACP Registry | VS Code ACP Client |
Claude Code НЕ имеет нативного --acp флага (есть Feature Request #6686). Работает через @zed-industries/claude-code-acp адаптер, который использует Claude Agent SDK.
IDE/Клиенты с ACP поддержкой
| Клиент | Статус |
|---|---|
| Zed | Нативная (создатели протокола) |
| JetBrains (IntelliJ, PyCharm и др.) | Нативная (co-maintainer) |
| Neovim | Через плагины (CodeCompanion, avante.nvim) |
| Emacs | Community extensions |
| Marimo (Python notebooks) | Встроенная |
| VS Code | НЕТ (ключевой вопрос для экосистемы) |
| Cursor | НЕТ (может появиться если будет спрос) |
5. Конкретный пример кода (из SDK)
Client (IDE side)
import { spawn } from 'node:child_process';
import { Writable, Readable } from 'node:stream';
import * as acp from '@agentclientprotocol/sdk';
class MyClient implements acp.Client {
async requestPermission(params: acp.RequestPermissionRequest): Promise<acp.RequestPermissionResponse> {
// UI показывает dialog с params.options
return {
outcome: { outcome: 'selected', optionId: params.options[0].optionId },
};
}
async sessionUpdate(params: acp.SessionNotification): Promise<void> {
const update = params.update;
switch (update.sessionUpdate) {
case 'agent_message_chunk':
if (update.content.type === 'text') {
console.log(update.content.text); // Streaming text
}
break;
case 'tool_call':
console.log(`Tool: ${update.title} (${update.status})`);
break;
case 'tool_call_update':
console.log(`Tool ${update.toolCallId}: ${update.status}`);
break;
case 'plan':
// Plan entries with status/priority
break;
}
}
async readTextFile(params: acp.ReadTextFileRequest): Promise<acp.ReadTextFileResponse> {
const content = fs.readFileSync(params.path, 'utf-8');
return { content };
}
async writeTextFile(params: acp.WriteTextFileRequest): Promise<acp.WriteTextFileResponse> {
fs.writeFileSync(params.path, params.content);
return {};
}
}
async function main() {
// Spawn agent process
const agentProcess = spawn('claude', ['--acp'], {
stdio: ['pipe', 'pipe', 'inherit'],
});
// Create NDJSON stream over stdio
const input = Writable.toWeb(agentProcess.stdin!);
const output = Readable.toWeb(agentProcess.stdout!) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(input, output);
// Create connection
const client = new MyClient();
const connection = new acp.ClientSideConnection((_agent) => client, stream);
// Initialize
const initResult = await connection.initialize({
protocolVersion: acp.PROTOCOL_VERSION,
clientCapabilities: {
fs: { readTextFile: true, writeTextFile: true },
terminal: { create: true, output: true, kill: true },
},
});
// Create session
const session = await connection.newSession({
cwd: '/path/to/project',
mcpServers: [],
});
// Send prompt (blocks until agent completes turn)
const result = await connection.prompt({
sessionId: session.sessionId,
prompt: [{ type: 'text', text: 'Fix the bug in main.ts' }],
});
console.log(`Stop reason: ${result.stopReason}`); // 'end_turn' | 'cancelled' | ...
}
Process spawning
ACP SDK НЕ управляет spawn'ом процесса. Это ответственность клиента. SDK берёт на себя только протокол поверх уже готового stream (stdin/stdout).
// ACP SDK expects web streams:
const input = Writable.toWeb(childProcess.stdin!); // WritableStream
const output = Readable.toWeb(childProcess.stdout!); // ReadableStream<Uint8Array>
const stream = acp.ndJsonStream(input, output); // ACP Stream
6. ACP vs MCP — Различия
| Аспект | MCP (Model Context Protocol) | ACP (Agent Client Protocol) |
|---|---|---|
| Создатель | Anthropic | Zed Industries + JetBrains |
| Фокус | Инструменты/данные для модели | Коммуникация IDE ↔ агент |
| Аналогия | "Дать человеку лучшие инструменты" | "Собрать команду из людей" |
| Отношение | Что (доступ к данным/tools) | Где (где агент живёт в workflow) |
| Протокол | JSON-RPC 2.0 поверх stdio/SSE/HTTP | JSON-RPC 2.0 поверх NDJSON stdio |
| Типы контента | Tools, Resources, Prompts | Messages, Tool Calls, Plans, Diffs, Permissions |
| Стейт | Stateless на уровне протокола | Stateful (sessions, message history) |
| Sessions | Нет (транспортные сессии) | Да (conversation sessions с ID) |
| Streaming | Через SSE или notifications | session/update notifications |
Ключевое: ACP и MCP комплементарны. ACP-сессия может принимать MCP-серверы (mcpServers в newSession). Агент использует MCP для доступа к инструментам, а ACP для общения с IDE.
7. Зрелость и стабильность
Версионирование
SDK на v0.16.1 (март 2026) — ещё pre-1.0. Много unstable_ методов.
Timeline ключевых событий
| Дата | Событие |
|---|---|
| Сентябрь 2025 | Zed анонсирует ACP |
| Октябрь 2025 | JetBrains присоединяется |
| Октябрь 2025 | Gemini CLI — первая интеграция |
| Январь 2026 | Copilot CLI ACP public preview |
| Январь 2026 | ACP Registry запущен |
| Февраль 2026 | Session Config Options стабилизированы |
| Март 2026 | session/list + session_info_update стабилизированы |
| Март 2026 | SDK v0.16.1 |
Что в Draft (ещё не стабилизировано)
session/close— закрытие сессийsession/fork— форк сессийsession/resume— возобновление сессий- Elicitation — формы ввода от агента
- Usage updates — статистика токенов
- Message IDs — идентификаторы сообщений
- Delete in Diff — удаление файлов через diff
- Next Edit Suggestions — предложения следующих правок
Breaking Changes
Протокол на стадии 0.x — breaking changes возможны между минорными версиями. Rename пакета @zed-industries/agent-client-protocol → @agentclientprotocol/sdk уже произошёл.
8. Анализ интеграции в наш Electron app
8.1 Текущая архитектура (как мы работаем сейчас)
Наш стек коммуникации с Claude Code:
Electron Main Process
└── TeamProvisioningService
├── spawnCli() → ChildProcess (stream-json)
├── stdin.write(NDJSON) → Claude CLI
├── stdout → parse NDJSON lines
│ ├── type: "user" / "assistant" / "result" / "system"
│ ├── type: "control_request" (tool approval)
│ └── result.success → turn complete
└── stderr → logs, error detection
Ключевые аргументы CLI:
--input-format stream-json --output-format stream-json
Наша обработка:
HANDLED_STREAM_JSON_TYPES = ['user', 'assistant', 'control_request', 'result', 'system']stdin.write(message + '\n')— отправка- Ручной парсинг NDJSON с carry buffer для неполных строк
control_request→ UI dialog для tool approvalresult.success→ turn complete, process alive- SIGKILL для остановки (SIGTERM вызывает cleanup)
8.2 Что ACP заменил бы
| Компонент | Сейчас (stream-json) | С ACP |
|---|---|---|
| Spawn | spawnCli() |
Остаётся наш spawnCli() |
| Transport | Ручной NDJSON парсинг с carry buffer | acp.ndJsonStream() + ClientSideConnection |
| Initialize | Нет (просто шлём prompt) | connection.initialize() — capabilities negotiation |
| Session | Нет (implicit) | connection.newSession() — explicit session ID |
| Prompt | stdin.write(JSON.stringify({type:'user',...}) + '\n') |
connection.prompt({sessionId, prompt}) |
| Streaming | Ручной парсинг stdout строк | sessionUpdate() callback с typed events |
| Tool approval | control_request парсинг |
requestPermission() callback |
| File ops | Нет (агент делает сам) | readTextFile() / writeTextFile() callbacks |
| Terminal | Нет | terminal/* callbacks |
| Cancel | SIGKILL | connection.cancel() (graceful) |
8.3 Что ACP НЕ решает (нам всё ещё нужно)
-
Agent Teams orchestration — ACP это one-agent ↔ one-client. Оркестрация команд, TaskCreate, SendMessage, TeamCreate — всё это наш domain logic поверх CLI-specific протокола.
-
stream-json специфика Claude Code — Claude Code НЕ поддерживает
--acpнативно. Он использует--input-format stream-json --output-format stream-json. ACP требует адаптер (@zed-industries/claude-code-acp), который внутри использует Claude Agent SDK. -
Team file monitoring — Наш
TeamConfigReader,TeamTaskReader,TeamInboxReaderмониторят файлы на диске. ACP не имеет concept of teams/tasks. -
Cross-team communication — Наш
cross_team_send, inbox relay, sentinel messages — всё это специфика нашей архитектуры. -
Post-compact context recovery — Наши
pendingPostCompactReminderи context reinjection — domain-specific. -
Member spawn management — Трекинг
MemberSpawnStatus, reconnect, stall detection — наш код. -
MCP config building —
TeamMcpConfigBuilder— наш код для сборки MCP конфигов. -
Tool approval auto-resolve —
shouldAutoAllow()и custom rules — наша логика.
8.4 Гипотетическая интеграция (Pseudocode)
// === ВАРИАНТ A: ACP для нового multi-agent клиента ===
// Если бы Claude Code поддерживал --acp нативно
import * as acp from '@agentclientprotocol/sdk';
import { spawnCli } from '@main/utils/childProcess';
class TeamAgentClient implements acp.Client {
constructor(
private teamName: string,
private memberName: string,
private onUpdate: (event: SessionUpdate) => void,
private onPermission: (request: ToolApprovalRequest) => Promise<ToolApprovalResponse>,
) {}
async requestPermission(params: acp.RequestPermissionRequest) {
// Проксируем в наш UI через существующий tool approval flow
const approval = await this.onPermission(mapToOurFormat(params));
return mapToAcpResponse(approval);
}
async sessionUpdate(params: acp.SessionNotification) {
// Маппим ACP events в наши TeamChangeEvent'ы
const update = params.update;
switch (update.sessionUpdate) {
case 'agent_message_chunk':
this.onUpdate({ type: 'agent-text', text: update.content.text });
break;
case 'tool_call':
this.onUpdate({ type: 'tool-call', ...mapToolCall(update) });
break;
case 'plan':
this.onUpdate({ type: 'plan-update', entries: update.entries });
break;
}
}
}
async function spawnAgentWithAcp(claudePath: string, args: string[], cwd: string) {
// 1. Spawn process (наш существующий код)
const child = spawnCli(claudePath, ['--acp', ...args], { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
// 2. Create ACP connection (заменяет весь ручной NDJSON парсинг)
const input = Writable.toWeb(child.stdin!);
const output = Readable.toWeb(child.stdout!) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(input, output);
const client = new TeamAgentClient(teamName, memberName, onUpdate, onPermission);
const connection = new acp.ClientSideConnection((_agent) => client, stream);
// 3. Initialize
const initResult = await connection.initialize({
protocolVersion: acp.PROTOCOL_VERSION,
clientCapabilities: {
fs: { readTextFile: true, writeTextFile: true },
terminal: { create: true, output: true, kill: true },
},
});
// 4. Create session with MCP servers
const mcpConfigPath = await mcpBuilder.writeConfigFile();
const session = await connection.newSession({
cwd,
mcpServers: [
{ type: 'stdio', command: 'node', args: [mcpServerPath] },
],
});
// 5. Send prompt
const result = await connection.prompt({
sessionId: session.sessionId,
prompt: [{ type: 'text', text: provisioningPrompt }],
});
// 6. Graceful cancel instead of SIGKILL
await connection.cancel({ sessionId: session.sessionId });
return { connection, session, child };
}
// === ВАРИАНТ B: ACP как дополнительный протокол (реалистичный) ===
// Claude Code -> stream-json (как сейчас)
// Другие агенты (Gemini, Codex, Copilot) -> ACP
// Наше приложение поддерживает ОБА протокола
interface AgentConnection {
sendPrompt(text: string): Promise<void>;
onMessage(callback: (msg: AgentMessage) => void): void;
cancel(): Promise<void>;
close(): void;
}
class StreamJsonConnection implements AgentConnection {
// Существующий код из TeamProvisioningService
// stream-json протокол Claude Code
}
class AcpConnection implements AgentConnection {
private connection: acp.ClientSideConnection;
private sessionId: string;
constructor(connection: acp.ClientSideConnection, sessionId: string) {
this.connection = connection;
this.sessionId = sessionId;
}
async sendPrompt(text: string) {
await this.connection.prompt({
sessionId: this.sessionId,
prompt: [{ type: 'text', text }],
});
}
async cancel() {
await this.connection.cancel({ sessionId: this.sessionId });
}
}
function createAgentConnection(agent: AgentType, child: ChildProcess): AgentConnection {
if (agent === 'claude-code') {
return new StreamJsonConnection(child); // Как сейчас
}
// Gemini CLI, Codex CLI, Copilot CLI и др.
const stream = acp.ndJsonStream(
Writable.toWeb(child.stdin!),
Readable.toWeb(child.stdout!) as ReadableStream<Uint8Array>,
);
const conn = new acp.ClientSideConnection((_agent) => new AcpClient(), stream);
return new AcpConnection(conn, sessionId);
}
8.5 Ключевые технические проблемы интеграции
Проблема 1: Web Streams vs Node Streams
ACP SDK использует Web Streams API (ReadableStream, WritableStream). Node.js child_process возвращает Node Streams. Нужна конвертация:
const input = Writable.toWeb(child.stdin!); // node:stream → web stream
const output = Readable.toWeb(child.stdout!); // node:stream → web stream
В Electron 40.x (Node 22+) эти конвертации доступны нативно.
Проблема 2: Claude Code не поддерживает ACP
Claude Code использует stream-json, не ACP. Для ACP нужен @zed-industries/claude-code-acp адаптер (который в свою очередь использует Claude Agent SDK — отдельный npm пакет с Anthropic API key).
Наш текущий подход (прямой CLI) не требует API key — используется auth token пользователя. Адаптер claude-code-acp требует ANTHROPIC_API_KEY, что делает его непрактичным для нашего zero-config подхода.
Проблема 3: Blocking prompt()
connection.prompt() блокирует до завершения turn'а. Streaming идёт через callback'и (sessionUpdate). Это отличается от нашего подхода где мы парсим stdout строку за строкой.
Проблема 4: Team orchestration
ACP — это 1:1 (один клиент, один агент). У нас N агентов в команде. Каждый агент = отдельный ACP connection. Координация между ними — полностью наш код.
9. Что код мы СОХРАНЯЕМ vs что ACP заменяет
Сохраняем (наш domain logic):
| Файл/Модуль | Причина |
|---|---|
TeamProvisioningService.ts (80%) |
Team orchestration, member management, task tracking |
TeamConfigReader.ts |
File-based team config monitoring |
TeamTaskReader.ts |
File-based task monitoring |
TeamInboxReader.ts |
File-based inbox monitoring |
TeamMcpConfigBuilder.ts |
MCP config generation |
TeamMembersMetaStore.ts |
Member metadata |
TeamSentMessagesStore.ts |
Sent messages tracking |
ClaudeBinaryResolver.ts |
CLI binary resolution |
childProcess.ts |
Process spawning (spawnCli, killProcessTree) |
toolApprovalRules.ts |
Auto-approval logic |
actionModeInstructions.ts |
Agent instructions |
| Cross-team communication | Inbox relay, sentinel messages |
| Post-compact recovery | Context reinjection |
| Stall detection | Watchdog timers |
| Auth retry | Re-spawn on auth failure |
ACP заменяет (если бы Claude Code поддерживал):
| Компонент | Строки кода | Чем заменяет |
|---|---|---|
| NDJSON парсинг stdout | ~200 LOC | ndJsonStream() + ClientSideConnection |
| Carry buffer логика | ~50 LOC | Автоматически в SDK |
| Message type dispatching | ~150 LOC | Typed sessionUpdate() callback |
| Tool approval protocol | ~100 LOC | requestPermission() callback |
| Session init handshake | ~30 LOC | initialize() + newSession() |
| Итого | ~530 LOC | Типизированный SDK |
Из ~6000 LOC TeamProvisioningService, ACP заменяет ~530 LOC (менее 9%). Остальные 91% — domain-specific orchestration.
10. Честные оценки
Сложность интеграции: 6/10 (Уверенность: 8/10)
- SDK сам по себе простой (0 dependencies, чистый API)
- Проблема: Claude Code не поддерживает ACP нативно
- Нужен маппинг между ACP events и нашими internal types
- Web Streams конвертация в Electron — тривиальна
- Основная сложность: поддержка двух протоколов (stream-json + ACP)
Полезность для нашего кейса: 4/10 (Уверенность: 9/10)
- Наш primary agent (Claude Code) НЕ поддерживает ACP
- 91% нашего кода — domain-specific, ACP не касается
- Выгода: если хотим поддержать ДРУГИЕ агенты (Gemini, Codex, Copilot) — тогда ACP становится очень полезным (8/10)
- Для Claude Code only — бессмысленно, мы уже общаемся напрямую через stream-json
Зрелость/стабильность: 5/10 (Уверенность: 7/10)
- Pre-1.0 (v0.16.1)
- Много
unstable_методов - Breaking changes между минорами возможны
- НО: 31 контрибьютор, 544 коммита, JetBrains + Zed backing
- Активная разработка, быстрый темп (18 npm versions)
- Usage updates и session management — ещё в draft
Риск adoption: 5/10 (Уверенность: 7/10)
- Zero dependencies — безопасно для bundle size
- Pre-1.0 → API может измениться
- Claude Code может получить нативную ACP поддержку в будущем (Feature Request существует)
- VS Code не поддерживает ACP — это риск для всей экосистемы
- JetBrains backing — сильный сигнал стабильности
11. Рекомендация
WAIT — Не интегрировать сейчас. Наблюдать.
Надёжность решения: 8/10. Уверенность в рекомендации: 9/10.
Почему WAIT, а не ADOPT:
-
Claude Code — наш primary agent и он НЕ говорит по ACP. Пока Anthropic не добавит
--acpфлаг (или не поменяетstream-jsonна ACP), интеграция ACP не даёт value для Claude Code. -
Мы заменим менее 9% кода. ROI не оправдывает migration effort + поддержку двух протоколов.
-
Pre-1.0 API. Breaking changes реальны. Лучше подождать стабилизации.
Когда стоит ADOPT:
-
Claude Code получит нативную ACP поддержку — тогда можно мигрировать stream-json → ACP, упростив парсинг.
-
Мы решим поддержать multi-agent (Gemini + Codex + Claude) — тогда ACP станет единым протоколом для не-Claude агентов. Архитектура: stream-json для Claude, ACP для остальных, общий
AgentConnectionинтерфейс. -
ACP достигнет 1.0 — стабильный API, можно инвестировать в интеграцию.
Что делать прямо сейчас:
- Следить за Feature Request #6686 (Claude Code ACP support)
- Следить за ACP Updates (protocol evolution)
- Проектировать
AgentConnectionabstraction в нашем коде, чтобы stream-json и ACP могли быть взаимозаменяемы в будущем - Если решим поддержать Gemini/Codex — начать с ACP как протокола для них
Приложение A: Полная архитектура ACP Protocol Schema
Error Codes (JSON-RPC)
| Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid request |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
| -32000 | Authentication required |
| -32002 | Resource not found |
Permission Option Kinds
| Kind | Описание |
|---|---|
allow_once |
Разрешить один раз |
allow_always |
Разрешить всегда |
reject_once |
Отклонить один раз |
reject_always |
Отклонить всегда |
Stop Reasons
| Reason | Описание |
|---|---|
end_turn |
Агент завершил turn нормально |
cancelled |
Пользователь отменил |
max_tokens |
Достигнут лимит токенов |
tool_use |
Агент ожидает результат tool (редко в ACP) |
Tool Call Kinds
| Kind | Описание |
|---|---|
read |
Чтение (файл, поиск) |
edit |
Редактирование файла |
command |
Выполнение команды |
tool |
Вызов MCP tool |
Diff Format
{
"path": "/absolute/path/to/file.ts",
"oldText": "original content (null for new files)",
"newText": "modified content"
}