Files
fusion/docs/acp-contract.md
gsxdsm 489a287d6f feat(acp): bundle into CLI, on-demand install, S1 safety + evidence (U8)
Wires the ACP runtime plugin into the published CLI (RUNTIME_PLUGIN_IDS in
tsup.config) and the on-demand BUILTIN_PLUGINS catalog (experimental), matching
the untrusted-subprocess security posture. Adds the Risk S1 default-policy
safety: an acpAllowUnrestricted acknowledgement (default false) — without it, a
blanket allow on a sensitive category is escalated to approval rather than
auto-approved under the allow-all default policy, applied in both the permission
floor and fs write gating. Adds docs/acp-contract.md (launch/readiness +
failure taxonomy), a README with the AGENTS.md-required upstream evidence
(SDK repo/docs/release/integrity), a bundle-output test for the staged plugin,
and a @runfusion/fusion minor changeset. Package green at 179 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:25:56 -07:00

3.1 KiB

ACP (Agent Client Protocol) Runtime Contract

Date: 2026-06-03

Launch/readiness contract and failure taxonomy for fusion-plugin-acp-runtime, which drives any external Agent Client Protocol agent over JSON-RPC/stdio. Mirrors the shape of docs/cursor-cli-contract.md.

Transport

  • Newline-delimited JSON-RPC 2.0 over stdio (no Content-Length framing). Provided by @agentclientprotocol/sdk (ndJsonStream + ClientSideConnection).
  • The client (Fusion) launches the agent as a subprocess with piped stdio. The agent's stdin is the JSON-RPC output stream; its stdout is the input stream.
  • stderr is captured (redacted) for diagnostics, never parsed as protocol.

Invocation and binary detection

  • Unlike a single-vendor CLI, ACP is a protocol — the agent binary + ACP-mode flag are user-configured:
    • acpBinaryPath — e.g. gemini, npx, or an absolute path.
    • acpArgs — the flag(s) that put the agent in ACP/stdio mode, e.g. ["--acp"].
  • The subprocess environment is built from the acpEnvAllowList allow-list only (inherited process.env is not forwarded — the agent is untrusted).

Readiness = the initialize handshake

There is no --version probe. Readiness is the protocol handshake itself:

  1. Spawn the agent subprocess.
  2. Send initialize { protocolVersion: 1, clientCapabilities: { fs } } under a timeout (default 30s — research flagged Gemini-on-macOS OAuth and Claude-adapter session/new stalls).
  3. The agent responds with its integer protocolVersion, agentCapabilities, and authMethods.
  4. The client compares the integer protocol version; an unsupported version is a hard failure (do not assume the agent errors first).

fs capabilities are advertised only when acpFsRead/acpFsWrite are enabled (writes default OFF).

Failure taxonomy (probe.ts AcpProbeReason)

Reason Trigger
ok Handshake completed (with authRequired: true when authMethods is non-empty)
missing_binary Spawn ENOENT (binary not found, code 127)
spawn_error Other spawn failure
handshake_timeout initialize did not complete within the bound (code 124)
incompatible_protocol Agent negotiated an unsupported integer protocol version
unauthenticated Agent requires an auth method the client cannot satisfy

Lifecycle / teardown

  • The engine has no AbortSignal in the runtime contract; teardown enters via an unawaited synchronous dispose() plus the process-registry kill. The registry SIGKILL is the authoritative no-orphan / no-deadlock guarantee; a best-effort session/cancel + pending-permission drain runs first when timing allows but is opportunistic.

Sources