diff --git a/web/src/chat/reducer.ts b/web/src/chat/reducer.ts index 79df2c79..798499c6 100644 --- a/web/src/chat/reducer.ts +++ b/web/src/chat/reducer.ts @@ -48,11 +48,24 @@ export function reduceChatBlocks( let hasReadyEvent = rootResult.hasReadyEvent // Only create permission-only tool cards when there is no tool call/result in the transcript. + // Also skip if the permission is older than the oldest message in the current view, + // to avoid mixing old tool cards with newer messages when paginating. + const oldestMessageTime = normalized.length > 0 + ? Math.min(...normalized.map(m => m.createdAt)) + : null + for (const [id, entry] of permissionsById) { if (toolIdsInMessages.has(id)) continue if (rootResult.toolBlocksById.has(id)) continue const createdAt = entry.permission.createdAt ?? Date.now() + + // Skip permissions that are older than the oldest message in the current view. + // These will be shown when the user loads older messages. + if (oldestMessageTime !== null && createdAt < oldestMessageTime) { + continue + } + const block = ensureToolBlock(rootResult.blocks, rootResult.toolBlocksById, id, { createdAt, localId: null, diff --git a/web/src/components/SessionChat.tsx b/web/src/components/SessionChat.tsx index c0bd9ac7..3990cc29 100644 --- a/web/src/components/SessionChat.tsx +++ b/web/src/components/SessionChat.tsx @@ -141,12 +141,22 @@ export function SessionChat(props: { voice.toggleMic() }, [voice]) + // Track session id to clear caches when it changes + const prevSessionIdRef = useRef(null) + useEffect(() => { normalizedCacheRef.current.clear() blocksByIdRef.current.clear() }, [props.session.id]) const normalizedMessages: NormalizedMessage[] = useMemo(() => { + // Clear caches immediately when session changes (before useEffect runs) + if (prevSessionIdRef.current !== null && prevSessionIdRef.current !== props.session.id) { + normalizedCacheRef.current.clear() + blocksByIdRef.current.clear() + } + prevSessionIdRef.current = props.session.id + const cache = normalizedCacheRef.current const normalized: NormalizedMessage[] = [] const seen = new Set() diff --git a/web/src/lib/messages.ts b/web/src/lib/messages.ts index d832624b..cd51888e 100644 --- a/web/src/lib/messages.ts +++ b/web/src/lib/messages.ts @@ -23,9 +23,11 @@ function isOptimisticMessage(msg: DecryptedMessage): boolean { function compareMessages(a: DecryptedMessage, b: DecryptedMessage): number { const aSeq = typeof a.seq === 'number' ? a.seq : null const bSeq = typeof b.seq === 'number' ? b.seq : null + if (aSeq !== null && bSeq !== null && aSeq !== bSeq) { return aSeq - bSeq } + if (a.createdAt !== b.createdAt) { return a.createdAt - b.createdAt }