feat(model-requirements): add k2p5, kimi-k2.5, gpt-5.4 medium to Sisyphus fallback chain

Sisyphus can now fall back through Kimi and OpenAI models when Claude
is unavailable, enabling OpenAI-only users to use Sisyphus directly
instead of being redirected to Hephaestus.

Runtime chain: claude-opus-4-6 max → k2p5 → kimi-k2.5 → gpt-5.4 medium → glm-5 → big-pickle
CLI chain: claude-opus-4-6 max → k2p5 → gpt-5.4 medium → glm-5
This commit is contained in:
YeonGyu-Kim
2026-03-07 19:23:08 +09:00
parent bf8d0ffcc0
commit 60bc9a7609
12 changed files with 55 additions and 19 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ These agents have Claude-optimized prompts — long, detailed, mechanics-driven.
| Agent | Role | Fallback Chain | Notes |
| ------------ | ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **Sisyphus** | Main orchestrator | Claude Opus → GLM 5 → Big Pickle | Claude-family first. GPT-5.4 has dedicated support, but Claude/Kimi/GLM remain the preferred fit. |
| **Sisyphus** | Main orchestrator | Claude Opus → K2P5 → Kimi K2.5 → GPT-5.4 → GLM 5 → Big Pickle | Claude-family first. GPT-5.4 has dedicated prompt support. Kimi/GLM as intermediate fallbacks. |
| **Metis** | Plan gap analyzer | Claude Opus → GPT-5.4 → Gemini 3.1 Pro | Claude preferred, GPT acceptable fallback. |
### Dual-Prompt Agents → Claude preferred, GPT supported
+1 -1
View File
@@ -10,7 +10,7 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each
| Agent | Model | Temp | Mode | Fallback Chain | Purpose |
|-------|-------|------|------|----------------|---------|
| **Sisyphus** | claude-opus-4-6 max | 0.1 | all | glm-5 → big-pickle | Main orchestrator, plans + delegates |
| **Sisyphus** | claude-opus-4-6 max | 0.1 | all | k2p5 → kimi-k2.5 → gpt-5.4 medium → glm-5 → big-pickle | Main orchestrator, plans + delegates |
| **Hephaestus** | gpt-5.3-codex medium | 0.1 | all | gpt-5.4 medium (copilot) | Autonomous deep worker |
| **Oracle** | gpt-5.4 high | 0.1 | subagent | gemini-3.1-pro high → claude-opus-4-6 max | Read-only consultation |
| **Librarian** | gemini-3-flash | 0.1 | subagent | minimax-m2.5-free → big-pickle | External docs/code search |
+2 -2
View File
@@ -891,9 +891,9 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
})
test("sisyphus is not created when no fallback model is available and provider not connected", async () => {
// #given - only openai/gpt-5.4 available, not in sisyphus fallback chain
// #given - only venice/deepseek-v3.2 available, not in sisyphus fallback chain
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
new Set(["openai/gpt-5.4"])
new Set(["venice/deepseek-v3.2"])
)
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue([])
@@ -224,6 +224,10 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"model": "openai/gpt-5.4",
"variant": "high",
},
"sisyphus": {
"model": "openai/gpt-5.4",
"variant": "medium",
},
},
"categories": {
"deep": {
@@ -293,6 +297,10 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"model": "openai/gpt-5.4",
"variant": "high",
},
"sisyphus": {
"model": "openai/gpt-5.4",
"variant": "medium",
},
},
"categories": {
"deep": {
+3 -2
View File
@@ -249,8 +249,9 @@ describe("generateOmoConfig - model fallback system", () => {
// #when generating config
const result = generateOmoConfig(config)
// #then Sisyphus is omitted (requires all fallback providers)
expect((result.agents as Record<string, { model: string }>).sisyphus).toBeUndefined()
// #then Sisyphus resolves to gpt-5.4 medium (openai is now in sisyphus chain)
expect((result.agents as Record<string, { model: string; variant?: string }>).sisyphus.model).toBe("openai/gpt-5.4")
expect((result.agents as Record<string, { model: string; variant?: string }>).sisyphus.variant).toBe("medium")
// #then Oracle should use native OpenAI (first fallback entry)
expect((result.agents as Record<string, { model: string }>).oracle.model).toBe("openai/gpt-5.4")
// #then multimodal-looker should use native OpenAI (first fallback entry is gpt-5.3-codex)
+1
View File
@@ -13,6 +13,7 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
variant: "max",
},
{ 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,
+3 -2
View File
@@ -396,7 +396,7 @@ describe("generateModelConfig", () => {
expect(result.agents?.sisyphus?.model).toBe("anthropic/claude-opus-4-6")
})
test("Sisyphus is omitted when no fallback provider is available (OpenAI not in chain)", () => {
test("Sisyphus resolves to gpt-5.4 medium when only OpenAI is available", () => {
// #given
const config = createConfig({ hasOpenAI: true })
@@ -404,7 +404,8 @@ describe("generateModelConfig", () => {
const result = generateModelConfig(config)
// #then
expect(result.agents?.sisyphus).toBeUndefined()
expect(result.agents?.sisyphus?.model).toBe("openai/gpt-5.4")
expect(result.agents?.sisyphus?.variant).toBe("medium")
})
})
+2 -2
View File
@@ -134,8 +134,8 @@ describe("model fallback hook", () => {
//#then - chain should progress to entry[1], not repeat entry[0]
expect(secondOutput.message["model"]).toEqual({
providerID: "zai-coding-plan",
modelID: "glm-5",
providerID: "kimi-for-coding",
modelID: "k2p5",
})
expect(secondOutput.message["variant"]).toBeUndefined()
})
+2 -2
View File
@@ -334,8 +334,8 @@ describe("createEventHandler - model fallback", () => {
//#then - second fallback entry applied (chain advanced)
expect(second.message["model"]).toEqual({
providerID: "zai-coding-plan",
modelID: "glm-5",
providerID: "kimi-for-coding",
modelID: "k2p5",
})
expect(second.message["variant"]).toBeUndefined()
expect(abortCalls).toEqual([sessionID, sessionID])
+3 -3
View File
@@ -124,8 +124,8 @@ describe("resolveVariantForModel", () => {
expect(variant).toBe("medium")
})
test("returns undefined for provider not in sisyphus chain", () => {
// #given openai is not in sisyphus fallback chain anymore
test("returns medium for openai/gpt-5.4 in sisyphus chain", () => {
// #given openai/gpt-5.4 is now in sisyphus fallback chain with variant medium
const config = {} as OhMyOpenCodeConfig
const model = { providerID: "openai", modelID: "gpt-5.4" }
@@ -133,7 +133,7 @@ describe("resolveVariantForModel", () => {
const variant = resolveVariantForModel(config, "sisyphus", model)
// then
expect(variant).toBeUndefined()
expect(variant).toBe("medium")
})
test("returns undefined for provider not in chain", () => {
+16 -4
View File
@@ -23,15 +23,15 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.variant).toBe("high")
})
test("sisyphus has claude-opus-4-6 as primary and requiresAnyModel", () => {
test("sisyphus has claude-opus-4-6 as primary with k2p5, kimi-k2.5, gpt-5.4 medium fallbacks", () => {
// #given - sisyphus agent requirement
const sisyphus = AGENT_MODEL_REQUIREMENTS["sisyphus"]
// #when - accessing Sisyphus requirement
// #then - fallbackChain has claude-opus-4-6 first, big-pickle last
// #then - fallbackChain has 6 entries with correct ordering
expect(sisyphus).toBeDefined()
expect(sisyphus.fallbackChain).toBeArray()
expect(sisyphus.fallbackChain).toHaveLength(3)
expect(sisyphus.fallbackChain).toHaveLength(6)
expect(sisyphus.requiresAnyModel).toBe(true)
const primary = sisyphus.fallbackChain[0]
@@ -39,7 +39,19 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
expect(primary.model).toBe("claude-opus-4-6")
expect(primary.variant).toBe("max")
const last = sisyphus.fallbackChain[2]
const second = sisyphus.fallbackChain[1]
expect(second.providers).toEqual(["kimi-for-coding"])
expect(second.model).toBe("k2p5")
const third = sisyphus.fallbackChain[2]
expect(third.model).toBe("kimi-k2.5")
const fourth = sisyphus.fallbackChain[3]
expect(fourth.providers).toContain("openai")
expect(fourth.model).toBe("gpt-5.4")
expect(fourth.variant).toBe("medium")
const last = sisyphus.fallbackChain[5]
expect(last.providers[0]).toBe("opencode")
expect(last.model).toBe("big-pickle")
})
+13
View File
@@ -20,6 +20,19 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
model: "claude-opus-4-6",
variant: "max",
},
{ providers: ["kimi-for-coding"], model: "k2p5" },
{
providers: [
"opencode",
"moonshotai",
"moonshotai-cn",
"firmware",
"ollama-cloud",
"aihubmix",
],
model: "kimi-k2.5",
},
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4", variant: "medium" },
{ providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
{ providers: ["opencode"], model: "big-pickle" },
],