authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-04 04:12:46-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 04:12:46-04:00
log5f878d2a85807618a4c8571cdef5daa3124672d6
tree1071e5e0d8dca7c48ed185d075a85f1977b10fc8
parentec3ec6f0b4e005c47aff07d4d9e31506241bce3a
signature Signed by PGP key B5690EEEBB952194

fix(web,mobile): fold context compaction under settled turn folds (#9623)

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

2 files changed, 24 insertions(+), 9 deletions(-)

apps/mobile/src/lib/threadActivity.ts+10-3
...@@ -1375,9 +1375,6 @@ function deriveThreadFeedTurnFolds(...@@ -1375,9 +1375,6 @@ function deriveThreadFeedTurnFolds(
1375 pendingUserBoundary = entry.message.createdAt;1375 pendingUserBoundary = entry.message.createdAt;
1376 continue;1376 continue;
1377 }1377 }
1378 if (entry.type === "activity-group" && isContextCompactionActivityGroup(entry)) {
1379 continue;
1380 }
1381 const turnId =1378 const turnId =
1382 entry.type === "message" && entry.message.role === "assistant"1379 entry.type === "message" && entry.message.role === "assistant"
1383 ? entry.message.turnId1380 ? entry.message.turnId
...@@ -1423,6 +1420,16 @@ function deriveThreadFeedTurnFolds(...@@ -1423,6 +1420,16 @@ function deriveThreadFeedTurnFolds(
1423 if (hiddenEntryIds.size === 0) {1420 if (hiddenEntryIds.size === 0) {
1424 continue;1421 continue;
1425 }1422 }
1423 // A lone compaction row stays visible on its own; it only folds away as
1424 // part of a turn that already folds other work.
1425 const hidesNonCompactionWork = entries.some(
1426 (entry) =>
1427 hiddenEntryIds.has(entry.id) &&
1428 !(entry.type === "activity-group" && isContextCompactionActivityGroup(entry)),
1429 );
1430 if (!hidesNonCompactionWork) {
1431 continue;
1432 }
14261433
1427 const firstEntry = entries[0];1434 const firstEntry = entries[0];
1428 const firstHiddenEntry = entries.find((entry) => hiddenEntryIds.has(entry.id));1435 const firstHiddenEntry = entries.find((entry) => hiddenEntryIds.has(entry.id));
apps/web/src/components/chat/MessagesTimeline.logic.ts+14-6
...@@ -619,17 +619,15 @@ function deriveTurnFolds(input: {...@@ -619,17 +619,15 @@ function deriveTurnFolds(input: {
619 if (entry.id === group.terminalEntry?.id) {619 if (entry.id === group.terminalEntry?.id) {
620 continue;620 continue;
621 }621 }
622 if (index > terminalEntryIndex) {622 const isCompaction =
623 entry.kind === "work" && entry.entry.sourceActivityKind === "context-compaction";
624 if (!isCompaction && index > terminalEntryIndex) {
623 continue;625 continue;
624 }626 }
625 // Agent-spawn CTA rows never fold: workflows outlive their launching627 // Agent-spawn CTA rows never fold: workflows outlive their launching
626 // turn (dynamic spawns, background execution), and folding the CTA628 // turn (dynamic spawns, background execution), and folding the CTA
627 // when the turn settles makes a still-running fleet invisible.629 // when the turn settles makes a still-running fleet invisible.
628 if (630 if (entry.kind === "work" && entry.entry.agentSpawn !== undefined) {
629 entry.kind === "work" &&
630 (entry.entry.agentSpawn !== undefined ||
631 entry.entry.sourceActivityKind === "context-compaction")
632 ) {
633 continue;631 continue;
634 }632 }
635 hiddenEntryIds.add(entry.id);633 hiddenEntryIds.add(entry.id);
...@@ -637,6 +635,16 @@ function deriveTurnFolds(input: {...@@ -637,6 +635,16 @@ function deriveTurnFolds(input: {
637 if (hiddenEntryIds.size === 0) {635 if (hiddenEntryIds.size === 0) {
638 continue;636 continue;
639 }637 }
638 // A lone compaction row stays visible on its own; it only folds away as
639 // part of a turn that already folds other work.
640 const hidesNonCompactionWork = group.entries.some(
641 (entry) =>
642 hiddenEntryIds.has(entry.id) &&
643 !(entry.kind === "work" && entry.entry.sourceActivityKind === "context-compaction"),
644 );
645 if (!hidesNonCompactionWork) {
646 continue;
647 }
640648
641 const firstEntry = group.entries[0];649 const firstEntry = group.entries[0];
642 const firstHiddenEntry = group.entries.find((entry) => hiddenEntryIds.has(entry.id));650 const firstHiddenEntry = group.entries.find((entry) => hiddenEntryIds.has(entry.id));