Fills in a cheaper sibling route for subagent calls that name no provider, model or effort (for example Opus to Sonnet 5.5 at medium, Qwen max to Qwen flash); explicit choices and subagent_fork are left alone. Requires the subagent-model-selection policy to be enabled with the target route in its allowedModels - without it the plugin does nothing.
Install
# from npm (prebuilt)
dsh plugin --profile web add @lucagiftzek/dsh-subagent-tiering
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:lucagiftzek/dsh-subagent-tiering
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
Subagents run on a cheaper sibling of the parent model unless the agent names a route.
dsh plugin --profile web add @lucagiftzek/dsh-subagent-tiering
# or from GitHub: dsh plugin --profile web add github:lucagiftzek/dsh-subagent-tiering
# then restart dsh web

Subagents run on a cheaper sibling of the parent's model unless the main agent names a route itself.
Install
Install from npm: dsh plugin --profile web add @lucagiftzek/dsh-subagent-tiering, or from GitHub: dsh plugin --profile web add github:lucagiftzek/dsh-subagent-tiering.
dsh plugin --profile web add @lucagiftzek/dsh-subagent-tiering
# or from GitHub: dsh plugin --profile web add github:lucagiftzek/dsh-subagent-tiering
# then restart `dsh web`
The package declares dsh.bundle, so dsh plugin add lists it in dsh.profile.bundles and
its own patch inserts the subagent-tiering entry with the built-in rules. A local checkout
works too: dsh plugin --profile web add link:/absolute/path/to/dsh-subagent-tiering.
Requires Node 22.18 or newer; used on DSH 0.1.5 and 0.2.1. Read "Needs" below: without a
subagent model-selection policy the plugin changes nothing.
| Parent route | Child default |
|---|---|
| anthropic Opus / Fable / Sonnet | anthropic claude-sonnet-5-5 @ medium (falls back to claude-sonnet-5) |
| alibaba Qwen max/plus | qwen3.8-flash @ high |
| alibaba / byteplus DeepSeek pro | the provider's DeepSeek flash @ high |
| byteplus GLM / Seed | glm-5-3-flash-260828 @ high |
| deepseek-official | deepseek-flash @ high |
| mistral large | mistral-medium-latest |
| any other mistral, alibaba GLM, hetzner | same model, effort capped at high (never raised) |
| anything else | inherits the parent, as stock dsh does |
vision-toolkit-<x> parents keep their wrapper provider. Rules live in
src/rules.js and can be overridden with a - id: subagent-tiering row in the
profile's cordis.patch.yml (config.rules, same shape).
A rule's child.fallbackModels (optional list of model ids, same provider and
effort) is tried in order when the primary model is not in the chat's captured
allow-list or does not resolve. 2026-09-29: the Anthropic default moved from
Sonnet 5 to Sonnet 5.5; chats created before that day have no Sonnet 5.5 in
their allow-list, so they keep getting Sonnet 5 instead of inheriting Opus.
Why (2026-09-27)
Over 14 days of session logs on one busy install, 70% of dsh's Anthropic calls were
subagents. The stock ptc preset gives subagent no child defaults, so every
child ran on the parent's route and effort (usually Opus 5.5 at max). Per human
prompt, dsh cost 2.1x what Claude Code did on the same box, while per call the
two were about equal. tool-subagent's own agentOptions is one static route
and cannot express "Opus parent → Sonnet child, Qwen parent → Qwen-flash child".
How it works
A tools/pre-execute handler. When a subagent call names no provider,
model or reasoning_effort, it fills them in from the first matching rule, as
if the model had asked. The stock tool then runs its normal checks. It leaves the
call untouched, so the child inherits the parent, when:
- the main agent passed any of the three fields itself (its choice always wins);
- the chat has no model-selection policy. dsh records one only for chats created
while
subagent-model-selection.enabledis true, so chats that predate 2026-09-27 are not affected; - the target route is not in that policy's
allowedModels; llm.resolveCallConfigrejects the route. If it rejects only the effort, the effort is dropped and the route kept;- core dsh ever freezes the execution record. This is logged once.
subagent_fork is never touched. A fork must stay on the parent route to reuse
its cached prefix.
A short system-prompt section (subagent-tiering, about 90 tokens) tells the
model about the default and how to override it.
Trade-off
A child on a different model cannot reuse the parent's prompt cache, so its first call pays a full cache write (about 48k tokens on Anthropic after the GenUI removal). A one-call subagent can therefore cost slightly more than before. The median subagent here makes 28 calls, so it comes out well ahead.
Needs
- settings
subagent-model-selection: { enabled: true, allowedModels: [...] }. On DSH 0.2.x this harness setting lives under the entrysubagent-model-selection-settingsand is not imported from an oldsettings.yaml(see docs/TROUBLESHOOTING.md). - the
ptc/ptc-leanpreset'stool-subagentrow withmodelSelectionSettings: true(stock). - adaptive-reasoning must NOT list
anthropic/claude-sonnet-5-5(orclaude-sonnet-5, the fallback) among its routes, or it would override the child's medium effort on every step.
Verify
node --test test/*.test.js runs the unit tests. End to end: start a new chat on
Opus 5.5 and have it call subagent with no model. The child session's
request/header shows claude-sonnet-5-5 / medium (was claude-sonnet-5
until 2026-09-29). Checked on 2026-09-27 on
a production install (Opus → Sonnet) and in a sandbox (Qwen max → Qwen flash; an explicit
qwen3.8-max @ low was left as asked).
When something goes wrong
- Ask your agent to load the
subagent-tiering-doctorskill (copyskills/subagent-tiering-doctorinto~/.dsh/skills/once, how). - Symptom-first guide: docs/TROUBLESHOOTING.md.
- Log lines and start-up messages: docs/ERRORS.md.
More plugins by the same author
All of them are open source and run on DeepSeek Harness 0.2.x (several on 0.1.5 too).
| Plugin | What it does |
|---|---|
| dsh-artifacts | An Artifacts tab that lists the files an agent produced and previews them in place, plus a template library agents can use and extend |
| dsh-mail | Mail client and agent mail tools: IMAP, POP3, SMTP and OAuth2 accounts, scheduled and bulk sends with unsubscribe handling |
| dsh-note-keeper | Obsidian-compatible Markdown notes in the sidebar with search, graph, audio notes and transcription, OCR and client-side encryption |
| dsh-phone | SMS and calls (Twilio, SIP via Asterisk) with call audio through your own server, live voicemail and Telegram |
| dsh-voice | Read-aloud, dictation and language-aware speech with 23 TTS and STT providers, per-language routing and word highlighting |
| dsh-orchestrator | Agent swarm and task orchestration: an Orchestrate tab, orch_* tools, runners, provider cards and a machine API |
| dsh-workspace-rail | A workspace-first sidebar rail: pinned chats, projects as folders, live Recents, search and unread markers |
| DSH-Idle-Watchdog | Idle-chat nudges and an authenticated HTTP API that posts a prompt into any chat |
| dsh-plugin-updates | Update checker that only offers releases your running DSH core can load |
| dsh-subagent-tiering (this one) | Subagents default to a cheaper sibling of the parent model unless the agent names a route |
| dsh-fs-reobserve | Ends self-inflicted stale-file errors after an agent's own shell commands |
| dsh-request-image-cap | Clamps every image sent to the model to a pixel cap, fixing many-image request errors |
| dsh-seal | An encrypted channel in front of DSH: a Go reverse proxy plus a service worker that seal all DSH browser traffic |
Built by Luca Tzekos, who also makes Job Opportunities API: employer-direct job data with per-field provenance you can verify yourself, free to read and available as an API. If this plugin is useful to you, a star on this repo or a look at jobopportunitiesapi.org is the best thanks.
Links
More in this category
Ebony-Vinyl/dsh-our-free-model★ 55971
Adds free model lanes to DeepSeek Harness — DeepSeek V4.1 Flash, Kimi K3 and others — with no login, sign-up or API key, plus a local OpenAI-compatible gateway, fallback routing and a usage dashboard.
dingminhua/dsh-connect-workbuddy★ 993
Connects locally signed-in WorkBuddy models, CN and international alike, to DSH, reading each model's credit multiplier from the upstream catalog on every refresh, with per-model image input toggles, account switching backed by an auto-failing-over pool, manual daily check-in, and read-only credit overviews shown as compact pills on the card and in the composer.
Alice-Marx/dsh-model-router★ 603
Routes DeepSeek Harness work across configured model APIs and supported vendor CLIs with dependency-aware packages and cost previews.
V1ki/dsh-plugin-subscriptions★ 546
Use ChatGPT (Codex), Claude, and Grok subscriptions as DeepSeek Harness LLM providers, with Settings login, model catalogs, usage, plus image_generate, video_generate, and x_search tools.
Mars-Sea/dsh-commandcode-provider★ 446
Unofficial Command Code LLM provider: registers a `commandcode` route with a live model catalog and reasoning-effort support.
FishBottle7/opencode2dsh★ 384
Exposes OpenCode Zen free models to DeepSeek Harness, with no API key.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.