dsh-agent-router
Identified★ 1Deterministic, inspectable delegation-time model routing for DeepSeek Harness (dsh) subagents
dsh-agent-router
A DeepSeek Harness plugin that picks a subagent's provider and model once, at delegation time, from a short list of keyword rules you write. No classifier, no scoring: the first rule whose keyword appears in the delegation label or prompt wins, and a delegation no rule claims runs exactly as it would without the plugin.
It wraps the official subagents.startActivation service, so the child still
goes through DSH's own capacity, cancellation, result delivery, persistence and
cleanup. It registers no tools and changes nothing for the root agent.
Install
Requires pnpm, which dsh plugin shells out to.
From source:
git clone https://github.com/gengwg/dsh-agent-router
cd dsh-agent-router
dsh plugin --profile web add "$PWD"
The package declares dsh.bundle, so dsh plugin add joins it to the profile's
bundle stack. Restart dsh to load it. It activates with an empty rule list, which
routes nothing.
Configure
Rules live in the profile's ~/.dsh/profiles/<profile>/cordis.patch.yml under the
agent-router entry. Each rule names the keywords that select it and the route to
apply:
- id: agent-router
name: dsh-agent-router
config:
rules:
- name: research
keywords: [investigate, research, compare]
route:
provider: openrouter
model: deepseek/deepseek-v4-pro
reasoningEffort: high
- name: routine
keywords: [typo, rename, format]
route:
provider: openrouter
model: deepseek/deepseek-v4-flash
| Field | Required | Meaning |
|---|---|---|
keywords | yes | Non-empty list of case-insensitive substrings, matched against the delegation label and prompt text. |
route.provider, route.model | yes | The child's provider and model. Must already be an allowed route in your DSH setup; the plugin cannot grant access. |
route.reasoningEffort | no | Set on the child. When omitted, a caller-supplied effort is dropped so it does not carry onto a different model. |
name | no | Label used in the decision record. |
overrideExplicit | no | Default false: a rule never replaces a route the caller set explicitly (provider, model or reasoningEffort in agentOptions). Set true to let this rule win anyway. |
A malformed rule fails the plugin at load time with the offending path (for
example rules.0.match: unknown rule key), so a typo cannot silently route or
silently pass everything through.
Behaviour
- First match wins, in config order. Matching is a lowercase substring test on
label + "\n" + prompt text; an empty keyword never matches. - Explicit routes are preserved. If the delegating agent asked for a
provider/model itself, rules without
overrideExplicitleave it alone. - No fallback route. A delegation no rule matches passes through untouched, so DSH's own inheritance applies.
- Non-route options survive.
cwd, token limits and otheragentOptionsare kept; onlyprovider,modelandreasoningEffortare rewritten. - Providers that cannot take
agentOptionsare left alone. Out-of-process backends (for example ACP) declarecapabilities.agentOptions: false; routing them would fail the delegation, so they pass through untouched. - Pure decisions.
decide(rules, { label, promptText, requested })is exported and returns{ action, reason, rule, matchedKeyword, agentOptions? }, so you can dry-run a rule set in a script before enabling it. Pass rules throughvalidateConfig(config).rulesfirst;decideassumes the validated shape.
Limitations
- Routes are applied after the subagent tool's own preflight, so the plugin does
not re-check a configured route against DSH's model-selection allowlist. Only
configure routes your profile already permits.
reasoningEffortis likewise passed on as written; an unknown effort id is rejected by DSH, not at load. - Routing happens when a child is created. A running child's model is never changed, and failures (quota, auth, rate limits) are not rerouted; pair with a fallback plugin if you need that.
- Keyword matching is deliberately simple. If you need classification, write the classifier and feed its result in as a label.
Development
npm test
The unit tests need Node 22.18+ (TypeScript test files run via type stripping).
Two tests load the plugin through DSH's real Cordis loader; they find a global DSH
install automatically or via DSH_NODE_MODULES=<dsh>/node_modules, and are
skipped when none is present. Tested against DSH 0.2.1-rc.5.
License
MIT
Comments
Loading…
From the same category
by awesome-dsh-plugin
A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表
★ 22.2k
CC0-1.0
Python
Nov 3, 2026
by 0xsline
DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.
★ 1.3k
CC0-1.0
Python
Oct 16, 2026
by Yuuyuko-uu
DSH 鱼的记忆:给会话一份会淡忘的长期记忆,每条都带时间
★ 464
MIT
JavaScript
Nov 3, 2026
dsh plugin --profile web add dsh-fish-memoryby uumit-ai
Open dsh web from your phone or another computer through a UUMit account, without running your own server or installing another program: a built-in tunnel client connects to UUMit's hosted tunnel, and
★ 454
↓ 268/wk
MIT
JavaScript
Oct 21, 2026
dsh plugin --profile web add @uumit-ai/dsh-uumit-remoteby SAOS-3624
Scene-aware storyboard for DeepSeek Harness: 9-16 numbered cells with timestamps + transcript + an audio envelope, packed into ONE image (384 tokens). Two tools: video_storyboard and video_zoom.
★ 419
↓ 17k/wk
MIT
JavaScript
Nov 3, 2026
dsh plugin --profile web add dsh-video-storyboardby xiajiajun516
DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.
★ 292
↓ 8.8k/wk
MIT
TypeScript
Nov 2, 2026
dsh plugin --profile web add dsh-config-manager