hermes-agent/apps/desktop/src/store
Austin Pickett 016bce1a09
fix(desktop): recover stranded session windows when resume fails (#47655)
* fix(desktop): recover stranded session windows when resume fails

Opening a session in a new window (or any routed resume) could latch the
thread loader on "session" forever — the reported "stays stuck loading,
even after a nap" bug. Two compounding causes:

1. use-session-actions.resumeSession's catch ran the REST transcript
   fallback OUTSIDE its own try. When session.resume rejected AND the
   fallback also threw (the common case on a wedged/unreachable backend),
   the throw skipped setMessages and left activeSessionId null with an
   empty transcript — exactly the state the loader gates on
   (messagesEmpty && !activeSessionId), with no terminal/error state.

2. use-route-resume's self-heal could never re-fire: resumeSession sets
   selectedStoredSessionIdRef synchronously at entry (before failing), so
   stuckOnRoutedSession stays false, and on an already-open idle window
   neither pathnameChanged nor gatewayBecameOpen fire again. The window
   never retried — naps, focus, nothing recovered it.

Fix:
- Wrap the REST fallback in its own try so a fallback failure can't strand
  the loader.
- Add $resumeFailedSessionId: armed on terminal resume failure, cleared at
  the next resume's entry (and left clear on success).
- use-route-resume gains a bounded backoff auto-retry (4 attempts, 1s→8s)
  that re-resumes while the routed session matches the failure flag, with a
  fire-time liveness recheck so a recovered session isn't double-resumed.

Regression tests cover: fallback-wrap arming the flag without throwing,
flag cleared on success, retry fires on backoff, no retry for a
non-routed/recovered session, and the retry cap.

* feat(desktop): show error + manual Retry when resume retries exhaust

When a stranded session window's bounded auto-retry gives up (gateway
resume RPC + REST fallback fail through all MAX_RESUME_RETRIES attempts),
the loader latched forever. Add a $resumeExhaustedSessionId atom armed at
the give-up point so the chat view swaps the perpetual spinner for an
explicit error state + manual Retry button. Retry / reconnect / reselect
clears the latch and resets the auto-retry counter for a fresh cycle; a
route-change away from the stranded session also clears it.

Distinct from $resumeFailedSessionId (armed during the backoff window) so
the error UI only appears once auto-recovery has actually given up, not
mid-retry. Adds i18n strings across en/ja/zh/zh-hant and 3 tests covering
latch-arms-on-exhaustion, stays-clear-while-retries-remain, and
clears-on-route-change.

* fix(desktop): address review on stranded-resume recovery layer

Follow-up to review on #47655 (PR head 253bfc0e3). Four issues on the
recovery layer:

1. (blocking) Arm $resumeFailedSessionId only when the transcript is still
   empty after the REST fallback ($messages.get().length === 0), matching the
   atom's documented contract and the loader's messagesEmpty gate. Previously
   armed on any resume-RPC reject regardless of fallback outcome, so a window
   that recovered its history via REST still auto-retried and, on exhaustion,
   blanked the visible transcript behind the error overlay.

2. Reset the bounded-retry attempt counter on the $resumeExhaustedSessionId
   armed->cleared edge so a manual Retry / reconnect / reselect on the SAME
   stranded session gets a fresh backoff cycle, not a single one-shot attempt
   that immediately re-arms the error. (Keyed on the exhausted latch rather
   than the resumeFailedSessionId null->value transition the review suggested:
   the auto-retry loop itself toggles resumeFailedSessionId every cycle, so
   keying the reset there would defeat the MAX_RESUME_RETRIES cap. Only
   resumeSession clears the exhausted latch, making its clear edge the
   unambiguous manual-retry signal.)

3. Advance retryAttemptRef only when the timer actually dispatches a resume,
   not at schedule time. Prevents unrelated dep changes during the 1s-8s
   backoff window (transient gatewayState flip, non-stable resumeSession) from
   burning attempts and hitting MAX with fewer than 4 real resume attempts.

4. Drop unrelated blank-line-only insertions in store/session.ts and
   use-session-actions.ts to keep the diff tight.

Tests: +3 (RPC-fails-REST-succeeds-no-arm; manual-retry-fresh-cycle;
no-attempts-burned-on-dep-churn). All 19 resume tests + full session-hook
suite (65) pass; tsc --noEmit clean.

