fix(desktop): keep in-flight new chats from vanishing on refresh#37908
Merged
Conversation
Creating several sessions in a row (Ctrl-N, type, send, repeat) and waiting for one to finish made the other still-running chats disappear from the sidebar. Root cause: a new session's first user message isn't flushed to the SessionDB until its turn is persisted, so the row's message_count stays 0 mid-response. `refreshSessions()` lists with min_messages=1 and then hard-replaces $sessions. Because every message.complete triggers a refresh, the moment one session finished, the others (still at message_count 0) were filtered out of the server page and dropped from the list. Fix: merge instead of replace. `mergeWorkingSessions()` preserves any session that is still in $workingSessionIds but absent from the server page, so concurrent new chats stay visible until their own turn persists. Optimistic deletes/archives already remove the row from the previous list, so a removed session can't be resurrected by the merge.
Contributor
🔎 Lint report:
|
The send path created the optimistic sidebar row with a null preview, so a new chat read "Untitled session" until its turn persisted and auto-title ran. With concurrent new chats now preserved across refreshes, several "Untitled session" rows could show at once. Seed the optimistic preview with the user's first message (the branch path already does this) so each in-flight row is labeled immediately. The server's own preview/title supersedes it once the turn persists.
davidgut1982
pushed a commit
to davidgut1982/hermes-agent
that referenced
this pull request
Jun 5, 2026
…ncurrent-session-loss fix(desktop): keep in-flight new chats from vanishing on refresh
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.
Summary
Fixes a bug where rapidly creating several chats (Ctrl-N → type → send, ~3×) and then waiting for one to finish caused the other still-running chats to disappear from the sidebar.
Root cause. A new session's first user message isn't flushed to the
SessionDBuntil its turn is persisted (_persist_sessionat turn end). The gateway lazily creates the DB row onprompt.submitwithmessage_count = 0(_ensure_session_db_row), so a session that's still mid-first-response hasmessage_count = 0in the DB.refreshSessions()lists withmin_messages=1and then hard-replaces$sessions. Everymessage.completetriggers a refresh (completeAssistantMessage→refreshSessions()), so the instant one session finished, the others — still atmessage_count = 0— were filtered out of the server page and dropped from the list.Fix. Merge instead of replace. New
mergeWorkingSessions()preserves any session that is still in$workingSessionIdsbut missing from the server page, so concurrent new chats stay visible until their own turn persists and the server starts returning them. Optimistic deletes/archives already drop the row from the previous list, so a removed session can't be resurrected by the merge. The sidebar re-sorts by recency, so merge order is cosmetic; theMath.max(sessionsTotal, length)footer math already tolerates the extra rows.UX follow-up. The send path created the optimistic row with a null
preview, so an in-flight chat read "Untitled session" until its turn persisted and auto-title ran. Now that concurrent in-flight chats are preserved, several "Untitled session" rows could appear at once. Seed the optimisticpreviewwith the user's first message (the branch path already does this) so each row is labeled immediately; the server's preview/title supersedes it once the turn persists.Changes
store/session.ts— add pure, testablemergeWorkingSessions(previous, incoming, workingIds)helper.app/desktop-controller.tsx—refreshSessions()merges working sessions instead of hard-replacing.session/hooks/use-session-actions.ts+use-prompt-actions.ts— seed optimistic sidebar preview with the first message.store/session.test.ts— 4 unit tests (preserve omitted working sessions, no duplication, never resurrect deleted, no-op when nothing working).Test plan
vitest run src/store/session.test.ts— 6/6 passnpm run type-check— cleaneslinton changed files — 0 new problems (pre-existing warnings/import-sort error already onmain)src/suite — 232 pass; the single failure (pane-shellwidth test) is pre-existing onmainand unrelated (verified viagit stash)