Files
deepseek-harness/packages/ui/acp/acp-feature-support.md
T

18 KiB

ACP feature support checklist

A structured inventory of Agent Client Protocol (ACP) features and where the harness's ACP bridge (@deepseek-ai/dsh-acp) stands on each. The bridge exposes the harness agent as an ACP server (the agent side of an editor↔agent connection), so "supported" below means the bridge implements the agent's half — answering an agent method, advertising a capability, or calling a client method.

Scope

This tracks the stable ACP v1 surface (schema 1.14.0, schema/v1/schema.json) PLUS the unstable/draft features that the two reference adapters — claude-agent-acp (Claude Code) and codex-acp (OpenAI Codex) — actually ship. A purely-unstable feature that neither reference adapter uses is omitted (see Out of scope).

Legend: supported · ⚠️ partial / fallback · not yet · — n/a. The Stable column marks whether the feature is in the released v1 schema (S) or only the unstable schema (U). The Claude / Codex columns record whether each reference adapter ships it, as a maturity signal.

At a glance

The bridge implements the core prompt-turn loop for N concurrent sessions: initialize, session new/load/list, prompt, cancel, streamed assistant/thought chunks, tool-call rendering (including Zed terminal cards), resumable session replay, slash commands, one-shot permission prompts, per-session model selection and permission presets, and session modes (the picker, via @deepseek-ai/dsh-plan-mode). The largest unbuilt areas are MCP passthrough and agent plans, plus the client filesystem and terminal method families (which the adapters mostly do NOT drive either — see rows 43-49). See Gap summary.

1. Agent methods (client → agent)

Method Stable Bridge Claude Codex Notes
initialize S Negotiates PROTOCOL_VERSION; advertises loadSession, sessionCapabilities.list, and baseline prompt caps. Snapshots the Zed _meta.terminal_output client cap.
authenticate S ⚠️ No-op stub; the bridge advertises no authMethods, so there is nothing to authenticate.
logout S Gated by agentCapabilities.auth.logout; not advertised.
session/new S Maps to agents.create; requires an absolute cwd (becomes the session workspace); rejects non-empty additionalDirectories / mcpServers.
session/load S Maps to agents.resume + full event-log replay; validates persisted cwd before constructing the agent.
session/resume S Reconnect WITHOUT replay; gated by sessionCapabilities.resume. Not advertised.
session/close S No session/close handler — the SDK dispatch returns method_not_found. The bridge tears sessions down on client disconnect / Cordis disposal (cross-cutting, see §8), but that is not the on-demand per-session method.
session/prompt S A flattened prompt beginning with / dispatches through ctx.commands without a model request; ordinary input maps to agent.followup. One request is in flight per session.
session/cancel S Aborts the exact direct command, or applies queue-aware agent.cancel and settles its prompt cancelled, scoped to one session.
session/set_mode S Composed opportunistically: with @deepseek-ai/dsh-plan-mode mounted, session/new/session/load advertise the fixed default / plan projection and session/set_mode records the boolean pending intent (optimistic current_mode_update; logged plan/mode lands at the turn boundary). Without the plugin: no modes advertised, set_mode rejected (see §6 Modes).
session/set_config_option S A provider/model select is present for a complete registered target; one permission select is added when ctx.permission is composed. Every response carries the complete refreshed state.
model selection S No distinct stable session/set_model — model is the model-category session/set_config_option. Values preserve the provider/model pair, catalogs come from ctx.llm, selection is per session, and session/load restores the last requested pair. Codex also supports the legacy unstable_setSessionModel ext method.
session/list S Uses live-preferred ctx.sessionQuery; returns absolute-cwd sessions newest-first with optional folded title and exact cwd filtering. Pagination is not emitted; supplied cursors are rejected.
session/delete S Gated by sessionCapabilities.delete.
session/fork U Claude ships unstable_forkSession; Codex does not.

2. Client methods the agent CALLS (agent → client)

These are capabilities the bridge would drive on the editor. The harness runs tools in-process (its own dsh-bash executor, direct file I/O), so it does not yet delegate to the editor for any of these.

