ACP adapter #
albedo acp serves Agent Client Protocol v1 over stdio. The Go adapter translates requests into daemon HTTP and SSE calls. The daemon owns execution, history, authorization, and MCP connections. OpenAPI defines the HTTP contract.
Connection and capabilities #
The adapter supports local daemons and workspaces. It connects lazily, never prompts or restarts an incompatible daemon, and requires capabilities.acp_attachment >= 3. Provider credentials come from normal albedo configuration. Missing setup returns provider_missing with instructions to run albedo login in a terminal.
initialize answers without contacting the daemon. Supported methods are session/new, session/list, session/load, session/resume, session/set_config_option, session/prompt, and session/cancel. Output uses session/update. Prompts accept text and resource links. Images, embedded resources, editor filesystem and terminal callbacks, and HTTP MCP servers are unsupported. File tools use saved disk contents.
Stdout contains newline-delimited JSON-RPC only; diagnostics use stderr. Reading, observation, and writing run independently. The writer bounds pending output to 4 MiB. Overflow or write failure disconnects the adapter without cancelling daemon work.
Ownership and recovery #
session/new requires an absolute cwd and an mcpServers list. Creation establishes the durable ACP marker, attachment, and MCP configuration before runtime preparation. Retries retain their operation identity and compare configuration fingerprints without persisting credentials.
Each session has one writer attachment, authorized by Albedo-Attachment-Token, separate from daemon authentication. The daemon retains only its digest. Leases renew every five seconds and expire after twenty. Competing attachments are refused. Other clients may inspect, stop, rename, change preferences, and delete ACP sessions, but cannot submit input or change execution.
Disconnect or lease expiry releases ownership without cancelling accepted work or removing MCP state. Sessions remain visible as attached or detached. Daemon restart clears attachments and MCP credentials. Interrupted ACP prompts are not re-executed.
A stream gap or incompatible durable replacement fails observation and requires resume. Observation failure never cancels execution. After updating albedo, reconnect the editor's agent process as well as restarting the daemon; an existing process continues running its old binary.
Models and history #
New, loaded, and resumed sessions return a configOptions model selector with profile/model choices from configured providers. Provider defaults remain available when a catalogue cannot be read. The current model remains visible even if no longer listed.
session/set_config_option accepts configId: "model" and an advertised value. Switching requires an idle session, affects only that session, and rechecks attachment ownership inside the daemon actor. Resume refreshes choices and the current selection. A failed pre-write read returns an error without invalidating the attachment. Only an unknown model-write outcome requires resume. After an acknowledged switch, a failed catalogue refresh returns success with cached choices and the selected value; the next switch or resume refreshes the choices. The ACP configuration contract requires the complete configuration list and its current values in the response.
session/list discovers ACP sessions without acquiring ownership. It supports an exact cwd filter and opaque pagination. Load and resume require full session IDs, matching workspaces, and the intended MCP configuration. Resume defaults an omitted or null mcpServers to an empty list. Stdio server args and env remain required, but null values default to empty lists. These defaults follow ResumeSessionRequest and McpServerStdio in the pinned ACP schema. Competing writers cannot attach.
Load replays durable user messages, replies, thoughts, and tools in order, then delivers buffered live activity. History pages follow the daemon's opaque newer token, including entries that share the cutoff row's position. It excludes internal provider records and system input. Replay failure returns an error and releases the attachment. Resume delivers future activity without replay. Resuming running work permits cancellation and holds one successor prompt until the inherited work ends. Further prompts return busy while that slot is occupied.
Prompts and output #
Whole-prompt validation precedes admission and preserves block order. Local file links retain their URI and decoded path; other schemes remain references without fetching. Descriptions reach model context, and negative resource sizes are rejected. Opaque metadata affects retry identity but is not stored or shown to the model.
Each ACP prompt runs as its own turn; mail and scheduled input cannot merge into it. One successor may wait behind inherited work, once cancellation begins, or once the prior response is queued. Cancellation before admission is retained until admission is known. Queued cancellation uses its receipt; running cancellation waits for confirmed completion. If completion wins the race, its actual outcome is returned. A failure after cancellation returns cancelled, including abort and observation errors, as required by ACP cancellation and the pinned schema's StopReason. Observation failure still requires resume before another prompt. All updates precede the final response.
Live and durable output reconcile by identity and streamed-prefix hashes, never text equality. Provider-only records cannot claim visible text. Reconciliation state is bounded and cleared at turn completion. An unprovable replacement requires resume because ACP cannot retract delivered chunks.
Tool identities and outcomes survive live delivery and replay. Displayed results include recorded arguments, failures, locations, and genuine before/after diffs. Missing snapshots produce explanatory text; unified diffs never reconstruct full files. Snapshots are produced only for ACP sessions and are bounded to 128 KiB per file and 256 KiB per trace. Their encoded text has a separate 256 KiB live-event allowance, so a small edit of a large file stays inline; larger encoded traces use history references. Tool display is limited to 256 KiB of encoded JSON, with explicit truncation; durable history is unchanged.
MCP overlays #
The default-enabled editor-mcp extension exposes editor-supplied stdio servers separately from configured MCP servers. See editor MCP tools for selection and Python-binding behavior.
Commands run directly, without a shell, in the session workspace. Literal environment values pass through the secrets path. Duplicate names and collisions with configured servers are rejected. Configuration, credentials, and catalogues stay in daemon memory across disconnects and disappear on restart.
Servers must initialize and finish discovery before publication. Preparation failure returns 409 mcp_overlay_failed without changing the previous overlay. After confirmed publication, reload failure returns 503 overlay_installed_reload_failed and keeps the replacement installed. An unanswered publication returns 503 overlay_update_uncertain; installation may still complete. An identical reattach reconciles or retries reload. Active turns accept only identical overlays.
With editor MCP enabled, publication awaiting reload blocks execution; new prompts are refused before admission. A daemon restart drops editor ownership: old editor prompts fail with attachment_lost, and other clients still cannot submit prompts. Mail, schedules, and job wakes may run without an editor attachment when the durable overlay is empty. A nonempty overlay lost at restart blocks those turns with overlay_reattach_required; the editor must resume and resupply its servers. A successful reattachment reload resumes blocked inputs immediately. Interrupted ACP prompts are never replayed. Disabling editor MCP removes the overlay execution dependency. Empty configuration removes only the editor overlay.
acp_attachments tracks Ready, ReloadPending, and ReattachRequired separately. Its module-keyed VM-lifetime started bit distinguishes daemon startup from a supervised attachment-owner restart. A fresh daemon has no cached editor tools, so an absent empty overlay is ready. An attachment-owner restart can leave cached runtimes alive, so even an empty overlay requires reattachment and reload before execution. Failed publication reloads retain ReloadPending and report overlay_installed_reload_failed.
Implementation #
cli/internal/acp owns framing, session state, replay, and output. cli/internal/app owns workflows; cli/internal/daemon owns HTTP calls. The pinned ACP schema is under cli/internal/acp/testdata.
On the daemon, acp_attachments.gleam owns leases and memory-only overlays, acp_session.gleam owns publication and readiness, and http_acp.gleam owns routes. Session actors serialize admission, model selection, publication, and capture of durable and live boundaries. Each actor retains its immutable ACP marker at startup; ordinary turns skip editor readiness work. HTTP and editor-MCP preparation check the durable marker before entering ACP modules, keeping that stack lazy for ordinary sessions.