@acp-kit/core
v0.10.1
Published
Agent Client Protocol framework and runtime for launching ACP agents, managing auth and permissions, normalizing session events, and building agent clients on top of @agentclientprotocol/sdk.
Downloads
3,803
Maintainers
Keywords
Readme
ACP Kit
ACP Kit is an Agent Client Protocol framework and runtime for building applications on top of ACP.
It launches an ACP agent process, manages the protocol connection, handles authentication, exposes host adapters for permissions / files / terminals, and turns raw session/update traffic into normalized turn, message, reasoning, and tool events. Use it when you need an agent client protocol framework for products such as editor extensions, desktop shells, web daemons, and CLIs. Your application chooses an agent profile, attaches a host, opens a session, and consumes stable events.
Why ACP Kit:
- Stable events over messy
session/update. Per-message, per-tool, per-turn events with correlation ids (messageId,toolCallId,turnId) — drive UI state and transcripts without parsing raw protocol traffic. - Lifecycle is handled for you. Cross-platform process spawn, startup diagnostics,
auth_requiredretry,session.errorsurfacing, vendor_metapass-through, multiple sessions over one agent process. - Enterprise runtime hooks. Structured observations, startup diagnostics, runtime inspectors, session recordings, approval queues, and replay helpers let products audit and debug what an agent did.
- Six common agents, one import.
import { ClaudeCode, GitHubCopilot, CodexCli, GeminiCli, QwenCode, OpenCode } from '@acp-kit/core'— or drive any other ACP-capable agent via a customAgentProfile.
Install
npm install @acp-kit/coreRequires Node.js >= 20.11 and a reachable ACP agent CLI. Built-in profiles prefer local binaries on PATH and fall back to non-interactive npx --yes <package>@latest launches when needed.
Quick Start
import { createAcpRuntime, ClaudeCode } from '@acp-kit/core';
await using acp = createAcpRuntime({
agent: ClaudeCode,
});
await using session = await acp.newSession({ cwd: process.cwd() });
session.on({
messageDelta: (e) => process.stdout.write(e.delta),
toolStart: (e) => process.stdout.write(`\n[tool ${e.toolCallId}] ${e.title ?? e.name}\n`),
toolEnd: (e) => process.stdout.write(`[tool ${e.toolCallId}] ${e.status}\n`),
turnCompleted: (e) => process.stdout.write(`\n(turn ${e.turnId} done: ${e.stopReason})\n`),
});
await session.prompt('Summarize this repository.');If your application wants a single turn object while preserving live streaming updates, use collectTurnResult(...):
import { collectTurnResult } from '@acp-kit/core';
const result = await collectTurnResult(session, 'Summarize this repository.', {
onUpdate: (snapshot) => render(snapshot.text, snapshot.tools),
});
console.log(result.text);createAcpRuntime(...) defaults to approving tool permissions once and selecting the first offered auth method. See Getting Started for explicit host policy, the full event vocabulary, multi-session use, and how to debug startup / auth failures.
Long-running applications can reuse the same lower-level helpers Spar uses: openOrCreateRuntimeSession(...) resumes saved ACP sessions without hiding transport failures, withRealWorkspaceDefaults(...) / enforceRealWorkspaceSession(...) apply explicit real-workspace policies for supported agents, and createSessionTurnManager(...) rotates sessions after a configured turn budget.
For a one-shot prompt, the helper API handles session disposal and yields the same event stream. Use onRuntimeEvent(...) only when you already have an event value, such as inside this for await loop; when you have a RuntimeSession, prefer session.on(...).
import { runOneShotPrompt, onRuntimeEvent, ClaudeCode } from '@acp-kit/core';
for await (const event of runOneShotPrompt({
agent: ClaudeCode,
cwd: process.cwd(),
prompt: 'Summarize this repository.',
})) {
onRuntimeEvent(event, {
messageDelta: (e) => process.stdout.write(e.delta),
});
}Examples
The repository ships with five runnable examples under examples/. Each one is a standalone npm package that installs the published @acp-kit/core from npm:
| Example | What it shows |
| --- | --- |
| quick-start/ | Minimal single-prompt script. |
| Spar | Flagship CLI built on @acp-kit/core: AUTHOR writes files, REVIEWER inspects them in a separate context, and the loop continues until APPROVED. |
| mock-runtime/ | Self-contained mock ACP server. Use this to see the full event flow without installing an agent. |
| real-agent-cli/ | Interactive CLI driver for real agents (copilot, claude, codex, gemini, qwen, opencode) with prompts for auth and permission decisions. |
| web-daemon/ | Tiny node:http + Server-Sent Events demo: POST a prompt, stream normalized events back to a browser. |
cd examples/mock-runtime
npm install
npm startSee examples/README.md for details.
What ACP Kit Does
flowchart TB
P["Your Product<br/>(editor extension · desktop shell · web daemon · CLI)"]
K["<b>ACP Kit</b><br/>process spawn · auth · session lifecycle<br/>event normalization · host adapters"]
S["@agentclientprotocol/sdk<br/>JSON-RPC framing · typed payloads"]
A["ACP Agent CLI<br/>(Claude · Copilot · Codex · Gemini · Qwen · OpenCode)"]
P -- normalized events --> K
K -- uses --> S
S -- stdio --> AWithout ACP Kit, every product that wants to host an ACP agent has to write the same plumbing: pick the right CLI for the platform, spawn it without breaking on Windows .cmd shims or login envs, surface stderr when it fails to start, run initialize, retry session/new after auth_required, expose host capabilities only when the application actually backs them, parse session/update notifications into something a UI can render, and decide when a turn is really done.
ACP Kit packages all of that behind createAcpRuntime({...}).newSession({ cwd }) (or the runOneShotPrompt one-shot helper). The agent stays a regular ACP server; your application stays a regular consumer of typed events. See Architecture for the layer breakdown.
API Overview
RuntimeSession emits normalized RuntimeSessionEvents: stable per-message, per-tool, and per-turn events with correlation ids (messageId, toolCallId, turnId). They drive transcripts, UI state, and multi-agent orchestration. If you need raw protocol traffic (debuggers, protocol bridges), composeWireMiddleware / normalizeWireMiddleware let you observe the exact JSON-RPC frames.
Create the runtime with an agent profile. Add a host only for the capabilities and policy hooks your application backs:
import {
createAcpRuntime,
ClaudeCode,
PermissionDecision,
type RuntimeHost,
type AgentProfile,
} from '@acp-kit/core';
await using acp = createAcpRuntime({
agent: ClaudeCode, // built-in constant, or a custom AgentProfile literal
host: {
requestPermission: async (req) => PermissionDecision.AllowOnce,
chooseAuthMethod: async ({ methods }) => methods[0]?.id ?? null,
// readTextFile / writeTextFile / createTerminal+friends are advertised to
// the agent only when you provide them. See docs/api-overview.md.
} satisfies RuntimeHost,
});Diagnostics and replay are first-class runtime features. Use an inspector while developing, and attach a recorder when you need a durable audit/debug artifact:
import {
createAcpRuntime,
createRuntimeInspector,
isAcpStartupError,
formatStartupDiagnostics,
ClaudeCode,
} from '@acp-kit/core';
import { createFileSessionRecorder } from '@acp-kit/core/node';
const inspector = createRuntimeInspector({ includeWire: true });
const recording = createFileSessionRecorder({ dir: '.acp/recordings' });
try {
await using acp = createAcpRuntime({ agent: ClaudeCode, inspector, recording });
await acp.ready();
} catch (error) {
if (isAcpStartupError(error)) {
console.error(formatStartupDiagnostics(error.diagnostics));
}
throw error;
}Then open a session, subscribe to events, send prompts:
await using session = await acp.newSession({ cwd: '/path/to/workspace' });
session.on({
messageDelta: (e) => process.stdout.write(e.delta),
toolStart: (e) => process.stdout.write(`[tool ${e.toolCallId}] ${e.title ?? e.name}\n`),
toolEnd: (e) => process.stdout.write(`[tool ${e.toolCallId}] ${e.status}\n`),
turnCompleted: (e) => process.stdout.write(`done: ${e.stopReason}\n`),
});
const result = await session.prompt('Refactor utils.ts'); // Promise<PromptResult>
await session.cancel(); // optional: cancel the in-flight turn
// session and runtime are disposed automatically by `await using`Lifecycle helpers: acp.shutdown() (explicit teardown), acp.reconnect() (drop the agent process and reconnect), session.setMode(modeId) / session.setModel(modelId) (switch mid-session when the agent advertises options). One-shot helper: runOneShotPrompt({ agent, cwd, prompt }) yields RuntimeSessionEvents and disposes everything when iteration completes.
The full surface is exported from a single entry point: @acp-kit/core. See API Overview for the complete RuntimeHost, AcpRuntime, RuntimeSession, and RuntimeSessionEvent reference.
Supported ACP Agents
ACP Kit can drive any agent that speaks the Agent Client Protocol over stdio. Six agents ship as named constants you import and pass as agent: <Constant>:
| Agent | Constant |
| --- | --- |
| Claude Code | ClaudeCode |
| GitHub Copilot | GitHubCopilot |
| Codex CLI | CodexCli |
| Gemini CLI | GeminiCli |
| Qwen Code | QwenCode |
| OpenCode | OpenCode |
The runtime treats every agent uniformly: features like
session/load,setMode,setModel,session/list, file system, and terminal capabilities are forwarded to whichever agent advertises them in itsinitializeresponse. Inspectacp.agentCapabilitiesafter the runtime is ready to see exactly what a given agent CLI version supports.
To override one field on a built-in profile (e.g. agent: { ...ClaudeCode, env: { ANTHROPIC_API_KEY } }) or to drive a brand-new agent via a custom AgentProfile literal, see Supported Agents for per-agent details (command/args, login flow, known quirks).
How It Compares to @agentclientprotocol/sdk
ACP Kit is built on top of @agentclientprotocol/sdk, not as a replacement.
@agentclientprotocol/sdkis the protocol toolkit. It gives youClientSideConnection,ndJsonStream, typed request/response/notification payloads, and JSON-RPC framing — once you already have a connection to an ACP server.- ACP Kit is the client runtime. It launches the agent, manages the connection lifecycle, runs auth, exposes host adapters, normalizes raw protocol updates into stable events, and tracks turn state.
The protocol layer underneath stays exactly @agentclientprotocol/sdk. ACP Kit does not fork it, replace it, or hide it — it depends on it as a regular npm dependency. For the full layered diagram and side-by-side code comparison, see SDK vs Runtime.
Compatibility
| Dependency | Version |
| --- | --- |
| @agentclientprotocol/sdk | ^0.18 |
| Node.js | >= 20.11 (matches the package's engines field; CI tests Node 20 and 22) |
| TypeScript (consumers) | >= 5.2 (for using / await using syntax) |
| OS | Windows, macOS, Linux |
ACP Kit aims to track the latest stable @agentclientprotocol/sdk minor release. Breaking changes in the SDK will be matched by a minor or major bump in @acp-kit/core while v0.x is in effect.
Status
ACP Kit is experimental (v0.x). The public API may change between minor versions until v1.0; every breaking change is called out in CHANGELOG.md. See docs/migration-plan.md for incremental adoption.
Documentation
- Getting Started — install, first session, common failures
- API Overview — complete
RuntimeHost,AcpRuntime,RuntimeSession, and event reference - Supported Agents — per-agent command, login, known quirks
- SDK vs Runtime — the boundary between the official SDK and ACP Kit
- Architecture — runtime layers and design principles
- Package Plan — why ACP Kit ships as a single package today
- Migration Plan — incremental adoption path for existing ACP products
Development
npm install # install workspace deps (packages/core only)
npm run build # tsc -b packages/core
npm test # vitest runTo try an example: cd examples/mock-runtime && npm install && npm start.
Contributions are welcome. Please open an issue to discuss non-trivial changes before sending a PR.
