fix(models): update Gemini 3 to 3.1 Pro and add Kimi to writing category fallback

Closes #2065

Closes #1968
This commit is contained in:
YeonGyu-Kim
2026-02-26 21:01:05 +09:00
parent c505989ad4
commit d425f9bb80
25 changed files with 242 additions and 239 deletions

View File

@@ -47,8 +47,8 @@ describe("isGptModel", () => {
});
test("gemini models are not gpt", () => {
expect(isGptModel("google/gemini-3-pro")).toBe(false);
expect(isGptModel("litellm/gemini-3-pro")).toBe(false);
expect(isGptModel("google/gemini-3.1-pro")).toBe(false);
expect(isGptModel("litellm/gemini-3.1-pro")).toBe(false);
});
test("opencode provider is not gpt", () => {
@@ -58,29 +58,29 @@ describe("isGptModel", () => {
describe("isGeminiModel", () => {
test("#given google provider models #then returns true", () => {
expect(isGeminiModel("google/gemini-3-pro")).toBe(true);
expect(isGeminiModel("google/gemini-3.1-pro")).toBe(true);
expect(isGeminiModel("google/gemini-3-flash")).toBe(true);
expect(isGeminiModel("google/gemini-2.5-pro")).toBe(true);
});
test("#given google-vertex provider models #then returns true", () => {
expect(isGeminiModel("google-vertex/gemini-3-pro")).toBe(true);
expect(isGeminiModel("google-vertex/gemini-3.1-pro")).toBe(true);
expect(isGeminiModel("google-vertex/gemini-3-flash")).toBe(true);
});
test("#given github copilot gemini models #then returns true", () => {
expect(isGeminiModel("github-copilot/gemini-3-pro")).toBe(true);
expect(isGeminiModel("github-copilot/gemini-3.1-pro")).toBe(true);
expect(isGeminiModel("github-copilot/gemini-3-flash")).toBe(true);
});
test("#given litellm proxied gemini models #then returns true", () => {
expect(isGeminiModel("litellm/gemini-3-pro")).toBe(true);
expect(isGeminiModel("litellm/gemini-3.1-pro")).toBe(true);
expect(isGeminiModel("litellm/gemini-3-flash")).toBe(true);
expect(isGeminiModel("litellm/gemini-2.5-pro")).toBe(true);
});
test("#given other proxied gemini models #then returns true", () => {
expect(isGeminiModel("custom-provider/gemini-3-pro")).toBe(true);
expect(isGeminiModel("custom-provider/gemini-3.1-pro")).toBe(true);
expect(isGeminiModel("ollama/gemini-3-flash")).toBe(true);
});

View File

@@ -988,7 +988,7 @@ describe("buildAgent with category and skills", () => {
const agent = buildAgent(source["test-agent"], TEST_MODEL)
// #then - category's built-in model is applied
expect(agent.model).toBe("google/gemini-3-pro")
expect(agent.model).toBe("google/gemini-3.1-pro")
})
test("agent with category and existing model keeps existing model", () => {

View File

@@ -325,7 +325,7 @@ exports[`generateModelConfig single native provider uses Gemini models when only
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
},
"explore": {
"model": "opencode/gpt-5-nano",
@@ -334,34 +334,34 @@ exports[`generateModelConfig single native provider uses Gemini models when only
"model": "opencode/glm-4.7-free",
},
"metis": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"momus": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
},
"oracle": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"prometheus": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
},
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"quick": {
"model": "google/gemini-3-flash-preview",
},
"ultrabrain": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"unspecified-high": {
@@ -371,7 +371,7 @@ exports[`generateModelConfig single native provider uses Gemini models when only
"model": "google/gemini-3-flash-preview",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -386,7 +386,7 @@ exports[`generateModelConfig single native provider uses Gemini models with isMa
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
},
"explore": {
"model": "opencode/gpt-5-nano",
@@ -395,44 +395,44 @@ exports[`generateModelConfig single native provider uses Gemini models with isMa
"model": "opencode/glm-4.7-free",
},
"metis": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"momus": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
},
"oracle": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"prometheus": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
},
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"quick": {
"model": "google/gemini-3-flash-preview",
},
"ultrabrain": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"unspecified-high": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
},
"unspecified-low": {
"model": "google/gemini-3-flash-preview",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -485,7 +485,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -506,7 +506,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -559,7 +559,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -581,7 +581,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -634,7 +634,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
},
"categories": {
"artistry": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"deep": {
@@ -655,7 +655,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"model": "opencode/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"writing": {
@@ -708,7 +708,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
},
"categories": {
"artistry": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"deep": {
@@ -730,7 +730,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"model": "opencode/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"writing": {
@@ -779,14 +779,14 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
},
"categories": {
"artistry": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"unspecified-high": {
@@ -796,7 +796,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"model": "github-copilot/claude-sonnet-4.5",
},
"visual-engineering": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -845,14 +845,14 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
},
"categories": {
"artistry": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"quick": {
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"unspecified-high": {
@@ -863,7 +863,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"model": "github-copilot/claude-sonnet-4.5",
},
"visual-engineering": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -1026,7 +1026,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
},
"categories": {
"artistry": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"deep": {
@@ -1047,7 +1047,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "opencode/gemini-3-pro",
"model": "opencode/gemini-3.1-pro",
"variant": "high",
},
"writing": {
@@ -1100,7 +1100,7 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
},
"categories": {
"artistry": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -1121,7 +1121,7 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"model": "github-copilot/claude-sonnet-4.5",
},
"visual-engineering": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -1217,7 +1217,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
"model": "google/gemini-3-flash-preview",
},
"oracle": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"prometheus": {
@@ -1231,14 +1231,14 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"quick": {
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"unspecified-high": {
@@ -1248,7 +1248,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -1301,7 +1301,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
},
"categories": {
"artistry": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -1322,7 +1322,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"model": "github-copilot/claude-sonnet-4.5",
},
"visual-engineering": {
"model": "github-copilot/gemini-3-pro-preview",
"model": "github-copilot/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -1375,7 +1375,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -1396,7 +1396,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {
@@ -1449,7 +1449,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
},
"categories": {
"artistry": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"deep": {
@@ -1471,7 +1471,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"model": "anthropic/claude-sonnet-4-5",
},
"visual-engineering": {
"model": "google/gemini-3-pro-preview",
"model": "google/gemini-3.1-pro-preview",
"variant": "high",
},
"writing": {

View File

@@ -178,7 +178,7 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
expect(models).toBeTruthy()
const required = [
"antigravity-gemini-3-pro",
"antigravity-gemini-3.1-pro",
"antigravity-gemini-3-flash",
"antigravity-claude-sonnet-4-6",
"antigravity-claude-sonnet-4-6-thinking",
@@ -206,7 +206,7 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
const models = (ANTIGRAVITY_PROVIDER_CONFIG as any).google.models as Record<string, any>
// #when checking Gemini Pro variants
const pro = models["antigravity-gemini-3-pro"]
const pro = models["antigravity-gemini-3.1-pro"]
// #then should have low and high variants
expect(pro.variants).toBeTruthy()
expect(pro.variants.low).toBeTruthy()

View File

@@ -4,10 +4,10 @@
* IMPORTANT: Model names MUST use `antigravity-` prefix for stability.
*
* Since opencode-antigravity-auth v1.3.0, models use a variant system:
* - `antigravity-gemini-3-pro` with variants: low, high
* - `antigravity-gemini-3.1-pro` with variants: low, high
* - `antigravity-gemini-3-flash` with variants: minimal, low, medium, high
*
* Legacy tier-suffixed names (e.g., `antigravity-gemini-3-pro-high`) still work
* Legacy tier-suffixed names (e.g., `antigravity-gemini-3.1-pro-high`) still work
* but variants are the recommended approach.
*
* @see https://github.com/NoeFabris/opencode-antigravity-auth#models
@@ -16,7 +16,7 @@ export const ANTIGRAVITY_PROVIDER_CONFIG = {
google: {
name: "Google",
models: {
"antigravity-gemini-3-pro": {
"antigravity-gemini-3.1-pro": {
name: "Gemini 3 Pro (Antigravity)",
limit: { context: 1048576, output: 65535 },
modalities: { input: ["text", "image", "pdf"], output: ["text"] },

View File

@@ -26,7 +26,7 @@ describe("model-resolution check", () => {
// then: Should have category entries
const visual = info.categories.find((c) => c.name === "visual-engineering")
expect(visual).toBeDefined()
expect(visual!.requirement.fallbackChain[0]?.model).toBe("gemini-3-pro")
expect(visual!.requirement.fallbackChain[0]?.model).toBe("gemini-3.1-pro")
expect(visual!.requirement.fallbackChain[0]?.providers).toContain("google")
})
})

View File

@@ -24,7 +24,7 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
oracle: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
@@ -59,7 +59,7 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
},
metis: {
@@ -68,14 +68,14 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
},
momus: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
},
atlas: {
@@ -84,7 +84,7 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ 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" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
},
}
@@ -92,7 +92,7 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
"visual-engineering": {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["zai-coding-plan"], model: "glm-5" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
@@ -101,7 +101,7 @@ export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> =
ultrabrain: {
fallbackChain: [
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
@@ -109,17 +109,17 @@ export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> =
fallbackChain: [
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
requiresModel: "gpt-5.3-codex",
},
artistry: {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ 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",
requiresModel: "gemini-3.1-pro",
},
quick: {
fallbackChain: [
@@ -139,7 +139,7 @@ export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> =
fallbackChain: [
{ 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" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
},
writing: {

View File

@@ -40,16 +40,16 @@ describe("transformModelForProvider", () => {
expect(result).toBe("claude-haiku-4.5")
})
test("transforms gemini-3-pro to gemini-3-pro-preview", () => {
// #given github-copilot provider and gemini-3-pro model
test("transforms gemini-3.1-pro to gemini-3.1-pro-preview", () => {
// #given github-copilot provider and gemini-3.1-pro model
const provider = "github-copilot"
const model = "gemini-3-pro"
const model = "gemini-3.1-pro"
// #when transformModelForProvider is called
const result = transformModelForProvider(provider, model)
// #then should transform to gemini-3-pro-preview
expect(result).toBe("gemini-3-pro-preview")
// #then should transform to gemini-3.1-pro-preview
expect(result).toBe("gemini-3.1-pro-preview")
})
test("transforms gemini-3-flash to gemini-3-flash-preview", () => {
@@ -64,16 +64,16 @@ describe("transformModelForProvider", () => {
expect(result).toBe("gemini-3-flash-preview")
})
test("prevents double transformation of gemini-3-pro-preview", () => {
// #given github-copilot provider and gemini-3-pro-preview model (already transformed)
test("prevents double transformation of gemini-3.1-pro-preview", () => {
// #given github-copilot provider and gemini-3.1-pro-preview model (already transformed)
const provider = "github-copilot"
const model = "gemini-3-pro-preview"
const model = "gemini-3.1-pro-preview"
// #when transformModelForProvider is called
const result = transformModelForProvider(provider, model)
// #then should NOT become gemini-3-pro-preview-preview
expect(result).toBe("gemini-3-pro-preview")
// #then should NOT become gemini-3.1-pro-preview-preview
expect(result).toBe("gemini-3.1-pro-preview")
})
test("prevents double transformation of gemini-3-flash-preview", () => {
@@ -102,16 +102,16 @@ describe("transformModelForProvider", () => {
expect(result).toBe("gemini-3-flash-preview")
})
test("transforms gemini-3-pro to gemini-3-pro-preview", () => {
// #given google provider and gemini-3-pro model
test("transforms gemini-3.1-pro to gemini-3.1-pro-preview", () => {
// #given google provider and gemini-3.1-pro model
const provider = "google"
const model = "gemini-3-pro"
const model = "gemini-3.1-pro"
// #when transformModelForProvider is called
const result = transformModelForProvider(provider, model)
// #then should transform to gemini-3-pro-preview
expect(result).toBe("gemini-3-pro-preview")
// #then should transform to gemini-3.1-pro-preview
expect(result).toBe("gemini-3.1-pro-preview")
})
test("passes through other gemini models unchanged", () => {
@@ -138,16 +138,16 @@ describe("transformModelForProvider", () => {
expect(result).toBe("gemini-3-flash-preview")
})
test("prevents double transformation of gemini-3-pro-preview", () => {
// #given google provider and gemini-3-pro-preview model (already transformed)
test("prevents double transformation of gemini-3.1-pro-preview", () => {
// #given google provider and gemini-3.1-pro-preview model (already transformed)
const provider = "google"
const model = "gemini-3-pro-preview"
const model = "gemini-3.1-pro-preview"
// #when transformModelForProvider is called
const result = transformModelForProvider(provider, model)
// #then should NOT become gemini-3-pro-preview-preview
expect(result).toBe("gemini-3-pro-preview")
// #then should NOT become gemini-3.1-pro-preview-preview
expect(result).toBe("gemini-3.1-pro-preview")
})
test("does not transform claude models for google provider", () => {

View File

@@ -34,7 +34,7 @@ describe("ConcurrencyManager.getConcurrencyLimit", () => {
test("should return provider limit even when modelConcurrency exists but doesn't match", () => {
// given
const config: BackgroundTaskConfig = {
modelConcurrency: { "google/gemini-3-pro": 5 },
modelConcurrency: { "google/gemini-3.1-pro": 5 },
providerConcurrency: { anthropic: 3 }
}
const manager = new ConcurrencyManager(config)
@@ -95,7 +95,7 @@ describe("ConcurrencyManager.getConcurrencyLimit", () => {
// when
const modelLimit = manager.getConcurrencyLimit("anthropic/claude-sonnet-4-6")
const providerLimit = manager.getConcurrencyLimit("anthropic/claude-opus-4-6")
const defaultLimit = manager.getConcurrencyLimit("google/gemini-3-pro")
const defaultLimit = manager.getConcurrencyLimit("google/gemini-3.1-pro")
// then
expect(modelLimit).toBe(10)

View File

@@ -162,7 +162,7 @@ describe("TaskToastManager", () => {
description: "Task with category default model",
agent: "sisyphus-junior",
isBackground: false,
modelInfo: { model: "google/gemini-3-pro", type: "category-default" as const },
modelInfo: { model: "google/gemini-3.1-pro", type: "category-default" as const },
}
// when - addTask is called

View File

@@ -125,7 +125,7 @@ describe("runtime-fallback", () => {
await hook.event({
event: {
type: "session.created",
properties: { info: { id: sessionID, model: "google/gemini-3-pro" } },
properties: { info: { id: sessionID, model: "google/gemini-3.1-pro" } },
},
})
@@ -1841,7 +1841,7 @@ describe("runtime-fallback", () => {
test("should apply fallback model on next chat.message after error", async () => {
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
config: createMockConfig({ notify_on_fallback: false }),
pluginConfig: createMockPluginConfigWithCategoryFallback(["openai/gpt-5.2", "google/gemini-3-pro"]),
pluginConfig: createMockPluginConfigWithCategoryFallback(["openai/gpt-5.2", "google/gemini-3.1-pro"]),
})
const sessionID = "test-session-switch"
SessionCategoryRegistry.register(sessionID, "test")
@@ -1916,7 +1916,7 @@ describe("runtime-fallback", () => {
const input = createMockPluginInput()
const hook = createRuntimeFallbackHook(input, {
config: createMockConfig({ notify_on_fallback: false }),
pluginConfig: createMockPluginConfigWithAgentFallback("oracle", ["openai/gpt-5.2", "google/gemini-3-pro"]),
pluginConfig: createMockPluginConfigWithAgentFallback("oracle", ["openai/gpt-5.2", "google/gemini-3.1-pro"]),
})
const sessionID = "test-agent-fallback"

View File

@@ -109,7 +109,7 @@ describe("createThinkModeHook", () => {
const input = createHookInput({
sessionID,
providerID: "google",
modelID: "gemini-3-pro",
modelID: "gemini-3.1-pro",
})
const output = createHookOutput("Please solve this directly")

View File

@@ -49,8 +49,8 @@ describe("think-mode switcher", () => {
it("should handle Gemini preview variants", () => {
// given Gemini preview model IDs
expect(getHighVariant("gemini-3-pro")).toBe(
"gemini-3-pro-high"
expect(getHighVariant("gemini-3.1-pro")).toBe(
"gemini-3-1-pro-high"
)
expect(getHighVariant("gemini-3-flash")).toBe(
"gemini-3-flash-high"
@@ -61,7 +61,7 @@ describe("think-mode switcher", () => {
// given model IDs that are already high variants
expect(getHighVariant("claude-opus-4-6-high")).toBeNull()
expect(getHighVariant("gpt-5-2-high")).toBeNull()
expect(getHighVariant("gemini-3-pro-high")).toBeNull()
expect(getHighVariant("gemini-3-1-pro-high")).toBeNull()
})
it("should return null for unknown models", () => {
@@ -77,7 +77,7 @@ describe("think-mode switcher", () => {
// given model IDs with -high suffix
expect(isAlreadyHighVariant("claude-opus-4-6-high")).toBe(true)
expect(isAlreadyHighVariant("gpt-5-2-high")).toBe(true)
expect(isAlreadyHighVariant("gemini-3-pro-high")).toBe(true)
expect(isAlreadyHighVariant("gemini-3.1-pro-high")).toBe(true)
})
it("should detect -high suffix after normalization", () => {
@@ -90,7 +90,7 @@ describe("think-mode switcher", () => {
expect(isAlreadyHighVariant("claude-opus-4-6")).toBe(false)
expect(isAlreadyHighVariant("claude-opus-4.6")).toBe(false)
expect(isAlreadyHighVariant("gpt-5.2")).toBe(false)
expect(isAlreadyHighVariant("gemini-3-pro")).toBe(false)
expect(isAlreadyHighVariant("gemini-3.1-pro")).toBe(false)
})
it("should return false for models with 'high' in name but not suffix", () => {
@@ -129,7 +129,7 @@ describe("think-mode switcher", () => {
// given various custom prefixes
expect(getHighVariant("azure/gpt-5")).toBe("azure/gpt-5-high")
expect(getHighVariant("bedrock/claude-sonnet-4-6")).toBe("bedrock/claude-sonnet-4-6-high")
expect(getHighVariant("custom-llm/gemini-3-pro")).toBe("custom-llm/gemini-3-pro-high")
expect(getHighVariant("custom-llm/gemini-3.1-pro")).toBe("custom-llm/gemini-3-1-pro-high")
})
it("should return null for prefixed models without high variant mapping", () => {
@@ -150,7 +150,7 @@ describe("think-mode switcher", () => {
// given prefixed model IDs with -high suffix
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-6-high")).toBe(true)
expect(isAlreadyHighVariant("openai/gpt-5-2-high")).toBe(true)
expect(isAlreadyHighVariant("custom/gemini-3-pro-high")).toBe(true)
expect(isAlreadyHighVariant("custom/gemini-3.1-pro-high")).toBe(true)
})
it("should return false for prefixed base models", () => {
@@ -167,4 +167,3 @@ describe("think-mode switcher", () => {
})
})
})

View File

@@ -62,8 +62,8 @@ const HIGH_VARIANT_MAP: Record<string, string> = {
"claude-sonnet-4-6": "claude-sonnet-4-6-high",
"claude-opus-4-6": "claude-opus-4-6-high",
// Gemini
"gemini-3-pro": "gemini-3-pro-high",
"gemini-3-pro-low": "gemini-3-pro-high",
"gemini-3-1-pro": "gemini-3-1-pro-high",
"gemini-3-1-pro-low": "gemini-3-1-pro-high",
"gemini-3-flash": "gemini-3-flash-high",
// GPT-5
"gpt-5": "gpt-5-high",
@@ -82,7 +82,7 @@ const HIGH_VARIANT_MAP: Record<string, string> = {
"gpt-5-2-chat-latest": "gpt-5-2-chat-latest-high",
"gpt-5-2-pro": "gpt-5-2-pro-high",
// Antigravity (Google)
"antigravity-gemini-3-pro": "antigravity-gemini-3-pro-high",
"antigravity-gemini-3-1-pro": "antigravity-gemini-3-1-pro-high",
"antigravity-gemini-3-flash": "antigravity-gemini-3-flash-high",
}
@@ -114,4 +114,3 @@ export function isAlreadyHighVariant(modelID: string): boolean {
return ALREADY_HIGH.has(base) || base.endsWith("-high")
}

View File

@@ -27,7 +27,7 @@ describe("mergeConfigs", () => {
temperature: 0.3,
},
visual: {
model: "google/gemini-3-pro",
model: "google/gemini-3.1-pro",
},
},
} as unknown as OhMyOpenCodeConfig;
@@ -41,7 +41,7 @@ describe("mergeConfigs", () => {
// then quick should be preserved from base
expect(result.categories?.quick?.model).toBe("anthropic/claude-haiku-4-5");
// then visual should be added from override
expect(result.categories?.visual?.model).toBe("google/gemini-3-pro");
expect(result.categories?.visual?.model).toBe("google/gemini-3.1-pro");
});
it("should preserve base categories when override has no categories", () => {

View File

@@ -570,7 +570,7 @@ describe("Prometheus category config resolution", () => {
// then
expect(config).toBeDefined()
expect(config?.model).toBe("google/gemini-3-pro")
expect(config?.model).toBe("google/gemini-3.1-pro")
})
test("user categories override default categories", () => {

View File

@@ -774,7 +774,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",
model: "google/gemini-3.1-pro",
temperature: 0.5,
top_p: 0.9,
}
@@ -823,7 +823,7 @@ describe("migrateAgentConfigToCategory", () => {
test("handles all mapped models correctly", () => {
// given: Configs for each mapped model
const configs = [
{ model: "google/gemini-3-pro" },
{ model: "google/gemini-3.1-pro" },
{ model: "google/gemini-3-flash" },
{ model: "openai/gpt-5.2" },
{ model: "anthropic/claude-haiku-4-5" },
@@ -893,7 +893,7 @@ describe("shouldDeleteAgentConfig", () => {
// given: Config with fields matching category defaults
const config = {
category: "visual-engineering",
model: "google/gemini-3-pro",
model: "google/gemini-3.1-pro",
}
// when: Check if config should be deleted
@@ -1021,7 +1021,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" },
"my-custom-agent": { model: "google/gemini-3.1-pro" },
},
}
@@ -1037,7 +1037,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")
expect(agents["my-custom-agent"].model).toBe("google/gemini-3.1-pro")
})
test("preserves category setting when explicitly set", () => {

View File

@@ -12,7 +12,7 @@
* 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": "visual-engineering",
"google/gemini-3.1-pro": "visual-engineering",
"google/gemini-3-flash": "writing",
"openai/gpt-5.2": "ultrabrain",
"anthropic/claude-haiku-4-5": "quick",

View File

@@ -63,7 +63,7 @@ describe("fetchAvailableModels", () => {
writeModelsCache({
openai: { id: "openai", models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { id: "anthropic", models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { id: "google", models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
google: { id: "google", models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -74,7 +74,7 @@ describe("fetchAvailableModels", () => {
expect(result.size).toBe(3)
expect(result.has("openai/gpt-5.2")).toBe(true)
expect(result.has("anthropic/claude-opus-4-6")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(true)
expect(result.has("google/gemini-3.1-pro")).toBe(true)
})
it("#given connectedProviders unknown #when fetchAvailableModels called without options #then returns empty Set", async () => {
@@ -97,7 +97,7 @@ describe("fetchAvailableModels", () => {
list: async () => ({
data: [
{ id: "gpt-5.3-codex", provider: "openai" },
{ id: "gemini-3-pro", provider: "google" },
{ id: "gemini-3.1-pro", provider: "google" },
],
}),
},
@@ -107,7 +107,7 @@ describe("fetchAvailableModels", () => {
expect(result).toBeInstanceOf(Set)
expect(result.has("openai/gpt-5.3-codex")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(false)
expect(result.has("google/gemini-3.1-pro")).toBe(false)
})
it("#given cache file not found #when fetchAvailableModels called with connectedProviders #then returns empty Set", async () => {
@@ -126,7 +126,7 @@ describe("fetchAvailableModels", () => {
list: async () => ({
data: [
{ id: "gpt-5.3-codex", provider: "openai" },
{ id: "gemini-3-pro", provider: "google" },
{ id: "gemini-3.1-pro", provider: "google" },
],
}),
},
@@ -136,7 +136,7 @@ describe("fetchAvailableModels", () => {
expect(result).toBeInstanceOf(Set)
expect(result.has("openai/gpt-5.3-codex")).toBe(true)
expect(result.has("google/gemini-3-pro")).toBe(true)
expect(result.has("google/gemini-3.1-pro")).toBe(true)
})
it("#given cache read twice #when second call made with same providers #then reads fresh each time", async () => {
@@ -515,7 +515,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
google: { models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -525,7 +525,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("openai/gpt-5.2")).toBe(false)
expect(result.has("google/gemini-3-pro")).toBe(false)
expect(result.has("google/gemini-3.1-pro")).toBe(false)
})
// given cache with multiple providers
@@ -535,7 +535,7 @@ describe("fetchAvailableModels with connected providers filtering", () => {
writeModelsCache({
openai: { models: { "gpt-5.2": { id: "gpt-5.2" } } },
anthropic: { models: { "claude-opus-4-6": { id: "claude-opus-4-6" } } },
google: { models: { "gemini-3-pro": { id: "gemini-3-pro" } } },
google: { models: { "gemini-3.1-pro": { id: "gemini-3.1-pro" } } },
})
const result = await fetchAvailableModels(undefined, {
@@ -544,7 +544,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("google/gemini-3-pro")).toBe(true)
expect(result.has("google/gemini-3.1-pro")).toBe(true)
expect(result.has("openai/gpt-5.2")).toBe(false)
})
@@ -759,7 +759,7 @@ describe("fetchAvailableModels with provider-models cache (whitelist-filtered)",
models: {
opencode: ["big-pickle"],
anthropic: ["claude-opus-4-6"],
google: ["gemini-3-pro"]
google: ["gemini-3.1-pro"]
},
connected: ["opencode", "anthropic", "google"]
})
@@ -771,7 +771,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("google/gemini-3-pro")).toBe(false)
expect(result.has("google/gemini-3.1-pro")).toBe(false)
})
it("should handle object[] format with metadata (Ollama-style)", async () => {
@@ -953,7 +953,7 @@ describe("fallback model availability", () => {
{ providers: ["openai"], model: "gpt-5.2" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
]
const availableModels = new Set(["google/gemini-3-pro"])
const availableModels = new Set(["google/gemini-3.1-pro"])
// when
const result = resolveFirstAvailableFallback(fallbackChain, availableModels)

View File

@@ -248,19 +248,19 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers[0]).toBe("openai")
})
test("visual-engineering has valid fallbackChain with gemini-3-pro high as primary", () => {
test("visual-engineering has valid fallbackChain with gemini-3.1-pro high as primary", () => {
// given - visual-engineering category requirement
const visualEngineering = CATEGORY_MODEL_REQUIREMENTS["visual-engineering"]
// when - accessing visual-engineering requirement
// then - fallbackChain: gemini-3-pro(high) → glm-5 → opus-4-6(max)
// then - fallbackChain: gemini-3.1-pro(high) → glm-5 → opus-4-6(max)
expect(visualEngineering).toBeDefined()
expect(visualEngineering.fallbackChain).toBeArray()
expect(visualEngineering.fallbackChain).toHaveLength(3)
const primary = visualEngineering.fallbackChain[0]
expect(primary.providers[0]).toBe("google")
expect(primary.model).toBe("gemini-3-pro")
expect(primary.model).toBe("gemini-3.1-pro")
expect(primary.variant).toBe("high")
const second = visualEngineering.fallbackChain[1]
@@ -319,39 +319,43 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
expect(primary.providers).toEqual(["anthropic", "github-copilot", "opencode"])
})
test("artistry has valid fallbackChain with gemini-3-pro as primary", () => {
test("artistry has valid fallbackChain with gemini-3.1-pro as primary", () => {
// given - artistry category requirement
const artistry = CATEGORY_MODEL_REQUIREMENTS["artistry"]
// when - accessing artistry requirement
// then - fallbackChain exists with gemini-3-pro as first entry
// then - fallbackChain exists with gemini-3.1-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")
expect(primary.model).toBe("gemini-3.1-pro")
expect(primary.variant).toBe("high")
expect(primary.providers[0]).toBe("google")
})
test("writing has valid fallbackChain with gemini-3-flash as primary", () => {
test("writing has valid fallbackChain with kimi-k2.5-free as primary", () => {
// given - writing category requirement
const writing = CATEGORY_MODEL_REQUIREMENTS["writing"]
// when - accessing writing requirement
// then - fallbackChain: gemini-3-flash claude-sonnet-4-6
// then - fallbackChain: kimi-k2.5-free -> gemini-3-flash -> claude-sonnet-4-6
expect(writing).toBeDefined()
expect(writing.fallbackChain).toBeArray()
expect(writing.fallbackChain).toHaveLength(2)
expect(writing.fallbackChain).toHaveLength(3)
const primary = writing.fallbackChain[0]
expect(primary.model).toBe("gemini-3-flash")
expect(primary.providers[0]).toBe("google")
expect(primary.model).toBe("kimi-k2.5-free")
expect(primary.providers[0]).toBe("opencode")
const second = writing.fallbackChain[1]
expect(second.model).toBe("claude-sonnet-4-6")
expect(second.providers[0]).toBe("anthropic")
expect(second.model).toBe("gemini-3-flash")
expect(second.providers[0]).toBe("google")
const third = writing.fallbackChain[2]
expect(third.model).toBe("claude-sonnet-4-6")
expect(third.providers[0]).toBe("anthropic")
})
test("all 8 categories have valid fallbackChain arrays", () => {
@@ -489,12 +493,12 @@ describe("requiresModel field in categories", () => {
expect(deep.requiresModel).toBe("gpt-5.3-codex")
})
test("artistry category has requiresModel set to gemini-3-pro", () => {
test("artistry category has requiresModel set to gemini-3.1-pro", () => {
// given
const artistry = CATEGORY_MODEL_REQUIREMENTS["artistry"]
// when / #then
expect(artistry.requiresModel).toBe("gemini-3-pro")
expect(artistry.requiresModel).toBe("gemini-3.1-pro")
})
})

View File

@@ -32,7 +32,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
oracle: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
@@ -65,7 +65,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
},
metis: {
@@ -73,14 +73,14 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
},
momus: {
fallbackChain: [
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
},
atlas: {
@@ -95,7 +95,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", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
@@ -103,7 +103,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
ultrabrain: {
fallbackChain: [
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
],
},
@@ -111,17 +111,17 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
requiresModel: "gpt-5.3-codex",
},
artistry: {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
{ 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",
requiresModel: "gemini-3.1-pro",
},
quick: {
fallbackChain: [
@@ -141,11 +141,12 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ 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" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
},
writing: {
fallbackChain: [
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
],

View File

@@ -10,7 +10,7 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: "anthropic/claude-opus-4-6",
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
@@ -25,7 +25,7 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: undefined,
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
@@ -40,14 +40,14 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: undefined,
inheritedModel: undefined,
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
const result = resolveModel(input)
// then
expect(result).toBe("google/gemini-3-pro")
expect(result).toBe("google/gemini-3.1-pro")
})
})
@@ -57,7 +57,7 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: "",
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
@@ -72,14 +72,14 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: " ",
inheritedModel: "",
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
const result = resolveModel(input)
// then
expect(result).toBe("google/gemini-3-pro")
expect(result).toBe("google/gemini-3.1-pro")
})
})
@@ -89,7 +89,7 @@ describe("resolveModel", () => {
const input: ModelResolutionInput = {
userModel: "anthropic/claude-opus-4-6",
inheritedModel: "openai/gpt-5.2",
systemDefault: "google/gemini-3-pro",
systemDefault: "google/gemini-3.1-pro",
}
// when
@@ -123,7 +123,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -141,7 +141,7 @@ describe("resolveModelWithFallback", () => {
uiSelectedModel: "opencode/big-pickle",
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -158,7 +158,7 @@ describe("resolveModelWithFallback", () => {
uiSelectedModel: " ",
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -175,7 +175,7 @@ describe("resolveModelWithFallback", () => {
uiSelectedModel: "",
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -195,7 +195,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "github-copilot"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -215,7 +215,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -234,7 +234,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -252,7 +252,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -271,7 +271,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6" },
],
availableModels: new Set(["github-copilot/claude-opus-4-6-preview", "opencode/claude-opus-4-7"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -294,8 +294,8 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["openai", "anthropic", "google"], model: "gpt-5.2" },
],
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6", "google/gemini-3-pro"]),
systemDefaultModel: "google/gemini-3-pro",
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6", "google/gemini-3.1-pro"]),
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -313,7 +313,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "opencode"], model: "gpt-5-nano" },
],
availableModels: new Set(["opencode/gpt-5-nano"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -331,7 +331,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "github-copilot"], model: "claude-opus" },
],
availableModels: new Set(["anthropic/claude-opus-4-6", "github-copilot/claude-opus-4-6-preview"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -346,7 +346,7 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -361,7 +361,7 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
fallbackChain: [],
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -378,7 +378,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "CLAUDE-OPUS" },
],
availableModels: new Set(["anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -397,7 +397,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-sonnet-4-6" },
],
availableModels: new Set(["opencode/glm-5", "anthropic/claude-sonnet-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -420,7 +420,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["zai-coding-plan"], model: "glm-5" },
],
availableModels: new Set(["zai-coding-plan/glm-5", "opencode/glm-5"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -438,7 +438,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["zai-coding-plan"], model: "glm-5", variant: "high" },
],
availableModels: new Set(["opencode/glm-5"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -457,7 +457,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-sonnet-4-6" },
],
availableModels: new Set(["anthropic/claude-sonnet-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -477,14 +477,14 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "nonexistent-model" },
],
availableModels: new Set(["openai/gpt-5.2", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.model).toBe("google/gemini-3.1-pro")
expect(result!.source).toBe("system-default")
expect(logSpy).toHaveBeenCalledWith("No available model found in fallback chain, falling through to system default")
})
@@ -516,7 +516,7 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic", "openai"], model: "claude-opus-4-6" },
],
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -533,7 +533,7 @@ describe("resolveModelWithFallback", () => {
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["github-copilot"])
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
availableModels: new Set(),
systemDefaultModel: "anthropic/claude-sonnet-4-6",
@@ -544,7 +544,7 @@ describe("resolveModelWithFallback", () => {
// then - should use github-copilot (second provider) since google not connected
// model name is transformed to preview variant for github-copilot provider
expect(result!.model).toBe("github-copilot/gemini-3-pro-preview")
expect(result!.model).toBe("github-copilot/gemini-3.1-pro-preview")
expect(result!.source).toBe("provider-fallback")
cacheSpy.mockRestore()
})
@@ -577,14 +577,14 @@ describe("resolveModelWithFallback", () => {
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
const result = resolveModelWithFallback(input)
// then - should fall through to system default
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.model).toBe("google/gemini-3.1-pro")
expect(result!.source).toBe("system-default")
cacheSpy.mockRestore()
})
@@ -593,14 +593,14 @@ describe("resolveModelWithFallback", () => {
// given
const input: ExtendedModelResolutionInput = {
availableModels: new Set(["openai/gpt-5.2"]),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
const result = resolveModelWithFallback(input)
// then
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.model).toBe("google/gemini-3.1-pro")
expect(result!.source).toBe("system-default")
})
})
@@ -627,20 +627,20 @@ describe("resolveModelWithFallback", () => {
test("tries all providers in first entry before moving to second entry", () => {
// given
const availableModels = new Set(["google/gemini-3-pro"])
const availableModels = new Set(["google/gemini-3.1-pro"])
// when
const result = resolveModelWithFallback({
fallbackChain: [
{ providers: ["openai", "anthropic"], model: "gpt-5.2" },
{ providers: ["google"], model: "gemini-3-pro" },
{ providers: ["google"], model: "gemini-3.1-pro" },
],
availableModels,
systemDefaultModel: "system/default",
})
// then
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.model).toBe("google/gemini-3.1-pro")
expect(result!.source).toBe("provider-fallback")
})
@@ -675,7 +675,7 @@ describe("resolveModelWithFallback", () => {
fallbackChain: [
{ providers: ["openai"], model: "gpt-5.2" },
{ providers: ["anthropic"], model: "claude-opus-4-6" },
{ providers: ["google"], model: "gemini-3-pro" },
{ providers: ["google"], model: "gemini-3.1-pro" },
],
availableModels,
systemDefaultModel: "system/default",
@@ -693,7 +693,7 @@ describe("resolveModelWithFallback", () => {
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-6",
availableModels: new Set(),
systemDefaultModel: "google/gemini-3-pro",
systemDefaultModel: "google/gemini-3.1-pro",
}
// when
@@ -708,32 +708,32 @@ describe("resolveModelWithFallback", () => {
describe("categoryDefaultModel (fuzzy matching for category defaults)", () => {
test("applies fuzzy matching to categoryDefaultModel when userModel not provided", () => {
// given - gemini-3-pro is the category default, but only gemini-3-pro-preview is available
// given - gemini-3.1-pro is the category default, but only gemini-3.1-pro-preview is available
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
categoryDefaultModel: "google/gemini-3.1-pro",
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
availableModels: new Set(["google/gemini-3.1-pro-preview", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "anthropic/claude-sonnet-4-6",
}
// when
const result = resolveModelWithFallback(input)
// then - should fuzzy match gemini-3-pro → gemini-3-pro-preview
expect(result!.model).toBe("google/gemini-3-pro-preview")
// then - should fuzzy match gemini-3.1-pro → gemini-3.1-pro-preview
expect(result!.model).toBe("google/gemini-3.1-pro-preview")
expect(result!.source).toBe("category-default")
})
test("categoryDefaultModel uses exact match when available", () => {
// given - exact match exists
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
categoryDefaultModel: "google/gemini-3.1-pro",
fallbackChain: [
{ providers: ["google"], model: "gemini-3-pro" },
{ providers: ["google"], model: "gemini-3.1-pro" },
],
availableModels: new Set(["google/gemini-3-pro", "google/gemini-3-pro-preview"]),
availableModels: new Set(["google/gemini-3.1-pro", "google/gemini-3.1-pro-preview"]),
systemDefaultModel: "anthropic/claude-sonnet-4-6",
}
@@ -741,14 +741,14 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - should use exact match
expect(result!.model).toBe("google/gemini-3-pro")
expect(result!.model).toBe("google/gemini-3.1-pro")
expect(result!.source).toBe("category-default")
})
test("categoryDefaultModel falls through to fallbackChain when no match in availableModels", () => {
// given - categoryDefaultModel has no match, but fallbackChain does
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
categoryDefaultModel: "google/gemini-3.1-pro",
fallbackChain: [
{ providers: ["anthropic"], model: "claude-opus-4-6" },
],
@@ -768,11 +768,11 @@ describe("resolveModelWithFallback", () => {
// given - both userModel and categoryDefaultModel provided
const input: ExtendedModelResolutionInput = {
userModel: "anthropic/claude-opus-4-6",
categoryDefaultModel: "google/gemini-3-pro",
categoryDefaultModel: "google/gemini-3.1-pro",
fallbackChain: [
{ providers: ["google"], model: "gemini-3-pro" },
{ providers: ["google"], model: "gemini-3.1-pro" },
],
availableModels: new Set(["google/gemini-3-pro-preview", "anthropic/claude-opus-4-6"]),
availableModels: new Set(["google/gemini-3.1-pro-preview", "anthropic/claude-opus-4-6"]),
systemDefaultModel: "system/default",
}
@@ -788,7 +788,7 @@ describe("resolveModelWithFallback", () => {
// given - no availableModels but connected provider cache exists
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["google"])
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro",
categoryDefaultModel: "google/gemini-3.1-pro",
availableModels: new Set(),
systemDefaultModel: "anthropic/claude-sonnet-4-6",
}
@@ -797,7 +797,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - should use transformed categoryDefaultModel since google is connected
expect(result!.model).toBe("google/gemini-3-pro-preview")
expect(result!.model).toBe("google/gemini-3.1-pro-preview")
expect(result!.source).toBe("category-default")
cacheSpy.mockRestore()
})
@@ -824,7 +824,7 @@ describe("resolveModelWithFallback", () => {
// given - category default already has -preview suffix
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["google"])
const input: ExtendedModelResolutionInput = {
categoryDefaultModel: "google/gemini-3-pro-preview",
categoryDefaultModel: "google/gemini-3.1-pro-preview",
availableModels: new Set(),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
}
@@ -832,18 +832,18 @@ describe("resolveModelWithFallback", () => {
// when
const result = resolveModelWithFallback(input)
// then - should NOT become gemini-3-pro-preview-preview
expect(result!.model).toBe("google/gemini-3-pro-preview")
// then - should NOT become gemini-3.1-pro-preview-preview
expect(result!.model).toBe("google/gemini-3.1-pro-preview")
expect(result!.source).toBe("category-default")
cacheSpy.mockRestore()
})
test("transforms gemini-3-pro in fallback chain for google connected provider", () => {
// given - google connected, fallback chain has gemini-3-pro
test("transforms gemini-3.1-pro in fallback chain for google connected provider", () => {
// given - google connected, fallback chain has gemini-3.1-pro
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["google"])
const input: ExtendedModelResolutionInput = {
fallbackChain: [
{ providers: ["google", "github-copilot"], model: "gemini-3-pro" },
{ providers: ["google", "github-copilot"], model: "gemini-3.1-pro" },
],
availableModels: new Set(),
systemDefaultModel: "anthropic/claude-sonnet-4-5",
@@ -853,7 +853,7 @@ describe("resolveModelWithFallback", () => {
const result = resolveModelWithFallback(input)
// then - should transform to preview variant for google provider
expect(result!.model).toBe("google/gemini-3-pro-preview")
expect(result!.model).toBe("google/gemini-3.1-pro-preview")
expect(result!.source).toBe("provider-fallback")
cacheSpy.mockRestore()
})

View File

@@ -6,12 +6,12 @@ export function transformModelForProvider(provider: string, model: string): stri
.replace("claude-sonnet-4-5", "claude-sonnet-4.5")
.replace("claude-haiku-4-5", "claude-haiku-4.5")
.replace("claude-sonnet-4", "claude-sonnet-4")
.replace(/gemini-3-pro(?!-)/g, "gemini-3-pro-preview")
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
}
if (provider === "google") {
return model
.replace(/gemini-3-pro(?!-)/g, "gemini-3-pro-preview")
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
}
return model

View File

@@ -208,10 +208,10 @@ You are NOT an interactive assistant. You are an autonomous problem-solver.
export const DEFAULT_CATEGORIES: Record<string, CategoryConfig> = {
"visual-engineering": { model: "google/gemini-3-pro", variant: "high" },
"visual-engineering": { model: "google/gemini-3.1-pro", variant: "high" },
ultrabrain: { model: "openai/gpt-5.3-codex", variant: "xhigh" },
deep: { model: "openai/gpt-5.3-codex", variant: "medium" },
artistry: { model: "google/gemini-3-pro", variant: "high" },
artistry: { model: "google/gemini-3.1-pro", variant: "high" },
quick: { model: "anthropic/claude-haiku-4-5" },
"unspecified-low": { model: "anthropic/claude-sonnet-4-6" },
"unspecified-high": { model: "anthropic/claude-opus-4-6", variant: "max" },

View File

@@ -17,7 +17,7 @@ const TEST_AVAILABLE_MODELS = new Set([
"anthropic/claude-opus-4-6",
"anthropic/claude-sonnet-4-6",
"anthropic/claude-haiku-4-5",
"google/gemini-3-pro",
"google/gemini-3.1-pro",
"google/gemini-3-flash",
"openai/gpt-5.2",
"openai/gpt-5.3-codex",
@@ -52,7 +52,7 @@ describe("sisyphus-task", () => {
providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue({
models: {
anthropic: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"],
google: ["gemini-3-pro", "gemini-3-flash"],
google: ["gemini-3.1-pro", "gemini-3-flash"],
openai: ["gpt-5.2", "gpt-5.3-codex"],
},
connected: ["anthropic", "google", "openai"],
@@ -73,7 +73,7 @@ describe("sisyphus-task", () => {
// when / #then
expect(category).toBeDefined()
expect(category.model).toBe("google/gemini-3-pro")
expect(category.model).toBe("google/gemini-3.1-pro")
expect(category.variant).toBe("high")
})
@@ -781,7 +781,7 @@ describe("sisyphus-task", () => {
// then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("google/gemini-3-pro")
expect(result!.config.model).toBe("google/gemini-3.1-pro")
expect(result!.promptAppend).toContain("VISUAL/UI")
})
@@ -805,7 +805,7 @@ describe("sisyphus-task", () => {
const categoryName = "visual-engineering"
const userCategories = {
"visual-engineering": {
model: "google/gemini-3-pro",
model: "google/gemini-3.1-pro",
prompt_append: "Custom instructions here",
},
}
@@ -845,7 +845,7 @@ describe("sisyphus-task", () => {
const categoryName = "visual-engineering"
const userCategories = {
"visual-engineering": {
model: "google/gemini-3-pro",
model: "google/gemini-3.1-pro",
temperature: 0.3,
},
}
@@ -868,7 +868,7 @@ describe("sisyphus-task", () => {
// then - category's built-in model wins over inheritedModel
expect(result).not.toBeNull()
expect(result!.config.model).toBe("google/gemini-3-pro")
expect(result!.config.model).toBe("google/gemini-3.1-pro")
})
test("systemDefaultModel is used as fallback when custom category has no model", () => {
@@ -910,7 +910,7 @@ describe("sisyphus-task", () => {
// then
expect(result).not.toBeNull()
expect(result!.config.model).toBe("google/gemini-3-pro")
expect(result!.config.model).toBe("google/gemini-3.1-pro")
})
})
@@ -1738,7 +1738,7 @@ describe("sisyphus-task", () => {
const mockClient = {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
model: { list: async () => [{ provider: "google", id: "gemini-3-pro" }] },
model: { list: async () => [{ provider: "google", id: "gemini-3.1-pro" }] },
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_unstable_gemini" } }),
@@ -2001,7 +2001,7 @@ describe("sisyphus-task", () => {
const mockClient = {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
model: { list: async () => [{ provider: "google", id: "gemini-3-pro" }] },
model: { list: async () => [{ provider: "google", id: "gemini-3.1-pro" }] },
session: {
get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "ses_artistry_gemini" } }),
@@ -2028,7 +2028,7 @@ describe("sisyphus-task", () => {
abort: new AbortController().signal,
}
// when - artistry category (gemini-3-pro with high variant)
// when - artistry category (gemini-3.1-pro with high variant)
const result = await tool.execute(
{
description: "Test artistry forced background",
@@ -3026,9 +3026,9 @@ describe("sisyphus-task", () => {
// when resolveCategoryConfig is called
const resolved = resolveCategoryConfig(categoryName, { userCategories, inheritedModel, systemDefaultModel: SYSTEM_DEFAULT_MODEL })
// then should use category's built-in model (gemini-3-pro for visual-engineering)
// then should use category's built-in model (gemini-3.1-pro for visual-engineering)
expect(resolved).not.toBeNull()
expect(resolved!.model).toBe("google/gemini-3-pro")
expect(resolved!.model).toBe("google/gemini-3.1-pro")
})
test("systemDefaultModel is used when no other model is available", () => {
@@ -3522,7 +3522,7 @@ describe("sisyphus-task", () => {
)
// then - should resolve via AGENT_MODEL_REQUIREMENTS fallback chain for oracle
// oracle fallback chain: gpt-5.2 (openai) > gemini-3-pro (google) > claude-opus-4-6 (anthropic)
// oracle fallback chain: gpt-5.2 (openai) > gemini-3.1-pro (google) > claude-opus-4-6 (anthropic)
// Since openai is in connectedProviders, should resolve to openai/gpt-5.2
expect(promptBody.model).toBeDefined()
expect(promptBody.model.providerID).toBe("openai")