chat-recall 0.7.18 → 0.7.20

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.
@@ -2040,7 +2040,7 @@ var require_thread_stream = __commonJS({
2040
2040
  var { version } = require_package();
2041
2041
  var { EventEmitter } = __require("events");
2042
2042
  var { Worker } = __require("worker_threads");
2043
- var { join: join14 } = __require("path");
2043
+ var { join: join15 } = __require("path");
2044
2044
  var { pathToFileURL } = __require("url");
2045
2045
  var { wait } = require_wait();
2046
2046
  var {
@@ -2083,7 +2083,7 @@ var require_thread_stream = __commonJS({
2083
2083
  function createWorker(stream, opts) {
2084
2084
  const { filename, workerData } = opts;
2085
2085
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
2086
- const toExecute = bundlerOverrides["thread-stream-worker"] || join14(__dirname, "lib", "worker.js");
2086
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
2087
2087
  const worker = new Worker(toExecute, {
2088
2088
  ...opts.workerOpts,
2089
2089
  trackUnmanagedFds: false,
@@ -2486,7 +2486,7 @@ var require_transport = __commonJS({
2486
2486
  "use strict";
2487
2487
  var { createRequire: createRequire3 } = __require("module");
2488
2488
  var getCallers = require_caller();
2489
- var { join: join14, isAbsolute, sep } = __require("node:path");
2489
+ var { join: join15, isAbsolute: isAbsolute2, sep } = __require("node:path");
2490
2490
  var sleep = require_atomic_sleep();
2491
2491
  var onExit = require_on_exit_leak_free();
2492
2492
  var ThreadStream = require_thread_stream();
@@ -2549,7 +2549,7 @@ var require_transport = __commonJS({
2549
2549
  throw new Error("only one of target or targets can be specified");
2550
2550
  }
2551
2551
  if (targets) {
2552
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
2552
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
2553
2553
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
2554
2554
  return {
2555
2555
  ...dest,
@@ -2567,7 +2567,7 @@ var require_transport = __commonJS({
2567
2567
  });
2568
2568
  });
2569
2569
  } else if (pipeline) {
2570
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
2570
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
2571
2571
  options.pipelines = [pipeline.map((dest) => {
2572
2572
  return {
2573
2573
  ...dest,
@@ -2585,11 +2585,11 @@ var require_transport = __commonJS({
2585
2585
  return buildStream(fixTarget(target), options, worker, sync);
2586
2586
  function fixTarget(origin) {
2587
2587
  origin = bundlerOverrides[origin] || origin;
2588
- if (isAbsolute(origin) || origin.indexOf("file://") === 0) {
2588
+ if (isAbsolute2(origin) || origin.indexOf("file://") === 0) {
2589
2589
  return origin;
2590
2590
  }
2591
2591
  if (origin === "pino/file") {
2592
- return join14(__dirname, "..", "file.js");
2592
+ return join15(__dirname, "..", "file.js");
2593
2593
  }
2594
2594
  let fixTarget2;
2595
2595
  for (const filePath of callers) {
@@ -3578,7 +3578,7 @@ var require_safe_stable_stringify = __commonJS({
3578
3578
  return circularValue;
3579
3579
  }
3580
3580
  let res = "";
3581
- let join14 = ",";
3581
+ let join15 = ",";
3582
3582
  const originalIndentation = indentation;
3583
3583
  if (Array.isArray(value)) {
3584
3584
  if (value.length === 0) {
@@ -3592,7 +3592,7 @@ var require_safe_stable_stringify = __commonJS({
3592
3592
  indentation += spacer;
3593
3593
  res += `
3594
3594
  ${indentation}`;
3595
- join14 = `,
3595
+ join15 = `,
3596
3596
  ${indentation}`;
3597
3597
  }
3598
3598
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3600,13 +3600,13 @@ ${indentation}`;
3600
3600
  for (; i < maximumValuesToStringify - 1; i++) {
3601
3601
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3602
3602
  res += tmp2 !== void 0 ? tmp2 : "null";
3603
- res += join14;
3603
+ res += join15;
3604
3604
  }
3605
3605
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3606
3606
  res += tmp !== void 0 ? tmp : "null";
3607
3607
  if (value.length - 1 > maximumBreadth) {
3608
3608
  const removedKeys = value.length - maximumBreadth - 1;
3609
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
3609
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
3610
3610
  }
3611
3611
  if (spacer !== "") {
3612
3612
  res += `
@@ -3627,7 +3627,7 @@ ${originalIndentation}`;
3627
3627
  let separator = "";
3628
3628
  if (spacer !== "") {
3629
3629
  indentation += spacer;
3630
- join14 = `,
3630
+ join15 = `,
3631
3631
  ${indentation}`;
3632
3632
  whitespace = " ";
3633
3633
  }
@@ -3641,13 +3641,13 @@ ${indentation}`;
3641
3641
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
3642
3642
  if (tmp !== void 0) {
3643
3643
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
3644
- separator = join14;
3644
+ separator = join15;
3645
3645
  }
3646
3646
  }
3647
3647
  if (keyLength > maximumBreadth) {
3648
3648
  const removedKeys = keyLength - maximumBreadth;
3649
3649
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
3650
- separator = join14;
3650
+ separator = join15;
3651
3651
  }
3652
3652
  if (spacer !== "" && separator.length > 1) {
3653
3653
  res = `
@@ -3688,7 +3688,7 @@ ${originalIndentation}`;
3688
3688
  }
3689
3689
  const originalIndentation = indentation;
3690
3690
  let res = "";
3691
- let join14 = ",";
3691
+ let join15 = ",";
3692
3692
  if (Array.isArray(value)) {
3693
3693
  if (value.length === 0) {
3694
3694
  return "[]";
@@ -3701,7 +3701,7 @@ ${originalIndentation}`;
3701
3701
  indentation += spacer;
3702
3702
  res += `
3703
3703
  ${indentation}`;
3704
- join14 = `,
3704
+ join15 = `,
3705
3705
  ${indentation}`;
3706
3706
  }
3707
3707
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3709,13 +3709,13 @@ ${indentation}`;
3709
3709
  for (; i < maximumValuesToStringify - 1; i++) {
3710
3710
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3711
3711
  res += tmp2 !== void 0 ? tmp2 : "null";
3712
- res += join14;
3712
+ res += join15;
3713
3713
  }
3714
3714
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3715
3715
  res += tmp !== void 0 ? tmp : "null";
3716
3716
  if (value.length - 1 > maximumBreadth) {
3717
3717
  const removedKeys = value.length - maximumBreadth - 1;
3718
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
3718
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
3719
3719
  }
3720
3720
  if (spacer !== "") {
3721
3721
  res += `
@@ -3728,7 +3728,7 @@ ${originalIndentation}`;
3728
3728
  let whitespace = "";
3729
3729
  if (spacer !== "") {
3730
3730
  indentation += spacer;
3731
- join14 = `,
3731
+ join15 = `,
3732
3732
  ${indentation}`;
3733
3733
  whitespace = " ";
3734
3734
  }
@@ -3737,7 +3737,7 @@ ${indentation}`;
3737
3737
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
3738
3738
  if (tmp !== void 0) {
3739
3739
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
3740
- separator = join14;
3740
+ separator = join15;
3741
3741
  }
3742
3742
  }
3743
3743
  if (spacer !== "" && separator.length > 1) {
@@ -3795,20 +3795,20 @@ ${originalIndentation}`;
3795
3795
  indentation += spacer;
3796
3796
  let res2 = `
3797
3797
  ${indentation}`;
3798
- const join15 = `,
3798
+ const join16 = `,
3799
3799
  ${indentation}`;
3800
3800
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
3801
3801
  let i = 0;
3802
3802
  for (; i < maximumValuesToStringify - 1; i++) {
3803
3803
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3804
3804
  res2 += tmp2 !== void 0 ? tmp2 : "null";
3805
- res2 += join15;
3805
+ res2 += join16;
3806
3806
  }
3807
3807
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3808
3808
  res2 += tmp !== void 0 ? tmp : "null";
3809
3809
  if (value.length - 1 > maximumBreadth) {
3810
3810
  const removedKeys = value.length - maximumBreadth - 1;
3811
- res2 += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
3811
+ res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
3812
3812
  }
3813
3813
  res2 += `
3814
3814
  ${originalIndentation}`;
@@ -3824,16 +3824,16 @@ ${originalIndentation}`;
3824
3824
  return '"[Object]"';
3825
3825
  }
3826
3826
  indentation += spacer;
3827
- const join14 = `,
3827
+ const join15 = `,
3828
3828
  ${indentation}`;
3829
3829
  let res = "";
3830
3830
  let separator = "";
3831
3831
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
3832
3832
  if (isTypedArrayWithEntries(value)) {
3833
- res += stringifyTypedArray(value, join14, maximumBreadth);
3833
+ res += stringifyTypedArray(value, join15, maximumBreadth);
3834
3834
  keys = keys.slice(value.length);
3835
3835
  maximumPropertiesToStringify -= value.length;
3836
- separator = join14;
3836
+ separator = join15;
3837
3837
  }
3838
3838
  if (deterministic) {
3839
3839
  keys = sort(keys, comparator);
@@ -3844,13 +3844,13 @@ ${indentation}`;
3844
3844
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
3845
3845
  if (tmp !== void 0) {
3846
3846
  res += `${separator}${strEscape(key2)}: ${tmp}`;
3847
- separator = join14;
3847
+ separator = join15;
3848
3848
  }
3849
3849
  }
3850
3850
  if (keyLength > maximumBreadth) {
3851
3851
  const removedKeys = keyLength - maximumBreadth;
3852
3852
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
3853
- separator = join14;
3853
+ separator = join15;
3854
3854
  }
3855
3855
  if (separator !== "") {
3856
3856
  res = `
@@ -4524,6 +4524,8 @@ function defaultSourcesEnabled() {
4524
4524
  commands: true
4525
4525
  },
4526
4526
  cursor: { sessions: true, skills: true, agents: true, commands: true },
4527
+ hermes: { sessions: true, skills: true },
4528
+ pi: { sessions: true, skills: true, commands: true },
4527
4529
  shared: { skills: true },
4528
4530
  common: { mcps: true, agentMd: true }
4529
4531
  };
@@ -4594,6 +4596,8 @@ function mergeSources(base, partial) {
4594
4596
  codex: { ...base.enabled.codex, ...partial.enabled?.codex ?? {} },
4595
4597
  agy: { ...base.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
4596
4598
  cursor: { ...base.enabled.cursor, ...partial.enabled?.cursor ?? {} },
4599
+ hermes: { ...base.enabled.hermes, ...partial.enabled?.hermes ?? {} },
4600
+ pi: { ...base.enabled.pi, ...partial.enabled?.pi ?? {} },
4597
4601
  shared: { ...base.enabled.shared, ...partial.enabled?.shared ?? {} },
4598
4602
  common: { ...base.enabled.common, ...partial.enabled?.common ?? {} }
4599
4603
  };
@@ -4605,6 +4609,8 @@ function mergeSources(base, partial) {
4605
4609
  agyHome: partial.agyHome ?? base.agyHome,
4606
4610
  cursorHome: partial.cursorHome ?? base.cursorHome,
4607
4611
  cursorIdeHome: partial.cursorIdeHome ?? base.cursorIdeHome,
4612
+ hermesHome: partial.hermesHome ?? base.hermesHome,
4613
+ piHome: partial.piHome ?? base.piHome,
4608
4614
  extraClaudeHomes: partial.extraClaudeHomes ?? base.extraClaudeHomes,
4609
4615
  approvedHomes: partial.approvedHomes ?? base.approvedHomes,
4610
4616
  declinedHomes: partial.declinedHomes ?? base.declinedHomes,
@@ -5284,6 +5290,8 @@ function declaredOrigin(value) {
5284
5290
  function userRecordOrigin(obj, text) {
5285
5291
  if (obj.isSidechain === true)
5286
5292
  return "subagent";
5293
+ if (obj.isCompactSummary === true)
5294
+ return "compact-summary";
5287
5295
  const declared = declaredOrigin(obj.origin);
5288
5296
  if (declared && declared !== "human")
5289
5297
  return declared;
@@ -5365,6 +5373,10 @@ var init_from_events = __esm({
5365
5373
  });
5366
5374
 
5367
5375
  // ../engine/dist/transcript/raw.js
5376
+ import { getHeapStatistics } from "v8";
5377
+ function rawParseMaxBytes(heapLimit) {
5378
+ return Math.max(0, Math.min(RAW_PARSE_CEILING, Math.floor((heapLimit - RAW_PARSE_HEAP_RESERVE) / RAW_PARSE_HEAP_PER_BYTE)));
5379
+ }
5368
5380
  function redactJsonLine(line, redact) {
5369
5381
  if (!line.includes('"'))
5370
5382
  return redact(line);
@@ -5430,7 +5442,7 @@ function redactContainer(c, redact) {
5430
5442
  }))
5431
5443
  };
5432
5444
  }
5433
- var RAW_PARSE_MAX_BYTES;
5445
+ var STREAM_PIECE_CHARS, RAW_PARSE_HEAP_PER_BYTE, RAW_PARSE_HEAP_RESERVE, RAW_PARSE_CEILING, RAW_PARSE_MAX_BYTES;
5434
5446
  var init_raw = __esm({
5435
5447
  "../engine/dist/transcript/raw.js"() {
5436
5448
  "use strict";
@@ -5438,7 +5450,11 @@ var init_raw = __esm({
5438
5450
  init_claude();
5439
5451
  init_codex();
5440
5452
  init_from_events();
5441
- RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
5453
+ STREAM_PIECE_CHARS = 1 << 20;
5454
+ RAW_PARSE_HEAP_PER_BYTE = 6;
5455
+ RAW_PARSE_HEAP_RESERVE = 128 * 1024 * 1024;
5456
+ RAW_PARSE_CEILING = 40 * 1024 * 1024;
5457
+ RAW_PARSE_MAX_BYTES = rawParseMaxBytes(getHeapStatistics().heap_size_limit);
5442
5458
  }
5443
5459
  });
5444
5460
 
@@ -5526,6 +5542,16 @@ function grandfatherLegacyHomes() {
5526
5542
  }
5527
5543
  return { seeded };
5528
5544
  }
5545
+ function defaultPiHome() {
5546
+ return join3(homedir2(), ".pi", "agent");
5547
+ }
5548
+ function defaultHermesHome() {
5549
+ if (process.platform === "win32") {
5550
+ const local = process.env.LOCALAPPDATA?.trim() || join3(homedir2(), "AppData", "Local");
5551
+ return join3(local, "hermes");
5552
+ }
5553
+ return join3(homedir2(), ".hermes");
5554
+ }
5529
5555
  function primaryHomes() {
5530
5556
  const home = homedir2();
5531
5557
  const env = process.env;
@@ -5542,7 +5568,9 @@ function primaryHomes() {
5542
5568
  env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
5543
5569
  env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
5544
5570
  env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
5545
- dirOf(opencodeDb)
5571
+ dirOf(opencodeDb),
5572
+ env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome(),
5573
+ env.CHAT_RECALL_PI_HOME || s?.piHome || env.PI_CODING_AGENT_DIR?.trim() || defaultPiHome()
5546
5574
  ];
5547
5575
  return candidates.map(normalizeHomePath);
5548
5576
  }
@@ -5578,9 +5606,9 @@ var init_home_approval = __esm({
5578
5606
  });
5579
5607
 
5580
5608
  // ../engine/dist/core/tool-paths.js
5581
- import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
5609
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync3, realpathSync as realpathSync2 } from "fs";
5582
5610
  import { homedir as homedir3 } from "os";
5583
- import { join as join4, dirname as dirname4, basename } from "path";
5611
+ import { join as join4, dirname as dirname4, basename, isAbsolute } from "path";
5584
5612
  function claudeHomeDir() {
5585
5613
  return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
5586
5614
  }
@@ -5621,6 +5649,9 @@ function geminiHomeDir() {
5621
5649
  function opencodeDbPath() {
5622
5650
  return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
5623
5651
  }
5652
+ function hermesHomeDir() {
5653
+ return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
5654
+ }
5624
5655
  function codexHomeDir() {
5625
5656
  return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
5626
5657
  }
@@ -5633,6 +5664,98 @@ function cursorHomeDir() {
5633
5664
  function cursorIdeHomeDir() {
5634
5665
  return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
5635
5666
  }
5667
+ function piHomeDir() {
5668
+ const raw = process.env.CHAT_RECALL_PI_HOME || sources().piHome || process.env.PI_CODING_AGENT_DIR?.trim();
5669
+ return raw ? expandTildePath(raw) : join4(homedir3(), ".pi", "agent");
5670
+ }
5671
+ function expandTildePath(value) {
5672
+ const t = value.trim();
5673
+ if (t === "~")
5674
+ return homedir3();
5675
+ if (t.startsWith("~/") || t.startsWith("~\\"))
5676
+ return join4(homedir3(), t.slice(2));
5677
+ return t;
5678
+ }
5679
+ function piSessionDir() {
5680
+ const override = piSessionDirEnv();
5681
+ if (override)
5682
+ return override;
5683
+ return piSessionDirForHome(piHomeDir());
5684
+ }
5685
+ function piSessionDirEnv() {
5686
+ const raw = process.env.CHAT_RECALL_PI_SESSION_DIR || process.env.PI_CODING_AGENT_SESSION_DIR?.trim();
5687
+ return raw && raw.trim() ? expandTildePath(raw) : null;
5688
+ }
5689
+ function piSessionDirForHome(home) {
5690
+ const env = piSessionDirEnv();
5691
+ if (env && sameDir(home, piHomeDir()))
5692
+ return env;
5693
+ return piSessionDirFromSettings(join4(home, "settings.json")) || join4(home, "sessions");
5694
+ }
5695
+ function sameDir(a, b) {
5696
+ if (a === b)
5697
+ return true;
5698
+ try {
5699
+ return realpathSync2(a) === realpathSync2(b);
5700
+ } catch {
5701
+ return false;
5702
+ }
5703
+ }
5704
+ function piSessionDirFromSettings(file) {
5705
+ let sig;
5706
+ try {
5707
+ sig = statSync3(file).mtimeMs;
5708
+ } catch {
5709
+ return null;
5710
+ }
5711
+ if (piSettingsCache && piSettingsCache.path === file && piSettingsCache.sig === sig) {
5712
+ return piSettingsCache.dir;
5713
+ }
5714
+ let dir = null;
5715
+ try {
5716
+ const parsed = JSON.parse(readFileSync2(file, "utf-8"));
5717
+ const value = typeof parsed.sessionDir === "string" ? parsed.sessionDir.trim() : "";
5718
+ const expanded = value ? expandTildePath(value) : "";
5719
+ dir = expanded && isAbsolute(expanded) ? expanded : null;
5720
+ } catch {
5721
+ }
5722
+ piSettingsCache = { path: file, sig, dir };
5723
+ return dir;
5724
+ }
5725
+ function piSessionDirs(opts = {}) {
5726
+ const home = piHomeDir();
5727
+ const dir = piSessionDir();
5728
+ if (!existsSync3(dir))
5729
+ return [];
5730
+ if (opts.includeExcluded)
5731
+ return [dir];
5732
+ try {
5733
+ grandfatherLegacyHomes();
5734
+ } catch {
5735
+ }
5736
+ try {
5737
+ if (!isHomeSynced(home))
5738
+ return [];
5739
+ } catch {
5740
+ }
5741
+ try {
5742
+ if (homeDecision(dir) === "declined")
5743
+ return [];
5744
+ } catch {
5745
+ }
5746
+ if (_sourceExclusionFilter && _sourceExclusionFilter(dir))
5747
+ return [];
5748
+ return [dir];
5749
+ }
5750
+ function piSkillsDir() {
5751
+ return join4(piHomeDir(), "skills");
5752
+ }
5753
+ function piPromptsDir() {
5754
+ return join4(piHomeDir(), "prompts");
5755
+ }
5756
+ function piMcpConfigPath() {
5757
+ return join4(piHomeDir(), "mcp.json");
5758
+ }
5636
5759
  function siblingHomes(base, overridden, suffix) {
5637
5760
  const roots = [];
5638
5761
  const add = (home) => {
@@ -5726,7 +5849,23 @@ function opencodeDbPaths(opts = {}) {
5726
5849
  dbs.push(primary);
5727
5850
  return applyExclusions(dbs, opts.includeExcluded);
5728
5851
  }
5729
- var sources, _sourceExclusionFilter;
5852
+ function hermesDbPaths(opts = {}) {
5853
+ const root = hermesHomeDir();
5854
+ const dbs = [join4(root, "state.db")];
5855
+ try {
5856
+ const profiles = join4(root, "profiles");
5857
+ for (const entry of readdirSync2(profiles, { withFileTypes: true })) {
5858
+ if (!entry.isDirectory())
5859
+ continue;
5860
+ const p = join4(profiles, entry.name, "state.db");
5861
+ if (existsSync3(p))
5862
+ dbs.push(p);
5863
+ }
5864
+ } catch {
5865
+ }
5866
+ return applyExclusions(dbs, opts.includeExcluded);
5867
+ }
5868
+ var sources, _sourceExclusionFilter, piSettingsCache;
5730
5869
  var init_tool_paths = __esm({
5731
5870
  "../engine/dist/core/tool-paths.js"() {
5732
5871
  "use strict";
@@ -5734,6 +5873,7 @@ var init_tool_paths = __esm({
5734
5873
  init_settings();
5735
5874
  sources = loadSourceSettings;
5736
5875
  _sourceExclusionFilter = null;
5876
+ piSettingsCache = null;
5737
5877
  }
5738
5878
  });
5739
5879
 
@@ -6899,7 +7039,7 @@ var init_opencode = __esm({
6899
7039
  });
6900
7040
 
6901
7041
  // ../engine/dist/core/backends/tail-read.js
6902
- import { openSync, readSync, closeSync, statSync as statSync3, fstatSync } from "node:fs";
7042
+ import { openSync, readSync, closeSync, statSync as statSync4, fstatSync } from "node:fs";
6903
7043
  function readAt(fd, pos, len) {
6904
7044
  const buf = Buffer.alloc(len);
6905
7045
  let got = 0;
@@ -6960,7 +7100,7 @@ function linesBeforeOffset(path, offset) {
6960
7100
  function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
6961
7101
  let size = 0;
6962
7102
  try {
6963
- size = statSync3(path).size;
7103
+ size = statSync4(path).size;
6964
7104
  } catch {
6965
7105
  return { text: "", newOffset: offset };
6966
7106
  }
@@ -7003,6 +7143,71 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7003
7143
  }
7004
7144
  }
7005
7145
  }
7146
+ function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7147
+ let start = 0;
7148
+ let linesBefore = 0;
7149
+ for (let i = 0; i < paths.length; i++) {
7150
+ const path = paths[i];
7151
+ let size = 0;
7152
+ try {
7153
+ size = statSync4(path).size;
7154
+ } catch {
7155
+ size = 0;
7156
+ }
7157
+ const last = i === paths.length - 1;
7158
+ if (!last && offset >= start + size) {
7159
+ const lines = linesBeforeOffset(path, size);
7160
+ if (lines === void 0)
7161
+ return { text: "", newOffset: offset };
7162
+ linesBefore += lines + (size > 0 && !endsWithNewline(path, size) ? 1 : 0);
7163
+ start += size;
7164
+ continue;
7165
+ }
7166
+ const local = offset - start;
7167
+ const read = readTailFromOffset(path, local, maxBytes);
7168
+ const shift = (r) => ({
7169
+ ...r,
7170
+ newOffset: start + r.newOffset,
7171
+ ...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
7172
+ });
7173
+ if (last || read.newOffset > local || local >= size)
7174
+ return shift(read);
7175
+ if (size - local > maxBytes)
7176
+ return { text: "", newOffset: start + size, skippedBytes: size - local };
7177
+ let fd = null;
7178
+ try {
7179
+ fd = openSync(path, "r");
7180
+ const text = readAt(fd, local, size - local).toString("utf-8");
7181
+ return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path, local) });
7182
+ } catch {
7183
+ return { text: "", newOffset: offset };
7184
+ } finally {
7185
+ if (fd !== null) {
7186
+ try {
7187
+ closeSync(fd);
7188
+ } catch {
7189
+ }
7190
+ }
7191
+ }
7192
+ }
7193
+ return { text: "", newOffset: offset };
7194
+ }
7195
+ function endsWithNewline(path, size) {
7196
+ let fd = null;
7197
+ try {
7198
+ fd = openSync(path, "r");
7199
+ return readAt(fd, size - 1, 1)[0] === 10;
7200
+ } catch {
7201
+ return true;
7202
+ } finally {
7203
+ if (fd !== null) {
7204
+ try {
7205
+ closeSync(fd);
7206
+ } catch {
7207
+ }
7208
+ }
7209
+ }
7210
+ }
7006
7211
  function findNewline(fd, from, size) {
7007
7212
  const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
7008
7213
  let pos = from;
@@ -7030,7 +7235,7 @@ var init_tail_read = __esm({
7030
7235
  });
7031
7236
 
7032
7237
  // ../engine/dist/core/backends/codex.js
7033
- import { readdirSync as readdirSync3, readFileSync as readFileSync2, statSync as statSync4, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
7238
+ import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
7034
7239
  import { basename as basename4, dirname as dirname7, join as join6 } from "path";
7035
7240
  function parseShellWrites(cmd) {
7036
7241
  const out = [];
@@ -7159,7 +7364,7 @@ function flattenContent(content) {
7159
7364
  }
7160
7365
  function readFirstLine(path) {
7161
7366
  try {
7162
- const head = readFileSync2(path, "utf-8").split("\n", 1)[0] || "";
7367
+ const head = readFileSync3(path, "utf-8").split("\n", 1)[0] || "";
7163
7368
  if (!head)
7164
7369
  return null;
7165
7370
  return JSON.parse(head);
@@ -7265,7 +7470,7 @@ var init_codex2 = __esm({
7265
7470
  return null;
7266
7471
  let mtime = 0;
7267
7472
  try {
7268
- mtime = statSync4(located.path).mtimeMs;
7473
+ mtime = statSync5(located.path).mtimeMs;
7269
7474
  } catch {
7270
7475
  }
7271
7476
  return {
@@ -7285,7 +7490,7 @@ var init_codex2 = __esm({
7285
7490
  for (const filePath of walkRollouts(root)) {
7286
7491
  let stat;
7287
7492
  try {
7288
- stat = statSync4(filePath);
7493
+ stat = statSync5(filePath);
7289
7494
  } catch {
7290
7495
  continue;
7291
7496
  }
@@ -7305,7 +7510,7 @@ var init_codex2 = __esm({
7305
7510
  let messageCount = 0;
7306
7511
  if (opts.previews !== false)
7307
7512
  try {
7308
- const lines = readFileSync2(filePath, "utf-8").split("\n");
7513
+ const lines = readFileSync3(filePath, "utf-8").split("\n");
7309
7514
  for (const line of lines) {
7310
7515
  if (!line.trim())
7311
7516
  continue;
@@ -7457,7 +7662,7 @@ var init_codex2 = __esm({
7457
7662
  continue;
7458
7663
  let firstLine = "";
7459
7664
  try {
7460
- firstLine = readFileSync2(path, "utf-8").split("\n", 1)[0] || "";
7665
+ firstLine = readFileSync3(path, "utf-8").split("\n", 1)[0] || "";
7461
7666
  } catch {
7462
7667
  continue;
7463
7668
  }
@@ -7481,8 +7686,8 @@ var init_codex2 = __esm({
7481
7686
  let raw = "";
7482
7687
  let mtime = 0;
7483
7688
  try {
7484
- raw = readFileSync2(path, "utf-8");
7485
- mtime = statSync4(path).mtimeMs;
7689
+ raw = readFileSync3(path, "utf-8");
7690
+ mtime = statSync5(path).mtimeMs;
7486
7691
  } catch {
7487
7692
  return [];
7488
7693
  }
@@ -7582,7 +7787,7 @@ var init_codex2 = __esm({
7582
7787
  const events = this.readEvents(this.toRawId(id));
7583
7788
  let fileMtime = 0;
7584
7789
  try {
7585
- fileMtime = statSync4(located.path).mtimeMs;
7790
+ fileMtime = statSync5(located.path).mtimeMs;
7586
7791
  } catch {
7587
7792
  }
7588
7793
  return liveScanEditsFromEvents(events, this.fileToolMap, {
@@ -7646,7 +7851,7 @@ var init_codex2 = __esm({
7646
7851
  const p = join6(dir, entry.name);
7647
7852
  let st;
7648
7853
  try {
7649
- st = statSync4(p);
7854
+ st = statSync5(p);
7650
7855
  } catch {
7651
7856
  continue;
7652
7857
  }
@@ -7658,7 +7863,7 @@ var init_codex2 = __esm({
7658
7863
  let parentId = null;
7659
7864
  let isSubagent = false;
7660
7865
  try {
7661
- const first = readFileSync2(p, "utf-8").split("\n")[0];
7866
+ const first = readFileSync3(p, "utf-8").split("\n")[0];
7662
7867
  if (first) {
7663
7868
  const meta = JSON.parse(first);
7664
7869
  const payload = meta?.payload || {};
@@ -7713,8 +7918,8 @@ var init_codex2 = __esm({
7713
7918
  let mtime = 0;
7714
7919
  const push = (path, name) => {
7715
7920
  try {
7716
- const st = statSync4(path);
7717
- files.push({ name, bytes: readFileSync2(path) });
7921
+ const st = statSync5(path);
7922
+ files.push({ name, bytes: readFileSync3(path) });
7718
7923
  if (st.mtimeMs > mtime)
7719
7924
  mtime = st.mtimeMs;
7720
7925
  } catch {
@@ -7755,7 +7960,7 @@ var init_codex2 = __esm({
7755
7960
  if (!loc)
7756
7961
  return 0;
7757
7962
  try {
7758
- return statSync4(loc.path).size;
7963
+ return statSync5(loc.path).size;
7759
7964
  } catch {
7760
7965
  return 0;
7761
7966
  }
@@ -7772,7 +7977,7 @@ var init_codex2 = __esm({
7772
7977
  });
7773
7978
 
7774
7979
  // ../engine/dist/core/backends/agy.js
7775
- import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync3, statSync as statSync5 } from "fs";
7980
+ import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync6 } from "fs";
7776
7981
  import { basename as basename5, dirname as dirname8, join as join7 } from "path";
7777
7982
  function agyTranscriptPath(logsDir) {
7778
7983
  const full = join7(logsDir, "transcript_full.jsonl");
@@ -7792,7 +7997,7 @@ function resolveAgyProject(logsDir, homeDir) {
7792
7997
  function extractProjectPathFromTranscript(path, homeDir) {
7793
7998
  let content;
7794
7999
  try {
7795
- content = readFileSync3(path, "utf-8");
8000
+ content = readFileSync4(path, "utf-8");
7796
8001
  } catch {
7797
8002
  return "";
7798
8003
  }
@@ -7965,7 +8170,7 @@ var init_agy = __esm({
7965
8170
  return null;
7966
8171
  let mtime = 0;
7967
8172
  try {
7968
- mtime = statSync5(path).mtimeMs;
8173
+ mtime = statSync6(path).mtimeMs;
7969
8174
  } catch {
7970
8175
  }
7971
8176
  const projectPath = resolveAgyProject(dirname8(path), this.homeDir());
@@ -7993,7 +8198,7 @@ var init_agy = __esm({
7993
8198
  for (const rawId of sessionDirs) {
7994
8199
  const sessionPath = join7(brainDir, rawId);
7995
8200
  try {
7996
- if (!statSync5(sessionPath).isDirectory())
8201
+ if (!statSync6(sessionPath).isDirectory())
7997
8202
  continue;
7998
8203
  } catch {
7999
8204
  continue;
@@ -8003,7 +8208,7 @@ var init_agy = __esm({
8003
8208
  continue;
8004
8209
  let stat;
8005
8210
  try {
8006
- stat = statSync5(filePath);
8211
+ stat = statSync6(filePath);
8007
8212
  } catch {
8008
8213
  continue;
8009
8214
  }
@@ -8017,7 +8222,7 @@ var init_agy = __esm({
8017
8222
  let messageCount = 0;
8018
8223
  if (opts.previews !== false)
8019
8224
  try {
8020
- const lines = readFileSync3(filePath, "utf-8").split("\n").filter(Boolean);
8225
+ const lines = readFileSync4(filePath, "utf-8").split("\n").filter(Boolean);
8021
8226
  messageCount = lines.length;
8022
8227
  for (const line of lines) {
8023
8228
  const obj = JSON.parse(line);
@@ -8107,7 +8312,7 @@ var init_agy = __esm({
8107
8312
  return [];
8108
8313
  let rawText;
8109
8314
  try {
8110
- rawText = readFileSync3(located.path, "utf-8");
8315
+ rawText = readFileSync4(located.path, "utf-8");
8111
8316
  } catch {
8112
8317
  return [];
8113
8318
  }
@@ -8269,7 +8474,7 @@ var init_agy = __esm({
8269
8474
  for (const rawId of sessionDirs) {
8270
8475
  const sessionPath = join7(brainDir, rawId);
8271
8476
  try {
8272
- if (!statSync5(sessionPath).isDirectory())
8477
+ if (!statSync6(sessionPath).isDirectory())
8273
8478
  continue;
8274
8479
  } catch {
8275
8480
  continue;
@@ -8278,7 +8483,7 @@ var init_agy = __esm({
8278
8483
  if (!filePath)
8279
8484
  continue;
8280
8485
  try {
8281
- const st = statSync5(filePath);
8486
+ const st = statSync6(filePath);
8282
8487
  if (st.mtimeMs < opts.sinceMs)
8283
8488
  continue;
8284
8489
  candidates.push({ rawId, mtime: st.mtimeMs });
@@ -8306,8 +8511,8 @@ var init_agy = __esm({
8306
8511
  try {
8307
8512
  return {
8308
8513
  tool: "agy",
8309
- mtime: statSync5(loc.path).mtimeMs,
8310
- files: [{ name: basename5(loc.path), bytes: readFileSync3(loc.path) }]
8514
+ mtime: statSync6(loc.path).mtimeMs,
8515
+ files: [{ name: basename5(loc.path), bytes: readFileSync4(loc.path) }]
8311
8516
  };
8312
8517
  } catch {
8313
8518
  return null;
@@ -8321,7 +8526,7 @@ var init_agy = __esm({
8321
8526
  if (!loc)
8322
8527
  return 0;
8323
8528
  try {
8324
- return statSync5(loc.path).size;
8529
+ return statSync6(loc.path).size;
8325
8530
  } catch {
8326
8531
  return 0;
8327
8532
  }
@@ -8338,7 +8543,7 @@ var init_agy = __esm({
8338
8543
  });
8339
8544
 
8340
8545
  // ../engine/dist/core/backends/cursor-store.js
8341
- import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as readFileSync4, statSync as statSync6 } from "fs";
8546
+ import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as readFileSync5, statSync as statSync7 } from "fs";
8342
8547
  import { join as join8 } from "path";
8343
8548
  function cursorProjectSlug(absPath) {
8344
8549
  return absPath.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
@@ -8355,7 +8560,7 @@ function listChatsIn(chatsRoot) {
8355
8560
  const hashPath = join8(chatsRoot, workspaceHash);
8356
8561
  let chatIds;
8357
8562
  try {
8358
- if (!statSync6(hashPath).isDirectory())
8563
+ if (!statSync7(hashPath).isDirectory())
8359
8564
  continue;
8360
8565
  chatIds = readdirSync5(hashPath);
8361
8566
  } catch {
@@ -8368,7 +8573,7 @@ function listChatsIn(chatsRoot) {
8368
8573
  continue;
8369
8574
  let meta = {};
8370
8575
  try {
8371
- meta = JSON.parse(readFileSync4(join8(dir, "meta.json"), "utf-8"));
8576
+ meta = JSON.parse(readFileSync5(join8(dir, "meta.json"), "utf-8"));
8372
8577
  } catch {
8373
8578
  }
8374
8579
  if (meta.hasConversation === false)
@@ -8377,7 +8582,7 @@ function listChatsIn(chatsRoot) {
8377
8582
  let created = meta.createdAtMs ?? 0;
8378
8583
  if (!mtime || !created) {
8379
8584
  try {
8380
- const st = statSync6(dbPath);
8585
+ const st = statSync7(dbPath);
8381
8586
  mtime = mtime || st.mtimeMs;
8382
8587
  created = created || st.birthtimeMs || st.mtimeMs;
8383
8588
  } catch {
@@ -8413,14 +8618,14 @@ function findChat(chatsRoots, chatId) {
8413
8618
  continue;
8414
8619
  let meta = {};
8415
8620
  try {
8416
- meta = JSON.parse(readFileSync4(join8(dir, "meta.json"), "utf-8"));
8621
+ meta = JSON.parse(readFileSync5(join8(dir, "meta.json"), "utf-8"));
8417
8622
  } catch {
8418
8623
  }
8419
8624
  let mtime = meta.updatedAtMs ?? 0;
8420
8625
  let created = meta.createdAtMs ?? 0;
8421
8626
  if (!mtime || !created) {
8422
8627
  try {
8423
- const st = statSync6(dbPath);
8628
+ const st = statSync7(dbPath);
8424
8629
  mtime = mtime || st.mtimeMs;
8425
8630
  created = created || st.birthtimeMs || st.mtimeMs;
8426
8631
  } catch {
@@ -8723,7 +8928,7 @@ var init_cursor_store = __esm({
8723
8928
  });
8724
8929
 
8725
8930
  // ../engine/dist/core/backends/cursor-ide.js
8726
- import { existsSync as existsSync8, readFileSync as readFileSync5, readdirSync as readdirSync6, statSync as statSync7 } from "fs";
8931
+ import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync6, statSync as statSync8 } from "fs";
8727
8932
  import { dirname as dirname9, join as join9 } from "path";
8728
8933
  function jsonOf(value) {
8729
8934
  if (value == null)
@@ -8776,7 +8981,7 @@ function projectPathForWorkspace(workspaceDir) {
8776
8981
  }
8777
8982
  function safeRead(path) {
8778
8983
  try {
8779
- return readFileSync5(path, "utf-8");
8984
+ return readFileSync6(path, "utf-8");
8780
8985
  } catch {
8781
8986
  return null;
8782
8987
  }
@@ -8793,7 +8998,7 @@ function workspaceIndex(workspaceRoots) {
8793
8998
  for (const id of entries) {
8794
8999
  const dir = join9(root, id);
8795
9000
  try {
8796
- if (!statSync7(dir).isDirectory())
9001
+ if (!statSync8(dir).isDirectory())
8797
9002
  continue;
8798
9003
  } catch {
8799
9004
  continue;
@@ -9139,7 +9344,7 @@ var init_cursor_ide = __esm({
9139
9344
  });
9140
9345
 
9141
9346
  // ../engine/dist/core/backends/cursor.js
9142
- import { existsSync as existsSync9, readFileSync as readFileSync6, statSync as statSync8 } from "fs";
9347
+ import { existsSync as existsSync9, readFileSync as readFileSync7, statSync as statSync9 } from "fs";
9143
9348
  import { join as join10 } from "path";
9144
9349
  var PREFIX4, CursorBackend, cursorBackend;
9145
9350
  var init_cursor = __esm({
@@ -9192,7 +9397,7 @@ var init_cursor = __esm({
9192
9397
  if (r.surface === "cli") {
9193
9398
  let mtime2 = r.chat.updatedAtMs;
9194
9399
  try {
9195
- mtime2 = statSync8(r.chat.dbPath).mtimeMs || mtime2;
9400
+ mtime2 = statSync9(r.chat.dbPath).mtimeMs || mtime2;
9196
9401
  } catch {
9197
9402
  }
9198
9403
  return {
@@ -9205,7 +9410,7 @@ var init_cursor = __esm({
9205
9410
  }
9206
9411
  let mtime = r.composer.updatedAtMs;
9207
9412
  try {
9208
- mtime = statSync8(r.composer.globalDbPath).mtimeMs || mtime;
9413
+ mtime = statSync9(r.composer.globalDbPath).mtimeMs || mtime;
9209
9414
  } catch {
9210
9415
  }
9211
9416
  return {
@@ -9363,7 +9568,7 @@ var init_cursor = __esm({
9363
9568
  if (!jsonl)
9364
9569
  return [];
9365
9570
  try {
9366
- return readTranscriptEvents(readFileSync6(jsonl, "utf-8"), chat.updatedAtMs);
9571
+ return readTranscriptEvents(readFileSync7(jsonl, "utf-8"), chat.updatedAtMs);
9367
9572
  } catch {
9368
9573
  return [];
9369
9574
  }
@@ -9449,9 +9654,9 @@ var init_cursor = __esm({
9449
9654
  const files = [{ name: `${r.chat.chatId}.jsonl`, bytes: Buffer.from(lines, "utf8") }];
9450
9655
  const metaPath = join10(r.chat.dir, "meta.json");
9451
9656
  if (existsSync9(metaPath)) {
9452
- files.push({ name: "meta.json", bytes: readFileSync6(metaPath) });
9657
+ files.push({ name: "meta.json", bytes: readFileSync7(metaPath) });
9453
9658
  }
9454
- return { tool: "cursor", mtime: statSync8(r.chat.dbPath).mtimeMs, files };
9659
+ return { tool: "cursor", mtime: statSync9(r.chat.dbPath).mtimeMs, files };
9455
9660
  }
9456
9661
  const events = readComposerEvents(r.composer);
9457
9662
  if (events.length === 0)
@@ -9459,7 +9664,7 @@ var init_cursor = __esm({
9459
9664
  const body = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
9460
9665
  return {
9461
9666
  tool: "cursor",
9462
- mtime: statSync8(r.composer.globalDbPath).mtimeMs,
9667
+ mtime: statSync9(r.composer.globalDbPath).mtimeMs,
9463
9668
  files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(body, "utf8") }]
9464
9669
  };
9465
9670
  } catch {
@@ -9471,7 +9676,7 @@ var init_cursor = __esm({
9471
9676
  if (!p)
9472
9677
  return "";
9473
9678
  try {
9474
- return readFileSync6(p, "utf-8");
9679
+ return readFileSync7(p, "utf-8");
9475
9680
  } catch {
9476
9681
  return "";
9477
9682
  }
@@ -9481,159 +9686,1247 @@ var init_cursor = __esm({
9481
9686
  }
9482
9687
  });
9483
9688
 
9484
- // ../engine/dist/core/backends/index.js
9485
- function bootstrapBackends() {
9486
- registerBackend(claudeBackend);
9487
- registerBackend(opencodeBackend);
9488
- registerBackend(codexBackend);
9489
- registerBackend(agyBackend);
9490
- registerBackend(cursorBackend);
9491
- }
9492
- var init_backends = __esm({
9493
- "../engine/dist/core/backends/index.js"() {
9494
- "use strict";
9495
- init_tool_backend();
9496
- init_claude2();
9497
- init_opencode();
9498
- init_codex2();
9499
- init_agy();
9500
- init_cursor();
9501
- init_tool_backend();
9502
- _setRegistryBootstrapper(bootstrapBackends);
9689
+ // ../engine/dist/core/backends/hermes.js
9690
+ import { existsSync as existsSync10 } from "fs";
9691
+ function firstUserPrompt(db, sessionId) {
9692
+ try {
9693
+ const row = db.prepare(`
9694
+ SELECT substr(content, 1, 400) AS text FROM messages
9695
+ WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
9696
+ AND (display_kind IS NULL OR display_kind = 'steer')
9697
+ AND length(trim(content)) > 0
9698
+ ORDER BY id LIMIT 1
9699
+ `).get(sessionId);
9700
+ return row?.text ? flatString(row.text.slice(0, 200)) : "";
9701
+ } catch {
9702
+ return "";
9503
9703
  }
9504
- });
9505
-
9506
- // ../engine/dist/core/session-multi-tool.js
9507
- function extractTurnsAny(sessionId, opts = {}) {
9508
- const backend = getBackendForId(sessionId);
9509
- if (!backend)
9510
- return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
9511
- return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
9512
- }
9513
- function replaySessionAny(sessionId) {
9514
- const backend = getBackendForId(sessionId);
9515
- if (!backend)
9516
- return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
9517
- return memoInSessionScope(`replay:${sessionId}`, () => backend.replay(sessionId));
9518
9704
  }
9519
- var init_session_multi_tool = __esm({
9520
- "../engine/dist/core/session-multi-tool.js"() {
9521
- "use strict";
9522
- init_tool_backend();
9523
- init_live_session_scan();
9524
- init_backends();
9525
- }
9526
- });
9527
-
9528
- // ../engine/dist/core/session-outcome.js
9529
- function extractDecisionsFromTurn(turn) {
9530
- if (turn.kind !== "assistant_text" || !turn.text)
9531
- return [];
9532
- const out = [];
9533
- for (const pat of DECISION_PATTERNS) {
9534
- const m = turn.text.match(pat);
9535
- if (m) {
9536
- const text = m[0].replace(/\s+/g, " ").trim();
9537
- if (text.length > 20 && text.length < 280) {
9538
- out.push({ text, ts: turn.ts, tsIso: turn.tsIso, line: turn.line });
9539
- break;
9540
- }
9705
+ function messageRowsToEvents(rows) {
9706
+ const events = [];
9707
+ let line = 0;
9708
+ for (const r of rows) {
9709
+ line++;
9710
+ if (r.active === 0)
9711
+ continue;
9712
+ if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
9713
+ continue;
9714
+ const ts = Math.round((r.timestamp || 0) * 1e3);
9715
+ const tsIso = ts ? new Date(ts).toISOString() : void 0;
9716
+ const text = (r.content ?? "").trim();
9717
+ if (r._compressed_summary === 1) {
9718
+ if (text)
9719
+ events.push({ kind: "summary", ts, tsIso, line, text });
9720
+ continue;
9541
9721
  }
9542
- }
9543
- return out;
9544
- }
9545
- function findLastClaimAndReaction(turns) {
9546
- let lastAssistantIdx = -1;
9547
- for (let i = turns.length - 1; i >= 0; i--) {
9548
- if (turns[i].kind === "assistant_text" && turns[i].text && turns[i].text.trim().length > 30) {
9549
- lastAssistantIdx = i;
9550
- break;
9722
+ if (r.role === "user") {
9723
+ if (!text)
9724
+ continue;
9725
+ events.push({
9726
+ kind: "user",
9727
+ ts,
9728
+ tsIso,
9729
+ line,
9730
+ text,
9731
+ ...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
9732
+ });
9733
+ continue;
9551
9734
  }
9552
- }
9553
- if (lastAssistantIdx === -1)
9554
- return {};
9555
- const claim = turns[lastAssistantIdx];
9556
- let reaction;
9557
- for (let i = lastAssistantIdx + 1; i < turns.length; i++) {
9558
- if (turns[i].kind === "user" && turns[i].text && turns[i].text.trim().length > 0) {
9559
- reaction = turns[i];
9560
- break;
9735
+ if (r.role === "assistant") {
9736
+ if (text)
9737
+ events.push({ kind: "assistant_text", ts, tsIso, line, text });
9738
+ for (const call of parseToolCalls(r.tool_calls)) {
9739
+ const toolName = call.function?.name ?? "";
9740
+ const toolInput = parseArguments(call.function?.arguments);
9741
+ const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
9742
+ events.push({
9743
+ kind: "tool_use",
9744
+ ts,
9745
+ tsIso,
9746
+ line,
9747
+ toolName,
9748
+ toolUseId: call.id || call.call_id || `${r.id}:${toolName}`,
9749
+ toolInput,
9750
+ ...command ? { command } : {}
9751
+ });
9752
+ }
9753
+ continue;
9561
9754
  }
9562
- }
9563
- if (!reaction) {
9564
- return {
9565
- claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line }
9566
- };
9567
- }
9568
- const marked = markPrompt(reaction.text);
9569
- return {
9570
- claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line },
9571
- reaction: {
9572
- text: reaction.text,
9573
- ts: reaction.ts,
9574
- tsIso: reaction.tsIso,
9575
- line: reaction.line,
9576
- markers: marked.markers,
9577
- intensity: marked.intensity
9755
+ if (r.role === "tool") {
9756
+ const body = r.content ?? "";
9757
+ const { exitCode, isError } = toolResultStatus(body);
9758
+ events.push({
9759
+ kind: "tool_result",
9760
+ ts,
9761
+ tsIso,
9762
+ line,
9763
+ toolUseId: r.tool_call_id ?? void 0,
9764
+ toolName: r.tool_name ?? void 0,
9765
+ resultBody: body,
9766
+ resultIsError: isError,
9767
+ ...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
9768
+ resultBytes: Buffer.byteLength(body)
9769
+ });
9578
9770
  }
9579
- };
9580
- }
9581
- function classifyStatus(opts) {
9582
- const lastPrompt = opts.prompts[opts.prompts.length - 1];
9583
- if (lastPrompt && lastPrompt.markers.includes("interrupt")) {
9584
- return { status: "interrupted", reason: "last user message was an interrupt" };
9585
- }
9586
- const ageHours = (Date.now() - opts.endMs) / (3600 * 1e3);
9587
- if (ageHours < 2) {
9588
- return { status: "in_progress", reason: "most recent activity is within the last 2 hours" };
9589
9771
  }
9590
- if (opts.hasCommits) {
9591
- return { status: "shipped", reason: "commits landed in repos that the session edited during its window" };
9772
+ return events;
9773
+ }
9774
+ function parseToolCalls(json) {
9775
+ if (!json)
9776
+ return [];
9777
+ try {
9778
+ const calls = JSON.parse(json);
9779
+ return Array.isArray(calls) ? calls : [];
9780
+ } catch {
9781
+ return [];
9592
9782
  }
9593
- if (opts.filesChanged === 0) {
9594
- return { status: "unknown", reason: "no file edits and no commits in window" };
9783
+ }
9784
+ function parseArguments(args) {
9785
+ if (typeof args !== "string")
9786
+ return args ?? {};
9787
+ try {
9788
+ return JSON.parse(args);
9789
+ } catch {
9790
+ return { raw: args };
9595
9791
  }
9596
- const lastAssistant = [...opts.turns].reverse().find((t) => t.kind === "assistant_text" && t.text);
9597
- if (lastAssistant && lastAssistant.text && /\?\s*$/.test(lastAssistant.text.trim())) {
9598
- return { status: "abandoned", reason: "last assistant turn ended on a question and nothing landed" };
9792
+ }
9793
+ function toolResultStatus(body) {
9794
+ if (!body.startsWith("{"))
9795
+ return { isError: false };
9796
+ try {
9797
+ const r = JSON.parse(body);
9798
+ const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
9799
+ const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
9800
+ return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
9801
+ } catch {
9802
+ return { isError: false };
9599
9803
  }
9600
- return { status: "abandoned", reason: "edits made but no commits \u2014 work stayed local" };
9601
9804
  }
9602
- function computeOutcome(sessionId, opts = {}) {
9603
- const backend = getBackendForId(sessionId);
9604
- const notFound = {
9605
- sessionId,
9606
- found: false,
9607
- status: "unknown",
9608
- reason: "session not found",
9609
- startMs: 0,
9610
- endMs: 0,
9611
- decisions: [],
9612
- blockers: [],
9613
- claimReaction: {},
9614
- prompts: [],
9615
- promptMarkers: { total: 0, interrupt: 0, frustrated: 0, correction: 0, approval: 0, question: 0, directive: 0, clarification_request: 0, peakIntensity: 0 },
9616
- commits: { sessionId, startMs: 0, endMs: 0, repos: [], totalCommits: 0 },
9617
- fileCount: 0,
9618
- filesChanged: [],
9619
- totalLinesAdded: 0,
9620
- totalLinesRemoved: 0
9621
- };
9622
- if (!backend)
9623
- return notFound;
9624
- const turnsRes = extractTurnsAny(sessionId, { assistantMax: 2e3 });
9625
- if (!turnsRes.found)
9626
- return notFound;
9627
- const decisions = [];
9628
- const blockers = [];
9629
- for (const t of turnsRes.turns) {
9630
- if (t.kind === "assistant_text")
9631
- decisions.push(...extractDecisionsFromTurn(t));
9632
- if (t.kind === "tool_result" && t.resultIsError) {
9633
- blockers.push({
9634
- kind: "tool_error",
9635
- text: t.resultSummary?.slice(0, 240) || "tool error",
9636
- ts: t.ts,
9805
+ var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
9806
+ var init_hermes = __esm({
9807
+ "../engine/dist/core/backends/hermes.js"() {
9808
+ "use strict";
9809
+ init_sqlite_reader();
9810
+ init_tool_paths();
9811
+ init_session_outcome();
9812
+ init_session_git();
9813
+ init_generic_engine();
9814
+ init_flat_string();
9815
+ PREFIX5 = "hermes_";
9816
+ MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
9817
+ ARCHIVED_SESSION_COLUMNS = [
9818
+ "id",
9819
+ "source",
9820
+ "title",
9821
+ "model",
9822
+ "cwd",
9823
+ "git_branch",
9824
+ "git_repo_root",
9825
+ "parent_session_id",
9826
+ "started_at",
9827
+ "ended_at",
9828
+ "end_reason",
9829
+ "message_count",
9830
+ "tool_call_count",
9831
+ "input_tokens",
9832
+ "output_tokens",
9833
+ "cache_read_tokens",
9834
+ "cache_write_tokens",
9835
+ "reasoning_tokens",
9836
+ "estimated_cost_usd",
9837
+ "actual_cost_usd"
9838
+ ];
9839
+ SESSION_MTIME_SQL = `MAX(
9840
+ s.started_at,
9841
+ COALESCE(s.ended_at, 0),
9842
+ COALESCE(s.last_activity_at, 0),
9843
+ COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
9844
+ )`;
9845
+ HermesBackend = class {
9846
+ id = "hermes";
9847
+ idPrefix = PREFIX5;
9848
+ displayName = "Hermes";
9849
+ homeDir() {
9850
+ return hermesHomeDir();
9851
+ }
9852
+ /** Every Hermes database on this machine, the root profile first. */
9853
+ dbPaths() {
9854
+ return hermesDbPaths();
9855
+ }
9856
+ isAvailable() {
9857
+ return this.dbPaths().some((p) => existsSync10(p));
9858
+ }
9859
+ /** The database that holds this session. A session id is unique within a
9860
+ * database, and each named profile has its own. */
9861
+ dbPathFor(rawId) {
9862
+ const cached = this._dbForSession.get(rawId);
9863
+ if (cached)
9864
+ return cached;
9865
+ for (const path of this.dbPaths()) {
9866
+ const db = openSqliteReadonly(path);
9867
+ if (!db)
9868
+ continue;
9869
+ try {
9870
+ if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
9871
+ this._dbForSession.set(rawId, path);
9872
+ return path;
9873
+ }
9874
+ } catch {
9875
+ } finally {
9876
+ db.close();
9877
+ }
9878
+ }
9879
+ return null;
9880
+ }
9881
+ _dbForSession = /* @__PURE__ */ new Map();
9882
+ /** Tests, and anything that adds a profile mid-process. */
9883
+ _clearDbRouting() {
9884
+ this._dbForSession.clear();
9885
+ }
9886
+ // ── ID handling ────────────────────────────────────────────────
9887
+ matchesId(id) {
9888
+ return id.startsWith(PREFIX5);
9889
+ }
9890
+ toRawId(id) {
9891
+ return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
9892
+ }
9893
+ toPrefixedId(rawId) {
9894
+ return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
9895
+ }
9896
+ // ── Location ───────────────────────────────────────────────────
9897
+ findSession(id) {
9898
+ const rawId = this.toRawId(id);
9899
+ const path = this.dbPathFor(rawId);
9900
+ if (!path)
9901
+ return null;
9902
+ const db = openSqliteReadonly(path);
9903
+ if (!db)
9904
+ return null;
9905
+ try {
9906
+ const row = db.prepare(`
9907
+ SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
9908
+ FROM sessions s WHERE s.id = ?
9909
+ `).get(rawId);
9910
+ if (!row)
9911
+ return null;
9912
+ return {
9913
+ path,
9914
+ format: "sqlite",
9915
+ projectDir: "",
9916
+ projectPath: row.git_repo_root || row.cwd || "",
9917
+ mtime: Math.round((row.mtime || 0) * 1e3)
9918
+ };
9919
+ } catch {
9920
+ return null;
9921
+ } finally {
9922
+ db.close();
9923
+ }
9924
+ }
9925
+ listSessions(opts = {}) {
9926
+ const byId = /* @__PURE__ */ new Map();
9927
+ for (const dbPath of this.dbPaths()) {
9928
+ if (!existsSync10(dbPath))
9929
+ continue;
9930
+ for (const ref of this.listSessionsInDb(dbPath, opts)) {
9931
+ const prior = byId.get(ref.rawId);
9932
+ if (!prior || ref.mtime > prior.mtime)
9933
+ byId.set(ref.rawId, ref);
9934
+ }
9935
+ }
9936
+ const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
9937
+ return opts.limit ? all.slice(0, opts.limit) : all;
9938
+ }
9939
+ listSessionsInDb(dbPath, opts) {
9940
+ const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
9941
+ const filter = opts.projectFilter?.toLowerCase();
9942
+ const db = openSqliteReadonly(dbPath);
9943
+ if (!db)
9944
+ return [];
9945
+ try {
9946
+ const rows = db.prepare(`
9947
+ SELECT * FROM (
9948
+ SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
9949
+ ${SESSION_MTIME_SQL} AS mtime
9950
+ FROM sessions s
9951
+ ) WHERE mtime >= ? ORDER BY mtime DESC
9952
+ `).all(cutoffSec);
9953
+ const out = [];
9954
+ for (const row of rows) {
9955
+ const projectPath = row.git_repo_root || row.cwd || "";
9956
+ if (filter && !projectPath.toLowerCase().includes(filter))
9957
+ continue;
9958
+ const mtime = Math.round(row.mtime * 1e3);
9959
+ this._dbForSession.set(row.id, dbPath);
9960
+ out.push({
9961
+ toolId: "hermes",
9962
+ rawId: row.id,
9963
+ prefixedId: this.toPrefixedId(row.id),
9964
+ projectPath,
9965
+ projectDir: "",
9966
+ fullPath: dbPath,
9967
+ created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
9968
+ modified: new Date(mtime).toISOString(),
9969
+ mtime,
9970
+ firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
9971
+ messageCount: opts.previews === false ? 0 : row.message_count ?? 0
9972
+ });
9973
+ if (opts.limit && out.length >= opts.limit)
9974
+ break;
9975
+ }
9976
+ return out;
9977
+ } catch {
9978
+ return [];
9979
+ } finally {
9980
+ db.close();
9981
+ }
9982
+ }
9983
+ /** Hermes writes a title for each session (a model-written one, or the
9984
+ * first prompt for a one-shot run). */
9985
+ getNativeTitle(rawId) {
9986
+ const path = this.dbPathFor(this.toRawId(rawId));
9987
+ if (!path)
9988
+ return null;
9989
+ const db = openSqliteReadonly(path);
9990
+ if (!db)
9991
+ return null;
9992
+ try {
9993
+ const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
9994
+ const t = row?.title?.trim();
9995
+ return t ? t.slice(0, 200) : null;
9996
+ } catch {
9997
+ return null;
9998
+ } finally {
9999
+ db.close();
10000
+ }
10001
+ }
10002
+ // ── Generic-engine inputs ───────────────────────────────────────
10003
+ fileToolMap = {
10004
+ write_file: "write",
10005
+ patch: "edit",
10006
+ read_file: "read"
10007
+ };
10008
+ /**
10009
+ * `write_file` carries `{ path, content }`. `patch` carries
10010
+ * `{ mode: 'replace', path, old_string, new_string }`; its other mode,
10011
+ * `patch`, is a multi-file V4A patch with no single before and after.
10012
+ */
10013
+ extractEditDelta(toolName, input) {
10014
+ if (input == null || typeof input !== "object")
10015
+ return null;
10016
+ const inp = input;
10017
+ if (toolName === "write_file") {
10018
+ return { before: "", after: typeof inp.content === "string" ? inp.content : null };
10019
+ }
10020
+ if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
10021
+ const before = typeof inp.old_string === "string" ? inp.old_string : null;
10022
+ const after = typeof inp.new_string === "string" ? inp.new_string : null;
10023
+ if (before === null && after === null)
10024
+ return null;
10025
+ return { before, after };
10026
+ }
10027
+ return null;
10028
+ }
10029
+ readEvents(rawId) {
10030
+ const id = this.toRawId(rawId);
10031
+ const path = this.dbPathFor(id);
10032
+ if (!path)
10033
+ return [];
10034
+ const db = openSqliteReadonly(path);
10035
+ if (!db)
10036
+ return [];
10037
+ try {
10038
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
10039
+ return messageRowsToEvents(rows);
10040
+ } catch {
10041
+ return [];
10042
+ } finally {
10043
+ db.close();
10044
+ }
10045
+ }
10046
+ /**
10047
+ * Events from the dump `exportRawSession` writes, for a session rebuilt from
10048
+ * the server's archive. The archive joins the lines of every dump it got, so
10049
+ * a row that changed (a rewind sets `active` to 0) is there more than once.
10050
+ * The last copy of each row id is the newest.
10051
+ */
10052
+ readEventsFromText(text) {
10053
+ const rows = /* @__PURE__ */ new Map();
10054
+ for (const line of text.split("\n")) {
10055
+ if (!line.trim())
10056
+ continue;
10057
+ try {
10058
+ const rec = JSON.parse(line);
10059
+ if (rec.kind === "message" && rec.row) {
10060
+ rows.delete(rec.row.id);
10061
+ rows.set(rec.row.id, rec.row);
10062
+ }
10063
+ } catch {
10064
+ }
10065
+ }
10066
+ return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
10067
+ }
10068
+ // ── Per-session operations — all delegate to the generic engine ─
10069
+ extractTurns(id, opts = {}) {
10070
+ return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
10071
+ }
10072
+ liveScanEdits(id) {
10073
+ const located = this.findSession(id);
10074
+ if (!located) {
10075
+ return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
10076
+ }
10077
+ return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
10078
+ sessionId: this.toPrefixedId(id),
10079
+ tool: "hermes",
10080
+ projectPath: located.projectPath,
10081
+ projectDir: located.projectDir,
10082
+ fileMtime: located.mtime,
10083
+ found: true
10084
+ });
10085
+ }
10086
+ replay(id) {
10087
+ const located = this.findSession(id);
10088
+ if (!located) {
10089
+ return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
10090
+ }
10091
+ return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
10092
+ projectPath: located.projectPath,
10093
+ found: true
10094
+ });
10095
+ }
10096
+ computeOutcome(id, opts) {
10097
+ return computeOutcome(this.toPrefixedId(id), opts);
10098
+ }
10099
+ /** File edits since `sinceMs` across the sessions active since then. */
10100
+ collectRecentEdits(opts) {
10101
+ const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
10102
+ const edits = [];
10103
+ for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
10104
+ for (const e of this.liveScanEdits(ref.rawId).edits) {
10105
+ if (e.ts >= opts.sinceMs)
10106
+ edits.push(e);
10107
+ }
10108
+ }
10109
+ return edits.sort((a, b) => b.ts - a.ts);
10110
+ }
10111
+ getCommits(id, files, startMs, endMs, bufferMinutes) {
10112
+ return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
10113
+ }
10114
+ /**
10115
+ * The session row and its messages as JSONL, one row per line in id order.
10116
+ * Hermes rewrites and compacts its database, so the archive keeps the rows
10117
+ * as they were synced.
10118
+ */
10119
+ exportRawSession(id) {
10120
+ const rawId = this.toRawId(id);
10121
+ const path = this.dbPathFor(rawId);
10122
+ if (!path)
10123
+ return null;
10124
+ const db = openSqliteReadonly(path);
10125
+ if (!db)
10126
+ return null;
10127
+ try {
10128
+ const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
10129
+ const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
10130
+ const session = db.prepare(`
10131
+ SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
10132
+ FROM sessions s WHERE s.id = ?
10133
+ `).get(rawId);
10134
+ if (!session)
10135
+ return null;
10136
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
10137
+ const lines = [JSON.stringify({ kind: "session", row: session })];
10138
+ for (const r of rows)
10139
+ lines.push(JSON.stringify({ kind: "message", row: r }));
10140
+ return {
10141
+ tool: "hermes",
10142
+ mtime: Math.round(Number(session.mtime || 0) * 1e3),
10143
+ files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
10144
+ };
10145
+ } catch {
10146
+ return null;
10147
+ } finally {
10148
+ db.close();
10149
+ }
10150
+ }
10151
+ };
10152
+ hermesBackend = new HermesBackend();
10153
+ }
10154
+ });
10155
+
10156
+ // ../engine/dist/core/backends/pi.js
10157
+ import { readdirSync as readdirSync7, readFileSync as readFileSync8, statSync as statSync10, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
10158
+ import { createHash } from "crypto";
10159
+ import { basename as basename6, join as join11 } from "path";
10160
+ function parsePiFile(path) {
10161
+ let raw;
10162
+ try {
10163
+ raw = readFileSync8(path, "utf-8");
10164
+ } catch {
10165
+ return { header: null, entries: [] };
10166
+ }
10167
+ const out = { header: null, entries: [] };
10168
+ for (const line of raw.split("\n")) {
10169
+ if (!line.trim())
10170
+ continue;
10171
+ let obj;
10172
+ try {
10173
+ obj = JSON.parse(line);
10174
+ } catch {
10175
+ continue;
10176
+ }
10177
+ if (!obj || typeof obj !== "object")
10178
+ continue;
10179
+ if (obj.type === "session" && !out.header) {
10180
+ out.header = obj;
10181
+ continue;
10182
+ }
10183
+ out.entries.push(obj);
10184
+ }
10185
+ return out;
10186
+ }
10187
+ function idFromFilename(file) {
10188
+ if (!file.endsWith(".jsonl"))
10189
+ return null;
10190
+ const stem = file.slice(0, -".jsonl".length);
10191
+ const at = stem.indexOf("_");
10192
+ const id = at === -1 ? stem : stem.slice(at + 1);
10193
+ return id || null;
10194
+ }
10195
+ function* walkSessionFiles(root, depth = 2) {
10196
+ if (depth < 0)
10197
+ return;
10198
+ let entries;
10199
+ try {
10200
+ entries = readdirSync7(root, { withFileTypes: true });
10201
+ } catch {
10202
+ return;
10203
+ }
10204
+ for (const entry of entries) {
10205
+ if (entry.name.startsWith("."))
10206
+ continue;
10207
+ const path = join11(root, entry.name);
10208
+ if (entry.isDirectory()) {
10209
+ yield* walkSessionFiles(path, depth - 1);
10210
+ continue;
10211
+ }
10212
+ if (entry.isFile() && entry.name.endsWith(".jsonl"))
10213
+ yield path;
10214
+ }
10215
+ }
10216
+ function readPiHeader(path) {
10217
+ let fd = null;
10218
+ try {
10219
+ fd = openSync3(path, "r");
10220
+ const buf = Buffer.alloc(8192);
10221
+ const n = readSync3(fd, buf, 0, buf.length, 0);
10222
+ const firstLine = buf.toString("utf-8", 0, n).split("\n", 1)[0];
10223
+ if (!firstLine.trim())
10224
+ return null;
10225
+ const obj = JSON.parse(firstLine);
10226
+ if (!obj || typeof obj !== "object" || obj.type !== "session")
10227
+ return null;
10228
+ return obj;
10229
+ } catch {
10230
+ return null;
10231
+ } finally {
10232
+ if (fd !== null) {
10233
+ try {
10234
+ closeSync3(fd);
10235
+ } catch {
10236
+ }
10237
+ }
10238
+ }
10239
+ }
10240
+ function dirSignature(root) {
10241
+ const parts = [];
10242
+ try {
10243
+ parts.push(String(statSync10(root).mtimeMs));
10244
+ } catch {
10245
+ return "missing";
10246
+ }
10247
+ let entries = [];
10248
+ try {
10249
+ entries = readdirSync7(root, { withFileTypes: true });
10250
+ } catch {
10251
+ return parts.join(",");
10252
+ }
10253
+ for (const entry of entries) {
10254
+ if (!entry.isDirectory())
10255
+ continue;
10256
+ try {
10257
+ parts.push(`${entry.name}:${statSync10(join11(root, entry.name)).mtimeMs}`);
10258
+ } catch {
10259
+ }
10260
+ }
10261
+ parts.sort();
10262
+ return parts.join(",");
10263
+ }
10264
+ function piSessionKey(file) {
10265
+ return `${file.id}~${projectSuffix(file.projectPath || file.projectDir)}`;
10266
+ }
10267
+ function projectSuffix(projectDir) {
10268
+ return createHash("sha256").update(projectDir).digest("hex").slice(0, 8);
10269
+ }
10270
+ function mtimeOf(path, fallback) {
10271
+ try {
10272
+ return statSync10(path).mtimeMs;
10273
+ } catch {
10274
+ return fallback;
10275
+ }
10276
+ }
10277
+ function piText(content) {
10278
+ if (typeof content === "string")
10279
+ return content;
10280
+ if (!Array.isArray(content))
10281
+ return "";
10282
+ const parts = [];
10283
+ for (const block of content) {
10284
+ if (!block || typeof block !== "object")
10285
+ continue;
10286
+ const b = block;
10287
+ if (b.type === "text" && typeof b.text === "string")
10288
+ parts.push(b.text);
10289
+ }
10290
+ return parts.join("\n");
10291
+ }
10292
+ function piToolInput(args) {
10293
+ return args && typeof args === "object" && !Array.isArray(args) ? { ...args } : {};
10294
+ }
10295
+ var PREFIX6, PiBackend, piBackend;
10296
+ var init_pi = __esm({
10297
+ "../engine/dist/core/backends/pi.js"() {
10298
+ "use strict";
10299
+ init_tail_read();
10300
+ init_tool_paths();
10301
+ init_session_outcome();
10302
+ init_session_git();
10303
+ init_generic_engine();
10304
+ init_flat_string();
10305
+ PREFIX6 = "pi_";
10306
+ PiBackend = class {
10307
+ id = "pi";
10308
+ idPrefix = PREFIX6;
10309
+ displayName = "Pi";
10310
+ homeDir() {
10311
+ return piHomeDir();
10312
+ }
10313
+ /** Session root(s) — Pi has a single agent dir, so this is one entry. */
10314
+ sessionDirs() {
10315
+ return piSessionDirs();
10316
+ }
10317
+ skillsDir() {
10318
+ return piSkillsDir();
10319
+ }
10320
+ promptsDir() {
10321
+ return piPromptsDir();
10322
+ }
10323
+ mcpConfigPath() {
10324
+ return piMcpConfigPath();
10325
+ }
10326
+ isAvailable() {
10327
+ return this.sessionDirs().length > 0;
10328
+ }
10329
+ // ── ID handling ────────────────────────────────────────────────
10330
+ matchesId(id) {
10331
+ return id.startsWith(PREFIX6);
10332
+ }
10333
+ /**
10334
+ * A Pi id can itself start with `<prefix>` (`--session-id` allows `_`), so the
10335
+ * bare prefix does not identify a stored id. The index does.
10336
+ */
10337
+ toRawId(id) {
10338
+ if (!id.startsWith(PREFIX6))
10339
+ return id;
10340
+ if (this.fileIndex().has(id))
10341
+ return id;
10342
+ return id.slice(PREFIX6.length);
10343
+ }
10344
+ toPrefixedId(rawId) {
10345
+ if (!rawId.startsWith(PREFIX6))
10346
+ return PREFIX6 + rawId;
10347
+ return this.fileIndex().has(rawId) ? PREFIX6 + rawId : rawId;
10348
+ }
10349
+ // ── Location ───────────────────────────────────────────────────
10350
+ /**
10351
+ * Session key → file path for every session on this machine.
10352
+ *
10353
+ * The entry list is cached on the project dirs. When two files carry one key
10354
+ * — a copy of a session — this stats them on every call, because an append to
10355
+ * the live file changes its mtime and not its folder.
10356
+ */
10357
+ fileIndex() {
10358
+ const roots = this.sessionDirs();
10359
+ const signature = roots.map(dirSignature).join("|");
10360
+ if (this._index?.signature !== signature) {
10361
+ const entries2 = [];
10362
+ for (const root of roots) {
10363
+ for (const path of walkSessionFiles(root)) {
10364
+ let mtime = 0;
10365
+ try {
10366
+ mtime = statSync10(path).mtimeMs;
10367
+ } catch {
10368
+ continue;
10369
+ }
10370
+ const header = readPiHeader(path);
10371
+ const id = header ? header.id || idFromFilename(basename6(path)) : "";
10372
+ entries2.push({
10373
+ id: id || "",
10374
+ projectPath: header?.cwd || "",
10375
+ projectDir: basename6(join11(path, "..")),
10376
+ mtime,
10377
+ path
10378
+ });
10379
+ }
10380
+ }
10381
+ this._index = { signature, entries: entries2 };
10382
+ }
10383
+ const entries = this._index.entries;
10384
+ const seen = /* @__PURE__ */ new Map();
10385
+ for (const entry of entries) {
10386
+ if (!entry.id) {
10387
+ const header = readPiHeader(entry.path);
10388
+ if (!header)
10389
+ continue;
10390
+ entry.id = header.id || idFromFilename(basename6(entry.path)) || "";
10391
+ entry.projectPath = header.cwd || "";
10392
+ if (!entry.id)
10393
+ continue;
10394
+ }
10395
+ const key = piSessionKey(entry);
10396
+ const prior = seen.get(key);
10397
+ if (!prior) {
10398
+ seen.set(key, entry);
10399
+ continue;
10400
+ }
10401
+ const priorMtime = mtimeOf(prior.path, prior.mtime);
10402
+ const entryMtime = mtimeOf(entry.path, entry.mtime);
10403
+ if (entryMtime > priorMtime)
10404
+ seen.set(key, entry);
10405
+ }
10406
+ const files = /* @__PURE__ */ new Map();
10407
+ for (const [key, file] of seen)
10408
+ files.set(key, file.path);
10409
+ return files;
10410
+ }
10411
+ _index = null;
10412
+ findSession(id) {
10413
+ const rawId = this.toRawId(id);
10414
+ const path = this.fileIndex().get(rawId);
10415
+ if (!path)
10416
+ return null;
10417
+ const header = readPiHeader(path);
10418
+ if (!header)
10419
+ return null;
10420
+ let mtime = 0;
10421
+ try {
10422
+ mtime = statSync10(path).mtimeMs;
10423
+ } catch {
10424
+ }
10425
+ return {
10426
+ path,
10427
+ format: "jsonl",
10428
+ projectDir: basename6(join11(path, "..")),
10429
+ projectPath: header.cwd || "",
10430
+ mtime
10431
+ };
10432
+ }
10433
+ listSessions(opts = {}) {
10434
+ const cutoff = opts.sinceMs ?? 0;
10435
+ const filter = opts.projectFilter?.toLowerCase();
10436
+ const out = [];
10437
+ for (const [rawId, path] of this.fileIndex()) {
10438
+ let stat;
10439
+ try {
10440
+ stat = statSync10(path);
10441
+ } catch {
10442
+ continue;
10443
+ }
10444
+ if (stat.mtimeMs < cutoff)
10445
+ continue;
10446
+ const header = readPiHeader(path);
10447
+ if (!header)
10448
+ continue;
10449
+ const projectPath = header.cwd || "";
10450
+ if (filter && !projectPath.toLowerCase().includes(filter))
10451
+ continue;
10452
+ let firstPrompt = "";
10453
+ let messageCount = 0;
10454
+ if (opts.previews !== false) {
10455
+ const parsed = parsePiFile(path);
10456
+ for (const entry of parsed.entries) {
10457
+ if (entry.type !== "message")
10458
+ continue;
10459
+ const role = entry.message?.role;
10460
+ if (role !== "user" && role !== "assistant")
10461
+ continue;
10462
+ messageCount++;
10463
+ if (!firstPrompt && role === "user") {
10464
+ const text = piText(entry.message?.content).trim();
10465
+ if (text)
10466
+ firstPrompt = flatString(text.slice(0, 200));
10467
+ }
10468
+ }
10469
+ }
10470
+ out.push({
10471
+ toolId: "pi",
10472
+ rawId,
10473
+ prefixedId: this.toPrefixedId(rawId),
10474
+ projectPath,
10475
+ projectDir: basename6(join11(path, "..")),
10476
+ fullPath: path,
10477
+ created: header.timestamp || stat.birthtime.toISOString(),
10478
+ modified: stat.mtime.toISOString(),
10479
+ mtime: stat.mtimeMs,
10480
+ firstPrompt,
10481
+ messageCount
10482
+ });
10483
+ }
10484
+ out.sort((a, b) => b.mtime - a.mtime);
10485
+ return opts.limit ? out.slice(0, opts.limit) : out;
10486
+ }
10487
+ // ── Generic-engine inputs ───────────────────────────────────────
10488
+ /**
10489
+ * The file tools and their EditOp. A shell command carries no path, so it
10490
+ * produces no file entry.
10491
+ */
10492
+ fileToolMap = {
10493
+ write: "write",
10494
+ edit: "edit",
10495
+ read: "read"
10496
+ };
10497
+ /**
10498
+ * The inline diff of a file tool.
10499
+ * - write: { path, content }.
10500
+ * - edit: { path, edits: [{ oldText, newText }] }.
10501
+ *
10502
+ * An edit holds several disjoint replacements. Join them into one pair.
10503
+ */
10504
+ extractEditDelta(toolName, input) {
10505
+ if (input == null || typeof input !== "object")
10506
+ return null;
10507
+ const inp = input;
10508
+ if (toolName === "write") {
10509
+ const after = typeof inp.content === "string" ? inp.content : null;
10510
+ if (after === null)
10511
+ return null;
10512
+ return { before: "", after };
10513
+ }
10514
+ if (toolName === "edit") {
10515
+ const edits = Array.isArray(inp.edits) ? inp.edits : [];
10516
+ let before = "";
10517
+ let after = "";
10518
+ for (const raw of edits) {
10519
+ if (!raw || typeof raw !== "object")
10520
+ continue;
10521
+ const e = raw;
10522
+ if (typeof e.oldText === "string")
10523
+ before += (before ? "\n" : "") + e.oldText;
10524
+ if (typeof e.newText === "string")
10525
+ after += (after ? "\n" : "") + e.newText;
10526
+ }
10527
+ if (!before && !after)
10528
+ return null;
10529
+ return { before: before || null, after: after || null };
10530
+ }
10531
+ return null;
10532
+ }
10533
+ readEvents(rawId) {
10534
+ const located = this.findSession(rawId);
10535
+ if (!located)
10536
+ return [];
10537
+ let rawText;
10538
+ try {
10539
+ rawText = readFileSync8(located.path, "utf-8");
10540
+ } catch {
10541
+ return [];
10542
+ }
10543
+ return this.readEventsFromText(rawText, located.mtime);
10544
+ }
10545
+ /**
10546
+ * Parse Pi session text into canonical events. Split out from readEvents so
10547
+ * the same parser runs on archived/shadow container bytes.
10548
+ */
10549
+ readEventsFromText(rawText, mtimeFallback = 0) {
10550
+ const events = [];
10551
+ let lineNum = 0;
10552
+ for (const line of rawText.split("\n")) {
10553
+ lineNum++;
10554
+ if (!line.trim())
10555
+ continue;
10556
+ let obj;
10557
+ try {
10558
+ obj = JSON.parse(line);
10559
+ } catch {
10560
+ continue;
10561
+ }
10562
+ const tsIso = typeof obj.timestamp === "string" ? obj.timestamp : void 0;
10563
+ const msgTs = typeof obj.message?.timestamp === "number" ? obj.message.timestamp : void 0;
10564
+ const ts = tsIso ? Date.parse(tsIso) || mtimeFallback : msgTs ?? mtimeFallback;
10565
+ if (obj.type === "message") {
10566
+ const msg = obj.message || {};
10567
+ const role = msg.role;
10568
+ if (role === "user") {
10569
+ const text = piText(msg.content).trim();
10570
+ if (text)
10571
+ events.push({ kind: "user", ts, tsIso, line: lineNum, text });
10572
+ continue;
10573
+ }
10574
+ if (role === "assistant") {
10575
+ const text = piText(msg.content).trim();
10576
+ if (text)
10577
+ events.push({ kind: "assistant_text", ts, tsIso, line: lineNum, text });
10578
+ const blocks = Array.isArray(msg.content) ? msg.content : [];
10579
+ for (const block of blocks) {
10580
+ if (!block || typeof block !== "object" || block.type !== "toolCall")
10581
+ continue;
10582
+ const toolName = String(block.name || "");
10583
+ if (!toolName)
10584
+ continue;
10585
+ const input = piToolInput(block.arguments);
10586
+ const command = toolName === "bash" || toolName === "powershell" ? typeof input.command === "string" ? input.command : void 0 : void 0;
10587
+ events.push({
10588
+ kind: "tool_use",
10589
+ ts,
10590
+ tsIso,
10591
+ line: lineNum,
10592
+ toolName,
10593
+ toolUseId: typeof block.id === "string" ? block.id : "",
10594
+ toolInput: input,
10595
+ command
10596
+ });
10597
+ }
10598
+ continue;
10599
+ }
10600
+ if (role === "toolResult") {
10601
+ const body = piText(msg.content);
10602
+ events.push({
10603
+ kind: "tool_result",
10604
+ ts,
10605
+ tsIso,
10606
+ line: lineNum,
10607
+ toolName: typeof msg.toolName === "string" ? msg.toolName : void 0,
10608
+ toolUseId: typeof msg.toolCallId === "string" ? msg.toolCallId : "",
10609
+ resultBody: body,
10610
+ resultIsError: msg.isError === true,
10611
+ resultBytes: body.length
10612
+ });
10613
+ continue;
10614
+ }
10615
+ if (role === "bashExecution") {
10616
+ const command = typeof msg.command === "string" ? msg.command : "";
10617
+ const output = typeof msg.output === "string" ? msg.output : "";
10618
+ const toolUseId = `pi_bash_${lineNum}`;
10619
+ events.push({
10620
+ kind: "tool_use",
10621
+ ts,
10622
+ tsIso,
10623
+ line: lineNum,
10624
+ toolName: "bash",
10625
+ toolUseId,
10626
+ toolInput: { command },
10627
+ command
10628
+ });
10629
+ events.push({
10630
+ kind: "tool_result",
10631
+ ts,
10632
+ tsIso,
10633
+ line: lineNum,
10634
+ toolUseId,
10635
+ toolName: "bash",
10636
+ resultBody: output,
10637
+ resultIsError: typeof msg.exitCode === "number" && msg.exitCode !== 0,
10638
+ resultExitCode: typeof msg.exitCode === "number" ? msg.exitCode : void 0,
10639
+ resultBytes: output.length
10640
+ });
10641
+ continue;
10642
+ }
10643
+ continue;
10644
+ }
10645
+ if (obj.type === "compaction" && typeof obj.summary === "string" && obj.summary.trim()) {
10646
+ events.push({ kind: "summary", ts, tsIso, line: lineNum, text: obj.summary.trim() });
10647
+ continue;
10648
+ }
10649
+ if (obj.type === "branch_summary" && typeof obj.summary === "string" && obj.summary.trim()) {
10650
+ events.push({ kind: "summary", ts, tsIso, line: lineNum, text: obj.summary.trim() });
10651
+ }
10652
+ }
10653
+ return events;
10654
+ }
10655
+ // ── Per-session operations — all delegate to the generic engine ─
10656
+ extractTurns(id, opts = {}) {
10657
+ const events = this.readEvents(this.toRawId(id));
10658
+ return extractTurnsFromEvents(this.toPrefixedId(id), events, opts);
10659
+ }
10660
+ liveScanEdits(id) {
10661
+ const located = this.findSession(this.toRawId(id));
10662
+ if (!located) {
10663
+ return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "pi" };
10664
+ }
10665
+ const events = this.readEvents(this.toRawId(id));
10666
+ return liveScanEditsFromEvents(events, this.fileToolMap, {
10667
+ sessionId: this.toPrefixedId(id),
10668
+ tool: "pi",
10669
+ projectPath: located.projectPath,
10670
+ projectDir: located.projectDir,
10671
+ fileMtime: located.mtime,
10672
+ found: true
10673
+ }, this.extractEditDelta.bind(this));
10674
+ }
10675
+ replay(id) {
10676
+ const located = this.findSession(this.toRawId(id));
10677
+ if (!located) {
10678
+ return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
10679
+ }
10680
+ const events = this.readEvents(this.toRawId(id));
10681
+ return replayFromEvents(this.toPrefixedId(id), events, this.fileToolMap, this.extractEditDelta.bind(this), { projectPath: located.projectPath, found: true });
10682
+ }
10683
+ computeOutcome(id, opts) {
10684
+ return computeOutcome(this.toPrefixedId(id), opts);
10685
+ }
10686
+ collectRecentEdits(opts) {
10687
+ const candidates = [];
10688
+ for (const [rawId, path] of this.fileIndex()) {
10689
+ let stat;
10690
+ try {
10691
+ stat = statSync10(path);
10692
+ } catch {
10693
+ continue;
10694
+ }
10695
+ if (stat.mtimeMs < opts.sinceMs)
10696
+ continue;
10697
+ if (opts.projectFilter) {
10698
+ const header = readPiHeader(path);
10699
+ const projectPath = header?.cwd || "";
10700
+ if (!projectPath.toLowerCase().includes(opts.projectFilter.toLowerCase()))
10701
+ continue;
10702
+ }
10703
+ candidates.push({ rawId, mtime: stat.mtimeMs });
10704
+ }
10705
+ candidates.sort((a, b) => b.mtime - a.mtime);
10706
+ const limited = opts.limitSessions ? candidates.slice(0, opts.limitSessions) : candidates;
10707
+ const edits = [];
10708
+ for (const c of limited)
10709
+ edits.push(...this.liveScanEdits(c.rawId).edits);
10710
+ return edits;
10711
+ }
10712
+ getCommits(id, files, startMs, endMs, bufferMinutes) {
10713
+ return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
10714
+ }
10715
+ /**
10716
+ * Pi's user-assigned session name (`/name`, `--name`), from the newest
10717
+ * `session_info` entry. Null when the session was never named.
10718
+ */
10719
+ getNativeTitle(rawId) {
10720
+ const located = this.findSession(rawId);
10721
+ if (!located)
10722
+ return null;
10723
+ const parsed = parsePiFile(located.path);
10724
+ for (let i = parsed.entries.length - 1; i >= 0; i--) {
10725
+ const entry = parsed.entries[i];
10726
+ if (entry.type !== "session_info")
10727
+ continue;
10728
+ const name = typeof entry.name === "string" ? entry.name.trim() : "";
10729
+ if (name)
10730
+ return name;
10731
+ }
10732
+ return null;
10733
+ }
10734
+ exportRawSession(id) {
10735
+ const located = this.findSession(id);
10736
+ if (!located)
10737
+ return null;
10738
+ try {
10739
+ const st = statSync10(located.path);
10740
+ return {
10741
+ tool: "pi",
10742
+ mtime: st.mtimeMs,
10743
+ files: [{ name: basename6(located.path), bytes: readFileSync8(located.path) }]
10744
+ };
10745
+ } catch {
10746
+ return null;
10747
+ }
10748
+ }
10749
+ isAppendOnly() {
10750
+ return true;
10751
+ }
10752
+ fileSize(prefixedId) {
10753
+ const located = this.findSession(prefixedId);
10754
+ if (!located)
10755
+ return 0;
10756
+ try {
10757
+ return statSync10(located.path).size;
10758
+ } catch {
10759
+ return 0;
10760
+ }
10761
+ }
10762
+ async readFromOffset(prefixedId, offset, maxBytes) {
10763
+ const located = this.findSession(prefixedId);
10764
+ if (!located)
10765
+ return { text: "", newOffset: offset };
10766
+ return readTailFromOffset(located.path, offset, maxBytes);
10767
+ }
10768
+ };
10769
+ piBackend = new PiBackend();
10770
+ }
10771
+ });
10772
+
10773
+ // ../engine/dist/core/backends/index.js
10774
+ function bootstrapBackends() {
10775
+ registerBackend(claudeBackend);
10776
+ registerBackend(opencodeBackend);
10777
+ registerBackend(codexBackend);
10778
+ registerBackend(agyBackend);
10779
+ registerBackend(cursorBackend);
10780
+ registerBackend(hermesBackend);
10781
+ registerBackend(piBackend);
10782
+ }
10783
+ var init_backends = __esm({
10784
+ "../engine/dist/core/backends/index.js"() {
10785
+ "use strict";
10786
+ init_tool_backend();
10787
+ init_claude2();
10788
+ init_opencode();
10789
+ init_codex2();
10790
+ init_agy();
10791
+ init_cursor();
10792
+ init_hermes();
10793
+ init_pi();
10794
+ init_tool_backend();
10795
+ _setRegistryBootstrapper(bootstrapBackends);
10796
+ }
10797
+ });
10798
+
10799
+ // ../engine/dist/core/session-multi-tool.js
10800
+ function extractTurnsAny(sessionId, opts = {}) {
10801
+ const backend = getBackendForId(sessionId);
10802
+ if (!backend)
10803
+ return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
10804
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
10805
+ }
10806
+ function replaySessionAny(sessionId) {
10807
+ const backend = getBackendForId(sessionId);
10808
+ if (!backend)
10809
+ return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
10810
+ return memoInSessionScope(`replay:${sessionId}`, () => backend.replay(sessionId));
10811
+ }
10812
+ var init_session_multi_tool = __esm({
10813
+ "../engine/dist/core/session-multi-tool.js"() {
10814
+ "use strict";
10815
+ init_tool_backend();
10816
+ init_live_session_scan();
10817
+ init_backends();
10818
+ }
10819
+ });
10820
+
10821
+ // ../engine/dist/core/session-outcome.js
10822
+ function extractDecisionsFromTurn(turn) {
10823
+ if (turn.kind !== "assistant_text" || !turn.text)
10824
+ return [];
10825
+ const out = [];
10826
+ for (const pat of DECISION_PATTERNS) {
10827
+ const m = turn.text.match(pat);
10828
+ if (m) {
10829
+ const text = m[0].replace(/\s+/g, " ").trim();
10830
+ if (text.length > 20 && text.length < 280) {
10831
+ out.push({ text, ts: turn.ts, tsIso: turn.tsIso, line: turn.line });
10832
+ break;
10833
+ }
10834
+ }
10835
+ }
10836
+ return out;
10837
+ }
10838
+ function findLastClaimAndReaction(turns) {
10839
+ let lastAssistantIdx = -1;
10840
+ for (let i = turns.length - 1; i >= 0; i--) {
10841
+ if (turns[i].kind === "assistant_text" && turns[i].text && turns[i].text.trim().length > 30) {
10842
+ lastAssistantIdx = i;
10843
+ break;
10844
+ }
10845
+ }
10846
+ if (lastAssistantIdx === -1)
10847
+ return {};
10848
+ const claim = turns[lastAssistantIdx];
10849
+ let reaction;
10850
+ for (let i = lastAssistantIdx + 1; i < turns.length; i++) {
10851
+ if (turns[i].kind === "user" && turns[i].text && turns[i].text.trim().length > 0) {
10852
+ reaction = turns[i];
10853
+ break;
10854
+ }
10855
+ }
10856
+ if (!reaction) {
10857
+ return {
10858
+ claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line }
10859
+ };
10860
+ }
10861
+ const marked = markPrompt(reaction.text);
10862
+ return {
10863
+ claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line },
10864
+ reaction: {
10865
+ text: reaction.text,
10866
+ ts: reaction.ts,
10867
+ tsIso: reaction.tsIso,
10868
+ line: reaction.line,
10869
+ markers: marked.markers,
10870
+ intensity: marked.intensity
10871
+ }
10872
+ };
10873
+ }
10874
+ function classifyStatus(opts) {
10875
+ const lastPrompt = opts.prompts[opts.prompts.length - 1];
10876
+ if (lastPrompt && lastPrompt.markers.includes("interrupt")) {
10877
+ return { status: "interrupted", reason: "last user message was an interrupt" };
10878
+ }
10879
+ const ageHours = (Date.now() - opts.endMs) / (3600 * 1e3);
10880
+ if (ageHours < 2) {
10881
+ return { status: "in_progress", reason: "most recent activity is within the last 2 hours" };
10882
+ }
10883
+ if (opts.hasCommits) {
10884
+ return { status: "shipped", reason: "commits landed in repos that the session edited during its window" };
10885
+ }
10886
+ if (opts.filesChanged === 0) {
10887
+ return { status: "unknown", reason: "no file edits and no commits in window" };
10888
+ }
10889
+ const lastAssistant = [...opts.turns].reverse().find((t) => t.kind === "assistant_text" && t.text);
10890
+ if (lastAssistant && lastAssistant.text && /\?\s*$/.test(lastAssistant.text.trim())) {
10891
+ return { status: "abandoned", reason: "last assistant turn ended on a question and nothing landed" };
10892
+ }
10893
+ return { status: "abandoned", reason: "edits made but no commits \u2014 work stayed local" };
10894
+ }
10895
+ function computeOutcome(sessionId, opts = {}) {
10896
+ const backend = getBackendForId(sessionId);
10897
+ const notFound = {
10898
+ sessionId,
10899
+ found: false,
10900
+ status: "unknown",
10901
+ reason: "session not found",
10902
+ startMs: 0,
10903
+ endMs: 0,
10904
+ decisions: [],
10905
+ blockers: [],
10906
+ claimReaction: {},
10907
+ prompts: [],
10908
+ promptMarkers: { total: 0, interrupt: 0, frustrated: 0, correction: 0, approval: 0, question: 0, directive: 0, clarification_request: 0, peakIntensity: 0 },
10909
+ commits: { sessionId, startMs: 0, endMs: 0, repos: [], totalCommits: 0 },
10910
+ fileCount: 0,
10911
+ filesChanged: [],
10912
+ totalLinesAdded: 0,
10913
+ totalLinesRemoved: 0
10914
+ };
10915
+ if (!backend)
10916
+ return notFound;
10917
+ const turnsRes = extractTurnsAny(sessionId, { assistantMax: 2e3 });
10918
+ if (!turnsRes.found)
10919
+ return notFound;
10920
+ const decisions = [];
10921
+ const blockers = [];
10922
+ for (const t of turnsRes.turns) {
10923
+ if (t.kind === "assistant_text")
10924
+ decisions.push(...extractDecisionsFromTurn(t));
10925
+ if (t.kind === "tool_result" && t.resultIsError) {
10926
+ blockers.push({
10927
+ kind: "tool_error",
10928
+ text: t.resultSummary?.slice(0, 240) || "tool error",
10929
+ ts: t.ts,
9637
10930
  tsIso: t.tsIso,
9638
10931
  line: t.line
9639
10932
  });
@@ -9710,7 +11003,7 @@ var init_session_outcome = __esm({
9710
11003
  });
9711
11004
 
9712
11005
  // ../engine/dist/core/first-prompt.js
9713
- import { createReadStream, existsSync as existsSync10, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
11006
+ import { createReadStream, existsSync as existsSync11, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
9714
11007
  import { StringDecoder } from "string_decoder";
9715
11008
  function stripBanners(s) {
9716
11009
  let out = s;
@@ -9764,7 +11057,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
9764
11057
  const maxBytes = options.maxBytes ?? 4 * 1024 * 1024;
9765
11058
  let fd;
9766
11059
  try {
9767
- fd = openSync3(filePath, "r");
11060
+ fd = openSync4(filePath, "r");
9768
11061
  } catch {
9769
11062
  return "";
9770
11063
  }
@@ -9776,7 +11069,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
9776
11069
  let pos = 0;
9777
11070
  let lineNo = 0;
9778
11071
  for (; ; ) {
9779
- const n = readSync3(fd, buf, 0, CHUNK, pos);
11072
+ const n = readSync4(fd, buf, 0, CHUNK, pos);
9780
11073
  if (n <= 0)
9781
11074
  break;
9782
11075
  pos += n;
@@ -9802,7 +11095,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
9802
11095
  return "";
9803
11096
  } finally {
9804
11097
  try {
9805
- closeSync3(fd);
11098
+ closeSync4(fd);
9806
11099
  } catch {
9807
11100
  }
9808
11101
  }
@@ -9821,7 +11114,7 @@ var init_first_prompt = __esm({
9821
11114
  });
9822
11115
 
9823
11116
  // ../engine/dist/core/project-dir-name.js
9824
- import { readdirSync as readdirSync7 } from "node:fs";
11117
+ import { readdirSync as readdirSync8 } from "node:fs";
9825
11118
  function looksWindowsEncoded(dirName) {
9826
11119
  return /^[A-Za-z]--/.test(dirName);
9827
11120
  }
@@ -9841,7 +11134,7 @@ function resolveProjectDirName(dirName, deps = {}) {
9841
11134
  return structural;
9842
11135
  if (looksWindowsEncoded(dirName))
9843
11136
  return structural;
9844
- const readdir = deps.readdir ?? ((p) => readdirSync7(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name));
11137
+ const readdir = deps.readdir ?? ((p) => readdirSync8(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name));
9845
11138
  const encodeName = (name) => name.replace(/[-_.]/g, "-");
9846
11139
  let remaining = dirName.replace(/^-/, "");
9847
11140
  let current = "/";
@@ -9879,8 +11172,8 @@ var init_project_dir_name = __esm({
9879
11172
  });
9880
11173
 
9881
11174
  // ../engine/dist/core/backends/claude.js
9882
- import { existsSync as existsSync11, readdirSync as readdirSync8, readFileSync as readFileSync7, statSync as statSync9, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
9883
- import { join as join11, basename as basename6 } from "path";
11175
+ import { existsSync as existsSync12, readdirSync as readdirSync9, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
11176
+ import { join as join12, basename as basename7 } from "path";
9884
11177
  function stripReminders(text) {
9885
11178
  return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
9886
11179
  }
@@ -9912,44 +11205,44 @@ var init_claude2 = __esm({
9912
11205
  }
9913
11206
  // ── Subpath helpers used by the parser/source layer ─────────────
9914
11207
  projectsDir() {
9915
- return join11(this.homeDir(), "projects");
11208
+ return join12(this.homeDir(), "projects");
9916
11209
  }
9917
11210
  plansDir() {
9918
- return join11(this.homeDir(), "plans");
11211
+ return join12(this.homeDir(), "plans");
9919
11212
  }
9920
11213
  todosDir() {
9921
- return join11(this.homeDir(), "todos");
11214
+ return join12(this.homeDir(), "todos");
9922
11215
  }
9923
11216
  /** Legacy task-list directory still used by older Claude Code releases. */
9924
11217
  tasksDir() {
9925
- return join11(this.homeDir(), "tasks");
11218
+ return join12(this.homeDir(), "tasks");
9926
11219
  }
9927
11220
  pasteCacheDir() {
9928
- return join11(this.homeDir(), "paste-cache");
11221
+ return join12(this.homeDir(), "paste-cache");
9929
11222
  }
9930
11223
  skillsDir() {
9931
- return join11(this.homeDir(), "skills");
11224
+ return join12(this.homeDir(), "skills");
9932
11225
  }
9933
11226
  agentsDir() {
9934
- return join11(this.homeDir(), "agents");
11227
+ return join12(this.homeDir(), "agents");
9935
11228
  }
9936
11229
  commandsDir() {
9937
- return join11(this.homeDir(), "commands");
11230
+ return join12(this.homeDir(), "commands");
9938
11231
  }
9939
11232
  pluginsManifestPath() {
9940
- return join11(this.homeDir(), "plugins", "installed_plugins.json");
11233
+ return join12(this.homeDir(), "plugins", "installed_plugins.json");
9941
11234
  }
9942
11235
  hooksFile() {
9943
- return join11(this.homeDir(), "hooks.json");
11236
+ return join12(this.homeDir(), "hooks.json");
9944
11237
  }
9945
11238
  settingsFile() {
9946
- return join11(this.homeDir(), "settings.json");
11239
+ return join12(this.homeDir(), "settings.json");
9947
11240
  }
9948
11241
  historyFile() {
9949
- return join11(this.homeDir(), "history.jsonl");
11242
+ return join12(this.homeDir(), "history.jsonl");
9950
11243
  }
9951
11244
  isAvailable() {
9952
- return existsSync11(this.projectsDir());
11245
+ return existsSync12(this.projectsDir());
9953
11246
  }
9954
11247
  // ── ID handling ────────────────────────────────────────────────
9955
11248
  /**
@@ -9977,13 +11270,13 @@ var init_claude2 = __esm({
9977
11270
  return null;
9978
11271
  let head;
9979
11272
  try {
9980
- const fd = openSync4(located.path, "r");
11273
+ const fd = openSync5(located.path, "r");
9981
11274
  try {
9982
11275
  const buf = Buffer.alloc(65536);
9983
- const n = readSync4(fd, buf, 0, buf.length, 0);
11276
+ const n = readSync5(fd, buf, 0, buf.length, 0);
9984
11277
  head = buf.toString("utf-8", 0, n);
9985
11278
  } finally {
9986
- closeSync4(fd);
11279
+ closeSync5(fd);
9987
11280
  }
9988
11281
  } catch {
9989
11282
  return null;
@@ -10010,7 +11303,7 @@ var init_claude2 = __esm({
10010
11303
  let mtime = 0;
10011
11304
  for (const c of copies) {
10012
11305
  try {
10013
- mtime = Math.max(mtime, statSync9(c.path).mtimeMs);
11306
+ mtime = Math.max(mtime, statSync11(c.path).mtimeMs);
10014
11307
  } catch {
10015
11308
  }
10016
11309
  }
@@ -10055,23 +11348,23 @@ var init_claude2 = __esm({
10055
11348
  prior.modified = ref.modified;
10056
11349
  }
10057
11350
  };
10058
- if (!existsSync11(root))
11351
+ if (!existsSync12(root))
10059
11352
  return;
10060
- for (const proj of readdirSync8(root, { withFileTypes: true })) {
11353
+ for (const proj of readdirSync9(root, { withFileTypes: true })) {
10061
11354
  if (!proj.isDirectory())
10062
11355
  continue;
10063
11356
  if (filter && !proj.name.toLowerCase().includes(filter))
10064
11357
  continue;
10065
- const projPath = join11(root, proj.name);
11358
+ const projPath = join12(root, proj.name);
10066
11359
  const projectPath = resolveProjectDirName(proj.name);
10067
- const indexPath = join11(projPath, "sessions-index.json");
11360
+ const indexPath = join12(projPath, "sessions-index.json");
10068
11361
  const indexed = /* @__PURE__ */ new Set();
10069
- if (existsSync11(indexPath)) {
11362
+ if (existsSync12(indexPath)) {
10070
11363
  try {
10071
- const indexData = JSON.parse(readFileSync7(indexPath, "utf-8"));
11364
+ const indexData = JSON.parse(readFileSync9(indexPath, "utf-8"));
10072
11365
  const entries = indexData.entries || [];
10073
11366
  for (const entry of entries) {
10074
- if (!entry.fullPath || !existsSync11(entry.fullPath))
11367
+ if (!entry.fullPath || !existsSync12(entry.fullPath))
10075
11368
  continue;
10076
11369
  indexed.add(entry.sessionId);
10077
11370
  add({
@@ -10093,20 +11386,20 @@ var init_claude2 = __esm({
10093
11386
  }
10094
11387
  let files;
10095
11388
  try {
10096
- files = readdirSync8(projPath);
11389
+ files = readdirSync9(projPath);
10097
11390
  } catch {
10098
11391
  continue;
10099
11392
  }
10100
11393
  for (const f of files) {
10101
11394
  if (!f.endsWith(".jsonl") || f === "sessions-index.json")
10102
11395
  continue;
10103
- const sessionId = basename6(f, ".jsonl");
11396
+ const sessionId = basename7(f, ".jsonl");
10104
11397
  if (indexed.has(sessionId))
10105
11398
  continue;
10106
- const fullPath = join11(projPath, f);
11399
+ const fullPath = join12(projPath, f);
10107
11400
  let stat;
10108
11401
  try {
10109
- stat = statSync9(fullPath);
11402
+ stat = statSync11(fullPath);
10110
11403
  } catch {
10111
11404
  continue;
10112
11405
  }
@@ -10360,7 +11653,7 @@ var init_claude2 = __esm({
10360
11653
  const events = this.readEvents(rawId);
10361
11654
  let fileMtime = 0;
10362
11655
  try {
10363
- fileMtime = statSync9(located.path).mtimeMs;
11656
+ fileMtime = statSync11(located.path).mtimeMs;
10364
11657
  } catch {
10365
11658
  }
10366
11659
  return liveScanEditsFromEvents(events, this.fileToolMap, {
@@ -10396,32 +11689,32 @@ var init_claude2 = __esm({
10396
11689
  collectRecentEdits(opts) {
10397
11690
  const candidates = [];
10398
11691
  for (const root of claudeProjectDirs()) {
10399
- if (!existsSync11(root))
11692
+ if (!existsSync12(root))
10400
11693
  continue;
10401
- for (const projEntry of readdirSync8(root, { withFileTypes: true })) {
11694
+ for (const projEntry of readdirSync9(root, { withFileTypes: true })) {
10402
11695
  if (!projEntry.isDirectory())
10403
11696
  continue;
10404
11697
  if (opts.projectFilter && !projEntry.name.toLowerCase().includes(opts.projectFilter.toLowerCase()))
10405
11698
  continue;
10406
- const projPath = join11(root, projEntry.name);
11699
+ const projPath = join12(root, projEntry.name);
10407
11700
  let files;
10408
11701
  try {
10409
- files = readdirSync8(projPath);
11702
+ files = readdirSync9(projPath);
10410
11703
  } catch {
10411
11704
  continue;
10412
11705
  }
10413
11706
  for (const f of files) {
10414
11707
  if (!f.endsWith(".jsonl") || f === "sessions-index.json")
10415
11708
  continue;
10416
- const fp = join11(projPath, f);
11709
+ const fp = join12(projPath, f);
10417
11710
  try {
10418
- const st = statSync9(fp);
11711
+ const st = statSync11(fp);
10419
11712
  let mtime = st.mtimeMs;
10420
- const subDir = join11(projPath, f.slice(0, -6), "subagents");
10421
- if (existsSync11(subDir)) {
10422
- for (const sub of readdirSync8(subDir)) {
11713
+ const subDir = join12(projPath, f.slice(0, -6), "subagents");
11714
+ if (existsSync12(subDir)) {
11715
+ for (const sub of readdirSync9(subDir)) {
10423
11716
  try {
10424
- const subSt = statSync9(join11(subDir, sub));
11717
+ const subSt = statSync11(join12(subDir, sub));
10425
11718
  if (subSt.mtimeMs > mtime)
10426
11719
  mtime = subSt.mtimeMs;
10427
11720
  } catch {
@@ -10430,7 +11723,7 @@ var init_claude2 = __esm({
10430
11723
  }
10431
11724
  if (mtime < opts.sinceMs)
10432
11725
  continue;
10433
- candidates.push({ rawId: basename6(f, ".jsonl"), mtime });
11726
+ candidates.push({ rawId: basename7(f, ".jsonl"), mtime });
10434
11727
  } catch {
10435
11728
  }
10436
11729
  }
@@ -10486,7 +11779,7 @@ var init_claude2 = __esm({
10486
11779
  let total = 0;
10487
11780
  for (const located of findSessionFiles(this.toRawId(prefixedId))) {
10488
11781
  try {
10489
- total += statSync9(located.path).size;
11782
+ total += statSync11(located.path).size;
10490
11783
  } catch {
10491
11784
  }
10492
11785
  }
@@ -10495,27 +11788,45 @@ var init_claude2 = __esm({
10495
11788
  /**
10496
11789
  * True when this session exists in MORE THAN ONE home.
10497
11790
  *
10498
- * Such a session cannot be tail-appended: `fileSize()` is the SUM across
10499
- * copies while a byte offset addresses ONE file, so the two disagree by
10500
- * construction and the "tail" is either empty or the wrong bytes. The caller
10501
- * uses this to force a FULL sync, which ships the unioned container and is
10502
- * correct regardless of how the halves are arranged.
10503
- *
10504
11791
  * Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
10505
- * zero overlap. An offset-based append against a 5846662-byte "size" that no
10506
- * single file has could only ever ship nonsense.
11792
+ * zero overlap. `fileSize()` is the sum across the copies, and
11793
+ * `readFromOffset()` reads them joined end to end, primary first.
10507
11794
  */
10508
11795
  spansMultipleSources(prefixedId) {
10509
11796
  return findSessionFiles(this.toRawId(prefixedId)).length > 1;
10510
11797
  }
11798
+ /**
11799
+ * The copies before the last one, each with its size. Claude Code writes to
11800
+ * the home of the profile that runs the session, so the live copy can grow,
11801
+ * appear, vanish or merge back into the primary. Each of those moves bytes
11802
+ * under an offset into the joined copies; this key changes with all of them.
11803
+ */
11804
+ sourceLayout(prefixedId) {
11805
+ const copies = findSessionFiles(this.toRawId(prefixedId));
11806
+ if (copies.length < 2)
11807
+ return "";
11808
+ return copies.slice(0, -1).map((c) => {
11809
+ let size = -1;
11810
+ try {
11811
+ size = statSync11(c.path).size;
11812
+ } catch {
11813
+ }
11814
+ return `${c.path}:${size}`;
11815
+ }).join("|");
11816
+ }
11817
+ /**
11818
+ * Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
11819
+ * the 24 MB build ceiling a sync ships the transcript in chunks read here,
11820
+ * and this returned no text for a session in two homes. Each sync shipped
11821
+ * nothing, and the server stayed three hours behind.
11822
+ */
10511
11823
  async readFromOffset(prefixedId, offset, maxBytes) {
10512
- const rawId = this.toRawId(prefixedId);
10513
- const copies = findSessionFiles(rawId);
11824
+ const copies = findSessionFiles(this.toRawId(prefixedId));
10514
11825
  if (copies.length === 0)
10515
11826
  return { text: "", newOffset: offset };
10516
- if (copies.length > 1)
10517
- return { text: "", newOffset: offset };
10518
- return readTailFromOffset(copies[0].path, offset, maxBytes);
11827
+ if (copies.length === 1)
11828
+ return readTailFromOffset(copies[0].path, offset, maxBytes);
11829
+ return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
10519
11830
  }
10520
11831
  };
10521
11832
  claudeBackend = new ClaudeBackend();
@@ -10530,11 +11841,11 @@ var init_model_pricing = __esm({
10530
11841
  });
10531
11842
 
10532
11843
  // ../engine/dist/parsers/session.js
10533
- import { createReadStream as createReadStream2, existsSync as existsSync12, readdirSync as readdirSync9, readFileSync as readFileSync8, statSync as statSync10, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
10534
- import { join as join12, basename as basename7 } from "path";
11844
+ import { createReadStream as createReadStream2, existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync10, statSync as statSync12, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
11845
+ import { join as join13, basename as basename8 } from "path";
10535
11846
  function hasSubagentsDir(sessionPath) {
10536
11847
  const dir = sessionPath.slice(0, -6);
10537
- return existsSync12(join12(dir, "subagents"));
11848
+ return existsSync13(join13(dir, "subagents"));
10538
11849
  }
10539
11850
  var init_session = __esm({
10540
11851
  "../engine/dist/parsers/session.js"() {
@@ -10549,9 +11860,9 @@ var init_session = __esm({
10549
11860
  });
10550
11861
 
10551
11862
  // ../engine/dist/transcript/shadow.js
10552
- import { createHash } from "crypto";
11863
+ import { createHash as createHash2 } from "crypto";
10553
11864
  function sha1(s) {
10554
- return createHash("sha1").update(s).digest("hex");
11865
+ return createHash2("sha1").update(s).digest("hex");
10555
11866
  }
10556
11867
  function lineKey(line) {
10557
11868
  try {
@@ -10633,11 +11944,11 @@ __export(live_session_scan_exports, {
10633
11944
  withSessionReadCache: () => withSessionReadCache,
10634
11945
  withSessionScanScope: () => withSessionScanScope
10635
11946
  });
10636
- import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
10637
- import { join as join13, basename as basename8 } from "path";
10638
- import { createHash as createHash2 } from "crypto";
11947
+ import { existsSync as existsSync14, readdirSync as readdirSync11, readFileSync as readFileSync11, statSync as statSync13, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
11948
+ import { join as join14, basename as basename9 } from "path";
11949
+ import { createHash as createHash3 } from "crypto";
10639
11950
  function lazyGeminiProjectsJson() {
10640
- return join13(geminiHomeDir(), "projects.json");
11951
+ return join14(geminiHomeDir(), "projects.json");
10641
11952
  }
10642
11953
  function detectTool(sessionId) {
10643
11954
  return getBackendForId(sessionId)?.id ?? "claude";
@@ -10694,24 +12005,24 @@ function sessionFileIndex() {
10694
12005
  return p;
10695
12006
  };
10696
12007
  for (const root of roots) {
10697
- if (seenRoots.has(root) || !existsSync13(root))
12008
+ if (seenRoots.has(root) || !existsSync14(root))
10698
12009
  continue;
10699
12010
  seenRoots.add(root);
10700
12011
  dirs.set(root, dirMtime(root));
10701
12012
  let entries;
10702
12013
  try {
10703
- entries = readdirSync10(root, { withFileTypes: true });
12014
+ entries = readdirSync11(root, { withFileTypes: true });
10704
12015
  } catch {
10705
12016
  continue;
10706
12017
  }
10707
12018
  for (const entry of entries) {
10708
12019
  if (!entry.isDirectory())
10709
12020
  continue;
10710
- const dir = join13(root, entry.name);
12021
+ const dir = join14(root, entry.name);
10711
12022
  dirs.set(dir, dirMtime(dir));
10712
12023
  let names;
10713
12024
  try {
10714
- names = readdirSync10(dir);
12025
+ names = readdirSync11(dir);
10715
12026
  } catch {
10716
12027
  continue;
10717
12028
  }
@@ -10720,7 +12031,7 @@ function sessionFileIndex() {
10720
12031
  continue;
10721
12032
  const id = name.slice(0, -".jsonl".length);
10722
12033
  const located = {
10723
- path: join13(dir, name),
12034
+ path: join14(dir, name),
10724
12035
  projectDir: entry.name,
10725
12036
  projectPath: projectPathOf(entry.name)
10726
12037
  };
@@ -10752,7 +12063,7 @@ function resolveSessionContentGroups(sessionId) {
10752
12063
  };
10753
12064
  for (const located of findSessionFiles(sessionId)) {
10754
12065
  for (const p of resolveSessionContentPaths(located.path)) {
10755
- add(p === located.path ? "main" : `subagents/${basename8(p)}`, p);
12066
+ add(p === located.path ? "main" : `subagents/${basename9(p)}`, p);
10756
12067
  }
10757
12068
  }
10758
12069
  const names = [...groups.keys()].sort((a, b) => a === "main" ? -1 : b === "main" ? 1 : a.localeCompare(b));
@@ -10786,7 +12097,7 @@ function readSessionGroupText(group) {
10786
12097
  const parts = [];
10787
12098
  for (const p of group.paths) {
10788
12099
  try {
10789
- const st = statSync11(p);
12100
+ const st = statSync13(p);
10790
12101
  parts.push(`${p}:${st.mtimeMs}:${st.size}`);
10791
12102
  } catch {
10792
12103
  parts.push(`${p}:missing`);
@@ -10801,12 +12112,12 @@ function readSessionGroupText(group) {
10801
12112
  for (const p of group.paths) {
10802
12113
  let raw;
10803
12114
  try {
10804
- raw = readFileSync9(p, "utf-8");
12115
+ raw = readFileSync11(p, "utf-8");
10805
12116
  } catch {
10806
12117
  continue;
10807
12118
  }
10808
12119
  try {
10809
- mtime = Math.max(mtime, statSync11(p).mtimeMs);
12120
+ mtime = Math.max(mtime, statSync13(p).mtimeMs);
10810
12121
  } catch {
10811
12122
  }
10812
12123
  text = text ? mergeLineText(text, raw).text : raw;
@@ -10819,10 +12130,10 @@ function readSessionGroupText(group) {
10819
12130
  function resolveSessionContentPaths(sessionFile) {
10820
12131
  if (!hasSubagentsDir(sessionFile))
10821
12132
  return [sessionFile];
10822
- const subDir = join13(sessionFile.slice(0, -6), "subagents");
10823
- if (!existsSync13(subDir))
12133
+ const subDir = join14(sessionFile.slice(0, -6), "subagents");
12134
+ if (!existsSync14(subDir))
10824
12135
  return [sessionFile];
10825
- const subPaths = readdirSync10(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join13(subDir, f));
12136
+ const subPaths = readdirSync11(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join14(subDir, f));
10826
12137
  return [sessionFile, ...subPaths];
10827
12138
  }
10828
12139
  function liveScanSessionEdits(sessionId) {
@@ -10837,11 +12148,11 @@ function loadGeminiProjectMap() {
10837
12148
  return geminiProjectMapCache;
10838
12149
  const map = /* @__PURE__ */ new Map();
10839
12150
  const projectsPath = lazyGeminiProjectsJson();
10840
- if (existsSync13(projectsPath)) {
12151
+ if (existsSync14(projectsPath)) {
10841
12152
  try {
10842
- const data = JSON.parse(readFileSync9(projectsPath, "utf-8"));
12153
+ const data = JSON.parse(readFileSync11(projectsPath, "utf-8"));
10843
12154
  for (const path of Object.keys(data.projects || {})) {
10844
- map.set(createHash2("sha256").update(path).digest("hex"), path);
12155
+ map.set(createHash3("sha256").update(path).digest("hex"), path);
10845
12156
  }
10846
12157
  } catch {
10847
12158
  }
@@ -10873,23 +12184,23 @@ function geminiIndex() {
10873
12184
  const projMap = loadGeminiProjectMap();
10874
12185
  const isGeminiChat = (f) => f.startsWith("session-") && (f.endsWith(".json") || f.endsWith(".jsonl"));
10875
12186
  for (const tmpRoot of geminiTmpDirs()) {
10876
- if (!existsSync13(tmpRoot))
12187
+ if (!existsSync14(tmpRoot))
10877
12188
  continue;
10878
12189
  let projs;
10879
12190
  try {
10880
- projs = readdirSync10(tmpRoot, { withFileTypes: true });
12191
+ projs = readdirSync11(tmpRoot, { withFileTypes: true });
10881
12192
  } catch {
10882
12193
  continue;
10883
12194
  }
10884
12195
  for (const proj of projs) {
10885
12196
  if (!proj.isDirectory())
10886
12197
  continue;
10887
- const chats = join13(tmpRoot, proj.name, "chats");
10888
- if (!existsSync13(chats))
12198
+ const chats = join14(tmpRoot, proj.name, "chats");
12199
+ if (!existsSync14(chats))
10889
12200
  continue;
10890
12201
  let files;
10891
12202
  try {
10892
- files = readdirSync10(chats);
12203
+ files = readdirSync11(chats);
10893
12204
  } catch {
10894
12205
  continue;
10895
12206
  }
@@ -10898,7 +12209,7 @@ function geminiIndex() {
10898
12209
  continue;
10899
12210
  const base = f.replace(/\.jsonl?$/, "");
10900
12211
  const located = {
10901
- path: join13(chats, f),
12212
+ path: join14(chats, f),
10902
12213
  projectDir: proj.name,
10903
12214
  projectPath: projMap.get(proj.name) || "",
10904
12215
  format: f.endsWith(".jsonl") ? "jsonl" : "json"
@@ -10933,19 +12244,19 @@ function geminiInnerIdIndex(idx) {
10933
12244
  return byInnerId;
10934
12245
  }
10935
12246
  function readHeadSync(path, bytes = 65536) {
10936
- const fd = openSync6(path, "r");
12247
+ const fd = openSync7(path, "r");
10937
12248
  try {
10938
12249
  const buf = Buffer.alloc(bytes);
10939
- const n = readSync6(fd, buf, 0, buf.length, 0);
12250
+ const n = readSync7(fd, buf, 0, buf.length, 0);
10940
12251
  return buf.toString("utf-8", 0, n);
10941
12252
  } finally {
10942
- closeSync6(fd);
12253
+ closeSync7(fd);
10943
12254
  }
10944
12255
  }
10945
12256
  function readGeminiSessionIdFromFile(path, format) {
10946
12257
  if (format === "json") {
10947
12258
  try {
10948
- return JSON.parse(readFileSync9(path, "utf-8"))?.sessionId ?? null;
12259
+ return JSON.parse(readFileSync11(path, "utf-8"))?.sessionId ?? null;
10949
12260
  } catch {
10950
12261
  return null;
10951
12262
  }
@@ -10960,7 +12271,7 @@ function readGeminiSessionIdFromFile(path, format) {
10960
12271
  function readGeminiMessages(path, format) {
10961
12272
  if (format === "json") {
10962
12273
  try {
10963
- const json = JSON.parse(readFileSync9(path, "utf-8"));
12274
+ const json = JSON.parse(readFileSync11(path, "utf-8"));
10964
12275
  return Array.isArray(json.messages) ? json.messages : [];
10965
12276
  } catch {
10966
12277
  return [];
@@ -10969,7 +12280,7 @@ function readGeminiMessages(path, format) {
10969
12280
  const out = [];
10970
12281
  let raw;
10971
12282
  try {
10972
- raw = readFileSync9(path, "utf-8");
12283
+ raw = readFileSync11(path, "utf-8");
10973
12284
  } catch {
10974
12285
  return out;
10975
12286
  }
@@ -11006,23 +12317,23 @@ function findCodexSessionFile(sessionId) {
11006
12317
  return null;
11007
12318
  }
11008
12319
  function findCodexSessionFileIn(root, sessionId) {
11009
- if (!existsSync13(root))
12320
+ if (!existsSync14(root))
11010
12321
  return null;
11011
- for (const year of readdirSync10(root, { withFileTypes: true })) {
12322
+ for (const year of readdirSync11(root, { withFileTypes: true })) {
11012
12323
  if (!year.isDirectory())
11013
12324
  continue;
11014
- const yearPath = join13(root, year.name);
11015
- for (const month of readdirSync10(yearPath, { withFileTypes: true })) {
12325
+ const yearPath = join14(root, year.name);
12326
+ for (const month of readdirSync11(yearPath, { withFileTypes: true })) {
11016
12327
  if (!month.isDirectory())
11017
12328
  continue;
11018
- const monthPath = join13(yearPath, month.name);
11019
- for (const day of readdirSync10(monthPath, { withFileTypes: true })) {
12329
+ const monthPath = join14(yearPath, month.name);
12330
+ for (const day of readdirSync11(monthPath, { withFileTypes: true })) {
11020
12331
  if (!day.isDirectory())
11021
12332
  continue;
11022
- const dayPath = join13(monthPath, day.name);
12333
+ const dayPath = join14(monthPath, day.name);
11023
12334
  let files;
11024
12335
  try {
11025
- files = readdirSync10(dayPath);
12336
+ files = readdirSync11(dayPath);
11026
12337
  } catch {
11027
12338
  continue;
11028
12339
  }
@@ -11030,22 +12341,22 @@ function findCodexSessionFileIn(root, sessionId) {
11030
12341
  if (!f.endsWith(".jsonl"))
11031
12342
  continue;
11032
12343
  if (f.includes(sessionId)) {
11033
- return { path: join13(dayPath, f), projectPath: "" };
12344
+ return { path: join14(dayPath, f), projectPath: "" };
11034
12345
  }
11035
12346
  }
11036
12347
  }
11037
12348
  }
11038
12349
  }
11039
12350
  function scanDir(dir) {
11040
- for (const entry of readdirSync10(dir, { withFileTypes: true })) {
12351
+ for (const entry of readdirSync11(dir, { withFileTypes: true })) {
11041
12352
  if (entry.isDirectory()) {
11042
- const found = scanDir(join13(dir, entry.name));
12353
+ const found = scanDir(join14(dir, entry.name));
11043
12354
  if (found)
11044
12355
  return found;
11045
12356
  } else if (entry.name.endsWith(".jsonl")) {
11046
- const p = join13(dir, entry.name);
12357
+ const p = join14(dir, entry.name);
11047
12358
  try {
11048
- const first = readFileSync9(p, "utf-8").split("\n")[0];
12359
+ const first = readFileSync11(p, "utf-8").split("\n")[0];
11049
12360
  if (!first)
11050
12361
  continue;
11051
12362
  const meta = JSON.parse(first);
@@ -11121,7 +12432,7 @@ var init_live_session_scan = __esm({
11121
12432
  INDEX_RECHECK_MS = 1e3;
11122
12433
  dirMtime = (p) => {
11123
12434
  try {
11124
- return statSync11(p).mtimeMs;
12435
+ return statSync13(p).mtimeMs;
11125
12436
  } catch {
11126
12437
  return -1;
11127
12438
  }