diff --git a/src/agents/model-selection.plugin-runtime.test.ts b/src/agents/model-selection.plugin-runtime.test.ts index ba62b712261..fcfb2c25a82 100644 --- a/src/agents/model-selection.plugin-runtime.test.ts +++ b/src/agents/model-selection.plugin-runtime.test.ts @@ -16,25 +16,25 @@ describe("model-selection plugin runtime normalization", () => { it("delegates provider-owned model id normalization to plugin runtime hooks", async () => { normalizeProviderModelIdWithPluginMock.mockImplementation(({ provider, context }) => { if ( - provider === "xai" && - (context as { modelId?: string }).modelId === "grok-4.20-experimental-beta-0304-reasoning" + provider === "custom-provider" && + (context as { modelId?: string }).modelId === "custom-legacy-model" ) { - return "grok-4.20-beta-latest-reasoning"; + return "custom-modern-model"; } return undefined; }); const { parseModelRef } = await import("./model-selection.js"); - expect(parseModelRef("grok-4.20-experimental-beta-0304-reasoning", "xai")).toEqual({ - provider: "xai", - model: "grok-4.20-beta-latest-reasoning", + expect(parseModelRef("custom-legacy-model", "custom-provider")).toEqual({ + provider: "custom-provider", + model: "custom-modern-model", }); expect(normalizeProviderModelIdWithPluginMock).toHaveBeenCalledWith({ - provider: "xai", + provider: "custom-provider", context: { - provider: "xai", - modelId: "grok-4.20-experimental-beta-0304-reasoning", + provider: "custom-provider", + modelId: "custom-legacy-model", }, }); }); diff --git a/src/agents/model-selection.ts b/src/agents/model-selection.ts index 066b1570be6..b3c3c12d273 100644 --- a/src/agents/model-selection.ts +++ b/src/agents/model-selection.ts @@ -6,6 +6,8 @@ import { toAgentModelListLike, } from "../config/model-input.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; +import { normalizeGoogleModelId } from "../plugin-sdk/google.js"; +import { normalizeXaiModelId } from "../plugin-sdk/xai.js"; import { resolveRuntimeCliBackends } from "../plugins/cli-backends.runtime.js"; import { normalizeProviderModelIdWithPlugin } from "../plugins/provider-runtime.js"; import { sanitizeForLog } from "../terminal/ansi.js"; @@ -117,6 +119,18 @@ function normalizeProviderModelId(provider: string, model: string): string { if (provider === "anthropic") { return normalizeAnthropicModelId(model); } + if (provider === "google" || provider === "google-vertex") { + return normalizeGoogleModelId(model); + } + if (provider === "openai") { + return model; + } + if (provider === "openrouter") { + return model.includes("/") ? model : `openrouter/${model}`; + } + if (provider === "xai") { + return normalizeXaiModelId(model); + } if (provider === "vercel-ai-gateway" && !model.includes("/")) { // Allow Vercel-specific Claude refs without an upstream prefix. const normalizedAnthropicModel = normalizeAnthropicModelId(model); @@ -124,13 +138,6 @@ function normalizeProviderModelId(provider: string, model: string): string { return `anthropic/${normalizedAnthropicModel}`; } } - // OpenRouter-native models (e.g. "openrouter/aurora-alpha") need the full - // "openrouter/" as the model ID sent to the API. Models from external - // providers already contain a slash (e.g. "anthropic/claude-sonnet-4-5") and - // are passed through as-is (#12924). - if (provider === "openrouter" && !model.includes("/")) { - return `openrouter/${model}`; - } return ( normalizeProviderModelIdWithPlugin({ provider, @@ -201,7 +208,9 @@ export function inferUniqueProviderFromConfiguredModels(params: { if (!ref || !ref.includes("/")) { continue; } - const parsed = parseModelRef(ref, DEFAULT_PROVIDER); + const parsed = parseModelRef(ref, DEFAULT_PROVIDER, { + allowPluginNormalization: false, + }); if (!parsed) { continue; }