| 1 | # ACP 编辑器接入 |
| 2 | |
| 3 | <a href="../README.zh-CN.md">README</a> |
| 4 | · |
| 5 | <a href="./ACP.md">English</a> |
| 6 | · |
| 7 | <a href="./GUIDE.zh-CN.md">使用指南</a> |
| 8 | · |
| 9 | <a href="https://agentclientprotocol.com/">ACP 规范</a> |
| 10 | |
| 11 | Reasonix 实现了 Agent Client Protocol(ACP)v1,通过标准输入输出提供 NDJSON |
| 12 | JSON-RPC 2.0 agent。编辑器和其他 ACP host 负责启动进程、打开一个或多个工作区会话, |
| 13 | 并接收流式消息、工具活动、计划、权限请求和配置更新。 |
| 14 | |
| 15 | ## 启动 agent |
| 16 | |
| 17 | ACP host 应启动以下命令之一: |
| 18 | |
| 19 | ```sh |
| 20 | reasonix acp |
| 21 | reasonix acp --model deepseek-pro |
| 22 | reasonix acp --profile delivery |
| 23 | ``` |
| 24 | |
| 25 | 客户端未覆盖模型时,`--model` 用于选择启动模型;`--profile` 把启动工作模式设为 |
| 26 | `economy`、`balanced` 或 `delivery`。初始化后,两者仍可按会话切换。 |
| 27 | |
| 28 | 标准输出专用于 ACP 消息,Reasonix 会把诊断写入标准错误,因此 host 不应合并这两个 |
| 29 | 流。尚未配置 provider 时先运行 `reasonix setup`;initialize 响应也会声明一个启动 |
| 30 | `reasonix setup` 的 terminal authentication method。 |
| 31 | |
| 32 | ## 初始化与能力协商 |
| 33 | |
| 34 | 客户端应在打开会话前调用 `initialize`。Reasonix 会声明以下能力结构(省略无关字段): |
| 35 | |
| 36 | ```json |
| 37 | { |
| 38 | "protocolVersion": 1, |
| 39 | "agentCapabilities": { |
| 40 | "loadSession": true, |
| 41 | "sessionCapabilities": { |
| 42 | "list": {}, |
| 43 | "resume": {}, |
| 44 | "close": {}, |
| 45 | "delete": {} |
| 46 | }, |
| 47 | "promptCapabilities": { |
| 48 | "image": false, |
| 49 | "audio": false, |
| 50 | "embeddedContext": true |
| 51 | }, |
| 52 | "mcpCapabilities": { |
| 53 | "http": true, |
| 54 | "sse": false |
| 55 | }, |
| 56 | "_meta": { |
| 57 | "reasonix.io": { |
| 58 | "sessionSteer": { |
| 59 | "method": "_reasonix.io/session/steer" |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | ``` |
| 66 | |
| 67 | 客户端声明 `fs.readTextFile`、`fs.writeTextFile` 或 `terminal` 后,Reasonix 会让 |
| 68 | 适用的文件操作经过编辑器的未保存 buffer,并让适用的前台命令在客户端持有的 terminal |
| 69 | 中运行。客户端没有声明这些能力时,常规工作区工具会在 Reasonix 进程内本地运行。 |
| 70 | |
| 71 | ## 会话生命周期 |
| 72 | |
| 73 | 每个 ACP 会话都拥有独立的 Reasonix Controller、工作区根目录、模型、工作模式、协作 |
| 74 | 模式、审批模式、MCP 集合和持久化 transcript,会话之间不会泄漏状态。 |
| 75 | |
| 76 | | 方法 | 行为 | |
| 77 | | --- | --- | |
| 78 | | `session/new` | 为绝对路径 `cwd` 打开会话并返回配置状态。 | |
| 79 | | `session/load` | 打开持久化 ACP 会话,并通过 `session/update` 通知回放 transcript。 | |
| 80 | | `session/resume` | 打开持久化会话,但不回放 transcript。 | |
| 81 | | `session/prompt` | 执行一轮任务,流式发送更新,最后返回停止原因。 | |
| 82 | | `session/cancel` | 取消活动回合;它是一条 notification。 | |
| 83 | | `session/list` | 列出活动和持久化 ACP 会话,可按绝对路径 `cwd` 过滤。 | |
| 84 | | `session/close` | 停止活动会话并释放资源,但不删除历史。 | |
| 85 | | `session/delete` | 停止会话并删除其持久化 ACP 历史。 | |
| 86 | |
| 87 | `session/new`、`session/load` 和 `session/resume` 可以携带 `mcpServers`。 |
| 88 | Reasonix 支持 stdio、Streamable HTTP 和 legacy SSE server。 |
| 89 | stdio `env` 和 HTTP `headers` 支持 ACP 官方的 |
| 90 | `[{"name":"...","value":"..."}]` 结构,同时继续接受旧版 object-map 结构。 |
| 91 | |
| 92 | ## 会话控制 |
| 93 | |
| 94 | Reasonix 把互不相关的选择拆成独立控制轴,而不是混在一个 mode selector 中: |
| 95 | |
| 96 | | 控制项 | 可选值 | 协议入口 | |
| 97 | | --- | --- | --- | |
| 98 | | 协作模式 | `normal`、`plan`、`goal` | `modes` 和 `session/set_mode` | |
| 99 | | 模型 | 已配置的 `provider/model` | id 为 `model` 的 `configOptions` | |
| 100 | | 推理强度 | provider 支持的等级或 `auto` | id 为 `effort` 的 `configOptions` | |
| 101 | | 工作模式 | `economy`、`balanced`、`delivery` | id 为 `work_mode` 的 `configOptions` | |
| 102 | | 工具审批 | `ask`、`auto`、`yolo` | id 为 `tool_approval` 的 `configOptions` | |
| 103 | |
| 104 | 模型、推理强度、工作模式和工具审批统一使用 `session/set_config_option`。它的参数是 |
| 105 | `sessionId`、`configId` 和 `value`,其中 `configId` 取 `configOptions` 中该选项的 |
| 106 | `id`: |
| 107 | |
| 108 | ```json |
| 109 | { |
| 110 | "jsonrpc": "2.0", |
| 111 | "id": 3, |
| 112 | "method": "session/set_config_option", |
| 113 | "params": { |
| 114 | "sessionId": "session-id", |
| 115 | "configId": "tool_approval", |
| 116 | "value": "yolo" |
| 117 | } |
| 118 | } |
| 119 | ``` |
| 120 | |
| 121 | 注意字段名是 `configId`,不是 `optionId`。返回值是刷新后的完整 `configOptions` |
| 122 | 数组;id 未知时返回 `-32602 InvalidParams`。 |
| 123 | |
| 124 | 切换模型、推理强度或工作模式时会重建会话 Controller,同时保留历史和其他控制轴; |
| 125 | 切换工具审批只更新 gate,不重建 Controller。 |
| 126 | |
| 127 | 旧客户端仍可使用 `session/set_model`。`session/set_mode` 也继续接受 legacy 值 |
| 128 | `default` 和 `auto`,分别表示“常规 + 询问”和“常规 + Yolo”;新客户端应使用上面的 |
| 129 | 独立 selector。 |
| 130 | |
| 131 | ## Prompt、更新与审批 |
| 132 | |
| 133 | `session/prompt` 支持文本 block 和内嵌文本 resource,不声明图片或音频能力。执行回合 |
| 134 | 期间,Reasonix 可能发送: |
| 135 | |
| 136 | - agent 消息和思考内容 chunk; |
| 137 | - pending 和 completed 工具调用更新; |
| 138 | - 从 `todo_write` 生成的完整计划更新; |
| 139 | - 可用的斜杠命令; |
| 140 | - 当前 mode 和配置项更新; |
| 141 | - 针对受权限控制工具及用户问题的 `session/request_permission` 请求。 |
| 142 | |
| 143 | Host 应让 `session/prompt` 请求保持打开,直到 Reasonix 返回停止原因;期间仍需同时处理 |
| 144 | 双向 request 和 notification。 |
| 145 | |
| 146 | ## 回合中引导扩展 |
| 147 | |
| 148 | Reasonix 通过 ACP v1 厂商扩展提供回合中引导。它不是 ACP 核心方法,也不是仍未发布的 |
| 149 | ACP v2 `session/inject` 提案。 |
| 150 | |
| 151 | ### 发现能力 |
| 152 | |
| 153 | 从以下位置读取方法名: |
| 154 | |
| 155 | ```text |
| 156 | agentCapabilities._meta["reasonix.io"].sessionSteer.method |
| 157 | ``` |
| 158 | |
| 159 | 不要假设该扩展一定存在,也不要调用无命名空间的 `session/steer`。ACP 为核心协议保留 |
| 160 | 所有不以下划线开头的方法名。 |
| 161 | |
| 162 | ### 发送引导 |
| 163 | |
| 164 | 在 `session/prompt` 仍处于活动状态时调用声明的方法: |
| 165 | |
| 166 | ```json |
| 167 | { |
| 168 | "jsonrpc": "2.0", |
| 169 | "id": 2, |
| 170 | "method": "_reasonix.io/session/steer", |
| 171 | "params": { |
| 172 | "sessionId": "session-id", |
| 173 | "prompt": [ |
| 174 | {"type": "text", "text": "把用户名改成邮箱"} |
| 175 | ] |
| 176 | } |
| 177 | } |
| 178 | ``` |
| 179 | |
| 180 | 成功返回 `{}` 表示活动回合已接受引导。Reasonix 会在下一个安全的模型调用边界前把它 |
| 181 | 作为 user message 加入上下文,不会取消回合,也不会额外消耗工具步骤预算。该消息会进入 |
| 182 | 正常历史;回放 transcript 时显示用户原文,不显示 Reasonix 内部 steer marker。 |
| 183 | |
| 184 | | 条件 | JSON-RPC 结果 | |
| 185 | | --- | --- | |
| 186 | | 活动 prompt 接受引导 | `{}` | |
| 187 | | session 不存在或 prompt 为空 | `-32602 InvalidParams` | |
| 188 | | session 没有活动 prompt | `-32600 InvalidRequest` | |
| 189 | | 客户端调用 `session/steer` | `-32601 MethodNotFound` | |
| 190 | |
| 191 | 收到 `InvalidRequest` 时,引导没有入队。客户端可以等待活动 prompt 结束,再让用户把该 |
| 192 | 文本作为普通新 prompt 提交,但不能把失败的 steer 静默显示为已接受。 |
| 193 | |
| 194 | ## 运行时重载与扩展表面 |
| 195 | |
| 196 | Reasonix 还在 `agentCapabilities._meta["reasonix.io"]` 中通告两个扩展点: |
| 197 | |
| 198 | - `sessionReloadExtensions`——vendor method |
| 199 | `_reasonix.io/session/reloadExtensions`。调用后按与 CLI `/reload` |
| 200 | 相同的失败原子语义重载该会话的 agent 运行时(扩展、工具、skills、 |
| 201 | commands、hooks、providers):回合或重建进行中只排队一次 |
| 202 | (`{"queued": true}`),空闲后执行;否则原子重建并交换,重建失败时 |
| 203 | 保留旧运行时。重载成功后 Reasonix 会推送新的 |
| 204 | `available_commands_update`。 |
| 205 | - `extensionSurface`——结构化扩展 UI 能力。在 initialize `_meta` 中 |
| 206 | 同样声明了 `reasonix.io.extensionSurface` 的客户端会收到结构化的 |
| 207 | 扩展表面载荷;未声明的客户端收到等价文本 fallback(card/status 退 |
| 208 | 化为 `agent_message_chunk`,扩展表单退化为权限请求),因此客户端 |
| 209 | 不做任何处理也能保持兼容。 |
| 210 | |
| 211 | 已安装插件声明的扩展 action 以 `/<plugin>:<action>` 出现在 |
| 212 | `available_commands_update` 中,可像普通斜杠命令一样调用。 |
| 213 | |
| 214 | ## 兼容性与缓存行为 |
| 215 | |
| 216 | | 表面 | 旧版或非 Reasonix 客户端的行为 | 结论 | |
| 217 | | --- | --- | --- | |
| 218 | | 现有 ACP v1 方法 | 方法名和响应结构不变。 | 兼容 | |
| 219 | | Capability `_meta` | 可以忽略未知 metadata。 | 兼容 | |
| 220 | | 持久化 transcript | 不需要新增持久化 schema。 | 兼容 | |
| 221 | | CLI、Desktop、Bot steer | 保留现有 idle fallback。 | 兼容 | |
| 222 | |
| 223 | Steer 只会把用户请求的消息追加到正常会话历史,不改变 system prompt、工具 schema、工具 |
| 224 | 顺序或其他稳定的 provider prefix 字节。下一次 provider 请求必然包含这条新消息,和任何 |
| 225 | 普通新用户消息一样会改变新增后缀,但此前的稳定前缀仍可复用。 |
| 226 | |
| 227 | ## 客户端接入检查清单 |
| 228 | |
| 229 | 1. 启动 `reasonix acp`,分离 stdin、stdout 和 stderr。 |
| 230 | 2. 调用 `initialize`,同时遵守标准 capability 和 `_meta` capability。 |
| 231 | 3. 使用绝对工作区路径打开会话,并隔离保存各 session id。 |
| 232 | 4. Prompt 运行期间继续处理 agent 发往客户端的文件、terminal 和权限请求。 |
| 233 | 5. 只有在 Reasonix 声明 capability 且 prompt 活动时才显示 steer UI。 |
| 234 | 6. 把成功的 steer 响应理解为“引导已入队”,而不是“模型已立即完成处理”。 |
| 235 | 7. 用 `session/close` 释放资源;只有用户明确要删除持久化历史时才调用 |
| 236 | `session/delete`。 |
| 237 |