---
sidebar_position: 2
title: "ACP 内部机制"
description: "ACP 适配器的工作原理：生命周期、会话、事件桥接、审批流程与工具渲染"
---

# ACP 内部机制

ACP 适配器将 Hermes 的同步 `AIAgent` 封装为异步 JSON-RPC stdio 服务器。

关键实现文件：

- `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`

## 启动流程

```text
hermes acp / hermes-acp / python -m acp_adapter
  -> acp_adapter.entry.main()
  -> parse --version / --check / --setup before server startup
  -> load ~/.hermes/.env
  -> configure stderr logging
  -> construct HermesACPAgent
  -> acp.run_agent(agent, use_unstable_protocol=True)
```

stdout 保留用于 ACP JSON-RPC 传输。人类可读的日志输出至 stderr。

## 主要组件

### `HermesACPAgent`

`acp_adapter/server.py` 实现 ACP agent 协议。

职责：

- 初始化 / 认证
- 新建/加载/恢复/fork/列出/取消会话方法
- prompt（提示词）执行
- 会话模型切换
- 将同步 AIAgent 回调接入 ACP 异步通知

### `SessionManager`

`acp_adapter/session.py` 跟踪活跃的 ACP 会话。

每个会话存储：

- `session_id`
- `agent`
- `cwd`
- `model`
- `history`
- `cancel_event`

管理器线程安全，支持：

- create
- get
- remove
- fork
- list
- cleanup
- cwd 更新

### 事件桥接

`acp_adapter/events.py` 将 AIAgent 回调转换为 ACP `session_update` 事件。

已桥接的回调：

- `tool_progress_callback`
- `thinking_callback`（当前在 ACP 桥接中设置为 `None`——推理内容通过 `step_callback` 转发）
- `step_callback`

由于 `AIAgent` 在工作线程中运行，而 ACP I/O 位于主事件循环，桥接使用：

```python
asyncio.run_coroutine_threadsafe(...)
```

### 权限桥接

`acp_adapter/permissions.py` 将危险终端审批 prompt 适配为 ACP 权限请求。

映射关系：

- `allow_once` -> Hermes `once`
- `allow_always` -> Hermes `always`
- 拒绝选项 -> Hermes `deny`

超时和桥接失败默认拒绝。

### 工具渲染辅助

`acp_adapter/tools.py` 将 Hermes 工具映射到 ACP 工具类型，并构建面向编辑器的内容。

示例：

- `patch` / `write_file` -> 文件 diff
- `terminal` -> shell 命令文本
- `read_file` / `search_files` -> 文本预览
- 大型结果 -> 截断文本块（保障 UI 安全）

## 会话生命周期

```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
```

### 取消

`cancel(session_id)`：

- 设置会话取消事件
- 在可用时调用 `agent.interrupt()`
- 使 prompt 响应返回 `stop_reason="cancelled"`

### Fork

`fork_session()` 将消息历史深拷贝至新的活跃会话，在保留对话状态的同时为 fork 分配独立的 session ID 和 cwd。

## Provider/认证行为

ACP 不实现自己的认证存储。

而是复用 Hermes 的运行时解析器：

- `acp_adapter/auth.py`
- `hermes_cli/runtime_provider.py`

因此 ACP 通告并使用当前配置的 Hermes provider/凭据。它还始终通告一个终端 setup 认证方法（`hermes-setup`，参数 `--setup`），以便首次运行的 ACP 客户端在启动正常 ACP 会话前可以打开 Hermes 的交互式模型/provider 配置。

## 工作目录绑定

ACP 会话携带编辑器 cwd。

会话管理器通过任务作用域的终端/文件覆盖将该 cwd 绑定到 ACP session ID，使文件和终端工具相对于编辑器工作区运行。

## 重复同名工具调用

事件桥接按工具名称以 FIFO 队列跟踪工具 ID，而非每个名称仅保留一个 ID。这对以下场景至关重要：

- 并行同名调用
- 单步内重复同名调用

若不使用 FIFO 队列，完成事件将附加到错误的工具调用上。

## 审批回调恢复

ACP 在 prompt 执行期间临时在终端工具上安装审批回调，执行完成后恢复之前的回调。这避免了将 ACP 会话特定的审批处理器永久全局安装。

## 当前限制

- ACP 会话持久化至共享的 `~/.hermes/state.db`（SessionDB），在进程重启后透明恢复；它们会出现在 `session_search` 中
- 非文本 prompt 块在请求文本提取时当前被忽略
- 编辑器特定的 UX 因 ACP 客户端实现而异

## 相关文件

- `tests/acp/` — ACP 测试套件
- `toolsets.py` — `hermes-acp` toolset 定义
- `hermes_cli/main.py` — `hermes acp` CLI 子命令
- `pyproject.toml` — `[acp]` 可选依赖 + `hermes-acp` 脚本