From ee1d410deafba5bc9bc36e14a941342e7ce65f2f Mon Sep 17 00:00:00 2001 From: Brendonovich <14191578+Brendonovich@users.noreply.github.com> Date: Thu, 24 Sep 2026 10:27:09 +0000 Subject: [PATCH] refactor(app): choose reasoning-only rows at turn construction --- .../session-timeline-working.spec.ts | 2 +- .../session-ui/src/timeline/projection.ts | 32 +++++++++++-------- .../src/timeline/rows-current.test.ts | 14 ++++++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/app/e2e/regression/session-timeline-working.spec.ts b/packages/app/e2e/regression/session-timeline-working.spec.ts index f09cbfa6e96..e21f66a8a94 100644 --- a/packages/app/e2e/regression/session-timeline-working.spec.ts +++ b/packages/app/e2e/regression/session-timeline-working.spec.ts @@ -67,8 +67,8 @@ for (const width of [1400, 390]) { await expect(group).toHaveCount(0) await expect(page.getByRole("button", { name: "Used 1 Thought" })).toHaveCount(0) + if (width === 390) return await timeline.send(partUpdated(toolPart("prt_after_thought", "read", "running", { filePath: "package.json" }))) - if (width === 390) await page.getByRole("button", { name: "Jump to latest" }).click() await expect(group).toBeVisible() await expect(group).toHaveAttribute("data-timeline-part-ids", `${assistantID}:reasoning:0,prt_after_thought`) await expect(working).toBeVisible() diff --git a/packages/session-ui/src/timeline/projection.ts b/packages/session-ui/src/timeline/projection.ts index a9d5f57de5e..5840fa261db 100644 --- a/packages/session-ui/src/timeline/projection.ts +++ b/packages/session-ui/src/timeline/projection.ts @@ -248,10 +248,26 @@ export namespace Timeline { : [], ), ] + const rowEntries = + detail?.thinking.placement === "grouped" && + status.type === "busy" && + turn.id === activeMessageID && + turn.entries.length > 0 && + turn.entries.every( + (entry) => + entry.type === "assistant" && + !entry.message.error && + !entry.message.retry && + entry.message.content.every( + (content) => content.type === "reasoning" || (content.type === "text" && !content.text.trim()), + ), + ) + ? [] + : turn.entries return constructMessageRows( turn.user, turn.id, - turn.entries, + rowEntries, index, showReasoning, status, @@ -307,16 +323,6 @@ export namespace Timeline { !lastAssistant?.retry && lastContent?.type === "reasoning" && lastContent.time?.completed === undefined - const onlyThinking = - detail?.thinking.placement === "grouped" && - isActive && - status.type === "busy" && - !!lastAssistant && - !lastAssistant.error && - !lastAssistant.retry && - entries.every( - (entry) => entry.type === "assistant" && entry.message.content.every((content) => content.type === "reasoning"), - ) if (previousUserMessage) rows.push(new TimelineRow.TurnGap({ userMessageID: turnID })) if (userMessage) rows.push(new TimelineRow.UserMessage({ userMessageID: turnID })) @@ -330,9 +336,7 @@ export namespace Timeline { contentEntries(message) .filter( (entry) => - isRenderable(entry.content, showReasoning, detail) && - !(thinking && entry.content === lastContent) && - !(onlyThinking && entry.content.type === "reasoning"), + isRenderable(entry.content, showReasoning, detail) && !(thinking && entry.content === lastContent), ) .map((entry) => ({ messageID: message.id, messageIndex, partID: entry.id, content: entry.content })), ) diff --git a/packages/session-ui/src/timeline/rows-current.test.ts b/packages/session-ui/src/timeline/rows-current.test.ts index 808da81ad0b..591b97839f9 100644 --- a/packages/session-ui/src/timeline/rows-current.test.ts +++ b/packages/session-ui/src/timeline/rows-current.test.ts @@ -280,6 +280,7 @@ describe("current session timeline rows", () => { expect(rows([{ ...thought, time: { created: 1 } }], true).map((row) => row._tag)).toEqual(["UserMessage"]) expect(rows([thought], true).map((row) => row._tag)).toEqual(["UserMessage"]) + expect(rows([thought, { type: "text", text: "" }], true).map((row) => row._tag)).toEqual(["UserMessage"]) expect(rows([thought, { type: "text", text: "Answer" }], true).map((row) => row._tag)).toEqual([ "UserMessage", "AssistantPart", @@ -289,6 +290,19 @@ describe("current session timeline rows", () => { rows([thought, storyTool("read", "read", "running", { filePath: "package.json" })], true).map((row) => row._tag), ).toEqual(["UserMessage", "AssistantPart"]) expect(rows([thought], false).map((row) => row._tag)).toEqual(["UserMessage", "AssistantPart"]) + + const failed = storyDocument([thought], true) + const messages = failed.messages.map((message) => + message.type === "assistant" ? { ...message, error: { type: "provider.error", message: "Failed" } } : message, + ) + expect( + createTimelineProjection({ + sessionMessages: messages, + status: failed.status, + reasoningMode: "compact", + timelineDetail: timelinePresets[2].value, + }).rows.map((row) => row._tag), + ).toEqual(["UserMessage", "AssistantPart", "Error"]) }) test("stops thinking on idle, message completion, errors and retries", () => {