Files
2026-06-14 14:30:48 -04:00

184 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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`
- `acp_registry/agent.json`
## 启动流程
```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)
```
Zed ACP Registry 路径通过 `uvx --from 'hermes-agent[acp]==<version>' hermes-acp` 启动同一适配器,指向 `hermes-agent` PyPI 发布包。
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`),以便首次运行的 registry 客户端在启动正常 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` 脚本