dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
自己选定子代理运行的模型——附带推理力度上限和输出 token 上限——在代码层面对 DSH 启动的每一个子代理强制执行,无论它由哪个工具启动。
Pick the model your subagents run on — with a reasoning-effort ceiling and an output-token cap — enforced in code on every child DSH starts, whichever tool starts it.
dsh plugin --profile web add github:frederico-kluser/dsh-orquestrator
English · Português (Brasil) · 中文
A plugin for DeepSeek Harness (DSH). When you send a new task, a dialog in the stock DSH look asks one question:
Should subagents run on a different model than the one selected for the main agent?
If yes, you pick the model, and the plugin enforces it in code, on every child DSH
starts for that session: the subagent tools, but also the agents a workflow starts,
ralph rounds, one-shot background jobs and agent teams. The main agent cannot talk its way
around it: it is not an instruction to the model, it is the plugin standing in the doors DSH
starts children through. See Which delegations are covered.
Every governed child also gets a reasoning-effort ceiling and an output-token cap,
because DSH otherwise runs a re-routed child at its route's default (max on many setups):
the cause of subagents that burn their whole budget on one edge case. See
Reasoning effort.
Since 0.8.0 the plugin also ships a global agent skill, orchestrate-subagents, and the dialog
carries a checkbox for it, checked by default: when it is checked, the message goes out with
/orchestrate-subagents and DSH loads the skill's instructions for that task. The skill teaches the main
agent to split the work into small pieces, run subagents in parallel, send subagents to read the code
instead of reading it itself, and check every result with verifier subagents. See
The orchestration skill. The subagent list of a task page now also shows which
model each subagent runs on and a status icon (running, done, failed). See
Subagent list: model and state.
Cancel, Escape and the close button all mean the same thing now: abort the send — nothing goes out, no bubble appears, and the typed text stays in the composer as a draft. Only "Send with these options" sends. Nothing else about DSH changes.
| Dark | Light |
|---|---|
![]() | ![]() |
With a model picked from the composer's own list:

