Files
deepseek-harness/examples/acp-agent
2026-07-21 00:44:28 +08:00
..
2026-07-04 01:07:26 +08:00

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 loggerstdout 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 Permissions select. workspace-write means workspace-confined bash plus ask; danger-full-access means unconfined bash plus never. Switching writes one permission/preset event through to the sandbox-mode and approval-policy events, and session/load reports the resumed value.
  • Every approval is one-shot: the choices are Allow once and Reject; 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/edit tools through an in-process path fence (dsh-fs-sandbox); both receive the calling session's cwd as workspaceRoot.

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.