diff --git a/CHANGELOG.md b/CHANGELOG.md index 0495091d633..d5496b9aaac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Exec/YOLO: stop rejecting gateway-host exec in `security=full` plus `ask=off` mode via the Python/Node script preflight hardening path, so promptless YOLO exec once again runs direct interpreter stdin and heredoc forms such as `node <<'NODE' ... NODE`. +- Anthropic/plugins: scope Anthropic `api: "anthropic-messages"` defaulting to Anthropic-owned providers, so `openai-codex` and other providers without an explicit `api` no longer get rewritten to the wrong transport. Fixes #64534. - fix(qqbot): add SSRF guard to direct-upload URL paths in uploadC2CMedia and uploadGroupMedia [AI-assisted]. (#69595) Thanks @pgondhi987. - fix(gateway): enforce allowRequestSessionKey gate on template-rendered mapping sessionKeys. (#69381) Thanks @pgondhi987. - Webchat/images: treat inline image attachments as media for empty-turn gating while still ignoring metadata-only blank turns. (#69474) Thanks @Jaswir. diff --git a/extensions/anthropic/config-defaults.ts b/extensions/anthropic/config-defaults.ts index ddad9dd5829..201263b6699 100644 --- a/extensions/anthropic/config-defaults.ts +++ b/extensions/anthropic/config-defaults.ts @@ -159,6 +159,16 @@ export function normalizeAnthropicProviderConfig(params: { provider: string; providerConfig: T }): T { + const provider = normalizeProviderId(params.provider); + if (provider !== "anthropic" && provider !== CLAUDE_CLI_BACKEND_ID) { + return params.providerConfig; + } + return normalizeAnthropicProviderConfig(params.providerConfig); +} + export function applyAnthropicConfigDefaults(params: { config: OpenClawConfig; env: NodeJS.ProcessEnv; diff --git a/extensions/anthropic/index.test.ts b/extensions/anthropic/index.test.ts index fe4453c9044..2371394c4c5 100644 --- a/extensions/anthropic/index.test.ts +++ b/extensions/anthropic/index.test.ts @@ -100,6 +100,36 @@ describe("anthropic provider replay hooks", () => { }); }); + it("defaults Claude CLI provider api through plugin config normalization", async () => { + const provider = await registerSingleProviderPlugin(anthropicPlugin); + + expect( + provider.normalizeConfig?.({ + provider: "claude-cli", + providerConfig: { + models: [{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }], + }, + } as never), + ).toMatchObject({ + api: "anthropic-messages", + }); + }); + + it("does not default non-Anthropic provider api through plugin config normalization", async () => { + const provider = await registerSingleProviderPlugin(anthropicPlugin); + const providerConfig = { + baseUrl: "https://chatgpt.com/backend-api/codex", + models: [{ id: "gpt-5.4", name: "GPT-5.4" }], + }; + + expect( + provider.normalizeConfig?.({ + provider: "openai-codex", + providerConfig, + } as never), + ).toBe(providerConfig); + }); + it("applies Anthropic pruning defaults through plugin hooks", async () => { const provider = await registerSingleProviderPlugin(anthropicPlugin); diff --git a/extensions/anthropic/provider-policy-api.test.ts b/extensions/anthropic/provider-policy-api.test.ts index daa6b84b29b..b1beddbcaf8 100644 --- a/extensions/anthropic/provider-policy-api.test.ts +++ b/extensions/anthropic/provider-policy-api.test.ts @@ -35,6 +35,34 @@ describe("anthropic provider policy public artifact", () => { }); }); + it("normalizes Claude CLI provider config", () => { + expect( + normalizeConfig({ + provider: "claude-cli", + providerConfig: { + baseUrl: "https://api.anthropic.com", + models: [createModel("claude-sonnet-4-6", "Claude Sonnet 4.6")], + }, + }), + ).toMatchObject({ + api: "anthropic-messages", + }); + }); + + it("does not normalize non-Anthropic provider config", () => { + const providerConfig = { + baseUrl: "https://chatgpt.com/backend-api/codex", + models: [createModel("gpt-5.4", "GPT-5.4")], + }; + + expect( + normalizeConfig({ + provider: "openai-codex", + providerConfig, + }), + ).toBe(providerConfig); + }); + it("applies Anthropic API-key defaults without loading the full provider plugin", () => { const nextConfig = applyConfigDefaults({ config: { diff --git a/extensions/anthropic/provider-policy-api.ts b/extensions/anthropic/provider-policy-api.ts index 21e25abf660..912ad48810c 100644 --- a/extensions/anthropic/provider-policy-api.ts +++ b/extensions/anthropic/provider-policy-api.ts @@ -1,11 +1,11 @@ import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types"; import { applyAnthropicConfigDefaults, - normalizeAnthropicProviderConfig, + normalizeAnthropicProviderConfigForProvider, } from "./config-defaults.js"; export function normalizeConfig(params: { provider: string; providerConfig: ModelProviderConfig }) { - return normalizeAnthropicProviderConfig(params.providerConfig); + return normalizeAnthropicProviderConfigForProvider(params); } export function applyConfigDefaults(params: Parameters[0]) { diff --git a/extensions/anthropic/register.runtime.ts b/extensions/anthropic/register.runtime.ts index e695f2870cd..cfc870484de 100644 --- a/extensions/anthropic/register.runtime.ts +++ b/extensions/anthropic/register.runtime.ts @@ -34,7 +34,7 @@ import { } from "./cli-shared.js"; import { applyAnthropicConfigDefaults, - normalizeAnthropicProviderConfig, + normalizeAnthropicProviderConfigForProvider, } from "./config-defaults.js"; import { anthropicMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { buildAnthropicReplayPolicy } from "./replay-policy.js"; @@ -483,7 +483,8 @@ export function buildAnthropicProvider(): ProviderPlugin { }, }), ], - normalizeConfig: ({ providerConfig }) => normalizeAnthropicProviderConfig(providerConfig), + normalizeConfig: ({ provider, providerConfig }) => + normalizeAnthropicProviderConfigForProvider({ provider, providerConfig }), applyConfigDefaults: ({ config, env }) => applyAnthropicConfigDefaults({ config, env }), resolveDynamicModel: (ctx) => resolveAnthropicForwardCompatModel(ctx), resolveSyntheticAuth: ({ provider }) =>