From a76aa6198cf72dae995e7b2a38ee61be8f272eb9 Mon Sep 17 00:00:00 2001 From: srojk34 <286497132+srojk34@users.noreply.github.com> Date: Fri, 19 Jun 2026 05:19:04 +0300 Subject: [PATCH] fix(cli): flush un-persisted messages before /resume and /branch end the old session compress_context() and /new already flush un-persisted messages before calling end_session() (fixed in #47202), but /resume and /branch still call end_session() directly. When a turn is interrupted mid-flight and the user immediately runs /resume or /branch, messages generated during that turn have not yet been written to state.db and are silently lost on session rotation. Add the same best-effort _flush_messages_to_session_db() call before end_session() in both _handle_resume_command and _handle_branch_command, mirroring the pattern established in cli.py:new_session(). Regression tests verify the flush is called when an agent is present. --- hermes_cli/cli_commands_mixin.py | 17 ++++++++++++++++ tests/cli/test_branch_command.py | 18 +++++++++++++++++ tests/cli/test_cli_resume_command.py | 30 ++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/hermes_cli/cli_commands_mixin.py b/hermes_cli/cli_commands_mixin.py index be5fb8e92..70f82f2ea 100644 --- a/hermes_cli/cli_commands_mixin.py +++ b/hermes_cli/cli_commands_mixin.py @@ -712,6 +712,14 @@ class CLICommandsMixin: return old_session_id = self.session_id + # Flush un-persisted messages before ending the old session (#47202). + if self.agent: + try: + self.agent._flush_messages_to_session_db( + self.conversation_history + ) + except Exception: + pass # End current session try: self._session_db.end_session(self.session_id, "resumed_other") @@ -851,6 +859,15 @@ class CLICommandsMixin: # Save the current session's state before branching parent_session_id = self.session_id + # Flush un-persisted messages before ending the old session (#47202). + if self.agent: + try: + self.agent._flush_messages_to_session_db( + self.conversation_history + ) + except Exception: + pass + # End the old session try: self._session_db.end_session(self.session_id, "branched") diff --git a/tests/cli/test_branch_command.py b/tests/cli/test_branch_command.py index 8f8a70749..9c14243cf 100644 --- a/tests/cli/test_branch_command.py +++ b/tests/cli/test_branch_command.py @@ -240,3 +240,21 @@ class TestBranchCommandDef: from hermes_cli.commands import COMMAND_REGISTRY branch = next(c for c in COMMAND_REGISTRY if c.name == "branch") assert branch.category == "Session" + + +class TestBranchFlushesBeforeEndSession: + """Regression for #47202: /branch must flush un-persisted messages to + the session DB before ending the old session, just like /new and + compress_context() already do.""" + + def test_branch_flushes_when_agent_present(self, cli_instance, session_db): + from cli import HermesCLI + + agent = MagicMock() + cli_instance.agent = agent + + HermesCLI._handle_branch_command(cli_instance, "/branch") + + agent._flush_messages_to_session_db.assert_called_once_with( + cli_instance.conversation_history + ) diff --git a/tests/cli/test_cli_resume_command.py b/tests/cli/test_cli_resume_command.py index b062f9326..299d5fad2 100644 --- a/tests/cli/test_cli_resume_command.py +++ b/tests/cli/test_cli_resume_command.py @@ -321,3 +321,33 @@ class TestRestoreSessionCwdMarkup: assert "Working directory" in printed or "working" in printed.lower() finally: os.chdir(original_cwd) + + +class TestResumeFlushesBeforeEndSession: + """Regression for #47202: /resume must flush un-persisted messages to + the session DB before ending the old session, just like /new and + compress_context() already do.""" + + def test_resume_flushes_when_agent_present(self): + cli_obj = _make_cli() + cli_obj.conversation_history = [ + {"role": "user", "content": "hello"}, + {"role": "assistant", "content": "hi"}, + ] + agent = MagicMock() + cli_obj.agent = agent + + cli_obj._session_db.get_session.return_value = {"id": "target", "title": "T"} + cli_obj._session_db.get_messages_as_conversation.return_value = [] + cli_obj._session_db.resolve_resume_session_id.return_value = "target" + + with ( + patch("hermes_cli.main._resolve_session_by_name_or_id", return_value="target"), + patch("cli._cprint"), + ): + cli_obj._handle_resume_command("/resume target") + + agent._flush_messages_to_session_db.assert_called_once_with( + [{"role": "user", "content": "hello"}, {"role": "assistant", "content": "hi"}] + ) + cli_obj._session_db.end_session.assert_called_once()