diff --git a/CHANGELOG.md b/CHANGELOG.md index 512e59c45ef..2c74b16af0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,9 @@ Docs: https://docs.openclaw.ai - Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9. - CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974. +- Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda. - Memory/Dreaming: retry Dream Diary once with the session default when a configured dreaming model is unavailable, while leaving subagent trust and allowlist errors visible instead of silently masking configuration problems. Refs #67409 and #69209. Thanks @Ghiggins18 and @everySympathy. - Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing. - -### Fixes - - Channels/Telegram: normalize accidental full `/bot` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris. ## 2026.4.27 diff --git a/docs/providers/openai.md b/docs/providers/openai.md index e4e83e088b4..0282f9d551d 100644 --- a/docs/providers/openai.md +++ b/docs/providers/openai.md @@ -217,6 +217,12 @@ Choose your preferred auth method and follow the setup steps. It does not select or auto-enable the bundled Codex app-server harness. + + `openai-codex/gpt-5.4-mini` is not a supported Codex OAuth route. Use + `openai/gpt-5.4-mini` with an OpenAI API key, or use + `openai-codex/gpt-5.5` with Codex OAuth. + + ### Config example ```json5 diff --git a/extensions/openai/openai-codex-provider.test.ts b/extensions/openai/openai-codex-provider.test.ts index 732fe0f600c..785b1b9485d 100644 --- a/extensions/openai/openai-codex-provider.test.ts +++ b/extensions/openai/openai-codex-provider.test.ts @@ -439,7 +439,7 @@ describe("openai codex provider", () => { }); }); - it("resolves gpt-5.4-mini from codex templates with codex-sized limits", () => { + it("does not resolve gpt-5.4-mini through the Codex OAuth route", () => { const provider = buildOpenAICodexProviderPlugin(); const model = provider.resolveDynamicModel?.({ @@ -454,13 +454,7 @@ describe("openai codex provider", () => { ) as never, } as never); - expect(model).toMatchObject({ - id: "gpt-5.4-mini", - contextWindow: 272_000, - maxTokens: 128_000, - cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }, - }); - expect(model).not.toHaveProperty("contextTokens"); + expect(model).toBeUndefined(); }); it("augments catalog with gpt-5.5-pro and gpt-5.4 native metadata", () => { @@ -509,11 +503,9 @@ describe("openai codex provider", () => { cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 }, }), ); - expect(entries).toContainEqual( + expect(entries).not.toContainEqual( expect.objectContaining({ id: "gpt-5.4-mini", - contextWindow: 272_000, - cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }, }), ); }); diff --git a/extensions/openai/openai-codex-provider.ts b/extensions/openai/openai-codex-provider.ts index e3afd33a1ce..33d121887a3 100644 --- a/extensions/openai/openai-codex-provider.ts +++ b/extensions/openai/openai-codex-provider.ts @@ -53,14 +53,12 @@ const OPENAI_CODEX_GPT_55_PRO_MODEL_ID = "gpt-5.5-pro"; const OPENAI_CODEX_GPT_54_MODEL_ID = "gpt-5.4"; const OPENAI_CODEX_GPT_54_LEGACY_MODEL_ID = "gpt-5.4-codex"; const OPENAI_CODEX_GPT_54_PRO_MODEL_ID = "gpt-5.4-pro"; -const OPENAI_CODEX_GPT_54_MINI_MODEL_ID = "gpt-5.4-mini"; const OPENAI_CODEX_GPT_55_CODEX_CONTEXT_TOKENS = 400_000; const OPENAI_CODEX_GPT_55_DEFAULT_RUNTIME_CONTEXT_TOKENS = 272_000; const OPENAI_CODEX_GPT_55_PRO_NATIVE_CONTEXT_TOKENS = 1_000_000; const OPENAI_CODEX_GPT_55_PRO_DEFAULT_CONTEXT_TOKENS = 272_000; const OPENAI_CODEX_GPT_54_NATIVE_CONTEXT_TOKENS = 1_050_000; const OPENAI_CODEX_GPT_54_DEFAULT_CONTEXT_TOKENS = 272_000; -const OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS = 272_000; const OPENAI_CODEX_GPT_54_MAX_TOKENS = 128_000; const OPENAI_CODEX_GPT_55_PRO_COST = { input: 30, @@ -80,12 +78,6 @@ const OPENAI_CODEX_GPT_54_PRO_COST = { cacheRead: 0, cacheWrite: 0, } as const; -const OPENAI_CODEX_GPT_54_MINI_COST = { - input: 0.75, - output: 4.5, - cacheRead: 0.075, - cacheWrite: 0, -} as const; const OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS = ["gpt-5.3-codex", "gpt-5.2-codex"] as const; /** Legacy codex rows first; fall back to catalog `gpt-5.4` when the API omits 5.3/5.2. */ const OPENAI_CODEX_GPT_54_CATALOG_SYNTH_TEMPLATE_MODEL_IDS = [ @@ -97,11 +89,6 @@ const OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS = [ OPENAI_CODEX_GPT_54_PRO_MODEL_ID, ...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS, ] as const; -const OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS = [ - OPENAI_CODEX_GPT_54_MODEL_ID, - "gpt-5.1-codex-mini", - ...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS, -] as const; const OPENAI_CODEX_GPT_53_MODEL_ID = "gpt-5.3-codex"; const OPENAI_CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.2-codex"] as const; const OPENAI_CODEX_XHIGH_MODEL_IDS = [ @@ -109,7 +96,6 @@ const OPENAI_CODEX_XHIGH_MODEL_IDS = [ OPENAI_CODEX_GPT_55_PRO_MODEL_ID, OPENAI_CODEX_GPT_54_MODEL_ID, OPENAI_CODEX_GPT_54_PRO_MODEL_ID, - OPENAI_CODEX_GPT_54_MINI_MODEL_ID, OPENAI_CODEX_GPT_53_MODEL_ID, "gpt-5.2-codex", "gpt-5.1-codex", @@ -119,7 +105,6 @@ const OPENAI_CODEX_MODERN_MODEL_IDS = [ OPENAI_CODEX_GPT_55_PRO_MODEL_ID, OPENAI_CODEX_GPT_54_MODEL_ID, OPENAI_CODEX_GPT_54_PRO_MODEL_ID, - OPENAI_CODEX_GPT_54_MINI_MODEL_ID, "gpt-5.2", "gpt-5.2-codex", OPENAI_CODEX_GPT_53_MODEL_ID, @@ -242,13 +227,6 @@ function resolveCodexForwardCompatModel(ctx: ProviderResolveDynamicModelContext) maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS, cost: OPENAI_CODEX_GPT_54_PRO_COST, }; - } else if (lower === OPENAI_CODEX_GPT_54_MINI_MODEL_ID) { - templateIds = OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS; - patch = { - contextWindow: OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS, - maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS, - cost: OPENAI_CODEX_GPT_54_MINI_COST, - }; } else if (lower === OPENAI_CODEX_GPT_53_MODEL_ID) { templateIds = OPENAI_CODEX_TEMPLATE_MODEL_IDS; } else { @@ -552,11 +530,6 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin { providerId: PROVIDER_ID, templateIds: OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS, }); - const gpt54MiniTemplate = findCatalogTemplate({ - entries: ctx.entries, - providerId: PROVIDER_ID, - templateIds: OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS, - }); return [ buildOpenAISyntheticCatalogEntry(gpt55ProTemplate, { id: OPENAI_CODEX_GPT_55_PRO_MODEL_ID, @@ -582,13 +555,6 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin { contextTokens: OPENAI_CODEX_GPT_54_DEFAULT_CONTEXT_TOKENS, cost: OPENAI_CODEX_GPT_54_PRO_COST, }), - buildOpenAISyntheticCatalogEntry(gpt54MiniTemplate, { - id: OPENAI_CODEX_GPT_54_MINI_MODEL_ID, - reasoning: true, - input: ["text", "image"], - contextWindow: OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS, - cost: OPENAI_CODEX_GPT_54_MINI_COST, - }), ].filter((entry): entry is NonNullable => entry !== undefined); }, }; diff --git a/extensions/openai/openclaw.plugin.json b/extensions/openai/openclaw.plugin.json index 38dab74bb77..6467f98e6ab 100644 --- a/extensions/openai/openclaw.plugin.json +++ b/extensions/openai/openclaw.plugin.json @@ -128,6 +128,11 @@ "provider": "openai-codex", "model": "gpt-5.3-codex-spark", "reason": "gpt-5.3-codex-spark is no longer exposed by the OpenAI or Codex catalogs. Use openai/gpt-5.5." + }, + { + "provider": "openai-codex", + "model": "gpt-5.4-mini", + "reason": "gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth." } ] }, diff --git a/src/agents/pi-embedded-runner/model.provider-runtime.test-support.ts b/src/agents/pi-embedded-runner/model.provider-runtime.test-support.ts index 18823f619c7..9172d655101 100644 --- a/src/agents/pi-embedded-runner/model.provider-runtime.test-support.ts +++ b/src/agents/pi-embedded-runner/model.provider-runtime.test-support.ts @@ -269,20 +269,9 @@ function buildDynamicModel( ? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.4-pro", "gpt-5.3-codex"]) : lower === "gpt-5.4" || isLegacyGpt54Alias || lower === "gpt-5.4-pro" ? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex"]) - : lower === "gpt-5.4-mini" - ? findTemplate(params, "openai-codex", [ - "gpt-5.4", - "gpt-5.1-codex-mini", - "gpt-5.3-codex", - "gpt-5.2-codex", - ]) - : lower === "gpt-5.3-codex-spark" - ? findTemplate(params, "openai-codex", [ - "gpt-5.4", - "gpt-5.3-codex", - "gpt-5.2-codex", - ]) - : findTemplate(params, "openai-codex", ["gpt-5.4"]); + : lower === "gpt-5.3-codex-spark" + ? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex"]) + : findTemplate(params, "openai-codex", ["gpt-5.4"]); const fallback = { provider: "openai-codex", api: "openai-codex-responses", @@ -341,21 +330,6 @@ function buildDynamicModel( fallback, ); } - if (lower === "gpt-5.4-mini") { - return cloneTemplate( - template, - modelId, - { - provider: "openai-codex", - api: "openai-codex-responses", - baseUrl: OPENAI_CODEX_BASE_URL, - cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }, - contextWindow: 272_000, - maxTokens: 128_000, - }, - fallback, - ); - } if (lower === "gpt-5.3-codex-spark") { return cloneTemplate( template, diff --git a/src/agents/pi-embedded-runner/model.test.ts b/src/agents/pi-embedded-runner/model.test.ts index f1913cc9656..d23b0a734d9 100644 --- a/src/agents/pi-embedded-runner/model.test.ts +++ b/src/agents/pi-embedded-runner/model.test.ts @@ -4,11 +4,15 @@ import { createProviderRuntimeTestMock } from "./model.provider-runtime.test-sup vi.mock("../model-suppression.js", () => ({ shouldSuppressBuiltInModel: ({ provider, id }: { provider?: string; id?: string }) => - (provider === "openai" || + ((provider === "openai" || provider === "azure-openai-responses" || provider === "openai-codex") && - id?.trim().toLowerCase() === "gpt-5.3-codex-spark", + id?.trim().toLowerCase() === "gpt-5.3-codex-spark") || + (provider === "openai-codex" && id?.trim().toLowerCase() === "gpt-5.4-mini"), buildSuppressedBuiltInModelError: ({ provider, id }: { provider?: string; id?: string }) => { + if (provider === "openai-codex" && id?.trim().toLowerCase() === "gpt-5.4-mini") { + return "Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth."; + } if ( (provider !== "openai" && provider !== "azure-openai-responses" && @@ -1363,13 +1367,15 @@ describe("resolveModel", () => { }); }); - it("builds an openai-codex fallback for gpt-5.4-mini", () => { + it("does not build an openai-codex fallback for unsupported gpt-5.4-mini", () => { mockOpenAICodexTemplateModel(discoverModels); const result = resolveModelForTest("openai-codex", "gpt-5.4-mini", "/tmp/agent"); - expect(result.error).toBeUndefined(); - expect(result.model).toMatchObject(buildOpenAICodexForwardCompatExpectation("gpt-5.4-mini")); + expect(result.model).toBeUndefined(); + expect(result.error).toBe( + "Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth.", + ); }); it("does not build an openai-codex fallback for removed gpt-5.3-codex-spark", () => { @@ -1763,7 +1769,7 @@ describe("resolveModel", () => { }); }); - it("keeps exact discovered metadata for other openai-codex models", () => { + it("rejects stale discovered openai-codex gpt-5.4-mini rows", () => { mockDiscoveredModel(discoverModels, { provider: "openai-codex", modelId: "gpt-5.4-mini", @@ -1777,15 +1783,10 @@ describe("resolveModel", () => { const result = resolveModelForTest("openai-codex", "gpt-5.4-mini", "/tmp/agent"); - expect(result.error).toBeUndefined(); - expect(result.model).toMatchObject({ - provider: "openai-codex", - id: "gpt-5.4-mini", - api: "openai-codex-responses", - baseUrl: "https://chatgpt.com/backend-api", - contextWindow: 64_000, - input: ["text"], - }); + expect(result.model).toBeUndefined(); + expect(result.error).toBe( + "Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth.", + ); }); it("rejects stale direct openai gpt-5.3-codex-spark discovery rows", () => {