zcode-acp-server 0.1.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 +192 -9
- package/README.zh-CN.md +143 -8
- package/dist/backend/client.d.ts +12 -4
- package/dist/backend/client.d.ts.map +1 -1
- package/dist/backend/client.js +72 -7
- package/dist/backend/client.js.map +1 -1
- package/dist/backend/credentials.d.ts.map +1 -1
- package/dist/backend/credentials.js +2 -1
- package/dist/backend/credentials.js.map +1 -1
- package/dist/backend/listener.d.ts +12 -4
- package/dist/backend/listener.d.ts.map +1 -1
- package/dist/backend/listener.js +68 -12
- package/dist/backend/listener.js.map +1 -1
- package/dist/backend/types.d.ts +1 -1
- package/dist/backend/types.d.ts.map +1 -1
- 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/bin/quota.d.ts +54 -0
- package/dist/bin/quota.d.ts.map +1 -0
- package/dist/bin/quota.js +333 -0
- package/dist/bin/quota.js.map +1 -0
- package/dist/config/auto-compact.d.ts +23 -0
- package/dist/config/auto-compact.d.ts.map +1 -0
- package/dist/config/auto-compact.js +67 -0
- package/dist/config/auto-compact.js.map +1 -0
- package/dist/config/mcp-discovery.d.ts +34 -0
- package/dist/config/mcp-discovery.d.ts.map +1 -0
- package/dist/config/mcp-discovery.js +153 -0
- package/dist/config/mcp-discovery.js.map +1 -0
- package/dist/config/model-cache.d.ts +12 -1
- package/dist/config/model-cache.d.ts.map +1 -1
- package/dist/config/model-cache.js +30 -8
- package/dist/config/model-cache.js.map +1 -1
- package/dist/config/options.d.ts +87 -9
- package/dist/config/options.d.ts.map +1 -1
- package/dist/config/options.js +248 -45
- package/dist/config/options.js.map +1 -1
- package/dist/config/plugin-commands.d.ts +24 -0
- package/dist/config/plugin-commands.d.ts.map +1 -0
- package/dist/config/plugin-commands.js +107 -0
- package/dist/config/plugin-commands.js.map +1 -0
- package/dist/config/provider-registry.d.ts +60 -0
- package/dist/config/provider-registry.d.ts.map +1 -0
- package/dist/config/provider-registry.js +128 -0
- package/dist/config/provider-registry.js.map +1 -0
- package/dist/config/runtime-model.d.ts +54 -18
- package/dist/config/runtime-model.d.ts.map +1 -1
- package/dist/config/runtime-model.js +105 -61
- package/dist/config/runtime-model.js.map +1 -1
- package/dist/config/skill-discovery.d.ts +35 -0
- package/dist/config/skill-discovery.d.ts.map +1 -0
- package/dist/config/skill-discovery.js +188 -0
- package/dist/config/skill-discovery.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/background-tasks.d.ts +72 -0
- package/dist/handlers/background-tasks.d.ts.map +1 -0
- package/dist/handlers/background-tasks.js +330 -0
- package/dist/handlers/background-tasks.js.map +1 -0
- package/dist/handlers/dispatch.d.ts.map +1 -1
- package/dist/handlers/dispatch.js +134 -22
- package/dist/handlers/dispatch.js.map +1 -1
- package/dist/handlers/extensions.d.ts +15 -0
- package/dist/handlers/extensions.d.ts.map +1 -1
- package/dist/handlers/extensions.js +43 -27
- package/dist/handlers/extensions.js.map +1 -1
- package/dist/handlers/io.d.ts +31 -3
- package/dist/handlers/io.d.ts.map +1 -1
- package/dist/handlers/io.js +108 -9
- 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/server-requests.d.ts +20 -4
- package/dist/handlers/server-requests.d.ts.map +1 -1
- package/dist/handlers/server-requests.js +293 -63
- package/dist/handlers/server-requests.js.map +1 -1
- package/dist/handlers/session.d.ts +113 -10
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +932 -277
- package/dist/handlers/session.js.map +1 -1
- package/dist/handlers/slash.d.ts +37 -1
- package/dist/handlers/slash.d.ts.map +1 -1
- package/dist/handlers/slash.js +143 -8
- package/dist/handlers/slash.js.map +1 -1
- package/dist/index.js +76 -12
- package/dist/index.js.map +1 -1
- package/dist/interaction/adapter.d.ts +19 -33
- package/dist/interaction/adapter.d.ts.map +1 -1
- package/dist/interaction/adapter.js +80 -79
- package/dist/interaction/adapter.js.map +1 -1
- package/dist/lazy-sessions.d.ts +35 -0
- package/dist/lazy-sessions.d.ts.map +1 -0
- package/dist/lazy-sessions.js +98 -0
- package/dist/lazy-sessions.js.map +1 -0
- package/dist/quota/cache.d.ts +18 -0
- package/dist/quota/cache.d.ts.map +1 -0
- package/dist/quota/cache.js +32 -0
- package/dist/quota/cache.js.map +1 -0
- package/dist/quota/client.d.ts +30 -0
- package/dist/quota/client.d.ts.map +1 -0
- package/dist/quota/client.js +57 -0
- package/dist/quota/client.js.map +1 -0
- package/dist/quota/color.d.ts +58 -0
- package/dist/quota/color.d.ts.map +1 -0
- package/dist/quota/color.js +95 -0
- package/dist/quota/color.js.map +1 -0
- package/dist/quota/combined.d.ts +66 -0
- package/dist/quota/combined.d.ts.map +1 -0
- package/dist/quota/combined.js +179 -0
- package/dist/quota/combined.js.map +1 -0
- package/dist/quota/format.d.ts +105 -0
- package/dist/quota/format.d.ts.map +1 -0
- package/dist/quota/format.js +219 -0
- package/dist/quota/format.js.map +1 -0
- package/dist/quota/index.d.ts +23 -0
- package/dist/quota/index.d.ts.map +1 -0
- package/dist/quota/index.js +42 -0
- package/dist/quota/index.js.map +1 -0
- package/dist/quota/opencode-go/cache.d.ts +17 -0
- package/dist/quota/opencode-go/cache.d.ts.map +1 -0
- package/dist/quota/opencode-go/cache.js +31 -0
- package/dist/quota/opencode-go/cache.js.map +1 -0
- package/dist/quota/opencode-go/client.d.ts +28 -0
- package/dist/quota/opencode-go/client.d.ts.map +1 -0
- package/dist/quota/opencode-go/client.js +48 -0
- package/dist/quota/opencode-go/client.js.map +1 -0
- package/dist/quota/opencode-go/config.d.ts +37 -0
- package/dist/quota/opencode-go/config.d.ts.map +1 -0
- package/dist/quota/opencode-go/config.js +58 -0
- package/dist/quota/opencode-go/config.js.map +1 -0
- package/dist/quota/opencode-go/format.d.ts +36 -0
- package/dist/quota/opencode-go/format.d.ts.map +1 -0
- package/dist/quota/opencode-go/format.js +87 -0
- package/dist/quota/opencode-go/format.js.map +1 -0
- package/dist/quota/opencode-go/index.d.ts +30 -0
- package/dist/quota/opencode-go/index.d.ts.map +1 -0
- package/dist/quota/opencode-go/index.js +108 -0
- package/dist/quota/opencode-go/index.js.map +1 -0
- package/dist/quota/opencode-go/parse.d.ts +41 -0
- package/dist/quota/opencode-go/parse.d.ts.map +1 -0
- package/dist/quota/opencode-go/parse.js +75 -0
- package/dist/quota/opencode-go/parse.js.map +1 -0
- package/dist/quota/opencode-go/types.d.ts +48 -0
- package/dist/quota/opencode-go/types.d.ts.map +1 -0
- package/dist/quota/opencode-go/types.js +11 -0
- package/dist/quota/opencode-go/types.js.map +1 -0
- package/dist/quota/parse.d.ts +33 -0
- package/dist/quota/parse.d.ts.map +1 -0
- package/dist/quota/parse.js +200 -0
- package/dist/quota/parse.js.map +1 -0
- package/dist/quota/types.d.ts +72 -0
- package/dist/quota/types.d.ts.map +1 -0
- package/dist/quota/types.js +10 -0
- package/dist/quota/types.js.map +1 -0
- 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 +139 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +179 -4
- package/dist/server.js.map +1 -1
- package/dist/tasks-index.d.ts +14 -4
- package/dist/tasks-index.d.ts.map +1 -1
- package/dist/tasks-index.js +145 -46
- package/dist/tasks-index.js.map +1 -1
- package/dist/translators/event-translator.d.ts +33 -0
- package/dist/translators/event-translator.d.ts.map +1 -1
- package/dist/translators/event-translator.js +101 -0
- package/dist/translators/event-translator.js.map +1 -1
- package/dist/translators/index.d.ts +1 -1
- package/dist/translators/index.d.ts.map +1 -1
- package/dist/translators/index.js +1 -1
- package/dist/translators/index.js.map +1 -1
- package/dist/translators/projection-differ.d.ts +8 -0
- package/dist/translators/projection-differ.d.ts.map +1 -1
- package/dist/translators/projection-differ.js +19 -9
- package/dist/translators/projection-differ.js.map +1 -1
- package/dist/translators/tool-helpers.d.ts +28 -0
- package/dist/translators/tool-helpers.d.ts.map +1 -1
- package/dist/translators/tool-helpers.js +104 -0
- package/dist/translators/tool-helpers.js.map +1 -1
- package/dist/translators/types.d.ts +38 -1
- package/dist/translators/types.d.ts.map +1 -1
- package/dist/translators/types.js.map +1 -1
- package/dist/utils.d.ts +36 -7
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +69 -5
- package/dist/utils.js.map +1 -1
- package/docs/ARCHITECTURE.md +130 -40
- package/docs/BACKLOG.md +59 -0
- package/docs/DEVELOPMENT.md +31 -0
- package/docs/PROTOCOL.md +328 -23
- package/docs/REMOTE-CLIENTS.md +260 -0
- package/docs/REPLAY-GUIDE.md +131 -0
- package/docs/TROUBLESHOOTING.md +131 -14
- 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/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +22 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/proposals/0001-tail-session-replay.md +136 -0
- package/docs/proposals/0002-plan-quota-usage.md +81 -0
- package/package.json +10 -6
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
|
|
|
@@ -78,9 +78,13 @@ Messages are classified by the presence of `id` and `method`:
|
|
|
78
78
|
|
|
79
79
|
### `session/create`
|
|
80
80
|
|
|
81
|
-
Create a new session.
|
|
81
|
+
Create a new session. Note: the bridge defers this call until a session's
|
|
82
|
+
first use — ACP `session/new` returns a local placeholder id and materializes
|
|
83
|
+
the backend session (this RPC) on the first prompt / config change / extension
|
|
84
|
+
method, so an editor startup that never sends a message leaves no session.
|
|
82
85
|
|
|
83
86
|
**Request:**
|
|
87
|
+
|
|
84
88
|
```json
|
|
85
89
|
{
|
|
86
90
|
"id": 1,
|
|
@@ -96,6 +100,7 @@ Create a new session.
|
|
|
96
100
|
```
|
|
97
101
|
|
|
98
102
|
**Response:**
|
|
103
|
+
|
|
99
104
|
```json
|
|
100
105
|
{
|
|
101
106
|
"id": 1,
|
|
@@ -114,6 +119,7 @@ Create a new session.
|
|
|
114
119
|
List all sessions.
|
|
115
120
|
|
|
116
121
|
**Request:**
|
|
122
|
+
|
|
117
123
|
```json
|
|
118
124
|
{
|
|
119
125
|
"id": 2,
|
|
@@ -131,7 +137,15 @@ List all sessions.
|
|
|
131
137
|
|
|
132
138
|
Resume an existing session.
|
|
133
139
|
|
|
140
|
+
The sessionId may be a lazy `session/new` placeholder (the editor persists it
|
|
141
|
+
and resumes it after a bridge restart). The bridge resolves it before the
|
|
142
|
+
backend call: an in-memory or persisted (`acp-lazy-sessions.json`) mapping is
|
|
143
|
+
followed to the real backend session — resuming it, or materializing a fresh
|
|
144
|
+
empty one if the placeholder was never used. Real ids from `session/list` pass
|
|
145
|
+
through unchanged.
|
|
146
|
+
|
|
134
147
|
**Request:**
|
|
148
|
+
|
|
135
149
|
```json
|
|
136
150
|
{
|
|
137
151
|
"id": 3,
|
|
@@ -151,6 +165,7 @@ Resume an existing session.
|
|
|
151
165
|
Send a prompt.
|
|
152
166
|
|
|
153
167
|
**Request:**
|
|
168
|
+
|
|
154
169
|
```json
|
|
155
170
|
{
|
|
156
171
|
"id": 4,
|
|
@@ -163,6 +178,7 @@ Send a prompt.
|
|
|
163
178
|
```
|
|
164
179
|
|
|
165
180
|
**Response:**
|
|
181
|
+
|
|
166
182
|
```json
|
|
167
183
|
{
|
|
168
184
|
"id": 4,
|
|
@@ -190,6 +206,7 @@ Stop the current turn (fire-and-forget).
|
|
|
190
206
|
Read the session state and projection.
|
|
191
207
|
|
|
192
208
|
**Request:**
|
|
209
|
+
|
|
193
210
|
```json
|
|
194
211
|
{
|
|
195
212
|
"id": 5,
|
|
@@ -201,6 +218,7 @@ Read the session state and projection.
|
|
|
201
218
|
```
|
|
202
219
|
|
|
203
220
|
**Response:**
|
|
221
|
+
|
|
204
222
|
```json
|
|
205
223
|
{
|
|
206
224
|
"id": 5,
|
|
@@ -216,9 +234,7 @@ Read the session state and projection.
|
|
|
216
234
|
"model": { "current": { "modelId": "GLM-5.2" } },
|
|
217
235
|
"thoughtLevel": { "current": "high" }
|
|
218
236
|
},
|
|
219
|
-
"todos": [
|
|
220
|
-
{ "content": "Implement login", "status": "pending", "priority": "high" }
|
|
221
|
-
]
|
|
237
|
+
"todos": [{ "content": "Implement login", "status": "pending", "priority": "high" }]
|
|
222
238
|
}
|
|
223
239
|
}
|
|
224
240
|
```
|
|
@@ -228,6 +244,7 @@ Read the session state and projection.
|
|
|
228
244
|
Fetch the session's historical messages.
|
|
229
245
|
|
|
230
246
|
**Request:**
|
|
247
|
+
|
|
231
248
|
```json
|
|
232
249
|
{
|
|
233
250
|
"id": 6,
|
|
@@ -245,6 +262,7 @@ Fetch the session's historical messages.
|
|
|
245
262
|
Subscribe to a session's event push.
|
|
246
263
|
|
|
247
264
|
**Request:**
|
|
265
|
+
|
|
248
266
|
```json
|
|
249
267
|
{
|
|
250
268
|
"id": 7,
|
|
@@ -259,6 +277,7 @@ Subscribe to a session's event push.
|
|
|
259
277
|
```
|
|
260
278
|
|
|
261
279
|
**Response:**
|
|
280
|
+
|
|
262
281
|
```json
|
|
263
282
|
{
|
|
264
283
|
"id": 7,
|
|
@@ -312,6 +331,7 @@ Model streaming output.
|
|
|
312
331
|
```
|
|
313
332
|
|
|
314
333
|
`kind` can be:
|
|
334
|
+
|
|
315
335
|
- `text_delta`: text delta
|
|
316
336
|
- `reasoning_delta`: reasoning text delta
|
|
317
337
|
- `tool_call`: tool call declaration (caches toolName and input)
|
|
@@ -338,6 +358,7 @@ Tool status update.
|
|
|
338
358
|
```
|
|
339
359
|
|
|
340
360
|
`kind` can be:
|
|
361
|
+
|
|
341
362
|
- `scheduled`: tool scheduled
|
|
342
363
|
- `started`: tool started executing
|
|
343
364
|
- `progress`: progress update (stdoutTail / stderrTail)
|
|
@@ -408,6 +429,53 @@ Session state update (usage, etc.).
|
|
|
408
429
|
}
|
|
409
430
|
```
|
|
410
431
|
|
|
432
|
+
### Steer lifecycle events
|
|
433
|
+
|
|
434
|
+
When `session/steer` appends instructions to a running turn, the backend emits
|
|
435
|
+
a pair of lifecycle events (available in app-server 0.15.2+). The bridge does
|
|
436
|
+
not currently translate these — they are tracked as a future enhancement (see
|
|
437
|
+
[`BACKLOG.md`](./BACKLOG.md)).
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"type": "turn.steerQueued",
|
|
442
|
+
"payload": {}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"type": "turn.steerDrained",
|
|
449
|
+
"payload": {}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### `turn.terminal`
|
|
454
|
+
|
|
455
|
+
Terminal turn lifecycle event (app-server 0.15.2+). Carries a status plus
|
|
456
|
+
usage. The bridge currently relies on `turn.completed`/`turn.failed` instead;
|
|
457
|
+
documented here for completeness.
|
|
458
|
+
|
|
459
|
+
```json
|
|
460
|
+
{
|
|
461
|
+
"method": "session/event",
|
|
462
|
+
"params": {
|
|
463
|
+
"sessionId": "sess_abc123",
|
|
464
|
+
"seq": 49,
|
|
465
|
+
"type": "turn.terminal",
|
|
466
|
+
"payload": {
|
|
467
|
+
"kind": "turn.terminal",
|
|
468
|
+
"status": "success",
|
|
469
|
+
"resultType": "end_turn",
|
|
470
|
+
"durationMs": 12345,
|
|
471
|
+
"inputTokens": 1234,
|
|
472
|
+
"outputTokens": 567,
|
|
473
|
+
"totalTokens": 1801
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
411
479
|
## Interaction Protocol (Server -> Client)
|
|
412
480
|
|
|
413
481
|
Requests that zcode actively sends to the bridge.
|
|
@@ -440,6 +508,7 @@ Tool permission request.
|
|
|
440
508
|
User input request (ExitPlanMode / AskUserQuestion).
|
|
441
509
|
|
|
442
510
|
**ExitPlanMode:**
|
|
511
|
+
|
|
443
512
|
```json
|
|
444
513
|
{
|
|
445
514
|
"id": 101,
|
|
@@ -455,6 +524,7 @@ User input request (ExitPlanMode / AskUserQuestion).
|
|
|
455
524
|
```
|
|
456
525
|
|
|
457
526
|
**AskUserQuestion:**
|
|
527
|
+
|
|
458
528
|
```json
|
|
459
529
|
{
|
|
460
530
|
"id": 102,
|
|
@@ -482,17 +552,18 @@ User input request (ExitPlanMode / AskUserQuestion).
|
|
|
482
552
|
ZCode `interaction/*` requests are routed to different ACP interaction
|
|
483
553
|
mechanisms based on client capabilities:
|
|
484
554
|
|
|
485
|
-
| Request type
|
|
486
|
-
|
|
487
|
-
| Tool auth (`interaction/requestPermission`)
|
|
488
|
-
| ExitPlanMode (`interaction/requestUserInput` + plan_approval) | `elicitation/create` (approve/reject form) |
|
|
489
|
-
| 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` |
|
|
490
560
|
|
|
491
561
|
**Capability detection**: at `initialize` time the client declares support via
|
|
492
562
|
`clientCapabilities.elicitation.form`. The server detects it with
|
|
493
563
|
`server.supportsElicitationForm()`.
|
|
494
564
|
|
|
495
565
|
**elicitation form example** (AskUserQuestion):
|
|
566
|
+
|
|
496
567
|
```json
|
|
497
568
|
{
|
|
498
569
|
"method": "elicitation/create",
|
|
@@ -505,17 +576,36 @@ mechanisms based on client capabilities:
|
|
|
505
576
|
"properties": {
|
|
506
577
|
"q_0": {
|
|
507
578
|
"type": "string",
|
|
508
|
-
"
|
|
509
|
-
"
|
|
579
|
+
"title": "Select the files to test",
|
|
580
|
+
"oneOf": [
|
|
581
|
+
{ "const": "auth.test.ts", "title": "auth.test.ts" },
|
|
582
|
+
{ "const": "user.test.ts", "title": "user.test.ts" },
|
|
583
|
+
{ "const": "__skip__", "title": "Skip this question" }
|
|
584
|
+
]
|
|
585
|
+
},
|
|
586
|
+
"q_0_other": {
|
|
587
|
+
"type": "string",
|
|
588
|
+
"title": "↳ or type a custom value (overrides the selection)"
|
|
510
589
|
}
|
|
511
590
|
},
|
|
512
|
-
"required": [
|
|
591
|
+
"required": []
|
|
513
592
|
}
|
|
514
593
|
}
|
|
515
594
|
}
|
|
516
595
|
```
|
|
517
596
|
|
|
597
|
+
ACP/MCP elicitation string fields are EITHER an enum (restricted dropdown) OR
|
|
598
|
+
free text — the spec forbids a single field that is both. So each question is
|
|
599
|
+
rendered as TWO fields: `q_<i>` (a `oneOf`/`anyOf` enum dropdown of the model's
|
|
600
|
+
suggested answers, with a trailing "Skip this question" option whose `const` is
|
|
601
|
+
the `__skip__` sentinel and whose `title` is the readable label) and
|
|
602
|
+
`q_<i>_other` (a free-text companion). On submit, a non-empty `q_<i>_other`
|
|
603
|
+
overrides the dropdown (single-select) or is appended to the picked values
|
|
604
|
+
(multi-select); selecting "Skip this question" or leaving both blank skips just
|
|
605
|
+
that question without cancelling the form.
|
|
606
|
+
|
|
518
607
|
**elicitation response** (accept/decline/cancel):
|
|
608
|
+
|
|
519
609
|
```json
|
|
520
610
|
{
|
|
521
611
|
"action": "accept",
|
|
@@ -523,6 +613,35 @@ mechanisms based on client capabilities:
|
|
|
523
613
|
}
|
|
524
614
|
```
|
|
525
615
|
|
|
616
|
+
**ExitPlanMode elicitation form** — single `feedback` text field; no
|
|
617
|
+
approve/reject dropdown. The client's own submit button is the approve action;
|
|
618
|
+
typing into the field is the reject action. Submitting with the field empty
|
|
619
|
+
approves the plan; submitting with text rejects it and returns the text to
|
|
620
|
+
zcode as the decline `reason` (so the agent sees the redirection when it
|
|
621
|
+
re-plans). The cancel/decline button is a plain reject with no reason.
|
|
622
|
+
|
|
623
|
+
```json
|
|
624
|
+
{
|
|
625
|
+
"method": "elicitation/create",
|
|
626
|
+
"params": {
|
|
627
|
+
"mode": "form",
|
|
628
|
+
"sessionId": "sess_abc123",
|
|
629
|
+
"message": "Ready to code?\n\n1. Implement login\n2. Implement signup\n\nLeave the box empty and submit to approve; type feedback to reject and redirect.",
|
|
630
|
+
"requestedSchema": {
|
|
631
|
+
"type": "object",
|
|
632
|
+
"properties": {
|
|
633
|
+
"feedback": {
|
|
634
|
+
"type": "string",
|
|
635
|
+
"title": "Feedback",
|
|
636
|
+
"description": "Empty = approve the plan. Anything typed = reject and use this text as the redirection."
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
"required": []
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
526
645
|
## Extension Methods (0.14.8+)
|
|
527
646
|
|
|
528
647
|
### `session/fork`
|
|
@@ -530,6 +649,7 @@ mechanisms based on client capabilities:
|
|
|
530
649
|
Fork a new session from a checkpoint.
|
|
531
650
|
|
|
532
651
|
**Request:**
|
|
652
|
+
|
|
533
653
|
```json
|
|
534
654
|
{
|
|
535
655
|
"id": 8,
|
|
@@ -546,6 +666,7 @@ Fork a new session from a checkpoint.
|
|
|
546
666
|
Rewind to a checkpoint.
|
|
547
667
|
|
|
548
668
|
**Request:**
|
|
669
|
+
|
|
549
670
|
```json
|
|
550
671
|
{
|
|
551
672
|
"id": 9,
|
|
@@ -563,6 +684,7 @@ Rewind to a checkpoint.
|
|
|
563
684
|
Read / set / replace / clear the goal.
|
|
564
685
|
|
|
565
686
|
**Request:**
|
|
687
|
+
|
|
566
688
|
```json
|
|
567
689
|
{
|
|
568
690
|
"id": 10,
|
|
@@ -582,6 +704,7 @@ Read / set / replace / clear the goal.
|
|
|
582
704
|
Compact the conversation history.
|
|
583
705
|
|
|
584
706
|
**Request:**
|
|
707
|
+
|
|
585
708
|
```json
|
|
586
709
|
{
|
|
587
710
|
"id": 11,
|
|
@@ -597,6 +720,7 @@ Compact the conversation history.
|
|
|
597
720
|
Append instructions to a running turn.
|
|
598
721
|
|
|
599
722
|
**Request:**
|
|
723
|
+
|
|
600
724
|
```json
|
|
601
725
|
{
|
|
602
726
|
"id": 12,
|
|
@@ -613,6 +737,7 @@ Append instructions to a running turn.
|
|
|
613
737
|
Switch the session mode.
|
|
614
738
|
|
|
615
739
|
**Request:**
|
|
740
|
+
|
|
616
741
|
```json
|
|
617
742
|
{
|
|
618
743
|
"id": 13,
|
|
@@ -629,6 +754,7 @@ Switch the session mode.
|
|
|
629
754
|
Set the thought level.
|
|
630
755
|
|
|
631
756
|
**Request:**
|
|
757
|
+
|
|
632
758
|
```json
|
|
633
759
|
{
|
|
634
760
|
"id": 14,
|
|
@@ -640,10 +766,189 @@ Set the thought level.
|
|
|
640
766
|
}
|
|
641
767
|
```
|
|
642
768
|
|
|
643
|
-
##
|
|
769
|
+
## Background Tasks & Sub-Agents
|
|
770
|
+
|
|
771
|
+
When the model dispatches a sub-agent via the `Agent` (or `Task`) tool, the
|
|
772
|
+
backend keeps producing events on the **same session stream** — both while the
|
|
773
|
+
sub-agent runs and after the main turn ends. The bridge forwards a curated
|
|
774
|
+
subset to the ACP client:
|
|
775
|
+
|
|
776
|
+
### Synchronous sub-agent (blocking)
|
|
777
|
+
|
|
778
|
+
The `Agent` tool blocks until the sub-agent finishes. Its internal tool calls
|
|
779
|
+
(`Read`, `Bash`, …) arrive as ordinary `tool.updated` events on the main stream
|
|
780
|
+
and are forwarded as regular `tool_call` cards. The `Agent` card itself carries
|
|
781
|
+
structured metadata in `_meta.subagent` (parsed from the result content):
|
|
782
|
+
|
|
783
|
+
```json
|
|
784
|
+
{
|
|
785
|
+
"sessionUpdate": "tool_call_update",
|
|
786
|
+
"toolCallId": "call_xxx",
|
|
787
|
+
"status": "completed",
|
|
788
|
+
"_meta": {
|
|
789
|
+
"claudeCode": { "toolName": "Agent" },
|
|
790
|
+
"subagent": {
|
|
791
|
+
"agentId": "agent_73c7c63d-...",
|
|
792
|
+
"tokens": 40904,
|
|
793
|
+
"toolUses": 1,
|
|
794
|
+
"durationMs": 10559
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### Background sub-agent (`run_in_background: true`)
|
|
801
|
+
|
|
802
|
+
The `Agent` tool returns immediately with a launch acknowledgement (result
|
|
803
|
+
content contains `agentId` + `output_file` + "working in the background"). The
|
|
804
|
+
main turn then completes, but the backend continues to push the task's
|
|
805
|
+
lifecycle on the same stream:
|
|
806
|
+
|
|
807
|
+
**1. Status changes** — `session.updated` carries a `taskId` and `status`:
|
|
808
|
+
|
|
809
|
+
```json
|
|
810
|
+
{
|
|
811
|
+
"method": "session/event",
|
|
812
|
+
"params": {
|
|
813
|
+
"sessionId": "sess_abc123",
|
|
814
|
+
"seq": 16,
|
|
815
|
+
"type": "session.updated",
|
|
816
|
+
"payload": {
|
|
817
|
+
"taskId": "agent_88a44529-...",
|
|
818
|
+
"toolCallId": "call_orig",
|
|
819
|
+
"toolName": "Agent",
|
|
820
|
+
"status": "running",
|
|
821
|
+
"description": "Read README first heading",
|
|
822
|
+
"outputPath": "/.../output.txt",
|
|
823
|
+
"terminalId": "agent_88a44529-...",
|
|
824
|
+
"startedAt": "2026-07-18T09:06:45.929Z"
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
The bridge's session-scoped `BackgroundTaskListener` turns these into a
|
|
831
|
+
dedicated ACP tool card (`[background] <description>`) plus status updates:
|
|
832
|
+
|
|
833
|
+
| Backend event | ACP notification |
|
|
834
|
+
| ------------------------------------------ | -------------------------------------------------------------- |
|
|
835
|
+
| first `session.updated` (status `running`) | `tool_call` (new card, `kind:"other"`, `status:"in_progress"`) |
|
|
836
|
+
| `session.updated` (status `completed`) | `tool_call_update` (`status:"completed"`) |
|
|
837
|
+
|
|
838
|
+
`session.updated` events WITHOUT a `taskId` (e.g. usage updates) are ignored by
|
|
839
|
+
the background listener — they remain owned by the turn loop.
|
|
840
|
+
|
|
841
|
+
**2. Completion notification turn** — when the background task finishes, the
|
|
842
|
+
backend auto-triggers a new turn whose `turn.started` carries
|
|
843
|
+
`inputSource:"background_task"`:
|
|
844
|
+
|
|
845
|
+
```json
|
|
846
|
+
{
|
|
847
|
+
"type": "turn.started",
|
|
848
|
+
"payload": {
|
|
849
|
+
"inputSource": "background_task",
|
|
850
|
+
"inputVisibility": "model-only",
|
|
851
|
+
"input": "<task-notification>\n <task-id>agent_...</task-id>\n <status>completed</status-status>\n ...\n</task-notification>",
|
|
852
|
+
"turnId": "turn_95197b25-..."
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
The background listener forwards that turn's `model.streaming text_delta` as
|
|
858
|
+
`agent_message_chunk` so the user sees the background result. The per-prompt
|
|
859
|
+
turn loop **defers** this entire turn (drops its events) to avoid double-
|
|
860
|
+
forwarding and to keep it from prematurely ending the user's real turn.
|
|
861
|
+
|
|
862
|
+
### Background Bash (`run_in_background: true`)
|
|
863
|
+
|
|
864
|
+
The `Bash` tool launched with `run_in_background: true` returns immediately
|
|
865
|
+
with a launch acknowledgement (result content: "Command running in background
|
|
866
|
+
with ID: exec_…"). Like the Agent sub-agent, the backend keeps pushing the
|
|
867
|
+
task's lifecycle on the same stream via `session.updated` events that carry
|
|
868
|
+
the originating `toolCallId`:
|
|
644
869
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
870
|
+
```json
|
|
871
|
+
{
|
|
872
|
+
"type": "session.updated",
|
|
873
|
+
"payload": {
|
|
874
|
+
"taskId": "exec_ac3a5053-...",
|
|
875
|
+
"toolCallId": "call_e282b4ec...",
|
|
876
|
+
"toolName": "Bash",
|
|
877
|
+
"status": "running",
|
|
878
|
+
"pid": 22410,
|
|
879
|
+
"outputPath": "/.../call_...-stdout.log",
|
|
880
|
+
"outputTail": "done\n"
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
**Card reuse, not duplication.** Unlike an Agent sub-agent (which mints a fresh
|
|
886
|
+
`bg_*` card), a background Bash task **reuses the launch card** — the very
|
|
887
|
+
terminal card the dispatcher created when `Bash` was scheduled. This keeps the
|
|
888
|
+
lifecycle on a single card instead of producing a duplicate `[background]` card
|
|
889
|
+
that the editor would show alongside the closed launch card.
|
|
890
|
+
|
|
891
|
+
The mechanism:
|
|
892
|
+
|
|
893
|
+
1. **Launch turn** — the dispatcher tags the `ToolCallNew`/`ToolCallUpdate`
|
|
894
|
+
with `background: true` (threaded from the cached `input.run_in_background`
|
|
895
|
+
flag) and, on the launch `result`, **skips `terminal_exit`** so the launch
|
|
896
|
+
card stays `in_progress`. It seeds an empty marker in `terminalSentData`
|
|
897
|
+
for the `toolCallId` — this is the signal the background listener uses to
|
|
898
|
+
recognise "this is a tracked launch card".
|
|
899
|
+
2. **Lifecycle (`session.updated`)** — the `BackgroundTaskListener` resolves
|
|
900
|
+
the `toolCallId`, sees it in `terminalSentData`, and routes status updates
|
|
901
|
+
back to the launch card. On `status:"completed"`, it emits the final
|
|
902
|
+
`outputTail` via `terminal_output` (iff launch text wasn't already streamed)
|
|
903
|
+
and closes the terminal UI with `terminal_exit` (exit code 0, or 1 on
|
|
904
|
+
`failed`), then clears the `terminalSentData` entry.
|
|
905
|
+
3. **Fallback** — if the `session.updated` lacks a `toolCallId`, or the
|
|
906
|
+
`toolCallId` is unknown to `terminalSentData` (sub-agent case), the listener
|
|
907
|
+
falls back to minting a fresh `bg_*` card — the Agent sub-agent path above.
|
|
908
|
+
|
|
909
|
+
| Backend event | ACP notification (background Bash) |
|
|
910
|
+
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
911
|
+
| first `session.updated` (status `running`) | `tool_call_update` on the launch card (`status:"in_progress"`) |
|
|
912
|
+
| `session.updated` (status `completed`, with `outputTail`) | `terminal_output` (final output, if not already streamed) + `tool_call_update` with `terminal_exit` (`status:"completed"`) |
|
|
913
|
+
| `session.updated` (status `failed`) | `tool_call_update` with `terminal_exit` (`status:"failed"`, exit_code 1) |
|
|
914
|
+
|
|
915
|
+
`session/cancelBackgroundTask` for a background Bash task additionally emits
|
|
916
|
+
`terminal_exit` with `_meta.backgroundTask.cancelled = true` so the terminal
|
|
917
|
+
UI closes on cancellation.
|
|
918
|
+
|
|
919
|
+
### `session/cancelBackgroundTask`
|
|
920
|
+
|
|
921
|
+
Cancels a background task. The bridge additionally marks the corresponding ACP
|
|
922
|
+
tool card as `failed` with `_meta.backgroundTask.cancelled = true`.
|
|
923
|
+
|
|
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 |
|
|
929
|
+
|
|
930
|
+
## Additional backend methods (not wired into the bridge)
|
|
931
|
+
|
|
932
|
+
The backend exposes more RPC methods than the bridge uses (sub-agent listing,
|
|
933
|
+
event pull, session usage/close, automation, workspace config, MCP/plugins).
|
|
934
|
+
These have no ACP-side counterpart yet. See [`BACKLOG.md`](./BACKLOG.md) for
|
|
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.
|