/** * Issue #2331 — Codex model alias effort suffixes * (`gpt-5.5-xhigh`, `-high`, `-medium`, `-low`) are the user's explicit * routing choice and must override a client-injected `reasoning.effort` * default. OpenCode auto-injects `reasoning.effort=medium` for GPT-5-family * requests, which used to silently mask the suffix. * * The fix is in `open-sse/executors/codex.ts`: priority is * modelEffort > explicitReasoning > requestReasoningEffort > fallback. * * These tests exercise the effort-resolution priority directly via a * small re-implementation of the resolution chain so we don't have to * spin up the full Codex executor (which talks to upstream). */ import test from "node:test"; import assert from "node:assert/strict"; // Replicate the priority chain that lives in // open-sse/executors/codex.ts:1382-1402 so tests fail loudly if someone // reverts the order. type Inputs = { modelEffort: string | null; explicitReasoning: string | undefined; requestReasoningEffort: string | undefined; fallbackReasoningEffort: string | undefined; }; function resolveEffort(i: Inputs): string | undefined { return ( i.modelEffort || i.explicitReasoning || i.requestReasoningEffort || i.fallbackReasoningEffort || undefined ); } test("#2331 model suffix wins over client reasoning.effort default", () => { const out = resolveEffort({ modelEffort: "xhigh", explicitReasoning: "medium", // OpenCode default requestReasoningEffort: undefined, fallbackReasoningEffort: undefined, }); assert.equal(out, "xhigh"); }); test("#2331 model suffix wins over body.reasoning_effort field too", () => { const out = resolveEffort({ modelEffort: "low", explicitReasoning: undefined, requestReasoningEffort: "high", fallbackReasoningEffort: undefined, }); assert.equal(out, "low"); }); test("#2331 without suffix, explicit client effort still works (backward compat)", () => { const out = resolveEffort({ modelEffort: null, explicitReasoning: "high", requestReasoningEffort: undefined, fallbackReasoningEffort: "medium", }); assert.equal(out, "high"); }); test("#2331 without suffix or client value, connection fallback applies", () => { const out = resolveEffort({ modelEffort: null, explicitReasoning: undefined, requestReasoningEffort: undefined, fallbackReasoningEffort: "medium", }); assert.equal(out, "medium"); }); test("#2331 no input anywhere → undefined (caller will skip body.reasoning)", () => { const out = resolveEffort({ modelEffort: null, explicitReasoning: undefined, requestReasoningEffort: undefined, fallbackReasoningEffort: undefined, }); assert.equal(out, undefined); }); // ─── Regression check on the actual source ───────────────────────────── import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const CODEX_SRC = path.resolve(__dirname, "../../open-sse/executors/codex.ts"); test("#2331 codex.ts still ranks modelEffort above client-injected reasoning defaults", () => { const src = fs.readFileSync(CODEX_SRC, "utf8"); // The chain we expect: rawEffort = modelEffort || explicitReasoning || ... // Anchor on the assignment so a future refactor that flips priority back // (the bug we just fixed) trips this guard. const ASSIGNMENT_RE = /const\s+rawEffort\s*=\s*([\s\S]{0,400}?);/; const match = src.match(ASSIGNMENT_RE); assert.ok(match, "rawEffort assignment not found in codex.ts"); const chain = match![1].replace(/\s+/g, " ").trim(); const order = chain.split("||").map((token) => token.trim()); const at = (token: string) => order.indexOf(token); // #2331's invariant is a RELATIVE one: a model-suffix alias (gpt-5.5-xhigh) must beat // the defaults a client injects (OpenCode's reasoning.effort=medium, reasoning_effort). // It is not a claim about the head of the chain — #13556 deliberately put the // server-selected force rule ahead of everything, which is stronger than both. assert.ok(at("modelEffort") >= 0, `modelEffort missing from chain: ${chain}`); assert.ok( at("modelEffort") < at("explicitReasoning"), `modelEffort must outrank explicitReasoning, got: ${chain}` ); assert.ok( at("modelEffort") < at("requestReasoningEffort"), `modelEffort must outrank requestReasoningEffort, got: ${chain}` ); });