From 14ac208cd0b3caf577be27b872bb7be6606604f7 Mon Sep 17 00:00:00 2001 From: Ammar Date: Mon, 13 Oct 2025 14:49:01 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Fix=20workspace=20recency=20afte?= =?UTF-8?q?r=20compaction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When chat history is compacted, user messages are replaced with a summary (assistant message with compacted=true). The recency calculation only checked for user messages, causing compacted workspaces to sink to the bottom. Changes: - Rename WorkspaceState.lastUserMessageAt → recencyTimestamp (clearer semantics) - Add fallback: if no user messages, use most recent compacted message timestamp - Keeps Date.now() for new compaction summaries (workspace jumps to top, correct UX) This ensures workspaces with only compacted history maintain their position in the sidebar, while still preferring user messages during active streams. Generated with `cmux` --- src/hooks/useWorkspaceAggregators.ts | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/hooks/useWorkspaceAggregators.ts b/src/hooks/useWorkspaceAggregators.ts index 52fa36fac..fe06c7948 100644 --- a/src/hooks/useWorkspaceAggregators.ts +++ b/src/hooks/useWorkspaceAggregators.ts @@ -31,7 +31,7 @@ export interface WorkspaceState { loading: boolean; cmuxMessages: CmuxMessage[]; currentModel: string; - lastUserMessageAt: number | null; // Timestamp of most recent user message (null if no user messages) + recencyTimestamp: number | null; // Timestamp for sorting: most recent user message, or compacted message if no user messages } /** @@ -69,14 +69,27 @@ export function useWorkspaceAggregators(workspaceMetadata: Map m.role === "user" && m.metadata?.timestamp); - const lastUserMessageAt = lastUserMsg?.metadata?.timestamp ?? null; + + let recencyTimestamp: number | null = null; + if (lastUserMsg?.metadata?.timestamp) { + recencyTimestamp = lastUserMsg.metadata.timestamp; + } else { + // No user messages - check for compacted messages + const lastCompactedMsg = [...messages] + .reverse() + .find((m) => m.metadata?.compacted === true && m.metadata?.timestamp); + if (lastCompactedMsg?.metadata?.timestamp) { + recencyTimestamp = lastCompactedMsg.metadata.timestamp; + } + } return { messages: aggregator.getDisplayedMessages(), @@ -85,7 +98,7 @@ export function useWorkspaceAggregators(workspaceMetadata: Map { const timestamps: Record = {}; for (const [id, state] of workspaceStates) { - if (state.lastUserMessageAt !== null) { - timestamps[id] = state.lastUserMessageAt; + if (state.recencyTimestamp !== null) { + timestamps[id] = state.recencyTimestamp; } } return timestamps;