mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-30 07:51:45 +08:00
Compare commits
3 Commits
bb/base-gu
...
sid/restru
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b49bd7b93d | ||
|
|
420c4d02e2 | ||
|
|
193f3b8339 |
@@ -69,7 +69,7 @@ hermes-agent/
|
||||
│ ├── server.py # RPC handlers and session logic
|
||||
│ ├── render.py # Optional rich/ANSI bridge
|
||||
│ └── slash_worker.py # Persistent HermesCLI subprocess for slash commands
|
||||
├── acp_adapter/ # ACP server (VS Code / Zed / JetBrains integration)
|
||||
├── hermes_agent/acp/ # ACP server (VS Code / Zed / JetBrains integration)
|
||||
├── cron/ # Scheduler (jobs.py, scheduler.py)
|
||||
├── environments/ # RL training environments (Atropos)
|
||||
├── tests/ # Pytest suite (~3000 tests)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
"""Allow running the ACP adapter as ``python -m acp_adapter``."""
|
||||
|
||||
from .entry import main
|
||||
|
||||
main()
|
||||
2
cli.py
2
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:
|
||||
|
||||
1
hermes_agent/__init__.py
Normal file
1
hermes_agent/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Hermes Agent — The self-improving AI agent."""
|
||||
5
hermes_agent/acp/__main__.py
Normal file
5
hermes_agent/acp/__main__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Allow running the ACP adapter as ``python -m hermes_agent.acp``."""
|
||||
|
||||
from hermes_agent.acp.entry import main
|
||||
|
||||
main()
|
||||
@@ -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:
|
||||
@@ -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,
|
||||
@@ -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__)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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 ----------------------------------------
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -10,20 +10,20 @@ The ACP adapter wraps Hermes' synchronous `AIAgent` in an async JSON-RPC stdio s
|
||||
|
||||
Key implementation files:
|
||||
|
||||
- `acp_adapter/entry.py`
|
||||
- `acp_adapter/server.py`
|
||||
- `acp_adapter/session.py`
|
||||
- `acp_adapter/events.py`
|
||||
- `acp_adapter/permissions.py`
|
||||
- `acp_adapter/tools.py`
|
||||
- `acp_adapter/auth.py`
|
||||
- `hermes_agent/acp/entry.py`
|
||||
- `hermes_agent/acp/server.py`
|
||||
- `hermes_agent/acp/session.py`
|
||||
- `hermes_agent/acp/events.py`
|
||||
- `hermes_agent/acp/permissions.py`
|
||||
- `hermes_agent/acp/tools.py`
|
||||
- `hermes_agent/acp/auth.py`
|
||||
- `acp_registry/agent.json`
|
||||
|
||||
## Boot flow
|
||||
|
||||
```text
|
||||
hermes acp / hermes-acp / python -m acp_adapter
|
||||
-> acp_adapter.entry.main()
|
||||
hermes acp / hermes-acp / python -m hermes_agent.acp
|
||||
-> hermes_agent.acp.entry.main()
|
||||
-> load ~/.hermes/.env
|
||||
-> configure stderr logging
|
||||
-> construct HermesACPAgent
|
||||
@@ -36,7 +36,7 @@ Stdout is reserved for ACP JSON-RPC transport. Human-readable logs go to stderr.
|
||||
|
||||
### `HermesACPAgent`
|
||||
|
||||
`acp_adapter/server.py` implements the ACP agent protocol.
|
||||
`hermes_agent/acp/server.py` implements the ACP agent protocol.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
@@ -48,7 +48,7 @@ Responsibilities:
|
||||
|
||||
### `SessionManager`
|
||||
|
||||
`acp_adapter/session.py` tracks live ACP sessions.
|
||||
`hermes_agent/acp/session.py` tracks live ACP sessions.
|
||||
|
||||
Each session stores:
|
||||
|
||||
@@ -71,7 +71,7 @@ The manager is thread-safe and supports:
|
||||
|
||||
### Event bridge
|
||||
|
||||
`acp_adapter/events.py` converts AIAgent callbacks into ACP `session_update` events.
|
||||
`hermes_agent/acp/events.py` converts AIAgent callbacks into ACP `session_update` events.
|
||||
|
||||
Bridged callbacks:
|
||||
|
||||
@@ -88,7 +88,7 @@ asyncio.run_coroutine_threadsafe(...)
|
||||
|
||||
### Permission bridge
|
||||
|
||||
`acp_adapter/permissions.py` adapts dangerous terminal approval prompts into ACP permission requests.
|
||||
`hermes_agent/acp/permissions.py` adapts dangerous terminal approval prompts into ACP permission requests.
|
||||
|
||||
Mapping:
|
||||
|
||||
@@ -100,7 +100,7 @@ Timeouts and bridge failures deny by default.
|
||||
|
||||
### Tool rendering helpers
|
||||
|
||||
`acp_adapter/tools.py` maps Hermes tools to ACP tool kinds and builds editor-facing content.
|
||||
`hermes_agent/acp/tools.py` maps Hermes tools to ACP tool kinds and builds editor-facing content.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -144,7 +144,7 @@ ACP does not implement its own auth store.
|
||||
|
||||
Instead it reuses Hermes' runtime resolver:
|
||||
|
||||
- `acp_adapter/auth.py`
|
||||
- `hermes_agent/acp/auth.py`
|
||||
- `hermes_cli/runtime_provider.py`
|
||||
|
||||
So ACP advertises and uses the currently configured Hermes provider/credentials.
|
||||
|
||||
@@ -14,7 +14,7 @@ This page is the top-level map of Hermes Agent internals. Use it to orient yours
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Entry Points │
|
||||
│ │
|
||||
│ CLI (cli.py) Gateway (gateway/run.py) ACP (acp_adapter/) │
|
||||
│ CLI (cli.py) Gateway (gateway/run.py) ACP (hermes_agent/acp/)│
|
||||
│ Batch Runner API Server Python Library │
|
||||
└──────────┬──────────────┬───────────────────────┬───────────────────┘
|
||||
│ │ │
|
||||
@@ -122,7 +122,7 @@ hermes-agent/
|
||||
│ # dingtalk, feishu, wecom, wecom_callback, weixin,
|
||||
│ # bluebubbles, qqbot, homeassistant, webhook, api_server
|
||||
│
|
||||
├── acp_adapter/ # ACP server (VS Code / Zed / JetBrains)
|
||||
├── hermes_agent/acp/ # ACP server (VS Code / Zed / JetBrains)
|
||||
├── cron/ # Scheduler (jobs.py, scheduler.py)
|
||||
├── plugins/memory/ # Memory provider plugins
|
||||
├── plugins/context_engine/ # Context engine plugins
|
||||
|
||||
@@ -650,7 +650,7 @@ Related entrypoints:
|
||||
|
||||
```bash
|
||||
hermes-acp
|
||||
python -m acp_adapter
|
||||
python -m hermes_agent.acp
|
||||
```
|
||||
|
||||
Install support first:
|
||||
|
||||
@@ -43,7 +43,7 @@ This installs the `agent-client-protocol` dependency and enables:
|
||||
|
||||
- `hermes acp`
|
||||
- `hermes-acp`
|
||||
- `python -m acp_adapter`
|
||||
- `python -m hermes_agent.acp`
|
||||
|
||||
## Launching the ACP server
|
||||
|
||||
@@ -58,7 +58,7 @@ hermes-acp
|
||||
```
|
||||
|
||||
```bash
|
||||
python -m acp_adapter
|
||||
python -m hermes_agent.acp
|
||||
```
|
||||
|
||||
Hermes logs to stderr so stdout remains reserved for ACP JSON-RPC traffic.
|
||||
|
||||
Reference in New Issue
Block a user