- Inicio
- Plugins
- Sesiones y mensajes
- dsh-routed-subagent
dsh-routed-subagent
bpc-oss/dsh-routed-subagent
Run a one-shot subagent fully mounted on any agent preset from any session, with per-call model/provider override, model pre-check, and external CLI engines (codex / claude / codebuddy) with background jobs, live progress, kill, and continuable sessions.
Instalar
dsh plugin --profile web add github:bpc-oss/dsh-routed-subagentREADME
dsh-routed-subagent
A global DeepSeek Harness plugin that lets any session dispatch a one-shot subagent fully mounted on ANY agent preset, with per-call model/provider override and a model-availability pre-check.
The stock subagent / subagent_fork tools force children to inherit the PARENT's preset. This plugin replaces that with a custom subagent provider whose async child setup calls agentPresets.mount(childCtx, <preset>) — so the child adopts the TARGET preset's complete composition: persona, prompt sections, skill catalog, and tools.
Features
- Background by default, parallel dispatch — the call returns a job id immediately (like the stock subagent tool); the conversation stays free to do other work or dispatch more children in parallel, and aborting the conversation does NOT cancel the child (stop it with job_kill). Set
run_in_background: falseto wait inline. - Full preset mount — the child runs under the target preset's standing composition (not a persona copy): identity, mission section, skills, tools.
- Per-call model override —
model/providerarguments route the child's LLM call to a different model than this session's (via the officialresolveChildAgentOptionschannel). - Model pre-check — an invalid model fails fast with the provider's candidate list instead of an opaque child failure.
- Official subagent ecosystem — one-shot lifecycle events, UI rows, trajectory; returns the child's final output.
- Idempotent provider registration — multiple presets can mount the row; the host-plane provider registry is never duplicated.
External engines (engine=...)
subagent_routed can also dispatch the subagent to an external CLI agent instead of an in-harness preset mount. The default engine is dsh (this plugin's kernel); alternatives:
| engine | driver | background job | live progress | kill | explicit model | continuable |
|---|---|---|---|---|---|---|
dsh (default) | preset-mount provider | ✅ | ✅ | ✅ | ✅ | ✅ |
codex | codex CLI app-server --stdio | ✅ | ✅ (process event stream) | ✅ turn/interrupt | ✅ thread/start model | ✅ same-thread resume |
claude | Claude Code SDK | ✅ | ✅ | ✅ abortController/close | ✅ | ⚠️ official Anthropic API only |
codebuddy | CodeBuddy Code CLI --print | ✅ | ✅ (NDJSON stream) | ✅ process kill | ✅ --model | ✅ --session-id / --resume |
// external codex subagent (background, explicit model, live progress)
await subagent_routed({
engine: 'codex',
provider: undefined, // external engines ignore the DSH provider
model: 'gpt-5.6-sol', // explicit codex thread model
prompt: '...',
run_in_background: true,
})
- codex engine: keeps one long-lived
codex app-server --stdioprocess (lazy start, init-once); each run usesthread/start+turn/start, live progress fromitem/agentMessage/deltaevents, kill viaturn/interrupt, continuable reuses the disk-persisted thread (samethreadId). Unattended default isapproval_policy: never. The codex CLI must be logged in (codex login). - codebuddy engine: spawns
codebuddy --print --output-format stream-json --include-partial-messages --dangerously-skip-permissions; live progress fromtext_deltaevents; continuable creates a session with--session-id <uuid>then resumes with--resume <uuid>(sessions persist on disk). Default modelhy3(overridable viaconfig.codebuddyModel/$CODEBUDDY_MODEL). CodeBuddy Code CLI must be installed (codebuddy --version). - binary discovery:
CODEX_BINenv wins (may point at nativecodex.exeorbin/codex.js); otherwise auto-probes the npm global@openai/codex/bin/codex.js. - claude engine: drives
@anthropic-ai/claude-agent-sdk; model, kill, progress and background all work. ⚠️ continuable depends on the official Anthropic API — when the claude CLI is configured with a custom backend (e.g.AnthropicBaseURLpointing at a third-party/local endpoint),sessionId + persistSessionmay hang or be unusable; pointAnthropicBaseURLat the official API for reliable resumes.
Distribution
GitHub-only. This plugin is not published to npm. Install it by mounting the package directory (see below). peerDependencies are declared with real semver ranges as metadata; they are not used for npm resolution.
Compatibility: targets DeepSeek Harness rc.7+ (behavior verified against rc.7 sources and rc.8 runtime) (the async child setup that this plugin relies on is a recent harness behavior).
Install
The plugin is a plain ESM package with a cordis.patch.yml bundle declaration.
1. Link the package into the harness install
The plugin statically imports @deepseek-ai/* packages, which resolve via Node ESM from the package location. Create a node_modules junction/symlink in the package directory pointing at the harness install:
:: Windows
mklink /J "<plugin-dir>\node_modules" "<harness>\resources\host\node_modules"
# POSIX (Linux/macOS)
ln -s "<harness>/resources/host/node_modules" "<plugin-dir>/node_modules"
2. Add the bundle to a profile
Add the package to your profile's dsh.profile.bundles list (e.g. <dshHome>/profiles/web/package.json):
{
"dependencies": { "dsh-routed-subagent": "link:<plugin-dir>" },
"dsh": { "profile": { "bundles": ["...", "dsh-routed-subagent"] } }
}
cordis.patch.yml in this repo is the bundle layer that registers the plugin; it is applied automatically when the package is listed in bundles.
Tip: if your deployment provides a hot-assembly helper (e.g. a super-injector-style
dev_install_package(dir=...)), you can use it instead of the manual steps above; restarts re-assemble from thebundleslist either way.
Usage
subagent_routed(prompt="Use the dev engineer standard to review this repository", preset="dev", description="dev review") # background one-shot
subagent_routed(prompt="Continue the review", preset="dev-reviewer", description="follow-up", fork=true) # inherits THIS conversation
subagent_routed(preset="dev", prompt="Audit this repo", description="audit", continuable=true) # send_message(<subagentId>, ...) later
Behavior:
Modes (one tool, four shapes):
| mode | how | returns |
|---|---|---|
| one-shot background (default) | run_in_background: true (default) | job id immediately; collect with job_output (live progress) / stop with job_kill; aborting the conversation leaves the child running |
| one-shot foreground | run_in_background: false | blocks until the child returns its final output |
| fork | fork: true | job id / run result — the child is seeded with this conversation's COMPLETED turns (inherits the context) then mounts the requested preset on top |
| continuable | continuable: true | durable subagent id — continue it later with send_message(subagentId, ...); the child mounts the requested preset and keeps it across resumes |
Parameters:
| input | behavior |
|---|---|
preset invalid / unresolvable | error, with the roster's available preset ids |
model / provider | per-call model override (fail-fast pre-check lists the provider's candidates; original error preserved as cause) |
max_tokens | output/token cap for the child's LLM calls (positive integer) |
tool_filter | DENY-only tool mask on top of the preset's tool surface ({ deny: string[] }, e.g. deny shell tools for a read-only audit) |
max_depth not a positive integer | tool-layer validation error |
fork + continuable together | supported (continuable fork seeds the parent's completed turns) |
| valid call | child fully mounted on the target preset |
How it works
- A custom subagent provider (
routed-mount) re-implements the official one-shot in-process driver (dsh-subagent-in-process-driver'sstartInProcessRun) with one load-bearing change: the child setup isasyncand awaitsagentPresets.mount(childCtx, targetPreset)instead of composing from the parent. agents.createawaits the setup (verified indsh-agent-loop), so the async mount runs inside the unpublished creation window; a failure rolls the whole child back.- The child's session header records
agentPreset: <target>(overriding the parent value), so cold reads rebuild the child under the composition it actually ran. - The tool settles the run in two sequential fault-tolerant phases — result first, then dispose — matching the official
settleForegroundRunordering (racing dispose against result would skip the child's turn and return "aborted").
Known limitations
- preset generation drift (known limitation) —
mountre-resolves the preset by id on every creation/resume, so editing a preset file between continuable turns hands later turns a NEWER generation of that preset (the officialcomposeFrompath joins the parent's exact standing instance instead). Documented behavior; restore the preset to its original state to keep turns consistent. - Failure semantics — like the official foreground subagent tool, a child that ends with
error/refusal/max-tokensmakes the tool call THROW (with any partial output attached); onlycompletedand caller-initiatedabortedreturn as values. Low-level LLM error details live in the child session log. - Pre-check is conditional — the model pre-check runs only when the harness exposes an
llmservice AND a provider route exists (explicitprovideror the parent's). Without either, it is skipped and the call proceeds. - Provider availability is environment-specific — the pre-check validates against the runtime model catalog, but a reachable provider with a valid key is still required for the call to succeed.
Platform patch (continuable + preset mount)
continuable mode mounts the requested preset on the child and keeps it across resumes — that required a small, additive patch to the open-source @deepseek-ai/dsh-subagent:
- install-level junction (single assembly point):
resources\host\node_modules\@deepseek-ai\dsh-subagent→ the patched fork (the original package is backed up beside it). The plugin's startup assertion logs a loud warning if the loaded instance is not the patched fork — continuable+preset will silently degrade to the parent composition, so check the log on upgrade. Do NOT add a profile-locallink:dependency to@deepseek-ai/dsh-subagent(that would split module identity and defeat the patch). - patch surface (additive only — official paths with no
presetare byte-identical):applyChildComposition:composition.presetmounts the TARGET preset instead of joining the parent's (composeFromskipped — a second bind would throw; delegation context / persona / toolFilter kept); appendsagent-preset/selected(target)so fork seeds replaying the parent's selection events cannot shadow the header on cold rebuildmaterializeTrackedsetup is async (awaited by the agent factory) and still returns the{ commit }contract- continuable descriptors gain an optional
presetfield (version 2 → 3 for continuable only; one-shot stays 2 — a rollback rejects v3 descriptors cleanly as NOT_RESUMABLE, and legacy v2 continuable descriptors still parse) coldResumerebuilds the child under the SAME preset fromdescriptor.preset; a missing/broken preset surfaces a named-preset error instead of a generic "unavailable"
- rollback: delete the install junction (
resources\host\node_modules\@deepseek-ai\dsh-subagent), copy<fork-dir>\@deepseek-ai\dsh-subagent.origback into place, restart — official behavior returns (pre-existing preset-continuable children become NOT_RESUMABLE, as designed). Note: keep the fork directory present if any other profile tree junctions to it (dsh-continuous-worker\node_modules\@deepseek-ai\dsh-subagent) still exist, or re-point them.
Disabling the stock subagent tools
Once the full stock surface is ported, subagent_routed becomes the single delegation entry point: a global tools.guard denies subagent / subagent_fork at execution with a redirect message (config.disableStockSubagent ?? true; set false to keep them). Scope: every preset that mounts this plugin row.
Development
node --check lib/index.js # syntax
The plugin is a plain ESM package (zero build step): lib/index.js plus per-engine providers under lib/engines/. CI runs node --check on every push.
License
MIT
Plugins relacionados
dsh-synapse
liangmianya/dsh-synapse
dsh-turn-rewind
anionex/dsh-turn-rewind
dsh-chat-import
nwflower/dsh-chat-import
dsh-plugin-bridge
totoro-qaq/dsh-plugin-bridge