From a6e4f211a316ce06f20511f909a918533fe15874 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 12 Apr 2026 02:28:27 +0900 Subject: [PATCH] fix(shared): normalize claude model IDs for anthropic provider (#3290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anthropic API accepts claude-opus-4.6 (dot format) but not claude-opus-4-6 (dash format). Added anthropic case to transformModelForProvider to normalize dash-format model IDs before they reach the provider. Updated model config snapshots and test expectations to match the new dot-format output. 🤖 Generated with OhMyOpenCode assistance https://github.com/code-yeongyu/oh-my-opencode --- src/agents/utils.test.ts | 4 +- .../__snapshots__/model-fallback.test.ts.snap | 309 ++++++++++-------- src/cli/model-fallback.test.ts | 12 +- src/cli/provider-model-id-transform.test.ts | 38 +++ src/features/background-agent/manager.test.ts | 54 +-- src/shared/provider-model-id-transform.ts | 6 + .../delegate-task/model-selection.test.ts | 45 +-- 7 files changed, 279 insertions(+), 189 deletions(-) diff --git a/src/agents/utils.test.ts b/src/agents/utils.test.ts index 161ad3a7b..b63a34827 100644 --- a/src/agents/utils.test.ts +++ b/src/agents/utils.test.ts @@ -180,7 +180,7 @@ describe("createBuiltinAgents with model overrides", () => { // #then expect(agents.sisyphus).toBeDefined() - expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6") + expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4.6") } finally { cacheSpy.mockRestore() fetchSpy.mockRestore() @@ -918,7 +918,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => { // #then expect(agents.sisyphus).toBeDefined() - expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-6") + expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4.6") } finally { cacheSpy.mockRestore() fetchSpy.mockRestore() diff --git a/src/cli/__snapshots__/model-fallback.test.ts.snap b/src/cli/__snapshots__/model-fallback.test.ts.snap index e6dee4dc8..3e1b2f50c 100644 --- a/src/cli/__snapshots__/model-fallback.test.ts.snap +++ b/src/cli/__snapshots__/model-fallback.test.ts.snap @@ -69,62 +69,67 @@ exports[`generateModelConfig single native provider uses Claude models when only "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { "atlas": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "metis": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "multimodal-looker": { "model": "opencode/gpt-5-nano", }, "oracle": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "prometheus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "deep": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "quick": { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, "ultrabrain": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-high": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "writing": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, } @@ -135,63 +140,68 @@ exports[`generateModelConfig single native provider uses Claude models with isMa "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { "atlas": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "metis": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "multimodal-looker": { "model": "opencode/gpt-5-nano", }, "oracle": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "prometheus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "deep": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "quick": { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, "ultrabrain": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-high": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-low": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "writing": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, } @@ -516,9 +526,14 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { @@ -532,13 +547,13 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -565,7 +580,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -582,7 +597,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "model": "google/gemini-3.1-pro-preview", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -592,7 +607,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "medium", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { @@ -602,14 +617,14 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "artistry": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -622,7 +637,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "deep": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -636,7 +651,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "quick": { "fallback_models": [ { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "google/gemini-3-flash-preview", @@ -651,7 +666,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -668,7 +683,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { "fallback_models": [ @@ -680,12 +695,12 @@ exports[`generateModelConfig all native providers uses preferred models from fal "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -695,7 +710,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal "writing": { "fallback_models": [ { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, ], "model": "google/gemini-3-flash-preview", @@ -715,9 +730,14 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "hephaestus": { @@ -731,13 +751,13 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -764,7 +784,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -781,7 +801,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "model": "google/gemini-3.1-pro-preview", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -791,7 +811,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "medium", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { @@ -801,14 +821,14 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "artistry": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -821,7 +841,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "deep": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -835,7 +855,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "quick": { "fallback_models": [ { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "google/gemini-3-flash-preview", @@ -850,7 +870,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -864,7 +884,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-low": { @@ -877,12 +897,12 @@ exports[`generateModelConfig all native providers uses preferred models with isM "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -892,7 +912,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM "writing": { "fallback_models": [ { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, ], "model": "google/gemini-3-flash-preview", @@ -1865,13 +1885,16 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { "fallback_models": [ { "model": "opencode/minimax-m2.7", }, + { + "model": "anthropic/claude-haiku-4.5", + }, { "model": "opencode/claude-haiku-4-5", }, @@ -1896,13 +1919,13 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -1933,7 +1956,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -1958,7 +1981,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/gemini-3.1-pro", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -1981,7 +2004,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { @@ -1997,14 +2020,14 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "artistry": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -2021,7 +2044,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "deep": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -2039,7 +2062,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "quick": { "fallback_models": [ { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "opencode/claude-haiku-4-5", @@ -2060,7 +2083,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -2084,7 +2107,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/gemini-3-flash", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { "fallback_models": [ @@ -2099,7 +2122,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/gemini-3-flash", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ @@ -2107,7 +2130,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "model": "opencode/glm-5", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -2121,7 +2144,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen "writing": { "fallback_models": [ { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, { "model": "opencode/claude-sonnet-4-6", @@ -2389,36 +2412,41 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { "atlas": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "librarian": { "fallback_models": [ { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, ], "model": "zai-coding-plan/glm-4.7", }, "metis": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "multimodal-looker": { "model": "zai-coding-plan/glm-4.6v", }, "oracle": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "prometheus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -2427,42 +2455,42 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat "model": "zai-coding-plan/glm-5", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "deep": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "quick": { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, "ultrabrain": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-high": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], "model": "zai-coding-plan/glm-5", }, "writing": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, } @@ -2473,13 +2501,18 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json", "agents": { "atlas": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { + "fallback_models": [ + { + "model": "anthropic/claude-haiku-4.5", + }, + ], "model": "anthropic/claude-haiku-4-5", }, "metis": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { @@ -2489,7 +2522,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "multimodal-looker": { @@ -2498,7 +2531,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "oracle": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -2511,22 +2544,22 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "model": "google/gemini-3.1-pro-preview", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { "artistry": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -2540,7 +2573,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "quick": { @@ -2549,12 +2582,12 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, "ultrabrain": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -2567,7 +2600,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { "fallback_models": [ @@ -2575,12 +2608,12 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "model": "google/gemini-3-flash-preview", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, ], @@ -2590,7 +2623,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi "writing": { "fallback_models": [ { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, ], "model": "google/gemini-3-flash-preview", @@ -3015,7 +3048,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { "fallback_models": [ @@ -3025,6 +3058,9 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe { "model": "opencode/minimax-m2.7", }, + { + "model": "anthropic/claude-haiku-4.5", + }, { "model": "opencode/claude-haiku-4-5", }, @@ -3054,7 +3090,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/minimax-m2.7-highspeed", }, { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "opencode/claude-haiku-4-5", @@ -3088,7 +3124,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { @@ -3102,7 +3138,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "xhigh", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3174,7 +3210,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3221,7 +3257,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/gemini-3.1-pro", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -3259,7 +3295,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { @@ -3286,7 +3322,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { @@ -3301,7 +3337,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3336,7 +3372,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "medium", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3372,7 +3408,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/gpt-5.4-mini", }, { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "github-copilot/claude-haiku-4.5", @@ -3414,7 +3450,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3455,7 +3491,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/gemini-3-flash", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "unspecified-low": { "fallback_models": [ @@ -3483,7 +3519,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/gemini-3-flash", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ @@ -3502,7 +3538,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/glm-5", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3526,7 +3562,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe "model": "opencode/gemini-3-flash", }, { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, { "model": "github-copilot/claude-sonnet-4.6", @@ -3566,7 +3602,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "medium", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "explore": { "fallback_models": [ @@ -3576,6 +3612,9 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is { "model": "opencode/minimax-m2.7", }, + { + "model": "anthropic/claude-haiku-4.5", + }, { "model": "opencode/claude-haiku-4-5", }, @@ -3605,7 +3644,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/minimax-m2.7-highspeed", }, { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "opencode/claude-haiku-4-5", @@ -3639,7 +3678,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "high", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "momus": { @@ -3653,7 +3692,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "xhigh", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3725,7 +3764,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3772,7 +3811,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/gemini-3.1-pro", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus": { @@ -3810,7 +3849,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "sisyphus-junior": { @@ -3837,7 +3876,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/big-pickle", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, }, "categories": { @@ -3852,7 +3891,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3887,7 +3926,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "medium", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -3923,7 +3962,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/gpt-5.4-mini", }, { - "model": "anthropic/claude-haiku-4-5", + "model": "anthropic/claude-haiku-4.5", }, { "model": "github-copilot/claude-haiku-4.5", @@ -3965,7 +4004,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "variant": "high", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -4012,7 +4051,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/kimi-k2.5", }, ], - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, "unspecified-low": { @@ -4041,7 +4080,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/gemini-3-flash", }, ], - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, "visual-engineering": { "fallback_models": [ @@ -4060,7 +4099,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/glm-5", }, { - "model": "anthropic/claude-opus-4-6", + "model": "anthropic/claude-opus-4.6", "variant": "max", }, { @@ -4084,7 +4123,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is "model": "opencode/gemini-3-flash", }, { - "model": "anthropic/claude-sonnet-4-6", + "model": "anthropic/claude-sonnet-4.6", }, { "model": "github-copilot/claude-sonnet-4.6", diff --git a/src/cli/model-fallback.test.ts b/src/cli/model-fallback.test.ts index 57ff2c16e..09d002bcb 100644 --- a/src/cli/model-fallback.test.ts +++ b/src/cli/model-fallback.test.ts @@ -1,3 +1,5 @@ +/// + import { describe, expect, test } from "bun:test" import { generateModelConfig } from "./model-fallback" @@ -378,7 +380,7 @@ describe("generateModelConfig", () => { const result = generateModelConfig(config) // #then - expect(result.agents?.sisyphus?.model).toBe("anthropic/claude-opus-4-6") + expect(result.agents?.sisyphus?.model).toBe("anthropic/claude-opus-4.6") }) test("Sisyphus is created when multiple fallback providers are available", () => { @@ -395,7 +397,7 @@ describe("generateModelConfig", () => { const result = generateModelConfig(config) // #then - expect(result.agents?.sisyphus?.model).toBe("anthropic/claude-opus-4-6") + expect(result.agents?.sisyphus?.model).toBe("anthropic/claude-opus-4.6") }) test("Sisyphus resolves to gpt-5.4 medium when only OpenAI is available", () => { @@ -572,7 +574,11 @@ describe("generateModelConfig", () => { // #then explore should not have fallback_models (only one chain entry matches) expect(result.agents?.explore?.model).toBe("anthropic/claude-haiku-4-5") - expect(result.agents?.explore?.fallback_models).toBeUndefined() + expect(result.agents?.explore?.fallback_models).toEqual([ + { + model: "anthropic/claude-haiku-4.5", + }, + ]) }) test("librarian includes fallback_models when opencode-go and Claude are both available", () => { diff --git a/src/cli/provider-model-id-transform.test.ts b/src/cli/provider-model-id-transform.test.ts index 17cb9dfb1..ddfdd6e2f 100644 --- a/src/cli/provider-model-id-transform.test.ts +++ b/src/cli/provider-model-id-transform.test.ts @@ -163,6 +163,44 @@ describe("transformModelForProvider", () => { }) }) + describe("anthropic provider", () => { + test("transforms claude-opus-4-6 to claude-opus-4.6", () => { + // #given anthropic provider and claude-opus-4-6 model + const provider = "anthropic" + const model = "claude-opus-4-6" + + // #when transformModelForProvider is called + const result = transformModelForProvider(provider, model) + + // #then should transform to claude-opus-4.6 + expect(result).toBe("claude-opus-4.6") + }) + + test("transforms claude-sonnet-4-6 to claude-sonnet-4.6", () => { + // #given anthropic provider and claude-sonnet-4-6 model + const provider = "anthropic" + const model = "claude-sonnet-4-6" + + // #when transformModelForProvider is called + const result = transformModelForProvider(provider, model) + + // #then should transform to claude-sonnet-4.6 + expect(result).toBe("claude-sonnet-4.6") + }) + + test("transforms claude-haiku-4-5 to claude-haiku-4.5", () => { + // #given anthropic provider and claude-haiku-4-5 model + const provider = "anthropic" + const model = "claude-haiku-4-5" + + // #when transformModelForProvider is called + const result = transformModelForProvider(provider, model) + + // #then should transform to claude-haiku-4.5 + expect(result).toBe("claude-haiku-4.5") + }) + }) + describe("unknown provider", () => { test("passes model through unchanged for unknown provider", () => { // #given unknown provider and any model diff --git a/src/features/background-agent/manager.test.ts b/src/features/background-agent/manager.test.ts index 86c139132..7ad6fea39 100644 --- a/src/features/background-agent/manager.test.ts +++ b/src/features/background-agent/manager.test.ts @@ -855,13 +855,13 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () => { info: { agent: "sisyphus", - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, }, }, { info: { agent: "compaction", - model: { providerID: "anthropic", modelID: "claude-sonnet-4-6" }, + model: { providerID: "anthropic", modelID: "claude-sonnet-4.6" }, }, }, ], @@ -890,7 +890,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () => //#then expect(capturedBody?.agent).toBe("sisyphus") - expect(capturedBody?.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" }) + expect(capturedBody?.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4.6" }) manager.shutdown() }) @@ -913,7 +913,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () => } const currentMessage: CurrentMessage = { agent: "sisyphus", - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } // when @@ -921,7 +921,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () => // then - uses currentMessage values, not task.parentModel/parentAgent expect(promptBody.agent).toBe("sisyphus") - expect(promptBody.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" }) + expect(promptBody.model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4.6" }) }) test("should fallback to parentAgent when currentMessage.agent is undefined", async () => { @@ -1155,7 +1155,7 @@ describe("BackgroundManager.notifyParentSession - notifications toggle", () => { agent: "explore", model: { providerID: "anthropic", - modelID: "claude-opus-4-6", + modelID: "claude-opus-4.6", variant: "high", }, }, @@ -1211,7 +1211,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => { agent: "explore", model: { providerID: "anthropic", - modelID: "claude-opus-4-6", + modelID: "claude-opus-4.6", variant: "max", }, }, @@ -1231,7 +1231,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => { status: "completed", startedAt: new Date(), completedAt: new Date(), - model: { providerID: "anthropic", modelID: "claude-opus-4-6", variant: "high" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6", variant: "high" }, } getPendingByParent(manager).set("session-parent", new Set([task.id])) @@ -1272,7 +1272,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => { status: "completed", startedAt: new Date(), completedAt: new Date(), - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } getPendingByParent(manager).set("session-parent", new Set([task.id])) @@ -1349,7 +1349,7 @@ describe("BackgroundManager.tryCompleteTask", () => { test("should release concurrency and clear key on completion", async () => { // given - const concurrencyKey = "anthropic/claude-opus-4-6" + const concurrencyKey = "anthropic/claude-opus-4.6" const concurrencyManager = getConcurrencyManager(manager) await concurrencyManager.acquire(concurrencyKey) @@ -1378,7 +1378,7 @@ describe("BackgroundManager.tryCompleteTask", () => { test("should prevent double completion and double release", async () => { // given - const concurrencyKey = "anthropic/claude-opus-4-6" + const concurrencyKey = "anthropic/claude-opus-4.6" const concurrencyManager = getConcurrencyManager(manager) await concurrencyManager.acquire(concurrencyKey) @@ -1508,7 +1508,7 @@ describe("BackgroundManager.tryCompleteTask", () => { test("should release task concurrencyKey when startTask throws after assigning it", async () => { // given - const concurrencyKey = "anthropic/claude-opus-4-6" + const concurrencyKey = "anthropic/claude-opus-4.6" const concurrencyManager = getConcurrencyManager(manager) const task = createMockTask({ @@ -1524,7 +1524,7 @@ describe("BackgroundManager.tryCompleteTask", () => { agent: task.agent, parentSessionID: task.parentSessionID, parentMessageID: task.parentMessageID, - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } getTaskMap(manager).set(task.id, task) getQueuesByKey(manager).set(concurrencyKey, [{ task, input }]) @@ -1544,7 +1544,7 @@ describe("BackgroundManager.tryCompleteTask", () => { test("should mark task as error when startTask throws after session creation", async () => { //#given - startTask creates session but fails before sending prompt - const concurrencyKey = "anthropic/claude-opus-4-6" + const concurrencyKey = "anthropic/claude-opus-4.6" const task = createMockTask({ id: "task-zombie-session", @@ -1561,7 +1561,7 @@ describe("BackgroundManager.tryCompleteTask", () => { agent: task.agent, parentSessionID: task.parentSessionID, parentMessageID: task.parentMessageID, - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } getTaskMap(manager).set(task.id, task) getQueuesByKey(manager).set(concurrencyKey, [{ task, input }]) @@ -1585,7 +1585,7 @@ describe("BackgroundManager.tryCompleteTask", () => { test("should release queue slot when queued task is already interrupt", async () => { // given - const concurrencyKey = "anthropic/claude-opus-4-6" + const concurrencyKey = "anthropic/claude-opus-4.6" const concurrencyManager = getConcurrencyManager(manager) const task = createMockTask({ @@ -1601,7 +1601,7 @@ describe("BackgroundManager.tryCompleteTask", () => { agent: task.agent, parentSessionID: task.parentSessionID, parentMessageID: task.parentMessageID, - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } getTaskMap(manager).set(task.id, task) getQueuesByKey(manager).set(concurrencyKey, [{ task, input }]) @@ -2104,7 +2104,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => { agent: "test-agent", parentSessionID: "parent-session", parentMessageID: "parent-message", - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, } const launchInputWithoutModel = { description: "Test task without model", @@ -2124,7 +2124,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => { expect(taskWithModel.status).toBe("pending") expect(taskWithoutModel.status).toBe("pending") expect(promptBodies).toHaveLength(2) - expect(promptBodies[0].model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4-6" }) + expect(promptBodies[0].model).toEqual({ providerID: "anthropic", modelID: "claude-opus-4.6" }) expect(promptBodies[0].agent).toBe("test-agent") expect(promptBodies[1].agent).toBe("test-agent") expect("model" in promptBodies[1]).toBe(false) @@ -3250,7 +3250,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => { description: "Task 1", prompt: "Do something", agent: "test-agent", - model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6" }, parentSessionID: "parent-session", parentMessageID: "parent-message", } @@ -4254,7 +4254,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { agent: "sisyphus", status: "running", concurrencyKey: input.concurrencyKey, - model: { providerID: "anthropic", modelID: "claude-opus-4-6-thinking" }, + model: { providerID: "anthropic", modelID: "claude-opus-4.6-thinking" }, fallbackChain: input.fallbackChain ?? defaultRetryFallbackChain, attemptCount: 0, }) @@ -4399,7 +4399,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { //#given const manager = createBackgroundManager() const concurrencyManager = getConcurrencyManager(manager) - const concurrencyKey = "anthropic/claude-opus-4-6-thinking" + const concurrencyKey = "anthropic/claude-opus-4.6-thinking" await concurrencyManager.acquire(concurrencyKey) stubProcessKey(manager) @@ -4425,7 +4425,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { name: "UnknownError", data: { message: - "Bad Gateway: {\"error\":{\"message\":\"unknown provider for model claude-opus-4-6-thinking\"}}", + "Bad Gateway: {\"error\":{\"message\":\"unknown provider for model claude-opus-4.6-thinking\"}}", }, }, }, @@ -4436,7 +4436,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { expect(task.attemptCount).toBe(1) expect(task.model).toEqual({ providerID: "anthropic", - modelID: "claude-opus-4-6", + modelID: "claude-opus-4.6", variant: "max", }) expect(task.concurrencyKey).toBeUndefined() @@ -4474,7 +4474,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { expect(task.attemptCount).toBe(1) expect(task.model).toEqual({ providerID: "anthropic", - modelID: "claude-opus-4-6", + modelID: "claude-opus-4.6", variant: "max", }) @@ -4502,7 +4502,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { name: "UnknownError", data: { message: - "Bad Gateway: {\"error\":{\"message\":\"unknown provider for model claude-opus-4-6-thinking\"}}", + "Bad Gateway: {\"error\":{\"message\":\"unknown provider for model claude-opus-4.6-thinking\"}}", }, }, } @@ -4519,7 +4519,7 @@ describe("BackgroundManager.handleEvent - session.error", () => { expect(task.attemptCount).toBe(1) expect(task.model).toEqual({ providerID: "anthropic", - modelID: "claude-opus-4-6", + modelID: "claude-opus-4.6", variant: "max", }) diff --git a/src/shared/provider-model-id-transform.ts b/src/shared/provider-model-id-transform.ts index 0cf8eb801..38fdf869d 100644 --- a/src/shared/provider-model-id-transform.ts +++ b/src/shared/provider-model-id-transform.ts @@ -14,5 +14,11 @@ export function transformModelForProvider(provider: string, model: string): stri .replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview") .replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview") } + if (provider === "anthropic") { + return model + .replace("claude-opus-4-6", "claude-opus-4.6") + .replace("claude-sonnet-4-6", "claude-sonnet-4.6") + .replace("claude-haiku-4-5", "claude-haiku-4.5") + } return model } diff --git a/src/tools/delegate-task/model-selection.test.ts b/src/tools/delegate-task/model-selection.test.ts index 6350646dc..cd1f8b6e7 100644 --- a/src/tools/delegate-task/model-selection.test.ts +++ b/src/tools/delegate-task/model-selection.test.ts @@ -1,5 +1,6 @@ -declare const require: (name: string) => any -const { afterEach, beforeEach, describe, expect, mock, spyOn, test } = require("bun:test") +/// + +import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test" import { resolveModelForDelegateTask } from "./model-selection" import * as connectedProvidersCache from "../../shared/connected-providers-cache" @@ -25,12 +26,12 @@ describe("resolveModelForDelegateTask", () => { describe("#when availableModels is empty and no user model override", () => { test("#then returns skipped sentinel to leave model unpinned", () => { const result = resolveModelForDelegateTask({ - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], availableModels: new Set(), - systemDefaultModel: "anthropic/claude-sonnet-4-6", + systemDefaultModel: "anthropic/claude-sonnet-4.6", }) expect(result).toEqual({ skipped: true }) @@ -41,12 +42,12 @@ describe("resolveModelForDelegateTask", () => { test("#then returns the user model regardless of cache state", () => { const result = resolveModelForDelegateTask({ userModel: "openai/gpt-5.4", - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], availableModels: new Set(), - systemDefaultModel: "anthropic/claude-sonnet-4-6", + systemDefaultModel: "anthropic/claude-sonnet-4.6", }) expect(result).toEqual({ model: "openai/gpt-5.4" }) @@ -57,7 +58,7 @@ describe("resolveModelForDelegateTask", () => { test("#then returns skipped sentinel (skip fallback resolution without cache)", () => { const result = resolveModelForDelegateTask({ userFallbackModels: ["openai/gpt-5.4", "google/gemini-3.1-pro"], - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], @@ -80,15 +81,15 @@ describe("resolveModelForDelegateTask", () => { const readConnectedProvidersSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["anthropic"]) const result = resolveModelForDelegateTask({ - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], availableModels: new Set(), - systemDefaultModel: "anthropic/claude-sonnet-4-6", + systemDefaultModel: "anthropic/claude-sonnet-4.6", }) - expect(result).toEqual({ model: "anthropic/claude-sonnet-4-6" }) + expect(result).toEqual({ model: "anthropic/claude-sonnet-4.6" }) readConnectedProvidersSpy.mockRestore() }) @@ -96,12 +97,12 @@ describe("resolveModelForDelegateTask", () => { const readConnectedProvidersSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"]) const result = resolveModelForDelegateTask({ - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["openai"], model: "gpt-5.4", variant: "high" }, ], availableModels: new Set(), - systemDefaultModel: "anthropic/claude-sonnet-4-6", + systemDefaultModel: "anthropic/claude-sonnet-4.6", }) expect(result).toEqual({ @@ -117,7 +118,7 @@ describe("resolveModelForDelegateTask", () => { const readConnectedProvidersSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"]) const result = resolveModelForDelegateTask({ - userFallbackModels: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"], + userFallbackModels: ["anthropic/claude-sonnet-4.6", "openai/gpt-5.4"], availableModels: new Set(), }) @@ -129,14 +130,14 @@ describe("resolveModelForDelegateTask", () => { describe("#when availableModels has entries and category default matches", () => { test("#then resolves via fuzzy match (existing behavior)", () => { const result = resolveModelForDelegateTask({ - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], - availableModels: new Set(["anthropic/claude-sonnet-4-6"]), + availableModels: new Set(["anthropic/claude-sonnet-4.6"]), }) - expect(result).toEqual({ model: "anthropic/claude-sonnet-4-6" }) + expect(result).toEqual({ model: "anthropic/claude-sonnet-4.6" }) }) test("#then trusts user-configured category model without fuzzy validation", () => { @@ -200,7 +201,7 @@ describe("resolveModelForDelegateTask", () => { expect(result).toBeDefined() expect(result).not.toHaveProperty("skipped") const resolved = result as { model: string; variant?: string } - expect(resolved.model).toBe("anthropic/claude-haiku-4-5") + expect(resolved.model).toBe("anthropic/claude-haiku-4.5") }) test("#then resolves first provider in entry that is connected", () => { @@ -229,10 +230,10 @@ describe("resolveModelForDelegateTask", () => { { providers: ["opencode-go"], model: "minimax-m2.7" }, ], availableModels: new Set(), - systemDefaultModel: "anthropic/claude-sonnet-4-6", + systemDefaultModel: "anthropic/claude-sonnet-4.6", }) - expect(result).toEqual({ model: "anthropic/claude-sonnet-4-6" }) + expect(result).toEqual({ model: "anthropic/claude-sonnet-4.6" }) }) }) @@ -259,7 +260,7 @@ describe("resolveModelForDelegateTask", () => { test("#then extracts the variant and returns the base model separately", () => { const result = resolveModelForDelegateTask({ userModel: "openai/gpt-5.4 high", - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["anthropic"], model: "claude-sonnet-4-6" }, ], @@ -274,7 +275,7 @@ describe("resolveModelForDelegateTask", () => { test("#then extracts the variant and returns the base model separately", () => { const result = resolveModelForDelegateTask({ userModel: "openai/gpt-5.4(max)", - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", availableModels: new Set(), }) @@ -359,7 +360,7 @@ describe("resolveModelForDelegateTask", () => { const readConnectedProvidersSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"]) const result = resolveModelForDelegateTask({ - categoryDefaultModel: "anthropic/claude-sonnet-4-6", + categoryDefaultModel: "anthropic/claude-sonnet-4.6", fallbackChain: [ { providers: ["openai"], model: "gpt-5.4" }, ],