@jmfederico/pi-web 1.202607.2 → 1.202608.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 (103) hide show
  1. package/README.md +1 -1
  2. package/dist/client/assets/{CodeViewer-B4M13447.js → CodeViewer-D91Sp61M.js} +1 -1
  3. package/dist/client/assets/{TerminalPanel-Bmvj-Ecn.js → TerminalPanel-fYrKc3jh.js} +2 -2
  4. package/dist/client/assets/{UnifiedDiffViewer-C0hygwRo.js → UnifiedDiffViewer-BafLWGtF.js} +1 -1
  5. package/dist/client/assets/index-CA8q9_o7.js +4025 -0
  6. package/dist/client/index.html +1 -1
  7. package/dist/config.js +39 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/pi-web-plugins/info/infoInternals.js +208 -0
  10. package/dist/pi-web-plugins/info/pi-web-plugin.js +12 -15
  11. package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
  12. package/dist/pi-web-plugins/relays/package.json +9 -0
  13. package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
  14. package/dist/pi-web-plugins/relays/relayDiscovery.js +228 -0
  15. package/dist/pi-web-plugins/relays/relaysPanelElement.js +550 -0
  16. package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
  17. package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
  18. package/dist/plugin-api.d.ts +7 -1
  19. package/dist/server/app.js +0 -22
  20. package/dist/server/app.js.map +1 -1
  21. package/dist/server/browserMessageProjection.js +0 -2
  22. package/dist/server/browserMessageProjection.js.map +1 -1
  23. package/dist/server/configRoutes.js +13 -11
  24. package/dist/server/configRoutes.js.map +1 -1
  25. package/dist/server/machines/machineProxyRoutes.js +2 -13
  26. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  27. package/dist/server/piWebStatus.js +12 -38
  28. package/dist/server/piWebStatus.js.map +1 -1
  29. package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
  30. package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
  31. package/dist/server/sessiond/agentProcessEnvironment.js +62 -0
  32. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
  33. package/dist/server/sessiond/sessionServiceDependencies.js +33 -0
  34. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
  35. package/dist/server/sessiond.js +145 -116
  36. package/dist/server/sessiond.js.map +1 -1
  37. package/dist/server/sessions/askUserTool.js +102 -0
  38. package/dist/server/sessions/askUserTool.js.map +1 -0
  39. package/dist/server/sessions/authRoutes.js +0 -10
  40. package/dist/server/sessions/authRoutes.js.map +1 -1
  41. package/dist/server/sessions/authService.js +0 -24
  42. package/dist/server/sessions/authService.js.map +1 -1
  43. package/dist/server/sessions/extensionDialogWaiters.js +76 -0
  44. package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
  45. package/dist/server/sessions/globalProviderPolicy.js +79 -10
  46. package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
  47. package/dist/server/sessions/messagePaging.js +2 -2
  48. package/dist/server/sessions/messagePaging.js.map +1 -1
  49. package/dist/server/sessions/modelCatalogRefresher.js +8 -0
  50. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  51. package/dist/server/sessions/oauthLoginFlowService.js +2 -5
  52. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
  53. package/dist/server/sessions/parentSessionLocator.js +75 -0
  54. package/dist/server/sessions/parentSessionLocator.js.map +1 -0
  55. package/dist/server/sessions/pendingAskStore.js +275 -0
  56. package/dist/server/sessions/pendingAskStore.js.map +1 -0
  57. package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
  58. package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
  59. package/dist/server/sessions/piSessionManagerGateway.js +182 -2
  60. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  61. package/dist/server/sessions/piSessionService.js +721 -203
  62. package/dist/server/sessions/piSessionService.js.map +1 -1
  63. package/dist/server/sessions/sessionFileHeader.js +117 -0
  64. package/dist/server/sessions/sessionFileHeader.js.map +1 -0
  65. package/dist/server/sessions/sessionRoutes.js +169 -54
  66. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  67. package/dist/server/sessions/sessionSummaryScanner.js +619 -0
  68. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
  69. package/dist/server/sessions/spawnSessionTool.js +8 -1
  70. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  71. package/dist/server/sessions/spawnSubsessionTool.js +7 -1
  72. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  73. package/dist/server/sessions/spawnTargetResolver.js +3 -16
  74. package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
  75. package/dist/server/workspaces/effectivePathAccess.js +0 -19
  76. package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
  77. package/dist/server/workspaces/gitWorktreeDiscovery.js +9 -0
  78. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
  79. package/dist/server/workspaces/projectWorkspaceCwds.js +22 -0
  80. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
  81. package/dist/server/workspaces/workspaceDeletionRoutes.js +30 -4
  82. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  83. package/dist/server/workspaces/workspaceService.js +15 -2
  84. package/dist/server/workspaces/workspaceService.js.map +1 -1
  85. package/dist/sessiond/config.js +2 -2
  86. package/dist/sessiond/config.js.map +1 -1
  87. package/dist/shared/activity.js +9 -1
  88. package/dist/shared/activity.js.map +1 -1
  89. package/dist/shared/apiTypes.d.ts +300 -24
  90. package/dist/shared/apiTypes.js +30 -15
  91. package/dist/shared/apiTypes.js.map +1 -1
  92. package/dist/shared/capabilities.js +10 -41
  93. package/dist/shared/capabilities.js.map +1 -1
  94. package/dist/shared/federatedRoutes.js +4 -3
  95. package/dist/shared/federatedRoutes.js.map +1 -1
  96. package/docs/config.md +115 -11
  97. package/docs/plugins.md +82 -14
  98. package/package.json +11 -9
  99. package/dist/client/assets/index-TvPsRyS1.js +0 -3545
  100. package/dist/server/sessiond/sessionDaemonStartup.js +0 -49
  101. package/dist/server/sessiond/sessionDaemonStartup.js.map +0 -1
  102. package/dist/server/sessions/sessionArchiveMigration.js +0 -590
  103. package/dist/server/sessions/sessionArchiveMigration.js.map +0 -1
