Skip to content

fix(agent): stop Multica picking a default model for the Claude runtime (MUL-5283)#5915

Open
forrestchang wants to merge 1 commit into
mainfrom
agent/lambda/claude-default-model
Open

fix(agent): stop Multica picking a default model for the Claude runtime (MUL-5283)#5915
forrestchang wants to merge 1 commit into
mainfrom
agent/lambda/claude-default-model

Conversation

@forrestchang

Copy link
Copy Markdown
Collaborator

Follows up on review feedback from #5910 (MUL-5282):

默认模型应该跟着 runtime cli 中的配置来走吧?不应该由 multica 来指定

Tracked as MUL-5283. #5910 stays scoped to Opus 5 support; this PR only changes who owns the default model.

The problem

Execution was already correct. When an agent's model is empty the daemon passes "" and Claude Code resolves its own default — Multica does not pin it.

But Default: true was not a display-only flag. ValidateThinkingLevel resolves an empty model to whichever catalog entry carries that flag — the hardcoded claude-sonnet-4-6 — and validates the thinking level against its effort allow-list.

Failure path:

  1. User's local claude CLI is configured to Opus (settings.json / managed policy / ANTHROPIC_MODEL)
  2. In Multica the agent's model is left empty, thinking level xhigh
  3. Daemon guard calls ValidateThinkingLevel(ctx, "claude", path, "", "xhigh")
  4. Empty model → Multica's hardcoded Sonnet 4.6 → Sonnet has no xhighfalse
  5. Daemon logs a warning and drops --effort xhigh

The task still runs on Opus, but at the CLI's own effort — a silent downgrade visible only in daemon.log. The frontend pickModelEntry mirrored the same logic, so the picker wouldn't offer xhigh to those users either.

Why Multica can't know

Claude Code's default comes from the user's own settings files / managed policy / ANTHROPIC_MODEL, and claude-code 2.1.219 has no config subcommand (verified) — there is no headless way to ask which model it would resolve to. Any Default baked into our catalog is a guess.

The codebase was already heading this way: thinking-picker.tsx documents that the "default model" badge was removed because "flagging one option as 'default' was misleading." The badge went; the validation proxy behind it did not.

The change

Follows the pattern opencode already uses in this file (anyModelSupportsThinkingValue):

  • ValidateThinkingLevel — an empty claude model takes the catalog-union path: accept any level an advertised model supports and let the CLI arbitrate. Matches the daemon guard's own stated rule: "if we can't tell, keep the level and let the CLI object."
  • claudeStaticModels() — no entry flagged Default
  • pickLevels (was pickModelEntry) mirrors the backend so the UI never hides a level the daemon would accept

Only widens, never narrows. No combination that validates today starts failing. Tokens outside the catalog (Codex's ultra) still fail closed. Codex is untouched — its empty model still fails closed per MUL-4347.

Open decision

The union path means that if the CLI resolves to Sonnet and the user picked xhigh, we pass --effort xhigh down for the CLI to handle. I could not establish from static analysis whether Claude clamps or hard-errors on a model-unsupported effort (testing live burns quota and wouldn't generalize across CLI versions).

The conservative alternative is codex-style fail-closed, but that strips the effort setting from every empty-model Claude agent — a wider blast radius than the current bug. This PR takes the widening side; happy to flip it.

Verification

  • go test ./pkg/agent/... ./internal/metrics/... — pass
  • pnpm exec vitest run agents/components/inspector/ — 37 pass
  • tsc --noEmit on packages/views, gofmt/go vet/eslint — clean

The two new frontend tests were run against the old implementation first to confirm they actually fail; they aren't passing vacuously.

Merge note

Overlaps #5910 in claudeStaticModels() / models_test.go. If #5910 merges first, rebase this branch and change the defaults assertion in TestClaudeStaticModelsExposesOpus5 to defaults != 0 to match the other two catalog tests.

…me (MUL-5283)

Which model an unset Claude agent runs on is the CLI's call. Execution
already honoured that — an empty agent.model sends "" and Claude Code
resolves its own default — but `Default: true` on claude-sonnet-4-6 leaked
into effort validation, where Multica substituted Sonnet for a model it
cannot know.

Concretely: a user whose claude CLI is configured to Opus, with the model
field left empty and thinking_level `xhigh`, had `--effort xhigh` silently
dropped by the daemon guard (warn-only) because Multica validated against
its own Sonnet guess. The picker wouldn't offer `xhigh` there either.

Claude Code takes its default from settings files / managed policy /
ANTHROPIC_MODEL and 2.1.219 has no `config` subcommand, so there is no
headless way to resolve it. Treat claude like opencode: for an empty model
accept any level the advertised catalog supports and let the CLI arbitrate,
matching the daemon guard's stated fail-open philosophy. This only widens —
nothing that validates today starts failing, and tokens outside the catalog
(e.g. Codex's `ultra`) still fail closed.

- ValidateThinkingLevel: empty claude model takes the catalog-union path.
- claudeStaticModels: no entry flagged Default; the picker already renders
  an unset model as "Default" meaning "whatever the CLI picks", and the
  thinking picker deliberately stopped badging a default row.
- pickLevels (frontend) mirrors the backend so the UI never hides a level
  the daemon would accept.

Codex behaviour is unchanged: its empty model still fails closed (MUL-4347).

Co-authored-by: multica-agent <github@multica.ai>
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
multica-docs Ready Ready Preview, Comment Jul 24, 2026 6:45pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant