From 51fd17a8c329aeb8b90b61327ba19a6e39dfd5d2 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 18 Aug 2026 16:23:00 -0400 Subject: [PATCH 1/2] fix(catalog): register plain Codex route for worker `-wm` SKUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex backend discovery advertises worker-mode SKUs under a `-wm` suffix (gpt-5.6-luna-wm). Authoritative discovery replaced the bundled catalog and kept those slugs verbatim, so a configured `openai-codex/gpt-5.6-luna` vanished from the resolved catalog and the resolver's fuzzy fallback selected `-wm` instead — a route some ChatGPT accounts reject. Model discovery now recognizes the `-wm` suffix: when the bundled Codex catalog ships the plain SKU, the `-wm` row is also registered under its plain id (re-derived so the 1M-window floor and daybreak pricing keyed on that slug still apply). Unknown `-wm` SKUs keep their authoritative verbatim slug and non-worker models are untouched, so distinct models and other providers are unaffected. --- packages/catalog/src/discovery/codex.ts | 114 +++++++++++--- packages/catalog/test/codex-discovery.test.ts | 144 ++++++++++++++++++ 2 files changed, 236 insertions(+), 22 deletions(-) diff --git a/packages/catalog/src/discovery/codex.ts b/packages/catalog/src/discovery/codex.ts index 9c950ee14..68d5f1f95 100644 --- a/packages/catalog/src/discovery/codex.ts +++ b/packages/catalog/src/discovery/codex.ts @@ -1,5 +1,6 @@ import { type } from "@oh-my-pi/omptype"; import { parseKnownModel, semverEqual } from "../identity/classify"; +import { getBundledModels } from "../models"; import { resolveOpenAIDaybreakStandardCost } from "../openai-pricing"; import type { FetchImpl, ModelSpec } from "../types"; import { discoveryFetch } from "../utils"; @@ -22,6 +23,21 @@ const GPT_5_6_CONTEXT_WINDOW = 372_000; */ const GPT_5_6_1M_CONTEXT_WINDOW = 1_000_000; const CODEX_GPT_5_6_1M_SLUGS: ReadonlySet = new Set(["gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]); +/** + * Codex advertises worker-mode SKUs under a `-wm` suffix (`gpt-5.6-luna-wm`). + * + * Those rows route through the same Codex backend as their plain SKU, but an + * authoritative discovery list that only advertises the `-wm` slug prunes the + * bundled plain model, leaving a configured `openai-codex/gpt-5.6-luna` + * unresolvable except via fuzzy fallback onto the `-wm` row — which this user's + * ChatGPT account rejects. The compatibility rule, scoped to Codex discovery: + * a `-wm` slug whose plain counterpart exists in the bundled Codex catalog is + * ALSO registered under its plain id (re-derived, not cloned, so the 1M-window + * floor and daybreak pricing keyed on the plain slug still apply). Models + * without a bundled plain counterpart stay verbatim, preserving authoritative + * discovery for genuinely distinct `-wm` SKUs. + */ +const CODEX_WORKER_SUFFIX = "-wm"; const CODEX_REMOTE_COMPACTION = { enabled: true, api: "openai-codex-responses", @@ -194,11 +210,26 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope } const entries = parsedResponse.models ?? parsedResponse.data ?? []; - const normalized: NormalizedCodexModel[] = []; + const parsedEntries: ParsedCodexModelEntry[] = []; for (const entry of entries) { - const model = normalizeCodexModelEntry(entry, baseUrl); - if (model) { - normalized.push(model); + const parsed = parseCodexModelEntry(entry); + if (parsed) { + parsedEntries.push(parsed); + } + } + + // A worker `-wm` slug gets an extra plain-id route only when the bundled + // catalog ships the plain SKU (the "safe" precondition); the backend's own + // plain slug wins over any synthesized clone, and unknown `-wm` SKUs stay + // verbatim. + const advertisedSlugs = new Set(parsedEntries.map(parsed => parsed.slug)); + const bundledCodexModelIds = getBundledCodexModelIds(); + const normalized: NormalizedCodexModel[] = []; + for (const parsed of parsedEntries) { + normalized.push(buildNormalizedCodexModel(parsed, parsed.slug, baseUrl)); + const plainSlug = plainCounterpartForWorkerSlug(parsed.slug, bundledCodexModelIds); + if (plainSlug && !advertisedSlugs.has(plainSlug)) { + normalized.push(buildNormalizedCodexModel(parsed, plainSlug, baseUrl)); } } @@ -212,7 +243,37 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope return normalized.map(item => item.model); } -function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCodexModel | null { +/** Ids of the bundled Codex catalog, consulted once per discovery run. */ +function getBundledCodexModelIds(): ReadonlySet { + const ids = new Set(getBundledModels("openai-codex").map(model => model.id)); + return ids; +} + +/** + * Map a Codex worker `-wm` slug to its plain counterpart when the bundled + * catalog registers that plain SKU. Returns `null` for non-worker slugs and + * for `-wm` slugs without a safe plain counterpart. + */ +function plainCounterpartForWorkerSlug(slug: string, bundledCodexModelIds: ReadonlySet): string | null { + if (!slug.endsWith(CODEX_WORKER_SUFFIX)) { + return null; + } + const plain = slug.slice(0, -CODEX_WORKER_SUFFIX.length); + return plain.length > 0 && bundledCodexModelIds.has(plain) ? plain : null; +} + +interface ParsedCodexModelEntry { + slug: string; + name: string; + contextWindow: number | null; + reasoning: boolean; + input: ("text" | "image")[]; + preferWebsockets: boolean; + useResponsesLite: boolean; + priority: number; +} + +function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null { const parsedEntry = codexModelEntrySchema(entry); if (parsedEntry instanceof type.errors) { return null; @@ -229,44 +290,53 @@ function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCo return null; } - const name = toNonEmptyString(payload.display_name) ?? slug; + return { + slug, + name: toNonEmptyString(payload.display_name) ?? slug, + contextWindow: toPositiveInt(payload.context_window), + reasoning: supportsReasoning(payload.default_reasoning_level, payload.supported_reasoning_levels), + input: normalizeInputModalities(payload.input_modalities), + preferWebsockets: toBoolean(payload.prefer_websockets) === true, + useResponsesLite: toBoolean(payload.use_responses_lite) === true, + priority: toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER, + }; +} + +function buildNormalizedCodexModel(parsed: ParsedCodexModelEntry, slug: string, baseUrl: string): NormalizedCodexModel { // Codex discovery historically omitted `context_window` for GPT-5.6-family // SKUs (#5705); luna/sol/terra additionally floor the reported value because - // the registry still declares the pre-1M 272000 window. - const parsed = parseKnownModel(slug); + // the registry still declares the pre-1M 272000 window. Re-derived from the + // effective `slug` so a synthesized plain route gets the same treatment as + // one actually advertised under that id. + const parsedKnown = parseKnownModel(slug); const fallbackContextWindow = - parsed.family === "openai" && semverEqual(parsed.version, "5.6") + parsedKnown.family === "openai" && semverEqual(parsedKnown.version, "5.6") ? GPT_5_6_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW; - const reportedContextWindow = toPositiveInt(payload.context_window) ?? fallbackContextWindow; + const reportedContextWindow = parsed.contextWindow ?? fallbackContextWindow; const contextWindow = CODEX_GPT_5_6_1M_SLUGS.has(slug) ? Math.max(reportedContextWindow, GPT_5_6_1M_CONTEXT_WINDOW) : reportedContextWindow; const maxTokens = Math.min(DEFAULT_MAX_TOKENS, contextWindow); - const reasoning = supportsReasoning(payload.default_reasoning_level, payload.supported_reasoning_levels); - const input = normalizeInputModalities(payload.input_modalities); - const preferWebsockets = toBoolean(payload.prefer_websockets) === true; - const useResponsesLite = toBoolean(payload.use_responses_lite) === true; - const priority = toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER; const daybreakCost = resolveOpenAIDaybreakStandardCost(slug); return { - priority, + priority: parsed.priority, model: { id: slug, - name, + name: parsed.name, api: "openai-codex-responses", provider: "openai-codex", baseUrl, - reasoning, - input, + reasoning: parsed.reasoning, + input: parsed.input, cost: daybreakCost ? { ...daybreakCost } : { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, remoteCompaction: CODEX_REMOTE_COMPACTION, contextWindow, maxTokens, - ...(preferWebsockets ? { preferWebsockets: true } : {}), - ...(useResponsesLite ? { useResponsesLite: true } : {}), - ...(priority !== Number.MAX_SAFE_INTEGER ? { priority } : {}), + ...(parsed.preferWebsockets ? { preferWebsockets: true } : {}), + ...(parsed.useResponsesLite ? { useResponsesLite: true } : {}), + ...(parsed.priority !== Number.MAX_SAFE_INTEGER ? { priority: parsed.priority } : {}), }, }; } diff --git a/packages/catalog/test/codex-discovery.test.ts b/packages/catalog/test/codex-discovery.test.ts index 7cf08217e..a05165c65 100644 --- a/packages/catalog/test/codex-discovery.test.ts +++ b/packages/catalog/test/codex-discovery.test.ts @@ -538,4 +538,148 @@ describe("Codex model discovery", () => { await fs.rm(tempDir, { recursive: true, force: true }); } }); + + it("registers a plain route when the backend advertises only the worker `-wm` slug", async () => { + const fetchFn: typeof fetch = Object.assign( + async () => + new Response( + JSON.stringify({ + models: [ + { + slug: "gpt-5.6-luna-wm", + display_name: "GPT-5.6 Luna", + context_window: 272_000, + default_reasoning_level: "medium", + supported_reasoning_levels: ["low", "medium", "high"], + input_modalities: ["text", "image"], + supported_in_api: true, + }, + ], + }), + ), + { preconnect() {} }, + ); + const result = await fetchCodexModels({ + accessToken: "test-token", + baseUrl: "https://codex.example/backend-api", + clientVersion: "0.99.0", + fetchFn, + }); + + // The authoritative `-wm` row stays surfaced verbatim… + expect(result?.models.some(model => model.id === "gpt-5.6-luna-wm")).toBe(true); + // …and the configured plain slug must also resolve to a real route. + const plainModel = result?.models.find(model => model.id === "gpt-5.6-luna"); + expect(plainModel).toBeDefined(); + // The plain route is re-derived for the plain slug, so the 1M floor applies. + expect(plainModel?.contextWindow).toBe(1_000_000); + expect(plainModel?.provider).toBe("openai-codex"); + }); + + it("keeps the plain route through authoritative discovery that advertises only the `-wm` slug", async () => { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-catalog-codex-luna-wm-")); + const bundledLuna: ModelSpec<"openai-codex-responses"> = { + id: "gpt-5.6-luna", + name: "GPT-5.6 Luna", + api: "openai-codex-responses", + provider: "openai-codex", + baseUrl: "https://chatgpt.com/backend-api", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1_000_000, + maxTokens: 128_000, + }; + const fetchFn: typeof fetch = Object.assign( + async () => + new Response( + JSON.stringify({ + models: [ + { + slug: "gpt-5.6-luna-wm", + display_name: "GPT-5.6 Luna", + default_reasoning_level: "medium", + supported_reasoning_levels: ["low", "medium", "high"], + input_modalities: ["text", "image"], + supported_in_api: true, + }, + ], + }), + ), + { preconnect() {} }, + ); + try { + const options = openaiCodexModelManagerOptions({ + resolveAccounts: async () => [{ accessToken: "test-token" }], + fetch: fetchFn, + }); + const result = await resolveProviderModels( + { ...options, staticModels: [bundledLuna], cacheDbPath: path.join(tempDir, "models.db") }, + "online", + ); + + const ids = result.models.map(model => model.id); + // The exact-id resolution the resolver performs for + // `openai-codex/gpt-5.6-luna` needs this row present. + expect(ids).toContain("gpt-5.6-luna"); + expect(ids).toContain("gpt-5.6-luna-wm"); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } + }); + + it("keeps a `-wm` slug verbatim when it has no bundled plain counterpart", async () => { + const fetchFn: typeof fetch = Object.assign( + async () => + new Response( + JSON.stringify({ + models: [ + { + slug: "gpt-9.9-mystery-wm", + display_name: "GPT-9.9 Mystery (worker)", + input_modalities: ["text"], + supported_in_api: true, + }, + ], + }), + ), + { preconnect() {} }, + ); + const result = await fetchCodexModels({ + accessToken: "test-token", + baseUrl: "https://codex.example/backend-api", + clientVersion: "0.99.0", + fetchFn, + }); + // No bundled `gpt-9.9-mystery` entry, so no phantom plain route is made up. + expect(result?.models.map(model => model.id)).toEqual(["gpt-9.9-mystery-wm"]); + }); + + it("leaves a non-worker slug untouched by the worker-mapping rule", async () => { + const fetchFn: typeof fetch = Object.assign( + async () => + new Response( + JSON.stringify({ + models: [ + { + slug: "gpt-5.6-luna", + display_name: "GPT-5.6 Luna", + default_reasoning_level: "medium", + supported_reasoning_levels: ["low", "medium", "high"], + input_modalities: ["text", "image"], + supported_in_api: true, + }, + ], + }), + ), + { preconnect() {} }, + ); + const result = await fetchCodexModels({ + accessToken: "test-token", + baseUrl: "https://codex.example/backend-api", + clientVersion: "0.99.0", + fetchFn, + }); + expect(result?.models.map(model => model.id)).toEqual(["gpt-5.6-luna"]); + }); }); From 93e95c7dfc77ac3a3596667e0a3767cb185559ce Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 18 Aug 2026 16:34:24 -0400 Subject: [PATCH 2/2] fix(catalog): derive Codex `-wm` cap and cost from the canonical plain slug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up: a safe `-wm` row previously kept backend-parsed capability metadata (no 1M floor, no daybreak pricing) while its synthesized plain listing was enriched — the same model reported two different contexts. Both listings now derive fallback window, the 1M floor, and daybreak cost from the canonical plain slug; unknown `-wm` SKUs and non-worker models keep their verbatim slug-derived metadata. The authoritative-discovery test now resolves the configured `openai-codex/gpt-5.6-luna` through the real model resolver and asserts the exact bound id (and that an explicit `-wm` config still resolves verbatim) instead of only checking `ids.toContain`. --- packages/catalog/src/discovery/codex.ts | 54 +++++++++++++------ packages/catalog/test/codex-discovery.test.ts | 38 ++++++------- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/packages/catalog/src/discovery/codex.ts b/packages/catalog/src/discovery/codex.ts index 68d5f1f95..049c3b92f 100644 --- a/packages/catalog/src/discovery/codex.ts +++ b/packages/catalog/src/discovery/codex.ts @@ -32,10 +32,18 @@ const CODEX_GPT_5_6_1M_SLUGS: ReadonlySet = new Set(["gpt-5.6-luna", "gp * unresolvable except via fuzzy fallback onto the `-wm` row — which this user's * ChatGPT account rejects. The compatibility rule, scoped to Codex discovery: * a `-wm` slug whose plain counterpart exists in the bundled Codex catalog is - * ALSO registered under its plain id (re-derived, not cloned, so the 1M-window - * floor and daybreak pricing keyed on the plain slug still apply). Models - * without a bundled plain counterpart stay verbatim, preserving authoritative - * discovery for genuinely distinct `-wm` SKUs. + * ALSO registered under its plain id. Both listings derive their base-model + * metadata (1M-window floor, daybreak pricing, context fallback) from the + * canonical plain slug — the suffix is a routing variant, not a different + * model, so the `-wm` row no longer keeps stale backend-parsed capability + * values while its plain listing is enriched. + * + * Deliberate boundary: the "safe" gate is the bundled Codex catalog. A `-wm` + * slug whose plain counterpart is only a user-local models.yml entry (not + * bundled) stays verbatim — authoritative discovery for genuinely distinct + * `-wm` SKUs is preserved, and a hidden plain backend entry can be re-surfaced + * through its advertised `-wm` row because the configured plain slug must + * resolve. */ const CODEX_WORKER_SUFFIX = "-wm"; const CODEX_REMOTE_COMPACTION = { @@ -221,15 +229,19 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope // A worker `-wm` slug gets an extra plain-id route only when the bundled // catalog ships the plain SKU (the "safe" precondition); the backend's own // plain slug wins over any synthesized clone, and unknown `-wm` SKUs stay - // verbatim. + // verbatim. Both listings of a safe `-wm` model carry the same base-model + // metadata (context-window floor, daybreak pricing) derived from the + // canonical plain slug — the suffix is a routing variant, not a different + // model. const advertisedSlugs = new Set(parsedEntries.map(parsed => parsed.slug)); const bundledCodexModelIds = getBundledCodexModelIds(); const normalized: NormalizedCodexModel[] = []; for (const parsed of parsedEntries) { - normalized.push(buildNormalizedCodexModel(parsed, parsed.slug, baseUrl)); - const plainSlug = plainCounterpartForWorkerSlug(parsed.slug, bundledCodexModelIds); + const canonicalSlug = plainCounterpartForWorkerSlug(parsed.slug, bundledCodexModelIds) ?? parsed.slug; + normalized.push(buildNormalizedCodexModel(parsed, parsed.slug, canonicalSlug, baseUrl)); + const plainSlug = canonicalSlug !== parsed.slug ? canonicalSlug : null; if (plainSlug && !advertisedSlugs.has(plainSlug)) { - normalized.push(buildNormalizedCodexModel(parsed, plainSlug, baseUrl)); + normalized.push(buildNormalizedCodexModel(parsed, plainSlug, canonicalSlug, baseUrl)); } } @@ -302,23 +314,35 @@ function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null { }; } -function buildNormalizedCodexModel(parsed: ParsedCodexModelEntry, slug: string, baseUrl: string): NormalizedCodexModel { +/** + * Build a normalized Codex model spec. `slug` is the registered id (either the + * advertised slug or a synthesized plain counterpart); `canonicalSlug` names + * the model's bundled SKU (`slug` itself for plain/unknown rows, the plain + * counterpart for a safe `-wm` row) and owns the base-model metadata derivation + * so both listings of a model report the same context window and pricing. + */ +function buildNormalizedCodexModel( + parsed: ParsedCodexModelEntry, + slug: string, + canonicalSlug: string, + baseUrl: string, +): NormalizedCodexModel { // Codex discovery historically omitted `context_window` for GPT-5.6-family // SKUs (#5705); luna/sol/terra additionally floor the reported value because - // the registry still declares the pre-1M 272000 window. Re-derived from the - // effective `slug` so a synthesized plain route gets the same treatment as - // one actually advertised under that id. - const parsedKnown = parseKnownModel(slug); + // the registry still declares the pre-1M 272000 window. Keyed on the + // canonical slug so a safe `gpt-5.6-luna-wm` row gets the same floor as its + // plain listing. + const parsedKnown = parseKnownModel(canonicalSlug); const fallbackContextWindow = parsedKnown.family === "openai" && semverEqual(parsedKnown.version, "5.6") ? GPT_5_6_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW; const reportedContextWindow = parsed.contextWindow ?? fallbackContextWindow; - const contextWindow = CODEX_GPT_5_6_1M_SLUGS.has(slug) + const contextWindow = CODEX_GPT_5_6_1M_SLUGS.has(canonicalSlug) ? Math.max(reportedContextWindow, GPT_5_6_1M_CONTEXT_WINDOW) : reportedContextWindow; const maxTokens = Math.min(DEFAULT_MAX_TOKENS, contextWindow); - const daybreakCost = resolveOpenAIDaybreakStandardCost(slug); + const daybreakCost = resolveOpenAIDaybreakStandardCost(canonicalSlug); return { priority: parsed.priority, diff --git a/packages/catalog/test/codex-discovery.test.ts b/packages/catalog/test/codex-discovery.test.ts index a05165c65..9415be50d 100644 --- a/packages/catalog/test/codex-discovery.test.ts +++ b/packages/catalog/test/codex-discovery.test.ts @@ -11,6 +11,7 @@ import { resolveProviderModels } from "@oh-my-pi/pi-catalog/model-manager"; import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking"; import { openaiCodexModelManagerOptions } from "@oh-my-pi/pi-catalog/provider-models/special"; import type { ModelSpec } from "@oh-my-pi/pi-catalog/types"; +import { resolveProviderModelReference } from "@oh-my-pi/pi-coding-agent/config/model-resolver"; describe("Codex model discovery", () => { it("marks discovered models for provider-native V2 compaction", async () => { @@ -567,29 +568,20 @@ describe("Codex model discovery", () => { }); // The authoritative `-wm` row stays surfaced verbatim… - expect(result?.models.some(model => model.id === "gpt-5.6-luna-wm")).toBe(true); + const workerModel = result?.models.find(model => model.id === "gpt-5.6-luna-wm"); + expect(workerModel).toBeDefined(); // …and the configured plain slug must also resolve to a real route. const plainModel = result?.models.find(model => model.id === "gpt-5.6-luna"); expect(plainModel).toBeDefined(); - // The plain route is re-derived for the plain slug, so the 1M floor applies. - expect(plainModel?.contextWindow).toBe(1_000_000); expect(plainModel?.provider).toBe("openai-codex"); + // Both rows are the same model: the worker variant shares the plain + // SKU's base metadata, so the 1M window floor applies to both. + expect(workerModel?.contextWindow).toBe(1_000_000); + expect(plainModel?.contextWindow).toBe(1_000_000); }); it("keeps the plain route through authoritative discovery that advertises only the `-wm` slug", async () => { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-catalog-codex-luna-wm-")); - const bundledLuna: ModelSpec<"openai-codex-responses"> = { - id: "gpt-5.6-luna", - name: "GPT-5.6 Luna", - api: "openai-codex-responses", - provider: "openai-codex", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 1_000_000, - maxTokens: 128_000, - }; const fetchFn: typeof fetch = Object.assign( async () => new Response( @@ -613,16 +605,26 @@ describe("Codex model discovery", () => { resolveAccounts: async () => [{ accessToken: "test-token" }], fetch: fetchFn, }); + // No artificial static input: the bundled Codex catalog is the real + // gate that licenses the plain-route synthesis. const result = await resolveProviderModels( - { ...options, staticModels: [bundledLuna], cacheDbPath: path.join(tempDir, "models.db") }, + { ...options, cacheDbPath: path.join(tempDir, "models.db") }, "online", ); const ids = result.models.map(model => model.id); - // The exact-id resolution the resolver performs for - // `openai-codex/gpt-5.6-luna` needs this row present. expect(ids).toContain("gpt-5.6-luna"); expect(ids).toContain("gpt-5.6-luna-wm"); + + // Same engine the runtime uses: resolving the configured + // `openai-codex/gpt-5.6-luna` must bind to the plain route by exact + // id, not fall through to the `-wm` fuzzy match. + const resolved = resolveProviderModelReference("openai-codex", "gpt-5.6-luna", result.models); + expect(resolved?.id).toBe("gpt-5.6-luna"); + expect(resolved?.provider).toBe("openai-codex"); + // An explicitly configured worker slug still resolves verbatim. + const resolvedWm = resolveProviderModelReference("openai-codex", "gpt-5.6-luna-wm", result.models); + expect(resolvedWm?.id).toBe("gpt-5.6-luna-wm"); } finally { await fs.rm(tempDir, { recursive: true, force: true }); }