- add ACP user and developer docs covering setup, lifecycle, callbacks, permissions, tool rendering, and runtime behavior - add developer guides for agent loop, provider runtime resolution, prompt assembly, context caching/compression, gateway internals, session storage, tools runtime, trajectories, and cron internals - refresh architecture, quickstart, installation, CLI reference, and environments docs to link the new implementation pages and ACP support
183 lines
4.5 KiB
Markdown
183 lines
4.5 KiB
Markdown
---
|
|
sidebar_position: 2
|
|
title: "ACP Internals"
|
|
description: "How the ACP adapter works: lifecycle, sessions, event bridge, approvals, and tool rendering"
|
|
---
|
|
|
|
# ACP Internals
|
|
|
|
The ACP adapter wraps Hermes' synchronous `AIAgent` in an async JSON-RPC stdio server.
|
|
|
|
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`
|
|
- `acp_registry/agent.json`
|
|
|
|
## Boot flow
|
|
|
|
```text
|
|
hermes acp / hermes-acp / python -m acp_adapter
|
|
-> acp_adapter.entry.main()
|
|
-> load ~/.hermes/.env
|
|
-> configure stderr logging
|
|
-> construct HermesACPAgent
|
|
-> acp.run_agent(agent)
|
|
```
|
|
|
|
Stdout is reserved for ACP JSON-RPC transport. Human-readable logs go to stderr.
|
|
|
|
## Major components
|
|
|
|
### `HermesACPAgent`
|
|
|
|
`acp_adapter/server.py` implements the ACP agent protocol.
|
|
|
|
Responsibilities:
|
|
|
|
- initialize / authenticate
|
|
- new/load/resume/fork/list/cancel session methods
|
|
- prompt execution
|
|
- session model switching
|
|
- wiring sync AIAgent callbacks into ACP async notifications
|
|
|
|
### `SessionManager`
|
|
|
|
`acp_adapter/session.py` tracks live ACP sessions.
|
|
|
|
Each session stores:
|
|
|
|
- `session_id`
|
|
- `agent`
|
|
- `cwd`
|
|
- `model`
|
|
- `history`
|
|
- `cancel_event`
|
|
|
|
The manager is thread-safe and supports:
|
|
|
|
- create
|
|
- get
|
|
- remove
|
|
- fork
|
|
- list
|
|
- cleanup
|
|
- cwd updates
|
|
|
|
### Event bridge
|
|
|
|
`acp_adapter/events.py` converts AIAgent callbacks into ACP `session_update` events.
|
|
|
|
Bridged callbacks:
|
|
|
|
- `tool_progress_callback`
|
|
- `thinking_callback`
|
|
- `step_callback`
|
|
- `message_callback`
|
|
|
|
Because `AIAgent` runs in a worker thread while ACP I/O lives on the main event loop, the bridge uses:
|
|
|
|
```python
|
|
asyncio.run_coroutine_threadsafe(...)
|
|
```
|
|
|
|
### Permission bridge
|
|
|
|
`acp_adapter/permissions.py` adapts dangerous terminal approval prompts into ACP permission requests.
|
|
|
|
Mapping:
|
|
|
|
- `allow_once` -> Hermes `once`
|
|
- `allow_always` -> Hermes `always`
|
|
- reject options -> Hermes `deny`
|
|
|
|
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.
|
|
|
|
Examples:
|
|
|
|
- `patch` / `write_file` -> file diffs
|
|
- `terminal` -> shell command text
|
|
- `read_file` / `search_files` -> text previews
|
|
- large results -> truncated text blocks for UI safety
|
|
|
|
## Session lifecycle
|
|
|
|
```text
|
|
new_session(cwd)
|
|
-> create SessionState
|
|
-> create AIAgent(platform="acp", enabled_toolsets=["hermes-acp"])
|
|
-> bind task_id/session_id to cwd override
|
|
|
|
prompt(..., session_id)
|
|
-> extract text from ACP content blocks
|
|
-> reset cancel event
|
|
-> install callbacks + approval bridge
|
|
-> run AIAgent in ThreadPoolExecutor
|
|
-> update session history
|
|
-> emit final agent message chunk
|
|
```
|
|
|
|
### Cancelation
|
|
|
|
`cancel(session_id)`:
|
|
|
|
- sets the session cancel event
|
|
- calls `agent.interrupt()` when available
|
|
- causes the prompt response to return `stop_reason="cancelled"`
|
|
|
|
### Forking
|
|
|
|
`fork_session()` deep-copies message history into a new live session, preserving conversation state while giving the fork its own session ID and cwd.
|
|
|
|
## Provider/auth behavior
|
|
|
|
ACP does not implement its own auth store.
|
|
|
|
Instead it reuses Hermes' runtime resolver:
|
|
|
|
- `acp_adapter/auth.py`
|
|
- `hermes_cli/runtime_provider.py`
|
|
|
|
So ACP advertises and uses the currently configured Hermes provider/credentials.
|
|
|
|
## Working directory binding
|
|
|
|
ACP sessions carry an editor cwd.
|
|
|
|
The session manager binds that cwd to the ACP session ID via task-scoped terminal/file overrides, so file and terminal tools operate relative to the editor workspace.
|
|
|
|
## Duplicate same-name tool calls
|
|
|
|
The event bridge tracks tool IDs FIFO per tool name, not just one ID per name. This is important for:
|
|
|
|
- parallel same-name calls
|
|
- repeated same-name calls in one step
|
|
|
|
Without FIFO queues, completion events would attach to the wrong tool invocation.
|
|
|
|
## Approval callback restoration
|
|
|
|
ACP temporarily installs an approval callback on the terminal tool during prompt execution, then restores the previous callback afterward. This avoids leaving ACP session-specific approval handlers installed globally forever.
|
|
|
|
## Current limitations
|
|
|
|
- ACP sessions are process-local from the ACP server's point of view
|
|
- non-text prompt blocks are currently ignored for request text extraction
|
|
- editor-specific UX varies by ACP client implementation
|
|
|
|
## Related files
|
|
|
|
- `tests/acp/` — ACP test suite
|
|
- `toolsets.py` — `hermes-acp` toolset definition
|
|
- `hermes_cli/main.py` — `hermes acp` CLI subcommand
|
|
- `pyproject.toml` — `[acp]` optional dependency + `hermes-acp` script
|