diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index 6fc96c59..4451a41e 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -522,10 +522,17 @@ export const ChatContainer: React.FC = ({ autoOpenDraft = tr // History metadata — use sync's hasMore/isLoading const historyMeta = React.useMemo(() => { if (!currentSessionId) return null; - const prefetchHasMore = Boolean(sessionPrefetchInfo?.cursor) && sessionPrefetchInfo?.complete !== true; + // Sync's meta is authoritative once a fetch has confirmed the history + // is fully loaded — a stale prefetch-cache entry (cursor recorded at + // the initial page) must not keep the "load older" affordance alive + // after the user has already reached the top. + const syncComplete = sync.isComplete(currentSessionId); + const prefetchHasMore = !syncComplete + && Boolean(sessionPrefetchInfo?.cursor) + && sessionPrefetchInfo?.complete !== true; return { limit: sessionMessages.length, - complete: !(sync.hasMore(currentSessionId) || prefetchHasMore), + complete: syncComplete || !(sync.hasMore(currentSessionId) || prefetchHasMore), loading: sync.isLoading(currentSessionId), }; }, [currentSessionId, sessionMessages.length, sessionPrefetchInfo, sync]); diff --git a/packages/ui/src/sync/use-sync.ts b/packages/ui/src/sync/use-sync.ts index 4f741d68..66d3f788 100644 --- a/packages/ui/src/sync/use-sync.ts +++ b/packages/ui/src/sync/use-sync.ts @@ -555,6 +555,14 @@ export function useSync() { [getMetaFor], ) + // True only when a fetch has positively confirmed the history is fully + // loaded (no next cursor). Distinct from !hasMore(), which is also true for + // sessions whose meta simply hasn't been populated yet. + const isComplete = useCallback( + (sessionID: string) => getMetaFor(sessionID).complete, + [getMetaFor], + ) + // Optimistic add (for prompt submission) const optimisticAdd = useCallback( (input: { sessionID: string; directory?: string | null; message: Message; parts: Part[] }) => { @@ -610,11 +618,12 @@ export function useSync() { loadMore, hasMore, isLoading, + isComplete, optimistic: { add: optimisticAdd, remove: optimisticRemove, }, }), - [syncSession, loadMore, hasMore, isLoading, optimisticAdd, optimisticRemove], + [syncSession, loadMore, hasMore, isLoading, isComplete, optimisticAdd, optimisticRemove], ) }