chat-recall 0.7.17 → 0.7.19

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.
package/dist/mcp.js CHANGED
@@ -2034,7 +2034,7 @@ var require_thread_stream = __commonJS({
2034
2034
  var { version: version2 } = require_package();
2035
2035
  var { EventEmitter } = __require("events");
2036
2036
  var { Worker: Worker2 } = __require("worker_threads");
2037
- var { join: join53 } = __require("path");
2037
+ var { join: join54 } = __require("path");
2038
2038
  var { pathToFileURL } = __require("url");
2039
2039
  var { wait } = require_wait();
2040
2040
  var {
@@ -2077,7 +2077,7 @@ var require_thread_stream = __commonJS({
2077
2077
  function createWorker(stream, opts) {
2078
2078
  const { filename, workerData } = opts;
2079
2079
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
2080
- const toExecute = bundlerOverrides["thread-stream-worker"] || join53(__dirname, "lib", "worker.js");
2080
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join54(__dirname, "lib", "worker.js");
2081
2081
  const worker = new Worker2(toExecute, {
2082
2082
  ...opts.workerOpts,
2083
2083
  trackUnmanagedFds: false,
@@ -2480,7 +2480,7 @@ var require_transport = __commonJS({
2480
2480
  "use strict";
2481
2481
  var { createRequire: createRequire3 } = __require("module");
2482
2482
  var getCallers = require_caller();
2483
- var { join: join53, isAbsolute: isAbsolute3, sep: sep4 } = __require("node:path");
2483
+ var { join: join54, isAbsolute: isAbsolute3, sep: sep4 } = __require("node:path");
2484
2484
  var sleep = require_atomic_sleep();
2485
2485
  var onExit = require_on_exit_leak_free();
2486
2486
  var ThreadStream = require_thread_stream();
@@ -2543,7 +2543,7 @@ var require_transport = __commonJS({
2543
2543
  throw new Error("only one of target or targets can be specified");
2544
2544
  }
2545
2545
  if (targets) {
2546
- target = bundlerOverrides["pino-worker"] || join53(__dirname, "worker.js");
2546
+ target = bundlerOverrides["pino-worker"] || join54(__dirname, "worker.js");
2547
2547
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
2548
2548
  return {
2549
2549
  ...dest,
@@ -2561,7 +2561,7 @@ var require_transport = __commonJS({
2561
2561
  });
2562
2562
  });
2563
2563
  } else if (pipeline) {
2564
- target = bundlerOverrides["pino-worker"] || join53(__dirname, "worker.js");
2564
+ target = bundlerOverrides["pino-worker"] || join54(__dirname, "worker.js");
2565
2565
  options.pipelines = [pipeline.map((dest) => {
2566
2566
  return {
2567
2567
  ...dest,
@@ -2583,7 +2583,7 @@ var require_transport = __commonJS({
2583
2583
  return origin;
2584
2584
  }
2585
2585
  if (origin === "pino/file") {
2586
- return join53(__dirname, "..", "file.js");
2586
+ return join54(__dirname, "..", "file.js");
2587
2587
  }
2588
2588
  let fixTarget2;
2589
2589
  for (const filePath of callers) {
@@ -3572,7 +3572,7 @@ var require_safe_stable_stringify = __commonJS({
3572
3572
  return circularValue;
3573
3573
  }
3574
3574
  let res = "";
3575
- let join53 = ",";
3575
+ let join54 = ",";
3576
3576
  const originalIndentation = indentation;
3577
3577
  if (Array.isArray(value)) {
3578
3578
  if (value.length === 0) {
@@ -3586,7 +3586,7 @@ var require_safe_stable_stringify = __commonJS({
3586
3586
  indentation += spacer;
3587
3587
  res += `
3588
3588
  ${indentation}`;
3589
- join53 = `,
3589
+ join54 = `,
3590
3590
  ${indentation}`;
3591
3591
  }
3592
3592
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3594,13 +3594,13 @@ ${indentation}`;
3594
3594
  for (; i < maximumValuesToStringify - 1; i++) {
3595
3595
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3596
3596
  res += tmp2 !== void 0 ? tmp2 : "null";
3597
- res += join53;
3597
+ res += join54;
3598
3598
  }
3599
3599
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3600
3600
  res += tmp !== void 0 ? tmp : "null";
3601
3601
  if (value.length - 1 > maximumBreadth) {
3602
3602
  const removedKeys = value.length - maximumBreadth - 1;
3603
- res += `${join53}"... ${getItemCount(removedKeys)} not stringified"`;
3603
+ res += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3604
3604
  }
3605
3605
  if (spacer !== "") {
3606
3606
  res += `
@@ -3621,7 +3621,7 @@ ${originalIndentation}`;
3621
3621
  let separator = "";
3622
3622
  if (spacer !== "") {
3623
3623
  indentation += spacer;
3624
- join53 = `,
3624
+ join54 = `,
3625
3625
  ${indentation}`;
3626
3626
  whitespace = " ";
3627
3627
  }
@@ -3635,13 +3635,13 @@ ${indentation}`;
3635
3635
  const tmp = stringifyFnReplacer(key3, value, stack, replacer, spacer, indentation);
3636
3636
  if (tmp !== void 0) {
3637
3637
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3638
- separator = join53;
3638
+ separator = join54;
3639
3639
  }
3640
3640
  }
3641
3641
  if (keyLength > maximumBreadth) {
3642
3642
  const removedKeys = keyLength - maximumBreadth;
3643
3643
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
3644
- separator = join53;
3644
+ separator = join54;
3645
3645
  }
3646
3646
  if (spacer !== "" && separator.length > 1) {
3647
3647
  res = `
@@ -3682,7 +3682,7 @@ ${originalIndentation}`;
3682
3682
  }
3683
3683
  const originalIndentation = indentation;
3684
3684
  let res = "";
3685
- let join53 = ",";
3685
+ let join54 = ",";
3686
3686
  if (Array.isArray(value)) {
3687
3687
  if (value.length === 0) {
3688
3688
  return "[]";
@@ -3695,7 +3695,7 @@ ${originalIndentation}`;
3695
3695
  indentation += spacer;
3696
3696
  res += `
3697
3697
  ${indentation}`;
3698
- join53 = `,
3698
+ join54 = `,
3699
3699
  ${indentation}`;
3700
3700
  }
3701
3701
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3703,13 +3703,13 @@ ${indentation}`;
3703
3703
  for (; i < maximumValuesToStringify - 1; i++) {
3704
3704
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3705
3705
  res += tmp2 !== void 0 ? tmp2 : "null";
3706
- res += join53;
3706
+ res += join54;
3707
3707
  }
3708
3708
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3709
3709
  res += tmp !== void 0 ? tmp : "null";
3710
3710
  if (value.length - 1 > maximumBreadth) {
3711
3711
  const removedKeys = value.length - maximumBreadth - 1;
3712
- res += `${join53}"... ${getItemCount(removedKeys)} not stringified"`;
3712
+ res += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3713
3713
  }
3714
3714
  if (spacer !== "") {
3715
3715
  res += `
@@ -3722,7 +3722,7 @@ ${originalIndentation}`;
3722
3722
  let whitespace = "";
3723
3723
  if (spacer !== "") {
3724
3724
  indentation += spacer;
3725
- join53 = `,
3725
+ join54 = `,
3726
3726
  ${indentation}`;
3727
3727
  whitespace = " ";
3728
3728
  }
@@ -3731,7 +3731,7 @@ ${indentation}`;
3731
3731
  const tmp = stringifyArrayReplacer(key3, value[key3], stack, replacer, spacer, indentation);
3732
3732
  if (tmp !== void 0) {
3733
3733
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3734
- separator = join53;
3734
+ separator = join54;
3735
3735
  }
3736
3736
  }
3737
3737
  if (spacer !== "" && separator.length > 1) {
@@ -3789,20 +3789,20 @@ ${originalIndentation}`;
3789
3789
  indentation += spacer;
3790
3790
  let res2 = `
3791
3791
  ${indentation}`;
3792
- const join54 = `,
3792
+ const join55 = `,
3793
3793
  ${indentation}`;
3794
3794
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
3795
3795
  let i = 0;
3796
3796
  for (; i < maximumValuesToStringify - 1; i++) {
3797
3797
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3798
3798
  res2 += tmp2 !== void 0 ? tmp2 : "null";
3799
- res2 += join54;
3799
+ res2 += join55;
3800
3800
  }
3801
3801
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3802
3802
  res2 += tmp !== void 0 ? tmp : "null";
3803
3803
  if (value.length - 1 > maximumBreadth) {
3804
3804
  const removedKeys = value.length - maximumBreadth - 1;
3805
- res2 += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3805
+ res2 += `${join55}"... ${getItemCount(removedKeys)} not stringified"`;
3806
3806
  }
3807
3807
  res2 += `
3808
3808
  ${originalIndentation}`;
@@ -3818,16 +3818,16 @@ ${originalIndentation}`;
3818
3818
  return '"[Object]"';
3819
3819
  }
3820
3820
  indentation += spacer;
3821
- const join53 = `,
3821
+ const join54 = `,
3822
3822
  ${indentation}`;
3823
3823
  let res = "";
3824
3824
  let separator = "";
3825
3825
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
3826
3826
  if (isTypedArrayWithEntries(value)) {
3827
- res += stringifyTypedArray(value, join53, maximumBreadth);
3827
+ res += stringifyTypedArray(value, join54, maximumBreadth);
3828
3828
  keys = keys.slice(value.length);
3829
3829
  maximumPropertiesToStringify -= value.length;
3830
- separator = join53;
3830
+ separator = join54;
3831
3831
  }
3832
3832
  if (deterministic) {
3833
3833
  keys = sort(keys, comparator);
@@ -3838,13 +3838,13 @@ ${indentation}`;
3838
3838
  const tmp = stringifyIndent(key3, value[key3], stack, spacer, indentation);
3839
3839
  if (tmp !== void 0) {
3840
3840
  res += `${separator}${strEscape(key3)}: ${tmp}`;
3841
- separator = join53;
3841
+ separator = join54;
3842
3842
  }
3843
3843
  }
3844
3844
  if (keyLength > maximumBreadth) {
3845
3845
  const removedKeys = keyLength - maximumBreadth;
3846
3846
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
3847
- separator = join53;
3847
+ separator = join54;
3848
3848
  }
3849
3849
  if (separator !== "") {
3850
3850
  res = `
@@ -5023,6 +5023,8 @@ function declaredOrigin(value) {
5023
5023
  function userRecordOrigin(obj, text) {
5024
5024
  if (obj.isSidechain === true)
5025
5025
  return "subagent";
5026
+ if (obj.isCompactSummary === true)
5027
+ return "compact-summary";
5026
5028
  const declared = declaredOrigin(obj.origin);
5027
5029
  if (declared && declared !== "human")
5028
5030
  return declared;
@@ -5206,6 +5208,7 @@ function defaultSourcesEnabled() {
5206
5208
  commands: true
5207
5209
  },
5208
5210
  cursor: { sessions: true, skills: true, agents: true, commands: true },
5211
+ hermes: { sessions: true },
5209
5212
  shared: { skills: true },
5210
5213
  common: { mcps: true, agentMd: true }
5211
5214
  };
@@ -5282,6 +5285,7 @@ function mergeSources(base2, partial) {
5282
5285
  codex: { ...base2.enabled.codex, ...partial.enabled?.codex ?? {} },
5283
5286
  agy: { ...base2.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
5284
5287
  cursor: { ...base2.enabled.cursor, ...partial.enabled?.cursor ?? {} },
5288
+ hermes: { ...base2.enabled.hermes, ...partial.enabled?.hermes ?? {} },
5285
5289
  shared: { ...base2.enabled.shared, ...partial.enabled?.shared ?? {} },
5286
5290
  common: { ...base2.enabled.common, ...partial.enabled?.common ?? {} }
5287
5291
  };
@@ -5293,6 +5297,7 @@ function mergeSources(base2, partial) {
5293
5297
  agyHome: partial.agyHome ?? base2.agyHome,
5294
5298
  cursorHome: partial.cursorHome ?? base2.cursorHome,
5295
5299
  cursorIdeHome: partial.cursorIdeHome ?? base2.cursorIdeHome,
5300
+ hermesHome: partial.hermesHome ?? base2.hermesHome,
5296
5301
  extraClaudeHomes: partial.extraClaudeHomes ?? base2.extraClaudeHomes,
5297
5302
  approvedHomes: partial.approvedHomes ?? base2.approvedHomes,
5298
5303
  declinedHomes: partial.declinedHomes ?? base2.declinedHomes,
@@ -5689,6 +5694,13 @@ function grandfatherLegacyHomes() {
5689
5694
  }
5690
5695
  return { seeded };
5691
5696
  }
5697
+ function defaultHermesHome() {
5698
+ if (process.platform === "win32") {
5699
+ const local = process.env.LOCALAPPDATA?.trim() || join6(homedir2(), "AppData", "Local");
5700
+ return join6(local, "hermes");
5701
+ }
5702
+ return join6(homedir2(), ".hermes");
5703
+ }
5692
5704
  function primaryHomes() {
5693
5705
  const home = homedir2();
5694
5706
  const env = process.env;
@@ -5705,7 +5717,8 @@ function primaryHomes() {
5705
5717
  env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join6(home, ".codex"),
5706
5718
  env.CHAT_RECALL_AGY_HOME || s?.agyHome || join6(home, ".gemini", "antigravity-cli"),
5707
5719
  env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join6(home, ".cursor"),
5708
- dirOf(opencodeDb)
5720
+ dirOf(opencodeDb),
5721
+ env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome()
5709
5722
  ];
5710
5723
  return candidates2.map(normalizeHomePath);
5711
5724
  }
@@ -5815,6 +5828,9 @@ function geminiHomeDir() {
5815
5828
  function opencodeDbPath() {
5816
5829
  return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join7(homedir3(), ".local", "share", "opencode", "opencode.db");
5817
5830
  }
5831
+ function hermesHomeDir() {
5832
+ return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
5833
+ }
5818
5834
  function codexHomeDir() {
5819
5835
  return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join7(homedir3(), ".codex");
5820
5836
  }
@@ -5916,6 +5932,22 @@ function opencodeDbPaths(opts = {}) {
5916
5932
  dbs.push(primary);
5917
5933
  return applyExclusions(dbs, opts.includeExcluded);
5918
5934
  }
5935
+ function hermesDbPaths(opts = {}) {
5936
+ const root = hermesHomeDir();
5937
+ const dbs = [join7(root, "state.db")];
5938
+ try {
5939
+ const profiles = join7(root, "profiles");
5940
+ for (const entry of readdirSync5(profiles, { withFileTypes: true })) {
5941
+ if (!entry.isDirectory())
5942
+ continue;
5943
+ const p = join7(profiles, entry.name, "state.db");
5944
+ if (existsSync5(p))
5945
+ dbs.push(p);
5946
+ }
5947
+ } catch {
5948
+ }
5949
+ return applyExclusions(dbs, opts.includeExcluded);
5950
+ }
5919
5951
  function extraClaudeHomeDirs() {
5920
5952
  const extras = sources().extraClaudeHomes ?? [];
5921
5953
  const primary = claudeHomeDir();
@@ -7212,6 +7244,71 @@ function readTailFromOffset(path2, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7212
7244
  }
7213
7245
  }
7214
7246
  }
7247
+ function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7248
+ let start = 0;
7249
+ let linesBefore = 0;
7250
+ for (let i = 0; i < paths.length; i++) {
7251
+ const path2 = paths[i];
7252
+ let size = 0;
7253
+ try {
7254
+ size = statSync6(path2).size;
7255
+ } catch {
7256
+ size = 0;
7257
+ }
7258
+ const last = i === paths.length - 1;
7259
+ if (!last && offset >= start + size) {
7260
+ const lines = linesBeforeOffset(path2, size);
7261
+ if (lines === void 0)
7262
+ return { text: "", newOffset: offset };
7263
+ linesBefore += lines + (size > 0 && !endsWithNewline(path2, size) ? 1 : 0);
7264
+ start += size;
7265
+ continue;
7266
+ }
7267
+ const local = offset - start;
7268
+ const read = readTailFromOffset(path2, local, maxBytes);
7269
+ const shift = (r) => ({
7270
+ ...r,
7271
+ newOffset: start + r.newOffset,
7272
+ ...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
7273
+ });
7274
+ if (last || read.newOffset > local || local >= size)
7275
+ return shift(read);
7276
+ if (size - local > maxBytes)
7277
+ return { text: "", newOffset: start + size, skippedBytes: size - local };
7278
+ let fd = null;
7279
+ try {
7280
+ fd = openSync(path2, "r");
7281
+ const text = readAt(fd, local, size - local).toString("utf-8");
7282
+ return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path2, local) });
7283
+ } catch {
7284
+ return { text: "", newOffset: offset };
7285
+ } finally {
7286
+ if (fd !== null) {
7287
+ try {
7288
+ closeSync(fd);
7289
+ } catch {
7290
+ }
7291
+ }
7292
+ }
7293
+ }
7294
+ return { text: "", newOffset: offset };
7295
+ }
7296
+ function endsWithNewline(path2, size) {
7297
+ let fd = null;
7298
+ try {
7299
+ fd = openSync(path2, "r");
7300
+ return readAt(fd, size - 1, 1)[0] === 10;
7301
+ } catch {
7302
+ return true;
7303
+ } finally {
7304
+ if (fd !== null) {
7305
+ try {
7306
+ closeSync(fd);
7307
+ } catch {
7308
+ }
7309
+ }
7310
+ }
7311
+ }
7215
7312
  function findNewline(fd, from, size) {
7216
7313
  const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
7217
7314
  let pos = from;
@@ -9690,6 +9787,473 @@ var init_cursor = __esm({
9690
9787
  }
9691
9788
  });
9692
9789
 
9790
+ // ../engine/dist/core/backends/hermes.js
9791
+ import { existsSync as existsSync12 } from "fs";
9792
+ function firstUserPrompt(db, sessionId) {
9793
+ try {
9794
+ const row = db.prepare(`
9795
+ SELECT substr(content, 1, 400) AS text FROM messages
9796
+ WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
9797
+ AND (display_kind IS NULL OR display_kind = 'steer')
9798
+ AND length(trim(content)) > 0
9799
+ ORDER BY id LIMIT 1
9800
+ `).get(sessionId);
9801
+ return row?.text ? flatString(row.text.slice(0, 200)) : "";
9802
+ } catch {
9803
+ return "";
9804
+ }
9805
+ }
9806
+ function messageRowsToEvents(rows) {
9807
+ const events = [];
9808
+ let line = 0;
9809
+ for (const r of rows) {
9810
+ line++;
9811
+ if (r.active === 0)
9812
+ continue;
9813
+ if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
9814
+ continue;
9815
+ const ts = Math.round((r.timestamp || 0) * 1e3);
9816
+ const tsIso = ts ? new Date(ts).toISOString() : void 0;
9817
+ const text = (r.content ?? "").trim();
9818
+ if (r._compressed_summary === 1) {
9819
+ if (text)
9820
+ events.push({ kind: "summary", ts, tsIso, line, text });
9821
+ continue;
9822
+ }
9823
+ if (r.role === "user") {
9824
+ if (!text)
9825
+ continue;
9826
+ events.push({
9827
+ kind: "user",
9828
+ ts,
9829
+ tsIso,
9830
+ line,
9831
+ text,
9832
+ ...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
9833
+ });
9834
+ continue;
9835
+ }
9836
+ if (r.role === "assistant") {
9837
+ if (text)
9838
+ events.push({ kind: "assistant_text", ts, tsIso, line, text });
9839
+ for (const call2 of parseToolCalls(r.tool_calls)) {
9840
+ const toolName = call2.function?.name ?? "";
9841
+ const toolInput = parseArguments(call2.function?.arguments);
9842
+ const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
9843
+ events.push({
9844
+ kind: "tool_use",
9845
+ ts,
9846
+ tsIso,
9847
+ line,
9848
+ toolName,
9849
+ toolUseId: call2.id || call2.call_id || `${r.id}:${toolName}`,
9850
+ toolInput,
9851
+ ...command ? { command } : {}
9852
+ });
9853
+ }
9854
+ continue;
9855
+ }
9856
+ if (r.role === "tool") {
9857
+ const body2 = r.content ?? "";
9858
+ const { exitCode, isError } = toolResultStatus(body2);
9859
+ events.push({
9860
+ kind: "tool_result",
9861
+ ts,
9862
+ tsIso,
9863
+ line,
9864
+ toolUseId: r.tool_call_id ?? void 0,
9865
+ toolName: r.tool_name ?? void 0,
9866
+ resultBody: body2,
9867
+ resultIsError: isError,
9868
+ ...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
9869
+ resultBytes: Buffer.byteLength(body2)
9870
+ });
9871
+ }
9872
+ }
9873
+ return events;
9874
+ }
9875
+ function parseToolCalls(json) {
9876
+ if (!json)
9877
+ return [];
9878
+ try {
9879
+ const calls = JSON.parse(json);
9880
+ return Array.isArray(calls) ? calls : [];
9881
+ } catch {
9882
+ return [];
9883
+ }
9884
+ }
9885
+ function parseArguments(args) {
9886
+ if (typeof args !== "string")
9887
+ return args ?? {};
9888
+ try {
9889
+ return JSON.parse(args);
9890
+ } catch {
9891
+ return { raw: args };
9892
+ }
9893
+ }
9894
+ function toolResultStatus(body2) {
9895
+ if (!body2.startsWith("{"))
9896
+ return { isError: false };
9897
+ try {
9898
+ const r = JSON.parse(body2);
9899
+ const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
9900
+ const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
9901
+ return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
9902
+ } catch {
9903
+ return { isError: false };
9904
+ }
9905
+ }
9906
+ var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
9907
+ var init_hermes = __esm({
9908
+ "../engine/dist/core/backends/hermes.js"() {
9909
+ "use strict";
9910
+ init_sqlite_reader();
9911
+ init_tool_paths();
9912
+ init_session_outcome();
9913
+ init_session_git();
9914
+ init_generic_engine();
9915
+ init_flat_string();
9916
+ PREFIX5 = "hermes_";
9917
+ MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
9918
+ ARCHIVED_SESSION_COLUMNS = [
9919
+ "id",
9920
+ "source",
9921
+ "title",
9922
+ "model",
9923
+ "cwd",
9924
+ "git_branch",
9925
+ "git_repo_root",
9926
+ "parent_session_id",
9927
+ "started_at",
9928
+ "ended_at",
9929
+ "end_reason",
9930
+ "message_count",
9931
+ "tool_call_count",
9932
+ "input_tokens",
9933
+ "output_tokens",
9934
+ "cache_read_tokens",
9935
+ "cache_write_tokens",
9936
+ "reasoning_tokens",
9937
+ "estimated_cost_usd",
9938
+ "actual_cost_usd"
9939
+ ];
9940
+ SESSION_MTIME_SQL = `MAX(
9941
+ s.started_at,
9942
+ COALESCE(s.ended_at, 0),
9943
+ COALESCE(s.last_activity_at, 0),
9944
+ COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
9945
+ )`;
9946
+ HermesBackend = class {
9947
+ id = "hermes";
9948
+ idPrefix = PREFIX5;
9949
+ displayName = "Hermes";
9950
+ homeDir() {
9951
+ return hermesHomeDir();
9952
+ }
9953
+ /** Every Hermes database on this machine, the root profile first. */
9954
+ dbPaths() {
9955
+ return hermesDbPaths();
9956
+ }
9957
+ isAvailable() {
9958
+ return this.dbPaths().some((p) => existsSync12(p));
9959
+ }
9960
+ /** The database that holds this session. A session id is unique within a
9961
+ * database, and each named profile has its own. */
9962
+ dbPathFor(rawId) {
9963
+ const cached = this._dbForSession.get(rawId);
9964
+ if (cached)
9965
+ return cached;
9966
+ for (const path2 of this.dbPaths()) {
9967
+ const db = openSqliteReadonly(path2);
9968
+ if (!db)
9969
+ continue;
9970
+ try {
9971
+ if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
9972
+ this._dbForSession.set(rawId, path2);
9973
+ return path2;
9974
+ }
9975
+ } catch {
9976
+ } finally {
9977
+ db.close();
9978
+ }
9979
+ }
9980
+ return null;
9981
+ }
9982
+ _dbForSession = /* @__PURE__ */ new Map();
9983
+ /** Tests, and anything that adds a profile mid-process. */
9984
+ _clearDbRouting() {
9985
+ this._dbForSession.clear();
9986
+ }
9987
+ // ── ID handling ────────────────────────────────────────────────
9988
+ matchesId(id) {
9989
+ return id.startsWith(PREFIX5);
9990
+ }
9991
+ toRawId(id) {
9992
+ return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
9993
+ }
9994
+ toPrefixedId(rawId) {
9995
+ return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
9996
+ }
9997
+ // ── Location ───────────────────────────────────────────────────
9998
+ findSession(id) {
9999
+ const rawId = this.toRawId(id);
10000
+ const path2 = this.dbPathFor(rawId);
10001
+ if (!path2)
10002
+ return null;
10003
+ const db = openSqliteReadonly(path2);
10004
+ if (!db)
10005
+ return null;
10006
+ try {
10007
+ const row = db.prepare(`
10008
+ SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
10009
+ FROM sessions s WHERE s.id = ?
10010
+ `).get(rawId);
10011
+ if (!row)
10012
+ return null;
10013
+ return {
10014
+ path: path2,
10015
+ format: "sqlite",
10016
+ projectDir: "",
10017
+ projectPath: row.git_repo_root || row.cwd || "",
10018
+ mtime: Math.round((row.mtime || 0) * 1e3)
10019
+ };
10020
+ } catch {
10021
+ return null;
10022
+ } finally {
10023
+ db.close();
10024
+ }
10025
+ }
10026
+ listSessions(opts = {}) {
10027
+ const byId = /* @__PURE__ */ new Map();
10028
+ for (const dbPath of this.dbPaths()) {
10029
+ if (!existsSync12(dbPath))
10030
+ continue;
10031
+ for (const ref of this.listSessionsInDb(dbPath, opts)) {
10032
+ const prior = byId.get(ref.rawId);
10033
+ if (!prior || ref.mtime > prior.mtime)
10034
+ byId.set(ref.rawId, ref);
10035
+ }
10036
+ }
10037
+ const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
10038
+ return opts.limit ? all.slice(0, opts.limit) : all;
10039
+ }
10040
+ listSessionsInDb(dbPath, opts) {
10041
+ const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
10042
+ const filter = opts.projectFilter?.toLowerCase();
10043
+ const db = openSqliteReadonly(dbPath);
10044
+ if (!db)
10045
+ return [];
10046
+ try {
10047
+ const rows = db.prepare(`
10048
+ SELECT * FROM (
10049
+ SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
10050
+ ${SESSION_MTIME_SQL} AS mtime
10051
+ FROM sessions s
10052
+ ) WHERE mtime >= ? ORDER BY mtime DESC
10053
+ `).all(cutoffSec);
10054
+ const out = [];
10055
+ for (const row of rows) {
10056
+ const projectPath = row.git_repo_root || row.cwd || "";
10057
+ if (filter && !projectPath.toLowerCase().includes(filter))
10058
+ continue;
10059
+ const mtime = Math.round(row.mtime * 1e3);
10060
+ this._dbForSession.set(row.id, dbPath);
10061
+ out.push({
10062
+ toolId: "hermes",
10063
+ rawId: row.id,
10064
+ prefixedId: this.toPrefixedId(row.id),
10065
+ projectPath,
10066
+ projectDir: "",
10067
+ fullPath: dbPath,
10068
+ created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
10069
+ modified: new Date(mtime).toISOString(),
10070
+ mtime,
10071
+ firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
10072
+ messageCount: opts.previews === false ? 0 : row.message_count ?? 0
10073
+ });
10074
+ if (opts.limit && out.length >= opts.limit)
10075
+ break;
10076
+ }
10077
+ return out;
10078
+ } catch {
10079
+ return [];
10080
+ } finally {
10081
+ db.close();
10082
+ }
10083
+ }
10084
+ /** Hermes writes a title for each session (a model-written one, or the
10085
+ * first prompt for a one-shot run). */
10086
+ getNativeTitle(rawId) {
10087
+ const path2 = this.dbPathFor(this.toRawId(rawId));
10088
+ if (!path2)
10089
+ return null;
10090
+ const db = openSqliteReadonly(path2);
10091
+ if (!db)
10092
+ return null;
10093
+ try {
10094
+ const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
10095
+ const t = row?.title?.trim();
10096
+ return t ? t.slice(0, 200) : null;
10097
+ } catch {
10098
+ return null;
10099
+ } finally {
10100
+ db.close();
10101
+ }
10102
+ }
10103
+ // ── Generic-engine inputs ───────────────────────────────────────
10104
+ fileToolMap = {
10105
+ write_file: "write",
10106
+ patch: "edit",
10107
+ read_file: "read"
10108
+ };
10109
+ /**
10110
+ * `write_file` carries `{ path, content }`. `patch` carries
10111
+ * `{ mode: 'replace', path, old_string, new_string }`; its other mode,
10112
+ * `patch`, is a multi-file V4A patch with no single before and after.
10113
+ */
10114
+ extractEditDelta(toolName, input) {
10115
+ if (input == null || typeof input !== "object")
10116
+ return null;
10117
+ const inp = input;
10118
+ if (toolName === "write_file") {
10119
+ return { before: "", after: typeof inp.content === "string" ? inp.content : null };
10120
+ }
10121
+ if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
10122
+ const before = typeof inp.old_string === "string" ? inp.old_string : null;
10123
+ const after = typeof inp.new_string === "string" ? inp.new_string : null;
10124
+ if (before === null && after === null)
10125
+ return null;
10126
+ return { before, after };
10127
+ }
10128
+ return null;
10129
+ }
10130
+ readEvents(rawId) {
10131
+ const id = this.toRawId(rawId);
10132
+ const path2 = this.dbPathFor(id);
10133
+ if (!path2)
10134
+ return [];
10135
+ const db = openSqliteReadonly(path2);
10136
+ if (!db)
10137
+ return [];
10138
+ try {
10139
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
10140
+ return messageRowsToEvents(rows);
10141
+ } catch {
10142
+ return [];
10143
+ } finally {
10144
+ db.close();
10145
+ }
10146
+ }
10147
+ /**
10148
+ * Events from the dump `exportRawSession` writes, for a session rebuilt from
10149
+ * the server's archive. The archive joins the lines of every dump it got, so
10150
+ * a row that changed (a rewind sets `active` to 0) is there more than once.
10151
+ * The last copy of each row id is the newest.
10152
+ */
10153
+ readEventsFromText(text) {
10154
+ const rows = /* @__PURE__ */ new Map();
10155
+ for (const line of text.split("\n")) {
10156
+ if (!line.trim())
10157
+ continue;
10158
+ try {
10159
+ const rec = JSON.parse(line);
10160
+ if (rec.kind === "message" && rec.row) {
10161
+ rows.delete(rec.row.id);
10162
+ rows.set(rec.row.id, rec.row);
10163
+ }
10164
+ } catch {
10165
+ }
10166
+ }
10167
+ return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
10168
+ }
10169
+ // ── Per-session operations — all delegate to the generic engine ─
10170
+ extractTurns(id, opts = {}) {
10171
+ return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
10172
+ }
10173
+ liveScanEdits(id) {
10174
+ const located = this.findSession(id);
10175
+ if (!located) {
10176
+ return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
10177
+ }
10178
+ return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
10179
+ sessionId: this.toPrefixedId(id),
10180
+ tool: "hermes",
10181
+ projectPath: located.projectPath,
10182
+ projectDir: located.projectDir,
10183
+ fileMtime: located.mtime,
10184
+ found: true
10185
+ });
10186
+ }
10187
+ replay(id) {
10188
+ const located = this.findSession(id);
10189
+ if (!located) {
10190
+ return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
10191
+ }
10192
+ return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
10193
+ projectPath: located.projectPath,
10194
+ found: true
10195
+ });
10196
+ }
10197
+ computeOutcome(id, opts) {
10198
+ return computeOutcome(this.toPrefixedId(id), opts);
10199
+ }
10200
+ /** File edits since `sinceMs` across the sessions active since then. */
10201
+ collectRecentEdits(opts) {
10202
+ const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
10203
+ const edits = [];
10204
+ for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
10205
+ for (const e of this.liveScanEdits(ref.rawId).edits) {
10206
+ if (e.ts >= opts.sinceMs)
10207
+ edits.push(e);
10208
+ }
10209
+ }
10210
+ return edits.sort((a, b) => b.ts - a.ts);
10211
+ }
10212
+ getCommits(id, files, startMs, endMs, bufferMinutes) {
10213
+ return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
10214
+ }
10215
+ /**
10216
+ * The session row and its messages as JSONL, one row per line in id order.
10217
+ * Hermes rewrites and compacts its database, so the archive keeps the rows
10218
+ * as they were synced.
10219
+ */
10220
+ exportRawSession(id) {
10221
+ const rawId = this.toRawId(id);
10222
+ const path2 = this.dbPathFor(rawId);
10223
+ if (!path2)
10224
+ return null;
10225
+ const db = openSqliteReadonly(path2);
10226
+ if (!db)
10227
+ return null;
10228
+ try {
10229
+ const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
10230
+ const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
10231
+ const session = db.prepare(`
10232
+ SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
10233
+ FROM sessions s WHERE s.id = ?
10234
+ `).get(rawId);
10235
+ if (!session)
10236
+ return null;
10237
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
10238
+ const lines = [JSON.stringify({ kind: "session", row: session })];
10239
+ for (const r of rows)
10240
+ lines.push(JSON.stringify({ kind: "message", row: r }));
10241
+ return {
10242
+ tool: "hermes",
10243
+ mtime: Math.round(Number(session.mtime || 0) * 1e3),
10244
+ files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
10245
+ };
10246
+ } catch {
10247
+ return null;
10248
+ } finally {
10249
+ db.close();
10250
+ }
10251
+ }
10252
+ };
10253
+ hermesBackend = new HermesBackend();
10254
+ }
10255
+ });
10256
+
9693
10257
  // ../engine/dist/core/backends/index.js
9694
10258
  function bootstrapBackends() {
9695
10259
  registerBackend(claudeBackend);
@@ -9697,6 +10261,7 @@ function bootstrapBackends() {
9697
10261
  registerBackend(codexBackend);
9698
10262
  registerBackend(agyBackend);
9699
10263
  registerBackend(cursorBackend);
10264
+ registerBackend(hermesBackend);
9700
10265
  }
9701
10266
  var init_backends = __esm({
9702
10267
  "../engine/dist/core/backends/index.js"() {
@@ -9707,6 +10272,7 @@ var init_backends = __esm({
9707
10272
  init_codex();
9708
10273
  init_agy();
9709
10274
  init_cursor();
10275
+ init_hermes();
9710
10276
  init_tool_backend();
9711
10277
  _setRegistryBootstrapper(bootstrapBackends);
9712
10278
  }
@@ -9919,7 +10485,7 @@ var init_session_outcome = __esm({
9919
10485
  });
9920
10486
 
9921
10487
  // ../engine/dist/core/first-prompt.js
9922
- import { createReadStream, existsSync as existsSync12, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
10488
+ import { createReadStream, existsSync as existsSync13, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
9923
10489
  import { StringDecoder } from "string_decoder";
9924
10490
  function stripBanners(s) {
9925
10491
  let out = s;
@@ -10088,7 +10654,7 @@ var init_project_dir_name = __esm({
10088
10654
  });
10089
10655
 
10090
10656
  // ../engine/dist/core/backends/claude.js
10091
- import { existsSync as existsSync13, readdirSync as readdirSync11, readFileSync as readFileSync9, statSync as statSync12, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
10657
+ import { existsSync as existsSync14, readdirSync as readdirSync11, readFileSync as readFileSync9, statSync as statSync12, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
10092
10658
  import { join as join14, basename as basename8 } from "path";
10093
10659
  function stripReminders(text) {
10094
10660
  return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
@@ -10158,7 +10724,7 @@ var init_claude = __esm({
10158
10724
  return join14(this.homeDir(), "history.jsonl");
10159
10725
  }
10160
10726
  isAvailable() {
10161
- return existsSync13(this.projectsDir());
10727
+ return existsSync14(this.projectsDir());
10162
10728
  }
10163
10729
  // ── ID handling ────────────────────────────────────────────────
10164
10730
  /**
@@ -10264,7 +10830,7 @@ var init_claude = __esm({
10264
10830
  prior.modified = ref.modified;
10265
10831
  }
10266
10832
  };
10267
- if (!existsSync13(root))
10833
+ if (!existsSync14(root))
10268
10834
  return;
10269
10835
  for (const proj of readdirSync11(root, { withFileTypes: true })) {
10270
10836
  if (!proj.isDirectory())
@@ -10275,12 +10841,12 @@ var init_claude = __esm({
10275
10841
  const projectPath = resolveProjectDirName(proj.name);
10276
10842
  const indexPath = join14(projPath, "sessions-index.json");
10277
10843
  const indexed = /* @__PURE__ */ new Set();
10278
- if (existsSync13(indexPath)) {
10844
+ if (existsSync14(indexPath)) {
10279
10845
  try {
10280
10846
  const indexData = JSON.parse(readFileSync9(indexPath, "utf-8"));
10281
10847
  const entries = indexData.entries || [];
10282
10848
  for (const entry of entries) {
10283
- if (!entry.fullPath || !existsSync13(entry.fullPath))
10849
+ if (!entry.fullPath || !existsSync14(entry.fullPath))
10284
10850
  continue;
10285
10851
  indexed.add(entry.sessionId);
10286
10852
  add({
@@ -10605,7 +11171,7 @@ var init_claude = __esm({
10605
11171
  collectRecentEdits(opts) {
10606
11172
  const candidates2 = [];
10607
11173
  for (const root of claudeProjectDirs()) {
10608
- if (!existsSync13(root))
11174
+ if (!existsSync14(root))
10609
11175
  continue;
10610
11176
  for (const projEntry of readdirSync11(root, { withFileTypes: true })) {
10611
11177
  if (!projEntry.isDirectory())
@@ -10627,7 +11193,7 @@ var init_claude = __esm({
10627
11193
  const st = statSync12(fp);
10628
11194
  let mtime = st.mtimeMs;
10629
11195
  const subDir = join14(projPath, f.slice(0, -6), "subagents");
10630
- if (existsSync13(subDir)) {
11196
+ if (existsSync14(subDir)) {
10631
11197
  for (const sub of readdirSync11(subDir)) {
10632
11198
  try {
10633
11199
  const subSt = statSync12(join14(subDir, sub));
@@ -10704,27 +11270,45 @@ var init_claude = __esm({
10704
11270
  /**
10705
11271
  * True when this session exists in MORE THAN ONE home.
10706
11272
  *
10707
- * Such a session cannot be tail-appended: `fileSize()` is the SUM across
10708
- * copies while a byte offset addresses ONE file, so the two disagree by
10709
- * construction and the "tail" is either empty or the wrong bytes. The caller
10710
- * uses this to force a FULL sync, which ships the unioned container and is
10711
- * correct regardless of how the halves are arranged.
10712
- *
10713
11273
  * Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
10714
- * zero overlap. An offset-based append against a 5846662-byte "size" that no
10715
- * single file has could only ever ship nonsense.
11274
+ * zero overlap. `fileSize()` is the sum across the copies, and
11275
+ * `readFromOffset()` reads them joined end to end, primary first.
10716
11276
  */
10717
11277
  spansMultipleSources(prefixedId) {
10718
11278
  return findSessionFiles(this.toRawId(prefixedId)).length > 1;
10719
11279
  }
11280
+ /**
11281
+ * The copies before the last one, each with its size. Claude Code writes to
11282
+ * the home of the profile that runs the session, so the live copy can grow,
11283
+ * appear, vanish or merge back into the primary. Each of those moves bytes
11284
+ * under an offset into the joined copies; this key changes with all of them.
11285
+ */
11286
+ sourceLayout(prefixedId) {
11287
+ const copies = findSessionFiles(this.toRawId(prefixedId));
11288
+ if (copies.length < 2)
11289
+ return "";
11290
+ return copies.slice(0, -1).map((c) => {
11291
+ let size = -1;
11292
+ try {
11293
+ size = statSync12(c.path).size;
11294
+ } catch {
11295
+ }
11296
+ return `${c.path}:${size}`;
11297
+ }).join("|");
11298
+ }
11299
+ /**
11300
+ * Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
11301
+ * the 24 MB build ceiling a sync ships the transcript in chunks read here,
11302
+ * and this returned no text for a session in two homes. Each sync shipped
11303
+ * nothing, and the server stayed three hours behind.
11304
+ */
10720
11305
  async readFromOffset(prefixedId, offset, maxBytes) {
10721
- const rawId = this.toRawId(prefixedId);
10722
- const copies = findSessionFiles(rawId);
11306
+ const copies = findSessionFiles(this.toRawId(prefixedId));
10723
11307
  if (copies.length === 0)
10724
11308
  return { text: "", newOffset: offset };
10725
- if (copies.length > 1)
10726
- return { text: "", newOffset: offset };
10727
- return readTailFromOffset(copies[0].path, offset, maxBytes);
11309
+ if (copies.length === 1)
11310
+ return readTailFromOffset(copies[0].path, offset, maxBytes);
11311
+ return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
10728
11312
  }
10729
11313
  };
10730
11314
  claudeBackend = new ClaudeBackend();
@@ -10902,7 +11486,7 @@ var init_model_pricing = __esm({
10902
11486
  });
10903
11487
 
10904
11488
  // ../engine/dist/parsers/session.js
10905
- import { createReadStream as createReadStream2, existsSync as existsSync14, readdirSync as readdirSync12, readFileSync as readFileSync10, statSync as statSync13, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
11489
+ import { createReadStream as createReadStream2, existsSync as existsSync15, readdirSync as readdirSync12, readFileSync as readFileSync10, statSync as statSync13, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
10906
11490
  import { createInterface } from "readline";
10907
11491
  import { join as join15, basename as basename9 } from "path";
10908
11492
  function removeCodeBlocks(text) {
@@ -10951,17 +11535,17 @@ function isValuableToolResult(content, toolName = "") {
10951
11535
  }
10952
11536
  function hasSubagentsDir(sessionPath) {
10953
11537
  const dir = sessionPath.slice(0, -6);
10954
- return existsSync14(join15(dir, "subagents"));
11538
+ return existsSync15(join15(dir, "subagents"));
10955
11539
  }
10956
11540
  function getSubagentPaths(sessionPath) {
10957
11541
  const subagentsDir = join15(sessionPath.slice(0, -6), "subagents");
10958
- if (!existsSync14(subagentsDir))
11542
+ if (!existsSync15(subagentsDir))
10959
11543
  return [];
10960
11544
  return readdirSync12(subagentsDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join15(subagentsDir, f));
10961
11545
  }
10962
11546
  function readCwdFromJsonl(sessionPath) {
10963
11547
  try {
10964
- if (!existsSync14(sessionPath))
11548
+ if (!existsSync15(sessionPath))
10965
11549
  return "";
10966
11550
  let text;
10967
11551
  const fd = openSync5(sessionPath, "r");
@@ -11018,7 +11602,7 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
11018
11602
  };
11019
11603
  const modelsUsed = /* @__PURE__ */ new Set();
11020
11604
  const filesModified = /* @__PURE__ */ new Set();
11021
- if (!existsSync14(sessionPath)) {
11605
+ if (!existsSync15(sessionPath)) {
11022
11606
  return content;
11023
11607
  }
11024
11608
  const filesToParse = hasSubagentsDir(sessionPath) ? [sessionPath, ...getSubagentPaths(sessionPath)] : [sessionPath];
@@ -11637,7 +12221,7 @@ function canonicalEventsToMessages(events) {
11637
12221
  switch (e.kind) {
11638
12222
  case "user":
11639
12223
  cur = null;
11640
- messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso });
12224
+ messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso, ...e.origin ? { origin: e.origin } : {} });
11641
12225
  break;
11642
12226
  case "assistant_text":
11643
12227
  cur = { line: e.line, role: "assistant", content: e.text ?? "", timestamp: e.tsIso };
@@ -11680,6 +12264,7 @@ var init_from_events = __esm({
11680
12264
  // ../engine/dist/transcript/raw.js
11681
12265
  var raw_exports = {};
11682
12266
  __export(raw_exports, {
12267
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
11683
12268
  archiveRawSession: () => archiveRawSession,
11684
12269
  buildRawContainer: () => buildRawContainer,
11685
12270
  containerSrcHash: () => containerSrcHash,
@@ -11979,11 +12564,11 @@ async function archiveRawSession(store2, sessionId) {
11979
12564
  return await store2.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
11980
12565
  }
11981
12566
  function detectForkPredecessor(sessionPath) {
11982
- const { readFileSync: readFileSync44, readdirSync: readdirSync34 } = requireFs();
11983
- const { dirname: dirname21, join: join53, basename: basename24 } = requirePath();
12567
+ const { readFileSync: readFileSync45, readdirSync: readdirSync34 } = requireFs();
12568
+ const { dirname: dirname21, join: join54, basename: basename24 } = requirePath();
11984
12569
  let text;
11985
12570
  try {
11986
- text = readFileSync44(sessionPath, "utf-8");
12571
+ text = readFileSync45(sessionPath, "utf-8");
11987
12572
  } catch {
11988
12573
  return null;
11989
12574
  }
@@ -12017,7 +12602,7 @@ function detectForkPredecessor(sessionPath) {
12017
12602
  if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
12018
12603
  continue;
12019
12604
  try {
12020
- if (readFileSync44(join53(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
12605
+ if (readFileSync45(join54(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
12021
12606
  return basename24(f, ".jsonl");
12022
12607
  }
12023
12608
  } catch {
@@ -12031,7 +12616,7 @@ function requireFs() {
12031
12616
  function requirePath() {
12032
12617
  return pathMod;
12033
12618
  }
12034
- var MARKER_QUOTE;
12619
+ var RAW_PARSE_MAX_BYTES, MARKER_QUOTE;
12035
12620
  var init_raw = __esm({
12036
12621
  "../engine/dist/transcript/raw.js"() {
12037
12622
  "use strict";
@@ -12039,6 +12624,7 @@ var init_raw = __esm({
12039
12624
  init_claude2();
12040
12625
  init_codex2();
12041
12626
  init_from_events();
12627
+ RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
12042
12628
  MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
12043
12629
  }
12044
12630
  });
@@ -12046,7 +12632,7 @@ var init_raw = __esm({
12046
12632
  // ../engine/dist/transcript/shadow.js
12047
12633
  import { gzipSync as gzipSync2, gunzipSync as gunzipSync2 } from "zlib";
12048
12634
  import { createHash as createHash4 } from "crypto";
12049
- import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
12635
+ import { existsSync as existsSync16, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
12050
12636
  import { dirname as dirname14, join as join18 } from "path";
12051
12637
  function shadowRoot() {
12052
12638
  return join18(getDataDir(), "shadow");
@@ -12057,7 +12643,7 @@ function shadowFileFor(tool, rawId) {
12057
12643
  }
12058
12644
  function readShadowContainer(tool, rawId) {
12059
12645
  const path2 = shadowFileFor(tool, rawId);
12060
- if (!existsSync15(path2))
12646
+ if (!existsSync16(path2))
12061
12647
  return null;
12062
12648
  try {
12063
12649
  const c = JSON.parse(gunzipSync2(readFileSync11(path2)).toString("utf-8"));
@@ -12259,7 +12845,7 @@ var init_shadow = __esm({
12259
12845
  });
12260
12846
 
12261
12847
  // ../engine/dist/core/live-session-scan.js
12262
- import { existsSync as existsSync16, readdirSync as readdirSync13, readFileSync as readFileSync12, statSync as statSync15, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
12848
+ import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync12, statSync as statSync15, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
12263
12849
  import { join as join19, basename as basename12 } from "path";
12264
12850
  function detectTool(sessionId) {
12265
12851
  return getBackendForId(sessionId)?.id ?? "claude";
@@ -12316,7 +12902,7 @@ function sessionFileIndex() {
12316
12902
  return p;
12317
12903
  };
12318
12904
  for (const root of roots) {
12319
- if (seenRoots.has(root) || !existsSync16(root))
12905
+ if (seenRoots.has(root) || !existsSync17(root))
12320
12906
  continue;
12321
12907
  seenRoots.add(root);
12322
12908
  dirs.set(root, dirMtime(root));
@@ -12442,7 +13028,7 @@ function resolveSessionContentPaths(sessionFile) {
12442
13028
  if (!hasSubagentsDir(sessionFile))
12443
13029
  return [sessionFile];
12444
13030
  const subDir = join19(sessionFile.slice(0, -6), "subagents");
12445
- if (!existsSync16(subDir))
13031
+ if (!existsSync17(subDir))
12446
13032
  return [sessionFile];
12447
13033
  const subPaths = readdirSync13(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join19(subDir, f));
12448
13034
  return [sessionFile, ...subPaths];
@@ -12459,7 +13045,7 @@ function findCodexSessionFile(sessionId) {
12459
13045
  return null;
12460
13046
  }
12461
13047
  function findCodexSessionFileIn(root, sessionId) {
12462
- if (!existsSync16(root))
13048
+ if (!existsSync17(root))
12463
13049
  return null;
12464
13050
  for (const year of readdirSync13(root, { withFileTypes: true })) {
12465
13051
  if (!year.isDirectory())
@@ -12552,10 +13138,10 @@ var init_types = __esm({
12552
13138
 
12553
13139
  // ../engine/dist/transcript/opencode.js
12554
13140
  async function parseOpenCodeSubagents(parentSessionId) {
12555
- const { existsSync: existsSync48 } = await import("fs");
13141
+ const { existsSync: existsSync49 } = await import("fs");
12556
13142
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
12557
13143
  const dbPath = opencodeBackend2.dbPath();
12558
- if (!existsSync48(dbPath))
13144
+ if (!existsSync49(dbPath))
12559
13145
  return [];
12560
13146
  const realParent = parentSessionId.replace("opencode_", "");
12561
13147
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -12596,10 +13182,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
12596
13182
  }
12597
13183
  }
12598
13184
  async function parseOpenCodeTranscript(sessionId) {
12599
- const { existsSync: existsSync48 } = await import("fs");
13185
+ const { existsSync: existsSync49 } = await import("fs");
12600
13186
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
12601
13187
  const dbPath = opencodeBackend2.dbPath();
12602
- if (!existsSync48(dbPath))
13188
+ if (!existsSync49(dbPath))
12603
13189
  return [];
12604
13190
  const realId = sessionId.replace("opencode_", "");
12605
13191
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -12873,7 +13459,7 @@ var init_credentials_env = __esm({
12873
13459
  });
12874
13460
 
12875
13461
  // ../engine/dist/core/which.js
12876
- import { existsSync as existsSync18, statSync as statSync17, accessSync as accessSync2, constants as constants2 } from "node:fs";
13462
+ import { existsSync as existsSync19, statSync as statSync17, accessSync as accessSync2, constants as constants2 } from "node:fs";
12877
13463
  import { join as join21, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep2 } from "node:path";
12878
13464
  function executable(p) {
12879
13465
  try {
@@ -12914,7 +13500,7 @@ function resolveOnPath(name) {
12914
13500
  continue;
12915
13501
  for (const c of candidates(name)) {
12916
13502
  const full = join21(dir, c);
12917
- if (existsSync18(full) && executable(full))
13503
+ if (existsSync19(full) && executable(full))
12918
13504
  return full;
12919
13505
  }
12920
13506
  }
@@ -13078,7 +13664,7 @@ var init_code_intel = __esm({
13078
13664
  });
13079
13665
 
13080
13666
  // ../engine/dist/core/companions.js
13081
- import { existsSync as existsSync19, mkdirSync as mkdirSync7, statSync as statSync18, chmodSync as chmodSync4, unlinkSync, renameSync as renameSync3, createWriteStream, readFileSync as readFileSync14, writeFileSync as writeFileSync4 } from "node:fs";
13667
+ import { existsSync as existsSync20, mkdirSync as mkdirSync7, statSync as statSync18, chmodSync as chmodSync4, unlinkSync, renameSync as renameSync3, createWriteStream, readFileSync as readFileSync14, writeFileSync as writeFileSync4 } from "node:fs";
13082
13668
  import { homedir as homedir5 } from "node:os";
13083
13669
  import { join as join23 } from "node:path";
13084
13670
  import { execSync as execSync2 } from "node:child_process";
@@ -13201,7 +13787,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
13201
13787
  renameSync3(tmpPath, CODEINDEX_BIN_PATH);
13202
13788
  } catch (err) {
13203
13789
  try {
13204
- if (existsSync19(tmpPath))
13790
+ if (existsSync20(tmpPath))
13205
13791
  unlinkSync(tmpPath);
13206
13792
  } catch {
13207
13793
  }
@@ -13638,7 +14224,7 @@ var init_secret_redactor = __esm({
13638
14224
 
13639
14225
  // ../engine/dist/core/detector-install.js
13640
14226
  import { createHash as createHash7 } from "crypto";
13641
- import { existsSync as existsSync20, mkdirSync as mkdirSync8, mkdtempSync, chmodSync as chmodSync5, renameSync as renameSync4, rmSync as rmSync2, writeFileSync as writeFileSync5, readFileSync as readFileSync15, statSync as statSync20, copyFileSync as copyFileSync2 } from "fs";
14227
+ import { existsSync as existsSync21, mkdirSync as mkdirSync8, mkdtempSync, chmodSync as chmodSync5, renameSync as renameSync4, rmSync as rmSync2, writeFileSync as writeFileSync5, readFileSync as readFileSync15, statSync as statSync20, copyFileSync as copyFileSync2 } from "fs";
13642
14228
  import { join as join24 } from "path";
13643
14229
  function managedDetectorPath(name) {
13644
14230
  return join24(getDetectorBinDir(), name + exeSuffix());
@@ -13678,7 +14264,7 @@ function resolveDetector(name) {
13678
14264
  const dir = configuredDir();
13679
14265
  if (dir) {
13680
14266
  const p2 = join24(dir, name + exeSuffix());
13681
- if (existsSync20(p2))
14267
+ if (existsSync21(p2))
13682
14268
  return { path: p2, version: "operator-supplied", source: "configured" };
13683
14269
  return { error: "not-installed" };
13684
14270
  }
@@ -13686,7 +14272,7 @@ function resolveDetector(name) {
13686
14272
  if (!spec.assets[platformKey()])
13687
14273
  return { error: "unsupported-platform" };
13688
14274
  const p = managedDetectorPath(name);
13689
- if (!existsSync20(p))
14275
+ if (!existsSync21(p))
13690
14276
  return { error: "not-installed" };
13691
14277
  const receipt = readReceipt(name);
13692
14278
  if (!receipt)
@@ -13742,7 +14328,7 @@ var init_detector_install = __esm({
13742
14328
 
13743
14329
  // ../engine/dist/core/secret-scanner.js
13744
14330
  import { spawnSync } from "child_process";
13745
- import { existsSync as existsSync21, unlinkSync as unlinkSync2, readFileSync as readFileSync16, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
14331
+ import { existsSync as existsSync22, unlinkSync as unlinkSync2, readFileSync as readFileSync16, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
13746
14332
  import { tmpdir as tmpdir2 } from "os";
13747
14333
  import { join as join25 } from "path";
13748
14334
  function mask(s) {
@@ -13908,7 +14494,7 @@ function runTrufflehogDir(binary, dir, opts = {}) {
13908
14494
  return out;
13909
14495
  }
13910
14496
  function scanDirForSecrets(dir, opts = {}) {
13911
- if (!existsSync21(dir))
14497
+ if (!existsSync22(dir))
13912
14498
  return [];
13913
14499
  const gitleaks = whichDetector("gitleaks");
13914
14500
  const trufflehog = whichDetector("trufflehog");
@@ -13945,7 +14531,7 @@ function scanTenantRules(text, tenantRules) {
13945
14531
  return out;
13946
14532
  }
13947
14533
  function scanFileForSecrets(filePath, opts = {}) {
13948
- if (!existsSync21(filePath))
14534
+ if (!existsSync22(filePath))
13949
14535
  return [];
13950
14536
  const gitleaks = whichDetector("gitleaks");
13951
14537
  const trufflehog = whichDetector("trufflehog");
@@ -15024,7 +15610,7 @@ var init_collector = __esm({
15024
15610
 
15025
15611
  // ../engine/dist/core/source-discovery.js
15026
15612
  import { createHash as createHash8 } from "crypto";
15027
- import { existsSync as existsSync23, readdirSync as readdirSync14, statSync as statSync22 } from "fs";
15613
+ import { existsSync as existsSync24, readdirSync as readdirSync14, statSync as statSync22 } from "fs";
15028
15614
  import { join as join28 } from "path";
15029
15615
  function sourceId(path2) {
15030
15616
  return "src_" + createHash8("sha256").update(path2).digest("hex").slice(0, 12);
@@ -15083,7 +15669,7 @@ function countTranscripts(root, tool) {
15083
15669
  walk(root, 0);
15084
15670
  return { sessions, newestMtime };
15085
15671
  }
15086
- function countOpencodeSessions(dbPath) {
15672
+ function countDbSessions(dbPath) {
15087
15673
  try {
15088
15674
  const st = statSync22(dbPath);
15089
15675
  return { sessions: 0, newestMtime: st.mtimeMs };
@@ -15101,18 +15687,24 @@ function discoverSessionSources() {
15101
15687
  ];
15102
15688
  for (const [tool, dirs] of dirTools) {
15103
15689
  for (const [i, path2] of dirs.entries()) {
15104
- if (!existsSync23(path2))
15690
+ if (!existsSync24(path2))
15105
15691
  continue;
15106
15692
  const { sessions, newestMtime } = countTranscripts(path2, tool);
15107
15693
  out.push({ id: sourceId(path2), tool, path: path2, sessions, newestMtime, isPrimary: i === 0 });
15108
15694
  }
15109
15695
  }
15110
15696
  for (const [i, path2] of opencodeDbPaths({ includeExcluded: true }).entries()) {
15111
- if (!existsSync23(path2))
15697
+ if (!existsSync24(path2))
15112
15698
  continue;
15113
- const { sessions, newestMtime } = countOpencodeSessions(path2);
15699
+ const { sessions, newestMtime } = countDbSessions(path2);
15114
15700
  out.push({ id: sourceId(path2), tool: "opencode", path: path2, sessions, newestMtime, isPrimary: i === 0 });
15115
15701
  }
15702
+ for (const [i, path2] of hermesDbPaths({ includeExcluded: true }).entries()) {
15703
+ if (!existsSync24(path2))
15704
+ continue;
15705
+ const { sessions, newestMtime } = countDbSessions(path2);
15706
+ out.push({ id: sourceId(path2), tool: "hermes", path: path2, sessions, newestMtime, isPrimary: i === 0 });
15707
+ }
15116
15708
  return out;
15117
15709
  }
15118
15710
  var excludedIds, SOURCE_ID_RE, MAX_DEPTH;
@@ -15128,7 +15720,7 @@ var init_source_discovery = __esm({
15128
15720
 
15129
15721
  // ../engine/dist/core/home-discovery.js
15130
15722
  import { execFileSync as execFileSync2 } from "child_process";
15131
- import { existsSync as existsSync24, readdirSync as readdirSync15, readFileSync as readFileSync19, realpathSync as realpathSync3, statSync as statSync23 } from "fs";
15723
+ import { existsSync as existsSync25, readdirSync as readdirSync15, readFileSync as readFileSync19, realpathSync as realpathSync3, statSync as statSync23 } from "fs";
15132
15724
  import { homedir as homedir7, platform as osPlatform } from "os";
15133
15725
  import { join as join29 } from "path";
15134
15726
  function candidateRoots(plat = osPlatform(), env = process.env) {
@@ -15149,10 +15741,10 @@ function candidateRoots(plat = osPlatform(), env = process.env) {
15149
15741
  add(env.XDG_CONFIG_HOME || join29(home, ".config"));
15150
15742
  add(env.XDG_DATA_HOME || join29(home, ".local", "share"));
15151
15743
  }
15152
- return roots.filter((r) => existsSync24(r));
15744
+ return roots.filter((r) => existsSync25(r));
15153
15745
  }
15154
15746
  function identifyHome(dir) {
15155
- const has = (...parts) => existsSync24(join29(dir, ...parts));
15747
+ const has = (...parts) => existsSync25(join29(dir, ...parts));
15156
15748
  if (has("projects") && dirHasTranscript(join29(dir, "projects"), (f) => f.endsWith(".jsonl"), 2))
15157
15749
  return "claude";
15158
15750
  if (has("sessions") && dirHasTranscript(join29(dir, "sessions"), (f) => f.startsWith("rollout-") && f.endsWith(".jsonl"), 4))
@@ -15163,6 +15755,8 @@ function identifyHome(dir) {
15163
15755
  return "cursor";
15164
15756
  if (has("opencode.db"))
15165
15757
  return "opencode";
15758
+ if (has("state.db") && has("SOUL.md"))
15759
+ return "hermes";
15166
15760
  return null;
15167
15761
  }
15168
15762
  function dirHasTranscript(root, match, depth) {
@@ -15190,7 +15784,8 @@ function countSessions(dir, tool) {
15190
15784
  codex: { sub: "sessions", depth: 4, match: (f) => f.startsWith("rollout-") && f.endsWith(".jsonl") },
15191
15785
  agy: { sub: "brain", depth: 4, match: (f) => f.endsWith(".jsonl") },
15192
15786
  cursor: { sub: "chats", depth: 3, match: (f) => f === "store.db" },
15193
- opencode: { sub: "", depth: 0, match: () => false }
15787
+ opencode: { sub: "", depth: 0, match: () => false },
15788
+ hermes: { sub: "", depth: 0, match: () => false }
15194
15789
  };
15195
15790
  const s = spec[tool];
15196
15791
  if (!s.sub)
@@ -15233,6 +15828,8 @@ function declaredHomes(env = process.env, plat = osPlatform()) {
15233
15828
  add(env.CHAT_RECALL_AGY_HOME);
15234
15829
  add(env.CHAT_RECALL_CURSOR_HOME);
15235
15830
  add(env.CHAT_RECALL_CURSOR_IDE_HOME);
15831
+ add(env.CHAT_RECALL_HERMES_HOME);
15832
+ add(env.HERMES_HOME);
15236
15833
  for (const d of (env.CLAUDE_DIRS || "").split(","))
15237
15834
  add(d);
15238
15835
  if (plat === "win32") {
@@ -15332,6 +15929,15 @@ function discoverHomes(opts = {}) {
15332
15929
  if (prior && !(via === "declared" && prior.via !== "declared"))
15333
15930
  return;
15334
15931
  found.set(real, { tool, path: real, via, sessions: countSessions(real, tool) });
15932
+ if (tool === "hermes") {
15933
+ try {
15934
+ for (const e of readdirSync15(join29(real, "profiles"), { withFileTypes: true })) {
15935
+ if (e.isDirectory())
15936
+ consider(join29(real, "profiles", e.name), via);
15937
+ }
15938
+ } catch {
15939
+ }
15940
+ }
15335
15941
  };
15336
15942
  for (const d of declaredHomes(env, plat))
15337
15943
  consider(d, "declared");
@@ -15999,7 +16605,7 @@ var init_source_policy = __esm({
15999
16605
  init_project_path_match();
16000
16606
  init_settings();
16001
16607
  cache2 = null;
16002
- TOOLS = ["claude", "opencode", "codex", "agy", "cursor"];
16608
+ TOOLS = ["claude", "opencode", "codex", "agy", "cursor", "hermes"];
16003
16609
  }
16004
16610
  });
16005
16611
 
@@ -16094,7 +16700,7 @@ var init_source_registry = __esm({
16094
16700
  });
16095
16701
 
16096
16702
  // ../engine/dist/core/utils.js
16097
- import { existsSync as existsSync25, readdirSync as readdirSync16 } from "fs";
16703
+ import { existsSync as existsSync26, readdirSync as readdirSync16 } from "fs";
16098
16704
  import { homedir as homedir8 } from "os";
16099
16705
  import { join as join30 } from "path";
16100
16706
  function splitByHeaders(markdown) {
@@ -16130,7 +16736,7 @@ function getClaudeDirs() {
16130
16736
  const dirs = [];
16131
16737
  const seen = /* @__PURE__ */ new Set();
16132
16738
  const add = (dir) => {
16133
- if (!seen.has(dir) && existsSync25(dir)) {
16739
+ if (!seen.has(dir) && existsSync26(dir)) {
16134
16740
  seen.add(dir);
16135
16741
  dirs.push(dir);
16136
16742
  }
@@ -16149,7 +16755,7 @@ function getClaudeDirs() {
16149
16755
  for (const entry of entries) {
16150
16756
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
16151
16757
  const candidate = join30(home, entry.name);
16152
- if (existsSync25(join30(candidate, "projects"))) {
16758
+ if (existsSync26(join30(candidate, "projects"))) {
16153
16759
  add(candidate);
16154
16760
  }
16155
16761
  }
@@ -16162,7 +16768,7 @@ function discoverSubdirs(subdir) {
16162
16768
  const result = [];
16163
16769
  for (const claudeDir of getClaudeDirs()) {
16164
16770
  const path2 = join30(claudeDir, subdir);
16165
- if (existsSync25(path2)) {
16771
+ if (existsSync26(path2)) {
16166
16772
  result.push(path2);
16167
16773
  }
16168
16774
  }
@@ -16175,7 +16781,7 @@ var init_utils = __esm({
16175
16781
  });
16176
16782
 
16177
16783
  // ../engine/dist/parsers/plan-source.js
16178
- import { existsSync as existsSync26, readdirSync as readdirSync17, readFileSync as readFileSync20, statSync as statSync25 } from "fs";
16784
+ import { existsSync as existsSync27, readdirSync as readdirSync17, readFileSync as readFileSync20, statSync as statSync25 } from "fs";
16179
16785
  import { homedir as homedir9 } from "os";
16180
16786
  import { join as join31, basename as basename14 } from "path";
16181
16787
  function parseFrontmatter(content) {
@@ -16233,7 +16839,7 @@ var init_plan_source = __esm({
16233
16839
  return this._knownProjectDirs;
16234
16840
  const projectsDir = claudeBackend.projectsDir();
16235
16841
  this._knownProjectDirs = [];
16236
- if (!existsSync26(projectsDir))
16842
+ if (!existsSync27(projectsDir))
16237
16843
  return this._knownProjectDirs;
16238
16844
  try {
16239
16845
  const entries = readdirSync17(projectsDir, { withFileTypes: true });
@@ -16241,7 +16847,7 @@ var init_plan_source = __esm({
16241
16847
  if (!entry.isDirectory() || entry.name.startsWith("."))
16242
16848
  continue;
16243
16849
  const projectPath = resolveProjectDirName(entry.name);
16244
- if (existsSync26(projectPath)) {
16850
+ if (existsSync27(projectPath)) {
16245
16851
  this._knownProjectDirs.push(projectPath);
16246
16852
  }
16247
16853
  }
@@ -16265,7 +16871,7 @@ var init_plan_source = __esm({
16265
16871
  async *discoverClaudePlans() {
16266
16872
  const seenClaude = /* @__PURE__ */ new Set();
16267
16873
  for (const plansDir of this.plansDirs) {
16268
- if (!existsSync26(plansDir))
16874
+ if (!existsSync27(plansDir))
16269
16875
  continue;
16270
16876
  const files = readdirSync17(plansDir).filter((f) => f.endsWith(".md"));
16271
16877
  for (const file of files) {
@@ -16307,7 +16913,7 @@ var init_plan_source = __esm({
16307
16913
  }
16308
16914
  async *discoverOpenCodePlans() {
16309
16915
  const plansDir = opencodeBackend.plansDir();
16310
- if (!existsSync26(plansDir))
16916
+ if (!existsSync27(plansDir))
16311
16917
  return;
16312
16918
  let files;
16313
16919
  try {
@@ -16394,7 +17000,7 @@ var init_plan_source = __esm({
16394
17000
  return "";
16395
17001
  }
16396
17002
  async parse(item) {
16397
- if (!existsSync26(item.filePath))
17003
+ if (!existsSync27(item.filePath))
16398
17004
  return [];
16399
17005
  const content = readFileSync20(item.filePath, "utf-8");
16400
17006
  const chunks = [];
@@ -16435,7 +17041,7 @@ var init_plan_source = __esm({
16435
17041
  const projPath = join31(root, proj);
16436
17042
  for (const sessionEntry of safeReaddir(projPath)) {
16437
17043
  const subDir = join31(projPath, sessionEntry, "subagents");
16438
- if (!existsSync26(subDir))
17044
+ if (!existsSync27(subDir))
16439
17045
  continue;
16440
17046
  for (const f of safeReaddir(subDir)) {
16441
17047
  const m = f.match(/^agent-([a-z0-9_-]+)\.jsonl$/i);
@@ -16510,7 +17116,7 @@ var init_plan_source = __esm({
16510
17116
  }
16511
17117
  async *discoverAgyPlans() {
16512
17118
  const brainDir = join31(agyBackend.homeDir(), "brain");
16513
- if (!existsSync26(brainDir))
17119
+ if (!existsSync27(brainDir))
16514
17120
  return;
16515
17121
  let sessionDirs;
16516
17122
  try {
@@ -16565,7 +17171,7 @@ var init_plan_source = __esm({
16565
17171
  });
16566
17172
 
16567
17173
  // ../engine/dist/parsers/task-source.js
16568
- import { existsSync as existsSync27, readdirSync as readdirSync18, readFileSync as readFileSync21, statSync as statSync26 } from "fs";
17174
+ import { existsSync as existsSync28, readdirSync as readdirSync18, readFileSync as readFileSync21, statSync as statSync26 } from "fs";
16569
17175
  import { join as join32 } from "path";
16570
17176
  var MAX_CHUNK_CHARS2, TaskSource;
16571
17177
  var init_task_source = __esm({
@@ -16595,7 +17201,7 @@ var init_task_source = __esm({
16595
17201
  }
16596
17202
  async *discoverClaudeTodos() {
16597
17203
  const todosDir = claudeBackend.todosDir();
16598
- if (!existsSync27(todosDir))
17204
+ if (!existsSync28(todosDir))
16599
17205
  return;
16600
17206
  let files;
16601
17207
  try {
@@ -16646,7 +17252,7 @@ var init_task_source = __esm({
16646
17252
  }
16647
17253
  }
16648
17254
  async *discoverInDir(tasksDir) {
16649
- if (!existsSync27(tasksDir))
17255
+ if (!existsSync28(tasksDir))
16650
17256
  return;
16651
17257
  const entries = readdirSync18(tasksDir, { withFileTypes: true });
16652
17258
  for (const entry of entries) {
@@ -16688,7 +17294,7 @@ var init_task_source = __esm({
16688
17294
  return this.parseAgentTodo(item);
16689
17295
  }
16690
17296
  const taskDir = item.filePath;
16691
- if (!existsSync27(taskDir))
17297
+ if (!existsSync28(taskDir))
16692
17298
  return [];
16693
17299
  const taskFiles = readdirSync18(taskDir).filter((f) => f.endsWith(".json"));
16694
17300
  const tasks = this.readTasks(taskDir, taskFiles);
@@ -16749,7 +17355,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
16749
17355
  }];
16750
17356
  }
16751
17357
  async parseAgentTodo(item) {
16752
- if (!existsSync27(item.filePath))
17358
+ if (!existsSync28(item.filePath))
16753
17359
  return [];
16754
17360
  let parsed;
16755
17361
  try {
@@ -16801,7 +17407,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
16801
17407
  });
16802
17408
 
16803
17409
  // ../engine/dist/parsers/claude-md-source.js
16804
- import { existsSync as existsSync28, readFileSync as readFileSync22, readdirSync as readdirSync19, statSync as statSync27 } from "fs";
17410
+ import { existsSync as existsSync29, readFileSync as readFileSync22, readdirSync as readdirSync19, statSync as statSync27 } from "fs";
16805
17411
  import { homedir as homedir10 } from "os";
16806
17412
  import { join as join33, basename as basename15, dirname as dirname16 } from "path";
16807
17413
  import { createHash as createHash9 } from "crypto";
@@ -16809,13 +17415,13 @@ function discoverProjectDirs(claudeDir) {
16809
17415
  const dirs = [];
16810
17416
  const seen = /* @__PURE__ */ new Set();
16811
17417
  const addProject = (path2) => {
16812
- if (!seen.has(path2) && existsSync28(path2)) {
17418
+ if (!seen.has(path2) && existsSync29(path2)) {
16813
17419
  seen.add(path2);
16814
17420
  dirs.push(path2);
16815
17421
  }
16816
17422
  };
16817
17423
  const scanProjectsDir = (projectsDir) => {
16818
- if (!existsSync28(projectsDir))
17424
+ if (!existsSync29(projectsDir))
16819
17425
  return;
16820
17426
  try {
16821
17427
  const entries = readdirSync19(projectsDir, { withFileTypes: true });
@@ -16843,7 +17449,7 @@ function discoverProjectDirs(claudeDir) {
16843
17449
  } catch {
16844
17450
  }
16845
17451
  const gemProjects = agyBackend.sharedProjectsJson();
16846
- if (existsSync28(gemProjects)) {
17452
+ if (existsSync29(gemProjects)) {
16847
17453
  try {
16848
17454
  const data = JSON.parse(readFileSync22(gemProjects, "utf-8"));
16849
17455
  for (const path2 of Object.keys(data.projects || {}))
@@ -16852,7 +17458,7 @@ function discoverProjectDirs(claudeDir) {
16852
17458
  }
16853
17459
  }
16854
17460
  const ocDb = opencodeBackend.dbPath();
16855
- if (existsSync28(ocDb)) {
17461
+ if (existsSync29(ocDb)) {
16856
17462
  try {
16857
17463
  const db = openSqliteReadonlyOrThrow(ocDb);
16858
17464
  try {
@@ -16903,7 +17509,7 @@ var init_claude_md_source = __esm({
16903
17509
  for (const note of NOTE_FILES) {
16904
17510
  const p = join33(homedir10(), note.filename);
16905
17511
  const key2 = seenKey(p, note.tool);
16906
- if (existsSync28(p) && !seen.has(key2)) {
17512
+ if (existsSync29(p) && !seen.has(key2)) {
16907
17513
  seen.add(key2);
16908
17514
  const item = this.fileToItem(p, note.filename, note.tool);
16909
17515
  if (item)
@@ -16915,7 +17521,7 @@ var init_claude_md_source = __esm({
16915
17521
  for (const note of NOTE_FILES) {
16916
17522
  const p = join33(projectDir, note.filename);
16917
17523
  const key2 = seenKey(p, note.tool);
16918
- if (existsSync28(p) && !seen.has(key2)) {
17524
+ if (existsSync29(p) && !seen.has(key2)) {
16919
17525
  seen.add(key2);
16920
17526
  const item = this.fileToItem(p, note.filename, note.tool);
16921
17527
  if (item)
@@ -16924,12 +17530,12 @@ var init_claude_md_source = __esm({
16924
17530
  }
16925
17531
  }
16926
17532
  for (const searchDir of this.searchDirs) {
16927
- if (!existsSync28(searchDir))
17533
+ if (!existsSync29(searchDir))
16928
17534
  continue;
16929
17535
  for (const note of NOTE_FILES) {
16930
17536
  const directPath = join33(searchDir, note.filename);
16931
17537
  const key2 = seenKey(directPath, note.tool);
16932
- if (existsSync28(directPath) && !seen.has(key2)) {
17538
+ if (existsSync29(directPath) && !seen.has(key2)) {
16933
17539
  seen.add(key2);
16934
17540
  const item = this.fileToItem(directPath, note.filename, note.tool);
16935
17541
  if (item)
@@ -16944,7 +17550,7 @@ var init_claude_md_source = __esm({
16944
17550
  for (const note of NOTE_FILES) {
16945
17551
  const p = join33(searchDir, entry.name, note.filename);
16946
17552
  const key2 = seenKey(p, note.tool);
16947
- if (existsSync28(p) && !seen.has(key2)) {
17553
+ if (existsSync29(p) && !seen.has(key2)) {
16948
17554
  seen.add(key2);
16949
17555
  const item = this.fileToItem(p, note.filename, note.tool);
16950
17556
  if (item)
@@ -16958,7 +17564,7 @@ var init_claude_md_source = __esm({
16958
17564
  }
16959
17565
  }
16960
17566
  async parse(item) {
16961
- if (!existsSync28(item.filePath))
17567
+ if (!existsSync29(item.filePath))
16962
17568
  return [];
16963
17569
  const content = readFileSync22(item.filePath, "utf-8");
16964
17570
  const chunks = [];
@@ -17027,7 +17633,7 @@ var init_claude_md_source = __esm({
17027
17633
  });
17028
17634
 
17029
17635
  // ../engine/dist/parsers/agent-memory-source.js
17030
- import { existsSync as existsSync29, readFileSync as readFileSync23, readdirSync as readdirSync20, statSync as statSync28 } from "fs";
17636
+ import { existsSync as existsSync30, readFileSync as readFileSync23, readdirSync as readdirSync20, statSync as statSync28 } from "fs";
17031
17637
  import { homedir as homedir11 } from "os";
17032
17638
  import { join as join34, basename as basename16 } from "path";
17033
17639
  function parseFrontmatter2(content) {
@@ -17061,7 +17667,7 @@ function discoverMemoryDirs() {
17061
17667
  const out = [];
17062
17668
  const seen = /* @__PURE__ */ new Set();
17063
17669
  const add = (memoryDir, projectHash) => {
17064
- if (!seen.has(memoryDir) && existsSync29(memoryDir)) {
17670
+ if (!seen.has(memoryDir) && existsSync30(memoryDir)) {
17065
17671
  seen.add(memoryDir);
17066
17672
  out.push({ memoryDir, projectHash });
17067
17673
  }
@@ -17077,7 +17683,7 @@ function discoverMemoryDirs() {
17077
17683
  } catch {
17078
17684
  }
17079
17685
  for (const projectsDir of claudeRoots) {
17080
- if (!existsSync29(projectsDir))
17686
+ if (!existsSync30(projectsDir))
17081
17687
  continue;
17082
17688
  try {
17083
17689
  for (const entry of readdirSync20(projectsDir, { withFileTypes: true })) {
@@ -17126,7 +17732,7 @@ var init_agent_memory_source = __esm({
17126
17732
  }
17127
17733
  }
17128
17734
  async parse(item) {
17129
- if (!existsSync29(item.filePath))
17735
+ if (!existsSync30(item.filePath))
17130
17736
  return [];
17131
17737
  const content = readFileSync23(item.filePath, "utf-8");
17132
17738
  const { body: body2 } = parseFrontmatter2(content);
@@ -17212,7 +17818,7 @@ var init_agent_memory_source = __esm({
17212
17818
  });
17213
17819
 
17214
17820
  // ../engine/dist/parsers/history-source.js
17215
- import { existsSync as existsSync30, readdirSync as readdirSync21, statSync as statSync29 } from "fs";
17821
+ import { existsSync as existsSync31, readdirSync as readdirSync21, statSync as statSync29 } from "fs";
17216
17822
  import { createReadStream as createReadStream3 } from "fs";
17217
17823
  import { createInterface as createInterface2 } from "readline";
17218
17824
  import { homedir as homedir12 } from "os";
@@ -17234,7 +17840,7 @@ var init_history_source = __esm({
17234
17840
  const home = homedir12();
17235
17841
  const paths = [];
17236
17842
  const tryAdd = (p) => {
17237
- if (existsSync30(p) && !paths.includes(p))
17843
+ if (existsSync31(p) && !paths.includes(p))
17238
17844
  paths.push(p);
17239
17845
  };
17240
17846
  tryAdd(claudeBackend.historyFile());
@@ -17252,7 +17858,7 @@ var init_history_source = __esm({
17252
17858
  async *discover() {
17253
17859
  if (!isSourceEnabled("claude", "history"))
17254
17860
  return;
17255
- const historyPath = this.historyPaths.find((p) => existsSync30(p));
17861
+ const historyPath = this.historyPaths.find((p) => existsSync31(p));
17256
17862
  if (!historyPath)
17257
17863
  return;
17258
17864
  const stat3 = statSync29(historyPath);
@@ -17348,7 +17954,7 @@ var init_history_source = __esm({
17348
17954
  async groupByProject() {
17349
17955
  const groups = /* @__PURE__ */ new Map();
17350
17956
  for (const historyPath of this.historyPaths) {
17351
- if (!existsSync30(historyPath))
17957
+ if (!existsSync31(historyPath))
17352
17958
  continue;
17353
17959
  const rl = createInterface2({
17354
17960
  input: createReadStream3(historyPath),
@@ -17396,7 +18002,7 @@ var init_history_source = __esm({
17396
18002
  });
17397
18003
 
17398
18004
  // ../engine/dist/parsers/paste-source.js
17399
- import { existsSync as existsSync31, readdirSync as readdirSync22, readFileSync as readFileSync24, statSync as statSync30 } from "fs";
18005
+ import { existsSync as existsSync32, readdirSync as readdirSync22, readFileSync as readFileSync24, statSync as statSync30 } from "fs";
17400
18006
  import { homedir as homedir13 } from "os";
17401
18007
  import { join as join36, basename as basename17 } from "path";
17402
18008
  var MAX_CHUNK_CHARS6, MAX_FILE_SIZE, PasteSource;
@@ -17417,7 +18023,7 @@ var init_paste_source = __esm({
17417
18023
  const home = homedir13();
17418
18024
  const dirs = [];
17419
18025
  const tryAdd = (p) => {
17420
- if (existsSync31(p) && !dirs.includes(p))
18026
+ if (existsSync32(p) && !dirs.includes(p))
17421
18027
  dirs.push(p);
17422
18028
  };
17423
18029
  tryAdd(claudeBackend.pasteCacheDir());
@@ -17437,7 +18043,7 @@ var init_paste_source = __esm({
17437
18043
  return;
17438
18044
  const seen = /* @__PURE__ */ new Set();
17439
18045
  for (const cacheDir of this.cacheDirs) {
17440
- if (!existsSync31(cacheDir))
18046
+ if (!existsSync32(cacheDir))
17441
18047
  continue;
17442
18048
  const files = readdirSync22(cacheDir).filter((f) => f.endsWith(".txt"));
17443
18049
  for (const file of files) {
@@ -17471,7 +18077,7 @@ var init_paste_source = __esm({
17471
18077
  }
17472
18078
  }
17473
18079
  async parse(item) {
17474
- if (!existsSync31(item.filePath))
18080
+ if (!existsSync32(item.filePath))
17475
18081
  return [];
17476
18082
  const content = readFileSync24(item.filePath, "utf-8");
17477
18083
  if (content.trim().length < 20)
@@ -17496,7 +18102,7 @@ var init_paste_source = __esm({
17496
18102
  });
17497
18103
 
17498
18104
  // ../engine/dist/parsers/antigravity-brain-source.js
17499
- import { existsSync as existsSync32, readdirSync as readdirSync23, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
18105
+ import { existsSync as existsSync33, readdirSync as readdirSync23, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
17500
18106
  import { join as join37 } from "path";
17501
18107
  var MAX_CHUNK_CHARS7, AntigravityBrainSource;
17502
18108
  var init_antigravity_brain_source = __esm({
@@ -17515,7 +18121,7 @@ var init_antigravity_brain_source = __esm({
17515
18121
  async *discover() {
17516
18122
  if (!isSourceEnabled("agy", "brain"))
17517
18123
  return;
17518
- if (!existsSync32(this.brainDir))
18124
+ if (!existsSync33(this.brainDir))
17519
18125
  return;
17520
18126
  const sessions = readdirSync23(this.brainDir, { withFileTypes: true });
17521
18127
  for (const entry of sessions) {
@@ -17534,7 +18140,7 @@ var init_antigravity_brain_source = __esm({
17534
18140
  const heading = content.split("\n").find((l) => l.startsWith("#"))?.replace(/^#+\s*/, "").trim() || file;
17535
18141
  const metaPath = `${filePath}.metadata.json`;
17536
18142
  let metaSummary = "";
17537
- if (existsSync32(metaPath)) {
18143
+ if (existsSync33(metaPath)) {
17538
18144
  try {
17539
18145
  const meta = JSON.parse(readFileSync25(metaPath, "utf-8"));
17540
18146
  metaSummary = meta.summary || "";
@@ -17565,7 +18171,7 @@ var init_antigravity_brain_source = __esm({
17565
18171
  }
17566
18172
  }
17567
18173
  async parse(item) {
17568
- if (!existsSync32(item.filePath))
18174
+ if (!existsSync33(item.filePath))
17569
18175
  return [];
17570
18176
  const content = readFileSync25(item.filePath, "utf-8");
17571
18177
  const chunks = [];
@@ -17608,7 +18214,7 @@ var init_antigravity_brain_source = __esm({
17608
18214
  });
17609
18215
 
17610
18216
  // ../engine/dist/parsers/opencode-source.js
17611
- import { existsSync as existsSync33 } from "fs";
18217
+ import { existsSync as existsSync34 } from "fs";
17612
18218
  var MAX_CHUNK_CHARS8, OpenCodeSource;
17613
18219
  var init_opencode_source = __esm({
17614
18220
  "../engine/dist/parsers/opencode-source.js"() {
@@ -17626,7 +18232,7 @@ var init_opencode_source = __esm({
17626
18232
  async *discover() {
17627
18233
  if (!isSourceEnabled("opencode", "sessions"))
17628
18234
  return;
17629
- if (!existsSync33(this.dbPath))
18235
+ if (!existsSync34(this.dbPath))
17630
18236
  return;
17631
18237
  let db;
17632
18238
  try {
@@ -17724,7 +18330,7 @@ var init_opencode_source = __esm({
17724
18330
  }
17725
18331
  }
17726
18332
  async parse(item) {
17727
- if (!existsSync33(this.dbPath))
18333
+ if (!existsSync34(this.dbPath))
17728
18334
  return [];
17729
18335
  const sessionId = item.id.replace("opencode_", "");
17730
18336
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -17796,7 +18402,7 @@ var init_opencode_source = __esm({
17796
18402
  });
17797
18403
 
17798
18404
  // ../engine/dist/parsers/opencode-todo-source.js
17799
- import { existsSync as existsSync34 } from "fs";
18405
+ import { existsSync as existsSync35 } from "fs";
17800
18406
  var OpenCodeTodoSource;
17801
18407
  var init_opencode_todo_source = __esm({
17802
18408
  "../engine/dist/parsers/opencode-todo-source.js"() {
@@ -17813,7 +18419,7 @@ var init_opencode_todo_source = __esm({
17813
18419
  async *discover() {
17814
18420
  if (!isSourceEnabled("opencode", "todos"))
17815
18421
  return;
17816
- if (!existsSync34(this.dbPath))
18422
+ if (!existsSync35(this.dbPath))
17817
18423
  return;
17818
18424
  let db;
17819
18425
  try {
@@ -17862,7 +18468,7 @@ var init_opencode_todo_source = __esm({
17862
18468
  }
17863
18469
  }
17864
18470
  async parse(item) {
17865
- if (!existsSync34(this.dbPath))
18471
+ if (!existsSync35(this.dbPath))
17866
18472
  return [];
17867
18473
  const sessionId = item.extra?.sessionId || item.id.replace(`${opencodeBackend.idPrefix}todo_`, "");
17868
18474
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -17910,7 +18516,7 @@ var init_opencode_todo_source = __esm({
17910
18516
  });
17911
18517
 
17912
18518
  // ../engine/dist/parsers/codex-session-source.js
17913
- import { existsSync as existsSync35, readdirSync as readdirSync24, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
18519
+ import { existsSync as existsSync36, readdirSync as readdirSync24, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
17914
18520
  import { join as join38, basename as basename18 } from "path";
17915
18521
  function getCodexSessionsDir() {
17916
18522
  return codexBackend.sessionsDir();
@@ -17967,7 +18573,7 @@ var init_codex_session_source = __esm({
17967
18573
  async *discover() {
17968
18574
  if (!isSourceEnabled("codex", "sessions"))
17969
18575
  return;
17970
- if (!existsSync35(this.sessionsDir))
18576
+ if (!existsSync36(this.sessionsDir))
17971
18577
  return;
17972
18578
  const jsonlFiles = this.collectJsonlFiles(this.sessionsDir);
17973
18579
  for (const filePath of jsonlFiles) {
@@ -18026,7 +18632,7 @@ var init_codex_session_source = __esm({
18026
18632
  }
18027
18633
  }
18028
18634
  async parse(item) {
18029
- if (!existsSync35(item.filePath))
18635
+ if (!existsSync36(item.filePath))
18030
18636
  return [];
18031
18637
  const events = readJsonlLines(item.filePath);
18032
18638
  const chunks = [];
@@ -18129,7 +18735,7 @@ var init_codex_session_source = __esm({
18129
18735
  }
18130
18736
  collectJsonlFiles(dir) {
18131
18737
  const results = [];
18132
- if (!existsSync35(dir))
18738
+ if (!existsSync36(dir))
18133
18739
  return results;
18134
18740
  for (const entry of readdirSync24(dir, { withFileTypes: true })) {
18135
18741
  const fullPath = join38(dir, entry.name);
@@ -18146,7 +18752,7 @@ var init_codex_session_source = __esm({
18146
18752
  });
18147
18753
 
18148
18754
  // ../engine/dist/parsers/diary-source.js
18149
- import { existsSync as existsSync36, mkdirSync as mkdirSync9, readdirSync as readdirSync25, readFileSync as readFileSync27, writeFileSync as writeFileSync6, statSync as statSync33 } from "fs";
18755
+ import { existsSync as existsSync37, mkdirSync as mkdirSync9, readdirSync as readdirSync25, readFileSync as readFileSync27, writeFileSync as writeFileSync6, statSync as statSync33 } from "fs";
18150
18756
  import { join as join39, basename as basename19 } from "path";
18151
18757
  var MAX_CHUNK_CHARS10, DiarySource;
18152
18758
  var init_diary_source = __esm({
@@ -18162,7 +18768,7 @@ var init_diary_source = __esm({
18162
18768
  }
18163
18769
  /** Discover all diary entries across all agents. */
18164
18770
  async *discover() {
18165
- if (!existsSync36(this.diaryDir))
18771
+ if (!existsSync37(this.diaryDir))
18166
18772
  return;
18167
18773
  const agents = readdirSync25(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
18168
18774
  for (const agentDir of agents) {
@@ -18197,7 +18803,7 @@ var init_diary_source = __esm({
18197
18803
  }
18198
18804
  }
18199
18805
  async parse(item) {
18200
- if (!existsSync36(item.filePath))
18806
+ if (!existsSync37(item.filePath))
18201
18807
  return [];
18202
18808
  const raw = readFileSync27(item.filePath, "utf-8");
18203
18809
  const entry = JSON.parse(raw);
@@ -18237,7 +18843,7 @@ var init_diary_source = __esm({
18237
18843
  static write(entry, diaryDir) {
18238
18844
  const dir = diaryDir || getDiaryDir();
18239
18845
  const agentDir = join39(dir, entry.agent.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
18240
- if (!existsSync36(agentDir)) {
18846
+ if (!existsSync37(agentDir)) {
18241
18847
  mkdirSync9(agentDir, { recursive: true });
18242
18848
  }
18243
18849
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -18256,7 +18862,7 @@ var init_diary_source = __esm({
18256
18862
  static read(agentName, lastN = 10, diaryDir) {
18257
18863
  const dir = diaryDir || getDiaryDir();
18258
18864
  const agentDir = join39(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
18259
- if (!existsSync36(agentDir))
18865
+ if (!existsSync37(agentDir))
18260
18866
  return [];
18261
18867
  const files = readdirSync25(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
18262
18868
  const entries = [];
@@ -18307,7 +18913,7 @@ var init_redact_inline = __esm({
18307
18913
  });
18308
18914
 
18309
18915
  // ../engine/dist/parsers/skills-source.js
18310
- import { existsSync as existsSync37, readdirSync as readdirSync26, readFileSync as readFileSync28, statSync as statSync34 } from "fs";
18916
+ import { existsSync as existsSync38, readdirSync as readdirSync26, readFileSync as readFileSync28, statSync as statSync34 } from "fs";
18311
18917
  import { homedir as homedir14 } from "os";
18312
18918
  import { join as join40 } from "path";
18313
18919
  function rebuildBody(content) {
@@ -18347,7 +18953,7 @@ function resolveRoots() {
18347
18953
  all.push({ path: join40(cursorHomeDir(), "skills"), tool: "cursor" });
18348
18954
  all.push({ path: join40(cursorHomeDir(), "skills-cursor"), tool: "cursor", readonly: true });
18349
18955
  }
18350
- return all.filter((r) => existsSync37(r.path));
18956
+ return all.filter((r) => existsSync38(r.path));
18351
18957
  }
18352
18958
  function parseFrontmatter3(text) {
18353
18959
  const m = text.match(/^---\n([\s\S]*?)\n---/);
@@ -18389,7 +18995,7 @@ var init_skills_source = __esm({
18389
18995
  return;
18390
18996
  for (const { name: pluginName, dir } of codexBackend.installedPluginDirs()) {
18391
18997
  const skillsDir = join40(dir, "skills");
18392
- if (!existsSync37(skillsDir))
18998
+ if (!existsSync38(skillsDir))
18393
18999
  continue;
18394
19000
  yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
18395
19001
  }
@@ -18414,7 +19020,7 @@ var init_skills_source = __esm({
18414
19020
  if (!st.isDirectory())
18415
19021
  continue;
18416
19022
  const skillMd = join40(skillDir, "SKILL.md");
18417
- if (!existsSync37(skillMd))
19023
+ if (!existsSync38(skillMd))
18418
19024
  continue;
18419
19025
  let content;
18420
19026
  try {
@@ -18479,7 +19085,7 @@ var init_skills_source = __esm({
18479
19085
  }
18480
19086
  }
18481
19087
  async parse(item) {
18482
- if (!existsSync37(item.filePath))
19088
+ if (!existsSync38(item.filePath))
18483
19089
  return [];
18484
19090
  const content = readFileSync28(item.filePath, "utf-8");
18485
19091
  if (!content.trim())
@@ -18504,11 +19110,11 @@ var init_skills_source = __esm({
18504
19110
  });
18505
19111
 
18506
19112
  // ../engine/dist/parsers/mcps-source.js
18507
- import { existsSync as existsSync38, readFileSync as readFileSync29, statSync as statSync35 } from "fs";
19113
+ import { existsSync as existsSync39, readFileSync as readFileSync29, statSync as statSync35 } from "fs";
18508
19114
  import { homedir as homedir15 } from "os";
18509
19115
  import { join as join41 } from "path";
18510
19116
  function readJson(path2) {
18511
- if (!existsSync38(path2))
19117
+ if (!existsSync39(path2))
18512
19118
  return null;
18513
19119
  try {
18514
19120
  const raw = readFileSync29(path2, "utf-8");
@@ -18671,7 +19277,7 @@ var init_mcps_source = __esm({
18671
19277
  }
18672
19278
  }
18673
19279
  async *fromCodexToml(path2) {
18674
- if (!existsSync38(path2))
19280
+ if (!existsSync39(path2))
18675
19281
  return;
18676
19282
  let content;
18677
19283
  try {
@@ -18827,7 +19433,7 @@ var init_toolkit_format = __esm({
18827
19433
  });
18828
19434
 
18829
19435
  // ../engine/dist/parsers/slash-commands-source.js
18830
- import { existsSync as existsSync39, readdirSync as readdirSync27, readFileSync as readFileSync30, statSync as statSync36 } from "fs";
19436
+ import { existsSync as existsSync40, readdirSync as readdirSync27, readFileSync as readFileSync30, statSync as statSync36 } from "fs";
18831
19437
  import { join as join42, basename as basename20 } from "path";
18832
19438
  function rebuildBody2(content) {
18833
19439
  const { text, redacted } = redactInlineSecrets(content);
@@ -18880,14 +19486,14 @@ var init_slash_commands_source = __esm({
18880
19486
  if (isSourceEnabled("claude", "commands")) {
18881
19487
  roots.push({ path: claudeBackend.commandsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
18882
19488
  const projectsRoot = claudeBackend.projectsDir();
18883
- if (existsSync39(projectsRoot)) {
19489
+ if (existsSync40(projectsRoot)) {
18884
19490
  try {
18885
19491
  for (const entry of readdirSync27(projectsRoot, { withFileTypes: true })) {
18886
19492
  if (!entry.isDirectory() || entry.name.startsWith("."))
18887
19493
  continue;
18888
19494
  const projectPath = resolveProjectDirName(entry.name);
18889
19495
  const cmdDir = join42(projectPath, ".claude", "commands");
18890
- if (existsSync39(cmdDir))
19496
+ if (existsSync40(cmdDir))
18891
19497
  roots.push({ path: cmdDir, tool: "claude", scope: "project", projectPath, format: "md" });
18892
19498
  }
18893
19499
  } catch {
@@ -18949,7 +19555,7 @@ var init_slash_commands_source = __esm({
18949
19555
  }
18950
19556
  }
18951
19557
  async parse(item) {
18952
- if (!existsSync39(item.filePath))
19558
+ if (!existsSync40(item.filePath))
18953
19559
  return [];
18954
19560
  const content = readFileSync30(item.filePath, "utf-8");
18955
19561
  if (!content.trim())
@@ -18974,7 +19580,7 @@ var init_slash_commands_source = __esm({
18974
19580
  });
18975
19581
 
18976
19582
  // ../engine/dist/parsers/subagents-source.js
18977
- import { existsSync as existsSync40, readdirSync as readdirSync28, readFileSync as readFileSync31, statSync as statSync37 } from "fs";
19583
+ import { existsSync as existsSync41, readdirSync as readdirSync28, readFileSync as readFileSync31, statSync as statSync37 } from "fs";
18978
19584
  import { join as join43, basename as basename21 } from "path";
18979
19585
  function rebuildBody3(content) {
18980
19586
  const { text, redacted } = redactInlineSecrets(content);
@@ -19029,14 +19635,14 @@ var init_subagents_source = __esm({
19029
19635
  if (isSourceEnabled("claude", "agents")) {
19030
19636
  roots.push({ path: claudeBackend.agentsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
19031
19637
  const projectsRoot = claudeBackend.projectsDir();
19032
- if (existsSync40(projectsRoot)) {
19638
+ if (existsSync41(projectsRoot)) {
19033
19639
  try {
19034
19640
  for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
19035
19641
  if (!entry.isDirectory() || entry.name.startsWith("."))
19036
19642
  continue;
19037
19643
  const projectPath = resolveProjectDirName(entry.name);
19038
19644
  const agentDir = join43(projectPath, ".claude", "agents");
19039
- if (existsSync40(agentDir))
19645
+ if (existsSync41(agentDir))
19040
19646
  roots.push({ path: agentDir, tool: "claude", scope: "project", projectPath, format: "md" });
19041
19647
  }
19042
19648
  } catch {
@@ -19099,7 +19705,7 @@ var init_subagents_source = __esm({
19099
19705
  }
19100
19706
  }
19101
19707
  async parse(item) {
19102
- if (!existsSync40(item.filePath))
19708
+ if (!existsSync41(item.filePath))
19103
19709
  return [];
19104
19710
  const content = readFileSync31(item.filePath, "utf-8");
19105
19711
  if (!content.trim())
@@ -19124,10 +19730,10 @@ var init_subagents_source = __esm({
19124
19730
  });
19125
19731
 
19126
19732
  // ../engine/dist/parsers/hooks-source.js
19127
- import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync29, statSync as statSync38 } from "fs";
19733
+ import { existsSync as existsSync42, readFileSync as readFileSync32, readdirSync as readdirSync29, statSync as statSync38 } from "fs";
19128
19734
  import { join as join44 } from "path";
19129
19735
  function readJson2(path2) {
19130
- if (!existsSync41(path2))
19736
+ if (!existsSync42(path2))
19131
19737
  return null;
19132
19738
  try {
19133
19739
  return JSON.parse(readFileSync32(path2, "utf-8"));
@@ -19149,14 +19755,14 @@ var init_hooks_source = __esm({
19149
19755
  return;
19150
19756
  yield* this.fromSettings(claudeBackend.settingsFile(), "user", "");
19151
19757
  const projectsRoot = claudeBackend.projectsDir();
19152
- if (existsSync41(projectsRoot)) {
19758
+ if (existsSync42(projectsRoot)) {
19153
19759
  try {
19154
19760
  for (const entry of readdirSync29(projectsRoot, { withFileTypes: true })) {
19155
19761
  if (!entry.isDirectory() || entry.name.startsWith("."))
19156
19762
  continue;
19157
19763
  const projectPath = resolveProjectDirName(entry.name);
19158
19764
  const settingsPath2 = join44(projectPath, ".claude", "settings.json");
19159
- if (existsSync41(settingsPath2)) {
19765
+ if (existsSync42(settingsPath2)) {
19160
19766
  yield* this.fromSettings(settingsPath2, "project", projectPath);
19161
19767
  }
19162
19768
  }
@@ -19247,11 +19853,11 @@ var init_hooks_source = __esm({
19247
19853
  });
19248
19854
 
19249
19855
  // ../engine/dist/parsers/plugins-source.js
19250
- import { existsSync as existsSync42, readFileSync as readFileSync33, readdirSync as readdirSync30, statSync as statSync39 } from "fs";
19856
+ import { existsSync as existsSync43, readFileSync as readFileSync33, readdirSync as readdirSync30, statSync as statSync39 } from "fs";
19251
19857
  import { homedir as homedir16 } from "os";
19252
19858
  import { join as join45 } from "path";
19253
19859
  function readJson3(path2) {
19254
- if (!existsSync42(path2))
19860
+ if (!existsSync43(path2))
19255
19861
  return null;
19256
19862
  try {
19257
19863
  return JSON.parse(readFileSync33(path2, "utf-8"));
@@ -19306,7 +19912,7 @@ var init_plugins_source = __esm({
19306
19912
  }
19307
19913
  }
19308
19914
  const gemRoot = agyBackend.sharedExtensionsDir();
19309
- if (isSourceEnabled("agy", "extensions") && existsSync42(gemRoot)) {
19915
+ if (isSourceEnabled("agy", "extensions") && existsSync43(gemRoot)) {
19310
19916
  let entries;
19311
19917
  try {
19312
19918
  entries = readdirSync30(gemRoot);
@@ -19482,6 +20088,8 @@ function toolOfId(id) {
19482
20088
  return "codex";
19483
20089
  if (id.startsWith("cursor_"))
19484
20090
  return "cursor";
20091
+ if (id.startsWith("hermes_"))
20092
+ return "hermes";
19485
20093
  return "claude";
19486
20094
  }
19487
20095
  function extractorVersionForTool(tool) {
@@ -19734,6 +20342,13 @@ function markSynced(server, rows) {
19734
20342
  base2.s = s;
19735
20343
  }
19736
20344
  if (r.hash !== void 0) base2.h = r.hash;
20345
+ const completesChunked = cursorFromCaller && r.chunkFinal === true && prev?.k === 1;
20346
+ if (cursorFromCaller && (r.full || completesChunked) && o > 0) base2.F = o;
20347
+ else if (prev?.F !== void 0) base2.F = prev.F;
20348
+ if (cursorFromCaller && r.chunkedHead) base2.k = 1;
20349
+ else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base2.k = 1;
20350
+ const layout = cursorFromCaller ? r.layout : prev?.L;
20351
+ if (layout) base2.L = layout;
19737
20352
  forServer[r.id] = base2;
19738
20353
  }
19739
20354
  persist(data);
@@ -19790,6 +20405,14 @@ function markFieldFullPassDone(server, field) {
19790
20405
  delete st.force[field.name];
19791
20406
  persistReconcile(data);
19792
20407
  }
20408
+ function forgetFullCoverage(server, sessionId) {
20409
+ const data = load();
20410
+ const row = data[server]?.[sessionId];
20411
+ if (!row || typeof row !== "object") return;
20412
+ delete row.F;
20413
+ delete row.h;
20414
+ persist(data);
20415
+ }
19793
20416
  function markFullResync(server, sessionId) {
19794
20417
  const data = load();
19795
20418
  const forServer = data[server];
@@ -19798,7 +20421,11 @@ function markFullResync(server, sessionId) {
19798
20421
  delete forServer[sessionId];
19799
20422
  persist(data);
19800
20423
  }
19801
- function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
20424
+ function fullCoveredOffset(row) {
20425
+ if (row.F !== void 0) return row.F;
20426
+ return row.h ? row.s ?? 0 : 0;
20427
+ }
20428
+ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly, nowMs = Date.now()) {
19802
20429
  if (!row) return "full";
19803
20430
  if (row.v < extractorVersion) return "full";
19804
20431
  const offset = row.o ?? 0;
@@ -19806,10 +20433,13 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19806
20433
  if (process.env.CHAT_RECALL_TAIL_APPEND !== "0" && isAppendOnly && fileSize > 0 && offset > 0 && fileSize > (row.s ?? 0)) {
19807
20434
  return "append";
19808
20435
  }
20436
+ if (isAppendOnly && offset > 0 && fileSize > 0 && fileSize === (row.s ?? 0) && !row.k && fullCoveredOffset(row) < offset && nowMs - Math.floor(mtime) >= SETTLE_QUIET_MS) {
20437
+ return "full";
20438
+ }
19809
20439
  if (row.m >= Math.floor(mtime)) return "skip";
19810
20440
  return "full";
19811
20441
  }
19812
- var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
20442
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache, SETTLE_QUIET_MS;
19813
20443
  var init_sync_ledger = __esm({
19814
20444
  "src/sync-ledger.ts"() {
19815
20445
  "use strict";
@@ -19829,6 +20459,7 @@ var init_sync_ledger = __esm({
19829
20459
  unackedCursorAttempts = 0;
19830
20460
  reconcilePath = () => join46(getDataDir(), "sync-field-reconcile.json");
19831
20461
  reconcileCache = null;
20462
+ SETTLE_QUIET_MS = 2 * 60 * 60 * 1e3;
19832
20463
  }
19833
20464
  });
19834
20465
 
@@ -19858,12 +20489,12 @@ var init_sync_fields = __esm({
19858
20489
  });
19859
20490
 
19860
20491
  // ../engine/dist/core/index-lock.js
19861
- import { existsSync as existsSync43, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as statSync41, closeSync as closeSync9 } from "fs";
20492
+ import { existsSync as existsSync44, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as statSync41, closeSync as closeSync9 } from "fs";
19862
20493
  import { dirname as dirname18, join as join47 } from "path";
19863
20494
  function acquireIndexLock(opts) {
19864
20495
  const lockPath = lockFilePath(opts.file);
19865
20496
  mkdirSync11(dirname18(lockPath), { recursive: true });
19866
- if (existsSync43(lockPath)) {
20497
+ if (existsSync44(lockPath)) {
19867
20498
  if (!stealStaleLock(lockPath, opts.staleAfterMs ?? 10 * 6e4)) {
19868
20499
  return null;
19869
20500
  }
@@ -19974,7 +20605,7 @@ var init_http = __esm({
19974
20605
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19975
20606
  } catch {
19976
20607
  try {
19977
- if (true) CLI_VERSION = "0.7.17";
20608
+ if (true) CLI_VERSION = "0.7.19";
19978
20609
  } catch {
19979
20610
  }
19980
20611
  }
@@ -20071,7 +20702,7 @@ var init_derived = __esm({
20071
20702
 
20072
20703
  // src/scan-pool.ts
20073
20704
  import { Worker } from "node:worker_threads";
20074
- import { existsSync as existsSync44 } from "node:fs";
20705
+ import { existsSync as existsSync45 } from "node:fs";
20075
20706
  import { fileURLToPath } from "node:url";
20076
20707
  import { cpus } from "node:os";
20077
20708
  function poolSize() {
@@ -20086,7 +20717,7 @@ function workerScript() {
20086
20717
  for (const rel of ["./scan-worker.js", "../dist/scan-worker.js"]) {
20087
20718
  try {
20088
20719
  const p = fileURLToPath(new URL(rel, import.meta.url));
20089
- if (existsSync44(p)) return p;
20720
+ if (existsSync45(p)) return p;
20090
20721
  } catch {
20091
20722
  }
20092
20723
  }
@@ -20472,7 +21103,7 @@ var init_telemetry = __esm({
20472
21103
  init_sync_client();
20473
21104
  cliVersion2 = () => {
20474
21105
  try {
20475
- return true ? "0.7.17" : "dev";
21106
+ return true ? "0.7.19" : "dev";
20476
21107
  } catch {
20477
21108
  return "dev";
20478
21109
  }
@@ -20482,6 +21113,150 @@ var init_telemetry = __esm({
20482
21113
  }
20483
21114
  });
20484
21115
 
21116
+ // src/raw-archive-parts.ts
21117
+ import { createReadStream as createReadStream4, createWriteStream as createWriteStream2, openSync as openSync10, readSync as readSync8, closeSync as closeSync10, rmSync as rmSync5, statSync as statSync42 } from "node:fs";
21118
+ import { tmpdir as tmpdir3 } from "node:os";
21119
+ import { join as join48 } from "node:path";
21120
+ import { randomBytes } from "node:crypto";
21121
+ import { createGzip } from "node:zlib";
21122
+ import { once } from "node:events";
21123
+ import { readFileSync as readFileSync37 } from "node:fs";
21124
+ async function* fileLines(path2) {
21125
+ let rest = "";
21126
+ const decoder = new TextDecoder("utf-8");
21127
+ for await (const chunk of createReadStream4(path2, { highWaterMark: 1024 * 1024 })) {
21128
+ rest += decoder.decode(chunk, { stream: true });
21129
+ let nl = rest.indexOf("\n");
21130
+ while (nl >= 0) {
21131
+ yield rest.slice(0, nl + 1);
21132
+ rest = rest.slice(nl + 1);
21133
+ nl = rest.indexOf("\n");
21134
+ }
21135
+ }
21136
+ rest += decoder.decode();
21137
+ if (rest) yield rest;
21138
+ }
21139
+ function* textLines(text) {
21140
+ let at = 0;
21141
+ while (at < text.length) {
21142
+ const nl = text.indexOf("\n", at);
21143
+ const end = nl < 0 ? text.length : nl + 1;
21144
+ yield text.slice(at, end);
21145
+ at = end;
21146
+ }
21147
+ }
21148
+ async function writeArchiveFile(source, meta, redact, dir = tmpdir3()) {
21149
+ const path2 = join48(dir, `cr-archive-${randomBytes(8).toString("hex")}.json.gz`);
21150
+ const gzip = createGzip({ level: 6 });
21151
+ const out = createWriteStream2(path2, { mode: 384 });
21152
+ const done = once(out, "finish");
21153
+ gzip.pipe(out);
21154
+ let size = 0;
21155
+ const write = async (s) => {
21156
+ size += Buffer.byteLength(s);
21157
+ if (!gzip.write(s)) await once(gzip, "drain");
21158
+ };
21159
+ try {
21160
+ await write(`{"v":1,"tool":${JSON.stringify(meta.tool)},"mtime":${Math.floor(meta.mtime)},"files":[`);
21161
+ for (const [i, f] of source.files.entries()) {
21162
+ await write(`${i ? "," : ""}{"name":${JSON.stringify(f.name)},"text":"`);
21163
+ const whole = () => source.kind === "memory" ? f.text : readFileSync37(f.path, "utf-8");
21164
+ if (!f.name.endsWith(".jsonl")) {
21165
+ await write(escapeJson(redact(whole())));
21166
+ } else {
21167
+ const lines = source.kind === "memory" ? textLines(f.text) : fileLines(f.path);
21168
+ for await (const line of lines) {
21169
+ const nl = line.endsWith("\n");
21170
+ const body2 = nl ? line.slice(0, -1) : line;
21171
+ await write(escapeJson((body2 ? redactJsonLine(body2, redact) : body2) + (nl ? "\n" : "")));
21172
+ }
21173
+ }
21174
+ await write('"}');
21175
+ }
21176
+ await write("]}");
21177
+ gzip.end();
21178
+ await done;
21179
+ } catch (err) {
21180
+ gzip.destroy();
21181
+ out.destroy();
21182
+ rmSync5(path2, { force: true });
21183
+ throw err;
21184
+ }
21185
+ return { path: path2, gzSize: statSync42(path2).size, size };
21186
+ }
21187
+ async function putPart(url, body2) {
21188
+ let last;
21189
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
21190
+ const ac = new AbortController();
21191
+ const timer = setTimeout(() => ac.abort(), PART_TIMEOUT_MS);
21192
+ try {
21193
+ const r = await fetch(url, { method: "PUT", body: body2, signal: ac.signal });
21194
+ const etag = r.headers.get("etag");
21195
+ if (r.ok && etag) return etag;
21196
+ last = new Error(`part PUT answered ${r.status}`);
21197
+ if (r.status === 403 || r.status === 404) break;
21198
+ } catch (err) {
21199
+ last = err;
21200
+ } finally {
21201
+ clearTimeout(timer);
21202
+ }
21203
+ }
21204
+ throw last instanceof Error ? last : new Error(String(last));
21205
+ }
21206
+ async function uploadArchiveFile(base2, token, args) {
21207
+ const headers = { "content-type": "application/json" };
21208
+ if (token) headers.authorization = `Bearer ${token}`;
21209
+ const post = async (path2, body2) => {
21210
+ const r = await fetch(`${base2}/api/sync/raw-upload/${path2}`, { method: "POST", headers, body: JSON.stringify(body2) });
21211
+ const json = await r.json().catch(() => ({}));
21212
+ return { status: r.status, json };
21213
+ };
21214
+ const start = await post("start", {
21215
+ session_id: args.sessionId,
21216
+ tool: args.tool,
21217
+ mtime: Math.floor(args.mtime),
21218
+ size: args.file.size,
21219
+ gz_size: args.file.gzSize,
21220
+ project_id: args.projectId,
21221
+ project_path: args.projectPath
21222
+ });
21223
+ if (start.status === 501 || start.status === 404) return "unsupported";
21224
+ if (start.status !== 200) throw new Error(`raw upload start answered ${start.status}: ${start.json.error ?? ""}`);
21225
+ if (start.json.result === "unchanged" || start.json.result === "shrink-protected") return start.json.result;
21226
+ const urls = start.json.urls;
21227
+ const partBytes = Number(start.json.part_bytes);
21228
+ const parts = [];
21229
+ const fd = openSync10(args.file.path, "r");
21230
+ try {
21231
+ for (const [i, url] of urls.entries()) {
21232
+ const len = Math.min(partBytes, args.file.gzSize - i * partBytes);
21233
+ const buf = Buffer.allocUnsafe(len);
21234
+ let got = 0;
21235
+ while (got < len) {
21236
+ const n = readSync8(fd, buf, got, len - got, i * partBytes + got);
21237
+ if (n <= 0) break;
21238
+ got += n;
21239
+ }
21240
+ parts.push({ part_number: i + 1, etag: await putPart(url, buf.subarray(0, got)) });
21241
+ }
21242
+ } finally {
21243
+ closeSync10(fd);
21244
+ }
21245
+ const done = await post("complete", { session_id: args.sessionId, upload_id: start.json.upload_id, parts });
21246
+ if (done.status !== 200) throw new Error(`raw upload complete answered ${done.status}: ${done.json.error ?? ""}`);
21247
+ return done.json.result;
21248
+ }
21249
+ var escapeJson, PART_TIMEOUT_MS, PART_ATTEMPTS;
21250
+ var init_raw_archive_parts = __esm({
21251
+ "src/raw-archive-parts.ts"() {
21252
+ "use strict";
21253
+ init_transcript();
21254
+ escapeJson = (s) => JSON.stringify(s).slice(1, -1);
21255
+ PART_TIMEOUT_MS = 12e4;
21256
+ PART_ATTEMPTS = 3;
21257
+ }
21258
+ });
21259
+
20485
21260
  // src/server-probe.ts
20486
21261
  var server_probe_exports = {};
20487
21262
  __export(server_probe_exports, {
@@ -20643,11 +21418,11 @@ __export(auto_update_exports, {
20643
21418
  tarballUrl: () => tarballUrl
20644
21419
  });
20645
21420
  import { execSync as execSync4, execFileSync as execFileSync3 } from "node:child_process";
20646
- import { readFileSync as readFileSync37, existsSync as existsSync45 } from "node:fs";
21421
+ import { readFileSync as readFileSync38, existsSync as existsSync46 } from "node:fs";
20647
21422
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20648
- import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync42, mkdirSync as mkdirSync12 } from "node:fs";
20649
- import { join as join48, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
20650
- import { tmpdir as tmpdir3, homedir as homedir17 } from "node:os";
21423
+ import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync31, statSync as statSync43, mkdirSync as mkdirSync12 } from "node:fs";
21424
+ import { join as join49, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
21425
+ import { tmpdir as tmpdir4, homedir as homedir17 } from "node:os";
20651
21426
  import { createHash as createHash10 } from "node:crypto";
20652
21427
  function compareVersions(a, b) {
20653
21428
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -20671,7 +21446,7 @@ function newerOf(a, b) {
20671
21446
  function installedVersion() {
20672
21447
  try {
20673
21448
  const here = dirname19(fileURLToPath2(import.meta.url));
20674
- const raw = readFileSync37(join48(here, "..", "package.json"), "utf-8");
21449
+ const raw = readFileSync38(join49(here, "..", "package.json"), "utf-8");
20675
21450
  return JSON.parse(raw).version || null;
20676
21451
  } catch {
20677
21452
  return null;
@@ -20684,7 +21459,7 @@ function planAutoUpdate(base2, caps, ownVersion2, flag) {
20684
21459
  if (compareVersions(rel.version, ownVersion2) <= 0) return { update: false, reason: `already current (own ${ownVersion2} >= server ${rel.version})` };
20685
21460
  return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base2, rel.version), fallbackUrl: tarballUrl(base2), sha256: rel.sha256, from: ownVersion2, to: rel.version };
20686
21461
  }
20687
- function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
21462
+ function sweepStaleStaging(root = tmpdir4(), now = Date.now()) {
20688
21463
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
20689
21464
  const COLD_MS = 60 * 60 * 1e3;
20690
21465
  let removed = 0;
@@ -20696,13 +21471,13 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20696
21471
  }
20697
21472
  for (const name of entries) {
20698
21473
  if (!MKDTEMP_SUFFIX.test(name)) continue;
20699
- const dir = join48(root, name);
21474
+ const dir = join49(root, name);
20700
21475
  try {
20701
- const st = statSync42(dir);
21476
+ const st = statSync43(dir);
20702
21477
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20703
21478
  const inside = readdirSync31(dir);
20704
21479
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20705
- rmSync5(dir, { recursive: true, force: true });
21480
+ rmSync6(dir, { recursive: true, force: true });
20706
21481
  removed++;
20707
21482
  } catch {
20708
21483
  }
@@ -20725,14 +21500,14 @@ async function executeAutoUpdate(plan, deps) {
20725
21500
  }
20726
21501
  const got = createHash10("sha256").update(buf).digest("hex");
20727
21502
  if (got !== plan.sha256) return { updated: false, reason: `checksum mismatch (expected ${plan.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026) \u2014 NOT installing` };
20728
- const dir = mkdtempSync3(join48(tmpdir3(), "cr-update-"));
21503
+ const dir = mkdtempSync3(join49(tmpdir4(), "cr-update-"));
20729
21504
  const discardStaging = () => {
20730
21505
  try {
20731
- rmSync5(dir, { recursive: true, force: true });
21506
+ rmSync6(dir, { recursive: true, force: true });
20732
21507
  } catch {
20733
21508
  }
20734
21509
  };
20735
- const tgz = join48(dir, "chat-recall.tgz");
21510
+ const tgz = join49(dir, "chat-recall.tgz");
20736
21511
  writeFileSync9(tgz, buf);
20737
21512
  if (deps.stopping?.()) {
20738
21513
  discardStaging();
@@ -20804,7 +21579,7 @@ function resolveNpm(loc) {
20804
21579
  }
20805
21580
  function realInstall(tgz) {
20806
21581
  const prefix = runningPrefix();
20807
- const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync45 });
21582
+ const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync46 });
20808
21583
  if (prefix) {
20809
21584
  npmInstall(npm, ["install", "-g", "--prefix", prefix, tgz]);
20810
21585
  return;
@@ -20812,7 +21587,7 @@ function realInstall(tgz) {
20812
21587
  try {
20813
21588
  npmInstall(npm, ["install", "-g", tgz]);
20814
21589
  } catch {
20815
- npmInstall(npm, ["install", "-g", "--prefix", join48(homedir17(), ".local"), tgz]);
21590
+ npmInstall(npm, ["install", "-g", "--prefix", join49(homedir17(), ".local"), tgz]);
20816
21591
  }
20817
21592
  }
20818
21593
  function acquireUpdateLock() {
@@ -20829,12 +21604,12 @@ function realRestart(platform2) {
20829
21604
  if (c) execSync4(c.cmd, { stdio: "ignore", ...c.shell ? { shell: c.shell } : {} });
20830
21605
  }
20831
21606
  function updateStatePath() {
20832
- const dir = process.env.CHAT_RECALL_DATA_DIR || join48(homedir17(), ".chat-recall");
20833
- return join48(dir, "auto-update-state.json");
21607
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join49(homedir17(), ".chat-recall");
21608
+ return join49(dir, "auto-update-state.json");
20834
21609
  }
20835
21610
  function readUpdateState() {
20836
21611
  try {
20837
- return JSON.parse(readFileSync37(updateStatePath(), "utf8"));
21612
+ return JSON.parse(readFileSync38(updateStatePath(), "utf8"));
20838
21613
  } catch {
20839
21614
  return {};
20840
21615
  }
@@ -20975,9 +21750,9 @@ __export(sync_client_exports, {
20975
21750
  syncIncremental: () => syncIncremental,
20976
21751
  syncSessions: () => syncSessions
20977
21752
  });
20978
- import { join as join49, dirname as dirname20, basename as basename22 } from "node:path";
20979
- import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync43 } from "node:fs";
20980
- import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
21753
+ import { join as join50, dirname as dirname20, basename as basename22 } from "node:path";
21754
+ import { readFileSync as readFileSync39, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync7, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync44 } from "node:fs";
21755
+ import { tmpdir as tmpdir5, hostname as hostname2 } from "node:os";
20981
21756
  import { createHash as createHash11 } from "node:crypto";
20982
21757
  import { fileURLToPath as fileURLToPath3 } from "node:url";
20983
21758
  import { gzipSync as gzipSync3 } from "zlib";
@@ -21031,7 +21806,7 @@ function loadAllCredentials() {
21031
21806
  const fromEnv = envCredentials();
21032
21807
  if (fromEnv) return [{ serverUrl: fromEnv.serverUrl, token: fromEnv.token }];
21033
21808
  try {
21034
- const parsed = JSON.parse(readFileSync38(credPath(), "utf-8"));
21809
+ const parsed = JSON.parse(readFileSync39(credPath(), "utf-8"));
21035
21810
  if (Array.isArray(parsed?.targets)) return parsed.targets.filter((t) => t?.serverUrl).map((t) => ({ serverUrl: t.serverUrl, token: t.token || "" }));
21036
21811
  if (parsed?.serverUrl) return [{ serverUrl: parsed.serverUrl, token: parsed.token || "" }];
21037
21812
  } catch {
@@ -21093,11 +21868,11 @@ function _resetTenantSecurityConfigCache() {
21093
21868
  _securityConfigCache.clear();
21094
21869
  }
21095
21870
  function syncConfigCachePath() {
21096
- return join49(getDataDir(), "sync-config-cache.json");
21871
+ return join50(getDataDir(), "sync-config-cache.json");
21097
21872
  }
21098
21873
  function readPersistedSyncConfigs() {
21099
21874
  try {
21100
- return JSON.parse(readFileSync38(syncConfigCachePath(), "utf-8"));
21875
+ return JSON.parse(readFileSync39(syncConfigCachePath(), "utf-8"));
21101
21876
  } catch {
21102
21877
  return {};
21103
21878
  }
@@ -21375,7 +22150,7 @@ async function reconcileFields(opts = {}) {
21375
22150
  }
21376
22151
  return { sessions: fullRefs().length, scanned, pushed, absent, perTarget };
21377
22152
  }
21378
- function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now()) {
22153
+ function reapStaleScanDirs(dir = tmpdir5(), maxAgeMs = 6 * 36e5, now = Date.now()) {
21379
22154
  let reaped = 0;
21380
22155
  let names;
21381
22156
  try {
@@ -21385,10 +22160,10 @@ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now(
21385
22160
  }
21386
22161
  for (const name of names) {
21387
22162
  if (!name.startsWith("cr-batchscan-")) continue;
21388
- const p = join49(dir, name);
22163
+ const p = join50(dir, name);
21389
22164
  try {
21390
- if (now - statSync43(p).mtimeMs < maxAgeMs) continue;
21391
- rmSync6(p, { recursive: true, force: true });
22165
+ if (now - statSync44(p).mtimeMs < maxAgeMs) continue;
22166
+ rmSync7(p, { recursive: true, force: true });
21392
22167
  reaped++;
21393
22168
  } catch {
21394
22169
  }
@@ -21402,7 +22177,7 @@ function batchScanExternal(items, opts = {}) {
21402
22177
  let scanned = 0, slices = 0, scanMs = 0;
21403
22178
  if (items.length === 0) return { findings, scanned, slices, scanMs };
21404
22179
  reapStaleScanDirs();
21405
- let scanDir = mkdtempSync4(join49(tmpdir4(), "cr-batchscan-"));
22180
+ let scanDir = mkdtempSync4(join50(tmpdir5(), "cr-batchscan-"));
21406
22181
  let safeToId = /* @__PURE__ */ new Map();
21407
22182
  let bytesInDir = 0;
21408
22183
  const flushSlice = () => {
@@ -21420,7 +22195,7 @@ function batchScanExternal(items, opts = {}) {
21420
22195
  slices++;
21421
22196
  }
21422
22197
  try {
21423
- rmSync6(scanDir, { recursive: true, force: true });
22198
+ rmSync7(scanDir, { recursive: true, force: true });
21424
22199
  } catch {
21425
22200
  }
21426
22201
  safeToId = /* @__PURE__ */ new Map();
@@ -21433,19 +22208,19 @@ function batchScanExternal(items, opts = {}) {
21433
22208
  if (raw === null) continue;
21434
22209
  const safe = item.id.replace(/[^A-Za-z0-9_-]/g, "");
21435
22210
  safeToId.set(safe, item.id);
21436
- writeFileSync10(join49(scanDir, `${safe}.txt`), raw);
22211
+ writeFileSync10(join50(scanDir, `${safe}.txt`), raw);
21437
22212
  bytesInDir += Buffer.byteLength(raw);
21438
22213
  } catch {
21439
22214
  }
21440
22215
  if (bytesInDir >= maxBytes) {
21441
22216
  flushSlice();
21442
- scanDir = mkdtempSync4(join49(tmpdir4(), "cr-batchscan-"));
22217
+ scanDir = mkdtempSync4(join50(tmpdir5(), "cr-batchscan-"));
21443
22218
  }
21444
22219
  }
21445
22220
  flushSlice();
21446
22221
  } finally {
21447
22222
  try {
21448
- rmSync6(scanDir, { recursive: true, force: true });
22223
+ rmSync7(scanDir, { recursive: true, force: true });
21449
22224
  } catch {
21450
22225
  }
21451
22226
  }
@@ -21657,6 +22432,11 @@ async function syncToTargetInScope(cred, opts = {}) {
21657
22432
  };
21658
22433
  const localSessionIds = /* @__PURE__ */ new Set();
21659
22434
  const shippedHash = /* @__PURE__ */ new Map();
22435
+ const shippedLayout = /* @__PURE__ */ new Map();
22436
+ const layoutOf = (id) => {
22437
+ const layout = shippedLayout.get(id);
22438
+ return layout ? { layout } : {};
22439
+ };
21660
22440
  const makeBatcher = (field, maxItems, flatten = false, onFlush, barrier) => {
21661
22441
  let batch = [];
21662
22442
  let batchBytes = 0;
@@ -21689,12 +22469,13 @@ async function syncToTargetInScope(cred, opts = {}) {
21689
22469
  markSynced(base2, rows.map((r) => {
21690
22470
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
21691
22471
  const hash = shippedHash.get(r.session_id);
21692
- return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true } : { id: r.session_id, mtime: r.mtime, hash };
22472
+ return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true, full: true, ...layoutOf(r.session_id) } : { id: r.session_id, mtime: r.mtime, hash };
21693
22473
  }));
21694
22474
  } catch {
21695
22475
  }
21696
22476
  });
21697
22477
  const appendResults = { uploaded: 0, fullResyncNeeded: /* @__PURE__ */ new Set() };
22478
+ const pendingArchives = [];
21698
22479
  const postAppend = async (convs) => {
21699
22480
  const headers = { "content-type": "application/json" };
21700
22481
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -21729,8 +22510,11 @@ async function syncToTargetInScope(cred, opts = {}) {
21729
22510
  offset: c.from_offset,
21730
22511
  size: c.from_offset,
21731
22512
  // size = new offset (file grew to here)
21732
- acked: true
22513
+ acked: true,
21733
22514
  // server merged this tail
22515
+ chunkFinal: c.final === true,
22516
+ // completes a chunked full sync
22517
+ ...layoutOf(c.session_id)
21734
22518
  })));
21735
22519
  } catch {
21736
22520
  }
@@ -21762,9 +22546,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21762
22546
  const mtime = conv.mtime;
21763
22547
  try {
21764
22548
  if (staged || !guarded) {
21765
- markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
22549
+ markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true, ...layoutOf(id) }]);
21766
22550
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21767
- markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
22551
+ markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true, ...layoutOf(id) }]);
21768
22552
  } else {
21769
22553
  markSynced(base2, [{ id, mtime }]);
21770
22554
  }
@@ -21859,8 +22643,8 @@ async function syncToTargetInScope(cred, opts = {}) {
21859
22643
  if (!ledger) return ref.mtime >= (opts.sinceMs ?? 0) ? "full" : "skip";
21860
22644
  const ack = ledger.get(ref.prefixedId);
21861
22645
  const backend = backendFor(ref);
21862
- const split = !!backend?.spansMultipleSources?.(ref.prefixedId);
21863
- const isAO = !!backend?.isAppendOnly?.() && !split;
22646
+ const sameLayout = (ack?.L ?? "") === (backend?.sourceLayout?.(ref.prefixedId) ?? "");
22647
+ const isAO = !!backend?.isAppendOnly?.() && sameLayout;
21864
22648
  const size = isAO ? backend?.fileSize?.(ref.prefixedId) ?? 0 : 0;
21865
22649
  return syncMode(ack, ref.mtime, size, extractorVersionForTool(ref.toolId), isAO);
21866
22650
  };
@@ -21907,6 +22691,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21907
22691
  skipped++;
21908
22692
  continue;
21909
22693
  }
22694
+ const layout = backendFor(ref)?.sourceLayout?.(ref.prefixedId) ?? "";
22695
+ if (layout) shippedLayout.set(ref.prefixedId, layout);
22696
+ else shippedLayout.delete(ref.prefixedId);
21910
22697
  trace?.(`build (${mode}) ${ref.prefixedId} \xB7 ${(sessionFileBytes(ref) / 1048576).toFixed(1)}MB`);
21911
22698
  let promotedForFindings = false;
21912
22699
  if (mode === "append" && ledger) {
@@ -21952,7 +22739,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21952
22739
  const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
21953
22740
  const priorCursor = ackRow?.s ?? 0;
21954
22741
  const safeToAck = off > 0 && off <= priorCursor;
21955
- markSynced(base2, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
22742
+ markSynced(base2, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true, ...layoutOf(ref.prefixedId) } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21956
22743
  } catch {
21957
22744
  }
21958
22745
  }
@@ -21973,6 +22760,14 @@ async function syncToTargetInScope(cred, opts = {}) {
21973
22760
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
21974
22761
  if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
21975
22762
  else await convBatch.add(built.conv);
22763
+ if (built.archive) {
22764
+ pendingArchives.push({
22765
+ id: ref.prefixedId,
22766
+ ...built.archive,
22767
+ projectId: built.conv.project_id,
22768
+ projectPath: built.conv.project_path
22769
+ });
22770
+ }
21976
22771
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
21977
22772
  if (upload.sessionMeta) {
21978
22773
  await accumulateKg(extractEntities(built.kgText, { projectPath: built.projectPath, sourceType: "session", sessionId: ref.prefixedId, validFrom: ref.mtime ? new Date(ref.mtime).toISOString().slice(0, 10) : void 0 }), ref.prefixedId);
@@ -22125,6 +22920,30 @@ async function syncToTargetInScope(cred, opts = {}) {
22125
22920
  await kgTripleBatch.drain();
22126
22921
  await findingsBatch.drain();
22127
22922
  await drainInflight();
22923
+ let archivesUnsupported = false;
22924
+ for (const a of pendingArchives) {
22925
+ try {
22926
+ if (!archivesUnsupported) {
22927
+ const r = await uploadArchiveFile(base2, cred.token, {
22928
+ sessionId: a.id,
22929
+ tool: a.tool,
22930
+ mtime: a.mtime,
22931
+ projectId: a.projectId,
22932
+ projectPath: a.projectPath,
22933
+ file: a.file
22934
+ });
22935
+ if (r === "unsupported") archivesUnsupported = true;
22936
+ }
22937
+ } catch (err) {
22938
+ console.error(`[sync] raw archive for ${a.id} not uploaded (tried again when the session is next quiet): ${err instanceof Error ? err.message : err}`);
22939
+ try {
22940
+ forgetFullCoverage(base2, a.id);
22941
+ } catch {
22942
+ }
22943
+ } finally {
22944
+ rmSync7(a.file.path, { force: true });
22945
+ }
22946
+ }
22128
22947
  {
22129
22948
  const sentHashes = loadInventoryHashes(base2);
22130
22949
  const entries = [];
@@ -22230,6 +23049,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22230
23049
  let includeRaw = opts.includeRaw !== false;
22231
23050
  let includeMeta = opts.includeMeta !== false;
22232
23051
  let srcHash;
23052
+ const wantRaw = opts.includeRaw !== false;
22233
23053
  const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
22234
23054
  let headOffsetEnd = 0;
22235
23055
  let oversizedFindings = null;
@@ -22398,7 +23218,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22398
23218
  } else if (opts.scanSecrets) {
22399
23219
  let tmp = null;
22400
23220
  try {
22401
- tmp = join49(tmpdir4(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
23221
+ tmp = join50(tmpdir5(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
22402
23222
  writeFileSync10(tmp, rawText);
22403
23223
  for (const f of scanFileForSecrets(tmp, { verifyOnly: opts.verifySecrets, tenantRules: opts.tenantRules })) {
22404
23224
  const verifiedAt = f.verified ? (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -22450,6 +23270,25 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22450
23270
  }
22451
23271
  const envTexts = envelope.messages.filter((m) => m.content?.trim());
22452
23272
  const redactedText = envTexts.map((m) => m.content).join("\n");
23273
+ let archive;
23274
+ if (wantRaw && !raw_b64) {
23275
+ try {
23276
+ const redact = (t) => redactSecrets(t, { force: true, count });
23277
+ let source = null;
23278
+ let archiveMtime = Math.floor(ref.mtime) || 0;
23279
+ let archiveTool = ref.toolId;
23280
+ if (container) {
23281
+ source = { kind: "memory", files: container.files };
23282
+ archiveMtime = container.mtime;
23283
+ archiveTool = container.tool;
23284
+ } else if (oversized) {
23285
+ source = diskArchiveSource(ref);
23286
+ }
23287
+ if (source) archive = { file: await writeArchiveFile(source, { tool: archiveTool, mtime: archiveMtime }, redact), tool: archiveTool, mtime: archiveMtime };
23288
+ } catch (err) {
23289
+ console.error(`[sync] raw archive for ${ref.prefixedId} not written: ${err instanceof Error ? err.message : err}`);
23290
+ }
23291
+ }
22453
23292
  return {
22454
23293
  conv: {
22455
23294
  session_id: ref.prefixedId,
@@ -22481,7 +23320,17 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22481
23320
  redactions: count.redactions,
22482
23321
  findings,
22483
23322
  scanMs,
22484
- srcHash
23323
+ srcHash,
23324
+ archive
23325
+ };
23326
+ }
23327
+ function diskArchiveSource(ref) {
23328
+ if (ref.toolId !== "claude") return null;
23329
+ const groups = resolveSessionContentGroups(ref.rawId);
23330
+ if (groups.length === 0 || groups.some((g) => g.paths.length !== 1)) return null;
23331
+ return {
23332
+ kind: "disk",
23333
+ files: groups.map((g) => ({ name: g.name === "main" ? `${ref.rawId}.jsonl` : g.name, path: g.paths[0] }))
22485
23334
  };
22486
23335
  }
22487
23336
  function parseTailMessages(tool, text) {
@@ -22637,14 +23486,15 @@ var init_sync_client = __esm({
22637
23486
  init_telemetry();
22638
23487
  init_collector_health();
22639
23488
  init_live_session_scan();
23489
+ init_raw_archive_parts();
22640
23490
  init_backends();
22641
23491
  ownVersion = "0.0.0";
22642
23492
  try {
22643
23493
  const dir = dirname20(fileURLToPath3(import.meta.url));
22644
- ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
23494
+ ownVersion = JSON.parse(readFileSync39(join50(dir, "../package.json"), "utf-8")).version || ownVersion;
22645
23495
  } catch {
22646
23496
  try {
22647
- if (true) ownVersion = "0.7.17";
23497
+ if (true) ownVersion = "0.7.19";
22648
23498
  } catch {
22649
23499
  }
22650
23500
  }
@@ -22656,7 +23506,7 @@ var init_sync_client = __esm({
22656
23506
  ]);
22657
23507
  walkProgressForUpload = null;
22658
23508
  lastServerPackSpec = null;
22659
- credPath = () => join49(getDataDir(), "credentials.json");
23509
+ credPath = () => join50(getDataDir(), "credentials.json");
22660
23510
  _securityConfigCache = /* @__PURE__ */ new Map();
22661
23511
  SECURITY_CONFIG_TTL_MS = 5 * 60 * 1e3;
22662
23512
  _syncConfigCache = /* @__PURE__ */ new Map();
@@ -22685,7 +23535,7 @@ __export(client_events_exports, {
22685
23535
  isTelemetryEnabled: () => isTelemetryEnabled,
22686
23536
  reportClientEvent: () => reportClientEvent2
22687
23537
  });
22688
- import { readFileSync as readFileSync39 } from "node:fs";
23538
+ import { readFileSync as readFileSync40 } from "node:fs";
22689
23539
  function scrub(msg) {
22690
23540
  if (!msg) return "";
22691
23541
  const noHome = msg.replace(/\/(?:home|Users)\/[^/\s]+/g, "~");
@@ -22738,7 +23588,7 @@ var init_client_events = __esm({
22738
23588
  init_secret_redactor();
22739
23589
  CLI_VERSION2 = "0.0.0";
22740
23590
  try {
22741
- CLI_VERSION2 = JSON.parse(readFileSync39(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
23591
+ CLI_VERSION2 = JSON.parse(readFileSync40(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
22742
23592
  } catch {
22743
23593
  }
22744
23594
  }
@@ -22752,15 +23602,15 @@ __export(update_notice_exports, {
22752
23602
  refreshUpdateCheck: () => refreshUpdateCheck,
22753
23603
  updateNotice: () => updateNotice2
22754
23604
  });
22755
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync11 } from "node:fs";
22756
- import { join as join50 } from "node:path";
23605
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync11 } from "node:fs";
23606
+ import { join as join51 } from "node:path";
22757
23607
  function newestKnown(c) {
22758
23608
  const npm = c.npmVersion ?? "0.0.0";
22759
23609
  return compareVersions(npm, c.serverVersion) > 0 ? { version: npm, source: "npm" } : { version: c.serverVersion, source: "server" };
22760
23610
  }
22761
23611
  function readCheck() {
22762
23612
  try {
22763
- const c = JSON.parse(readFileSync40(checkPath(), "utf-8"));
23613
+ const c = JSON.parse(readFileSync41(checkPath(), "utf-8"));
22764
23614
  return c && typeof c.serverVersion === "string" ? c : null;
22765
23615
  } catch {
22766
23616
  return null;
@@ -22854,7 +23704,7 @@ var init_update_notice = __esm({
22854
23704
  init_http();
22855
23705
  CHECK_TTL_MS = 6 * 3600 * 1e3;
22856
23706
  NPM_LATEST_URL = "https://registry.npmjs.org/chat-recall";
22857
- checkPath = () => join50(getDataDir(), "update-check.json");
23707
+ checkPath = () => join51(getDataDir(), "update-check.json");
22858
23708
  printed = false;
22859
23709
  }
22860
23710
  });
@@ -22926,15 +23776,15 @@ __export(service_installer_exports, {
22926
23776
  uninstallService: () => uninstallService
22927
23777
  });
22928
23778
  import { execSync as execSync5 } from "child_process";
22929
- import { existsSync as existsSync46, mkdirSync as mkdirSync14, readFileSync as readFileSync41, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
23779
+ import { existsSync as existsSync47, mkdirSync as mkdirSync14, readFileSync as readFileSync42, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "fs";
22930
23780
  import { homedir as homedir18 } from "os";
22931
- import { join as join51 } from "path";
23781
+ import { join as join52 } from "path";
22932
23782
  import { fileURLToPath as fileURLToPath4 } from "url";
22933
23783
  function resolveDaemonPaths() {
22934
23784
  const watchJs = fileURLToPath4(new URL("./watch.js", import.meta.url));
22935
23785
  const node = process.execPath;
22936
- const dataDir = join51(homedir18(), ".chat-recall");
22937
- const logFile = join51(dataDir, "watch.log");
23786
+ const dataDir = join52(homedir18(), ".chat-recall");
23787
+ const logFile = join52(dataDir, "watch.log");
22938
23788
  return { watchJs, node, logFile, dataDir };
22939
23789
  }
22940
23790
  function tryCmd(cmd) {
@@ -22961,12 +23811,12 @@ function refreshServiceDefinition() {
22961
23811
  if (process.platform !== "linux") return false;
22962
23812
  try {
22963
23813
  const { watchJs, node, logFile } = resolveDaemonPaths();
22964
- const unitPath = join51(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
22965
- if (!existsSync46(unitPath)) return false;
22966
- const current = readFileSync41(unitPath, "utf8");
23814
+ const unitPath = join52(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23815
+ if (!existsSync47(unitPath)) return false;
23816
+ const current = readFileSync42(unitPath, "utf8");
22967
23817
  if (!current.includes(watchJs)) return false;
22968
23818
  const wanted = renderSystemdUnit(watchJs, node, logFile);
22969
- if (readFileSync41(unitPath, "utf8") === wanted) return false;
23819
+ if (readFileSync42(unitPath, "utf8") === wanted) return false;
22970
23820
  writeFileSync12(unitPath, wanted);
22971
23821
  execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
22972
23822
  return true;
@@ -22992,8 +23842,8 @@ function installService() {
22992
23842
  );
22993
23843
  }
22994
23844
  function installSystemd(watchJs, node, logFile) {
22995
- const unitDir = join51(homedir18(), ".config", "systemd", "user");
22996
- const unitPath = join51(unitDir, "chat-recall-watch.service");
23845
+ const unitDir = join52(homedir18(), ".config", "systemd", "user");
23846
+ const unitPath = join52(unitDir, "chat-recall-watch.service");
22997
23847
  const unit = renderSystemdUnit(watchJs, node, logFile);
22998
23848
  mkdirSync14(unitDir, { recursive: true });
22999
23849
  writeFileSync12(unitPath, unit);
@@ -23072,8 +23922,8 @@ function renderSystemdUnit(watchJs, node, logFile) {
23072
23922
  }
23073
23923
  function installLaunchd(watchJs, node, logFile) {
23074
23924
  const label = "com.chat-recall.watch";
23075
- const plistDir = join51(homedir18(), "Library", "LaunchAgents");
23076
- const plistPath = join51(plistDir, `${label}.plist`);
23925
+ const plistDir = join52(homedir18(), "Library", "LaunchAgents");
23926
+ const plistPath = join52(plistDir, `${label}.plist`);
23077
23927
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
23078
23928
  mkdirSync14(plistDir, { recursive: true });
23079
23929
  writeFileSync12(plistPath, plist);
@@ -23129,9 +23979,9 @@ function uninstallService() {
23129
23979
  removed = true;
23130
23980
  } catch {
23131
23981
  }
23132
- const unitPath = join51(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23133
- if (existsSync46(unitPath)) {
23134
- rmSync7(unitPath, { force: true });
23982
+ const unitPath = join52(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23983
+ if (existsSync47(unitPath)) {
23984
+ rmSync8(unitPath, { force: true });
23135
23985
  removed = true;
23136
23986
  }
23137
23987
  try {
@@ -23141,14 +23991,14 @@ function uninstallService() {
23141
23991
  return removed;
23142
23992
  }
23143
23993
  if (process.platform === "darwin") {
23144
- const plistPath = join51(homedir18(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23994
+ const plistPath = join52(homedir18(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23145
23995
  try {
23146
23996
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
23147
23997
  } catch {
23148
23998
  }
23149
23999
  let removed = false;
23150
- if (existsSync46(plistPath)) {
23151
- rmSync7(plistPath, { force: true });
24000
+ if (existsSync47(plistPath)) {
24001
+ rmSync8(plistPath, { force: true });
23152
24002
  removed = true;
23153
24003
  }
23154
24004
  return removed;
@@ -23194,15 +24044,15 @@ __export(install_skills_exports, {
23194
24044
  skillsSourceDir: () => skillsSourceDir,
23195
24045
  uninstallSkills: () => uninstallSkills
23196
24046
  });
23197
- import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync44, realpathSync as realpathSync4 } from "fs";
24047
+ import { readdirSync as readdirSync33, existsSync as existsSync48, mkdirSync as mkdirSync15, rmSync as rmSync9, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync43, statSync as statSync45, realpathSync as realpathSync4 } from "fs";
23198
24048
  import { createHash as createHash12 } from "crypto";
23199
- import { join as join52, basename as basename23 } from "path";
24049
+ import { join as join53, basename as basename23 } from "path";
23200
24050
  function skillsSourceDir() {
23201
- return join52(import.meta.dirname, "..", "skills");
24051
+ return join53(import.meta.dirname, "..", "skills");
23202
24052
  }
23203
24053
  function version() {
23204
24054
  try {
23205
- return JSON.parse(readFileSync42(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
24055
+ return JSON.parse(readFileSync43(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
23206
24056
  } catch {
23207
24057
  return "0.0.0";
23208
24058
  }
@@ -23211,9 +24061,9 @@ function skillsFingerprint() {
23211
24061
  const src = skillsSourceDir();
23212
24062
  const h = createHash12("sha256");
23213
24063
  for (const name of bundledSkillNames()) {
23214
- for (const rel of filesUnder(join52(src, name)).sort()) {
24064
+ for (const rel of filesUnder(join53(src, name)).sort()) {
23215
24065
  h.update(name + "/" + rel);
23216
- h.update(readFileSync42(join52(src, name, rel)));
24066
+ h.update(readFileSync43(join53(src, name, rel)));
23217
24067
  }
23218
24068
  }
23219
24069
  return h.digest("hex").slice(0, 12);
@@ -23223,7 +24073,7 @@ function filesUnder(dir, prefix = "") {
23223
24073
  for (const e of readdirSync33(dir, { withFileTypes: true })) {
23224
24074
  if (e.name === MARKER) continue;
23225
24075
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23226
- if (e.isDirectory()) out.push(...filesUnder(join52(dir, e.name), rel));
24076
+ if (e.isDirectory()) out.push(...filesUnder(join53(dir, e.name), rel));
23227
24077
  else out.push(rel);
23228
24078
  }
23229
24079
  return out;
@@ -23233,8 +24083,8 @@ function skillStamp() {
23233
24083
  }
23234
24084
  function bundledSkillNames() {
23235
24085
  const src = skillsSourceDir();
23236
- if (!existsSync47(src)) return [];
23237
- return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join52(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
24086
+ if (!existsSync48(src)) return [];
24087
+ return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync48(join53(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23238
24088
  }
23239
24089
  function realPathOrSelf(p) {
23240
24090
  try {
@@ -23247,7 +24097,7 @@ function claudeSkillTargets() {
23247
24097
  const targets = [];
23248
24098
  const seen = /* @__PURE__ */ new Set();
23249
24099
  for (const home of claudeHomeDirs()) {
23250
- const dir = join52(home, "skills");
24100
+ const dir = join53(home, "skills");
23251
24101
  const key2 = realPathOrSelf(dir);
23252
24102
  if (seen.has(key2)) continue;
23253
24103
  seen.add(key2);
@@ -23258,7 +24108,7 @@ function claudeSkillTargets() {
23258
24108
  dir,
23259
24109
  // Judge each profile on ITS OWN transcripts, not the primary's: a home
23260
24110
  // with no projects/ is a directory someone made, not a Claude install.
23261
- available: existsSync47(join52(home, "projects"))
24111
+ available: existsSync48(join53(home, "projects"))
23262
24112
  });
23263
24113
  }
23264
24114
  if (targets.length === 0) {
@@ -23273,16 +24123,16 @@ function skillTargets() {
23273
24123
  // both produces "skill conflict" duplicate warnings. Every tool we support
23274
24124
  // has its own dir, so per-tool coverage is complete.
23275
24125
  ...claudeSkillTargets(),
23276
- { id: "agy", label: "Antigravity", dir: join52(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
24126
+ { id: "agy", label: "Antigravity", dir: join53(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
23277
24127
  { id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
23278
24128
  { id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
23279
24129
  // Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
23280
24130
  // it is Cursor's bundled set — read-only, never an install target.
23281
- { id: "cursor", label: "Cursor", dir: join52(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
24131
+ { id: "cursor", label: "Cursor", dir: join53(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
23282
24132
  ];
23283
24133
  }
23284
24134
  function isManaged(dir) {
23285
- return existsSync47(join52(dir, MARKER));
24135
+ return existsSync48(join53(dir, MARKER));
23286
24136
  }
23287
24137
  function installSkills(opts = {}) {
23288
24138
  const onlyAvailable = opts.onlyAvailable ?? true;
@@ -23299,15 +24149,15 @@ function installSkills(opts = {}) {
23299
24149
  continue;
23300
24150
  }
23301
24151
  for (const name of names) {
23302
- const dest = join52(t.dir, name);
23303
- if (existsSync47(dest) && !isManaged(dest)) {
24152
+ const dest = join53(t.dir, name);
24153
+ if (existsSync48(dest) && !isManaged(dest)) {
23304
24154
  skippedUserOwned.push(name);
23305
24155
  continue;
23306
24156
  }
23307
24157
  mkdirSync15(t.dir, { recursive: true });
23308
- rmSync8(dest, { recursive: true, force: true });
23309
- cpSync(join52(src, name), dest, { recursive: true });
23310
- writeFileSync13(join52(dest, MARKER), `${stamp}
24158
+ rmSync9(dest, { recursive: true, force: true });
24159
+ cpSync(join53(src, name), dest, { recursive: true });
24160
+ writeFileSync13(join53(dest, MARKER), `${stamp}
23311
24161
  `);
23312
24162
  installed.push(name);
23313
24163
  }
@@ -23320,15 +24170,15 @@ function uninstallSkills() {
23320
24170
  const perTarget = [];
23321
24171
  for (const t of skillTargets()) {
23322
24172
  const removed = [];
23323
- if (!existsSync47(t.dir)) {
24173
+ if (!existsSync48(t.dir)) {
23324
24174
  perTarget.push({ id: t.id, label: t.label, removed });
23325
24175
  continue;
23326
24176
  }
23327
24177
  for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
23328
24178
  if (!entry.isDirectory()) continue;
23329
- const dir = join52(t.dir, entry.name);
24179
+ const dir = join53(t.dir, entry.name);
23330
24180
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
23331
- rmSync8(dir, { recursive: true, force: true });
24181
+ rmSync9(dir, { recursive: true, force: true });
23332
24182
  removed.push(entry.name);
23333
24183
  }
23334
24184
  }
@@ -23343,10 +24193,10 @@ function skillsNeedRefresh() {
23343
24193
  for (const t of skillTargets()) {
23344
24194
  if (!t.available) continue;
23345
24195
  for (const name of names) {
23346
- const marker = join52(t.dir, name, MARKER);
23347
- if (!existsSync47(marker)) return true;
24196
+ const marker = join53(t.dir, name, MARKER);
24197
+ if (!existsSync48(marker)) return true;
23348
24198
  try {
23349
- if (readFileSync42(marker, "utf-8").trim() !== cur) return true;
24199
+ if (readFileSync43(marker, "utf-8").trim() !== cur) return true;
23350
24200
  } catch {
23351
24201
  return true;
23352
24202
  }
@@ -23361,11 +24211,11 @@ function skillStatus() {
23361
24211
  let installed = 0;
23362
24212
  let ver = null;
23363
24213
  for (const name of names) {
23364
- const marker = join52(t.dir, name, MARKER);
23365
- if (existsSync47(marker)) {
24214
+ const marker = join53(t.dir, name, MARKER);
24215
+ if (existsSync48(marker)) {
23366
24216
  installed++;
23367
24217
  try {
23368
- ver = readFileSync42(marker, "utf-8").trim();
24218
+ ver = readFileSync43(marker, "utf-8").trim();
23369
24219
  } catch {
23370
24220
  }
23371
24221
  }
@@ -23622,6 +24472,7 @@ var SESSION_ID_PREFIXES = {
23622
24472
  opencode: "opencode_",
23623
24473
  agy: "agy_",
23624
24474
  cursor: "cursor_",
24475
+ hermes: "hermes_",
23625
24476
  gemini: "gemini_"
23626
24477
  };
23627
24478
  function toolOfSessionId(sessionId) {
@@ -23650,6 +24501,8 @@ function resumeCommandFor(sessionId, tool) {
23650
24501
  // available answer for either.
23651
24502
  case "cursor":
23652
24503
  return `cursor-agent --resume ${raw("cursor_")}`;
24504
+ case "hermes":
24505
+ return `hermes --resume ${raw("hermes_")}`;
23653
24506
  // Gemini's `--resume` takes an index or "latest", never a session id.
23654
24507
  default:
23655
24508
  return null;
@@ -24639,7 +25492,7 @@ function isMultiTenant() {
24639
25492
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
24640
25493
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
24641
25494
  import { z } from "zod";
24642
- import { existsSync as existsSync22, readFileSync as readFileSync18, statSync as statSync21 } from "fs";
25495
+ import { existsSync as existsSync23, readFileSync as readFileSync18, statSync as statSync21 } from "fs";
24643
25496
  import { join as join27 } from "path";
24644
25497
 
24645
25498
  // ../engine/dist/core/context.js
@@ -24787,7 +25640,7 @@ function sanitizeQuery(query) {
24787
25640
 
24788
25641
  // ../engine/dist/core/write-ahead-log.js
24789
25642
  init_paths();
24790
- import { existsSync as existsSync17, mkdirSync as mkdirSync5, appendFileSync, openSync as openSync8, closeSync as closeSync8, chmodSync as chmodSync3 } from "fs";
25643
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, appendFileSync, openSync as openSync8, closeSync as closeSync8, chmodSync as chmodSync3 } from "fs";
24791
25644
  import { join as join20 } from "path";
24792
25645
  var REDACT_KEYS = /* @__PURE__ */ new Set([
24793
25646
  "content",
@@ -24804,7 +25657,7 @@ var WriteAheadLog = class {
24804
25657
  walPath;
24805
25658
  constructor(walDir) {
24806
25659
  const dir = walDir || getWalDir();
24807
- if (!existsSync17(dir)) {
25660
+ if (!existsSync18(dir)) {
24808
25661
  mkdirSync5(dir, { recursive: true });
24809
25662
  try {
24810
25663
  chmodSync3(dir, 448);
@@ -24812,7 +25665,7 @@ var WriteAheadLog = class {
24812
25665
  }
24813
25666
  }
24814
25667
  this.walPath = join20(dir, "write_log.jsonl");
24815
- if (!existsSync17(this.walPath)) {
25668
+ if (!existsSync18(this.walPath)) {
24816
25669
  const fd = openSync8(this.walPath, "w", 384);
24817
25670
  closeSync8(fd);
24818
25671
  }
@@ -25173,7 +26026,7 @@ var RecallEditsTimelineSchema = z.object({
25173
26026
  pattern: z.string().optional().describe("Optional substring filter on file path"),
25174
26027
  project_filter: z.string().optional().describe("Filter by project name (matched against the encoded project directory name)"),
25175
26028
  include_reads: z.boolean().optional().default(false).describe("Include read-type tool calls in addition to write/edit ones"),
25176
- tools: z.array(z.enum(["claude", "opencode", "codex", "agy", "cursor"])).optional().describe("Restrict to a subset of AI tools. Default: every tool this machine has."),
26029
+ tools: z.array(z.enum(["claude", "opencode", "codex", "agy", "cursor", "hermes"])).optional().describe("Restrict to a subset of AI tools. Default: every tool this machine has."),
25177
26030
  group_by_repo: z.boolean().optional().default(false).describe("Group output by detected git repo root instead of returning a flat list. Useful when a single session touched multiple repos."),
25178
26031
  view: z.enum(["timeline", "summary"]).optional().default("timeline").describe("timeline = the chronological edit rows; summary = an aggregated rollup over the same window"),
25179
26032
  group_by: z.enum(["session"]).optional().describe('"session" = aggregate edits per session instead of a flat timeline \u2014 answers "which sessions touched files matching X?" (pass `pattern` + a wide `since_hours`, e.g. 720 for 30 days).')
@@ -25885,7 +26738,7 @@ aggregate per session instead \u2014 "which sessions edited auth.rs in the last
25885
26738
  include_reads: { type: "boolean", default: false, description: "Include Read tool_uses too" },
25886
26739
  tools: {
25887
26740
  type: "array",
25888
- items: { type: "string", enum: ["claude", "opencode", "codex", "agy", "cursor"] },
26741
+ items: { type: "string", enum: ["claude", "opencode", "codex", "agy", "cursor", "hermes"] },
25889
26742
  description: "Restrict to specific AI tools. Default: every tool this machine has."
25890
26743
  },
25891
26744
  group_by_repo: { type: "boolean", default: false, description: "Group results by detected git repo root." },
@@ -27846,7 +28699,7 @@ _Edits stayed local or window was off \u2014 try increasing buffer_minutes._` }]
27846
28699
  }
27847
28700
  case "recall_project_context": {
27848
28701
  const params = RecallProjectContextSchema.parse(args);
27849
- const target = existsSync22(params.project_path) && statSync21(params.project_path).isDirectory() ? resolveProjectId(params.project_path).id : params.project_path;
28702
+ const target = existsSync23(params.project_path) && statSync21(params.project_path).isDirectory() ? resolveProjectId(params.project_path).id : params.project_path;
27850
28703
  const dossier = await remoteGetQS(`/api/projects/${encodeURIComponent(target)}/dossier`, { sessions: params.limit, tasks: params.tasks, plans: params.plans });
27851
28704
  return { content: [{ type: "text", text: dossier.markdown }] };
27852
28705
  }
@@ -28806,7 +29659,7 @@ Subject: ${params.subject}`;
28806
29659
  const start = resolve2(p.path ?? process.cwd());
28807
29660
  let root = start;
28808
29661
  for (let dir = start, last = ""; dir && dir !== last; last = dir, dir = resolve2(dir, "..")) {
28809
- if (existsSync22(join27(dir, ".git"))) {
29662
+ if (existsSync23(join27(dir, ".git"))) {
28810
29663
  root = dir;
28811
29664
  break;
28812
29665
  }
@@ -28963,7 +29816,7 @@ ${row.value}`
28963
29816
  let identity = params.identity ?? "AI coding assistant";
28964
29817
  if (!params.identity && !isMultiTenant()) {
28965
29818
  const idFile = getIdentityFilePath();
28966
- if (existsSync22(idFile)) {
29819
+ if (existsSync23(idFile)) {
28967
29820
  try {
28968
29821
  identity = readFileSync18(idFile, "utf-8").trim() || identity;
28969
29822
  } catch {
@@ -29460,9 +30313,9 @@ function openBrowser(url) {
29460
30313
  // src/mcp.ts
29461
30314
  init_client_events();
29462
30315
  init_update_notice();
29463
- import { readFileSync as readFileSync43 } from "node:fs";
30316
+ import { readFileSync as readFileSync44 } from "node:fs";
29464
30317
  setServerVersion(JSON.parse(
29465
- readFileSync43(new URL("../package.json", import.meta.url), "utf-8")
30318
+ readFileSync44(new URL("../package.json", import.meta.url), "utf-8")
29466
30319
  ).version);
29467
30320
  setEventReporter(reportClientEvent2);
29468
30321
  setIndexRunner(runIndexChild2);
@@ -29579,9 +30432,9 @@ var SYNC_TICK_MS = 3 * 6e4;
29579
30432
  function startBackgroundSync() {
29580
30433
  const tick = async (scope) => {
29581
30434
  try {
29582
- const { installedVersion: installedVersion2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
30435
+ const { installedVersion: installedVersion3 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
29583
30436
  const { installedIsNewer: installedIsNewer2, runInstalledSync: runInstalledSync2 } = await Promise.resolve().then(() => (init_sync_delegate(), sync_delegate_exports));
29584
- const installed = installedVersion2();
30437
+ const installed = installedVersion3();
29585
30438
  if (installedIsNewer2(RELAY_VERSION, installed)) {
29586
30439
  const code = await runInstalledSync2(fileURLToPath5(new URL("./cli.js", import.meta.url)));
29587
30440
  if (code !== 0) console.error(`[mcp] sync by the installed CLI ${installed} ended with ${code ?? "no exit code"}`);
@@ -29635,7 +30488,7 @@ function installCrashGuards() {
29635
30488
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29636
30489
  });
29637
30490
  }
29638
- var RELAY_VERSION = true ? "0.7.17" : "0.0.0";
30491
+ var RELAY_VERSION = true ? "0.7.19" : "0.0.0";
29639
30492
  function daemonIdleSecs() {
29640
30493
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29641
30494
  if (raw === void 0) return 900;
@@ -29731,8 +30584,25 @@ async function runDaemon() {
29731
30584
  };
29732
30585
  process.on("SIGTERM", shutdown);
29733
30586
  process.on("SIGINT", shutdown);
30587
+ const upgradeTimer = setInterval(() => {
30588
+ const installed = installedVersion2();
30589
+ if (installed && RELAY_VERSION !== "0.0.0" && installed !== RELAY_VERSION) {
30590
+ console.error(`[mcp] v${installed} is installed; daemon v${RELAY_VERSION} is leaving`);
30591
+ clearInterval(upgradeTimer);
30592
+ shutdown();
30593
+ }
30594
+ }, 3e4);
30595
+ upgradeTimer.unref();
29734
30596
  return true;
29735
30597
  }
30598
+ function installedVersion2() {
30599
+ try {
30600
+ const pkg = JSON.parse(readFileSync44(new URL("../package.json", import.meta.url), "utf-8"));
30601
+ return typeof pkg.version === "string" ? pkg.version : null;
30602
+ } catch {
30603
+ return null;
30604
+ }
30605
+ }
29736
30606
  async function main() {
29737
30607
  installCrashGuards();
29738
30608
  if (process.argv.includes("--daemon")) {