Repository navigation
Thinking-level (reasoning effort) picker in the chat composer #44
Description
Problem or Motivation
OpenWork supports six per-session thinking levels (off / low / medium /
high / xhigh / max), but there is no way to change the thinking level from
the chat composer. Today the only place to influence it is Settings → AI, which
sets a per-workspace default for new chats. To dial reasoning effort up for a
single hard task or down for a quick one, you have to leave the conversation, open
settings, change the default, and start a new chat.
Comparable desktop apps put this control right in the composer, next to the model
picker: Claude Code Desktop ships a dedicated effort menu (⌘⇧E) alongside its
model menu, so you can raise or lower reasoning effort per message without leaving
the prompt.
OpenWork already has the neighbor for it — the composer has a model picker
dropdown — but the thinking-level equivalent is missing even though all the
plumbing is already in place:
- The composer (
FreeFormInput) already receivesthinkingLeveland
onThinkingLevelChangeprops — they are threaded fromChatPage/
DraftChatPage→ChatDisplay→ChatInputZone→InputContainer→
FreeFormInput, and both pages already persist the value (draft
createOptions.thinkingLevel/ session options). ButFreeFormInputnever
renders a trigger for it — the prop is only read for an ACP command refresh. - The level metadata already exists:
THINKING_LEVELS(id +nameKey+
descriptionKey) inpackages/shared/src/agent/thinking-levels.ts, and the
thinking.*translation keys are already present in all locales.
So the value is fully wired end-to-end — the app just has no UI to set it from the
place you actually compose.
Proposed Solution
Add a thinking-level picker to the composer toolbar, immediately before the
model picker, mirroring the existing model-picker dropdown:
- A toolbar button showing the current level's name (e.g. "Medium") with a brain
icon and a chevron; tooltip "Thinking". - Opening it lists all six levels (
THINKING_LEVELSorder) with name +
description, a check on the active level, and calls the already-wired
onThinkingLevelChange(level)on select — so the choice persists exactly like a
model change does. - Hidden in
compactMode(same rule the model picker uses).
This is a frontend-only change:
- It renders a Radix dropdown (the same
StyledDropdownMenu*primitives the model
picker uses) and calls the existingonThinkingLevelChangeprop. No backend / no
qwen-code change — the value already flows to the runtime today. - It introduces no new i18n keys: level names/descriptions reuse the existing
thinking.*keys and the trigger tooltip reusessettings.ai.thinking.
Alternatives Considered
- Settings → AI thinking default already exists, but it is a workspace
default for new chats, not a per-conversation control. You can't raise effort
for the current task without starting over. - A keyboard-only shortcut (à la
⌘⇧E) could open a menu, but OpenWork's
established pattern for composer controls is a visible toolbar dropdown (the
model picker), so a visible picker is the more discoverable, consistent fit. A
shortcut can be layered on later.
Additional Context
Feasibility: frontend-only / doable. Verified against apps/electron — the
thinkingLevel / onThinkingLevelChange props already reach FreeFormInput and
both the draft and active chat pages already persist the value; only the UI trigger
is missing.
Acceptance criteria (CDP e2e assertion)
A new e2e/assertions/thinking-level-picker.assert.ts drives the real built app
over CDP and asserts the full path:
- The composer renders a thinking-level trigger button.
- Clicking it opens a menu listing all six thinking levels.
- The trigger reflects a known current level (the default), and the active level
is marked as selected in the menu. - Selecting a different level closes the menu and updates the trigger
label to the newly selected level — proving the picker actually changes the
session's thinking level, not merely displays a list.
Part of the autonomous desktop-feature loop (loop-bot).
Activity
Add a thinking-level (reasoning effort) picker to the chat composer (m…