Skip to content

Add ACP (Agent Client Protocol) support#379

Open
yazelin wants to merge 8 commits intogithub:mainfrom
yazelin:feature/acp-protocol-support
Open

Add ACP (Agent Client Protocol) support#379
yazelin wants to merge 8 commits intogithub:mainfrom
yazelin:feature/acp-protocol-support

Conversation

@yazelin
Copy link

@yazelin yazelin commented Feb 5, 2026

Summary

Adds native ACP (Agent Client Protocol) support to allow the Copilot SDK to connect directly to ACP-compatible agents (e.g., Gemini CLI with --experimental-acp) without requiring a protocol translation proxy.

Closes #377

Changes

  • Add protocol option to CopilotClientOptions ("copilot" | "acp")
  • Implement protocol adapter abstraction layer
  • Add NDJSON transport for ACP wire format
  • Translate Copilot SDK methods to ACP:
    • pinginitialize
    • session.createsession/new
    • session.sendsession/prompt
  • Map ACP session/update notifications to SessionEvent format
  • Handle Gemini's stopReason response for session.idle event

Usage

import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient({
  cliPath: "gemini",
  cliArgs: ["--experimental-acp"],
  protocol: "acp"
});

await client.start();
const session = await client.createSession();

session.on((event) => {
  if (event.type === "assistant.message_delta") {
    process.stdout.write(event.data.deltaContent);
  }
});

await session.send({ prompt: "Hello!" });
await client.stop();

Test Plan

  • Unit tests for ACP transport (17 tests)
  • Unit tests for ACP mapper (15 tests)
  • Unit tests for ACP adapter (11 tests)
  • E2E tests with Gemini CLI (12 tests, 10 skip without CLI)
  • Manual testing with real Gemini CLI

Files Changed

src/
  client.ts                      # Modified - protocol adapter integration
  types.ts                       # Modified - added protocol option
  protocols/
    protocol-adapter.ts          # New - adapter interface
    acp/
      acp-adapter.ts             # New - ACP protocol adapter
      acp-transport.ts           # New - NDJSON transport
      acp-types.ts               # New - ACP message types
      acp-mapper.ts              # New - format translation

@yazelin
Copy link
Author

yazelin commented Feb 5, 2026

Update: Tool Calls & Permission Request Support Added

This PR now includes complete ACP support with:

New Features

  • Tool Calls: Map ACP tool_calltool.execution_start, tool_call_updatetool.execution_complete/progress
  • Permission Requests: Handle ACP session/request_permission server-to-client requests

Test Results (All Passing)

  • Unit tests: 53 tests (17 transport + 21 mapper + 15 adapter)
  • E2E tests with Gemini CLI: 14 tests
    • ✅ Connection and ping
    • ✅ Session creation and messaging
    • ✅ Streaming via assistant.message_delta
    • ✅ Tool events (tool.execution_start, tool.execution_complete)
    • ✅ Error handling for unsupported methods

E2E Tool Call Test Output

Tool started: search list_directory-xxx
Tool completed: list_directory-xxx success
Received 2 tool events:
  - tool.execution_start: {"toolCallId":"read_file-xxx","toolName":"read"}
  - tool.execution_complete: {"toolCallId":"read_file-xxx","success":true}

Ready for review! 🚀

yazelin added 4 commits February 5, 2026 13:46
Enable direct connection to ACP-compatible agents (e.g., Gemini CLI)
without requiring a protocol translation proxy.

- Add protocol adapter abstraction layer
- Implement NDJSON transport for ACP wire format
- Translate Copilot SDK methods to ACP: ping→initialize,
  session.create→session/new, session.send→session/prompt
- Map ACP session/update notifications to SessionEvent format
- Add 'protocol' option to CopilotClientOptions ("copilot" | "acp")
- Include comprehensive unit tests and e2e tests

Usage:
```typescript
const client = new CopilotClient({
  cliPath: "gemini",
  cliArgs: ["--experimental-acp"],
  protocol: "acp"
});
```
Gemini returns stopReason in the session/prompt response instead of
sending a separate end_turn notification. Emit session.idle event
when stopReason is "end_turn" to properly signal turn completion.

Changes:
- Add stopReason to AcpSessionPromptResult type definition
- Emit session.idle via queueMicrotask when stopReason is "end_turn"
- Add unit test for stopReason handling
Implement support for ACP tool calls and permission requests:

- Add types for tool_call, tool_call_update, and permission requests
- Map ACP tool_call to tool.execution_start event
- Map ACP tool_call_update to tool.execution_progress/complete events
- Handle session/request_permission requests from ACP server
- Add sendResponse and onRequest to AcpTransport for server requests
- Remove tool.call and permission.request from unsupported methods

Tests: 53 passing (21 mapper + 17 transport + 15 adapter)
- Add test for receiving tool events when agent uses tools
- Add test for tool.execution_start and tool.execution_complete events
- Tests prompt Gemini to read files/list directory to trigger tool usage
- Tests are skipped when ACP_CLI_PATH is not set
@yazelin yazelin force-pushed the feature/acp-protocol-support branch from cddfeed to 9a225bf Compare February 5, 2026 05:46
yazelin added 4 commits February 5, 2026 15:28
The Node.js process would hang after calling client.stop() because
the child process streams (stdin, stdout, stderr) were not properly
cleaned up.

Changes:
- Store bound event handlers for proper removal on cleanup
- Remove all event listeners from streams before closing
- Call destroy() on stdin/stdout/stderr to properly close pipes
- Clean up both in stop() and forceStop() methods

This ensures the Node.js event loop can exit normally without
requiring process.exit() in user code.
JSON-RPC errors may include a 'data' field with additional details.
Now the error message includes this data, e.g.:
  Before: 'Internal error'
  After:  'Internal error: {"details":"Requested entity was not found."}'
Registers handlers for exit, SIGINT, and SIGTERM to ensure
the gemini child process is killed when Node.js exits unexpectedly.
Gemini expects env as array of "KEY=value" strings, not object.
Also ensure args and env are always present (empty array if not set).
@yazelin yazelin force-pushed the feature/acp-protocol-support branch from d3ed889 to 76d68a2 Compare February 5, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support ACP (Agent Client Protocol) Compatible CLIs

1 participant