zcode-acp-server 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -15
- package/README.zh-CN.md +68 -13
- package/dist/bin/hub.d.ts +16 -0
- package/dist/bin/hub.d.ts.map +1 -0
- package/dist/bin/hub.js +41 -0
- package/dist/bin/hub.js.map +1 -0
- package/dist/handlers/account.d.ts +43 -0
- package/dist/handlers/account.d.ts.map +1 -0
- package/dist/handlers/account.js +59 -0
- package/dist/handlers/account.js.map +1 -0
- package/dist/handlers/io.d.ts +20 -1
- package/dist/handlers/io.d.ts.map +1 -1
- package/dist/handlers/io.js +57 -2
- package/dist/handlers/io.js.map +1 -1
- package/dist/handlers/replay.d.ts +79 -0
- package/dist/handlers/replay.d.ts.map +1 -0
- package/dist/handlers/replay.js +252 -0
- package/dist/handlers/replay.js.map +1 -0
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +64 -65
- package/dist/handlers/session.js.map +1 -1
- package/dist/handlers/slash.d.ts +23 -1
- package/dist/handlers/slash.d.ts.map +1 -1
- package/dist/handlers/slash.js +67 -6
- package/dist/handlers/slash.js.map +1 -1
- package/dist/index.js +47 -17
- package/dist/index.js.map +1 -1
- package/dist/remote/broadcast.d.ts +47 -0
- package/dist/remote/broadcast.d.ts.map +1 -0
- package/dist/remote/broadcast.js +121 -0
- package/dist/remote/broadcast.js.map +1 -0
- package/dist/remote/config.d.ts +32 -0
- package/dist/remote/config.d.ts.map +1 -0
- package/dist/remote/config.js +65 -0
- package/dist/remote/config.js.map +1 -0
- package/dist/remote/endpoint.d.ts +30 -0
- package/dist/remote/endpoint.d.ts.map +1 -0
- package/dist/remote/endpoint.js +213 -0
- package/dist/remote/endpoint.js.map +1 -0
- package/dist/remote/hub-server.d.ts +41 -0
- package/dist/remote/hub-server.d.ts.map +1 -0
- package/dist/remote/hub-server.js +346 -0
- package/dist/remote/hub-server.js.map +1 -0
- package/dist/server.d.ts +41 -7
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +69 -11
- package/dist/server.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +17 -1
- package/dist/utils.js.map +1 -1
- package/docs/ARCHITECTURE.md +47 -15
- package/docs/BACKLOG.md +3 -1
- package/docs/DEVELOPMENT.md +26 -0
- package/docs/PROTOCOL.md +67 -27
- package/docs/REMOTE-CLIENTS.md +260 -0
- package/docs/REPLAY-GUIDE.md +131 -0
- package/docs/TROUBLESHOOTING.md +39 -6
- package/docs/adr/0001-bridge-lifetime-follows-primary-client.md +14 -0
- package/docs/adr/0002-stateless-hub-over-per-bridge-acp-endpoints.md +23 -0
- package/docs/adr/0003-tail-replay-meta-and-cursor-pagination.md +40 -0
- package/docs/proposals/0001-tail-session-replay.md +136 -0
- package/docs/proposals/0002-plan-quota-usage.md +81 -0
- package/package.json +5 -2
package/dist/utils.js
CHANGED
|
@@ -3,15 +3,31 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Logging goes to stderr so it never corrupts the stdout ACP protocol stream.
|
|
5
5
|
*/
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import process from "node:process";
|
|
8
9
|
/** ACP protocol version this server speaks. */
|
|
9
10
|
export const PROTOCOL_VERSION = 1;
|
|
11
|
+
/**
|
|
12
|
+
* Package version, read once from package.json. Kept in sync with releases by
|
|
13
|
+
* construction (the hardcoded constant used to drift from package.json); the
|
|
14
|
+
* hub-vs-bridge version handshake in remote/ relies on it changing per
|
|
15
|
+
* release.
|
|
16
|
+
*/
|
|
17
|
+
const PACKAGE_VERSION = (() => {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
20
|
+
return pkg.version ?? "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "0.0.0";
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
10
26
|
/** Agent identity advertised in the initialize response. */
|
|
11
27
|
export const AGENT_INFO = {
|
|
12
28
|
name: "zcode-acp-server",
|
|
13
29
|
title: "ZCode",
|
|
14
|
-
version:
|
|
30
|
+
version: PACKAGE_VERSION,
|
|
15
31
|
};
|
|
16
32
|
/** Path to the ZCode v2 config (credentials + provider/model metadata). */
|
|
17
33
|
export const ZCODE_CREDS_PATH = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".zcode", "v2", "config.json");
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -14,7 +14,7 @@ application-client (Zed / JetBrains)
|
|
|
14
14
|
|
|
|
15
15
|
v
|
|
16
16
|
zcode-acp-server (stdio JSON-RPC ACP)
|
|
17
|
-
|-- handlers/ session, extensions, dispatch, server-requests, io, slash
|
|
17
|
+
|-- handlers/ session, extensions, dispatch, server-requests, io, slash, account
|
|
18
18
|
|-- translators/ event-translator, projection-differ, tool-helpers
|
|
19
19
|
|-- interaction/ adapter
|
|
20
20
|
|-- config/ options, runtime-model, model-cache
|
|
@@ -142,12 +142,12 @@ local relay: prompts, code, and tool outputs pass through process memory on
|
|
|
142
142
|
their way between the editor and the ZCode subprocess, but reach the GLM cloud
|
|
143
143
|
API only because the ZCode backend itself sends them for inference.
|
|
144
144
|
|
|
145
|
-
| Concern
|
|
146
|
-
|
|
|
147
|
-
| Network
|
|
148
|
-
| Credentials | API key from `~/.zcode/v2/config.json` (authenticates the subprocess + quota request), never logged. OAuth handled by the ZCode subprocess, not this server
|
|
149
|
-
| Disk
|
|
150
|
-
| Logging
|
|
145
|
+
| Concern | Detail |
|
|
146
|
+
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| Network | One outbound request in the whole codebase — `src/quota/client.ts` GET to the quota API, Bearer token only, no body |
|
|
148
|
+
| Credentials | API key from `~/.zcode/v2/config.json` (authenticates the subprocess + quota request), never logged. OAuth handled by the ZCode subprocess, not this server |
|
|
149
|
+
| Disk | No new files. Writes only to the existing `~/.zcode/v2/tasks-index.sqlite` — syncs sessions into the ZCode app's history & search (session title + first prompt) |
|
|
150
|
+
| Logging | `log()`/`warn()` → stderr only for troubleshooting; even with `ZCODE_ACP_DEBUG=1`, no prompts/code/keys are logged |
|
|
151
151
|
|
|
152
152
|
## Module Responsibilities
|
|
153
153
|
|
|
@@ -170,15 +170,16 @@ API only because the ZCode backend itself sends them for inference.
|
|
|
170
170
|
|
|
171
171
|
### `handlers/` — ACP method handling
|
|
172
172
|
|
|
173
|
-
| File
|
|
174
|
-
|
|
|
175
|
-
| `session.ts`
|
|
176
|
-
| `extensions.ts`
|
|
177
|
-
| `dispatch.ts`
|
|
173
|
+
| File | Responsibility |
|
|
174
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| `session.ts` | session/new/list/resume/load/prompt/set_config_option/cancel |
|
|
176
|
+
| `extensions.ts` | fork/rewind/rewindCascade/goal/compact/steer/cancelBackgroundTask/setModel/setMode/setThoughtLevel |
|
|
177
|
+
| `dispatch.ts` | dispatchEvent single exit point: InternalEvent → ACP session/update |
|
|
178
178
|
| `background-tasks.ts` | Session-scoped `BackgroundTaskListener` — forwards background sub-agent status (`session.updated` taskId) + completion-notification turns to the client OUTSIDE request handlers (lives across prompts) |
|
|
179
|
-
| `server-requests.ts`
|
|
180
|
-
| `io.ts`
|
|
181
|
-
| `slash.ts`
|
|
179
|
+
| `server-requests.ts` | Handle zcode interaction/* requests (tool auth, ExitPlanMode, AskUserQuestion), protocol negotiation routing |
|
|
180
|
+
| `io.ts` | ACP notification helpers (including `sendAvailableCommandsDeferred` deferred notification) |
|
|
181
|
+
| `slash.ts` | Interception of `/`-prefixed commands (/compact /goal /fork /rewind /steer /model /mode /thought); non-advertised `/x` prompts are neutralized into plain text (`neutralizeSlashText`) |
|
|
182
|
+
| `account.ts` | `account/usage_stats` — account-level plan quota for remote clients (Proposal 0002; quota pipeline + graceful error) |
|
|
182
183
|
|
|
183
184
|
### `interaction/` — Interaction bridging
|
|
184
185
|
|
|
@@ -194,6 +195,37 @@ API only because the ZCode backend itself sends them for inference.
|
|
|
194
195
|
| `runtime-model.ts` | runtimeModel overlay construction and application |
|
|
195
196
|
| `model-cache.ts` | Model ID cache and usage initialization |
|
|
196
197
|
|
|
198
|
+
### `remote/` — Remote access (opt-in via `ZCODE_ACP_REMOTE=1`)
|
|
199
|
+
|
|
200
|
+
| File | Responsibility |
|
|
201
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
202
|
+
| `broadcast.ts` | ClientRegistry + broadcast proxy: notify fans out to all clients; request is first-response-wins with loser `$/cancel_request` |
|
|
203
|
+
| `config.ts` | ENV parsing (gate, mandatory token, hub/bridge ports) |
|
|
204
|
+
| `endpoint.ts` | Loopback ACP endpoint (SDK AcpServer transport, port auto-increment) + hub registration/heartbeat |
|
|
205
|
+
| `hub-server.ts` | The hub singleton: token auth, instance discovery, byte-level WS proxying, heartbeat pruning, on-demand `?probe=1` liveness, idle exit, version self-upgrade |
|
|
206
|
+
|
|
207
|
+
When enabled, the same `AgentApp` serves the stdio editor and a loopback
|
|
208
|
+
WebSocket endpoint. Every connection (editor or remote) joins the broadcast
|
|
209
|
+
registry via `trackConnections`, so one turn's notifications reach all clients
|
|
210
|
+
regardless of who prompted. The bridge registers itself with the machine-level
|
|
211
|
+
`zcode-acp-hub` (`bin/hub.ts`), which is the only public entry point and holds
|
|
212
|
+
no session state (see `docs/adr/0002`). The bridge's lifetime still follows the
|
|
213
|
+
stdio client (ADR-0001); the listener is `unref()`'d so remote clients alone
|
|
214
|
+
never keep the process alive.
|
|
215
|
+
|
|
216
|
+
Hub upgrades are self-managing: each heartbeat carries the bridge's package
|
|
217
|
+
version, and a hub that sees a NEWER bridge replies `{ok, restarting}`, exits,
|
|
218
|
+
and is re-spawned by that bridge from its own (upgraded) `dist/` within a few
|
|
219
|
+
seconds. Equal, older, or absent versions never trigger a restart — downgrades
|
|
220
|
+
and mixed-version fleets are fine. Without this handshake a long-lived hub
|
|
221
|
+
would keep running pre-upgrade code until its 10-minute idle exit.
|
|
222
|
+
|
|
223
|
+
Discovery liveness has two layers: the heartbeat TTL (30s, pruned every 5s)
|
|
224
|
+
drops bridges that stopped registering — the fallback for hard kills — and
|
|
225
|
+
`GET /api/instances?probe=1` actively TCP-probes each registered loopback port
|
|
226
|
+
on demand, so a client refresh gets an immediately-honest list with no
|
|
227
|
+
background probing cost.
|
|
228
|
+
|
|
197
229
|
## Key State Machines
|
|
198
230
|
|
|
199
231
|
### Turn state
|
package/docs/BACKLOG.md
CHANGED
|
@@ -33,7 +33,9 @@ have no ACP equivalent. Listed for completeness only — the bridge does not
|
|
|
33
33
|
intend to surface them.
|
|
34
34
|
|
|
35
35
|
`automation/create`, `automation/list`, `automation/delete` (scheduled tasks),
|
|
36
|
-
`usage/stats
|
|
36
|
+
`usage/stats` (token analytics; the account-level plan quota it does NOT cover
|
|
37
|
+
is exposed via the bridge's own `account/usage_stats` — see Proposal 0002),
|
|
38
|
+
`workspace/readState`, `workspace/upsertModelProvider`,
|
|
37
39
|
`workspace/removeModelProvider`, `workspace/updateProviderRegistry`,
|
|
38
40
|
`workspace/setDefaultModel`, `workspace/setDefaultThoughtLevel`,
|
|
39
41
|
`workspace/setDefaultMode`, `workspace/generateText`, `mcp/list`,
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -90,6 +90,32 @@ describe("backend", () => {
|
|
|
90
90
|
});
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
### Method 4: Remote access (hub + endpoint)
|
|
94
|
+
|
|
95
|
+
Run the bridge with the remote env vars on scratch ports (keep stdin open —
|
|
96
|
+
the bridge exits when its stdio client disconnects):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
ZCODE_ACP_REMOTE=1 \
|
|
100
|
+
ZCODE_ACP_REMOTE_TOKEN=dev-token \
|
|
101
|
+
ZCODE_ACP_HUB_PORT=18377 \
|
|
102
|
+
ZCODE_ACP_REMOTE_PORT=18378 \
|
|
103
|
+
ZCODE_ACP_DEBUG=1 \
|
|
104
|
+
tail -f /dev/null | node dist/index.js
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The first bridge spawns `dist/bin/hub.js` on its own. Verify discovery:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
curl -H "Authorization: Bearer dev-token" http://127.0.0.1:18377/api/instances
|
|
111
|
+
curl -H "Authorization: Bearer dev-token" "http://127.0.0.1:18377/api/instances?probe=1"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Integration tests live in `tests/hub.test.ts` (hub API, probe, proxy, idle
|
|
115
|
+
exit) and `tests/remote-endpoint.test.ts` (registration + end-to-end proxied
|
|
116
|
+
initialize). The remote features are opt-in and self-contained — remote
|
|
117
|
+
failures warn and disable remote only, so stdio testing works without them.
|
|
118
|
+
|
|
93
119
|
## Debugging Tips
|
|
94
120
|
|
|
95
121
|
### Enable verbose logging
|
package/docs/PROTOCOL.md
CHANGED
|
@@ -12,11 +12,11 @@ resembles JSON-RPC, but **does not include the `jsonrpc` field**.
|
|
|
12
12
|
|
|
13
13
|
Messages are classified by the presence of `id` and `method`:
|
|
14
14
|
|
|
15
|
-
| Combination
|
|
16
|
-
|
|
17
|
-
| `id` + no `method` | Response
|
|
18
|
-
| `id` + `method`
|
|
19
|
-
| `method` + no `id` | Notification | bidirectional
|
|
15
|
+
| Combination | Type | Direction |
|
|
16
|
+
| ------------------ | ------------ | ---------------------------------- |
|
|
17
|
+
| `id` + no `method` | Response | zcode -> bridge |
|
|
18
|
+
| `id` + `method` | Request | bridge -> zcode or zcode -> bridge |
|
|
19
|
+
| `method` + no `id` | Notification | bidirectional |
|
|
20
20
|
|
|
21
21
|
### Request format
|
|
22
22
|
|
|
@@ -84,6 +84,7 @@ the backend session (this RPC) on the first prompt / config change / extension
|
|
|
84
84
|
method, so an editor startup that never sends a message leaves no session.
|
|
85
85
|
|
|
86
86
|
**Request:**
|
|
87
|
+
|
|
87
88
|
```json
|
|
88
89
|
{
|
|
89
90
|
"id": 1,
|
|
@@ -99,6 +100,7 @@ method, so an editor startup that never sends a message leaves no session.
|
|
|
99
100
|
```
|
|
100
101
|
|
|
101
102
|
**Response:**
|
|
103
|
+
|
|
102
104
|
```json
|
|
103
105
|
{
|
|
104
106
|
"id": 1,
|
|
@@ -117,6 +119,7 @@ method, so an editor startup that never sends a message leaves no session.
|
|
|
117
119
|
List all sessions.
|
|
118
120
|
|
|
119
121
|
**Request:**
|
|
122
|
+
|
|
120
123
|
```json
|
|
121
124
|
{
|
|
122
125
|
"id": 2,
|
|
@@ -142,6 +145,7 @@ empty one if the placeholder was never used. Real ids from `session/list` pass
|
|
|
142
145
|
through unchanged.
|
|
143
146
|
|
|
144
147
|
**Request:**
|
|
148
|
+
|
|
145
149
|
```json
|
|
146
150
|
{
|
|
147
151
|
"id": 3,
|
|
@@ -161,6 +165,7 @@ through unchanged.
|
|
|
161
165
|
Send a prompt.
|
|
162
166
|
|
|
163
167
|
**Request:**
|
|
168
|
+
|
|
164
169
|
```json
|
|
165
170
|
{
|
|
166
171
|
"id": 4,
|
|
@@ -173,6 +178,7 @@ Send a prompt.
|
|
|
173
178
|
```
|
|
174
179
|
|
|
175
180
|
**Response:**
|
|
181
|
+
|
|
176
182
|
```json
|
|
177
183
|
{
|
|
178
184
|
"id": 4,
|
|
@@ -200,6 +206,7 @@ Stop the current turn (fire-and-forget).
|
|
|
200
206
|
Read the session state and projection.
|
|
201
207
|
|
|
202
208
|
**Request:**
|
|
209
|
+
|
|
203
210
|
```json
|
|
204
211
|
{
|
|
205
212
|
"id": 5,
|
|
@@ -211,6 +218,7 @@ Read the session state and projection.
|
|
|
211
218
|
```
|
|
212
219
|
|
|
213
220
|
**Response:**
|
|
221
|
+
|
|
214
222
|
```json
|
|
215
223
|
{
|
|
216
224
|
"id": 5,
|
|
@@ -226,9 +234,7 @@ Read the session state and projection.
|
|
|
226
234
|
"model": { "current": { "modelId": "GLM-5.2" } },
|
|
227
235
|
"thoughtLevel": { "current": "high" }
|
|
228
236
|
},
|
|
229
|
-
"todos": [
|
|
230
|
-
{ "content": "Implement login", "status": "pending", "priority": "high" }
|
|
231
|
-
]
|
|
237
|
+
"todos": [{ "content": "Implement login", "status": "pending", "priority": "high" }]
|
|
232
238
|
}
|
|
233
239
|
}
|
|
234
240
|
```
|
|
@@ -238,6 +244,7 @@ Read the session state and projection.
|
|
|
238
244
|
Fetch the session's historical messages.
|
|
239
245
|
|
|
240
246
|
**Request:**
|
|
247
|
+
|
|
241
248
|
```json
|
|
242
249
|
{
|
|
243
250
|
"id": 6,
|
|
@@ -255,6 +262,7 @@ Fetch the session's historical messages.
|
|
|
255
262
|
Subscribe to a session's event push.
|
|
256
263
|
|
|
257
264
|
**Request:**
|
|
265
|
+
|
|
258
266
|
```json
|
|
259
267
|
{
|
|
260
268
|
"id": 7,
|
|
@@ -269,6 +277,7 @@ Subscribe to a session's event push.
|
|
|
269
277
|
```
|
|
270
278
|
|
|
271
279
|
**Response:**
|
|
280
|
+
|
|
272
281
|
```json
|
|
273
282
|
{
|
|
274
283
|
"id": 7,
|
|
@@ -322,6 +331,7 @@ Model streaming output.
|
|
|
322
331
|
```
|
|
323
332
|
|
|
324
333
|
`kind` can be:
|
|
334
|
+
|
|
325
335
|
- `text_delta`: text delta
|
|
326
336
|
- `reasoning_delta`: reasoning text delta
|
|
327
337
|
- `tool_call`: tool call declaration (caches toolName and input)
|
|
@@ -348,6 +358,7 @@ Tool status update.
|
|
|
348
358
|
```
|
|
349
359
|
|
|
350
360
|
`kind` can be:
|
|
361
|
+
|
|
351
362
|
- `scheduled`: tool scheduled
|
|
352
363
|
- `started`: tool started executing
|
|
353
364
|
- `progress`: progress update (stdoutTail / stderrTail)
|
|
@@ -497,6 +508,7 @@ Tool permission request.
|
|
|
497
508
|
User input request (ExitPlanMode / AskUserQuestion).
|
|
498
509
|
|
|
499
510
|
**ExitPlanMode:**
|
|
511
|
+
|
|
500
512
|
```json
|
|
501
513
|
{
|
|
502
514
|
"id": 101,
|
|
@@ -512,6 +524,7 @@ User input request (ExitPlanMode / AskUserQuestion).
|
|
|
512
524
|
```
|
|
513
525
|
|
|
514
526
|
**AskUserQuestion:**
|
|
527
|
+
|
|
515
528
|
```json
|
|
516
529
|
{
|
|
517
530
|
"id": 102,
|
|
@@ -539,17 +552,18 @@ User input request (ExitPlanMode / AskUserQuestion).
|
|
|
539
552
|
ZCode `interaction/*` requests are routed to different ACP interaction
|
|
540
553
|
mechanisms based on client capabilities:
|
|
541
554
|
|
|
542
|
-
| Request type
|
|
543
|
-
|
|
544
|
-
| Tool auth (`interaction/requestPermission`)
|
|
545
|
-
| ExitPlanMode (`interaction/requestUserInput` + plan_approval) | `elicitation/create` (approve/reject form) |
|
|
546
|
-
| AskUserQuestion (`interaction/requestUserInput`)
|
|
555
|
+
| Request type | Client supports elicitation.form | Client does not |
|
|
556
|
+
| ------------------------------------------------------------- | :----------------------------------------: | :---------------------------------------: |
|
|
557
|
+
| Tool auth (`interaction/requestPermission`) | `session/request_permission` | `session/request_permission` |
|
|
558
|
+
| ExitPlanMode (`interaction/requestUserInput` + plan_approval) | `elicitation/create` (approve/reject form) | `session/request_permission` |
|
|
559
|
+
| AskUserQuestion (`interaction/requestUserInput`) | `elicitation/create` (single form) | per-question `session/request_permission` |
|
|
547
560
|
|
|
548
561
|
**Capability detection**: at `initialize` time the client declares support via
|
|
549
562
|
`clientCapabilities.elicitation.form`. The server detects it with
|
|
550
563
|
`server.supportsElicitationForm()`.
|
|
551
564
|
|
|
552
565
|
**elicitation form example** (AskUserQuestion):
|
|
566
|
+
|
|
553
567
|
```json
|
|
554
568
|
{
|
|
555
569
|
"method": "elicitation/create",
|
|
@@ -591,6 +605,7 @@ overrides the dropdown (single-select) or is appended to the picked values
|
|
|
591
605
|
that question without cancelling the form.
|
|
592
606
|
|
|
593
607
|
**elicitation response** (accept/decline/cancel):
|
|
608
|
+
|
|
594
609
|
```json
|
|
595
610
|
{
|
|
596
611
|
"action": "accept",
|
|
@@ -604,6 +619,7 @@ typing into the field is the reject action. Submitting with the field empty
|
|
|
604
619
|
approves the plan; submitting with text rejects it and returns the text to
|
|
605
620
|
zcode as the decline `reason` (so the agent sees the redirection when it
|
|
606
621
|
re-plans). The cancel/decline button is a plain reject with no reason.
|
|
622
|
+
|
|
607
623
|
```json
|
|
608
624
|
{
|
|
609
625
|
"method": "elicitation/create",
|
|
@@ -633,6 +649,7 @@ re-plans). The cancel/decline button is a plain reject with no reason.
|
|
|
633
649
|
Fork a new session from a checkpoint.
|
|
634
650
|
|
|
635
651
|
**Request:**
|
|
652
|
+
|
|
636
653
|
```json
|
|
637
654
|
{
|
|
638
655
|
"id": 8,
|
|
@@ -649,6 +666,7 @@ Fork a new session from a checkpoint.
|
|
|
649
666
|
Rewind to a checkpoint.
|
|
650
667
|
|
|
651
668
|
**Request:**
|
|
669
|
+
|
|
652
670
|
```json
|
|
653
671
|
{
|
|
654
672
|
"id": 9,
|
|
@@ -666,6 +684,7 @@ Rewind to a checkpoint.
|
|
|
666
684
|
Read / set / replace / clear the goal.
|
|
667
685
|
|
|
668
686
|
**Request:**
|
|
687
|
+
|
|
669
688
|
```json
|
|
670
689
|
{
|
|
671
690
|
"id": 10,
|
|
@@ -685,6 +704,7 @@ Read / set / replace / clear the goal.
|
|
|
685
704
|
Compact the conversation history.
|
|
686
705
|
|
|
687
706
|
**Request:**
|
|
707
|
+
|
|
688
708
|
```json
|
|
689
709
|
{
|
|
690
710
|
"id": 11,
|
|
@@ -700,6 +720,7 @@ Compact the conversation history.
|
|
|
700
720
|
Append instructions to a running turn.
|
|
701
721
|
|
|
702
722
|
**Request:**
|
|
723
|
+
|
|
703
724
|
```json
|
|
704
725
|
{
|
|
705
726
|
"id": 12,
|
|
@@ -716,6 +737,7 @@ Append instructions to a running turn.
|
|
|
716
737
|
Switch the session mode.
|
|
717
738
|
|
|
718
739
|
**Request:**
|
|
740
|
+
|
|
719
741
|
```json
|
|
720
742
|
{
|
|
721
743
|
"id": 13,
|
|
@@ -732,6 +754,7 @@ Switch the session mode.
|
|
|
732
754
|
Set the thought level.
|
|
733
755
|
|
|
734
756
|
**Request:**
|
|
757
|
+
|
|
735
758
|
```json
|
|
736
759
|
{
|
|
737
760
|
"id": 14,
|
|
@@ -807,10 +830,10 @@ lifecycle on the same stream:
|
|
|
807
830
|
The bridge's session-scoped `BackgroundTaskListener` turns these into a
|
|
808
831
|
dedicated ACP tool card (`[background] <description>`) plus status updates:
|
|
809
832
|
|
|
810
|
-
| Backend event
|
|
811
|
-
|
|
833
|
+
| Backend event | ACP notification |
|
|
834
|
+
| ------------------------------------------ | -------------------------------------------------------------- |
|
|
812
835
|
| first `session.updated` (status `running`) | `tool_call` (new card, `kind:"other"`, `status:"in_progress"`) |
|
|
813
|
-
| `session.updated` (status `completed`)
|
|
836
|
+
| `session.updated` (status `completed`) | `tool_call_update` (`status:"completed"`) |
|
|
814
837
|
|
|
815
838
|
`session.updated` events WITHOUT a `taskId` (e.g. usage updates) are ignored by
|
|
816
839
|
the background listener — they remain owned by the turn loop.
|
|
@@ -883,11 +906,11 @@ The mechanism:
|
|
|
883
906
|
`toolCallId` is unknown to `terminalSentData` (sub-agent case), the listener
|
|
884
907
|
falls back to minting a fresh `bg_*` card — the Agent sub-agent path above.
|
|
885
908
|
|
|
886
|
-
| Backend event
|
|
887
|
-
|
|
888
|
-
| first `session.updated` (status `running`)
|
|
909
|
+
| Backend event | ACP notification (background Bash) |
|
|
910
|
+
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
911
|
+
| first `session.updated` (status `running`) | `tool_call_update` on the launch card (`status:"in_progress"`) |
|
|
889
912
|
| `session.updated` (status `completed`, with `outputTail`) | `terminal_output` (final output, if not already streamed) + `tool_call_update` with `terminal_exit` (`status:"completed"`) |
|
|
890
|
-
| `session.updated` (status `failed`)
|
|
913
|
+
| `session.updated` (status `failed`) | `tool_call_update` with `terminal_exit` (`status:"failed"`, exit_code 1) |
|
|
891
914
|
|
|
892
915
|
`session/cancelBackgroundTask` for a background Bash task additionally emits
|
|
893
916
|
`terminal_exit` with `_meta.backgroundTask.cancelled = true` so the terminal
|
|
@@ -898,13 +921,11 @@ UI closes on cancellation.
|
|
|
898
921
|
Cancels a background task. The bridge additionally marks the corresponding ACP
|
|
899
922
|
tool card as `failed` with `_meta.backgroundTask.cancelled = true`.
|
|
900
923
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
|
904
|
-
|
|
905
|
-
|
|
|
906
|
-
| >= 0.14.8 | Supported | Partially supported | workspace/* unavailable |
|
|
907
|
-
| 0.14.5 ~ 0.14.7 | Not supported | Not supported | Incompatible with this project |
|
|
924
|
+
| ZCode CLI version | session/subscribe | Extension methods | Notes |
|
|
925
|
+
| ----------------- | ----------------- | ------------------- | ------------------------------ |
|
|
926
|
+
| >= 0.15.0 | Supported | All supported | Full functionality |
|
|
927
|
+
| >= 0.14.8 | Supported | Partially supported | workspace/* unavailable |
|
|
928
|
+
| 0.14.5 ~ 0.14.7 | Not supported | Not supported | Incompatible with this project |
|
|
908
929
|
|
|
909
930
|
## Additional backend methods (not wired into the bridge)
|
|
910
931
|
|
|
@@ -912,3 +933,22 @@ The backend exposes more RPC methods than the bridge uses (sub-agent listing,
|
|
|
912
933
|
event pull, session usage/close, automation, workspace config, MCP/plugins).
|
|
913
934
|
These have no ACP-side counterpart yet. See [`BACKLOG.md`](./BACKLOG.md) for
|
|
914
935
|
the full list and which are candidates for future support.
|
|
936
|
+
|
|
937
|
+
## Multi-client semantics (remote access)
|
|
938
|
+
|
|
939
|
+
When `ZCODE_ACP_REMOTE=1` is enabled, the bridge accepts additional ACP clients
|
|
940
|
+
over WebSocket (via the machine-level hub) alongside the stdio editor. All
|
|
941
|
+
clients share the same backend sessions; the rules below define how one agent
|
|
942
|
+
serves many clients.
|
|
943
|
+
|
|
944
|
+
| Aspect | Behaviour |
|
|
945
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
946
|
+
| `session/update` notifications | Broadcast to every connected client. A client that never saw a session (e.g. an editor receiving a phone-created session) simply ignores the update. |
|
|
947
|
+
| `session/request_permission`, `elicitation/create` | Sent to every client; the **first response wins**. Losing requests are aborted, which emits `$/cancel_request` so the losing client dismisses its dialog and replies `RequestCancelled`. |
|
|
948
|
+
| Capabilities | OR-merged across clients at each `initialize` (booleans union, `_meta` shallow-merged). A capability any client declares is enabled for interaction routing. |
|
|
949
|
+
| Concurrent `session/prompt` on one session | Serialized by the per-session preempt lock — identical to the single-client case; a second client's prompt preempts or queues the same way. |
|
|
950
|
+
| `session/cancel` | Affects the shared turn regardless of which client sent it. |
|
|
951
|
+
| Process lifetime | Follows the stdio client: when the editor disconnects, the bridge (and every remote attachment) exits. Remote clients never extend the lifetime. |
|
|
952
|
+
|
|
953
|
+
Transport details (hub discovery API, token auth, tunnel notes) live in the
|
|
954
|
+
[Remote Access](../README.md#remote-access) section of the README.
|