|
|
|
@@ -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" })
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|