fix(desktop): fall back to the engine's most recent session when the boot-restored chat is dead - #60607
Closed
Vencounsel wants to merge 1 commit into
Closed
Conversation
…boot-restored chat is dead Cold start navigates to the localStorage-pinned hermes.desktop.lastSessionId without validating it against the backend. When that id is stale (the session was deleted or rotated while the app was closed), the boot strands on the resume-error screen; the engine's actual most recent session is never considered. Keep the optimistic restore — the happy path costs no round trip and no new-chat flash — and use the existing resume-exhausted latch as the staleness verdict: it only arms while the gateway is open, after the bounded resume retries gave up, so it distinguishes a dead pin from a down backend (no extra validation RPC, no false positives from the paginated session list). When the latch fires for the boot-restored session while it is still the routed one, replace-navigate to the engine's most recent session — preferring an is_active session, then newest last_active, skipping archived rows and the dead session's own lineage — or to the new-chat route when nothing qualifies. Sessions the user opened deliberately are untouched: those keep the explicit error + Retry UI.
This was referenced Jul 26, 2026
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
On cold start the desktop app navigates straight to the localStorage-pinned
hermes.desktop.lastSessionIdwithout validating it against the backend. When that pin is stale (the session was deleted or rotated while the app was closed), the boot strands on the resume-error screen for a chat the user never chose; the engine's actual most recent session is never considered.This PR keeps the optimistic restore — the happy path still costs no round trip and no new-chat flash — and adds a fallback using the existing resume-exhausted latch as the staleness verdict: the latch only arms while the gateway is open, after the bounded resume retries gave up (
use-route-resume.ts), so it cleanly distinguishes a dead pin from a down backend. No extra validation RPC, and no false positives from checking membership in the paginated session list.When the latch fires for the boot-restored session while it's still the routed one, the controller replace-navigates to the engine's most recent session — preferring an
is_activesession (a runtime is live on it right now), then newestlast_active, skipping archived rows and the dead session's own lineage — or to the new-chat route when nothing qualifies. The dead pin is still cleared, as before.Deliberately narrow: sessions the user opened by hand keep the explicit error + Retry UI — the fallback fires only for the automatic boot restore, so it never yanks a session the user explicitly chose out from under them.
Related Issue
Fixes #60541
Type of Change
Changes Made
apps/desktop/src/app/desktop-controller.tsx: track which session id the cold-boot restore navigated to (bootRestoredSessionIdRef); on resume-exhaustion of exactly that session while routed to it, replace-navigate topickMostRecentSessionId($sessions, deadId)or the new-chat route.apps/desktop/src/app/desktop-controller-utils.ts: new pure helperpickMostRecentSessionId()(active-first, thenlast_active; skips archived and the dead lineage — id matching mirrorssessionMatchesStoredId()).apps/desktop/src/app/desktop-controller-utils.test.ts: six cases — empty list, newest-by-last_active, active-over-newer-inactive, archived skipped, exclusion by id and lineage root, all-excluded → null.How to Test
cd apps/desktop && npx vitest run --environment jsdom src/app/desktop-controller-utils.test.ts— 10 pass (4 existing + 6 new; the new ones fail on currentmain).hermes sessionstooling) or another surface, relaunch.main: boot lands on the resume-error screen for the deleted session.npm run typecheckandnpx eslinton the touched files pass.Checklist
Code
fix(desktop):)pytest tests/ -q: no Python changes in this diff; full-suite comparison against a pristinemaincheckout on the same machine shows no new failures (environment-dependent failures reproduce identically on base)vitest run --environment jsdom): no new failures vs. a pristine checkout of the same base commit (24 pre-existing environment-dependent failures on both, identical set)Documentation & Housekeeping
cli-config.yaml.example— N/ACONTRIBUTING.md/AGENTS.md— N/A