fix(desktop): Branch in new chat drops the question and loses the branched session on restart - #71960
Merged
austinpickett merged 2 commits intoJul 27, 2026
Conversation
…#issue) - session.branch on the backend now accepts a count param to truncate the parent history to the clicked message, instead of always forking the entire transcript. Also returns stored_session_id/messages/info so the frontend has parity with session.create's response shape. - branchCurrentSession (open live chat) now slices history from 0 instead of from the clicked message index, so the question preceding an assistant reply is no longer dropped when branching. - forkBranch now calls session.branch (not session.create) when branching an open live chat, since session.create only persists a DB row lazily on first prompt - a branched chat that nobody types into never got saved, and vanished as 'session not found' on the next app restart. branchStoredSession (branching from the sidebar, no live runtime) keeps using session.create as before.
- backend: assert session.branch with a count param only persists the first N messages of the live history to the new session. - frontend: BranchHarness now exposes branchCurrentSession; assert branching an open chat from a middle message calls session.branch with the parent session id and the correct trimmed count, instead of session.create.
austinpickett
approved these changes
Jul 27, 2026
austinpickett
left a comment
Collaborator
There was a problem hiding this comment.
Code Review Summary — Hermes Agent
Verdict: Approve. Clean root-cause fix for both bugs, no conflicts, tests pass.
What I verified
- Root cause on both bugs is correct.
- Dropped question:
branchCurrentSessionslicedmessages.slice(at, at+1)(only the clicked reply). Changingstartto0keeps everything up to and including the clicked message, so the preceding question survives. Correct. - Vanishes on restart:
session.createonly persists a DB row lazily on first prompt, so a read-only branch never got saved. Switching the open-live-chat path tosession.branch(which persists row + history synchronously) is the right fix, and matches how the TUI/branchalready persists.
- Dropped question:
- Backend
counthandling is safe and backward-compatible.if isinstance(count, int) and count > 0: history = history[:count]— omittingcountor passing 0/negative preserves the old full-transcript fork, so the existing TUI/branchcaller is unaffected. - Response parity is exact.
session.branchnow returnssession_id,stored_session_id,message_count,messages(via the shared_history_to_messages), andinfo(via the shared_session_info, fed the freshly-built agent + branched session) — the same shapesession.createreturns, so the frontend's singleSessionCreateResponseconsumer path works for both. - Both
forkBranchcall sites updated correctly.branchCurrentSessionpassesactiveSessionIdRef.current(live →session.branch);branchStoredSessionpassesnull(sidebar → unchangedsession.create). No other callers. - Tests are non-vacuous. Reverting the
history[:count]slice makestest_session_branch_with_count_truncates_historyfail on thelen(append_calls) == 2assertion — confirmed it actually guards the fix. Backend branch tests 4/4 pass; frontend suite 37/37 pass; app + electron typecheck clean.
Notes (non-blocking)
branched.messagesin the response is not consumed byforkBranch(it builds state from the localbranchMessages), somessages/message_countare there purely forsession.createparity. Harmless and intentional.
Verified in an isolated worktree off the PR head: tests/tui_gateway/test_protocol.py -k branch, use-session-actions.test.tsx, tsc -p . + tsc -p tsconfig.electron.json.
austinpickett
approved these changes
Jul 27, 2026
austinpickett
left a comment
Collaborator
There was a problem hiding this comment.
Code Review Summary — Hermes Agent
Verdict: Approve. Clean root-cause fix for both bugs, no conflicts, tests pass.
What I verified
- Root cause on both bugs is correct.
- Dropped question:
branchCurrentSessionslicedmessages.slice(at, at+1)(only the clicked reply). Changingstartto0keeps everything up to and including the clicked message, so the preceding question survives. Correct. - Vanishes on restart:
session.createonly persists a DB row lazily on first prompt, so a read-only branch never got saved. Switching the open-live-chat path tosession.branch(which persists row + history synchronously) is the right fix, and matches how the TUI/branchalready persists.
- Dropped question:
- Backend
counthandling is safe and backward-compatible.if isinstance(count, int) and count > 0: history = history[:count]— omittingcountor passing 0/negative preserves the old full-transcript fork, so the existing TUI/branchcaller is unaffected. - Response parity is exact.
session.branchnow returnssession_id,stored_session_id,message_count,messages(via the shared_history_to_messages), andinfo(via the shared_session_info, fed the freshly-built agent + branched session) — the same shapesession.createreturns, so the frontend's singleSessionCreateResponseconsumer path works for both. - Both
forkBranchcall sites updated correctly.branchCurrentSessionpassesactiveSessionIdRef.current(live →session.branch);branchStoredSessionpassesnull(sidebar → unchangedsession.create). No other callers. - Tests are non-vacuous. Reverting the
history[:count]slice makestest_session_branch_with_count_truncates_historyfail on thelen(append_calls) == 2assertion — confirmed it actually guards the fix. Backend branch tests 4/4 pass; frontend suite 37/37 pass; app + electron typecheck clean.
Notes (non-blocking)
branched.messagesin the response is not consumed byforkBranch(it builds state from the localbranchMessages), somessages/message_countare there purely forsession.createparity. Harmless and intentional.
Verified in an isolated worktree off the PR head: tests/tui_gateway/test_protocol.py -k branch, use-session-actions.test.tsx, tsc -p . + tsc -p tsconfig.electron.json.
This was referenced Jul 28, 2026
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…nched session on restart (NousResearch#71960) * fix: Branch in new chat loses the question and the branched session (#issue) - session.branch on the backend now accepts a count param to truncate the parent history to the clicked message, instead of always forking the entire transcript. Also returns stored_session_id/messages/info so the frontend has parity with session.create's response shape. - branchCurrentSession (open live chat) now slices history from 0 instead of from the clicked message index, so the question preceding an assistant reply is no longer dropped when branching. - forkBranch now calls session.branch (not session.create) when branching an open live chat, since session.create only persists a DB row lazily on first prompt - a branched chat that nobody types into never got saved, and vanished as 'session not found' on the next app restart. branchStoredSession (branching from the sidebar, no live runtime) keeps using session.create as before. * test: cover session.branch count truncation and open-chat branching - backend: assert session.branch with a count param only persists the first N messages of the live history to the new session. - frontend: BranchHarness now exposes branchCurrentSession; assert branching an open chat from a middle message calls session.branch with the parent session id and the correct trimmed count, instead of session.create.
33hodl
pushed a commit
to 33hodl/hermes-agent
that referenced
this pull request
Aug 12, 2026
…nched session on restart (NousResearch#71960) * fix: Branch in new chat loses the question and the branched session (#issue) - session.branch on the backend now accepts a count param to truncate the parent history to the clicked message, instead of always forking the entire transcript. Also returns stored_session_id/messages/info so the frontend has parity with session.create's response shape. - branchCurrentSession (open live chat) now slices history from 0 instead of from the clicked message index, so the question preceding an assistant reply is no longer dropped when branching. - forkBranch now calls session.branch (not session.create) when branching an open live chat, since session.create only persists a DB row lazily on first prompt - a branched chat that nobody types into never got saved, and vanished as 'session not found' on the next app restart. branchStoredSession (branching from the sidebar, no live runtime) keeps using session.create as before. * test: cover session.branch count truncation and open-chat branching - backend: assert session.branch with a count param only persists the first N messages of the live history to the new session. - frontend: BranchHarness now exposes branchCurrentSession; assert branching an open chat from a middle message calls session.branch with the parent session id and the correct trimmed count, instead of session.create.
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?
Fixes two related bugs in the Branch in new chat action on an open, live chat:
Both bugs live in the same code path (
branchCurrentSession/forkBranchfor an open chat):messages.slice(at, at + 1)— exactly the clicked message — discarding everything before it, including the question it answered.session.create, which only persists a DB row lazily, on the first prompt sent in that session. A branched chat is normally just read, not typed into, so the row never got created — hence "session not found" on the next app restart. This was confirmed to not be a timing/race issue (waited 30-40s before restart with the same result).The fix switches open-chat branching to the existing, purpose-built RPC
session.branch, which persists the session and its history synchronously.session.branchon the backend gained acountparameter so it can fork history up to a specific message instead of always forking the entire transcript, and its response was extended with the fields the frontend expects (stored_session_id,messages,info).Branching a stored session from the sidebar (
branchStoredSession, no live runtime to target) is unchanged and still usessession.create.Related Issue
Fixes #
Type of Change
Changes Made
tui_gateway/server.py:session.branchhandler now accepts an optionalcountparam to truncate the parent's live history to the first N messages instead of always forking the full transcript. Response now also includesstored_session_id,message_count,messages, andinfofor parity withsession.create.apps/desktop/src/app/session/hooks/use-session-actions/index.ts:branchCurrentSession: history slice now always starts at0instead of at the clicked message's index, so the question before the clicked reply is preserved.forkBranch: added asourceSessionIdparameter; when present (branching an open live chat) it callssession.branchwith{ session_id, count }instead ofsession.create.branchCurrentSessionnow passesactiveSessionIdRef.currentassourceSessionId.branchStoredSessionnow passesnullassourceSessionId, preserving the oldsession.createpath for sidebar branching.tests/tui_gateway/test_protocol.py: addedtest_session_branch_with_count_truncates_history, assertingcounttruncates what gets persisted.apps/desktop/src/app/session/hooks/use-session-actions.test.tsx: extendedBranchHarnessto exposebranchCurrentSession; added a test asserting that branching an open chat from a middle message callssession.branchwith the correctsession_id/count, notsession.create.How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs