fix(dashboard): force fresh PTY spawn when resume target changes - #61313
fix(dashboard): force fresh PTY spawn when resume target changes#61313sterman wants to merge 1 commit into
Conversation
When the user picks a different session from the ChatSessionList sidebar or the Sessions page's play button, the existing keep-alive PTY (same ?attach= token) was reattached without updating HERMES_TUI_RESUME in the child process environment. The TUI continued with the old session (or none) instead of loading the newly selected session's history. Root cause: PtySessionRegistry.attach_or_spawn() returns the existing live PTY when the attach token matches, so _resolve_chat_argv() is never called and the env var stays stale. Fix: track the previous ?resume= query param with a ref, and when it changes, set forceFresh = true so the new WebSocket connection sends fresh=1, forcing the backend to spawn a new PTY with the correct HERMES_TUI_RESUME. Fixes NousResearch#19085
Related: #60745 (fixes the same wrong-session-on-resume symptom by scoping the attach token by session on the backend in This PR fixes it on the frontend — detecting a |
|
We are hitting exactly this in a production deployment and can confirm the root-cause analysis. Environment: Hermes v0.18.2 (2026.7.7.2), Ubuntu 24.04, dashboard bound to 127.0.0.1 behind an SSH tunnel, Node 20.18.1, gateway + dashboard as systemd user services. Symptom (matches this PR's summary 1:1): clicking any session in the Chat sidebar — or the Sessions page play button — never loads the selected session's history. The embedded TUI always shows a fresh/empty session (banner screen), while the status bar correctly reports the live PTY count. Switching targets repeatedly makes no difference; the keep-alive PTY visibly re-attaches to the previous spawn instead of respawning for the new resume target. Session transcripts are fine (the Sessions page renders them), so this is purely the attach/spawn path. For us this makes "Resume in Chat" unusable for reviewing an agent's past runs, which is a daily workflow in our QA setup (the dashboard is our team's read-only window into an autonomous testing agent). We currently work around it via the Sessions page transcript view. Happy to test a build of this branch against our deployment and report back if that helps move it along. Thanks for the thorough root-cause writeup — the fix approach (forcing a fresh PTY spawn when the resume target changes) looks right to us. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the keep-alive reattach path. The current-main defect is real: web/src/pages/ChatPage.tsx:799-805 sends a changed resume alongside the stable attach token, while hermes_cli/web_server.py:15450-15454 looks up the live PTY solely by that token.
Problems
- The proposed
fresh=1route cannot resume the chosen session.hermes_cli/web_server.py:15402-15405deliberately setsresume = Nonefor fresh requests, andHERMES_TUI_RESUMEis only set when resume is non-empty athermes_cli/web_server.py:14526-14536. The resulting child is blank, not resumed. - No regression test covers changed resume targets.
tests/test_pty_keepalive_ws.py:7-52currently covers only same-token reuse.
Suggested changes
- Keep "spawn a different PTY" separate from the existing
freshmeaning of "start blank"—for example, scope the backend attach identity by rawresumeandprofileas in related #60745, or rotate the attach token without sendingfresh=1. - Add a two-target keep-alive regression test that verifies the second spawn receives its selected resume target.
Automated hermes-sweeper review.
| // the newly selected session's history. | ||
| const resumeChanged = resumeParam !== prevResumeParamRef.current; | ||
| if (resumeChanged && !forceFresh) { | ||
| forceFresh = true; |
There was a problem hiding this comment.
fresh=1 is not a generic respawn flag: current pty_ws clears resume when fresh is set (hermes_cli/web_server.py:15402-15405). This will spawn a blank TUI rather than one with HERMES_TUI_RESUME set to the newly selected session. Please separate attach-token rotation/new-spawn behavior from the established blank-session fresh semantic.
Summary
When the user picks a different session from the ChatSessionList sidebar or the Sessions page's play button, the embedded TUI was not loading the newly selected session's history. Instead it continued showing the previous session (or a fresh/empty one), making the "Resume in Chat" feature unusable for switching between conversations.
Root Cause
The keep-alive PTY mechanism (introduced in v0.18.0) reattaches to the existing live PTY process when the same
?attach=token is used. In this case_resolve_chat_argv()is never called, soHERMES_TUI_RESUMEstays at its original value and the TUI never receives the new session id.Specifically,
PtySessionRegistry.attach_or_spawn()atweb_server.pyreturns the existing session when the attach token matches, bypassing the spawn path that would set the correct env var.Fix
Track the previous
?resume=query param value with auseRef. When it changes between effect runs (user navigated to a different session), setforceFresh = trueso the new WebSocket connection sendsfresh=1, forcing the backend to spawn a fresh PTY with the correctHERMES_TUI_RESUME.Before: URL changed to
/chat?resume=Xbut the keep-alive PTY was reattached with stale env vars, loading the wrong session.After: Resume target change triggers
fresh=1→ new PTY spawned withHERMES_TUI_RESUME=X→session.resumeRPC loads the correct history.Related
Fixes #19085 — dashboard Sessions → Chat resume does not reliably load the selected historical session.