Merge pull request #17625 from NousResearch/bb/tui-reasoning-hide

fix(tui): hide reasoning panels immediately
This commit is contained in:
brooklyn!
2026-04-29 14:49:20 -07:00
committed by GitHub
5 changed files with 116 additions and 5 deletions

View File

@@ -1066,6 +1066,18 @@ def test_config_set_reasoning_updates_live_session_and_agent(tmp_path, monkeypat
)
assert resp_show["result"]["value"] == "show"
assert server._sessions["sid"]["show_reasoning"] is True
assert server._load_cfg()["display"]["sections"]["thinking"] == "expanded"
resp_hide = server.handle_request(
{
"id": "3",
"method": "config.set",
"params": {"session_id": "sid", "key": "reasoning", "value": "hide"},
}
)
assert resp_hide["result"]["value"] == "hide"
assert server._sessions["sid"]["show_reasoning"] is False
assert server._load_cfg()["display"]["sections"]["thinking"] == "hidden"
def test_config_set_verbose_updates_session_mode_and_agent(tmp_path, monkeypatch):

View File

@@ -3105,12 +3105,34 @@ def _(rid, params: dict) -> dict:
arg = str(value or "").strip().lower()
if arg in ("show", "on"):
_write_config_key("display.show_reasoning", True)
cfg = _load_cfg()
display = cfg.get("display") if isinstance(cfg.get("display"), dict) else {}
sections = (
display.get("sections")
if isinstance(display.get("sections"), dict)
else {}
)
display["show_reasoning"] = True
sections["thinking"] = "expanded"
display["sections"] = sections
cfg["display"] = display
_save_cfg(cfg)
if session:
session["show_reasoning"] = True
return _ok(rid, {"key": key, "value": "show"})
if arg in ("hide", "off"):
_write_config_key("display.show_reasoning", False)
cfg = _load_cfg()
display = cfg.get("display") if isinstance(cfg.get("display"), dict) else {}
sections = (
display.get("sections")
if isinstance(display.get("sections"), dict)
else {}
)
display["show_reasoning"] = False
sections["thinking"] = "hidden"
display["sections"] = sections
cfg["display"] = display
_save_cfg(cfg)
if session:
session["show_reasoning"] = False
return _ok(rid, {"key": key, "value": "hide"})

View File

@@ -76,6 +76,45 @@ describe('createSlashHandler', () => {
})
})
it('applies /reasoning hide to the thinking section immediately', async () => {
patchUiState({ sections: { thinking: 'expanded' }, showReasoning: true, sid: 'sid-abc' })
const ctx = buildCtx({
gateway: {
...buildGateway(),
rpc: vi.fn(() => Promise.resolve({ value: 'hide' }))
}
})
expect(createSlashHandler(ctx)('/reasoning hide')).toBe(true)
await vi.waitFor(() => {
expect(getUiState().showReasoning).toBe(false)
expect(getUiState().sections.thinking).toBe('hidden')
})
expect(ctx.gateway.rpc).toHaveBeenCalledWith('config.set', {
key: 'reasoning',
session_id: 'sid-abc',
value: 'hide'
})
})
it('applies /reasoning show to the thinking section immediately', async () => {
patchUiState({ sections: { thinking: 'hidden' }, showReasoning: false, sid: 'sid-abc' })
const ctx = buildCtx({
gateway: {
...buildGateway(),
rpc: vi.fn(() => Promise.resolve({ value: 'show' }))
}
})
expect(createSlashHandler(ctx)('/reasoning show')).toBe(true)
await vi.waitFor(() => {
expect(getUiState().showReasoning).toBe(true)
expect(getUiState().sections.thinking).toBe('expanded')
})
})
it('opens the skills hub locally for bare /skills', () => {
const ctx = buildCtx()

View File

@@ -332,7 +332,29 @@ export const sessionCommands: SlashCommand[] = [
ctx.gateway
.rpc<ConfigSetResponse>('config.set', { key: 'reasoning', session_id: ctx.sid, value: arg })
.then(ctx.guarded<ConfigSetResponse>(r => r.value && ctx.transcript.sys(`reasoning: ${r.value}`)))
.then(
ctx.guarded<ConfigSetResponse>(r => {
if (!r.value) {
return
}
if (r.value === 'hide') {
patchUiState(state => ({
...state,
sections: { ...state.sections, thinking: 'hidden' },
showReasoning: false
}))
} else if (r.value === 'show') {
patchUiState(state => ({
...state,
sections: { ...state.sections, thinking: 'expanded' },
showReasoning: true
}))
}
ctx.transcript.sys(`reasoning: ${r.value}`)
})
)
}
},

View File

@@ -711,6 +711,9 @@ export function useMainApp(gw: GatewayClient) {
const anyPanelVisible = SECTION_NAMES.some(
s => sectionMode(s, ui.detailsMode, ui.sections, ui.detailsModeCommandOverride) !== 'hidden'
)
const thinkingPanelVisible = sectionMode('thinking', ui.detailsMode, ui.sections, ui.detailsModeCommandOverride) !== 'hidden'
const toolsPanelVisible = sectionMode('tools', ui.detailsMode, ui.sections, ui.detailsModeCommandOverride) !== 'hidden'
const activityPanelVisible = sectionMode('activity', ui.detailsMode, ui.sections, ui.detailsModeCommandOverride) !== 'hidden'
const showProgressArea = useTurnSelector(state =>
anyPanelVisible
@@ -718,12 +721,25 @@ export function useMainApp(gw: GatewayClient) {
ui.busy ||
state.outcome ||
state.streamPendingTools.length ||
state.streamSegments.length ||
state.streamSegments.some(segment => {
const hasThinking = Boolean(segment.thinking?.trim())
const hasTrailTools = Boolean(segment.tools?.length)
if (segment.kind === 'trail' && !segment.text) {
return (thinkingPanelVisible && hasThinking) || ((toolsPanelVisible || activityPanelVisible) && hasTrailTools)
}
return (
Boolean(segment.text?.trim()) ||
(thinkingPanelVisible && hasThinking) ||
((toolsPanelVisible || activityPanelVisible) && hasTrailTools)
)
}) ||
state.subagents.length ||
state.tools.length ||
state.todos.length ||
state.turnTrail.length ||
hasReasoning ||
(thinkingPanelVisible && hasReasoning) ||
state.activity.length
)
: state.activity.some(item => item.tone !== 'info')