Bring your own models
Claude-Code-style agent definitions keep referring to stable model names like sonnet or opus. With dsh-cc you do not have to rewrite them: each logical lane is routed to a provider/model pair configured for your deployment, so your agents stay stable while you choose the models that fit your environment, cost, and latency needs.
The alias lanes
Claude Code-style agent definitions often refer to models using aliases:
model: sonnetdsh-cc can route those aliases to provider/model pairs configured for your deployment. Conceptually:
sonnet / draft -> <provider>/<general coding model>
opus / blueprint -> <provider>/<reasoning model>
haiku / sketch -> <provider>/<fast model>
fable / masterplan -> <provider>/<maximum-reasoning model>
architect -> parent agent route (planning / orchestration)
gauge -> typed-decision cheap lane (System One models; not generative)
inherit -> parent agent routeThe key point: aliases are configuration, not hard-coded vendor bindings. This lets you preserve familiar agent definitions while choosing the models that fit your own environment.
gauge is a decision-only lane for System One models, used by typed-decision consumers such as the permission classifier. Never use it as agent frontmatter model:. When unconfigured it follows haiku. The routes service rejects any chat request for a System One model with SystemOneChatModelError (code SYSTEMONE_NOT_CHAT_MODEL).
TIP
The dsh-cc project itself is developed with dsh-cc using this exact routing — see the current development mapping in the project README's Dogfooding dsh-cc section. It is a real project configuration, not a required default.
Tiered cascade routing (moa)
The @dsh-cc/moa plugin routes main-conversation turns over the four lane aliases sketch / draft / blueprint / masterplan: each user turn is classified by a System One decision call into the cheapest sufficient tier, and an optional acceptance judge escalates a rejected answer to the next tier up.
moa is off by default — with moa.enabled: false the plugin mounts nothing and costs nothing. Subagents and forks are never overlaid.
Configuration lives in the moa settings namespace:
| Key | Default | Meaning |
|---|---|---|
enabled | false | arm tiered routing on the main agent |
acceptance.enabled | false | run the acceptance judge and act on rejects |
acceptance.shadow | false | run the judge, log verdicts only |
acceptance.tau | 0.7 | reject when P(acceptable) < tau |
max-escalations | 1 | per-message escalation ceiling (read-time clamp to 3) |
judge-route | alias gauge | alias or {provider, model, protocol}; unset uses the gauge lane alias, which must resolve to a System One route, else moa stays unarmed |
classify-budget-tokens | 4000 | classify input budget |
call-budget-ms | 8000 | per-message cascade deadline |
The acceptance judge runs on the gauge lane described above — the typed-decision cheap lane for System One models — so a gauge mapping that resolves to a System One route is what arms judging when judge-route is left unset.
Truthful model bookkeeping
While a moa overlay is active, several things keep the session honest about which model is actually serving it:
- Phantom notices filtered. The harness re-emits
[model changed: …]notices on nearly every step under the overlay, claiming a continuation model the overlay will not serve. A sanitizer drops exactly those rows while the overlay is active; in every other state (moa disabled, disarmed, validation-failed, or no moa at all) harness notices pass through byte-identical. /modeldisarms moa. A mid-session/modelselection away from the boot pair disarms moa before the next turn is classified — the wire serves your selection (previously the switch was silently ignored while overlaid). Switching back to the boot selection re-arms moa from the next turn.- Persona states the effective model. The system-prompt
provider/modelvariables are rewritten to the resolved tier pair whenever the overlay applies; on any fail-open (classification failure or timeout, off-boot selection, disabled) the harness's own selection variables pass through untouched, so the prompt and the wire always agree. - Statusline shows the effective pair. While an overlay is active the statusline renders
<resolved-model> ◂ <tier-alias>(e.g.<provider-model> ◂ sketch) instead of the bare tier alias; custom statusline payloads gain amoa: {tier, model}field. A switch-away clears the segment. - Statusline shows why moa is not armed. With
moa.enabled: truebut arming refused (routes unavailable, degenerate ladder, judge-route window failure) the statusline rendersmoa:unarmed — <reason>instead of hiding the misconfiguration in a warn line.
Routing scope is the main conversation only — subagent sessions are never classified, overlaid, or judged.
Managing providers with /provider
The /provider command opens an overlay over your configured model providers:
| Command | What it does |
|---|---|
/provider | Open the provider overlay |
/provider list | Print the current routes |
/provider add <preset-id> | Walk a wizard for a built-in preset (Moonshot, Z.AI/Zhipu, DeepSeek) or a fully custom endpoint |
/model | Re-pick the provider/model for the current session |
The per-route detail view lets you rotate keys, refresh the model list, set the default, or remove the route. Notes on credentials:
- API keys are typed into a masked field and stored in the credential store at
~/.dsh/.credentials.yaml— never in settings. - Keys already supplied by the environment are shown read-only.
- Changes take effect for new sessions immediately (credentials resolve per request); the running session keeps its current provider until you pick again with
/model.
Per-task choice: aliases and agent frontmatter
Subagent definitions live in per-workspace .claude/agents directories and use Claude Code-style frontmatter, including a model: field. When dsh-cc dispatches a subagent, the persona comes from the definition's systemPrompt, and the model: alias in the frontmatter is resolved through the ccModelRoutes alias service — queried on every spawn — so a definition that says model: haiku lands on whatever provider/model your haiku lane maps to at that moment.
The same resolution applies elsewhere aliases are resolved: hook executors that resolve a model: field go through ccModelRoutes too (an omitted model falls back to the haiku cheap lane).
WARNING
Alias resolution covers agent frontmatter and hook executors. Aliasing the main-session default model is a known follow-up, and ANTHROPIC_* env vars have no Anthropic semantics — see the Models section of the parity matrix.
Alias semantics
Resolution follows a fixed lookup order: settings overlay → config defaults → builtin fallback. Alias key matching is case-insensitive (keys are folded to lowercase at merge and at lookup).
An alias entry comes in two shapes:
- String form —
sonnet: deepseek-v4-flash: the model id only, the provider inherits. - Object form —
opus: { provider: deepseek-official, model: deepseek-v4-pro, reasoningEffort: max }: an explicit route with an optional effort.
A few boundary behaviors worth knowing:
inherit(any case) means no override — the child inherits the parent route.- Only the settings layer may set an entry to
null, which deletes a same-named config-default entry (entry-shallow). Deleting a builtin alias falls through to the builtin fallback, i.e. inherit — config may never holdnull. - Unconfigured builtin aliases (
fable/opus/sonnet/haikuand the dsh-cc lanes) inherit the parent route. An unconfigured custom alias such asturbohas no fallback: it passes through verbatim as a literal model id, with a warning logged. - A configured string-form target that names another alias is followed one hop — the hop carries
reasoningEffort. Object-form targets are concrete routes and are not followed as names.
Cheap-lane observability
When an unconfigured builtin alias silently inherits the parent route, the resolver logs a warning — once per alias. The warning is gated by the warnOnInherit key in the model-aliases settings namespace (default true; set it to false to suppress).
Cycling models with Ctrl+P
In the TUI, Ctrl+P / Shift+Ctrl+P cycles through the alias list in cc-model-cycling.cycleOrder, resolved through the same alias resolver. Entries that do not resolve, or are not advertised, are skipped with a toast. The switch is in-memory only; nothing is written to settings.
model$level: effort on the reference
Any model reference — agent frontmatter model:, an alias target's model id, or the auto-mode classifier's route — may carry a `$<level>` suffix (opus$high, glm-5.3$xhigh):
- The suffix is stripped before the id leaves the resolver, so pricing, breakers, and pins key on bare model ids.
- The level rides
reasoningEfforton the resolved route and overrides an alias target's declaredreasoningEffort. - Unknown level spellings are carried to the harness boundary, which fails the request with
UNSUPPORTED_REASONING_EFFORT. - Malformed suffixes (a trailing
$, an empty or bad-charset level, a$inside a provider segment) pass the reference through verbatim.
Effort precedence
When several sources declare a reasoning effort, the effective value is picked by this order (higher rungs never mutate lower ones):
- An explicit
`$level`suffix. - The alias target's declared
reasoningEffort. - Agent frontmatter
effort(applied at spawn). - The
/effortsession selection. - The catalog/harness route default.
See the /effort row in /reference/commands.
Example: end-to-end walkthrough
A minimal path from a bare deployment to running a task on the fast lane:
# 1. Add a provider through the preset wizard
/provider add <preset-id>
# 2. Inspect the current routes
/provider list
# 3. In the per-route detail view, set the default
# and map the lanes (haiku/sketch -> fast model)
# 4. Re-pick the model for the current session if needed
/modelAfter the lanes are mapped, any agent definition pinned to a lane runs there. For example, a fast lookup agent in .claude/agents/lookup.md:
---
name: lookup
description: Fast codebase lookups
model: haiku
---
You are a read-only codebase scout. Return paths and line numbers.Dispatching this agent resolves haiku through ccModelRoutes at spawn time — no change to the definition is needed when you later remap the fast lane to a different provider.
Next
- /quickstart — get a session running first.
- /guide/subagents — how subagent definitions and dispatch work.
- /reference/commands — the full command surface, including
/providerand/model.