From 4a94dbe8afff92ab7ac8ad9b4c736161a76dbedf Mon Sep 17 00:00:00 2001 From: Aiden Date: Thu, 20 Aug 2026 03:20:57 +0000 Subject: [PATCH 1/2] fix(tui): show encrypted reasoning status --- packages/tui/src/routes/session/index.tsx | 83 ++++++++++++------- packages/tui/src/routes/session/rows.ts | 14 +++- .../tui/test/cli/tui/session-rows.test.ts | 22 +++++ 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 357df25780d6..d1c376571eb9 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -1629,10 +1629,11 @@ function SessionReasoningGroupView(props: { const message = props.message(ref.messageID) if (message?.type !== "assistant") return [] const part = resolvePart(message, ref.partID) - if (part?.type !== "reasoning" || !reasoningContent(part)) return [] + if (part?.type !== "reasoning" || (!reasoningContent(part) && !part.state)) return [] return [{ message, part }] }), ) + const opaque = createMemo(() => parts().length > 0 && parts().every((item) => !reasoningContent(item.part))) const latest = createMemo((previous: string | null) => { const item = parts().at(-1) if (!item) return previous @@ -1657,7 +1658,7 @@ function SessionReasoningGroupView(props: { > setHover(true)} + onMouseOver={() => !opaque() && setHover(true)} onMouseOut={() => setHover(false)} onMouseUp={() => { - if (renderer.getSelection()?.getSelectedText()) return + if (renderer.getSelection()?.getSelectedText() || opaque()) return setExpanded((value) => !value) }} > - {props.completed ? "Thought" : latest() ? `Thinking: ${latest()}` : "Thinking"} - : {latest()} - 1}> · {parts().length} steps - · {Locale.duration(duration())} + + {props.completed ? "Thought" : latest() ? `Thinking: ${latest()}` : "Thinking"} + : {latest()} + 1}> · {parts().length} steps + · {Locale.duration(duration())} + + } + > + Thought + · {Locale.duration(duration())} · encrypted + - + {(ref) => { @@ -2296,6 +2307,7 @@ function ReasoningPart(props: { const [expanded, setExpanded] = createSignal(false) const content = createMemo(() => reasoningContent(props.part)) + const opaque = createMemo(() => !content() && Boolean(props.part.state)) const isDone = createMemo( () => props.part.time?.completed !== undefined || props.message.time.completed !== undefined, ) @@ -2307,12 +2319,12 @@ function ReasoningPart(props: { }) const summary = createMemo(() => reasoningSummary(content())) const toggle = () => { - if (!inMinimal()) return + if (!inMinimal() || opaque()) return setExpanded((prev) => !prev) } return ( - + - + @@ -2387,21 +2401,34 @@ function ReasoningHeader(props: { - - {props.open ? "- " : "+ "} - - Thought - - : - - - {props.title} - - - - {props.title ? " · " : ""} - {props.duration} - + + + {props.open ? "- " : "+ "} + + Thought + + : + + + {props.title} + + + + {props.title ? " · " : ""} + {props.duration} + + + + } + > + Thought + + · {props.duration} + + · encrypted diff --git a/packages/tui/src/routes/session/rows.ts b/packages/tui/src/routes/session/rows.ts index e1e0efe78a2a..335c24aa9a76 100644 --- a/packages/tui/src/routes/session/rows.ts +++ b/packages/tui/src/routes/session/rows.ts @@ -239,6 +239,13 @@ export function createSessionRows(sessionID: Accessor, onSynced?: (sessi if (event.data.sessionID === sessionID() && event.data.text.trim()) appendPart({ messageID: event.data.assistantMessageID, partID: `text:${event.data.ordinal}` }, { type: "text" }) }), + data.on("session.reasoning.started", (event) => { + if (event.data.sessionID === sessionID() && event.data.state) + appendPart( + { messageID: event.data.assistantMessageID, partID: `reasoning:${event.data.ordinal}` }, + { type: "reasoning" }, + ) + }), data.on("session.reasoning.delta", (event) => { if (event.data.sessionID === sessionID() && event.data.delta.trim()) appendPart( @@ -305,7 +312,12 @@ export function reduceSessionRows(messages: SessionMessageInfo[], inputs = new S const ordinals = { text: 0, reasoning: 0 } message.content.forEach((part) => { const partID = part.type === "tool" ? part.id : `${part.type}:${ordinals[part.type]++}` - if ((part.type === "text" || part.type === "reasoning") && !part.text.trim()) return + if ( + (part.type === "text" || part.type === "reasoning") && + !part.text.trim() && + !(part.type === "reasoning" && part.state) + ) + return append(rows, { messageID: message.id, partID }, part) }) const terminal = (message.finish && !["tool-calls", "unknown"].includes(message.finish)) || message.error diff --git a/packages/tui/test/cli/tui/session-rows.test.ts b/packages/tui/test/cli/tui/session-rows.test.ts index 74fcf08e8ac5..f1bc58c4c2fc 100644 --- a/packages/tui/test/cli/tui/session-rows.test.ts +++ b/packages/tui/test/cli/tui/session-rows.test.ts @@ -266,6 +266,28 @@ test("groups across empty assistant reasoning parts", () => { ]) }) +test("keeps empty reasoning with provider state", () => { + const message = assistant("assistant-1", [ + { + type: "reasoning", + text: "", + state: { reasoningEncryptedContent: "opaque" }, + time: { created: 1_000, completed: 4_200 }, + }, + ]) + message.finish = "stop" + + expect(reduceSessionRows([message])).toEqual([ + { + type: "group", + kind: "reasoning", + completed: true, + refs: [{ messageID: "assistant-1", partID: "reasoning:0" }], + }, + { type: "assistant-footer", messageID: "assistant-1" }, + ]) +}) + test("completes exploration groups when another row follows", () => { const finished = assistant("assistant-2", [ { type: "tool", id: "grep-1", name: "grep", state: pending(), time: { created: 3 } }, From ff7b40ed4ac7dab03bf5195d6060e1517b4d604e Mon Sep 17 00:00:00 2001 From: Aiden Date: Thu, 20 Aug 2026 03:31:18 +0000 Subject: [PATCH 2/2] refactor(tui): handle reasoning starts uniformly --- packages/tui/src/routes/session/rows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tui/src/routes/session/rows.ts b/packages/tui/src/routes/session/rows.ts index 335c24aa9a76..017fb19506ff 100644 --- a/packages/tui/src/routes/session/rows.ts +++ b/packages/tui/src/routes/session/rows.ts @@ -240,7 +240,7 @@ export function createSessionRows(sessionID: Accessor, onSynced?: (sessi appendPart({ messageID: event.data.assistantMessageID, partID: `text:${event.data.ordinal}` }, { type: "text" }) }), data.on("session.reasoning.started", (event) => { - if (event.data.sessionID === sessionID() && event.data.state) + if (event.data.sessionID === sessionID()) appendPart( { messageID: event.data.assistantMessageID, partID: `reasoning:${event.data.ordinal}` }, { type: "reasoning" },