sessiongraph 1.0.0 → 1.0.2

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.
@@ -2635,6 +2635,14 @@ function databasePath(env = process.env, platform = process.platform, home = hom
2635
2635
  return paths.find((path) => existsSync(path)) || paths[0];
2636
2636
  }
2637
2637
 
2638
+ // src/playback-tokens.ts
2639
+ var validTokenCount = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
2640
+ function sumRecordedTokens(values) {
2641
+ if (!values.length || !values.every(validTokenCount)) return null;
2642
+ const total = values.reduce((sum, value) => sum + value, 0);
2643
+ return validTokenCount(total) ? total : null;
2644
+ }
2645
+
2638
2646
  // server/opencode.ts
2639
2647
  async function api(path, signal2) {
2640
2648
  const endpoint = await Service.discover({ file: serviceFile() });
@@ -2698,7 +2706,7 @@ async function loadSessionGraph() {
2698
2706
  agent: raw.agent || "build",
2699
2707
  cost: raw.cost || 0,
2700
2708
  // Input + output + cache tokens; reasoning is generally part of output.
2701
- tokens: (t?.input || 0) + (t?.output || 0) + (t?.cache?.read || 0) + (t?.cache?.write || 0),
2709
+ tokens: sumRecordedTokens([t?.input, t?.output, t?.cache?.read, t?.cache?.write]),
2702
2710
  outcome: raw.outcome
2703
2711
  });
2704
2712
  }
@@ -3028,11 +3036,11 @@ function opencodeEvents(raw, message) {
3028
3036
  if (part.type === "text") events.push(eventFor(message, id2, "message", "Assistant message", part.text || "", { ...native, ...timing }));
3029
3037
  else if (part.type === "reasoning") events.push(eventFor(message, id2, "reasoning", "Recorded reasoning", part.text || "", { ...native, ...timing }));
3030
3038
  else if (part.type === "tool") {
3031
- const state = part.state || {}, endTime = messageTime(part.time?.completed);
3032
- const status = state.status === "error" ? "failed" : state.status === "streaming" ? "pending" : ["running", "completed"].includes(state.status) ? state.status : "unknown";
3039
+ const state2 = part.state || {}, endTime = messageTime(part.time?.completed);
3040
+ const status = state2.status === "error" ? "failed" : state2.status === "streaming" ? "pending" : ["running", "completed"].includes(state2.status) ? state2.status : "unknown";
3033
3041
  const common = { ...native, callID: part.id, status, endTime };
3034
- events.push(eventFor(message, `${id2}:call`, "tool_call", part.name || "Tool call", "", { ...common, ...timing, input: eventText(state.input) }));
3035
- if (["completed", "error"].includes(state.status)) events.push(eventFor(message, `${id2}:result`, "tool_result", part.name || "Tool result", "", { ...common, role: "tool", ...endTime ? { time: endTime, timeSource: "recorded" } : {}, output: eventText(state.error ?? state.content, 64e3) }));
3042
+ events.push(eventFor(message, `${id2}:call`, "tool_call", part.name || "Tool call", "", { ...common, ...timing, input: eventText(state2.input) }));
3043
+ if (["completed", "error"].includes(state2.status)) events.push(eventFor(message, `${id2}:result`, "tool_result", part.name || "Tool result", "", { ...common, role: "tool", ...endTime ? { time: endTime, timeSource: "recorded" } : {}, output: eventText(state2.error ?? state2.content, 64e3) }));
3036
3044
  } else events.push(eventFor(message, id2, "event", `${part.type || "Unknown"} content`, "Unsupported content.", { ...native, ...timing, partial: true }));
3037
3045
  }
3038
3046
  if (raw.error) events.push(eventFor(message, "error", "error", "Assistant error", eventText(raw.error), { status: "failed" }));
