diff --git a/apps/desktop/src/app/chat/index.tsx b/apps/desktop/src/app/chat/index.tsx index baed10c47e..276271d64d 100644 --- a/apps/desktop/src/app/chat/index.tsx +++ b/apps/desktop/src/app/chat/index.tsx @@ -515,11 +515,10 @@ const ChatViewContent = memo(function ChatViewContent({ // spinning forever. Gated on the route matching so a stale latch from another // session can't blank the current one. const resumeExhausted = isPrimary && isRoutedSessionView && resumeExhaustedSessionId === routedSessionId + const routedHasHistory = Boolean( routedSessionId && - sessions.some( - session => sessionMatchesStoredId(session, routedSessionId) && sessionShouldHaveTranscript(session) - ) + sessions.some(session => sessionMatchesStoredId(session, routedSessionId) && sessionShouldHaveTranscript(session)) ) const loadingSession = routedSessionIsLoading({ diff --git a/apps/desktop/src/app/chat/thread-loading.ts b/apps/desktop/src/app/chat/thread-loading.ts index c9313966c6..6dbfc0a29b 100644 --- a/apps/desktop/src/app/chat/thread-loading.ts +++ b/apps/desktop/src/app/chat/thread-loading.ts @@ -63,4 +63,3 @@ export function routedSessionIsLoading({ // runtime is bound but messages are still suppressed. return !activeSessionId || knownHistory } - diff --git a/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx b/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx index 45dba0dc20..0e1fd625ab 100644 --- a/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx +++ b/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx @@ -3507,13 +3507,16 @@ describe('resumeSession warm-cache mapping integrity', () => { const runtimeIdByStoredSessionIdRef = { current: new Map([['stored-1', 'runtime-warm']]) } satisfies MutableRefObject> + const sessionStateByRuntimeIdRef = { current: new Map([['runtime-warm', cachedState]]) } satisfies MutableRefObject> + const persistedAuthority = deferred<{ messages: Array<{ content: string; role: 'assistant' | 'user'; timestamp: number }> session_id: string }>() + const publications: Array<{ older: boolean; latest: boolean }> = [] setSessions([storedSession({ message_count: 4 })]) diff --git a/apps/desktop/src/app/session/hooks/use-session-actions/index.ts b/apps/desktop/src/app/session/hooks/use-session-actions/index.ts index 5e7f506530..0f1ef5a301 100644 --- a/apps/desktop/src/app/session/hooks/use-session-actions/index.ts +++ b/apps/desktop/src/app/session/hooks/use-session-actions/index.ts @@ -172,10 +172,10 @@ interface SessionActionsOptions { busyRef: MutableRefObject creatingSessionRef: MutableRefObject ensureSessionState: (sessionId: string, storedSessionId?: string | null) => ClientSessionState - getRouteToken: () => string - getRoutedStoredSessionId: () => null | string - holdSessionTranscriptView?: (runtimeId: string) => () => void - navigate: NavigateFunction + getRouteToken: () => string + getRoutedStoredSessionId: () => null | string + holdSessionTranscriptView?: (runtimeId: string) => () => void + navigate: NavigateFunction onFreshDraftRouteIntent?: () => void requestGateway: (method: string, params?: Record) => Promise resetViewSync: () => void @@ -1039,6 +1039,7 @@ export function useSessionActions({ // transport, so a turn finishing between snapshot and reattach // cannot leave a stale partial on screen. const shouldRefreshPersistedTranscript = !isWatchWindow() + const suppressUnprovenWarmTranscript = !resumedSameSelectedSession && shouldRefreshPersistedTranscript && !hasValidProvenance @@ -1256,6 +1257,7 @@ export function useSessionActions({ (persisted.messages.length || !activatedMessages.length) ) { acceptedPersistedDisplayTranscript = Boolean(expectedProvenance) + // The REST hydration is a newest-tail page; graft it onto any // older pages the previous view already backfilled so // re-activating a scrolled-back session keeps its history.