"""Tests for the managed Hermes response-transform lifecycle patch.""" from __future__ import annotations import importlib.util from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] PATCHER = ROOT / "hermes-agent" / "patches" / "apply_response_transform_hook.py" def load_patcher(): spec = importlib.util.spec_from_file_location("hermes_response_transform_patcher", PATCHER) assert spec and spec.loader module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def upstream_fixture() -> str: return '''class Agent: def run(self, messages, user_message, completed, effective_task_id, conversation_history, final_response, interrupted, original_user_message): # Save trajectory if enabled self._save_trajectory(messages, user_message, completed) # Clean up VM and browser for this task after conversation completes self._cleanup_task_resources(effective_task_id) # Persist session to both JSON log and SQLite self._persist_session(messages, conversation_history) # Plugin hook: post_llm_call if final_response and not interrupted: from hermes_cli.plugins import invoke_hook as _invoke_hook _invoke_hook( "post_llm_call", session_id=self.session_id, user_message=original_user_message, assistant_response=final_response, ) # Extract reasoning from the last assistant message (if any) last_reasoning = None return final_response, last_reasoning ''' def test_patch_moves_response_transform_before_persistence_and_is_idempotent() -> None: module = load_patcher() patched, changed = module.patch_source(upstream_fixture()) assert changed is True assert module.PATCH_MARKER in patched assert patched.index('_candidate = _post_result.get("assistant_response")') < patched.index( "self._save_trajectory(messages, user_message, completed)" ) assert patched.index("self._save_trajectory(messages, user_message, completed)") < patched.index( "self._persist_session(messages, conversation_history)" ) assert module.patch_source(patched) == (patched, False) def test_patch_refuses_unknown_hermes_lifecycle() -> None: module = load_patcher() with pytest.raises(ValueError, match="anchors changed"): module.patch_source("class Agent:\n pass\n") def test_install_check_reports_patch_required_then_installed(tmp_path: Path) -> None: module = load_patcher() target = tmp_path / "run_agent.py" target.write_text(upstream_fixture(), encoding="utf-8") before, before_code = module.install(target, check=True) assert before_code == 1 assert before["status"] == "patch_required" applied, applied_code = module.install(target, check=False) assert applied_code == 0 assert applied["status"] == "installed_now" after, after_code = module.install(target, check=True) assert after_code == 0 assert after["status"] == "installed"