authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-04 14:26:31-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 14:26:31-04:00
logcfc9bf34156ddcc4a98b7f5c67193adb5aedce06
tree969c241f03318f1c87fdc6525f33e453c05205e4
parentc8f77e0d441264efb0acfac312e852c81ae3da83
signature Signed by PGP key B5690EEEBB952194

fix(web): fold single trailing activity (#9739)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

3 files changed, 17 insertions(+), 8 deletions(-)

apps/web/src/components/chat/MessagesTimeline.logic.test.ts+9-4
......@@ -1170,11 +1170,10 @@ describe("deriveMessagesTimelineRows", () => {
11701170 })),
11711171 ];
11721172
1173 const rows = deriveMessagesTimelineRows({
1174 timelineEntries,
1173 const input = {
11751174 latestTurn: {
11761175 turnId,
1177 state: "error",
1176 state: "error" as const,
11781177 startedAt: "2026-01-01T00:00:00Z",
11791178 completedAt: "2026-01-01T00:00:10Z",
11801179 },
......@@ -1182,7 +1181,8 @@ describe("deriveMessagesTimelineRows", () => {
11821181 activeTurnStartedAt: null,
11831182 turnDiffSummaryByAssistantMessageId: new Map(),
11841183 revertTurnCountByUserMessageId: new Map(),
1185 });
1184 };
1185 const rows = deriveMessagesTimelineRows({ ...input, timelineEntries });
11861186
11871187 expect(rows.map((row) => row.id)).toEqual([
11881188 "turn-fold:turn-1",
......@@ -1205,6 +1205,11 @@ describe("deriveMessagesTimelineRows", () => {
12051205 showAssistantMeta: false,
12061206 showAssistantCopyButton: false,
12071207 });
1208 expect(
1209 deriveMessagesTimelineRows({ ...input, timelineEntries: timelineEntries.slice(0, 3) }).map(
1210 (row) => row.id,
1211 ),
1212 ).toEqual(["turn-fold:turn-1", "assistant-final-entry"]);
12081213 });
12091214
12101215 it("folds all assistant messages before the terminal message", () => {
apps/web/src/components/chat/MessagesTimeline.logic.ts+7-3
......@@ -542,8 +542,8 @@ function workEntryIsActiveTurnActivity(entry: WorkLogEntry): boolean {
542542
543543/**
544544 * Settled turns fold activity before their terminal assistant message behind
545 * a "Worked for ..." row. Work that lands after that message stays visible so
546 * failed or interrupted turns do not hide their trailing tool-call summary.
545 * a "Worked for ..." row. A single ordinary activity after that message joins
546 * the fold, while larger groups and failures stay visible as a trailing summary.
547547 */
548548function deriveTurnFolds(input: {
549549 timelineEntries: ReadonlyArray<TimelineEntry>;
......@@ -623,7 +623,11 @@ function deriveTurnFolds(input: {
623623 }
624624 const isCompaction =
625625 entry.kind === "work" && entry.entry.sourceActivityKind === "context-compaction";
626 if (!isCompaction && index > terminalEntryIndex) {
626 const isSingleTrailingActivity =
627 group.entries.length === terminalEntryIndex + 2 &&
628 entry.kind === "work" &&
629 !workEntryDisplayIndicatesToolFailure(entry.entry);
630 if (!isCompaction && index > terminalEntryIndex && !isSingleTrailingActivity) {
627631 continue;
628632 }
629633 // Agent-spawn CTA rows never fold: workflows outlive their launching
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-1
......@@ -1135,7 +1135,7 @@ describe("MessagesTimeline", () => {
11351135 label: "Ran command",
11361136 tone: "tool",
11371137 itemType: "command_execution",
1138 toolLifecycleStatus: "completed",
1138 toolLifecycleStatus: "failed",
11391139 },
11401140 },
11411141 ]}