# ACP automation server and backend snapshot-record composition. With # `DSH_SNAPSHOT=record`, the app bin runs the real DeepSeek adapter and the # harness harvests its persisted log. The bin loads the gitignored root `.env` # before this config. This tree has no stdout logger or HMR because stdout # carries ACP JSON-RPC. # The DeepSeek adapter. Shipped default: full thinking at max effort on every # request (wire-only defaults; they never enter the request header). - id: llm-deepseek name: '@deepseek-ai/dsh-llm-deepseek' config: apiKey: !!js process.env.DEEPSEEK_API_KEY baseURL: !!js process.env.DEEPSEEK_BASE_URL thinking: enabled reasoningEffort: max defaultContextWindow: 256000 models: - id: deepseek-v4-flash - id: deepseek-v4-pro # The default composition confines bash AND the filesystem tools to the # workspace and asks before a wider retry. Snapshot runs select # danger-full-access so the established scenarios remain runner-independent; # DSH_PERMISSION_MODE provides the same explicit deployment/test override # outside the snapshot harness. The sandbox default + fallback root live on # ctx.sandboxPolicy; agent calls resolve both families against the session cwd. - id: sandbox name: '@deepseek-ai/dsh-sandbox-local' - id: sandbox-policy name: '@deepseek-ai/dsh-sandbox-policy' config: mode: !!js "process.env.DSH_PERMISSION_MODE ?? (process.env.DSH_SNAPSHOT === undefined ? 'workspace-write' : 'danger-full-access')" workspaceRoot: !!js process.cwd() - id: bash name: '@deepseek-ai/dsh-bash-sandbox' config: timeoutMs: 60000 - id: approval name: '@deepseek-ai/dsh-user-approval' config: policy: !!js "(process.env.DSH_PERMISSION_MODE ?? (process.env.DSH_SNAPSHOT === undefined ? 'workspace-write' : 'danger-full-access')) === 'danger-full-access' ? 'never' : 'ask'" # The ACP automation app: agent spine + JSONL persistence + protocol 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. # Snapshot modes use raw JSONL fixtures; ordinary runs keep the compressed default. - id: acp-agent name: '@deepseek-ai/dsh-acp-demo' config: provider: deepseek model: deepseek-v4-pro persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions' persistenceCompression: !!js "process.env.DSH_SNAPSHOT === undefined ? 'zstd' : 'none'" workspaceContext: maxBytes: 65536 # Keep the persona to identity and behavior; tool plugins own tool guidance. # The loop resolves {{model}} and each ACP session's client-supplied {{cwd}}. persona: | You are a coding assistant powered by the {{model}} model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug. Verify your work by running the code or tests. Keep answers brief and factual. # Replay-aware request pressure; the routed adapter supplies model capacity. - id: token-meter name: '@deepseek-ai/dsh-token-meter' # Summarize an older range after measured pressure or a canonical provider overflow. # Ratios scale against the routed model's context window. - id: compact-basic name: '@deepseek-ai/dsh-compact-basic' config: thresholdRatio: 0.8 retainRatio: 0.08 maxTokens: 8192 compactionRetries: 1 # Expose fresh-child `spawn` and completed-prefix `fork` through separate tool # names so multi-child scenarios exercise both transports. These leaves follow # the app because it provides `ctx.agents` and `ctx.tools`. - 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 maxDepth: 1 - id: tool-subagent-fork name: '@deepseek-ai/dsh-tool-subagent' config: provider: fork toolName: subagent_fork maxDepth: 1 # The worker-thread workflow engine fans a model-written JavaScript script's # `agent()` calls out through the spawn backend; the adjacent tool exposes it to the model. - id: workflow-workerthread name: '@deepseek-ai/dsh-workflow-workerthread' config: provider: spawn - id: tool-workflow name: '@deepseek-ai/dsh-tool-workflow' - id: tool-ralph name: '@deepseek-ai/dsh-tool-ralph' # `todo_write` replaces the logged whole list for later model requests. - id: tool-todo name: '@deepseek-ai/dsh-tool-todo' # Identical repeat calls trigger advisory context, never a block, at the default # thresholds [3, 5, 8]. Only the repeat-tool-guard snapshot scenario reaches them. - id: repeat-tool-guard name: '@deepseek-ai/dsh-repeat-tool-guard' # The filesystem stack rides the SAME sandbox policy as bash: dsh-fs-sandbox # replaces dsh-fs-local behind ctx.fs and fences write/edit by the effective # mode (read-only denies, workspace-write contains to the workspace + temp # roots, danger-full-access passes through), so read/write/edit are available # under every mode. fs-policy (read-before-edit) composes orthogonally on top. - id: fs-sandbox name: '@deepseek-ai/dsh-fs-sandbox' config: cwd: !!js process.cwd() - id: fs-policy name: '@deepseek-ai/dsh-fs-policy' - id: tool-fs name: '@deepseek-ai/dsh-tool-fs' # `configPath` is read once at load and resolves from the server launch cwd, not # `session/new.cwd`; one `hooks.json` therefore applies to every session and a # project-local file is not discovered. Missing config registers nothing. Hook # commands still run in the session cwd. Warnings use `ctx.logger`, never stdout; # see packages/hooks/hooks-claude/README.md for the deferred per-session design. - id: hooks-claude name: '@deepseek-ai/dsh-hooks-claude' config: configPath: ./hooks.json # Codex uses its own `codex-hooks.json` and snake_case five-event dialect; it # cannot share Claude's file. It has the same process-level, read-once, missing-is-no-op, # logger-only contract. Shipping both bridges lets a scenario seed and exercise either dialect. - id: hooks-codex name: '@deepseek-ai/dsh-hooks-codex' config: configPath: ./codex-hooks.json