@@ -0,0 +1,619 @@
1
+ import { open, readdir, stat } from "node:fs/promises";
2
+ import { join, sep } from "node:path";
3
+ /**
4
+ * Fast-path classification prefix of a session file line, as raw bytes.
5
+ *
6
+ * The Pi SDK writes every entry with `type` as the first JSON key, so the
7
+ * entry type can normally be read directly from the line's first bytes without
8
+ * decoding or parsing it. This is what lets a listing skip the (potentially
9
+ * huge) message bodies entirely: only lines whose type actually matters for
10
+ * the summary are ever decoded to strings and JSON-parsed.
11
+ */
12
+ const ENTRY_TYPE_PREFIX = Buffer.from('{"type":"');
13
+ const TYPE_QUOTE = 0x22; // `"`
14
+ const NEWLINE = 0x0a;
15
+ const CARRIAGE_RETURN = 0x0d;
16
+ const CLOSING_BRACE = 0x7d; // `}`
17
+ const SPACE = 0x20;
18
+ const TAB = 0x09;
19
+ /** The entry types the byte fast path recognizes, as raw bytes: type names are never decoded. */
20
+ const MESSAGE_TYPE_BYTES = Buffer.from("message");
21
+ const SESSION_INFO_TYPE_BYTES = Buffer.from("session_info");
22
+ /** Same bound the SDK uses for its concurrent session-info builds. */
23
+ const MAX_CONCURRENT_SESSION_SUMMARY_SCANS = 10;
24
+ /** Default read chunk size for the streaming pass; see SessionSummaryScannerOptions. */
25
+ const SCAN_CHUNK_BYTES = 4 * 1024 * 1024;
26
+ /** Types longer than this fall back to a full parse instead of byte classification. */
27
+ const MAX_CLASSIFIED_TYPE_LENGTH = 64;
28
+ /**
29
+ * List the sessions in one session directory with one lightweight streaming
30
+ * pass per file, instead of the SDK's full-transcript listing.
31
+ *
32
+ * Stateless: every call re-reads every file. Daemon listings should use
33
+ * {@link SessionSummaryScanner}, which memoizes these summaries across calls.
34
+ *
35
+ * The summary fields mirror the SDK listing (`SessionManager.listAll`):
36
+ * `messageCount` counts every `message` entry, `firstMessage` is the first
37
+ * user message with non-empty text content, `name` is the latest `session_info`
38
+ * name (an empty or missing name clears it), and `created`/`id`/`cwd`/
39
+ * `parentSessionPath` come from the header line. Three deliberate differences:
40
+ *
41
+ * - `modified` is the file mtime rather than the last message timestamp.
42
+ * Session files are append-only, so the mtime is a faithful "last activity"
43
+ * for listing order, the only thing `modified` is used for.
44
+ * - `allMessagesText` is always empty. Building it required parsing every
45
+ * message body — the cost this scanner exists to remove — and PI WEB never
46
+ * consumes it.
47
+ * - `messageCount` can transiently include a final write read mid-flight: a
48
+ * message-shaped line that ends with `}` counts even though its JSON is
49
+ * never validated, where the SDK fails to parse such a torn line. The count
50
+ * self-heals on the next listing once the line completes (an unterminated
51
+ * final line is always re-folded from its start).
52
+ *
53
+ * Files whose header is missing, unreadable, or not a session header are
54
+ * skipped, like the SDK does. Results are sorted by `modified` descending.
55
+ */
56
+ export async function scanSessionSummariesInDir(sessionDir) {
57
+ const files = await listSessionFilesInDir(sessionDir);
58
+ const summaries = await scanSessionFilesWithBoundedConcurrency(files, SCAN_CHUNK_BYTES, summarizeWholeFile);
59
+ return sortedSessionSummaries(summaries);
60
+ }
61
+ /**
62
+ * Summarize one session file in a single streaming pass.
63
+ *
64
+ * Per-line work is minimal: lines are classified from their leading
65
+ * `{"type":"..."` bytes without ever decoding them, and lines are only turned
66
+ * into strings and JSON-parsed when they matter — the header, `session_info`
67
+ * lines (rare, one per rename), and message lines until the first user text
68
+ * message has been found. Message bodies after that point (which hold the huge
69
+ * tool results and assistant replies) are neither decoded nor parsed.
70
+ *
71
+ * Returns undefined when the file is not a usable session (unreadable, empty,
72
+ * or first parseable entry is not a session header), matching the SDK.
73
+ */
74
+ export async function scanSessionFileSummary(filePath, chunkBuffer) {
75
+ return summarizeWholeFile(filePath, () => chunkBuffer ?? Buffer.allocUnsafe(SCAN_CHUNK_BYTES));
76
+ }
77
+ /** The streaming single-pass summary; `chunkBuffer` supplies the read buffer lazily. */
78
+ async function summarizeWholeFile(filePath, chunkBuffer) {
79
+ const scanned = await foldWholeSessionFile(filePath, chunkBuffer);
80
+ if (scanned === undefined)
81
+ return undefined;
82
+ return buildSummaryFromFold(scanned.fold, filePath, scanned.outcome.mtime);
83
+ }
84
+ /**
85
+ * Session summary scanner with a per-file memo, so repeated listings of the
86
+ * same session directory do not re-read transcripts that did not change.
87
+ *
88
+ * The memo is a last resort on top of the lightweight streaming scan and is
89
+ * deliberately trivial to invalidate — it never holds anything the file
90
+ * itself cannot re-derive:
91
+ *
92
+ * - Key: absolute file path. Trusted value: file identity (dev/ino) plus the
93
+ * size already parsed.
94
+ * - Identity and size unchanged → cached summary. The mtime is re-read from
95
+ * the same stat, so `modified` stays faithful even on a cache hit.
96
+ * - Identity unchanged and the file grew → session files are append-only, so
97
+ * only the appended tail is read and folded into the cached summary state.
98
+ * A trailing line that was still being written when it was cached is
99
+ * re-folded from its start, so completed lines are never counted twice.
100
+ * Identity here is re-verified on the opened file handle, not just the
101
+ * pathname stat: a replacement that lands between the two is fully
102
+ * re-parsed instead of being folded into the old summary.
103
+ * - Identity changed or the file shrunk → cached state is dropped and the
104
+ * file is fully re-parsed.
105
+ * - File gone (ENOENT) → its entry is dropped; entries for files that no
106
+ * longer appear in the directory listing are pruned on each scan.
107
+ * - {@link clear} drops every entry. There are no TTLs and nothing is
108
+ * persisted: the memo is an in-process speedup, and a daemon restart starts
109
+ * cold but correct.
110
+ *
111
+ * The one thing the key cannot detect is an in-place rewrite that keeps the
112
+ * inode and never shrinks the file — including the equal-size rewrite served
113
+ * by the stat-only fast path. The SDK never rewrites session files, but
114
+ * PI WEB's detach does (it clears the header), so callers that rewrite a file
115
+ * in place must call {@link invalidate} for it; {@link clear} remains the
116
+ * escape hatch for unknown external rewrites.
117
+ */
118
+ export class SessionSummaryScanner {
119
+ constructor(options = {}) {
120
+ this.memo = new Map();
121
+ const chunkBytes = options.chunkBytes ?? SCAN_CHUNK_BYTES;
122
+ if (!Number.isInteger(chunkBytes) || chunkBytes <= 0) {
123
+ throw new TypeError(`SessionSummaryScanner options.chunkBytes must be a positive integer, got ${String(chunkBytes)}`);
124
+ }
125
+ this.chunkBytes = chunkBytes;
126
+ }
127
+ /** Drop every cached summary, forcing full re-parses on the next listing. */
128
+ clear() {
129
+ this.memo.clear();
130
+ }
131
+ /**
132
+ * Drop the cached summary for one file, forcing a full re-parse of it on
133
+ * the next listing. Callers that rewrite a session file in place (keeping
134
+ * the inode) must invalidate it, since identity + size checks cannot detect
135
+ * such rewrites. Dropping a path that is not memoized is a no-op.
136
+ */
137
+ invalidate(filePath) {
138
+ this.memo.delete(filePath);
139
+ }
140
+ /**
141
+ * The memoized equivalent of {@link scanSessionSummariesInDir}: same fields,
142
+ * same order, same skip rules, but files that have not changed since the
143
+ * previous scan of their directory are answered from the memo (one stat
144
+ * each), and grown files only parse their appended tail.
145
+ */
146
+ async scanSessionSummariesInDir(sessionDir) {
147
+ const files = await listSessionFilesInDir(sessionDir);
148
+ this.pruneEntriesRemovedFrom(sessionDir, files);
149
+ const summaries = await scanSessionFilesWithBoundedConcurrency(files, this.chunkBytes, (file, chunkBuffer) => this.scanFileWithMemo(file, chunkBuffer));
150
+ return sortedSessionSummaries(summaries);
151
+ }
152
+ pruneEntriesRemovedFrom(sessionDir, existingFiles) {
153
+ const dirPrefix = sessionDir.endsWith(sep) ? sessionDir : sessionDir + sep;
154
+ const existing = new Set(existingFiles);
155
+ for (const path of this.memo.keys()) {
156
+ // Deletion invalidates automatically: entries for the scanned directory
157
+ // whose file no longer exists are dropped, keeping the memo bounded.
158
+ if (path.startsWith(dirPrefix) && !existing.has(path))
159
+ this.memo.delete(path);
160
+ }
161
+ }
162
+ async scanFileWithMemo(filePath, chunkBuffer) {
163
+ const memoized = this.memo.get(filePath);
164
+ if (memoized === undefined)
165
+ return this.fullScan(filePath, chunkBuffer);
166
+ let stats;
167
+ try {
168
+ stats = await stat(filePath);
169
+ }
170
+ catch {
171
+ // Went away between readdir and stat: drop it and skip, like the SDK.
172
+ this.memo.delete(filePath);
173
+ return undefined;
174
+ }
175
+ if (stats.dev !== memoized.dev || stats.ino !== memoized.ino || stats.size < memoized.size) {
176
+ // Replaced or shrunk: the cached prefix is no longer trustworthy.
177
+ return this.fullScan(filePath, chunkBuffer);
178
+ }
179
+ if (stats.size === memoized.size) {
180
+ // Stat-only fast path: nothing was appended, so no open or fold. Its
181
+ // one blind spot is an equal-size in-place rewrite that keeps the
182
+ // inode; identity + size cannot see it by design (see the class docs).
183
+ return buildSummaryFromFold(memoized.completeFold, filePath, stats.mtime);
184
+ }
185
+ // Growth on the same identity: append-only session files can only have
186
+ // gained a tail, so fold just the new bytes into the cached state.
187
+ return this.foldGrowth(filePath, memoized, chunkBuffer);
188
+ }
189
+ /**
190
+ * Fold the appended tail of a memoized file into its cached summary,
191
+ * re-verifying identity on the opened handle. The pathname stat only
192
+ * selected this branch: if a replacement landed between that stat and the
193
+ * open, the handle carries a different dev/ino and the opened file is
194
+ * re-parsed from the start instead of folding its tail into the old
195
+ * summary (same for a file that shrunk below the cached size after the
196
+ * stat). All metadata memoized afterwards comes from the handle, so the
197
+ * memo only ever describes the file it actually read.
198
+ */
199
+ async foldGrowth(filePath, memoized, chunkBuffer) {
200
+ const opened = await openSessionFile(filePath);
201
+ if (opened === undefined) {
202
+ // Became unreadable mid-scan: skip it (like the SDK) and forget it.
203
+ this.memo.delete(filePath);
204
+ return undefined;
205
+ }
206
+ try {
207
+ const { file, stats } = opened;
208
+ const cachedPrefixUsable = stats.dev === memoized.dev && stats.ino === memoized.ino && stats.size >= memoized.size;
209
+ const fold = cachedPrefixUsable ? { ...memoized.resumeFold } : createEmptyFold();
210
+ const startOffset = cachedPrefixUsable ? memoized.resumeOffset : 0;
211
+ const outcome = await foldSessionFileRange(file, stats, fold, startOffset, chunkBuffer());
212
+ if (outcome === undefined) {
213
+ this.memo.delete(filePath);
214
+ return undefined;
215
+ }
216
+ this.memo.set(filePath, {
217
+ dev: outcome.dev,
218
+ ino: outcome.ino,
219
+ size: outcome.size,
220
+ resumeOffset: outcome.resumeOffset,
221
+ resumeFold: outcome.resumeFold,
222
+ completeFold: fold,
223
+ });
224
+ return buildSummaryFromFold(fold, filePath, outcome.mtime);
225
+ }
226
+ finally {
227
+ await opened.file.close().catch(() => undefined);
228
+ }
229
+ }
230
+ async fullScan(filePath, chunkBuffer) {
231
+ const scanned = await foldWholeSessionFile(filePath, chunkBuffer);
232
+ if (scanned === undefined) {
233
+ this.memo.delete(filePath);
234
+ return undefined;
235
+ }
236
+ this.memo.set(filePath, {
237
+ dev: scanned.outcome.dev,
238
+ ino: scanned.outcome.ino,
239
+ size: scanned.outcome.size,
240
+ resumeOffset: scanned.outcome.resumeOffset,
241
+ resumeFold: scanned.outcome.resumeFold,
242
+ completeFold: scanned.fold,
243
+ });
244
+ return buildSummaryFromFold(scanned.fold, filePath, scanned.outcome.mtime);
245
+ }
246
+ }
247
+ function createEmptyFold() {
248
+ return { header: undefined, rejected: false, messageCount: 0, firstMessageText: undefined, name: undefined };
249
+ }
250
+ /** Open a session file for reading and fstat the opened handle. */
251
+ async function openSessionFile(filePath) {
252
+ let file;
253
+ try {
254
+ file = await open(filePath, "r");
255
+ }
256
+ catch {
257
+ return undefined;
258
+ }
259
+ try {
260
+ const stats = await file.stat();
261
+ return { file, stats };
262
+ }
263
+ catch {
264
+ await file.close().catch(() => undefined);
265
+ return undefined;
266
+ }
267
+ }
268
+ /** Open `filePath` and fold its entire contents; undefined when unreadable. */
269
+ async function foldWholeSessionFile(filePath, chunkBuffer) {
270
+ const opened = await openSessionFile(filePath);
271
+ if (opened === undefined)
272
+ return undefined;
273
+ try {
274
+ const fold = createEmptyFold();
275
+ const outcome = await foldSessionFileRange(opened.file, opened.stats, fold, 0, chunkBuffer());
276
+ if (outcome === undefined)
277
+ return undefined;
278
+ return { outcome, fold };
279
+ }
280
+ finally {
281
+ await opened.file.close().catch(() => undefined);
282
+ }
283
+ }
284
+ /**
285
+ * Fold the lines of an open session file from `startOffset` to end-of-file
286
+ * into `fold`, reading through `file`. The caller owns the handle and keeps
287
+ * it open for the whole fold, so the fold always reads the file it was
288
+ * opened on, even if the path is replaced concurrently. `stats` must be that
289
+ * handle's own fstat: all identity and metadata in the outcome come from it.
290
+ *
291
+ * On return, `fold` holds the state for the whole range including any
292
+ * unterminated trailing line, while the returned `resumeFold`/`resumeOffset`
293
+ * describe the range up to the last safe line boundary — the starting point
294
+ * for an incremental tail parse once the file grows.
295
+ *
296
+ * Returns undefined when the file cannot be read (unreadable or vanished
297
+ * mid-scan).
298
+ */
299
+ async function foldSessionFileRange(file, stats, fold, startOffset, chunkBuffer) {
300
+ try {
301
+ const outcome = await foldFileLines(file, fold, startOffset, chunkBuffer);
302
+ if (fold.rejected) {
303
+ // Rejection is final — the first parseable line decides it — so a
304
+ // rejected fold is memoized at the observed end of file: later listings
305
+ // take the stat-only fast path instead of resuming from a mid-file
306
+ // offset and re-reading one chunk per listing.
307
+ const endSize = Math.max(stats.size, outcome.endOffset);
308
+ return { dev: stats.dev, ino: stats.ino, size: endSize, mtime: stats.mtime, resumeOffset: endSize, resumeFold: outcome.resumeFold };
309
+ }
310
+ return { dev: stats.dev, ino: stats.ino, size: outcome.endOffset, mtime: stats.mtime, resumeOffset: outcome.resumeOffset, resumeFold: outcome.resumeFold };
311
+ }
312
+ catch {
313
+ // One unreadable/corrupt file must not break the listing; the SDK skips
314
+ // such files too.
315
+ return undefined;
316
+ }
317
+ }
318
+ /** The streaming line fold over an open file range; see foldSessionFileRange. */
319
+ async function foldFileLines(file, fold, startOffset, chunkBuffer) {
320
+ let position = startOffset;
321
+ let pendingChunks = [];
322
+ let pendingLineStart = startOffset;
323
+ for (;;) {
324
+ const { bytesRead } = await file.read(chunkBuffer, 0, chunkBuffer.length, position);
325
+ const reachedEnd = bytesRead === 0;
326
+ const data = chunkBuffer.subarray(0, bytesRead);
327
+ let lineStart = 0;
328
+ let newlineAt = data.indexOf(NEWLINE);
329
+ while (newlineAt !== -1) {
330
+ if (pendingChunks.length > 0) {
331
+ // A line longer than one chunk: join the saved pieces and finish it.
332
+ pendingChunks.push(Buffer.from(data.subarray(lineStart, newlineAt)));
333
+ const whole = Buffer.concat(pendingChunks);
334
+ pendingChunks = [];
335
+ processLineBytes(whole, 0, whole.length, fold);
336
+ }
337
+ else {
338
+ processLineBytes(data, lineStart, newlineAt, fold);
339
+ }
340
+ if (fold.rejected)
341
+ break;
342
+ lineStart = newlineAt + 1;
343
+ newlineAt = data.indexOf(NEWLINE, lineStart);
344
+ }
345
+ if (fold.rejected) {
346
+ const endOffset = position + data.length;
347
+ return { resumeOffset: endOffset, endOffset, resumeFold: fold };
348
+ }
349
+ if (reachedEnd) {
350
+ const endOffset = position + data.length;
351
+ if (pendingChunks.length === 0 && lineStart >= data.length) {
352
+ return { resumeOffset: endOffset, endOffset, resumeFold: fold };
353
+ }
354
+ // Final line without a trailing newline (foreign writers, or a line
355
+ // still being written when we read): fold it for this listing's result,
356
+ // but keep an un-folded copy and the line's start offset — appended
357
+ // bytes may complete this line, and it must then be re-folded whole.
358
+ const resumeOffset = pendingChunks.length > 0 ? pendingLineStart : position + lineStart;
359
+ const resumeFold = { ...fold };
360
+ if (pendingChunks.length > 0) {
361
+ pendingChunks.push(Buffer.from(data.subarray(lineStart)));
362
+ const whole = Buffer.concat(pendingChunks);
363
+ processLineBytes(whole, 0, whole.length, fold);
364
+ }
365
+ else {
366
+ processLineBytes(data, lineStart, data.length, fold);
367
+ }
368
+ return { resumeOffset, endOffset, resumeFold };
369
+ }
370
+ if (lineStart < data.length) {
371
+ if (pendingChunks.length === 0)
372
+ pendingLineStart = position + lineStart;
373
+ pendingChunks.push(Buffer.from(data.subarray(lineStart)));
374
+ }
375
+ position += bytesRead;
376
+ }
377
+ }
378
+ /** The listing entry for a fold, or undefined when the file is not a usable session. */
379
+ function buildSummaryFromFold(fold, filePath, mtime) {
380
+ if (fold.rejected || fold.header === undefined)
381
+ return undefined;
382
+ const id = fold.header["id"];
383
+ // The SDK would list a header without a usable id; downstream lookups then
384
+ // call `.startsWith` on it and crash. Skip such files instead.
385
+ if (typeof id !== "string" || id === "")
386
+ return undefined;
387
+ const headerCwd = fold.header["cwd"];
388
+ const parentSessionPath = fold.header["parentSession"];
389
+ const headerTimestamp = fold.header["timestamp"];
390
+ return {
391
+ path: filePath,
392
+ id,
393
+ cwd: typeof headerCwd === "string" ? headerCwd : "",
394
+ created: typeof headerTimestamp === "string" || typeof headerTimestamp === "number" ? new Date(headerTimestamp) : new Date(Number.NaN),
395
+ modified: mtime,
396
+ messageCount: fold.messageCount,
397
+ firstMessage: fold.firstMessageText ?? "(no messages)",
398
+ // Never built: see the directory-level doc comment. Kept because SDK-built
399
+ // entries (cleanup listing) still carry the field.
400
+ allMessagesText: "",
401
+ ...(fold.name === undefined ? {} : { name: fold.name }),
402
+ ...(typeof parentSessionPath === "string" ? { parentSessionPath } : {}),
403
+ };
404
+ }
405
+ /** Classify and fold one line, addressed as bytes inside `data`. */
406
+ function processLineBytes(data, start, end, state) {
407
+ // Readline parity: a CRLF file yields lines without their trailing `\r`.
408
+ if (end > start && data[end - 1] === CARRIAGE_RETURN)
409
+ end -= 1;
410
+ if (state.header === undefined) {
411
+ const outcome = classifyPreHeaderLine(data.toString("utf8", start, end));
412
+ if (outcome === "skip")
413
+ return;
414
+ if (outcome === "reject") {
415
+ state.rejected = true;
416
+ return;
417
+ }
418
+ state.header = outcome;
419
+ return;
420
+ }
421
+ const entryType = classifyLineType(data, start, end);
422
+ if (entryType === "session_info") {
423
+ const entry = tryParseEntry(data.toString("utf8", start, end));
424
+ if (entry !== undefined)
425
+ state.name = sessionInfoName(entry);
426
+ return;
427
+ }
428
+ if (entryType === "message") {
429
+ // A line still being written can be complete JSON only if its last
430
+ // significant byte is `}`; treating anything else as malformed matches
431
+ // the SDK, which skips unparseable lines. Ending in `}` counts the line
432
+ // without validating its JSON — a torn final write adds a transient +1
433
+ // that self-heals when the line completes (see messageCount's contract).
434
+ if (!endsWithClosingBrace(data, start, end))
435
+ return;
436
+ state.messageCount += 1;
437
+ // The expensive part of a listing was parsing message bodies; decode and
438
+ // parse only until the first user text message is known.
439
+ if (state.firstMessageText !== undefined)
440
+ return;
441
+ const entry = tryParseEntry(data.toString("utf8", start, end));
442
+ if (entry !== undefined) {
443
+ const userText = firstUserMessageText(entry);
444
+ if (userText !== undefined)
445
+ state.firstMessageText = userText;
446
+ }
447
+ return;
448
+ }
449
+ if (entryType !== undefined)
450
+ return;
451
+ // Lines that do not start with the SDK-style `{"type":"..."}` prefix
452
+ // (foreign writers, garbage) fall back to a parse so they are classified
453
+ // exactly like the SDK would.
454
+ const entry = tryParseEntry(data.toString("utf8", start, end));
455
+ if (entry === undefined)
456
+ return;
457
+ if (entry["type"] === "session_info")
458
+ state.name = sessionInfoName(entry);
459
+ else if (entry["type"] === "message") {
460
+ state.messageCount += 1;
461
+ if (state.firstMessageText === undefined) {
462
+ const userText = firstUserMessageText(entry);
463
+ if (userText !== undefined)
464
+ state.firstMessageText = userText;
465
+ }
466
+ }
467
+ }
468
+ /**
469
+ * The entry type from a line's leading bytes, without decoding it. The type
470
+ * bytes are compared directly against the known SDK entry types: decoding
471
+ * them first would mask each byte's high bit — `Buffer.toString("ascii")`
472
+ * turns bytes like `ed e5 f3 f3 e1 e7 e5` into "message" — fabricating
473
+ * matches for corrupt input. Returns "other" when the line carries the
474
+ * SDK-style prefix but not a known type (only message/session_info matter
475
+ * for the summary, so such lines need no parse), or undefined when the line
476
+ * does not carry the prefix (or the type is unreasonably long), leaving
477
+ * classification to the parse fallback.
478
+ */
479
+ function classifyLineType(data, start, end) {
480
+ const prefixLength = ENTRY_TYPE_PREFIX.length;
481
+ if (end - start < prefixLength + 1)
482
+ return undefined;
483
+ for (let i = 0; i < prefixLength; i += 1) {
484
+ if (data[start + i] !== ENTRY_TYPE_PREFIX[i])
485
+ return undefined;
486
+ }
487
+ const searchLimit = Math.min(end, start + prefixLength + MAX_CLASSIFIED_TYPE_LENGTH);
488
+ const closeAt = data.indexOf(TYPE_QUOTE, start + prefixLength);
489
+ if (closeAt === -1 || closeAt > searchLimit)
490
+ return undefined;
491
+ if (sameBytes(data, start + prefixLength, closeAt, MESSAGE_TYPE_BYTES))
492
+ return "message";
493
+ if (sameBytes(data, start + prefixLength, closeAt, SESSION_INFO_TYPE_BYTES))
494
+ return "session_info";
495
+ return "other";
496
+ }
497
+ /** Whether `data[start, end)` holds exactly `expected`'s bytes. */
498
+ function sameBytes(data, start, end, expected) {
499
+ if (end - start !== expected.length)
500
+ return false;
501
+ return expected.compare(data, start, end) === 0;
502
+ }
503
+ /**
504
+ * Whether the line's last significant byte is `}`, skipping trailing spaces,
505
+ * tabs, and carriage returns: JSON.parse (and therefore the SDK) tolerates
506
+ * that whitespace, so valid message lines with it still count.
507
+ */
508
+ function endsWithClosingBrace(data, start, end) {
509
+ let last = end;
510
+ while (last > start) {
511
+ const byte = data[last - 1];
512
+ if (byte !== SPACE && byte !== TAB && byte !== CARRIAGE_RETURN)
513
+ break;
514
+ last -= 1;
515
+ }
516
+ return last > start && data[last - 1] === CLOSING_BRACE;
517
+ }
518
+ /**
519
+ * First parseable entry of a session file must be its session header —
520
+ * exactly the SDK's rule: unparseable lines are skipped until one parses, and
521
+ * a parseable non-session entry disqualifies the file.
522
+ */
523
+ function classifyPreHeaderLine(line) {
524
+ const entry = tryParseEntry(line);
525
+ if (entry === undefined)
526
+ return "skip";
527
+ if (entry["type"] !== "session")
528
+ return "reject";
529
+ return entry;
530
+ }
531
+ /** The SDK's name rule: latest `session_info` wins, and empty/missing names clear. */
532
+ function sessionInfoName(entry) {
533
+ const value = entry["name"];
534
+ if (typeof value !== "string")
535
+ return undefined;
536
+ const trimmed = value.trim();
537
+ return trimmed === "" ? undefined : trimmed;
538
+ }
539
+ /**
540
+ * The first user message with non-empty text content, mirroring the SDK's
541
+ * `firstMessage` extraction (role/content shape checks and text-block join).
542
+ */
543
+ function firstUserMessageText(entry) {
544
+ const message = entry["message"];
545
+ if (!isRecord(message))
546
+ return undefined;
547
+ if (message["role"] !== "user" || !("content" in message))
548
+ return undefined;
549
+ const text = extractTextContent(message["content"]);
550
+ return text === "" ? undefined : text;
551
+ }
552
+ function extractTextContent(content) {
553
+ if (typeof content === "string")
554
+ return content;
555
+ if (!Array.isArray(content))
556
+ return "";
557
+ const texts = [];
558
+ for (const block of content) {
559
+ if (!isRecord(block) || block["type"] !== "text")
560
+ continue;
561
+ const text = block["text"];
562
+ if (typeof text === "string")
563
+ texts.push(text);
564
+ }
565
+ return texts.join(" ");
566
+ }
567
+ function tryParseEntry(line) {
568
+ if (line.trim() === "")
569
+ return undefined;
570
+ try {
571
+ const parsed = JSON.parse(line);
572
+ return isRecord(parsed) ? parsed : undefined;
573
+ }
574
+ catch {
575
+ return undefined;
576
+ }
577
+ }
578
+ function isRecord(value) {
579
+ return typeof value === "object" && value !== null;
580
+ }
581
+ async function listSessionFilesInDir(sessionDir) {
582
+ let fileNames;
583
+ try {
584
+ fileNames = await readdir(sessionDir);
585
+ }
586
+ catch {
587
+ // Matches the SDK listing behavior: an unreadable directory lists nothing.
588
+ return [];
589
+ }
590
+ return fileNames.filter((name) => name.endsWith(".jsonl")).map((name) => join(sessionDir, name));
591
+ }
592
+ function sortedSessionSummaries(summaries) {
593
+ const sessions = summaries.filter((summary) => summary !== undefined);
594
+ sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
595
+ return sessions;
596
+ }
597
+ async function scanSessionFilesWithBoundedConcurrency(files, chunkBytes, scan) {
598
+ const results = Array.from({ length: files.length }, () => undefined);
599
+ let nextIndex = 0;
600
+ const workerCount = Math.min(MAX_CONCURRENT_SESSION_SUMMARY_SCANS, files.length);
601
+ const workers = Array.from({ length: workerCount }, async () => {
602
+ // One reusable read buffer per worker, allocated on first use: warm
603
+ // listings answer every file from the memo's stat-only fast path and
604
+ // must not pay a chunk-sized allocation per worker for reads that
605
+ // never happen.
606
+ let chunkBuffer;
607
+ const readBuffer = () => (chunkBuffer ??= Buffer.allocUnsafe(chunkBytes));
608
+ for (;;) {
609
+ const index = nextIndex++;
610
+ const file = files[index];
611
+ if (file === undefined)
612
+ return;
613
+ results[index] = await scan(file, readBuffer).catch(() => undefined);
614
+ }
615
+ });
616
+ await Promise.all(workers);
617
+ return results;
618
+ }
619
+ //# sourceMappingURL=sessionSummaryScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionSummaryScanner.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionSummaryScanner.ts"],"names":[],"mappings":"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"}