| 1 | # ACP editor integration |
| 2 | |
| 3 | <a href="../README.md">README</a> |
| 4 | · |
| 5 | <a href="./ACP.zh-CN.md">简体中文</a> |
| 6 | · |
| 7 | <a href="./GUIDE.md">Guide</a> |
| 8 | · |
| 9 | <a href="https://agentclientprotocol.com/">ACP specification</a> |
| 10 | |
| 11 | Reasonix implements Agent Client Protocol (ACP) v1 as an NDJSON JSON-RPC 2.0 |
| 12 | agent over standard input and output. Editors and other ACP hosts launch the |
| 13 | process, open one or more workspace-scoped sessions, and receive streamed |
| 14 | messages, tool activity, plans, permission requests, and configuration updates. |
| 15 | |
| 16 | ## Start the agent |
| 17 | |
| 18 | An ACP host should launch one of these commands: |
| 19 | |
| 20 | ```sh |
| 21 | reasonix acp |
| 22 | reasonix acp --model deepseek-pro |
| 23 | reasonix acp --profile delivery |
| 24 | ``` |
| 25 | |
| 26 | `--model` selects the startup model when the client does not override it. |
| 27 | `--profile` sets the startup work mode to `economy`, `balanced`, or `delivery`. |
| 28 | Both remain session-configurable after initialization. |
| 29 | |
| 30 | Standard output is reserved for ACP messages. Reasonix sends diagnostics to |
| 31 | standard error, so hosts must not merge the two streams. Run `reasonix setup` |
| 32 | beforehand when no provider is configured; the initialize response also |
| 33 | advertises a terminal authentication method that launches `reasonix setup`. |
| 34 | |
| 35 | ## Initialize and negotiate capabilities |
| 36 | |
| 37 | Clients should call `initialize` before opening a session. Reasonix advertises |
| 38 | the following capability shape (irrelevant fields omitted): |
| 39 | |
| 40 | ```json |
| 41 | { |
| 42 | "protocolVersion": 1, |
| 43 | "agentCapabilities": { |
| 44 | "loadSession": true, |
| 45 | "sessionCapabilities": { |
| 46 | "list": {}, |
| 47 | "resume": {}, |
| 48 | "close": {}, |
| 49 | "delete": {} |
| 50 | }, |
| 51 | "promptCapabilities": { |
| 52 | "image": false, |
| 53 | "audio": false, |
| 54 | "embeddedContext": true |
| 55 | }, |
| 56 | "mcpCapabilities": { |
| 57 | "http": true, |
| 58 | "sse": false |
| 59 | }, |
| 60 | "_meta": { |
| 61 | "reasonix.io": { |
| 62 | "sessionSteer": { |
| 63 | "method": "_reasonix.io/session/steer" |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | ``` |
| 70 | |
| 71 | When the client advertises `fs.readTextFile`, `fs.writeTextFile`, or |
| 72 | `terminal`, Reasonix routes eligible file operations through the editor's |
| 73 | unsaved buffers and eligible foreground commands through a client-owned |
| 74 | terminal. Without those client capabilities, the normal workspace tools run |
| 75 | locally inside the Reasonix process. |
| 76 | |
| 77 | ## Session lifecycle |
| 78 | |
| 79 | Each ACP session owns an independent Reasonix controller, workspace root, model, |
| 80 | work mode, collaboration mode, approval mode, MCP set, and persisted transcript. |
| 81 | State does not leak between sessions. |
| 82 | |
| 83 | | Method | Behavior | |
| 84 | | --- | --- | |
| 85 | | `session/new` | Opens a session for an absolute `cwd` and returns its configuration state. | |
| 86 | | `session/load` | Opens a persisted ACP session and replays its transcript as `session/update` notifications. | |
| 87 | | `session/resume` | Opens a persisted session without replaying the transcript. | |
| 88 | | `session/prompt` | Runs one turn and streams updates until it returns a stop reason. | |
| 89 | | `session/cancel` | Cancels the active turn; this is a notification. | |
| 90 | | `session/list` | Lists live and persisted ACP sessions, optionally filtered by absolute `cwd`. | |
| 91 | | `session/close` | Stops a live session and releases resources without deleting history. | |
| 92 | | `session/delete` | Stops the session and removes its persisted ACP history. | |
| 93 | |
| 94 | `session/new`, `session/load`, and `session/resume` may include `mcpServers`. |
| 95 | Reasonix accepts stdio, Streamable HTTP, and legacy SSE servers. ACP's official `[{"name":"...","value":"..."}]` |
| 96 | shape is supported for stdio `env` and HTTP `headers`; the older object-map |
| 97 | shape remains accepted for compatibility. |
| 98 | |
| 99 | ## Session controls |
| 100 | |
| 101 | Reasonix exposes independent controls instead of combining unrelated choices in |
| 102 | one mode selector: |
| 103 | |
| 104 | | Control | Values | Wire surface | |
| 105 | | --- | --- | --- | |
| 106 | | Collaboration mode | `normal`, `plan`, `goal` | `modes` and `session/set_mode` | |
| 107 | | Model | Configured `provider/model` entries | `configOptions` with id `model` | |
| 108 | | Reasoning effort | Provider-supported levels or `auto` | `configOptions` with id `effort` | |
| 109 | | Work mode | `economy`, `balanced`, `delivery` | `configOptions` with id `work_mode` | |
| 110 | | Tool approval | `ask`, `auto`, `yolo` | `configOptions` with id `tool_approval` | |
| 111 | |
| 112 | Use `session/set_config_option` for model, effort, work mode, and tool approval. |
| 113 | Its parameters are `sessionId`, `configId` and `value`, where `configId` is the |
| 114 | `id` of the option as advertised in `configOptions`: |
| 115 | |
| 116 | ```json |
| 117 | { |
| 118 | "jsonrpc": "2.0", |
| 119 | "id": 3, |
| 120 | "method": "session/set_config_option", |
| 121 | "params": { |
| 122 | "sessionId": "session-id", |
| 123 | "configId": "tool_approval", |
| 124 | "value": "yolo" |
| 125 | } |
| 126 | } |
| 127 | ``` |
| 128 | |
| 129 | Note that the field is `configId`, not `optionId`. The result is the full |
| 130 | refreshed `configOptions` array. An unknown id returns `-32602 InvalidParams`. |
| 131 | |
| 132 | Model, effort, and work-mode changes rebuild the session controller while |
| 133 | preserving its history and the other axes. Tool-approval changes update the |
| 134 | gate without rebuilding the controller. |
| 135 | |
| 136 | For older clients, `session/set_model` remains available. The legacy |
| 137 | `session/set_mode` values `default` and `auto` are also accepted as Normal + Ask |
| 138 | and Normal + Yolo respectively; new clients should use the independent |
| 139 | selectors above. |
| 140 | |
| 141 | ## Prompts, updates, and approvals |
| 142 | |
| 143 | `session/prompt` accepts text blocks and embedded text resources. Images and |
| 144 | audio are not advertised. During a turn, Reasonix may send: |
| 145 | |
| 146 | - agent message and thought chunks; |
| 147 | - pending and completed tool-call updates; |
| 148 | - complete plan updates derived from `todo_write`; |
| 149 | - available slash commands; |
| 150 | - current-mode and configuration-option updates; and |
| 151 | - `session/request_permission` requests for permission-gated tools and user |
| 152 | questions. |
| 153 | |
| 154 | Hosts should keep the `session/prompt` request open until Reasonix returns its |
| 155 | stop reason, while continuing to process requests and notifications in both |
| 156 | directions. |
| 157 | |
| 158 | ## Mid-turn steering extension |
| 159 | |
| 160 | Reasonix exposes mid-turn guidance as an ACP v1 vendor extension. It is not a |
| 161 | core ACP method, and it is not the still-unreleased ACP v2 `session/inject` |
| 162 | proposal. |
| 163 | |
| 164 | ### Discover support |
| 165 | |
| 166 | Read the method name from: |
| 167 | |
| 168 | ```text |
| 169 | agentCapabilities._meta["reasonix.io"].sessionSteer.method |
| 170 | ``` |
| 171 | |
| 172 | Do not assume the extension exists, and do not call the unnamespaced |
| 173 | `session/steer` name. ACP reserves non-underscore method names for the core |
| 174 | protocol. |
| 175 | |
| 176 | ### Send guidance |
| 177 | |
| 178 | Call the advertised method while `session/prompt` is active: |
| 179 | |
| 180 | ```json |
| 181 | { |
| 182 | "jsonrpc": "2.0", |
| 183 | "id": 2, |
| 184 | "method": "_reasonix.io/session/steer", |
| 185 | "params": { |
| 186 | "sessionId": "session-id", |
| 187 | "prompt": [ |
| 188 | {"type": "text", "text": "use email instead of username"} |
| 189 | ] |
| 190 | } |
| 191 | } |
| 192 | ``` |
| 193 | |
| 194 | A successful `{}` result means the active turn accepted the guidance. Reasonix |
| 195 | adds it as a user message before the next safe model-call boundary, without |
| 196 | cancelling the turn or consuming an extra tool-step budget. The message is |
| 197 | persisted in normal history; transcript replay shows the original user text, |
| 198 | not Reasonix's internal steer marker. |
| 199 | |
| 200 | | Condition | JSON-RPC result | |
| 201 | | --- | --- | |
| 202 | | Active prompt accepted the guidance | `{}` | |
| 203 | | Unknown session or empty prompt | `-32602 InvalidParams` | |
| 204 | | Session has no active prompt | `-32600 InvalidRequest` | |
| 205 | | Client calls `session/steer` | `-32601 MethodNotFound` | |
| 206 | |
| 207 | On `InvalidRequest`, the guidance was not queued. A client may wait for the |
| 208 | active prompt to finish and offer the text as a normal new prompt, but it should |
| 209 | not silently report the failed steer as accepted. |
| 210 | |
| 211 | ## Runtime reload and extension surface |
| 212 | |
| 213 | Reasonix advertises two more extension points in |
| 214 | `agentCapabilities._meta["reasonix.io"]`: |
| 215 | |
| 216 | - `sessionReloadExtensions` — the vendor method |
| 217 | `_reasonix.io/session/reloadExtensions`. Calling it reloads the session's |
| 218 | agent runtime (extensions, tools, skills, commands, hooks, providers) with |
| 219 | the same fail-atomic semantics as the CLI `/reload` command: while a turn |
| 220 | or rebuild is active exactly one reload is queued (`{"queued": true}`) and |
| 221 | runs when the session goes idle; otherwise the runtime is rebuilt and |
| 222 | swapped atomically, and a failed rebuild keeps the previous runtime. After |
| 223 | a successful reload Reasonix pushes a fresh `available_commands_update`. |
| 224 | - `extensionSurface` — structured extension UI support. Clients that also |
| 225 | advertise `reasonix.io.extensionSurface` in their initialize `_meta` |
| 226 | receive structured extension surface payloads; clients without it receive |
| 227 | equivalent text fallbacks (`agent_message_chunk` for cards and statuses, |
| 228 | permission requests for extension forms), so no client-side handling is |
| 229 | required to stay compatible. |
| 230 | |
| 231 | Extension actions declared by installed plugins are exposed as |
| 232 | `/<plugin>:<action>` in `available_commands_update` and can be invoked like |
| 233 | any other slash command. |
| 234 | |
| 235 | ## Compatibility and cache behavior |
| 236 | |
| 237 | | Surface | Older or non-Reasonix clients | Conclusion | |
| 238 | | --- | --- | --- | |
| 239 | | Existing ACP v1 methods | Their names and response shapes are unchanged. | Compatible | |
| 240 | | Capability `_meta` | Unknown metadata may be ignored. | Compatible | |
| 241 | | Persisted transcripts | No new persisted schema is required. | Compatible | |
| 242 | | CLI, Desktop, and Bot steering | Their existing idle fallback remains unchanged. | Compatible | |
| 243 | |
| 244 | Steering appends a user-requested message to normal conversation history. It |
| 245 | does not change the system prompt, tool schemas, tool order, or other stable |
| 246 | provider-prefix bytes. The next provider request necessarily misses the suffix |
| 247 | that did not previously exist, just like any normal new user message, while the |
| 248 | earlier prefix remains reusable. |
| 249 | |
| 250 | ## Client integration checklist |
| 251 | |
| 252 | 1. Launch `reasonix acp` with separate stdin, stdout, and stderr streams. |
| 253 | 2. Call `initialize` and honor both standard and `_meta` capabilities. |
| 254 | 3. Open sessions with absolute workspace paths and keep their ids isolated. |
| 255 | 4. Process agent-to-client filesystem, terminal, and permission requests while |
| 256 | a prompt is running. |
| 257 | 5. Show steer UI only when the Reasonix capability is advertised and a prompt |
| 258 | is active. |
| 259 | 6. Treat a successful steer response as queued guidance, not immediate model |
| 260 | completion. |
| 261 | 7. Use `session/close` for resource cleanup and `session/delete` only when the |
| 262 | user intends to remove persisted history. |
| 263 |