zcode-acp-server 0.18.2 → 0.19.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 (37) hide show
  1. package/dist/handlers/sandbox-allow.d.ts +67 -5
  2. package/dist/handlers/sandbox-allow.d.ts.map +1 -1
  3. package/dist/handlers/sandbox-allow.js +136 -29
  4. package/dist/handlers/sandbox-allow.js.map +1 -1
  5. package/dist/handlers/session.d.ts.map +1 -1
  6. package/dist/handlers/session.js +28 -8
  7. package/dist/handlers/session.js.map +1 -1
  8. package/dist/i18n.d.ts +3 -2
  9. package/dist/i18n.d.ts.map +1 -1
  10. package/dist/i18n.js +4 -4
  11. package/dist/i18n.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +7 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/remote/config.d.ts +18 -0
  16. package/dist/remote/config.d.ts.map +1 -1
  17. package/dist/remote/config.js +13 -1
  18. package/dist/remote/config.js.map +1 -1
  19. package/dist/remote/endpoint.d.ts.map +1 -1
  20. package/dist/remote/endpoint.js +4 -0
  21. package/dist/remote/endpoint.js.map +1 -1
  22. package/dist/remote/hub-server.d.ts +49 -3
  23. package/dist/remote/hub-server.d.ts.map +1 -1
  24. package/dist/remote/hub-server.js +381 -44
  25. package/dist/remote/hub-server.js.map +1 -1
  26. package/dist/remote/session-list-endpoint.d.ts +51 -0
  27. package/dist/remote/session-list-endpoint.d.ts.map +1 -0
  28. package/dist/remote/session-list-endpoint.js +153 -0
  29. package/dist/remote/session-list-endpoint.js.map +1 -0
  30. package/dist/server.d.ts +15 -3
  31. package/dist/server.d.ts.map +1 -1
  32. package/dist/server.js +14 -2
  33. package/dist/server.js.map +1 -1
  34. package/docs/REMOTE-CLIENTS.md +88 -26
  35. package/docs/REMOTE.md +30 -8
  36. package/docs/SANDBOX.md +3 -1
  37. package/package.json +1 -1
@@ -46,17 +46,18 @@ ACP editor ────── stdio ──────────┘
46
46
 
47
47
  ## Discovery API
48
48
 
