dsh-root-planner-preset
Recursive delegation for dsh agent trees: a preset plus Host limits bound what each subagent may do, subagents work in separate _agents/ workspaces, and the parent decides which results to integrate.
Install
dsh plugin --profile web add dsh-root-planner-preset
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-root-planner-preset
An agent preset for DeepSeek Harness (DSH) that separates deciding from doing.
The root agent's context holds the plan and the decisions; reading, editing and running commands go to subagents that report back summaries. Subagents are full workers — they have write, edit and a shell — and the one thing none of them may leave behind is a process still running.
The root decides, the workers do the work, and nothing outlives the task.
Requires DSH 0.2.1-rc.2 or newer. The package declares it as engines.dsh, so the plugin manager refuses to install on an older runtime. The floor is not cosmetic: 0.2.1 introduced agentPreset on the delegation tool and removed toolFilter. On 0.2.0 this preset still composes, and then quietly runs every child on the platform's default preset — wrong persona, twice the tools — which is worse than not installing it at all.
[中文说明](README.zh.md) | [Install](INSTALL.md) | [Maintainer notes](docs/DEVELOPMENT.md)
The problem it solves
On a long task a coding agent's context fills up with raw file contents and command output, and it forgets the goal, loses the plan, and starts repeating work. The usual answer — "delegate more to subagents" — fails in two opposite directions: a delegate with no hands cannot do anything, so every step bounces back; and a fleet of delegates with no bounds will edit whatever they like and leave servers running.
This preset takes the middle line: workers with tools, bounded by their task.
Requirement · Mechanism · Strength
A subagent can read, edit files and run commands · The root-planner-worker preset mounts tool-fs, tool-pwsh, tool-web and the delegation rows; every delegation spawns into that preset by name · Grant by construction — a worker without tools is not a worker
A subagent cannot leave a process running · enableRunInBackground: false on the preset's pwsh row: the call is refused and run_in_background is removed from the tool schema · Structural — no model at any depth can reach for a detached process
Nobody runs an unbounded service · Persona rules for the root and for subagents · Prompt-level, covering what a schema cannot (ops_bash has no job registry)
A subagent stays inside its own workspace · Persona rules: write only under _agents/<label>/, the parent's files read-only to it, outside changes sent as a request · Prompt-level, verified by the parent (see below)
Delegation depth is the user's setting · The runtime depth cap (subagent.maxDepth) · Structural: past the cap it throws SubagentDepthError
Live children are capped as well · subagent.maxActiveSubagents · Structural: past the cap a delegation fails with subagent limit reached, and it does not queue
The root decides; workers execute · Persona rules · Prompt-level
The worker's toolset is an allow list by construction: a tool exists for a worker only if the worker preset mounts the plugin that provides it. A tool added by a future plugin — or by the app — is invisible to workers until a row is added here.
One caveat, measured rather than assumed: a small number of tools are registered globally by the Host and cannot be removed by any preset. On 0.2.1-rc.2 those were ops_glob, ops_grep, ops_inspect_local_file, ops_replace (from the dsh-ops plugin) and working_directory. A worker sees them whatever this package does; the curated set below is what the rows add on top.
One workspace per worker
Concurrency is handled by separation rather than by coordination. Before delegating,
the root creates _agents/<label>/ and puts that absolute path in the task; the child
writes only there, reads the parent's files freely, and sends a change request
instead of editing anything outside. On completion the child reports its artifacts and
the root decides which to copy in — merging into an existing file where that is what the
change means, keeping them separate where it is not. _agents/ stays (and belongs in
.gitignore) so the same agent can be continued later.
The boundary is enforced, not just agreed. This preset's package also ships the fence
as a profile-level entry (dsh-root-planner-preset/guard), so one install carries the
protocol and its enforcement together. It refuses a write, edit or ops_replace whose
target resolves outside <workspace>/_agents/, and refuses a shell call whose workdir is
outside it or whose command names an absolute path into your files:
write: outside your workspace. You may write only inside "...\_agents"; everything else in the parent's directory is read-only to you. Send your parent a change request instead of writing "...\report.md" directly.
The root session is never fenced: it owns the workspace and has to be able to write
anywhere in it to integrate a child's artifacts.
Being profile-level, it fences every delegated agent in this profile — including
subagents under a different preset, which were never told about _agents/. If that matters
on your machine, turn the entry off in your profile patch:
- id: root-planner-guard disabled: true
Declaring the guard as a row inside the preset would scope it to this preset only, and
that was tried first: measured against a live session it registered nothing — six probe
steps all came back allowed while the composed config looked correct. verify-patch.mjs
now fails if anyone declares it as a preset row again.
One limitation survives, and it is worth knowing before you rely on this: a shell command
can build a path at runtime ($env:X + '..', a script that writes, a Here-String), and
no argument scan can see it. The command scan matches drive-letter paths, so on POSIX
it detects nothing at all — the two CI cases for it are skipped there rather than pretending
otherwise. Confining a child's shell properly would n
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::31,最后更新 2026-10-21