Broad drift audit against origin/main (b52b63396).
Reference pages (most user-visible drift):
- slash-commands: add /busy, /curator, /footer, /indicator, /redraw, /steer
that were missing; drop non-existent /terminal-setup; fix /q footnote
(resolves to /queue, not /quit); extend CLI-only list with all 24
CLI-only commands in the registry
- cli-commands: add dedicated sections for hermes curator / fallback /
hooks (new subcommands not previously documented); remove stale
hermes honcho standalone section (the plugin registers dynamically
via hermes memory); list curator/fallback/hooks in top-level table;
fix completion to include fish
- toolsets-reference: document the real 52-toolset count; split browser
vs browser-cdp; add discord / discord_admin / spotify / yuanbao;
correct hermes-cli tool count from 36 to 38; fix misleading claim
that hermes-homeassistant adds tools (it's identical to hermes-cli)
- tools-reference: bump tool count 55 -> 68; add 7 Spotify, 5 Yuanbao,
2 Discord toolsets; move browser_cdp/browser_dialog to their own
browser-cdp toolset section
- environment-variables: add 40+ user-facing HERMES_* vars that were
undocumented (--yolo, --accept-hooks, --ignore-*, inference model
override, agent/stream/checkpoint timeouts, OAuth trace, per-platform
batch tuning for Telegram/Discord/Matrix/Feishu/WeCom, cron knobs,
gateway restart/connect timeouts); dedupe the Cron Scheduler section;
replace stale QQ_SANDBOX with QQ_PORTAL_HOST
User-guide (top level):
- cli.md: compression preserves last 20 turns, not 4 (protect_last_n: 20)
- configuration.md: display.platforms is the canonical per-platform
override key; tool_progress_overrides is deprecated and auto-migrated
- profiles.md: model.default is the config key, not model.model
- sessions.md: CLI/TUI session IDs use 6-char hex, gateway uses 8
- checkpoints-and-rollback.md: destructive-command list now matches
_DESTRUCTIVE_PATTERNS (adds rmdir, cp, install, dd)
- docker.md: the container runs as non-root hermes (UID 10000) via
gosu; fix install command (uv pip); add missing --insecure on the
dashboard compose example (required for non-loopback bind)
- security.md: systemctl danger pattern also matches 'restart'
- index.md: built-in tool count 47 -> 68
- integrations/index.md: 6 STT providers, 8 memory providers
- integrations/providers.md: drop fictional dashscope/qwen aliases
Features:
- overview.md: 9 image models (not 8), 9 TTS providers (not 5),
8 memory providers (Supermemory was missing)
- tool-gateway.md: 9 image models
- tools.md: extend common-toolsets list with search / messaging /
spotify / discord / debugging / safe
- fallback-providers.md: add 6 real providers from PROVIDER_REGISTRY
(lmstudio, kimi-coding-cn, stepfun, alibaba-coding-plan,
tencent-tokenhub, azure-foundry)
- plugins.md: Available Hooks table now includes on_session_finalize,
on_session_reset, subagent_stop
- built-in-plugins.md: add the 7 bundled plugins the page didn't
mention (spotify, google_meet, three image_gen providers, two
dashboard examples)
- web-dashboard.md: add --insecure and --tui flags
- cron.md: hermes cron create takes positional schedule/prompt, not
flags
Messaging:
- telegram.md: TELEGRAM_WEBHOOK_SECRET is now REQUIRED when
TELEGRAM_WEBHOOK_URL is set (gateway refuses to start without it
per GHSA-3vpc-7q5r-276h). Biggest user-visible drift in the batch.
- discord.md: HERMES_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS default
is 2.0, not 0.1
- dingtalk.md: document DINGTALK_REQUIRE_MENTION /
FREE_RESPONSE_CHATS / MENTION_PATTERNS / HOME_CHANNEL /
ALLOW_ALL_USERS that the adapter supports
- bluebubbles.md: drop fictional BLUEBUBBLES_SEND_READ_RECEIPTS env
var; the setting lives in platforms.bluebubbles.extra only
- qqbot.md: drop dead QQ_SANDBOX; add real QQ_PORTAL_HOST and
QQ_GROUP_ALLOWED_USERS
- wecom-callback.md: replace 'hermes gateway start' (service-only)
with 'hermes gateway' for first-time setup
Developer-guide:
- architecture.md: refresh tool/toolset counts (61/52), terminal
backend count (7), line counts for run_agent.py (~13.7k), cli.py
(~11.5k), main.py (~10.4k), setup.py (~3.5k), gateway/run.py
(~12.2k), mcp_tool.py (~3.1k); add yuanbao adapter, bump platform
adapter count 18 -> 20
- agent-loop.md: run_agent.py line count 10.7k -> 13.7k
- tools-runtime.md: add vercel_sandbox backend
- adding-tools.md: remove stale 'Discovery import added to
model_tools.py' checklist item (registry auto-discovery)
- adding-platform-adapters.md: mark send_typing / get_chat_info as
concrete base methods; only connect/disconnect/send are abstract
- acp-internals.md: ACP sessions now persist to SessionDB
(~/.hermes/state.db); acp.run_agent call uses
use_unstable_protocol=True
- cron-internals.md: gateway runs scheduler in a dedicated background
thread via _start_cron_ticker, not on a maintenance cycle; locking
is cross-process via fcntl.flock (Unix) / msvcrt.locking (Windows)
- gateway-internals.md: gateway/run.py ~12k lines
- provider-runtime.md: cron DOES support fallback (run_job reads
fallback_providers from config)
- session-storage.md: SCHEMA_VERSION = 11 (not 9); add migrations
10 and 11 (trigram FTS, inline-mode FTS5 re-index); add
api_call_count column to Sessions DDL; document messages_fts_trigram
and state_meta in the architecture tree
- context-compression-and-caching.md: remove the obsolete 'context
pressure warnings' section (warnings were removed for causing
models to give up early)
- context-engine-plugin.md: compress() signature now includes
focus_topic param
- extending-the-cli.md: _build_tui_layout_children signature now
includes model_picker_widget; add to default layout
Also fixed three pre-existing broken links/anchors the build warned
about (docker.md -> api-server.md, yuanbao.md -> cron-jobs.md and
tips#background-tasks, nix-setup.md -> #container-aware-cli).
Regenerated per-skill pages via website/scripts/generate-skill-docs.py
so catalog tables and sidebar are consistent with current SKILL.md
frontmatter.
docusaurus build: clean, no broken links or anchors.
4.6 KiB
| sidebar_position | title | description |
|---|---|---|
| 2 | ACP Internals | 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.pyacp_adapter/server.pyacp_adapter/session.pyacp_adapter/events.pyacp_adapter/permissions.pyacp_adapter/tools.pyacp_adapter/auth.pyacp_registry/agent.json
Boot flow
hermes acp / hermes-acp / python -m acp_adapter
-> acp_adapter.entry.main()
-> load ~/.hermes/.env
-> configure stderr logging
-> construct HermesACPAgent
-> acp.run_agent(agent, use_unstable_protocol=True)
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_idagentcwdmodelhistorycancel_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_callbackthinking_callbackstep_callbackmessage_callback
Because AIAgent runs in a worker thread while ACP I/O lives on the main event loop, the bridge uses:
asyncio.run_coroutine_threadsafe(...)
Permission bridge
acp_adapter/permissions.py adapts dangerous terminal approval prompts into ACP permission requests.
Mapping:
allow_once-> Hermesonceallow_always-> Hermesalways- 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 diffsterminal-> shell command textread_file/search_files-> text previews- large results -> truncated text blocks for UI safety
Session lifecycle
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.pyhermes_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 persisted to the shared
~/.hermes/state.db(SessionDB) and transparently restored across process restarts; they appear insession_search - 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 suitetoolsets.py—hermes-acptoolset definitionhermes_cli/main.py—hermes acpCLI subcommandpyproject.toml—[acp]optional dependency +hermes-acpscript