Tianyi Cui 7a09ca8cd2 Merge remote-tracking branch 'origin/master' into worktree/pr540-merge-master-20260723 hace 1 mes
..
tests 64eebc1b35 Merge branch 'master' into codex/plan-mode-active-exit hace 1 mes
README.md 10a55d7a27 Merge remote-tracking branch 'origin/master' into codex/session-scoped-sandbox-roots hace 1 mes
advanced.cordis.snapshot.yml d139948ed2 feat(persistence): compress JSONL logs with Zstandard hace 2 meses
advanced.cordis.yml 0e06d0691d feat(examples): default agent configs to DeepSeek Pro Max Thinking hace 1 mes
both-mode.cordis.snapshot.yml d139948ed2 feat(persistence): compress JSONL logs with Zstandard hace 2 meses
both-mode.cordis.yml 0e06d0691d feat(examples): default agent configs to DeepSeek Pro Max Thinking hace 1 mes
code-mode-workspace-context.cordis.snapshot.yml 82ce9dfe0e Merge branch 'master' into codex/jsonl-zstd-persistence hace 1 mes
code-mode-workspace-context.cordis.yml 0e06d0691d feat(examples): default agent configs to DeepSeek Pro Max Thinking hace 1 mes
code-mode.cordis.snapshot.yml d139948ed2 feat(persistence): compress JSONL logs with Zstandard hace 2 meses
code-mode.cordis.yml 0e06d0691d feat(examples): default agent configs to DeepSeek Pro Max Thinking hace 1 mes
composition.md f4185122dc refactor(plan): remove generic mode abstraction hace 1 mes
cordis.snapshot.yml 7eec0d81ec test(snapshot): reconcile replay overlays with the merged configs and fixtures hace 1 mes
cordis.yml 6d7e4c339d feat(llm): add default context window fallback hace 1 mes
depth-two.cordis.snapshot.yml 7eec0d81ec test(snapshot): reconcile replay overlays with the merged configs and fixtures hace 1 mes
depth-two.cordis.yml 46d60d6d90 fix(tool-subagent): enforce depth only at runtime hace 1 mes
fs.cordis.snapshot.yml 7eec0d81ec test(snapshot): reconcile replay overlays with the merged configs and fixtures hace 1 mes
fs.cordis.yml 2e7cdddb89 Merge remote-tracking branch 'origin/master' into cross-family-fs-sandbox hace 1 mes
package.json 51640362b5 docs: rename coding demo to repl hace 2 meses
packed-chunks.cordis.snapshot.yml f4ad731079 test(snapshot): add the packed-chunks opt-in replay scenario hace 1 mes
packed-chunks.cordis.yml f4ad731079 test(snapshot): add the packed-chunks opt-in replay scenario hace 1 mes
pty-snapshot-backend.mjs 58cde5103a feat: add persistent PTY sessions hace 1 mes
pty.cordis.snapshot.yml 57a47b1fb3 fix(pty): close review lifecycle gaps hace 1 mes
pty.cordis.yml 58cde5103a feat: add persistent PTY sessions hace 1 mes
session-sandbox-root.cordis.snapshot.yml 7e5d5795a5 test(sandbox): adapt scenarios to latest master hace 1 mes
session-sandbox-root.cordis.yml 7677d49c93 test(snapshot): cover session-scoped sandbox roots hace 1 mes
workspace-context.cordis.snapshot.yml 82ce9dfe0e Merge branch 'master' into codex/jsonl-zstd-persistence hace 1 mes
workspace-context.cordis.yml 0e06d0691d feat(examples): default agent configs to DeepSeek Pro Max Thinking hace 1 mes

README.md

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, plan mode, 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 primary 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. Projects outside the platform temporary areas do not grant either session writes into the other; /tmp and os.tmpdir() remain shared writable scratch roots under workspace-write, so projects placed there are not mutually isolated (writable-root contract). 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 policy.

Plan mode

The same demo:acp server composes @deepseek-ai/dsh-plan-mode, so a capable client advertises default and plan in its mode picker. ACP owns those protocol ids and projects them onto the plugin's boolean plan state. This composition owns the complete plan instructions in cordis.yml: remain in plan mode, inspect before asking, avoid mutations, resolve discoverable repository facts, and submit a decision-complete plan through exit_plan_mode. Those are the instrumental behaviors shared by the local Codex and Claude Code references; product-specific plan files, phase machinery, and protocol tags stay out of the plugin contract.

Plan mode adds only that configured guidance section. Every tool, including exit_plan_mode, keeps the same schema while plan mode is inactive or active; the exit tool describes itself as plan-only and rejects if called while inactive. Stable native schemas and Code Mode SDK bindings avoid tool-catalog churn at the transition. ask_user_question carries blocking user-owned choices through ACP elicitation, while exit_plan_mode renders the exact logged plan for approval and returns keep-planning feedback to the model. The mode picker and permission select remain independent: switching plan state never changes sandbox or approval state, and deployments that need a hard read-only planning floor configure that policy separately. The plan-mode Agent Note owns the state and review contract.

Snapshot tests (record-once / replay-deterministic)

This example hosts the ACP snapshot suite, including the picker advertisement and both plan-review branches. 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 home-directory project sessions and world-verifies concurrent own-root success plus sibling-root denial through both shipped tool families. The keyless session-sandbox-root ACP snapshot places its generated project under the user home while an overlay points the deployment fallback at /tmp; its successful workspace-write call proves the assembled app used the session cwd. Most snapshots start at danger-full-access so bash fixtures remain runner-independent. 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.