zcode-acp-server 0.1.0 → 0.2.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 +98 -1
- package/README.zh-CN.md +81 -1
- 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/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/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 +11 -2
- package/dist/handlers/io.d.ts.map +1 -1
- package/dist/handlers/io.js +51 -7
- package/dist/handlers/io.js.map +1 -1
- 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 +876 -220
- package/dist/handlers/session.js.map +1 -1
- package/dist/handlers/slash.d.ts +14 -0
- package/dist/handlers/slash.d.ts.map +1 -1
- package/dist/handlers/slash.js +80 -6
- package/dist/handlers/slash.js.map +1 -1
- package/dist/index.js +38 -4
- 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/server.d.ts +104 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +119 -2
- 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 +35 -6
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +52 -4
- package/dist/utils.js.map +1 -1
- package/docs/ARCHITECTURE.md +96 -38
- package/docs/BACKLOG.md +57 -0
- package/docs/DEVELOPMENT.md +5 -0
- package/docs/PROTOCOL.md +270 -5
- package/docs/TROUBLESHOOTING.md +98 -14
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +22 -0
- package/docs/agents/triage-labels.md +15 -0
- package/package.json +6 -5
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -25,18 +25,58 @@ zcode-acp-server (stdio JSON-RPC ACP)
|
|
|
25
25
|
zcode app-server --stdio (line-delimited JSON)
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## ACP Handshake
|
|
29
|
+
|
|
30
|
+
The `initialize` request (`server.ts`) negotiates the protocol version and
|
|
31
|
+
declares the agent's shape to the editor:
|
|
32
|
+
|
|
33
|
+
- **`protocolVersion`** — pinned to `PROTOCOL_VERSION` (currently 1).
|
|
34
|
+
- **`agentInfo`** — name/title/version from `AGENT_INFO` in `utils.ts`.
|
|
35
|
+
- **`agentCapabilities`** — `loadSession`, plus `sessionCapabilities.list /
|
|
36
|
+
resume / fork`. The prompt capabilities (image/audio/embeddedContext) and
|
|
37
|
+
MCP capabilities are all off.
|
|
38
|
+
- **`authMethods`** — a single agent-type entry (`zcode-credentials`). The
|
|
39
|
+
bridge reads the GLM API key itself from `~/.zcode/v2/config.json` and
|
|
40
|
+
forwards it to the ZCode subprocess via `ANTHROPIC_API_KEY`; the editor
|
|
41
|
+
never supplies credentials. Omitting the `type` field defaults to `"agent"`,
|
|
42
|
+
which the ACP registry's auth-check accepts as "agent self-handles auth".
|
|
43
|
+
|
|
44
|
+
`initialize` does **not** spawn the backend, and neither does `session/new`:
|
|
45
|
+
the backend is lazily created on the first backend RPC — for a fresh session
|
|
46
|
+
that is the first `session/create` at its first use (prompt / config change /
|
|
47
|
+
extension method). This keeps the handshake succeeding even in an environment
|
|
48
|
+
without `~/.zcode/v2/config.json` (e.g. the registry CI runs `initialize` with
|
|
49
|
+
an isolated `HOME`).
|
|
50
|
+
|
|
51
|
+
Client capabilities advertised at `initialize` are recorded on the server
|
|
52
|
+
(`clientCapabilities`) and drive later behaviour: `supportsElicitationForm()`
|
|
53
|
+
gates form-based elicitation, and `supportsTerminalOutput()` gates Zed's Bash
|
|
54
|
+
terminal UI.
|
|
55
|
+
|
|
28
56
|
## Core Data Flow
|
|
29
57
|
|
|
30
58
|
### 1. Session lifecycle
|
|
31
59
|
|
|
32
60
|
```
|
|
33
|
-
session/new → session
|
|
61
|
+
session/new → placeholder id (backend session NOT created yet)
|
|
62
|
+
|
|
|
63
|
+
first use: prompt / set_config_option / extension method
|
|
64
|
+
|
|
|
65
|
+
session/create → register EventListener
|
|
34
66
|
|
|
|
35
67
|
prompt request → session/send → EventTranslator translates → dispatchEvent
|
|
36
68
|
| |
|
|
37
69
|
end_turn / cancelled session/update notification
|
|
38
70
|
```
|
|
39
71
|
|
|
72
|
+
Sessions are materialized lazily (`ensureRealSession`): an editor startup that
|
|
73
|
+
never sends a message leaves no empty session in the backend or the App's task
|
|
74
|
+
index. The placeholder → backend-session mapping is persisted to
|
|
75
|
+
`~/.zcode/v2/acp-lazy-sessions.json` (`src/lazy-sessions.ts`), so a `session/
|
|
76
|
+
resume` / `session/load` of a placeholder from a previous bridge lifetime still
|
|
77
|
+
resolves: with a recorded backend id the real session is resumed, without one a
|
|
78
|
+
fresh (empty) session is materialized — never "Session not found".
|
|
79
|
+
|
|
40
80
|
### 2. Event stream subscription
|
|
41
81
|
|
|
42
82
|
```
|
|
@@ -52,11 +92,13 @@ pollEvent() consumes → EventTranslator.translate()
|
|
|
52
92
|
### 3. Dual-path event handling
|
|
53
93
|
|
|
54
94
|
#### Real-time path (EventTranslator)
|
|
95
|
+
|
|
55
96
|
- Listens to zcode `session/event` pushes
|
|
56
97
|
- Translates each event to an ACP `session/update` in real time
|
|
57
98
|
- Maintains `seenToolIds` to avoid duplicates
|
|
58
99
|
|
|
59
100
|
#### Snapshot path (ProjectionDiffer)
|
|
101
|
+
|
|
60
102
|
- On turn completion, builds a snapshot from `session/messages` + `session/read`
|
|
61
103
|
- Diffs two snapshots to produce new events (PlanUpdate / TextDelta / ToolCallNew, etc.)
|
|
62
104
|
- Used for turn-completion triage and stall recovery
|
|
@@ -82,6 +124,7 @@ Key: **`seenToolIds` synchronization**
|
|
|
82
124
|
|
|
83
125
|
In `session.ts:629`, after the event path finishes processing, the state is
|
|
84
126
|
synced to the differ:
|
|
127
|
+
|
|
85
128
|
```typescript
|
|
86
129
|
for (const seenId of translator.seenToolIds) {
|
|
87
130
|
differ.markToolSeen(seenId);
|
|
@@ -92,49 +135,64 @@ This ensures the snapshot diff does not re-emit tools already handled by the
|
|
|
92
135
|
event path, preventing Bash terminal output from being overwritten by a
|
|
93
136
|
content-less ToolCallNew.
|
|
94
137
|
|
|
138
|
+
## Data & Privacy
|
|
139
|
+
|
|
140
|
+
**No telemetry, no analytics, no third-party network calls.** The server is a
|
|
141
|
+
local relay: prompts, code, and tool outputs pass through process memory on
|
|
142
|
+
their way between the editor and the ZCode subprocess, but reach the GLM cloud
|
|
143
|
+
API only because the ZCode backend itself sends them for inference.
|
|
144
|
+
|
|
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
|
+
|
|
95
152
|
## Module Responsibilities
|
|
96
153
|
|
|
97
154
|
### `backend/` — ZCode process communication
|
|
98
155
|
|
|
99
|
-
| File
|
|
100
|
-
|
|
101
|
-
| `client.ts`
|
|
102
|
-
| `listener.ts` | EventStreamListener (subscribe/consume the event stream) and TurnMonitor (snapshot polling)
|
|
103
|
-
| `types.ts`
|
|
156
|
+
| File | Responsibility |
|
|
157
|
+
| ------------- | ----------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `client.ts` | Spawn/manage the zcode subprocess, reader-loop, request/response multiplexing, process watchdog |
|
|
159
|
+
| `listener.ts` | EventStreamListener (subscribe/consume the event stream) and TurnMonitor (snapshot polling) |
|
|
160
|
+
| `types.ts` | ZCode JSON-RPC message type definitions |
|
|
104
161
|
|
|
105
162
|
### `translators/` — Event translation
|
|
106
163
|
|
|
107
|
-
| File
|
|
108
|
-
|
|
109
|
-
| `event-translator.ts`
|
|
110
|
-
| `projection-differ.ts` | Diff two snapshots to produce InternalEvent (snapshot path)
|
|
111
|
-
| `tool-helpers.ts`
|
|
112
|
-
| `types.ts`
|
|
164
|
+
| File | Responsibility |
|
|
165
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `event-translator.ts` | Translate zcode events to InternalEvent (real-time path) |
|
|
167
|
+
| `projection-differ.ts` | Diff two snapshots to produce InternalEvent (snapshot path) |
|
|
168
|
+
| `tool-helpers.ts` | Tool-related pure functions: title generation, output rendering, diff parsing, location extraction |
|
|
169
|
+
| `types.ts` | InternalEvent union type and plan entry builders |
|
|
113
170
|
|
|
114
171
|
### `handlers/` — ACP method handling
|
|
115
172
|
|
|
116
|
-
| File
|
|
117
|
-
|
|
118
|
-
| `session.ts`
|
|
119
|
-
| `extensions.ts`
|
|
120
|
-
| `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
|
+
| `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) |
|
|
121
179
|
| `server-requests.ts` | Handle zcode interaction/* requests (tool auth, ExitPlanMode, AskUserQuestion), protocol negotiation routing |
|
|
122
|
-
| `io.ts`
|
|
123
|
-
| `slash.ts`
|
|
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) |
|
|
124
182
|
|
|
125
183
|
### `interaction/` — Interaction bridging
|
|
126
184
|
|
|
127
|
-
| File
|
|
128
|
-
|
|
185
|
+
| File | Responsibility |
|
|
186
|
+
| ------------ | ------------------------------------------------------------------------------------------------ |
|
|
129
187
|
| `adapter.ts` | Conversion adapter from zcode interaction requests to ACP (requestPermission + elicitation form) |
|
|
130
188
|
|
|
131
189
|
### `config/` — Configuration management
|
|
132
190
|
|
|
133
|
-
| File
|
|
134
|
-
|
|
135
|
-
| `options.ts`
|
|
136
|
-
| `runtime-model.ts` | runtimeModel overlay construction and application
|
|
137
|
-
| `model-cache.ts`
|
|
191
|
+
| File | Responsibility |
|
|
192
|
+
| ------------------ | -------------------------------------------------------------- |
|
|
193
|
+
| `options.ts` | configOptions / modes construction, set_config_option dispatch |
|
|
194
|
+
| `runtime-model.ts` | runtimeModel overlay construction and application |
|
|
195
|
+
| `model-cache.ts` | Model ID cache and usage initialization |
|
|
138
196
|
|
|
139
197
|
## Key State Machines
|
|
140
198
|
|
|
@@ -222,12 +280,12 @@ mirroring the Python bridge's `_pending_post_notifs` queue +
|
|
|
222
280
|
The session mode can change through four entry points, all of which must
|
|
223
281
|
notify the editor UI:
|
|
224
282
|
|
|
225
|
-
| Trigger
|
|
226
|
-
|
|
227
|
-
| `session/setMode` request
|
|
228
|
-
| `session/set_config_option` (mode)
|
|
229
|
-
| `/mode` slash command
|
|
230
|
-
| In-turn `EnterPlanMode`/`ExitPlanMode` | reconciled at turn completion
|
|
283
|
+
| Trigger | Path | Notifies UI |
|
|
284
|
+
| -------------------------------------- | -------------------------------------------------------------- | :---------: |
|
|
285
|
+
| `session/setMode` request | `extensions.ts:setMode` | yes |
|
|
286
|
+
| `session/set_config_option` (mode) | `session.ts:setConfigOptionHandler` → `emitConfigOptionUpdate` | yes |
|
|
287
|
+
| `/mode` slash command | `slash.ts` → `emitConfigOptionUpdate` | yes |
|
|
288
|
+
| In-turn `EnterPlanMode`/`ExitPlanMode` | reconciled at turn completion | yes |
|
|
231
289
|
|
|
232
290
|
The in-turn path bypasses the bridge entirely, so `prompt()` runs
|
|
233
291
|
`emitModeIfChanged` (`session.ts`) at turn completion: it re-reads the
|
|
@@ -277,12 +335,12 @@ zcode process exits, so a normal shutdown leaves no lingering watchdog.
|
|
|
277
335
|
|
|
278
336
|
### Why a dual path?
|
|
279
337
|
|
|
280
|
-
| Scenario
|
|
281
|
-
|
|
282
|
-
| Normal streaming
|
|
283
|
-
| Lost events
|
|
284
|
-
| Deduplication
|
|
285
|
-
| Turn-completion triage | Not triggered
|
|
338
|
+
| Scenario | Real-time path | Snapshot path |
|
|
339
|
+
| ---------------------- | -------------- | ------------------------------- |
|
|
340
|
+
| Normal streaming | Low latency | Must wait for turn end |
|
|
341
|
+
| Lost events | Loses data | Recovers from snapshot |
|
|
342
|
+
| Deduplication | seenToolIds | seenMessageIds + markToolSeen() |
|
|
343
|
+
| Turn-completion triage | Not triggered | PlanUpdate / usage_update |
|
|
286
344
|
|
|
287
345
|
### Why no polling fallback?
|
|
288
346
|
|
package/docs/BACKLOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Protocol Backlog
|
|
2
|
+
|
|
3
|
+
Backend RPC methods and event types exposed by the ZCode CLI (`zcode app-server`)
|
|
4
|
+
that are **not yet wired into the bridge**, tracked for potential future support.
|
|
5
|
+
|
|
6
|
+
Last audited against **app-server 0.15.2** (bundled in ZCode desktop 3.4.2,
|
|
7
|
+
2026-07-22). Method names were extracted from the bundled `zcode.cjs`.
|
|
8
|
+
|
|
9
|
+
## Candidate methods (optional enhancements)
|
|
10
|
+
|
|
11
|
+
These are available in the backend but have no ACP-side counterpart yet. Pick
|
|
12
|
+
them up when a concrete ACP/editor need appears.
|
|
13
|
+
|
|
14
|
+
| Method | Purpose | Current bridge behavior |
|
|
15
|
+
|--------|---------|-------------------------|
|
|
16
|
+
| `session/subagents` | Query the list of sub-agents for a session | Sub-agent info is parsed from the `Agent` tool result (`_meta.subagent`); sufficient for now |
|
|
17
|
+
| `session/events` | Pull-mode event history (complement to `session/subscribe`) | Not used; could support event replay/gap-fill |
|
|
18
|
+
| `session/usage` | Per-session token usage | Context bar uses `session.updated` usage payload instead |
|
|
19
|
+
| `session/close` | Explicitly close a session (vs `session/stop` which ends a turn) | Not used; sessions close on process exit |
|
|
20
|
+
|
|
21
|
+
### New event types (undocumented in PROTOCOL.md)
|
|
22
|
+
|
|
23
|
+
| Event | Status field | Notes |
|
|
24
|
+
|-------|--------------|-------|
|
|
25
|
+
| `turn.steerQueued` | — | Emitted when a `session/steer` is queued behind a running turn. Enhances steer UI feedback |
|
|
26
|
+
| `turn.steerDrained` | — | Emitted when queued steer instructions are drained into the turn |
|
|
27
|
+
| `turn.terminal` | `status: success \| interrupted \| failed`, `resultType?`, `durationMs`, `...usage` | Terminal turn lifecycle event; currently the bridge relies on `turn.completed`/`turn.failed` |
|
|
28
|
+
|
|
29
|
+
## Not planned (client/config layer)
|
|
30
|
+
|
|
31
|
+
These methods belong to the desktop client or workspace configuration layer and
|
|
32
|
+
have no ACP equivalent. Listed for completeness only — the bridge does not
|
|
33
|
+
intend to surface them.
|
|
34
|
+
|
|
35
|
+
`automation/create`, `automation/list`, `automation/delete` (scheduled tasks),
|
|
36
|
+
`usage/stats`, `workspace/readState`, `workspace/upsertModelProvider`,
|
|
37
|
+
`workspace/removeModelProvider`, `workspace/updateProviderRegistry`,
|
|
38
|
+
`workspace/setDefaultModel`, `workspace/setDefaultThoughtLevel`,
|
|
39
|
+
`workspace/setDefaultMode`, `workspace/generateText`, `mcp/list`,
|
|
40
|
+
`plugins/list`, `plugins/setEnabled`, `plugins/overview`, `plugins/describe`,
|
|
41
|
+
`plugins/marketplace/*`.
|
|
42
|
+
|
|
43
|
+
## Verification method
|
|
44
|
+
|
|
45
|
+
The bundled CLI is minified, so a literal `grep "session/rewind"` returns 0
|
|
46
|
+
hits even when the method is fully supported — method names are split across
|
|
47
|
+
variable references. To audit reliably:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
cd /Applications/ZCode.app/Contents/Resources/glm
|
|
51
|
+
# Full RPC enum: search for the method-dispatch table (Key:"ns/name" pairs)
|
|
52
|
+
python3 -c "import re; ..." # see commit that added this file
|
|
53
|
+
# Word-level presence is more reliable for verifying a method still exists:
|
|
54
|
+
grep -oiF "rewind" zcode.cjs | wc -l # 357 → definitely present
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Never conclude a method was removed from a single string-literal search.
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -117,6 +117,11 @@ cat ~/.zcode/v2/config.json
|
|
|
117
117
|
# confirm a provider is enabled and has models
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
The bridge reads the GLM API key from this file and forwards it to the ZCode
|
|
121
|
+
subprocess — no editor-side API key or environment variable is required. If
|
|
122
|
+
`config.json` is missing or no provider is enabled, install and log into the
|
|
123
|
+
ZCode desktop app so it writes a valid file.
|
|
124
|
+
|
|
120
125
|
### Test session/subscribe
|
|
121
126
|
|
|
122
127
|
```bash
|
package/docs/PROTOCOL.md
CHANGED
|
@@ -78,7 +78,10 @@ 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:**
|
|
84
87
|
```json
|
|
@@ -131,6 +134,13 @@ List all sessions.
|
|
|
131
134
|
|
|
132
135
|
Resume an existing session.
|
|
133
136
|
|
|
137
|
+
The sessionId may be a lazy `session/new` placeholder (the editor persists it
|
|
138
|
+
and resumes it after a bridge restart). The bridge resolves it before the
|
|
139
|
+
backend call: an in-memory or persisted (`acp-lazy-sessions.json`) mapping is
|
|
140
|
+
followed to the real backend session — resuming it, or materializing a fresh
|
|
141
|
+
empty one if the placeholder was never used. Real ids from `session/list` pass
|
|
142
|
+
through unchanged.
|
|
143
|
+
|
|
134
144
|
**Request:**
|
|
135
145
|
```json
|
|
136
146
|
{
|
|
@@ -408,6 +418,53 @@ Session state update (usage, etc.).
|
|
|
408
418
|
}
|
|
409
419
|
```
|
|
410
420
|
|
|
421
|
+
### Steer lifecycle events
|
|
422
|
+
|
|
423
|
+
When `session/steer` appends instructions to a running turn, the backend emits
|
|
424
|
+
a pair of lifecycle events (available in app-server 0.15.2+). The bridge does
|
|
425
|
+
not currently translate these — they are tracked as a future enhancement (see
|
|
426
|
+
[`BACKLOG.md`](./BACKLOG.md)).
|
|
427
|
+
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"type": "turn.steerQueued",
|
|
431
|
+
"payload": {}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
```json
|
|
436
|
+
{
|
|
437
|
+
"type": "turn.steerDrained",
|
|
438
|
+
"payload": {}
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### `turn.terminal`
|
|
443
|
+
|
|
444
|
+
Terminal turn lifecycle event (app-server 0.15.2+). Carries a status plus
|
|
445
|
+
usage. The bridge currently relies on `turn.completed`/`turn.failed` instead;
|
|
446
|
+
documented here for completeness.
|
|
447
|
+
|
|
448
|
+
```json
|
|
449
|
+
{
|
|
450
|
+
"method": "session/event",
|
|
451
|
+
"params": {
|
|
452
|
+
"sessionId": "sess_abc123",
|
|
453
|
+
"seq": 49,
|
|
454
|
+
"type": "turn.terminal",
|
|
455
|
+
"payload": {
|
|
456
|
+
"kind": "turn.terminal",
|
|
457
|
+
"status": "success",
|
|
458
|
+
"resultType": "end_turn",
|
|
459
|
+
"durationMs": 12345,
|
|
460
|
+
"inputTokens": 1234,
|
|
461
|
+
"outputTokens": 567,
|
|
462
|
+
"totalTokens": 1801
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
411
468
|
## Interaction Protocol (Server -> Client)
|
|
412
469
|
|
|
413
470
|
Requests that zcode actively sends to the bridge.
|
|
@@ -505,16 +562,34 @@ mechanisms based on client capabilities:
|
|
|
505
562
|
"properties": {
|
|
506
563
|
"q_0": {
|
|
507
564
|
"type": "string",
|
|
508
|
-
"
|
|
509
|
-
"
|
|
565
|
+
"title": "Select the files to test",
|
|
566
|
+
"oneOf": [
|
|
567
|
+
{ "const": "auth.test.ts", "title": "auth.test.ts" },
|
|
568
|
+
{ "const": "user.test.ts", "title": "user.test.ts" },
|
|
569
|
+
{ "const": "__skip__", "title": "Skip this question" }
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
"q_0_other": {
|
|
573
|
+
"type": "string",
|
|
574
|
+
"title": "↳ or type a custom value (overrides the selection)"
|
|
510
575
|
}
|
|
511
576
|
},
|
|
512
|
-
"required": [
|
|
577
|
+
"required": []
|
|
513
578
|
}
|
|
514
579
|
}
|
|
515
580
|
}
|
|
516
581
|
```
|
|
517
582
|
|
|
583
|
+
ACP/MCP elicitation string fields are EITHER an enum (restricted dropdown) OR
|
|
584
|
+
free text — the spec forbids a single field that is both. So each question is
|
|
585
|
+
rendered as TWO fields: `q_<i>` (a `oneOf`/`anyOf` enum dropdown of the model's
|
|
586
|
+
suggested answers, with a trailing "Skip this question" option whose `const` is
|
|
587
|
+
the `__skip__` sentinel and whose `title` is the readable label) and
|
|
588
|
+
`q_<i>_other` (a free-text companion). On submit, a non-empty `q_<i>_other`
|
|
589
|
+
overrides the dropdown (single-select) or is appended to the picked values
|
|
590
|
+
(multi-select); selecting "Skip this question" or leaving both blank skips just
|
|
591
|
+
that question without cancelling the form.
|
|
592
|
+
|
|
518
593
|
**elicitation response** (accept/decline/cancel):
|
|
519
594
|
```json
|
|
520
595
|
{
|
|
@@ -523,6 +598,34 @@ mechanisms based on client capabilities:
|
|
|
523
598
|
}
|
|
524
599
|
```
|
|
525
600
|
|
|
601
|
+
**ExitPlanMode elicitation form** — single `feedback` text field; no
|
|
602
|
+
approve/reject dropdown. The client's own submit button is the approve action;
|
|
603
|
+
typing into the field is the reject action. Submitting with the field empty
|
|
604
|
+
approves the plan; submitting with text rejects it and returns the text to
|
|
605
|
+
zcode as the decline `reason` (so the agent sees the redirection when it
|
|
606
|
+
re-plans). The cancel/decline button is a plain reject with no reason.
|
|
607
|
+
```json
|
|
608
|
+
{
|
|
609
|
+
"method": "elicitation/create",
|
|
610
|
+
"params": {
|
|
611
|
+
"mode": "form",
|
|
612
|
+
"sessionId": "sess_abc123",
|
|
613
|
+
"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.",
|
|
614
|
+
"requestedSchema": {
|
|
615
|
+
"type": "object",
|
|
616
|
+
"properties": {
|
|
617
|
+
"feedback": {
|
|
618
|
+
"type": "string",
|
|
619
|
+
"title": "Feedback",
|
|
620
|
+
"description": "Empty = approve the plan. Anything typed = reject and use this text as the redirection."
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
"required": []
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
526
629
|
## Extension Methods (0.14.8+)
|
|
527
630
|
|
|
528
631
|
### `session/fork`
|
|
@@ -640,10 +743,172 @@ Set the thought level.
|
|
|
640
743
|
}
|
|
641
744
|
```
|
|
642
745
|
|
|
643
|
-
##
|
|
746
|
+
## Background Tasks & Sub-Agents
|
|
747
|
+
|
|
748
|
+
When the model dispatches a sub-agent via the `Agent` (or `Task`) tool, the
|
|
749
|
+
backend keeps producing events on the **same session stream** — both while the
|
|
750
|
+
sub-agent runs and after the main turn ends. The bridge forwards a curated
|
|
751
|
+
subset to the ACP client:
|
|
752
|
+
|
|
753
|
+
### Synchronous sub-agent (blocking)
|
|
754
|
+
|
|
755
|
+
The `Agent` tool blocks until the sub-agent finishes. Its internal tool calls
|
|
756
|
+
(`Read`, `Bash`, …) arrive as ordinary `tool.updated` events on the main stream
|
|
757
|
+
and are forwarded as regular `tool_call` cards. The `Agent` card itself carries
|
|
758
|
+
structured metadata in `_meta.subagent` (parsed from the result content):
|
|
759
|
+
|
|
760
|
+
```json
|
|
761
|
+
{
|
|
762
|
+
"sessionUpdate": "tool_call_update",
|
|
763
|
+
"toolCallId": "call_xxx",
|
|
764
|
+
"status": "completed",
|
|
765
|
+
"_meta": {
|
|
766
|
+
"claudeCode": { "toolName": "Agent" },
|
|
767
|
+
"subagent": {
|
|
768
|
+
"agentId": "agent_73c7c63d-...",
|
|
769
|
+
"tokens": 40904,
|
|
770
|
+
"toolUses": 1,
|
|
771
|
+
"durationMs": 10559
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Background sub-agent (`run_in_background: true`)
|
|
778
|
+
|
|
779
|
+
The `Agent` tool returns immediately with a launch acknowledgement (result
|
|
780
|
+
content contains `agentId` + `output_file` + "working in the background"). The
|
|
781
|
+
main turn then completes, but the backend continues to push the task's
|
|
782
|
+
lifecycle on the same stream:
|
|
783
|
+
|
|
784
|
+
**1. Status changes** — `session.updated` carries a `taskId` and `status`:
|
|
785
|
+
|
|
786
|
+
```json
|
|
787
|
+
{
|
|
788
|
+
"method": "session/event",
|
|
789
|
+
"params": {
|
|
790
|
+
"sessionId": "sess_abc123",
|
|
791
|
+
"seq": 16,
|
|
792
|
+
"type": "session.updated",
|
|
793
|
+
"payload": {
|
|
794
|
+
"taskId": "agent_88a44529-...",
|
|
795
|
+
"toolCallId": "call_orig",
|
|
796
|
+
"toolName": "Agent",
|
|
797
|
+
"status": "running",
|
|
798
|
+
"description": "Read README first heading",
|
|
799
|
+
"outputPath": "/.../output.txt",
|
|
800
|
+
"terminalId": "agent_88a44529-...",
|
|
801
|
+
"startedAt": "2026-07-18T09:06:45.929Z"
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
The bridge's session-scoped `BackgroundTaskListener` turns these into a
|
|
808
|
+
dedicated ACP tool card (`[background] <description>`) plus status updates:
|
|
809
|
+
|
|
810
|
+
| Backend event | ACP notification |
|
|
811
|
+
|---|---|
|
|
812
|
+
| first `session.updated` (status `running`) | `tool_call` (new card, `kind:"other"`, `status:"in_progress"`) |
|
|
813
|
+
| `session.updated` (status `completed`) | `tool_call_update` (`status:"completed"`) |
|
|
814
|
+
|
|
815
|
+
`session.updated` events WITHOUT a `taskId` (e.g. usage updates) are ignored by
|
|
816
|
+
the background listener — they remain owned by the turn loop.
|
|
817
|
+
|
|
818
|
+
**2. Completion notification turn** — when the background task finishes, the
|
|
819
|
+
backend auto-triggers a new turn whose `turn.started` carries
|
|
820
|
+
`inputSource:"background_task"`:
|
|
821
|
+
|
|
822
|
+
```json
|
|
823
|
+
{
|
|
824
|
+
"type": "turn.started",
|
|
825
|
+
"payload": {
|
|
826
|
+
"inputSource": "background_task",
|
|
827
|
+
"inputVisibility": "model-only",
|
|
828
|
+
"input": "<task-notification>\n <task-id>agent_...</task-id>\n <status>completed</status-status>\n ...\n</task-notification>",
|
|
829
|
+
"turnId": "turn_95197b25-..."
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
The background listener forwards that turn's `model.streaming text_delta` as
|
|
835
|
+
`agent_message_chunk` so the user sees the background result. The per-prompt
|
|
836
|
+
turn loop **defers** this entire turn (drops its events) to avoid double-
|
|
837
|
+
forwarding and to keep it from prematurely ending the user's real turn.
|
|
838
|
+
|
|
839
|
+
### Background Bash (`run_in_background: true`)
|
|
840
|
+
|
|
841
|
+
The `Bash` tool launched with `run_in_background: true` returns immediately
|
|
842
|
+
with a launch acknowledgement (result content: "Command running in background
|
|
843
|
+
with ID: exec_…"). Like the Agent sub-agent, the backend keeps pushing the
|
|
844
|
+
task's lifecycle on the same stream via `session.updated` events that carry
|
|
845
|
+
the originating `toolCallId`:
|
|
846
|
+
|
|
847
|
+
```json
|
|
848
|
+
{
|
|
849
|
+
"type": "session.updated",
|
|
850
|
+
"payload": {
|
|
851
|
+
"taskId": "exec_ac3a5053-...",
|
|
852
|
+
"toolCallId": "call_e282b4ec...",
|
|
853
|
+
"toolName": "Bash",
|
|
854
|
+
"status": "running",
|
|
855
|
+
"pid": 22410,
|
|
856
|
+
"outputPath": "/.../call_...-stdout.log",
|
|
857
|
+
"outputTail": "done\n"
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
**Card reuse, not duplication.** Unlike an Agent sub-agent (which mints a fresh
|
|
863
|
+
`bg_*` card), a background Bash task **reuses the launch card** — the very
|
|
864
|
+
terminal card the dispatcher created when `Bash` was scheduled. This keeps the
|
|
865
|
+
lifecycle on a single card instead of producing a duplicate `[background]` card
|
|
866
|
+
that the editor would show alongside the closed launch card.
|
|
867
|
+
|
|
868
|
+
The mechanism:
|
|
869
|
+
|
|
870
|
+
1. **Launch turn** — the dispatcher tags the `ToolCallNew`/`ToolCallUpdate`
|
|
871
|
+
with `background: true` (threaded from the cached `input.run_in_background`
|
|
872
|
+
flag) and, on the launch `result`, **skips `terminal_exit`** so the launch
|
|
873
|
+
card stays `in_progress`. It seeds an empty marker in `terminalSentData`
|
|
874
|
+
for the `toolCallId` — this is the signal the background listener uses to
|
|
875
|
+
recognise "this is a tracked launch card".
|
|
876
|
+
2. **Lifecycle (`session.updated`)** — the `BackgroundTaskListener` resolves
|
|
877
|
+
the `toolCallId`, sees it in `terminalSentData`, and routes status updates
|
|
878
|
+
back to the launch card. On `status:"completed"`, it emits the final
|
|
879
|
+
`outputTail` via `terminal_output` (iff launch text wasn't already streamed)
|
|
880
|
+
and closes the terminal UI with `terminal_exit` (exit code 0, or 1 on
|
|
881
|
+
`failed`), then clears the `terminalSentData` entry.
|
|
882
|
+
3. **Fallback** — if the `session.updated` lacks a `toolCallId`, or the
|
|
883
|
+
`toolCallId` is unknown to `terminalSentData` (sub-agent case), the listener
|
|
884
|
+
falls back to minting a fresh `bg_*` card — the Agent sub-agent path above.
|
|
885
|
+
|
|
886
|
+
| Backend event | ACP notification (background Bash) |
|
|
887
|
+
|---|---|
|
|
888
|
+
| first `session.updated` (status `running`) | `tool_call_update` on the launch card (`status:"in_progress"`) |
|
|
889
|
+
| `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`) | `tool_call_update` with `terminal_exit` (`status:"failed"`, exit_code 1) |
|
|
891
|
+
|
|
892
|
+
`session/cancelBackgroundTask` for a background Bash task additionally emits
|
|
893
|
+
`terminal_exit` with `_meta.backgroundTask.cancelled = true` so the terminal
|
|
894
|
+
UI closes on cancellation.
|
|
895
|
+
|
|
896
|
+
### `session/cancelBackgroundTask`
|
|
897
|
+
|
|
898
|
+
Cancels a background task. The bridge additionally marks the corresponding ACP
|
|
899
|
+
tool card as `failed` with `_meta.backgroundTask.cancelled = true`.
|
|
900
|
+
|
|
901
|
+
|
|
644
902
|
|
|
645
903
|
| ZCode CLI version | session/subscribe | Extension methods | Notes |
|
|
646
904
|
|---------------|-------------------|----------|------|
|
|
647
905
|
| >= 0.15.0 | Supported | All supported | Full functionality |
|
|
648
906
|
| >= 0.14.8 | Supported | Partially supported | workspace/* unavailable |
|
|
649
907
|
| 0.14.5 ~ 0.14.7 | Not supported | Not supported | Incompatible with this project |
|
|
908
|
+
|
|
909
|
+
## Additional backend methods (not wired into the bridge)
|
|
910
|
+
|
|
911
|
+
The backend exposes more RPC methods than the bridge uses (sub-agent listing,
|
|
912
|
+
event pull, session usage/close, automation, workspace config, MCP/plugins).
|
|
913
|
+
These have no ACP-side counterpart yet. See [`BACKLOG.md`](./BACKLOG.md) for
|
|
914
|
+
the full list and which are candidates for future support.
|