---------

Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
2026-06-17 17:33:53 -04:00
..
activity.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
boot.ts feat(desktop): persist i18n language in config 2026-06-05 10:32:26 -07:00
clarify.test.ts fix(desktop): surface background-session clarify prompts instead of hanging 2026-06-03 21:07:33 -05:00
clarify.ts fix(desktop): surface background-session clarify prompts instead of hanging 2026-06-03 21:07:33 -05:00
command-palette.ts feat(desktop): global Cmd+K palette + UI consistency overhaul 2026-06-03 23:45:45 -05:00
compaction.test.ts fix(desktop): polish compaction indicator and preserve scrollback 2026-06-14 02:48:48 -05:00
compaction.ts fix(desktop): polish compaction indicator and preserve scrollback 2026-06-14 02:48:48 -05:00
completion-sound.ts feat(desktop): add curated completion cue for agent turn completion (#42480) 2026-06-14 00:21:40 -05:00
composer-input-history.test.ts feat(desktop): arrow up/down to navigate previous user messages 2026-06-05 20:32:29 -05:00
composer-input-history.ts feat(desktop): integrate arrow history with the message queue 2026-06-05 20:33:53 -05:00
composer-queue.test.ts fix(desktop): stop stranding queued prompts across backend bounces 2026-06-13 00:20:51 -05:00
composer-queue.ts fix(desktop): stop stranding queued prompts across backend bounces 2026-06-13 00:20:51 -05:00
composer-status.test.ts feat(desktop): composer status stack, live subagent windows, editable prompts (#44630) 2026-06-12 08:30:06 -05:00
composer-status.ts feat(desktop): native OS notifications with per-type toggles 2026-06-14 00:31:03 -05:00
composer.test.ts feat(desktop): strict per-thread drafts on decoupled composer 2026-06-11 00:01:06 -05:00
composer.ts feat(desktop): strict per-thread drafts on decoupled composer 2026-06-11 00:01:06 -05:00
cron.ts fix(desktop): cron overlay mutations sync the sidebar instantly 2026-06-06 16:47:46 -05:00
gateway.ts feat(desktop): concurrent multi-profile gateway sockets 2026-06-04 20:44:19 -05:00
haptics.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
keybinds.ts feat(desktop): Mac-style session switcher (^Tab / ^⇧Tab / ^1-9) (#43111) 2026-06-09 20:12:46 -05:00
layout.ts fix(desktop): keep recents sorted unless manually reordered (#45404) 2026-06-13 00:38:10 -05:00
model-presets.test.ts feat(desktop): per-model effort/fast presets in the picker 2026-06-16 00:08:20 -05:00
model-presets.ts feat(desktop): per-model effort/fast presets in the picker 2026-06-16 00:08:20 -05:00
model-visibility.test.ts fix(desktop): declutter date-pinned model snapshots in the picker 2026-06-15 23:53:41 -05:00
model-visibility.ts fix(desktop): declutter date-pinned model snapshots in the picker 2026-06-15 23:53:41 -05:00
native-notifications.test.ts feat(desktop): native OS notifications with per-type toggles 2026-06-14 00:31:03 -05:00
native-notifications.ts feat(desktop): native OS notifications with per-type toggles 2026-06-14 00:31:03 -05:00
notifications.ts feat(desktop): persist i18n language in config 2026-06-05 10:32:26 -07:00
onboarding.test.ts fix(desktop): collect + persist API key for custom OpenAI endpoints (#43896) 2026-06-12 00:03:55 +00:00
onboarding.ts fix(desktop): collect + persist API key for custom OpenAI endpoints (#43896) 2026-06-12 00:03:55 +00:00
panes.test.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
panes.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
preview.test.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
preview.ts fix(desktop): stage dropped files into the remote session workspace 2026-06-09 16:50:08 -05:00
profile.test.ts test(desktop): cover $connection resync on profile switch 2026-06-15 07:11:02 -07:00
profile.ts fix(desktop): sync $connection on profile switch so remote profiles attach images as bytes 2026-06-15 07:11:02 -07:00
prompts.test.ts fix(approval): carry allow_permanent to TUI + desktop approval prompts 2026-06-11 18:23:59 -05:00
prompts.ts chore(approval): tighten allow_permanent comments + DRY the no-always opt set 2026-06-11 18:42:59 -05:00
session-switcher.test.ts feat(desktop): Mac-style session switcher (^Tab / ^⇧Tab / ^1-9) (#43111) 2026-06-09 20:12:46 -05:00
session-switcher.ts feat(desktop): Mac-style session switcher (^Tab / ^⇧Tab / ^1-9) (#43111) 2026-06-09 20:12:46 -05:00
session-sync.ts fix(desktop): sync new sessions across windows 2026-06-15 20:59:57 -05:00
session.test.ts fix(desktop): scope remote workspace defaults 2026-06-11 09:41:35 -07:00
session.ts fix(desktop): recover stranded session windows when resume fails (#47655) 2026-06-17 17:33:53 -04:00
subagents.test.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
subagents.ts feat(desktop): composer status stack, live subagent windows, editable prompts (#44630) 2026-06-12 08:30:06 -05:00
thread-scroll.ts fix(desktop): rebuild thread autoscroll on use-stick-to-bottom 2026-06-13 01:57:30 -05:00
todos.test.ts feat(desktop): composer status stack, live subagent windows, editable prompts (#44630) 2026-06-12 08:30:06 -05:00
todos.ts feat(desktop): composer status stack, live subagent windows, editable prompts (#44630) 2026-06-12 08:30:06 -05:00
tool-diffs.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
tool-dismiss.ts fix(desktop): persist tool-row dismissal across virtualization; keep caret hittable 2026-06-12 17:34:48 -05:00
tool-view.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
translucency.ts feat(desktop): window translucency slider in Appearance settings (#45086) 2026-06-12 12:02:38 -05:00
updates.test.ts Merge pull request #47913 from xxxigm/fix/desktop-backend-skew-toast-nag 2026-06-17 10:04:34 -05:00
updates.ts Merge pull request #47913 from xxxigm/fix/desktop-backend-skew-toast-nag 2026-06-17 10:04:34 -05:00
voice-playback.ts Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
windows.test.ts feat(desktop): open new sessions in compact windows 2026-06-15 20:59:57 -05:00
windows.ts feat(desktop): open new sessions in compact windows 2026-06-15 20:59:57 -05:00