@@ -30380,16 +30388,121 @@ async function f3e(e10, t, n, r) {
30380
30388
  import { readdir, stat } from "node:fs/promises";
30381
30389
  import { homedir as homedir3 } from "node:os";
30382
30390
  import { join as join4 } from "node:path";
30391
+
30392
+ // server/adapters/claude-transcripts.ts
30393
+ import { constants as constants2 } from "node:fs";
30394
+ import { lstat, open } from "node:fs/promises";
30395
+ import { basename as basename3 } from "node:path";
30396
+ var claudeSessionID = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
30397
+ var MAX_BYTES = 128e6;
30398
+ var sidecar = (path) => basename3(path).startsWith("agent-") ? path.replace(/\.jsonl$/, ".meta.json") : void 0;
30399
+ async function state(path) {
30400
+ const s = await lstat(path);
30401
+ if (!s.isFile()) throw new Error("Claude transcript must be a regular file.");
30402
+ return { size: s.size, modified: s.mtimeMs, signature: [s.dev, s.ino, s.size, s.mtimeMs] };
30403
+ }
30404
+ async function metadataState(path) {
30405
+ const file = sidecar(path);
30406
+ try {
30407
+ return file ? await state(file) : void 0;
30408
+ } catch (error) {
30409
+ if (error.code === "ENOENT") return;
30410
+ throw error;
30411
+ }
30412
+ }
30413
+ async function claudeRevision(path) {
30414
+ return digest([6, (await state(path)).signature, (await metadataState(path))?.signature]);
30415
+ }
30416
+ async function bytes(path, max) {
30417
+ const before = await state(path);
30418
+ if (before.size > max) throw new Error(`Claude transcript exceeds the ${max === MAX_BYTES ? "128 MB import" : "metadata size"} limit.`);
30419
+ const file = await open(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW || 0) | (constants2.O_NONBLOCK || 0));
30420
+ try {
30421
+ const opened = await file.stat();
30422
+ if (!opened.isFile() || opened.dev !== before.signature[0] || opened.ino !== before.signature[1]) throw new Error("Claude transcript changed during import; import again.");
30423
+ const buffer = Buffer.alloc(before.size + 1);
30424
+ let length = 0;
30425
+ while (length < buffer.length) {
30426
+ const part = await file.read(buffer, length, buffer.length - length, length);
30427
+ if (!part.bytesRead) break;
30428
+ length += part.bytesRead;
30429
+ }
30430
+ if (length !== before.size || digest((await state(path)).signature) !== digest(before.signature)) throw new Error("Claude transcript changed during import; import again.");
30431
+ return buffer.toString("utf8", 0, length);
30432
+ } finally {
30433
+ await file.close();
30434
+ }
30435
+ }
30436
+ var cached;
30437
+ async function claudeTranscript(path, revision2) {
30438
+ if (await claudeRevision(path) !== revision2) throw new Error("Claude transcript changed during import; import again.");
30439
+ if (cached?.path === path && cached.revision === revision2) return cached.transcript;
30440
+ const source = await bytes(path, MAX_BYTES), entries2 = [], times = /* @__PURE__ */ new Map();
30441
+ let partial = false;
30442
+ for (const line of source.split("\n")) {
30443
+ if (!line.trim()) continue;
30444
+ try {
30445
+ const row = JSON.parse(line);
30446
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
30447
+ partial = true;
30448
+ continue;
30449
+ }
30450
+ entries2.push(row);
30451
+ if (typeof row.uuid === "string") {
30452
+ const time2 = typeof row.timestamp === "string" ? Date.parse(row.timestamp) : NaN;
30453
+ times.set(row.uuid, Number.isFinite(time2) && time2 > 0 ? time2 : 0);
30454
+ }
30455
+ } catch {
30456
+ partial = true;
30457
+ }
30458
+ if (entries2.length > 2e5) throw new Error("Claude transcript exceeds the 200,000-record import limit.");
30459
+ }
30460
+ const metadata2 = sidecar(path);
30461
+ if (metadata2 && await metadataState(path)) {
30462
+ try {
30463
+ entries2.push({ ...JSON.parse(await bytes(metadata2, 64e3)), type: "agent_metadata" });
30464
+ } catch {
30465
+ partial = true;
30466
+ }
30467
+ }
30468
+ if (await claudeRevision(path) !== revision2) throw new Error("Claude transcript changed during import; import again.");
30469
+ const { size, modified } = await state(path), transcript = { entries: entries2, times, partial, size, modified };
30470
+ cached = { path, revision: revision2, transcript };
30471
+ return transcript;
30472
+ }
30473
+ function claudeTranscriptInfo(transcript, sessionId) {
30474
+ const rows = transcript.entries;
30475
+ if (rows[0]?.isSidechain === true) return;
30476
+ const messages = rows.filter((row) => (row.type === "user" || row.type === "assistant") && row.sessionId === sessionId && row.isSidechain !== true && typeof row.uuid === "string" && row.message && typeof row.message === "object");
30477
+ if (!messages.length) return;
30478
+ const firstPrompt = messages.filter((row) => row.type === "user" && !row.isMeta).map((row) => {
30479
+ const content = row.message.content;
30480
+ const text2 = typeof content === "string" ? content : Array.isArray(content) ? content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n") : "";
30481
+ return text2.replace(/<(ide_opened_file|ide_selection|ide_diagnostics)>[\s\S]*?<\/\1>/g, "").trim().replace(/\s+/g, " ").slice(0, 180);
30482
+ }).find(Boolean);
30483
+ const title = rows.findLast((row) => ["custom-title", "ai-title", "summary"].includes(String(row.type)) && typeof (row.customTitle || row.aiTitle || row.summary) === "string");
30484
+ const cwd2 = rows.findLast((row) => row.type === "relocated" && typeof row.relocatedCwd === "string")?.relocatedCwd || messages.find((row) => typeof row.cwd === "string")?.cwd;
30485
+ const createdAt = messages.map((row) => transcript.times.get(String(row.uuid))).find((time2) => time2 && time2 > 0);
30486
+ return { sessionId, summary: String(title?.customTitle || title?.aiTitle || title?.summary || firstPrompt || "Claude Code session"), firstPrompt, cwd: typeof cwd2 === "string" ? cwd2 : void 0, createdAt, lastModified: transcript.modified, fileSize: transcript.size };
30487
+ }
30488
+ function claudeTranscriptStore(transcript, sessionId, agent) {
30489
+ return {
30490
+ async load(key) {
30491
+ return key.sessionId === sessionId && key.subpath === (agent ? `subagents/agent-${agent}` : void 0) ? transcript.entries : null;
30492
+ },
30493
+ async append() {
30494
+ throw new Error("Claude history is read-only.");
30495
+ }
30496
+ };
30497
+ }
30498
+
30499
+ // server/adapters/claude.ts
30383
30500
  var root = () => join4(process.env.CLAUDE_CONFIG_DIR || join4(homedir3(), ".claude"), "projects");
