fix(model-fallback): apply transformModelForProvider in getNextFallback

The getNextFallback function returned raw model names from the
hardcoded fallback chain without transforming them for the target
provider. For example, github-copilot requires dot notation
(claude-sonnet-4.6) but the fallback chain stores hyphen notation
(claude-sonnet-4-6).

The background-agent retry handler already calls
transformModelForProvider correctly, but the sync chat.message
hook in model-fallback was missing it — a copy-paste omission.

Add transformModelForProvider call in getNextFallback and a test
verifying github-copilot model name transformation.
This commit is contained in:
Jaden 2026-02-25 17:15:13 +09:00
parent 2ab40124ee
commit f6d5f6f79f
2 changed files with 48 additions and 1 deletions

View File

@ -3,12 +3,14 @@ import { beforeEach, describe, expect, test } from "bun:test"
import {
clearPendingModelFallback,
createModelFallbackHook,
setSessionFallbackChain,
setPendingModelFallback,
} from "./hook"
describe("model fallback hook", () => {
beforeEach(() => {
clearPendingModelFallback("ses_model_fallback_main")
clearPendingModelFallback("ses_model_fallback_ghcp")
})
test("applies pending fallback on chat.message by overriding model", async () => {
@ -138,4 +140,48 @@ describe("model fallback hook", () => {
expect(toastCalls.length).toBe(1)
expect(toastCalls[0]?.title).toBe("Model fallback")
})
test("transforms model names for github-copilot provider via fallback chain", async () => {
//#given
const sessionID = "ses_model_fallback_ghcp"
clearPendingModelFallback(sessionID)
const hook = createModelFallbackHook() as unknown as {
"chat.message"?: (
input: { sessionID: string },
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
) => Promise<void>
}
// Set a custom fallback chain that routes through github-copilot
setSessionFallbackChain(sessionID, [
{ providers: ["github-copilot"], model: "claude-sonnet-4-6" },
])
const set = setPendingModelFallback(
sessionID,
"Atlas (Plan Executor)",
"github-copilot",
"claude-sonnet-4-6",
)
expect(set).toBe(true)
const output = {
message: {
model: { providerID: "github-copilot", modelID: "claude-sonnet-4-6" },
},
parts: [{ type: "text", text: "continue" }],
}
//#when
await hook["chat.message"]?.({ sessionID }, output)
//#then — model name should be transformed from hyphen to dot notation
expect(output.message["model"]).toEqual({
providerID: "github-copilot",
modelID: "claude-sonnet-4.6",
})
clearPendingModelFallback(sessionID)
})
})

View File

@ -3,6 +3,7 @@ import { getAgentConfigKey } from "../../shared/agent-display-names"
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
import { readConnectedProvidersCache, readProviderModelsCache } from "../../shared/connected-providers-cache"
import { selectFallbackProvider } from "../../shared/model-error-classifier"
import { transformModelForProvider } from "../../shared/provider-model-id-transform"
import { log } from "../../shared/logger"
import { getTaskToastManager } from "../../features/task-toast-manager"
import type { ChatMessageInput, ChatMessageHandlerOutput } from "../../plugin/chat-message"
@ -145,7 +146,7 @@ export function getNextFallback(
return {
providerID,
modelID: fallback.model,
modelID: transformModelForProvider(providerID, fallback.model),
variant: fallback.variant,
}
}