diff --git a/CHANGELOG.md b/CHANGELOG.md index 73148e590..18b805e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.17.1 — Unreleased +### Fixed + +- Browser: keep `--browser-thinking-time extra-high` as Extra High (non-Pro) on GPT-5.6 Sol; reserve `heavy` for explicit Pro selection. Fixes #353. + ## 0.17.0 — 2026-08-02 ### Added diff --git a/bin/oracle-cli.ts b/bin/oracle-cli.ts index 1fd574969..4569337f7 100755 --- a/bin/oracle-cli.ts +++ b/bin/oracle-cli.ts @@ -150,7 +150,7 @@ interface CliOptions extends OptionValues { browserManualLogin?: boolean; browserManualLoginProfileDir?: string; copyProfile?: string; - browserThinkingTime?: "light" | "standard" | "extended" | "heavy"; + browserThinkingTime?: "light" | "standard" | "extended" | "extra-high" | "heavy"; browserResearch?: "off" | "deep"; browserFollowUp?: string[]; browserAllowCookieErrors?: boolean; @@ -801,7 +801,7 @@ program .addOption( new Option( "--browser-thinking-time ", - "Thinking time intensity for Thinking/Pro models: light, standard, extended, heavy, or ChatGPT UI aliases.", + "Thinking time intensity for Thinking/Pro models: light, standard, extended, extra-high (Extra High), heavy (Pro), or ChatGPT UI aliases.", ) .argParser(parseThinkingTimeOption) .hideHelp(), diff --git a/docs/browser-mode.md b/docs/browser-mode.md index 63a319743..48d7f9540 100644 --- a/docs/browser-mode.md +++ b/docs/browser-mode.md @@ -101,7 +101,7 @@ Notes: - If an assistant response still times out (common with long Pro runs), Oracle marks the session as an incomplete capture, stores reattach/runtime diagnostics, and keeps enough browser metadata for `oracle session ` to recover the final answer. Visible ChatGPT rate-limit, temporary-unavailable, and authentication/challenge warnings are included in the error and session metadata instead of being reduced to a generic timeout. Increase `--browser-timeout` only when the browser session is truly unrecoverable. - `--browser-model-strategy `: control ChatGPT model selection. `select` (default) switches to the requested model; `current` keeps the active model and logs its label; `ignore` skips the picker entirely. (Ignored for Gemini web runs.) - Temporary Chat can reduce account-sidebar clutter for one-shot browser consults, but it is a different ChatGPT workflow: Oracle skips archive attempts there and the local transcript/artifacts are the durable record. Verify live behavior before relying on Project Sources, Deep Research reports, or multi-turn persistence. -- `--browser-thinking-time `: set the ChatGPT thinking-time intensity (Thinking/Pro models only). You can also set a default in `~/.oracle/config.json` via `browser.thinkingTime`. +- `--browser-thinking-time `: set the ChatGPT thinking-time intensity (Thinking/Pro models only). On GPT-5.6 Sol, `extra-high` selects Extra High and `heavy` selects Pro. You can also set a default in `~/.oracle/config.json` via `browser.thinkingTime`. - GPT-5.5 Pro Extended is verified from the selected item in ChatGPT's standalone Pro/Thinking effort pill or compatible Intelligence/model-picker menu. A run **fails closed** if Extended cannot be confirmed rather than silently submitting at a weaker effort. Detection failures write a bounded, redacted model-picker diagnostic to the normal session log. - `--browser-research deep`: activate ChatGPT Deep Research before submitting the prompt. Use this for broad public-web research and final cited reports, not as a replacement for GPT-5.x Pro Heavy code review or pure reasoning. - `--browser-follow-up `: submit another prompt in the same ChatGPT conversation after the initial answer. Repeat the flag for multi-turn reviews such as “challenge your recommendation”, “compare against this constraint”, then “give the final decision”. Deep Research has its own report lifecycle, so browser follow-ups are rejected when `--browser-research deep` is enabled. @@ -167,7 +167,7 @@ Oracle activates ChatGPT Deep Research through the composer tools menu, recogniz If ChatGPT initially exposes only `Called tool` / `Used tool`, Oracle treats that as an incomplete capture for Deep Research rather than a final answer. Reattach the existing session with `oracle session --render` so Oracle can recover the lazy-loaded report from the existing Chrome tab; do not rerun the research unless the browser session is unrecoverable. -Deep Research is browser-only. It does not use connected apps in v1; give it public-web scope, uploaded files, and any domain/source guidance in the prompt. For deep thinking over code or architecture without web search, prefer a normal browser run with a Pro/Thinking model and `--browser-thinking-time heavy`. +Deep Research is browser-only. It does not use connected apps in v1; give it public-web scope, uploaded files, and any domain/source guidance in the prompt. For deep thinking over code or architecture without web search, prefer a normal browser run with GPT-5.6 Sol and `--browser-thinking-time extra-high`, or a Pro model with `--browser-thinking-time extended`. Completed browser sessions also save durable artifacts under `~/.oracle/sessions//artifacts/`. Deep Research writes the extracted report to `deep-research-report.md`, and every browser run writes `transcript.md` with the prompt, final answer, conversation URL, and saved artifact references. Use `--write-output ` when you also need a copy of just the final answer at a specific path. @@ -214,7 +214,7 @@ Use browser follow-ups when a one-shot review would be too easy for the model to ```bash oracle --engine browser \ --model gpt-5.5-pro \ - --browser-thinking-time heavy \ + --browser-thinking-time extended \ -p "Review this migration plan and identify the top risks." \ --file docs/migration-plan.md \ --browser-follow-up "Challenge your previous recommendation. What would fail in production?" \ diff --git a/docs/cli-reference.md b/docs/cli-reference.md index c69e4ef67..99d4daa03 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -85,7 +85,7 @@ See [OpenAI / Azure / OpenRouter](openai-endpoints.md) and [OpenRouter](openrout | `--browser-manual-login` | Use persistent profile + manual login (no Keychain). | | `--browser-attach-running` | Attach to your already-running Chrome via DevTools. | | `--browser-tab ` | Reuse an existing tab (`current`, id, URL, title substring). | -| `--browser-thinking-time ` | Pro / Thinking model intensity. | +| `--browser-thinking-time ` | Effort intensity (`extra-high` = Extra High; `heavy` = Pro). | | `--browser-research deep` | Activate Deep Research mode. | | `--browser-follow-up ` | Multi-turn in the same ChatGPT conversation. | | `--browser-port ` | Pin Chrome DevTools port. | diff --git a/docs/configuration.md b/docs/configuration.md index dc23dddb5..2c89f7f9b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -44,7 +44,7 @@ JSON5 parsing, so trailing commas and comments are allowed. autoReattachIntervalMs: 0, // interval between auto-reattach attempts (0 = disabled) autoReattachTimeoutMs: 120000, // time budget per auto-reattach attempt (default: 2m) modelStrategy: "select", // select | current | ignore (ChatGPT only; ignored for Gemini web) - thinkingTime: "extended", // light | standard | extended | heavy (ChatGPT Thinking/Pro models) + thinkingTime: "extended", // light | standard | extended | extra-high | heavy (ChatGPT Thinking/Pro models) researchMode: "off", // off | deep (ChatGPT Deep Research; browser only) manualLogin: false, // set true to reuse a persistent automation profile and sign in once (Windows defaults to true when unset) manualLoginProfileDir: null, // override profile dir (or set ORACLE_BROWSER_PROFILE_DIR) diff --git a/docs/mcp.md b/docs/mcp.md index f27b1d74f..35989f422 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -28,11 +28,11 @@ Claude Code can call `oracle-mcp` and ask a subscription-backed ChatGPT browser - Inputs: `prompt` (required), `files?: string[]` (globs), `model?: string` (defaults to CLI), `engine?: "api" | "browser"` (optional; Oracle follows CLI defaults: `ORACLE_ENGINE` and the effective config first, then API when `OPENAI_API_KEY` is set, otherwise browser), `slug?: string`. - Presets: `preset?: "chatgpt-pro-heavy"` applies browser mode + current Pro model alias + extended thinking, unless the request overrides those fields. -- Browser-only extras: `browserAttachments?: "auto"|"never"|"always"`, `browserBundleFiles?: boolean`, `browserBundleFormat?: "auto"|"text"|"zip"`, `browserThinkingTime?: "light"|"standard"|"extended"|"heavy"`, `browserResearchMode?: "deep"`, `browserFollowUps?: string[]`, `browserArchive?: "auto"|"always"|"never"`, `browserKeepBrowser?: boolean`, `browserModelLabel?: string`, `browserModelStrategy?: "select"|"current"|"ignore"`, `generateImage?: string`, `outputPath?: string`. +- Browser-only extras: `browserAttachments?: "auto"|"never"|"always"`, `browserBundleFiles?: boolean`, `browserBundleFormat?: "auto"|"text"|"zip"`, `browserThinkingTime?: "light"|"standard"|"extended"|"extra-high"|"heavy"`, `browserResearchMode?: "deep"`, `browserFollowUps?: string[]`, `browserArchive?: "auto"|"always"|"never"`, `browserKeepBrowser?: boolean`, `browserModelLabel?: string`, `browserModelStrategy?: "select"|"current"|"ignore"`, `generateImage?: string`, `outputPath?: string`. - Dry runs: set `dryRun: true` to preview the resolved request without creating a session or touching the browser. - Behavior: starts a session, runs it with the chosen engine, returns final output + metadata. Background/foreground follows the CLI (e.g., GPT‑5 Pro detaches by default). If API mode fails because `OPENAI_API_KEY` is missing and you have ChatGPT Pro, retry with `engine: "browser"` or `preset: "chatgpt-pro-heavy"` to use your signed-in ChatGPT session instead of an API key. - Logging: emits MCP logs (`info` per line, `debug` for streamed chunks with byte sizes). If browser prerequisites are missing, returns an error payload instead of running. -- Research mode: set `browserResearchMode:"deep"` for broad public-web research and cited reports. Use normal browser runs with `gpt-5.5-pro` + `browserThinkingTime:"extended"` for Pro Extended code review, or `gpt-5.5` + `browserThinkingTime:"heavy"` when you explicitly want Thinking Heavy. +- Research mode: set `browserResearchMode:"deep"` for broad public-web research and cited reports. Use normal browser runs with `gpt-5.5-pro` + `browserThinkingTime:"extended"` for Pro Extended code review, `gpt-5.6-sol` + `browserThinkingTime:"extra-high"` for Extra High, or `browserThinkingTime:"heavy"` when you explicitly want Pro. - Multi-turn consults: set `browserFollowUps:["Challenge your recommendation", "Give the final decision"]` to keep one ChatGPT browser conversation open and ask sequential follow-up prompts. Use one-shot calls for narrow bugs and exact file-set reviews; use multi-turn for ambiguous architecture/product decisions where a challenge pass and final recommendation are useful; use Deep Research for broad public-web work with citations. Oracle never invents follow-ups automatically. - Archiving: set `browserArchive:"auto"|"always"|"never"` to control ChatGPT conversation cleanup. `auto` archives only successful browser one-shots after local artifacts are saved, and skips project, Deep Research, multi-turn, failed, and incomplete sessions. - ChatGPT image generation: set `engine:"browser"` and `generateImage` to a path under `ORACLE_HOME_DIR/generated` to use the same image-aware wait/download path as CLI `--generate-image`. Saved files are returned in `structuredContent.images` and recorded as session artifacts; multiple images save as numbered siblings. Agent-supplied `generateImage` / `outputPath` are constrained to that generated-output directory by default (set `ORACLE_MCP_ALLOW_EXTERNAL_OUTPUT=1` to allow external paths). diff --git a/docs/mythical-pro-agents.md b/docs/mythical-pro-agents.md index dd8577c20..41d9afd77 100644 --- a/docs/mythical-pro-agents.md +++ b/docs/mythical-pro-agents.md @@ -86,12 +86,13 @@ The captured report lands at `~/.oracle/sessions//artifacts/deep-research-re Pro / Thinking models in browser mode accept a `--browser-thinking-time` knob: -| Level | What it maps to in ChatGPT | -| ---------- | -------------------------------- | -| `light` | Quick | -| `standard` | Default | -| `extended` | Pro Extended / Thinking Extended | -| `heavy` | Heavy thinking | +| Level | What it maps to in ChatGPT | +| ------------- | --------------------------------------------------- | +| `light` | Instant / Quick | +| `standard` | Medium / Standard | +| `extended` | High / Pro Extended / Thinking Extended | +| `extra-high` | Extra High (GPT-5.6 Sol non-Pro max) | +| `heavy` | Pro (GPT-5.6 Sol) / Heavy thinking on older layouts | ```bash oracle --engine browser --model gpt-5.5-pro \ diff --git a/skills/oracle/SKILL.md b/skills/oracle/SKILL.md index 69de9b638..e271d80a8 100644 --- a/skills/oracle/SKILL.md +++ b/skills/oracle/SKILL.md @@ -21,7 +21,8 @@ Recommended defaults: - Engine: browser (`--engine browser`) - Base Sol: `--model gpt-5.6-sol` -- Base Sol maximum reasoning: `--browser-thinking-time heavy` (Extra High) +- Base Sol maximum reasoning: `--browser-thinking-time extra-high` (Extra High) +- Explicit Pro effort on GPT-5.6 Sol: `--browser-thinking-time heavy` (Pro UI) - Browser Pro: `--model gpt-5-pro`, without a thinking-time flag - API Pro maximum reasoning: `--model gpt-5.6-sol --reasoning-mode pro --reasoning-effort max` - Fallback: explicitly use `--model gpt-5.5-pro` when GPT-5.6 is unavailable @@ -44,7 +45,7 @@ For base Sol, use: ```bash oracle --engine browser --model gpt-5.6-sol \ - --browser-thinking-time heavy \ + --browser-thinking-time extra-high \ -p "" --file "src/**" ``` @@ -108,7 +109,7 @@ and a live browser run records strict GPT-5.6 selection evidence. - `npx -y @steipete/oracle --dry-run summary --files-report -p "" --file "src/**"` - Browser run: - - `oracle --engine browser --model gpt-5.6-sol --browser-thinking-time heavy -p "" --file "src/**"` + - `oracle --engine browser --model gpt-5.6-sol --browser-thinking-time extra-high -p "" --file "src/**"` - Manual paste fallback: - `npx -y @steipete/oracle --render-markdown --copy-markdown -p "" --file "src/**"` diff --git a/src/browser/actions/thinkingTime.ts b/src/browser/actions/thinkingTime.ts index 291a5cebf..521b11cd0 100644 --- a/src/browser/actions/thinkingTime.ts +++ b/src/browser/actions/thinkingTime.ts @@ -116,7 +116,7 @@ export async function ensureThinkingTime( /** * Best-effort selection of a thinking time level in ChatGPT's composer pill menu. * Safe by default: if the pill/menu/option isn't present, we continue without throwing. - * @param level - The thinking time intensity: 'light', 'standard', 'extended', or 'heavy' + * @param level - The thinking time intensity: 'light', 'standard', 'extended', 'extra-high', or 'heavy' */ export async function ensureThinkingTimeIfAvailable( Runtime: ChromeClient["Runtime"], @@ -206,7 +206,8 @@ function buildThinkingTimeExpression( light: ['light', 'instant', '轻', '极速'], standard: ['standard', 'medium', '标准', '中'], extended: ['extended', 'high', '扩展', '深度', '加强', '高'], - heavy: ['heavy', 'extra high', '重度', '加重', '极高'], + 'extra-high': ['extra high', '极高'], + heavy: ['heavy', '重度', '加重'], }; const targetTokens = LEVEL_TOKENS[TARGET_LEVEL] || [TARGET_LEVEL]; @@ -488,10 +489,10 @@ function buildThinkingTimeExpression( return item; } } - if (TARGET_LEVEL === 'heavy') { - // Older Chinese layouts used bare 高 for the highest effort. Keep it - // only as a second-pass exact fallback so a current 高 row can never - // win before the primary 极高 row. + if (TARGET_LEVEL === 'extra-high') { + // Older Chinese layouts used bare 高 for the highest non-Pro effort. + // Keep it only as a second-pass exact fallback so a current 高 row can + // never win before the primary 极高 row. for (const item of items) { const itemText = normalize(item.textContent ?? ''); const ariaLabel = normalize(item.getAttribute?.('aria-label') ?? ''); diff --git a/src/cli/browserConfig.ts b/src/cli/browserConfig.ts index bf4e3f44e..860b9804f 100644 --- a/src/cli/browserConfig.ts +++ b/src/cli/browserConfig.ts @@ -80,7 +80,7 @@ export interface BrowserFlagOptions { browserManualLoginProfileDir?: string | null; copyProfile?: string; remoteHost?: string; - /** Thinking time intensity: 'light', 'standard', 'extended', 'heavy' */ + /** Thinking time intensity: 'light', 'standard', 'extended', 'extra-high', 'heavy' */ browserThinkingTime?: ThinkingTimeLevel; browserResearch?: BrowserResearchMode; browserArchive?: BrowserArchiveMode; diff --git a/src/cli/options.ts b/src/cli/options.ts index 35e5996e8..f227b1964 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -164,7 +164,7 @@ export function parseThinkingTimeOption(value: string): ThinkingTimeLevel { return normalized; } throw new InvalidArgumentError( - 'Thinking time must be one of "light", "standard", "extended", "heavy", or a ChatGPT UI alias like "instant", "medium", "high", or "extra-high".', + 'Thinking time must be one of "light", "standard", "extended", "extra-high", "heavy", or a ChatGPT UI alias like "instant", "medium", "high", or "xhigh".', ); } diff --git a/src/mcp/tools/consult.ts b/src/mcp/tools/consult.ts index 830cb3d42..6e3247e00 100644 --- a/src/mcp/tools/consult.ts +++ b/src/mcp/tools/consult.ts @@ -328,7 +328,7 @@ export function buildConsultBrowserConfig({ runModel: string; inputModel?: string; browserModelLabel?: string; - browserThinkingTime?: "light" | "standard" | "extended" | "heavy"; + browserThinkingTime?: "light" | "standard" | "extended" | "extra-high" | "heavy"; browserModelStrategy?: BrowserModelStrategy; browserResearchMode?: "deep"; browserArchive?: "auto" | "always" | "never"; diff --git a/src/oracle/thinkingTime.ts b/src/oracle/thinkingTime.ts index d2991070f..563f1c4d8 100644 --- a/src/oracle/thinkingTime.ts +++ b/src/oracle/thinkingTime.ts @@ -1,12 +1,17 @@ import type { ThinkingTimeLevel } from "./types.js"; -export const THINKING_TIME_LEVELS = ["light", "standard", "extended", "heavy"] as const; +export const THINKING_TIME_LEVELS = [ + "light", + "standard", + "extended", + "extra-high", + "heavy", +] as const; export const THINKING_TIME_ALIASES = [ "instant", "low", "medium", "high", - "extra-high", "extra high", "extrahigh", "xhigh", @@ -37,10 +42,11 @@ export function normalizeThinkingTimeLevel( case "extended": case "high": return "extended"; - case "heavy": case "extra-high": case "extrahigh": case "xhigh": + return "extra-high"; + case "heavy": return "heavy"; default: return null; diff --git a/src/oracle/types.ts b/src/oracle/types.ts index 604476969..20840b43e 100644 --- a/src/oracle/types.ts +++ b/src/oracle/types.ts @@ -37,7 +37,12 @@ export type ProModelName = export type ReasoningEffort = "none" | "low" | "medium" | "high" | "xhigh" | "max"; export type ReasoningMode = "standard" | "pro"; -export type ThinkingTimeLevel = "light" | "standard" | "extended" | "heavy"; +export type ThinkingTimeLevel = + | "light" + | "standard" + | "extended" + | "extra-high" + | "heavy"; export type BrowserBundleFormat = "auto" | "text" | "zip"; diff --git a/tests/browser/thinkingTime.test.ts b/tests/browser/thinkingTime.test.ts index f867a1cd9..ca68346ff 100644 --- a/tests/browser/thinkingTime.test.ts +++ b/tests/browser/thinkingTime.test.ts @@ -20,7 +20,7 @@ describe("browser thinking-time selection expression", () => { }); it("targets the requested thinking time level", () => { - const levels = ["light", "standard", "extended", "heavy"] as const; + const levels = ["light", "standard", "extended", "extra-high", "heavy"] as const; for (const level of levels) { const expression = buildThinkingTimeExpressionForTest(level); expect(expression).toContain("const TARGET_LEVEL"); @@ -46,7 +46,13 @@ describe("browser thinking-time selection expression", () => { expect(buildThinkingTimeExpressionForTest("extended")).toContain( "extended: ['extended', 'high'", ); - expect(buildThinkingTimeExpressionForTest("heavy")).toContain("heavy: ['heavy', 'extra high'"); + expect(buildThinkingTimeExpressionForTest("extra-high")).toContain( + "'extra-high': ['extra high'", + ); + expect(buildThinkingTimeExpressionForTest("heavy")).toContain("heavy: ['heavy'"); + expect(buildThinkingTimeExpressionForTest("heavy")).not.toContain( + "heavy: ['heavy', 'extra high'", + ); }); it("accepts standard selected-state markers when verifying effort", () => { @@ -66,9 +72,9 @@ describe("browser thinking-time selection expression", () => { }); it("preserves Chinese thinking-effort labels while normalizing", () => { - const expression = buildThinkingTimeExpressionForTest("heavy"); + const expression = buildThinkingTimeExpressionForTest("extra-high"); expect(expression).toContain("\\u4e00-\\u9fa5"); - expect(expression).toContain("'重度'"); + expect(expression).toContain("'极高'"); }); it("infers target model kind with token matching", () => { @@ -670,6 +676,158 @@ describe("browser thinking-time selection expression", () => { FakeElement, ), ).resolves.toEqual({ status: "switched", label: "Pro" }); + + const extraHighAttributes: Record = { + role: "menuitemradio", + "aria-checked": "false", + "data-state": "unchecked", + }; + const selectableExtraHigh = new FakeElement( + "Extra High", + extraHighAttributes, + [], + null, + () => { + extraHighAttributes["aria-checked"] = "true"; + extraHighAttributes["data-state"] = "checked"; + }, + ); + const extraHighItems = [ + selectableExtraHigh, + new FakeElement("Pro", { + role: "menuitemradio", + "aria-checked": "false", + "data-state": "unchecked", + }), + new FakeElement("GPT-5.6 Sol", { role: "menuitem", "aria-haspopup": "menu" }), + ]; + const extraHighGroup = new FakeElement( + "Extra High Pro GPT-5.6 Sol", + { "data-testid": "composer-intelligence-picker-content", role: "group" }, + extraHighItems, + ); + const extraHighMenu = new FakeElement( + extraHighGroup.textContent, + { role: "menu" }, + extraHighItems, + extraHighGroup, + ); + const mediumPill = new FakeElement("High", { + class: "__composer-pill", + "aria-expanded": "true", + "aria-haspopup": "menu", + }); + const extraHighDocumentStub = { + ...documentStub, + querySelector: (selector: string) => { + if (selector.includes("composer-intelligence-pro-thinking-effort-trigger")) return null; + if (selector.includes("composer-intelligence-picker-content")) return extraHighGroup; + if ( + selector.includes("model-switcher-dropdown-button") || + selector.includes("__composer-pill") + ) { + return mediumPill; + } + return null; + }, + querySelectorAll: (selector: string) => { + if (selector.includes("__composer-pill")) return [mediumPill]; + if (selector.includes('role="menu"') || selector.includes("data-radix")) { + return [extraHighMenu]; + } + return []; + }, + }; + const evaluateSolExtraHigh = new Function( + "document", + "performance", + "setTimeout", + "window", + "EventTarget", + "PointerEvent", + "MouseEvent", + "HTMLElement", + `return ${buildThinkingTimeExpressionForTest("extra-high", "gpt-5.6-sol")};`, + ) as typeof evaluate; + + await expect( + evaluateSolExtraHigh( + extraHighDocumentStub, + performanceStub, + (callback: () => void) => callback(), + { PointerEvent: FakeMouseEvent, MouseEvent: FakeMouseEvent, Event: FakeMouseEvent }, + FakeEventTarget, + FakeMouseEvent, + FakeMouseEvent, + FakeElement, + ), + ).resolves.toEqual({ status: "switched", label: "Extra High" }); + + const alreadyExtraHighPill = new FakeElement("Extra High", { + class: "__composer-pill", + "aria-expanded": "false", + "aria-haspopup": "menu", + }); + const alreadyExtraHighItems = [ + new FakeElement("Extra High", { + role: "menuitemradio", + "aria-checked": "true", + "data-state": "checked", + }), + new FakeElement("Pro", { + role: "menuitemradio", + "aria-checked": "false", + "data-state": "unchecked", + }), + new FakeElement("GPT-5.6 Sol", { role: "menuitem", "aria-haspopup": "menu" }), + ]; + const alreadyExtraHighGroup = new FakeElement( + "Extra High Pro GPT-5.6 Sol", + { "data-testid": "composer-intelligence-picker-content", role: "group" }, + alreadyExtraHighItems, + ); + const alreadyExtraHighMenu = new FakeElement( + alreadyExtraHighGroup.textContent, + { role: "menu" }, + alreadyExtraHighItems, + alreadyExtraHighGroup, + ); + const alreadyExtraHighDocumentStub = { + ...documentStub, + querySelector: (selector: string) => { + if (selector.includes("composer-intelligence-pro-thinking-effort-trigger")) return null; + if (selector.includes("composer-intelligence-picker-content")) { + return alreadyExtraHighGroup; + } + if ( + selector.includes("model-switcher-dropdown-button") || + selector.includes("__composer-pill") + ) { + return alreadyExtraHighPill; + } + return null; + }, + querySelectorAll: (selector: string) => { + if (selector.includes("__composer-pill")) return [alreadyExtraHighPill]; + if (selector.includes('role="menu"') || selector.includes("data-radix")) { + return [alreadyExtraHighMenu]; + } + return []; + }, + }; + + await expect( + evaluateSolExtraHigh( + alreadyExtraHighDocumentStub, + performanceStub, + (callback: () => void) => callback(), + { PointerEvent: FakeMouseEvent, MouseEvent: FakeMouseEvent, Event: FakeMouseEvent }, + FakeEventTarget, + FakeMouseEvent, + FakeMouseEvent, + FakeElement, + ), + ).resolves.toEqual({ status: "already-selected", label: "Extra High" }); }); it("selects exact Chinese Intelligence tiers without prefix collisions", async () => { @@ -729,7 +887,7 @@ describe("browser thinking-time selection expression", () => { } const cases: Array<{ - level: "light" | "standard" | "extended" | "heavy"; + level: "light" | "standard" | "extended" | "extra-high" | "heavy"; label: string; reverseAmbiguousPair?: boolean; omitExtraHigh?: boolean; @@ -737,9 +895,9 @@ describe("browser thinking-time selection expression", () => { { level: "light", label: "极速5.5" }, { level: "standard", label: "中" }, { level: "extended", label: "高", reverseAmbiguousPair: true }, - { level: "heavy", label: "极高" }, - { level: "heavy", label: "极高", reverseAmbiguousPair: true }, - { level: "heavy", label: "高", omitExtraHigh: true }, + { level: "extra-high", label: "极高" }, + { level: "extra-high", label: "极高", reverseAmbiguousPair: true }, + { level: "extra-high", label: "高", omitExtraHigh: true }, ]; for (const testCase of cases) { @@ -1303,7 +1461,7 @@ describe("browser thinking-time selection expression", () => { extraHigh.setAttribute("data-state", "unchecked"); let now = 0; let timers = 0; - const expression = buildThinkingTimeExpressionForTest("heavy", targetModel); + const expression = buildThinkingTimeExpressionForTest("extra-high", targetModel); const evaluate = new Function( "document", "performance", @@ -2061,7 +2219,7 @@ describe("browser thinking-time selection expression", () => { }; let now = 0; const performanceStub = { now: () => (now += 100) }; - const expression = buildThinkingTimeExpressionForTest("heavy", "Thinking 5.5"); + const expression = buildThinkingTimeExpressionForTest("extra-high", "Thinking 5.5"); const evaluate = new Function( "document", "performance", diff --git a/tests/cli/options.test.ts b/tests/cli/options.test.ts index cb9550cba..50c53d07f 100644 --- a/tests/cli/options.test.ts +++ b/tests/cli/options.test.ts @@ -194,10 +194,10 @@ describe("parseThinkingTimeOption", () => { ["extended", "extended"], ["high", "extended"], ["heavy", "heavy"], - ["extra-high", "heavy"], - ["extra high", "heavy"], - ["extrahigh", "heavy"], - ["xhigh", "heavy"], + ["extra-high", "extra-high"], + ["extra high", "extra-high"], + ["extrahigh", "extra-high"], + ["xhigh", "extra-high"], ] as const)("normalizes %s to %s", (input, expected) => { expect(parseThinkingTimeOption(input)).toBe(expected); }); diff --git a/tests/mcp/chatgptImage.test.ts b/tests/mcp/chatgptImage.test.ts index b01563ae1..da51f3ec3 100644 --- a/tests/mcp/chatgptImage.test.ts +++ b/tests/mcp/chatgptImage.test.ts @@ -74,7 +74,7 @@ describe("chatgpt_image MCP tool", () => { })) as { structuredContent: { resolved: { browser?: { thinkingTime?: string } } }; }; - expect(result.structuredContent.resolved.browser?.thinkingTime).toBe("heavy"); + expect(result.structuredContent.resolved.browser?.thinkingTime).toBe("extra-high"); }); test("uses a unique default output path when agents only provide a prompt", () => { diff --git a/tests/mcp/consult.test.ts b/tests/mcp/consult.test.ts index 65232fee9..60436c4ed 100644 --- a/tests/mcp/consult.test.ts +++ b/tests/mcp/consult.test.ts @@ -65,7 +65,7 @@ describe("summarizeModelRunsForConsult", () => { browserThinkingTime: "xhigh", }), ).toMatchObject({ - browserThinkingTime: "heavy", + browserThinkingTime: "extra-high", }); });