30384
- var revision = async (path) => {
30385
- const s = await stat(path);
30386
- return digest([5, s.mtimeMs, s.size]);
30387
- };
30388
- function claudeMessages(rows, candidate) {
30501
+ function claudeMessages(rows, candidate, times) {
30389
30502
  const calls = /* @__PURE__ */ new Map();
30390
30503
  return rows.map((row) => {
30391
30504
  const raw = row.message;
30392
- const msg = blankMessage(row.uuid, Number.isFinite(Date.parse(raw?.timestamp)) ? Date.parse(raw.timestamp) : 0, row.type);
30505
+ const msg = blankMessage(row.uuid, times?.get(row.uuid) ?? (Number.isFinite(Date.parse(raw?.timestamp)) ? Date.parse(raw.timestamp) : 0), row.type);
30393
30506
  if (raw?.model) candidate.session.model = raw.model;
30394
30507
  if (row.parent_agent_id && candidate.locator.parent) candidate.session.parentID = identity("claude-code", `${candidate.locator.parent}/agent/${row.parent_agent_id}`);
30395
30508
  if (typeof raw?.content === "string") addText(msg, raw.content, row.type === "user" ? "You" : "Assistant");
@@ -30418,7 +30531,7 @@ function claudeMessages(rows, candidate) {
30418
30531
  var claudeAdapter = {
30419
30532
  id: "claude-code",
30420
30533
  name: "Claude Code",
30421
- description: "Local SDK history \xB7 all projects and subagent transcripts",
30534
+ description: "Local CLI and VS Code history \xB7 all projects and subagent transcripts",
30422
30535
  capabilities: { history: true, forks: false, children: true, files: true },
30423
30536
  async detect() {
30424
30537
  try {
@@ -30432,49 +30545,89 @@ var claudeAdapter = {
30432
30545
  const infos = await I1t({ limit: 2e4, includeProgrammatic: true });
30433
30546
  if (infos.length === 2e4) warnings.push("Claude Code listing is limited to 20,000 sessions.");
30434
30547
  const files = /* @__PURE__ */ new Map();
30548
+ let unreadable = 0;
30435
30549
  for (const dir of await readdir(root(), { withFileTypes: true })) {
30436
30550
  if (!dir.isDirectory()) continue;
30437
30551
  const parent2 = join4(root(), dir.name);
30438
- for (const file of await readdir(parent2, { withFileTypes: true })) if (file.isFile() && file.name.endsWith(".jsonl")) {
30552
+ let entries2;
30553
+ try {
30554
+ entries2 = await readdir(parent2, { withFileTypes: true });
30555
+ } catch {
30556
+ unreadable++;
30557
+ continue;
30558
+ }
30559
+ for (const file of entries2) if (file.isFile() && file.name.endsWith(".jsonl") && claudeSessionID(file.name.slice(0, -6))) {
30439
30560
  const id2 = file.name.slice(0, -6), path = join4(parent2, file.name);
30440
- const old = files.get(id2);
30441
- if (!old || (await stat(path)).mtimeMs > (await stat(old.path)).mtimeMs) files.set(id2, { path, children: join4(parent2, id2, "subagents") });
30561
+ try {
30562
+ const old = files.get(id2);
30563
+ if (!old || (await stat(path)).mtimeMs > (await stat(old.path)).mtimeMs) files.set(id2, { path, children: join4(parent2, id2, "subagents") });
30564
+ } catch {
30565
+ unreadable++;
30566
+ }
30567
+ }
30568
+ }
30569
+ const listed = new Set(infos.map((info) => info.sessionId));
30570
+ for (const [id2, file] of files) {
30571
+ if (listed.has(id2)) continue;
30572
+ if (infos.length >= 2e4) {
30573
+ warnings.push("Claude Code discovery is limited to 20,000 sessions.");
30574
+ break;
30575
+ }
30576
+ try {
30577
+ const transcript = await claudeTranscript(file.path, await claudeRevision(file.path));
30578
+ const info = claudeTranscriptInfo(transcript, id2);
30579
+ if (info) infos.push(info);
30580
+ else if (transcript.partial) unreadable++;
30581
+ } catch {
30582
+ unreadable++;
30442
30583
  }
30443
30584
  }
30444
30585
  for (const info of infos) {
30445
30586
  const file = files.get(info.sessionId);
30446
30587
  if (!file) continue;
30447
30588
  const s = { ...blankSession("claude-code", info.sessionId, info.cwd), title: info.customTitle || info.summary || info.firstPrompt || "Untitled session", created: info.createdAt || info.lastModified, updated: info.lastModified, provider: "anthropic", agent: "Claude Code" };
30448
- sessions.push({ session: s, revision: await revision(file.path), locator: { path: file.path, dir: info.cwd || "" } });
30589
+ try {
30590
+ sessions.push({ session: s, revision: await claudeRevision(file.path), locator: { path: file.path, dir: info.cwd || "" } });
30591
+ } catch {
30592
+ unreadable++;
30593
+ continue;
30594
+ }
30449
30595
  let children;
30450
30596
  try {
30451
30597
  children = await readdir(file.children, { withFileTypes: true });
30452
- } catch {
30598
+ } catch (error) {
30599
+ if (error.code !== "ENOENT") unreadable++;
30453
30600
  continue;
30454
30601
  }
30455
30602
  for (const child of children) {
30456
30603
  if (!child.isFile() || !/^agent-[\w-]+\.jsonl$/.test(child.name)) continue;
30457
- const agent = child.name.slice(6, -6), path = join4(file.children, child.name), stats = await stat(path);
30458
- sessions.push({ session: { ...s, id: identity("claude-code", `${info.sessionId}/agent/${agent}`), nativeID: `${info.sessionId}/agent/${agent}`, title: `Subagent ${agent}`, created: stats.birthtimeMs || s.created, updated: stats.mtimeMs, parentID: s.id, agent }, revision: await revision(path), locator: { path, dir: info.cwd || "", parent: info.sessionId, agent } });
30604
+ try {
30605
+ const agent = child.name.slice(6, -6), path = join4(file.children, child.name), stats = await stat(path);
30606
+ sessions.push({ session: { ...s, id: identity("claude-code", `${info.sessionId}/agent/${agent}`), nativeID: `${info.sessionId}/agent/${agent}`, title: `Subagent ${agent}`, created: stats.birthtimeMs || s.created, updated: stats.mtimeMs, parentID: s.id, agent }, revision: await claudeRevision(path), locator: { path, dir: info.cwd || "", parent: info.sessionId, agent } });
30607
+ } catch {
30608
+ unreadable++;
30609
+ }
30459
30610
  }
30460
30611
  }
30612
+ if (unreadable) warnings.push(`${unreadable} Claude Code histories unavailable or too large. Saved imports are kept.`);
30461
30613
  return { sessions, projects: [...new Map(sessions.map((c) => {
30462
30614
  const p = project("claude-code", c.session.directory);
30463
30615
  return [p.id, p];
30464
30616
  })).values()], warnings };
30465
30617
  },
30466
30618
  async read(candidate, cursor) {
30467
- const size = (await stat(candidate.locator.path)).size;
30468
- if (size > 128e6) throw new Error("Transcript exceeds the 128 MB import limit.");
30469
30619
  const offset = Number(cursor || 0);
30620
+ if (!Number.isSafeInteger(offset) || offset < 0) throw new Error("Invalid history cursor.");
30621
+ const transcript = await claudeTranscript(candidate.locator.path, candidate.revision);
30470
30622
  const start = Math.max(0, offset - 1), count = 120 + offset - start;
30471
- const options = { dir: candidate.locator.dir || void 0, offset: start, limit: count + 1 };
30623
+ const sessionId = candidate.locator.parent || candidate.session.nativeID;
30624
+ const options = { dir: candidate.locator.dir || void 0, offset: start, limit: count + 1, sessionStore: claudeTranscriptStore(transcript, sessionId, candidate.locator.agent) };
30472
30625
  const rows = candidate.locator.parent ? await U1t(candidate.locator.parent, candidate.locator.agent, options) : await P1t(candidate.session.nativeID, options);
30473
30626
  const more = rows.length > count;
30474
- const messages = claudeMessages(rows.slice(0, count), candidate).slice(offset - start);
30475
- if (!offset && !messages.length && size > 100) throw new Error("SDK returned no readable conversation for this transcript.");
30476
- if (!more && await revision(candidate.locator.path) !== candidate.revision) throw new Error("Session changed during import; import again to capture the latest messages.");
30477
- return { messages, cursor: more ? String(offset + 120) : void 0, note: "SDK-visible conversation chain; pre-compaction text and unsupported message types may be absent. Fork ancestry is unavailable." };
30627
+ const messages = claudeMessages(rows.slice(0, count), candidate, transcript.times).slice(offset - start);
30628
+ if (!offset && !messages.length && transcript.size > 100) throw new Error("SDK returned no readable conversation for this transcript.");
30629
+ if (!more && await claudeRevision(candidate.locator.path) !== candidate.revision) throw new Error("Session changed during import; import again to capture the latest messages.");
30630
+ return { messages, cursor: more ? String(offset + 120) : void 0, partial: transcript.partial, note: `SDK-visible conversation chain from the local CLI/VS Code transcript; pre-compaction text and unsupported message types may be absent. Fork ancestry is unavailable.${transcript.partial ? " Some transcript records or subagent metadata were unreadable." : ""}` };
30478
30631
  }
30479
30632
  };
30480
30633
 
@@ -30484,10 +30637,10 @@ import { homedir as homedir4 } from "node:os";
30484
30637
  import { join as join6, sep as sep2 } from "node:path";
30485
30638
 
30486
30639
  // server/adapters/jsonl.ts
30487
- import { open, readdir as readdir2 } from "node:fs/promises";
30640
+ import { open as open2, readdir as readdir2 } from "node:fs/promises";
30488
30641
  import { join as join5 } from "node:path";
30489
30642
  async function readJSONL(path, offset = 0, budget = 2e6, limit = 200) {
30490
- const file = await open(path, "r");
30643
+ const file = await open2(path, "r");
30491
30644
  const rows = [];
30492
30645
  let position = offset, carry = Buffer.alloc(0), lineStart = offset, partial = false, skipping = false, eof = false;
30493
30646
  try {
@@ -30556,7 +30709,7 @@ async function transcriptFiles(root3, max = 2e4) {
30556
30709
 
30557
30710
  // server/adapters/codex.ts
30558
30711
  var root2 = () => process.env.CODEX_HOME || join6(homedir4(), ".codex");
30559
- var revision2 = async (path) => {
30712
+ var revision = async (path) => {
30560
30713
  const s = await stat2(path);
30561
30714
  return digest([7, s.mtimeMs, s.size]);
30562
30715
  };
@@ -30671,7 +30824,7 @@ var codexAdapter = {
30671
30824
  const parent2 = meta.parent_thread_id || source?.parent_thread_id;
30672
30825
  const fork = meta.forked_from_id || meta.forkedFromId;
30673
30826
  Object.assign(session, { created: Date.parse(meta.timestamp) || stats.birthtimeMs || stats.mtimeMs, updated: stats.mtimeMs, archived: path.includes(`${sep2}archived_sessions${sep2}`), provider: meta.model_provider || "", agent: "Codex", parentID: parent2 ? identity("codex", parent2) : void 0, forkID: fork ? identity("codex", fork) : void 0 });
30674
- const candidate = { session, locator: { path }, revision: await revision2(path) };
30827
+ const candidate = { session, locator: { path }, revision: await revision(path) };
30675
30828
  for (const row of first.rows) {
30676
30829
  const msg = codexRecord(row.value, row.offset, candidate);
30677
30830
  if (session.title === "Untitled session" && msg?.role === "user") {
@@ -30693,7 +30846,7 @@ var codexAdapter = {
30693
30846
  },
30694
30847
  async read(candidate, cursor) {
30695
30848
  const page = await readJSONL(candidate.locator.path, Number(cursor || 0));
30696
- if (page.cursor === void 0 && await revision2(candidate.locator.path) !== candidate.revision) throw new Error("Session changed during import; import again to capture the latest messages.");
30849
+ if (page.cursor === void 0 && await revision(candidate.locator.path) !== candidate.revision) throw new Error("Session changed during import; import again to capture the latest messages.");
30697
30850
  return { messages: page.rows.flatMap((r) => {
30698
30851
  const msg = codexRecord(r.value, r.offset, candidate);
30699
30852
  return msg ? [msg] : [];
@@ -30703,7 +30856,7 @@ var codexAdapter = {
30703
30856
 
30704
30857
  // server/adapters/cursor.ts
30705
30858
  import { createHash as createHash3 } from "node:crypto";
30706
- import { lstat, opendir, readFile as readFile3 } from "node:fs/promises";
30859
+ import { lstat as lstat2, opendir, readFile as readFile3 } from "node:fs/promises";
30707
30860
  import { homedir as homedir5 } from "node:os";
30708
30861
  import { isAbsolute as isAbsolute2, join as join8 } from "node:path";
30709
30862
  import { fileURLToPath } from "node:url";
@@ -30785,7 +30938,7 @@ import { chmodSync, copyFileSync, lstatSync as lstatSync2, mkdtempSync, rmSync a
30785
30938
  import { tmpdir } from "node:os";
30786
30939
  import { join as join7 } from "node:path";
30787
30940
  var MAX_VALUE = 2e6;
30788
- var MAX_BYTES = 64e6;
30941
+ var MAX_BYTES2 = 64e6;
30789
30942
  var MAX_NODES = 5e4;
30790
30943
  var MAX_TEXT = 16e6;
30791
30944
  function withStore(path, read3) {
@@ -30872,10 +31025,10 @@ function readCursorStore(path) {
30872
31025
  if (typeof root3 !== "string" || !/^[a-f\d]{64}$/i.test(root3)) throw new Error("Unsupported Cursor conversation root.");
30873
31026
  const get = db2.prepare("SELECT data FROM blobs WHERE id=? AND length(data)<=?");
30874
31027
  const messages = [];
30875
- let bytes = 0, nodes = 0, text2 = 0, partial = false, ordinal = 0;
31028
+ let bytes2 = 0, nodes = 0, text2 = 0, partial = false, ordinal = 0;
30876
31029
  const stack = [{ id: root3, leaf: false, ancestors: [] }];
30877
31030
  while (stack.length) {
30878
- if (++nodes > MAX_NODES || bytes >= MAX_BYTES || text2 >= MAX_TEXT) {
31031
+ if (++nodes > MAX_NODES || bytes2 >= MAX_BYTES2 || text2 >= MAX_TEXT) {
30879
31032
  partial = true;
30880
31033
  break;
30881
31034
  }
@@ -30884,13 +31037,13 @@ function readCursorStore(path) {
30884
31037
  partial = true;
30885
31038
  continue;
30886
31039
  }
30887
- const row = get.get(item.id, Math.min(MAX_VALUE, MAX_BYTES - bytes));
31040
+ const row = get.get(item.id, Math.min(MAX_VALUE, MAX_BYTES2 - bytes2));
30888
31041
  if (!(row?.data instanceof Uint8Array)) {
30889
31042
  partial = true;
30890
31043
  continue;
30891
31044
  }
30892
31045
  const data = Buffer.from(row.data);
30893
- bytes += data.length;
31046
+ bytes2 += data.length;
30894
31047
  try {
30895
31048
  if (item.leaf) {
30896
31049
  const json4 = data[0] === 123 ? data : fields(data).find((v3) => v3.field === 4)?.data;
@@ -30949,7 +31102,7 @@ var hashCwd = (path) => createHash3("md5").update(path).digest("hex");
30949
31102
  var userData = () => process.platform === "darwin" ? join8(homedir5(), "Library/Application Support/Cursor") : process.platform === "win32" ? join8(process.env.APPDATA || join8(homedir5(), "AppData/Roaming"), "Cursor") : join8(process.env.XDG_CONFIG_HOME || join8(homedir5(), ".config"), "Cursor");
30950
31103
  async function fileStats(path) {
30951
31104
  try {
30952
- const s = await lstat(path);
31105
+ const s = await lstat2(path);
30953
31106
  if (s.isSymbolicLink()) throw new Error("Cursor history symlinks are not followed.");
30954
31107
  return s.isFile() ? s : void 0;
30955
31108
  } catch (error) {
@@ -30974,7 +31127,7 @@ async function json2(path) {
30974
31127
  async function entries(path, warnings) {
30975
31128
  const result = [];
30976
31129
  try {
30977
- if (!(await lstat(path)).isDirectory()) return result;
31130
+ if (!(await lstat2(path)).isDirectory()) return result;
30978
31131
  for await (const entry of await opendir(path)) {
30979
31132
  if (!entry.isSymbolicLink()) result.push(entry);
30980
31133
  if (result.length === LIMIT) {
@@ -31013,7 +31166,7 @@ function createCursorAdapter(options = {}) {
31013
31166
  async detect() {
31014
31167
  for (const name of ["projects", "chats", "acp-sessions"]) {
31015
31168
  try {
31016
- if ((await lstat(join8(root3(), name))).isDirectory()) return true;
31169
+ if ((await lstat2(join8(root3(), name))).isDirectory()) return true;
31017
31170
  } catch {
31018
31171
  }
31019
31172
  }
@@ -31166,11 +31319,11 @@ var cursorAdapter = createCursorAdapter();
31166
31319
 
31167
31320
  // server/adapters/gemini.ts
31168
31321
  import { homedir as homedir6 } from "node:os";
31169
- import { basename as basename3, dirname as dirname2, join as join10, sep as sep3 } from "node:path";
31322
+ import { basename as basename4, dirname as dirname2, join as join10, sep as sep3 } from "node:path";
31170
31323
 
31171
31324
  // server/adapters/local-files.ts
31172
- import { constants as constants2 } from "node:fs";
31173
- import { lstat as lstat2, open as open2, readdir as readdir3 } from "node:fs/promises";
31325
+ import { constants as constants3 } from "node:fs";
31326
+ import { lstat as lstat3, open as open3, readdir as readdir3 } from "node:fs/promises";
31174
31327
  import { join as join9, relative } from "node:path";
31175
31328
  var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
31176
31329
  var text = (value) => typeof value === "string" ? value : "";
@@ -31178,14 +31331,14 @@ var time = (value) => {
31178
31331
  const n = typeof value === "number" ? value : typeof value === "string" ? /^\d{13}$/.test(value) ? Number(value) : Date.parse(value) : 0;
31179
31332
  return Number.isFinite(n) && n > 0 ? n : 0;
31180
31333
  };
31181
- var MAX_BYTES2 = 32e6;
31334
+ var MAX_BYTES3 = 32e6;
31182
31335
  var MAX_RECORDS = 5e4;
31183
31336
  var MAX_FILES = 2e4;
31184
31337
  async function fileState(path) {
31185
31338
  try {
31186
- const s = await lstat2(path);
31339
+ const s = await lstat3(path);
31187
31340
  if (!s.isFile()) throw new Error("History must be a regular file, not a symlink.");
31188
- if (s.size > MAX_BYTES2) throw new Error("History exceeds the 32 MB per-file limit.");
31341
+ if (s.size > MAX_BYTES3) throw new Error("History exceeds the 32 MB per-file limit.");
31189
31342
  return { path, signature: digest([s.size, s.mtimeMs, s.ctimeMs, s.ino]), created: s.birthtimeMs || s.mtimeMs, updated: s.mtimeMs, size: s.size };
31190
31343
  } catch (error) {
31191
31344
  if (error.code === "ENOENT") return { path, signature: "missing", created: 0, updated: 0, size: 0 };
@@ -31197,7 +31350,7 @@ async function historyFiles(roots, match) {
31197
31350
  const warnings = [];
31198
31351
  for (const root3 of new Set(roots)) {
31199
31352
  try {
31200
- if ((await lstat2(root3)).isDirectory()) queue.push({ root: root3, path: root3, depth: 0 });
31353
+ if ((await lstat3(root3)).isDirectory()) queue.push({ root: root3, path: root3, depth: 0 });
31201
31354
  } catch (error) {
31202
31355
  if (error.code !== "ENOENT") warnings.push("A history directory could not be read.");
31203
31356
  }
@@ -31250,27 +31403,27 @@ function jsonLines(value) {
31250
31403
  }
31251
31404
  function localFileAdapter(source) {
31252
31405
  const metadata2 = /* @__PURE__ */ new Map();
31253
- let cached;
31406
+ let cached2;
31254
31407
  const states = (path) => Promise.all([path, ...source.extras?.(path) || []].map(fileState));
31255
- const revision3 = (values) => digest([1, ...values.filter((_, i) => i === 0 || source.revisionExtras !== false).map((s) => [s.path, s.signature])]);
31408
+ const revision2 = (values) => digest([1, ...values.filter((_, i) => i === 0 || source.revisionExtras !== false).map((s) => [s.path, s.signature])]);
31256
31409
  async function load(path, expected, before) {
31257
- if (revision3(before) !== expected) throw new Error("Session changed during import; import again.");
31258
- if (cached?.path === path && cached.revision === expected) return cached.history;
31410
+ if (revision2(before) !== expected) throw new Error("Session changed during import; import again.");
31411
+ if (cached2?.path === path && cached2.revision === expected) return cached2.history;
31259
31412
  if (before[0].signature === "missing") throw new Error("History file is unavailable.");
31260
- if (before.reduce((n, s) => n + s.size, 0) > MAX_BYTES2) throw new Error("Combined history exceeds the 32 MB snapshot limit.");
31413
+ if (before.reduce((n, s) => n + s.size, 0) > MAX_BYTES3) throw new Error("Combined history exceeds the 32 MB snapshot limit.");
31261
31414
  const files = /* @__PURE__ */ new Map();
31262
31415
  for (const s of before.filter((s2) => s2.signature !== "missing")) {
31263
- const handle = await open2(s.path, constants2.O_RDONLY | (constants2.O_NOFOLLOW || 0));
31416
+ const handle = await open3(s.path, constants3.O_RDONLY | (constants3.O_NOFOLLOW || 0));
31264
31417
  try {
31265
31418
  const buffer = Buffer.alloc(s.size + 1);
31266
- let bytes = 0;
31267
- while (bytes < buffer.length) {
31268
- const part = await handle.read(buffer, bytes, buffer.length - bytes, bytes);
31419
+ let bytes2 = 0;
31420
+ while (bytes2 < buffer.length) {
31421
+ const part = await handle.read(buffer, bytes2, buffer.length - bytes2, bytes2);
31269
31422
  if (!part.bytesRead) break;
31270
- bytes += part.bytesRead;
31423
+ bytes2 += part.bytesRead;
31271
31424
  }
31272
- if (bytes !== s.size) throw new Error("History changed while reading; import again.");
31273
- files.set(s.path, buffer.toString("utf8", 0, bytes));
31425
+ if (bytes2 !== s.size) throw new Error("History changed while reading; import again.");
31426
+ files.set(s.path, buffer.toString("utf8", 0, bytes2));
31274
31427
  } finally {
31275
31428
  await handle.close();
31276
31429
  }
@@ -31279,7 +31432,7 @@ function localFileAdapter(source) {
31279
31432
  const history = source.parse(path, files);
31280
31433
  if (!history.nativeID || history.nativeID.length > 512) throw new Error("History has no supported native session identity.");
31281
31434
  if (history.messages.length > MAX_RECORDS) throw new Error("History exceeds the 50,000-message limit.");
31282
- cached = { path, revision: expected, history };
31435
+ cached2 = { path, revision: expected, history };
31283
31436
  return history;
31284
31437
  }
31285
31438
  return {
@@ -31288,7 +31441,7 @@ function localFileAdapter(source) {
31288
31441
  description: source.description,
31289
31442
  capabilities: source.capabilities,
31290
31443
  async detect() {
31291
- for (const root3 of source.roots()) if ((await lstat2(root3).catch(() => void 0))?.isDirectory()) return true;
31444
+ for (const root3 of source.roots()) if ((await lstat3(root3).catch(() => void 0))?.isDirectory()) return true;
31292
31445
  return false;
31293
31446
  },
31294
31447
  async discover() {
@@ -31296,7 +31449,7 @@ function localFileAdapter(source) {
31296
31449
  let failed = 0;
31297
31450
  for (const path of listing.files) {
31298
31451
  try {
31299
- const before = await states(path), rev = revision3(before);
31452
+ const before = await states(path), rev = revision2(before);
31300
31453
  let candidate = metadata2.get(path);
31301
31454
  if (!candidate || candidate.revision !== rev) {
31302
31455
  const history = await load(path, rev, before);
@@ -31497,7 +31650,7 @@ var geminiAdapter = localFileAdapter({
31497
31650
  return message;
31498
31651
  });
31499
31652
  successfulEdits(messages, ["write_file", "replace"]);
31500
- const parent2 = meta.kind === "subagent" && basename3(dirname2(dirname2(path))) === "chats" ? basename3(dirname2(path)) : void 0;
31653
+ const parent2 = meta.kind === "subagent" && basename4(dirname2(dirname2(path))) === "chats" ? basename4(dirname2(path)) : void 0;
31501
31654
  return {
31502
31655
  nativeID: meta.sessionId,
31503
31656
  messages,
@@ -31646,7 +31799,7 @@ var copilotAdapter = localFileAdapter({
31646
31799
 
31647
31800
  // server/adapters/extensions.ts
31648
31801
  import { homedir as homedir10 } from "node:os";
31649
- import { basename as basename4, dirname as dirname3, join as join14 } from "node:path";
31802
+ import { basename as basename5, dirname as dirname3, join as join14 } from "node:path";
31650
31803
  function editorTaskRoots(extension) {
31651
31804
  const home = homedir10();
31652
31805
  const base = process.platform === "darwin" ? join14(home, "Library", "Application Support") : process.platform === "win32" ? process.env.APPDATA || join14(home, "AppData", "Roaming") : process.env.XDG_CONFIG_HOME || join14(home, ".config");
@@ -31668,7 +31821,7 @@ function taskAdapter(id2, name, extension, env) {
31668
31821
  if (!Array.isArray(records) || sdk && !text(value.sessionId)) throw new Error("Unsupported task history.");
31669
31822
  const read3 = (name2) => object(files.has(join14(dirname3(path), name2)) ? json3(files.get(join14(dirname3(path), name2))) : void 0);
31670
31823
  const history = read3("history_item.json"), info = read3("session.json"), task = read3("task_metadata.json");
31671
- const nativeID = sdk ? value.sessionId : basename4(dirname3(path));
31824
+ const nativeID = sdk ? value.sessionId : basename5(dirname3(path));
31672
31825
  if (history.id && history.id !== nativeID || info.session_id && info.session_id !== nativeID) throw new Error("Task metadata identity mismatch.");
31673
31826
  const messages = records.map((raw, index) => {
31674
31827
  const row = object(raw), message = conversationMessage(row, `record:${index}`);
@@ -31721,7 +31874,7 @@ var continueAdapter = localFileAdapter({
31721
31874
  return {
31722
31875
  nativeID: value.sessionId,
31723
31876
  messages,
31724
- session: { directory: text(value.workspaceDirectory), title: text(value.title), created: time(meta.dateCreated), model: text(value.chatModelTitle) || "Unknown model", cost: typeof usage.totalCost === "number" ? usage.totalCost : null, tokens: typeof usage.promptTokens === "number" && typeof usage.completionTokens === "number" ? usage.promptTokens + usage.completionTokens : null },
31877
+ session: { directory: text(value.workspaceDirectory), title: text(value.title), created: time(meta.dateCreated), model: text(value.chatModelTitle) || "Unknown model", cost: typeof usage.totalCost === "number" ? usage.totalCost : null, tokens: sumRecordedTokens([usage.promptTokens, usage.completionTokens]) },
31725
31878
  note: "Local Continue conversation snapshots. Missing message times remain unknown. Context attachments and transient tool UI state are not imported; tool outputs are retained when recorded as messages."
31726
31879
  };
31727
31880
  }
@@ -31771,14 +31924,14 @@ var EVENT_SCHEMA = `CREATE TABLE IF NOT EXISTS timeline_items (
31771
31924
  function writeEvents(db2, id2, rows) {
31772
31925
  db2.prepare("DELETE FROM timeline_items WHERE session_id=?").run(id2);
31773
31926
  const insert = db2.prepare("INSERT OR IGNORE INTO timeline_items VALUES (?,?,?,?,?,?,?,?,?,?,?)");
31774
- let ordinal = 0, bytes = 0, partial = false, legacy = false, limited = false;
31927
+ let ordinal = 0, bytes2 = 0, partial = false, legacy = false, limited = false;
31775
31928
  for (const row of rows) {
31776
31929
  const message = JSON.parse(String(row.data));
31777
31930
  legacy ||= !message.events;
31778
31931
  for (const event of normalizedEvents(message, id2)) {
31779
31932
  const item = { ...event, ordinal: ordinal++ }, data = JSON.stringify(item);
31780
- bytes += Buffer.byteLength(data);
31781
- if (ordinal > 2e5 || bytes > 32e6) {
31933
+ bytes2 += Buffer.byteLength(data);
31934
+ if (ordinal > 2e5 || bytes2 > 32e6) {
31782
31935
  limited = true;
31783
31936
  break;
31784
31937
  }
@@ -31802,29 +31955,29 @@ var EventReadError = class extends Error {
31802
31955
  }
31803
31956
  status;
31804
31957
  };
31805
- var filters = { all: "1", messages: "kind='message'", tools: "kind IN ('tool_call','tool_result')", events: "kind NOT IN ('message','tool_call','tool_result')", untimed: "time IS NULL" };
31958
+ var filters = { all: "1", messages: "kind='message' AND role IN ('user','assistant')", tools: "kind IN ('tool_call','tool_result')", events: "kind NOT IN ('message','tool_call','tool_result')", untimed: "time IS NULL" };
31806
31959
  function coverage(db2, session) {
31807
31960
  const row = db2.prepare("SELECT * FROM timeline_coverage WHERE session_id=?").get(session.id);
31808
31961
  return { revision: String(row?.revision || "pending"), partial: Boolean(row?.partial) || session.coverage?.history !== "complete", note: [row?.note, session.coverage?.note].filter(Boolean).join(" ") || void 0 };
31809
31962
  }
31810
31963
  function readEventMarkers(db2, session) {
31811
31964
  const end = Math.max(session.created, session.activity?.lastMessageAt || session.created);
31812
- const totals = db2.prepare("SELECT count(*) AS total, count(*) FILTER (WHERE time IS NULL) AS untimed, count(*) FILTER (WHERE time<? OR time>?) AS outside FROM timeline_items WHERE session_id=?").get(session.created, end, session.id);
31813
- const rows = db2.prepare(`SELECT ${summaryColumns} FROM timeline_items WHERE session_id=? AND time>=? AND time<=? ORDER BY time DESC,ordinal DESC LIMIT 2000`).all(session.id, session.created, end);
31965
+ const totals = db2.prepare(`SELECT count(*) AS total, count(*) FILTER (WHERE time IS NULL) AS untimed, count(*) FILTER (WHERE time<? OR time>?) AS outside FROM timeline_items WHERE session_id=? AND (${filters.messages})`).get(session.created, end, session.id);
31966
+ const rows = db2.prepare(`SELECT ${summaryColumns} FROM timeline_items WHERE session_id=? AND (${filters.messages}) AND time>=? AND time<=? ORDER BY time DESC,ordinal DESC`).all(session.id, session.created, end);
31814
31967
  return { sessionID: session.id, ...coverage(db2, session), items: rows.reverse().map(eventSummary), total: Number(totals.total), untimed: Number(totals.untimed), outsideLifetime: Number(totals.outside), limited: Number(totals.total) - Number(totals.untimed) - Number(totals.outside) > rows.length };
31815
31968
  }
31816
31969
  function readEvents(db2, session, options = {}) {
31817
- const state = coverage(db2, session), filter = options.filter || "all", size = 60;
31970
+ const state2 = coverage(db2, session), filter = options.filter || "all", size = 60;
31818
31971
  if (!Object.hasOwn(filters, filter)) throw new EventReadError("Invalid event filter.", 400);
31819
31972
  const where = filters[filter];
31820
- if (options.revision && options.revision !== state.revision) throw new EventReadError("Saved history changed. Reload the event explorer.", 409);
31973
+ if (options.revision && options.revision !== state2.revision) throw new EventReadError("Saved history changed. Reload the event explorer.", 409);
31821
31974
  let offset = 0;
31822
31975
  if (options.cursor) {
31823
31976
  try {
31824
31977
  if (options.cursor.length > 512) throw new Error();
31825
31978
  const cursor2 = JSON.parse(Buffer.from(options.cursor, "base64url").toString());
31826
31979
  if (!Number.isSafeInteger(cursor2.offset) || cursor2.offset < 0 || cursor2.offset > 2e5 || cursor2.filter !== filter || typeof cursor2.revision !== "string") throw new Error();
31827
- if (cursor2.revision !== state.revision) throw new EventReadError("Saved history changed. Reload the event explorer.", 409);
31980
+ if (cursor2.revision !== state2.revision) throw new EventReadError("Saved history changed. Reload the event explorer.", 409);
31828
31981
  offset = cursor2.offset;
31829
31982
  } catch (error) {
31830
31983
  if (error instanceof EventReadError) throw error;
@@ -31845,10 +31998,10 @@ function readEvents(db2, session, options = {}) {
31845
31998
  const related = selected?.callID ? db2.prepare(`SELECT ${summaryColumns} FROM timeline_items WHERE session_id=? AND call_id=? AND id<>? ORDER BY ordinal LIMIT 21`).all(session.id, selected.callID, selected.id).map(eventSummary) : [];
31846
31999
  const previousItem = selected ? db2.prepare(`SELECT id FROM timeline_items WHERE session_id=? AND ordinal<? AND (${where}) ORDER BY ordinal DESC LIMIT 1`).get(session.id, selected.ordinal)?.id : void 0;
31847
32000
  const nextItem = selected ? db2.prepare(`SELECT id FROM timeline_items WHERE session_id=? AND ordinal>? AND (${where}) ORDER BY ordinal LIMIT 1`).get(session.id, selected.ordinal)?.id : void 0;
31848
- const cursor = (position) => Buffer.from(JSON.stringify({ revision: state.revision, filter, offset: position })).toString("base64url");
32001
+ const cursor = (position) => Buffer.from(JSON.stringify({ revision: state2.revision, filter, offset: position })).toString("base64url");
31849
32002
  return {
31850
32003
  sessionID: session.id,
31851
- ...state,
32004
+ ...state2,
31852
32005
  items,
31853
32006
  total,
31854
32007
  offset,
@@ -31909,11 +32062,11 @@ var DEFAULT_VIEW_STATE = Object.freeze({
31909
32062
  sourceID: "",
31910
32063
  projectID: "",
31911
32064
  directory: "",
31912
- days: 90,
32065
+ days: 0,
31913
32066
  fromDate: "",
31914
32067
  toDate: "",
31915
32068
  includeChildren: true,
31916
- includeArchived: true
32069
+ includeArchived: false
31917
32070
  });
31918
32071
  var validDate = (value) => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
31919
32072
  function normalizeViewState(value) {
@@ -32147,12 +32300,12 @@ var SessionGraphStore = class {
32147
32300
  }
32148
32301
  }
32149
32302
  events(id2, options = {}) {
32150
- return this.eventSnapshot(id2, (session) => readEvents(this.db, session, options));
32303
+ return this.readSnapshot(id2, (session) => readEvents(this.db, session, options));
32151
32304
  }
32152
32305
  eventMarkers(id2) {
32153
- return this.eventSnapshot(id2, (session) => readEventMarkers(this.db, session));
32306
+ return this.readSnapshot(id2, (session) => readEventMarkers(this.db, session));
32154
32307
  }
32155
- eventSnapshot(id2, read3) {
32308
+ readSnapshot(id2, read3) {
32156
32309
  this.db.exec("BEGIN");
32157
32310
  try {
32158
32311
  const session = this.getSession(id2);
@@ -32181,27 +32334,30 @@ var SessionGraphStore = class {
32181
32334
  return { id: id2, fragments, messages: page.messages.length, nextCursor: page.next, truncated };
32182
32335
  }
32183
32336
  detail(id2, cursor) {
32184
- const page = this.messagePage(id2, Number(cursor || 0), 120), session = this.getSession(id2);
32185
- const files = /* @__PURE__ */ new Map(), tools = /* @__PURE__ */ new Map();
32186
- for (const message of page.messages) {
32187
- for (const file of message.files) {
32188
- const previous = files.get(file.path);
32189
- files.set(file.path, { ...file, source: previous?.source === "snapshot" ? "snapshot" : file.source, steps: (previous?.steps || 0) + file.steps });
32337
+ return this.readSnapshot(id2, (session) => {
32338
+ const page = this.messagePage(id2, Number(cursor || 0), 120);
32339
+ const files = /* @__PURE__ */ new Map(), tools = /* @__PURE__ */ new Map();
32340
+ for (const message of page.messages) {
32341
+ for (const file of message.files) {
32342
+ const previous = files.get(file.path);
32343
+ files.set(file.path, { ...file, source: previous?.source === "snapshot" ? "snapshot" : file.source, steps: (previous?.steps || 0) + file.steps });
32344
+ }
32345
+ message.tools.forEach((tool) => tools.set(tool, (tools.get(tool) || 0) + 1));
32190
32346
  }
32191
- message.tools.forEach((tool) => tools.set(tool, (tools.get(tool) || 0) + 1));
32192
- }
32193
- return {
32194
- id: id2,
32195
- files: [...files.values()].sort((a, b) => b.steps - a.steps),
32196
- tools: [...tools].map(([name, count]) => ({ name, count })),
32197
- locations: [],
32198
- messagesScanned: page.messages.length,
32199
- snapshotSteps: page.messages.reduce((n, m) => n + (m.snapshots || 0), 0),
32200
- nextCursor: page.next,
32201
- partial: Boolean(page.next) || session.coverage?.history !== "complete",
32202
- loadedAt: Date.now(),
32203
- note: session.coverage?.note || (session.coverage?.history === "pending" ? "Waiting for import." : void 0)
32204
- };
32347
+ return {
32348
+ id: id2,
32349
+ revision: session.revision,
32350
+ files: [...files.values()].sort((a, b) => b.steps - a.steps),
32351
+ tools: [...tools].map(([name, count]) => ({ name, count })),
32352
+ locations: [],
32353
+ messagesScanned: page.messages.length,
32354
+ snapshotSteps: page.messages.reduce((n, m) => n + (m.snapshots || 0), 0),
32355
+ nextCursor: page.next,
32356
+ partial: Boolean(page.next) || session.coverage?.history !== "complete",
32357
+ loadedAt: Date.now(),
32358
+ note: session.coverage?.note || (session.coverage?.history === "pending" ? "Waiting for import." : void 0)
32359
+ };
32360
+ });
32205
32361
  }
32206
32362
  checkpoints(after = "") {
32207
32363
  const rows = this.db.prepare("SELECT id,data FROM search_cache WHERE id>? ORDER BY id LIMIT 51").all(after);
@@ -32378,7 +32534,7 @@ var Importer = class {
32378
32534
  import { execFile, spawn as spawn2 } from "node:child_process";
32379
32535
  import { promisify } from "node:util";
32380
32536
  import { stat as stat4 } from "node:fs/promises";
32381
- import { basename as basename5, dirname as dirname5, isAbsolute as isAbsolute4 } from "node:path";
32537
+ import { basename as basename6, dirname as dirname5, isAbsolute as isAbsolute4 } from "node:path";
32382
32538
 
32383
32539
  // src/launch-links.ts
32384
32540
  var HARNESSES = [
@@ -32411,7 +32567,7 @@ function resumeArgs(value) {
32411
32567
 
32412
32568
  // server/harnesses.ts
32413
32569
  import { access, stat as stat3 } from "node:fs/promises";
32414
- import { constants as constants3 } from "node:fs";
32570
+ import { constants as constants4 } from "node:fs";
32415
32571
  import { homedir as homedir11 } from "node:os";
32416
32572
  import { delimiter, isAbsolute as isAbsolute3, join as join15 } from "node:path";
32417
32573
  async function executable(name) {
@@ -32423,7 +32579,7 @@ async function executable(name) {
32423
32579
  for (const path of candidates) {
32424
32580
  try {
32425
32581
  if ((await stat3(path)).isFile()) {
32426
- await access(path, process.platform === "win32" ? constants3.F_OK : constants3.X_OK);
32582
+ await access(path, process.platform === "win32" ? constants4.F_OK : constants4.X_OK);
32427
32583
  return path;
32428
32584
  }
32429
32585
  } catch {
@@ -32586,7 +32742,7 @@ function commandInvocation(command, terminal, platform = process.platform) {
32586
32742
  const encoded = Buffer.from(command, "utf16le").toString("base64");
32587
32743
  return { file: terminal, args: ["-NoProfile", "-NonInteractive", "-Command", `Start-Process powershell.exe -ArgumentList '-NoExit','-NoProfile','-EncodedCommand','${encoded}'`] };
32588
32744
  }
32589
- return { file: terminal, args: [basename5(terminal) === "gnome-terminal" ? "--" : "-e", "sh", "-lc", command] };
32745
+ return { file: terminal, args: [basename6(terminal) === "gnome-terminal" ? "--" : "-e", "sh", "-lc", command] };
32590
32746
  }
32591
32747
  async function launchTerminal(session, prepared) {
32592
32748
  if (!prepared.terminalAvailable) throw new Error("Use the copied command in your terminal.");
@@ -32762,7 +32918,7 @@ var server = createServer(async (req, res) => {
32762
32918
  const url = new URL(req.url || "/", "http://127.0.0.1");
32763
32919
  res.setHeader("Referrer-Policy", "no-referrer");
32764
32920
  res.setHeader("X-Content-Type-Options", "nosniff");
32765
- res.setHeader("Content-Security-Policy", `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'${ipc}; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'`);
32921
+ res.setHeader("Content-Security-Policy", `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self'; connect-src 'self'${ipc}; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'`);
32766
32922
  if (url.pathname === "/__sessiongraph" && equals(url.searchParams.get("token") || "")) {
32767
32923
  res.setHeader("Set-Cookie", `sessiongraph=${token}; HttpOnly; SameSite=Strict; Path=/`);
32768
32924
  res.setHeader("Cache-Control", "no-store");