diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc6dfd1d..c05633f51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Providers/OpenRouter: preserve the required `openrouter/` prefix for OpenRouter-native model IDs during model-ref normalization. (#12942) Thanks @omair445. - Telegram/Webhook: keep webhook monitors alive until gateway abort signals fire, preventing false channel exits and immediate webhook auto-restart loops. - Telegram/Polling: retry recoverable setup-time network failures in monitor startup and await runner teardown before retry to avoid overlapping polling sessions. - Telegram/Polling: clear Telegram webhooks (`deleteWebhook`) before starting long-poll `getUpdates`, including retry handling for transient cleanup failures. diff --git a/src/agents/model-selection.test.ts b/src/agents/model-selection.test.ts index 20947a8a1..b903189b2 100644 --- a/src/agents/model-selection.test.ts +++ b/src/agents/model-selection.test.ts @@ -83,6 +83,20 @@ describe("model-selection", () => { expect(parseModelRef(" ", "anthropic")).toBeNull(); }); + it("should preserve openrouter/ prefix for native models", () => { + expect(parseModelRef("openrouter/aurora-alpha", "openai")).toEqual({ + provider: "openrouter", + model: "openrouter/aurora-alpha", + }); + }); + + it("should pass through openrouter external provider models as-is", () => { + expect(parseModelRef("openrouter/anthropic/claude-sonnet-4-5", "openai")).toEqual({ + provider: "openrouter", + model: "anthropic/claude-sonnet-4-5", + }); + }); + it("should handle invalid slash usage", () => { expect(parseModelRef("/", "anthropic")).toBeNull(); expect(parseModelRef("anthropic/", "anthropic")).toBeNull(); diff --git a/src/agents/model-selection.ts b/src/agents/model-selection.ts index d7e3a6bf0..2df5acb14 100644 --- a/src/agents/model-selection.ts +++ b/src/agents/model-selection.ts @@ -111,6 +111,13 @@ function normalizeProviderModelId(provider: string, model: string): string { if (provider === "google") { return normalizeGoogleModelId(model); } + // 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 model; }