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.
Files changed (223) hide show
  1. package/README.md +192 -9
  2. package/README.zh-CN.md +143 -8
  3. package/dist/backend/client.d.ts +12 -4
  4. package/dist/backend/client.d.ts.map +1 -1
  5. package/dist/backend/client.js +72 -7
  6. package/dist/backend/client.js.map +1 -1
  7. package/dist/backend/credentials.d.ts.map +1 -1
  8. package/dist/backend/credentials.js +2 -1
  9. package/dist/backend/credentials.js.map +1 -1
  10. package/dist/backend/listener.d.ts +12 -4
  11. package/dist/backend/listener.d.ts.map +1 -1
  12. package/dist/backend/listener.js +68 -12
  13. package/dist/backend/listener.js.map +1 -1
  14. package/dist/backend/types.d.ts +1 -1
  15. package/dist/backend/types.d.ts.map +1 -1
  16. package/dist/bin/hub.d.ts +16 -0
  17. package/dist/bin/hub.d.ts.map +1 -0
  18. package/dist/bin/hub.js +41 -0
  19. package/dist/bin/hub.js.map +1 -0
  20. package/dist/bin/quota.d.ts +54 -0
  21. package/dist/bin/quota.d.ts.map +1 -0
  22. package/dist/bin/quota.js +333 -0
  23. package/dist/bin/quota.js.map +1 -0
  24. package/dist/config/auto-compact.d.ts +23 -0
  25. package/dist/config/auto-compact.d.ts.map +1 -0
  26. package/dist/config/auto-compact.js +67 -0
  27. package/dist/config/auto-compact.js.map +1 -0
  28. package/dist/config/mcp-discovery.d.ts +34 -0
  29. package/dist/config/mcp-discovery.d.ts.map +1 -0
  30. package/dist/config/mcp-discovery.js +153 -0
  31. package/dist/config/mcp-discovery.js.map +1 -0
  32. package/dist/config/model-cache.d.ts +12 -1
  33. package/dist/config/model-cache.d.ts.map +1 -1
  34. package/dist/config/model-cache.js +30 -8
  35. package/dist/config/model-cache.js.map +1 -1
  36. package/dist/config/options.d.ts +87 -9
  37. package/dist/config/options.d.ts.map +1 -1
  38. package/dist/config/options.js +248 -45
  39. package/dist/config/options.js.map +1 -1
  40. package/dist/config/plugin-commands.d.ts +24 -0
  41. package/dist/config/plugin-commands.d.ts.map +1 -0
  42. package/dist/config/plugin-commands.js +107 -0
  43. package/dist/config/plugin-commands.js.map +1 -0
  44. package/dist/config/provider-registry.d.ts +60 -0
  45. package/dist/config/provider-registry.d.ts.map +1 -0
  46. package/dist/config/provider-registry.js +128 -0
  47. package/dist/config/provider-registry.js.map +1 -0
  48. package/dist/config/runtime-model.d.ts +54 -18
  49. package/dist/config/runtime-model.d.ts.map +1 -1
  50. package/dist/config/runtime-model.js +105 -61
  51. package/dist/config/runtime-model.js.map +1 -1
  52. package/dist/config/skill-discovery.d.ts +35 -0
  53. package/dist/config/skill-discovery.d.ts.map +1 -0
  54. package/dist/config/skill-discovery.js +188 -0
  55. package/dist/config/skill-discovery.js.map +1 -0
  56. package/dist/handlers/account.d.ts +43 -0
  57. package/dist/handlers/account.d.ts.map +1 -0
  58. package/dist/handlers/account.js +59 -0
  59. package/dist/handlers/account.js.map +1 -0
  60. package/dist/handlers/background-tasks.d.ts +72 -0
  61. package/dist/handlers/background-tasks.d.ts.map +1 -0
  62. package/dist/handlers/background-tasks.js +330 -0
  63. package/dist/handlers/background-tasks.js.map +1 -0
  64. package/dist/handlers/dispatch.d.ts.map +1 -1
  65. package/dist/handlers/dispatch.js +134 -22
  66. package/dist/handlers/dispatch.js.map +1 -1
  67. package/dist/handlers/extensions.d.ts +15 -0
  68. package/dist/handlers/extensions.d.ts.map +1 -1
  69. package/dist/handlers/extensions.js +43 -27
  70. package/dist/handlers/extensions.js.map +1 -1
  71. package/dist/handlers/io.d.ts +31 -3
  72. package/dist/handlers/io.d.ts.map +1 -1
  73. package/dist/handlers/io.js +108 -9
  74. package/dist/handlers/io.js.map +1 -1
  75. package/dist/handlers/replay.d.ts +79 -0
  76. package/dist/handlers/replay.d.ts.map +1 -0
  77. package/dist/handlers/replay.js +252 -0
  78. package/dist/handlers/replay.js.map +1 -0
  79. package/dist/handlers/server-requests.d.ts +20 -4
  80. package/dist/handlers/server-requests.d.ts.map +1 -1
  81. package/dist/handlers/server-requests.js +293 -63
  82. package/dist/handlers/server-requests.js.map +1 -1
  83. package/dist/handlers/session.d.ts +113 -10
  84. package/dist/handlers/session.d.ts.map +1 -1
  85. package/dist/handlers/session.js +932 -277
  86. package/dist/handlers/session.js.map +1 -1
  87. package/dist/handlers/slash.d.ts +37 -1
  88. package/dist/handlers/slash.d.ts.map +1 -1
  89. package/dist/handlers/slash.js +143 -8
  90. package/dist/handlers/slash.js.map +1 -1
  91. package/dist/index.js +76 -12
  92. package/dist/index.js.map +1 -1
  93. package/dist/interaction/adapter.d.ts +19 -33
  94. package/dist/interaction/adapter.d.ts.map +1 -1
  95. package/dist/interaction/adapter.js +80 -79
  96. package/dist/interaction/adapter.js.map +1 -1
  97. package/dist/lazy-sessions.d.ts +35 -0
  98. package/dist/lazy-sessions.d.ts.map +1 -0
  99. package/dist/lazy-sessions.js +98 -0
  100. package/dist/lazy-sessions.js.map +1 -0
  101. package/dist/quota/cache.d.ts +18 -0
  102. package/dist/quota/cache.d.ts.map +1 -0
  103. package/dist/quota/cache.js +32 -0
  104. package/dist/quota/cache.js.map +1 -0
  105. package/dist/quota/client.d.ts +30 -0
  106. package/dist/quota/client.d.ts.map +1 -0
  107. package/dist/quota/client.js +57 -0
  108. package/dist/quota/client.js.map +1 -0
  109. package/dist/quota/color.d.ts +58 -0
  110. package/dist/quota/color.d.ts.map +1 -0
  111. package/dist/quota/color.js +95 -0
  112. package/dist/quota/color.js.map +1 -0
  113. package/dist/quota/combined.d.ts +66 -0
  114. package/dist/quota/combined.d.ts.map +1 -0
  115. package/dist/quota/combined.js +179 -0
  116. package/dist/quota/combined.js.map +1 -0
  117. package/dist/quota/format.d.ts +105 -0
  118. package/dist/quota/format.d.ts.map +1 -0
  119. package/dist/quota/format.js +219 -0
  120. package/dist/quota/format.js.map +1 -0
  121. package/dist/quota/index.d.ts +23 -0
  122. package/dist/quota/index.d.ts.map +1 -0
  123. package/dist/quota/index.js +42 -0
  124. package/dist/quota/index.js.map +1 -0
  125. package/dist/quota/opencode-go/cache.d.ts +17 -0
  126. package/dist/quota/opencode-go/cache.d.ts.map +1 -0
  127. package/dist/quota/opencode-go/cache.js +31 -0
  128. package/dist/quota/opencode-go/cache.js.map +1 -0
  129. package/dist/quota/opencode-go/client.d.ts +28 -0
  130. package/dist/quota/opencode-go/client.d.ts.map +1 -0
  131. package/dist/quota/opencode-go/client.js +48 -0
  132. package/dist/quota/opencode-go/client.js.map +1 -0
  133. package/dist/quota/opencode-go/config.d.ts +37 -0
  134. package/dist/quota/opencode-go/config.d.ts.map +1 -0
  135. package/dist/quota/opencode-go/config.js +58 -0
  136. package/dist/quota/opencode-go/config.js.map +1 -0
  137. package/dist/quota/opencode-go/format.d.ts +36 -0
  138. package/dist/quota/opencode-go/format.d.ts.map +1 -0
  139. package/dist/quota/opencode-go/format.js +87 -0
  140. package/dist/quota/opencode-go/format.js.map +1 -0
  141. package/dist/quota/opencode-go/index.d.ts +30 -0
  142. package/dist/quota/opencode-go/index.d.ts.map +1 -0
  143. package/dist/quota/opencode-go/index.js +108 -0
  144. package/dist/quota/opencode-go/index.js.map +1 -0
  145. package/dist/quota/opencode-go/parse.d.ts +41 -0
  146. package/dist/quota/opencode-go/parse.d.ts.map +1 -0
  147. package/dist/quota/opencode-go/parse.js +75 -0
  148. package/dist/quota/opencode-go/parse.js.map +1 -0
  149. package/dist/quota/opencode-go/types.d.ts +48 -0
  150. package/dist/quota/opencode-go/types.d.ts.map +1 -0
  151. package/dist/quota/opencode-go/types.js +11 -0
  152. package/dist/quota/opencode-go/types.js.map +1 -0
  153. package/dist/quota/parse.d.ts +33 -0
  154. package/dist/quota/parse.d.ts.map +1 -0
  155. package/dist/quota/parse.js +200 -0
  156. package/dist/quota/parse.js.map +1 -0
  157. package/dist/quota/types.d.ts +72 -0
  158. package/dist/quota/types.d.ts.map +1 -0
  159. package/dist/quota/types.js +10 -0
  160. package/dist/quota/types.js.map +1 -0
  161. package/dist/remote/broadcast.d.ts +47 -0
  162. package/dist/remote/broadcast.d.ts.map +1 -0
  163. package/dist/remote/broadcast.js +121 -0
  164. package/dist/remote/broadcast.js.map +1 -0
  165. package/dist/remote/config.d.ts +32 -0
  166. package/dist/remote/config.d.ts.map +1 -0
  167. package/dist/remote/config.js +65 -0
  168. package/dist/remote/config.js.map +1 -0
  169. package/dist/remote/endpoint.d.ts +30 -0
  170. package/dist/remote/endpoint.d.ts.map +1 -0
  171. package/dist/remote/endpoint.js +213 -0
  172. package/dist/remote/endpoint.js.map +1 -0
  173. package/dist/remote/hub-server.d.ts +41 -0
  174. package/dist/remote/hub-server.d.ts.map +1 -0
  175. package/dist/remote/hub-server.js +346 -0
  176. package/dist/remote/hub-server.js.map +1 -0
  177. package/dist/server.d.ts +139 -1
  178. package/dist/server.d.ts.map +1 -1
  179. package/dist/server.js +179 -4
  180. package/dist/server.js.map +1 -1
  181. package/dist/tasks-index.d.ts +14 -4
  182. package/dist/tasks-index.d.ts.map +1 -1
  183. package/dist/tasks-index.js +145 -46
  184. package/dist/tasks-index.js.map +1 -1
  185. package/dist/translators/event-translator.d.ts +33 -0
  186. package/dist/translators/event-translator.d.ts.map +1 -1
  187. package/dist/translators/event-translator.js +101 -0
  188. package/dist/translators/event-translator.js.map +1 -1
  189. package/dist/translators/index.d.ts +1 -1
  190. package/dist/translators/index.d.ts.map +1 -1
  191. package/dist/translators/index.js +1 -1
  192. package/dist/translators/index.js.map +1 -1
  193. package/dist/translators/projection-differ.d.ts +8 -0
  194. package/dist/translators/projection-differ.d.ts.map +1 -1
  195. package/dist/translators/projection-differ.js +19 -9
  196. package/dist/translators/projection-differ.js.map +1 -1
  197. package/dist/translators/tool-helpers.d.ts +28 -0
  198. package/dist/translators/tool-helpers.d.ts.map +1 -1
  199. package/dist/translators/tool-helpers.js +104 -0
  200. package/dist/translators/tool-helpers.js.map +1 -1
  201. package/dist/translators/types.d.ts +38 -1
  202. package/dist/translators/types.d.ts.map +1 -1
  203. package/dist/translators/types.js.map +1 -1
  204. package/dist/utils.d.ts +36 -7
  205. package/dist/utils.d.ts.map +1 -1
  206. package/dist/utils.js +69 -5
  207. package/dist/utils.js.map +1 -1
  208. package/docs/ARCHITECTURE.md +130 -40
  209. package/docs/BACKLOG.md +59 -0
  210. package/docs/DEVELOPMENT.md +31 -0
  211. package/docs/PROTOCOL.md +328 -23
  212. package/docs/REMOTE-CLIENTS.md +260 -0
  213. package/docs/REPLAY-GUIDE.md +131 -0
  214. package/docs/TROUBLESHOOTING.md +131 -14
  215. package/docs/adr/0001-bridge-lifetime-follows-primary-client.md +14 -0
  216. package/docs/adr/0002-stateless-hub-over-per-bridge-acp-endpoints.md +23 -0
  217. package/docs/adr/0003-tail-replay-meta-and-cursor-pagination.md +40 -0
  218. package/docs/agents/domain.md +51 -0
  219. package/docs/agents/issue-tracker.md +22 -0
  220. package/docs/agents/triage-labels.md +15 -0
  221. package/docs/proposals/0001-tail-session-replay.md +136 -0
  222. package/docs/proposals/0002-plan-quota-usage.md +81 -0
  223. package/package.json +10 -6
