refactor(models): bump claude-opus-4-6 to claude-opus-4-7 across fallback chains, categories, and hooks
Updates the canonical Anthropic Opus model in every fallback chain (sisyphus, oracle, prometheus, metis, momus, visual-engineering, ultrabrain, deep, artistry, unspecified-high), the unspecified-high category default, the think-mode HIGH_VARIANT_MAP, the Claude Code alias map, the claude-thinking legacy alias, the context-limit GA regex, and event.ts fallback strings. Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and claude-*-4-7 so the 1M context cache still applies across the bump. Regenerates the bundled model-capabilities snapshot from models.dev and the model-fallback snapshot to match the new source output.
This commit is contained in:
@@ -84,7 +84,7 @@ describe("executeCompact lock management", () => {
|
||||
let pluginConfig: ReturnType<typeof OhMyOpenCodeConfigSchema.parse>
|
||||
const sessionID = "test-session-123"
|
||||
const directory = "/test/dir"
|
||||
const msg = { providerID: "anthropic", modelID: "claude-opus-4-6" }
|
||||
const msg = { providerID: "anthropic", modelID: "claude-opus-4-7" }
|
||||
|
||||
beforeEach(() => {
|
||||
// given: Fresh state for each test
|
||||
@@ -132,7 +132,7 @@ describe("executeCompact lock management", () => {
|
||||
expect(mockClient.session.summarize).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: { id: sessionID },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-6", auto: true },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-7", auto: true },
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ describe("executeCompact lock management", () => {
|
||||
expect(mockClient.session.summarize).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: { id: sessionID },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-6", auto: true },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-7", auto: true },
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -352,7 +352,7 @@ describe("executeCompact lock management", () => {
|
||||
expect(mockClient.session.summarize).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: { id: sessionID },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-6", auto: true },
|
||||
body: { providerID: "anthropic", modelID: "claude-opus-4-7", auto: true },
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function createMockParams(overrides: {
|
||||
existingOptions?: Record<string, unknown>
|
||||
}): { input: ChatParamsInput; output: ChatParamsOutput } {
|
||||
const providerID = overrides.providerID ?? "anthropic"
|
||||
const modelID = overrides.modelID ?? "claude-opus-4-6"
|
||||
const modelID = overrides.modelID ?? "claude-opus-4-7"
|
||||
const variant = "variant" in overrides ? overrides.variant : "max"
|
||||
const agentName = overrides.agentName ?? "sisyphus"
|
||||
const existingOptions = overrides.existingOptions ?? {}
|
||||
@@ -71,7 +71,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
|
||||
it("injects effort max for dotted opus ids", async () => {
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4.6" })
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4.7" })
|
||||
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
@@ -158,7 +158,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-opus-4-6",
|
||||
modelID: "claude-opus-4-7",
|
||||
})
|
||||
|
||||
// when
|
||||
@@ -256,7 +256,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
// given an Anthropic OAuth session and variant=max on an Opus model
|
||||
writeAuthFile({ anthropic: { type: "oauth" } })
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-6" })
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-7" })
|
||||
|
||||
// when chat.params fires
|
||||
await hook["chat.params"](input, output)
|
||||
@@ -270,7 +270,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
// given an Anthropic OAuth session and a dotted opus id
|
||||
writeAuthFile({ anthropic: { type: "oauth" } })
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4.6" })
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4.7" })
|
||||
|
||||
// when chat.params fires
|
||||
await hook["chat.params"](input, output)
|
||||
@@ -284,7 +284,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
// given an Anthropic API-key session (not OAuth)
|
||||
writeAuthFile({ anthropic: { type: "api", key: "sk-ant-xxx" } })
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-6" })
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-7" })
|
||||
|
||||
// when chat.params fires
|
||||
await hook["chat.params"](input, output)
|
||||
@@ -298,7 +298,7 @@ describe("createAnthropicEffortHook", () => {
|
||||
// given OAuth entries for unrelated providers only
|
||||
writeAuthFile({ "github-copilot": { type: "oauth" }, opencode: { type: "api", key: "sk-x" } })
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-6", providerID: "anthropic" })
|
||||
const { input, output } = createMockParams({ modelID: "claude-opus-4-7", providerID: "anthropic" })
|
||||
|
||||
// when chat.params fires for the anthropic provider
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
@@ -58,7 +58,7 @@ describe("atlas hook compaction agent filtering", () => {
|
||||
join(messageDir, fileName),
|
||||
JSON.stringify({
|
||||
agent,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ describe("Atlas final-wave approval gate regressions", () => {
|
||||
join(messageDirectory, "msg_test001.json"),
|
||||
JSON.stringify({
|
||||
agent: "atlas",
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ describe("Atlas final verification approval gate", () => {
|
||||
join(messageDirectory, "msg_test001.json"),
|
||||
JSON.stringify({
|
||||
agent: "atlas",
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("atlas hook", () => {
|
||||
}
|
||||
const messageData = {
|
||||
agent,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}
|
||||
writeFileSync(join(messageDir, "msg_test001.json"), JSON.stringify(messageData))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const selectFallbackProviderMock = mock((providers: string[], preferredProviderI
|
||||
const transformModelForProviderMock = mock((provider: string, model: string) => {
|
||||
if (provider === "github-copilot") {
|
||||
return model
|
||||
.replace("claude-opus-4-6", "claude-opus-4.6")
|
||||
.replace("claude-opus-4-7", "claude-opus-4.7")
|
||||
.replace("claude-sonnet-4-6", "claude-sonnet-4.6")
|
||||
.replace("claude-sonnet-4-5", "claude-sonnet-4.5")
|
||||
.replace("claude-haiku-4-5", "claude-haiku-4.5")
|
||||
@@ -96,13 +96,13 @@ describe("model fallback hook", () => {
|
||||
"ses_model_fallback_main",
|
||||
"Sisyphus - Ultraworker",
|
||||
"anthropic",
|
||||
"claude-opus-4-6-thinking",
|
||||
"claude-opus-4-7-thinking",
|
||||
)
|
||||
expect(set).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6-thinking" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7-thinking" },
|
||||
variant: "max",
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
@@ -117,7 +117,7 @@ describe("model fallback hook", () => {
|
||||
//#then
|
||||
expect(output.message["model"]).toEqual({
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-opus-4-6",
|
||||
modelID: "claude-opus-4-7",
|
||||
})
|
||||
})
|
||||
|
||||
@@ -132,12 +132,12 @@ describe("model fallback hook", () => {
|
||||
const sessionID = "ses_model_fallback_main"
|
||||
|
||||
expect(
|
||||
setPendingModelFallback(sessionID, "Sisyphus - Ultraworker", "anthropic", "claude-opus-4-6-thinking"),
|
||||
setPendingModelFallback(sessionID, "Sisyphus - Ultraworker", "anthropic", "claude-opus-4-7-thinking"),
|
||||
).toBe(true)
|
||||
|
||||
const firstOutput = {
|
||||
message: {
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6-thinking" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7-thinking" },
|
||||
variant: "max",
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
@@ -149,17 +149,17 @@ describe("model fallback hook", () => {
|
||||
//#then
|
||||
expect(firstOutput.message["model"]).toEqual({
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-opus-4-6",
|
||||
modelID: "claude-opus-4-7",
|
||||
})
|
||||
|
||||
//#when - second error re-arms fallback and should advance to next entry
|
||||
expect(
|
||||
setPendingModelFallback(sessionID, "Sisyphus - Ultraworker", "anthropic", "claude-opus-4-6"),
|
||||
setPendingModelFallback(sessionID, "Sisyphus - Ultraworker", "anthropic", "claude-opus-4-7"),
|
||||
).toBe(true)
|
||||
|
||||
const secondOutput = {
|
||||
message: {
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
}
|
||||
@@ -183,13 +183,13 @@ describe("model fallback hook", () => {
|
||||
sessionID,
|
||||
"Sisyphus - Ultraworker",
|
||||
"anthropic",
|
||||
"claude-opus-4-6-thinking",
|
||||
"claude-opus-4-7-thinking",
|
||||
)
|
||||
const secondSet = setPendingModelFallback(
|
||||
sessionID,
|
||||
"Sisyphus - Ultraworker",
|
||||
"anthropic",
|
||||
"claude-opus-4-6-thinking",
|
||||
"claude-opus-4-7-thinking",
|
||||
)
|
||||
|
||||
//#then
|
||||
@@ -211,7 +211,7 @@ describe("model fallback hook", () => {
|
||||
}
|
||||
|
||||
setSessionFallbackChain(sessionID, [
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-6" },
|
||||
{ providers: ["anthropic"], model: "claude-opus-4-7" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
])
|
||||
|
||||
@@ -220,13 +220,13 @@ describe("model fallback hook", () => {
|
||||
sessionID,
|
||||
"Sisyphus - Ultraworker",
|
||||
"anthropic",
|
||||
"claude-opus-4-6",
|
||||
"claude-opus-4-7",
|
||||
),
|
||||
).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
}
|
||||
@@ -255,7 +255,7 @@ describe("model fallback hook", () => {
|
||||
}
|
||||
|
||||
setSessionFallbackChain(sessionID, [
|
||||
{ providers: ["quotio"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["quotio"], model: "claude-opus-4-7", variant: "max" },
|
||||
{ providers: ["quotio"], model: "gpt-5.2" },
|
||||
])
|
||||
|
||||
@@ -264,13 +264,13 @@ describe("model fallback hook", () => {
|
||||
sessionID,
|
||||
"Sisyphus - Ultraworker",
|
||||
"quotio",
|
||||
"claude-opus-4-6",
|
||||
"claude-opus-4-7",
|
||||
),
|
||||
).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "quotio", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "quotio", modelID: "claude-opus-4-7" },
|
||||
variant: "max",
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
@@ -369,13 +369,13 @@ describe("model fallback hook", () => {
|
||||
"ses_model_fallback_toast",
|
||||
"Sisyphus - Ultraworker",
|
||||
"anthropic",
|
||||
"claude-opus-4-6-thinking",
|
||||
"claude-opus-4-7-thinking",
|
||||
)
|
||||
expect(set).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6-thinking" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7-thinking" },
|
||||
variant: "max",
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
|
||||
@@ -30,12 +30,12 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_1",
|
||||
agent: HEPHAESTUS_DISPLAY,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output1)
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_1",
|
||||
agent: HEPHAESTUS_DISPLAY,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output2)
|
||||
|
||||
// then - toast is shown and agent is switched to sisyphus
|
||||
@@ -66,7 +66,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_opt_out",
|
||||
agent: HEPHAESTUS_DISPLAY,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output)
|
||||
|
||||
// then - warning toast is shown but agent is not switched
|
||||
@@ -114,7 +114,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_3",
|
||||
agent: SISYPHUS_DISPLAY,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output)
|
||||
|
||||
// then - no toast
|
||||
@@ -136,7 +136,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
// when - chat.message runs without input.agent
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_4",
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output)
|
||||
|
||||
// then - toast shown via session-agent fallback, switched to sisyphus
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("no-sisyphus-gpt hook", () => {
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_2",
|
||||
agent: SISYPHUS_DISPLAY,
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-6" },
|
||||
model: { providerID: "anthropic", modelID: "claude-opus-4-7" },
|
||||
}, output)
|
||||
|
||||
// then - no toast
|
||||
|
||||
@@ -125,10 +125,10 @@ describe("createRuntimeFallbackHook dispose", () => {
|
||||
const fallbackTimeout = setTimeout(() => {}, 60_000)
|
||||
|
||||
capturedDeps?.sessionStates.set("session-1", {
|
||||
originalModel: "anthropic/claude-opus-4-6",
|
||||
originalModel: "anthropic/claude-opus-4-7",
|
||||
currentModel: "openai/gpt-5.4",
|
||||
fallbackIndex: 1,
|
||||
failedModels: new Map([["anthropic/claude-opus-4-6", 1]]),
|
||||
failedModels: new Map([["anthropic/claude-opus-4-7", 1]]),
|
||||
attemptCount: 1,
|
||||
})
|
||||
capturedDeps?.sessionLastAccess.set("session-1", Date.now())
|
||||
|
||||
@@ -7,7 +7,7 @@ describe("runtime-fallback error classifier", () => {
|
||||
//#given
|
||||
const info = {
|
||||
status:
|
||||
"All credentials for model claude-opus-4-6-thinking are cooling down [retrying in ~5 days attempt #1]",
|
||||
"All credentials for model claude-opus-4-7-thinking are cooling down [retrying in ~5 days attempt #1]",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -21,7 +21,7 @@ describe("runtime-fallback error classifier", () => {
|
||||
//#given
|
||||
const info = {
|
||||
status:
|
||||
"All credentials for model claude-opus-4-6 are cooldown [retrying in 7m 56s attempt #1]",
|
||||
"All credentials for model claude-opus-4-7 are cooldown [retrying in 7m 56s attempt #1]",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -49,7 +49,7 @@ describe("runtime-fallback error classifier", () => {
|
||||
//#given
|
||||
const error = {
|
||||
message:
|
||||
"All credentials for model claude-opus-4-6-thinking are cooling down [retrying in ~5 days attempt #1]",
|
||||
"All credentials for model claude-opus-4-7-thinking are cooling down [retrying in ~5 days attempt #1]",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -65,8 +65,8 @@ describe("runtime-fallback error classifier", () => {
|
||||
name: "ProviderModelNotFoundError",
|
||||
data: {
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-opus-4-6",
|
||||
message: "Model not found: anthropic/claude-opus-4-6.",
|
||||
modelID: "claude-opus-4-7",
|
||||
message: "Model not found: anthropic/claude-opus-4-7.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
const pluginConfig = {
|
||||
categories: {
|
||||
quick: {
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-6"],
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-7"],
|
||||
},
|
||||
},
|
||||
} as any
|
||||
@@ -24,7 +24,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
const result = getFallbackModelsForSession(sessionID, undefined, pluginConfig)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual(["openai/gpt-5.2", "anthropic/claude-opus-4-6"])
|
||||
expect(result).toEqual(["openai/gpt-5.2", "anthropic/claude-opus-4-7"])
|
||||
})
|
||||
|
||||
test("uses agent-specific fallback_models when agent is resolved", () => {
|
||||
@@ -32,7 +32,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
const pluginConfig = {
|
||||
agents: {
|
||||
oracle: {
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-6"],
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-7"],
|
||||
},
|
||||
},
|
||||
} as any
|
||||
@@ -41,7 +41,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
const result = getFallbackModelsForSession("ses_runtime_fallback_agent", "oracle", pluginConfig)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual(["openai/gpt-5.2", "anthropic/claude-opus-4-6"])
|
||||
expect(result).toEqual(["openai/gpt-5.2", "anthropic/claude-opus-4-7"])
|
||||
})
|
||||
|
||||
test("does not fall back to another agent chain when agent cannot be resolved", () => {
|
||||
@@ -52,7 +52,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
fallback_models: ["quotio/gpt-5.2", "quotio/glm-5", "quotio/kimi-k2.5"],
|
||||
},
|
||||
oracle: {
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-6"],
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-7"],
|
||||
},
|
||||
},
|
||||
} as any
|
||||
|
||||
@@ -51,7 +51,7 @@ describe("createRuntimeFallbackHook dispose retry-key cleanup", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("createRuntimeFallbackHook dispose retry-key cleanup", () => {
|
||||
status: {
|
||||
type: "retry",
|
||||
attempt: 1,
|
||||
message: "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
message: "All credentials for model claude-opus-4-7 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -77,7 +77,7 @@ describe("createRuntimeFallbackHook dispose retry-key cleanup", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
await hook.event(retryEvent)
|
||||
|
||||
@@ -329,7 +329,7 @@ describe("runtime-fallback", () => {
|
||||
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"anthropic/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4.7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
})
|
||||
@@ -365,14 +365,14 @@ describe("runtime-fallback", () => {
|
||||
type: "session.error",
|
||||
properties: {
|
||||
sessionID,
|
||||
error: { name: "UnknownError", data: { message: "Model not found: anthropic/claude-opus-4.6." } },
|
||||
error: { name: "UnknownError", data: { message: "Model not found: anthropic/claude-opus-4.7." } },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const fallbackLogs = logCalls.filter((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLogs.length).toBeGreaterThanOrEqual(2)
|
||||
expect(fallbackLogs[1]?.data).toMatchObject({ from: "anthropic/claude-opus-4.6", to: "openai/gpt-5.4" })
|
||||
expect(fallbackLogs[1]?.data).toMatchObject({ from: "anthropic/claude-opus-4.7", to: "openai/gpt-5.4" })
|
||||
|
||||
const nonRetryLog = logCalls.find(
|
||||
(c) => c.msg.includes("Error not retryable") && (c.data as { sessionID?: string } | undefined)?.sessionID === sessionID
|
||||
@@ -384,7 +384,7 @@ describe("runtime-fallback", () => {
|
||||
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"anthropic/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4.7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
})
|
||||
@@ -421,8 +421,8 @@ describe("runtime-fallback", () => {
|
||||
name: "ProviderModelNotFoundError",
|
||||
data: {
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-opus-4.6",
|
||||
message: "Model not found: anthropic/claude-opus-4.6.",
|
||||
modelID: "claude-opus-4.7",
|
||||
message: "Model not found: anthropic/claude-opus-4.7.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -431,7 +431,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLogs = logCalls.filter((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLogs.length).toBeGreaterThanOrEqual(2)
|
||||
expect(fallbackLogs[1]?.data).toMatchObject({ from: "anthropic/claude-opus-4.6", to: "openai/gpt-5.4" })
|
||||
expect(fallbackLogs[1]?.data).toMatchObject({ from: "anthropic/claude-opus-4.7", to: "openai/gpt-5.4" })
|
||||
})
|
||||
|
||||
test("should bootstrap session.error fallback from session category model and preserve variant", async () => {
|
||||
@@ -500,7 +500,7 @@ describe("runtime-fallback", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "github-copilot/claude-opus-4.6" } },
|
||||
properties: { info: { id: sessionID, model: "github-copilot/claude-opus-4.7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -511,7 +511,7 @@ describe("runtime-fallback", () => {
|
||||
info: {
|
||||
sessionID,
|
||||
role: "assistant",
|
||||
model: "github-copilot/claude-opus-4.6",
|
||||
model: "github-copilot/claude-opus-4.7",
|
||||
status:
|
||||
"Too Many Requests: quota exceeded [retrying in ~2 weeks attempt #1]",
|
||||
},
|
||||
@@ -524,13 +524,13 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLog).toBeDefined()
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "github-copilot/claude-opus-4.6", to: "openai/gpt-5.4" })
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "github-copilot/claude-opus-4.7", to: "openai/gpt-5.4" })
|
||||
})
|
||||
|
||||
test("should trigger fallback on OpenAI auto-retry signal in message.updated", async () => {
|
||||
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-6"]),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-7"]),
|
||||
})
|
||||
|
||||
const sessionID = "test-session-openai-auto-retry"
|
||||
@@ -562,7 +562,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLog).toBeDefined()
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "openai/gpt-5.3-codex", to: "anthropic/claude-opus-4-6" })
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "openai/gpt-5.3-codex", to: "anthropic/claude-opus-4-7" })
|
||||
})
|
||||
|
||||
test("should trigger fallback on auto-retry signal in assistant text parts", async () => {
|
||||
@@ -577,7 +577,7 @@ describe("runtime-fallback", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -588,7 +588,7 @@ describe("runtime-fallback", () => {
|
||||
info: {
|
||||
sessionID,
|
||||
role: "assistant",
|
||||
model: "quotio/claude-opus-4-6",
|
||||
model: "quotio/claude-opus-4-7",
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
@@ -605,7 +605,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLog).toBeDefined()
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-6", to: "openai/gpt-5.2" })
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-7", to: "openai/gpt-5.2" })
|
||||
})
|
||||
|
||||
test("should trigger fallback when auto-retry text parts are nested under info.parts", async () => {
|
||||
@@ -620,7 +620,7 @@ describe("runtime-fallback", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -631,7 +631,7 @@ describe("runtime-fallback", () => {
|
||||
info: {
|
||||
sessionID,
|
||||
role: "assistant",
|
||||
model: "quotio/claude-opus-4-6",
|
||||
model: "quotio/claude-opus-4-7",
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
@@ -648,7 +648,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLog).toBeDefined()
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-6", to: "openai/gpt-5.2" })
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-7", to: "openai/gpt-5.2" })
|
||||
})
|
||||
|
||||
test("should trigger fallback on session.status auto-retry signal", async () => {
|
||||
@@ -682,7 +682,7 @@ describe("runtime-fallback", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -695,7 +695,7 @@ describe("runtime-fallback", () => {
|
||||
type: "retry",
|
||||
next: 476,
|
||||
attempt: 1,
|
||||
message: "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
message: "All credentials for model claude-opus-4-7 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -706,7 +706,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
const fallbackLog = logCalls.find((c) => c.msg.includes("Preparing fallback"))
|
||||
expect(fallbackLog).toBeDefined()
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-6", to: "openai/gpt-5.2" })
|
||||
expect(fallbackLog?.data).toMatchObject({ from: "quotio/claude-opus-4-7", to: "openai/gpt-5.2" })
|
||||
expect(promptCalls.length).toBe(1)
|
||||
})
|
||||
|
||||
@@ -741,7 +741,7 @@ describe("runtime-fallback", () => {
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "session.created",
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-6" } },
|
||||
properties: { info: { id: sessionID, model: "quotio/claude-opus-4-7" } },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -754,7 +754,7 @@ describe("runtime-fallback", () => {
|
||||
type: "retry",
|
||||
next: 476,
|
||||
attempt: 1,
|
||||
message: "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
message: "All credentials for model claude-opus-4-7 are cooling down [retrying in 7m 56s attempt #1]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -769,7 +769,7 @@ describe("runtime-fallback", () => {
|
||||
type: "retry",
|
||||
next: 475,
|
||||
attempt: 1,
|
||||
message: "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 55s attempt #1]",
|
||||
message: "All credentials for model claude-opus-4-7 are cooling down [retrying in 7m 55s attempt #1]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -781,7 +781,7 @@ describe("runtime-fallback", () => {
|
||||
test("should NOT trigger fallback on auto-retry signal when timeout_seconds is 0", async () => {
|
||||
const hook = createRuntimeFallbackHook(createMockPluginInput(), {
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 0 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-6"]),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-7"]),
|
||||
})
|
||||
|
||||
const sessionID = "test-session-auto-retry-timeout-disabled"
|
||||
@@ -1161,8 +1161,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
}
|
||||
@@ -1212,7 +1212,7 @@ describe("runtime-fallback", () => {
|
||||
"Google Generative AI API key is missing. Pass it using the 'apiKey' parameter or the GOOGLE_GENERATIVE_AI_API_KEY environment variable.",
|
||||
},
|
||||
},
|
||||
model: "github-copilot/claude-opus-4.6",
|
||||
model: "github-copilot/claude-opus-4.7",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1251,8 +1251,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
}
|
||||
@@ -1294,7 +1294,7 @@ describe("runtime-fallback", () => {
|
||||
info: {
|
||||
sessionID,
|
||||
role: "assistant",
|
||||
model: "github-copilot/claude-opus-4.6",
|
||||
model: "github-copilot/claude-opus-4.7",
|
||||
status:
|
||||
"Too Many Requests: quota exceeded [retrying in ~2 weeks attempt #1]",
|
||||
},
|
||||
@@ -1303,8 +1303,8 @@ describe("runtime-fallback", () => {
|
||||
})
|
||||
|
||||
expect(retriedModels.length).toBeGreaterThanOrEqual(2)
|
||||
expect(retriedModels[0]).toBe("github-copilot/claude-opus-4.6")
|
||||
expect(retriedModels[1]).toBe("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels[0]).toBe("github-copilot/claude-opus-4.7")
|
||||
expect(retriedModels[1]).toBe("anthropic/claude-opus-4-7")
|
||||
|
||||
void sessionErrorPromise
|
||||
})
|
||||
@@ -1335,8 +1335,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
@@ -1372,8 +1372,8 @@ describe("runtime-fallback", () => {
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.6")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.7")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-7")
|
||||
expect(abortCalls.some((call) => call.path?.id === sessionID)).toBe(true)
|
||||
|
||||
const timeoutLog = logCalls.find((c) => c.msg.includes("Session fallback timeout reached"))
|
||||
@@ -1401,8 +1401,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
@@ -1443,15 +1443,15 @@ describe("runtime-fallback", () => {
|
||||
await hook["chat.message"]?.(
|
||||
{
|
||||
sessionID,
|
||||
model: { providerID: "github-copilot", modelID: "claude-opus-4.6" },
|
||||
model: { providerID: "github-copilot", modelID: "claude-opus-4.7" },
|
||||
},
|
||||
output
|
||||
)
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.6")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.7")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-7")
|
||||
})
|
||||
|
||||
test("should abort in-flight fallback request before advancing on timeout", async () => {
|
||||
@@ -1486,8 +1486,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
@@ -1524,8 +1524,8 @@ describe("runtime-fallback", () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
|
||||
expect(abortCalls.some((call) => call.path?.id === sessionID)).toBe(true)
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.6")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.7")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-7")
|
||||
|
||||
void sessionErrorPromise
|
||||
})
|
||||
@@ -1551,8 +1551,8 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"openai/gpt-5.4",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
@@ -1586,7 +1586,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.6")
|
||||
expect(retriedModels).toContain("github-copilot/claude-opus-4.7")
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -1624,9 +1624,9 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -1659,7 +1659,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -1695,7 +1695,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
})
|
||||
|
||||
test("should not clear fallback timeout on assistant non-error update with Copilot retry signal", async () => {
|
||||
@@ -1719,9 +1719,9 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -1754,7 +1754,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -1796,7 +1796,7 @@ describe("runtime-fallback", () => {
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -1846,7 +1846,7 @@ describe("runtime-fallback", () => {
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 60))
|
||||
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-7")
|
||||
})
|
||||
|
||||
test("should not clear fallback timeout on assistant non-error update without user-visible content", async () => {
|
||||
@@ -1870,9 +1870,9 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -1905,7 +1905,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -1914,7 +1914,7 @@ describe("runtime-fallback", () => {
|
||||
info: {
|
||||
sessionID,
|
||||
role: "assistant",
|
||||
model: "github-copilot/claude-opus-4.6",
|
||||
model: "github-copilot/claude-opus-4.7",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1946,9 +1946,9 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -1981,7 +1981,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -2022,9 +2022,9 @@ describe("runtime-fallback", () => {
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false, timeout_seconds: 30 }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback([
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"openai/gpt-5.3-codex",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
]),
|
||||
session_timeout_ms: 20,
|
||||
}
|
||||
@@ -2057,7 +2057,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.6"])
|
||||
expect(retriedModels).toEqual(["github-copilot/claude-opus-4.7"])
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
@@ -2145,7 +2145,7 @@ describe("runtime-fallback", () => {
|
||||
}),
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-6"]),
|
||||
pluginConfig: createMockPluginConfigWithCategoryFallback(["anthropic/claude-opus-4-7"]),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2176,7 +2176,7 @@ describe("runtime-fallback", () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-6")
|
||||
expect(retriedModels).toContain("anthropic/claude-opus-4-7")
|
||||
})
|
||||
|
||||
test("does NOT trigger fallback for normal type:error-free messages", async () => {
|
||||
@@ -2452,7 +2452,7 @@ describe("runtime-fallback", () => {
|
||||
}),
|
||||
{
|
||||
config: createMockConfig({ notify_on_fallback: false }),
|
||||
pluginConfig: createMockPluginConfigWithAgentFallback("prometheus", ["github-copilot/claude-opus-4.6"]),
|
||||
pluginConfig: createMockPluginConfigWithAgentFallback("prometheus", ["github-copilot/claude-opus-4.7"]),
|
||||
},
|
||||
)
|
||||
const sessionID = "test-preserve-agent-on-retry"
|
||||
@@ -2462,7 +2462,7 @@ describe("runtime-fallback", () => {
|
||||
type: "session.error",
|
||||
properties: {
|
||||
sessionID,
|
||||
model: "anthropic/claude-opus-4-6",
|
||||
model: "anthropic/claude-opus-4-7",
|
||||
error: { statusCode: 503, message: "Service unavailable" },
|
||||
agent: "prometheus",
|
||||
},
|
||||
@@ -2472,7 +2472,7 @@ describe("runtime-fallback", () => {
|
||||
expect(promptCalls.length).toBe(1)
|
||||
const callBody = promptCalls[0]?.body as Record<string, unknown>
|
||||
expect(callBody?.agent).toBe("prometheus")
|
||||
expect(callBody?.model).toEqual({ providerID: "github-copilot", modelID: "claude-opus-4.6" })
|
||||
expect(callBody?.model).toEqual({ providerID: "github-copilot", modelID: "claude-opus-4.7" })
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ describe("runtime-fallback provider matrix quota tests", () => {
|
||||
//#given
|
||||
const error = {
|
||||
name: "AI_APICallError",
|
||||
message: "All credentials for model claude-opus-4-6 are cooling down [retrying in ~2 weeks]",
|
||||
message: "All credentials for model claude-opus-4-7 are cooling down [retrying in ~2 weeks]",
|
||||
provider: "anthropic",
|
||||
}
|
||||
|
||||
|
||||
@@ -74,12 +74,12 @@ describe("createSessionStatusHandler", () => {
|
||||
const deps = createDeps()
|
||||
const abortCalls: string[] = []
|
||||
const retryCalls: Array<{ sessionID: string; model: string; source: string }> = []
|
||||
const state = createFallbackState("anthropic/claude-opus-4-6")
|
||||
const state = createFallbackState("anthropic/claude-opus-4-7")
|
||||
state.currentModel = "openai/gpt-5.4"
|
||||
state.fallbackIndex = 0
|
||||
state.attemptCount = 1
|
||||
state.pendingFallbackModel = "openai/gpt-5.4"
|
||||
state.failedModels.set("anthropic/claude-opus-4-6", Date.now())
|
||||
state.failedModels.set("anthropic/claude-opus-4-7", Date.now())
|
||||
deps.sessionStates.set(sessionID, state)
|
||||
|
||||
const handler = createSessionStatusHandler(deps, createHelpers(abortCalls, retryCalls), deps.sessionStatusRetryKeys)
|
||||
|
||||
@@ -49,7 +49,7 @@ describe("createThinkModeHook", () => {
|
||||
const input = createHookInput({
|
||||
sessionID,
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-opus-4-6",
|
||||
modelID: "claude-opus-4-7",
|
||||
})
|
||||
const output = createHookOutput("Please think deeply about this")
|
||||
|
||||
|
||||
@@ -23,26 +23,26 @@ describe("think-mode switcher", () => {
|
||||
describe("getHighVariant with dots vs hyphens", () => {
|
||||
it("should handle dots in Claude version numbers", () => {
|
||||
// given a Claude model ID with dot format
|
||||
const variant = getHighVariant("claude-opus-4.6")
|
||||
const variant = getHighVariant("claude-opus-4.7")
|
||||
|
||||
// then should return high variant with hyphen format
|
||||
expect(variant).toBe("claude-opus-4-6-high")
|
||||
expect(variant).toBe("claude-opus-4-7-high")
|
||||
})
|
||||
|
||||
it("should handle hyphens in Claude version numbers", () => {
|
||||
// given a Claude model ID with hyphen format
|
||||
const variant = getHighVariant("claude-opus-4-6")
|
||||
const variant = getHighVariant("claude-opus-4-7")
|
||||
|
||||
// then should return high variant
|
||||
expect(variant).toBe("claude-opus-4-6-high")
|
||||
expect(variant).toBe("claude-opus-4-7-high")
|
||||
})
|
||||
|
||||
it("should handle claude-opus-4-6 high variant", () => {
|
||||
it("should handle claude-opus-4-7 high variant", () => {
|
||||
// given a Claude Opus 4.6 model ID
|
||||
const variant = getHighVariant("claude-opus-4-6")
|
||||
const variant = getHighVariant("claude-opus-4-7")
|
||||
|
||||
// then should return high variant
|
||||
expect(variant).toBe("claude-opus-4-6-high")
|
||||
expect(variant).toBe("claude-opus-4-7-high")
|
||||
})
|
||||
|
||||
it("should handle dots in GPT version numbers", () => {
|
||||
@@ -73,7 +73,7 @@ describe("think-mode switcher", () => {
|
||||
|
||||
it("should return null for already-high variants", () => {
|
||||
// given model IDs that are already high variants
|
||||
expect(getHighVariant("claude-opus-4-6-high")).toBeNull()
|
||||
expect(getHighVariant("claude-opus-4-7-high")).toBeNull()
|
||||
expect(getHighVariant("gpt-5-4-high")).toBeNull()
|
||||
expect(getHighVariant("gemini-3-1-pro-high")).toBeNull()
|
||||
})
|
||||
@@ -89,7 +89,7 @@ describe("think-mode switcher", () => {
|
||||
describe("isAlreadyHighVariant", () => {
|
||||
it("should detect -high suffix", () => {
|
||||
// given model IDs with -high suffix
|
||||
expect(isAlreadyHighVariant("claude-opus-4-6-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("claude-opus-4-7-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("gpt-5-4-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("gemini-3.1-pro-high")).toBe(true)
|
||||
})
|
||||
@@ -101,8 +101,8 @@ describe("think-mode switcher", () => {
|
||||
|
||||
it("should return false for base models", () => {
|
||||
// given base model IDs without -high suffix
|
||||
expect(isAlreadyHighVariant("claude-opus-4-6")).toBe(false)
|
||||
expect(isAlreadyHighVariant("claude-opus-4.6")).toBe(false)
|
||||
expect(isAlreadyHighVariant("claude-opus-4-7")).toBe(false)
|
||||
expect(isAlreadyHighVariant("claude-opus-4.7")).toBe(false)
|
||||
expect(isAlreadyHighVariant("gpt-5.4")).toBe(false)
|
||||
expect(isAlreadyHighVariant("gemini-3.1-pro")).toBe(false)
|
||||
})
|
||||
@@ -133,10 +133,10 @@ describe("think-mode switcher", () => {
|
||||
|
||||
it("should handle prefixes with dots in version numbers", () => {
|
||||
// given a model ID with prefix and dots
|
||||
const variant = getHighVariant("vertex_ai/claude-opus-4.6")
|
||||
const variant = getHighVariant("vertex_ai/claude-opus-4.7")
|
||||
|
||||
// then should normalize dots and preserve prefix
|
||||
expect(variant).toBe("vertex_ai/claude-opus-4-6-high")
|
||||
expect(variant).toBe("vertex_ai/claude-opus-4-7-high")
|
||||
})
|
||||
|
||||
it("should handle multiple different prefixes", () => {
|
||||
@@ -167,7 +167,7 @@ describe("think-mode switcher", () => {
|
||||
|
||||
it("should return null for already-high prefixed models", () => {
|
||||
// given prefixed model IDs that are already high
|
||||
expect(getHighVariant("vertex_ai/claude-opus-4-6-high")).toBeNull()
|
||||
expect(getHighVariant("vertex_ai/claude-opus-4-7-high")).toBeNull()
|
||||
expect(getHighVariant("openai/gpt-5-4-high")).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -175,14 +175,14 @@ describe("think-mode switcher", () => {
|
||||
describe("isAlreadyHighVariant with prefixes", () => {
|
||||
it("should detect -high suffix in prefixed models", () => {
|
||||
// given prefixed model IDs with -high suffix
|
||||
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-6-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-7-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("openai/gpt-5-4-high")).toBe(true)
|
||||
expect(isAlreadyHighVariant("custom/gemini-3.1-pro-high")).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false for prefixed base models", () => {
|
||||
// given prefixed base model IDs without -high suffix
|
||||
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-6")).toBe(false)
|
||||
expect(isAlreadyHighVariant("vertex_ai/claude-opus-4-7")).toBe(false)
|
||||
expect(isAlreadyHighVariant("openai/gpt-5-4")).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ function extractModelPrefix(modelID: string): { prefix: string; base: string } {
|
||||
const HIGH_VARIANT_MAP: Record<string, string> = {
|
||||
// Claude
|
||||
"claude-sonnet-4-6": "claude-sonnet-4-6-high",
|
||||
"claude-opus-4-6": "claude-opus-4-6-high",
|
||||
"claude-opus-4-7": "claude-opus-4-7-high",
|
||||
// Gemini
|
||||
"gemini-3-1-pro": "gemini-3-1-pro-high",
|
||||
"gemini-3-1-pro-low": "gemini-3-1-pro-high",
|
||||
|
||||
Reference in New Issue
Block a user