From d0b9a4ef41dcc22b1b4c91b5561218eaac22d593 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 9 Jun 2026 20:06:48 -0700 Subject: [PATCH] [eric] frontend: warm-store chats connect the live stream immediately and hydrate in background, reopen no longer waits a round trip --- .../src/app/pages/AgentChat/AgentChat.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index da6867ba..18710845 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -39,6 +39,7 @@ import { clearSessionMessages, clearMcpSuggestions, } from '@/shared/state/agentsSlice'; +import { store } from '@/shared/state/store'; import { fetchModes } from '@/shared/state/modesSlice'; import { createSessionWs } from '@/shared/ws/WebSocketManager'; import StreamingBubble from './bubbles/StreamingBubble'; @@ -232,11 +233,20 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose // out again. Awaiting fetchSession before connect makes the slice // authoritative before any replay event lands. (async () => { - try { - await dispatch(fetchSession(id)); - } catch { - // Even if the REST hydrate fails, still connect , the WS resume - // protocol can hydrate from buffered events as a fallback. + // The await exists so the slice isn't EMPTY at replay time. A warm store + // (remount after a hop) already satisfies that, so connect immediately and + // let the fetch reconcile in the background; awaiting serialized a slow + // round trip in front of the live stream on every reopen. + const warm = !!store.getState().agents.sessions[id]?.messages?.length; + if (warm) { + dispatch(fetchSession(id)); + } else { + try { + await dispatch(fetchSession(id)); + } catch { + // Even if the REST hydrate fails, still connect , the WS resume + // protocol can hydrate from buffered events as a fallback. + } } if (cancelled) return; ws = createSessionWs(id);