@@ -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
@@ -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/create register EventListener
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,96 @@ 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 | Responsibility |
100
- |------|------|
101
- | `client.ts` | Spawn/manage the zcode subprocess, reader-loop, request/response multiplexing, process watchdog |
102
- | `listener.ts` | EventStreamListener (subscribe/consume the event stream) and TurnMonitor (snapshot polling) |
103
- | `types.ts` | ZCode JSON-RPC message type definitions |
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 | Responsibility |
108
- |------|------|
109
- | `event-translator.ts` | Translate zcode events to InternalEvent (real-time path) |
110
- | `projection-differ.ts` | Diff two snapshots to produce InternalEvent (snapshot path) |
111
- | `tool-helpers.ts` | Tool-related pure functions: title generation, output rendering, diff parsing, location extraction |
112
- | `types.ts` | InternalEvent union type and plan entry builders |
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 | Responsibility |
117
- |------|------|
118
- | `session.ts` | session/new/list/resume/load/prompt/set_config_option/cancel |
119
- | `extensions.ts` | fork/rewind/rewindCascade/goal/compact/steer/cancelBackgroundTask/setModel/setMode/setThoughtLevel |
120
- | `dispatch.ts` | dispatchEvent single exit point: InternalEvent → ACP session/update |
121
- | `server-requests.ts` | Handle zcode interaction/* requests (tool auth, ExitPlanMode, AskUserQuestion), protocol negotiation routing |
122
- | `io.ts` | ACP notification helpers (including `sendAvailableCommandsDeferred` deferred notification) |
123
- | `slash.ts` | Interception of `/`-prefixed commands (/compact /goal /fork /rewind /steer /model /mode /thought) |
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) |
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) |
124
183
 
125
184
  ### `interaction/` — Interaction bridging
126
185
 
127
- | File | Responsibility |
128
- |------|------|
186
+ | File | Responsibility |
187
+ | ------------ | ------------------------------------------------------------------------------------------------ |
129
188
  | `adapter.ts` | Conversion adapter from zcode interaction requests to ACP (requestPermission + elicitation form) |
130
189
 
131
190
  ### `config/` — Configuration management
132
191
 
133
- | File | Responsibility |
134
- |------|------|
135
- | `options.ts` | configOptions / modes construction, set_config_option dispatch |
136
- | `runtime-model.ts` | runtimeModel overlay construction and application |
137
- | `model-cache.ts` | Model ID cache and usage initialization |
192
+ | File | Responsibility |
193
+ | ------------------ | -------------------------------------------------------------- |
194
+ | `options.ts` | configOptions / modes construction, set_config_option dispatch |
195
+ | `runtime-model.ts` | runtimeModel overlay construction and application |
196
+ | `model-cache.ts` | Model ID cache and usage initialization |
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.
138
228
 
139
229
  ## Key State Machines
140
230
 
@@ -222,12 +312,12 @@ mirroring the Python bridge's `_pending_post_notifs` queue +
222
312
  The session mode can change through four entry points, all of which must
223
313
  notify the editor UI:
224
314
 
225
- | Trigger | Path | Notifies UI |
226
- |------|------|:---:|
227
- | `session/setMode` request | `extensions.ts:setMode` | yes |
228
- | `session/set_config_option` (mode) | `session.ts:setConfigOptionHandler` → `emitConfigOptionUpdate` | yes |
229
- | `/mode` slash command | `slash.ts` → `emitConfigOptionUpdate` | yes |
230
- | In-turn `EnterPlanMode`/`ExitPlanMode` | reconciled at turn completion | yes |
315
+ | Trigger | Path | Notifies UI |
316
+ | -------------------------------------- | -------------------------------------------------------------- | :---------: |
317
+ | `session/setMode` request | `extensions.ts:setMode` | yes |
318
+ | `session/set_config_option` (mode) | `session.ts:setConfigOptionHandler` → `emitConfigOptionUpdate` | yes |
319
+ | `/mode` slash command | `slash.ts` → `emitConfigOptionUpdate` | yes |
320
+ | In-turn `EnterPlanMode`/`ExitPlanMode` | reconciled at turn completion | yes |
231
321
 
232
322
  The in-turn path bypasses the bridge entirely, so `prompt()` runs
233
323
  `emitModeIfChanged` (`session.ts`) at turn completion: it re-reads the
@@ -277,12 +367,12 @@ zcode process exits, so a normal shutdown leaves no lingering watchdog.
277
367
 
278
368
  ### Why a dual path?
279
369
 
280
- | Scenario | Real-time path | Snapshot path |
281
- |------|---------|----------|
282
- | Normal streaming | Low latency | Must wait for turn end |
283
- | Lost events | Loses data | Recovers from snapshot |
284
- | Deduplication | seenToolIds | seenMessageIds + markToolSeen() |
285
- | Turn-completion triage | Not triggered | PlanUpdate / usage_update |
370
+ | Scenario | Real-time path | Snapshot path |
371
+ | ---------------------- | -------------- | ------------------------------- |
372
+ | Normal streaming | Low latency | Must wait for turn end |
373
+ | Lost events | Loses data | Recovers from snapshot |
374
+ | Deduplication | seenToolIds | seenMessageIds + markToolSeen() |
375
+ | Turn-completion triage | Not triggered | PlanUpdate / usage_update |
286
376
 
287
377
  ### Why no polling fallback?
288
378
 
@@ -0,0 +1,59 @@
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` (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`,
39
+ `workspace/removeModelProvider`, `workspace/updateProviderRegistry`,
40
+ `workspace/setDefaultModel`, `workspace/setDefaultThoughtLevel`,
41
+ `workspace/setDefaultMode`, `workspace/generateText`, `mcp/list`,
42
+ `plugins/list`, `plugins/setEnabled`, `plugins/overview`, `plugins/describe`,
43
+ `plugins/marketplace/*`.
44
+
45
+ ## Verification method
46
+
47
+ The bundled CLI is minified, so a literal `grep "session/rewind"` returns 0
48
+ hits even when the method is fully supported — method names are split across
49
+ variable references. To audit reliably:
50
+
51
+ ```sh
52
+ cd /Applications/ZCode.app/Contents/Resources/glm
53
+ # Full RPC enum: search for the method-dispatch table (Key:"ns/name" pairs)
54
+ python3 -c "import re; ..." # see commit that added this file
55
+ # Word-level presence is more reliable for verifying a method still exists:
56
+ grep -oiF "rewind" zcode.cjs | wc -l # 357 → definitely present
57
+ ```
58
+
59
+ Never conclude a method was removed from a single string-literal search.
@@ -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
@@ -117,6 +143,11 @@ cat ~/.zcode/v2/config.json
117
143
  # confirm a provider is enabled and has models
118
144
  ```
119
145
 
146
+ The bridge reads the GLM API key from this file and forwards it to the ZCode
147
+ subprocess — no editor-side API key or environment variable is required. If
148
+ `config.json` is missing or no provider is enabled, install and log into the
149
+ ZCode desktop app so it writes a valid file.
150
+
120
151
  ### Test session/subscribe
121
152
 
122
153
  ```bash