zcode-acp-server 0.2.0 → 0.3.1

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 (64) hide show
  1. package/README.md +101 -15
  2. package/README.zh-CN.md +68 -13
  3. package/dist/bin/hub.d.ts +16 -0
  4. package/dist/bin/hub.d.ts.map +1 -0
  5. package/dist/bin/hub.js +41 -0
  6. package/dist/bin/hub.js.map +1 -0
  7. package/dist/handlers/account.d.ts +43 -0
  8. package/dist/handlers/account.d.ts.map +1 -0
  9. package/dist/handlers/account.js +59 -0
  10. package/dist/handlers/account.js.map +1 -0
  11. package/dist/handlers/io.d.ts +20 -1
  12. package/dist/handlers/io.d.ts.map +1 -1
  13. package/dist/handlers/io.js +57 -2
  14. package/dist/handlers/io.js.map +1 -1
  15. package/dist/handlers/replay.d.ts +79 -0
  16. package/dist/handlers/replay.d.ts.map +1 -0
  17. package/dist/handlers/replay.js +256 -0
  18. package/dist/handlers/replay.js.map +1 -0
  19. package/dist/handlers/session.d.ts.map +1 -1
  20. package/dist/handlers/session.js +83 -68
  21. package/dist/handlers/session.js.map +1 -1
  22. package/dist/handlers/slash.d.ts +23 -1
  23. package/dist/handlers/slash.d.ts.map +1 -1
  24. package/dist/handlers/slash.js +67 -6
  25. package/dist/handlers/slash.js.map +1 -1
  26. package/dist/index.js +47 -17
  27. package/dist/index.js.map +1 -1
  28. package/dist/remote/broadcast.d.ts +47 -0
  29. package/dist/remote/broadcast.d.ts.map +1 -0
  30. package/dist/remote/broadcast.js +121 -0
  31. package/dist/remote/broadcast.js.map +1 -0
  32. package/dist/remote/config.d.ts +32 -0
  33. package/dist/remote/config.d.ts.map +1 -0
  34. package/dist/remote/config.js +65 -0
  35. package/dist/remote/config.js.map +1 -0
  36. package/dist/remote/endpoint.d.ts +43 -0
  37. package/dist/remote/endpoint.d.ts.map +1 -0
  38. package/dist/remote/endpoint.js +222 -0
  39. package/dist/remote/endpoint.js.map +1 -0
  40. package/dist/remote/hub-server.d.ts +41 -0
  41. package/dist/remote/hub-server.d.ts.map +1 -0
  42. package/dist/remote/hub-server.js +346 -0
  43. package/dist/remote/hub-server.js.map +1 -0
  44. package/dist/server.d.ts +62 -7
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +99 -11
  47. package/dist/server.js.map +1 -1
  48. package/dist/utils.d.ts +1 -1
  49. package/dist/utils.d.ts.map +1 -1
  50. package/dist/utils.js +17 -1
  51. package/dist/utils.js.map +1 -1
  52. package/docs/ARCHITECTURE.md +47 -15
  53. package/docs/BACKLOG.md +3 -1
  54. package/docs/DEVELOPMENT.md +26 -0
  55. package/docs/PROTOCOL.md +67 -27
  56. package/docs/REMOTE-CLIENTS.md +264 -0
  57. package/docs/REPLAY-GUIDE.md +131 -0
  58. package/docs/TROUBLESHOOTING.md +51 -6
  59. package/docs/adr/0001-bridge-lifetime-follows-primary-client.md +14 -0
  60. package/docs/adr/0002-stateless-hub-over-per-bridge-acp-endpoints.md +23 -0
  61. package/docs/adr/0003-tail-replay-meta-and-cursor-pagination.md +40 -0
  62. package/docs/proposals/0001-tail-session-replay.md +136 -0
  63. package/docs/proposals/0002-plan-quota-usage.md +81 -0
  64. package/package.json +5 -2
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Tail replay kernel — slicing, cursor pagination, and the `session/load`
3
+ * replay/`session/load_earlier` handlers (Proposal 0001 / ADR-0003).
4
+ *
5
+ * session/load replays history as session/update notifications; for long
6
+ * sessions that cost is O(full history) on every attach and reconnect. The
7
+ * helpers here slice the fetched messages into turn-aligned batches and page
8
+ * backwards with an opaque cursor. Batches are sent under the per-session
9
+ * replay lock (`withReplayBatch` in io.ts) so a batch never interleaves with
10
+ * live-turn updates for the same session; `replayMessages` is the one sender
11
+ * that bypasses the per-message lock — it only runs inside a batch.
12
+ */
13
+ import type * as acp from "@agentclientprotocol/sdk";
14
+ import type { ZcodeMessage } from "../backend/types.js";
15
+ import type { ZcodeAcpServer } from "../server.js";
16
+ /** Upper bound for a requested tail/page size (values above clamp to this). */
17
+ export declare const MAX_REPLAY_LIMIT = 500;
18
+ /** Page size for `session/load_earlier` when the request omits `limit`. */
19
+ export declare const DEFAULT_EARLIER_LIMIT = 50;
20
+ /** Wire metadata describing one delivered batch (additive-only over time). */
21
+ export interface ReplayMeta {
22
+ cursor: string;
23
+ hasMore: boolean;
24
+ replayedMessages: number;
25
+ replayedTurns: number;
26
+ totalMessages: number;
27
+ totalTurns: number;
28
+ }
29
+ export interface ReplaySlice {
30
+ batch: ZcodeMessage[];
31
+ meta: ReplayMeta;
32
+ }
33
+ /** `session/load_earlier` params (top-level — our parser, not an ACP spec method). */
34
+ export interface LoadEarlierParams {
35
+ sessionId: string;
36
+ before?: string;
37
+ limit?: number;
38
+ }
39
+ /**
40
+ * Slice the last `limit` messages, aligned back to the start of the turn
41
+ * containing the oldest one — never a mid-turn cut. `limit: 0` attaches with
42
+ * metadata only (cursor anchors at the end of history).
43
+ */
44
+ export declare function sliceTail(messages: ZcodeMessage[], limit: number): ReplaySlice;
45
+ /** Full-history slice (no `_meta.zcode.limit` on session/load). */
46
+ export declare function fullSlice(messages: ZcodeMessage[]): ReplaySlice;
47
+ /**
48
+ * Slice up to `limit` messages strictly older than the `before` cursor.
49
+ * The cursor points into a prefix of history, so turns appended after it was
50
+ * minted keep it valid; only a history that shrank (compaction/truncation)
51
+ * throws `cursor expired` — clients map that to a full re-`session/load`.
52
+ */
53
+ export declare function sliceBefore(messages: ZcodeMessage[], before: string, limit: number): ReplaySlice;
54
+ /**
55
+ * Read the tail limit from `session/load`'s `_meta.zcode.limit`. The SDK's
56
+ * zod params schema strips unknown top-level keys, so bridge extensions ride
57
+ * in `_meta` (ADR-0003). Returns null when absent/non-finite = full replay.
58
+ */
59
+ export declare function readTailLimit(params: acp.LoadSessionRequest): number | null;
60
+ /** Fetch session/messages from zcode (the bridge's only history source). */
61
+ export declare function fetchMessages(server: ZcodeAcpServer, zcodeSid: string): Promise<ZcodeMessage[]>;
62
+ /**
63
+ * Replay messages as session/update notifications, oldest → newest.
64
+ *
65
+ * MUST run inside `withReplayBatch` for the session: this is the one sender
66
+ * that bypasses the per-message lock in sendSessionUpdate (the batch already
67
+ * holds it), which is what makes the batch atomic against live dispatch.
68
+ */
69
+ export declare function replayMessages(cx: acp.AgentContext, acpSid: string, messages: ZcodeMessage[]): Promise<number>;
70
+ /**
71
+ * `session/load_earlier` — deliver one page of history strictly older than
72
+ * the `before` cursor, oldest → newest (clients prepend). Requires the
73
+ * session to already be attached in this bridge; pagination never triggers
74
+ * an implicit backend resume.
75
+ */
76
+ export declare function loadEarlier(server: ZcodeAcpServer, params: LoadEarlierParams, cx: acp.AgentContext): Promise<{
77
+ replayMeta: ReplayMeta;
78
+ }>;
79
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/handlers/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInD,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqFD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAW9E;AAED,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,WAAW,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAchG;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAK3E;AAED,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAYD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,CA+DjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,iBAAiB,EACzB,EAAE,EAAE,GAAG,CAAC,YAAY,GACnB,OAAO,CAAC;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAarC"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Tail replay kernel — slicing, cursor pagination, and the `session/load`
3
+ * replay/`session/load_earlier` handlers (Proposal 0001 / ADR-0003).
4
+ *
5
+ * session/load replays history as session/update notifications; for long
6
+ * sessions that cost is O(full history) on every attach and reconnect. The
7
+ * helpers here slice the fetched messages into turn-aligned batches and page
8
+ * backwards with an opaque cursor. Batches are sent under the per-session
9
+ * replay lock (`withReplayBatch` in io.ts) so a batch never interleaves with
10
+ * live-turn updates for the same session; `replayMessages` is the one sender
11
+ * that bypasses the per-message lock — it only runs inside a batch.
12
+ */
13
+ import { randomUUID } from "node:crypto";
14
+ import { log, warn } from "../utils.js";
15
+ import { throwError, withReplayBatch } from "./io.js";
16
+ /** Upper bound for a requested tail/page size (values above clamp to this). */
17
+ export const MAX_REPLAY_LIMIT = 500;
18
+ /** Page size for `session/load_earlier` when the request omits `limit`. */
19
+ export const DEFAULT_EARLIER_LIMIT = 50;
20
+ /**
21
+ * Indices where a turn starts: every user message, plus 0 so leading
22
+ * non-user messages (system preambles) belong to the first turn.
23
+ */
24
+ function turnStarts(messages) {
25
+ const starts = messages.length > 0 ? [0] : [];
26
+ messages.forEach((m, i) => {
27
+ if (m.info?.role === "user" && i > 0)
28
+ starts.push(i);
29
+ });
30
+ return starts;
31
+ }
32
+ /** Count of turn starts inside [start, end). */
33
+ function turnsInRange(starts, start, end) {
34
+ return starts.filter((s) => s >= start && s < end).length;
35
+ }
36
+ function encodeCursor(messages, index, totalTurns) {
37
+ const anchor = messages[index]?.info?.id;
38
+ const payload = { v: 1, index, totalTurns };
39
+ if (anchor)
40
+ payload.id = anchor;
41
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
42
+ }
43
+ function decodeCursor(before) {
44
+ try {
45
+ const raw = JSON.parse(Buffer.from(before, "base64url").toString("utf8"));
46
+ if (raw?.v !== 1 ||
47
+ !Number.isInteger(raw.index) ||
48
+ raw.index < 0 ||
49
+ !Number.isInteger(raw.totalTurns)) {
50
+ throw new Error("bad shape");
51
+ }
52
+ return raw;
53
+ }
54
+ catch {
55
+ // Garbage or foreign cursors are indistinguishable from expired ones.
56
+ return throwError(-32602, "cursor expired");
57
+ }
58
+ }
59
+ function buildSlice(messages, starts, start, end) {
60
+ const totalTurns = starts.length;
61
+ return {
62
+ batch: messages.slice(start, end),
63
+ meta: {
64
+ cursor: encodeCursor(messages, start, totalTurns),
65
+ hasMore: start > 0,
66
+ replayedMessages: end - start,
67
+ replayedTurns: turnsInRange(starts, start, end),
68
+ totalMessages: messages.length,
69
+ totalTurns,
70
+ },
71
+ };
72
+ }
73
+ /** The greatest turn start at or before `pos` (0 when none — starts include 0). */
74
+ function alignToTurnStart(starts, pos) {
75
+ let aligned = 0;
76
+ for (const s of starts) {
77
+ if (s <= pos)
78
+ aligned = s;
79
+ else
80
+ break;
81
+ }
82
+ return aligned;
83
+ }
84
+ function clampLimit(limit) {
85
+ return Math.max(0, Math.min(Math.floor(limit), MAX_REPLAY_LIMIT));
86
+ }
87
+ /**
88
+ * Slice the last `limit` messages, aligned back to the start of the turn
89
+ * containing the oldest one — never a mid-turn cut. `limit: 0` attaches with
90
+ * metadata only (cursor anchors at the end of history).
91
+ */
92
+ export function sliceTail(messages, limit) {
93
+ const starts = turnStarts(messages);
94
+ const clamped = clampLimit(limit);
95
+ if (clamped === 0) {
96
+ // Metadata-only attach: an empty batch whose cursor anchors at the end of
97
+ // history, so load_earlier pages the whole tail.
98
+ return buildSlice(messages, starts, messages.length, messages.length);
99
+ }
100
+ if (clamped >= messages.length)
101
+ return buildSlice(messages, starts, 0, messages.length);
102
+ const start = alignToTurnStart(starts, messages.length - clamped);
103
+ return buildSlice(messages, starts, start, messages.length);
104
+ }
105
+ /** Full-history slice (no `_meta.zcode.limit` on session/load). */
106
+ export function fullSlice(messages) {
107
+ return buildSlice(messages, turnStarts(messages), 0, messages.length);
108
+ }
109
+ /**
110
+ * Slice up to `limit` messages strictly older than the `before` cursor.
111
+ * The cursor points into a prefix of history, so turns appended after it was
112
+ * minted keep it valid; only a history that shrank (compaction/truncation)
113
+ * throws `cursor expired` — clients map that to a full re-`session/load`.
114
+ */
115
+ export function sliceBefore(messages, before, limit) {
116
+ const starts = turnStarts(messages);
117
+ const cur = decodeCursor(before);
118
+ if (cur.index > messages.length || cur.totalTurns > starts.length) {
119
+ return throwError(-32602, "cursor expired");
120
+ }
121
+ if (cur.id != null && cur.index < messages.length && messages[cur.index].info?.id !== cur.id) {
122
+ return throwError(-32602, "cursor expired");
123
+ }
124
+ const end = cur.index;
125
+ if (end === 0)
126
+ return buildSlice(messages, starts, 0, 0);
127
+ const clamped = clampLimit(limit);
128
+ const start = clamped === 0 ? end : alignToTurnStart(starts, Math.max(0, end - clamped));
129
+ return buildSlice(messages, starts, start, end);
130
+ }
131
+ /**
132
+ * Read the tail limit from `session/load`'s `_meta.zcode.limit`. The SDK's
133
+ * zod params schema strips unknown top-level keys, so bridge extensions ride
134
+ * in `_meta` (ADR-0003). Returns null when absent/non-finite = full replay.
135
+ */
136
+ export function readTailLimit(params) {
137
+ const zcode = params._meta?.zcode;
138
+ const raw = zcode?.limit;
139
+ if (typeof raw !== "number" || !Number.isFinite(raw))
140
+ return null;
141
+ return clampLimit(raw);
142
+ }
143
+ /** Fetch session/messages from zcode (the bridge's only history source). */
144
+ export async function fetchMessages(server, zcodeSid) {
145
+ const backend = server.ensureBackend();
146
+ const resp = await backend.request(server.nextId(), "session/messages", { sessionId: zcodeSid }, 8000);
147
+ if (resp.error) {
148
+ // Swallowed on purpose (replay must not crash the load) — but loudly: a
149
+ // silent empty here renders the whole conversation blank for the client.
150
+ warn(`session/messages failed for ${zcodeSid}: ${resp.error.message ?? ""}`);
151
+ return [];
152
+ }
153
+ const result = (resp.result ?? {});
154
+ return result.messages ?? [];
155
+ }
156
+ /**
157
+ * Strip harness-injected reminder blocks from user text. The agent runtime
158
+ * appends `<system-reminder>…</system-reminder>` blocks (TodoWrite nudges,
159
+ * context handoffs) to user turns as context plumbing — they are not user
160
+ * speech, and replaying them verbatim makes clients render them as user input.
161
+ */
162
+ function stripSystemReminders(text) {
163
+ return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
164
+ }
165
+ /**
166
+ * Replay messages as session/update notifications, oldest → newest.
167
+ *
168
+ * MUST run inside `withReplayBatch` for the session: this is the one sender
169
+ * that bypasses the per-message lock in sendSessionUpdate (the batch already
170
+ * holds it), which is what makes the batch atomic against live dispatch.
171
+ */
172
+ export async function replayMessages(cx, acpSid, messages) {
173
+ let replayed = 0;
174
+ for (const m of messages) {
175
+ const info = m.info ?? {};
176
+ const role = info.role;
177
+ const mid = info.id ?? `hist_${randomUUID().slice(0, 12)}`;
178
+ for (const p of m.parts ?? []) {
179
+ if (!p || typeof p !== "object")
180
+ continue;
181
+ const ptype = p.type;
182
+ if (ptype === "text") {
183
+ let text = p.text ?? "";
184
+ if (!text)
185
+ continue;
186
+ if (role === "user") {
187
+ text = stripSystemReminders(text);
188
+ if (!text)
189
+ continue;
190
+ }
191
+ await cx.notify("session/update", {
192
+ sessionId: acpSid,
193
+ update: {
194
+ sessionUpdate: role === "user" ? "user_message_chunk" : "agent_message_chunk",
195
+ content: { type: "text", text },
196
+ messageId: mid,
197
+ },
198
+ });
199
+ }
200
+ else if (ptype === "reasoning") {
201
+ const rp = p;
202
+ const text = rp.text ?? rp.content ?? "";
203
+ if (text) {
204
+ await cx.notify("session/update", {
205
+ sessionId: acpSid,
206
+ update: {
207
+ sessionUpdate: "agent_thought_chunk",
208
+ content: { type: "text", text },
209
+ messageId: `thought_${mid}`,
210
+ },
211
+ });
212
+ }
213
+ }
214
+ else if (ptype === "tool") {
215
+ const tp = p;
216
+ const title = tp.title ?? tp.tool ?? "tool call";
217
+ const histToolName = tp.tool ?? "";
218
+ await cx.notify("session/update", {
219
+ sessionId: acpSid,
220
+ update: {
221
+ sessionUpdate: "tool_call",
222
+ toolCallId: tp.id ?? `histtool_${randomUUID().slice(0, 8)}`,
223
+ title,
224
+ kind: "other",
225
+ status: tp.status ?? "completed",
226
+ ...(histToolName ? { _meta: { claudeCode: { toolName: histToolName } } } : {}),
227
+ },
228
+ });
229
+ }
230
+ // patch / step-start / other: skipped (history replay focuses on text + tool summary)
231
+ }
232
+ replayed += 1;
233
+ }
234
+ return replayed;
235
+ }
236
+ /**
237
+ * `session/load_earlier` — deliver one page of history strictly older than
238
+ * the `before` cursor, oldest → newest (clients prepend). Requires the
239
+ * session to already be attached in this bridge; pagination never triggers
240
+ * an implicit backend resume.
241
+ */
242
+ export async function loadEarlier(server, params, cx) {
243
+ const acpSid = params.sessionId;
244
+ const zcodeSid = server.resolveSid(acpSid);
245
+ if (!zcodeSid) {
246
+ return throwError(-32602, "session not registered — attach via session/load first");
247
+ }
248
+ if (!params.before)
249
+ return throwError(-32602, "before (cursor) required");
250
+ const messages = await fetchMessages(server, zcodeSid);
251
+ const slice = sliceBefore(messages, params.before, params.limit ?? DEFAULT_EARLIER_LIMIT);
252
+ await withReplayBatch(acpSid, () => replayMessages(cx, acpSid, slice.batch));
253
+ log(`session/load_earlier: ${slice.meta.replayedMessages} messages before cursor`);
254
+ return { replayMeta: slice.meta };
255
+ }
256
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/handlers/replay.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/handlers/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/handlers/session.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,8 @@ import { lookupLazySession, recordMaterializedSession, rememberLazySession, } fr
22
22
  import { buildDiffContent, EventTranslator, extractLocations, formatTurnError, isTransientTurnError, ProjectionDiffer, } from "../translators/index.js";
