# Agent Note: ACP 作为仅面向自动化的协议

Status: implemented

[English](2026-07-23-acp-automation-only-protocol.md) | 中文

## 问题

ACP（Agent Client Protocol）桥接层已经变成第二套交互式产品 UI。它将持久事件转换为编辑器卡片、终端元数据、diff、计划、标题、推理（reasoning）、命令、模式、模型和权限选择器、会话导航以及面向人类的询问。这些职责与 TUI 和 Web 客户端重复，同时将自动化传输层与 UI 服务、持久化查询、展示策略和编辑器特定约定耦合在一起。

ACP 仍有一个有用的职责：另一个 agent（智能体）或自动化控制器可以启动 harness 进程、创建隔离会话、发送文本、接收已提交的回答、取消工作并回答权限请求。跨进程 ACP subagent 后端依赖这个标准协议边界。

快照套件使移除工作更复杂。大多数 ACP 场景测试的是组装后的 agent 后端，而不是 ACP 展示层；如果随编辑器桥接层一起删除整个套件，就会丢失大量无密钥行为测试。

## 决策

`@deepseek-ai/dsh-acp` 是位于 [`packages/acp/acp`](../../../../packages/acp/acp/README.md) 下、独立于 `ui` 包组的自动化传输层。其公开协议特意保持精简：版本协商、全新文本会话（每个会话最多允许一个进行中的提示词）、已提交的助手文本更新、按会话取消、并发会话，以及由连接负责的资源清理。提示词只承载规范要求的基线内容——文本，加上被展平为方括号文本引用的资源链接；桥接层会拒绝附加目录、MCP 服务器、超出基线的提示词内容（图片、音频、内嵌资源）、空提示词、未知会话和重叠提示词。

桥接层只发出已提交的 `assistant/message` 文本。推理、原始分片、工具活动、待办事项、计划、标题、重试标记、终端元数据、diff、位置和资源链接仍保留在持久会话日志或 UI 专用传输层中。它不提供会话加载、列出与删除、命令、模式、配置选择器、模型切换、plan 评审或面向人类的询问。

保留一次性 `session/request_permission`。它是为桥接层拥有的 agent 提供的机器策略通道，而不是面向人类的审批 UI：应答者只接受桥接层当前会话映射中登记的同一 agent 对象；外部请求或缺少调用标识的请求会继续委派；RPC 失败则映射为拒绝请求的 `unavailable` 结果。客户端可选择允许一次、拒绝一次或取消，桥接层绝不会将该响应转换为持久授权。询问策略仍归审批 seam 及其生产者所有；[`dsh-subagent-acp`](../../../../packages/subagent/subagent-acp/README.md) 会以程序化方式使用该通道。

应用组装包含 agent 主干、持久化、检查点策略和 ACP 传输层。它不会为 ACP 挂载命令、会话查询、会话引用、plan mode、权限选择器或用户交互服务。SDK 脚手架同样将 `ask_user_question` 视为 TUI 专属功能。

传输层调用 agent、会话和审批的接口服务，而不依赖具体的 agent loop。工具执行仍留在 harness 内；ACP 绝不会把 shell 执行委派给编辑器。stdout 只承载分帧 JSON-RPC，因此 app 不挂载 stdout logger，桥接层也不会 monkey-patch 进程输出。

断开连接与插件 dispose（资源释放）共享同一个经记忆化的完全停稳边界。传输关闭无论成功还是失败，都会将待处理提示词以已取消状态结算，dispose 每个由桥接层拥有的 agent，并等待循环和会话清理完成。创建流程如果在与关闭的竞态中落败，就会 dispose 其尚未发布的 handle。

## 快照边界

ACP 快照套件仍会启动组装后的 ACP 示例，并保留用于锁定后端行为的场景。从该套件移出的只有通过已删除的 UI 方法驱动的场景；由于 ACP 不再加载会话，语义检查点恢复通过 headless `stream-json` 示例执行。

协议与生命周期测试会锁定停止原因编解码器和提示词编解码器、版本协商、新会话创建、文本与资源链接展平、拒绝空提示词或不受支持的提示词、基于同一 agent 对象的权限归属、多会话隔离、提示词结算、按会话取消、传输关闭失败、ACP 专属重载清理，以及拆卸完全停稳。构建产物冒烟测试与真实 stdio 冒烟测试会拒绝混入 stdout 的额外输出。`session/new` 中在真实 stdio 关闭竞态中落败的分支仍豁免覆盖率要求，因为内存传输层无法复现这一顺序；该分支会 dispose 尚未发布的 handle，而周边 dispose 测试会锁定无遗留资源不变式。

## 考虑过的替代方案

**在 Web 达到同等能力前，继续将 ACP 作为编辑器 UI。** 不予采用，因为这会留下两套需要演进的交互约定，并使编辑器约定继续存在于自动化边界中。

**通过严格的服务边界保留早期编辑器桥接层。** 不予采用，尽管该桥接层正确使用了接口服务、工具自有的 render intent、审批与用户交互应答者、harness 自有执行，以及保持 stdout 纯净的组装。其终端卡片是经过能力门控、仅用于展示的 Zed `_meta` 投影，并提供文本回退，而非使用 ACP `terminal/create`，因此 shell 执行从未离开 harness。该投影从稳定的逐调用 id 派生每个展示用终端 id，以避免冲突；由于纯结果展示器接收的是内容块，而不是结构化退出信息，它会从渲染后的状态标记中恢复退出码或信号。标记往返测试和显式的无能力 `console` 回退测试锁定了这两项约定。这些边界保持一致，却无法让编辑器卡片、会话导航、配置选择器和面向人类的询问成为自动化协议应有的职责。

**用私有 subagent RPC 替换 ACP。** 不予采用，因为 ACP 已经提供类型化、可互操作的进程协议，并由跨进程 subagent 后端使用。

**随其他交互功能一起移除机器权限请求。** 不予采用，因为自动化父 agent 必须回答子 agent 的一次性策略决策；这是 agent 之间的控制流，而不是展示层。

**删除 ACP 快照套件，或在本次变更中迁移每个场景。** 不予采用，因为大多数场景测试后端且仍有价值，而完整的 harness 迁移是一项独立的测试变更。只有通过已删除的 UI 方法驱动的场景才离开该套件。

## 结果

ACP 具有适合 agent 与自动化的精简约定，而 TUI 和 Web 拥有面向人类的交互与展示。该包注入的服务、依赖、协议分支和生命周期状态更少，也不再将自身定位为通用编辑器入口。

自动化客户端收到完整的已提交文本，而不是 token 增量或结构化工具 UI。当它们需要推理、工具跟踪信息、标题或更丰富的状态时，需要查看持久日志或其他 API。只支持全新会话也意味着，需要浏览持久会话或恢复会话的调用方必须使用 host API，而不是 ACP。

因此，后端快照测试仍与 ACP 传输层耦合，尽管对于受测行为而言，该传输层只是附带因素。
