@xmoon76/dsh-subagent-router
English | 中文
A model-facing DSH plugin that starts a subagent on a model-selected LLM provider and model. The model picks the route (provider/model); the deployment owns the subagent backend (subagentProvider, default spawn), the lifecycle mode (executionMode), and the route allowlist (allowedProviders). Later turns reuse the official send_message / list_agents / interrupt_agent tools from @deepseek-ai/dsh-tool-subagent-control.
Installation (as a DSH profile bundle)
This package ships as a DSH profile bundle: its cordis.patch.yml
(dsh.bundle.patch) inserts the tool-subagent-router row into the profile
composition automatically. Requires a profile whose bundles include
@deepseek-ai/dsh-base (every shipped web/headless template does) — the
subagents registry and its spawn backend come from that base layer.
dsh plugin --profile <name> add @xmoon76/dsh-subagent-router
The command installs the package into the profile and adds it to the profile's
dsh.profile.bundles layer list; on the next boot its patch inserts the
tool-subagent-router row with the defaults below. To override defaults, patch
the same row id in the profile's own cordis.patch.yml (a patch replaces the
row's whole config):
- id: tool-subagent-router
config:
allowedProviders:
- deepseek-official
Usage walkthrough
The following flow is a practical model-facing invocation. Confirm that the profile has registered subagent_start and that the selected provider/model route is configured before dispatching.
Start a continuable child
subagent_start requires all four fields. The model chooses only the LLM route; deployment configuration still owns the backend and lifecycle:
{
"description": "say hi",
"prompt": "Say hello briefly, then state which model you are using.",
"provider": "codex",
"model": "gpt-5.6-luna"
}
// continuable result: started continuable subagent <id>
A continuable result acknowledges inbox acceptance and returns a durable id; it does not contain the child reply. Wait for the DSH settlement notice or inspect the child transcript by id.
Continue for more turns
Use the official send_message control tool to queue the next FIFO turn after the child has been accepted:
{
"subagent_id": "<id>",
"message": "What kinds of engineering tasks are you best at?"
}
// message queued as the next turn for subagent <id>
The child keeps its creation-time provider/model across later turns and cold resume. There is no mid-session route switch.
Companion controls
These controls are separate from this package and must be mounted from the official @deepseek-ai/dsh-tool-subagent-control plugin:
| Tool | Purpose |
|---|---|
send_message |
Queue the next turn for a durable child (FIFO). |
list_agents |
List or recall started children. |
interrupt_agent |
Interrupt a running child turn. |
subagent_fork |
Create a child from completed turns with an incremental prompt. |
Best practices
- Make a fresh-child
promptself-contained: it does not see the parent conversation. A forked child may instead receive an incremental prompt over completed turns. - Verify provider/model availability before dispatch. There is no model-discovery tool, and route errors may surface only when the child's first request resolves the route.
- Treat a continuable start result as an acknowledgement, not as the child answer; use settlement notices and the transcript for the actual result.
- Configure
allowedProviderswhen deployment policy restricts routes; do not rely on prompt wording for enforcement. - Keep credentials, endpoints, and headers out of prompts and tool arguments. Use a unique
toolNamefor each loaded instance. - Remember that
maxTokensis not durable across activations.
Why this package
The official @deepseek-ai/dsh-tool-subagent binds one instance to one fixed child agentOptions (deployment-fixed provider/model). This plugin moves the LLM route choice into the model's hands while keeping every capability owned by the DSH seam: it is a thin Consumer over ctx.subagents.startContinuable() / ctx.subagents.start() and does not re-implement continuation, sessions, persistence, authority, or queues.
Contract
The model-facing tool subagent_start takes:
| Parameter | Required | Meaning |
|---|---|---|
description |
yes | Short (3-5 word) label of the delegated task. |
prompt |
yes | Complete standalone task (fresh child) or delta over completed turns (forked child). |
provider |
yes | Configured DSH LLM provider route for the child. |
model |
yes | Model id for the child conversation. |
Success returns { kind: 'continuable', subagentId } (durable id, resolved at inbox acceptance) or { kind: 'one-shot', runId, output } (final child output), depending on the instance's executionMode. Credentials, endpoints, headers, maxTokens, outputSchema, and backend selection are never exposed to the model.
Config
| Key | Default | Meaning |
|---|---|---|
subagentProvider |
spawn |
ctx.subagents provider name. Continuable mode requires prepareContinuable; one-shot mode requires a start-capable provider (fork is the supported one-shot backend). |
executionMode |
continuable |
continuable calls startContinuable() and returns a durable subagent id; one-shot calls start() and returns the run's final output. Never model-selectable. |
toolName |
subagent_start |
Model-facing tool name; distinct per loaded instance. |
maxDepth |
3 |
Absolute delegation-depth cap, or 'provider-managed' for no cap. |
persona |
— | Per-child persona shadowing deployment:persona. |
toolFilter |
— | Per-child global-tool restriction; requires the toolFilter capability. |
allowedProviders |
— | Deployment-side LLM provider allowlist, enforced in execute() before any child work; explicit [] denies all. |
Routing policy
- The model selects only the LLM route:
providermust name a registered DSH LLM adapter route andmodela model id on it. allowedProvidersis executor-level enforcement, not a prompt hint.provider/modelvalidity is ultimately resolved by the DSH LLM/Agent resolution at the child's first request (nolistModels()hard whitelist, preserving dynamic model routes).- The subagent backend and lifecycle mode are deployment configuration; the model never selects them.
Continuation behavior
- A continuable child is a durable conversation:
send_message(official control tool) delivers later FIFO turns,list_agentslists it,interrupt_agentinterrupts it — all throughctx.subagentsauthority paths. - Cold resume keeps the same
agentProvider/agentModel: the durable descriptor persists them, so a resumed Activation still uses the creation-time route. provider/modelare fixed at creation; there is no mid-session model switching.
Model Experience
Tool schema
What the model sees
The registered subagent_start schema: description, prompt, provider, model, all required. The description/prompt wording follows the backend provider's inheritsParentContext: a fresh child is told to provide a complete standalone prompt; a forked child is told it already sees completed turns. No api_key, base_url, max_tokens, run_in_background, or backend/mode parameters exist.
Token effect
Fixed schema cost per request where the tool is visible; no system-prompt section is contributed by this package.
KV Cache effect
Prefix-stable while the registered tool schema is unchanged; provider registration lifecycle may invalidate reuse from the first changed tool definition.
Tool result
What the model sees
started continuable subagent <id> (continuable mode) or the child's final text (one-shot mode). Continuable results carry no child reply; the child's transcript by its id is the source of what it did, and its settlement notice arrives independently.
Token effect
One short result appended per accepted creation (continuable) or the child's output (one-shot).
KV Cache effect
Append-only after the reusable request prefix.
Known Limitations and Deferred Work
- No mid-session model switching —
provider/modelare fixed at creation; the durable descriptor persists them, so a resumed Activation still uses the creation-time route. - No model discovery tool — the model must already know the configured provider/model ids; a read-only discovery tool is deferred.
- No synchronous collection of a continuable child result —
subagent_startresolves at inbox acceptance; the child's settlement arrives via the DSH notice mechanism, and its transcript by id is the detailed source. maxTokensis not durable — per-activation budgets are not persisted in the DSH continuable descriptor, so the tool does not expose them.- Only configured LLM adapters/routes can be used — the child route must resolve at request time;
provider/modelvalidity may fail only when the child's route is resolved (nolistModels()hard whitelist by design). - Continuation controls require the official control tool —
send_message/list_agents/interrupt_agentcome from@deepseek-ai/dsh-tool-subagent-control, mounted separately. - One-shot output is not streamed — the run's final output is returned once the child settles; intermediate steps stay in the child's transcript.
- Output schema uses the DSH tools value-schema dialect — the canonical one-shot
outputis{ type: 'array', items: { type: 'json' } }, where'json'is@deepseek-ai/dsh-tools's Schemastery-based value type (the same dialect the officialtool-subagentuses), not a bare JSON-Schema keyword; only DSH's tool registry consumes it.
Development
Prerequisites
Node.js ≥ 22 and npm. All DSH peer dependencies resolve from the npm registry
(@deepseek-ai/dsh-* 0.1.0-rc.x), so no deepseek-harness checkout is
required.
Gates
npm run typecheck # tsc over src + tests
npm run lint # oxlint
npm run test # vitest (package integration + Loader composition)
npm run test:coverage # per-file 100% on src/
npm run build # tsc emit to lib/
No comments yet. Be the first to write one.