@swarmai/acp
v0.1.1
Published
Agent Communication Protocol server — JSON-RPC surface for editor extensions (VS Code / Zed / JetBrains / Cursor) to drive SwarmAI from inside the editor
Downloads
34
Readme
@swarmai/acp
Agent Communication Protocol server for SwarmAI — the JSON-RPC surface that editor extensions (VS Code, Zed, JetBrains, Cursor) connect to so the agent can read open buffers, propose edits, fetch diagnostics, and drive the integrated terminal.
This plugin ships the server side only. Editor extensions live in their own repos and are not part of this package as of v0.1.0.
What this is
ACP is the wire protocol that lets an editor act as an agent surface equal to the gateway dashboard. Once an editor is paired:
- The editor pushes
workspace/didOpenandworkspace/fileChangednotifications to the gateway. - The gateway pulls
buffer/get,diagnostics/get,selection/getfrom the editor. - The gateway proposes edits via
buffer/proposeEdit; the editor shows the diff inline and the user accepts/rejects. - The gateway can drive the integrated terminal (
terminal/run) and invoke editor commands (command/run).
See docs/15-device-and-developer-integration.md §2 in the monorepo for the full design.
Install
- Drop
swarmai-acp/under your$SWARMAI_PLUGINS_DIRroot (defaultF:\Published\Pluggins\on Windows when the operator has set it). - Add
'@swarmai/acp'to yourplugins.yamlso the loader imports it at boot. - Restart the gateway. You should see
[@swarmai/acp] ACP plugin loaded — host must explicitly wire AcpServer via the library exports.in the boot log.
Status: v0.1.0 server only
Editor extensions are not published yet. Until they ship, @swarmai/acp is useful for:
- Hand-rolled clients that speak the JSON-RPC verbs directly (e.g. a Neovim integration).
- Custom IDE plugins built against the typed schemas.
- Sidecar editor processes that the gateway spawns and talks to over stdio (see
StdioTransport).
The register() entry is a no-op log line because the host integration shape for "long-lived JSON-RPC server with WS upgrade" hasn't stabilised across SDK versions. Host code wires the server explicitly via the library exports:
Wire-up sketch
import { AcpServer, AcpPairingRegistry } from '@swarmai/acp/lib.js';
import type { AcpSession } from '@swarmai/acp/lib.js';
const pairing = new AcpPairingRegistry();
const acp = new AcpServer({
pairing,
onVerb: async (session: AcpSession, verb: string, params: unknown) => {
// Route inbound editor verbs into the agent session.
// E.g. workspace/didOpen → record the workspace; workspace/fileChanged → invalidate caches.
return await routeIntoSession(session, verb, params);
},
});
// In the gateway's HTTP upgrade handler:
httpServer.on('upgrade', (req, socket, head) => {
if (req.url === '/ws/acp') {
const pairToken = extractBearer(req);
wss.handleUpgrade(req, socket, head, (ws) => {
const transport = wrapWebSocketAsTransport(ws);
acp.attach(transport, { pairToken });
});
}
});
// Mint a pair code from a CLI subcommand or the dashboard:
const { code, expiresAt } = pairing.mint({
workspaceId: ctx.workspaceId,
editor: 'vscode',
createdBy: ctx.userId,
});
console.log(`Editor pair code: ${code} (valid for 5 min)`);JSON-RPC verbs
Wire-protocol shape is JSON-RPC 2.0. Verbs split by direction:
| Direction | Verb | Purpose |
|---|---|---|
| Editor → Gateway | workspace/didOpen | Editor signals a workspace just opened |
| Editor → Gateway | workspace/fileChanged | A file was saved or modified |
| Gateway → Editor | buffer/get | Read the current buffer content of a URI |
| Gateway → Editor | buffer/proposeEdit | Propose a diff; editor shows it inline for accept/reject |
| Gateway → Editor | diagnostics/get | Fetch lint / type errors |
| Gateway → Editor | selection/get | Read the current selection |
| Gateway → Editor | command/run | Invoke an editor command (run tests, format, …) |
| Gateway → Editor | terminal/run | Run a command in the integrated terminal |
Each verb's params and result are Zod schemas exported from '@swarmai/acp/lib.js' — WorkspaceDidOpenParams, BufferGetParams, BufferGetResult, etc.
Pairing flow
- 6-character alphanumeric codes drawn from an unambiguous alphabet (excludes
O,0,I,1,L). - 5-minute TTL, single-use.
- Constant-time compare on redeem to defeat timing attacks.
- Per-mint metadata: workspace id, editor kind, who created the code.
const { code } = pairing.mint({ workspaceId: 'main', editor: 'vscode', createdBy: 'op-1' });
// Operator enters `code` in their editor extension.
const result = pairing.redeem(code);
if (result) {
const { workspaceId, editor } = result;
// Proceed to attach the transport.
}Writing a hand-rolled client
If you want to write a custom client (e.g. for Neovim or a hand-rolled VS Code extension), import the JSON-RPC primitives:
import {
parseFrame, encodeFrame, createRequest, createIdGenerator,
WorkspaceDidOpenParams,
} from '@swarmai/acp/lib.js';
const nextId = createIdGenerator();
const frame = encodeFrame(
createRequest(nextId(), 'workspace/didOpen', WorkspaceDidOpenParams.parse({
workspaceId: 'main',
rootUri: 'file:///path/to/project',
editor: { kind: 'vscode', version: '1.85.0' },
})),
);
ws.send(frame);parseFrame validates inbound JSON against the JSON-RPC 2.0 envelope; the verb schemas validate the params.
Transports
StdioTransport (newline-delimited JSON over stdio) is provided for sidecar editor processes. WebSocket is the recommended transport for browser-hosted editors — the gateway wraps the WS in a Transport adapter during the upgrade handshake (see the wire-up sketch above).
createMemoryTransportPair() is exported for tests — gives you both ends of an in-memory transport pair.
Security
- Pairing codes are single-use and expire in 5 minutes — replay-safe.
- No persistent secrets — pair codes are mint-and-burn.
- The plugin itself does NOT bind a port — the host controls where the JSON-RPC server lives (typically
/ws/acpon the gateway's existing HTTP server). All of the gateway's existing TLS / reverse-proxy / origin-gate posture applies. buffer/proposeEditis never auto-applied — the editor shows the diff inline and the user accepts or rejects. This is enforced editor-side; the protocol just describes the proposal.
License
PolyForm-Noncommercial-1.0.0 — see LICENSE.
