fix(model-fallback): align OpenAI fallback resolution across CLI and runtime

Keep install-time and runtime model tables in sync, stop OpenAI-only misrouting when OpenCode Go is present, and add valid OpenAI fallbacks for atlas, metis, and sisyphus-junior.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-14 13:46:26 +09:00
parent b63082a3bb
commit 532995bb51
11 changed files with 276 additions and 355 deletions
@@ -69,7 +69,7 @@ exports[`generateModelConfig single native provider uses Claude models when only
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -110,17 +110,17 @@ exports[`generateModelConfig single native provider uses Claude models when only
"variant": "max",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "anthropic/claude-opus-4-6",
"variant": "max",
},
"writing": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
},
}
@@ -131,7 +131,7 @@ exports[`generateModelConfig single native provider uses Claude models with isMa
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -176,14 +176,14 @@ exports[`generateModelConfig single native provider uses Claude models with isMa
"variant": "max",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "anthropic/claude-opus-4-6",
"variant": "max",
},
"writing": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
},
}
@@ -194,7 +194,8 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "medium",
},
"explore": {
"model": "openai/gpt-5.4",
@@ -209,7 +210,8 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"variant": "medium",
},
"metis": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "high",
},
"momus": {
"model": "openai/gpt-5.4",
@@ -232,7 +234,8 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"variant": "medium",
},
"sisyphus-junior": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "medium",
},
},
"categories": {
@@ -249,7 +252,7 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"variant": "low",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
@@ -277,7 +280,8 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "medium",
},
"explore": {
"model": "openai/gpt-5.4",
@@ -292,7 +296,8 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"variant": "medium",
},
"metis": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "high",
},
"momus": {
"model": "openai/gpt-5.4",
@@ -315,7 +320,8 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"variant": "medium",
},
"sisyphus-junior": {
"model": "opencode/glm-4.7-free",
"model": "openai/gpt-5.4",
"variant": "medium",
},
},
"categories": {
@@ -332,7 +338,7 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"variant": "low",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
@@ -480,7 +486,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -530,14 +536,14 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "google/gemini-3.1-pro-preview",
@@ -555,7 +561,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -605,15 +611,15 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "openai/gpt-5.4",
"variant": "high",
"model": "anthropic/claude-opus-4-6",
"variant": "max",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "google/gemini-3.1-pro-preview",
@@ -631,7 +637,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/claude-sonnet-4-5",
"model": "opencode/claude-sonnet-4-6",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -681,14 +687,14 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"model": "opencode/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.3-codex",
"model": "opencode/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "opencode/claude-sonnet-4-5",
"model": "opencode/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "opencode/claude-sonnet-4-5",
"model": "opencode/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "opencode/gemini-3.1-pro",
@@ -706,7 +712,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/claude-sonnet-4-5",
"model": "opencode/claude-sonnet-4-6",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -756,15 +762,15 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"model": "opencode/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.3-codex",
"model": "opencode/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "opencode/gpt-5.4",
"variant": "high",
"model": "opencode/claude-opus-4-6",
"variant": "max",
},
"unspecified-low": {
"model": "opencode/claude-sonnet-4-5",
"model": "opencode/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "opencode/gemini-3.1-pro",
@@ -782,11 +788,15 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"explore": {
"model": "github-copilot/gpt-5-mini",
},
"hephaestus": {
"model": "github-copilot/gpt-5.4",
"variant": "medium",
},
"metis": {
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
@@ -796,7 +806,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"variant": "xhigh",
},
"multimodal-looker": {
"model": "opencode/glm-4.7-free",
"model": "github-copilot/gpt-5-nano",
},
"oracle": {
"model": "github-copilot/gpt-5.4",
@@ -827,10 +837,10 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
"variant": "high",
},
"unspecified-high": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"unspecified-low": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"visual-engineering": {
"model": "github-copilot/gemini-3.1-pro-preview",
@@ -848,11 +858,15 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"explore": {
"model": "github-copilot/gpt-5-mini",
},
"hephaestus": {
"model": "github-copilot/gpt-5.4",
"variant": "medium",
},
"metis": {
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
@@ -862,7 +876,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"variant": "xhigh",
},
"multimodal-looker": {
"model": "opencode/glm-4.7-free",
"model": "github-copilot/gpt-5-nano",
},
"oracle": {
"model": "github-copilot/gpt-5.4",
@@ -893,11 +907,11 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
"variant": "high",
},
"unspecified-high": {
"model": "github-copilot/gpt-5.4",
"variant": "high",
"model": "github-copilot/claude-opus-4.6",
"variant": "max",
},
"unspecified-low": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"visual-engineering": {
"model": "github-copilot/gemini-3.1-pro-preview",
@@ -1031,7 +1045,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1081,14 +1095,14 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "opencode/gpt-5.3-codex",
"model": "opencode/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "opencode/gemini-3.1-pro",
@@ -1106,7 +1120,7 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"explore": {
"model": "github-copilot/gpt-5-mini",
@@ -1156,14 +1170,14 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"unspecified-low": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"visual-engineering": {
"model": "github-copilot/gemini-3.1-pro-preview",
@@ -1181,7 +1195,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1225,16 +1239,16 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat
"variant": "max",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "zai-coding-plan/glm-5",
},
"writing": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
},
}
@@ -1245,7 +1259,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1290,10 +1304,10 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
"variant": "high",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "google/gemini-3.1-pro-preview",
@@ -1311,7 +1325,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -1364,14 +1378,14 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"model": "github-copilot/claude-haiku-4.5",
},
"ultrabrain": {
"model": "opencode/gpt-5.3-codex",
"model": "opencode/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"unspecified-low": {
"model": "github-copilot/claude-sonnet-4.5",
"model": "github-copilot/claude-sonnet-4.6",
},
"visual-engineering": {
"model": "github-copilot/gemini-3.1-pro-preview",
@@ -1389,7 +1403,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1442,14 +1456,14 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "google/gemini-3.1-pro-preview",
@@ -1467,7 +1481,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1520,15 +1534,15 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"model": "anthropic/claude-haiku-4-5",
},
"ultrabrain": {
"model": "openai/gpt-5.3-codex",
"model": "openai/gpt-5.4",
"variant": "xhigh",
},
"unspecified-high": {
"model": "openai/gpt-5.4",
"variant": "high",
"model": "anthropic/claude-opus-4-6",
"variant": "max",
},
"unspecified-low": {
"model": "anthropic/claude-sonnet-4-5",
"model": "anthropic/claude-sonnet-4-6",
},
"visual-engineering": {
"model": "google/gemini-3.1-pro-preview",
@@ -0,0 +1,31 @@
import { describe, expect, test } from "bun:test"
import {
CLI_AGENT_MODEL_REQUIREMENTS,
CLI_CATEGORY_MODEL_REQUIREMENTS,
} from "./model-fallback-requirements"
import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "../shared/model-requirements"
describe("CLI model fallback requirements", () => {
test("agent requirements stay aligned with runtime requirements", () => {
// #given
const runtimeAgents = AGENT_MODEL_REQUIREMENTS
// #when
const cliAgents = CLI_AGENT_MODEL_REQUIREMENTS
// #then
expect(cliAgents).toEqual(runtimeAgents)
})
test("category requirements stay aligned with runtime requirements", () => {
// #given
const runtimeCategories = CATEGORY_MODEL_REQUIREMENTS
// #when
const cliCategories = CLI_CATEGORY_MODEL_REQUIREMENTS
// #then
expect(cliCategories).toEqual(runtimeCategories)
})
})
+7 -281
View File
@@ -1,283 +1,9 @@
import type { ModelRequirement } from "../shared/model-requirements";
import {
AGENT_MODEL_REQUIREMENTS,
CATEGORY_MODEL_REQUIREMENTS,
type ModelRequirement,
} from "../shared/model-requirements"
// NOTE: These requirements are used by the CLI config generator (`generateModelConfig`).
// They intentionally use "install-time" provider IDs (anthropic/openai/google/opencode/etc),
// not runtime-only providers like `nvidia`.
export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = AGENT_MODEL_REQUIREMENTS
export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
sisyphus: {
fallbackChain: [
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-6",
variant: "max",
},
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4", variant: "medium" },
{ providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
],
requiresAnyModel: true,
},
hephaestus: {
fallbackChain: [
{
providers: ["openai", "opencode"],
model: "gpt-5.3-codex",
variant: "medium",
},
],
requiresProvider: ["openai", "opencode"],
},
oracle: {
fallbackChain: [
{
providers: ["openai", "github-copilot", "opencode"],
model: "gpt-5.4",
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: ["opencode-go"], model: "glm-5" },
],
},
librarian: {
fallbackChain: [
{ providers: ["opencode-go"], model: "minimax-m2.5" },
{ providers: ["opencode"], model: "minimax-m2.5-free" },
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
explore: {
fallbackChain: [
{ providers: ["github-copilot"], model: "grok-code-fast-1" },
{ providers: ["opencode-go"], model: "minimax-m2.5" },
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
"multimodal-looker": {
fallbackChain: [
{ providers: ["openai", "opencode"], model: "gpt-5.4", variant: "medium" },
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
prometheus: {
fallbackChain: [
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-6",
variant: "max",
},
{ providers: ["kimi-for-coding"], model: "k2p5" },
{
providers: ["openai", "github-copilot", "opencode"],
model: "gpt-5.4",
variant: "high",
},
{ providers: ["opencode-go"], model: "glm-5" },
{
providers: ["google", "github-copilot", "opencode"],
model: "gemini-3.1-pro",
},
],
},
metis: {
fallbackChain: [
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-6",
variant: "max",
},
{ providers: ["opencode-go"], model: "glm-5" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
],
},
momus: {
fallbackChain: [
{
providers: ["openai", "github-copilot", "opencode"],
model: "gpt-5.4",
variant: "xhigh",
},
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-6",
variant: "max",
},
{
providers: ["google", "github-copilot", "opencode"],
model: "gemini-3.1-pro",
variant: "high",
},
{ providers: ["opencode-go"], model: "glm-5" },
],
},
atlas: {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["opencode-go"], model: "kimi-k2.5" },
],
},
"sisyphus-junior": {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{ providers: ["opencode"], model: "big-pickle" },
],
},
};
export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
"visual-engineering": {
fallbackChain: [
{
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",
},
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode-go"], model: "glm-5" },
],
},
ultrabrain: {
fallbackChain: [
{
providers: ["openai", "opencode"],
model: "gpt-5.3-codex",
variant: "xhigh",
},
{
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: ["opencode-go"], model: "glm-5" },
],
},
deep: {
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.1-pro",
variant: "high",
},
],
requiresModel: "gpt-5.3-codex",
},
artistry: {
fallbackChain: [
{
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.4",
},
],
requiresModel: "gemini-3.1-pro",
},
quick: {
fallbackChain: [
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-haiku-4-5",
},
{
providers: ["google", "github-copilot", "opencode"],
model: "gemini-3-flash",
},
{ providers: ["opencode-go"], model: "minimax-m2.5" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
"unspecified-low": {
fallbackChain: [
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-sonnet-4-5",
},
{
providers: ["openai", "opencode"],
model: "gpt-5.3-codex",
variant: "medium",
},
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{
providers: ["google", "github-copilot", "opencode"],
model: "gemini-3-flash",
},
],
},
"unspecified-high": {
fallbackChain: [
{
providers: ["openai", "github-copilot", "opencode"],
model: "gpt-5.4",
variant: "high",
},
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-opus-4-6",
variant: "max",
},
{ providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5" },
{ providers: ["opencode-go"], model: "glm-5" },
],
},
writing: {
fallbackChain: [
{ providers: ["kimi-for-coding"], model: "k2p5" },
{
providers: ["google", "github-copilot", "opencode"],
model: "gemini-3-flash",
},
{ providers: ["opencode-go"], model: "kimi-k2.5" },
{
providers: ["anthropic", "github-copilot", "opencode"],
model: "claude-sonnet-4-5",
},
],
},
};
export const CLI_CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = CATEGORY_MODEL_REQUIREMENTS
+45 -3
View File
@@ -13,6 +13,7 @@ function createConfig(overrides: Partial<InstallConfig> = {}): InstallConfig {
hasOpencodeZen: false,
hasZaiCodingPlan: false,
hasKimiForCoding: false,
hasOpencodeGo: false,
...overrides,
}
}
@@ -410,6 +411,44 @@ describe("generateModelConfig", () => {
})
})
describe("OpenAI fallback coverage", () => {
test("Atlas resolves to OpenAI when only OpenAI is available", () => {
// #given
const config = createConfig({ hasOpenAI: true })
// #when
const result = generateModelConfig(config)
// #then
expect(result.agents?.atlas?.model).toBe("openai/gpt-5.4")
expect(result.agents?.atlas?.variant).toBe("medium")
})
test("Metis resolves to OpenAI when only OpenAI is available", () => {
// #given
const config = createConfig({ hasOpenAI: true })
// #when
const result = generateModelConfig(config)
// #then
expect(result.agents?.metis?.model).toBe("openai/gpt-5.4")
expect(result.agents?.metis?.variant).toBe("high")
})
test("Sisyphus-Junior resolves to OpenAI when only OpenAI is available", () => {
// #given
const config = createConfig({ hasOpenAI: true })
// #when
const result = generateModelConfig(config)
// #then
expect(result.agents?.["sisyphus-junior"]?.model).toBe("openai/gpt-5.4")
expect(result.agents?.["sisyphus-junior"]?.variant).toBe("medium")
})
})
describe("Hephaestus agent special cases", () => {
test("Hephaestus is created when OpenAI is available (openai provider connected)", () => {
// #given
@@ -423,15 +462,18 @@ describe("generateModelConfig", () => {
expect(result.agents?.hephaestus?.variant).toBe("medium")
})
test("Hephaestus is NOT created when only Copilot is available (gpt-5.3-codex unavailable on github-copilot)", () => {
test("Hephaestus falls back to Copilot GPT-5.4 when only Copilot is available", () => {
// #given
const config = createConfig({ hasCopilot: true })
// #when
const result = generateModelConfig(config)
// #then - hephaestus is omitted because gpt-5.3-codex is not available on github-copilot
expect(result.agents?.hephaestus).toBeUndefined()
// #then
expect(result.agents?.hephaestus).toEqual({
model: "github-copilot/gpt-5.4",
variant: "medium",
})
})
test("Hephaestus is created when OpenCode Zen is available (opencode provider connected)", () => {
+14
View File
@@ -13,6 +13,7 @@ function createConfig(overrides: Partial<InstallConfig> = {}): InstallConfig {
hasOpencodeZen: false,
hasZaiCodingPlan: false,
hasKimiForCoding: false,
hasOpencodeGo: false,
...overrides,
}
}
@@ -43,4 +44,17 @@ describe("generateModelConfig OpenAI-only model catalog", () => {
expect(result.categories?.["visual-engineering"]).toEqual({ model: "openai/gpt-5.4", variant: "high" })
expect(result.categories?.writing).toEqual({ model: "openai/gpt-5.4", variant: "medium" })
})
test("does not apply OpenAI-only overrides when OpenCode Go is also available", () => {
// #given
const config = createConfig({ hasOpenAI: true, hasOpencodeGo: true })
// #when
const result = generateModelConfig(config)
// #then
expect(result.agents?.explore).toEqual({ model: "opencode-go/minimax-m2.5" })
expect(result.agents?.librarian).toEqual({ model: "opencode-go/minimax-m2.5" })
expect(result.categories?.quick).toEqual({ model: "opencode-go/minimax-m2.5" })
})
})
+1
View File
@@ -17,6 +17,7 @@ export function isOpenAiOnlyAvailability(availability: ProviderAvailability): bo
availability.native.openai &&
!availability.native.claude &&
!availability.native.gemini &&
!availability.opencodeGo &&
!availability.opencodeZen &&
!availability.copilot &&
!availability.zai &&