When the client advertises clientCapabilities._meta.terminal_output (Zed), a
bash tool call now renders as a real TERMINAL card — a cwd header + the command
+ its output — instead of the plain ```console text block. Keeps agent-side
dsh-bash execution; rejects the spec's client-side terminal/create (which would
bypass sandbox/env-scrub/ownership/cwd). Matches what claude-agent-acp and
codex-acp do; wire contract verified against Zed's source.
- dsh-tools: a provider-neutral ToolTerminal shape ({ cwd?, output? }) on
ToolCallPresentation/ToolResultPresentation — a tool asks "render me as a
terminal"; no ACP types leak in.
- dsh-tool-bash: bash presentCall marks terminal (cwd from an explicit absolute
workdir, else left for the bridge to fill from the session cwd); presentResult
carries the output alongside the ```console fallback.
- dsh-acp: initialize reads/remembers the _meta.terminal_output capability;
streamSessionEventUpdate maps a terminal presentation to
content:[{type:'terminal',terminalId}] + _meta.terminal_info on the call and
_meta.terminal_output on the update WHEN capable — else the unchanged text
path. terminalId is the callId; cwd defaults to the session header. The pure
translator gained a TerminalRendering {enabled,cwd} param (off by default).
Tests via the REAL tool-bash + bash-local: capability ON -> terminal content +
_meta; OFF -> no _meta (text path). The with-key e2e adds a real-model terminal
card case (echo over ACP with the capability on). 773 tests, 100% coverage.
The exit-status pill (_meta.terminal_exit), live streaming
(_meta.terminal_output_delta), and command classification are RFC follow-ups.
acp-agent example
The DeepSeek Harness coding agent exposed as an Agent Client Protocol (ACP) server over JSON-RPC stdio — drive it from Zed or any other ACP client.
pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
This boots @deepseek-ai/dsh-acp over the shared provider/tool core (../base.yml), with agent-loop configured with no pre-created agents (ACP session/new creates them on demand) and JSONL session persistence (so session/load works).
stdout is the protocol
This example loads no stdout logger — stdout carries the JSON-RPC frames, and any other write corrupts them. Do not add @cordisjs/plugin-logger-console or a stdio UI here. Use a stderr exporter if you need logs.
Zed configuration
Add to your Zed settings.json under agent_servers:
{
"agent_servers": {
"DeepSeek Harness": {
"command": "pnpm",
"args": ["run", "demo:acp"],
"env": { "DEEPSEEK_API_KEY": "sk-…" }
}
}
}
The editor sets each session's cwd to the project it opens; the agent's bash tools run there (see the per-session cwd note in packages/acp), so the server does not need to be launched in the workspace.
MVP limitations
The bridge supports N concurrent sessions per connection, each in its own workspace cwd (RFC 011). Remaining limits: text-only prompts, additionalDirectories rejected (a session operates in its single cwd), and the tool-permission gate is deferred (TODO(rfc010-permission-gate) — tools run with the executor's full authority). See packages/acp/README.md for the full contract.