@pasko70/pibo 1.6.0 → 1.7.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 (31) hide show
  1. package/dist/apps/chat/chat-trace-helpers.js +47 -4
  2. package/dist/apps/chat/trace-v2.js +302 -0
  3. package/dist/apps/chat/trace.js +31 -2
  4. package/dist/apps/chat/web-app.js +368 -8
  5. package/dist/apps/chat-ui/assets/{dist-D87he9he.js → dist-BFmfAHTa.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-Dm6UhVM6.js → dist-BHS5hqHn.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-aLS3alom.js → dist-BKWZVzkX.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-CFoGUjhX.js → dist-BgiyRvMc.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-Yx8i6oQ1.js → dist-C1irMXX8.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-B0i7AU1O.js → dist-C3CzqW75.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-C-UobPQQ.js → dist-DD6PyrDV.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-wyhLeLW6.js → dist-DYHLT66j.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-DYqJWYtm.js → dist-DbMhwIF-.js} +1 -1
  14. package/dist/apps/chat-ui/assets/{dist-BwZj_mI4.js → dist-Dp3T6E_K.js} +1 -1
  15. package/dist/apps/chat-ui/assets/{dist-BbCXk-v9.js → dist-v8XQydKe.js} +1 -1
  16. package/dist/apps/chat-ui/assets/index-D8itqvK_.css +1 -0
  17. package/dist/apps/chat-ui/assets/index-DZdXJmcO.js +166 -0
  18. package/dist/apps/chat-ui/index.html +2 -2
  19. package/dist/apps/chat-vscode-web/assets/index-KKfk8l1P.js +41 -0
  20. package/dist/apps/chat-vscode-web/index.html +1 -1
  21. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  22. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.7.0.vsix +0 -0
  23. package/dist/cli.js +10 -0
  24. package/dist/data/payload-store.js +18 -9
  25. package/dist/session-ui/terminalRows.js +5 -0
  26. package/dist/web/http.js +17 -6
  27. package/package.json +1 -1
  28. package/dist/apps/chat-ui/assets/index-CNDX-4Kp.js +0 -166
  29. package/dist/apps/chat-ui/assets/index-DvBWSeIO.css +0 -1
  30. package/dist/apps/chat-vscode-web/assets/index-lA76A7Pc.js +0 -41
  31. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.6.0.vsix +0 -0
@@ -1,7 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { patchTraceViewWithEvent } from "../../shared/trace-engine.js";
3
- export const DEFAULT_TRACE_EVENTS_PAGE_SIZE = 2_000;
4
- export const MAX_TRACE_EVENTS_PER_REQUEST = 50_000;
3
+ export const DEFAULT_TRACE_EVENTS_PAGE_SIZE = 50;
4
+ export const MAX_TRACE_EVENTS_PER_REQUEST = 1000;
5
+ export const TRACE_CACHE_MAX_BYTES = 8 * 1024 * 1024;
5
6
  export function etagForVersion(version) {
6
7
  return `"${version}"`;
7
8
  }
@@ -80,15 +81,57 @@ export function annotateTracePage(trace, events, input) {
80
81
  hasOlderEvents: firstEventSequence !== undefined ? firstEventSequence > 1 : false,
81
82
  };
82
83
  }
