fix(desktop): preserve the active chat across a sleep/wake gateway reconnect

After a Windows/macOS sleep, the desktop's WS connections drop and the
auto-reconnect reopens the gateway. The route-resume hook treated every
closed->open transition on a new-chat route as 'the user navigated to
/new' and called startFreshSessionDraft, discarding the active runtime
session and parking the previous chat in the sidebar as a detached
session. Keep the active chat when the gateway reopens mid-chat: no
navigation happened, only the transport came back.

Co-authored-by: 0disoft <rodisoft1@gmail.com>
This commit is contained in:
Hermes Agent
2026-09-25 11:39:38 -05:00
committed by brooklyn!
parent b5d583c4ac
commit 28545254dd
2 changed files with 61 additions and 0 deletions

View File

@@ -429,6 +429,58 @@ describe('useRouteResume', () => {
expect(resumeSession).toHaveBeenCalledWith('session-2', true)
})
it('preserves an active new-chat session when the gateway reconnects (#53374)', () => {
const resumeSession = vi.fn(async () => undefined)
const startFreshSessionDraft = vi.fn()
const activeSessionIdRef: MutableRefObject<null | string> = { current: 'runtime-1' }
const creatingSessionRef = { current: false }
const runtimeIdByStoredSessionIdRef = { current: new Map<string, string>() }
const selectedStoredSessionIdRef: MutableRefObject<null | string> = { current: null }
// First render: gateway CLOSED on the new-chat route with an active runtime
// session (the machine just woke; the WS dropped mid-chat).
const { rerender } = render(
<RouteResumeHarness
activeSessionId="runtime-1"
activeSessionIdRef={activeSessionIdRef}
creatingSessionRef={creatingSessionRef}
currentView="chat"
freshDraftReady={false}
gatewayState="closed"
locationPathname="/"
resumeSession={resumeSession}
routedSessionId={null}
runtimeIdByStoredSessionIdRef={runtimeIdByStoredSessionIdRef}
selectedStoredSessionId={null}
selectedStoredSessionIdRef={selectedStoredSessionIdRef}
startFreshSessionDraft={startFreshSessionDraft}
/>
)
// Sleep/wake: the gateway reopens, nothing navigated. The active chat must
// survive — no forced fresh draft, no new session.
rerender(
<RouteResumeHarness
activeSessionId="runtime-1"
activeSessionIdRef={activeSessionIdRef}
creatingSessionRef={creatingSessionRef}
currentView="chat"
freshDraftReady={false}
gatewayState="open"
locationPathname="/"
resumeSession={resumeSession}
routedSessionId={null}
runtimeIdByStoredSessionIdRef={runtimeIdByStoredSessionIdRef}
selectedStoredSessionId={null}
selectedStoredSessionIdRef={selectedStoredSessionIdRef}
startFreshSessionDraft={startFreshSessionDraft}
/>
)
expect(startFreshSessionDraft).not.toHaveBeenCalled()
expect(resumeSession).not.toHaveBeenCalled()
})
it('does not re-resume the old session when the new profile gateway opens before /new commits (#68594)', () => {
const resumeSession = vi.fn(async () => undefined)
const startFreshSessionDraft = vi.fn()

View File

@@ -193,6 +193,15 @@ export function useRouteResume({
return
}
// A sleep/wake WS reconnect can reopen on a new-chat route while the active
// runtime session is still the user's current chat. The gateway re-opened;
// nothing navigated. Forcing a fresh draft here is what turned every
// Windows/macOS sleep/wake cycle into a brand-new session and parked the
// previous chat in the sidebar (#53374). Preserve the active chat instead.
if (isNewChatRoute(locationPathname) && gatewayBecameOpen && activeSessionId && !freshDraftReady) {
return
}
if (
isNewChatRoute(locationPathname) &&
!creatingSessionRef.current &&