23
23
  import { log, warn } from "../utils.js";
24
24
  import { dispatchEvent } from "./dispatch.js";
25
- import { sendSessionUpdate, sendTextChunk } from "./io.js";
25
+ import { sendSessionUpdate, sendTextChunk, withReplayBatch } from "./io.js";
26
+ import { fetchMessages, fullSlice, readTailLimit, replayMessages, sliceTail } from "./replay.js";
26
27
  import { handleServerRequests } from "./server-requests.js";
27
28
  /** Workspace descriptor used in session create/resume calls. */
28
29
  function workspaceFor(cwd) {
@@ -70,6 +71,9 @@ export async function newSession(server, params) {
70
71
  // backend session materializes; never shown in session/list.
71
72
  const acpSid = randomUUID();
72
73
  server.pendingSessions.set(acpSid, { cwd, mcpServers: params.mcpServers });
74
+ // Persists past materialization (pendingSessions is cleared on first use) so
75
+ // the remote discovery payload can still label the workspace.
76
+ server.sessionCwds.set(acpSid, cwd);
73
77
  // Durable alias so the placeholder survives a bridge restart and session/
74
78
  // resume can still resolve it (best-effort; failures are swallowed inside
75
79
  // the store).
@@ -113,6 +117,7 @@ export async function ensureRealSession(server, acpSid) {
113
117
  if (record) {
114
118
  pending = { cwd: record.cwd };
115
119
  server.pendingSessions.set(acpSid, pending);
120
+ server.sessionCwds.set(acpSid, record.cwd);
116
121
  }
117
122
  }
118
123
  if (!pending)
@@ -155,6 +160,8 @@ export async function ensureRealSession(server, acpSid) {
155
160
  throw new Error("zcode create returned no sessionId");
156
161
  server.pendingSessions.delete(acpSid);
157
162
  server.registerSession(acpSid, sid);
163
+ // session/create loads the session into this backend process.
164
+ server.backendLoadedSessions.add(acpSid);
158
165
  // Keep the durable alias in sync so a later bridge restart can still
159
166
  // resume this session via the placeholder id.
160
167
  recordMaterializedSession(acpSid, sid, pending.cwd);
@@ -201,13 +208,47 @@ export async function listSessions(server, params) {
201
208
  log(`session/list → ${sessions.length} sessions`);
202
209
  return { sessions };
203
210
  }
211
+ /**
212
+ * Adopt the backend's stored title for a loaded/resumed session.
213
+ *
214
+ * The prompt loop's auto-title only fires for freshly created sessions
215
+ * (`titleEligibleSessions`), so a session resumed across a bridge restart
216
+ * would otherwise appear title-less in the hub's discovery API — remote
217
+ * clients have no editor-side session storage to fall back on. The backend's
218
+ * session/list is the only title source for sessions born in a previous
219
+ * bridge lifetime. Best-effort: failures log and leave the session untitled.
220
+ */
221
+ async function adoptStoredTitle(server, acpSid, zcodeSid) {
222
+ if (server.sessionTitles.has(acpSid))
223
+ return;
224
+ try {
225
+ const backend = server.ensureBackend();
226
+ const resp = await backend.request(server.nextId(), "session/list", {}, 15000);
227
+ if (resp.error)
228
+ return;
229
+ const result = (resp.result ?? {});
230
+ const hit = (result.sessions ?? []).find((s) => s.sessionId === zcodeSid);
231
+ if (hit?.title) {
232
+ server.sessionTitles.set(acpSid, hit.title);
233
+ server.touchSessionSummary(acpSid, hit.title);
234
+ log(`adopted stored title for ${acpSid.slice(0, 8)}: ${hit.title}`);
235
+ }
236
+ }
237
+ catch (e) {
238
+ log(`stored title lookup failed (non-fatal): ${e instanceof Error ? e.message : String(e)}`);
239
+ }
240
+ }
204
241
  /**
205
242
  * Resolve the backend session id for `session/resume` / `session/load`.
206
243
  *
207
244
  * A `session/new` placeholder has no backend counterpart until first use, yet
208
245
  * the editor may resume it anyway (panel reopen, bridge restart) — resolving it
209
246
  * here prevents an otherwise unavoidable "Session not found". Resolution order:
210
- * 1. in-memory mapping → the session is already live in this subprocess;
247
+ * 1. in-memory mapping → live only if verified loaded in this backend
248
+ * subprocess (`backendLoadedSessions`); a bare mapping may have been
249
+ * re-registered from the durable store without a resume, and the backend
250
+ * only serves messages for sessions it has loaded — those must fall
251
+ * through to the resume RPC or the replay comes back empty;
211
252
  * 2. pending placeholder → materialize it (an empty session, matching the
212
253
  * pre-lazy behavior where a never-used session/new always resumed);
213
254
  * 3. durable store → a placeholder from a previous bridge lifetime: with a
@@ -219,8 +260,9 @@ export async function listSessions(server, params) {
219
260
  */
220
261
  async function resolveResumeTarget(server, acpSid) {
221
262
  const mapped = server.resolveSid(acpSid);
222
- if (mapped)
223
- return { zcodeSid: mapped, alreadyLive: true };
263
+ if (mapped) {
264
+ return { zcodeSid: mapped, alreadyLive: server.backendLoadedSessions.has(acpSid) };
265
+ }
224
266
  if (server.pendingSessions.has(acpSid)) {
225
267
  return { zcodeSid: await ensureRealSession(server, acpSid), alreadyLive: true };
226
268
  }
@@ -269,10 +311,13 @@ export async function resumeSession(server, params, cx) {
269
311
  // registered to even process the resume turn.
270
312
  await syncProviderRegistry(server, cwd);
271
313
  await resumeBackendSession(server, zcParams);
314
+ // The resume RPC succeeded — the session is now loaded in this backend.
315
+ server.backendLoadedSessions.add(acpSid);
272
316
  }
273
317
  server.registerSession(acpSid, zcodeSid);
274
318
  log(`session/resume -> ${zcodeSid}`);
275
319
  server.ensureBackgroundListener(zcodeSid);
320
+ await adoptStoredTitle(server, acpSid, zcodeSid);
276
321
  // Initial usage_update so the editor shows the context bar immediately for a
277
322
  // resumed session (mirrors Python _on_session_resume → _emit_initial_usage).
278
323
  await emitInitialUsage(server, cx, acpSid, zcodeSid, getOrCreateDiffer(server, zcodeSid));
@@ -308,61 +353,26 @@ export async function loadSession(server, params, cx) {
308
353
  // registered to process it.
309
354
  await syncProviderRegistry(server, cwd);
310
355
  await resumeBackendSession(server, zcParams);
356
+ // The resume RPC succeeded — the session is now loaded in this backend.
357
+ server.backendLoadedSessions.add(acpSid);
311
358
  }
312
359
  server.registerSession(acpSid, zcodeSid);
313
360
  log(`session/load → ${zcodeSid}`);
314
361
  server.ensureBackgroundListener(zcodeSid);
362
+ await adoptStoredTitle(server, acpSid, zcodeSid);
315
363
  const messages = await fetchMessages(server, zcodeSid);
316
- let replayed = 0;
317
- for (const m of messages) {
318
- const info = m.info ?? {};
319
- const role = info.role;
320
- const mid = info.id ?? `hist_${randomUUID().slice(0, 12)}`;
321
- for (const p of m.parts ?? []) {
322
- if (!p || typeof p !== "object")
323
- continue;
324
- const ptype = p.type;
325
- if (ptype === "text") {
326
- const text = p.text ?? "";
327
- if (!text)
328
- continue;
329
- const sessionUpdate = role === "user" ? "user_message_chunk" : "agent_message_chunk";
330
- await sendSessionUpdate(cx, acpSid, {
331
- sessionUpdate,
332
- content: { type: "text", text },
333
- messageId: mid,
334
- });
335
- }
336
- else if (ptype === "reasoning") {
337
- const rp = p;
338
- const text = rp.text ?? rp.content ?? "";
339
- if (text) {
340
- await sendSessionUpdate(cx, acpSid, {
341
- sessionUpdate: "agent_thought_chunk",
342
- content: { type: "text", text },
343
- messageId: `thought_${mid}`,
344
- });
345
- }
346
- }
347
- else if (ptype === "tool") {
348
- const tp = p;
349
- const title = tp.title ?? tp.tool ?? "tool call";
350
- const histToolName = tp.tool ?? "";
351
- const update = {
352
- sessionUpdate: "tool_call",
353
- toolCallId: tp.id ?? `histtool_${randomUUID().slice(0, 8)}`,
354
- title,
355
- kind: "other",
356
- status: tp.status ?? "completed",
357
- ...(histToolName ? { _meta: { claudeCode: { toolName: histToolName } } } : {}),
358
- };
359
- await sendSessionUpdate(cx, acpSid, update);
360
- }
361
- // patch / step-start / other: skipped (history replay focuses on text + tool summary)
362
- }
363
- replayed += 1;
364
- }
365
- log(`session/load: replayed ${replayed} messages`);
364
+ // History on disk = real interaction (covers untitled sessions resumed from
365
+ // a previous bridge lifetime) — make the session discoverable remotely.
366
+ if (messages.length > 0)
367
+ server.markSessionActive(acpSid);
368
+ // Tail replay (Proposal 0001): a `_meta.zcode.limit` replays only the last
369
+ // N messages aligned to turn boundaries — the full replay stays the default
370
+ // for editors that send no `_meta` (Zed path unchanged).
371
+ const limit = readTailLimit(params);
372
+ const slice = limit === null ? fullSlice(messages) : sliceTail(messages, limit);
373
+ await withReplayBatch(acpSid, () => replayMessages(cx, acpSid, slice.batch));
374
+ log(`session/load: replayed ${slice.meta.replayedMessages} messages` +
375
+ `${limit === null ? "" : ` (tail limit ${limit}, total ${slice.meta.totalMessages})`}`);
366
376
  // Replay the existing todo list as an initial plan so a loaded session shows
367
377
  // its todos immediately (filter to PlanUpdate only — text/tools were already
368
378
  // replayed above and the differ hasn't mark_seen'd this history).
@@ -381,10 +391,13 @@ export async function loadSession(server, params, cx) {
381
391
  await emitInitialUsage(server, cx, acpSid, zcodeSid, getOrCreateDiffer(server, zcodeSid));
382
392
  const modes = await buildModes(server, zcodeSid);
383
393
  server.lastMode.set(acpSid, modes.currentModeId);
384
- return {
394
+ const result = {
385
395
  modes,
386
396
  configOptions: await buildConfigOptions(server, zcodeSid),
397
+ // Additive replay metadata — the anchor for load_earlier pagination.
398
+ replayMeta: slice.meta,
387
399
  };
400
+ return result;
388
401
  }
389
402
  /** `session/prompt` → subscribe-before-send, run the event-driven turn loop. */
390
403
  export async function prompt(server, params, cx, requestId) {
@@ -400,11 +413,17 @@ export async function prompt(server, params, cx, requestId) {
400
413
  // empty-prompt check so an invalid request doesn't create a backend session.
401
414
  const zcodeSid = await ensureRealSession(server, params.sessionId);
402
415
  // Slash-command interception: dispatches directly to ZCode methods and
403
- // returns end_turn without entering the turn loop. Unknown /x falls through.
404
- const { handleSlashCommand } = await import("./slash.js");
416
+ // returns end_turn without entering the turn loop. Known passthrough
417
+ // commands and unknown /x both return null for the normal turn loop.
418
+ const { handleSlashCommand, neutralizeSlashText } = await import("./slash.js");
405
419
  const intercepted = await handleSlashCommand(server, cx, params.sessionId, zcodeSid, text);
406
420
  if (intercepted)
407
421
  return intercepted;
422
+ // Wire text for the backend: unknown `/x` prompts (not advertised commands)
423
+ // are neutralized so the backend's command resolver never sees them — an
424
+ // unresolvable name can hard-fail the turn. Known commands pass through
425
+ // unchanged. The title/auto-compact paths below keep using the raw `text`.
426
+ const sendText = neutralizeSlashText(text);
408
427
  // Register self + preempt others under a per-session lock. The lock
409
428
  // serializes the critical section so that two concurrent prompts (B, C) for
410
429
  // the same session can't both miss each other and register at once: C waits
@@ -491,8 +510,8 @@ export async function prompt(server, params, cx, requestId) {
491
510
  const SEND_RETRY_INTERVAL_MS = 500;
492
511
  const SEND_RETRY_TIMEOUT_MS = 30_000;
493
512
  const sendParams = attachments.length > 0
494
- ? { sessionId: zcodeSid, content: text, attachments }
495
- : { sessionId: zcodeSid, content: text };
513
+ ? { sessionId: zcodeSid, content: sendText, attachments }
514
+ : { sessionId: zcodeSid, content: sendText };
496
515
  const sendT0 = Date.now();
497
516
  let sendAttempt = 0;
498
517
  while (true) {
@@ -556,6 +575,7 @@ export async function prompt(server, params, cx, requestId) {
556
575
  .find((l) => l.length > 0)
557
576
  ?.slice(0, 80) ?? text.slice(0, 80);
558
577
  server.sessionTitles.set(params.sessionId, title);
578
+ server.touchSessionSummary(params.sessionId, title);
559
579
  const { updateSessionTitle } = await import("../tasks-index.js");
560
580
  void updateSessionTitle(zcodeSid, title, text);
561
581
  await sendSessionUpdate(cx, params.sessionId, {
@@ -603,6 +623,10 @@ export async function prompt(server, params, cx, requestId) {
603
623
  finally {
604
624
  backend.unregisterEventListener(zcodeSid, listener);
605
625
  server.pendingTurns.delete(requestId);
626
+ // Turn end = session activity — refresh the discovery summary and mark the
627
+ // session discoverable regardless of outcome (end_turn, cancelled, retries
628
+ // exhausted).
629
+ server.markSessionActive(params.sessionId);
606
630
  }
607
631
  }
608
632
  /**
@@ -914,15 +938,6 @@ async function resumeBackendSession(server, zcParams) {
914
938
  await sleep(1000);
915
939
  }
916
940
  }
917
- /** Fetch session/messages from zcode. */
918
- async function fetchMessages(server, zcodeSid) {
919
- const backend = server.ensureBackend();
920
- const resp = await backend.request(server.nextId(), "session/messages", { sessionId: zcodeSid }, 8000);
921
- if (resp.error)
922
- return [];
923
- const result = (resp.result ?? {});
924
- return result.messages ?? [];
925
- }
926
941
  /** Get or create the session-level ProjectionDiffer (persists across turns). */
927
942
  function getOrCreateDiffer(server, zcodeSid) {
928
943
  let d = server.differs.get(zcodeSid);