83
- export function setTraceCache(cache, key, trace, maxEntries) {
84
+ export function setTraceCache(cache, key, trace, maxEntries, maxBytes = TRACE_CACHE_MAX_BYTES) {
84
85
  if (trace.rawEvents.length > 0)
85
86
  return;
86
87
  cache.delete(key);
87
88
  cache.set(key, trace);
88
- while (cache.size > maxEntries) {
89
+ while (cache.size > maxEntries || traceCacheEstimatedBytes(cache) > maxBytes) {
89
90
  const oldestKey = cache.keys().next().value;
90
91
  if (typeof oldestKey !== "string")
91
92
  break;
92
93
  cache.delete(oldestKey);
93
94
  }
94
95
  }
96
+ export function traceCacheEstimatedBytes(cache) {
97
+ let bytes = 0;
98
+ for (const trace of cache.values())
99
+ bytes += estimateTraceViewBytes(trace);
100
+ return bytes;
101
+ }
102
+ export function estimateTraceViewBytes(trace) {
103
+ let bytes = 512;
104
+ bytes += byteLength(trace.piboSessionId) + byteLength(trace.piSessionId) + byteLength(trace.title) + byteLength(trace.version);
105
+ for (const node of flattenEstimateNodes(trace.nodes)) {
106
+ bytes += 256;
107
+ bytes += byteLength(node.id) + byteLength(node.parentId) + byteLength(node.title) + byteLength(node.summary);
108
+ bytes += estimatePayloadBytes(node.input) + estimatePayloadBytes(node.output) + byteLength(node.error);
109
+ }
110
+ return bytes;
111
+ }
112
+ function flattenEstimateNodes(nodes) {
113
+ const result = [];
114
+ const visit = (items) => {
115
+ for (const item of items) {
116
+ result.push(item);
117
+ visit(item.children);
118
+ }
119
+ };
120
+ visit(nodes);
121
+ return result;
122
+ }
123
+ function estimatePayloadBytes(value) {
124
+ if (value === undefined || value === null)
125
+ return 0;
126
+ if (typeof value === "string")
127
+ return byteLength(value);
128
+ try {
129
+ return byteLength(JSON.stringify(value));
130
+ }
131
+ catch {
132
+ return byteLength(String(value));
133
+ }
134
+ }
135
+ function byteLength(value) {
136
+ return value ? Buffer.byteLength(value, "utf8") : 0;
137
+ }
@@ -0,0 +1,302 @@
1
+ import { createHash } from "node:crypto";
2
+ export const TRACE_V2_DEFAULT_TIMELINE_LIMIT = 50;
3
+ export const TRACE_V2_MAX_TIMELINE_LIMIT = 240;
4
+ export const TRACE_V2_TIMELINE_HARD_BYTES = 256 * 1024;
5
+ export const TRACE_V2_PREVIEW_CHARS = 64;
6
+ export const TRACE_V2_INLINE_PAYLOAD_MAX_BYTES = 8 * 1024;
7
+ export const TRACE_V2_INLINE_TRANSCRIPT_PAYLOAD_MAX_BYTES = 64 * 1024;
8
+ export const TRACE_V2_PAYLOAD_REF_THRESHOLD_BYTES = 4096;
9
+ export const TRACE_V2_PAYLOAD_DEFAULT_LIMIT_BYTES = 64 * 1024;
10
+ export const TRACE_V2_PAYLOAD_MAX_LIMIT_BYTES = 1024 * 1024;
11
+ export const TRACE_V2_RAW_EVENTS_DEFAULT_LIMIT = 80;
12
+ export const TRACE_V2_RAW_EVENTS_MAX_LIMIT = 500;
13
+ export const TRACE_V2_RAW_EVENTS_HARD_BYTES = 256 * 1024;
14
+ export function traceTimelinePageFromView(input) {
15
+ const byteLimit = input.byteLimit ?? TRACE_V2_TIMELINE_HARD_BYTES;
16
+ const nodes = compactTraceNodes({
17
+ nodes: input.trace.nodes,
18
+ payloadStore: input.payloadStore,
19
+ piboSessionId: input.trace.piboSessionId,
20
+ limit: Math.max(1, Math.min(input.limit, TRACE_V2_MAX_TIMELINE_LIMIT)),
21
+ fromTail: input.fromTail,
22
+ });
23
+ let page = {
24
+ piboSessionId: input.trace.piboSessionId,
25
+ piSessionId: input.trace.piSessionId,
26
+ title: input.trace.title,
27
+ version: input.trace.version,
28
+ latestStreamId: input.trace.latestStreamId,
29
+ projectionStatus: "ready",
30
+ cursor: {
31
+ before: input.trace.nextBeforeSequence !== undefined ? String(input.trace.nextBeforeSequence) : undefined,
32
+ after: input.trace.lastEventSequence !== undefined ? String(input.trace.lastEventSequence) : undefined,
33
+ hasOlder: input.trace.hasOlderEvents === true,
34
+ hasNewer: false,
35
+ },
36
+ nodes,
37
+ responseBudget: {
38
+ nodeLimit: input.limit,
39
+ byteLimit,
40
+ truncatedByBytes: false,
41
+ },
42
+ eventCount: input.trace.eventCount,
43
+ pageSize: input.trace.pageSize,
44
+ firstEventSequence: input.trace.firstEventSequence,
45
+ lastEventSequence: input.trace.lastEventSequence,
46
+ nextBeforeSequence: input.trace.nextBeforeSequence,
47
+ hasOlderEvents: input.trace.hasOlderEvents,
48
+ };
49
+ while (Buffer.byteLength(JSON.stringify(page), "utf8") > byteLimit && page.nodes.length > 1) {
50
+ page = {
51
+ ...page,
52
+ nodes: page.nodes.slice(Math.ceil(page.nodes.length / 4)),
53
+ responseBudget: { ...page.responseBudget, truncatedByBytes: true },
54
+ };
55
+ }
56
+ return page;
57
+ }
58
+ export function traceRawEventsPageFromEvents(input) {
59
+ const byteLimit = input.byteLimit ?? TRACE_V2_RAW_EVENTS_HARD_BYTES;
60
+ const limited = input.events.slice(-Math.max(1, Math.min(input.limit, TRACE_V2_RAW_EVENTS_MAX_LIMIT)));
61
+ let page = {
62
+ piboSessionId: input.piboSessionId,
63
+ cursor: {
64
+ before: limited[0]?.eventSequence !== undefined ? String(limited[0].eventSequence) : undefined,
65
+ hasOlder: (limited[0]?.eventSequence ?? 1) > 1,
66
+ },
67
+ limit: input.limit,
68
+ events: limited.map((event) => compactRawEvent(event, input.payloadStore, input.piboSessionId)),
69
+ responseBudget: {
70
+ byteLimit,
71
+ truncatedByBytes: false,
72
+ },
73
+ };
74
+ while (Buffer.byteLength(JSON.stringify(page), "utf8") > byteLimit && page.events.length > 1) {
75
+ page = {
76
+ ...page,
77
+ events: page.events.slice(Math.ceil(page.events.length / 4)),
78
+ responseBudget: { ...page.responseBudget, truncatedByBytes: true },
79
+ };
80
+ }
81
+ return page;
82
+ }
83
+ export function parseTracePayloadRef(ref) {
84
+ if (!ref.startsWith("trace_"))
85
+ return undefined;
86
+ try {
87
+ const parsed = JSON.parse(Buffer.from(ref.slice("trace_".length), "base64url").toString("utf8"));
88
+ if (!parsed || typeof parsed !== "object")
89
+ return undefined;
90
+ const record = parsed;
91
+ return typeof record.p === "string" && typeof record.id === "string"
92
+ ? { piboSessionId: record.p, payloadId: record.id }
93
+ : undefined;
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ export function readTracePayloadChunk(input) {
100
+ const parsed = parseTracePayloadRef(input.ref);
101
+ if (!parsed)
102
+ return undefined;
103
+ const payload = input.payloadStore.getPayload(parsed.payloadId);
104
+ if (!payload)
105
+ return undefined;
106
+ const bytes = Buffer.from(input.payloadStore.readPayloadBytes(parsed.payloadId));
107
+ const offset = Math.max(0, Math.min(input.offset, bytes.byteLength));
108
+ const limit = Math.max(1, Math.min(input.limit, TRACE_V2_PAYLOAD_MAX_LIMIT_BYTES));
109
+ const chunk = bytes.subarray(offset, Math.min(bytes.byteLength, offset + limit));
110
+ const preview = payload.previewText ?? "";
111
+ const traceRef = {
112
+ ref: input.ref,
113
+ contentType: normalizeContentType(payload.contentType),
114
+ byteLength: payload.byteSize,
115
+ preview,
116
+ truncatedPreview: Buffer.byteLength(preview, "utf8") < payload.byteSize,
117
+ hash: payload.sha256,
118
+ };
119
+ const nextOffset = offset + chunk.byteLength < bytes.byteLength ? offset + chunk.byteLength : undefined;
120
+ return {
121
+ ref: traceRef,
122
+ offset,
123
+ limit,
124
+ data: chunk.toString("utf8"),
125
+ byteLength: chunk.byteLength,
126
+ nextOffset,
127
+ hasMore: nextOffset !== undefined,
128
+ };
129
+ }
130
+ function compactTraceNodes(input) {
131
+ const result = [];
132
+ const visit = (nodes, depth) => {
133
+ for (const node of nodes) {
134
+ result.push(compactTraceNode(node, input.payloadStore, input.piboSessionId, depth));
135
+ visit(node.children, depth + 1);
136
+ }
137
+ };
138
+ visit(input.nodes, 0);
139
+ return input.fromTail ? result.slice(-input.limit) : result.slice(0, input.limit);
140
+ }
141
+ function compactTraceNode(node, payloadStore, piboSessionId, depth) {
142
+ const outputKind = node.type === "model.reasoning" ? "reasoning" : "output";
143
+ const inlinePayloads = compactObject({
144
+ input: inlinePayloadForNodeValue(node, "input", node.input),
145
+ [outputKind]: inlinePayloadForNodeValue(node, outputKind, node.output),
146
+ error: inlinePayloadForNodeValue(node, "error", node.error),
147
+ });
148
+ const payloadRefs = compactObject({
149
+ input: inlinePayloads.input === undefined
150
+ ? payloadRefForValue({ store: payloadStore, piboSessionId, nodeId: node.id, kind: "input", value: node.input })
151
+ : undefined,
152
+ [outputKind]: inlinePayloads[outputKind] === undefined
153
+ ? payloadRefForValue({ store: payloadStore, piboSessionId, nodeId: node.id, kind: outputKind, value: node.output })
154
+ : undefined,
155
+ error: inlinePayloads.error === undefined
156
+ ? payloadRefForValue({ store: payloadStore, piboSessionId, nodeId: node.id, kind: "error", value: node.error })
157
+ : undefined,
158
+ });
159
+ const preview = previewForNode(node, payloadRefs);
160
+ return compactObject({
161
+ nodeId: node.id,
162
+ parentId: node.parentId,
163
+ piboSessionId: node.piboSessionId,
164
+ type: node.type,
165
+ status: node.status,
166
+ title: node.title,
167
+ startedAt: node.startedAt,
168
+ completedAt: node.completedAt,
169
+ durationMs: node.durationMs,
170
+ orderKey: node.orderKey,
171
+ depth,
172
+ hasChildren: node.children.length > 0,
173
+ childCount: node.children.length || undefined,
174
+ preview,
175
+ inlinePayloads: Object.keys(inlinePayloads).length ? inlinePayloads : undefined,
176
+ payloadRefs: Object.keys(payloadRefs).length ? payloadRefs : undefined,
177
+ linkedPiboSessionId: node.linkedPiboSessionId,
178
+ toolCallId: node.toolCallId,
179
+ runId: node.runId,
180
+ eventId: node.eventId,
181
+ entryId: node.entryId,
182
+ source: node.source,
183
+ stableKey: node.stableKey,
184
+ });
185
+ }
186
+ function inlinePayloadForNodeValue(node, kind, value) {
187
+ return inlinePayloadForValue(value, inlinePayloadByteLimit(node, kind));
188
+ }
189
+ function inlinePayloadByteLimit(node, kind) {
190
+ if ((node.type === "user.message" || node.type === "assistant.message" || node.type === "model.reasoning") && kind !== "error") {
191
+ return TRACE_V2_INLINE_TRANSCRIPT_PAYLOAD_MAX_BYTES;
192
+ }
193
+ return TRACE_V2_INLINE_PAYLOAD_MAX_BYTES;
194
+ }
195
+ function inlinePayloadForValue(value, maxBytes = TRACE_V2_INLINE_PAYLOAD_MAX_BYTES) {
196
+ if (value === undefined || value === null || value === "")
197
+ return undefined;
198
+ const { bytes } = payloadBytes(value);
199
+ if (bytes.byteLength > maxBytes)
200
+ return undefined;
201
+ return toPayloadValue(value);
202
+ }
203
+ function payloadRefForValue(input) {
204
+ if (input.value === undefined || input.value === null || input.value === "")
205
+ return undefined;
206
+ const { text, bytes, contentType } = payloadBytes(input.value);
207
+ const preview = textPreview(text);
208
+ const truncatedPreview = normalizedPreviewText(text).length > preview.length;
209
+ if (!truncatedPreview && bytes.byteLength <= TRACE_V2_PAYLOAD_REF_THRESHOLD_BYTES) {
210
+ return undefined;
211
+ }
212
+ const payload = input.store.writePayload({
213
+ value: toPayloadValue(input.value),
214
+ contentType,
215
+ retentionClass: "trace_event",
216
+ });
217
+ return {
218
+ ref: encodeTracePayloadRef(input.piboSessionId, payload.id),
219
+ contentType: normalizeContentType(contentType),
220
+ byteLength: bytes.byteLength,
221
+ preview,
222
+ truncatedPreview,
223
+ hash: createHash("sha256").update(bytes).digest("hex"),
224
+ };
225
+ }
226
+ function previewForNode(node, payloadRefs) {
227
+ if (node.error) {
228
+ const text = textPreview(String(node.error));
229
+ return { text, source: "error", truncated: String(node.error).length > text.length };
230
+ }
231
+ const payloadPreview = payloadRefs.output?.preview ?? payloadRefs.reasoning?.preview ?? payloadRefs.input?.preview;
232
+ if (payloadPreview)
233
+ return { text: payloadPreview, source: "payload", truncated: true };
234
+ const candidate = node.output ?? node.summary ?? node.input ?? node.title;
235
+ const text = textPreview(textForPreview(candidate));
236
+ if (!text)
237
+ return undefined;
238
+ return { text, source: node.summary !== undefined && node.output === undefined ? "summary" : "payload", truncated: textForPreview(candidate).length > text.length };
239
+ }
240
+ function compactRawEvent(event, payloadStore, piboSessionId) {
241
+ const payloadRef = payloadRefForValue({ store: payloadStore, piboSessionId, nodeId: event.id, kind: "raw", value: event.payload });
242
+ if (!payloadRef)
243
+ return event;
244
+ return {
245
+ ...event,
246
+ payload: {
247
+ type: event.type,
248
+ payloadRef,
249
+ preview: payloadRef.preview,
250
+ byteLength: payloadRef.byteLength,
251
+ truncated: true,
252
+ },
253
+ };
254
+ }
255
+ function encodeTracePayloadRef(piboSessionId, payloadId) {
256
+ return `trace_${Buffer.from(JSON.stringify({ p: piboSessionId, id: payloadId }), "utf8").toString("base64url")}`;
257
+ }
258
+ function payloadBytes(value) {
259
+ if (typeof value === "string") {
260
+ const bytes = Buffer.from(value, "utf8");
261
+ return { text: value, bytes, contentType: "text/plain; charset=utf-8" };
262
+ }
263
+ const text = JSON.stringify(value);
264
+ return { text, bytes: Buffer.from(text, "utf8"), contentType: "application/json" };
265
+ }
266
+ function textForPreview(value) {
267
+ if (typeof value === "string")
268
+ return value;
269
+ if (value === undefined || value === null)
270
+ return "";
271
+ try {
272
+ return JSON.stringify(value);
273
+ }
274
+ catch {
275
+ return String(value);
276
+ }
277
+ }
278
+ function textPreview(value) {
279
+ return normalizedPreviewText(value).slice(0, TRACE_V2_PREVIEW_CHARS);
280
+ }
281
+ function normalizedPreviewText(value) {
282
+ return value.replace(/\s+/g, " ").trim();
283
+ }
284
+ function toPayloadValue(value) {
285
+ if (typeof value === "string")
286
+ return value;
287
+ return JSON.parse(JSON.stringify(value));
288
+ }
289
+ function normalizeContentType(contentType) {
290
+ if (contentType.includes("json"))
291
+ return "application/json";
292
+ if (contentType.startsWith("text/markdown"))
293
+ return "text/markdown";
294
+ if (contentType.startsWith("text/"))
295
+ return "text/plain";
296
+ if (contentType.includes("x-ndjson"))
297
+ return "application/x-ndjson";
298
+ return "application/octet-stream";
299
+ }
300
+ function compactObject(value) {
301
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
302
+ }
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { parseSessionEntries, SessionManager } from "@mariozechner/pi-coding-agent";
@@ -7,6 +7,7 @@ import { buildTraceViewFromEvents } from "../../shared/trace-engine.js";
7
7
  import { isChatWebSessionArchived } from "./session-metadata.js";
8
8
  import { workflowSessionKindFromMetadata } from "../../sessions/workflow-session-kind.js";
9
9
  export { compareTraceNodes, sortTraceNodes, nestTraceNodes, flattenTraceNodes, mapTraceNodesById, buildTraceViewFromEvents, traceNodesFromEntries, } from "../../shared/trace-engine.js";
10
+ export const TRACE_TRANSCRIPT_TAIL_MAX_BYTES = 2 * 1024 * 1024;
10
11
  export async function loadPiSessionMetadata(session, cwd = process.cwd()) {
11
12
  const piSession = await findPiSession(session, cwd);
12
13
  return metadataFromPiSession(piSession);
@@ -109,7 +110,7 @@ function sessionNodeStatus(indexedStatus) {
109
110
  }
110
111
  export async function buildTraceView(input) {
111
112
  const metadata = input.metadata ?? (await loadPiSessionMetadata(input.session, input.session.workspace ?? input.cwd));
112
- const allEntries = metadata.sessionPath ? readEntries(metadata.sessionPath) : [];
113
+ const allEntries = input.transcriptEntries ?? (metadata.sessionPath ? readEntries(metadata.sessionPath) : []);
113
114
  const sessionStatus = input.status ?? "idle";
114
115
  const view = buildTraceViewFromEvents({
115
116
  session: {
@@ -145,6 +146,12 @@ export async function buildTraceView(input) {
145
146
  }),
146
147
  };
147
148
  }
149
+ export async function loadPiSessionTailEntries(session, cwd = process.cwd(), maxBytes = TRACE_TRANSCRIPT_TAIL_MAX_BYTES) {
150
+ const metadata = await loadPiSessionMetadata(session, cwd);
151
+ if (!metadata.sessionPath)
152
+ return { metadata, entries: [] };
153
+ return { metadata, entries: readTailEntries(metadata.sessionPath, maxBytes) };
154
+ }
148
155
  export function createTraceViewVersion(input) {
149
156
  const relevantSessions = input.sessions
150
157
  .map((session) => ({
@@ -295,6 +302,28 @@ function readEntries(path) {
295
302
  const content = readFileSync(path, "utf8");
296
303
  return parseSessionEntries(content).filter((entry) => entry.type !== "session");
297
304
  }
305
+ export function readTailEntries(path, maxBytes = TRACE_TRANSCRIPT_TAIL_MAX_BYTES) {
306
+ if (!existsSync(path))
307
+ return [];
308
+ const stats = statSync(path);
309
+ if (stats.size <= maxBytes)
310
+ return readEntries(path);
311
+ const length = Math.max(1, Math.min(maxBytes, stats.size));
312
+ const start = stats.size - length;
313
+ const buffer = Buffer.alloc(length);
314
+ const fd = openSync(path, "r");
315
+ try {
316
+ const bytesRead = readSync(fd, buffer, 0, length, start);
317
+ let content = buffer.subarray(0, bytesRead).toString("utf8");
318
+ const firstNewline = content.indexOf("\n");
319
+ if (start > 0 && firstNewline >= 0)
320
+ content = content.slice(firstNewline + 1);
321
+ return parseSessionEntries(content).filter((entry) => entry.type !== "session");
322
+ }
323
+ finally {
324
+ closeSync(fd);
325
+ }
326
+ }
298
327
  function annotateForkableUserMessageNodes(nodes, entries) {
299
328
  const candidates = userMessageForkCandidates(entries);
300
329
  if (!candidates.length)