diff --git a/cli.py b/cli.py index da4b29d6ef..572566054f 100644 --- a/cli.py +++ b/cli.py @@ -1363,6 +1363,10 @@ def _notify_single_query_session_finalize(cli, *, reason: str = "shutdown") -> N session_id = getattr(agent, "session_id", None) or getattr(cli, "session_id", None) if session_id in _single_query_finalize_attempted_session_ids: return + # Don't finalize a session that was handed off to the gateway — + # the gateway owns the lifecycle now (#88234). + if session_id in _handed_off_session_ids: + return try: _notify_session_finalize( diff --git a/tests/cli/test_handoff_cleanup_race.py b/tests/cli/test_handoff_cleanup_race.py index cb996621ca..ed7c2b5cd0 100644 --- a/tests/cli/test_handoff_cleanup_race.py +++ b/tests/cli/test_handoff_cleanup_race.py @@ -136,3 +136,24 @@ def test_cleanup_finalizes_normal_session(): cli_mod._run_cleanup() mock_finalize.assert_called_once() + + +def test_single_query_finalize_skipped_for_handed_off(): + """_notify_single_query_session_finalize must not fire for a handed-off session.""" + import cli as cli_mod + + _reset_cli_globals(cli_mod) + cli_mod._handed_off_session_ids.add("handoff-session-single") + + agent = MagicMock() + agent.session_id = "handoff-session-single" + agent.platform = "cli" + + cli_mock = MagicMock() + cli_mock.agent = agent + cli_mock.session_id = "handoff-session-single" + + with patch("hermes_cli.lifecycle.finalize_session") as mock_finalize: + cli_mod._notify_single_query_session_finalize(cli_mock) + + mock_finalize.assert_not_called()