0.5.0 removed the independent reviewer that 0.2 to 0.4 offered next to the model choice. What governs the models stayed, and it now is the plugin's only mechanism. Stored choices and patch files written for 0.4 keep working (the reviewer fields are ignored, with a warning). Why: Why the reviewer was removed.
dsh plugin --profile web add github:frederico-kluser/dsh-orquestrator
dsh --profile web # (re)start it: see the note below
Restart dsh after installing or updating, not only the page. A plugin's host half is loaded when dsh
starts and its browser half when the page loads, so refreshing the page alone leaves the old host half running
(and the old guard with it). From 0.5.1 the two halves also keep talking to 0.2 to 0.4 halves while you do
(a disabled reviewer block stays on the wire for that), so a mixed state still saves; before that, it failed with
"config does not match the expected shape".
From a local clone: dsh plugin --profile web add /path/to/dsh-orquestrator.
Tested on DSH 0.1.6-alpha.2 (Node 24, pnpm 11). The committed lib/ is the
build output, so no build step is needed to install.
Type anything in the composer and send it. The dialog appears before every message you
send — plain text, @file references or /skill invocations, in any conversation, even while
a turn is running:
Subagent model: turn it on and pick a model from the same provider-grouped list the composer's model seat uses. It applies to every subagent, including the agents a workflow starts. Off means subagents keep the main agent's model. The dialog shows short, dated notes for models that need them (for example: MiMo-V2.6-Pro can take minutes per turn at high effort; GLM 5.3 is text only).
Reasoning effort (a select right under the model picker, always visible — there is no section to open
any more): only the effort levels the chosen model really offers, low to high, plus a neutral Model default.
Picking or changing a model jumps the select to that model's highest level at once; change it if you want
less. The last level you confirmed pre-fills the next dialog (and survives a model whose ladder is still
loading). With the switch off the select still works: a level picked there is an effort-only choice (children
keep the main agent's model, under that ceiling).
What the model understands (a small strip under the effort select): four marks — audio, photo, text,
video — lit when the selected model takes that kind of input, and its headline score, Terminal-Bench 4 · 41.8%
when the official Terminal-Bench 4.0 leaderboard knows the model (a snapshot baked into the build) or
Intelligence · 39.5, OpenRouter's intelligence index. The data comes live from OpenRouter's public model
catalog (no key, no proxy); a model it cannot resolve shows no strip.

Orchestration skill (a checkbox, checked by default; shown when the plugin's host half has registered the
skill): applies the global skill orchestrate-subagents to this message. The message goes out with the
/orchestrate-subagents token and DSH injects the skill's instructions into that step. Uncheck it to send
the message without the skill; the dialog remembers your last answer for the next message. It is always
visible and toggleable, whatever the subagent-model switch says. If the message already contains the token (you typed
it), the box is shown ticked and locked, because DSH loads the skill anyway. See
The orchestration skill.
There is no "do not ask again": the modal is raised for every message you send and nothing can silence it. One answer never hides it from a later message or from another conversation. The last confirmed choice only pre-fills the dialog.
Cancel / Esc / ✕: abort the send — nothing goes out, no bubble appears, and the typed text stays in the composer as a draft; nothing is stored. Only "Send with these options" sends.
/orquestrar opens the same dialog on demand (to change or clear the stored choice).
A small chip under the composer (the conversation.composer.dock row) always shows this
conversation's orchestration — Subagents: <model> · <effort> when a model is chosen,
Subagents: same as the main agent when not — and clicking it opens the same dialog.
Nothing skips the dialog: only an empty send passes straight through. Earlier releases skipped
/ lines, messages sent while a turn was running and sub-agent conversations, so every task
that began with a skill invocation (/skill ...) went out with no dialog at all; from 0.6.0
the question is asked no matter what the message looks like.
DSH has more ways to start a child than the two subagent tools. All of them go through two
doors, SubagentRuntime.start() and startContinuable(), and the start guard
(src/guard.ts) stands in both. For a session with a confirmed choice (stored, an ancestor's, or
defaults) it plans every child: the picked route, the effort the user asked for or the model's
ceiling, and the output-token cap, handed to DSH as the child's agentOptions.
| How DSH starts the child | Model, effort ceiling, token cap |
|---|---|
subagent and subagent_fork tools (the standard preset) | yes |
subagent as a one-shot background job (backgroundMode: one-shot, run_in_background: true) | yes |
the workflow tool: every agent() call of the script | yes |
ralph (off in the standard preset; it runs on the workflow engine) | yes |
| agent teams (experimental) | yes |
codex, claude-code and ACP providers | no: they run their own agents on their own models and take no agent options (the plugin logs a warning once per provider) |
| the DSH SDK provider (a separate DSH child runtime) | yes when a subagent model is picked (it takes the route, effort and token limit); with no model picked its child keeps the provider's own model |
What was run live, on the three target models: the subagent tool (foreground and background),
subagent_fork, and the workflow tool (with the default override, with keep and with the
enforcement off), headless and through the dialog in a real browser. The other rows follow from the
doors they use, which the contract tests pin against the DSH source (ralph runs on the workflow
engine, a one-shot job and the team call start / startContinuable, the SDK provider takes agent
options). No live run used a one-shot background job, ralph, an agent team, the SDK provider or
codex / claude-code / ACP.
Why a guard and not a tool wrapper: a wrapper never saw a workflow call's agents, because the
engine starts them through the service itself. In the session that exposed it, 34 workflow
agents ran on Claude Sonnet 5.5 at max (about 7.2 million output tokens and 1.26 billion
cache-read tokens) although DeepSeek V4.1 Flash was confirmed for subagents. Version 0.3 and
older have this hole; the validation page reproduces it on an isolated DSH and shows it closed.
What a governed child gets: the model the user picked, the effort the user picked (or the model's ceiling) and the output-token cap. The main agent is never touched, and a session with no confirmed choice is never touched.
If the model you confirmed is gone (renamed or removed from your DSH settings after you confirmed it),
the child's start is rejected with a message that says so and what to do (/orquestrar, or cancel the
dialog). The alternatives are worse: running the child on the main agent's model is the bug this
design exists to prevent, and forcing the dead route makes every workflow agent fail into a silent null.
A model the caller names itself (agent({ provider, model }) in a workflow script) loses to the
user's pick by default (children.explicitModel: override): the dialog is the user's explicit
instruction. keep lets the caller's model stand, under the same ceilings. A choice with an effort but
no model (defaults.workerEffort alone) leaves children on the main agent's model under that level,
and a model a script names itself stands.
When it loads, the plugin registers one skill with DSH's skill registry: orchestrate-subagents
(skills/orchestrate-subagents/SKILL.md). It is registered by the
plugin, not copied into a skills folder, so installing the plugin is all it takes for every task of every
session to have it.
What it teaches the agent that coordinates (never a subagent; the skill says so itself):
subagent calls in one message, background
jobs for the slow pieces, a workflow script when many pieces share one shape.path:line
references, facts kept apart from guesses and a size limit, so a report costs little context.It also carries the brief template (goal, where, out of scope, context, done when, report format), the verifier brief, and the fixed report format every subagent is asked for. Its wording was tried on four imagined tasks (a code change, a read-only question, a one-line typo, a 40-file migration) before it was settled, and the rough edges those exposed are what rules 3 and 6 now say.
It reaches a task in three ways, all native DSH:
| How | What happens |
|---|---|
| The dialog's checkbox (checked by default) | The message goes out with the /orchestrate-subagents token on a line of its own at the end (the end, because DSH titles a conversation with the first five words of its first message; a first message |
| shorter than five words still carries the token in its title). DSH's skill gesture sees it and injects the skill's full instructions into that step, closest to the model's answer. The transcript shows the token, so you can see which messages carried the skill. | |
You type /orchestrate-subagents yourself (any profile, headless included) | The same gesture. |
| The model's own choice | The skill is listed in the model's skill catalog, so the main agent may load it with the skill tool when a task clearly matches. skill: { modelInvocable: false } keeps it out of the catalog, so only the token loads it. |
The checkbox is shown only when the host half has registered the skill and DSH's dsh-tool-skill, the
plugin that owns the /name gesture, is loaded (the dialog asks the host), so a page whose host half is older
than 0.8.0, or a composition without DSH's skill registry or without that plugin, never sends a token that
nothing would expand. It is not shown when you are typing to a subagent in its own conversation either: the
skill is for the agent that coordinates, and a child that received it would try to coordinate.
skill: false switches the registration off, and the checkbox with it.

