Every configurable plugin is identified by its package name, and a patch targets that identifier to replace its whole config or insert new rows. Write the patch in $DSH_HOME/profiles/<name>/cordis.patch.yml, then confirm with dsh --profile <name> --dump-config, which prints the composed result after all four layers.
Every capability in DeepSeek Harness is a plugin, and every configurable plugin is identified by its package name. That single fact is what makes the configuration system learnable: you are never looking for a setting, you are looking for a row.
The config catalog
The repository ships a catalog of every configurable plugin, organised by package ID. Each entry carries four things:
- Identifier — the package name, e.g.
@deepseek-ai/dsh-agent-loop - Purpose — what the plugin configures
- Service dependencies — listed as
Requires:, e.g.agents,sessions,llm - Configuration shape — a TypeScript interface showing every accepted field
That last item is the reason to read the catalog rather than guess: the interface is the complete list of what a row will accept.
A few entries worth knowing:
| Package | Configures |
|---|---|
@deepseek-ai/dsh-agent-loop | maxParallelToolCalls, plus an agents array of agent startup definitions |
@deepseek-ai/dsh-llm-deepseek | The DeepSeek provider: API-key environment variable, endpoint, thinking-mode policy, token limits, retry behaviour |
@deepseek-ai/dsh-session-persistence-jsonl | JSONL session log storage: root directory, compression (zstd or none), chunk packing, cache sizing |
Writing a patch
A patch is YAML keyed by the row identifier:
# Configure agent-loop concurrency
@deepseek-ai/dsh-agent-loop:
maxParallelToolCalls: 4
agents:
- id: primary-agent
sessionId: session-123
cwd: /workspacePut it in the profile's own file:
$DSH_HOME/profiles/web/cordis.patch.yml
A patch targets a row by identifier and either replaces its whole config or inserts new rows. Treat a targeted row as replaced rather than merged — write the complete config you want for it, not just the field you are changing.
The layer order decides who wins
Patches apply to an empty entry list in this order:
- each bundle the profile stacks, in sequence
- the profile's
cordis.patch.yml - the home-level
$DSH_HOME/cordis.patch.yml - any
--patchoverlay at runtime
Later layers win. So the home-level file beats your profile file — which is the correct behaviour for something meant to apply everywhere, and the most common reason a profile-level edit appears to do nothing.
Two settings worth thinking about before you change them
maxParallelToolCalls bounds how many tool calls run at once. Raising it makes broad mechanical
work faster and makes the agent's behaviour harder to follow, because interleaved output loses its
narrative order. Raise it for sweeps; leave it low while you are still learning what the agent does.
Session persistence compression. The default zstd is the right choice for anything long-running.
Switch to none only when you actively want to read raw session logs with ordinary text tools —
and remember that an append-only log of everything the model saw grows faster than intuition
suggests.
Verify, always
dsh --profile web --dump-configPrints the composed result. Compare it with the clean baseline:
dsh --profile web --dump-default-config > /tmp/base.txt
dsh --profile web --dump-config > /tmp/mine.txt
diff /tmp/base.txt /tmp/mine.txtThe diff is exactly the set of changes your layers make. If what you intended is not in it, the patch did not apply. If it is in it but wrong, a later layer rewrote it.
Keep the patch file in version control
A profile's cordis.patch.yml is the entire description of how your agent differs from stock. It is
small, it is text, and it is the thing you will want to explain to a colleague or roll back after a
bad experiment.
$DSH_HOME/profiles/web/cordis.patch.yml → commit this
$DSH_HOME/.credentials.yaml → never commit this
Frequently asked
How do I find out what a plugin accepts?
The repository ships a config catalog listing every configurable plugin by package ID, with its purpose, its service dependencies, and a TypeScript interface showing every accepted field.
Does a patch merge or replace?
A patch targets a row by identifier and either replaces its whole config or inserts new rows. Treat it as a replacement of the targeted row, and write the complete config you want for it.
Which file should I put a patch in?
The profile's own cordis.patch.yml for anything profile-specific. The home-level file applies to every profile and overrides the profile's own, so use it only for things you genuinely want everywhere.