From 61f983098be44316944e77e3bc81538a6a8fb0e9 Mon Sep 17 00:00:00 2001 From: Ibrahim Elkamali <126423069+Marve10s@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:38:29 +0200 Subject: [PATCH] fix(web): allow switching away from Ultrathink without manual prompt editing (#1307) --- .../CompactComposerControlsMenu.browser.tsx | 25 ++++++++++++++++--- .../components/chat/TraitsPicker.browser.tsx | 22 +++++++++++++--- apps/web/src/components/chat/TraitsPicker.tsx | 24 ++++++++++++++---- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx index aa3550e0071f157d4170bbfaa3bdd1f01a6280d9..eee6f885e9468af35bda2547dd2b80d034e05f2d 100644 --- a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx +++ b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx @@ -220,7 +220,7 @@ describe("CompactComposerControlsMenu", () => { }); }); - it("shows prompt-controlled Ultrathink messaging with disabled effort controls", async () => { + it("shows prompt-controlled Ultrathink state with selectable effort controls", async () => { await using _ = await mountMenu({ modelSelection: { provider: "claudeAgent", @@ -235,8 +235,27 @@ describe("CompactComposerControlsMenu", () => { await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).toContain("Effort"); - expect(text).toContain("Remove Ultrathink from the prompt to change effort."); - expect(text).not.toContain("Fallback Effort"); + expect(text).not.toContain("ultrathink"); + }); + }); + + it("warns when ultrathink appears in prompt body text", async () => { + await using _ = await mountMenu({ + modelSelection: { + provider: "claudeAgent", + model: "claude-opus-4-6", + options: { effort: "high" }, + }, + prompt: "Ultrathink:\nplease ultrathink about this problem", + }); + + await page.getByLabelText("More composer controls").click(); + + await vi.waitFor(() => { + const text = document.body.textContent ?? ""; + expect(text).toContain( + 'Your prompt contains "ultrathink" in the text. Remove it to change effort.', + ); }); }); }); diff --git a/apps/web/src/components/chat/TraitsPicker.browser.tsx b/apps/web/src/components/chat/TraitsPicker.browser.tsx index 99d09fd634dc4a34ffcc8729122fe967f94a60df..9dea3651ea89021c117edf08d1198be7c06951b2 100644 --- a/apps/web/src/components/chat/TraitsPicker.browser.tsx +++ b/apps/web/src/components/chat/TraitsPicker.browser.tsx @@ -296,7 +296,7 @@ describe("TraitsPicker (Claude)", () => { }); }); - it("shows prompt-controlled Ultrathink state with disabled effort controls", async () => { + it("shows prompt-controlled Ultrathink state with selectable effort controls", async () => { await using _ = await mountClaudePicker({ model: "claude-opus-4-6", options: { effort: "high" }, @@ -312,8 +312,24 @@ describe("TraitsPicker (Claude)", () => { await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).toContain("Effort"); - expect(text).toContain("Remove Ultrathink from the prompt to change effort."); - expect(text).not.toContain("Fallback Effort"); + expect(text).not.toContain("ultrathink"); + }); + }); + + it("warns when ultrathink appears in prompt body text", async () => { + await using _ = await mountClaudePicker({ + model: "claude-opus-4-6", + options: { effort: "high" }, + prompt: "Ultrathink:\nplease ultrathink about this problem", + }); + + await page.getByRole("button").click(); + + await vi.waitFor(() => { + const text = document.body.textContent ?? ""; + expect(text).toContain( + 'Your prompt contains "ultrathink" in the text. Remove it to change effort.', + ); }); }); diff --git a/apps/web/src/components/chat/TraitsPicker.tsx b/apps/web/src/components/chat/TraitsPicker.tsx index a3b6cbb48f7c42ef76776af07ccfe2a60562aef7..061594ad53879757def01e55f991f0e5272b4eb2 100644 --- a/apps/web/src/components/chat/TraitsPicker.tsx +++ b/apps/web/src/components/chat/TraitsPicker.tsx @@ -120,6 +120,10 @@ function getSelectedTraits( caps.promptInjectedEffortLevels.length > 0 && isClaudeUltrathinkPrompt(prompt); + // Check if "ultrathink" appears in the body text (not just our prefix) + const ultrathinkInBodyText = + ultrathinkPromptControlled && isClaudeUltrathinkPrompt(prompt.replace(/^Ultrathink:\s*/i, "")); + return { caps, effort, @@ -130,6 +134,7 @@ function getSelectedTraits( contextWindow, defaultContextWindow, ultrathinkPromptControlled, + ultrathinkInBodyText, }; } @@ -176,12 +181,12 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ contextWindow, defaultContextWindow, ultrathinkPromptControlled, + ultrathinkInBodyText, } = getSelectedTraits(provider, models, model, prompt, modelOptions, allowPromptInjectedEffort); const defaultEffort = getDefaultEffort(caps); const handleEffortChange = useCallback( (value: string) => { - if (ultrathinkPromptControlled) return; if (!value) return; const nextOption = effortLevels.find((option) => option.value === value); if (!nextOption) return; @@ -193,6 +198,11 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ onPromptChange(nextPrompt); return; } + if (ultrathinkInBodyText) return; + if (ultrathinkPromptControlled) { + const stripped = prompt.replace(/^Ultrathink:\s*/i, ""); + onPromptChange(stripped); + } const effortKey = provider === "codex" ? "reasoningEffort" : "effort"; updateModelOptions( buildNextOptions(provider, modelOptions, { [effortKey]: nextOption.value }), @@ -200,6 +210,7 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ }, [ ultrathinkPromptControlled, + ultrathinkInBodyText, modelOptions, onPromptChange, updateModelOptions, @@ -220,17 +231,20 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ <>
Effort
- {ultrathinkPromptControlled ? ( + {ultrathinkInBodyText ? (
- Remove Ultrathink from the prompt to change effort. + Your prompt contains "ultrathink" in the text. Remove it to change effort.
) : null} - + {effortLevels.map((option) => ( {option.label} {option.value === defaultEffort ? " (default)" : ""} -- 2.54.0