From 420c4d02e2b586a3ca5b5b8c3a0823cd3e3a40cf Mon Sep 17 00:00:00 2001 From: alt-glitch Date: Thu, 23 Apr 2026 20:52:20 +0530 Subject: [PATCH] refactor(acp): rewrite imports and update infra for hermes_agent.acp Rewrite all acp_adapter imports to hermes_agent.acp in source, tests, and pyproject.toml. Convert relative imports to absolute per manifest convention. Strip sys.path hack from entry.py (redundant with editable install). Update pyproject.toml entry point and packages.find. Part of #14586, #14182 --- cli.py | 2 +- hermes_agent/acp/__main__.py | 4 +-- hermes_agent/acp/entry.py | 10 ++----- hermes_agent/acp/events.py | 2 +- hermes_agent/acp/server.py | 8 +++--- hermes_cli/main.py | 2 +- pyproject.toml | 4 +-- tests/acp/test_auth.py | 4 +-- tests/acp/test_entry.py | 4 +-- tests/acp/test_events.py | 42 +++++++++++++++--------------- tests/acp/test_mcp_e2e.py | 6 ++--- tests/acp/test_permissions.py | 10 +++---- tests/acp/test_ping_suppression.py | 4 +-- tests/acp/test_server.py | 16 ++++++------ tests/acp/test_session.py | 6 ++--- tests/acp/test_tools.py | 4 +-- 16 files changed, 61 insertions(+), 67 deletions(-) diff --git a/cli.py b/cli.py index a289e3ab2..0de2a53a3 100644 --- a/cli.py +++ b/cli.py @@ -8454,7 +8454,7 @@ class HermesCLI: # in terminal_tool is populated for this thread. The main thread # registration (run() line ~9046) is invisible here because # _callback_tls is threading.local(). Matches the pattern used - # by acp_adapter/server.py for ACP sessions. + # by hermes_agent/acp/server.py for ACP sessions. set_sudo_password_callback(self._sudo_password_callback) set_approval_callback(self._approval_callback) try: diff --git a/hermes_agent/acp/__main__.py b/hermes_agent/acp/__main__.py index a6ccd0997..2ca0c20ad 100644 --- a/hermes_agent/acp/__main__.py +++ b/hermes_agent/acp/__main__.py @@ -1,5 +1,5 @@ -"""Allow running the ACP adapter as ``python -m acp_adapter``.""" +"""Allow running the ACP adapter as ``python -m hermes_agent.acp``.""" -from .entry import main +from hermes_agent.acp.entry import main main() diff --git a/hermes_agent/acp/entry.py b/hermes_agent/acp/entry.py index 3089f78c2..bd1f1db38 100644 --- a/hermes_agent/acp/entry.py +++ b/hermes_agent/acp/entry.py @@ -6,7 +6,7 @@ and starts the ACP agent server. Usage:: - python -m acp_adapter.entry + python -m hermes_agent.acp.entry # or hermes acp # or @@ -16,7 +16,6 @@ Usage:: import asyncio import logging import sys -from pathlib import Path from hermes_constants import get_hermes_home @@ -104,13 +103,8 @@ def main() -> None: logger = logging.getLogger(__name__) logger.info("Starting hermes-agent ACP adapter") - # Ensure the project root is on sys.path so ``from run_agent import AIAgent`` works - project_root = str(Path(__file__).resolve().parent.parent) - if project_root not in sys.path: - sys.path.insert(0, project_root) - import acp - from .server import HermesACPAgent + from hermes_agent.acp.server import HermesACPAgent agent = HermesACPAgent() try: diff --git a/hermes_agent/acp/events.py b/hermes_agent/acp/events.py index 1257f902e..9386c0bc8 100644 --- a/hermes_agent/acp/events.py +++ b/hermes_agent/acp/events.py @@ -15,7 +15,7 @@ from typing import Any, Callable, Deque, Dict import acp -from .tools import ( +from hermes_agent.acp.tools import ( build_tool_complete, build_tool_start, make_tool_call_id, diff --git a/hermes_agent/acp/server.py b/hermes_agent/acp/server.py index d73c71157..0d266b252 100644 --- a/hermes_agent/acp/server.py +++ b/hermes_agent/acp/server.py @@ -52,15 +52,15 @@ try: except ImportError: from acp.schema import AuthMethod as AuthMethodAgent # type: ignore[attr-defined] -from acp_adapter.auth import detect_provider -from acp_adapter.events import ( +from hermes_agent.acp.auth import detect_provider +from hermes_agent.acp.events import ( make_message_cb, make_step_cb, make_thinking_cb, make_tool_progress_cb, ) -from acp_adapter.permissions import make_approval_callback -from acp_adapter.session import SessionManager, SessionState +from hermes_agent.acp.permissions import make_approval_callback +from hermes_agent.acp.session import SessionManager, SessionState logger = logging.getLogger(__name__) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index ec0441f8b..6896f3b15 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -8516,7 +8516,7 @@ Examples: def cmd_acp(args): """Launch Hermes Agent as an ACP server.""" try: - from acp_adapter.entry import main as acp_main + from hermes_agent.acp.entry import main as acp_main acp_main() except ImportError: diff --git a/pyproject.toml b/pyproject.toml index ac4bdcc5f..77bec16a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,7 +117,7 @@ all = [ [project.scripts] hermes = "hermes_cli.main:main" hermes-agent = "run_agent:main" -hermes-acp = "acp_adapter.entry:main" +hermes-acp = "hermes_agent.acp.entry:main" [tool.setuptools] py-modules = ["run_agent", "model_tools", "toolsets", "batch_runner", "trajectory_compressor", "toolset_distributions", "cli", "hermes_constants", "hermes_state", "hermes_time", "hermes_logging", "rl_cli", "utils"] @@ -126,7 +126,7 @@ py-modules = ["run_agent", "model_tools", "toolsets", "batch_runner", "trajector hermes_cli = ["web_dist/**/*"] [tool.setuptools.packages.find] -include = ["agent", "agent.*", "tools", "tools.*", "hermes_cli", "gateway", "gateway.*", "tui_gateway", "tui_gateway.*", "cron", "acp_adapter", "plugins", "plugins.*"] +include = ["agent", "agent.*", "tools", "tools.*", "hermes_cli", "gateway", "gateway.*", "tui_gateway", "tui_gateway.*", "cron", "hermes_agent", "hermes_agent.*", "plugins", "plugins.*"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/tests/acp/test_auth.py b/tests/acp/test_auth.py index ffb07463f..cda11a748 100644 --- a/tests/acp/test_auth.py +++ b/tests/acp/test_auth.py @@ -1,6 +1,6 @@ -"""Tests for acp_adapter.auth — provider detection.""" +"""Tests for hermes_agent.acp.auth — provider detection.""" -from acp_adapter.auth import has_provider, detect_provider +from hermes_agent.acp.auth import has_provider, detect_provider class TestHasProvider: diff --git a/tests/acp/test_entry.py b/tests/acp/test_entry.py index 760522c31..4d76f2db4 100644 --- a/tests/acp/test_entry.py +++ b/tests/acp/test_entry.py @@ -1,8 +1,8 @@ -"""Tests for acp_adapter.entry startup wiring.""" +"""Tests for hermes_agent.acp.entry startup wiring.""" import acp -from acp_adapter import entry +from hermes_agent.acp import entry def test_main_enables_unstable_protocol(monkeypatch): diff --git a/tests/acp/test_events.py b/tests/acp/test_events.py index c9f91a181..eb233fbe0 100644 --- a/tests/acp/test_events.py +++ b/tests/acp/test_events.py @@ -1,4 +1,4 @@ -"""Tests for acp_adapter.events — callback factories for ACP notifications.""" +"""Tests for hermes_agent.acp.events — callback factories for ACP notifications.""" import asyncio from concurrent.futures import Future @@ -9,7 +9,7 @@ import pytest import acp from acp.schema import ToolCallStart, ToolCallProgress, AgentThoughtChunk, AgentMessageChunk -from acp_adapter.events import ( +from hermes_agent.acp.events import ( make_message_cb, make_step_cb, make_thinking_cb, @@ -48,7 +48,7 @@ class TestToolProgressCallback: cb = make_tool_progress_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) # Run callback in the event loop context - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -72,7 +72,7 @@ class TestToolProgressCallback: cb = make_tool_progress_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -89,7 +89,7 @@ class TestToolProgressCallback: cb = make_tool_progress_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -107,7 +107,7 @@ class TestToolProgressCallback: progress_cb = make_tool_progress_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) step_cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -135,7 +135,7 @@ class TestThinkingCallback: cb = make_thinking_cb(mock_conn, "session-1", loop) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -150,7 +150,7 @@ class TestThinkingCallback: cb = make_thinking_cb(mock_conn, "session-1", loop) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: cb("") mock_rcts.assert_not_called() @@ -169,7 +169,7 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, {}) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -187,7 +187,7 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, {}) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: cb(1, [{"name": "unknown_tool", "result": "ok"}]) mock_rcts.assert_not_called() @@ -199,7 +199,7 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, {}) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -218,8 +218,8 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, {}) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ - patch("acp_adapter.events.build_tool_complete") as mock_btc: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ + patch("hermes_agent.acp.events.build_tool_complete") as mock_btc: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -240,8 +240,8 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, {}) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ - patch("acp_adapter.events.build_tool_complete") as mock_btc: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ + patch("hermes_agent.acp.events.build_tool_complete") as mock_btc: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -259,8 +259,8 @@ class TestStepCallback: cb = make_step_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ - patch("acp_adapter.events.build_tool_complete") as mock_btc: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts, \ + patch("hermes_agent.acp.events.build_tool_complete") as mock_btc: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -280,8 +280,8 @@ class TestStepCallback: tool_call_meta = {} loop = event_loop_fixture - with patch("acp_adapter.events.make_tool_call_id", return_value="tc-meta"), \ - patch("acp_adapter.events._send_update") as mock_send, \ + with patch("hermes_agent.acp.events.make_tool_call_id", return_value="tc-meta"), \ + patch("hermes_agent.acp.events._send_update") as mock_send, \ patch("agent.display.capture_local_edit_snapshot", return_value="snapshot"): cb = make_tool_progress_cb(mock_conn, "session-1", loop, tool_call_ids, tool_call_meta) cb("tool.started", "write_file", None, {"path": "diff-test.txt", "content": "hello"}) @@ -306,7 +306,7 @@ class TestMessageCallback: cb = make_message_cb(mock_conn, "session-1", loop) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: future = MagicMock(spec=Future) future.result.return_value = None mock_rcts.return_value = future @@ -321,7 +321,7 @@ class TestMessageCallback: cb = make_message_cb(mock_conn, "session-1", loop) - with patch("acp_adapter.events.asyncio.run_coroutine_threadsafe") as mock_rcts: + with patch("hermes_agent.acp.events.asyncio.run_coroutine_threadsafe") as mock_rcts: cb("") mock_rcts.assert_not_called() diff --git a/tests/acp/test_mcp_e2e.py b/tests/acp/test_mcp_e2e.py index 88e89acf2..8e3b2247e 100644 --- a/tests/acp/test_mcp_e2e.py +++ b/tests/acp/test_mcp_e2e.py @@ -27,9 +27,9 @@ from acp.schema import ( ToolCallStart, ) -from acp_adapter.server import HermesACPAgent -from acp_adapter.session import SessionManager -from acp_adapter.tools import build_tool_start +from hermes_agent.acp.server import HermesACPAgent +from hermes_agent.acp.session import SessionManager +from hermes_agent.acp.tools import build_tool_start # --------------------------------------------------------------------------- diff --git a/tests/acp/test_permissions.py b/tests/acp/test_permissions.py index 57e2bd4e5..a16f57fee 100644 --- a/tests/acp/test_permissions.py +++ b/tests/acp/test_permissions.py @@ -1,4 +1,4 @@ -"""Tests for acp_adapter.permissions — ACP approval bridging.""" +"""Tests for hermes_agent.acp.permissions — ACP approval bridging.""" import asyncio from concurrent.futures import Future @@ -11,7 +11,7 @@ from acp.schema import ( DeniedOutcome, RequestPermissionResponse, ) -from acp_adapter.permissions import make_approval_callback +from hermes_agent.acp.permissions import make_approval_callback def _make_response(outcome): @@ -37,7 +37,7 @@ def _setup_callback(outcome, timeout=60.0): future = MagicMock(spec=Future) future.result.return_value = response - with patch("acp_adapter.permissions.asyncio.run_coroutine_threadsafe", return_value=future): + with patch("hermes_agent.acp.permissions.asyncio.run_coroutine_threadsafe", return_value=future): cb = make_approval_callback(mock_rp, loop, session_id="s1", timeout=timeout) result = cb("rm -rf /", "dangerous command") @@ -68,7 +68,7 @@ class TestApprovalMapping: future = MagicMock(spec=Future) future.result.side_effect = TimeoutError("timed out") - with patch("acp_adapter.permissions.asyncio.run_coroutine_threadsafe", return_value=future): + with patch("hermes_agent.acp.permissions.asyncio.run_coroutine_threadsafe", return_value=future): cb = make_approval_callback(mock_rp, loop, session_id="s1", timeout=0.01) result = cb("rm -rf /", "dangerous") @@ -82,7 +82,7 @@ class TestApprovalMapping: future = MagicMock(spec=Future) future.result.return_value = None - with patch("acp_adapter.permissions.asyncio.run_coroutine_threadsafe", return_value=future): + with patch("hermes_agent.acp.permissions.asyncio.run_coroutine_threadsafe", return_value=future): cb = make_approval_callback(mock_rp, loop, session_id="s1", timeout=1.0) result = cb("echo hi", "demo") diff --git a/tests/acp/test_ping_suppression.py b/tests/acp/test_ping_suppression.py index b072bbd7a..d9d63275a 100644 --- a/tests/acp/test_ping_suppression.py +++ b/tests/acp/test_ping_suppression.py @@ -1,4 +1,4 @@ -"""Tests for acp_adapter.entry._BenignProbeMethodFilter. +"""Tests for hermes_agent.acp.entry._BenignProbeMethodFilter. Covers both the isolated filter logic and the full end-to-end path where a client sends a bare JSON-RPC ``ping`` request over stdio and the acp runtime @@ -18,7 +18,7 @@ import pytest from acp.exceptions import RequestError -from acp_adapter.entry import _BenignProbeMethodFilter +from hermes_agent.acp.entry import _BenignProbeMethodFilter # -- Unit tests on the filter itself ---------------------------------------- diff --git a/tests/acp/test_server.py b/tests/acp/test_server.py index faa4c18a7..257138856 100644 --- a/tests/acp/test_server.py +++ b/tests/acp/test_server.py @@ -1,4 +1,4 @@ -"""Tests for acp_adapter.server — HermesACPAgent ACP server.""" +"""Tests for hermes_agent.acp.server — HermesACPAgent ACP server.""" import asyncio import os @@ -28,8 +28,8 @@ from acp.schema import ( TextContentBlock, Usage, ) -from acp_adapter.server import HermesACPAgent, HERMES_VERSION -from acp_adapter.session import SessionManager +from hermes_agent.acp.server import HermesACPAgent, HERMES_VERSION +from hermes_agent.acp.session import SessionManager from hermes_state import SessionDB @@ -97,7 +97,7 @@ class TestAuthenticate: @pytest.mark.asyncio async def test_authenticate_with_matching_method_id(self, agent, monkeypatch): monkeypatch.setattr( - "acp_adapter.server.detect_provider", + "hermes_agent.acp.server.detect_provider", lambda: "openrouter", ) resp = await agent.authenticate(method_id="openrouter") @@ -106,7 +106,7 @@ class TestAuthenticate: @pytest.mark.asyncio async def test_authenticate_is_case_insensitive(self, agent, monkeypatch): monkeypatch.setattr( - "acp_adapter.server.detect_provider", + "hermes_agent.acp.server.detect_provider", lambda: "openrouter", ) resp = await agent.authenticate(method_id="OpenRouter") @@ -115,7 +115,7 @@ class TestAuthenticate: @pytest.mark.asyncio async def test_authenticate_rejects_mismatched_method_id(self, agent, monkeypatch): monkeypatch.setattr( - "acp_adapter.server.detect_provider", + "hermes_agent.acp.server.detect_provider", lambda: "openrouter", ) resp = await agent.authenticate(method_id="totally-invalid-method") @@ -124,7 +124,7 @@ class TestAuthenticate: @pytest.mark.asyncio async def test_authenticate_without_provider(self, agent, monkeypatch): monkeypatch.setattr( - "acp_adapter.server.detect_provider", + "hermes_agent.acp.server.detect_provider", lambda: None, ) resp = await agent.authenticate(method_id="openrouter") @@ -272,7 +272,7 @@ class TestListAndFork: @pytest.mark.asyncio async def test_list_sessions_pagination_first_page(self, agent): - from acp_adapter import server as acp_server + from hermes_agent.acp import server as acp_server infos = [ {"session_id": f"s{i}", "cwd": "/tmp", "title": None, "updated_at": 0.0} diff --git a/tests/acp/test_session.py b/tests/acp/test_session.py index 50d04b1a9..e3222ffa8 100644 --- a/tests/acp/test_session.py +++ b/tests/acp/test_session.py @@ -1,4 +1,4 @@ -"""Tests for acp_adapter.session — SessionManager and SessionState.""" +"""Tests for hermes_agent.acp.session — SessionManager and SessionState.""" import contextlib import io @@ -8,7 +8,7 @@ from types import SimpleNamespace import pytest from unittest.mock import MagicMock, patch -from acp_adapter.session import SessionManager, SessionState +from hermes_agent.acp.session import SessionManager, SessionState from hermes_state import SessionDB @@ -38,7 +38,7 @@ class TestCreateSession: def test_create_session_registers_task_cwd(self, manager, monkeypatch): calls = [] - monkeypatch.setattr("acp_adapter.session._register_task_cwd", lambda task_id, cwd: calls.append((task_id, cwd))) + monkeypatch.setattr("hermes_agent.acp.session._register_task_cwd", lambda task_id, cwd: calls.append((task_id, cwd))) state = manager.create_session(cwd="/tmp/work") assert calls == [(state.session_id, "/tmp/work")] diff --git a/tests/acp/test_tools.py b/tests/acp/test_tools.py index 603fe7459..488e01ef7 100644 --- a/tests/acp/test_tools.py +++ b/tests/acp/test_tools.py @@ -1,8 +1,8 @@ -"""Tests for acp_adapter.tools — tool kind mapping and ACP content building.""" +"""Tests for hermes_agent.acp.tools — tool kind mapping and ACP content building.""" import pytest -from acp_adapter.tools import ( +from hermes_agent.acp.tools import ( TOOL_KIND_MAP, build_tool_complete, build_tool_start,