fix(models): update model names to match OpenCode Zen catalog (#1048)

* fix(models): update model names to match OpenCode Zen catalog

OpenCode Zen recently updated their official model catalog, deprecating
several preview and free model variants:

DEPRECATED → NEW (Official Zen Names):
- gemini-3-pro-preview → gemini-3-pro
- gemini-3-flash-preview → gemini-3-flash
- grok-code → gpt-5-nano (FREE tier maintained)
- glm-4.7-free → big-pickle (FREE tier maintained)
- glm-4.6v → glm-4.6

Changes:
- Updated 6 source files (model-requirements, delegate-task, think-mode, etc.)
- Updated 9 documentation files (installation, configurations, features, etc.)
- Updated 14 test files with new model references
- Regenerated snapshots to reflect catalog changes
- Removed duplicate think-mode entries for preview variants

Impact:
- FREE tier access preserved via gpt-5-nano and big-pickle
- All 55 model-related tests passing
- Zero breaking changes - pure string replacement
- Aligns codebase with official OpenCode Zen model catalog

Verified:
- Zero deprecated model names in codebase
- All model-related tests pass (55/55)
- Snapshots regenerated and validated

Affects: 30 files (6 source, 9 docs, 14 tests, 1 snapshot)

* fix(multimodal-looker): update fallback chain with glm-4.6v and gpt-5-nano

- Change glm-4.6 to glm-4.6v for zai-coding-plan provider
- Add opencode/gpt-5-nano as 4th fallback (FREE tier)
- Push gpt-5.2 to 5th position

Fallback chain now:
1. gemini-3-flash (google, github-copilot, opencode)
2. claude-haiku-4-5 (anthropic, github-copilot, opencode)
3. glm-4.6v (zai-coding-plan)
4. gpt-5-nano (opencode) - FREE
5. gpt-5.2 (openai, github-copilot, opencode)

* chore: update bun.lock

---------

Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com>
This commit is contained in:
YeonGyu-Kim
2026-01-24 15:30:35 +09:00
committed by GitHub
parent 58459e692b
commit 04633ba208
31 changed files with 292 additions and 293 deletions
+2 -2
View File
@@ -46,7 +46,7 @@ describe("Agent Config Integration", () => {
const config = {
sisyphus: { model: "anthropic/claude-opus-4-5" },
oracle: { model: "openai/gpt-5.2" },
librarian: { model: "opencode/glm-4.7-free" },
librarian: { model: "opencode/big-pickle" },
}
// #when - migration is applied
@@ -65,7 +65,7 @@ describe("Agent Config Integration", () => {
Sisyphus: { model: "anthropic/claude-opus-4-5" },
oracle: { model: "openai/gpt-5.2" },
"Prometheus (Planner)": { model: "anthropic/claude-opus-4-5" },
librarian: { model: "opencode/glm-4.7-free" },
librarian: { model: "opencode/big-pickle" },
}
// #when - migration is applied
+7 -7
View File
@@ -37,7 +37,7 @@ describe("migrateAgentNames", () => {
const agents = {
oracle: { model: "openai/gpt-5.2" },
librarian: { model: "google/gemini-3-flash" },
explore: { model: "opencode/grok-code" },
explore: { model: "opencode/gpt-5-nano" },
}
// #when: Migrate agent names
@@ -47,7 +47,7 @@ describe("migrateAgentNames", () => {
expect(changed).toBe(false)
expect(migrated["oracle"]).toEqual({ model: "openai/gpt-5.2" })
expect(migrated["librarian"]).toEqual({ model: "google/gemini-3-flash" })
expect(migrated["explore"]).toEqual({ model: "opencode/grok-code" })
expect(migrated["explore"]).toEqual({ model: "opencode/gpt-5-nano" })
})
test("handles case-insensitive migration", () => {
@@ -418,7 +418,7 @@ describe("migrateAgentConfigToCategory", () => {
test("migrates model to category when mapping exists", () => {
// #given: Config with a model that has a category mapping
const config = {
model: "google/gemini-3-pro-preview",
model: "google/gemini-3-pro",
temperature: 0.5,
top_p: 0.9,
}
@@ -467,7 +467,7 @@ describe("migrateAgentConfigToCategory", () => {
test("handles all mapped models correctly", () => {
// #given: Configs for each mapped model
const configs = [
{ model: "google/gemini-3-pro-preview" },
{ model: "google/gemini-3-pro" },
{ model: "openai/gpt-5.2" },
{ model: "anthropic/claude-haiku-4-5" },
{ model: "anthropic/claude-opus-4-5" },
@@ -536,7 +536,7 @@ describe("shouldDeleteAgentConfig", () => {
// #given: Config with fields matching category defaults
const config = {
category: "visual-engineering",
model: "google/gemini-3-pro-preview",
model: "google/gemini-3-pro",
}
// #when: Check if config should be deleted
@@ -664,7 +664,7 @@ describe("migrateConfigFile with backup", () => {
agents: {
"multimodal-looker": { model: "anthropic/claude-haiku-4-5" },
oracle: { model: "openai/gpt-5.2" },
"my-custom-agent": { model: "google/gemini-3-pro-preview" },
"my-custom-agent": { model: "google/gemini-3-pro" },
},
}
@@ -680,7 +680,7 @@ describe("migrateConfigFile with backup", () => {
const agents = rawConfig.agents as Record<string, Record<string, unknown>>
expect(agents["multimodal-looker"].model).toBe("anthropic/claude-haiku-4-5")
expect(agents.oracle.model).toBe("openai/gpt-5.2")
expect(agents["my-custom-agent"].model).toBe("google/gemini-3-pro-preview")
expect(agents["my-custom-agent"].model).toBe("google/gemini-3-pro")
})
test("preserves category setting when explicitly set", () => {
+1 -1
View File
@@ -82,7 +82,7 @@ export const HOOK_NAME_MAP: Record<string, string | null> = {
* This map will be removed in a future major version once migration period ends.
*/
export const MODEL_TO_CATEGORY_MAP: Record<string, string> = {
"google/gemini-3-pro-preview": "visual-engineering",
"google/gemini-3-pro": "visual-engineering",
"openai/gpt-5.2": "ultrabrain",
"anthropic/claude-haiku-4-5": "quick",
"anthropic/claude-opus-4-5": "unspecified-high",
+2 -2
View File
@@ -80,7 +80,7 @@ describe("fetchAvailableModels", () => {
openai: { id: "openai", models: { "gpt-5.2-codex": { id: "gpt-5.2-codex" } } },
anthropic: { id: "anthropic", models: { "claude-sonnet-4-5": { id: "claude-sonnet-4-5" } } },
google: { id: "google", models: { "gemini-3-flash": { id: "gemini-3-flash" } } },
opencode: { id: "opencode", models: { "grok-code": { id: "grok-code" } } },
opencode: { id: "opencode", models: { "gpt-5-nano": { id: "gpt-5-nano" } } },
})
const result = await fetchAvailableModels()
@@ -89,7 +89,7 @@ describe("fetchAvailableModels", () => {
expect(result.has("openai/gpt-5.2-codex")).toBe(true)
expect(result.has("anthropic/claude-sonnet-4-5")).toBe(true)
expect(result.has("google/gemini-3-flash")).toBe(true)
expect(result.has("opencode/grok-code")).toBe(true)
expect(result.has("opencode/gpt-5-nano")).toBe(true)
})
})
+14 -14
View File
@@ -69,19 +69,19 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.model).toBe("claude-haiku-4-5")
})
test("multimodal-looker has valid fallbackChain with gemini-3-flash-preview as primary", () => {
test("multimodal-looker has valid fallbackChain with gemini-3-flash as primary", () => {
// #given - multimodal-looker agent requirement
const multimodalLooker = AGENT_MODEL_REQUIREMENTS["multimodal-looker"]
// #when - accessing multimodal-looker requirement
// #then - fallbackChain exists with gemini-3-flash-preview as first entry
// #then - fallbackChain exists with gemini-3-flash as first entry
expect(multimodalLooker).toBeDefined()
expect(multimodalLooker.fallbackChain).toBeArray()
expect(multimodalLooker.fallbackChain.length).toBeGreaterThan(0)
const primary = multimodalLooker.fallbackChain[0]
expect(primary.providers[0]).toBe("google")
expect(primary.model).toBe("gemini-3-flash-preview")
expect(primary.model).toBe("gemini-3-flash")
})
test("prometheus has valid fallbackChain with claude-opus-4-5 as primary", () => {
@@ -199,19 +199,19 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers[0]).toBe("openai")
})
test("visual-engineering has valid fallbackChain with gemini-3-pro-preview as primary", () => {
test("visual-engineering has valid fallbackChain with gemini-3-pro as primary", () => {
// #given - visual-engineering category requirement
const visualEngineering = CATEGORY_MODEL_REQUIREMENTS["visual-engineering"]
// #when - accessing visual-engineering requirement
// #then - fallbackChain exists with gemini-3-pro-preview as first entry
// #then - fallbackChain exists with gemini-3-pro as first entry
expect(visualEngineering).toBeDefined()
expect(visualEngineering.fallbackChain).toBeArray()
expect(visualEngineering.fallbackChain.length).toBeGreaterThan(0)
const primary = visualEngineering.fallbackChain[0]
expect(primary.providers[0]).toBe("google")
expect(primary.model).toBe("gemini-3-pro-preview")
expect(primary.model).toBe("gemini-3-pro")
})
test("quick has valid fallbackChain with claude-haiku-4-5 as primary", () => {
@@ -260,34 +260,34 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers[0]).toBe("anthropic")
})
test("artistry has valid fallbackChain with gemini-3-pro-preview as primary", () => {
test("artistry has valid fallbackChain with gemini-3-pro as primary", () => {
// #given - artistry category requirement
const artistry = CATEGORY_MODEL_REQUIREMENTS["artistry"]
// #when - accessing artistry requirement
// #then - fallbackChain exists with gemini-3-pro-preview as first entry
// #then - fallbackChain exists with gemini-3-pro as first entry
expect(artistry).toBeDefined()
expect(artistry.fallbackChain).toBeArray()
expect(artistry.fallbackChain.length).toBeGreaterThan(0)
const primary = artistry.fallbackChain[0]
expect(primary.model).toBe("gemini-3-pro-preview")
expect(primary.model).toBe("gemini-3-pro")
expect(primary.variant).toBe("max")
expect(primary.providers[0]).toBe("google")
})
test("writing has valid fallbackChain with gemini-3-flash-preview as primary", () => {
test("writing has valid fallbackChain with gemini-3-flash as primary", () => {
// #given - writing category requirement
const writing = CATEGORY_MODEL_REQUIREMENTS["writing"]
// #when - accessing writing requirement
// #then - fallbackChain exists with gemini-3-flash-preview as first entry
// #then - fallbackChain exists with gemini-3-flash as first entry
expect(writing).toBeDefined()
expect(writing.fallbackChain).toBeArray()
expect(writing.fallbackChain.length).toBeGreaterThan(0)
const primary = writing.fallbackChain[0]
expect(primary.model).toBe("gemini-3-flash-preview")
expect(primary.model).toBe("gemini-3-flash")
expect(primary.providers[0]).toBe("google")
})
@@ -344,7 +344,7 @@ describe("FallbackEntry type", () => {
// #given - a FallbackEntry without variant
const entry: FallbackEntry = {
providers: ["opencode", "anthropic"],
model: "glm-4.7-free",
model: "big-pickle",
}
// #when - accessing variant
@@ -374,7 +374,7 @@ describe("ModelRequirement type", () => {
test("ModelRequirement variant is optional", () => {
// #given - a ModelRequirement without top-level variant
const requirement: ModelRequirement = {
fallbackChain: [{ providers: ["opencode"], model: "glm-4.7-free" }],
fallbackChain: [{ providers: ["opencode"], model: "big-pickle" }],
}
// #when - accessing variant
+24 -23
View File
@@ -15,34 +15,35 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
oracle: {
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: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
],
},
librarian: {
fallbackChain: [
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
{ providers: ["opencode"], model: "glm-4.7-free" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
librarian: {
fallbackChain: [
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
{ providers: ["opencode"], model: "big-pickle" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
],
},
explore: {
fallbackChain: [
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["opencode"], model: "grok-code" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
"multimodal-looker": {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
{ providers: ["opencode"], model: "gpt-5-nano" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
],
},
@@ -50,28 +51,28 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
metis: {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
],
},
momus: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
],
},
atlas: {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
}
@@ -79,7 +80,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
"visual-engineering": {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
],
@@ -88,12 +89,12 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "xhigh" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
artistry: {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "max" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
],
@@ -101,27 +102,27 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
quick: {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash-preview" },
{ providers: ["opencode"], model: "grok-code" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
"unspecified-low": {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2-codex", variant: "medium" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
],
},
"unspecified-high": {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-5", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
],
},
writing: {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash-preview" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
+6 -6
View File
@@ -236,9 +236,9 @@ describe("resolveModelWithFallback", () => {
// #given
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["anthropic", "opencode", "github-copilot"], model: "grok-code" },
{ providers: ["anthropic", "opencode", "github-copilot"], model: "gpt-5-nano" },
],
availableModels: new Set(["opencode/grok-code", "github-copilot/grok-code-preview"]),
availableModels: new Set(["opencode/gpt-5-nano", "github-copilot/gpt-5-nano-preview"]),
systemDefaultModel: "google/gemini-3-pro",
}
@@ -246,7 +246,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// #then
expect(result.model).toBe("opencode/grok-code")
expect(result.model).toBe("opencode/gpt-5-nano")
expect(result.source).toBe("provider-fallback")
})
@@ -392,20 +392,20 @@ describe("resolveModelWithFallback", () => {
test("tries all providers in first entry before moving to second entry", () => {
// #given
const availableModels = new Set(["google/gemini-3-pro-preview"])
const availableModels = new Set(["google/gemini-3-pro"])
// #when
const result = resolveModelWithFallback({
fallbackChain: [
{ providers: ["openai", "anthropic"], model: "gpt-5.2" },
{ providers: ["google"], model: "gemini-3-pro-preview" },
{ providers: ["google"], model: "gemini-3-pro" },
],
availableModels,
systemDefaultModel: "system/default",
})
// #then
expect(result.model).toBe("google/gemini-3-pro-preview")
expect(result.model).toBe("google/gemini-3-pro")
expect(result.source).toBe("provider-fallback")
})