From e7ad2b9817c377cd16f11bed549c9198358e9685 Mon Sep 17 00:00:00 2001 From: auyua9 Date: Sat, 25 Apr 2026 11:39:07 +0800 Subject: [PATCH 1/3] fix(ast-grep): restore pattern hints in tools --- src/tools/ast-grep/tools.test.ts | 55 ++++++++++++++++++++++++++++++++ src/tools/ast-grep/tools.ts | 45 ++++++-------------------- 2 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 src/tools/ast-grep/tools.test.ts diff --git a/src/tools/ast-grep/tools.test.ts b/src/tools/ast-grep/tools.test.ts new file mode 100644 index 000000000..b02fa8ea4 --- /dev/null +++ b/src/tools/ast-grep/tools.test.ts @@ -0,0 +1,55 @@ +/// + +import { beforeEach, describe, expect, it, mock } from "bun:test" +import { AST_GREP_REPLACE_DESCRIPTION, AST_GREP_SEARCH_DESCRIPTION } from "./tool-descriptions" + +const runSgMock = mock(async () => ({ + matches: [], + totalMatches: 0, + truncated: false, +})) + +mock.module("./cli", () => ({ + runSg: runSgMock, +})) + +import { createAstGrepTools } from "./tools" + +describe("createAstGrepTools", () => { + beforeEach(() => { + runSgMock.mockClear() + }) + + it("#given the production tool factory #when creating tools #then exposes shared ast-grep descriptions", () => { + // given / when + const tools = createAstGrepTools({ directory: "/repo" } as never) + + // then + expect(tools.ast_grep_search.description).toBe(AST_GREP_SEARCH_DESCRIPTION) + expect(tools.ast_grep_replace.description).toBe(AST_GREP_REPLACE_DESCRIPTION) + expect(tools.ast_grep_search.description).toContain("NOT regex") + }) + + it("#given empty search results from a regex-shaped pattern #when executing #then appends the pattern hint", async () => { + // given + const tools = createAstGrepTools({ directory: "/repo" } as never) + + // when + const output = await tools.ast_grep_search.execute( + { pattern: "foo|bar", lang: "typescript" }, + {}, + ) + + // then + expect(output).toContain("No matches found") + expect(output).toContain("alternation") + expect(output).toContain("grep") + expect(runSgMock).toHaveBeenCalledWith({ + pattern: "foo|bar", + lang: "typescript", + paths: ["/repo"], + globs: undefined, + context: undefined, + }) + }) +}) diff --git a/src/tools/ast-grep/tools.ts b/src/tools/ast-grep/tools.ts index 98b2d0c7e..2a2454fd2 100644 --- a/src/tools/ast-grep/tools.ts +++ b/src/tools/ast-grep/tools.ts @@ -3,6 +3,12 @@ import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool" import { CLI_LANGUAGES } from "./constants" import { runSg } from "./cli" import { formatSearchResult, formatReplaceResult } from "./result-formatter" +import { getPatternHint } from "./pattern-hints" +import { + AST_GREP_REPLACE_DESCRIPTION, + AST_GREP_SEARCH_DESCRIPTION, + AST_GREP_SEARCH_PATTERN_PARAM, +} from "./tool-descriptions" import type { CliLanguage } from "./types" async function showOutputToUser(context: unknown, output: string): Promise { @@ -12,39 +18,11 @@ async function showOutputToUser(context: unknown, output: string): Promise await ctx.metadata?.({ metadata: { output } }) } -function getEmptyResultHint(pattern: string, lang: CliLanguage): string | null { - const src = pattern.trim() - - if (lang === "python") { - if (src.startsWith("class ") && src.endsWith(":")) { - const withoutColon = src.slice(0, -1) - return `Hint: Remove trailing colon. Try: "${withoutColon}"` - } - if ((src.startsWith("def ") || src.startsWith("async def ")) && src.endsWith(":")) { - const withoutColon = src.slice(0, -1) - return `Hint: Remove trailing colon. Try: "${withoutColon}"` - } - } - - if (["javascript", "typescript", "tsx"].includes(lang)) { - if (/^(export\s+)?(async\s+)?function\s+\$[A-Z_]+\s*$/i.test(src)) { - return `Hint: Function patterns need params and body. Try "function $NAME($$$) { $$$ }"` - } - } - - return null -} - export function createAstGrepTools(ctx: PluginInput): Record { const ast_grep_search: ToolDefinition = tool({ - description: - "Search code patterns across filesystem using AST-aware matching. Supports 25 languages. " + - "Use meta-variables: $VAR (single node), $$$ (multiple nodes). " + - "IMPORTANT: Patterns must be complete AST nodes (valid code). " + - "For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not 'export async function $NAME'. " + - "Examples: 'console.log($MSG)', 'def $FUNC($$$):', 'async function $NAME($$$)'", + description: AST_GREP_SEARCH_DESCRIPTION, args: { - pattern: tool.schema.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."), + pattern: tool.schema.string().describe(AST_GREP_SEARCH_PATTERN_PARAM), lang: tool.schema.enum(CLI_LANGUAGES).describe("Target language"), paths: tool.schema.array(tool.schema.string()).optional().describe("Paths to search (default: ['.'])"), globs: tool.schema.array(tool.schema.string()).optional().describe("Include/exclude globs (prefix ! to exclude)"), @@ -63,7 +41,7 @@ export function createAstGrepTools(ctx: PluginInput): Record Date: Sun, 26 Apr 2026 01:35:23 +0800 Subject: [PATCH 2/3] chore: retrigger ci From 5bbac51e6d58866f4b9bc89988737163fbf1fdbe Mon Sep 17 00:00:00 2001 From: auyua9 Date: Sun, 26 Apr 2026 02:00:41 +0800 Subject: [PATCH 3/3] test: align model expectations with GPT-5.5 defaults --- src/agents/utils.test.ts | 66 +++++++++---------- .../generate-omo-config.test.ts | 6 +- src/plugin-handlers/config-handler.test.ts | 4 +- src/shared/agent-variant.test.ts | 10 +-- .../model-capability-guardrails.test.ts | 2 +- .../look-at/multimodal-fallback-chain.test.ts | 16 ++--- 6 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/agents/utils.test.ts b/src/agents/utils.test.ts index 74e1145b3..51482652b 100644 --- a/src/agents/utils.test.ts +++ b/src/agents/utils.test.ts @@ -58,14 +58,14 @@ describe("createBuiltinAgents with model overrides", () => { const providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue(null) const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set()) const overrides = { - sisyphus: { model: "github-copilot/gpt-5.4" }, + sisyphus: { model: "github-copilot/gpt-5.5" }, } // #when const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], undefined, undefined) // #then - expect(agents.sisyphus.model).toBe("github-copilot/gpt-5.4") + expect(agents.sisyphus.model).toBe("github-copilot/gpt-5.5") expect(agents.sisyphus.reasoningEffort).toBe("medium") expect(agents.sisyphus.thinking).toBeUndefined() providerModelsSpy.mockRestore() @@ -75,9 +75,9 @@ describe("createBuiltinAgents with model overrides", () => { test("Atlas uses uiSelectedModel", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["openai/gpt-5.4", "anthropic/claude-sonnet-4-6"]) + new Set(["openai/gpt-5.5", "anthropic/claude-sonnet-4-6"]) ) - const uiSelectedModel = "openai/gpt-5.4" + const uiSelectedModel = "openai/gpt-5.5" try { // #when @@ -96,7 +96,7 @@ describe("createBuiltinAgents with model overrides", () => { // #then expect(agents.atlas).toBeDefined() - expect(agents.atlas.model).toBe("openai/gpt-5.4") + expect(agents.atlas.model).toBe("openai/gpt-5.5") } finally { fetchSpy.mockRestore() } @@ -105,9 +105,9 @@ describe("createBuiltinAgents with model overrides", () => { test("user config model takes priority over uiSelectedModel for sisyphus", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["openai/gpt-5.4", "anthropic/claude-sonnet-4-6"]) + new Set(["openai/gpt-5.5", "anthropic/claude-sonnet-4-6"]) ) - const uiSelectedModel = "openai/gpt-5.4" + const uiSelectedModel = "openai/gpt-5.5" const overrides = { sisyphus: { model: "google/antigravity-claude-opus-4-5-thinking" }, } @@ -138,9 +138,9 @@ describe("createBuiltinAgents with model overrides", () => { test("user config model takes priority over uiSelectedModel for atlas", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["openai/gpt-5.4", "anthropic/claude-sonnet-4-6"]) + new Set(["openai/gpt-5.5", "anthropic/claude-sonnet-4-6"]) ) - const uiSelectedModel = "openai/gpt-5.4" + const uiSelectedModel = "openai/gpt-5.5" const overrides = { atlas: { model: "google/antigravity-claude-opus-4-5-thinking" }, } @@ -263,14 +263,14 @@ describe("createBuiltinAgents with model overrides", () => { const providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue(null) const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set()) const overrides = { - sisyphus: { model: "github-copilot/gpt-5.4", temperature: 0.5 }, + sisyphus: { model: "github-copilot/gpt-5.5", temperature: 0.5 }, } // #when const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], undefined, undefined) // #then - expect(agents.sisyphus.model).toBe("github-copilot/gpt-5.4") + expect(agents.sisyphus.model).toBe("github-copilot/gpt-5.5") expect(agents.sisyphus.temperature).toBe(0.5) providerModelsSpy.mockRestore() fetchSpy.mockRestore() @@ -304,7 +304,7 @@ describe("createBuiltinAgents with model overrides", () => { "opencode/kimi-k2.5-free", "zai-coding-plan/glm-5", "opencode/big-pickle", - "openai/gpt-5.4", + "openai/gpt-5.5", ]) ) @@ -341,7 +341,7 @@ describe("createBuiltinAgents with model overrides", () => { test("excludes hidden custom agents from orchestrator prompts", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) const customAgentSummaries = [ @@ -377,7 +377,7 @@ describe("createBuiltinAgents with model overrides", () => { test("excludes disabled custom agents from orchestrator prompts", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) const customAgentSummaries = [ @@ -413,7 +413,7 @@ describe("createBuiltinAgents with model overrides", () => { test("excludes custom agents when disabledAgents contains their name (case-insensitive)", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) const disabledAgents = ["ReSeArChEr"] @@ -449,7 +449,7 @@ describe("createBuiltinAgents with model overrides", () => { test("does not advertise duplicate custom agents case-insensitively", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) const customAgentSummaries = [ @@ -481,7 +481,7 @@ describe("createBuiltinAgents with model overrides", () => { test("does not surface custom agent strings in orchestrator prompts", async () => { // #given const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) const customAgentSummaries = [ @@ -840,7 +840,7 @@ describe("Atlas is unaffected by environment context toggle", () => { beforeEach(() => { fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.4"]) + new Set(["anthropic/claude-opus-4-7", "openai/gpt-5.5"]) ) }) @@ -966,7 +966,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => { // #given - user configures a model from a plugin provider (like antigravity) // that is NOT in the availableModels cache and NOT in the fallback chain const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( - new Set(["openai/gpt-5.4"]) + new Set(["openai/gpt-5.5"]) ) const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue( ["openai"] @@ -1016,7 +1016,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => { test("atlas and metis resolve to OpenAI in an OpenAI-only environment without a system default", async () => { // #given - const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set(["openai/gpt-5.4"])) + const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(new Set(["openai/gpt-5.5"])) const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"]) try { @@ -1025,10 +1025,10 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => { // #then expect(agents.atlas).toBeDefined() - expect(agents.atlas.model).toBe("openai/gpt-5.4") + expect(agents.atlas.model).toBe("openai/gpt-5.5") expect(agents.atlas.variant).toBe("medium") expect(agents.metis).toBeDefined() - expect(agents.metis.model).toBe("openai/gpt-5.4") + expect(agents.metis.model).toBe("openai/gpt-5.5") expect(agents.metis.variant).toBe("high") } finally { fetchSpy.mockRestore() @@ -1096,7 +1096,7 @@ describe("buildAgent with category and skills", () => { const categories = { "custom-category": { - model: "openai/gpt-5.4", + model: "openai/gpt-5.5", variant: "xhigh", }, } @@ -1105,7 +1105,7 @@ describe("buildAgent with category and skills", () => { const agent = buildAgent(source["test-agent"], TEST_MODEL, categories) // #then - expect(agent.model).toBe("openai/gpt-5.4") + expect(agent.model).toBe("openai/gpt-5.5") expect(agent.variant).toBe("xhigh") }) @@ -1185,7 +1185,7 @@ describe("buildAgent with category and skills", () => { const agent = buildAgent(source["test-agent"], TEST_MODEL) // #then - category's built-in model and skills are applied - expect(agent.model).toBe("openai/gpt-5.4") + expect(agent.model).toBe("openai/gpt-5.5") expect(agent.variant).toBe("xhigh") expect(agent.prompt).toContain("Role: Designer-Turned-Developer") expect(agent.prompt).toContain("Task description") @@ -1309,9 +1309,9 @@ describe("override.category expansion in createBuiltinAgents", () => { // #when const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL) - // #then - ultrabrain category: model=openai/gpt-5.4, variant=xhigh + // #then - ultrabrain category: model=openai/gpt-5.5, variant=xhigh expect(agents.oracle).toBeDefined() - expect(agents.oracle.model).toBe("openai/gpt-5.4") + expect(agents.oracle.model).toBe("openai/gpt-5.5") expect(agents.oracle.variant).toBe("xhigh") }) @@ -1333,7 +1333,7 @@ describe("override.category expansion in createBuiltinAgents", () => { // #given - custom category has reasoningEffort=xhigh, direct override says "low" const categories = { "test-cat": { - model: "openai/gpt-5.4", + model: "openai/gpt-5.5", reasoningEffort: "xhigh" as const, }, } @@ -1353,7 +1353,7 @@ describe("override.category expansion in createBuiltinAgents", () => { // #given - custom category has reasoningEffort, no direct reasoningEffort in override const categories = { "reasoning-cat": { - model: "openai/gpt-5.4", + model: "openai/gpt-5.5", reasoningEffort: "high" as const, }, } @@ -1378,9 +1378,9 @@ describe("override.category expansion in createBuiltinAgents", () => { // #when const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL) - // #then - ultrabrain category: model=openai/gpt-5.4, variant=xhigh + // #then - ultrabrain category: model=openai/gpt-5.5, variant=xhigh expect(agents.sisyphus).toBeDefined() - expect(agents.sisyphus.model).toBe("openai/gpt-5.4") + expect(agents.sisyphus.model).toBe("openai/gpt-5.5") expect(agents.sisyphus.variant).toBe("xhigh") }) @@ -1393,9 +1393,9 @@ describe("override.category expansion in createBuiltinAgents", () => { // #when const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL) - // #then - ultrabrain category: model=openai/gpt-5.4, variant=xhigh + // #then - ultrabrain category: model=openai/gpt-5.5, variant=xhigh expect(agents.atlas).toBeDefined() - expect(agents.atlas.model).toBe("openai/gpt-5.4") + expect(agents.atlas.model).toBe("openai/gpt-5.5") expect(agents.atlas.variant).toBe("xhigh") }) diff --git a/src/cli/config-manager/generate-omo-config.test.ts b/src/cli/config-manager/generate-omo-config.test.ts index bebf2f94a..3e4f9aa86 100644 --- a/src/cli/config-manager/generate-omo-config.test.ts +++ b/src/cli/config-manager/generate-omo-config.test.ts @@ -96,10 +96,10 @@ describe("generateOmoConfig - model fallback system", () => { const result = generateOmoConfig(config) //#then - expect((result.agents as Record).sisyphus.model).toBe("openai/gpt-5.4") + expect((result.agents as Record).sisyphus.model).toBe("openai/gpt-5.5") expect((result.agents as Record).sisyphus.variant).toBe("medium") expect((result.agents as Record).oracle.model).toBe("openai/gpt-5.5") - expect((result.agents as Record)['multimodal-looker'].model).toBe("openai/gpt-5.4") + expect((result.agents as Record)['multimodal-looker'].model).toBe("openai/gpt-5.5") }) test("adds fallback_models when multiple providers are available", () => { @@ -134,7 +134,7 @@ describe("generateOmoConfig - model fallback system", () => { expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-7") expect(agents.sisyphus.fallback_models).toEqual([ { - model: "openai/gpt-5.4", + model: "openai/gpt-5.5", variant: "medium", }, ]) diff --git a/src/plugin-handlers/config-handler.test.ts b/src/plugin-handlers/config-handler.test.ts index 344dba4c3..0c4ea1cea 100644 --- a/src/plugin-handlers/config-handler.test.ts +++ b/src/plugin-handlers/config-handler.test.ts @@ -750,7 +750,7 @@ describe("Prometheus category config resolution", () => { // then expect(config).toBeDefined() - expect(config?.model).toBe("openai/gpt-5.4") + expect(config?.model).toBe("openai/gpt-5.5") expect(config?.variant).toBe("xhigh") }) @@ -810,7 +810,7 @@ describe("Prometheus category config resolution", () => { // then - falls back to DEFAULT_CATEGORIES expect(config).toBeDefined() - expect(config?.model).toBe("openai/gpt-5.4") + expect(config?.model).toBe("openai/gpt-5.5") expect(config?.variant).toBe("xhigh") }) diff --git a/src/shared/agent-variant.test.ts b/src/shared/agent-variant.test.ts index 963b6c3e6..1596c291f 100644 --- a/src/shared/agent-variant.test.ts +++ b/src/shared/agent-variant.test.ts @@ -36,7 +36,7 @@ describe("resolveAgentVariant", () => { sisyphus: { category: "ultrabrain" }, }, categories: { - ultrabrain: { model: "openai/gpt-5.4", variant: "xhigh" }, + ultrabrain: { model: "openai/gpt-5.5", variant: "xhigh" }, }, } as OhMyOpenCodeConfig @@ -124,10 +124,10 @@ describe("resolveVariantForModel", () => { expect(variant).toBe("medium") }) - test("returns medium for openai/gpt-5.4 in sisyphus chain", () => { - // #given openai/gpt-5.4 is now in sisyphus fallback chain with variant medium + test("returns medium for openai/gpt-5.5 in sisyphus chain", () => { + // #given openai/gpt-5.5 is now in sisyphus fallback chain with variant medium const config = {} as OhMyOpenCodeConfig - const model = { providerID: "openai", modelID: "gpt-5.4" } + const model = { providerID: "openai", modelID: "gpt-5.5" } // when const variant = resolveVariantForModel(config, "sisyphus", model) @@ -179,7 +179,7 @@ describe("resolveVariantForModel", () => { "custom-agent": { category: "ultrabrain" }, }, } as OhMyOpenCodeConfig - const model = { providerID: "openai", modelID: "gpt-5.4" } + const model = { providerID: "openai", modelID: "gpt-5.5" } // when const variant = resolveVariantForModel(config, "custom-agent", model) diff --git a/src/shared/model-capability-guardrails.test.ts b/src/shared/model-capability-guardrails.test.ts index 63ff3aab2..3c818850a 100644 --- a/src/shared/model-capability-guardrails.test.ts +++ b/src/shared/model-capability-guardrails.test.ts @@ -20,7 +20,7 @@ describe("model-capability-guardrails", () => { expect(modelIDs).toEqual([...modelIDs].sort()) expect(new Set(modelIDs).size).toBe(modelIDs.length) expect(modelIDs).toContain("claude-opus-4-7") - expect(modelIDs).toContain("gpt-5.4") + expect(modelIDs).toContain("gpt-5.5") expect(modelIDs).toContain("kimi-k2.5") }) diff --git a/src/tools/look-at/multimodal-fallback-chain.test.ts b/src/tools/look-at/multimodal-fallback-chain.test.ts index 4d614d070..d334383cf 100644 --- a/src/tools/look-at/multimodal-fallback-chain.test.ts +++ b/src/tools/look-at/multimodal-fallback-chain.test.ts @@ -5,36 +5,36 @@ describe("buildMultimodalLookerFallbackChain", () => { // given const { buildMultimodalLookerFallbackChain } = await import("./multimodal-fallback-chain") const visionCapableModels = [ - { providerID: "openai", modelID: "gpt-5.4" }, - { providerID: "opencode", modelID: "gpt-5.4" }, + { providerID: "openai", modelID: "gpt-5.5" }, + { providerID: "opencode", modelID: "gpt-5.5" }, ] // when const result = buildMultimodalLookerFallbackChain(visionCapableModels) // then - const gpt54Entries = result.filter((entry) => entry.model === "gpt-5.4") - expect(gpt54Entries.length).toBeGreaterThan(0) + const gpt55Entries = result.filter((entry) => entry.model === "gpt-5.5") + expect(gpt55Entries.length).toBeGreaterThan(0) }) it("avoids duplicates when adding hardcoded entries", async () => { // given const { buildMultimodalLookerFallbackChain } = await import("./multimodal-fallback-chain") - const visionCapableModels = [{ providerID: "openai", modelID: "gpt-5.4" }] + const visionCapableModels = [{ providerID: "openai", modelID: "gpt-5.5" }] // when const result = buildMultimodalLookerFallbackChain(visionCapableModels) // then expect(result.length).toBeGreaterThan(0) - expect(result[0].model).toBe("gpt-5.4") + expect(result[0].model).toBe("gpt-5.5") expect(result[0].providers).toContain("openai") }) it("preserves hardcoded variant metadata for cache-derived entries", async () => { // given const { buildMultimodalLookerFallbackChain } = await import("./multimodal-fallback-chain") - const visionCapableModels = [{ providerID: "openai", modelID: "gpt-5.4" }] + const visionCapableModels = [{ providerID: "openai", modelID: "gpt-5.5" }] // when const result = buildMultimodalLookerFallbackChain(visionCapableModels) @@ -42,7 +42,7 @@ describe("buildMultimodalLookerFallbackChain", () => { // then expect(result[0]).toEqual({ providers: ["openai"], - model: "gpt-5.4", + model: "gpt-5.5", variant: "medium", }) })