test(openai): cover GPT-5.5 defaults

This commit is contained in:
Peter Steinberger
2026-04-23 20:00:51 +01:00
parent a36903b94c
commit cd5bc2fc93
65 changed files with 437 additions and 181 deletions

View File

@@ -224,7 +224,7 @@ describe("openai codex provider", () => {
},
},
],
defaultModel: "openai-codex/gpt-5.4",
defaultModel: "openai-codex/gpt-5.5",
});
expect(result?.profiles[0]?.credential).not.toHaveProperty("idToken");
expect(result?.profiles[0]?.credential).not.toHaveProperty("accountId");
@@ -329,6 +329,40 @@ describe("openai codex provider", () => {
});
});
it("resolves gpt-5.5 and gpt-5.5-pro with launch pricing and codex-sized runtime cap", () => {
const provider = buildOpenAICodexProviderPlugin();
const model = provider.resolveDynamicModel?.({
provider: "openai-codex",
modelId: "gpt-5.5",
modelRegistry: createSingleModelRegistry(createCodexTemplate({ id: "gpt-5.4" })) as never,
});
const pro = provider.resolveDynamicModel?.({
provider: "openai-codex",
modelId: "gpt-5.5-pro",
modelRegistry: createSingleModelRegistry(createCodexTemplate({ id: "gpt-5.4-pro" })) as never,
});
expect(model).toMatchObject({
id: "gpt-5.5",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
contextWindow: 1_000_000,
contextTokens: 272_000,
maxTokens: 128_000,
cost: { input: 5, output: 30, cacheRead: 0, cacheWrite: 0 },
});
expect(pro).toMatchObject({
id: "gpt-5.5-pro",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
contextWindow: 1_000_000,
contextTokens: 272_000,
maxTokens: 128_000,
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
});
});
it("resolves gpt-5.4-pro from a gpt-5.4 runtime template when legacy codex rows are absent", () => {
const provider = buildOpenAICodexProviderPlugin();
@@ -398,7 +432,7 @@ describe("openai codex provider", () => {
expect(model).not.toHaveProperty("contextTokens");
});
it("augments catalog with gpt-5.4 native contextWindow and runtime cap", () => {
it("augments catalog with gpt-5.5 and gpt-5.4 native metadata", () => {
const provider = buildOpenAICodexProviderPlugin();
const entries = provider.augmentModelCatalog?.({
@@ -415,6 +449,22 @@ describe("openai codex provider", () => {
],
} as never);
expect(entries).toContainEqual(
expect.objectContaining({
id: "gpt-5.5",
contextWindow: 1_000_000,
contextTokens: 272_000,
cost: { input: 5, output: 30, cacheRead: 0, cacheWrite: 0 },
}),
);
expect(entries).toContainEqual(
expect.objectContaining({
id: "gpt-5.5-pro",
contextWindow: 1_000_000,
contextTokens: 272_000,
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
}),
);
expect(entries).toContainEqual(
expect.objectContaining({
id: "gpt-5.4",

View File

@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
import { buildOpenAIProvider } from "./openai-provider.js";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const DEFAULT_LIVE_MODEL_IDS = ["gpt-5.4-mini", "gpt-5.4-nano"] as const;
const DEFAULT_LIVE_MODEL_IDS = ["gpt-5.5", "gpt-5.4-mini", "gpt-5.4-nano"] as const;
const liveEnabled = OPENAI_API_KEY.trim().length > 0 && process.env.OPENCLAW_LIVE_TEST === "1";
const describeLive = liveEnabled ? describe : describe.skip;
@@ -18,6 +18,24 @@ type LiveModelCase = {
function resolveLiveModelCase(modelId: string): LiveModelCase {
switch (modelId) {
case "gpt-5.5":
return {
modelId,
templateId: "gpt-5.4",
templateName: "GPT-5.4",
cost: { input: 5, output: 30, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1_000_000,
maxTokens: 128_000,
};
case "gpt-5.5-pro":
return {
modelId,
templateId: "gpt-5.4-pro",
templateName: "GPT-5.4 Pro",
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1_000_000,
maxTokens: 128_000,
};
case "gpt-5.4":
return {
modelId,

View File

@@ -229,6 +229,101 @@ describe("buildOpenAIProvider", () => {
});
});
it("resolves gpt-5.5 and gpt-5.5-pro with launch metadata", () => {
const provider = buildOpenAIProvider();
const model = provider.resolveDynamicModel?.({
provider: "openai",
modelId: "gpt-5.5",
modelRegistry: {
find: (_provider: string, id: string) =>
id === "gpt-5.4"
? {
id,
name: "GPT-5.4",
provider: "openai",
api: "openai-responses",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
input: ["text", "image"],
cost: { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },
contextWindow: 1_050_000,
maxTokens: 128_000,
}
: null,
} as never,
});
const pro = provider.resolveDynamicModel?.({
provider: "openai",
modelId: "gpt-5.5-pro",
modelRegistry: {
find: (_provider: string, id: string) =>
id === "gpt-5.4-pro"
? {
id,
name: "GPT-5.4 Pro",
provider: "openai",
api: "openai-responses",
baseUrl: "https://api.openai.com/v1",
reasoning: true,
input: ["text", "image"],
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1_050_000,
maxTokens: 128_000,
}
: null,
} as never,
});
expect(model).toMatchObject({
provider: "openai",
id: "gpt-5.5",
api: "openai-responses",
baseUrl: "https://api.openai.com/v1",
contextWindow: 1_000_000,
maxTokens: 128_000,
cost: { input: 5, output: 30, cacheRead: 0, cacheWrite: 0 },
});
expect(pro).toMatchObject({
provider: "openai",
id: "gpt-5.5-pro",
api: "openai-responses",
baseUrl: "https://api.openai.com/v1",
contextWindow: 1_000_000,
maxTokens: 128_000,
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
});
});
it("surfaces gpt-5.5 in xhigh and augmented catalog metadata", () => {
const provider = buildOpenAIProvider();
expect(
provider
.resolveThinkingProfile?.({
provider: "openai",
modelId: "gpt-5.5",
} as never)
?.levels.some((level) => level.id === "xhigh"),
).toBe(true);
const entries = provider.augmentModelCatalog?.({
env: process.env,
entries: [{ provider: "openai", id: "gpt-5.4", name: "GPT-5.4" }],
} as never);
expect(entries).toContainEqual(
expect.objectContaining({
provider: "openai",
id: "gpt-5.5",
name: "gpt-5.5",
reasoning: true,
input: ["text", "image"],
contextWindow: 1_000_000,
}),
);
});
it("keeps modern live selection on OpenAI 5.2+ and Codex 5.2+", () => {
const provider = buildOpenAIProvider();
const codexProvider = buildOpenAICodexProviderPlugin();
@@ -251,6 +346,12 @@ describe("buildOpenAIProvider", () => {
modelId: "gpt-5.4",
} as never),
).toBe(true);
expect(
provider.isModernModelRef?.({
provider: "openai",
modelId: "gpt-5.5",
} as never),
).toBe(true);
expect(
codexProvider.isModernModelRef?.({
@@ -276,6 +377,12 @@ describe("buildOpenAIProvider", () => {
modelId: "gpt-5.4",
} as never),
).toBe(true);
expect(
codexProvider.isModernModelRef?.({
provider: "openai-codex",
modelId: "gpt-5.5",
} as never),
).toBe(true);
});
it("owns replay policy for OpenAI and Codex transports", () => {

View File

@@ -17,7 +17,7 @@ import { runRealtimeSttLiveTest } from "../../test/helpers/stt-live-audio.js";
import plugin from "./index.js";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const LIVE_MODEL_ID = process.env.OPENCLAW_LIVE_OPENAI_PLUGIN_MODEL?.trim() || "gpt-5.4-nano";
const LIVE_MODEL_ID = process.env.OPENCLAW_LIVE_OPENAI_PLUGIN_MODEL?.trim() || "gpt-5.5";
const LIVE_IMAGE_MODEL = process.env.OPENCLAW_LIVE_OPENAI_IMAGE_MODEL?.trim() || "gpt-image-2";
const LIVE_VISION_MODEL = process.env.OPENCLAW_LIVE_OPENAI_VISION_MODEL?.trim() || "gpt-4.1-mini";
const liveEnabled = OPENAI_API_KEY.trim().length > 0 && process.env.OPENCLAW_LIVE_TEST === "1";
@@ -29,6 +29,8 @@ const ModelRegistryCtor = ModelRegistry as unknown as {
function resolveTemplateModelId(modelId: string) {
switch (modelId) {
case "gpt-5.5":
return "gpt-5.4";
case "gpt-5.4":
return "gpt-5.2";
case "gpt-5.4-mini":

View File

@@ -1,6 +1,7 @@
import { beforeEach, describe, it, vi } from "vitest";
import {
expectAugmentedCodexCatalog,
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
expectCodexBuiltInSuppression,
expectCodexMissingAuthHint,
importProviderRuntimeCatalogModule,
@@ -116,7 +117,10 @@ export function describeOpenAIProviderCatalogContract() {
it("keeps bundled model augmentation wired through the provider runtime", async () => {
const { augmentModelCatalogWithProviderPlugins } = await contractDepsPromise;
await expectAugmentedCodexCatalog(augmentModelCatalogWithProviderPlugins);
await expectAugmentedCodexCatalog(
augmentModelCatalogWithProviderPlugins,
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
);
});
},
);

View File

@@ -223,7 +223,7 @@ describe("runQaCharacterEval", () => {
expect(runSuite).toHaveBeenCalledTimes(8);
expect(runSuite.mock.calls.map(([params]) => params.primaryModel)).toEqual([
"openai/gpt-5.4",
"openai/gpt-5.5",
"openai/gpt-5.2",
"openai/gpt-5",
"anthropic/claude-opus-4-6",
@@ -254,7 +254,7 @@ describe("runQaCharacterEval", () => {
]);
expect(runJudge).toHaveBeenCalledTimes(2);
expect(runJudge.mock.calls.map(([params]) => params.judgeModel)).toEqual([
"openai/gpt-5.4",
"openai/gpt-5.5",
"anthropic/claude-opus-4-6",
]);
expect(runJudge.mock.calls.map(([params]) => params.judgeThinkingDefault)).toEqual([

View File

@@ -1135,8 +1135,8 @@ describe("qa cli runtime", () => {
repoRoot: path.resolve("/tmp/openclaw-repo"),
transportId: "qa-channel",
providerMode: "live-frontier",
primaryModel: "openai/gpt-5.4",
alternateModel: "openai/gpt-5.4",
primaryModel: "openai/gpt-5.5",
alternateModel: "openai/gpt-5.5",
fastMode: undefined,
message: "read qa kickoff and reply short",
timeoutMs: undefined,
@@ -1166,7 +1166,7 @@ describe("qa cli runtime", () => {
it("defaults manual frontier runs onto Codex OAuth when the runtime resolver prefers it", async () => {
defaultQaRuntimeModelForMode.mockImplementation((mode, options) =>
mode === "live-frontier"
? "openai-codex/gpt-5.4"
? "openai-codex/gpt-5.5"
: defaultQaProviderModelForMode(mode as QaProviderModeInput, options),
);
@@ -1179,8 +1179,8 @@ describe("qa cli runtime", () => {
repoRoot: path.resolve("/tmp/openclaw-repo"),
transportId: "qa-channel",
providerMode: "live-frontier",
primaryModel: "openai-codex/gpt-5.4",
alternateModel: "openai-codex/gpt-5.4",
primaryModel: "openai-codex/gpt-5.5",
alternateModel: "openai-codex/gpt-5.5",
fastMode: undefined,
message: "read qa kickoff and reply short",
timeoutMs: undefined,

View File

@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { selectQaRunnerModelOptions } from "./model-catalog.runtime.js";
describe("qa runner model catalog", () => {
it("filters to available rows and prefers gpt-5.4 first", () => {
it("filters to available rows and prefers gpt-5.5 first", () => {
expect(
selectQaRunnerModelOptions([
{
@@ -13,8 +13,8 @@ describe("qa runner model catalog", () => {
missing: false,
},
{
key: "openai/gpt-5.4",
name: "gpt-5.4",
key: "openai/gpt-5.5",
name: "gpt-5.5",
input: "text,image",
available: true,
missing: false,
@@ -27,6 +27,6 @@ describe("qa runner model catalog", () => {
missing: false,
},
]).map((entry) => entry.key),
).toEqual(["openai/gpt-5.4", "anthropic/claude-sonnet-4-5"]);
).toEqual(["openai/gpt-5.5", "anthropic/claude-sonnet-4-5"]);
});
});

View File

@@ -34,7 +34,7 @@ describe("qa model selection runtime", () => {
resolveEnvApiKey.mockReturnValue({ apiKey: "sk-test" });
expect(resolveQaPreferredLiveModel()).toBeUndefined();
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai/gpt-5.4");
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai/gpt-5.5");
expect(loadAuthProfileStoreForRuntime).not.toHaveBeenCalled();
});
@@ -43,8 +43,8 @@ describe("qa model selection runtime", () => {
provider === "openai-codex" ? ["openai-codex:user@example.com"] : [],
);
expect(resolveQaPreferredLiveModel()).toBe("openai-codex/gpt-5.4");
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai-codex/gpt-5.4");
expect(resolveQaPreferredLiveModel()).toBe("openai-codex/gpt-5.5");
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai-codex/gpt-5.5");
});
it("keeps the OpenAI live default when stored OpenAI profiles are available", () => {
@@ -53,7 +53,7 @@ describe("qa model selection runtime", () => {
);
expect(resolveQaPreferredLiveModel()).toBeUndefined();
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai/gpt-5.4");
expect(defaultQaRuntimeModelForMode("live-frontier")).toBe("openai/gpt-5.5");
});
it("leaves mock defaults unchanged", () => {

View File

@@ -19,7 +19,7 @@ describe("extractQaFailureReplyText", () => {
it("classifies explicit provider auth guidance as a failure", () => {
expect(
extractQaFailureReplyText(
'⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4.',
'⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5.',
),
).toContain('No API key found for provider "openai".');
});
@@ -27,7 +27,7 @@ describe("extractQaFailureReplyText", () => {
it("classifies curated missing-key guidance as a failure", () => {
expect(
extractQaFailureReplyText(
"⚠️ Missing API key for OpenAI on the gateway. Use `openai-codex/gpt-5.4` for OAuth, or set `OPENAI_API_KEY`, then try again.",
"⚠️ Missing API key for OpenAI on the gateway. Use `openai-codex/gpt-5.5` for OAuth, or set `OPENAI_API_KEY`, then try again.",
),
).toContain("Missing API key for OpenAI on the gateway.");
});

View File

@@ -45,8 +45,8 @@ describe("qa run config", () => {
it("creates a live-by-default selection that arms every scenario", () => {
expect(createDefaultQaRunSelection(scenarios)).toEqual({
providerMode: "live-frontier",
primaryModel: "openai/gpt-5.4",
alternateModel: "openai/gpt-5.4",
primaryModel: "openai/gpt-5.5",
alternateModel: "openai/gpt-5.5",
fastMode: true,
scenarioIds: ["dm-chat-baseline", "thread-lifecycle"],
});
@@ -57,7 +57,7 @@ describe("qa run config", () => {
normalizeQaRunSelection(
{
providerMode: "live-frontier",
primaryModel: "openai/gpt-5.4",
primaryModel: "openai/gpt-5.5",
alternateModel: "",
fastMode: false,
scenarioIds: ["thread-lifecycle", "missing", "thread-lifecycle"],
@@ -66,8 +66,8 @@ describe("qa run config", () => {
),
).toEqual({
providerMode: "live-frontier",
primaryModel: "openai/gpt-5.4",
alternateModel: "openai/gpt-5.4",
primaryModel: "openai/gpt-5.5",
alternateModel: "openai/gpt-5.5",
fastMode: true,
scenarioIds: ["thread-lifecycle"],
});
@@ -99,13 +99,13 @@ describe("qa run config", () => {
});
it("keeps idle snapshots on static defaults so startup does not inspect auth profiles", () => {
defaultQaRuntimeModelForMode.mockReturnValue("openai-codex/gpt-5.4");
defaultQaRuntimeModelForMode.mockReturnValue("openai-codex/gpt-5.5");
defaultQaRuntimeModelForMode.mockClear();
expect(createIdleQaRunnerSnapshot(scenarios).selection).toMatchObject({
providerMode: "live-frontier",
primaryModel: "openai/gpt-5.4",
alternateModel: "openai/gpt-5.4",
primaryModel: "openai/gpt-5.5",
alternateModel: "openai/gpt-5.5",
});
expect(defaultQaRuntimeModelForMode).not.toHaveBeenCalled();
});
@@ -138,14 +138,14 @@ describe("qa run config", () => {
it("prefers the Codex OAuth default when the runtime resolver says it is available", () => {
defaultQaRuntimeModelForMode.mockImplementation((mode, options) =>
mode === "live-frontier"
? "openai-codex/gpt-5.4"
? "openai-codex/gpt-5.5"
: defaultQaProviderModelForMode(mode as QaProviderModeInput, options),
);
expect(createDefaultQaRunSelection(scenarios)).toEqual({
providerMode: "live-frontier",
primaryModel: "openai-codex/gpt-5.4",
alternateModel: "openai-codex/gpt-5.4",
primaryModel: "openai-codex/gpt-5.5",
alternateModel: "openai-codex/gpt-5.5",
fastMode: true,
scenarioIds: ["dm-chat-baseline", "thread-lifecycle"],
});

View File

@@ -137,7 +137,7 @@ describe("qa scenario catalog", () => {
expect(scenario.sourcePath).toBe("qa/scenarios/models/gpt54-thinking-visibility-switch.md");
expect(config?.requiredLiveProvider).toBe("openai");
expect(config?.requiredLiveModel).toBe("gpt-5.4");
expect(config?.requiredLiveModel).toBe("gpt-5.5");
expect(config?.offDirective).toBe("/think off");
expect(config?.maxDirective).toBe("/think max");
expect(config?.reasoningDirective).toBe("/reasoning on");
@@ -169,10 +169,10 @@ describe("qa scenario catalog", () => {
},
});
expect(config?.requiredProvider).toBe("openai");
expect(config?.requiredModel).toBe("gpt-5.4");
expect(config?.requiredModel).toBe("gpt-5.5");
expect(config?.expectedMarker).toBe("WEB-SEARCH-OK");
expect(scenario.execution.flow?.steps.map((step) => step.name)).toEqual([
"confirms live OpenAI GPT-5.4 web search auto mode",
"confirms live OpenAI GPT-5.5 web search auto mode",
"searches official OpenAI News through the live model",
]);
});
@@ -191,7 +191,7 @@ describe("qa scenario catalog", () => {
expect(scenario.sourcePath).toBe("qa/scenarios/models/thinking-slash-model-remap.md");
expect(config?.requiredProviderMode).toBe("live-frontier");
expect(config?.anthropicModelRef).toBe("anthropic/claude-sonnet-4-6");
expect(config?.openAiXhighModelRef).toBe("openai/gpt-5.4");
expect(config?.openAiXhighModelRef).toBe("openai/gpt-5.5");
expect(config?.noXhighModelRef).toBe("anthropic/claude-sonnet-4-6");
expect(scenario.execution.flow?.steps.map((step) => step.name)).toEqual([
"selects Anthropic and verifies adaptive options",

View File

@@ -35,7 +35,7 @@ describe("qa suite transport helpers", () => {
state.addOutboundMessage({
to: "dm:qa-operator",
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4.',
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5.',
senderId: "openclaw",
senderName: "OpenClaw QA",
});
@@ -117,7 +117,7 @@ describe("qa suite transport helpers", () => {
state.addOutboundMessage({
to: "dm:qa-operator",
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4.',
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5.',
senderId: "openclaw",
senderName: "OpenClaw QA",
});
@@ -164,7 +164,7 @@ describe("qa suite transport helpers", () => {
state.addOutboundMessage({
to: "dm:qa-operator",
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4.',
text: '⚠️ No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5.',
senderId: "openclaw",
senderName: "OpenClaw QA",
});

View File

@@ -37,8 +37,8 @@ GPT baseline:
```bash
pnpm openclaw qa suite \
--provider-mode live-frontier \
--model openai/gpt-5.4 \
--alt-model openai/gpt-5.4 \
--model openai/gpt-5.5 \
--alt-model openai/gpt-5.5 \
--fast \
--scenario approval-turn-tool-followthrough \
--scenario model-switch-tool-continuity \
@@ -104,8 +104,8 @@ GPT manual lane:
```bash
pnpm openclaw qa manual \
--provider-mode live-frontier \
--model openai/gpt-5.4 \
--alt-model openai/gpt-5.4 \
--model openai/gpt-5.5 \
--alt-model openai/gpt-5.5 \
--fast \
--message "read QA_KICKOFF_TASK.md, tell me what feels half-baked about this qa mission, and keep it to two short sentences"
```

View File

@@ -24,10 +24,10 @@ codeRefs:
- extensions/qa-lab/src/suite.ts
execution:
kind: flow
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model codex/gpt-5.4 --alt-model codex/gpt-5.4 --scenario codex-harness-no-meta-leak`.
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model codex/gpt-5.5 --alt-model codex/gpt-5.5 --scenario codex-harness-no-meta-leak`.
config:
requiredProvider: codex
requiredModel: gpt-5.4
requiredModel: gpt-5.5
harnessRuntime: codex
harnessFallback: none
expectedReply: QA_LEAK_OK
@@ -47,7 +47,7 @@ execution:
```yaml qa-flow
steps:
- name: confirms GPT-5.4 Codex harness target
- name: confirms GPT-5.5 Codex harness target
actions:
- set: selected
value:

View File

@@ -1,17 +1,17 @@
# GPT-5.4 thinking visibility switch
# GPT-5.5 thinking visibility switch
```yaml qa-scenario
id: gpt54-thinking-visibility-switch
title: GPT-5.4 thinking visibility switch
title: GPT-5.5 thinking visibility switch
surface: models
coverage:
primary:
- models.thinking
secondary:
- runtime.reasoning-visibility
objective: Verify GPT-5.4 can switch from disabled thinking to max thinking while reasoning display stays enabled.
objective: Verify GPT-5.5 can switch from disabled thinking to max thinking while reasoning display stays enabled.
successCriteria:
- Live runs target openai/gpt-5.4, not a mini or pro variant.
- Live runs target openai/gpt-5.5, not a mini or pro variant.
- The session enables reasoning display before the comparison turns.
- The disabled-thinking turn returns its visible marker without a Reasoning-prefixed message.
- The max-thinking turn returns its visible marker and a separate Reasoning-prefixed message.
@@ -27,10 +27,10 @@ codeRefs:
- extensions/qa-lab/src/providers/mock-openai/server.ts
execution:
kind: flow
summary: Toggle reasoning display and GPT-5.4 thinking between off/none and max/high, then verify visible reasoning only on the max turn.
summary: Toggle reasoning display and GPT-5.5 thinking between off/none and max/high, then verify visible reasoning only on the max turn.
config:
requiredLiveProvider: openai
requiredLiveModel: gpt-5.4
requiredLiveModel: gpt-5.5
offDirective: /think off
maxDirective: /think max
reasoningDirective: /reasoning on
@@ -60,7 +60,7 @@ steps:
- assert:
expr: "env.providerMode !== 'live-frontier' || (selected?.provider === config.requiredLiveProvider && selected?.model === config.requiredLiveModel)"
message:
expr: "`expected live GPT-5.4, got ${env.primaryModel}`"
expr: "`expected live GPT-5.5, got ${env.primaryModel}`"
- call: state.addInboundMessage
args:
- conversation:
@@ -133,9 +133,9 @@ steps:
value:
expr: "requests.find((request) => String(request.allInputText ?? '').includes(config.offPrompt))"
- assert:
expr: "String(offRequest?.model ?? '').includes('gpt-5.4')"
expr: "String(offRequest?.model ?? '').includes('gpt-5.5')"
message:
expr: "`expected GPT-5.4 off mock request, got ${String(offRequest?.model ?? '')}`"
expr: "`expected GPT-5.5 off mock request, got ${String(offRequest?.model ?? '')}`"
detailsExpr: "`off ack=${offAck.text}; off answer=${offAnswer.text}`"
- name: switches to max thinking
actions:
@@ -204,8 +204,8 @@ steps:
value:
expr: "requests.find((request) => String(request.allInputText ?? '').includes(config.maxPrompt))"
- assert:
expr: "String(maxRequest?.model ?? '').includes('gpt-5.4')"
expr: "String(maxRequest?.model ?? '').includes('gpt-5.5')"
message:
expr: "`expected GPT-5.4 mock request, got ${String(maxRequest?.model ?? '')}`"
expr: "`expected GPT-5.5 mock request, got ${String(maxRequest?.model ?? '')}`"
detailsExpr: "`answer=${maxAnswer.text}`"
```

View File

@@ -12,7 +12,7 @@ coverage:
objective: Verify a live OpenAI GPT model can use OpenAI native web_search when OpenClaw web search is enabled in auto mode.
successCriteria:
- A live-frontier run fails fast unless the selected primary provider is openai.
- The selected primary model is GPT-5.4, not a mini or pro variant.
- The selected primary model is GPT-5.5, not a mini or pro variant.
- Web search is enabled without pinning a managed web_search provider.
- The live reply includes the required marker plus an official OpenAI News URL and headline found through web search.
gatewayConfigPatch:
@@ -32,10 +32,10 @@ codeRefs:
- extensions/qa-lab/src/suite.ts
execution:
kind: flow
summary: Run with `OPENCLAW_LIVE_OPENAI_KEY="${OPENAI_API_KEY}" pnpm openclaw qa suite --provider-mode live-frontier --model openai/gpt-5.4 --alt-model openai/gpt-5.4 --scenario openai-native-web-search-live`.
summary: Run with `OPENCLAW_LIVE_OPENAI_KEY="${OPENAI_API_KEY}" pnpm openclaw qa suite --provider-mode live-frontier --model openai/gpt-5.5 --alt-model openai/gpt-5.5 --scenario openai-native-web-search-live`.
config:
requiredProvider: openai
requiredModel: gpt-5.4
requiredModel: gpt-5.5
expectedMarker: WEB-SEARCH-OK
failureMarker: WEB-SEARCH-FAILED
searchPrompt: |-
@@ -49,7 +49,7 @@ execution:
```yaml qa-flow
steps:
- name: confirms live OpenAI GPT-5.4 web search auto mode
- name: confirms live OpenAI GPT-5.5 web search auto mode
actions:
- call: waitForGatewayHealthy
args:

View File

@@ -13,8 +13,8 @@ coverage:
objective: Verify /think lists provider-owned levels and remaps stored thinking levels when /model changes provider capabilities.
successCriteria:
- Anthropic Claude Sonnet 4.6 advertises adaptive but not OpenAI-only xhigh or Opus max.
- A stored adaptive level remaps to medium when switching to OpenAI GPT-5.4.
- OpenAI GPT-5.4 advertises xhigh but not adaptive or max.
- A stored adaptive level remaps to medium when switching to OpenAI GPT-5.5.
- OpenAI GPT-5.5 advertises xhigh but not adaptive or max.
- A stored xhigh level remaps to high when switching to an Anthropic model without xhigh support.
docsRefs:
- docs/tools/thinking.md
@@ -33,7 +33,7 @@ execution:
config:
requiredProviderMode: live-frontier
anthropicModelRef: anthropic/claude-sonnet-4-6
openAiXhighModelRef: openai/gpt-5.4
openAiXhighModelRef: openai/gpt-5.5
noXhighModelRef: anthropic/claude-sonnet-4-6
conversationId: qa-thinking-slash-remap
```
@@ -165,7 +165,7 @@ steps:
- assert:
expr: "/Options: .*\\bxhigh\\b/i.test(openAiThinkStatus.text) && !/Options: .*\\badaptive\\b/i.test(openAiThinkStatus.text) && !/Options: .*\\bmax\\b/i.test(openAiThinkStatus.text)"
message:
expr: "`expected OpenAI GPT-5.4 /think options to include xhigh only, got ${openAiThinkStatus.text}`"
expr: "`expected OpenAI GPT-5.5 /think options to include xhigh only, got ${openAiThinkStatus.text}`"
detailsExpr: "`adaptive=${adaptiveAck.text}; switch=${openAiModelAck.text}; think=${openAiThinkStatus.text}`"
- name: maps xhigh to high on a model without xhigh
actions:

View File

@@ -11,7 +11,7 @@ coverage:
- models.codex-cli
objective: Verify the Codex app-server harness can plan and build a medium-complex self-contained browser game.
successCriteria:
- A live-frontier run fails fast unless the selected primary model is codex/gpt-5.4.
- A live-frontier run fails fast unless the selected primary model is codex/gpt-5.5.
- The scenario forces the Codex embedded harness and disables PI fallback.
- The prompt explicitly asks the agent to enter plan mode before editing.
- The agent writes a self-contained HTML game with a canvas loop, controls, scoring, waves, pause, and restart.
@@ -25,10 +25,10 @@ codeRefs:
- extensions/qa-lab/src/suite.ts
execution:
kind: flow
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model codex/gpt-5.4 --alt-model codex/gpt-5.4 --scenario medium-game-plan-codex-harness`.
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model codex/gpt-5.5 --alt-model codex/gpt-5.5 --scenario medium-game-plan-codex-harness`.
config:
requiredProvider: codex
requiredModel: gpt-5.4
requiredModel: gpt-5.5
harnessRuntime: codex
harnessFallback: none
artifactFile: star-garden-defenders-codex.html
@@ -52,7 +52,7 @@ execution:
```yaml qa-flow
steps:
- name: confirms GPT-5.4 Codex harness target
- name: confirms GPT-5.5 Codex harness target
actions:
- set: selected
value:

View File

@@ -9,9 +9,9 @@ coverage:
- workspace.planning
secondary:
- agents.pi-harness
objective: Verify GPT-5.4 can use the PI harness to plan and build a medium-complex self-contained browser game.
objective: Verify GPT-5.5 can use the PI harness to plan and build a medium-complex self-contained browser game.
successCriteria:
- A live-frontier run fails fast unless the selected primary model is openai/gpt-5.4.
- A live-frontier run fails fast unless the selected primary model is openai/gpt-5.5.
- The scenario forces the embedded PI harness before the build turn.
- The prompt explicitly asks the agent to enter plan mode before editing.
- The agent writes a self-contained HTML game with a canvas loop, controls, scoring, waves, pause, and restart.
@@ -25,10 +25,10 @@ codeRefs:
- extensions/qa-lab/src/suite.ts
execution:
kind: flow
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model openai/gpt-5.4 --alt-model openai/gpt-5.4 --scenario medium-game-plan-pi-harness`.
summary: Run with `pnpm openclaw qa suite --provider-mode live-frontier --model openai/gpt-5.5 --alt-model openai/gpt-5.5 --scenario medium-game-plan-pi-harness`.
config:
requiredProvider: openai
requiredModel: gpt-5.4
requiredModel: gpt-5.5
harnessRuntime: pi
harnessFallback: pi
artifactFile: star-garden-defenders-pi.html
@@ -52,7 +52,7 @@ execution:
```yaml qa-flow
steps:
- name: confirms GPT-5.4 PI harness target
- name: confirms GPT-5.5 PI harness target
actions:
- set: selected
value:

View File

@@ -46,7 +46,7 @@ function createBackendEntry(params: {
params.id === "claude-cli"
? "claude-cli/claude-sonnet-4-6"
: params.id === "codex-cli"
? "codex-cli/gpt-5.4"
? "codex-cli/gpt-5.5"
: params.id === "google-gemini-cli"
? "google-gemini-cli/gemini-3-flash-preview"
: undefined,
@@ -385,7 +385,7 @@ describe("resolveCliBackendLiveTest", () => {
it("returns plugin-owned live smoke metadata for codex", () => {
expect(resolveCliBackendLiveTest("codex-cli")).toEqual({
defaultModelRef: "codex-cli/gpt-5.4",
defaultModelRef: "codex-cli/gpt-5.5",
defaultImageProbe: true,
defaultMcpProbe: true,
dockerNpmPackage: "@openai/codex",

View File

@@ -117,7 +117,7 @@ vi.mock("../plugins/provider-runtime.js", () => ({
context: { listProfileIds: (providerId: string) => string[] };
}) => {
if (params.provider === "openai" && params.context.listProfileIds("openai-codex").length > 0) {
return 'No API key found for provider "openai". Use openai-codex/gpt-5.4.';
return 'No API key found for provider "openai". Use openai-codex/gpt-5.5.';
}
return undefined;
},
@@ -365,7 +365,7 @@ describe("getApiKeyForModel", () => {
} catch (err) {
error = err;
}
expect(String(error)).toContain("openai-codex/gpt-5.4");
expect(String(error)).toContain("openai-codex/gpt-5.5");
},
);
} finally {

View File

@@ -386,10 +386,14 @@ describe("isModernModelRef", () => {
it("includes plugin-advertised modern models", () => {
providerRuntimeMocks.resolveProviderModernModelRef.mockImplementation(({ provider, context }) =>
provider === "openai" &&
["gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini", "gpt-5.4-nano"].includes(context.modelId)
["gpt-5.5", "gpt-5.5-pro", "gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini", "gpt-5.4-nano"].includes(
context.modelId,
)
? true
: provider === "openai-codex" &&
["gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini"].includes(context.modelId)
["gpt-5.5", "gpt-5.5-pro", "gpt-5.4", "gpt-5.4-pro", "gpt-5.4-mini"].includes(
context.modelId,
)
? true
: provider === "opencode" && ["claude-opus-4-6", "gemini-3-pro"].includes(context.modelId)
? true
@@ -398,10 +402,14 @@ describe("isModernModelRef", () => {
: undefined,
);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.5" })).toBe(true);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.5-pro" })).toBe(true);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.4" })).toBe(true);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.4-pro" })).toBe(true);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.4-mini" })).toBe(true);
expect(isModernModelRef({ provider: "openai", id: "gpt-5.4-nano" })).toBe(true);
expect(isModernModelRef({ provider: "openai-codex", id: "gpt-5.5" })).toBe(true);
expect(isModernModelRef({ provider: "openai-codex", id: "gpt-5.5-pro" })).toBe(true);
expect(isModernModelRef({ provider: "openai-codex", id: "gpt-5.4" })).toBe(true);
expect(isModernModelRef({ provider: "openai-codex", id: "gpt-5.4-pro" })).toBe(true);
expect(isModernModelRef({ provider: "openai-codex", id: "gpt-5.4-mini" })).toBe(true);

View File

@@ -680,7 +680,7 @@ describeLive("live models (profile keys)", () => {
if (
model.provider === "openai" &&
model.api === "openai-responses" &&
model.id === "gpt-5.4"
(model.id === "gpt-5.5" || model.id === "gpt-5.4")
) {
logProgress(`${progressLabel}: tool-only regression`);
const noopTool = {

View File

@@ -805,7 +805,7 @@ describeCacheLive("pi embedded runner prompt caching (live)", () => {
provider: "openai",
api: "openai-responses",
envVar: "OPENCLAW_LIVE_OPENAI_CACHE_MODEL",
preferredModelIds: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.4"],
preferredModelIds: ["gpt-5.5", "gpt-5.4-mini", "gpt-5.4"],
});
logLiveCache(`openai model=${fixture.model.provider}/${fixture.model.id}`);
}, 120_000);

View File

@@ -162,7 +162,7 @@ describeCacheLive("MCP-style prompt caching (live)", () => {
provider: "openai",
api: "openai-responses",
envVar: "OPENCLAW_LIVE_OPENAI_CACHE_MODEL",
preferredModelIds: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.4"],
preferredModelIds: ["gpt-5.5", "gpt-5.4-mini", "gpt-5.4"],
});
logLiveCache(`openai mcp-style model=${fixture.model.provider}/${fixture.model.id}`);

View File

@@ -17,7 +17,7 @@ describeLive("provider response headers (live)", () => {
provider: "openai",
api: "openai-responses",
envVar: "OPENCLAW_LIVE_OPENAI_CACHE_MODEL",
preferredModelIds: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.4"],
preferredModelIds: ["gpt-5.5", "gpt-5.4-mini", "gpt-5.4"],
});
}, 120_000);

View File

@@ -82,7 +82,7 @@ describe("resolveSimpleCompletionSelectionForAgent", () => {
expect(selection).toEqual(
expect.objectContaining({
provider: "openai",
modelId: "gpt-5.4",
modelId: "gpt-5.5",
}),
);
});
@@ -118,7 +118,7 @@ describe("resolveSimpleCompletionSelectionForAgent", () => {
expect(selection).toEqual(
expect.objectContaining({
provider: "openai",
modelId: "gpt-5.4",
modelId: "gpt-5.5",
}),
);
});

View File

@@ -1517,7 +1517,7 @@ describe("runAgentTurnWithFallback", () => {
it("surfaces direct provider auth guidance for missing API keys", async () => {
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(
new Error(
'No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4. | No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.4 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.4.',
'No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5. | No API key found for provider "openai". You are authenticated with OpenAI Codex OAuth. Use openai-codex/gpt-5.5 (OAuth) or set OPENAI_API_KEY to use openai/gpt-5.5.',
),
);
@@ -1549,7 +1549,7 @@ describe("runAgentTurnWithFallback", () => {
expect(result.kind).toBe("final");
if (result.kind === "final") {
expect(result.payload.text).toBe(
"⚠️ Missing API key for OpenAI on the gateway. Use `openai-codex/gpt-5.4` for OAuth, or set `OPENAI_API_KEY`, then try again.",
"⚠️ Missing API key for OpenAI on the gateway. Use `openai-codex/gpt-5.5` for OAuth, or set `OPENAI_API_KEY`, then try again.",
);
}
});

View File

@@ -181,7 +181,7 @@ describe("runMemoryFlushIfNeeded", () => {
cfg: { agents: { defaults: { cliBackends: { "codex-cli": { command: "codex" } } } } },
followupRun: createTestFollowupRun({ provider: "codex-cli" }),
sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
defaultModel: "codex-cli/gpt-5.4",
defaultModel: "codex-cli/gpt-5.5",
agentCfgContextTokens: 100_000,
resolvedVerboseLevel: "off",
sessionEntry,

View File

@@ -83,8 +83,8 @@ vi.mock("../agents/model-selection.js", () => {
return primary?.primary;
};
const resolveDefaultRef = (cfg?: ConfigWithModels): ModelRef => {
const parsed = parseModelRefImpl(resolvePrimary(cfg) ?? "openai/gpt-5.4", "openai");
return parsed ?? { provider: "openai", model: "gpt-5.4" };
const parsed = parseModelRefImpl(resolvePrimary(cfg) ?? "openai/gpt-5.5", "openai");
return parsed ?? { provider: "openai", model: "gpt-5.5" };
};
const resolveModelConfig = (cfg: ConfigWithModels | undefined, ref: ModelRef) => {
const models = cfg?.agents?.defaults?.models ?? {};

View File

@@ -130,8 +130,8 @@ function createAcpEnabledConfig(home: string, storePath: string): OpenClawConfig
},
agents: {
defaults: {
model: { primary: "openai/gpt-5.4" },
models: { "openai/gpt-5.4": {} },
model: { primary: "openai/gpt-5.5" },
models: { "openai/gpt-5.5": {} },
workspace: path.join(home, "openclaw"),
},
},

View File

@@ -492,7 +492,7 @@ async function createDefaultProviderPlugins(): Promise<ProviderPlugin[]> {
flagName: "--openai-api-key",
envVar: "OPENAI_API_KEY",
promptMessage: "Enter OpenAI API key",
defaultModel: "openai/gpt-5.4",
defaultModel: "openai/gpt-5.5",
}),
await createApiKeyProvider({
providerId: "opencode",

View File

@@ -168,7 +168,7 @@ describe("promptAuthConfig", () => {
agents: {
defaults: {
models: {
"openai/gpt-5.4": { alias: "GPT" },
"openai/gpt-5.5": { alias: "GPT" },
"anthropic/claude-opus-4-6": { alias: "Opus" },
},
},
@@ -193,7 +193,7 @@ describe("promptAuthConfig", () => {
const result = await promptAuthConfig({}, makeRuntime(), noopPrompter);
expect(result.agents?.defaults?.models).toEqual({
"openai/gpt-5.4": { alias: "GPT" },
"openai/gpt-5.5": { alias: "GPT" },
"anthropic/claude-sonnet-4-6": {},
});
});

View File

@@ -36,7 +36,7 @@ describe("normalizeStoredCronJobs", () => {
jobId: "legacy-job",
schedule: { kind: "cron", cron: "*/5 * * * *", tz: "UTC" },
message: "say hi",
model: "openai/gpt-5.4",
model: "openai/gpt-5.5",
deliver: true,
provider: " TeLeGrAm ",
to: "12345",
@@ -73,7 +73,7 @@ describe("normalizeStoredCronJobs", () => {
expect(job?.payload).toMatchObject({
kind: "agentTurn",
message: "say hi",
model: "openai/gpt-5.4",
model: "openai/gpt-5.5",
});
});

View File

@@ -116,13 +116,13 @@ describe("promptDefaultModel", () => {
loadModelCatalog.mockResolvedValue([
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
},
{
provider: "openai-codex",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
},
]);
@@ -141,11 +141,11 @@ describe("promptDefaultModel", () => {
expect(options).toEqual(
expect.arrayContaining([
expect.objectContaining({
value: "openai/gpt-5.4",
value: "openai/gpt-5.5",
hint: expect.stringContaining("API key route"),
}),
expect.objectContaining({
value: "openai-codex/gpt-5.4",
value: "openai-codex/gpt-5.5",
hint: expect.stringContaining("ChatGPT OAuth route"),
}),
]),
@@ -156,8 +156,8 @@ describe("promptDefaultModel", () => {
loadModelCatalog.mockResolvedValue([
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
},
{
provider: "byteplus-plan",
@@ -171,7 +171,7 @@ describe("promptDefaultModel", () => {
const config = {
agents: {
defaults: {
model: "openai/gpt-5.4",
model: "openai/gpt-5.5",
},
},
} as OpenClawConfig;
@@ -276,8 +276,8 @@ describe("promptDefaultModel", () => {
loadModelCatalog.mockResolvedValue([
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
},
]);
providerModelPickerContributionRuntime.enabled = true;
@@ -343,8 +343,8 @@ describe("promptModelAllowlist", () => {
},
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.2",
id: "gpt-5.5",
name: "GPT-5.5",
},
]);
@@ -374,8 +374,8 @@ describe("promptModelAllowlist", () => {
},
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
},
{
provider: "openai",
@@ -396,7 +396,7 @@ describe("promptModelAllowlist", () => {
const options = multiselect.mock.calls[0]?.[0]?.options ?? [];
expect(options.map((opt: { value: string }) => opt.value)).toEqual([
"openai/gpt-5.4",
"openai/gpt-5.5",
"openai/gpt-5.4-mini",
]);
});
@@ -440,16 +440,16 @@ describe("applyModelAllowlist", () => {
agents: {
defaults: {
models: {
"openai/gpt-5.4": { alias: "gpt" },
"openai/gpt-5.5": { alias: "gpt" },
"anthropic/claude-opus-4-6": { alias: "opus" },
},
},
},
} as OpenClawConfig;
const next = applyModelAllowlist(config, ["openai/gpt-5.4"]);
const next = applyModelAllowlist(config, ["openai/gpt-5.5"]);
expect(next.agents?.defaults?.models).toEqual({
"openai/gpt-5.4": { alias: "gpt" },
"openai/gpt-5.5": { alias: "gpt" },
});
});
@@ -458,7 +458,7 @@ describe("applyModelAllowlist", () => {
agents: {
defaults: {
models: {
"openai/gpt-5.4": { alias: "gpt" },
"openai/gpt-5.5": { alias: "gpt" },
"anthropic/claude-opus-4-6": { alias: "opus" },
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
},
@@ -470,7 +470,7 @@ describe("applyModelAllowlist", () => {
scopeKeys: ["anthropic/claude-opus-4-6", "anthropic/claude-sonnet-4-6"],
});
expect(next.agents?.defaults?.models).toEqual({
"openai/gpt-5.4": { alias: "gpt" },
"openai/gpt-5.5": { alias: "gpt" },
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
});
});
@@ -480,7 +480,7 @@ describe("applyModelAllowlist", () => {
agents: {
defaults: {
models: {
"openai/gpt-5.4": { alias: "gpt" },
"openai/gpt-5.5": { alias: "gpt" },
},
},
},
@@ -515,15 +515,15 @@ describe("applyModelFallbacksFromSelection", () => {
const config = {
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-6", fallbacks: ["openai/gpt-5.4"] },
model: { primary: "anthropic/claude-opus-4-6", fallbacks: ["openai/gpt-5.5"] },
},
},
} as OpenClawConfig;
const next = applyModelFallbacksFromSelection(config, ["openai/gpt-5.4"]);
const next = applyModelFallbacksFromSelection(config, ["openai/gpt-5.5"]);
expect(next.agents?.defaults?.model).toEqual({
primary: "anthropic/claude-opus-4-6",
fallbacks: ["openai/gpt-5.4"],
fallbacks: ["openai/gpt-5.5"],
});
});
});

View File

@@ -298,7 +298,7 @@ describe("modelsAuthLoginCommand", () => {
},
},
],
defaultModel: "openai-codex/gpt-5.4",
defaultModel: "openai-codex/gpt-5.5",
});
mocks.resolvePluginProviders.mockReturnValue([
createProvider({
@@ -365,7 +365,7 @@ describe("modelsAuthLoginCommand", () => {
"Auth profile: openai-codex:user@example.com (openai-codex/oauth)",
);
expect(runtime.log).toHaveBeenCalledWith(
"Default model available: openai-codex/gpt-5.4 (use --set-default to apply)",
"Default model available: openai-codex/gpt-5.5 (use --set-default to apply)",
);
expect(runtime.log).toHaveBeenCalledWith(
"Tip: Codex-capable models can use native Codex web search. Enable it with openclaw configure --section web (recommended mode: cached). Docs: https://docs.openclaw.ai/tools/web",
@@ -585,7 +585,7 @@ describe("modelsAuthLoginCommand", () => {
"anthropic/claude-sonnet-4-6": { alias: "sonnet" },
"anthropic/claude-opus-4-6": { alias: "opus" },
"moonshot/kimi-k2.5": { alias: "kimi" },
"openai-codex/gpt-5.4": { alias: "gpt54" },
"openai-codex/gpt-5.5": { alias: "gpt55" },
};
currentConfig = { agents: { defaults: { models: existingModels } } };
runProviderAuth.mockResolvedValue({
@@ -602,8 +602,8 @@ describe("modelsAuthLoginCommand", () => {
},
},
],
configPatch: { agents: { defaults: { models: { "openai-codex/gpt-5.4": {} } } } },
defaultModel: "openai-codex/gpt-5.4",
configPatch: { agents: { defaults: { models: { "openai-codex/gpt-5.5": {} } } } },
defaultModel: "openai-codex/gpt-5.5",
});
await modelsAuthLoginCommand({ provider: "openai-codex" }, runtime);

View File

@@ -13,9 +13,9 @@ describe("resolveConfiguredEntries", () => {
const { entries } = resolveConfiguredEntries({
agents: {
defaults: {
model: { primary: "codex/gpt-5.4", fallbacks: ["codex/gpt-5.4-mini"] },
model: { primary: "codex/gpt-5.5", fallbacks: ["codex/gpt-5.4-mini"] },
models: {
"codex/gpt-5.4": { alias: "Codex" },
"codex/gpt-5.5": { alias: "Codex" },
"codex/gpt-5.4-mini": {},
},
},
@@ -23,7 +23,7 @@ describe("resolveConfiguredEntries", () => {
models: { providers: {} },
});
expect(entries.map((entry) => entry.key)).toEqual(["codex/gpt-5.4", "codex/gpt-5.4-mini"]);
expect(entries.map((entry) => entry.key)).toEqual(["codex/gpt-5.5", "codex/gpt-5.4-mini"]);
expect(entries[0]?.tags).toEqual(new Set(["default", "configured"]));
expect(entries[0]?.aliases).toEqual(["Codex"]);
expect(entries[1]?.tags).toEqual(new Set(["fallback#1", "configured"]));

View File

@@ -8,8 +8,8 @@ const mocks = vi.hoisted(() => ({
}),
loadProviderCatalogModelsForList: vi.fn().mockResolvedValue([
{
id: "gpt-5.4",
name: "gpt-5.4",
id: "gpt-5.5",
name: "gpt-5.5",
provider: "codex",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
@@ -57,7 +57,7 @@ describe("appendProviderCatalogRows", () => {
seenKeys: new Set(),
context: {
cfg: {
agents: { defaults: { model: { primary: "codex/gpt-5.4" } } },
agents: { defaults: { model: { primary: "codex/gpt-5.5" } } },
models: { providers: {} },
},
agentDir: "/tmp/openclaw-agent",
@@ -72,7 +72,7 @@ describe("appendProviderCatalogRows", () => {
expect(mocks.shouldSuppressBuiltInModel).not.toHaveBeenCalled();
expect(rows).toMatchObject([
{
key: "codex/gpt-5.4",
key: "codex/gpt-5.5",
available: true,
missing: false,
},

View File

@@ -429,8 +429,8 @@ describe("modelsStatusCommand auth overview", () => {
mocks.loadConfig.mockReturnValue({
agents: {
defaults: {
model: { primary: "codex/gpt-5.4", fallbacks: [] },
models: { "codex/gpt-5.4": {} },
model: { primary: "codex/gpt-5.5", fallbacks: [] },
models: { "codex/gpt-5.5": {} },
},
},
models: { providers: {} },

View File

@@ -64,7 +64,7 @@ describe("onboard auth provider config merges", () => {
agents: {
defaults: {
models: {
"openai/gpt-5.4": { alias: "GPT" },
"openai/gpt-5.5": { alias: "GPT" },
},
},
},
@@ -89,7 +89,7 @@ describe("onboard auth provider config merges", () => {
});
expect(next.agents?.defaults?.models).toEqual({
"openai/gpt-5.4": { alias: "GPT" },
"openai/gpt-5.5": { alias: "GPT" },
...agentModels,
});
});

View File

@@ -73,7 +73,7 @@ describe("applyDefaultModelChoice", () => {
});
it("uses applyDefaultConfig path when setDefaultModel is true", async () => {
const defaultModel = "openai/gpt-5.4";
const defaultModel = "openai/gpt-5.5";
const applied = await applyDefaultModelChoice({
config: {},
setDefaultModel: true,

View File

@@ -19,7 +19,7 @@ describe("status-overview-rows", () => {
"1 files · 2 chunks · plugin memory · ok(vector ready) · warn(fts ready) · muted(cache warm)",
},
{ Item: "Plugin compatibility", Value: "warn(1 notice · 1 plugin)" },
{ Item: "Sessions", Value: "2 active · default gpt-5.4 (12k ctx) · store.json" },
{ Item: "Sessions", Value: "2 active · default gpt-5.5 (12k ctx) · store.json" },
]),
);
});

View File

@@ -54,10 +54,10 @@ describe("status-overview-values", () => {
sessions: {
count: 2,
paths: ["store.json", "other.json"],
defaults: { model: "gpt-5.4", contextTokens: 12_000 },
defaults: { model: "gpt-5.5", contextTokens: 12_000 },
},
formatKTokens: (value) => `${Math.round(value / 1000)}k`,
}),
).toBe("2 active · default gpt-5.4 (12k ctx) · 2 stores");
).toBe("2 active · default gpt-5.5 (12k ctx) · 2 stores");
});
});

View File

@@ -79,17 +79,17 @@ describe("statusSummaryRuntime.resolveSessionModelRef", () => {
{
agents: {
defaults: {
model: { primary: "openai/gpt-5.4" },
model: { primary: "openai/gpt-5.5" },
},
},
} as never,
{
model: "gpt-5.4",
model: "gpt-5.5",
},
),
).toEqual({
provider: "openai",
model: "gpt-5.4",
model: "gpt-5.5",
});
});

View File

@@ -14,11 +14,11 @@ vi.mock("./status.summary.runtime.js", () => ({
classifySessionKey: vi.fn(() => "direct"),
resolveConfiguredStatusModelRef: vi.fn(() => ({
provider: "openai",
model: "gpt-5.4",
model: "gpt-5.5",
})),
resolveSessionModelRef: vi.fn(() => ({
provider: "openai",
model: "gpt-5.4",
model: "gpt-5.5",
})),
resolveContextTokensForModel: vi.fn(() => 200_000),
},
@@ -26,7 +26,7 @@ vi.mock("./status.summary.runtime.js", () => ({
vi.mock("../agents/defaults.js", () => ({
DEFAULT_CONTEXT_TOKENS: 200_000,
DEFAULT_MODEL: "gpt-5.4",
DEFAULT_MODEL: "gpt-5.5",
DEFAULT_PROVIDER: "openai",
}));

View File

@@ -92,14 +92,14 @@ export const baseStatusSummary = {
sessions: {
count: 2,
paths: ["store.json"],
defaults: { model: "gpt-5.4", contextTokens: 12_000 },
defaults: { model: "gpt-5.5", contextTokens: 12_000 },
recent: [
{
key: "session-key",
kind: "direct",
updatedAt: 1,
age: 5_000,
model: "gpt-5.4",
model: "gpt-5.5",
totalTokens: 12_000,
totalTokensFresh: true,
remainingTokens: 4_000,

View File

@@ -36,7 +36,7 @@ const describeLive = LIVE && ACP_BIND_LIVE ? describe : describe.skip;
const CONNECT_TIMEOUT_MS = 90_000;
const LIVE_TIMEOUT_MS = 240_000;
const DEFAULT_LIVE_CODEX_MODEL = "gpt-5.4";
const DEFAULT_LIVE_CODEX_MODEL = "gpt-5.5";
type LiveAcpAgent = "claude" | "codex" | "gemini";
function createSlackCurrentConversationBindingRegistry() {

View File

@@ -112,7 +112,7 @@ describe("gateway cli backend live helpers", () => {
);
expect(shouldRunCliModelSwitchProbe("claude-cli", "claude-cli/claude-sonnet-4-6")).toBe(true);
expect(shouldRunCliModelSwitchProbe("claude-cli", "claude-cli/claude-opus-4-6")).toBe(false);
expect(shouldRunCliModelSwitchProbe("codex-cli", "codex-cli/gpt-5.4")).toBe(false);
expect(shouldRunCliModelSwitchProbe("codex-cli", "codex-cli/gpt-5.5")).toBe(false);
});
it("lets env disable the model switch probe", async () => {

View File

@@ -515,7 +515,7 @@ describe("resolveGatewayLiveMaxModels", () => {
});
it("keeps explicit gateway model lists uncapped unless a cap is provided", () => {
process.env.OPENCLAW_LIVE_GATEWAY_MODELS = "openai/gpt-5.4,anthropic/claude-opus-4-6";
process.env.OPENCLAW_LIVE_GATEWAY_MODELS = "openai/gpt-5.5,anthropic/claude-opus-4-6";
delete process.env.OPENCLAW_LIVE_GATEWAY_MAX_MODELS;
delete process.env.OPENCLAW_LIVE_MAX_MODELS;

View File

@@ -344,8 +344,8 @@ function createChatContext(): Pick<
mockState.modelCatalog ?? [
{
provider: "openai",
id: "gpt-5.4",
name: "GPT-5.4",
id: "gpt-5.5",
name: "GPT-5.5",
input: ["text", "image"],
},
{

View File

@@ -122,7 +122,7 @@ describe("gateway startup primary model warmup", () => {
agents: {
defaults: {
model: {
primary: "codex-cli/gpt-5.4",
primary: "codex-cli/gpt-5.5",
},
cliBackends: {
"codex-cli": {

View File

@@ -17,7 +17,7 @@ describe("resolveDefaultMediaModel", () => {
"MiniMax-VL-01",
);
expect(resolveDefaultMediaModel({ providerId: "openai-codex", capability: "image" })).toBe(
"gpt-5.4",
"gpt-5.5",
);
expect(resolveDefaultMediaModel({ providerId: "moonshot", capability: "image" })).toBe(
"kimi-k2.6",

View File

@@ -94,12 +94,12 @@ describe("normalizePluginsConfig", () => {
name: "normalizes plugin subagent override policy settings",
subagent: {
allowModelOverride: true,
allowedModels: [" anthropic/claude-sonnet-4-6 ", "", "openai/gpt-5.4"],
allowedModels: [" anthropic/claude-sonnet-4-6 ", "", "openai/gpt-5.5"],
},
expected: {
allowModelOverride: true,
hasAllowedModelsConfig: true,
allowedModels: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"],
allowedModels: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.5"],
},
},
{

View File

@@ -17,11 +17,11 @@ describe("applyProviderAuthConfigPatch", () => {
};
it("merges default model maps by default so other providers survive login", () => {
const patch = { agents: { defaults: { models: { "openai-codex/gpt-5.4": {} } } } };
const patch = { agents: { defaults: { models: { "openai-codex/gpt-5.5": {} } } } };
const next = applyProviderAuthConfigPatch(base, patch);
expect(next.agents?.defaults?.models).toEqual({
...base.agents.defaults.models,
"openai-codex/gpt-5.4": {},
"openai-codex/gpt-5.5": {},
});
expect(next.agents?.defaults?.model).toEqual(base.agents.defaults.model);
});

View File

@@ -23,6 +23,15 @@ export const expectedAugmentedOpenaiCodexCatalogEntries = [
},
];
export const expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55 = [
{ provider: "openai", id: "gpt-5.5", name: "gpt-5.5" },
{ provider: "openai", id: "gpt-5.5-pro", name: "gpt-5.5-pro" },
...expectedAugmentedOpenaiCodexCatalogEntries.slice(0, 4),
{ provider: "openai-codex", id: "gpt-5.5", name: "gpt-5.5" },
{ provider: "openai-codex", id: "gpt-5.5-pro", name: "gpt-5.5-pro" },
...expectedAugmentedOpenaiCodexCatalogEntries.slice(4),
];
export function expectCodexMissingAuthHint(
buildProviderMissingAuthMessageWithPlugin: (params: {
provider: string;
@@ -44,7 +53,7 @@ export function expectCodexMissingAuthHint(
listProfileIds: (providerId) => (providerId === "openai-codex" ? ["p1"] : []),
},
}),
).toContain("openai-codex/gpt-5.4");
).toContain("openai-codex/gpt-5.5");
}
export function expectCodexBuiltInSuppression(
@@ -80,6 +89,7 @@ export async function expectAugmentedCodexCatalog(
entries: typeof openaiCodexCatalogEntries;
};
}) => Promise<unknown>,
expectedEntries = expectedAugmentedOpenaiCodexCatalogEntries,
) {
const result = (await augmentModelCatalogWithProviderPlugins({
env: process.env,
@@ -88,8 +98,8 @@ export async function expectAugmentedCodexCatalog(
entries: openaiCodexCatalogEntries,
},
})) as Array<Record<string, unknown>>;
expect(result).toHaveLength(expectedAugmentedOpenaiCodexCatalogEntries.length);
for (const entry of expectedAugmentedOpenaiCodexCatalogEntries) {
expect(result).toHaveLength(expectedEntries.length);
for (const entry of expectedEntries) {
expect(result).toContainEqual(expect.objectContaining(entry));
}
}

View File

@@ -943,7 +943,7 @@ describe("provider-runtime", () => {
{
...createOpenAiCatalogProviderPlugin({
buildMissingAuthMessage: () =>
'No API key found for provider "openai". Use openai-codex/gpt-5.4.',
'No API key found for provider "openai". Use openai-codex/gpt-5.5.',
buildUnknownModelHint,
}),
} as ProviderPlugin,

View File

@@ -437,7 +437,7 @@ describe("plugin status reports", () => {
enabled: true,
subagent: {
allowModelOverride: true,
allowedModels: ["openai/gpt-5.4"],
allowedModels: ["openai/gpt-5.5"],
hasAllowedModelsConfig: true,
},
},
@@ -467,7 +467,7 @@ describe("plugin status reports", () => {
expectInspectPolicy(inspect!, {
allowPromptInjection: undefined,
allowModelOverride: true,
allowedModels: ["openai/gpt-5.4"],
allowedModels: ["openai/gpt-5.5"],
hasAllowedModelsConfig: true,
});
expectPluginLoaderCall({ loadModules: true });
@@ -586,7 +586,7 @@ describe("plugin status reports", () => {
hooks: { allowPromptInjection: false },
subagent: {
allowModelOverride: true,
allowedModels: ["openai/gpt-5.4"],
allowedModels: ["openai/gpt-5.5"],
},
},
},
@@ -624,7 +624,7 @@ describe("plugin status reports", () => {
expectInspectPolicy(inspect!, {
allowPromptInjection: false,
allowModelOverride: true,
allowedModels: ["openai/gpt-5.4"],
allowedModels: ["openai/gpt-5.5"],
hasAllowedModelsConfig: true,
});
expect(inspect?.diagnostics).toEqual([

View File

@@ -99,8 +99,8 @@ const modelCatalogMocks = getSharedMocks("openclaw.trigger-handling.model-catalo
contextWindow: 200000,
},
{ provider: "openai", id: "gpt-4.1-mini", name: "GPT-4.1 mini" },
{ provider: "openai", id: "gpt-5.4", name: "GPT-5.2" },
{ provider: "openai-codex", id: "gpt-5.4", name: "GPT-5.2 (Codex)" },
{ provider: "openai", id: "gpt-5.5", name: "GPT-5.5" },
{ provider: "openai-codex", id: "gpt-5.5", name: "GPT-5.5 (Codex)" },
{ provider: "minimax", id: "MiniMax-M2.7", name: "MiniMax M2.7" },
]),
resetModelCatalogCacheForTest: vi.fn(),

View File

@@ -10,7 +10,7 @@ import {
import type { PluginRuntime } from "../../../src/plugins/runtime/types.js";
const DEFAULT_PROVIDER = "openai";
const DEFAULT_MODEL = "gpt-5.4";
const DEFAULT_MODEL = "gpt-5.5";
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends (...args: never[]) => unknown

View File

@@ -119,12 +119,12 @@ function buildOpenAICodexOAuthResult(params: {
agents: {
defaults: {
models: {
"openai-codex/gpt-5.4": {},
"openai-codex/gpt-5.5": {},
},
},
},
},
defaultModel: "openai-codex/gpt-5.4",
defaultModel: "openai-codex/gpt-5.5",
notes: undefined,
};
}

View File

@@ -1,5 +1,6 @@
export {
expectAugmentedCodexCatalog,
expectedAugmentedOpenaiCodexCatalogEntriesWithGpt55,
expectCodexBuiltInSuppression,
expectCodexMissingAuthHint,
} from "../../../src/plugins/provider-runtime.test-support.js";

View File

@@ -445,6 +445,34 @@ export function describeOpenAIProviderRuntimeContract(load: ProviderRuntimeContr
});
});
it("owns openai gpt-5.5 forward-compat resolution", () => {
const provider = requireProviderContractProvider("openai");
const model = provider.resolveDynamicModel?.({
provider: "openai",
modelId: "gpt-5.5",
modelRegistry: {
find: (_provider: string, id: string) =>
id === "gpt-5.4"
? createModel({
id,
provider: "openai",
baseUrl: "https://api.openai.com/v1",
input: ["text", "image"],
})
: null,
} as never,
});
expect(model).toMatchObject({
id: "gpt-5.5",
provider: "openai",
api: "openai-responses",
baseUrl: "https://api.openai.com/v1",
contextWindow: 1_000_000,
maxTokens: 128_000,
});
});
it("owns openai gpt-5.4 mini forward-compat resolution", () => {
const provider = requireProviderContractProvider("openai");
const model = provider.resolveDynamicModel?.({
@@ -542,6 +570,34 @@ export function describeOpenAIProviderRuntimeContract(load: ProviderRuntimeContr
});
});
it("owns forward-compat codex gpt-5.5 models", () => {
const provider = requireProviderContractProvider("openai-codex");
const model = provider.resolveDynamicModel?.({
provider: "openai-codex",
modelId: "gpt-5.5",
modelRegistry: {
find: (_provider: string, id: string) =>
id === "gpt-5.4"
? createModel({
id,
api: "openai-codex-responses",
provider: "openai-codex",
baseUrl: "https://chatgpt.com/backend-api",
})
: null,
} as never,
});
expect(model).toMatchObject({
id: "gpt-5.5",
provider: "openai-codex",
api: "openai-codex-responses",
contextWindow: 1_000_000,
contextTokens: 272_000,
maxTokens: 128_000,
});
});
it("owns forward-compat codex mini models", () => {
const provider = requireProviderContractProvider("openai-codex");
const model = provider.resolveDynamicModel?.({

View File

@@ -183,7 +183,7 @@ describe("buildAgentContext", () => {
id: "main",
workspace: "/tmp/agent-workspace",
model: {
primary: "openai/gpt-5.4",
primary: "openai/gpt-5.5",
fallbacks: ["openai-codex/gpt-5.2-codex"],
},
},
@@ -194,7 +194,7 @@ describe("buildAgentContext", () => {
);
expect(context.workspace).toBe("/tmp/agent-workspace");
expect(context.model).toBe("openai/gpt-5.4 (+1 fallback)");
expect(context.model).toBe("openai/gpt-5.5 (+1 fallback)");
expect(context.isDefault).toBe(true);
});
@@ -206,7 +206,7 @@ describe("buildAgentContext", () => {
defaults: {
workspace: "/tmp/default-workspace",
model: {
primary: "openai/gpt-5.4",
primary: "openai/gpt-5.5",
fallbacks: ["openai-codex/gpt-5.2-codex"],
},
},
@@ -219,6 +219,6 @@ describe("buildAgentContext", () => {
);
expect(context.workspace).toBe("/tmp/default-workspace");
expect(context.model).toBe("openai/gpt-5.4 (+1 fallback)");
expect(context.model).toBe("openai/gpt-5.5 (+1 fallback)");
});
});

View File

@@ -6,7 +6,7 @@ import { renderQuickSettings, type QuickSettingsProps } from "./config-quick.ts"
function createProps(overrides: Partial<QuickSettingsProps> = {}): QuickSettingsProps {
return {
currentModel: "gpt-5.4",
currentModel: "gpt-5.5",
thinkingLevel: "off",
fastMode: false,
onModelChange: vi.fn(),