A hands-on walkthrough of the dsh preset format, matched line-for-line to the official built-in presets — persona rows, tool packages, groups, and how to verify it works.
A DeepSeek Harness preset is a directory with two small YAML files. This walkthrough creates a read-only code reviewer from scratch — shell access hard-disabled, a reviewer persona wired in — and every snippet below is matched to the format of the official built-in presets, not paraphrased from memory.
<your-preset-id>/
├── agent.cordis.yml # the composition — required, this file IS the preset
└── preset.yml # display metadata for the preset picker (name/description/order)
Two things the design notes make official:
[a-z0-9][a-z0-9-]* — lowercase, hyphens, no spaces. The check runs before the
directory name is ever used.The iron rule is now official. Shipped presets refuse writes and deletes — the deployment’s copy is the reference a broken local preset gets compared against. The supported authoring path is, verbatim from the notes, “duplicate, then edit.”
npx @deepseek-ai/dsh web # UI at http://127.0.0.1:3080
Not sure what plugin rows your build actually boots? This prints the composed tree, and any row it prints can be replaced by a patch of your own:
dsh --profile web --dump-config
Duplicate standard (or minimal if you want the smallest baseline) into your preset
root under the dsh home — community docs document $DSH_HOME/.agent-presets/ — under a
new id: my-reviewer. The roster discovers user-authored presets by filesystem scan.
preset.yml is what the picker renders. The built-ins themselves use it exactly like
this (yes, with Chinese display names):
name: My Read-Only Reviewer
description: Reads everything, executes nothing. Findings sorted by severity.
order: 10
order sorts the picker — the built-ins take 1–4, so start yours at 10.
agent.cordis.yml is a list of rows. Each row has a local id, a name pointing
at the real package, and optional config. Three shapes cover almost everything:
The persona row — identity is a composable package, not a prompt string:
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
text: |-
You are a staff-level code reviewer. You never run or modify code.
Report findings as BLOCKER / MAJOR / MINOR / NIT, each with file:line
and a concrete suggested fix.
Tool rows — mount only what the agent should see:
- id: tool-fs
name: '@deepseek-ai/dsh-tool-fs'
- id: tool-fs-search
name: '@deepseek-ai/dsh-tool-fs-search'
Disabling is structural. disabled: true unmounts the row for this preset — the
tool isn’t suggested away, it isn’t there. A reviewer that must not execute anything:
- id: tool-bash
name: '@deepseek-ai/dsh-tool-bash'
disabled: true
The real package vocabulary you’ll reach for most: dsh-tool-fs,
dsh-tool-fs-search, dsh-tool-str-replace-editor, dsh-tool-bash,
dsh-tool-web (config includes fetch and searchTimeoutMs),
dsh-tool-skill, dsh-tool-goal, dsh-tool-todo, dsh-tool-ask-user,
dsh-tool-jobs, dsh-persona, dsh-agent-instructions. The built-in
standard/agent.cordis.yml is the authoritative catalog — read it before
inventing ids.
Some capabilities come as groups: a cordis:group row with an isolate
realm and child rows. You’ll meet this for plan mode and delegation:
- id: planning
name: cordis:group
group: true
isolate:
planMode: true
config:
- id: plan-mode
name: '@deepseek-ai/dsh-plan-mode'
The realm rules are enforced, not advisory: a service row outside a group publishes into the process-global root realm and the mount rejects it rather than letting two sessions collide. When you duplicate a built-in, keep its groups intact.
Open a new session and pick your preset. Then test the edges, not the happy path:
preset.yml.Two switching rules from the design notes: you can only pick a preset while the session is still blank (once a turn has run, the history was produced under that tool set and the host refuses the swap), and a resumed session rebuilds the composition it was created with — the preset id rides the session header. Edits to your files apply to the next session, never the running one.
If a package name rejects, you’ve probably hit a typo or a renamed package —
--dump-config lists what actually resolves in your build.
The authoritative example is the
official standard/agent.cordis.yml —
duplicate it, keep its groups intact, and trim rows until the toolset says exactly
what your agent should be. (A curated community catalog lives on this site too; it
returns once every preset is verified against a live install.)