From 97addf32037ac66eb45b0a39775f0bd3aafe3c4e Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sat, 29 Aug 2026 22:22:18 +0900 Subject: [PATCH] fix(catalog): refresh Codex 0.151 shell contract --- src/codex/catalog/metadata.ts | 2 +- src/codex/catalog/parsing.ts | 7 ++++ src/codex/catalog/sync.ts | 2 +- src/codex/data/upstream-models.json | 48 ++++++++++++++++++++---- tests/codex-catalog.test.ts | 58 ++++++++++++++++++++++++++++- tests/codex-tool-mode.test.ts | 5 +-- tests/native-model-toggle.test.ts | 9 +++-- 7 files changed, 113 insertions(+), 18 deletions(-) diff --git a/src/codex/catalog/metadata.ts b/src/codex/catalog/metadata.ts index 5494d12ddd..e797f59d2b 100644 --- a/src/codex/catalog/metadata.ts +++ b/src/codex/catalog/metadata.ts @@ -548,7 +548,7 @@ function hasNativeCatalogRowShape(entry: RawEntry): boolean { return typeof entry.base_instructions === "string" && entry.base_instructions.length > 0 && (typeof entry.comp_hash === "string" || entry.comp_hash === null) - && entry.shell_type === "shell_command" + && (entry.shell_type === "unified_exec" || entry.shell_type === "shell_command") && Array.isArray(levels) && levels.length > 0 && levels.every(level => typeof level === "object" && level !== null diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index 218a806824..de195b9abc 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -431,6 +431,13 @@ export function ensureStrictCatalogFields( entry: RawEntry, options: { preserveExactInputModalities?: boolean; isRouted?: boolean } = {}, ): RawEntry { + if (entry.shell_type === "default" || entry.shell_type === "local" || entry.shell_type === "shell_command") { + entry.shell_type = "unified_exec"; + } + if (typeof entry.node_repl_disabled !== "boolean") entry.node_repl_disabled = false; + if (typeof entry.node_repl_auto_review_required !== "boolean") entry.node_repl_auto_review_required = false; + if (typeof entry.include_plugin_usage_instructions !== "boolean") entry.include_plugin_usage_instructions = false; + if (typeof entry.include_apps_usage_instructions !== "boolean") entry.include_apps_usage_instructions = true; if (typeof entry.supports_reasoning_summaries !== "boolean") entry.supports_reasoning_summaries = false; if (typeof entry.default_reasoning_summary !== "string") entry.default_reasoning_summary = "none"; if (typeof entry.support_verbosity !== "boolean") entry.support_verbosity = true; diff --git a/src/codex/catalog/sync.ts b/src/codex/catalog/sync.ts index fb50db039e..b7ab78cfb8 100644 --- a/src/codex/catalog/sync.ts +++ b/src/codex/catalog/sync.ts @@ -375,7 +375,7 @@ export function deriveEntry( const isCursorFallback = isRouted && model?.provider === "cursor"; const entry: RawEntry = { slug, display_name: routedDisplayName(slug), description: desc, - shell_type: "shell_command", visibility: "list", supported_in_api: true, + shell_type: "unified_exec", visibility: "list", supported_in_api: true, priority, base_instructions: "You are a helpful coding assistant.", ...(isRouted ? isCursorFallback diff --git a/src/codex/data/upstream-models.json b/src/codex/data/upstream-models.json index c2f9653272..16336b4ee0 100644 --- a/src/codex/data/upstream-models.json +++ b/src/codex/data/upstream-models.json @@ -21,6 +21,10 @@ "multi_agent_version": "v2", "use_responses_lite": true, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 372000, "max_context_window": 372000, @@ -57,7 +61,7 @@ "description": "Maximum reasoning with automatic task delegation" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.142.2", "supported_in_api": true, @@ -135,6 +139,10 @@ "multi_agent_version": "v2", "use_responses_lite": true, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 372000, "max_context_window": 372000, @@ -171,7 +179,7 @@ "description": "Maximum reasoning with automatic task delegation" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.142.2", "supported_in_api": true, @@ -247,6 +255,10 @@ "multi_agent_version": "v1", "use_responses_lite": true, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 372000, "max_context_window": 372000, @@ -279,7 +291,7 @@ "description": "Maximum reasoning depth for the hardest problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.142.2", "supported_in_api": true, @@ -355,6 +367,10 @@ "multi_agent_version": null, "use_responses_lite": false, "include_skills_usage_instructions": true, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 272000, "max_context_window": 272000, @@ -383,7 +399,7 @@ "description": "Extra high reasoning depth for complex problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.124.0", "supported_in_api": true, @@ -461,6 +477,10 @@ "multi_agent_version": null, "use_responses_lite": false, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 272000, "max_context_window": 1000000, @@ -489,7 +509,7 @@ "description": "Extra high reasoning depth for complex problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.98.0", "supported_in_api": true, @@ -562,6 +582,10 @@ "multi_agent_version": null, "use_responses_lite": false, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 272000, "max_context_window": 272000, @@ -590,7 +614,7 @@ "description": "Extra high reasoning depth for complex problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.98.0", "supported_in_api": true, @@ -658,6 +682,10 @@ "multi_agent_version": null, "use_responses_lite": false, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 272000, "max_context_window": 272000, @@ -686,7 +714,7 @@ "description": "Extra high reasoning for complex problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "list", "minimal_client_version": "0.0.1", "supported_in_api": true, @@ -754,6 +782,10 @@ "multi_agent_version": null, "use_responses_lite": false, "include_skills_usage_instructions": false, + "node_repl_disabled": false, + "node_repl_auto_review_required": false, + "include_plugin_usage_instructions": true, + "include_apps_usage_instructions": true, "auto_review_model_override": null, "context_window": 272000, "max_context_window": 1000000, @@ -782,7 +814,7 @@ "description": "Extra high reasoning depth for complex problems" } ], - "shell_type": "shell_command", + "shell_type": "unified_exec", "visibility": "hide", "minimal_client_version": "0.98.0", "supported_in_api": true, diff --git a/tests/codex-catalog.test.ts b/tests/codex-catalog.test.ts index f6c96420ec..3235b993b9 100644 --- a/tests/codex-catalog.test.ts +++ b/tests/codex-catalog.test.ts @@ -2734,6 +2734,20 @@ describe("Codex catalog routed normalization", () => { expect(terra?.multi_agent_version).toBe("v2"); expect(luna?.multi_agent_version).toBe("v1"); + const codex0151Contract = { + shell_type: "unified_exec", + node_repl_disabled: false, + node_repl_auto_review_required: false, + include_plugin_usage_instructions: true, + include_apps_usage_instructions: true, + }; + for (const slug of ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]) { + expect(upstreamNativeEntry(slug)).toMatchObject(codex0151Contract); + } + for (const entry of [sol, terra, luna]) { + expect(entry).toMatchObject(codex0151Contract); + } + // ocx adaptations: client-version gate stripped; ws preference gated off by default. for (const e of [sol, terra, luna]) { expect(e).not.toHaveProperty("minimal_client_version"); @@ -3240,7 +3254,14 @@ describe("Codex catalog routed normalization", () => { ]); const routed = rows.find(row => row.slug === "deepseek/deepseek-v4-flash"); - expect(routed?.tool_mode).toBe("code_mode_only"); + expect(routed).toMatchObject({ + tool_mode: "code_mode_only", + shell_type: "unified_exec", + node_repl_disabled: false, + node_repl_auto_review_required: false, + include_plugin_usage_instructions: false, + include_apps_usage_instructions: true, + }); }); test("buildCatalogEntries preserves native tool mode on account-qualified rows", () => { @@ -5973,3 +5994,38 @@ describe("routed rows never carry native eligibility metadata", () => { expect(entry).not.toHaveProperty("upgrade"); }); }); + +describe("Codex 0.151 catalog contract fields", () => { + test("legacy shell types canonicalize while disabled remains disabled", () => { + const normalizedLegacy = ["default", "local", "shell_command"].map(shell_type => + ensureStrictCatalogFields({ slug: "test", shell_type }).shell_type); + + expect(normalizedLegacy).toEqual(["unified_exec", "unified_exec", "unified_exec"]); + expect(ensureStrictCatalogFields({ slug: "test", shell_type: "disabled" }).shell_type) + .toBe("disabled"); + }); + + test("missing booleans receive serde defaults", () => { + expect(ensureStrictCatalogFields({ slug: "test" })).toMatchObject({ + node_repl_disabled: false, + node_repl_auto_review_required: false, + include_plugin_usage_instructions: false, + include_apps_usage_instructions: true, + }); + }); + + test("explicit per-model booleans are never overwritten by defaults", () => { + expect(ensureStrictCatalogFields({ + slug: "test", + node_repl_disabled: true, + node_repl_auto_review_required: true, + include_plugin_usage_instructions: true, + include_apps_usage_instructions: false, + })).toMatchObject({ + node_repl_disabled: true, + node_repl_auto_review_required: true, + include_plugin_usage_instructions: true, + include_apps_usage_instructions: false, + }); + }); +}); diff --git a/tests/codex-tool-mode.test.ts b/tests/codex-tool-mode.test.ts index eb87143707..92d80ffcd9 100644 --- a/tests/codex-tool-mode.test.ts +++ b/tests/codex-tool-mode.test.ts @@ -56,7 +56,7 @@ describe("Codex tool mode configuration (#2106)", () => { const deepseekEntry = entries.find(e => e.slug === "deepseek/deepseek-v4-flash"); expect(deepseekEntry).toBeDefined(); expect(deepseekEntry?.tool_mode).toBe("code_mode_only"); - expect(deepseekEntry?.shell_type).toBe("shell_command"); + expect(deepseekEntry?.shell_type).toBe("unified_exec"); }); test("buildCatalogEntries leaves tool_mode unset when codexToolMode is shell", () => { @@ -70,7 +70,7 @@ describe("Codex tool mode configuration (#2106)", () => { const deepseekEntry = entries.find(e => e.slug === "deepseek/deepseek-v4-flash"); expect(deepseekEntry).toBeDefined(); expect(deepseekEntry?.tool_mode).toBeUndefined(); - expect(deepseekEntry?.shell_type).toBe("shell_command"); + expect(deepseekEntry?.shell_type).toBe("unified_exec"); }); test("under shell mode with declared exec_command, undeclared-tool-guard allows exec_command", () => { @@ -238,4 +238,3 @@ describe("#2503 combo derivation preserves a member's verbosity opt-out", () => expect(derived?.supportsVerbosity).toBeUndefined(); }); }); - diff --git a/tests/native-model-toggle.test.ts b/tests/native-model-toggle.test.ts index d63dfa771a..9fcbaa187d 100644 --- a/tests/native-model-toggle.test.ts +++ b/tests/native-model-toggle.test.ts @@ -391,19 +391,20 @@ describe("native GPT model toggles (bare slugs in disabledModels)", () => { // catalog, so the effect is advertisement in discovery, not a newly reachable route. If this // test ever needs to flip to rejection, the fix is a real provenance signal, not a longer // list of fields to match. - test("a full-shape hand-written row IS accepted — the check is plausibility, not provenance", () => { - const forged = [{ + test("full-shape unified_exec and legacy shell_command rows are accepted", () => { + const forged = (shell_type: string) => [{ slug: "gpt-not-a-real-model", visibility: "list", supported_in_api: true, base_instructions: "anything non-empty", comp_hash: null, - shell_type: "shell_command", + shell_type, supported_reasoning_levels: [{ effort: "high" }], model_messages: {}, }]; - expect(accountBoundNativeOpenAiSlugs(forged)).toContain("gpt-not-a-real-model"); + expect(accountBoundNativeOpenAiSlugs(forged("unified_exec"))).toContain("gpt-not-a-real-model"); + expect(accountBoundNativeOpenAiSlugs(forged("shell_command"))).toContain("gpt-not-a-real-model"); }); test("exact account disables hide only the matching generated picker row", () => {