acp-agent example
The DeepSeek Harness SDK agent demo 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)
pnpm run demo:code-mode acp # the same server in Code Mode: one wire tool, run_code
The leaf config loads the ACP app, DeepSeek adapter, sandboxed bash, the sandboxed filesystem stack, approval and permission services, model-facing tools, and repeat guard. The app bundles the agent spine, JSONL persistence, and bridge, creates agents on session/new, and keeps stdout logger-free. fs.cordis.yml adds local tool-result spill storage for its dedicated scenarios; code-mode.cordis.yml adds run_code and its generated TypeScript SDK. See Code Mode.
stdout is the protocol
This example loads no stdout logger — stdout carries the JSON-RPC frames, and any other write corrupts them. @deepseek-ai/dsh-acp-demo includes no logger entry, so this leaf has none to get wrong by default; do not add one (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": ["--dir", "/path/to/deepseek-harness", "run", "demo:acp"],
"env": { "DEEPSEEK_API_KEY": "sk-…" }
}
}
}
The editor sets each session's cwd to the project it opens. That directory is both bash's default workdir and the session's workspace-write boundary: every bash or filesystem mutation carries one policy resolved from the calling session, so a single server process may serve concurrent projects without granting either session writes into the other. The configured workspaceRoot: process.cwd() remains the fallback for calls without a session cwd. The filesystem tools ride the same policy through @deepseek-ai/dsh-fs-sandbox, so read/write/edit are available under every mode and confined to the same session root.
Snapshot tests (record-once / replay-deterministic)
This example hosts the ACP snapshot suite. It replays through dsh-llm-replay, which reconstructs model streams from assistant/chunk events in each scenario's session JSONL. Recording runs the real ACP agent and harvests its logs; refresh keeps the committed transcript as mock input and rewrites current replay outputs. replay.override.json covers throw and hang cases that chunks cannot express, and an optional workspace/ seeds files. The snapshot Agent Note owns the ACP harness design.
Permissions and sandboxing
The default tree composes @deepseek-ai/dsh-sandbox-local, @deepseek-ai/dsh-sandbox-policy, @deepseek-ai/dsh-bash-sandbox, @deepseek-ai/dsh-fs-sandbox, @deepseek-ai/dsh-user-approval, and @deepseek-ai/dsh-permission. Bash and the read/write/edit tools start in workspace-write; a denied operation returns a structured marker, and a retry with sandbox_permissions plus justification becomes a one-shot session/request_permission prompt in the editor. "Allow once" runs exactly that retry under the wider mode (sandbox Agent Note § Escalation).
- One session config option is live: a capable client shows one
Permissionsselect.workspace-writemeans workspace-confined bash plusask;danger-full-accessmeans unconfined bash plusnever. Switching writes onepermission/presetevent through to the sandbox-mode and approval-policy events, andsession/loadreports the resumed value. - Every approval is one-shot: the choices are
Allow onceandReject; a dismissal, rejection, missing editor, or unavailable runner fails closed. - The boundary spans bash and the filesystem tools per session: bash confines through the OS runner and the
read/write/edittools through an in-process path fence (dsh-fs-sandbox); both receive the calling session's cwd asworkspaceRoot.
tests/escalation.e2e.ts boots this default tree keyless, drives the permission select, and—with a key and usable runner—proves both approval outcomes against the filesystem. The agent-spine e2e independently boots one context with two project sessions and world-verifies concurrent own-root success plus sibling-root denial through both shipped tool families. Most snapshots use the ACP tree and start at danger-full-access so bash fixtures remain runner-independent; scenarios that call read, write, or edit use the full-access fs overlay and a separate request-header pin. The permission-switching and escalation inputs select workspace-write before exercising the bash policy path. No fixture pins real runner denial text because its dialect is platform-specific.
MVP limitations
The bridge supports N concurrent sessions per connection, each with its own cwd (RFC 011). Prompts support ACP's baseline text and resource_link blocks only; additionalDirectories and mcpServers are rejected. See packages/ui/acp/README.md for the full contract.