Skip to content

ACP Protocol ​

源码版本v2026.7.20

Responsibility ​

ACP (Agent Client Protocol) is a standard protocol for editors/IDEs to talk to an agent. acp_adapter/ exposes Hermes as an ACP server so that ACP-capable clients (Zed, VS Code extensions, etc.) can directly drive Hermes: manage sessions, read/write resources (files/images), approve edits, trace provenance, and isolate tools by permission. This lets Hermes extend beyond messaging platforms and embed into development workflows.

Design Motivation ​

Hermes was originally designed for messaging platforms — the gateway layer connects long conversations like Telegram and Discord. But an IDE is different: the working directory is opened by the editor, so cwd has to follow the editor; file changes can't just happen because the agent says so — they have to surface to the user for review; and sessions need to trace "this session was compressed from which one." The ACP layer translates these IDE-specific constraints into primitives Hermes already has. The code doesn't rewrite an agent loop — it wires the ACP message stream into run_conversation, converts ACP resources into OpenAI content parts, and turns the write-file operation inside tool calls into an EditProposal that goes through human review. The core is "adapt, don't rewrite."

Key Files ​

Data Flow ​

  1. An ACP client (editor) discovers and launches the Hermes ACP server via acp_registry/agent.json (acp_adapter/__main__.py).
  2. The client opens a session → SessionManager:175 creates a SessionState, aligns cwd (acp_adapter/session.py:29), and expands toolsets (acp_adapter/session.py:129).
  3. The client sends a message/resource → resource conversion:201 turns the ACP resource (file/image, subject to the acp_adapter/server.py:96 size limit) into OpenAI content parts, fed to run_conversation:588.
  4. When the agent wants to modify a file, the tool call becomes an EditProposal via build_edit_proposal:178, popped to the user for confirmation via the approval requester (acp_adapter/edit_approval.py:51).
  5. Tool execution is gated by acp_adapter/permissions.py permissions; each operation records provenance via acp_adapter/provenance.py:22.
  6. Streaming output and events (acp_adapter/events.py) are returned to the client.

acp_registry/agent.json is the client's first-impression business card for Hermes: version, distribution (uvx), launch command. This contract has to stay aligned with the actual binary — otherwise the client installs the wrong version and can't start.

cwd translation is the easiest place to trip in ACP scenarios: a Windows client runs Hermes inside WSL, but the workspace is E:\Projects or a \\wsl.localhost\... UNC path. _translate_acp_cwd unifies these into POSIX — otherwise every Path operation in the agent is misaligned.

The edit proposal is a frozen dataclass. The key field is old_text: _proposal_for_write_file reads the existing file content first, so the diff display can show "before / after" instead of a blind change:

python
@dataclass(frozen=True)
class EditProposal:
    tool_name: str
    path: str
    old_text: str | None
    new_text: str
    arguments: dict[str, Any]

The approval requester binds via ContextVar, not a global variable. Multiple concurrent ACP sessions each have their own requester and don't cross-contaminate. .env / id_rsa default to ask rather than workspace_session, avoiding being mistakenly allowed through by an "everything in the workspace is trusted" policy.

Provenance is specific to the IDE scenario: on messaging platforms sessions are linear; in IDEs they get compressed, forked, and resumed. build_session_provenance walks up parent_session_id, counts layers where end_reason == 'compression', and tags sessionKind and compressionDepth, putting the internal Hermes session id alongside the external ACP session id inside _meta.hermes.sessionProvenance.

Boundaries and Failure ​

  • Handshake version mismatch: agent.json pins hermes-agent[acp]==0.19.0. Once Hermes upgrades to 0.20, a client with a cached old manifest starts a server whose schema doesn't match — the InitializeResponse is missing fields and the handshake fails.
  • WSL cwd translation gap: When translate_cwd_for_wsl_backend doesn't cover some new Windows path form, the agent ends up with a misaligned cwd. Every Path operation drifts, but doesn't error immediately — it only blows up as "directory not found" on the first file write.
  • Approval-requester deadlock: Approval goes through ContextVar + user callback. If the client event loop stalls and never ACKs, the wait after build_edit_proposal just hangs. Whether the ACP protocol layer's cancel actually propagates to that wait point depends on whether events.py's interrupt path covers it.
  • Provenance chain break: If parent_session_id is written wrong during a compression fork, build_session_provenance hits a dead end and returns root. The client then displays a continuation as a new session — history looks "lost."

Summary ​

ACP lets Hermes embed into editor workflows: session management, resource read/write, edit approval, permission isolation, provenance tracing. The core is server.py (protocol adaptation) + session.py (sessions) + edit_approval.py (human-approved edits). It complements the gateway layer — the gateway connects to messaging platforms, ACP connects to IDEs.

Unofficial community learning site. Content based on the MIT-licensed NousResearch/hermes-agent source. · Privacy · Terms · About