# The acp-agent plugin tree: the ACP server. Also the snapshot RECORD config # (the dsh-acp-agent bin selects it for DSH_SNAPSHOT=record): a real llm-deepseek # run whose persisted log the snapshot harness harvests. The swappable DeepSeek # adapter, local bash/filesystem executors, the ACP server app # (@deepseek-ai/dsh-acp-agent), and the optional model-facing fs/subagent/todo # tools loaded below. # # CRITICAL: this tree loads NO stdout logger and NO hmr — stdout is reserved for # the ACP JSON-RPC protocol (see packages/ui/acp). That guarantee is now a # property of @deepseek-ai/dsh-acp-agent (it contains no logger entry), not a # leaf convention: there is no logger here to get wrong. # # Requires DEEPSEEK_API_KEY (and optionally DEEPSEEK_BASE_URL) — the # dsh-acp-agent bin loads the gitignored repo-root .env first (on STDERR only). # The DeepSeek adapter. - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: apiKey: !!js process.env.DEEPSEEK_API_KEY baseURL: !!js process.env.DEEPSEEK_BASE_URL models: - deepseek-v4-flash - deepseek-v4-pro # Local bash executor for agent-core's tool-bash schema. # FIXME(config-comments): keep this executor note from implying bash is the # whole tool set; filesystem, subagent, and todo_write are loaded below. - id: bash name: '@deepseek-ai/dsh-bash-local' config: timeoutMs: 60000 # The ACP server app: the agent-core spine + JSONL persistence + the ACP bridge. # Persistence root: $DSH_SNAPSHOT_SESSIONS_ROOT when the snapshot harness sets it # (so it can harvest / isolate the log), else ./.sessions for the demo. - id: acp-agent name: '@deepseek-ai/dsh-acp-agent' config: model: deepseek-v4-flash persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions' systemPrompt: | You are a coding assistant driven over the Agent Client Protocol. Your tools are read/write/edit for file operations, bash (plus bash_output/bash_kill for background tasks), and subagent. Use read to inspect UTF-8 text files, write to create or replace files, and edit for targeted literal replacements. Use bash for shell commands, tests, searches, and operations that are not ordinary file reads or edits. Each bash call runs in a fresh shell — pass workdir instead of cd. Check the [exit code: N] marker; verify your work. Keep answers brief and factual. Use the subagent tool to delegate a focused, self-contained subtask to a fresh child agent (it works in its own context and returns only its final result) — give it a complete, standalone instruction. Use subagent_fork instead when the subtask needs THIS conversation's context: the child inherits the log so far. For multi-step work, use the todo_write tool to track a task list: send the WHOLE list each call (it replaces the previous one), keep at most one task in_progress (exactly one while work remains), and mark a task completed as soon as it is done. Skip it for trivial single-step tasks. # The subagent seam + both in-process backends + two model-facing tools, as leaf # entries after the app (which provides ctx.agents/ctx.tools). spawn (a fresh # child) and fork (a child seeded with the parent's completed-turn prefix) are # both reachable by the model: dsh-tool-subagent is loaded once per backend with # a distinct toolName (subagent → spawn, subagent_fork → fork), so a multi-child # scenario can exercise both transports. - id: subagent name: '@deepseek-ai/dsh-subagent' - id: subagent-spawn name: '@deepseek-ai/dsh-subagent-spawn' config: providerName: spawn - id: subagent-fork name: '@deepseek-ai/dsh-subagent-fork' config: providerName: fork - id: tool-subagent name: '@deepseek-ai/dsh-tool-subagent' config: provider: spawn toolName: subagent - id: tool-subagent-fork name: '@deepseek-ai/dsh-tool-subagent' config: provider: fork toolName: subagent_fork # The model-facing todo_write tool: whole-list task tracking written to the # session log (todo/write), surfaced to the ACP client as a `plan` update. - id: tool-todo name: '@deepseek-ai/dsh-tool-todo' # Filesystem capability stack: local provider, read-before-write/edit policy # gate, then the model-facing read/write/edit tools. Relative filesystem paths # resolve from the server launch cwd; the documented Zed setup launches this # demo from the harness checkout with `pnpm --dir`. - id: fs-local name: '@deepseek-ai/dsh-fs-local' config: cwd: !!js process.cwd() - id: fs-policy name: '@deepseek-ai/dsh-fs-policy' - id: tool-fs name: '@deepseek-ai/dsh-tool-fs' # The Claude Code hook bridge. `configPath` is PROCESS-LEVEL: it is read ONCE at # load and the relative `./hooks.json` resolves against the ACP server's launch # cwd, NOT each `session/new.cwd`. So a single `hooks.json` next to where the # server starts applies to every session; a project-local, per-session hooks.json # is NOT discovered (per-session config resolution is a TODO — see the bridge # README). With no file present the parse fails-soft and the bridge registers # nothing (a silent no-op). Hooks THEMSELVES run in the session cwd (the bridge # passes it as the workdir); only WHERE the config is read from is process-level. # stdout is the ACP JSON-RPC channel — the bridge's warnings go through ctx.logger # (no exporter here), never to stdout. - id: hooks-claude name: '@deepseek-ai/dsh-hooks-claude' config: configPath: ./hooks.json # The Codex hook bridge, loaded alongside the Claude one. It reads its OWN config # file (`./codex-hooks.json`, Codex's snake_case five-event dialect) — the two # bridges cannot share one file, so each owns a distinct path. Same process-level # read-once semantics and same fails-soft-when-absent contract: a launch cwd with # no `codex-hooks.json` registers nothing (a silent no-op through ctx.logger, never # stdout). The example ships both bridges so a scenario can exercise EITHER dialect # end-to-end by seeding the matching file in its workspace/. - id: hooks-codex name: '@deepseek-ai/dsh-hooks-codex' config: configPath: ./codex-hooks.json