49
- | Endpoint | Auth | Purpose |
50
- | ----------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------- |
51
- | `GET /api/health` | none | Liveness probe; `200` body `ok`. |
52
- | `GET /api/instances` | required | Registered bridge instances. Add `?probe=1` to verify first. |
53
- | `GET /api/instances/{id}/status` | required | Real-time per-session running status of one bridge. |
54
- | `POST /api/instances/{id}/sessions/{sessionId}/close` | required | Retire a session from remote discovery — see [Closing a session](#closing-a-session). |
55
- | `POST /api/instances/{id}/sessions/{sessionId}/rename` | required | Rename a session — see [Renaming a session](#renaming-a-session). |
56
- | `GET /api/quota` | required | Account-level usage stats — same payload as `account/usage_stats`, no ACP connection needed. |
57
- | `POST /api/upgrade` | required | Trigger the hub's own staleness check — see [Hub self-upgrade](#hub-self-upgrade). |
58
- | `GET /api/projects` | required | Known-project list (remote session-create whitelist) — see below. |
59
- | `POST /api/instances {workspacePath}` | required | Create (or reuse) a headless serve bridge for one known project — see below. |
49
+ | Endpoint | Auth | Purpose |
50
+ | ------------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------- |
51
+ | `GET /api/health` | none | Liveness probe; `200` body `ok`. |
52
+ | `GET /api/instances` | required | Registered bridge instances. Add `?probe=1` to verify first. |
53
+ | `GET /api/instances/{id}/status` | required | Real-time per-session running status of one bridge. |
54
+ | `POST /api/instances/{id}/sessions/{sessionId}/close` | required | Retire a session from remote discovery — see [Closing a session](#closing-a-session). |
55
+ | `POST /api/instances/{id}/sessions/{sessionId}/rename` | required | Rename a session — see [Renaming a session](#renaming-a-session). |
56
+ | `GET /api/quota` | required | Account-level usage stats — same payload as `account/usage_stats`, no ACP connection needed. |
57
+ | `POST /api/upgrade` | required | Trigger the hub's own staleness check — see [Hub self-upgrade](#hub-self-upgrade). |
58
+ | `GET /api/projects` | required | Known-project list (remote session-create whitelist) — see below. |
59
+ | `GET /api/projects/sessions?workspacePath=` | required | A project's full session store incl. closed ones — see [Resuming a closed session](#resuming-a-closed-session). |
60
+ | `POST /api/instances {workspacePath}` | required | Create (or reuse) a headless serve bridge for one known project — see below. |
60
61
 
61
62
  HTTP auth: `Authorization: Bearer <token>` or `?token=<token>`.
62
63
 
@@ -149,7 +150,8 @@ GET {hub}/api/projects
149
150
  POST {hub}/api/instances body {"workspacePath":"/Users/me/proj"}
150
151
  → 200 {"id":"47073","reused":false}
151
152
  → 403 "unknown project" (path not on the known-project list)
152
- → 502 (spawn failed / bridge died during startup / never registered, 10s budget)
153
+ → 502 (spawn failed / bridge died during startup / never registered;
154
+ ~10s headless, ~20s when a terminal window is opened)
153
155
  ```
154
156
 
155
157
  - `GET /api/projects` aggregates the App's tasks index: every workspace that
@@ -159,20 +161,80 @@ POST {hub}/api/instances body {"workspacePath":"/Users/me/proj"}
159
161
  bound, not a security boundary: a token holder can already run any
160
162
  editor-bridge session in an arbitrary cwd; the trust boundary is the
161
163
  token itself.
162
- - On create the hub spawns `zcode-acp serve` — a headless bridge — detached
163
- in the project's cwd and waits for its heartbeat registration (it appears
164
- in `/api/instances` with `origin:"serve"` within seconds). Connect with
165
- the normal `WS /acp?instance=<id>` and drive it like any instance; the
166
- new conversation's cwd is the project directory regardless of what
167
- `session/new` sends.
168
- - A live serve instance for the same workspace is reused (`reused:true`)
169
- instead of spawning a second one; concurrent creates join the same
170
- in-flight spawn instead of racing a duplicate.
171
- - Lifetime: the serve bridge exists for remote interest only. It exits ~10
172
- minutes after the last client detaches AND the last running turn
173
- finishes — a running turn always completes, even with all clients gone.
174
- Treat a vanished serve instance like any other dead bridge: re-create it
175
- on demand.
164
+ - On create the hub incubates a VISIBLE interactive REPL in the machine's
165
+ terminal (ADR-0016, macOS): the project's owner gets a real local CLI
166
+ window, and its bridge registers with the hub like any serve instance (it
167
+ appears in `/api/instances` with `origin:"serve"` — allow up to ~20s for
168
+ the GUI round-trip). Headless machines, SSH sessions, or
169
+ `ZCODE_ACP_HUB_TERMINAL=0` fall back to the detached `zcode-acp serve`
170
+ bridge of the original design. Connect with the normal
171
+ `WS /acp?instance=<id>` and drive it like any instance; the new
172
+ conversation's cwd is the project directory regardless of what
173
+ `session/new` sends (session roots are pinned in both surfaces).
174
+ - **Which terminal opens** (nothing is auto-detected — the hub is a
175
+ background process and macOS has no default-terminal setting):
176
+ `ZCODE_ACP_HUB_TERMINAL_COMMAND` (a shell command; `{script}` is replaced
177
+ by the quoted script path) wins over `ZCODE_ACP_HUB_TERMINAL_APP`, which
178
+ is matched against built-in launch recipes: Terminal and iTerm run the
179
+ script via `open -a` (both execute `.command`); WezTerm, kitty, Alacritty,
180
+ and Ghostty are driven by their own CLI (`open -na <app> --args … -e …`);
181
+ any other name passes through to `open -a` as-is. Default: Terminal.app.
182
+ Warp cannot execute scripts or commands programmatically at all
183
+ (warpdotdev/warp#1917, #3959, #9083) — naming it warns, and the flow
184
+ degrades to the headless bridge after the register timeout.
185
+ - A live serve-origin instance for the same workspace is reused
186
+ (`reused:true`) instead of spawning a second one; concurrent creates join
187
+ the same in-flight spawn instead of racing a duplicate.
188
+ - Lifetime depends on the surface: a terminal-REPL instance lives while its
189
+ window lives (the owner closing it retires the bridge — re-create on
190
+ demand); the headless fallback exists for remote interest only and exits
191
+ ~10 minutes after the last client detaches AND the last running turn
192
+ finishes. Treat any vanished instance like a dead bridge.
193
+
194
+ ## Resuming a closed session
195
+
196
+ Discovery lists only currently-running conversations. To find and reopen a
197
+ PREVIOUS one — including conversations no bridge currently holds — use the
198
+ per-project history listing (ADR-0015):
199
+
200
+ ```text
201
+ GET {hub}/api/projects/sessions?workspacePath=/Users/me/proj
202
+ → 200 {"workspacePath":"/Users/me/proj",
203
+ "instance":{"id":"47073","origin":"serve"},
204
+ "sessions":[
205
+ {"sessionId":"sess_9f2…","title":"Fix login bug",
206
+ "cwd":"/Users/me/proj","updatedAt":"2026-09-01T10:00:00.000Z",
207
+ "live":false,"running":false}, …],
208
+ "nextCursor":{"before":1723800000000,"beforeId":"sess_9f2…"}}
209
+ → 400 "workspacePath required" / "invalid limit/before …"
210
+ → 403 "unknown project" (path not on the known-project list)
211
+ → 502 (spawn failed / bridge unreachable / broken list)
212
+ ```
213
+
214
+ - The listing is the project's backend session store — closed conversations
215
+ included. `live` marks conversations currently advertised by a bridge (the
216
+ same membership as discovery); `running` marks an in-flight turn. Titles
217
+ are the store's authoritative ones.
218
+ - **Pagination** (projects can hold dozens of sessions): rows come
219
+ newest-first (`updatedAt` descending). `?limit=<n>` sets the page size
220
+ (default 20, max 200); the previous response's `nextCursor` splits into
221
+ `?before=<ms-epoch>&beforeId=<sessionId>` for the next older page — the
222
+ composite cursor names the exact last row, so timestamps tied across a
223
+ page boundary are never skipped or repeated. `nextCursor: null` means
224
+ there are no older sessions. Order is total (id tiebreak), so
225
+ paging never repeats or skips rows.
226
+ - The first listing of a cold project incubates its serve bridge (the same
227
+ machinery as remote session-create; budget ~12s) — later listings and the
228
+ follow-up load reuse it.
229
+ - Resume with the existing flow: `POST /api/instances` (answers
230
+ `reused:true` with the listing's instance), attach
231
+ `WS /acp?instance=<id>`, then
232
+ `session/load {"sessionId":"<the listed id>"}` — history replays and the
233
+ conversation continues with `session/prompt`.
234
+ - `sessionId` here is the backend store id (`sess_…`), which `session/load`
235
+ accepts as-is (pass-through resume). The same conversation may also appear
236
+ in discovery under a different (ACP) id — treat discovery as the
237
+ live-attention surface and this listing as the browse/resume surface.
176
238
 
177
239
  ## Connecting
178
240
 
package/docs/REMOTE.md CHANGED
@@ -68,14 +68,36 @@ ran a session (system temp trees, `~/.zcode` itself, and vanished
68
68
  directories filtered out), newest activity first. The list gates the POST —
69
69
  paths outside it get 403 (a convenience bound, not a security boundary: a
70
70
  token holder can already drive an editor-bridge session in any cwd; the
71
- trust boundary is the token). On create the hub
72
- spawns `zcode-acp serve` — a headless bridge — in the project's cwd; it
73
- registers back within seconds and is reachable like any other instance. A
74
- live serve instance for the same workspace is reused instead of re-spawned
75
- (`reused:true`). The serve bridge lives for remote interest only: it exits
76
- ~10 minutes after the last client detaches and the last turn finishes, and
77
- its `session/new` always uses the project cwd regardless of what a client
78
- sends.
71
+ trust boundary is the token). On create the hub incubates a VISIBLE
72
+ interactive REPL in the machine's terminal (ADR-0016, macOS; headless/SSH or
73
+ `ZCODE_ACP_HUB_TERMINAL=0` falls back to the detached headless
74
+ `zcode-acp serve`); its bridge registers back within seconds (budget ~20s)
75
+ and is reachable like any other instance. The hub itself is a background
76
+ process with no terminal, so nothing is auto-detected: the target terminal
77
+ resolves as `ZCODE_ACP_HUB_TERMINAL_COMMAND` → `ZCODE_ACP_HUB_TERMINAL_APP`
78
+ (built-in launch recipes for Terminal, iTerm, WezTerm, kitty, Alacritty,
79
+ Ghostty; other names pass through to `open -a`) → plain Terminal.app. Warp
80
+ cannot execute scripts or commands programmatically (warpdotdev/warp#1917,
81
+ #3959) and is deliberately unsupported — it degrades to the headless bridge.
82
+ A live serve-origin instance for
83
+ the same workspace is reused instead of re-spawned (`reused:true`). A
84
+ terminal-REPL instance lives while its window lives; the headless fallback
85
+ exits ~10 minutes after the last client detaches and the last turn
86
+ finishes. Session roots are pinned to the project cwd in both surfaces
87
+ regardless of what a client sends.
88
+
89
+ **Remote session-resume** (ADR-0015). Remote clients can also reopen a
90
+ PREVIOUS conversation of a project — including closed ones no bridge
91
+ currently advertises (discovery lists only running conversations):
92
+
93
+ ```text
94
+ GET /api/projects/sessions?workspacePath=… → {workspacePath, instance, sessions}
95
+ ```
96
+
97
+ The listing is the project's full backend session store; the hub incubates
98
+ the project's serve bridge on first listing and reuses it after. Resume is
99
+ the normal attach flow with `session/load` on a listed backend id. See
100
+ `docs/REMOTE-CLIENTS.md` ("Resuming a closed session") for the client contract.
79
101
 
80
102
  `sessions` lists the project's **currently running** conversations (live
81
103
  editor tabs and remote attachments) under the same ACP session ids the
package/docs/SANDBOX.md CHANGED
@@ -42,7 +42,9 @@ that file — the sandbox denies
42
42
  writes to `.zcode/acp/` inside the workspace while the bridge (outside the
43
43
  sandbox) writes it on your behalf. After an allow, the backend restarts with
44
44
  the widened profile (a few seconds; the bridge auto-continues the interrupted
45
- task). Set `"strictGit": true` in the config to also put `.git` behind the
45
+ task). Approvals are batched for a short window, so several pending popups
46
+ granted in a row share ONE restart instead of each killing the others. Set
47
+ `"strictGit": true` in the config to also put `.git` behind the
46
48
  popup.
47
49
 
48
50
  Ordinary filesystem permission failures (`Permission denied`, EACCES — a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zcode-acp-server",
3
- "version": "0.18.2",
3
+ "version": "0.19.0",
4
4
  "description": "Agent Client Protocol (ACP) server bridging headless ZCode to editors like Zed and JetBrains.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",