The skill text is embedded in lib/index.js at build time (pnpm run gen:skill renders
src/skill.generated.ts from the Markdown, and the tests fail when the two differ), so the installed plugin
never reads it from disk; the file is only handed to DSH as the skill's path, so the transcript can open it.
To use the same text in another agent harness, link the folder into that harness's own skills directory (for
Claude Code: ln -s <clone>/skills/orchestrate-subagents ~/.claude/skills/orchestrate-subagents). Do not link
it under ~/.agents/skills: DSH reads that directory too and would only log, on every catalog build, that the
plugin's own registration outranks the copy.
It is an instruction, not an enforcement. Unlike the model, effort and token cap, which the start guard imposes in code, the skill asks the model to work a certain way, and a model can ignore it, above all late in a long conversation. It does not block any tool: the main agent can still read and edit files. If it matters that the main agent never touches code, that is a job for the session's permission preset.
A task page lists its subagents in a dropdown in the header (the count next to the title). From 0.8.0 each row also shows:
DeepSeek V4.1 Flash · medium): the
model's name from the composer's own catalog, plus the reasoning level when there is one.
DSH records no outcome for a subagent (its catalog only knows "running" and "not running"), so the host half
records one. It listens to DSH's subagent/start and subagent/end events, which every in-process child
emits whichever tool started it, and keeps the model, the state and the stop reason of the latest run of each
child in <stateDir>/subagents.json (owner-only, the 2000 most recent). The page reads it from
GET /dsh-orquestrator/subagents?sessionId=<id>, behind the same trust fence as the configuration route.
A child that ran before 0.8.0, or while the plugin was not loaded, has no record: its row shows the model
DSH itself knows for it, when it knows one, and a grey dot, never an invented "done".
The model on a row is the one DSH itself reports for the child's session (lastUsed) when it has one, and
otherwise the one the host recorded when the run started. The host can correct what it recorded at the end of
a run only for a one-shot child: DSH releases a continuable child (what the standard preset's subagent and
subagent_fork tools start by default) before it announces the end, so the agent is already gone, and the
record keeps the route requested at the start of the run. Its state and stop reason are still recorded.
DSH's dropdown cannot be extended through a slot, so the page half decorates its rows from the outside: it watches for the menu, works out which child each row is from DSH's own session store, and adds two small elements to the row. It is fail-open (anything unexpected leaves DSH's row as it is), reads only roles and structure, never class names, and removes everything it added when the plugin unloads. The DSH structure it relies on is pinned in the contract tests.
Everything is optional; without configuration the plugin does nothing until a user
confirms the dialog. Add overrides to your profile's cordis.patch.yml
(a patch replaces the row's whole config):
- id: orquestrator
config:
# Headless/TUI/SDK sessions have no dialog: apply this to every session.
defaults:
subagentModel: { provider: azure-opencode, model: DeepSeek-V4.1-Flash }
workerEffort: medium # optional; absent = the recommended level for the model
effort: # ceiling on the reasoning effort of every subagent; false turns it off
worker: medium
limits: # output tokens per model request, reasoning included; false = no cap
workerMaxTokens: 64000
children: # the start guard; false switches the enforcement off (the dialog still stores choices)
explicitModel: override # override | keep: a model the caller names itself, e.g. agent({ model }) in a workflow script
skill: # the global orchestration skill; false = do not register it (the dialog then has no checkbox)
modelInvocable: true # list it in the model's skill catalog; false = only the /orchestrate-subagents token loads it
persist: true # remember choices and subagent outcomes across restarts
stateDir: /home/me/.dsh/dsh-orquestrator # an absolute path: "~" is not expanded
maxSessions: 500 # stored sessions before the oldest are pruned
Fields of 0.4 and older that belonged to the reviewer (tools, reviewerProvider, reviewerContext,
structuredVerdict, workerHandoff, maxWorkerReportChars, retryOnTokenLimit, workspaceChecks,
sensitivePaths, defaults.reviewer, effort.reviewer, limits.reviewerMaxTokens) are ignored with a
warning in the DSH log, so an existing patch file keeps loading.
DSH resolves a child's options from its parent, and when the route changes without an
effort it clears the parent's level so the new model "resolves its own default". On setups
whose routes say reasoning: max that means every re-routed child thinks at max, with the
route's full declared output ceiling (131K to 943K tokens on the routes this was built on) as its limit.
The plugin therefore asks DSH for the model's own ladder and applies a ceiling, never a
setting: a route already at or below it is left alone, and a ladder that skips rungs (GLM 5.3
offers low, high, max) gets the highest rung not above the ceiling. off is never picked
on its own. The ceiling is, in order: the level picked in the dialog (it may be above the
ceiling), effort.worker, the model's row in src/models.ts (dated, with its
sources), and medium.
| Model | Subagent ceiling |
|---|---|
| DeepSeek V4.1 Flash | medium |
| MiMo-V2.6-Pro | low |
| GLM 5.3 / GLM 5.3 Flash | high |
| Claude Sonnet / Opus | high |
| Anything else | medium |
limits caps the output tokens of a single model request (reasoning included) and only ever
lowers a ceiling the model is known to have. Why these numbers, and which study recommendations
were left out and why: docs/estudos/.
dsh and reload the page:
a page that loaded before the host half had the new route asked for it once, got a 404, and does not ask
again (until it is reloaded); its rows then show a spinner for a child DSH says is running, a grey dot for
the rest, and the model DSH knows for each. A child on a backend without a session of its own (acp,
codex, claude-code) is not in DSH's dropdown at all, so it has no row to mark. A cancelled child shows as
stopped, any other endi