refactor(models): bump claude-opus-4-6 to claude-opus-4-7 across fallback chains, categories, and hooks

Updates the canonical Anthropic Opus model in every fallback chain
(sisyphus, oracle, prometheus, metis, momus, visual-engineering,
ultrabrain, deep, artistry, unspecified-high), the unspecified-high
category default, the think-mode HIGH_VARIANT_MAP, the Claude Code
alias map, the claude-thinking legacy alias, the context-limit GA
regex, and event.ts fallback strings.

Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and
claude-*-4-7 so the 1M context cache still applies across the bump.

Regenerates the bundled model-capabilities snapshot from models.dev
and the model-fallback snapshot to match the new source output.
This commit is contained in:
YeonGyu-Kim
2026-04-17 14:51:52 +09:00
parent b1764a880c
commit def44338ff
85 changed files with 39904 additions and 38116 deletions
+43 -43
View File
@@ -65,7 +65,7 @@ describe("fetchAvailableModels", () => {
it("#given cache file with models #when fetchAvailableModels called with connectedProviders #then returns Set of model IDs", async () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
google: { id: "google", models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
@@ -76,7 +76,7 @@ describe("fetchAvailableModels", () => {
expect(result).toBeInstanceOf(Set)
expect(result.size).toBe(3)
expect(result.has("openai/gpt-5.4")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
expect(result.has("google/gemini-3.1-pro")).toBe(true)
})
@@ -145,7 +145,7 @@ describe("fetchAvailableModels", () => {
it("#given cache read twice #when second call made with same providers #then reads fresh each time", async () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
})
const result1 = await fetchAvailableModels(undefined, { connectedProviders: ["openai"] })
@@ -192,7 +192,7 @@ describe("fuzzyMatchModel", () => {
const available = new Set([
"openai/gpt-5.4",
"openai/gpt-5.3-codex",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
])
const result = fuzzyMatchModel("gpt-5.4", available)
expect(result).toBe("openai/gpt-5.4")
@@ -239,25 +239,25 @@ describe("fuzzyMatchModel", () => {
// given available models with claude variants
// when searching for claude-opus
// then return matching claude-opus model
it("should match claude-opus to claude-opus-4-6", () => {
it("should match claude-opus to claude-opus-4-7", () => {
const available = new Set([
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
"anthropic/claude-sonnet-4-6",
])
const result = fuzzyMatchModel("claude-opus", available)
expect(result).toBe("anthropic/claude-opus-4-6")
expect(result).toBe("anthropic/claude-opus-4-7")
})
// given github-copilot serves claude versions with dot notation
// when fallback chain uses hyphen notation in requested model
// then normalize both forms and match github-copilot model
it("should match github-copilot claude-opus-4-6 to claude-opus-4.6", () => {
it("should match github-copilot claude-opus-4-7 to claude-opus-4.7", () => {
const available = new Set([
"github-copilot/claude-opus-4.6",
"github-copilot/claude-opus-4.7",
"opencode/big-pickle",
])
const result = fuzzyMatchModel("claude-opus-4-6", available, ["github-copilot"])
expect(result).toBe("github-copilot/claude-opus-4.6")
const result = fuzzyMatchModel("claude-opus-4-7", available, ["github-copilot"])
expect(result).toBe("github-copilot/claude-opus-4.7")
})
// given claude models can evolve to newer version numbers
@@ -275,7 +275,7 @@ describe("fuzzyMatchModel", () => {
it("should filter by provider when providers array is given", () => {
const available = new Set([
"openai/gpt-5.4",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
"google/gemini-3",
])
const result = fuzzyMatchModel("gpt", available, ["openai"])
@@ -288,7 +288,7 @@ describe("fuzzyMatchModel", () => {
it("should return null when provider filter excludes all matches", () => {
const available = new Set([
"openai/gpt-5.4",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
])
const result = fuzzyMatchModel("claude", available, ["openai"])
expect(result).toBeNull()
@@ -300,7 +300,7 @@ describe("fuzzyMatchModel", () => {
it("should return null when no match found", () => {
const available = new Set([
"openai/gpt-5.4",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
])
const result = fuzzyMatchModel("gemini", available)
expect(result).toBeNull()
@@ -312,7 +312,7 @@ describe("fuzzyMatchModel", () => {
it("should match case-insensitively", () => {
const available = new Set([
"openai/gpt-5.4",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
])
const result = fuzzyMatchModel("GPT-5.4", available)
expect(result).toBe("openai/gpt-5.4")
@@ -323,11 +323,11 @@ describe("fuzzyMatchModel", () => {
// then return exact match first
it("should prioritize exact match over longer variants", () => {
const available = new Set([
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-6-extended",
"anthropic/claude-opus-4-7",
"anthropic/claude-opus-4-7-extended",
])
const result = fuzzyMatchModel("claude-opus-4-6", available)
expect(result).toBe("anthropic/claude-opus-4-6")
const result = fuzzyMatchModel("claude-opus-4-7", available)
expect(result).toBe("anthropic/claude-opus-4-7")
})
// given available models with similar model IDs (e.g., glm-5 and big-pickle)
@@ -372,7 +372,7 @@ describe("fuzzyMatchModel", () => {
it("should search all specified providers", () => {
const available = new Set([
"openai/gpt-5.4",
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
"google/gemini-3",
])
const result = fuzzyMatchModel("gpt", available, ["openai", "google"])
@@ -520,7 +520,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should filter models by connected providers", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
google: { models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
@@ -529,7 +529,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(1)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
expect(result.has("openai/gpt-5.4")).toBe(false)
expect(result.has("google/gemini-3.1-pro")).toBe(false)
})
@@ -540,7 +540,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should filter models by multiple connected providers", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
google: { models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
@@ -549,7 +549,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(2)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
expect(result.has("google/gemini-3.1-pro")).toBe(true)
expect(result.has("openai/gpt-5.4")).toBe(false)
})
@@ -560,7 +560,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return empty set when connectedProviders is empty", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -576,7 +576,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return empty set when connectedProviders not specified", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
})
const result = await fetchAvailableModels()
@@ -605,7 +605,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should return models from providers that exist in both cache and connected list", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -613,7 +613,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
})
expect(result.size).toBe(1)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
})
// given filtered fetch
@@ -622,7 +622,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
it("should not cache filtered results", async () => {
writeModelsCache({
openai: { models: { "gpt-5.4": { id: "gpt-5.4" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
anthropic: { models: { "claude-opus-4-7": { id: "claude-opus-4-7" } } },
})
// First call with anthropic
@@ -706,13 +706,13 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
writeProviderModelsCache({
models: {
opencode: ["big-pickle", "gpt-5-nano"],
anthropic: ["claude-opus-4-6"]
anthropic: ["claude-opus-4-7"]
},
connected: ["opencode", "anthropic"]
})
writeModelsCache({
opencode: { models: { "big-pickle": {}, "gpt-5-nano": {}, "gpt-5.4": {} } },
anthropic: { models: { "claude-opus-4-6": {}, "claude-sonnet-4-6": {} } }
anthropic: { models: { "claude-opus-4-7": {}, "claude-sonnet-4-6": {} } }
})
const result = await fetchAvailableModels(undefined, {
@@ -722,7 +722,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
expect(result.size).toBe(3)
expect(result.has("opencode/big-pickle")).toBe(true)
expect(result.has("opencode/gpt-5-nano")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
expect(result.has("opencode/gpt-5.4")).toBe(false)
expect(result.has("anthropic/claude-sonnet-4-6")).toBe(false)
})
@@ -773,7 +773,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
writeProviderModelsCache({
models: {
opencode: ["big-pickle"],
anthropic: ["claude-opus-4-6"],
anthropic: ["claude-opus-4-7"],
google: ["gemini-3.1-pro"]
},
connected: ["opencode", "anthropic", "google"]
@@ -785,7 +785,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
expect(result.size).toBe(1)
expect(result.has("opencode/big-pickle")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(false)
expect(result.has("anthropic/claude-opus-4-7")).toBe(false)
expect(result.has("google/gemini-3.1-pro")).toBe(false)
})
@@ -812,7 +812,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
it("should handle mixed string[] and object[] formats across providers", async () => {
writeProviderModelsCache({
models: {
anthropic: ["claude-opus-4-6", "claude-sonnet-4-6"],
anthropic: ["claude-opus-4-7", "claude-sonnet-4-6"],
ollama: [
{ id: "ministral-3:14b-32k-agent", provider: "ollama" },
{ id: "qwen3-coder:32k-agent", provider: "ollama" }
@@ -826,7 +826,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
})
expect(result.size).toBe(4)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("anthropic/claude-opus-4-7")).toBe(true)
expect(result.has("anthropic/claude-sonnet-4-6")).toBe(true)
expect(result.has("ollama/ministral-3:14b-32k-agent")).toBe(true)
expect(result.has("ollama/qwen3-coder:32k-agent")).toBe(true)
@@ -859,7 +859,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
describe("isModelAvailable", () => {
it("returns true when model exists via fuzzy match", () => {
// given
const available = new Set(["openai/gpt-5.3-codex", "anthropic/claude-opus-4-6"])
const available = new Set(["openai/gpt-5.3-codex", "anthropic/claude-opus-4-7"])
// when
const result = isModelAvailable("gpt-5.3-codex", available)
@@ -870,7 +870,7 @@ describe("isModelAvailable", () => {
it("returns false when model not found", () => {
// given
const available = new Set(["anthropic/claude-opus-4-6"])
const available = new Set(["anthropic/claude-opus-4-7"])
// when
const result = isModelAvailable("gpt-5.3-codex", available)
@@ -924,7 +924,7 @@ describe("fallback model availability", () => {
it("returns null for completely unknown model", () => {
// given
const available = new Set(["openai/gpt-5.4", "anthropic/claude-opus-4-6"])
const available = new Set(["openai/gpt-5.4", "anthropic/claude-opus-4-7"])
// when
const result = fuzzyMatchModel("non-existent-model-family", available)
@@ -936,7 +936,7 @@ describe("fallback model availability", () => {
it("returns true when models do not match but provider is connected", () => {
// given
const fallbackChain = [{ providers: ["openai"], model: "gpt-5.4" }]
const availableModels = new Set(["anthropic/claude-opus-4-6"])
const availableModels = new Set(["anthropic/claude-opus-4-7"])
writeConnectedProvidersCache(["openai"])
// when
@@ -950,10 +950,10 @@ describe("fallback model availability", () => {
// given
const fallbackChain = [
{ providers: ["openai"], model: "gpt-5.4" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
{ providers: ["anthropic"], model: "claude-opus-4-7" },
]
const availableModels = new Set([
"anthropic/claude-opus-4-6",
"anthropic/claude-opus-4-7",
"openai/gpt-5.4-preview",
])
@@ -968,7 +968,7 @@ describe("fallback model availability", () => {
// given
const fallbackChain = [
{ providers: ["openai"], model: "gpt-5.4" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
{ providers: ["anthropic"], model: "claude-opus-4-7" },
]
const availableModels = new Set(["google/gemini-3.1-pro"])