agent-ecosystem/docs/research/acp-deep-dive.md
iliya 71db7f153b feat(research): add comprehensive documentation on AI agent protocols and orchestration tools
- 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.
2026-03-25 14:47:52 +02:00

39 KiB
Raw Blame History

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)

Источники:

ВАЖНО: Существует ТРИ разных протокола с аббревиатурой ACP:

  1. Agent Client Protocol (Zed/JetBrains) — редактор ↔ агент. Это наш фокус.
  2. Agent Communication Protocol (IBM BeeAI) — агент ↔ агент. Сливается с A2A (Linux Foundation). Не релевантно.
  3. 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 approval
  • result.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 НЕ решает (нам всё ещё нужно)

  1. Agent Teams orchestration — ACP это one-agent ↔ one-client. Оркестрация команд, TaskCreate, SendMessage, TeamCreate — всё это наш domain logic поверх CLI-specific протокола.

  2. stream-json специфика Claude Code — Claude Code НЕ поддерживает --acp нативно. Он использует --input-format stream-json --output-format stream-json. ACP требует адаптер (@zed-industries/claude-code-acp), который внутри использует Claude Agent SDK.

  3. Team file monitoring — Наш TeamConfigReader, TeamTaskReader, TeamInboxReader мониторят файлы на диске. ACP не имеет concept of teams/tasks.

  4. Cross-team communication — Наш cross_team_send, inbox relay, sentinel messages — всё это специфика нашей архитектуры.

  5. Post-compact context recovery — Наши pendingPostCompactReminder и context reinjection — domain-specific.

  6. Member spawn management — Трекинг MemberSpawnStatus, reconnect, stall detection — наш код.

  7. MCP config buildingTeamMcpConfigBuilder — наш код для сборки MCP конфигов.

  8. Tool approval auto-resolveshouldAutoAllow() и 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:

  1. Claude Code — наш primary agent и он НЕ говорит по ACP. Пока Anthropic не добавит --acp флаг (или не поменяет stream-json на ACP), интеграция ACP не даёт value для Claude Code.

  2. Мы заменим менее 9% кода. ROI не оправдывает migration effort + поддержку двух протоколов.

  3. Pre-1.0 API. Breaking changes реальны. Лучше подождать стабилизации.

Когда стоит ADOPT:

  1. Claude Code получит нативную ACP поддержку — тогда можно мигрировать stream-json → ACP, упростив парсинг.

  2. Мы решим поддержать multi-agent (Gemini + Codex + Claude) — тогда ACP станет единым протоколом для не-Claude агентов. Архитектура: stream-json для Claude, ACP для остальных, общий AgentConnection интерфейс.

  3. ACP достигнет 1.0 — стабильный API, можно инвестировать в интеграцию.

Что делать прямо сейчас:

  1. Следить за Feature Request #6686 (Claude Code ACP support)
  2. Следить за ACP Updates (protocol evolution)
  3. Проектировать AgentConnection abstraction в нашем коде, чтобы stream-json и ACP могли быть взаимозаменяемы в будущем
  4. Если решим поддержать 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"
}

Приложение B: Ссылки

Спецификация и документация

SDK

Claude Code ACP

Ecosystem