feat(shared,host-service): pinned Claude model ids in the workspace-create picker, and reject an unsupported model
Summary
- Add pinned Claude releases (Opus 4.8/4.7/4.6/4.5, Sonnet 5/4.6, Fable 5, Haiku 4.5) to the workspace-create model picker, alongside the existing family aliases.
- Section the picker into "Latest" and "Pinned releases" so the two kinds are distinguishable without suffixing every label; Codex's retirement date moves out of a source comment and into a "Retiring 2026-08-31" header.
- Add
validateAgentModelSelection, so a model the agent can't take fails with an actionable message instead of being silently dropped at launch.
Why / Context
Reported by @iamwix on X, mid-rollout at his org:
When I go to start a new workspace I can't choose a legacy model from the dropdown or configure it in the settings. For when I don't want to default to Opus 5.
The picker itself has existed since #5248 and renders in both create surfaces. The problem is its contents: Claude's catalog was fable / opus / claude-opus-5 / sonnet / haiku — four aliases that resolve to whatever the CLI considers newest, and one pinned id. A team standardising on an older release had nothing to pick.
The second half is worse than a missing option. buildAgentModelArgs returns [] for any id outside the curated list, so the flag is dropped and the agent launches on its own default — exit code 0, no warning:
buildAgentModelArgs("claude", "claude-opus-4-8") -> [] # silently ignored
buildAgentModelArgs("claude", "opuss") -> [] # typo, silently ignored
Effort and mode have validated with a readable error since they shipped (validateAgentEffortSelection, validateAgentModeSelection); model was the odd one out. This matters more once #6805 lands --model on the CLI, SDK, and MCP — that PR's docs already word around the gap ("Unsupported effort values fail before launch").
How It Works
validateAgentModelSelection mirrors the effort validator exactly, and is wired into both entry points:
buildTerminalAgentLaunch— a direct agent launch rejects the model before building the command.validateAgentLaunchOptions— the workspace-create preflight, so--model bogusfails before the worktree is created rather than after.
Unknown ids now produce Unsupported model "claude-opus-4-9" for Claude. Choose one of: fable, opus, sonnet, haiku, claude-fable-5, …. buildAgentModelArgs keeps its degrade-to-default behavior as the backstop for callers that bypass validation.
Manual QA Checklist
Workspace create
- New-workspace dialog with a Claude agent selected shows a "Latest" section and a "Pinned releases" section
- Effort and mode pickers (no groups in their catalogs) still render as flat lists
- Picking
Opus 4.8launchesclaude --model claude-opus-4-8 -
Default modelstill omits the flag entirely - Selection still sticks per preset across dialog opens and host switches
API / tRPC error handling
-
agents.runwith an unknown model returns BAD_REQUEST naming the supported ids -
workspaces.createwith an unknown model fails before the worktree exists - An agent without model support (e.g. Superset chat) rejects a model override
Testing
bun test packages/shared/src/agent-models.test.ts apps/desktop/src/renderer/components/AgentModelSelect packages/host-service/src/trpc/router/agents/agents.test.ts— 86 passbun test packages/host-service/src/trpc/router/workspaces packages/host-service/src/trpc/router/workspace-creation— 190 pass, no regressionsbunx tsc --noEmitclean forpackages/sharedandpackages/host-servicebunx biome checkclean on the four touched files- Reverted the two source files and re-ran: the new assertions fail (plus the missing export), confirming the tests can fail
Design Decisions
- Sections, not label suffixes. A
(latest)suffix on every alias pays for the distinction once per row; a header says it once. Options carry an optionalgroup, and a catalog that sets none renders flat exactly as before — so the effort and mode pickers are untouched. Reuses theSelectGroup/SelectLabel/SelectSeparatorpattern from the theme picker rather than inventing a list UI. - Aliases kept, not replaced. Most users want "newest Opus" and shouldn't have to re-pick after every release. The pinned ids are additive, and
(latest)labels keep the dropdown readable now that both kinds sit in one list. - No
defaultModelId. #6541 pairs its catalog with a per-preset default that removes theDefault modeloption and pins Claude to Fable 5 out of the box. That's a product decision about spend, separate from "let me pick an older model", so it isn't here. - Validate rather than pass through. Accepting arbitrary ids would also serve Bedrock/Vertex users with pinned ids like
us.anthropic.claude-opus-4-6-v1, but it keeps the silent-drop failure for typos. Rejecting is the honest default; a deliberate free-text escape hatch can come later if asked for.
Known Limitations
- The catalog stays hand-maintained and will drift with CLI releases. #6541 addresses that properly with runtime capability discovery — though notably it does not discover Claude's models (it only probes
claude auth status), so Claude would rely on this same curated list either way. - The new error strings are not Lingui-wrapped, matching the sibling validators. Neither
packages/host-servicenorpackages/sharedis inpackages/i18n/test/enforced-dirs.ts, and host-service has no i18n wiring yet; converting it is out of scope here. - Still no default-model setting. That's the other half of the report ("or configure it in the settings") and wants a
default_modelonhost_agent_configsplus a control in Settings → Agents.
Follow-ups
- #6805 (
--modelon CLI/SDK/MCP) should land after this, so the flag is honest the day it ships. - Per-agent default model in Settings → Agents.
- #6802 is superseded — #6851 already fixed the icon-override bug by adding
launchPresetId.
Risks / Rollout
- Risk: a caller passing a model id outside the catalog now gets a hard error instead of a silent fallback. Blast radius is small — no agent model ids are persisted anywhere (not in
packages/db, not in automations), and the only sender today is the desktop picker, which is catalog-bounded. - Rollback: revert;
buildAgentModelArgsdegrade-to-default is unchanged underneath.
Summary by cubic
Adds pinned Claude model releases to the workspace-create model picker and rejects unsupported model overrides before launch. Previously the picker only offered family aliases that track the CLI's newest model, and unknown model IDs were silently dropped, launching the agent on its default with no error.
- The picker now lists Opus 4.8/4.7/4.6/4.5, Sonnet 5/4.6, Fable 5, and Haiku 4.5 alongside the aliases, split into "Latest" and "Pinned releases" sections; Codex gets the same grouping, surfacing retiring models.
- Unsupported or mistyped model IDs now fail before launch with a message naming the supported IDs, and overrides for agents without model support are rejected.
- The default option is visually separated from the first model section, while catalogs without groups still render as flat lists.
Summary by CodeRabbit
-
New Features
- Added support for selecting specific Claude model versions, including pinned releases and latest-model aliases.
- Organized Claude and Codex models into clearer picker sections, including current and retiring models.
- Validated model selections against each agent’s supported options before launch.
- Added clear errors for unsupported models and agents without model-selection support.
-
Bug Fixes
- Prevented invalid or stale model selections from silently falling back to an agent’s default model.