Restore the ACP editor-integration implementation that was present on the original PR branch but did not actually land in main. Includes: - acp_adapter/ server, session manager, event bridge, auth, permissions, and tool helpers - hermes acp subcommand and hermes-acp entry point - hermes-acp curated toolset - ACP registry manifest, setup guide, and ACP test suite - jupyter-live-kernel data science skill from the original branch Also updates the revived ACP code for current main by: - resolving runtime providers through the modern shared provider router - binding ACP sessions to per-session cwd task overrides - tracking duplicate same-name tool calls with FIFO IDs - restoring terminal approval callbacks after prompts - normalizing supporting docs/skill metadata Validated with tests/acp and the full pytest suite (-n0).
89 lines
2.4 KiB
Python
89 lines
2.4 KiB
Python
"""CLI entry point for the hermes-agent ACP adapter.
|
|
|
|
Loads environment variables from ``~/.hermes/.env``, configures logging
|
|
to write to stderr (so stdout is reserved for ACP JSON-RPC transport),
|
|
and starts the ACP agent server.
|
|
|
|
Usage::
|
|
|
|
python -m acp_adapter.entry
|
|
# or
|
|
hermes acp
|
|
# or
|
|
hermes-acp
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def _setup_logging() -> None:
|
|
"""Route all logging to stderr so stdout stays clean for ACP stdio."""
|
|
handler = logging.StreamHandler(sys.stderr)
|
|
handler.setFormatter(
|
|
logging.Formatter(
|
|
"%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
)
|
|
root = logging.getLogger()
|
|
root.handlers.clear()
|
|
root.addHandler(handler)
|
|
root.setLevel(logging.INFO)
|
|
|
|
# Quiet down noisy libraries
|
|
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
logging.getLogger("openai").setLevel(logging.WARNING)
|
|
|
|
|
|
def _load_env() -> None:
|
|
"""Load .env from HERMES_HOME (default ``~/.hermes``)."""
|
|
from dotenv import load_dotenv
|
|
|
|
hermes_home = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
|
|
env_file = hermes_home / ".env"
|
|
if env_file.exists():
|
|
try:
|
|
load_dotenv(dotenv_path=env_file, encoding="utf-8")
|
|
except UnicodeDecodeError:
|
|
load_dotenv(dotenv_path=env_file, encoding="latin-1")
|
|
logging.getLogger(__name__).info("Loaded env from %s", env_file)
|
|
else:
|
|
logging.getLogger(__name__).info(
|
|
"No .env found at %s, using system env", env_file
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
"""Entry point: load env, configure logging, run the ACP agent."""
|
|
_setup_logging()
|
|
_load_env()
|
|
|
|
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
|
|
|
|
agent = HermesACPAgent()
|
|
try:
|
|
asyncio.run(acp.run_agent(agent))
|
|
except KeyboardInterrupt:
|
|
logger.info("Shutting down (KeyboardInterrupt)")
|
|
except Exception:
|
|
logger.exception("ACP agent crashed")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|