Method Stable Bridge Claude Codex Notes
session/update S The bridge's primary output channel (see §4).
session/request_permission S The bridge answers the ctx.approval seam for the agents it owns: an ask from a hook/plugin becomes an editor prompt attached to the streamed tool call, one-shot allow_once/reject_once options only. Whether a call asks is policy (nothing asks by default); allow_always is deferred (grant storage).
fs/read_text_file S The harness reads files directly (it does not see the editor's unsaved buffer state). Claude delegates; Codex does not.
fs/write_text_file S Same — direct writes, no editor delegation.
terminal/create S Neither reference adapter drives the client terminal API either — both, like the bridge, render shell output as tool-call content + a _meta channel (see §5 Terminal).
terminal/output S As above.
terminal/wait_for_exit S As above.
terminal/kill S As above.
terminal/release S As above.
elicitation/create · elicitation/complete U ⚠️ ⚠️ The bridge drives unstable_createElicitation for ask_user_question form prompts (session-scoped, no URL-mode flow yet). Claude calls the unstable_* elicitation methods for MCP server elicitations; Codex maps elicitations onto session/request_permission.

3. Capabilities

3a. agentCapabilities (advertised by the bridge)

Capability Stable Bridge Claude Codex Notes
loadSession S Advertised true; backs session/load.
promptCapabilities.image S Bridge advertises image: false; image prompt blocks are rejected.
promptCapabilities.audio S audio: false; neither adapter accepts audio either.
promptCapabilities.embeddedContext S embeddedContext: false; embedded resource blocks rejected.
mcpCapabilities.{http,sse} S ⚠️ No MCP passthrough; mcpServers is rejected. Claude advertises http+sse, Codex http only.
sessionCapabilities.* S ⚠️ list is advertised; delete/resume/close/additionalDirectories/fork remain off.
auth.logout S Not advertised.
authMethods[] S ⚠️ Advertised as empty (no auth required to reach the model).
agentInfo (name/version) S Fixed literals: deepseek-harness-acp / 0.0.1 (not config).
_meta custom caps S E.g. Claude's claudeCode.promptQueueing. The bridge advertises no custom _meta.

3b. clientCapabilities (consumed by the bridge)

Capability Stable Bridge Notes
fs.{readTextFile,writeTextFile} S Not consulted (the bridge never calls fs/*).
terminal S Not consulted; the bridge keys terminal rendering off the Zed _meta.terminal_output cap instead.
_meta.terminal_output (Zed) S (_meta) Snapshotted per session at create/load; gates terminal-card rendering.

4. session/update variants

sessionUpdate Stable Bridge Claude Codex Notes
agent_message_chunk S From assistant/chunk text-delta.
agent_thought_chunk S From assistant/chunk reasoning-delta.
user_message_chunk S Emitted during session/load replay to reconstruct the user side.
tool_call S Tool-owned presentation (presentCall); see §5.
tool_call_update S From appended tool/result via presentResult; replacement results rewrite model context and do not duplicate or overwrite execution presentation.
plan S No agent plan emitted. Both adapters emit real plan entries (Codex's CodexEventHandler.updatePlan maps turn/plan/updated{ sessionUpdate: 'plan', entries }).
available_commands_update S Full effective snapshot after create/load and registry changes; names, descriptions, and unstructured-input hints come from ctx.commands.
current_mode_update S Echoed optimistically on session/set_mode and re-notified when a logged plan/mode maps to a different wire id (covers the exit_plan_mode tool flipping the session back).
config_option_update S Config options exist (advertised in session/new/session/load, switched via session/set_config_option), but the bridge never pushes agent-initiated changes — an operator default drift is narrated to the MODEL, not echoed to the editor. Future work in the sandbox Agent Note § Per-session mode switching.
usage_update S Token/cost reporting not surfaced (the harness records token usage internally on assistant/message).
session_info_update S ⚠️ ⚠️ Log-backed title events push title and event time; load replay uses the same mapping.

5. Tool-call rendering

Tool-call presentation is owned by each tool (presentCall / presentResult on the dsh-tools definition), not special-cased in the bridge — see the terminal-and-tool-rendering Agent Note.

Feature Stable Bridge Claude Codex Notes
ToolCallKind mapping S execute/read/edit declared by each tool's presentCall; presenter-less tools render other (no name sniffing); richer mapping possible.
ToolCallStatus S in_progresscompleted/failed.
content blocks S Text content; the description renders above the card.
diff content S The write/edit tools declare a diff render intent: presentCall → a call-time { card: 'diff' } snippet, and presentResult → a result-time { card: 'diff' }. For an edit or an overwrite it carries the applied hunk(s) with surrounding context (one per replace_all site), computed from the before/after text and persisted on the tool/result event as meta; for a create (no before-image) it is an args-derived whole-file diff. The bridge emits { type: 'diff', path, oldText, newText } content blocks; a successful mutation ALWAYS returns the result diff (an ACP tool_call_update.content replaces the call's content, so the result diff — not the model-facing text — is what survives).
terminal content S Via the Zed _meta terminal convention (see below), not the spec terminal/* sub-protocol.
locations (follow-along) S The read/write/edit tools emit { path, line? } file-location hints via presentCall.
rawInput S ⚠️ Parsed tool args surfaced as rawInput.
rawOutput S ⚠️ Not emitted.

Terminal rendering

⚠️ Implemented via the Zed _meta convention (terminal_info / terminal_output / terminal_exit), gated on the client advertising _meta.terminal_output — NOT the spec's terminal/create sub-protocol (which would make the editor execute the command, bypassing dsh-bash's sandbox / env-scrub / ownership / cwd). Both reference adapters take the same _meta approach. Live incremental streaming (terminal_output_delta, which Codex negotiates) is a follow-up — the bridge currently sends the full captured output once on the result.

6. Session modes / config options / models

Session modes (the plan-mode Agent Note): ACP owns the fixed default / plan wire vocabulary and projects it onto ctx.planMode's boolean { active, pending? } state; session/set_mode calls set() and current_mode_update tracks the optimistic selection plus each distinct committed plan/mode flip. Config options : the bridge advertises a model select from the advisory LLM provider/model catalog, preserving each provider/model pair in an opaque value and grouping multiple providers. A selected pair is isolated to one session, snapshotted with the prompt for each step, applied through agent/request, and restored from the logged request header on load. When ctx.permission is composed, the bridge also advertises one permission select whose values come from the deployment preset table and whose current value derives from the session log; idle permission switches anchor at the next agent/step inside its open turn. The division is picker-to-collaboration-state / knobs-to-config-options: individual environment knobs and the provider/model selector are not modes. See the model-catalog Agent Note and sandbox Agent Note.

7. Content blocks

Block Stable In prompts In updates Notes
text S Baseline.
resource_link S ⚠️ Accepted in prompts and rendered into text (acpPromptToText); not emitted as a structured update block.
image S Rejected in prompts (promptCapabilities.image: false).
audio S Rejected.
resource (embedded) S Rejected (embeddedContext: false).

The bridge rejects unsupported prompt blocks rather than silently dropping them (promptHasUnsupportedContent), per the "explicit over implicit" convention.

8. Cross-cutting

Feature Stable Bridge Notes
StopReason mapping S turnEndToStopReason is total over harness turn-end reasons → end_turn/max_tokens/cancelled.
Multi-session (N per connection) S Strict per-session demux; concurrent streams never interleave. See the multi-session Agent Note.
Disconnect / disposal teardown S Quiesces every live session on client disconnect or Cordis disposal.
_meta extensibility S ⚠️ Consumed for the Zed terminal cap and emitted for terminal cards. Listed sessions add deepseek-harness/sessionReference with a canonical URI when cross-session references are mounted.
Background-task ownership isolation Generic task_output/task_kill reject tasks whose branded owner SessionId belongs to another session.
stdout-is-the-protocol guarantee S The bridge runs in an example with no stdout logger.

Gap summary

Ranked by how commonly the reference adapters ship them and how much UX they unlock:

  1. Session lifecyclesession/delete, then session/resume / session/close.
  2. Agent plan (sessionUpdate: 'plan') — surface the loop's plan as structured entries.
  3. MCP passthrough (mcpServers on session/new + mcpCapabilities).
  4. Richer prompt content — image / embedded resource blocks (needs a multimodal model path).
  5. Usage reporting (usage_update) — the harness already records token usage internally (on assistant/message).
  6. Editor filesystem delegation (fs/read_text_file / fs/write_text_file) — lets the agent see unsaved buffers; lower priority since the harness has direct disk access.

Out of scope

Unstable/draft ACP features that neither reference adapter ships are not tracked above: providers/* (LLM provider selection), mcp/connect·mcp/message·mcp/disconnect (client-side MCP passthrough), nes/* (Next Edit Suggestion), document/did* (LSP-style document sync), the v2 plan model (plan_update / plan_removed), boolean config options, $/cancel_request, and the draft Streamable-HTTP transport. They can be added if a target editor adopts them.

Sources