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()