chore: update claude-opus-4-5 references to claude-opus-4-6 (excludes antigravity models)
This commit is contained in:
@@ -8,9 +8,9 @@ describe("Agent Config Integration", () => {
|
||||
test("migrates old format agent keys to lowercase", () => {
|
||||
// given - config with old format keys
|
||||
const oldConfig = {
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-5" },
|
||||
Atlas: { model: "anthropic/claude-opus-4-5" },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-6" },
|
||||
Atlas: { model: "anthropic/claude-opus-4-6" },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
|
||||
"Metis (Plan Consultant)": { model: "anthropic/claude-sonnet-4-5" },
|
||||
"Momus (Plan Reviewer)": { model: "anthropic/claude-sonnet-4-5" },
|
||||
}
|
||||
@@ -33,9 +33,9 @@ describe("Agent Config Integration", () => {
|
||||
expect(result.migrated).not.toHaveProperty("Momus (Plan Reviewer)")
|
||||
|
||||
// then - values are preserved
|
||||
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(result.migrated.atlas).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
expect(result.migrated.atlas).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
|
||||
// then - changed flag is true
|
||||
expect(result.changed).toBe(true)
|
||||
@@ -44,7 +44,7 @@ describe("Agent Config Integration", () => {
|
||||
test("preserves already lowercase keys", () => {
|
||||
// given - config with lowercase keys
|
||||
const config = {
|
||||
sisyphus: { model: "anthropic/claude-opus-4-5" },
|
||||
sisyphus: { model: "anthropic/claude-opus-4-6" },
|
||||
oracle: { model: "openai/gpt-5.2" },
|
||||
librarian: { model: "opencode/glm-4.7-free" },
|
||||
}
|
||||
@@ -62,9 +62,9 @@ describe("Agent Config Integration", () => {
|
||||
test("handles mixed case config", () => {
|
||||
// given - config with mixed old and new format
|
||||
const mixedConfig = {
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-5" },
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-6" },
|
||||
oracle: { model: "openai/gpt-5.2" },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
|
||||
librarian: { model: "opencode/glm-4.7-free" },
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ describe("Agent Config Integration", () => {
|
||||
test("old config migrates and displays correctly", () => {
|
||||
// given - old format config
|
||||
const oldConfig = {
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-5", temperature: 0.1 },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
|
||||
Sisyphus: { model: "anthropic/claude-opus-4-6", temperature: 0.1 },
|
||||
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-6" },
|
||||
}
|
||||
|
||||
// when - config is migrated
|
||||
@@ -192,15 +192,15 @@ describe("Agent Config Integration", () => {
|
||||
expect(prometheusDisplay).toBe("Prometheus (Plan Builder)")
|
||||
|
||||
// then - config values are preserved
|
||||
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-5", temperature: 0.1 })
|
||||
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(result.migrated.sisyphus).toEqual({ model: "anthropic/claude-opus-4-6", temperature: 0.1 })
|
||||
expect(result.migrated.prometheus).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
})
|
||||
|
||||
test("new config works without migration", () => {
|
||||
// given - new format config (already lowercase)
|
||||
const newConfig = {
|
||||
sisyphus: { model: "anthropic/claude-opus-4-5" },
|
||||
atlas: { model: "anthropic/claude-opus-4-5" },
|
||||
sisyphus: { model: "anthropic/claude-opus-4-6" },
|
||||
atlas: { model: "anthropic/claude-opus-4-6" },
|
||||
}
|
||||
|
||||
// when - migration is applied (should be no-op)
|
||||
|
||||
@@ -84,14 +84,14 @@ describe("applyAgentVariant", () => {
|
||||
|
||||
describe("resolveVariantForModel", () => {
|
||||
test("returns agent override variant when configured", () => {
|
||||
// given - use a model in sisyphus chain (claude-opus-4-5 has default variant "max")
|
||||
// given - use a model in sisyphus chain (claude-opus-4-6 has default variant "max")
|
||||
// to verify override takes precedence over fallback chain
|
||||
const config = {
|
||||
agents: {
|
||||
sisyphus: { variant: "high" },
|
||||
},
|
||||
} as OhMyOpenCodeConfig
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
|
||||
|
||||
// when
|
||||
const variant = resolveVariantForModel(config, "sisyphus", model)
|
||||
@@ -103,7 +103,7 @@ describe("resolveVariantForModel", () => {
|
||||
test("returns correct variant for anthropic provider", () => {
|
||||
// given
|
||||
const config = {} as OhMyOpenCodeConfig
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
|
||||
|
||||
// when
|
||||
const variant = resolveVariantForModel(config, "sisyphus", model)
|
||||
@@ -151,7 +151,7 @@ describe("resolveVariantForModel", () => {
|
||||
test("returns undefined for unknown agent", () => {
|
||||
// given
|
||||
const config = {} as OhMyOpenCodeConfig
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
|
||||
|
||||
// when
|
||||
const variant = resolveVariantForModel(config, "nonexistent-agent", model)
|
||||
@@ -203,7 +203,7 @@ describe("resolveVariantForModel", () => {
|
||||
test("returns correct variant for oracle agent with anthropic", () => {
|
||||
// given
|
||||
const config = {} as OhMyOpenCodeConfig
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-5" }
|
||||
const model = { providerID: "anthropic", modelID: "claude-opus-4-6" }
|
||||
|
||||
// when
|
||||
const variant = resolveVariantForModel(config, "oracle", model)
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("migrateAgentNames", () => {
|
||||
test("migrates legacy OmO names to lowercase", () => {
|
||||
// given: Config with legacy OmO agent names
|
||||
const agents = {
|
||||
omo: { model: "anthropic/claude-opus-4-5" },
|
||||
omo: { model: "anthropic/claude-opus-4-6" },
|
||||
OmO: { temperature: 0.5 },
|
||||
"OmO-Plan": { prompt: "custom prompt" },
|
||||
}
|
||||
@@ -84,7 +84,7 @@ describe("migrateAgentNames", () => {
|
||||
test("migrates orchestrator-sisyphus to atlas", () => {
|
||||
// given: Config with legacy orchestrator-sisyphus agent name
|
||||
const agents = {
|
||||
"orchestrator-sisyphus": { model: "anthropic/claude-opus-4-5" },
|
||||
"orchestrator-sisyphus": { model: "anthropic/claude-opus-4-6" },
|
||||
}
|
||||
|
||||
// when: Migrate agent names
|
||||
@@ -92,14 +92,14 @@ describe("migrateAgentNames", () => {
|
||||
|
||||
// then: orchestrator-sisyphus should be migrated to atlas
|
||||
expect(changed).toBe(true)
|
||||
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
expect(migrated["orchestrator-sisyphus"]).toBeUndefined()
|
||||
})
|
||||
|
||||
test("migrates lowercase atlas to atlas", () => {
|
||||
// given: Config with lowercase atlas agent name
|
||||
const agents = {
|
||||
atlas: { model: "anthropic/claude-opus-4-5" },
|
||||
atlas: { model: "anthropic/claude-opus-4-6" },
|
||||
}
|
||||
|
||||
// when: Migrate agent names
|
||||
@@ -107,7 +107,7 @@ describe("migrateAgentNames", () => {
|
||||
|
||||
// then: lowercase atlas should remain atlas (no change needed)
|
||||
expect(changed).toBe(false)
|
||||
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-5" })
|
||||
expect(migrated["atlas"]).toEqual({ model: "anthropic/claude-opus-4-6" })
|
||||
})
|
||||
|
||||
test("migrates Sisyphus variants to lowercase", () => {
|
||||
@@ -470,7 +470,7 @@ describe("migrateAgentConfigToCategory", () => {
|
||||
{ model: "google/gemini-3-flash" },
|
||||
{ model: "openai/gpt-5.2" },
|
||||
{ model: "anthropic/claude-haiku-4-5" },
|
||||
{ model: "anthropic/claude-opus-4-5" },
|
||||
{ model: "anthropic/claude-opus-4-6" },
|
||||
{ model: "anthropic/claude-sonnet-4-5" },
|
||||
]
|
||||
|
||||
@@ -550,7 +550,7 @@ describe("shouldDeleteAgentConfig", () => {
|
||||
// given: Config with custom model override
|
||||
const config = {
|
||||
category: "visual-engineering",
|
||||
model: "anthropic/claude-opus-4-5",
|
||||
model: "anthropic/claude-opus-4-6",
|
||||
}
|
||||
|
||||
// when: Check if config should be deleted
|
||||
|
||||
@@ -85,7 +85,7 @@ export const MODEL_TO_CATEGORY_MAP: Record<string, string> = {
|
||||
"google/gemini-3-flash": "writing",
|
||||
"openai/gpt-5.2": "ultrabrain",
|
||||
"anthropic/claude-haiku-4-5": "quick",
|
||||
"anthropic/claude-opus-4-5": "unspecified-high",
|
||||
"anthropic/claude-opus-4-6": "unspecified-high",
|
||||
"anthropic/claude-sonnet-4-5": "unspecified-low",
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,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.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { id: "anthropic", models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
google: { id: "google", models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@ describe("fetchAvailableModels", () => {
|
||||
expect(result).toBeInstanceOf(Set)
|
||||
expect(result.size).toBe(3)
|
||||
expect(result.has("openai/gpt-5.2")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
expect(result.has("google/gemini-3-pro")).toBe(true)
|
||||
})
|
||||
|
||||
@@ -113,7 +113,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.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { id: "anthropic", models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
})
|
||||
|
||||
const result1 = await fetchAvailableModels(undefined, { connectedProviders: ["openai"] })
|
||||
@@ -160,7 +160,7 @@ describe("fuzzyMatchModel", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
])
|
||||
const result = fuzzyMatchModel("gpt-5.2", available)
|
||||
expect(result).toBe("openai/gpt-5.2")
|
||||
@@ -207,13 +207,13 @@ 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-5", () => {
|
||||
it("should match claude-opus to claude-opus-4-6", () => {
|
||||
const available = new Set([
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-sonnet-4-5",
|
||||
])
|
||||
const result = fuzzyMatchModel("claude-opus", available)
|
||||
expect(result).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result).toBe("anthropic/claude-opus-4-6")
|
||||
})
|
||||
|
||||
// given available models from multiple providers
|
||||
@@ -222,7 +222,7 @@ describe("fuzzyMatchModel", () => {
|
||||
it("should filter by provider when providers array is given", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"google/gemini-3",
|
||||
])
|
||||
const result = fuzzyMatchModel("gpt", available, ["openai"])
|
||||
@@ -235,7 +235,7 @@ describe("fuzzyMatchModel", () => {
|
||||
it("should return null when provider filter excludes all matches", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
])
|
||||
const result = fuzzyMatchModel("claude", available, ["openai"])
|
||||
expect(result).toBeNull()
|
||||
@@ -247,7 +247,7 @@ describe("fuzzyMatchModel", () => {
|
||||
it("should return null when no match found", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
])
|
||||
const result = fuzzyMatchModel("gemini", available)
|
||||
expect(result).toBeNull()
|
||||
@@ -259,7 +259,7 @@ describe("fuzzyMatchModel", () => {
|
||||
it("should match case-insensitively", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
])
|
||||
const result = fuzzyMatchModel("GPT-5.2", available)
|
||||
expect(result).toBe("openai/gpt-5.2")
|
||||
@@ -270,11 +270,11 @@ describe("fuzzyMatchModel", () => {
|
||||
// then return exact match first
|
||||
it("should prioritize exact match over longer variants", () => {
|
||||
const available = new Set([
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-5-extended",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-6-extended",
|
||||
])
|
||||
const result = fuzzyMatchModel("claude-opus-4-5", available)
|
||||
expect(result).toBe("anthropic/claude-opus-4-5")
|
||||
const result = fuzzyMatchModel("claude-opus-4-6", available)
|
||||
expect(result).toBe("anthropic/claude-opus-4-6")
|
||||
})
|
||||
|
||||
// given available models with similar model IDs (e.g., glm-4.7 and glm-4.7-free)
|
||||
@@ -319,7 +319,7 @@ describe("fuzzyMatchModel", () => {
|
||||
it("should search all specified providers", () => {
|
||||
const available = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"google/gemini-3",
|
||||
])
|
||||
const result = fuzzyMatchModel("gpt", available, ["openai", "google"])
|
||||
@@ -464,7 +464,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
it("should filter models by connected providers", async () => {
|
||||
writeModelsCache({
|
||||
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
|
||||
})
|
||||
|
||||
@@ -473,7 +473,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
})
|
||||
|
||||
expect(result.size).toBe(1)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
expect(result.has("openai/gpt-5.2")).toBe(false)
|
||||
expect(result.has("google/gemini-3-pro")).toBe(false)
|
||||
})
|
||||
@@ -484,7 +484,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
it("should filter models by multiple connected providers", async () => {
|
||||
writeModelsCache({
|
||||
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
|
||||
})
|
||||
|
||||
@@ -493,7 +493,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
})
|
||||
|
||||
expect(result.size).toBe(2)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
expect(result.has("google/gemini-3-pro")).toBe(true)
|
||||
expect(result.has("openai/gpt-5.2")).toBe(false)
|
||||
})
|
||||
@@ -504,7 +504,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
it("should return empty set when connectedProviders is empty", async () => {
|
||||
writeModelsCache({
|
||||
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
})
|
||||
|
||||
const result = await fetchAvailableModels(undefined, {
|
||||
@@ -520,7 +520,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
it("should return empty set when connectedProviders not specified", async () => {
|
||||
writeModelsCache({
|
||||
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
})
|
||||
|
||||
const result = await fetchAvailableModels()
|
||||
@@ -549,7 +549,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.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
})
|
||||
|
||||
const result = await fetchAvailableModels(undefined, {
|
||||
@@ -557,7 +557,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
})
|
||||
|
||||
expect(result.size).toBe(1)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
})
|
||||
|
||||
// given filtered fetch
|
||||
@@ -566,7 +566,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
|
||||
it("should not cache filtered results", async () => {
|
||||
writeModelsCache({
|
||||
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
|
||||
anthropic: { models: { "claude-opus-4-5": { id: "claude-opus-4-5" } } },
|
||||
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
|
||||
})
|
||||
|
||||
// First call with anthropic
|
||||
@@ -641,13 +641,13 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
|
||||
writeProviderModelsCache({
|
||||
models: {
|
||||
opencode: ["glm-4.7-free", "gpt-5-nano"],
|
||||
anthropic: ["claude-opus-4-5"]
|
||||
anthropic: ["claude-opus-4-6"]
|
||||
},
|
||||
connected: ["opencode", "anthropic"]
|
||||
})
|
||||
writeModelsCache({
|
||||
opencode: { models: { "glm-4.7-free": {}, "gpt-5-nano": {}, "gpt-5.2": {} } },
|
||||
anthropic: { models: { "claude-opus-4-5": {}, "claude-sonnet-4-5": {} } }
|
||||
anthropic: { models: { "claude-opus-4-6": {}, "claude-sonnet-4-5": {} } }
|
||||
})
|
||||
|
||||
const result = await fetchAvailableModels(undefined, {
|
||||
@@ -657,7 +657,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
|
||||
expect(result.size).toBe(3)
|
||||
expect(result.has("opencode/glm-4.7-free")).toBe(true)
|
||||
expect(result.has("opencode/gpt-5-nano")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
expect(result.has("opencode/gpt-5.2")).toBe(false)
|
||||
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(false)
|
||||
})
|
||||
@@ -708,7 +708,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
|
||||
writeProviderModelsCache({
|
||||
models: {
|
||||
opencode: ["glm-4.7-free"],
|
||||
anthropic: ["claude-opus-4-5"],
|
||||
anthropic: ["claude-opus-4-6"],
|
||||
google: ["gemini-3-pro"]
|
||||
},
|
||||
connected: ["opencode", "anthropic", "google"]
|
||||
@@ -720,7 +720,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
|
||||
|
||||
expect(result.size).toBe(1)
|
||||
expect(result.has("opencode/glm-4.7-free")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(false)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(false)
|
||||
expect(result.has("google/gemini-3-pro")).toBe(false)
|
||||
})
|
||||
|
||||
@@ -747,7 +747,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-5", "claude-sonnet-4-5"],
|
||||
anthropic: ["claude-opus-4-6", "claude-sonnet-4-5"],
|
||||
ollama: [
|
||||
{ id: "ministral-3:14b-32k-agent", provider: "ollama" },
|
||||
{ id: "qwen3-coder:32k-agent", provider: "ollama" }
|
||||
@@ -761,7 +761,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
|
||||
})
|
||||
|
||||
expect(result.size).toBe(4)
|
||||
expect(result.has("anthropic/claude-opus-4-5")).toBe(true)
|
||||
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
|
||||
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(true)
|
||||
expect(result.has("ollama/ministral-3:14b-32k-agent")).toBe(true)
|
||||
expect(result.has("ollama/qwen3-coder:32k-agent")).toBe(true)
|
||||
@@ -794,7 +794,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-5"])
|
||||
const available = new Set(["openai/gpt-5.3-codex", "anthropic/claude-opus-4-6"])
|
||||
|
||||
// when
|
||||
const result = isModelAvailable("gpt-5.3-codex", available)
|
||||
@@ -805,7 +805,7 @@ describe("isModelAvailable", () => {
|
||||
|
||||
it("returns false when model not found", () => {
|
||||
// given
|
||||
const available = new Set(["anthropic/claude-opus-4-5"])
|
||||
const available = new Set(["anthropic/claude-opus-4-6"])
|
||||
|
||||
// when
|
||||
const result = isModelAvailable("gpt-5.3-codex", available)
|
||||
|
||||
@@ -20,7 +20,7 @@ import { readProviderModelsCache, hasProviderModelsCache, readConnectedProviders
|
||||
* If providers array is given, only models starting with "provider/" are considered.
|
||||
*
|
||||
* @example
|
||||
* const available = new Set(["openai/gpt-5.2", "openai/gpt-5.3-codex", "anthropic/claude-opus-4-5"])
|
||||
* const available = new Set(["openai/gpt-5.2", "openai/gpt-5.3-codex", "anthropic/claude-opus-4-6"])
|
||||
* fuzzyMatchModel("gpt-5.2", available) // → "openai/gpt-5.2"
|
||||
* fuzzyMatchModel("claude", available, ["openai"]) // → null (provider filter excludes anthropic)
|
||||
*/
|
||||
|
||||
@@ -23,12 +23,12 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.variant).toBe("high")
|
||||
})
|
||||
|
||||
test("sisyphus has claude-opus-4-6 as primary before claude-opus-4-5 and requiresAnyModel", () => {
|
||||
test("sisyphus has claude-opus-4-6 as primary before claude-opus-4-6 and requiresAnyModel", () => {
|
||||
// #given - sisyphus agent requirement
|
||||
const sisyphus = AGENT_MODEL_REQUIREMENTS["sisyphus"]
|
||||
|
||||
// #when - accessing Sisyphus requirement
|
||||
// #then - fallbackChain has claude-opus-4-6 first, claude-opus-4-5 second, glm-4.7-free last
|
||||
// #then - fallbackChain has claude-opus-4-6 first, claude-opus-4-6 second, glm-4.7-free last
|
||||
expect(sisyphus).toBeDefined()
|
||||
expect(sisyphus.fallbackChain).toBeArray()
|
||||
expect(sisyphus.fallbackChain).toHaveLength(6)
|
||||
@@ -41,7 +41,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
|
||||
const secondary = sisyphus.fallbackChain[1]
|
||||
expect(secondary.providers[0]).toBe("anthropic")
|
||||
expect(secondary.model).toBe("claude-opus-4-5")
|
||||
expect(secondary.model).toBe("claude-opus-4-6")
|
||||
expect(secondary.variant).toBe("max")
|
||||
|
||||
const last = sisyphus.fallbackChain[5]
|
||||
@@ -103,12 +103,12 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.model).toBe("gemini-3-flash")
|
||||
})
|
||||
|
||||
test("prometheus has claude-opus-4-6 as primary before claude-opus-4-5", () => {
|
||||
test("prometheus has claude-opus-4-6 as primary before claude-opus-4-6", () => {
|
||||
// #given - prometheus agent requirement
|
||||
const prometheus = AGENT_MODEL_REQUIREMENTS["prometheus"]
|
||||
|
||||
// #when - accessing Prometheus requirement
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
|
||||
expect(prometheus).toBeDefined()
|
||||
expect(prometheus.fallbackChain).toBeArray()
|
||||
expect(prometheus.fallbackChain.length).toBeGreaterThan(1)
|
||||
@@ -119,17 +119,17 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.variant).toBe("max")
|
||||
|
||||
const secondary = prometheus.fallbackChain[1]
|
||||
expect(secondary.model).toBe("claude-opus-4-5")
|
||||
expect(secondary.model).toBe("claude-opus-4-6")
|
||||
expect(secondary.providers[0]).toBe("anthropic")
|
||||
expect(secondary.variant).toBe("max")
|
||||
})
|
||||
|
||||
test("metis has claude-opus-4-6 as primary before claude-opus-4-5", () => {
|
||||
test("metis has claude-opus-4-6 as primary before claude-opus-4-6", () => {
|
||||
// #given - metis agent requirement
|
||||
const metis = AGENT_MODEL_REQUIREMENTS["metis"]
|
||||
|
||||
// #when - accessing Metis requirement
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
|
||||
expect(metis).toBeDefined()
|
||||
expect(metis.fallbackChain).toBeArray()
|
||||
expect(metis.fallbackChain.length).toBeGreaterThan(1)
|
||||
@@ -140,7 +140,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.variant).toBe("max")
|
||||
|
||||
const secondary = metis.fallbackChain[1]
|
||||
expect(secondary.model).toBe("claude-opus-4-5")
|
||||
expect(secondary.model).toBe("claude-opus-4-6")
|
||||
expect(secondary.providers[0]).toBe("anthropic")
|
||||
expect(secondary.variant).toBe("max")
|
||||
})
|
||||
@@ -301,12 +301,12 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.providers[0]).toBe("anthropic")
|
||||
})
|
||||
|
||||
test("unspecified-high has claude-opus-4-6 as primary before claude-opus-4-5", () => {
|
||||
test("unspecified-high has claude-opus-4-6 as primary before claude-opus-4-6", () => {
|
||||
// #given - unspecified-high category requirement
|
||||
const unspecifiedHigh = CATEGORY_MODEL_REQUIREMENTS["unspecified-high"]
|
||||
|
||||
// #when - accessing unspecified-high requirement
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-5 is second
|
||||
// #then - claude-opus-4-6 is first, claude-opus-4-6 is second
|
||||
expect(unspecifiedHigh).toBeDefined()
|
||||
expect(unspecifiedHigh.fallbackChain).toBeArray()
|
||||
expect(unspecifiedHigh.fallbackChain.length).toBeGreaterThan(1)
|
||||
@@ -317,7 +317,7 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
|
||||
expect(primary.providers).toEqual(["anthropic"])
|
||||
|
||||
const secondary = unspecifiedHigh.fallbackChain[1]
|
||||
expect(secondary.model).toBe("claude-opus-4-5")
|
||||
expect(secondary.model).toBe("claude-opus-4-6")
|
||||
expect(secondary.variant).toBe("max")
|
||||
expect(secondary.providers[0]).toBe("anthropic")
|
||||
})
|
||||
@@ -392,14 +392,14 @@ describe("FallbackEntry type", () => {
|
||||
// given - a valid FallbackEntry object
|
||||
const entry: FallbackEntry = {
|
||||
providers: ["anthropic", "github-copilot", "opencode"],
|
||||
model: "claude-opus-4-5",
|
||||
model: "claude-opus-4-6",
|
||||
variant: "high",
|
||||
}
|
||||
|
||||
// when - accessing properties
|
||||
// then - all properties are accessible
|
||||
expect(entry.providers).toEqual(["anthropic", "github-copilot", "opencode"])
|
||||
expect(entry.model).toBe("claude-opus-4-5")
|
||||
expect(entry.model).toBe("claude-opus-4-6")
|
||||
expect(entry.variant).toBe("high")
|
||||
})
|
||||
|
||||
@@ -421,7 +421,7 @@ describe("ModelRequirement type", () => {
|
||||
// given - a valid ModelRequirement object
|
||||
const requirement: ModelRequirement = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["openai", "github-copilot"], model: "gpt-5.2", variant: "high" },
|
||||
],
|
||||
}
|
||||
@@ -430,7 +430,7 @@ describe("ModelRequirement type", () => {
|
||||
// then - fallbackChain is accessible with correct structure
|
||||
expect(requirement.fallbackChain).toBeArray()
|
||||
expect(requirement.fallbackChain).toHaveLength(2)
|
||||
expect(requirement.fallbackChain[0].model).toBe("claude-opus-4-5")
|
||||
expect(requirement.fallbackChain[0].model).toBe("claude-opus-4-6")
|
||||
expect(requirement.fallbackChain[1].model).toBe("gpt-5.2")
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
sisyphus: {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
|
||||
@@ -35,7 +35,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
],
|
||||
},
|
||||
librarian: {
|
||||
@@ -66,7 +66,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
prometheus: {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
@@ -76,7 +76,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
metis: {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
@@ -87,7 +87,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
],
|
||||
},
|
||||
@@ -107,7 +107,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
|
||||
],
|
||||
},
|
||||
@@ -116,14 +116,14 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
],
|
||||
},
|
||||
deep: {
|
||||
fallbackChain: [
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
],
|
||||
requiresModel: "gpt-5.3-codex",
|
||||
@@ -132,7 +132,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
|
||||
],
|
||||
requiresModel: "gemini-3-pro",
|
||||
@@ -154,7 +154,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
"unspecified-high": {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@ describe("resolveModel", () => {
|
||||
test("returns userModel when all three are set", () => {
|
||||
// given
|
||||
const input: ModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
inheritedModel: "openai/gpt-5.2",
|
||||
systemDefault: "google/gemini-3-pro",
|
||||
}
|
||||
@@ -17,7 +17,7 @@ describe("resolveModel", () => {
|
||||
const result = resolveModel(input)
|
||||
|
||||
// then
|
||||
expect(result).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result).toBe("anthropic/claude-opus-4-6")
|
||||
})
|
||||
|
||||
test("returns inheritedModel when userModel is undefined", () => {
|
||||
@@ -87,7 +87,7 @@ describe("resolveModel", () => {
|
||||
test("same input returns same output (referential transparency)", () => {
|
||||
// given
|
||||
const input: ModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
inheritedModel: "openai/gpt-5.2",
|
||||
systemDefault: "google/gemini-3-pro",
|
||||
}
|
||||
@@ -118,11 +118,11 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
uiSelectedModel: "opencode/glm-4.7-free",
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
uiSelectedModel: "opencode/glm-4.7-free",
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
uiSelectedModel: " ",
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -165,16 +165,16 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-5" })
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-6" })
|
||||
})
|
||||
|
||||
test("empty string uiSelectedModel falls through to config override", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
uiSelectedModel: "",
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -190,11 +190,11 @@ describe("resolveModelWithFallback", () => {
|
||||
test("returns userModel with override source when userModel is provided", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -202,9 +202,9 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("override")
|
||||
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-5" })
|
||||
expect(logSpy).toHaveBeenCalledWith("Model resolved via config override", { model: "anthropic/claude-opus-4-6" })
|
||||
})
|
||||
|
||||
test("override takes priority even if model not in availableModels", () => {
|
||||
@@ -212,9 +212,9 @@ describe("resolveModelWithFallback", () => {
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "custom/my-model",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -231,9 +231,9 @@ describe("resolveModelWithFallback", () => {
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: " ",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -249,9 +249,9 @@ describe("resolveModelWithFallback", () => {
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -268,9 +268,9 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["github-copilot/claude-opus-4-5-preview", "opencode/claude-opus-4-7"]),
|
||||
availableModels: new Set(["github-copilot/claude-opus-4-6-preview", "opencode/claude-opus-4-7"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -278,12 +278,12 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("github-copilot/claude-opus-4-5-preview")
|
||||
expect(result!.model).toBe("github-copilot/claude-opus-4-6-preview")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
expect(logSpy).toHaveBeenCalledWith("Model resolved via fallback chain (availability confirmed)", {
|
||||
provider: "github-copilot",
|
||||
model: "claude-opus-4-5",
|
||||
match: "github-copilot/claude-opus-4-5-preview",
|
||||
model: "claude-opus-4-6",
|
||||
match: "github-copilot/claude-opus-4-6-preview",
|
||||
variant: undefined,
|
||||
})
|
||||
})
|
||||
@@ -294,7 +294,7 @@ describe("resolveModelWithFallback", () => {
|
||||
fallbackChain: [
|
||||
{ providers: ["openai", "anthropic", "google"], model: "gpt-5.2" },
|
||||
],
|
||||
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-5", "google/gemini-3-pro"]),
|
||||
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6", "google/gemini-3-pro"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ describe("resolveModelWithFallback", () => {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot"], model: "claude-opus" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5", "github-copilot/claude-opus-4-5-preview"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -338,14 +338,14 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
})
|
||||
|
||||
test("skips fallback chain when not provided", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ describe("resolveModelWithFallback", () => {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "CLAUDE-OPUS" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
})
|
||||
|
||||
@@ -476,7 +476,7 @@ describe("resolveModelWithFallback", () => {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "nonexistent-model" },
|
||||
],
|
||||
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null)
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: undefined, // no system default configured
|
||||
@@ -513,7 +513,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai", "google"])
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "openai"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic", "openai"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
@@ -523,7 +523,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then - should use connected provider (openai) from fallback chain
|
||||
expect(result!.model).toBe("openai/claude-opus-4-5")
|
||||
expect(result!.model).toBe("openai/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
cacheSpy.mockRestore()
|
||||
})
|
||||
@@ -556,14 +556,14 @@ describe("resolveModelWithFallback", () => {
|
||||
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
|
||||
],
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: "quotio/claude-opus-4-5-20251101",
|
||||
systemDefaultModel: "quotio/claude-opus-4-6-20251101",
|
||||
}
|
||||
|
||||
// when
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then - no provider in fallback is connected, fall through to system default
|
||||
expect(result!.model).toBe("quotio/claude-opus-4-5-20251101")
|
||||
expect(result!.model).toBe("quotio/claude-opus-4-6-20251101")
|
||||
expect(result!.source).toBe("system-default")
|
||||
cacheSpy.mockRestore()
|
||||
})
|
||||
@@ -573,7 +573,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null)
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
@@ -607,20 +607,20 @@ describe("resolveModelWithFallback", () => {
|
||||
describe("Multi-entry fallbackChain", () => {
|
||||
test("resolves to claude-opus when OpenAI unavailable but Anthropic available (oracle scenario)", () => {
|
||||
// given
|
||||
const availableModels = new Set(["anthropic/claude-opus-4-5"])
|
||||
const availableModels = new Set(["anthropic/claude-opus-4-6"])
|
||||
|
||||
// when
|
||||
const result = resolveModelWithFallback({
|
||||
fallbackChain: [
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
],
|
||||
availableModels,
|
||||
systemDefaultModel: "system/default",
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
})
|
||||
|
||||
@@ -647,14 +647,14 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const availableModels = new Set([
|
||||
"openai/gpt-5.2",
|
||||
"anthropic/claude-opus-4-5",
|
||||
"anthropic/claude-opus-4-6",
|
||||
])
|
||||
|
||||
// when
|
||||
const result = resolveModelWithFallback({
|
||||
fallbackChain: [
|
||||
{ providers: ["openai"], model: "gpt-5.2" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels,
|
||||
systemDefaultModel: "system/default",
|
||||
@@ -673,7 +673,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback({
|
||||
fallbackChain: [
|
||||
{ providers: ["openai"], model: "gpt-5.2" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
{ providers: ["google"], model: "gemini-3-pro" },
|
||||
],
|
||||
availableModels,
|
||||
@@ -690,7 +690,7 @@ describe("resolveModelWithFallback", () => {
|
||||
test("result has correct ModelResolutionResult shape", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: "google/gemini-3-pro",
|
||||
}
|
||||
@@ -713,7 +713,7 @@ describe("resolveModelWithFallback", () => {
|
||||
fallbackChain: [
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
|
||||
],
|
||||
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "anthropic/claude-sonnet-4-5",
|
||||
}
|
||||
|
||||
@@ -749,9 +749,9 @@ describe("resolveModelWithFallback", () => {
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
categoryDefaultModel: "google/gemini-3-pro",
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "system/default",
|
||||
}
|
||||
|
||||
@@ -759,19 +759,19 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then - should fall through to fallbackChain
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
})
|
||||
|
||||
test("userModel takes priority over categoryDefaultModel", () => {
|
||||
// given - both userModel and categoryDefaultModel provided
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
categoryDefaultModel: "google/gemini-3-pro",
|
||||
fallbackChain: [
|
||||
{ providers: ["google"], model: "gemini-3-pro" },
|
||||
],
|
||||
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: "system/default",
|
||||
}
|
||||
|
||||
@@ -779,7 +779,7 @@ describe("resolveModelWithFallback", () => {
|
||||
const result = resolveModelWithFallback(input)
|
||||
|
||||
// then - userModel wins
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("override")
|
||||
})
|
||||
|
||||
@@ -837,7 +837,7 @@ describe("resolveModelWithFallback", () => {
|
||||
test("still returns override when userModel provided even if systemDefaultModel undefined", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
userModel: "anthropic/claude-opus-4-5",
|
||||
userModel: "anthropic/claude-opus-4-6",
|
||||
availableModels: new Set(),
|
||||
systemDefaultModel: undefined,
|
||||
}
|
||||
@@ -847,7 +847,7 @@ describe("resolveModelWithFallback", () => {
|
||||
|
||||
// then
|
||||
expect(result).toBeDefined()
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("override")
|
||||
})
|
||||
|
||||
@@ -855,9 +855,9 @@ describe("resolveModelWithFallback", () => {
|
||||
// given
|
||||
const input: ExtendedModelResolutionInput = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-5" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
],
|
||||
availableModels: new Set(["anthropic/claude-opus-4-5"]),
|
||||
availableModels: new Set(["anthropic/claude-opus-4-6"]),
|
||||
systemDefaultModel: undefined,
|
||||
}
|
||||
|
||||
@@ -866,7 +866,7 @@ describe("resolveModelWithFallback", () => {
|
||||
|
||||
// then
|
||||
expect(result).toBeDefined()
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-5")
|
||||
expect(result!.model).toBe("anthropic/claude-opus-4-6")
|
||||
expect(result!.source).toBe("provider-fallback")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user