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/cli.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: join62 } = __require("path");
2037
+ var { join: join63 } = __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"] || join62(__dirname, "lib", "worker.js");
2080
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join63(__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: join62, isAbsolute: isAbsolute4, sep: sep4 } = __require("node:path");
2483
+ var { join: join63, isAbsolute: isAbsolute4, sep: sep4 } = __require("node:path");
2484
2484
  var sleep2 = 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"] || join62(__dirname, "worker.js");
2546
+ target = bundlerOverrides["pino-worker"] || join63(__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"] || join62(__dirname, "worker.js");
2564
+ target = bundlerOverrides["pino-worker"] || join63(__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 join62(__dirname, "..", "file.js");
2586
+ return join63(__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 join62 = ",";
3575
+ let join63 = ",";
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
- join62 = `,
3589
+ join63 = `,
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 += join62;
3597
+ res += join63;
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 += `${join62}"... ${getItemCount(removedKeys)} not stringified"`;
3603
+ res += `${join63}"... ${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
- join62 = `,
3624
+ join63 = `,
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 = join62;
3638
+ separator = join63;
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 = join62;
3644
+ separator = join63;
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 join62 = ",";
3685
+ let join63 = ",";
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
- join62 = `,
3698
+ join63 = `,
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 += join62;
3706
+ res += join63;
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 += `${join62}"... ${getItemCount(removedKeys)} not stringified"`;
3712
+ res += `${join63}"... ${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
- join62 = `,
3725
+ join63 = `,
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 = join62;
3734
+ separator = join63;
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 join63 = `,
3792
+ const join64 = `,
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 += join63;
3799
+ res2 += join64;
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 += `${join63}"... ${getItemCount(removedKeys)} not stringified"`;
3805
+ res2 += `${join64}"... ${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 join62 = `,
3821
+ const join63 = `,
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, join62, maximumBreadth);
3827
+ res += stringifyTypedArray(value, join63, maximumBreadth);
3828
3828
  keys = keys.slice(value.length);
3829
3829
  maximumPropertiesToStringify -= value.length;
3830
- separator = join62;
3830
+ separator = join63;
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 = join62;
3841
+ separator = join63;
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 = join62;
3847
+ separator = join63;
3848
3848
  }
3849
3849
  if (separator !== "") {
3850
3850
  res = `
@@ -4586,6 +4586,8 @@ function declaredOrigin(value) {
4586
4586
  function userRecordOrigin(obj, text) {
4587
4587
  if (obj.isSidechain === true)
4588
4588
  return "subagent";
4589
+ if (obj.isCompactSummary === true)
4590
+ return "compact-summary";
4589
4591
  const declared = declaredOrigin(obj.origin);
4590
4592
  if (declared && declared !== "human")
4591
4593
  return declared;
@@ -4769,6 +4771,7 @@ function defaultSourcesEnabled() {
4769
4771
  commands: true
4770
4772
  },
4771
4773
  cursor: { sessions: true, skills: true, agents: true, commands: true },
4774
+ hermes: { sessions: true },
4772
4775
  shared: { skills: true },
4773
4776
  common: { mcps: true, agentMd: true }
4774
4777
  };
@@ -4845,6 +4848,7 @@ function mergeSources(base4, partial) {
4845
4848
  codex: { ...base4.enabled.codex, ...partial.enabled?.codex ?? {} },
4846
4849
  agy: { ...base4.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
4847
4850
  cursor: { ...base4.enabled.cursor, ...partial.enabled?.cursor ?? {} },
4851
+ hermes: { ...base4.enabled.hermes, ...partial.enabled?.hermes ?? {} },
4848
4852
  shared: { ...base4.enabled.shared, ...partial.enabled?.shared ?? {} },
4849
4853
  common: { ...base4.enabled.common, ...partial.enabled?.common ?? {} }
4850
4854
  };
@@ -4856,6 +4860,7 @@ function mergeSources(base4, partial) {
4856
4860
  agyHome: partial.agyHome ?? base4.agyHome,
4857
4861
  cursorHome: partial.cursorHome ?? base4.cursorHome,
4858
4862
  cursorIdeHome: partial.cursorIdeHome ?? base4.cursorIdeHome,
4863
+ hermesHome: partial.hermesHome ?? base4.hermesHome,
4859
4864
  extraClaudeHomes: partial.extraClaudeHomes ?? base4.extraClaudeHomes,
4860
4865
  approvedHomes: partial.approvedHomes ?? base4.approvedHomes,
4861
4866
  declinedHomes: partial.declinedHomes ?? base4.declinedHomes,
@@ -5180,6 +5185,7 @@ var home_approval_exports = {};
5180
5185
  __export(home_approval_exports, {
5181
5186
  approveHome: () => approveHome,
5182
5187
  declineHome: () => declineHome,
5188
+ defaultHermesHome: () => defaultHermesHome,
5183
5189
  grandfatherLegacyHomes: () => grandfatherLegacyHomes,
5184
5190
  homeDecision: () => homeDecision,
5185
5191
  isHomeSynced: () => isHomeSynced,
@@ -5262,6 +5268,13 @@ function grandfatherLegacyHomes() {
5262
5268
  }
5263
5269
  return { seeded };
5264
5270
  }
5271
+ function defaultHermesHome() {
5272
+ if (process.platform === "win32") {
5273
+ const local = process.env.LOCALAPPDATA?.trim() || join3(homedir2(), "AppData", "Local");
5274
+ return join3(local, "hermes");
5275
+ }
5276
+ return join3(homedir2(), ".hermes");
5277
+ }
5265
5278
  function primaryHomes() {
5266
5279
  const home = homedir2();
5267
5280
  const env = process.env;
@@ -5278,7 +5291,8 @@ function primaryHomes() {
5278
5291
  env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
5279
5292
  env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
5280
5293
  env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
5281
- dirOf(opencodeDb)
5294
+ dirOf(opencodeDb),
5295
+ env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome()
5282
5296
  ];
5283
5297
  return candidates2.map(normalizeHomePath);
5284
5298
  }
@@ -5405,6 +5419,9 @@ function geminiHomeDir() {
5405
5419
  function opencodeDbPath() {
5406
5420
  return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
5407
5421
  }
5422
+ function hermesHomeDir() {
5423
+ return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
5424
+ }
5408
5425
  function codexHomeDir() {
5409
5426
  return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
5410
5427
  }
@@ -5506,6 +5523,22 @@ function opencodeDbPaths(opts = {}) {
5506
5523
  dbs.push(primary);
5507
5524
  return applyExclusions(dbs, opts.includeExcluded);
5508
5525
  }
5526
+ function hermesDbPaths(opts = {}) {
5527
+ const root = hermesHomeDir();
5528
+ const dbs = [join4(root, "state.db")];
5529
+ try {
5530
+ const profiles = join4(root, "profiles");
5531
+ for (const entry of readdirSync3(profiles, { withFileTypes: true })) {
5532
+ if (!entry.isDirectory())
5533
+ continue;
5534
+ const p = join4(profiles, entry.name, "state.db");
5535
+ if (existsSync4(p))
5536
+ dbs.push(p);
5537
+ }
5538
+ } catch {
5539
+ }
5540
+ return applyExclusions(dbs, opts.includeExcluded);
5541
+ }
5509
5542
  function extraClaudeHomeDirs() {
5510
5543
  const extras = sources().extraClaudeHomes ?? [];
5511
5544
  const primary = claudeHomeDir();
@@ -6574,7 +6607,7 @@ function canonicalEventsToMessages(events) {
6574
6607
  switch (e.kind) {
6575
6608
  case "user":
6576
6609
  cur = null;
6577
- messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso });
6610
+ messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso, ...e.origin ? { origin: e.origin } : {} });
6578
6611
  break;
6579
6612
  case "assistant_text":
6580
6613
  cur = { line: e.line, role: "assistant", content: e.text ?? "", timestamp: e.tsIso };
@@ -6617,6 +6650,7 @@ var init_from_events = __esm({
6617
6650
  // ../engine/dist/transcript/raw.js
6618
6651
  var raw_exports = {};
6619
6652
  __export(raw_exports, {
6653
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
6620
6654
  archiveRawSession: () => archiveRawSession,
6621
6655
  buildRawContainer: () => buildRawContainer,
6622
6656
  containerSrcHash: () => containerSrcHash,
@@ -6916,11 +6950,11 @@ async function archiveRawSession(store, sessionId) {
6916
6950
  return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
6917
6951
  }
6918
6952
  function detectForkPredecessor(sessionPath) {
6919
- const { readFileSync: readFileSync50, readdirSync: readdirSync34 } = requireFs();
6920
- const { dirname: dirname30, join: join62, basename: basename28 } = requirePath();
6953
+ const { readFileSync: readFileSync51, readdirSync: readdirSync34 } = requireFs();
6954
+ const { dirname: dirname30, join: join63, basename: basename28 } = requirePath();
6921
6955
  let text;
6922
6956
  try {
6923
- text = readFileSync50(sessionPath, "utf-8");
6957
+ text = readFileSync51(sessionPath, "utf-8");
6924
6958
  } catch {
6925
6959
  return null;
6926
6960
  }
@@ -6954,7 +6988,7 @@ function detectForkPredecessor(sessionPath) {
6954
6988
  if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
6955
6989
  continue;
6956
6990
  try {
6957
- if (readFileSync50(join62(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6991
+ if (readFileSync51(join63(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6958
6992
  return basename28(f, ".jsonl");
6959
6993
  }
6960
6994
  } catch {
@@ -6968,7 +7002,7 @@ function requireFs() {
6968
7002
  function requirePath() {
6969
7003
  return pathMod;
6970
7004
  }
6971
- var MARKER_QUOTE;
7005
+ var RAW_PARSE_MAX_BYTES, MARKER_QUOTE;
6972
7006
  var init_raw = __esm({
6973
7007
  "../engine/dist/transcript/raw.js"() {
6974
7008
  "use strict";
@@ -6976,6 +7010,7 @@ var init_raw = __esm({
6976
7010
  init_claude();
6977
7011
  init_codex();
6978
7012
  init_from_events();
7013
+ RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
6979
7014
  MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
6980
7015
  }
6981
7016
  });
@@ -8457,6 +8492,71 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8457
8492
  }
8458
8493
  }
8459
8494
  }
8495
+ function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8496
+ let start = 0;
8497
+ let linesBefore = 0;
8498
+ for (let i = 0; i < paths.length; i++) {
8499
+ const path = paths[i];
8500
+ let size = 0;
8501
+ try {
8502
+ size = statSync4(path).size;
8503
+ } catch {
8504
+ size = 0;
8505
+ }
8506
+ const last = i === paths.length - 1;
8507
+ if (!last && offset >= start + size) {
8508
+ const lines = linesBeforeOffset(path, size);
8509
+ if (lines === void 0)
8510
+ return { text: "", newOffset: offset };
8511
+ linesBefore += lines + (size > 0 && !endsWithNewline(path, size) ? 1 : 0);
8512
+ start += size;
8513
+ continue;
8514
+ }
8515
+ const local = offset - start;
8516
+ const read = readTailFromOffset(path, local, maxBytes);
8517
+ const shift = (r) => ({
8518
+ ...r,
8519
+ newOffset: start + r.newOffset,
8520
+ ...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
8521
+ });
8522
+ if (last || read.newOffset > local || local >= size)
8523
+ return shift(read);
8524
+ if (size - local > maxBytes)
8525
+ return { text: "", newOffset: start + size, skippedBytes: size - local };
8526
+ let fd = null;
8527
+ try {
8528
+ fd = openSync2(path, "r");
8529
+ const text = readAt(fd, local, size - local).toString("utf-8");
8530
+ return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path, local) });
8531
+ } catch {
8532
+ return { text: "", newOffset: offset };
8533
+ } finally {
8534
+ if (fd !== null) {
8535
+ try {
8536
+ closeSync2(fd);
8537
+ } catch {
8538
+ }
8539
+ }
8540
+ }
8541
+ }
8542
+ return { text: "", newOffset: offset };
8543
+ }
8544
+ function endsWithNewline(path, size) {
8545
+ let fd = null;
8546
+ try {
8547
+ fd = openSync2(path, "r");
8548
+ return readAt(fd, size - 1, 1)[0] === 10;
8549
+ } catch {
8550
+ return true;
8551
+ } finally {
8552
+ if (fd !== null) {
8553
+ try {
8554
+ closeSync2(fd);
8555
+ } catch {
8556
+ }
8557
+ }
8558
+ }
8559
+ }
8460
8560
  function findNewline(fd, from, size) {
8461
8561
  const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
8462
8562
  let pos = from;
@@ -10935,6 +11035,473 @@ var init_cursor = __esm({
10935
11035
  }
10936
11036
  });
10937
11037
 
11038
+ // ../engine/dist/core/backends/hermes.js
11039
+ import { existsSync as existsSync12 } from "fs";
11040
+ function firstUserPrompt(db, sessionId) {
11041
+ try {
11042
+ const row = db.prepare(`
11043
+ SELECT substr(content, 1, 400) AS text FROM messages
11044
+ WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
11045
+ AND (display_kind IS NULL OR display_kind = 'steer')
11046
+ AND length(trim(content)) > 0
11047
+ ORDER BY id LIMIT 1
11048
+ `).get(sessionId);
11049
+ return row?.text ? flatString(row.text.slice(0, 200)) : "";
11050
+ } catch {
11051
+ return "";
11052
+ }
11053
+ }
11054
+ function messageRowsToEvents(rows) {
11055
+ const events = [];
11056
+ let line = 0;
11057
+ for (const r of rows) {
11058
+ line++;
11059
+ if (r.active === 0)
11060
+ continue;
11061
+ if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
11062
+ continue;
11063
+ const ts = Math.round((r.timestamp || 0) * 1e3);
11064
+ const tsIso = ts ? new Date(ts).toISOString() : void 0;
11065
+ const text = (r.content ?? "").trim();
11066
+ if (r._compressed_summary === 1) {
11067
+ if (text)
11068
+ events.push({ kind: "summary", ts, tsIso, line, text });
11069
+ continue;
11070
+ }
11071
+ if (r.role === "user") {
11072
+ if (!text)
11073
+ continue;
11074
+ events.push({
11075
+ kind: "user",
11076
+ ts,
11077
+ tsIso,
11078
+ line,
11079
+ text,
11080
+ ...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
11081
+ });
11082
+ continue;
11083
+ }
11084
+ if (r.role === "assistant") {
11085
+ if (text)
11086
+ events.push({ kind: "assistant_text", ts, tsIso, line, text });
11087
+ for (const call2 of parseToolCalls(r.tool_calls)) {
11088
+ const toolName = call2.function?.name ?? "";
11089
+ const toolInput = parseArguments(call2.function?.arguments);
11090
+ const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
11091
+ events.push({
11092
+ kind: "tool_use",
11093
+ ts,
11094
+ tsIso,
11095
+ line,
11096
+ toolName,
11097
+ toolUseId: call2.id || call2.call_id || `${r.id}:${toolName}`,
11098
+ toolInput,
11099
+ ...command ? { command } : {}
11100
+ });
11101
+ }
11102
+ continue;
11103
+ }
11104
+ if (r.role === "tool") {
11105
+ const body2 = r.content ?? "";
11106
+ const { exitCode, isError } = toolResultStatus(body2);
11107
+ events.push({
11108
+ kind: "tool_result",
11109
+ ts,
11110
+ tsIso,
11111
+ line,
11112
+ toolUseId: r.tool_call_id ?? void 0,
11113
+ toolName: r.tool_name ?? void 0,
11114
+ resultBody: body2,
11115
+ resultIsError: isError,
11116
+ ...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
11117
+ resultBytes: Buffer.byteLength(body2)
11118
+ });
11119
+ }
11120
+ }
11121
+ return events;
11122
+ }
11123
+ function parseToolCalls(json) {
11124
+ if (!json)
11125
+ return [];
11126
+ try {
11127
+ const calls = JSON.parse(json);
11128
+ return Array.isArray(calls) ? calls : [];
11129
+ } catch {
11130
+ return [];
11131
+ }
11132
+ }
11133
+ function parseArguments(args) {
11134
+ if (typeof args !== "string")
11135
+ return args ?? {};
11136
+ try {
11137
+ return JSON.parse(args);
11138
+ } catch {
11139
+ return { raw: args };
11140
+ }
11141
+ }
11142
+ function toolResultStatus(body2) {
11143
+ if (!body2.startsWith("{"))
11144
+ return { isError: false };
11145
+ try {
11146
+ const r = JSON.parse(body2);
11147
+ const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
11148
+ const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
11149
+ return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
11150
+ } catch {
11151
+ return { isError: false };
11152
+ }
11153
+ }
11154
+ var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
11155
+ var init_hermes = __esm({
11156
+ "../engine/dist/core/backends/hermes.js"() {
11157
+ "use strict";
11158
+ init_sqlite_reader();
11159
+ init_tool_paths();
11160
+ init_session_outcome();
11161
+ init_session_git();
11162
+ init_generic_engine();
11163
+ init_flat_string();
11164
+ PREFIX5 = "hermes_";
11165
+ MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
11166
+ ARCHIVED_SESSION_COLUMNS = [
11167
+ "id",
11168
+ "source",
11169
+ "title",
11170
+ "model",
11171
+ "cwd",
11172
+ "git_branch",
11173
+ "git_repo_root",
11174
+ "parent_session_id",
11175
+ "started_at",
11176
+ "ended_at",
11177
+ "end_reason",
11178
+ "message_count",
11179
+ "tool_call_count",
11180
+ "input_tokens",
11181
+ "output_tokens",
11182
+ "cache_read_tokens",
11183
+ "cache_write_tokens",
11184
+ "reasoning_tokens",
11185
+ "estimated_cost_usd",
11186
+ "actual_cost_usd"
11187
+ ];
11188
+ SESSION_MTIME_SQL = `MAX(
11189
+ s.started_at,
11190
+ COALESCE(s.ended_at, 0),
11191
+ COALESCE(s.last_activity_at, 0),
11192
+ COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
11193
+ )`;
11194
+ HermesBackend = class {
11195
+ id = "hermes";
11196
+ idPrefix = PREFIX5;
11197
+ displayName = "Hermes";
11198
+ homeDir() {
11199
+ return hermesHomeDir();
11200
+ }
11201
+ /** Every Hermes database on this machine, the root profile first. */
11202
+ dbPaths() {
11203
+ return hermesDbPaths();
11204
+ }
11205
+ isAvailable() {
11206
+ return this.dbPaths().some((p) => existsSync12(p));
11207
+ }
11208
+ /** The database that holds this session. A session id is unique within a
11209
+ * database, and each named profile has its own. */
11210
+ dbPathFor(rawId) {
11211
+ const cached = this._dbForSession.get(rawId);
11212
+ if (cached)
11213
+ return cached;
11214
+ for (const path of this.dbPaths()) {
11215
+ const db = openSqliteReadonly(path);
11216
+ if (!db)
11217
+ continue;
11218
+ try {
11219
+ if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
11220
+ this._dbForSession.set(rawId, path);
11221
+ return path;
11222
+ }
11223
+ } catch {
11224
+ } finally {
11225
+ db.close();
11226
+ }
11227
+ }
11228
+ return null;
11229
+ }
11230
+ _dbForSession = /* @__PURE__ */ new Map();
11231
+ /** Tests, and anything that adds a profile mid-process. */
11232
+ _clearDbRouting() {
11233
+ this._dbForSession.clear();
11234
+ }
11235
+ // ── ID handling ────────────────────────────────────────────────
11236
+ matchesId(id) {
11237
+ return id.startsWith(PREFIX5);
11238
+ }
11239
+ toRawId(id) {
11240
+ return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
11241
+ }
11242
+ toPrefixedId(rawId) {
11243
+ return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
11244
+ }
11245
+ // ── Location ───────────────────────────────────────────────────
11246
+ findSession(id) {
11247
+ const rawId = this.toRawId(id);
11248
+ const path = this.dbPathFor(rawId);
11249
+ if (!path)
11250
+ return null;
11251
+ const db = openSqliteReadonly(path);
11252
+ if (!db)
11253
+ return null;
11254
+ try {
11255
+ const row = db.prepare(`
11256
+ SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
11257
+ FROM sessions s WHERE s.id = ?
11258
+ `).get(rawId);
11259
+ if (!row)
11260
+ return null;
11261
+ return {
11262
+ path,
11263
+ format: "sqlite",
11264
+ projectDir: "",
11265
+ projectPath: row.git_repo_root || row.cwd || "",
11266
+ mtime: Math.round((row.mtime || 0) * 1e3)
11267
+ };
11268
+ } catch {
11269
+ return null;
11270
+ } finally {
11271
+ db.close();
11272
+ }
11273
+ }
11274
+ listSessions(opts = {}) {
11275
+ const byId = /* @__PURE__ */ new Map();
11276
+ for (const dbPath of this.dbPaths()) {
11277
+ if (!existsSync12(dbPath))
11278
+ continue;
11279
+ for (const ref of this.listSessionsInDb(dbPath, opts)) {
11280
+ const prior = byId.get(ref.rawId);
11281
+ if (!prior || ref.mtime > prior.mtime)
11282
+ byId.set(ref.rawId, ref);
11283
+ }
11284
+ }
11285
+ const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
11286
+ return opts.limit ? all.slice(0, opts.limit) : all;
11287
+ }
11288
+ listSessionsInDb(dbPath, opts) {
11289
+ const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
11290
+ const filter = opts.projectFilter?.toLowerCase();
11291
+ const db = openSqliteReadonly(dbPath);
11292
+ if (!db)
11293
+ return [];
11294
+ try {
11295
+ const rows = db.prepare(`
11296
+ SELECT * FROM (
11297
+ SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
11298
+ ${SESSION_MTIME_SQL} AS mtime
11299
+ FROM sessions s
11300
+ ) WHERE mtime >= ? ORDER BY mtime DESC
11301
+ `).all(cutoffSec);
11302
+ const out = [];
11303
+ for (const row of rows) {
11304
+ const projectPath = row.git_repo_root || row.cwd || "";
11305
+ if (filter && !projectPath.toLowerCase().includes(filter))
11306
+ continue;
11307
+ const mtime = Math.round(row.mtime * 1e3);
11308
+ this._dbForSession.set(row.id, dbPath);
11309
+ out.push({
11310
+ toolId: "hermes",
11311
+ rawId: row.id,
11312
+ prefixedId: this.toPrefixedId(row.id),
11313
+ projectPath,
11314
+ projectDir: "",
11315
+ fullPath: dbPath,
11316
+ created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
11317
+ modified: new Date(mtime).toISOString(),
11318
+ mtime,
11319
+ firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
11320
+ messageCount: opts.previews === false ? 0 : row.message_count ?? 0
11321
+ });
11322
+ if (opts.limit && out.length >= opts.limit)
11323
+ break;
11324
+ }
11325
+ return out;
11326
+ } catch {
11327
+ return [];
11328
+ } finally {
11329
+ db.close();
11330
+ }
11331
+ }
11332
+ /** Hermes writes a title for each session (a model-written one, or the
11333
+ * first prompt for a one-shot run). */
11334
+ getNativeTitle(rawId) {
11335
+ const path = this.dbPathFor(this.toRawId(rawId));
11336
+ if (!path)
11337
+ return null;
11338
+ const db = openSqliteReadonly(path);
11339
+ if (!db)
11340
+ return null;
11341
+ try {
11342
+ const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
11343
+ const t = row?.title?.trim();
11344
+ return t ? t.slice(0, 200) : null;
11345
+ } catch {
11346
+ return null;
11347
+ } finally {
11348
+ db.close();
11349
+ }
11350
+ }
11351
+ // ── Generic-engine inputs ───────────────────────────────────────
11352
+ fileToolMap = {
11353
+ write_file: "write",
11354
+ patch: "edit",
11355
+ read_file: "read"
11356
+ };
11357
+ /**
11358
+ * `write_file` carries `{ path, content }`. `patch` carries
11359
+ * `{ mode: 'replace', path, old_string, new_string }`; its other mode,
11360
+ * `patch`, is a multi-file V4A patch with no single before and after.
11361
+ */
11362
+ extractEditDelta(toolName, input) {
11363
+ if (input == null || typeof input !== "object")
11364
+ return null;
11365
+ const inp = input;
11366
+ if (toolName === "write_file") {
11367
+ return { before: "", after: typeof inp.content === "string" ? inp.content : null };
11368
+ }
11369
+ if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
11370
+ const before = typeof inp.old_string === "string" ? inp.old_string : null;
11371
+ const after = typeof inp.new_string === "string" ? inp.new_string : null;
11372
+ if (before === null && after === null)
11373
+ return null;
11374
+ return { before, after };
11375
+ }
11376
+ return null;
11377
+ }
11378
+ readEvents(rawId) {
11379
+ const id = this.toRawId(rawId);
11380
+ const path = this.dbPathFor(id);
11381
+ if (!path)
11382
+ return [];
11383
+ const db = openSqliteReadonly(path);
11384
+ if (!db)
11385
+ return [];
11386
+ try {
11387
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
11388
+ return messageRowsToEvents(rows);
11389
+ } catch {
11390
+ return [];
11391
+ } finally {
11392
+ db.close();
11393
+ }
11394
+ }
11395
+ /**
11396
+ * Events from the dump `exportRawSession` writes, for a session rebuilt from
11397
+ * the server's archive. The archive joins the lines of every dump it got, so
11398
+ * a row that changed (a rewind sets `active` to 0) is there more than once.
11399
+ * The last copy of each row id is the newest.
11400
+ */
11401
+ readEventsFromText(text) {
11402
+ const rows = /* @__PURE__ */ new Map();
11403
+ for (const line of text.split("\n")) {
11404
+ if (!line.trim())
11405
+ continue;
11406
+ try {
11407
+ const rec = JSON.parse(line);
11408
+ if (rec.kind === "message" && rec.row) {
11409
+ rows.delete(rec.row.id);
11410
+ rows.set(rec.row.id, rec.row);
11411
+ }
11412
+ } catch {
11413
+ }
11414
+ }
11415
+ return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
11416
+ }
11417
+ // ── Per-session operations — all delegate to the generic engine ─
11418
+ extractTurns(id, opts = {}) {
11419
+ return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
11420
+ }
11421
+ liveScanEdits(id) {
11422
+ const located = this.findSession(id);
11423
+ if (!located) {
11424
+ return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
11425
+ }
11426
+ return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
11427
+ sessionId: this.toPrefixedId(id),
11428
+ tool: "hermes",
11429
+ projectPath: located.projectPath,
11430
+ projectDir: located.projectDir,
11431
+ fileMtime: located.mtime,
11432
+ found: true
11433
+ });
11434
+ }
11435
+ replay(id) {
11436
+ const located = this.findSession(id);
11437
+ if (!located) {
11438
+ return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
11439
+ }
11440
+ return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
11441
+ projectPath: located.projectPath,
11442
+ found: true
11443
+ });
11444
+ }
11445
+ computeOutcome(id, opts) {
11446
+ return computeOutcome(this.toPrefixedId(id), opts);
11447
+ }
11448
+ /** File edits since `sinceMs` across the sessions active since then. */
11449
+ collectRecentEdits(opts) {
11450
+ const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
11451
+ const edits = [];
11452
+ for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
11453
+ for (const e of this.liveScanEdits(ref.rawId).edits) {
11454
+ if (e.ts >= opts.sinceMs)
11455
+ edits.push(e);
11456
+ }
11457
+ }
11458
+ return edits.sort((a, b) => b.ts - a.ts);
11459
+ }
11460
+ getCommits(id, files, startMs, endMs, bufferMinutes) {
11461
+ return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
11462
+ }
11463
+ /**
11464
+ * The session row and its messages as JSONL, one row per line in id order.
11465
+ * Hermes rewrites and compacts its database, so the archive keeps the rows
11466
+ * as they were synced.
11467
+ */
11468
+ exportRawSession(id) {
11469
+ const rawId = this.toRawId(id);
11470
+ const path = this.dbPathFor(rawId);
11471
+ if (!path)
11472
+ return null;
11473
+ const db = openSqliteReadonly(path);
11474
+ if (!db)
11475
+ return null;
11476
+ try {
11477
+ const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
11478
+ const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
11479
+ const session = db.prepare(`
11480
+ SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
11481
+ FROM sessions s WHERE s.id = ?
11482
+ `).get(rawId);
11483
+ if (!session)
11484
+ return null;
11485
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
11486
+ const lines = [JSON.stringify({ kind: "session", row: session })];
11487
+ for (const r of rows)
11488
+ lines.push(JSON.stringify({ kind: "message", row: r }));
11489
+ return {
11490
+ tool: "hermes",
11491
+ mtime: Math.round(Number(session.mtime || 0) * 1e3),
11492
+ files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
11493
+ };
11494
+ } catch {
11495
+ return null;
11496
+ } finally {
11497
+ db.close();
11498
+ }
11499
+ }
11500
+ };
11501
+ hermesBackend = new HermesBackend();
11502
+ }
11503
+ });
11504
+
10938
11505
  // ../engine/dist/core/backends/index.js
10939
11506
  var backends_exports = {};
10940
11507
  __export(backends_exports, {
@@ -10942,6 +11509,7 @@ __export(backends_exports, {
10942
11509
  ClaudeBackend: () => ClaudeBackend,
10943
11510
  CodexBackend: () => CodexBackend,
10944
11511
  CursorBackend: () => CursorBackend,
11512
+ HermesBackend: () => HermesBackend,
10945
11513
  OpencodeBackend: () => OpencodeBackend,
10946
11514
  agyBackend: () => agyBackend,
10947
11515
  bootstrapBackends: () => bootstrapBackends,
@@ -10950,6 +11518,7 @@ __export(backends_exports, {
10950
11518
  cursorBackend: () => cursorBackend,
10951
11519
  getBackend: () => getBackend,
10952
11520
  getBackendForId: () => getBackendForId,
11521
+ hermesBackend: () => hermesBackend,
10953
11522
  listAllBackends: () => listAllBackends,
10954
11523
  listAvailableBackends: () => listAvailableBackends,
10955
11524
  opencodeBackend: () => opencodeBackend,
@@ -10962,6 +11531,7 @@ function bootstrapBackends() {
10962
11531
  registerBackend(codexBackend);
10963
11532
  registerBackend(agyBackend);
10964
11533
  registerBackend(cursorBackend);
11534
+ registerBackend(hermesBackend);
10965
11535
  }
10966
11536
  var init_backends = __esm({
10967
11537
  "../engine/dist/core/backends/index.js"() {
@@ -10972,6 +11542,7 @@ var init_backends = __esm({
10972
11542
  init_codex2();
10973
11543
  init_agy();
10974
11544
  init_cursor();
11545
+ init_hermes();
10975
11546
  init_tool_backend();
10976
11547
  _setRegistryBootstrapper(bootstrapBackends);
10977
11548
  }
@@ -10980,7 +11551,7 @@ var init_backends = __esm({
10980
11551
  // ../engine/dist/transcript/shadow.js
10981
11552
  import { gzipSync as gzipSync2, gunzipSync as gunzipSync2 } from "zlib";
10982
11553
  import { createHash as createHash2 } from "crypto";
10983
- import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync10, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
11554
+ import { existsSync as existsSync13, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync10, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
10984
11555
  import { dirname as dirname12, join as join14 } from "path";
10985
11556
  function shadowRoot() {
10986
11557
  return join14(getDataDir(), "shadow");
@@ -10991,7 +11562,7 @@ function shadowFileFor(tool, rawId) {
10991
11562
  }
10992
11563
  function readShadowContainer(tool, rawId) {
10993
11564
  const path = shadowFileFor(tool, rawId);
10994
- if (!existsSync12(path))
11565
+ if (!existsSync13(path))
10995
11566
  return null;
10996
11567
  try {
10997
11568
  const c = JSON.parse(gunzipSync2(readFileSync8(path)).toString("utf-8"));
@@ -11249,7 +11820,7 @@ var init_shadow = __esm({
11249
11820
  });
11250
11821
 
11251
11822
  // ../engine/dist/core/live-session-scan.js
11252
- import { existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
11823
+ import { existsSync as existsSync14, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
11253
11824
  import { join as join15, basename as basename9 } from "path";
11254
11825
  function detectTool(sessionId) {
11255
11826
  return getBackendForId(sessionId)?.id ?? "claude";
@@ -11306,7 +11877,7 @@ function sessionFileIndex() {
11306
11877
  return p;
11307
11878
  };
11308
11879
  for (const root of roots) {
11309
- if (seenRoots.has(root) || !existsSync13(root))
11880
+ if (seenRoots.has(root) || !existsSync14(root))
11310
11881
  continue;
11311
11882
  seenRoots.add(root);
11312
11883
  dirs.set(root, dirMtime(root));
@@ -11432,7 +12003,7 @@ function resolveSessionContentPaths(sessionFile) {
11432
12003
  if (!hasSubagentsDir(sessionFile))
11433
12004
  return [sessionFile];
11434
12005
  const subDir = join15(sessionFile.slice(0, -6), "subagents");
11435
- if (!existsSync13(subDir))
12006
+ if (!existsSync14(subDir))
11436
12007
  return [sessionFile];
11437
12008
  const subPaths = readdirSync10(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join15(subDir, f));
11438
12009
  return [sessionFile, ...subPaths];
@@ -11449,7 +12020,7 @@ function findCodexSessionFile(sessionId) {
11449
12020
  return null;
11450
12021
  }
11451
12022
  function findCodexSessionFileIn(root, sessionId) {
11452
- if (!existsSync13(root))
12023
+ if (!existsSync14(root))
11453
12024
  return null;
11454
12025
  for (const year of readdirSync10(root, { withFileTypes: true })) {
11455
12026
  if (!year.isDirectory())
@@ -11532,7 +12103,7 @@ var init_live_session_scan = __esm({
11532
12103
  });
11533
12104
 
11534
12105
  // ../engine/dist/core/first-prompt.js
11535
- import { createReadStream as createReadStream2, existsSync as existsSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
12106
+ import { createReadStream as createReadStream2, existsSync as existsSync15, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
11536
12107
  import { StringDecoder } from "string_decoder";
11537
12108
  function stripBanners2(s) {
11538
12109
  let out = s;
@@ -11643,7 +12214,7 @@ var init_first_prompt = __esm({
11643
12214
  });
11644
12215
 
11645
12216
  // ../engine/dist/core/backends/claude.js
11646
- import { existsSync as existsSync15, readdirSync as readdirSync11, readFileSync as readFileSync10, statSync as statSync12, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
12217
+ import { existsSync as existsSync16, readdirSync as readdirSync11, readFileSync as readFileSync10, statSync as statSync12, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
11647
12218
  import { join as join16, basename as basename10 } from "path";
11648
12219
  function stripReminders(text) {
11649
12220
  return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
@@ -11713,7 +12284,7 @@ var init_claude2 = __esm({
11713
12284
  return join16(this.homeDir(), "history.jsonl");
11714
12285
  }
11715
12286
  isAvailable() {
11716
- return existsSync15(this.projectsDir());
12287
+ return existsSync16(this.projectsDir());
11717
12288
  }
11718
12289
  // ── ID handling ────────────────────────────────────────────────
11719
12290
  /**
@@ -11819,7 +12390,7 @@ var init_claude2 = __esm({
11819
12390
  prior.modified = ref.modified;
11820
12391
  }
11821
12392
  };
11822
- if (!existsSync15(root))
12393
+ if (!existsSync16(root))
11823
12394
  return;
11824
12395
  for (const proj of readdirSync11(root, { withFileTypes: true })) {
11825
12396
  if (!proj.isDirectory())
@@ -11830,12 +12401,12 @@ var init_claude2 = __esm({
11830
12401
  const projectPath = resolveProjectDirName(proj.name);
11831
12402
  const indexPath = join16(projPath, "sessions-index.json");
11832
12403
  const indexed = /* @__PURE__ */ new Set();
11833
- if (existsSync15(indexPath)) {
12404
+ if (existsSync16(indexPath)) {
11834
12405
  try {
11835
12406
  const indexData = JSON.parse(readFileSync10(indexPath, "utf-8"));
11836
12407
  const entries = indexData.entries || [];
11837
12408
  for (const entry of entries) {
11838
- if (!entry.fullPath || !existsSync15(entry.fullPath))
12409
+ if (!entry.fullPath || !existsSync16(entry.fullPath))
11839
12410
  continue;
11840
12411
  indexed.add(entry.sessionId);
11841
12412
  add({
@@ -12160,7 +12731,7 @@ var init_claude2 = __esm({
12160
12731
  collectRecentEdits(opts) {
12161
12732
  const candidates2 = [];
12162
12733
  for (const root of claudeProjectDirs()) {
12163
- if (!existsSync15(root))
12734
+ if (!existsSync16(root))
12164
12735
  continue;
12165
12736
  for (const projEntry of readdirSync11(root, { withFileTypes: true })) {
12166
12737
  if (!projEntry.isDirectory())
@@ -12182,7 +12753,7 @@ var init_claude2 = __esm({
12182
12753
  const st = statSync12(fp);
12183
12754
  let mtime = st.mtimeMs;
12184
12755
  const subDir = join16(projPath, f.slice(0, -6), "subagents");
12185
- if (existsSync15(subDir)) {
12756
+ if (existsSync16(subDir)) {
12186
12757
  for (const sub of readdirSync11(subDir)) {
12187
12758
  try {
12188
12759
  const subSt = statSync12(join16(subDir, sub));
@@ -12259,27 +12830,45 @@ var init_claude2 = __esm({
12259
12830
  /**
12260
12831
  * True when this session exists in MORE THAN ONE home.
12261
12832
  *
12262
- * Such a session cannot be tail-appended: `fileSize()` is the SUM across
12263
- * copies while a byte offset addresses ONE file, so the two disagree by
12264
- * construction and the "tail" is either empty or the wrong bytes. The caller
12265
- * uses this to force a FULL sync, which ships the unioned container and is
12266
- * correct regardless of how the halves are arranged.
12267
- *
12268
12833
  * Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
12269
- * zero overlap. An offset-based append against a 5846662-byte "size" that no
12270
- * single file has could only ever ship nonsense.
12834
+ * zero overlap. `fileSize()` is the sum across the copies, and
12835
+ * `readFromOffset()` reads them joined end to end, primary first.
12271
12836
  */
12272
12837
  spansMultipleSources(prefixedId) {
12273
12838
  return findSessionFiles(this.toRawId(prefixedId)).length > 1;
12274
12839
  }
12840
+ /**
12841
+ * The copies before the last one, each with its size. Claude Code writes to
12842
+ * the home of the profile that runs the session, so the live copy can grow,
12843
+ * appear, vanish or merge back into the primary. Each of those moves bytes
12844
+ * under an offset into the joined copies; this key changes with all of them.
12845
+ */
12846
+ sourceLayout(prefixedId) {
12847
+ const copies = findSessionFiles(this.toRawId(prefixedId));
12848
+ if (copies.length < 2)
12849
+ return "";
12850
+ return copies.slice(0, -1).map((c) => {
12851
+ let size = -1;
12852
+ try {
12853
+ size = statSync12(c.path).size;
12854
+ } catch {
12855
+ }
12856
+ return `${c.path}:${size}`;
12857
+ }).join("|");
12858
+ }
12859
+ /**
12860
+ * Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
12861
+ * the 24 MB build ceiling a sync ships the transcript in chunks read here,
12862
+ * and this returned no text for a session in two homes. Each sync shipped
12863
+ * nothing, and the server stayed three hours behind.
12864
+ */
12275
12865
  async readFromOffset(prefixedId, offset, maxBytes) {
12276
- const rawId = this.toRawId(prefixedId);
12277
- const copies = findSessionFiles(rawId);
12866
+ const copies = findSessionFiles(this.toRawId(prefixedId));
12278
12867
  if (copies.length === 0)
12279
12868
  return { text: "", newOffset: offset };
12280
- if (copies.length > 1)
12281
- return { text: "", newOffset: offset };
12282
- return readTailFromOffset(copies[0].path, offset, maxBytes);
12869
+ if (copies.length === 1)
12870
+ return readTailFromOffset(copies[0].path, offset, maxBytes);
12871
+ return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
12283
12872
  }
12284
12873
  };
12285
12874
  claudeBackend = new ClaudeBackend();
@@ -12300,7 +12889,7 @@ __export(project_resolver_exports, {
12300
12889
  });
12301
12890
  import { execSync } from "child_process";
12302
12891
  import { createHash as createHash3 } from "crypto";
12303
- import { existsSync as existsSync16, readFileSync as readFileSync11, realpathSync as realpathSync2, readdirSync as readdirSync12, statSync as statSync13 } from "fs";
12892
+ import { existsSync as existsSync17, readFileSync as readFileSync11, realpathSync as realpathSync2, readdirSync as readdirSync12, statSync as statSync13 } from "fs";
12304
12893
  import { basename as basename11, dirname as dirname13, isAbsolute, join as join17, resolve, sep } from "path";
12305
12894
  function getProjectsConfigPath() {
12306
12895
  return join17(getDataDir(), "projects.json");
@@ -12310,7 +12899,7 @@ function loadProjectsConfig() {
12310
12899
  return cachedConfig.cfg;
12311
12900
  }
12312
12901
  const path = getProjectsConfigPath();
12313
- if (!existsSync16(path)) {
12902
+ if (!existsSync17(path)) {
12314
12903
  cachedConfig = { mtime: 0, cfg: {} };
12315
12904
  return cachedConfig.cfg;
12316
12905
  }
@@ -12484,7 +13073,7 @@ function findGitToplevel(realPath) {
12484
13073
  let cur = realPath;
12485
13074
  let last = "";
12486
13075
  while (cur && cur !== last) {
12487
- if (existsSync16(join17(cur, ".git")))
13076
+ if (existsSync17(join17(cur, ".git")))
12488
13077
  return cur;
12489
13078
  last = cur;
12490
13079
  cur = dirname13(cur);
@@ -12551,7 +13140,7 @@ function resolveAutoWorkspace(realPath, minRepos) {
12551
13140
  let last = "";
12552
13141
  let depth = 0;
12553
13142
  while (cur && cur !== last && depth < AUTO_WORKSPACE_MAX_DEPTH) {
12554
- if (!existsSync16(join17(cur, ".git"))) {
13143
+ if (!existsSync17(join17(cur, ".git"))) {
12555
13144
  const cached = workspaceCache.get(cur);
12556
13145
  let qualifies;
12557
13146
  if (cached === void 0) {
@@ -12587,7 +13176,7 @@ function countGitChildren(dir) {
12587
13176
  const st = statSync13(child);
12588
13177
  if (!st.isDirectory())
12589
13178
  continue;
12590
- if (existsSync16(join17(child, ".git")))
13179
+ if (existsSync17(join17(child, ".git")))
12591
13180
  count++;
12592
13181
  } catch {
12593
13182
  }
@@ -13177,7 +13766,7 @@ var init_secret_redactor = __esm({
13177
13766
 
13178
13767
  // ../engine/dist/core/source-discovery.js
13179
13768
  import { createHash as createHash5 } from "crypto";
13180
- import { existsSync as existsSync17, readdirSync as readdirSync13, statSync as statSync15 } from "fs";
13769
+ import { existsSync as existsSync18, readdirSync as readdirSync13, statSync as statSync15 } from "fs";
13181
13770
  import { join as join18 } from "path";
13182
13771
  function sourceId(path) {
13183
13772
  return "src_" + createHash5("sha256").update(path).digest("hex").slice(0, 12);
@@ -13236,7 +13825,7 @@ function countTranscripts(root, tool) {
13236
13825
  walk(root, 0);
13237
13826
  return { sessions, newestMtime };
13238
13827
  }
13239
- function countOpencodeSessions(dbPath) {
13828
+ function countDbSessions(dbPath) {
13240
13829
  try {
13241
13830
  const st = statSync15(dbPath);
13242
13831
  return { sessions: 0, newestMtime: st.mtimeMs };
@@ -13254,18 +13843,24 @@ function discoverSessionSources() {
13254
13843
  ];
13255
13844
  for (const [tool, dirs] of dirTools) {
13256
13845
  for (const [i, path] of dirs.entries()) {
13257
- if (!existsSync17(path))
13846
+ if (!existsSync18(path))
13258
13847
  continue;
13259
13848
  const { sessions, newestMtime } = countTranscripts(path, tool);
13260
13849
  out.push({ id: sourceId(path), tool, path, sessions, newestMtime, isPrimary: i === 0 });
13261
13850
  }
13262
13851
  }
13263
13852
  for (const [i, path] of opencodeDbPaths({ includeExcluded: true }).entries()) {
13264
- if (!existsSync17(path))
13853
+ if (!existsSync18(path))
13265
13854
  continue;
13266
- const { sessions, newestMtime } = countOpencodeSessions(path);
13855
+ const { sessions, newestMtime } = countDbSessions(path);
13267
13856
  out.push({ id: sourceId(path), tool: "opencode", path, sessions, newestMtime, isPrimary: i === 0 });
13268
13857
  }
13858
+ for (const [i, path] of hermesDbPaths({ includeExcluded: true }).entries()) {
13859
+ if (!existsSync18(path))
13860
+ continue;
13861
+ const { sessions, newestMtime } = countDbSessions(path);
13862
+ out.push({ id: sourceId(path), tool: "hermes", path, sessions, newestMtime, isPrimary: i === 0 });
13863
+ }
13269
13864
  return out;
13270
13865
  }
13271
13866
  var excludedIds, SOURCE_ID_RE, MAX_DEPTH;
@@ -13290,7 +13885,7 @@ __export(home_discovery_exports, {
13290
13885
  sessionIdsInHome: () => sessionIdsInHome
13291
13886
  });
13292
13887
  import { execFileSync as execFileSync2 } from "child_process";
13293
- import { existsSync as existsSync18, readdirSync as readdirSync14, readFileSync as readFileSync12, realpathSync as realpathSync3, statSync as statSync16 } from "fs";
13888
+ import { existsSync as existsSync19, readdirSync as readdirSync14, readFileSync as readFileSync12, realpathSync as realpathSync3, statSync as statSync16 } from "fs";
13294
13889
  import { homedir as homedir5, platform as osPlatform } from "os";
13295
13890
  import { join as join19 } from "path";
13296
13891
  function candidateRoots(plat = osPlatform(), env = process.env) {
@@ -13311,10 +13906,10 @@ function candidateRoots(plat = osPlatform(), env = process.env) {
13311
13906
  add(env.XDG_CONFIG_HOME || join19(home, ".config"));
13312
13907
  add(env.XDG_DATA_HOME || join19(home, ".local", "share"));
13313
13908
  }
13314
- return roots.filter((r) => existsSync18(r));
13909
+ return roots.filter((r) => existsSync19(r));
13315
13910
  }
13316
13911
  function identifyHome(dir) {
13317
- const has = (...parts) => existsSync18(join19(dir, ...parts));
13912
+ const has = (...parts) => existsSync19(join19(dir, ...parts));
13318
13913
  if (has("projects") && dirHasTranscript(join19(dir, "projects"), (f) => f.endsWith(".jsonl"), 2))
13319
13914
  return "claude";
13320
13915
  if (has("sessions") && dirHasTranscript(join19(dir, "sessions"), (f) => f.startsWith("rollout-") && f.endsWith(".jsonl"), 4))
@@ -13325,6 +13920,8 @@ function identifyHome(dir) {
13325
13920
  return "cursor";
13326
13921
  if (has("opencode.db"))
13327
13922
  return "opencode";
13923
+ if (has("state.db") && has("SOUL.md"))
13924
+ return "hermes";
13328
13925
  return null;
13329
13926
  }
13330
13927
  function dirHasTranscript(root, match, depth) {
@@ -13352,7 +13949,8 @@ function countSessions(dir, tool) {
13352
13949
  codex: { sub: "sessions", depth: 4, match: (f) => f.startsWith("rollout-") && f.endsWith(".jsonl") },
13353
13950
  agy: { sub: "brain", depth: 4, match: (f) => f.endsWith(".jsonl") },
13354
13951
  cursor: { sub: "chats", depth: 3, match: (f) => f === "store.db" },
13355
- opencode: { sub: "", depth: 0, match: () => false }
13952
+ opencode: { sub: "", depth: 0, match: () => false },
13953
+ hermes: { sub: "", depth: 0, match: () => false }
13356
13954
  };
13357
13955
  const s = spec[tool];
13358
13956
  if (!s.sub)
@@ -13402,7 +14000,9 @@ function sessionIdsInHome(dir, tool) {
13402
14000
  // id is the DIRECTORY name
13403
14001
  cursor: { sub: "chats", depth: 2, idOf: () => null },
13404
14002
  // id is the DIRECTORY name
13405
- opencode: { sub: "", depth: 0, idOf: () => null }
14003
+ opencode: { sub: "", depth: 0, idOf: () => null },
14004
+ // rows, not files
14005
+ hermes: { sub: "", depth: 0, idOf: () => null }
13406
14006
  // rows, not files
13407
14007
  };
13408
14008
  const s = spec[tool];
@@ -13475,6 +14075,8 @@ function declaredHomes(env = process.env, plat = osPlatform()) {
13475
14075
  add(env.CHAT_RECALL_AGY_HOME);
13476
14076
  add(env.CHAT_RECALL_CURSOR_HOME);
13477
14077
  add(env.CHAT_RECALL_CURSOR_IDE_HOME);
14078
+ add(env.CHAT_RECALL_HERMES_HOME);
14079
+ add(env.HERMES_HOME);
13478
14080
  for (const d of (env.CLAUDE_DIRS || "").split(","))
13479
14081
  add(d);
13480
14082
  if (plat === "win32") {
@@ -13574,6 +14176,15 @@ function discoverHomes(opts = {}) {
13574
14176
  if (prior && !(via === "declared" && prior.via !== "declared"))
13575
14177
  return;
13576
14178
  found.set(real, { tool, path: real, via, sessions: countSessions(real, tool) });
14179
+ if (tool === "hermes") {
14180
+ try {
14181
+ for (const e of readdirSync14(join19(real, "profiles"), { withFileTypes: true })) {
14182
+ if (e.isDirectory())
14183
+ consider(join19(real, "profiles", e.name), via);
14184
+ }
14185
+ } catch {
14186
+ }
14187
+ }
13577
14188
  };
13578
14189
  for (const d of declaredHomes(env, plat))
13579
14190
  consider(d, "declared");
@@ -13653,7 +14264,7 @@ __export(detector_install_exports, {
13653
14264
  });
13654
14265
  import { spawnSync } from "child_process";
13655
14266
  import { createHash as createHash6 } from "crypto";
13656
- import { existsSync as existsSync19, mkdirSync as mkdirSync4, mkdtempSync, chmodSync as chmodSync2, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync13, statSync as statSync17, copyFileSync as copyFileSync2 } from "fs";
14267
+ import { existsSync as existsSync20, mkdirSync as mkdirSync4, mkdtempSync, chmodSync as chmodSync2, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync13, statSync as statSync17, copyFileSync as copyFileSync2 } from "fs";
13657
14268
  import { join as join20 } from "path";
13658
14269
  function managedDetectorPath(name) {
13659
14270
  return join20(getDetectorBinDir(), name + exeSuffix());
@@ -13696,7 +14307,7 @@ function resolveDetector(name) {
13696
14307
  const dir = configuredDir();
13697
14308
  if (dir) {
13698
14309
  const p2 = join20(dir, name + exeSuffix());
13699
- if (existsSync19(p2))
14310
+ if (existsSync20(p2))
13700
14311
  return { path: p2, version: "operator-supplied", source: "configured" };
13701
14312
  return { error: "not-installed" };
13702
14313
  }
@@ -13704,7 +14315,7 @@ function resolveDetector(name) {
13704
14315
  if (!spec.assets[platformKey()])
13705
14316
  return { error: "unsupported-platform" };
13706
14317
  const p = managedDetectorPath(name);
13707
- if (!existsSync19(p))
14318
+ if (!existsSync20(p))
13708
14319
  return { error: "not-installed" };
13709
14320
  const receipt = readReceipt(name);
13710
14321
  if (!receipt)
@@ -13724,7 +14335,7 @@ function downloadUrl(spec, asset) {
13724
14335
  }
13725
14336
  function extractMember(archive, member, destDir) {
13726
14337
  const r = spawnSync("tar", ["-xf", archive, "-C", destDir, member], { encoding: "utf-8", timeout: 12e4 });
13727
- if (r.status === 0 && existsSync19(join20(destDir, member)))
14338
+ if (r.status === 0 && existsSync20(join20(destDir, member)))
13728
14339
  return true;
13729
14340
  log4.error({ status: r.status, stderr: (r.stderr || "").slice(0, 400), archive, member }, "extract failed");
13730
14341
  return false;
@@ -13793,7 +14404,7 @@ function removeDetector(name) {
13793
14404
  let removed = false;
13794
14405
  for (const p of [managedDetectorPath(name), receiptPath(name)]) {
13795
14406
  try {
13796
- if (existsSync19(p)) {
14407
+ if (existsSync20(p)) {
13797
14408
  rmSync2(p, { force: true });
13798
14409
  removed = true;
13799
14410
  }
@@ -13819,7 +14430,7 @@ function detectorStatus() {
13819
14430
  }
13820
14431
  function ensureDetectorBinDir() {
13821
14432
  const d = getDetectorBinDir();
13822
- if (!existsSync19(d))
14433
+ if (!existsSync20(d))
13823
14434
  mkdirSync4(d, { recursive: true, mode: 493 });
13824
14435
  return d;
13825
14436
  }
@@ -13877,7 +14488,7 @@ __export(secret_scanner_exports, {
13877
14488
  scanTenantRules: () => scanTenantRules
13878
14489
  });
13879
14490
  import { spawnSync as spawnSync2 } from "child_process";
13880
- import { existsSync as existsSync20, unlinkSync, readFileSync as readFileSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
14491
+ import { existsSync as existsSync21, unlinkSync, readFileSync as readFileSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
13881
14492
  import { tmpdir } from "os";
13882
14493
  import { join as join21 } from "path";
13883
14494
  function mask(s) {
@@ -14046,7 +14657,7 @@ function runTrufflehogDir(binary, dir, opts = {}) {
14046
14657
  return out;
14047
14658
  }
14048
14659
  function scanDirForSecrets(dir, opts = {}) {
14049
- if (!existsSync20(dir))
14660
+ if (!existsSync21(dir))
14050
14661
  return [];
14051
14662
  const gitleaks = whichDetector("gitleaks");
14052
14663
  const trufflehog = whichDetector("trufflehog");
@@ -14083,7 +14694,7 @@ function scanTenantRules(text, tenantRules) {
14083
14694
  return out;
14084
14695
  }
14085
14696
  function scanFileForSecrets(filePath, opts = {}) {
14086
- if (!existsSync20(filePath))
14697
+ if (!existsSync21(filePath))
14087
14698
  return [];
14088
14699
  const gitleaks = whichDetector("gitleaks");
14089
14700
  const trufflehog = whichDetector("trufflehog");
@@ -14105,7 +14716,7 @@ function scanFileForSecrets(filePath, opts = {}) {
14105
14716
  }
14106
14717
  async function scanSessionForSecrets(opts) {
14107
14718
  const result = { sessionId: opts.sessionId, byDetector: {}, total: 0, ran: false };
14108
- if (!existsSync20(opts.filePath))
14719
+ if (!existsSync21(opts.filePath))
14109
14720
  return result;
14110
14721
  const gitleaks = whichDetector("gitleaks");
14111
14722
  const trufflehog = whichDetector("trufflehog");
@@ -14261,10 +14872,10 @@ var init_types = __esm({
14261
14872
 
14262
14873
  // ../engine/dist/transcript/opencode.js
14263
14874
  async function parseOpenCodeSubagents(parentSessionId) {
14264
- const { existsSync: existsSync58 } = await import("fs");
14875
+ const { existsSync: existsSync59 } = await import("fs");
14265
14876
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
14266
14877
  const dbPath = opencodeBackend2.dbPath();
14267
- if (!existsSync58(dbPath))
14878
+ if (!existsSync59(dbPath))
14268
14879
  return [];
14269
14880
  const realParent = parentSessionId.replace("opencode_", "");
14270
14881
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -14305,10 +14916,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
14305
14916
  }
14306
14917
  }
14307
14918
  async function parseOpenCodeTranscript(sessionId) {
14308
- const { existsSync: existsSync58 } = await import("fs");
14919
+ const { existsSync: existsSync59 } = await import("fs");
14309
14920
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
14310
14921
  const dbPath = opencodeBackend2.dbPath();
14311
- if (!existsSync58(dbPath))
14922
+ if (!existsSync59(dbPath))
14312
14923
  return [];
14313
14924
  const realId = sessionId.replace("opencode_", "");
14314
14925
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -14360,6 +14971,7 @@ var init_opencode2 = __esm({
14360
14971
  // ../engine/dist/transcript/index.js
14361
14972
  var transcript_exports = {};
14362
14973
  __export(transcript_exports, {
14974
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
14363
14975
  TRANSCRIPT_VERSION: () => TRANSCRIPT_VERSION,
14364
14976
  archiveRawSession: () => archiveRawSession,
14365
14977
  buildRawContainer: () => buildRawContainer,
@@ -15065,7 +15677,7 @@ var init_source_policy = __esm({
15065
15677
  init_project_path_match();
15066
15678
  init_settings();
15067
15679
  cache2 = null;
15068
- TOOLS = ["claude", "opencode", "codex", "agy", "cursor"];
15680
+ TOOLS = ["claude", "opencode", "codex", "agy", "cursor", "hermes"];
15069
15681
  }
15070
15682
  });
15071
15683
 
@@ -15160,7 +15772,7 @@ var init_source_registry = __esm({
15160
15772
  });
15161
15773
 
15162
15774
  // ../engine/dist/core/utils.js
15163
- import { existsSync as existsSync21, readdirSync as readdirSync15 } from "fs";
15775
+ import { existsSync as existsSync22, readdirSync as readdirSync15 } from "fs";
15164
15776
  import { homedir as homedir6 } from "os";
15165
15777
  import { join as join22 } from "path";
15166
15778
  function splitByHeaders(markdown) {
@@ -15196,7 +15808,7 @@ function getClaudeDirs() {
15196
15808
  const dirs = [];
15197
15809
  const seen = /* @__PURE__ */ new Set();
15198
15810
  const add = (dir) => {
15199
- if (!seen.has(dir) && existsSync21(dir)) {
15811
+ if (!seen.has(dir) && existsSync22(dir)) {
15200
15812
  seen.add(dir);
15201
15813
  dirs.push(dir);
15202
15814
  }
@@ -15215,7 +15827,7 @@ function getClaudeDirs() {
15215
15827
  for (const entry of entries) {
15216
15828
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15217
15829
  const candidate = join22(home, entry.name);
15218
- if (existsSync21(join22(candidate, "projects"))) {
15830
+ if (existsSync22(join22(candidate, "projects"))) {
15219
15831
  add(candidate);
15220
15832
  }
15221
15833
  }
@@ -15228,7 +15840,7 @@ function discoverSubdirs(subdir) {
15228
15840
  const result = [];
15229
15841
  for (const claudeDir of getClaudeDirs()) {
15230
15842
  const path = join22(claudeDir, subdir);
15231
- if (existsSync21(path)) {
15843
+ if (existsSync22(path)) {
15232
15844
  result.push(path);
15233
15845
  }
15234
15846
  }
@@ -15241,7 +15853,7 @@ var init_utils = __esm({
15241
15853
  });
15242
15854
 
15243
15855
  // ../engine/dist/parsers/plan-source.js
15244
- import { existsSync as existsSync22, readdirSync as readdirSync16, readFileSync as readFileSync15, statSync as statSync20 } from "fs";
15856
+ import { existsSync as existsSync23, readdirSync as readdirSync16, readFileSync as readFileSync15, statSync as statSync20 } from "fs";
15245
15857
  import { homedir as homedir7 } from "os";
15246
15858
  import { join as join23, basename as basename12 } from "path";
15247
15859
  function parseFrontmatter(content) {
@@ -15299,7 +15911,7 @@ var init_plan_source = __esm({
15299
15911
  return this._knownProjectDirs;
15300
15912
  const projectsDir = claudeBackend.projectsDir();
15301
15913
  this._knownProjectDirs = [];
15302
- if (!existsSync22(projectsDir))
15914
+ if (!existsSync23(projectsDir))
15303
15915
  return this._knownProjectDirs;
15304
15916
  try {
15305
15917
  const entries = readdirSync16(projectsDir, { withFileTypes: true });
@@ -15307,7 +15919,7 @@ var init_plan_source = __esm({
15307
15919
  if (!entry.isDirectory() || entry.name.startsWith("."))
15308
15920
  continue;
15309
15921
  const projectPath = resolveProjectDirName(entry.name);
15310
- if (existsSync22(projectPath)) {
15922
+ if (existsSync23(projectPath)) {
15311
15923
  this._knownProjectDirs.push(projectPath);
15312
15924
  }
15313
15925
  }
@@ -15331,7 +15943,7 @@ var init_plan_source = __esm({
15331
15943
  async *discoverClaudePlans() {
15332
15944
  const seenClaude = /* @__PURE__ */ new Set();
15333
15945
  for (const plansDir of this.plansDirs) {
15334
- if (!existsSync22(plansDir))
15946
+ if (!existsSync23(plansDir))
15335
15947
  continue;
15336
15948
  const files = readdirSync16(plansDir).filter((f) => f.endsWith(".md"));
15337
15949
  for (const file of files) {
@@ -15373,7 +15985,7 @@ var init_plan_source = __esm({
15373
15985
  }
15374
15986
  async *discoverOpenCodePlans() {
15375
15987
  const plansDir = opencodeBackend.plansDir();
15376
- if (!existsSync22(plansDir))
15988
+ if (!existsSync23(plansDir))
15377
15989
  return;
15378
15990
  let files;
15379
15991
  try {
@@ -15460,7 +16072,7 @@ var init_plan_source = __esm({
15460
16072
  return "";
15461
16073
  }
15462
16074
  async parse(item) {
15463
- if (!existsSync22(item.filePath))
16075
+ if (!existsSync23(item.filePath))
15464
16076
  return [];
15465
16077
  const content = readFileSync15(item.filePath, "utf-8");
15466
16078
  const chunks = [];
@@ -15501,7 +16113,7 @@ var init_plan_source = __esm({
15501
16113
  const projPath = join23(root, proj);
15502
16114
  for (const sessionEntry of safeReaddir(projPath)) {
15503
16115
  const subDir = join23(projPath, sessionEntry, "subagents");
15504
- if (!existsSync22(subDir))
16116
+ if (!existsSync23(subDir))
15505
16117
  continue;
15506
16118
  for (const f of safeReaddir(subDir)) {
15507
16119
  const m = f.match(/^agent-([a-z0-9_-]+)\.jsonl$/i);
@@ -15576,7 +16188,7 @@ var init_plan_source = __esm({
15576
16188
  }
15577
16189
  async *discoverAgyPlans() {
15578
16190
  const brainDir = join23(agyBackend.homeDir(), "brain");
15579
- if (!existsSync22(brainDir))
16191
+ if (!existsSync23(brainDir))
15580
16192
  return;
15581
16193
  let sessionDirs;
15582
16194
  try {
@@ -15631,7 +16243,7 @@ var init_plan_source = __esm({
15631
16243
  });
15632
16244
 
15633
16245
  // ../engine/dist/parsers/task-source.js
15634
- import { existsSync as existsSync23, readdirSync as readdirSync17, readFileSync as readFileSync16, statSync as statSync21 } from "fs";
16246
+ import { existsSync as existsSync24, readdirSync as readdirSync17, readFileSync as readFileSync16, statSync as statSync21 } from "fs";
15635
16247
  import { join as join24 } from "path";
15636
16248
  var MAX_CHUNK_CHARS2, TaskSource;
15637
16249
  var init_task_source = __esm({
@@ -15661,7 +16273,7 @@ var init_task_source = __esm({
15661
16273
  }
15662
16274
  async *discoverClaudeTodos() {
15663
16275
  const todosDir = claudeBackend.todosDir();
15664
- if (!existsSync23(todosDir))
16276
+ if (!existsSync24(todosDir))
15665
16277
  return;
15666
16278
  let files;
15667
16279
  try {
@@ -15712,7 +16324,7 @@ var init_task_source = __esm({
15712
16324
  }
15713
16325
  }
15714
16326
  async *discoverInDir(tasksDir) {
15715
- if (!existsSync23(tasksDir))
16327
+ if (!existsSync24(tasksDir))
15716
16328
  return;
15717
16329
  const entries = readdirSync17(tasksDir, { withFileTypes: true });
15718
16330
  for (const entry of entries) {
@@ -15754,7 +16366,7 @@ var init_task_source = __esm({
15754
16366
  return this.parseAgentTodo(item);
15755
16367
  }
15756
16368
  const taskDir = item.filePath;
15757
- if (!existsSync23(taskDir))
16369
+ if (!existsSync24(taskDir))
15758
16370
  return [];
15759
16371
  const taskFiles = readdirSync17(taskDir).filter((f) => f.endsWith(".json"));
15760
16372
  const tasks2 = this.readTasks(taskDir, taskFiles);
@@ -15815,7 +16427,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15815
16427
  }];
15816
16428
  }
15817
16429
  async parseAgentTodo(item) {
15818
- if (!existsSync23(item.filePath))
16430
+ if (!existsSync24(item.filePath))
15819
16431
  return [];
15820
16432
  let parsed;
15821
16433
  try {
@@ -15867,7 +16479,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15867
16479
  });
15868
16480
 
15869
16481
  // ../engine/dist/parsers/claude-md-source.js
15870
- import { existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
16482
+ import { existsSync as existsSync25, readFileSync as readFileSync17, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
15871
16483
  import { homedir as homedir8 } from "os";
15872
16484
  import { join as join25, basename as basename13, dirname as dirname14 } from "path";
15873
16485
  import { createHash as createHash7 } from "crypto";
@@ -15875,13 +16487,13 @@ function discoverProjectDirs(claudeDir) {
15875
16487
  const dirs = [];
15876
16488
  const seen = /* @__PURE__ */ new Set();
15877
16489
  const addProject = (path) => {
15878
- if (!seen.has(path) && existsSync24(path)) {
16490
+ if (!seen.has(path) && existsSync25(path)) {
15879
16491
  seen.add(path);
15880
16492
  dirs.push(path);
15881
16493
  }
15882
16494
  };
15883
16495
  const scanProjectsDir = (projectsDir) => {
15884
- if (!existsSync24(projectsDir))
16496
+ if (!existsSync25(projectsDir))
15885
16497
  return;
15886
16498
  try {
15887
16499
  const entries = readdirSync18(projectsDir, { withFileTypes: true });
@@ -15909,7 +16521,7 @@ function discoverProjectDirs(claudeDir) {
15909
16521
  } catch {
15910
16522
  }
15911
16523
  const gemProjects = agyBackend.sharedProjectsJson();
15912
- if (existsSync24(gemProjects)) {
16524
+ if (existsSync25(gemProjects)) {
15913
16525
  try {
15914
16526
  const data = JSON.parse(readFileSync17(gemProjects, "utf-8"));
15915
16527
  for (const path of Object.keys(data.projects || {}))
@@ -15918,7 +16530,7 @@ function discoverProjectDirs(claudeDir) {
15918
16530
  }
15919
16531
  }
15920
16532
  const ocDb = opencodeBackend.dbPath();
15921
- if (existsSync24(ocDb)) {
16533
+ if (existsSync25(ocDb)) {
15922
16534
  try {
15923
16535
  const db = openSqliteReadonlyOrThrow(ocDb);
15924
16536
  try {
@@ -15969,7 +16581,7 @@ var init_claude_md_source = __esm({
15969
16581
  for (const note of NOTE_FILES) {
15970
16582
  const p = join25(homedir8(), note.filename);
15971
16583
  const key2 = seenKey(p, note.tool);
15972
- if (existsSync24(p) && !seen.has(key2)) {
16584
+ if (existsSync25(p) && !seen.has(key2)) {
15973
16585
  seen.add(key2);
15974
16586
  const item = this.fileToItem(p, note.filename, note.tool);
15975
16587
  if (item)
@@ -15981,7 +16593,7 @@ var init_claude_md_source = __esm({
15981
16593
  for (const note of NOTE_FILES) {
15982
16594
  const p = join25(projectDir, note.filename);
15983
16595
  const key2 = seenKey(p, note.tool);
15984
- if (existsSync24(p) && !seen.has(key2)) {
16596
+ if (existsSync25(p) && !seen.has(key2)) {
15985
16597
  seen.add(key2);
15986
16598
  const item = this.fileToItem(p, note.filename, note.tool);
15987
16599
  if (item)
@@ -15990,12 +16602,12 @@ var init_claude_md_source = __esm({
15990
16602
  }
15991
16603
  }
15992
16604
  for (const searchDir of this.searchDirs) {
15993
- if (!existsSync24(searchDir))
16605
+ if (!existsSync25(searchDir))
15994
16606
  continue;
15995
16607
  for (const note of NOTE_FILES) {
15996
16608
  const directPath = join25(searchDir, note.filename);
15997
16609
  const key2 = seenKey(directPath, note.tool);
15998
- if (existsSync24(directPath) && !seen.has(key2)) {
16610
+ if (existsSync25(directPath) && !seen.has(key2)) {
15999
16611
  seen.add(key2);
16000
16612
  const item = this.fileToItem(directPath, note.filename, note.tool);
16001
16613
  if (item)
@@ -16010,7 +16622,7 @@ var init_claude_md_source = __esm({
16010
16622
  for (const note of NOTE_FILES) {
16011
16623
  const p = join25(searchDir, entry.name, note.filename);
16012
16624
  const key2 = seenKey(p, note.tool);
16013
- if (existsSync24(p) && !seen.has(key2)) {
16625
+ if (existsSync25(p) && !seen.has(key2)) {
16014
16626
  seen.add(key2);
16015
16627
  const item = this.fileToItem(p, note.filename, note.tool);
16016
16628
  if (item)
@@ -16024,7 +16636,7 @@ var init_claude_md_source = __esm({
16024
16636
  }
16025
16637
  }
16026
16638
  async parse(item) {
16027
- if (!existsSync24(item.filePath))
16639
+ if (!existsSync25(item.filePath))
16028
16640
  return [];
16029
16641
  const content = readFileSync17(item.filePath, "utf-8");
16030
16642
  const chunks = [];
@@ -16093,7 +16705,7 @@ var init_claude_md_source = __esm({
16093
16705
  });
16094
16706
 
16095
16707
  // ../engine/dist/parsers/agent-memory-source.js
16096
- import { existsSync as existsSync25, readFileSync as readFileSync18, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
16708
+ import { existsSync as existsSync26, readFileSync as readFileSync18, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
16097
16709
  import { homedir as homedir9 } from "os";
16098
16710
  import { join as join26, basename as basename14 } from "path";
16099
16711
  function parseFrontmatter2(content) {
@@ -16127,7 +16739,7 @@ function discoverMemoryDirs() {
16127
16739
  const out = [];
16128
16740
  const seen = /* @__PURE__ */ new Set();
16129
16741
  const add = (memoryDir, projectHash) => {
16130
- if (!seen.has(memoryDir) && existsSync25(memoryDir)) {
16742
+ if (!seen.has(memoryDir) && existsSync26(memoryDir)) {
16131
16743
  seen.add(memoryDir);
16132
16744
  out.push({ memoryDir, projectHash });
16133
16745
  }
@@ -16143,7 +16755,7 @@ function discoverMemoryDirs() {
16143
16755
  } catch {
16144
16756
  }
16145
16757
  for (const projectsDir of claudeRoots) {
16146
- if (!existsSync25(projectsDir))
16758
+ if (!existsSync26(projectsDir))
16147
16759
  continue;
16148
16760
  try {
16149
16761
  for (const entry of readdirSync19(projectsDir, { withFileTypes: true })) {
@@ -16192,7 +16804,7 @@ var init_agent_memory_source = __esm({
16192
16804
  }
16193
16805
  }
16194
16806
  async parse(item) {
16195
- if (!existsSync25(item.filePath))
16807
+ if (!existsSync26(item.filePath))
16196
16808
  return [];
16197
16809
  const content = readFileSync18(item.filePath, "utf-8");
16198
16810
  const { body: body2 } = parseFrontmatter2(content);
@@ -16278,7 +16890,7 @@ var init_agent_memory_source = __esm({
16278
16890
  });
16279
16891
 
16280
16892
  // ../engine/dist/parsers/history-source.js
16281
- import { existsSync as existsSync26, readdirSync as readdirSync20, statSync as statSync24 } from "fs";
16893
+ import { existsSync as existsSync27, readdirSync as readdirSync20, statSync as statSync24 } from "fs";
16282
16894
  import { createReadStream as createReadStream3 } from "fs";
16283
16895
  import { createInterface as createInterface2 } from "readline";
16284
16896
  import { homedir as homedir10 } from "os";
@@ -16300,7 +16912,7 @@ var init_history_source = __esm({
16300
16912
  const home = homedir10();
16301
16913
  const paths = [];
16302
16914
  const tryAdd = (p) => {
16303
- if (existsSync26(p) && !paths.includes(p))
16915
+ if (existsSync27(p) && !paths.includes(p))
16304
16916
  paths.push(p);
16305
16917
  };
16306
16918
  tryAdd(claudeBackend.historyFile());
@@ -16318,7 +16930,7 @@ var init_history_source = __esm({
16318
16930
  async *discover() {
16319
16931
  if (!isSourceEnabled("claude", "history"))
16320
16932
  return;
16321
- const historyPath = this.historyPaths.find((p) => existsSync26(p));
16933
+ const historyPath = this.historyPaths.find((p) => existsSync27(p));
16322
16934
  if (!historyPath)
16323
16935
  return;
16324
16936
  const stat3 = statSync24(historyPath);
@@ -16414,7 +17026,7 @@ var init_history_source = __esm({
16414
17026
  async groupByProject() {
16415
17027
  const groups = /* @__PURE__ */ new Map();
16416
17028
  for (const historyPath of this.historyPaths) {
16417
- if (!existsSync26(historyPath))
17029
+ if (!existsSync27(historyPath))
16418
17030
  continue;
16419
17031
  const rl = createInterface2({
16420
17032
  input: createReadStream3(historyPath),
@@ -16462,7 +17074,7 @@ var init_history_source = __esm({
16462
17074
  });
16463
17075
 
16464
17076
  // ../engine/dist/parsers/paste-source.js
16465
- import { existsSync as existsSync27, readdirSync as readdirSync21, readFileSync as readFileSync19, statSync as statSync25 } from "fs";
17077
+ import { existsSync as existsSync28, readdirSync as readdirSync21, readFileSync as readFileSync19, statSync as statSync25 } from "fs";
16466
17078
  import { homedir as homedir11 } from "os";
16467
17079
  import { join as join28, basename as basename15 } from "path";
16468
17080
  var MAX_CHUNK_CHARS6, MAX_FILE_SIZE, PasteSource;
@@ -16483,7 +17095,7 @@ var init_paste_source = __esm({
16483
17095
  const home = homedir11();
16484
17096
  const dirs = [];
16485
17097
  const tryAdd = (p) => {
16486
- if (existsSync27(p) && !dirs.includes(p))
17098
+ if (existsSync28(p) && !dirs.includes(p))
16487
17099
  dirs.push(p);
16488
17100
  };
16489
17101
  tryAdd(claudeBackend.pasteCacheDir());
@@ -16503,7 +17115,7 @@ var init_paste_source = __esm({
16503
17115
  return;
16504
17116
  const seen = /* @__PURE__ */ new Set();
16505
17117
  for (const cacheDir of this.cacheDirs) {
16506
- if (!existsSync27(cacheDir))
17118
+ if (!existsSync28(cacheDir))
16507
17119
  continue;
16508
17120
  const files = readdirSync21(cacheDir).filter((f) => f.endsWith(".txt"));
16509
17121
  for (const file of files) {
@@ -16537,7 +17149,7 @@ var init_paste_source = __esm({
16537
17149
  }
16538
17150
  }
16539
17151
  async parse(item) {
16540
- if (!existsSync27(item.filePath))
17152
+ if (!existsSync28(item.filePath))
16541
17153
  return [];
16542
17154
  const content = readFileSync19(item.filePath, "utf-8");
16543
17155
  if (content.trim().length < 20)
@@ -16562,7 +17174,7 @@ var init_paste_source = __esm({
16562
17174
  });
16563
17175
 
16564
17176
  // ../engine/dist/parsers/antigravity-brain-source.js
16565
- import { existsSync as existsSync28, readdirSync as readdirSync22, readFileSync as readFileSync20, statSync as statSync26 } from "fs";
17177
+ import { existsSync as existsSync29, readdirSync as readdirSync22, readFileSync as readFileSync20, statSync as statSync26 } from "fs";
16566
17178
  import { join as join29 } from "path";
16567
17179
  var MAX_CHUNK_CHARS7, AntigravityBrainSource;
16568
17180
  var init_antigravity_brain_source = __esm({
@@ -16581,7 +17193,7 @@ var init_antigravity_brain_source = __esm({
16581
17193
  async *discover() {
16582
17194
  if (!isSourceEnabled("agy", "brain"))
16583
17195
  return;
16584
- if (!existsSync28(this.brainDir))
17196
+ if (!existsSync29(this.brainDir))
16585
17197
  return;
16586
17198
  const sessions = readdirSync22(this.brainDir, { withFileTypes: true });
16587
17199
  for (const entry of sessions) {
@@ -16600,7 +17212,7 @@ var init_antigravity_brain_source = __esm({
16600
17212
  const heading = content.split("\n").find((l) => l.startsWith("#"))?.replace(/^#+\s*/, "").trim() || file;
16601
17213
  const metaPath = `${filePath}.metadata.json`;
16602
17214
  let metaSummary = "";
16603
- if (existsSync28(metaPath)) {
17215
+ if (existsSync29(metaPath)) {
16604
17216
  try {
16605
17217
  const meta = JSON.parse(readFileSync20(metaPath, "utf-8"));
16606
17218
  metaSummary = meta.summary || "";
@@ -16631,7 +17243,7 @@ var init_antigravity_brain_source = __esm({
16631
17243
  }
16632
17244
  }
16633
17245
  async parse(item) {
16634
- if (!existsSync28(item.filePath))
17246
+ if (!existsSync29(item.filePath))
16635
17247
  return [];
16636
17248
  const content = readFileSync20(item.filePath, "utf-8");
16637
17249
  const chunks = [];
@@ -16674,7 +17286,7 @@ var init_antigravity_brain_source = __esm({
16674
17286
  });
16675
17287
 
16676
17288
  // ../engine/dist/parsers/opencode-source.js
16677
- import { existsSync as existsSync29 } from "fs";
17289
+ import { existsSync as existsSync30 } from "fs";
16678
17290
  var MAX_CHUNK_CHARS8, OpenCodeSource;
16679
17291
  var init_opencode_source = __esm({
16680
17292
  "../engine/dist/parsers/opencode-source.js"() {
@@ -16692,7 +17304,7 @@ var init_opencode_source = __esm({
16692
17304
  async *discover() {
16693
17305
  if (!isSourceEnabled("opencode", "sessions"))
16694
17306
  return;
16695
- if (!existsSync29(this.dbPath))
17307
+ if (!existsSync30(this.dbPath))
16696
17308
  return;
16697
17309
  let db;
16698
17310
  try {
@@ -16790,7 +17402,7 @@ var init_opencode_source = __esm({
16790
17402
  }
16791
17403
  }
16792
17404
  async parse(item) {
16793
- if (!existsSync29(this.dbPath))
17405
+ if (!existsSync30(this.dbPath))
16794
17406
  return [];
16795
17407
  const sessionId = item.id.replace("opencode_", "");
16796
17408
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -16862,7 +17474,7 @@ var init_opencode_source = __esm({
16862
17474
  });
16863
17475
 
16864
17476
  // ../engine/dist/parsers/opencode-todo-source.js
16865
- import { existsSync as existsSync30 } from "fs";
17477
+ import { existsSync as existsSync31 } from "fs";
16866
17478
  var OpenCodeTodoSource;
16867
17479
  var init_opencode_todo_source = __esm({
16868
17480
  "../engine/dist/parsers/opencode-todo-source.js"() {
@@ -16879,7 +17491,7 @@ var init_opencode_todo_source = __esm({
16879
17491
  async *discover() {
16880
17492
  if (!isSourceEnabled("opencode", "todos"))
16881
17493
  return;
16882
- if (!existsSync30(this.dbPath))
17494
+ if (!existsSync31(this.dbPath))
16883
17495
  return;
16884
17496
  let db;
16885
17497
  try {
@@ -16928,7 +17540,7 @@ var init_opencode_todo_source = __esm({
16928
17540
  }
16929
17541
  }
16930
17542
  async parse(item) {
16931
- if (!existsSync30(this.dbPath))
17543
+ if (!existsSync31(this.dbPath))
16932
17544
  return [];
16933
17545
  const sessionId = item.extra?.sessionId || item.id.replace(`${opencodeBackend.idPrefix}todo_`, "");
16934
17546
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -16976,7 +17588,7 @@ var init_opencode_todo_source = __esm({
16976
17588
  });
16977
17589
 
16978
17590
  // ../engine/dist/parsers/codex-session-source.js
16979
- import { existsSync as existsSync31, readdirSync as readdirSync23, readFileSync as readFileSync21, statSync as statSync27 } from "fs";
17591
+ import { existsSync as existsSync32, readdirSync as readdirSync23, readFileSync as readFileSync21, statSync as statSync27 } from "fs";
16980
17592
  import { join as join30, basename as basename16 } from "path";
16981
17593
  function getCodexSessionsDir() {
16982
17594
  return codexBackend.sessionsDir();
@@ -17033,7 +17645,7 @@ var init_codex_session_source = __esm({
17033
17645
  async *discover() {
17034
17646
  if (!isSourceEnabled("codex", "sessions"))
17035
17647
  return;
17036
- if (!existsSync31(this.sessionsDir))
17648
+ if (!existsSync32(this.sessionsDir))
17037
17649
  return;
17038
17650
  const jsonlFiles = this.collectJsonlFiles(this.sessionsDir);
17039
17651
  for (const filePath of jsonlFiles) {
@@ -17092,7 +17704,7 @@ var init_codex_session_source = __esm({
17092
17704
  }
17093
17705
  }
17094
17706
  async parse(item) {
17095
- if (!existsSync31(item.filePath))
17707
+ if (!existsSync32(item.filePath))
17096
17708
  return [];
17097
17709
  const events = readJsonlLines(item.filePath);
17098
17710
  const chunks = [];
@@ -17195,7 +17807,7 @@ var init_codex_session_source = __esm({
17195
17807
  }
17196
17808
  collectJsonlFiles(dir) {
17197
17809
  const results = [];
17198
- if (!existsSync31(dir))
17810
+ if (!existsSync32(dir))
17199
17811
  return results;
17200
17812
  for (const entry of readdirSync23(dir, { withFileTypes: true })) {
17201
17813
  const fullPath = join30(dir, entry.name);
@@ -17212,7 +17824,7 @@ var init_codex_session_source = __esm({
17212
17824
  });
17213
17825
 
17214
17826
  // ../engine/dist/parsers/diary-source.js
17215
- import { existsSync as existsSync32, mkdirSync as mkdirSync5, readdirSync as readdirSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync4, statSync as statSync28 } from "fs";
17827
+ import { existsSync as existsSync33, mkdirSync as mkdirSync5, readdirSync as readdirSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync4, statSync as statSync28 } from "fs";
17216
17828
  import { join as join31, basename as basename17 } from "path";
17217
17829
  var MAX_CHUNK_CHARS10, DiarySource;
17218
17830
  var init_diary_source = __esm({
@@ -17228,7 +17840,7 @@ var init_diary_source = __esm({
17228
17840
  }
17229
17841
  /** Discover all diary entries across all agents. */
17230
17842
  async *discover() {
17231
- if (!existsSync32(this.diaryDir))
17843
+ if (!existsSync33(this.diaryDir))
17232
17844
  return;
17233
17845
  const agents = readdirSync24(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
17234
17846
  for (const agentDir of agents) {
@@ -17263,7 +17875,7 @@ var init_diary_source = __esm({
17263
17875
  }
17264
17876
  }
17265
17877
  async parse(item) {
17266
- if (!existsSync32(item.filePath))
17878
+ if (!existsSync33(item.filePath))
17267
17879
  return [];
17268
17880
  const raw = readFileSync22(item.filePath, "utf-8");
17269
17881
  const entry = JSON.parse(raw);
@@ -17303,7 +17915,7 @@ var init_diary_source = __esm({
17303
17915
  static write(entry, diaryDir) {
17304
17916
  const dir = diaryDir || getDiaryDir();
17305
17917
  const agentDir = join31(dir, entry.agent.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
17306
- if (!existsSync32(agentDir)) {
17918
+ if (!existsSync33(agentDir)) {
17307
17919
  mkdirSync5(agentDir, { recursive: true });
17308
17920
  }
17309
17921
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -17322,7 +17934,7 @@ var init_diary_source = __esm({
17322
17934
  static read(agentName, lastN = 10, diaryDir) {
17323
17935
  const dir = diaryDir || getDiaryDir();
17324
17936
  const agentDir = join31(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
17325
- if (!existsSync32(agentDir))
17937
+ if (!existsSync33(agentDir))
17326
17938
  return [];
17327
17939
  const files = readdirSync24(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
17328
17940
  const entries = [];
@@ -17373,7 +17985,7 @@ var init_redact_inline = __esm({
17373
17985
  });
17374
17986
 
17375
17987
  // ../engine/dist/parsers/skills-source.js
17376
- import { existsSync as existsSync33, readdirSync as readdirSync25, readFileSync as readFileSync23, statSync as statSync29 } from "fs";
17988
+ import { existsSync as existsSync34, readdirSync as readdirSync25, readFileSync as readFileSync23, statSync as statSync29 } from "fs";
17377
17989
  import { homedir as homedir12 } from "os";
17378
17990
  import { join as join32 } from "path";
17379
17991
  function rebuildBody(content) {
@@ -17413,7 +18025,7 @@ function resolveRoots() {
17413
18025
  all.push({ path: join32(cursorHomeDir(), "skills"), tool: "cursor" });
17414
18026
  all.push({ path: join32(cursorHomeDir(), "skills-cursor"), tool: "cursor", readonly: true });
17415
18027
  }
17416
- return all.filter((r) => existsSync33(r.path));
18028
+ return all.filter((r) => existsSync34(r.path));
17417
18029
  }
17418
18030
  function parseFrontmatter3(text) {
17419
18031
  const m = text.match(/^---\n([\s\S]*?)\n---/);
@@ -17455,7 +18067,7 @@ var init_skills_source = __esm({
17455
18067
  return;
17456
18068
  for (const { name: pluginName, dir } of codexBackend.installedPluginDirs()) {
17457
18069
  const skillsDir = join32(dir, "skills");
17458
- if (!existsSync33(skillsDir))
18070
+ if (!existsSync34(skillsDir))
17459
18071
  continue;
17460
18072
  yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
17461
18073
  }
@@ -17480,7 +18092,7 @@ var init_skills_source = __esm({
17480
18092
  if (!st.isDirectory())
17481
18093
  continue;
17482
18094
  const skillMd = join32(skillDir, "SKILL.md");
17483
- if (!existsSync33(skillMd))
18095
+ if (!existsSync34(skillMd))
17484
18096
  continue;
17485
18097
  let content;
17486
18098
  try {
@@ -17545,7 +18157,7 @@ var init_skills_source = __esm({
17545
18157
  }
17546
18158
  }
17547
18159
  async parse(item) {
17548
- if (!existsSync33(item.filePath))
18160
+ if (!existsSync34(item.filePath))
17549
18161
  return [];
17550
18162
  const content = readFileSync23(item.filePath, "utf-8");
17551
18163
  if (!content.trim())
@@ -17570,11 +18182,11 @@ var init_skills_source = __esm({
17570
18182
  });
17571
18183
 
17572
18184
  // ../engine/dist/parsers/mcps-source.js
17573
- import { existsSync as existsSync34, readFileSync as readFileSync24, statSync as statSync30 } from "fs";
18185
+ import { existsSync as existsSync35, readFileSync as readFileSync24, statSync as statSync30 } from "fs";
17574
18186
  import { homedir as homedir13 } from "os";
17575
18187
  import { join as join33 } from "path";
17576
18188
  function readJson(path) {
17577
- if (!existsSync34(path))
18189
+ if (!existsSync35(path))
17578
18190
  return null;
17579
18191
  try {
17580
18192
  const raw = readFileSync24(path, "utf-8");
@@ -17737,7 +18349,7 @@ var init_mcps_source = __esm({
17737
18349
  }
17738
18350
  }
17739
18351
  async *fromCodexToml(path) {
17740
- if (!existsSync34(path))
18352
+ if (!existsSync35(path))
17741
18353
  return;
17742
18354
  let content;
17743
18355
  try {
@@ -17910,7 +18522,7 @@ var init_toolkit_format = __esm({
17910
18522
  });
17911
18523
 
17912
18524
  // ../engine/dist/parsers/slash-commands-source.js
17913
- import { existsSync as existsSync35, readdirSync as readdirSync26, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
18525
+ import { existsSync as existsSync36, readdirSync as readdirSync26, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
17914
18526
  import { join as join34, basename as basename18 } from "path";
17915
18527
  function rebuildBody2(content) {
17916
18528
  const { text, redacted } = redactInlineSecrets(content);
@@ -17963,14 +18575,14 @@ var init_slash_commands_source = __esm({
17963
18575
  if (isSourceEnabled("claude", "commands")) {
17964
18576
  roots.push({ path: claudeBackend.commandsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
17965
18577
  const projectsRoot = claudeBackend.projectsDir();
17966
- if (existsSync35(projectsRoot)) {
18578
+ if (existsSync36(projectsRoot)) {
17967
18579
  try {
17968
18580
  for (const entry of readdirSync26(projectsRoot, { withFileTypes: true })) {
17969
18581
  if (!entry.isDirectory() || entry.name.startsWith("."))
17970
18582
  continue;
17971
18583
  const projectPath = resolveProjectDirName(entry.name);
17972
18584
  const cmdDir = join34(projectPath, ".claude", "commands");
17973
- if (existsSync35(cmdDir))
18585
+ if (existsSync36(cmdDir))
17974
18586
  roots.push({ path: cmdDir, tool: "claude", scope: "project", projectPath, format: "md" });
17975
18587
  }
17976
18588
  } catch {
@@ -18032,7 +18644,7 @@ var init_slash_commands_source = __esm({
18032
18644
  }
18033
18645
  }
18034
18646
  async parse(item) {
18035
- if (!existsSync35(item.filePath))
18647
+ if (!existsSync36(item.filePath))
18036
18648
  return [];
18037
18649
  const content = readFileSync25(item.filePath, "utf-8");
18038
18650
  if (!content.trim())
@@ -18057,7 +18669,7 @@ var init_slash_commands_source = __esm({
18057
18669
  });
18058
18670
 
18059
18671
  // ../engine/dist/parsers/subagents-source.js
18060
- import { existsSync as existsSync36, readdirSync as readdirSync27, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
18672
+ import { existsSync as existsSync37, readdirSync as readdirSync27, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
18061
18673
  import { join as join35, basename as basename19 } from "path";
18062
18674
  function rebuildBody3(content) {
18063
18675
  const { text, redacted } = redactInlineSecrets(content);
@@ -18112,14 +18724,14 @@ var init_subagents_source = __esm({
18112
18724
  if (isSourceEnabled("claude", "agents")) {
18113
18725
  roots.push({ path: claudeBackend.agentsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
18114
18726
  const projectsRoot = claudeBackend.projectsDir();
18115
- if (existsSync36(projectsRoot)) {
18727
+ if (existsSync37(projectsRoot)) {
18116
18728
  try {
18117
18729
  for (const entry of readdirSync27(projectsRoot, { withFileTypes: true })) {
18118
18730
  if (!entry.isDirectory() || entry.name.startsWith("."))
18119
18731
  continue;
18120
18732
  const projectPath = resolveProjectDirName(entry.name);
18121
18733
  const agentDir = join35(projectPath, ".claude", "agents");
18122
- if (existsSync36(agentDir))
18734
+ if (existsSync37(agentDir))
18123
18735
  roots.push({ path: agentDir, tool: "claude", scope: "project", projectPath, format: "md" });
18124
18736
  }
18125
18737
  } catch {
@@ -18182,7 +18794,7 @@ var init_subagents_source = __esm({
18182
18794
  }
18183
18795
  }
18184
18796
  async parse(item) {
18185
- if (!existsSync36(item.filePath))
18797
+ if (!existsSync37(item.filePath))
18186
18798
  return [];
18187
18799
  const content = readFileSync26(item.filePath, "utf-8");
18188
18800
  if (!content.trim())
@@ -18207,10 +18819,10 @@ var init_subagents_source = __esm({
18207
18819
  });
18208
18820
 
18209
18821
  // ../engine/dist/parsers/hooks-source.js
18210
- import { existsSync as existsSync37, readFileSync as readFileSync27, readdirSync as readdirSync28, statSync as statSync33 } from "fs";
18822
+ import { existsSync as existsSync38, readFileSync as readFileSync27, readdirSync as readdirSync28, statSync as statSync33 } from "fs";
18211
18823
  import { join as join36 } from "path";
18212
18824
  function readJson2(path) {
18213
- if (!existsSync37(path))
18825
+ if (!existsSync38(path))
18214
18826
  return null;
18215
18827
  try {
18216
18828
  return JSON.parse(readFileSync27(path, "utf-8"));
@@ -18232,14 +18844,14 @@ var init_hooks_source = __esm({
18232
18844
  return;
18233
18845
  yield* this.fromSettings(claudeBackend.settingsFile(), "user", "");
18234
18846
  const projectsRoot = claudeBackend.projectsDir();
18235
- if (existsSync37(projectsRoot)) {
18847
+ if (existsSync38(projectsRoot)) {
18236
18848
  try {
18237
18849
  for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
18238
18850
  if (!entry.isDirectory() || entry.name.startsWith("."))
18239
18851
  continue;
18240
18852
  const projectPath = resolveProjectDirName(entry.name);
18241
18853
  const settingsPath2 = join36(projectPath, ".claude", "settings.json");
18242
- if (existsSync37(settingsPath2)) {
18854
+ if (existsSync38(settingsPath2)) {
18243
18855
  yield* this.fromSettings(settingsPath2, "project", projectPath);
18244
18856
  }
18245
18857
  }
@@ -18330,11 +18942,11 @@ var init_hooks_source = __esm({
18330
18942
  });
18331
18943
 
18332
18944
  // ../engine/dist/parsers/plugins-source.js
18333
- import { existsSync as existsSync38, readFileSync as readFileSync28, readdirSync as readdirSync29, statSync as statSync34 } from "fs";
18945
+ import { existsSync as existsSync39, readFileSync as readFileSync28, readdirSync as readdirSync29, statSync as statSync34 } from "fs";
18334
18946
  import { homedir as homedir14 } from "os";
18335
18947
  import { join as join37 } from "path";
18336
18948
  function readJson3(path) {
18337
- if (!existsSync38(path))
18949
+ if (!existsSync39(path))
18338
18950
  return null;
18339
18951
  try {
18340
18952
  return JSON.parse(readFileSync28(path, "utf-8"));
@@ -18389,7 +19001,7 @@ var init_plugins_source = __esm({
18389
19001
  }
18390
19002
  }
18391
19003
  const gemRoot = agyBackend.sharedExtensionsDir();
18392
- if (isSourceEnabled("agy", "extensions") && existsSync38(gemRoot)) {
19004
+ if (isSourceEnabled("agy", "extensions") && existsSync39(gemRoot)) {
18393
19005
  let entries;
18394
19006
  try {
18395
19007
  entries = readdirSync29(gemRoot);
@@ -18565,6 +19177,8 @@ function toolOfId(id) {
18565
19177
  return "codex";
18566
19178
  if (id.startsWith("cursor_"))
18567
19179
  return "cursor";
19180
+ if (id.startsWith("hermes_"))
19181
+ return "hermes";
18568
19182
  return "claude";
18569
19183
  }
18570
19184
  function extractorVersionForTool(tool) {
@@ -18820,6 +19434,13 @@ function markSynced(server, rows) {
18820
19434
  base4.s = s;
18821
19435
  }
18822
19436
  if (r.hash !== void 0) base4.h = r.hash;
19437
+ const completesChunked = cursorFromCaller && r.chunkFinal === true && prev?.k === 1;
19438
+ if (cursorFromCaller && (r.full || completesChunked) && o > 0) base4.F = o;
19439
+ else if (prev?.F !== void 0) base4.F = prev.F;
19440
+ if (cursorFromCaller && r.chunkedHead) base4.k = 1;
19441
+ else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base4.k = 1;
19442
+ const layout = cursorFromCaller ? r.layout : prev?.L;
19443
+ if (layout) base4.L = layout;
18823
19444
  forServer[r.id] = base4;
18824
19445
  }
18825
19446
  persist(data);
@@ -18876,6 +19497,14 @@ function markFieldFullPassDone(server, field) {
18876
19497
  delete st.force[field.name];
18877
19498
  persistReconcile(data);
18878
19499
  }
19500
+ function forgetFullCoverage(server, sessionId) {
19501
+ const data = load();
19502
+ const row = data[server]?.[sessionId];
19503
+ if (!row || typeof row !== "object") return;
19504
+ delete row.F;
19505
+ delete row.h;
19506
+ persist(data);
19507
+ }
18879
19508
  function markFullResync(server, sessionId) {
18880
19509
  const data = load();
18881
19510
  const forServer = data[server];
@@ -18884,7 +19513,11 @@ function markFullResync(server, sessionId) {
18884
19513
  delete forServer[sessionId];
18885
19514
  persist(data);
18886
19515
  }
18887
- function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19516
+ function fullCoveredOffset(row) {
19517
+ if (row.F !== void 0) return row.F;
19518
+ return row.h ? row.s ?? 0 : 0;
19519
+ }
19520
+ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly, nowMs = Date.now()) {
18888
19521
  if (!row) return "full";
18889
19522
  if (row.v < extractorVersion) return "full";
18890
19523
  const offset = row.o ?? 0;
@@ -18892,10 +19525,13 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
18892
19525
  if (process.env.CHAT_RECALL_TAIL_APPEND !== "0" && isAppendOnly && fileSize > 0 && offset > 0 && fileSize > (row.s ?? 0)) {
18893
19526
  return "append";
18894
19527
  }
19528
+ if (isAppendOnly && offset > 0 && fileSize > 0 && fileSize === (row.s ?? 0) && !row.k && fullCoveredOffset(row) < offset && nowMs - Math.floor(mtime) >= SETTLE_QUIET_MS) {
19529
+ return "full";
19530
+ }
18895
19531
  if (row.m >= Math.floor(mtime)) return "skip";
18896
19532
  return "full";
18897
19533
  }
18898
- var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
19534
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache, SETTLE_QUIET_MS;
18899
19535
  var init_sync_ledger = __esm({
18900
19536
  "src/sync-ledger.ts"() {
18901
19537
  "use strict";
@@ -18915,6 +19551,7 @@ var init_sync_ledger = __esm({
18915
19551
  unackedCursorAttempts = 0;
18916
19552
  reconcilePath = () => join38(getDataDir(), "sync-field-reconcile.json");
18917
19553
  reconcileCache = null;
19554
+ SETTLE_QUIET_MS = 2 * 60 * 60 * 1e3;
18918
19555
  }
18919
19556
  });
18920
19557
 
@@ -18944,12 +19581,12 @@ var init_sync_fields = __esm({
18944
19581
  });
18945
19582
 
18946
19583
  // ../engine/dist/core/index-lock.js
18947
- import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as statSync36, closeSync as closeSync8 } from "fs";
19584
+ import { existsSync as existsSync40, mkdirSync as mkdirSync7, openSync as openSync8, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as statSync36, closeSync as closeSync8 } from "fs";
18948
19585
  import { dirname as dirname16, join as join39 } from "path";
18949
19586
  function acquireIndexLock(opts) {
18950
19587
  const lockPath = lockFilePath(opts.file);
18951
19588
  mkdirSync7(dirname16(lockPath), { recursive: true });
18952
- if (existsSync39(lockPath)) {
19589
+ if (existsSync40(lockPath)) {
18953
19590
  if (!stealStaleLock(lockPath, opts.staleAfterMs ?? 10 * 6e4)) {
18954
19591
  return null;
18955
19592
  }
@@ -19066,7 +19703,7 @@ var init_http = __esm({
19066
19703
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19067
19704
  } catch {
19068
19705
  try {
19069
- if (true) CLI_VERSION = "0.7.17";
19706
+ if (true) CLI_VERSION = "0.7.19";
19070
19707
  } catch {
19071
19708
  }
19072
19709
  }
@@ -19163,7 +19800,7 @@ var init_derived = __esm({
19163
19800
 
19164
19801
  // src/scan-pool.ts
19165
19802
  import { Worker } from "node:worker_threads";
19166
- import { existsSync as existsSync40 } from "node:fs";
19803
+ import { existsSync as existsSync41 } from "node:fs";
19167
19804
  import { fileURLToPath } from "node:url";
19168
19805
  import { cpus } from "node:os";
19169
19806
  function poolSize() {
@@ -19178,7 +19815,7 @@ function workerScript() {
19178
19815
  for (const rel of ["./scan-worker.js", "../dist/scan-worker.js"]) {
19179
19816
  try {
19180
19817
  const p = fileURLToPath(new URL(rel, import.meta.url));
19181
- if (existsSync40(p)) return p;
19818
+ if (existsSync41(p)) return p;
19182
19819
  } catch {
19183
19820
  }
19184
19821
  }
@@ -19564,7 +20201,7 @@ var init_telemetry = __esm({
19564
20201
  init_sync_client();
19565
20202
  cliVersion2 = () => {
19566
20203
  try {
19567
- return true ? "0.7.17" : "dev";
20204
+ return true ? "0.7.19" : "dev";
19568
20205
  } catch {
19569
20206
  return "dev";
19570
20207
  }
@@ -19727,6 +20364,150 @@ var init_collector_health = __esm({
19727
20364
  }
19728
20365
  });
19729
20366
 
20367
+ // src/raw-archive-parts.ts
20368
+ import { createReadStream as createReadStream4, createWriteStream, openSync as openSync9, readSync as readSync8, closeSync as closeSync9, rmSync as rmSync5, statSync as statSync37 } from "node:fs";
20369
+ import { tmpdir as tmpdir2 } from "node:os";
20370
+ import { join as join41 } from "node:path";
20371
+ import { randomBytes } from "node:crypto";
20372
+ import { createGzip } from "node:zlib";
20373
+ import { once } from "node:events";
20374
+ import { readFileSync as readFileSync33 } from "node:fs";
20375
+ async function* fileLines(path) {
20376
+ let rest = "";
20377
+ const decoder = new TextDecoder("utf-8");
20378
+ for await (const chunk of createReadStream4(path, { highWaterMark: 1024 * 1024 })) {
20379
+ rest += decoder.decode(chunk, { stream: true });
20380
+ let nl = rest.indexOf("\n");
20381
+ while (nl >= 0) {
20382
+ yield rest.slice(0, nl + 1);
20383
+ rest = rest.slice(nl + 1);
20384
+ nl = rest.indexOf("\n");
20385
+ }
20386
+ }
20387
+ rest += decoder.decode();
20388
+ if (rest) yield rest;
20389
+ }
20390
+ function* textLines(text) {
20391
+ let at = 0;
20392
+ while (at < text.length) {
20393
+ const nl = text.indexOf("\n", at);
20394
+ const end = nl < 0 ? text.length : nl + 1;
20395
+ yield text.slice(at, end);
20396
+ at = end;
20397
+ }
20398
+ }
20399
+ async function writeArchiveFile(source, meta, redact, dir = tmpdir2()) {
20400
+ const path = join41(dir, `cr-archive-${randomBytes(8).toString("hex")}.json.gz`);
20401
+ const gzip = createGzip({ level: 6 });
20402
+ const out = createWriteStream(path, { mode: 384 });
20403
+ const done = once(out, "finish");
20404
+ gzip.pipe(out);
20405
+ let size = 0;
20406
+ const write = async (s) => {
20407
+ size += Buffer.byteLength(s);
20408
+ if (!gzip.write(s)) await once(gzip, "drain");
20409
+ };
20410
+ try {
20411
+ await write(`{"v":1,"tool":${JSON.stringify(meta.tool)},"mtime":${Math.floor(meta.mtime)},"files":[`);
20412
+ for (const [i, f] of source.files.entries()) {
20413
+ await write(`${i ? "," : ""}{"name":${JSON.stringify(f.name)},"text":"`);
20414
+ const whole = () => source.kind === "memory" ? f.text : readFileSync33(f.path, "utf-8");
20415
+ if (!f.name.endsWith(".jsonl")) {
20416
+ await write(escapeJson(redact(whole())));
20417
+ } else {
20418
+ const lines = source.kind === "memory" ? textLines(f.text) : fileLines(f.path);
20419
+ for await (const line of lines) {
20420
+ const nl = line.endsWith("\n");
20421
+ const body2 = nl ? line.slice(0, -1) : line;
20422
+ await write(escapeJson((body2 ? redactJsonLine(body2, redact) : body2) + (nl ? "\n" : "")));
20423
+ }
20424
+ }
20425
+ await write('"}');
20426
+ }
20427
+ await write("]}");
20428
+ gzip.end();
20429
+ await done;
20430
+ } catch (err) {
20431
+ gzip.destroy();
20432
+ out.destroy();
20433
+ rmSync5(path, { force: true });
20434
+ throw err;
20435
+ }
20436
+ return { path, gzSize: statSync37(path).size, size };
20437
+ }
20438
+ async function putPart(url, body2) {
20439
+ let last;
20440
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
20441
+ const ac = new AbortController();
20442
+ const timer = setTimeout(() => ac.abort(), PART_TIMEOUT_MS);
20443
+ try {
20444
+ const r = await fetch(url, { method: "PUT", body: body2, signal: ac.signal });
20445
+ const etag = r.headers.get("etag");
20446
+ if (r.ok && etag) return etag;
20447
+ last = new Error(`part PUT answered ${r.status}`);
20448
+ if (r.status === 403 || r.status === 404) break;
20449
+ } catch (err) {
20450
+ last = err;
20451
+ } finally {
20452
+ clearTimeout(timer);
20453
+ }
20454
+ }
20455
+ throw last instanceof Error ? last : new Error(String(last));
20456
+ }
20457
+ async function uploadArchiveFile(base4, token, args) {
20458
+ const headers = { "content-type": "application/json" };
20459
+ if (token) headers.authorization = `Bearer ${token}`;
20460
+ const post = async (path, body2) => {
20461
+ const r = await fetch(`${base4}/api/sync/raw-upload/${path}`, { method: "POST", headers, body: JSON.stringify(body2) });
20462
+ const json = await r.json().catch(() => ({}));
20463
+ return { status: r.status, json };
20464
+ };
20465
+ const start = await post("start", {
20466
+ session_id: args.sessionId,
20467
+ tool: args.tool,
20468
+ mtime: Math.floor(args.mtime),
20469
+ size: args.file.size,
20470
+ gz_size: args.file.gzSize,
20471
+ project_id: args.projectId,
20472
+ project_path: args.projectPath
20473
+ });
20474
+ if (start.status === 501 || start.status === 404) return "unsupported";
20475
+ if (start.status !== 200) throw new Error(`raw upload start answered ${start.status}: ${start.json.error ?? ""}`);
20476
+ if (start.json.result === "unchanged" || start.json.result === "shrink-protected") return start.json.result;
20477
+ const urls = start.json.urls;
20478
+ const partBytes = Number(start.json.part_bytes);
20479
+ const parts = [];
20480
+ const fd = openSync9(args.file.path, "r");
20481
+ try {
20482
+ for (const [i, url] of urls.entries()) {
20483
+ const len = Math.min(partBytes, args.file.gzSize - i * partBytes);
20484
+ const buf = Buffer.allocUnsafe(len);
20485
+ let got = 0;
20486
+ while (got < len) {
20487
+ const n = readSync8(fd, buf, got, len - got, i * partBytes + got);
20488
+ if (n <= 0) break;
20489
+ got += n;
20490
+ }
20491
+ parts.push({ part_number: i + 1, etag: await putPart(url, buf.subarray(0, got)) });
20492
+ }
20493
+ } finally {
20494
+ closeSync9(fd);
20495
+ }
20496
+ const done = await post("complete", { session_id: args.sessionId, upload_id: start.json.upload_id, parts });
20497
+ if (done.status !== 200) throw new Error(`raw upload complete answered ${done.status}: ${done.json.error ?? ""}`);
20498
+ return done.json.result;
20499
+ }
20500
+ var escapeJson, PART_TIMEOUT_MS, PART_ATTEMPTS;
20501
+ var init_raw_archive_parts = __esm({
20502
+ "src/raw-archive-parts.ts"() {
20503
+ "use strict";
20504
+ init_transcript();
20505
+ escapeJson = (s) => JSON.stringify(s).slice(1, -1);
20506
+ PART_TIMEOUT_MS = 12e4;
20507
+ PART_ATTEMPTS = 3;
20508
+ }
20509
+ });
20510
+
19730
20511
  // src/server-probe.ts
19731
20512
  var server_probe_exports = {};
19732
20513
  __export(server_probe_exports, {
@@ -19870,7 +20651,7 @@ __export(client_events_exports, {
19870
20651
  isTelemetryEnabled: () => isTelemetryEnabled,
19871
20652
  reportClientEvent: () => reportClientEvent
19872
20653
  });
19873
- import { readFileSync as readFileSync33 } from "node:fs";
20654
+ import { readFileSync as readFileSync34 } from "node:fs";
19874
20655
  function scrub(msg) {
19875
20656
  if (!msg) return "";
19876
20657
  const noHome = msg.replace(/\/(?:home|Users)\/[^/\s]+/g, "~");
@@ -19923,7 +20704,7 @@ var init_client_events = __esm({
19923
20704
  init_secret_redactor();
19924
20705
  CLI_VERSION2 = "0.0.0";
19925
20706
  try {
19926
- CLI_VERSION2 = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
20707
+ CLI_VERSION2 = JSON.parse(readFileSync34(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
19927
20708
  } catch {
19928
20709
  }
19929
20710
  }
@@ -19953,11 +20734,11 @@ __export(auto_update_exports, {
19953
20734
  tarballUrl: () => tarballUrl
19954
20735
  });
19955
20736
  import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
19956
- import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
20737
+ import { readFileSync as readFileSync35, existsSync as existsSync42 } from "node:fs";
19957
20738
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19958
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as statSync37, mkdirSync as mkdirSync9 } from "node:fs";
19959
- import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
19960
- import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
20739
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync30, statSync as statSync38, mkdirSync as mkdirSync9 } from "node:fs";
20740
+ import { join as join42, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
20741
+ import { tmpdir as tmpdir3, homedir as homedir15 } from "node:os";
19961
20742
  import { createHash as createHash8 } from "node:crypto";
19962
20743
  function compareVersions(a, b) {
19963
20744
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -19981,7 +20762,7 @@ function newerOf(a, b) {
19981
20762
  function installedVersion() {
19982
20763
  try {
19983
20764
  const here = dirname18(fileURLToPath2(import.meta.url));
19984
- const raw = readFileSync34(join41(here, "..", "package.json"), "utf-8");
20765
+ const raw = readFileSync35(join42(here, "..", "package.json"), "utf-8");
19985
20766
  return JSON.parse(raw).version || null;
19986
20767
  } catch {
19987
20768
  return null;
@@ -19994,7 +20775,7 @@ function planAutoUpdate(base4, caps, ownVersion3, flag) {
19994
20775
  if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
19995
20776
  return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base4, rel.version), fallbackUrl: tarballUrl(base4), sha256: rel.sha256, from: ownVersion3, to: rel.version };
19996
20777
  }
19997
- function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20778
+ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
19998
20779
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
19999
20780
  const COLD_MS = 60 * 60 * 1e3;
20000
20781
  let removed = 0;
@@ -20006,13 +20787,13 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20006
20787
  }
20007
20788
  for (const name of entries) {
20008
20789
  if (!MKDTEMP_SUFFIX.test(name)) continue;
20009
- const dir = join41(root, name);
20790
+ const dir = join42(root, name);
20010
20791
  try {
20011
- const st = statSync37(dir);
20792
+ const st = statSync38(dir);
20012
20793
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20013
20794
  const inside = readdirSync30(dir);
20014
20795
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20015
- rmSync5(dir, { recursive: true, force: true });
20796
+ rmSync6(dir, { recursive: true, force: true });
20016
20797
  removed++;
20017
20798
  } catch {
20018
20799
  }
@@ -20035,14 +20816,14 @@ async function executeAutoUpdate(plan, deps) {
20035
20816
  }
20036
20817
  const got = createHash8("sha256").update(buf).digest("hex");
20037
20818
  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` };
20038
- const dir = mkdtempSync3(join41(tmpdir2(), "cr-update-"));
20819
+ const dir = mkdtempSync3(join42(tmpdir3(), "cr-update-"));
20039
20820
  const discardStaging = () => {
20040
20821
  try {
20041
- rmSync5(dir, { recursive: true, force: true });
20822
+ rmSync6(dir, { recursive: true, force: true });
20042
20823
  } catch {
20043
20824
  }
20044
20825
  };
20045
- const tgz = join41(dir, "chat-recall.tgz");
20826
+ const tgz = join42(dir, "chat-recall.tgz");
20046
20827
  writeFileSync8(tgz, buf);
20047
20828
  if (deps.stopping?.()) {
20048
20829
  discardStaging();
@@ -20114,7 +20895,7 @@ function resolveNpm(loc) {
20114
20895
  }
20115
20896
  function realInstall(tgz) {
20116
20897
  const prefix = runningPrefix();
20117
- const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync41 });
20898
+ const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync42 });
20118
20899
  if (prefix) {
20119
20900
  npmInstall(npm, ["install", "-g", "--prefix", prefix, tgz]);
20120
20901
  return;
@@ -20122,7 +20903,7 @@ function realInstall(tgz) {
20122
20903
  try {
20123
20904
  npmInstall(npm, ["install", "-g", tgz]);
20124
20905
  } catch {
20125
- npmInstall(npm, ["install", "-g", "--prefix", join41(homedir15(), ".local"), tgz]);
20906
+ npmInstall(npm, ["install", "-g", "--prefix", join42(homedir15(), ".local"), tgz]);
20126
20907
  }
20127
20908
  }
20128
20909
  function acquireUpdateLock() {
@@ -20139,12 +20920,12 @@ function realRestart(platform2) {
20139
20920
  if (c) execSync2(c.cmd, { stdio: "ignore", ...c.shell ? { shell: c.shell } : {} });
20140
20921
  }
20141
20922
  function updateStatePath() {
20142
- const dir = process.env.CHAT_RECALL_DATA_DIR || join41(homedir15(), ".chat-recall");
20143
- return join41(dir, "auto-update-state.json");
20923
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join42(homedir15(), ".chat-recall");
20924
+ return join42(dir, "auto-update-state.json");
20144
20925
  }
20145
20926
  function readUpdateState() {
20146
20927
  try {
20147
- return JSON.parse(readFileSync34(updateStatePath(), "utf8"));
20928
+ return JSON.parse(readFileSync35(updateStatePath(), "utf8"));
20148
20929
  } catch {
20149
20930
  return {};
20150
20931
  }
@@ -20285,9 +21066,9 @@ __export(sync_client_exports, {
20285
21066
  syncIncremental: () => syncIncremental,
20286
21067
  syncSessions: () => syncSessions
20287
21068
  });
20288
- import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
20289
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as statSync38 } from "node:fs";
20290
- import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
21069
+ import { join as join43, dirname as dirname19, basename as basename20 } from "node:path";
21070
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync7, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as statSync39 } from "node:fs";
21071
+ import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
20291
21072
  import { createHash as createHash9 } from "node:crypto";
20292
21073
  import { fileURLToPath as fileURLToPath3 } from "node:url";
20293
21074
  import { gzipSync as gzipSync3 } from "zlib";
@@ -20341,7 +21122,7 @@ function loadAllCredentials() {
20341
21122
  const fromEnv = envCredentials();
20342
21123
  if (fromEnv) return [{ serverUrl: fromEnv.serverUrl, token: fromEnv.token }];
20343
21124
  try {
20344
- const parsed = JSON.parse(readFileSync35(credPath(), "utf-8"));
21125
+ const parsed = JSON.parse(readFileSync36(credPath(), "utf-8"));
20345
21126
  if (Array.isArray(parsed?.targets)) return parsed.targets.filter((t) => t?.serverUrl).map((t) => ({ serverUrl: t.serverUrl, token: t.token || "" }));
20346
21127
  if (parsed?.serverUrl) return [{ serverUrl: parsed.serverUrl, token: parsed.token || "" }];
20347
21128
  } catch {
@@ -20403,11 +21184,11 @@ function _resetTenantSecurityConfigCache() {
20403
21184
  _securityConfigCache.clear();
20404
21185
  }
20405
21186
  function syncConfigCachePath() {
20406
- return join42(getDataDir(), "sync-config-cache.json");
21187
+ return join43(getDataDir(), "sync-config-cache.json");
20407
21188
  }
20408
21189
  function readPersistedSyncConfigs() {
20409
21190
  try {
20410
- return JSON.parse(readFileSync35(syncConfigCachePath(), "utf-8"));
21191
+ return JSON.parse(readFileSync36(syncConfigCachePath(), "utf-8"));
20411
21192
  } catch {
20412
21193
  return {};
20413
21194
  }
@@ -20685,7 +21466,7 @@ async function reconcileFields(opts = {}) {
20685
21466
  }
20686
21467
  return { sessions: fullRefs().length, scanned, pushed, absent, perTarget };
20687
21468
  }
20688
- function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now()) {
21469
+ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now()) {
20689
21470
  let reaped = 0;
20690
21471
  let names;
20691
21472
  try {
@@ -20695,10 +21476,10 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
20695
21476
  }
20696
21477
  for (const name of names) {
20697
21478
  if (!name.startsWith("cr-batchscan-")) continue;
20698
- const p = join42(dir, name);
21479
+ const p = join43(dir, name);
20699
21480
  try {
20700
- if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
20701
- rmSync6(p, { recursive: true, force: true });
21481
+ if (now - statSync39(p).mtimeMs < maxAgeMs) continue;
21482
+ rmSync7(p, { recursive: true, force: true });
20702
21483
  reaped++;
20703
21484
  } catch {
20704
21485
  }
@@ -20712,7 +21493,7 @@ function batchScanExternal(items, opts = {}) {
20712
21493
  let scanned = 0, slices = 0, scanMs = 0;
20713
21494
  if (items.length === 0) return { findings, scanned, slices, scanMs };
20714
21495
  reapStaleScanDirs();
20715
- let scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
21496
+ let scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20716
21497
  let safeToId = /* @__PURE__ */ new Map();
20717
21498
  let bytesInDir = 0;
20718
21499
  const flushSlice = () => {
@@ -20730,7 +21511,7 @@ function batchScanExternal(items, opts = {}) {
20730
21511
  slices++;
20731
21512
  }
20732
21513
  try {
20733
- rmSync6(scanDir, { recursive: true, force: true });
21514
+ rmSync7(scanDir, { recursive: true, force: true });
20734
21515
  } catch {
20735
21516
  }
20736
21517
  safeToId = /* @__PURE__ */ new Map();
@@ -20743,19 +21524,19 @@ function batchScanExternal(items, opts = {}) {
20743
21524
  if (raw === null) continue;
20744
21525
  const safe = item.id.replace(/[^A-Za-z0-9_-]/g, "");
20745
21526
  safeToId.set(safe, item.id);
20746
- writeFileSync9(join42(scanDir, `${safe}.txt`), raw);
21527
+ writeFileSync9(join43(scanDir, `${safe}.txt`), raw);
20747
21528
  bytesInDir += Buffer.byteLength(raw);
20748
21529
  } catch {
20749
21530
  }
20750
21531
  if (bytesInDir >= maxBytes) {
20751
21532
  flushSlice();
20752
- scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
21533
+ scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20753
21534
  }
20754
21535
  }
20755
21536
  flushSlice();
20756
21537
  } finally {
20757
21538
  try {
20758
- rmSync6(scanDir, { recursive: true, force: true });
21539
+ rmSync7(scanDir, { recursive: true, force: true });
20759
21540
  } catch {
20760
21541
  }
20761
21542
  }
@@ -20967,6 +21748,11 @@ async function syncToTargetInScope(cred, opts = {}) {
20967
21748
  };
20968
21749
  const localSessionIds = /* @__PURE__ */ new Set();
20969
21750
  const shippedHash = /* @__PURE__ */ new Map();
21751
+ const shippedLayout = /* @__PURE__ */ new Map();
21752
+ const layoutOf = (id) => {
21753
+ const layout = shippedLayout.get(id);
21754
+ return layout ? { layout } : {};
21755
+ };
20970
21756
  const makeBatcher = (field, maxItems, flatten = false, onFlush, barrier) => {
20971
21757
  let batch = [];
20972
21758
  let batchBytes = 0;
@@ -20999,12 +21785,13 @@ async function syncToTargetInScope(cred, opts = {}) {
20999
21785
  markSynced(base4, rows.map((r) => {
21000
21786
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
21001
21787
  const hash = shippedHash.get(r.session_id);
21002
- 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 };
21788
+ 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 };
21003
21789
  }));
21004
21790
  } catch {
21005
21791
  }
21006
21792
  });
21007
21793
  const appendResults = { uploaded: 0, fullResyncNeeded: /* @__PURE__ */ new Set() };
21794
+ const pendingArchives = [];
21008
21795
  const postAppend = async (convs) => {
21009
21796
  const headers = { "content-type": "application/json" };
21010
21797
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -21039,8 +21826,11 @@ async function syncToTargetInScope(cred, opts = {}) {
21039
21826
  offset: c.from_offset,
21040
21827
  size: c.from_offset,
21041
21828
  // size = new offset (file grew to here)
21042
- acked: true
21829
+ acked: true,
21043
21830
  // server merged this tail
21831
+ chunkFinal: c.final === true,
21832
+ // completes a chunked full sync
21833
+ ...layoutOf(c.session_id)
21044
21834
  })));
21045
21835
  } catch {
21046
21836
  }
@@ -21072,9 +21862,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21072
21862
  const mtime = conv.mtime;
21073
21863
  try {
21074
21864
  if (staged || !guarded) {
21075
- markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21865
+ markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true, ...layoutOf(id) }]);
21076
21866
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21077
- markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
21867
+ markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true, ...layoutOf(id) }]);
21078
21868
  } else {
21079
21869
  markSynced(base4, [{ id, mtime }]);
21080
21870
  }
@@ -21169,8 +21959,8 @@ async function syncToTargetInScope(cred, opts = {}) {
21169
21959
  if (!ledger) return ref.mtime >= (opts.sinceMs ?? 0) ? "full" : "skip";
21170
21960
  const ack = ledger.get(ref.prefixedId);
21171
21961
  const backend = backendFor(ref);
21172
- const split = !!backend?.spansMultipleSources?.(ref.prefixedId);
21173
- const isAO = !!backend?.isAppendOnly?.() && !split;
21962
+ const sameLayout = (ack?.L ?? "") === (backend?.sourceLayout?.(ref.prefixedId) ?? "");
21963
+ const isAO = !!backend?.isAppendOnly?.() && sameLayout;
21174
21964
  const size = isAO ? backend?.fileSize?.(ref.prefixedId) ?? 0 : 0;
21175
21965
  return syncMode(ack, ref.mtime, size, extractorVersionForTool(ref.toolId), isAO);
21176
21966
  };
@@ -21217,6 +22007,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21217
22007
  skipped++;
21218
22008
  continue;
21219
22009
  }
22010
+ const layout = backendFor(ref)?.sourceLayout?.(ref.prefixedId) ?? "";
22011
+ if (layout) shippedLayout.set(ref.prefixedId, layout);
22012
+ else shippedLayout.delete(ref.prefixedId);
21220
22013
  trace?.(`build (${mode}) ${ref.prefixedId} \xB7 ${(sessionFileBytes(ref) / 1048576).toFixed(1)}MB`);
21221
22014
  let promotedForFindings = false;
21222
22015
  if (mode === "append" && ledger) {
@@ -21262,7 +22055,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21262
22055
  const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
21263
22056
  const priorCursor = ackRow?.s ?? 0;
21264
22057
  const safeToAck = off > 0 && off <= priorCursor;
21265
- markSynced(base4, [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 }]);
22058
+ markSynced(base4, [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 }]);
21266
22059
  } catch {
21267
22060
  }
21268
22061
  }
@@ -21283,6 +22076,14 @@ async function syncToTargetInScope(cred, opts = {}) {
21283
22076
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
21284
22077
  if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
21285
22078
  else await convBatch.add(built.conv);
22079
+ if (built.archive) {
22080
+ pendingArchives.push({
22081
+ id: ref.prefixedId,
22082
+ ...built.archive,
22083
+ projectId: built.conv.project_id,
22084
+ projectPath: built.conv.project_path
22085
+ });
22086
+ }
21286
22087
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
21287
22088
  if (upload.sessionMeta) {
21288
22089
  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);
@@ -21435,6 +22236,30 @@ async function syncToTargetInScope(cred, opts = {}) {
21435
22236
  await kgTripleBatch.drain();
21436
22237
  await findingsBatch.drain();
21437
22238
  await drainInflight();
22239
+ let archivesUnsupported = false;
22240
+ for (const a of pendingArchives) {
22241
+ try {
22242
+ if (!archivesUnsupported) {
22243
+ const r = await uploadArchiveFile(base4, cred.token, {
22244
+ sessionId: a.id,
22245
+ tool: a.tool,
22246
+ mtime: a.mtime,
22247
+ projectId: a.projectId,
22248
+ projectPath: a.projectPath,
22249
+ file: a.file
22250
+ });
22251
+ if (r === "unsupported") archivesUnsupported = true;
22252
+ }
22253
+ } catch (err) {
22254
+ console.error(`[sync] raw archive for ${a.id} not uploaded (tried again when the session is next quiet): ${err instanceof Error ? err.message : err}`);
22255
+ try {
22256
+ forgetFullCoverage(base4, a.id);
22257
+ } catch {
22258
+ }
22259
+ } finally {
22260
+ rmSync7(a.file.path, { force: true });
22261
+ }
22262
+ }
21438
22263
  {
21439
22264
  const sentHashes = loadInventoryHashes(base4);
21440
22265
  const entries = [];
@@ -21540,6 +22365,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21540
22365
  let includeRaw = opts.includeRaw !== false;
21541
22366
  let includeMeta = opts.includeMeta !== false;
21542
22367
  let srcHash;
22368
+ const wantRaw = opts.includeRaw !== false;
21543
22369
  const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
21544
22370
  let headOffsetEnd = 0;
21545
22371
  let oversizedFindings = null;
@@ -21708,7 +22534,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21708
22534
  } else if (opts.scanSecrets) {
21709
22535
  let tmp = null;
21710
22536
  try {
21711
- tmp = join42(tmpdir3(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
22537
+ tmp = join43(tmpdir4(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
21712
22538
  writeFileSync9(tmp, rawText);
21713
22539
  for (const f of scanFileForSecrets(tmp, { verifyOnly: opts.verifySecrets, tenantRules: opts.tenantRules })) {
21714
22540
  const verifiedAt = f.verified ? (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -21760,6 +22586,25 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21760
22586
  }
21761
22587
  const envTexts = envelope.messages.filter((m) => m.content?.trim());
21762
22588
  const redactedText = envTexts.map((m) => m.content).join("\n");
22589
+ let archive;
22590
+ if (wantRaw && !raw_b64) {
22591
+ try {
22592
+ const redact = (t) => redactSecrets(t, { force: true, count });
22593
+ let source = null;
22594
+ let archiveMtime = Math.floor(ref.mtime) || 0;
22595
+ let archiveTool = ref.toolId;
22596
+ if (container) {
22597
+ source = { kind: "memory", files: container.files };
22598
+ archiveMtime = container.mtime;
22599
+ archiveTool = container.tool;
22600
+ } else if (oversized) {
22601
+ source = diskArchiveSource(ref);
22602
+ }
22603
+ if (source) archive = { file: await writeArchiveFile(source, { tool: archiveTool, mtime: archiveMtime }, redact), tool: archiveTool, mtime: archiveMtime };
22604
+ } catch (err) {
22605
+ console.error(`[sync] raw archive for ${ref.prefixedId} not written: ${err instanceof Error ? err.message : err}`);
22606
+ }
22607
+ }
21763
22608
  return {
21764
22609
  conv: {
21765
22610
  session_id: ref.prefixedId,
@@ -21791,7 +22636,17 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21791
22636
  redactions: count.redactions,
21792
22637
  findings,
21793
22638
  scanMs,
21794
- srcHash
22639
+ srcHash,
22640
+ archive
22641
+ };
22642
+ }
22643
+ function diskArchiveSource(ref) {
22644
+ if (ref.toolId !== "claude") return null;
22645
+ const groups = resolveSessionContentGroups(ref.rawId);
22646
+ if (groups.length === 0 || groups.some((g) => g.paths.length !== 1)) return null;
22647
+ return {
22648
+ kind: "disk",
22649
+ files: groups.map((g) => ({ name: g.name === "main" ? `${ref.rawId}.jsonl` : g.name, path: g.paths[0] }))
21795
22650
  };
21796
22651
  }
21797
22652
  function parseTailMessages(tool, text) {
@@ -21947,14 +22802,15 @@ var init_sync_client = __esm({
21947
22802
  init_telemetry();
21948
22803
  init_collector_health();
21949
22804
  init_live_session_scan();
22805
+ init_raw_archive_parts();
21950
22806
  init_backends();
21951
22807
  ownVersion = "0.0.0";
21952
22808
  try {
21953
22809
  const dir = dirname19(fileURLToPath3(import.meta.url));
21954
- ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
22810
+ ownVersion = JSON.parse(readFileSync36(join43(dir, "../package.json"), "utf-8")).version || ownVersion;
21955
22811
  } catch {
21956
22812
  try {
21957
- if (true) ownVersion = "0.7.17";
22813
+ if (true) ownVersion = "0.7.19";
21958
22814
  } catch {
21959
22815
  }
21960
22816
  }
@@ -21966,7 +22822,7 @@ var init_sync_client = __esm({
21966
22822
  ]);
21967
22823
  walkProgressForUpload = null;
21968
22824
  lastServerPackSpec = null;
21969
- credPath = () => join42(getDataDir(), "credentials.json");
22825
+ credPath = () => join43(getDataDir(), "credentials.json");
21970
22826
  _securityConfigCache = /* @__PURE__ */ new Map();
21971
22827
  SECURITY_CONFIG_TTL_MS = 5 * 60 * 1e3;
21972
22828
  _syncConfigCache = /* @__PURE__ */ new Map();
@@ -21997,15 +22853,15 @@ __export(update_notice_exports, {
21997
22853
  refreshUpdateCheck: () => refreshUpdateCheck,
21998
22854
  updateNotice: () => updateNotice
21999
22855
  });
22000
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync10 } from "node:fs";
22001
- import { join as join43 } from "node:path";
22856
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync10 } from "node:fs";
22857
+ import { join as join44 } from "node:path";
22002
22858
  function newestKnown(c) {
22003
22859
  const npm = c.npmVersion ?? "0.0.0";
22004
22860
  return compareVersions(npm, c.serverVersion) > 0 ? { version: npm, source: "npm" } : { version: c.serverVersion, source: "server" };
22005
22861
  }
22006
22862
  function readCheck() {
22007
22863
  try {
22008
- const c = JSON.parse(readFileSync36(checkPath(), "utf-8"));
22864
+ const c = JSON.parse(readFileSync37(checkPath(), "utf-8"));
22009
22865
  return c && typeof c.serverVersion === "string" ? c : null;
22010
22866
  } catch {
22011
22867
  return null;
@@ -22099,17 +22955,17 @@ var init_update_notice = __esm({
22099
22955
  init_http();
22100
22956
  CHECK_TTL_MS = 6 * 3600 * 1e3;
22101
22957
  NPM_LATEST_URL = "https://registry.npmjs.org/chat-recall";
22102
- checkPath = () => join43(getDataDir(), "update-check.json");
22958
+ checkPath = () => join44(getDataDir(), "update-check.json");
22103
22959
  printed = false;
22104
22960
  }
22105
22961
  });
22106
22962
 
22107
22963
  // ../engine/dist/core/which.js
22108
- import { existsSync as existsSync42, statSync as statSync39, accessSync, constants } from "node:fs";
22109
- import { join as join44, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22964
+ import { existsSync as existsSync43, statSync as statSync40, accessSync, constants } from "node:fs";
22965
+ import { join as join45, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22110
22966
  function executable(p) {
22111
22967
  try {
22112
- if (!statSync39(p).isFile())
22968
+ if (!statSync40(p).isFile())
22113
22969
  return false;
22114
22970
  } catch {
22115
22971
  return false;
@@ -22145,8 +23001,8 @@ function resolveOnPath(name) {
22145
23001
  if (!dir)
22146
23002
  continue;
22147
23003
  for (const c of candidates(name)) {
22148
- const full = join44(dir, c);
22149
- if (existsSync42(full) && executable(full))
23004
+ const full = join45(dir, c);
23005
+ if (existsSync43(full) && executable(full))
22150
23006
  return full;
22151
23007
  }
22152
23008
  }
@@ -22209,9 +23065,9 @@ __export(mcp_clients_exports, {
22209
23065
  inspectMcpClients: () => inspectMcpClients,
22210
23066
  registerMcpEverywhere: () => registerMcpEverywhere
22211
23067
  });
22212
- import { existsSync as existsSync44, mkdirSync as mkdirSync12, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "fs";
23068
+ import { existsSync as existsSync45, mkdirSync as mkdirSync12, readFileSync as readFileSync38, writeFileSync as writeFileSync12 } from "fs";
22213
23069
  import { homedir as homedir16 } from "os";
22214
- import { dirname as dirname21, join as join46 } from "path";
23070
+ import { dirname as dirname21, join as join47 } from "path";
22215
23071
  function envHome(varName, fallback) {
22216
23072
  const v = process.env[varName];
22217
23073
  return v && v.trim() ? v.trim() : fallback;
@@ -22221,14 +23077,14 @@ function writeFileMkdir(path, body2) {
22221
23077
  writeFileSync12(path, body2);
22222
23078
  }
22223
23079
  function readJsonObject(path) {
22224
- const raw = readFileSync37(path, "utf-8");
23080
+ const raw = readFileSync38(path, "utf-8");
22225
23081
  if (raw.trim() === "")
22226
23082
  return {};
22227
23083
  return JSON.parse(raw);
22228
23084
  }
22229
23085
  function registerMcpServersJson(target, path, spec) {
22230
23086
  let cfg = {};
22231
- if (existsSync44(path)) {
23087
+ if (existsSync45(path)) {
22232
23088
  try {
22233
23089
  cfg = readJsonObject(path);
22234
23090
  } catch (err) {
@@ -22257,7 +23113,7 @@ function registerOpencodeJson(path, spec) {
22257
23113
  const id = "opencode";
22258
23114
  const label = "OpenCode";
22259
23115
  let cfg = {};
22260
- if (existsSync44(path)) {
23116
+ if (existsSync45(path)) {
22261
23117
  try {
22262
23118
  cfg = readJsonObject(path);
22263
23119
  } catch (err) {
@@ -22296,7 +23152,7 @@ function registerCodexToml(path, spec) {
22296
23152
  const id = "codex";
22297
23153
  const label = "Codex";
22298
23154
  const block = codexBlock(spec);
22299
- const prev = existsSync44(path) ? readFileSync37(path, "utf-8") : "";
23155
+ const prev = existsSync45(path) ? readFileSync38(path, "utf-8") : "";
22300
23156
  const lines = prev.length ? prev.split("\n") : [];
22301
23157
  const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
22302
23158
  if (start === -1) {
@@ -22337,9 +23193,9 @@ function inspectMcpClients(opts = {}) {
22337
23193
  return MCP_CLIENTS.map((c) => {
22338
23194
  const path = c.configPath(home);
22339
23195
  let registered = false;
22340
- if (existsSync44(path)) {
23196
+ if (existsSync45(path)) {
22341
23197
  try {
22342
- const raw = readFileSync37(path, "utf-8");
23198
+ const raw = readFileSync38(path, "utf-8");
22343
23199
  registered = c.id === "codex" ? raw.includes(CODEX_HEADER) : !!(JSON.parse(raw).mcpServers?.[MCP_SERVER_NAME] ?? JSON.parse(raw).mcp?.[MCP_SERVER_NAME]);
22344
23200
  } catch {
22345
23201
  registered = false;
@@ -22359,7 +23215,7 @@ var init_mcp_clients = __esm({
22359
23215
  id: "claude",
22360
23216
  label: "Claude Code",
22361
23217
  bin: "claude",
22362
- configPath: (home) => join46(home, ".mcp.json"),
23218
+ configPath: (home) => join47(home, ".mcp.json"),
22363
23219
  // Claude Code is the reference client and `init`'s historical target, so we
22364
23220
  // always write it — a fresh machine has no ~/.claude yet on first install.
22365
23221
  isPresent: () => true,
@@ -22371,10 +23227,10 @@ var init_mcp_clients = __esm({
22371
23227
  id: "codex",
22372
23228
  label: "Codex",
22373
23229
  bin: "codex",
22374
- configPath: (home) => join46(envHome("CHAT_RECALL_CODEX_HOME", join46(home, ".codex")), "config.toml"),
23230
+ configPath: (home) => join47(envHome("CHAT_RECALL_CODEX_HOME", join47(home, ".codex")), "config.toml"),
22375
23231
  isPresent(home) {
22376
23232
  const p = this.configPath(home);
22377
- return existsSync44(p) || existsSync44(dirname21(p));
23233
+ return existsSync45(p) || existsSync45(dirname21(p));
22378
23234
  },
22379
23235
  register(spec, home) {
22380
23236
  return registerCodexToml(this.configPath(home), spec);
@@ -22384,10 +23240,10 @@ var init_mcp_clients = __esm({
22384
23240
  id: "opencode",
22385
23241
  label: "OpenCode",
22386
23242
  bin: "opencode",
22387
- configPath: (home) => join46(envHome("XDG_CONFIG_HOME", join46(home, ".config")), "opencode", "opencode.json"),
23243
+ configPath: (home) => join47(envHome("XDG_CONFIG_HOME", join47(home, ".config")), "opencode", "opencode.json"),
22388
23244
  isPresent(home) {
22389
23245
  const p = this.configPath(home);
22390
- return existsSync44(p) || existsSync44(dirname21(p));
23246
+ return existsSync45(p) || existsSync45(dirname21(p));
22391
23247
  },
22392
23248
  register(spec, home) {
22393
23249
  return registerOpencodeJson(this.configPath(home), spec);
@@ -22399,9 +23255,9 @@ var init_mcp_clients = __esm({
22399
23255
  id: "agy",
22400
23256
  label: "Antigravity",
22401
23257
  bin: "agy",
22402
- configPath: (home) => join46(home, ".gemini", "config", "mcp_config.json"),
23258
+ configPath: (home) => join47(home, ".gemini", "config", "mcp_config.json"),
22403
23259
  isPresent(home) {
22404
- return existsSync44(join46(home, ".gemini", "antigravity-cli")) || existsSync44(this.configPath(home));
23260
+ return existsSync45(join47(home, ".gemini", "antigravity-cli")) || existsSync45(this.configPath(home));
22405
23261
  },
22406
23262
  register(spec, home) {
22407
23263
  return registerMcpServersJson(this, this.configPath(home), spec);
@@ -22411,10 +23267,10 @@ var init_mcp_clients = __esm({
22411
23267
  id: "cursor",
22412
23268
  label: "Cursor",
22413
23269
  bin: "cursor",
22414
- configPath: (home) => join46(home, ".cursor", "mcp.json"),
23270
+ configPath: (home) => join47(home, ".cursor", "mcp.json"),
22415
23271
  isPresent(home) {
22416
23272
  const p = this.configPath(home);
22417
- return existsSync44(p) || existsSync44(dirname21(p));
23273
+ return existsSync45(p) || existsSync45(dirname21(p));
22418
23274
  },
22419
23275
  register(spec, home) {
22420
23276
  return registerMcpServersJson(this, this.configPath(home), spec);
@@ -22438,9 +23294,9 @@ __export(companions_exports, {
22438
23294
  uninstallCodeindex: () => uninstallCodeindex,
22439
23295
  unregisterCodeindexMcp: () => unregisterCodeindexMcp
22440
23296
  });
22441
- import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync40, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "node:fs";
23297
+ import { existsSync as existsSync46, mkdirSync as mkdirSync13, statSync as statSync41, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream as createWriteStream2, readFileSync as readFileSync39, writeFileSync as writeFileSync13 } from "node:fs";
22442
23298
  import { homedir as homedir17 } from "node:os";
22443
- import { join as join47 } from "node:path";
23299
+ import { join as join48 } from "node:path";
22444
23300
  import { execSync as execSync3 } from "node:child_process";
22445
23301
  import { createHash as createHash10 } from "node:crypto";
22446
23302
  function expectedCodeindexSha256(artifact) {
@@ -22489,7 +23345,7 @@ function checkCodeindexStatus() {
22489
23345
  status.installed = true;
22490
23346
  status.path = foundPath;
22491
23347
  try {
22492
- status.size = statSync40(foundPath).size;
23348
+ status.size = statSync41(foundPath).size;
22493
23349
  } catch {
22494
23350
  }
22495
23351
  try {
@@ -22515,7 +23371,7 @@ async function downloadArtifact(artifact, dest) {
22515
23371
  throw new Error(`Download failed: ${res.status} ${res.statusText}`);
22516
23372
  if (!res.body)
22517
23373
  throw new Error("Download returned empty body");
22518
- const file = createWriteStream(dest);
23374
+ const file = createWriteStream2(dest);
22519
23375
  await new Promise((resolve3, reject) => {
22520
23376
  const reader = res.body.getReader();
22521
23377
  const pump = async () => {
@@ -22550,7 +23406,7 @@ async function installCodeindex(opts = {}) {
22550
23406
  if (!expected) {
22551
23407
  throw new Error(`No pinned checksum for ${status.artifactName} \u2014 refusing to install an unverified binary. Add its sha256 to CODEINDEX_SHA256 in companions.ts.`);
22552
23408
  }
22553
- const actual = createHash10("sha256").update(readFileSync38(tmpPath)).digest("hex");
23409
+ const actual = createHash10("sha256").update(readFileSync39(tmpPath)).digest("hex");
22554
23410
  if (actual !== expected) {
22555
23411
  throw new Error(`Checksum mismatch for ${status.artifactName}.
22556
23412
  expected ${expected}
@@ -22561,7 +23417,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
22561
23417
  renameSync5(tmpPath, CODEINDEX_BIN_PATH);
22562
23418
  } catch (err) {
22563
23419
  try {
22564
- if (existsSync45(tmpPath))
23420
+ if (existsSync46(tmpPath))
22565
23421
  unlinkSync3(tmpPath);
22566
23422
  } catch {
22567
23423
  }
@@ -22570,7 +23426,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
22570
23426
  return checkCodeindexStatus();
22571
23427
  }
22572
23428
  function uninstallCodeindex() {
22573
- if (existsSync45(CODEINDEX_BIN_PATH)) {
23429
+ if (existsSync46(CODEINDEX_BIN_PATH)) {
22574
23430
  unlinkSync3(CODEINDEX_BIN_PATH);
22575
23431
  return { removed: true, path: CODEINDEX_BIN_PATH };
22576
23432
  }
@@ -22578,9 +23434,9 @@ function uninstallCodeindex() {
22578
23434
  }
22579
23435
  function registerCodeindexMcp(mcpJsonPath, binaryPath) {
22580
23436
  let cfg = {};
22581
- if (existsSync45(mcpJsonPath)) {
23437
+ if (existsSync46(mcpJsonPath)) {
22582
23438
  try {
22583
- cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
23439
+ cfg = JSON.parse(readFileSync39(mcpJsonPath, "utf-8"));
22584
23440
  } catch {
22585
23441
  cfg = {};
22586
23442
  }
@@ -22593,11 +23449,11 @@ function registerCodeindexMcp(mcpJsonPath, binaryPath) {
22593
23449
  return { added: true };
22594
23450
  }
22595
23451
  function unregisterCodeindexMcp(mcpJsonPath) {
22596
- if (!existsSync45(mcpJsonPath))
23452
+ if (!existsSync46(mcpJsonPath))
22597
23453
  return { removed: false };
22598
23454
  let cfg;
22599
23455
  try {
22600
- cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
23456
+ cfg = JSON.parse(readFileSync39(mcpJsonPath, "utf-8"));
22601
23457
  } catch {
22602
23458
  return { removed: false };
22603
23459
  }
@@ -22622,9 +23478,9 @@ var init_companions = __esm({
22622
23478
  "x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
22623
23479
  "aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
22624
23480
  };
22625
- CODEINDEX_INSTALL_DIR = join47(homedir17(), ".local", "bin");
23481
+ CODEINDEX_INSTALL_DIR = join48(homedir17(), ".local", "bin");
22626
23482
  CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
22627
- CODEINDEX_BIN_PATH = join47(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
23483
+ CODEINDEX_BIN_PATH = join48(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22628
23484
  }
22629
23485
  });
22630
23486
 
@@ -22639,15 +23495,15 @@ __export(install_skills_exports, {
22639
23495
  skillsSourceDir: () => skillsSourceDir,
22640
23496
  uninstallSkills: () => uninstallSkills
22641
23497
  });
22642
- import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as statSync41, realpathSync as realpathSync4 } from "fs";
23498
+ import { readdirSync as readdirSync32, existsSync as existsSync47, mkdirSync as mkdirSync14, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync40, statSync as statSync42, realpathSync as realpathSync4 } from "fs";
22643
23499
  import { createHash as createHash11 } from "crypto";
22644
- import { join as join48, basename as basename21 } from "path";
23500
+ import { join as join49, basename as basename21 } from "path";
22645
23501
  function skillsSourceDir() {
22646
- return join48(import.meta.dirname, "..", "skills");
23502
+ return join49(import.meta.dirname, "..", "skills");
22647
23503
  }
22648
23504
  function version() {
22649
23505
  try {
22650
- return JSON.parse(readFileSync39(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
23506
+ return JSON.parse(readFileSync40(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
22651
23507
  } catch {
22652
23508
  return "0.0.0";
22653
23509
  }
@@ -22656,9 +23512,9 @@ function skillsFingerprint() {
22656
23512
  const src = skillsSourceDir();
22657
23513
  const h = createHash11("sha256");
22658
23514
  for (const name of bundledSkillNames()) {
22659
- for (const rel of filesUnder(join48(src, name)).sort()) {
23515
+ for (const rel of filesUnder(join49(src, name)).sort()) {
22660
23516
  h.update(name + "/" + rel);
22661
- h.update(readFileSync39(join48(src, name, rel)));
23517
+ h.update(readFileSync40(join49(src, name, rel)));
22662
23518
  }
22663
23519
  }
22664
23520
  return h.digest("hex").slice(0, 12);
@@ -22668,7 +23524,7 @@ function filesUnder(dir, prefix = "") {
22668
23524
  for (const e of readdirSync32(dir, { withFileTypes: true })) {
22669
23525
  if (e.name === MARKER) continue;
22670
23526
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
22671
- if (e.isDirectory()) out.push(...filesUnder(join48(dir, e.name), rel));
23527
+ if (e.isDirectory()) out.push(...filesUnder(join49(dir, e.name), rel));
22672
23528
  else out.push(rel);
22673
23529
  }
22674
23530
  return out;
@@ -22678,8 +23534,8 @@ function skillStamp() {
22678
23534
  }
22679
23535
  function bundledSkillNames() {
22680
23536
  const src = skillsSourceDir();
22681
- if (!existsSync46(src)) return [];
22682
- return readdirSync32(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join48(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23537
+ if (!existsSync47(src)) return [];
23538
+ return readdirSync32(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22683
23539
  }
22684
23540
  function realPathOrSelf(p) {
22685
23541
  try {
@@ -22692,7 +23548,7 @@ function claudeSkillTargets() {
22692
23548
  const targets = [];
22693
23549
  const seen = /* @__PURE__ */ new Set();
22694
23550
  for (const home of claudeHomeDirs()) {
22695
- const dir = join48(home, "skills");
23551
+ const dir = join49(home, "skills");
22696
23552
  const key2 = realPathOrSelf(dir);
22697
23553
  if (seen.has(key2)) continue;
22698
23554
  seen.add(key2);
@@ -22703,7 +23559,7 @@ function claudeSkillTargets() {
22703
23559
  dir,
22704
23560
  // Judge each profile on ITS OWN transcripts, not the primary's: a home
22705
23561
  // with no projects/ is a directory someone made, not a Claude install.
22706
- available: existsSync46(join48(home, "projects"))
23562
+ available: existsSync47(join49(home, "projects"))
22707
23563
  });
22708
23564
  }
22709
23565
  if (targets.length === 0) {
@@ -22718,16 +23574,16 @@ function skillTargets() {
22718
23574
  // both produces "skill conflict" duplicate warnings. Every tool we support
22719
23575
  // has its own dir, so per-tool coverage is complete.
22720
23576
  ...claudeSkillTargets(),
22721
- { id: "agy", label: "Antigravity", dir: join48(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
23577
+ { id: "agy", label: "Antigravity", dir: join49(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
22722
23578
  { id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
22723
23579
  { id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
22724
23580
  // Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
22725
23581
  // it is Cursor's bundled set — read-only, never an install target.
22726
- { id: "cursor", label: "Cursor", dir: join48(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
23582
+ { id: "cursor", label: "Cursor", dir: join49(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
22727
23583
  ];
22728
23584
  }
22729
23585
  function isManaged(dir) {
22730
- return existsSync46(join48(dir, MARKER));
23586
+ return existsSync47(join49(dir, MARKER));
22731
23587
  }
22732
23588
  function installSkills(opts = {}) {
22733
23589
  const onlyAvailable = opts.onlyAvailable ?? true;
@@ -22744,15 +23600,15 @@ function installSkills(opts = {}) {
22744
23600
  continue;
22745
23601
  }
22746
23602
  for (const name of names) {
22747
- const dest = join48(t.dir, name);
22748
- if (existsSync46(dest) && !isManaged(dest)) {
23603
+ const dest = join49(t.dir, name);
23604
+ if (existsSync47(dest) && !isManaged(dest)) {
22749
23605
  skippedUserOwned.push(name);
22750
23606
  continue;
22751
23607
  }
22752
23608
  mkdirSync14(t.dir, { recursive: true });
22753
- rmSync7(dest, { recursive: true, force: true });
22754
- cpSync(join48(src, name), dest, { recursive: true });
22755
- writeFileSync14(join48(dest, MARKER), `${stamp}
23609
+ rmSync8(dest, { recursive: true, force: true });
23610
+ cpSync(join49(src, name), dest, { recursive: true });
23611
+ writeFileSync14(join49(dest, MARKER), `${stamp}
22756
23612
  `);
22757
23613
  installed.push(name);
22758
23614
  }
@@ -22765,15 +23621,15 @@ function uninstallSkills() {
22765
23621
  const perTarget = [];
22766
23622
  for (const t of skillTargets()) {
22767
23623
  const removed = [];
22768
- if (!existsSync46(t.dir)) {
23624
+ if (!existsSync47(t.dir)) {
22769
23625
  perTarget.push({ id: t.id, label: t.label, removed });
22770
23626
  continue;
22771
23627
  }
22772
23628
  for (const entry of readdirSync32(t.dir, { withFileTypes: true })) {
22773
23629
  if (!entry.isDirectory()) continue;
22774
- const dir = join48(t.dir, entry.name);
23630
+ const dir = join49(t.dir, entry.name);
22775
23631
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
22776
- rmSync7(dir, { recursive: true, force: true });
23632
+ rmSync8(dir, { recursive: true, force: true });
22777
23633
  removed.push(entry.name);
22778
23634
  }
22779
23635
  }
@@ -22788,10 +23644,10 @@ function skillsNeedRefresh() {
22788
23644
  for (const t of skillTargets()) {
22789
23645
  if (!t.available) continue;
22790
23646
  for (const name of names) {
22791
- const marker = join48(t.dir, name, MARKER);
22792
- if (!existsSync46(marker)) return true;
23647
+ const marker = join49(t.dir, name, MARKER);
23648
+ if (!existsSync47(marker)) return true;
22793
23649
  try {
22794
- if (readFileSync39(marker, "utf-8").trim() !== cur) return true;
23650
+ if (readFileSync40(marker, "utf-8").trim() !== cur) return true;
22795
23651
  } catch {
22796
23652
  return true;
22797
23653
  }
@@ -22806,11 +23662,11 @@ function skillStatus() {
22806
23662
  let installed = 0;
22807
23663
  let ver = null;
22808
23664
  for (const name of names) {
22809
- const marker = join48(t.dir, name, MARKER);
22810
- if (existsSync46(marker)) {
23665
+ const marker = join49(t.dir, name, MARKER);
23666
+ if (existsSync47(marker)) {
22811
23667
  installed++;
22812
23668
  try {
22813
- ver = readFileSync39(marker, "utf-8").trim();
23669
+ ver = readFileSync40(marker, "utf-8").trim();
22814
23670
  } catch {
22815
23671
  }
22816
23672
  }
@@ -22834,8 +23690,8 @@ var init_install_skills = __esm({
22834
23690
  });
22835
23691
 
22836
23692
  // ../engine/dist/core/artifact-codec.js
22837
- import { readFileSync as readFileSync40 } from "fs";
22838
- import { basename as basename22, join as join49 } from "path";
23693
+ import { readFileSync as readFileSync41 } from "fs";
23694
+ import { basename as basename22, join as join50 } from "path";
22839
23695
  import { homedir as homedir18 } from "os";
22840
23696
  function parseCommandText(content, format, fallbackName) {
22841
23697
  if (format === "toml") {
@@ -22858,41 +23714,41 @@ function parseAgentText(content, format, fallbackName) {
22858
23714
  return { name: fm.name || fallbackName, description: fm.description || "", tools: fm.tools, body: body2.trim() };
22859
23715
  }
22860
23716
  function readCommand(filePath, format) {
22861
- return parseCommandText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
23717
+ return parseCommandText(readFileSync41(filePath, "utf-8"), format, base2(filePath));
22862
23718
  }
22863
23719
  function readAgent(filePath, format) {
22864
- return parseAgentText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
23720
+ return parseAgentText(readFileSync41(filePath, "utf-8"), format, base2(filePath));
22865
23721
  }
22866
23722
  function readInstructions(filePath, name) {
22867
- const content = readFileSync40(filePath, "utf-8");
23723
+ const content = readFileSync41(filePath, "utf-8");
22868
23724
  return { name, description: "", body: content };
22869
23725
  }
22870
23726
  function commandPath(tool, name) {
22871
23727
  switch (tool) {
22872
23728
  case "claude":
22873
- return join49(claudeBackend.commandsDir(), `${name}.md`);
23729
+ return join50(claudeBackend.commandsDir(), `${name}.md`);
22874
23730
  case "agy":
22875
- return join49(join49(agyBackend.homeDir(), "commands"), `${name}.toml`);
23731
+ return join50(join50(agyBackend.homeDir(), "commands"), `${name}.toml`);
22876
23732
  case "opencode":
22877
- return join49(opencodeBackend.commandsDir(), `${name}.md`);
23733
+ return join50(opencodeBackend.commandsDir(), `${name}.md`);
22878
23734
  case "codex":
22879
- return join49(codexBackend.promptsDir(), `${name}.md`);
23735
+ return join50(codexBackend.promptsDir(), `${name}.md`);
22880
23736
  case "cursor":
22881
- return join49(cursorHomeDir(), "commands", `${name}.md`);
23737
+ return join50(cursorHomeDir(), "commands", `${name}.md`);
22882
23738
  }
22883
23739
  }
22884
23740
  function agentPath(tool, name) {
22885
23741
  switch (tool) {
22886
23742
  case "claude":
22887
- return join49(claudeBackend.agentsDir(), `${name}.md`);
23743
+ return join50(claudeBackend.agentsDir(), `${name}.md`);
22888
23744
  case "agy":
22889
- return join49(join49(agyBackend.homeDir(), "agents"), `${name}.md`);
23745
+ return join50(join50(agyBackend.homeDir(), "agents"), `${name}.md`);
22890
23746
  case "opencode":
22891
- return join49(opencodeBackend.agentsDir(), `${name}.md`);
23747
+ return join50(opencodeBackend.agentsDir(), `${name}.md`);
22892
23748
  case "codex":
22893
- return join49(codexBackend.agentsDir(), `${name}.toml`);
23749
+ return join50(codexBackend.agentsDir(), `${name}.toml`);
22894
23750
  case "cursor":
22895
- return join49(cursorHomeDir(), "agents", `${name}.md`);
23751
+ return join50(cursorHomeDir(), "agents", `${name}.md`);
22896
23752
  }
22897
23753
  }
22898
23754
  function instructionsFilename(tool) {
@@ -22917,21 +23773,21 @@ function instructionsFilename(tool) {
22917
23773
  function instructionsPath(tool, projectPath) {
22918
23774
  const file = instructionsFilename(tool);
22919
23775
  if (projectPath)
22920
- return join49(projectPath, file);
23776
+ return join50(projectPath, file);
22921
23777
  switch (tool) {
22922
23778
  case "claude":
22923
- return join49(claudeBackend.homeDir(), file);
23779
+ return join50(claudeBackend.homeDir(), file);
22924
23780
  case "agy":
22925
- return join49(agyBackend.homeDir(), file);
23781
+ return join50(agyBackend.homeDir(), file);
22926
23782
  case "codex":
22927
- return join49(codexBackend.homeDir(), file);
23783
+ return join50(codexBackend.homeDir(), file);
22928
23784
  case "opencode":
22929
- return join49(homedir18(), ".config", "opencode", file);
23785
+ return join50(homedir18(), ".config", "opencode", file);
22930
23786
  // Cursor has no single user-scope instructions FILE — user-level guidance
22931
23787
  // is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
22932
23788
  // the closest equivalent and is what a mirror can actually write.
22933
23789
  case "cursor":
22934
- return join49(cursorHomeDir(), file);
23790
+ return join50(cursorHomeDir(), file);
22935
23791
  }
22936
23792
  }
22937
23793
  function emit(type, art, toTool, projectPath) {
@@ -22995,8 +23851,8 @@ __export(toolkit_sync_exports, {
22995
23851
  supportedTargetsFor: () => supportedTargetsFor,
22996
23852
  writeMcpEntry: () => writeMcpEntry
22997
23853
  });
22998
- import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync41, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
22999
- import { dirname as dirname22, join as join50, basename as basename23 } from "path";
23854
+ import { existsSync as existsSync48, mkdirSync as mkdirSync15, readFileSync as readFileSync42, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
23855
+ import { dirname as dirname22, join as join51, basename as basename23 } from "path";
23000
23856
  import { homedir as homedir19 } from "os";
23001
23857
  function supportedTargetsFor(type) {
23002
23858
  return SUPPORTED_TARGETS[type];
@@ -23038,23 +23894,23 @@ function skillsDirFor(tool) {
23038
23894
  case "claude":
23039
23895
  return claudeBackend.skillsDir();
23040
23896
  case "agy":
23041
- return join50(agyBackend.homeDir(), "skills");
23897
+ return join51(agyBackend.homeDir(), "skills");
23042
23898
  case "opencode":
23043
23899
  return opencodeBackend.skillsDir();
23044
23900
  case "codex":
23045
23901
  return codexBackend.skillsDir();
23046
23902
  // user skills, NOT .system
23047
23903
  case "cursor":
23048
- return join50(cursorHomeDir(), "skills");
23904
+ return join51(cursorHomeDir(), "skills");
23049
23905
  }
23050
23906
  }
23051
23907
  function copySkill(row, toTool) {
23052
23908
  const skillDir = row.extra.skillDir || dirname22(row.filePath);
23053
- if (!existsSync47(skillDir))
23909
+ if (!existsSync48(skillDir))
23054
23910
  return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
23055
23911
  const name = row.extra.skillName || basename23(skillDir);
23056
- const targetDir = join50(skillsDirFor(toTool), name);
23057
- if (existsSync47(targetDir))
23912
+ const targetDir = join51(skillsDirFor(toTool), name);
23913
+ if (existsSync48(targetDir))
23058
23914
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
23059
23915
  try {
23060
23916
  mkdirSync15(dirname22(targetDir), { recursive: true });
@@ -23068,28 +23924,28 @@ function readMcpEntry(tool, name) {
23068
23924
  const home = homedir19();
23069
23925
  const tries = [];
23070
23926
  if (tool === "claude") {
23071
- tries.push({ path: join50(home, ".mcp.json"), key: "mcpServers" });
23072
- tries.push({ path: join50(home, ".claude.json"), key: "mcpServers" });
23927
+ tries.push({ path: join51(home, ".mcp.json"), key: "mcpServers" });
23928
+ tries.push({ path: join51(home, ".claude.json"), key: "mcpServers" });
23073
23929
  } else if (tool === "gemini") {
23074
- tries.push({ path: join50(home, ".gemini", "settings.json"), key: "mcpServers" });
23930
+ tries.push({ path: join51(home, ".gemini", "settings.json"), key: "mcpServers" });
23075
23931
  } else if (tool === "agy") {
23076
- tries.push({ path: join50(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
23932
+ tries.push({ path: join51(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
23077
23933
  } else if (tool === "cursor") {
23078
- tries.push({ path: join50(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
23934
+ tries.push({ path: join51(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
23079
23935
  } else if (tool === "opencode") {
23080
- tries.push({ path: join50(home, ".config", "opencode", "opencode.json"), key: "mcp" });
23081
- tries.push({ path: join50(home, ".config", "opencode", "config.json"), key: "mcp" });
23082
- tries.push({ path: join50(home, ".opencode", "config.json"), key: "mcp" });
23936
+ tries.push({ path: join51(home, ".config", "opencode", "opencode.json"), key: "mcp" });
23937
+ tries.push({ path: join51(home, ".config", "opencode", "config.json"), key: "mcp" });
23938
+ tries.push({ path: join51(home, ".opencode", "config.json"), key: "mcp" });
23083
23939
  } else if (tool === "codex") {
23084
- const entry = readCodexMcpEntry(join50(home, ".codex", "config.toml"), name);
23940
+ const entry = readCodexMcpEntry(join51(home, ".codex", "config.toml"), name);
23085
23941
  if (entry)
23086
23942
  return entry;
23087
23943
  }
23088
23944
  for (const t of tries) {
23089
- if (!existsSync47(t.path))
23945
+ if (!existsSync48(t.path))
23090
23946
  continue;
23091
23947
  try {
23092
- const cfg = JSON.parse(readFileSync41(t.path, "utf-8"));
23948
+ const cfg = JSON.parse(readFileSync42(t.path, "utf-8"));
23093
23949
  const block = cfg[t.key] || {};
23094
23950
  if (block[name])
23095
23951
  return block[name];
@@ -23099,11 +23955,11 @@ function readMcpEntry(tool, name) {
23099
23955
  return null;
23100
23956
  }
23101
23957
  function readCodexMcpEntry(path, target) {
23102
- if (!existsSync47(path))
23958
+ if (!existsSync48(path))
23103
23959
  return null;
23104
23960
  let content;
23105
23961
  try {
23106
- content = readFileSync41(path, "utf-8");
23962
+ content = readFileSync42(path, "utf-8");
23107
23963
  } catch {
23108
23964
  return null;
23109
23965
  }
@@ -23153,8 +24009,8 @@ function readCodexMcpEntry(path, target) {
23153
24009
  }
23154
24010
  function writeCodexMcpEntry(path, name, entry) {
23155
24011
  let content = "";
23156
- if (existsSync47(path))
23157
- content = readFileSync41(path, "utf-8");
24012
+ if (existsSync48(path))
24013
+ content = readFileSync42(path, "utf-8");
23158
24014
  const header = `[mcp_servers.${name}]`;
23159
24015
  if (content.includes(header))
23160
24016
  return false;
@@ -23190,7 +24046,7 @@ function writeCodexMcpEntry(path, name, entry) {
23190
24046
  function writeMcpEntry(toTool, name, entry) {
23191
24047
  const home = homedir19();
23192
24048
  if (toTool === "codex") {
23193
- const path2 = join50(home, ".codex", "config.toml");
24049
+ const path2 = join51(home, ".codex", "config.toml");
23194
24050
  if (!writeCodexMcpEntry(path2, name, entry))
23195
24051
  return { ok: false, status: 409, error: `MCP "${name}" already exists in ${path2}.` };
23196
24052
  return { ok: true, targetPath: path2 };
@@ -23198,22 +24054,22 @@ function writeMcpEntry(toTool, name, entry) {
23198
24054
  let path;
23199
24055
  let key2;
23200
24056
  if (toTool === "claude") {
23201
- path = join50(home, ".mcp.json");
24057
+ path = join51(home, ".mcp.json");
23202
24058
  key2 = "mcpServers";
23203
24059
  } else if (toTool === "agy") {
23204
- path = join50(home, ".gemini", "config", "mcp_config.json");
24060
+ path = join51(home, ".gemini", "config", "mcp_config.json");
23205
24061
  key2 = "mcpServers";
23206
24062
  } else if (toTool === "cursor") {
23207
- path = join50(cursorHomeDir(), "mcp.json");
24063
+ path = join51(cursorHomeDir(), "mcp.json");
23208
24064
  key2 = "mcpServers";
23209
24065
  } else {
23210
- path = join50(home, ".config", "opencode", "opencode.json");
24066
+ path = join51(home, ".config", "opencode", "opencode.json");
23211
24067
  key2 = "mcp";
23212
24068
  }
23213
24069
  let cfg = {};
23214
- if (existsSync47(path)) {
24070
+ if (existsSync48(path)) {
23215
24071
  try {
23216
- cfg = JSON.parse(readFileSync41(path, "utf-8"));
24072
+ cfg = JSON.parse(readFileSync42(path, "utf-8"));
23217
24073
  } catch {
23218
24074
  return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
23219
24075
  }
@@ -23275,7 +24131,7 @@ function copyCommandOrAgent(type, row, toTool) {
23275
24131
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
23276
24132
  }
23277
24133
  const out = emit(type, art, toTool);
23278
- if (existsSync47(out.path))
24134
+ if (existsSync48(out.path))
23279
24135
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
23280
24136
  try {
23281
24137
  mkdirSync15(dirname22(out.path), { recursive: true });
@@ -23295,7 +24151,7 @@ function copyInstructions(row, toTool) {
23295
24151
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
23296
24152
  }
23297
24153
  const out = emit("instructions", art, toTool, row.projectPath || void 0);
23298
- if (existsSync47(out.path))
24154
+ if (existsSync48(out.path))
23299
24155
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
23300
24156
  try {
23301
24157
  mkdirSync15(dirname22(out.path), { recursive: true });
@@ -23446,11 +24302,11 @@ __export(toolkit_pull_exports, {
23446
24302
  platformRefusal: () => platformRefusal,
23447
24303
  portableCommand: () => portableCommand
23448
24304
  });
23449
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync42, writeFileSync as writeFileSync16 } from "node:fs";
23450
- import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
24305
+ import { existsSync as existsSync49, mkdirSync as mkdirSync16, statSync as statSync43, writeFileSync as writeFileSync16 } from "node:fs";
24306
+ import { dirname as dirname23, join as join52, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
23451
24307
  function isExecutableFile(p) {
23452
24308
  try {
23453
- return statSync42(p).isFile();
24309
+ return statSync43(p).isFile();
23454
24310
  } catch {
23455
24311
  return false;
23456
24312
  }
@@ -23617,9 +24473,9 @@ function executePull(rows, opts = {}) {
23617
24473
  const outcomes = [];
23618
24474
  for (const { name, body: body2, truncated, redacted, tools } of skills) {
23619
24475
  for (const tool of tools) {
23620
- const dir = join51(skillsDirFor(tool), name);
23621
- const file = join51(dir, "SKILL.md");
23622
- if (existsSync48(file)) {
24476
+ const dir = join52(skillsDirFor(tool), name);
24477
+ const file = join52(dir, "SKILL.md");
24478
+ if (existsSync49(file)) {
23623
24479
  outcomes.push({ type: "skill", name, tool, status: "present" });
23624
24480
  continue;
23625
24481
  }
@@ -23664,7 +24520,7 @@ function executePull(rows, opts = {}) {
23664
24520
  outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
23665
24521
  continue;
23666
24522
  }
23667
- if (existsSync48(out.path)) {
24523
+ if (existsSync49(out.path)) {
23668
24524
  outcomes.push({ type, name, tool, status: "present" });
23669
24525
  continue;
23670
24526
  }
@@ -23827,8 +24683,8 @@ __export(board_hooks_exports, {
23827
24683
  removeBoardHooks: () => removeBoardHooks,
23828
24684
  resolveCliEntry: () => resolveCliEntry
23829
24685
  });
23830
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync42, writeFileSync as writeFileSync17 } from "node:fs";
23831
- import { dirname as dirname24, join as join52 } from "node:path";
24686
+ import { existsSync as existsSync50, mkdirSync as mkdirSync17, readFileSync as readFileSync43, writeFileSync as writeFileSync17 } from "node:fs";
24687
+ import { dirname as dirname24, join as join53 } from "node:path";
23832
24688
  function boardHookCommand(execPath, cliEntry, mode) {
23833
24689
  return `"${execPath}" "${cliEntry}" task-hook --${mode}`;
23834
24690
  }
@@ -23867,9 +24723,9 @@ function removeBoardHooks(config2) {
23867
24723
  return removed;
23868
24724
  }
23869
24725
  function readConfig(file) {
23870
- if (!existsSync49(file)) return { hooks: {} };
24726
+ if (!existsSync50(file)) return { hooks: {} };
23871
24727
  try {
23872
- const parsed = JSON.parse(readFileSync42(file, "utf-8"));
24728
+ const parsed = JSON.parse(readFileSync43(file, "utf-8"));
23873
24729
  if (!parsed.hooks || typeof parsed.hooks !== "object") parsed.hooks = {};
23874
24730
  return parsed;
23875
24731
  } catch {
@@ -23879,9 +24735,9 @@ function readConfig(file) {
23879
24735
  function registerBoardHooks(files, execPath, cliEntry) {
23880
24736
  const written = [];
23881
24737
  for (const file of files) {
23882
- if (existsSync49(file)) {
24738
+ if (existsSync50(file)) {
23883
24739
  try {
23884
- JSON.parse(readFileSync42(file, "utf-8"));
24740
+ JSON.parse(readFileSync43(file, "utf-8"));
23885
24741
  } catch {
23886
24742
  continue;
23887
24743
  }
@@ -23898,8 +24754,8 @@ function registerBoardHooks(files, execPath, cliEntry) {
23898
24754
  return written;
23899
24755
  }
23900
24756
  function resolveCliEntry(distDir) {
23901
- const bundled = join52(distDir, "cli.js");
23902
- return existsSync49(bundled) ? bundled : join52(distDir, "cli.ts");
24757
+ const bundled = join53(distDir, "cli.js");
24758
+ return existsSync50(bundled) ? bundled : join53(distDir, "cli.ts");
23903
24759
  }
23904
24760
  var BOARD_HOOKS;
23905
24761
  var init_board_hooks = __esm({
@@ -24108,11 +24964,16 @@ async function repairAll(opts = {}) {
24108
24964
  scanned++;
24109
24965
  if (opts.verbose && scanned % 50 === 0) log6(`checked ${scanned}/${ids.length}\u2026`);
24110
24966
  const arch = await fetchJson(`${primary.url}/api/conversations/${id}/raw-archive?count=1`, primary.token);
24111
- const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
24112
- if (archiveMsgs < minMessages) continue;
24113
- const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
24114
- const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
24115
- if (archiveMsgs <= current) continue;
24967
+ const counted = arch && arch.messages !== null;
24968
+ if (counted) {
24969
+ const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
24970
+ if (archiveMsgs < minMessages) continue;
24971
+ const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
24972
+ const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
24973
+ if (archiveMsgs <= current) continue;
24974
+ } else if (!arch) {
24975
+ continue;
24976
+ }
24116
24977
  const r = await repairSession(id, { dryRun: !opts.apply, verbose: opts.verbose, server: opts.server });
24117
24978
  if (r.status === "already-full") continue;
24118
24979
  candidates2.push(r);
@@ -24297,21 +25158,21 @@ __export(guard_registration_exports, {
24297
25158
  unregisterGuard: () => unregisterGuard
24298
25159
  });
24299
25160
  import { homedir as homedir20 } from "os";
24300
- import { join as join53 } from "path";
25161
+ import { join as join54 } from "path";
24301
25162
  function opencodeConfigDir() {
24302
25163
  const xdg = process.env.XDG_CONFIG_HOME;
24303
- const base4 = xdg && xdg.trim() ? xdg : join53(homedir20(), ".config");
24304
- return join53(base4, "opencode");
25164
+ const base4 = xdg && xdg.trim() ? xdg : join54(homedir20(), ".config");
25165
+ return join54(base4, "opencode");
24305
25166
  }
24306
25167
  function opencodePluginDir() {
24307
- return join53(opencodeConfigDir(), "plugins");
25168
+ return join54(opencodeConfigDir(), "plugins");
24308
25169
  }
24309
25170
  function guardTargets() {
24310
25171
  return [
24311
25172
  {
24312
25173
  tool: "codex",
24313
25174
  label: "Codex",
24314
- configPath: join53(codexBackend.homeDir(), "hooks.json"),
25175
+ configPath: join54(codexBackend.homeDir(), "hooks.json"),
24315
25176
  homeDir: codexBackend.homeDir(),
24316
25177
  // Codex will not run a hook until a person has reviewed it, and the
24317
25178
  // approval is bound to the hook's hash. There is no way to grant it from
@@ -24324,20 +25185,20 @@ function guardTargets() {
24324
25185
  {
24325
25186
  tool: "agy",
24326
25187
  label: "Antigravity",
24327
- configPath: join53(agyBackend.homeDir(), "hooks.json"),
25188
+ configPath: join54(agyBackend.homeDir(), "hooks.json"),
24328
25189
  homeDir: agyBackend.homeDir()
24329
25190
  },
24330
25191
  {
24331
25192
  tool: "cursor",
24332
25193
  label: "Cursor",
24333
- configPath: join53(cursorBackend.homeDir(), "hooks.json"),
25194
+ configPath: join54(cursorBackend.homeDir(), "hooks.json"),
24334
25195
  homeDir: cursorBackend.homeDir(),
24335
25196
  note: "Cursor checks a shell command before it runs and a file edit after it is written."
24336
25197
  },
24337
25198
  {
24338
25199
  tool: "opencode",
24339
25200
  label: "OpenCode",
24340
- configPath: join53(opencodePluginDir(), "chat-recall-guard.js"),
25201
+ configPath: join54(opencodePluginDir(), "chat-recall-guard.js"),
24341
25202
  homeDir: opencodeConfigDir(),
24342
25203
  note: "OpenCode plugin hooks do not fire for subagent tool calls, so a delegating agent bypasses this."
24343
25204
  }
@@ -24566,11 +25427,11 @@ var verify_repair_exports = {};
24566
25427
  __export(verify_repair_exports, {
24567
25428
  forceFullResync: () => forceFullResync
24568
25429
  });
24569
- import { copyFileSync as copyFileSync3, existsSync as existsSync50 } from "fs";
25430
+ import { copyFileSync as copyFileSync3, existsSync as existsSync51 } from "fs";
24570
25431
  function forceFullResync(server, sessionIds) {
24571
25432
  if (sessionIds.length === 0) return 0;
24572
25433
  const path = ledgerFilePath();
24573
- if (existsSync50(path)) {
25434
+ if (existsSync51(path)) {
24574
25435
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/-/g, "");
24575
25436
  try {
24576
25437
  copyFileSync3(path, `${path}.bak-${stamp}`);
@@ -24760,8 +25621,8 @@ __export(collector_exports, {
24760
25621
  });
24761
25622
  import { spawn as spawn3 } from "node:child_process";
24762
25623
  import { execSync as execSync4 } from "node:child_process";
24763
- import { readFileSync as readFileSync43 } from "node:fs";
24764
- import { join as join54, basename as basename26 } from "node:path";
25624
+ import { readFileSync as readFileSync44 } from "node:fs";
25625
+ import { join as join55, basename as basename26 } from "node:path";
24765
25626
  async function resolveCodeindexBin(autoInstall = true) {
24766
25627
  let st = checkCodeindexStatus();
24767
25628
  if (!st.installed && autoInstall)
@@ -24943,7 +25804,7 @@ async function collectCode(opts) {
24943
25804
  }
24944
25805
  const complexity = (r) => {
24945
25806
  try {
24946
- const txt = readFileSync43(join54(ws, r), "utf8");
25807
+ const txt = readFileSync44(join55(ws, r), "utf8");
24947
25808
  if (txt.length > 2e6)
24948
25809
  return 1;
24949
25810
  const m = txt.match(CF_RE);
@@ -25727,8 +26588,8 @@ __export(project_tasks_exports, {
25727
26588
  pushProjectTaskStatuses: () => pushProjectTaskStatuses,
25728
26589
  renderTeamTasksMd: () => renderTeamTasksMd
25729
26590
  });
25730
- import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync43 } from "node:fs";
25731
- import { join as join55, dirname as dirname25 } from "node:path";
26591
+ import { existsSync as existsSync52, readFileSync as readFileSync45, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync44 } from "node:fs";
26592
+ import { join as join56, dirname as dirname25 } from "node:path";
25732
26593
  import { homedir as homedir21 } from "node:os";
25733
26594
  function isTeamTaskStatus(v) {
25734
26595
  return !!v && TEAM_STATUSES.has(v);
@@ -25779,11 +26640,11 @@ function parseTaskFile(content) {
25779
26640
  return out;
25780
26641
  }
25781
26642
  function ledgerPath2() {
25782
- return join55(homedir21(), ".chat-recall", "project-tasks-ledger.json");
26643
+ return join56(homedir21(), ".chat-recall", "project-tasks-ledger.json");
25783
26644
  }
25784
26645
  function readLedger() {
25785
26646
  try {
25786
- return JSON.parse(readFileSync44(ledgerPath2(), "utf8"));
26647
+ return JSON.parse(readFileSync45(ledgerPath2(), "utf8"));
25787
26648
  } catch {
25788
26649
  return {};
25789
26650
  }
@@ -25812,18 +26673,18 @@ async function pushProjectTaskStatuses(base4, authHeaders, opts = {}) {
25812
26673
  continue;
25813
26674
  }
25814
26675
  for (const project of projects) {
25815
- const file = join55(project, fk.filename);
25816
- if (!existsSync51(file)) continue;
26676
+ const file = join56(project, fk.filename);
26677
+ if (!existsSync52(file)) continue;
25817
26678
  let mtime;
25818
26679
  try {
25819
- mtime = statSync43(file).mtimeMs;
26680
+ mtime = statSync44(file).mtimeMs;
25820
26681
  } catch {
25821
26682
  continue;
25822
26683
  }
25823
26684
  if (ledger[file] === mtime) continue;
25824
26685
  let changed;
25825
26686
  try {
25826
- changed = parseTaskFile(readFileSync44(file, "utf8")).filter((t) => t.kind === fk.kind);
26687
+ changed = parseTaskFile(readFileSync45(file, "utf8")).filter((t) => t.kind === fk.kind);
25827
26688
  } catch {
25828
26689
  continue;
25829
26690
  }
@@ -25895,8 +26756,8 @@ __export(intent_drain_exports, {
25895
26756
  drainSyncIntents: () => drainSyncIntents,
25896
26757
  instructionTargets: () => instructionTargets
25897
26758
  });
25898
- import { existsSync as existsSync52, readFileSync as readFileSync45, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
25899
- import { join as join56 } from "node:path";
26759
+ import { existsSync as existsSync53, readFileSync as readFileSync46, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
26760
+ import { join as join57 } from "node:path";
25900
26761
  import { fileURLToPath as fileURLToPath4 } from "node:url";
25901
26762
  import { dirname as dirname26 } from "node:path";
25902
26763
  function instructionTargets(rootPath, isGlobal) {
@@ -25910,16 +26771,16 @@ function instructionTargets(rootPath, isGlobal) {
25910
26771
  const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
25911
26772
  if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
25912
26773
  if (!rootPath) return [];
25913
- const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) => existsSync52(p));
26774
+ const present = INSTRUCTION_FILENAMES.map((f) => join57(rootPath, f)).filter((p) => existsSync53(p));
25914
26775
  if (present.length) return present;
25915
- return [...new Set(tools.map((t) => join56(rootPath, instructionsFilename(t.id))))];
26776
+ return [...new Set(tools.map((t) => join57(rootPath, instructionsFilename(t.id))))];
25916
26777
  }
25917
26778
  function applyCodeRecommendation(intent) {
25918
26779
  try {
25919
26780
  const meta = JSON.parse(intent.name || "{}");
25920
26781
  if (intent.artifact_type === "write_tasks_file") {
25921
26782
  if (!meta.rootPath || !meta.content) return { status: "error", result: JSON.stringify({ error: "missing rootPath or content" }) };
25922
- const file = join56(meta.rootPath, meta.filename || "CODE_TASKS.md");
26783
+ const file = join57(meta.rootPath, meta.filename || "CODE_TASKS.md");
25923
26784
  writeFileSync19(file, meta.content);
25924
26785
  return { status: "done", result: JSON.stringify({ wrote: file }) };
25925
26786
  }
@@ -25938,7 +26799,7 @@ function applyCodeRecommendation(intent) {
25938
26799
  mkdirSync19(dir, { recursive: true });
25939
26800
  } catch {
25940
26801
  }
25941
- const existing = existsSync52(file) ? readFileSync45(file, "utf8") : "";
26802
+ const existing = existsSync53(file) ? readFileSync46(file, "utf8") : "";
25942
26803
  if (existing.includes(text)) {
25943
26804
  skipped.push(file);
25944
26805
  continue;
@@ -26113,10 +26974,10 @@ var init_intent_drain = __esm({
26113
26974
  ownVersion2 = "0.0.0";
26114
26975
  try {
26115
26976
  const dir = dirname26(fileURLToPath4(import.meta.url));
26116
- ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
26977
+ ownVersion2 = JSON.parse(readFileSync46(join57(dir, "../package.json"), "utf-8")).version || ownVersion2;
26117
26978
  } catch {
26118
26979
  try {
26119
- if (true) ownVersion2 = "0.7.17";
26980
+ if (true) ownVersion2 = "0.7.19";
26120
26981
  } catch {
26121
26982
  }
26122
26983
  }
@@ -26317,48 +27178,48 @@ var team_merge_exports = {};
26317
27178
  __export(team_merge_exports, {
26318
27179
  mergePullResult: () => mergePullResult
26319
27180
  });
26320
- import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync44 } from "fs";
26321
- import { dirname as dirname27, join as join57 } from "path";
27181
+ import { existsSync as existsSync54, mkdirSync as mkdirSync20, readFileSync as readFileSync47, rmSync as rmSync9, writeFileSync as writeFileSync20, statSync as statSync45 } from "fs";
27182
+ import { dirname as dirname27, join as join58 } from "path";
26322
27183
  import { createHash as createHash14 } from "crypto";
26323
27184
  function installPathFor(art, tool) {
26324
27185
  switch (art.type) {
26325
27186
  case "skill":
26326
27187
  switch (tool) {
26327
27188
  case "claude":
26328
- return join57(claudeBackend.skillsDir(), art.name, "SKILL.md");
27189
+ return join58(claudeBackend.skillsDir(), art.name, "SKILL.md");
26329
27190
  // Antigravity's OWN skills dir. It used to resolve to the shared
26330
27191
  // ~/.gemini/skills while toolkit-sync installed to
26331
27192
  // ~/.gemini/antigravity-cli/skills, so a team skill landed in one place
26332
27193
  // and a synced skill in the other.
26333
27194
  case "agy":
26334
- return join57(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
27195
+ return join58(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
26335
27196
  case "opencode":
26336
- return join57(opencodeBackend.skillsDir(), art.name, "SKILL.md");
27197
+ return join58(opencodeBackend.skillsDir(), art.name, "SKILL.md");
26337
27198
  // User skills go to ~/.codex/skills (NOT .system, which is OpenAI's bundle).
26338
27199
  case "codex":
26339
- return join57(codexBackend.skillsDir(), art.name, "SKILL.md");
27200
+ return join58(codexBackend.skillsDir(), art.name, "SKILL.md");
26340
27201
  case "cursor":
26341
- return join57(cursorHomeDir(), "skills", art.name, "SKILL.md");
27202
+ return join58(cursorHomeDir(), "skills", art.name, "SKILL.md");
26342
27203
  }
26343
27204
  break;
26344
27205
  case "agent":
26345
27206
  if (tool === "claude")
26346
- return join57(claudeBackend.agentsDir(), `${art.name}.md`);
27207
+ return join58(claudeBackend.agentsDir(), `${art.name}.md`);
26347
27208
  return null;
26348
27209
  case "command":
26349
27210
  if (tool === "claude")
26350
- return join57(claudeBackend.commandsDir(), `${art.name}.md`);
27211
+ return join58(claudeBackend.commandsDir(), `${art.name}.md`);
26351
27212
  return null;
26352
27213
  case "hook":
26353
27214
  if (tool === "claude")
26354
- return join57(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
27215
+ return join58(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
26355
27216
  return null;
26356
27217
  case "plan":
26357
27218
  switch (tool) {
26358
27219
  case "claude":
26359
- return join57(claudeBackend.plansDir(), `${art.name}.md`);
27220
+ return join58(claudeBackend.plansDir(), `${art.name}.md`);
26360
27221
  case "opencode":
26361
- return join57(opencodeBackend.plansDir(), `${art.name}.md`);
27222
+ return join58(opencodeBackend.plansDir(), `${art.name}.md`);
26362
27223
  case "agy":
26363
27224
  return null;
26364
27225
  // Antigravity plans live per-session
@@ -26369,11 +27230,11 @@ function installPathFor(art, tool) {
26369
27230
  }
26370
27231
  break;
26371
27232
  case "instructions":
26372
- return join57(getDataDir(), "team-instructions", tool, `${art.name}.md`);
27233
+ return join58(getDataDir(), "team-instructions", tool, `${art.name}.md`);
26373
27234
  case "mcp":
26374
- return join57(getDataDir(), "team-mcps", `${art.name}.json`);
27235
+ return join58(getDataDir(), "team-mcps", `${art.name}.json`);
26375
27236
  case "plugin":
26376
- return join57(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
27237
+ return join58(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
26377
27238
  }
26378
27239
  return null;
26379
27240
  }
@@ -26434,8 +27295,8 @@ async function mergePullResult(opts) {
26434
27295
  if (!path)
26435
27296
  continue;
26436
27297
  try {
26437
- if (existsSync53(path))
26438
- rmSync8(path, { force: true });
27298
+ if (existsSync54(path))
27299
+ rmSync9(path, { force: true });
26439
27300
  await teamInstallsForget(id, row.tool);
26440
27301
  report.removed.push({ artifactId: id, path });
26441
27302
  } catch (err) {
@@ -26452,12 +27313,12 @@ async function mergePullResult(opts) {
26452
27313
  }
26453
27314
  function readFileIfExists(path) {
26454
27315
  try {
26455
- if (!existsSync53(path))
27316
+ if (!existsSync54(path))
26456
27317
  return null;
26457
- const s = statSync44(path);
27318
+ const s = statSync45(path);
26458
27319
  if (!s.isFile())
26459
27320
  return null;
26460
- return readFileSync46(path);
27321
+ return readFileSync47(path);
26461
27322
  } catch {
26462
27323
  return null;
26463
27324
  }
@@ -26538,8 +27399,8 @@ var init_vault_crypto = __esm({
26538
27399
  });
26539
27400
 
26540
27401
  // ../engine/dist/core/vault-source.js
26541
- import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as statSync45 } from "fs";
26542
- import { extname, join as join58 } from "path";
27402
+ import { existsSync as existsSync55, readdirSync as readdirSync33, statSync as statSync46 } from "fs";
27403
+ import { extname, join as join59 } from "path";
26543
27404
  function* walkVaultSources() {
26544
27405
  const t = loadSettings().team.vault;
26545
27406
  const include2 = new Set(t.syncTools);
@@ -26554,7 +27415,7 @@ function* walkVaultSources() {
26554
27415
  }
26555
27416
  function safeStat(path) {
26556
27417
  try {
26557
- const s = statSync45(path);
27418
+ const s = statSync46(path);
26558
27419
  if (!s.isFile())
26559
27420
  return null;
26560
27421
  return { mtimeMs: s.mtimeMs, size: s.size };
@@ -26564,7 +27425,7 @@ function safeStat(path) {
26564
27425
  }
26565
27426
  function* walkClaude() {
26566
27427
  const root = claudeBackend.projectsDir();
26567
- if (!existsSync54(root))
27428
+ if (!existsSync55(root))
26568
27429
  return;
26569
27430
  let projectDirs = [];
26570
27431
  try {
@@ -26576,7 +27437,7 @@ function* walkClaude() {
26576
27437
  if (proj.startsWith("."))
26577
27438
  continue;
26578
27439
  const projectPath = resolveProjectDirName(proj);
26579
- const projDir = join58(root, proj);
27440
+ const projDir = join59(root, proj);
26580
27441
  let entries = [];
26581
27442
  try {
26582
27443
  entries = readdirSync33(projDir);
@@ -26586,7 +27447,7 @@ function* walkClaude() {
26586
27447
  for (const f of entries) {
26587
27448
  if (extname(f) !== ".jsonl")
26588
27449
  continue;
26589
- const path = join58(projDir, f);
27450
+ const path = join59(projDir, f);
26590
27451
  const s = safeStat(path);
26591
27452
  if (!s)
26592
27453
  continue;
@@ -26616,8 +27477,8 @@ function* walkCursor() {
26616
27477
  }
26617
27478
  }
26618
27479
  function* walkCodex() {
26619
- const sessionsRoot = join58(codexBackend.homeDir(), "sessions");
26620
- if (!existsSync54(sessionsRoot))
27480
+ const sessionsRoot = join59(codexBackend.homeDir(), "sessions");
27481
+ if (!existsSync55(sessionsRoot))
26621
27482
  return;
26622
27483
  const stack = [sessionsRoot];
26623
27484
  while (stack.length) {
@@ -26629,7 +27490,7 @@ function* walkCodex() {
26629
27490
  continue;
26630
27491
  }
26631
27492
  for (const e of entries) {
26632
- const path = join58(dir, e.name);
27493
+ const path = join59(dir, e.name);
26633
27494
  if (e.isDirectory()) {
26634
27495
  stack.push(path);
26635
27496
  continue;
@@ -26645,8 +27506,8 @@ function* walkCodex() {
26645
27506
  }
26646
27507
  }
26647
27508
  function* walkAgy() {
26648
- const brainDir = join58(agyBackend.homeDir(), "brain");
26649
- if (!existsSync54(brainDir))
27509
+ const brainDir = join59(agyBackend.homeDir(), "brain");
27510
+ if (!existsSync55(brainDir))
26650
27511
  return;
26651
27512
  let sessionDirs = [];
26652
27513
  try {
@@ -26655,18 +27516,18 @@ function* walkAgy() {
26655
27516
  return;
26656
27517
  }
26657
27518
  for (const rawId of sessionDirs) {
26658
- const sessionPath = join58(brainDir, rawId);
27519
+ const sessionPath = join59(brainDir, rawId);
26659
27520
  try {
26660
- if (!statSync45(sessionPath).isDirectory())
27521
+ if (!statSync46(sessionPath).isDirectory())
26661
27522
  continue;
26662
27523
  } catch {
26663
27524
  continue;
26664
27525
  }
26665
- let filePath = join58(sessionPath, ".system_generated", "logs", "transcript.jsonl");
26666
- if (!existsSync54(filePath)) {
26667
- filePath = join58(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
27526
+ let filePath = join59(sessionPath, ".system_generated", "logs", "transcript.jsonl");
27527
+ if (!existsSync55(filePath)) {
27528
+ filePath = join59(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
26668
27529
  }
26669
- if (!existsSync54(filePath))
27530
+ if (!existsSync55(filePath))
26670
27531
  continue;
26671
27532
  const s = safeStat(filePath);
26672
27533
  if (!s)
@@ -26702,8 +27563,8 @@ __export(vault_client_exports, {
26702
27563
  vaultStatus: () => vaultStatus,
26703
27564
  vaultSync: () => vaultSync
26704
27565
  });
26705
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync47, writeFileSync as writeFileSync21, existsSync as existsSync55 } from "fs";
26706
- import { dirname as dirname28, join as join59 } from "path";
27566
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync48, writeFileSync as writeFileSync21, existsSync as existsSync56 } from "fs";
27567
+ import { dirname as dirname28, join as join60 } from "path";
26707
27568
  function context2() {
26708
27569
  const s = loadSettings().team;
26709
27570
  if (!s.enabled)
@@ -26842,7 +27703,7 @@ async function vaultSync(passphrase) {
26842
27703
  continue;
26843
27704
  }
26844
27705
  try {
26845
- const plaintext = readFileSync47(src.path);
27706
+ const plaintext = readFileSync48(src.path);
26846
27707
  const sourceSha = sha256Hex(plaintext);
26847
27708
  const priorSha = uploaded.get(uploadKey(src.sessionId, src.tool));
26848
27709
  if (priorSha === sourceSha) {
@@ -26894,7 +27755,7 @@ async function vaultSync(passphrase) {
26894
27755
  }
26895
27756
  function importedPathFor(tool, sessionId) {
26896
27757
  const base4 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
26897
- return join59(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
27758
+ return join60(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
26898
27759
  }
26899
27760
  async function vaultRestore(passphrase, opts = {}) {
26900
27761
  const ctx = context2();
@@ -26915,7 +27776,7 @@ async function vaultRestore(passphrase, opts = {}) {
26915
27776
  }
26916
27777
  try {
26917
27778
  const path = importedPathFor(blob.tool, blob.sessionId);
26918
- if (existsSync55(path)) {
27779
+ if (existsSync56(path)) {
26919
27780
  const localStat = await import("fs").then((m) => m.statSync(path));
26920
27781
  if (Math.abs(localStat.mtimeMs - blob.mtimeSourceMs) < 1) {
26921
27782
  report.skipped.push({ sessionId: blob.sessionId, tool: blob.tool, reason: "unchanged" });
@@ -26983,7 +27844,7 @@ var init_vault_client = __esm({
26983
27844
  uploadKey = (sessionId, tool) => `${sessionId}\0${tool}`;
26984
27845
  VaultPassphraseMismatchError = class extends Error {
26985
27846
  };
26986
- IMPORTED_ROOT = () => join59(getDataDir(), "imported");
27847
+ IMPORTED_ROOT = () => join60(getDataDir(), "imported");
26987
27848
  }
26988
27849
  });
26989
27850
 
@@ -27545,15 +28406,15 @@ __export(service_installer_exports, {
27545
28406
  uninstallService: () => uninstallService
27546
28407
  });
27547
28408
  import { execSync as execSync5 } from "child_process";
27548
- import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync48, rmSync as rmSync9, writeFileSync as writeFileSync22 } from "fs";
28409
+ import { existsSync as existsSync57, mkdirSync as mkdirSync22, readFileSync as readFileSync49, rmSync as rmSync10, writeFileSync as writeFileSync22 } from "fs";
27549
28410
  import { homedir as homedir22 } from "os";
27550
- import { join as join60 } from "path";
28411
+ import { join as join61 } from "path";
27551
28412
  import { fileURLToPath as fileURLToPath5 } from "url";
27552
28413
  function resolveDaemonPaths() {
27553
28414
  const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
27554
28415
  const node = process.execPath;
27555
- const dataDir = join60(homedir22(), ".chat-recall");
27556
- const logFile = join60(dataDir, "watch.log");
28416
+ const dataDir = join61(homedir22(), ".chat-recall");
28417
+ const logFile = join61(dataDir, "watch.log");
27557
28418
  return { watchJs, node, logFile, dataDir };
27558
28419
  }
27559
28420
  function tryCmd(cmd) {
@@ -27580,12 +28441,12 @@ function refreshServiceDefinition() {
27580
28441
  if (process.platform !== "linux") return false;
27581
28442
  try {
27582
28443
  const { watchJs, node, logFile } = resolveDaemonPaths();
27583
- const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27584
- if (!existsSync56(unitPath)) return false;
27585
- const current = readFileSync48(unitPath, "utf8");
28444
+ const unitPath = join61(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
28445
+ if (!existsSync57(unitPath)) return false;
28446
+ const current = readFileSync49(unitPath, "utf8");
27586
28447
  if (!current.includes(watchJs)) return false;
27587
28448
  const wanted = renderSystemdUnit(watchJs, node, logFile);
27588
- if (readFileSync48(unitPath, "utf8") === wanted) return false;
28449
+ if (readFileSync49(unitPath, "utf8") === wanted) return false;
27589
28450
  writeFileSync22(unitPath, wanted);
27590
28451
  execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
27591
28452
  return true;
@@ -27611,8 +28472,8 @@ function installService() {
27611
28472
  );
27612
28473
  }
27613
28474
  function installSystemd(watchJs, node, logFile) {
27614
- const unitDir = join60(homedir22(), ".config", "systemd", "user");
27615
- const unitPath = join60(unitDir, "chat-recall-watch.service");
28475
+ const unitDir = join61(homedir22(), ".config", "systemd", "user");
28476
+ const unitPath = join61(unitDir, "chat-recall-watch.service");
27616
28477
  const unit = renderSystemdUnit(watchJs, node, logFile);
27617
28478
  mkdirSync22(unitDir, { recursive: true });
27618
28479
  writeFileSync22(unitPath, unit);
@@ -27691,8 +28552,8 @@ function renderSystemdUnit(watchJs, node, logFile) {
27691
28552
  }
27692
28553
  function installLaunchd(watchJs, node, logFile) {
27693
28554
  const label = "com.chat-recall.watch";
27694
- const plistDir = join60(homedir22(), "Library", "LaunchAgents");
27695
- const plistPath = join60(plistDir, `${label}.plist`);
28555
+ const plistDir = join61(homedir22(), "Library", "LaunchAgents");
28556
+ const plistPath = join61(plistDir, `${label}.plist`);
27696
28557
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
27697
28558
  mkdirSync22(plistDir, { recursive: true });
27698
28559
  writeFileSync22(plistPath, plist);
@@ -27748,9 +28609,9 @@ function uninstallService() {
27748
28609
  removed = true;
27749
28610
  } catch {
27750
28611
  }
27751
- const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27752
- if (existsSync56(unitPath)) {
27753
- rmSync9(unitPath, { force: true });
28612
+ const unitPath = join61(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
28613
+ if (existsSync57(unitPath)) {
28614
+ rmSync10(unitPath, { force: true });
27754
28615
  removed = true;
27755
28616
  }
27756
28617
  try {
@@ -27760,14 +28621,14 @@ function uninstallService() {
27760
28621
  return removed;
27761
28622
  }
27762
28623
  if (process.platform === "darwin") {
27763
- const plistPath = join60(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
28624
+ const plistPath = join61(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
27764
28625
  try {
27765
28626
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
27766
28627
  } catch {
27767
28628
  }
27768
28629
  let removed = false;
27769
- if (existsSync56(plistPath)) {
27770
- rmSync9(plistPath, { force: true });
28630
+ if (existsSync57(plistPath)) {
28631
+ rmSync10(plistPath, { force: true });
27771
28632
  removed = true;
27772
28633
  }
27773
28634
  return removed;
@@ -27811,7 +28672,7 @@ __export(device_auth_exports, {
27811
28672
  betterAuthDeviceLogin: () => betterAuthDeviceLogin,
27812
28673
  deviceLogin: () => deviceLogin
27813
28674
  });
27814
- import { createHash as createHash15, randomBytes } from "node:crypto";
28675
+ import { createHash as createHash15, randomBytes as randomBytes2 } from "node:crypto";
27815
28676
  async function readDeviceCode(res, where) {
27816
28677
  const text = await res.text().catch(() => "");
27817
28678
  let parsed;
@@ -27839,7 +28700,7 @@ async function deviceLogin(opts, onPrompt) {
27839
28700
  }
27840
28701
  const clientId = opts.clientId || DEFAULT_CLIENT_ID;
27841
28702
  const scope = opts.scope || "openid email profile";
27842
- const codeVerifier = b64url(randomBytes(32));
28703
+ const codeVerifier = b64url(randomBytes2(32));
27843
28704
  const codeChallenge = b64url(createHash15("sha256").update(codeVerifier).digest());
27844
28705
  const startRes = await fetch(`${issuer}/protocol/openid-connect/auth/device`, {
27845
28706
  method: "POST",
@@ -28138,6 +28999,7 @@ var SESSION_ID_PREFIXES = {
28138
28999
  opencode: "opencode_",
28139
29000
  agy: "agy_",
28140
29001
  cursor: "cursor_",
29002
+ hermes: "hermes_",
28141
29003
  gemini: "gemini_"
28142
29004
  };
28143
29005
  function toolOfSessionId(sessionId) {
@@ -28166,6 +29028,8 @@ function resumeCommandFor(sessionId, tool) {
28166
29028
  // available answer for either.
28167
29029
  case "cursor":
28168
29030
  return `cursor-agent --resume ${raw("cursor_")}`;
29031
+ case "hermes":
29032
+ return `hermes --resume ${raw("hermes_")}`;
28169
29033
  // Gemini's `--resume` takes an index or "latest", never a session id.
28170
29034
  default:
28171
29035
  return null;
@@ -28289,9 +29153,9 @@ init_project_resolver();
28289
29153
  import { config } from "dotenv";
28290
29154
  import { Command } from "commander";
28291
29155
  import chalk2 from "chalk";
28292
- import { existsSync as existsSync57, readFileSync as readFileSync49, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
29156
+ import { existsSync as existsSync58, readFileSync as readFileSync50, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
28293
29157
  import { homedir as homedir23 } from "os";
28294
- import { join as join61, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
29158
+ import { join as join62, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
28295
29159
 
28296
29160
  // ../engine/dist/core/score-tier.js
28297
29161
  var STRONG_RATIO = 0.85;
@@ -28398,11 +29262,11 @@ function renderEnvNeeds(needs, installedCount) {
28398
29262
 
28399
29263
  // src/star-nudge.ts
28400
29264
  init_paths();
28401
- import { existsSync as existsSync43, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
28402
- import { dirname as dirname20, join as join45 } from "node:path";
29265
+ import { existsSync as existsSync44, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
29266
+ import { dirname as dirname20, join as join46 } from "node:path";
28403
29267
  var REPO = "https://github.com/munhq/chat-recall";
28404
29268
  function markerPath() {
28405
- return join45(getDataDir(), ".star-asked");
29269
+ return join46(getDataDir(), ".star-asked");
28406
29270
  }
28407
29271
  function suppressed() {
28408
29272
  const env = process.env;
@@ -28414,7 +29278,7 @@ function shouldAskForStar(resultCount) {
28414
29278
  if (suppressed()) return false;
28415
29279
  if (!process.stderr.isTTY) return false;
28416
29280
  try {
28417
- return !existsSync43(markerPath());
29281
+ return !existsSync44(markerPath());
28418
29282
  } catch {
28419
29283
  return false;
28420
29284
  }
@@ -28455,8 +29319,8 @@ function claudeHookConfigFiles() {
28455
29319
  const out = [];
28456
29320
  const seen = /* @__PURE__ */ new Set();
28457
29321
  for (const home of claudeHomeDirs()) {
28458
- if (!existsSync57(join61(home, "projects"))) continue;
28459
- const file = join61(home, "hooks.json");
29322
+ if (!existsSync58(join62(home, "projects"))) continue;
29323
+ const file = join62(home, "hooks.json");
28460
29324
  let key2;
28461
29325
  try {
28462
29326
  key2 = realpathSync5(file);
@@ -28551,13 +29415,13 @@ async function serverDelete(path, body2) {
28551
29415
  }
28552
29416
  var pkgEngines = (() => {
28553
29417
  try {
28554
- return JSON.parse(readFileSync49(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
29418
+ return JSON.parse(readFileSync50(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
28555
29419
  } catch {
28556
29420
  return "";
28557
29421
  }
28558
29422
  })();
28559
29423
  var pkgVersion = JSON.parse(
28560
- readFileSync49(new URL("../package.json", import.meta.url), "utf-8")
29424
+ readFileSync50(new URL("../package.json", import.meta.url), "utf-8")
28561
29425
  ).version;
28562
29426
  var DEFAULT_SERVER = (process.env.CHAT_RECALL_DEFAULT_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28563
29427
  var DEFAULT_ALLOW = [
@@ -28731,10 +29595,10 @@ program.command("init").description("Set up chat-recall: connect to your server,
28731
29595
  } else if (target) {
28732
29596
  }
28733
29597
  if (!options.skipMcp) {
28734
- const projectRoot = join61(import.meta.dirname, "..");
29598
+ const projectRoot = join62(import.meta.dirname, "..");
28735
29599
  let mcpBinOnPath = false;
28736
29600
  mcpBinOnPath = isOnPath("chat-recall-mcp");
28737
- const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join61(projectRoot, "dist", "mcp-relay.js")] };
29601
+ const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join62(projectRoot, "dist", "mcp-relay.js")] };
28738
29602
  const MCP_ENV = { NODE_OPTIONS: "--max-old-space-size=1024" };
28739
29603
  const { registerMcpEverywhere: registerMcpEverywhere2 } = await Promise.resolve().then(() => (init_mcp_clients(), mcp_clients_exports));
28740
29604
  const MCP_CLIENT_FOR_BIN = {
@@ -28774,7 +29638,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
28774
29638
  const detected = checkCodeindexStatus2();
28775
29639
  if (detected.installed && !forceInstall) {
28776
29640
  if (!options.skipMcp) {
28777
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29641
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
28778
29642
  const reg = registerCodeindexMcp2(mcpJsonPath, detected.path);
28779
29643
  }
28780
29644
  } else if (forceInstall) {
@@ -28783,7 +29647,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
28783
29647
  if (result.installed) {
28784
29648
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
28785
29649
  if (!options.skipMcp) {
28786
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29650
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
28787
29651
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
28788
29652
  }
28789
29653
  } else if (!result.prebuiltAvailable) {
@@ -29240,8 +30104,8 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
29240
30104
  try {
29241
30105
  const identityFile = getIdentityFilePath();
29242
30106
  let identity = "AI coding assistant";
29243
- if (existsSync57(identityFile)) {
29244
- identity = readFileSync49(identityFile, "utf-8").trim();
30107
+ if (existsSync58(identityFile)) {
30108
+ identity = readFileSync50(identityFile, "utf-8").trim();
29245
30109
  }
29246
30110
  const lines = [
29247
30111
  chalk2.bold("# Wake-Up Context"),
@@ -29341,7 +30205,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
29341
30205
  }
29342
30206
  });
29343
30207
  program.command("doctor").description("Quick health check across login, server, credentials, hooks, MCP server, and codeindex").option("--purge-local", "Remove the legacy local index (cache.db + vector/KG files) left by pre-thin-collector versions \u2014 storage is server-side now. Requires login; keeps credentials, sync ledger, identity, and the audit log.").action(async (opts) => {
29344
- const { existsSync: existsSync58, readFileSync: readFileSync50, statSync: statSync46 } = await import("fs");
30208
+ const { existsSync: existsSync59, readFileSync: readFileSync51, statSync: statSync47 } = await import("fs");
29345
30209
  const { execSync: execSync6 } = await import("child_process");
29346
30210
  const rows = [];
29347
30211
  const note = (ok, label, detail) => rows.push({ ok, label, detail });
@@ -29404,12 +30268,12 @@ program.command("doctor").description("Quick health check across login, server,
29404
30268
  }
29405
30269
  }
29406
30270
  }
29407
- const credFile = join61(getDataDir(), "credentials.json");
29408
- if (!existsSync58(credFile)) {
30271
+ const credFile = join62(getDataDir(), "credentials.json");
30272
+ if (!existsSync59(credFile)) {
29409
30273
  note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
29410
30274
  } else {
29411
30275
  try {
29412
- const mode = statSync46(credFile).mode & 511;
30276
+ const mode = statSync47(credFile).mode & 511;
29413
30277
  const isWin = process.platform === "win32";
29414
30278
  const safe = isWin || (mode & 63) === 0;
29415
30279
  note(safe, "Credentials file", isWin ? `${credFile} (Windows ACL \u2014 POSIX mode does not apply)` : `${credFile} (mode ${mode.toString(8).padStart(3, "0")}${safe ? "" : " \u2014 should be 600"})`);
@@ -29420,21 +30284,21 @@ program.command("doctor").description("Quick health check across login, server,
29420
30284
  for (const hooksJson of claudeHookConfigFiles()) {
29421
30285
  const profile = basename27(dirname29(hooksJson));
29422
30286
  const label = `Claude hooks (${profile})`;
29423
- if (!existsSync58(hooksJson)) {
30287
+ if (!existsSync59(hooksJson)) {
29424
30288
  note(false, label, `no ${hooksJson} \u2014 run \`chat-recall install-hooks\``);
29425
30289
  continue;
29426
30290
  }
29427
30291
  try {
29428
30292
  const readHooks = (file) => {
29429
- if (!existsSync58(file)) return {};
30293
+ if (!existsSync59(file)) return {};
29430
30294
  try {
29431
- return JSON.parse(readFileSync50(file, "utf-8")).hooks || {};
30295
+ return JSON.parse(readFileSync51(file, "utf-8")).hooks || {};
29432
30296
  } catch {
29433
30297
  return {};
29434
30298
  }
29435
30299
  };
29436
30300
  const cfg = readHooks(hooksJson);
29437
- const settings2 = readHooks(join61(dirname29(hooksJson), "settings.json"));
30301
+ const settings2 = readHooks(join62(dirname29(hooksJson), "settings.json"));
29438
30302
  const all = ["SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd"];
29439
30303
  const hits = [];
29440
30304
  for (const ev of all) {
@@ -29469,9 +30333,9 @@ program.command("doctor").description("Quick health check across login, server,
29469
30333
  try {
29470
30334
  const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
29471
30335
  for (const target2 of reg.guardTargets()) {
29472
- if (!existsSync58(target2.homeDir)) continue;
30336
+ if (!existsSync59(target2.homeDir)) continue;
29473
30337
  const label = `Decision guard \u2014 ${target2.label}`;
29474
- if (!existsSync58(target2.configPath)) {
30338
+ if (!existsSync59(target2.configPath)) {
29475
30339
  note(false, label, `not registered \u2014 run \`chat-recall install-hooks\``);
29476
30340
  continue;
29477
30341
  }
@@ -29481,7 +30345,7 @@ program.command("doctor").description("Quick health check across login, server,
29481
30345
  }
29482
30346
  let registered = false;
29483
30347
  try {
29484
- const cfg = JSON.parse(readFileSync50(target2.configPath, "utf-8"));
30348
+ const cfg = JSON.parse(readFileSync51(target2.configPath, "utf-8"));
29485
30349
  registered = JSON.stringify(cfg).includes("chat_recall_guard");
29486
30350
  } catch {
29487
30351
  }
@@ -29496,7 +30360,7 @@ program.command("doctor").description("Quick health check across login, server,
29496
30360
  const want = bundledSkillNames2().length;
29497
30361
  for (const t of skillTargets2()) {
29498
30362
  if (!t.available) continue;
29499
- const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync58(join61(t.dir, n, ".chat-recall-managed"))).length;
30363
+ const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync59(join62(t.dir, n, ".chat-recall-managed"))).length;
29500
30364
  note(
29501
30365
  have === want,
29502
30366
  `Skills \u2014 ${t.label}`,
@@ -29571,25 +30435,25 @@ program.command("doctor").description("Quick health check across login, server,
29571
30435
  note(watchRunning, "Watch daemon", watchRunning ? "running" : "not running (run `chat-recall watch --install-service` to keep collection running)");
29572
30436
  const dataDir = getDataDir();
29573
30437
  const legacyPaths = [
29574
- join61(dataDir, "cache.db"),
29575
- join61(dataDir, "cache.db-wal"),
29576
- join61(dataDir, "cache.db-shm"),
29577
- join61(dataDir, "index", "lancedb"),
29578
- join61(dataDir, "index", "knowledge_graph.db"),
29579
- join61(dataDir, "index", "metadata.db")
30438
+ join62(dataDir, "cache.db"),
30439
+ join62(dataDir, "cache.db-wal"),
30440
+ join62(dataDir, "cache.db-shm"),
30441
+ join62(dataDir, "index", "lancedb"),
30442
+ join62(dataDir, "index", "knowledge_graph.db"),
30443
+ join62(dataDir, "index", "metadata.db")
29580
30444
  ];
29581
- const legacyPresent = legacyPaths.filter((p) => existsSync58(p));
30445
+ const legacyPresent = legacyPaths.filter((p) => existsSync59(p));
29582
30446
  if (opts.purgeLocal) {
29583
30447
  if (!target) {
29584
30448
  note(false, "Purge legacy index", "refused \u2014 log in and `chat-recall sync` first, so your history is on the server before removing local copies");
29585
30449
  } else if (legacyPresent.length === 0) {
29586
30450
  note(true, "Purge legacy index", "nothing to remove");
29587
30451
  } else {
29588
- const { rmSync: rmSync10 } = await import("fs");
30452
+ const { rmSync: rmSync11 } = await import("fs");
29589
30453
  let removed = 0;
29590
30454
  for (const p of legacyPresent) {
29591
30455
  try {
29592
- rmSync10(p, { recursive: true, force: true });
30456
+ rmSync11(p, { recursive: true, force: true });
29593
30457
  removed++;
29594
30458
  } catch {
29595
30459
  }
@@ -29649,7 +30513,7 @@ companions.command("install").description("Install codeindex (downloads from mun
29649
30513
  if (result.installed) {
29650
30514
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
29651
30515
  console.log(chalk2.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
29652
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
30516
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
29653
30517
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
29654
30518
  if (reg.added) console.log(chalk2.dim(` Registered as MCP server in ${mcpJsonPath}`));
29655
30519
  else console.log(chalk2.dim(` Already registered as MCP server`));
@@ -29671,7 +30535,7 @@ companions.command("uninstall").description("Remove the codeindex binary chat-re
29671
30535
  const r = uninstallCodeindex2();
29672
30536
  if (r.removed) console.log(chalk2.green(`\u2713 Removed ${r.path}`));
29673
30537
  else console.log(chalk2.dim(` Nothing to remove at ${r.path}`));
29674
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
30538
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
29675
30539
  const u = unregisterCodeindexMcp2(mcpJsonPath);
29676
30540
  if (u.removed) console.log(chalk2.dim(` Unregistered MCP server from ${mcpJsonPath}`));
29677
30541
  });
@@ -30223,10 +31087,10 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
30223
31087
  try {
30224
31088
  const { resolveProjectId: resolveProjectId2 } = await Promise.resolve().then(() => (init_project_resolver(), project_resolver_exports));
30225
31089
  const { renderTeamTasksMd: renderTeamTasksMd2, parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
30226
- const { writeFileSync: writeFileSync24, readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
30227
- const { join: join62 } = await import("node:path");
30228
- const file = join62(cwd, "TEAM_TASKS.md");
30229
- if (!opts.force && existsSync58(file) && parseTeamTasksFile2(readFileSync50(file, "utf8")).length > 0) {
31090
+ const { writeFileSync: writeFileSync24, readFileSync: readFileSync51, existsSync: existsSync59 } = await import("node:fs");
31091
+ const { join: join63 } = await import("node:path");
31092
+ const file = join63(cwd, "TEAM_TASKS.md");
31093
+ if (!opts.force && existsSync59(file) && parseTeamTasksFile2(readFileSync51(file, "utf8")).length > 0) {
30230
31094
  console.error(chalk2.red("TEAM_TASKS.md has unpushed edits."), "Run", chalk2.bold("chat-recall tasks push"), "first, or", chalk2.bold("tasks pull --force"), "to discard.");
30231
31095
  process.exit(1);
30232
31096
  }
@@ -30245,14 +31109,14 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
30245
31109
  const cwd = dir || process.cwd();
30246
31110
  try {
30247
31111
  const { parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
30248
- const { readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
30249
- const { join: join62 } = await import("node:path");
30250
- const file = join62(cwd, "TEAM_TASKS.md");
30251
- if (!existsSync58(file)) {
31112
+ const { readFileSync: readFileSync51, existsSync: existsSync59 } = await import("node:fs");
31113
+ const { join: join63 } = await import("node:path");
31114
+ const file = join63(cwd, "TEAM_TASKS.md");
31115
+ if (!existsSync59(file)) {
30252
31116
  console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
30253
31117
  process.exit(1);
30254
31118
  }
30255
- const edits = parseTeamTasksFile2(readFileSync50(file, "utf8"));
31119
+ const edits = parseTeamTasksFile2(readFileSync51(file, "utf8"));
30256
31120
  let n = 0;
30257
31121
  for (const e of edits) {
30258
31122
  await serverPatch(`/api/tasks/${encodeURIComponent(e.id)}`, { status: e.intent });
@@ -30358,8 +31222,8 @@ team.command("publish <type> <name> <file>").description("Publish a local file a
30358
31222
  console.error(chalk2.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
30359
31223
  process.exit(1);
30360
31224
  }
30361
- const { readFileSync: readFileSync50 } = await import("fs");
30362
- const body2 = readFileSync50(file);
31225
+ const { readFileSync: readFileSync51 } = await import("fs");
31226
+ const body2 = readFileSync51(file);
30363
31227
  const { teamPublish: teamPublish2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
30364
31228
  const a = await teamPublish2({
30365
31229
  type,
@@ -30735,20 +31599,20 @@ program.command("task-hook").description("Move a board card when a prompt or a c
30735
31599
  }
30736
31600
  });
30737
31601
  program.command("install-hooks").description("Install Claude Code hooks (Stop + PreCompact + UserPromptSubmit + SessionEnd + SessionStart) into every Claude profile").option("--uninstall", "Remove hooks instead of installing them").option("--no-resume-hint", "Don't install the UserPromptSubmit resume-hint hook").option("--no-escalate", "Don't install the SessionEnd learnings-escalation hook").option("--no-wakeup", "Don't install the SessionStart wake-up hook").option("--no-guard", "Don't install the PreToolUse decision guard").option("--no-task-board", "Don't install the hooks that claim and close board cards").action(async (opts) => {
30738
- const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync46, unlinkSync: unlinkSync4 } = await import("fs");
31602
+ const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync59, readFileSync: readFileSync51, writeFileSync: writeFileSync24, statSync: statSync47, unlinkSync: unlinkSync4 } = await import("fs");
30739
31603
  const { fileURLToPath: fileURLToPath6 } = await import("url");
30740
31604
  const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
30741
31605
  const here = fileURLToPath6(new URL(".", import.meta.url));
30742
31606
  const findHook = (name) => {
30743
31607
  const candidates2 = [
30744
- join61(here, "..", "hooks", name),
30745
- join61(here, "..", "..", "hooks", name),
31608
+ join62(here, "..", "hooks", name),
31609
+ join62(here, "..", "..", "hooks", name),
30746
31610
  // The monorepo root, for a run straight from the checkout. The copy
30747
31611
  // under packages/cli/hooks is written by the package build, so a
30748
31612
  // freshly added script is only at the root until that build runs.
30749
- join61(here, "..", "..", "..", "hooks", name)
31613
+ join62(here, "..", "..", "..", "hooks", name)
30750
31614
  ];
30751
- return candidates2.find((p) => existsSync58(p));
31615
+ return candidates2.find((p) => existsSync59(p));
30752
31616
  };
30753
31617
  const sourceSaveHook = findHook("chat_recall_save_hook.sh");
30754
31618
  const sourceGuardHook = findHook("chat_recall_guard_hook.sh");
@@ -30760,16 +31624,16 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30760
31624
  process.exit(1);
30761
31625
  }
30762
31626
  const hooksDir = getHooksDir();
30763
- const installedSaveHook = join61(hooksDir, "chat_recall_save_hook.sh");
30764
- const installedGuardHook = join61(hooksDir, "chat_recall_guard_hook.sh");
30765
- const installedResumeHook = join61(hooksDir, "chat_recall_resume_hook.sh");
30766
- const installedEscalateHook = join61(hooksDir, "chat_recall_escalate_hook.sh");
30767
- const installedWakeupHook = join61(hooksDir, "chat_recall_wakeup_hook.sh");
31627
+ const installedSaveHook = join62(hooksDir, "chat_recall_save_hook.sh");
31628
+ const installedGuardHook = join62(hooksDir, "chat_recall_guard_hook.sh");
31629
+ const installedResumeHook = join62(hooksDir, "chat_recall_resume_hook.sh");
31630
+ const installedEscalateHook = join62(hooksDir, "chat_recall_escalate_hook.sh");
31631
+ const installedWakeupHook = join62(hooksDir, "chat_recall_wakeup_hook.sh");
30768
31632
  const wakeupWiredInSettings = (home) => {
30769
- const settings2 = join61(home, "settings.json");
30770
- if (!existsSync58(settings2)) return false;
31633
+ const settings2 = join62(home, "settings.json");
31634
+ if (!existsSync59(settings2)) return false;
30771
31635
  try {
30772
- const cfg = JSON.parse(readFileSync50(settings2, "utf-8"));
31636
+ const cfg = JSON.parse(readFileSync51(settings2, "utf-8"));
30773
31637
  const arr = Array.isArray(cfg.hooks?.SessionStart) ? cfg.hooks.SessionStart : [];
30774
31638
  return arr.some((h) => (h?.hooks?.[0]?.command || "").includes("chat_recall_wakeup_hook"));
30775
31639
  } catch {
@@ -30778,9 +31642,9 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30778
31642
  };
30779
31643
  const hookConfigFiles = claudeHookConfigFiles();
30780
31644
  const readHookConfig = (file) => {
30781
- if (!existsSync58(file)) return { hooks: {} };
31645
+ if (!existsSync59(file)) return { hooks: {} };
30782
31646
  try {
30783
- const c = JSON.parse(readFileSync50(file, "utf-8"));
31647
+ const c = JSON.parse(readFileSync51(file, "utf-8"));
30784
31648
  if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
30785
31649
  return c;
30786
31650
  } catch (err) {
@@ -30795,9 +31659,9 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30795
31659
  };
30796
31660
  const HOOK_EVENTS = ["Stop", "PreCompact", "UserPromptSubmit", "SessionEnd", "SessionStart", "PreToolUse"];
30797
31661
  const readJson4 = (file) => {
30798
- if (!existsSync58(file)) return {};
31662
+ if (!existsSync59(file)) return {};
30799
31663
  try {
30800
- return JSON.parse(readFileSync50(file, "utf-8"));
31664
+ return JSON.parse(readFileSync51(file, "utf-8"));
30801
31665
  } catch {
30802
31666
  return {};
30803
31667
  }
@@ -30806,8 +31670,8 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30806
31670
  const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
30807
31671
  const notes = [];
30808
31672
  for (const target of reg.guardTargets()) {
30809
- if (!existsSync58(target.homeDir)) continue;
30810
- const wrapper = join61(hooksDir, reg.guardWrapperName(target.tool));
31673
+ if (!existsSync59(target.homeDir)) continue;
31674
+ const wrapper = join62(hooksDir, reg.guardWrapperName(target.tool));
30811
31675
  writeFileSync24(wrapper, reg.guardWrapperSource(target.tool, hookPath));
30812
31676
  chmodSync5(wrapper, 493);
30813
31677
  mkdirSync23(dirname29(target.configPath), { recursive: true });
@@ -30827,9 +31691,9 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30827
31691
  const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
30828
31692
  let removed = 0;
30829
31693
  for (const target of reg.guardTargets()) {
30830
- if (!existsSync58(target.configPath)) continue;
31694
+ if (!existsSync59(target.configPath)) continue;
30831
31695
  if (target.tool === "opencode") {
30832
- const body2 = readFileSync50(target.configPath, "utf-8");
31696
+ const body2 = readFileSync51(target.configPath, "utf-8");
30833
31697
  if (!body2.includes("chat-recall install-hooks")) continue;
30834
31698
  unlinkSync4(target.configPath);
30835
31699
  removed += 1;
@@ -30855,7 +31719,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30855
31719
  if (filtered.length) config2.hooks[event] = filtered;
30856
31720
  else delete config2.hooks[event];
30857
31721
  }
30858
- if (removed === 0 && !existsSync58(hooksJson)) continue;
31722
+ if (removed === 0 && !existsSync59(hooksJson)) continue;
30859
31723
  writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
30860
31724
  total += removed;
30861
31725
  console.log(chalk2.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
@@ -30937,7 +31801,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30937
31801
  const config2 = readHookConfig(hooksJson);
30938
31802
  const home = dirname29(hooksJson);
30939
31803
  const alreadyWired = wakeupWiredInSettings(home);
30940
- if (alreadyWired) wakeupSkipped.push(join61(home, "settings.json"));
31804
+ if (alreadyWired) wakeupSkipped.push(join62(home, "settings.json"));
30941
31805
  const perProfile = installWakeup && !alreadyWired ? [...events, ["SessionStart", wakeupEntry]] : events;
30942
31806
  for (const event of HOOK_EVENTS) {
30943
31807
  const arr = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
@@ -30950,18 +31814,18 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30950
31814
  writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
30951
31815
  }
30952
31816
  if (installGuard) await addGuardToOtherTools(installedGuardHook);
30953
- const saveSz = statSync46(installedSaveHook).size;
31817
+ const saveSz = statSync47(installedSaveHook).size;
30954
31818
  console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
30955
31819
  if (installResume) {
30956
- const resumeSz = statSync46(installedResumeHook).size;
31820
+ const resumeSz = statSync47(installedResumeHook).size;
30957
31821
  console.log(chalk2.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
30958
31822
  }
30959
31823
  if (installEscalate) {
30960
- const escalateSz = statSync46(installedEscalateHook).size;
31824
+ const escalateSz = statSync47(installedEscalateHook).size;
30961
31825
  console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
30962
31826
  }
30963
31827
  if (installGuard) {
30964
- const guardSz = statSync46(installedGuardHook).size;
31828
+ const guardSz = statSync47(installedGuardHook).size;
30965
31829
  console.log(chalk2.green(`\u2713 Installed chat-recall decision guard (${guardSz} bytes)`));
30966
31830
  }
30967
31831
  if (installTaskBoard) {
@@ -30969,7 +31833,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30969
31833
  console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
30970
31834
  }
30971
31835
  if (installWakeup) {
30972
- const wakeupSz = statSync46(installedWakeupHook).size;
31836
+ const wakeupSz = statSync47(installedWakeupHook).size;
30973
31837
  console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
30974
31838
  for (const f of wakeupSkipped) {
30975
31839
  console.log(chalk2.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));
@@ -31430,8 +32294,8 @@ exclude.command("project <path>").description("Exclude a project path from synci
31430
32294
  saveSettings2(s);
31431
32295
  console.log(chalk2.green(`\u2713 Excluded ${abs}`) + chalk2.dim(" \u2014 takes effect next sync; already-synced rows stay until deleted."));
31432
32296
  });
31433
- exclude.command("tool <tool>").description("Exclude an AI tool entirely (claude | gemini | codex | opencode | agy | cursor)").action(async (tool) => {
31434
- const valid = ["claude", "gemini", "codex", "opencode", "agy", "cursor"];
32297
+ exclude.command("tool <tool>").description("Exclude an AI tool entirely (claude | gemini | codex | opencode | agy | cursor | hermes)").action(async (tool) => {
32298
+ const valid = ["claude", "gemini", "codex", "opencode", "agy", "cursor", "hermes"];
31435
32299
  if (!valid.includes(tool)) {
31436
32300
  console.error(chalk2.red(`Unknown tool '${tool}'.`) + chalk2.dim(` Use one of: ${valid.join(" | ")}`));
31437
32301
  process.exit(1);