chat-recall 0.7.0 → 0.7.1

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/watch.js CHANGED
@@ -6585,7 +6585,7 @@ var init_raw = __esm({
6585
6585
  // ../engine/dist/transcript/shadow.js
6586
6586
  import { gzipSync as gzipSync2, gunzipSync as gunzipSync2 } from "zlib";
6587
6587
  import { createHash as createHash2 } from "crypto";
6588
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync4 } from "fs";
6588
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, statSync as statSync4, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
6589
6589
  import { dirname as dirname7, join as join8 } from "path";
6590
6590
  function shadowRoot() {
6591
6591
  return join8(getDataDir(), "shadow");
@@ -6809,6 +6809,29 @@ function seedShadow(sessionId, container) {
6809
6809
  }
6810
6810
  return fullest;
6811
6811
  }
6812
+ function shadowUncompressedBytes(tool, rawId) {
6813
+ const path = shadowFileFor(tool, rawId);
6814
+ let fd = null;
6815
+ try {
6816
+ const size = statSync4(path).size;
6817
+ if (size < 18)
6818
+ return 0;
6819
+ fd = openSync2(path, "r");
6820
+ const tail = Buffer.alloc(4);
6821
+ if (readSync2(fd, tail, 0, 4, size - 4) !== 4)
6822
+ return 0;
6823
+ return tail.readUInt32LE(0);
6824
+ } catch {
6825
+ return 0;
6826
+ } finally {
6827
+ if (fd !== null) {
6828
+ try {
6829
+ closeSync2(fd);
6830
+ } catch {
6831
+ }
6832
+ }
6833
+ }
6834
+ }
6812
6835
  function shadowSizeOnDisk(tool, rawId) {
6813
6836
  try {
6814
6837
  return statSync4(shadowFileFor(tool, rawId)).size;
@@ -6829,7 +6852,7 @@ var init_shadow = __esm({
6829
6852
  });
6830
6853
 
6831
6854
  // ../engine/dist/core/live-session-scan.js
6832
- import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
6855
+ import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync5, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
6833
6856
  import { join as join9, basename as basename5 } from "path";
6834
6857
  function detectTool(sessionId) {
6835
6858
  return getBackendForId(sessionId)?.id ?? "claude";
@@ -6856,6 +6879,15 @@ function sessionFileIndex() {
6856
6879
  return indexCache;
6857
6880
  const byId = /* @__PURE__ */ new Map();
6858
6881
  const seenRoots = /* @__PURE__ */ new Set();
6882
+ const projectPaths = /* @__PURE__ */ new Map();
6883
+ const projectPathOf = (dirName) => {
6884
+ let p = projectPaths.get(dirName);
6885
+ if (p === void 0) {
6886
+ p = resolveProjectDirName(dirName);
6887
+ projectPaths.set(dirName, p);
6888
+ }
6889
+ return p;
6890
+ };
6859
6891
  for (const root of claudeProjectDirs()) {
6860
6892
  if (seenRoots.has(root) || !existsSync7(root))
6861
6893
  continue;
@@ -6883,7 +6915,7 @@ function sessionFileIndex() {
6883
6915
  const located = {
6884
6916
  path: join9(dir, name),
6885
6917
  projectDir: entry.name,
6886
- projectPath: resolveProjectDirName(entry.name)
6918
+ projectPath: projectPathOf(entry.name)
6887
6919
  };
6888
6920
  const prior = byId.get(id);
6889
6921
  if (prior)
@@ -7565,7 +7597,7 @@ var init_flat_string = __esm({
7565
7597
  });
7566
7598
 
7567
7599
  // ../engine/dist/core/first-prompt.js
7568
- import { createReadStream as createReadStream2, existsSync as existsSync9, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
7600
+ import { createReadStream as createReadStream2, existsSync as existsSync9, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
7569
7601
  import { StringDecoder } from "string_decoder";
7570
7602
  function stripBanners2(s) {
7571
7603
  let out = s;
@@ -7619,7 +7651,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
7619
7651
  const maxBytes = options.maxBytes ?? 4 * 1024 * 1024;
7620
7652
  let fd;
7621
7653
  try {
7622
- fd = openSync3(filePath, "r");
7654
+ fd = openSync4(filePath, "r");
7623
7655
  } catch {
7624
7656
  return "";
7625
7657
  }
@@ -7631,7 +7663,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
7631
7663
  let pos = 0;
7632
7664
  let lineNo = 0;
7633
7665
  for (; ; ) {
7634
- const n = readSync3(fd, buf, 0, CHUNK, pos);
7666
+ const n = readSync4(fd, buf, 0, CHUNK, pos);
7635
7667
  if (n <= 0)
7636
7668
  break;
7637
7669
  pos += n;
@@ -7657,7 +7689,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
7657
7689
  return "";
7658
7690
  } finally {
7659
7691
  try {
7660
- closeSync3(fd);
7692
+ closeSync4(fd);
7661
7693
  } catch {
7662
7694
  }
7663
7695
  }
@@ -7676,8 +7708,8 @@ var init_first_prompt = __esm({
7676
7708
  });
7677
7709
 
7678
7710
  // ../engine/dist/core/backends/tail-read.js
7679
- import { openSync as openSync4, readSync as readSync4, closeSync as closeSync4, statSync as statSync6 } from "node:fs";
7680
- function readTailFromOffset(path, offset) {
7711
+ import { openSync as openSync5, readSync as readSync5, closeSync as closeSync5, statSync as statSync6 } from "node:fs";
7712
+ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7681
7713
  let size = 0;
7682
7714
  try {
7683
7715
  size = statSync6(path).size;
@@ -7686,14 +7718,14 @@ function readTailFromOffset(path, offset) {
7686
7718
  }
7687
7719
  if (size <= offset)
7688
7720
  return { text: "", newOffset: offset };
7689
- const len = Math.min(size - offset, 16 * 1024 * 1024);
7721
+ const len = Math.min(size - offset, Math.max(1, maxBytes));
7690
7722
  const buf = Buffer.allocUnsafe(len);
7691
7723
  let fd = null;
7692
7724
  try {
7693
- fd = openSync4(path, "r");
7725
+ fd = openSync5(path, "r");
7694
7726
  let got = 0;
7695
7727
  while (got < len) {
7696
- const n = readSync4(fd, buf, got, len - got, offset + got);
7728
+ const n = readSync5(fd, buf, got, len - got, offset + got);
7697
7729
  if (n <= 0)
7698
7730
  break;
7699
7731
  got += n;
@@ -7702,25 +7734,47 @@ function readTailFromOffset(path, offset) {
7702
7734
  return { text: "", newOffset: offset };
7703
7735
  const slice = got < len ? buf.subarray(0, got) : buf;
7704
7736
  const lastNl = slice.lastIndexOf(10);
7705
- if (lastNl < 0) {
7706
- return { text: "", newOffset: offset };
7737
+ if (lastNl >= 0) {
7738
+ const text = slice.subarray(0, lastNl + 1).toString("utf-8");
7739
+ return { text, newOffset: offset + lastNl + 1 };
7707
7740
  }
7708
- const text = slice.subarray(0, lastNl + 1).toString("utf-8");
7709
- return { text, newOffset: offset + lastNl + 1 };
7741
+ if (offset + got >= size)
7742
+ return { text: "", newOffset: offset };
7743
+ const end = findNewline(fd, offset + got, size);
7744
+ if (end < 0)
7745
+ return { text: "", newOffset: offset };
7746
+ return { text: "", newOffset: end + 1, skippedBytes: end + 1 - offset };
7710
7747
  } catch {
7711
7748
  return { text: "", newOffset: offset };
7712
7749
  } finally {
7713
7750
  if (fd !== null) {
7714
7751
  try {
7715
- closeSync4(fd);
7752
+ closeSync5(fd);
7716
7753
  } catch {
7717
7754
  }
7718
7755
  }
7719
7756
  }
7720
7757
  }
7758
+ function findNewline(fd, from, size) {
7759
+ const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
7760
+ let pos = from;
7761
+ while (pos < size) {
7762
+ const n = readSync5(fd, block, 0, Math.min(SKIP_SCAN_BYTES, size - pos), pos);
7763
+ if (n <= 0)
7764
+ return -1;
7765
+ const i = block.subarray(0, n).indexOf(10);
7766
+ if (i >= 0)
7767
+ return pos + i;
7768
+ pos += n;
7769
+ }
7770
+ return -1;
7771
+ }
7772
+ var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
7721
7773
  var init_tail_read = __esm({
7722
7774
  "../engine/dist/core/backends/tail-read.js"() {
7723
7775
  "use strict";
7776
+ TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
7777
+ SKIP_SCAN_BYTES = 1024 * 1024;
7724
7778
  }
7725
7779
  });
7726
7780
 
@@ -7997,7 +8051,7 @@ var init_generic_engine = __esm({
7997
8051
  });
7998
8052
 
7999
8053
  // ../engine/dist/core/backends/claude.js
8000
- import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as statSync7, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
8054
+ import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as statSync7, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
8001
8055
  import { join as join10, basename as basename8 } from "path";
8002
8056
  function stripReminders(text) {
8003
8057
  return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
@@ -8094,13 +8148,13 @@ var init_claude2 = __esm({
8094
8148
  return null;
8095
8149
  let head;
8096
8150
  try {
8097
- const fd = openSync5(located.path, "r");
8151
+ const fd = openSync6(located.path, "r");
8098
8152
  try {
8099
8153
  const buf = Buffer.alloc(65536);
8100
- const n = readSync5(fd, buf, 0, buf.length, 0);
8154
+ const n = readSync6(fd, buf, 0, buf.length, 0);
8101
8155
  head = buf.toString("utf-8", 0, n);
8102
8156
  } finally {
8103
- closeSync5(fd);
8157
+ closeSync6(fd);
8104
8158
  }
8105
8159
  } catch {
8106
8160
  return null;
@@ -8572,14 +8626,14 @@ var init_claude2 = __esm({
8572
8626
  spansMultipleSources(prefixedId) {
8573
8627
  return findSessionFiles(this.toRawId(prefixedId)).length > 1;
8574
8628
  }
8575
- async readFromOffset(prefixedId, offset) {
8629
+ async readFromOffset(prefixedId, offset, maxBytes) {
8576
8630
  const rawId = this.toRawId(prefixedId);
8577
8631
  const copies = findSessionFiles(rawId);
8578
8632
  if (copies.length === 0)
8579
8633
  return { text: "", newOffset: offset };
8580
8634
  if (copies.length > 1)
8581
8635
  return { text: "", newOffset: offset };
8582
- return readTailFromOffset(copies[0].path, offset);
8636
+ return readTailFromOffset(copies[0].path, offset, maxBytes);
8583
8637
  }
8584
8638
  };
8585
8639
  claudeBackend = new ClaudeBackend();
@@ -9163,7 +9217,7 @@ var init_opencode = __esm({
9163
9217
  });
9164
9218
 
9165
9219
  // ../engine/dist/core/backends/codex.js
9166
- import { readdirSync as readdirSync8, readFileSync as readFileSync6, statSync as statSync8, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
9220
+ import { readdirSync as readdirSync8, readFileSync as readFileSync6, statSync as statSync8, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
9167
9221
  import { basename as basename9, dirname as dirname10, join as join12 } from "path";
9168
9222
  function parseShellWrites(cmd) {
9169
9223
  const out = [];
@@ -9837,10 +9891,10 @@ var init_codex2 = __esm({
9837
9891
  if (p === loc.path)
9838
9892
  continue;
9839
9893
  try {
9840
- const fd = openSync6(p, "r");
9894
+ const fd = openSync7(p, "r");
9841
9895
  const buf = Buffer.alloc(4096);
9842
- const n = readSync6(fd, buf, 0, 4096, 0);
9843
- closeSync6(fd);
9896
+ const n = readSync7(fd, buf, 0, 4096, 0);
9897
+ closeSync7(fd);
9844
9898
  const head = buf.toString("utf-8", 0, n);
9845
9899
  if (head.includes(parentId) && head.includes("parent_thread_id"))
9846
9900
  push(p, `subagents/${f}`);
@@ -9865,11 +9919,11 @@ var init_codex2 = __esm({
9865
9919
  return 0;
9866
9920
  }
9867
9921
  }
9868
- async readFromOffset(prefixedId, offset) {
9922
+ async readFromOffset(prefixedId, offset, maxBytes) {
9869
9923
  const loc = this.findSession(prefixedId);
9870
9924
  if (!loc)
9871
9925
  return { text: "", newOffset: offset };
9872
- return readTailFromOffset(loc.path, offset);
9926
+ return readTailFromOffset(loc.path, offset, maxBytes);
9873
9927
  }
9874
9928
  };
9875
9929
  codexBackend = new CodexBackend();
@@ -10430,11 +10484,11 @@ var init_agy = __esm({
10430
10484
  return 0;
10431
10485
  }
10432
10486
  }
10433
- async readFromOffset(prefixedId, offset) {
10487
+ async readFromOffset(prefixedId, offset, maxBytes) {
10434
10488
  const loc = this.findSession(prefixedId);
10435
10489
  if (!loc)
10436
10490
  return { text: "", newOffset: offset };
10437
- return readTailFromOffset(loc.path, offset);
10491
+ return readTailFromOffset(loc.path, offset, maxBytes);
10438
10492
  }
10439
10493
  };
10440
10494
  agyBackend = new AgyBackend();
@@ -12895,10 +12949,10 @@ var init_types = __esm({
12895
12949
 
12896
12950
  // ../engine/dist/transcript/opencode.js
12897
12951
  async function parseOpenCodeSubagents(parentSessionId) {
12898
- const { existsSync: existsSync50 } = await import("fs");
12952
+ const { existsSync: existsSync51 } = await import("fs");
12899
12953
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
12900
12954
  const dbPath = opencodeBackend2.dbPath();
12901
- if (!existsSync50(dbPath))
12955
+ if (!existsSync51(dbPath))
12902
12956
  return [];
12903
12957
  const realParent = parentSessionId.replace("opencode_", "");
12904
12958
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -12939,10 +12993,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
12939
12993
  }
12940
12994
  }
12941
12995
  async function parseOpenCodeTranscript(sessionId) {
12942
- const { existsSync: existsSync50 } = await import("fs");
12996
+ const { existsSync: existsSync51 } = await import("fs");
12943
12997
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
12944
12998
  const dbPath = opencodeBackend2.dbPath();
12945
- if (!existsSync50(dbPath))
12999
+ if (!existsSync51(dbPath))
12946
13000
  return [];
12947
13001
  const realId = sessionId.replace("opencode_", "");
12948
13002
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -13019,6 +13073,7 @@ __export(transcript_exports, {
13019
13073
  shadowFileFor: () => shadowFileFor,
13020
13074
  shadowRoot: () => shadowRoot,
13021
13075
  shadowSizeOnDisk: () => shadowSizeOnDisk,
13076
+ shadowUncompressedBytes: () => shadowUncompressedBytes,
13022
13077
  snapshotShadow: () => snapshotShadow,
13023
13078
  trimTranscriptForSync: () => trimTranscriptForSync,
13024
13079
  updateShadow: () => updateShadow,
@@ -17875,7 +17930,7 @@ var init_sync_fields = __esm({
17875
17930
  });
17876
17931
 
17877
17932
  // ../engine/dist/core/index-lock.js
17878
- import { existsSync as existsSync39, mkdirSync as mkdirSync8, openSync as openSync7, writeFileSync as writeFileSync7, readFileSync as readFileSync31, rmSync as rmSync4, statSync as statSync35, closeSync as closeSync7 } from "fs";
17933
+ import { existsSync as existsSync39, mkdirSync as mkdirSync8, openSync as openSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync31, rmSync as rmSync4, statSync as statSync35, closeSync as closeSync8 } from "fs";
17879
17934
  import { dirname as dirname17, join as join40 } from "path";
17880
17935
  function acquireIndexLock(opts) {
17881
17936
  const lockPath = lockFilePath();
@@ -17887,7 +17942,7 @@ function acquireIndexLock(opts) {
17887
17942
  }
17888
17943
  let fd;
17889
17944
  try {
17890
- fd = openSync7(lockPath, "wx", 384);
17945
+ fd = openSync8(lockPath, "wx", 384);
17891
17946
  } catch (err) {
17892
17947
  if (err.code === "EEXIST")
17893
17948
  return null;
@@ -17897,7 +17952,7 @@ function acquireIndexLock(opts) {
17897
17952
  try {
17898
17953
  writeFileSync7(fd, JSON.stringify(holder));
17899
17954
  } finally {
17900
- closeSync7(fd);
17955
+ closeSync8(fd);
17901
17956
  }
17902
17957
  let released = false;
17903
17958
  return {
@@ -17988,7 +18043,7 @@ var init_http = __esm({
17988
18043
  CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
17989
18044
  } catch {
17990
18045
  try {
17991
- if (true) CLI_VERSION = "0.7.0";
18046
+ if (true) CLI_VERSION = "0.7.1";
17992
18047
  } catch {
17993
18048
  }
17994
18049
  }
@@ -18483,7 +18538,7 @@ var init_telemetry = __esm({
18483
18538
  init_sync_client();
18484
18539
  cliVersion = () => {
18485
18540
  try {
18486
- return true ? "0.7.0" : "dev";
18541
+ return true ? "0.7.1" : "dev";
18487
18542
  } catch {
18488
18543
  return "dev";
18489
18544
  }
@@ -18708,16 +18763,17 @@ __export(auto_update_exports, {
18708
18763
  noteUpdateFailure: () => noteUpdateFailure,
18709
18764
  planAutoUpdate: () => planAutoUpdate,
18710
18765
  readUpdateState: () => readUpdateState,
18766
+ resolveNpm: () => resolveNpm,
18711
18767
  runAutoUpdate: () => runAutoUpdate,
18712
18768
  runningPrefix: () => runningPrefix,
18713
18769
  sweepStaleStaging: () => sweepStaleStaging,
18714
18770
  tarballUrl: () => tarballUrl
18715
18771
  });
18716
- import { execSync as execSync2 } from "node:child_process";
18717
- import { readFileSync as readFileSync34 } from "node:fs";
18772
+ import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
18773
+ import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
18718
18774
  import { fileURLToPath as fileURLToPath2 } from "node:url";
18719
18775
  import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36, mkdirSync as mkdirSync9 } from "node:fs";
18720
- import { join as join41, dirname as dirname18, sep as sep2 } from "node:path";
18776
+ import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
18721
18777
  import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
18722
18778
  import { createHash as createHash7 } from "node:crypto";
18723
18779
  function compareVersions(a, b) {
@@ -18832,25 +18888,46 @@ function runningPrefix(moduleUrl = import.meta.url) {
18832
18888
  const beforeNodeModules = here.slice(0, at);
18833
18889
  return beforeNodeModules.endsWith(`${sep2}lib`) ? beforeNodeModules.slice(0, -4) : beforeNodeModules;
18834
18890
  }
18835
- function npmInstall(cmd) {
18891
+ function npmInstall(npm, args) {
18836
18892
  try {
18837
- execSync2(cmd, { stdio: ["ignore", "ignore", "pipe"] });
18893
+ const opts = { stdio: ["ignore", "ignore", "pipe"], env: npm.env };
18894
+ if (npm.shell) execSync2([npm.file, ...npm.args, ...args].map((a) => `"${a}"`).join(" "), opts);
18895
+ else execFileSync3(npm.file, [...npm.args, ...args], opts);
18838
18896
  } catch (e) {
18839
18897
  const err = e;
18840
18898
  const detail = String(err.stderr ?? "").trim().split("\n").filter((l) => l.trim() && !/^npm (notice|warn)\b/i.test(l)).slice(-4).join(" | ").slice(0, 400);
18841
18899
  throw new Error(detail ? `${err.message ?? "install failed"} \u2014 ${detail}` : err.message ?? "install failed");
18842
18900
  }
18843
18901
  }
18902
+ function resolveNpm(loc) {
18903
+ const win = loc.platform === "win32";
18904
+ const p = win ? pathWin32 : pathPosix;
18905
+ const nodeDir = p.dirname(loc.execPath);
18906
+ const cliUnder = (root) => win ? p.join(root, "node_modules", "npm", "bin", "npm-cli.js") : p.join(root, "lib", "node_modules", "npm", "bin", "npm-cli.js");
18907
+ const candidates2 = [
18908
+ cliUnder(win ? nodeDir : p.join(nodeDir, "..")),
18909
+ ...loc.prefix ? [cliUnder(loc.prefix)] : []
18910
+ ];
18911
+ const pathKey = Object.keys(loc.env).find((k) => k.toUpperCase() === "PATH") ?? "PATH";
18912
+ const binDirs = [nodeDir, ...loc.prefix ? [win ? loc.prefix : p.join(loc.prefix, "bin")] : []];
18913
+ const delimiter3 = win ? ";" : ":";
18914
+ const current = loc.env[pathKey] ?? "";
18915
+ const env = { ...loc.env, [pathKey]: [...binDirs, ...current ? [current] : []].join(delimiter3) };
18916
+ const cli = candidates2.find((c) => loc.exists(c));
18917
+ if (cli) return { file: loc.execPath, args: [cli], env, shell: false };
18918
+ return { file: "npm", args: [], env, shell: true };
18919
+ }
18844
18920
  function realInstall(tgz) {
18845
18921
  const prefix = runningPrefix();
18922
+ const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync41 });
18846
18923
  if (prefix) {
18847
- npmInstall(`npm install -g --prefix "${prefix}" "${tgz}"`);
18924
+ npmInstall(npm, ["install", "-g", "--prefix", prefix, tgz]);
18848
18925
  return;
18849
18926
  }
18850
18927
  try {
18851
- npmInstall(`npm install -g "${tgz}"`);
18928
+ npmInstall(npm, ["install", "-g", tgz]);
18852
18929
  } catch {
18853
- npmInstall(`npm install -g --prefix "${process.env.HOME}/.local" "${tgz}"`);
18930
+ npmInstall(npm, ["install", "-g", "--prefix", join41(homedir15(), ".local"), tgz]);
18854
18931
  }
18855
18932
  }
18856
18933
  function realRestart(platform2) {
@@ -19653,16 +19730,53 @@ async function syncToTargetInScope(cred, opts = {}) {
19653
19730
  clearTimeout(timer);
19654
19731
  }
19655
19732
  };
19733
+ const postChunkedHead = async (conv, fileSize) => {
19734
+ const headers = { "content-type": "application/json" };
19735
+ if (cred.token) headers.authorization = `Bearer ${cred.token}`;
19736
+ const ac = new AbortController();
19737
+ const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
19738
+ try {
19739
+ const res = await fetch(`${base2}/api/sync`, {
19740
+ method: "POST",
19741
+ headers,
19742
+ body: JSON.stringify({ conversations: [conv] }),
19743
+ signal: ac.signal
19744
+ });
19745
+ if (!res.ok) throw new Error(`chunked head sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
19746
+ const body = await res.json().catch(() => ({}));
19747
+ const id = conv.session_id;
19748
+ const guarded = (body.shrink_guarded ?? []).find((g) => g.session_id === id);
19749
+ const mtime = conv.mtime;
19750
+ try {
19751
+ if (!guarded) {
19752
+ markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
19753
+ } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
19754
+ markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
19755
+ } else {
19756
+ markSynced(base2, [{ id, mtime }]);
19757
+ }
19758
+ } catch {
19759
+ }
19760
+ appendResults.uploaded++;
19761
+ } finally {
19762
+ clearTimeout(timer);
19763
+ }
19764
+ };
19656
19765
  let appendBatch = [];
19766
+ let appendBatchBytes = 0;
19657
19767
  const APPEND_BATCH_SIZE = 50;
19768
+ const APPEND_BATCH_MAX_BYTES = SYNC_CHUNK_BYTES;
19658
19769
  const appendFlush = async () => {
19659
19770
  if (appendBatch.length === 0) return;
19660
19771
  const batch = appendBatch;
19661
19772
  appendBatch = [];
19773
+ appendBatchBytes = 0;
19662
19774
  await postAppend(batch);
19663
19775
  };
19664
- const appendAdd = async (conv) => {
19776
+ const appendAdd = async (conv, bytes = 0) => {
19777
+ if (appendBatch.length > 0 && appendBatchBytes + bytes > APPEND_BATCH_MAX_BYTES) await appendFlush();
19665
19778
  appendBatch.push(conv);
19779
+ appendBatchBytes += bytes;
19666
19780
  if (appendBatch.length >= APPEND_BATCH_SIZE) await appendFlush();
19667
19781
  };
19668
19782
  const itemsBatch = makeBatcher("items", 100);
@@ -19741,7 +19855,7 @@ async function syncToTargetInScope(cred, opts = {}) {
19741
19855
  let externalFindings = null;
19742
19856
  if (scanSecrets) {
19743
19857
  externalFindings = /* @__PURE__ */ new Map();
19744
- const toScan = slice.filter((ref) => modeOf(ref) === "full" && sessionFileBytes(ref) <= FULL_BUILD_MAX_BYTES);
19858
+ const toScan = slice.filter((ref) => modeOf(ref) === "full" && sessionBuildBytes(ref) <= FULL_BUILD_MAX_BYTES);
19745
19859
  trace?.(`batch secret scan start: ${toScan.length} session(s), \u2264${(BATCH_SCAN_MAX_BYTES / 1048576).toFixed(0)}MB per slice`);
19746
19860
  const batch = batchScanExternal(
19747
19861
  toScan.map((ref) => ({
@@ -19785,11 +19899,17 @@ async function syncToTargetInScope(cred, opts = {}) {
19785
19899
  const ack = ledger.get(ref.prefixedId);
19786
19900
  try {
19787
19901
  const tail = await buildConversationTail(ref, ack.o ?? 0, { mapPath, tenantRules });
19788
- if (tail && tail.findings.length > 0) {
19902
+ const chunked = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
19903
+ if (tail && tail.findings.length > 0 && !chunked) {
19789
19904
  promotedForFindings = true;
19790
19905
  } else if (tail) {
19791
19906
  redactions += tail.redactions;
19792
- await appendAdd(tail.conv);
19907
+ await appendAdd(tail.conv, tail.bytes);
19908
+ if (tail.findings.length > 0) {
19909
+ await appendFlush();
19910
+ const all = await scanTranscriptFindingsChunked(ref, tenantRules);
19911
+ if (all.length > 0) await findingsBatch.add(all);
19912
+ }
19793
19913
  walked++;
19794
19914
  if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
19795
19915
  continue;
@@ -19837,12 +19957,13 @@ async function syncToTargetInScope(cred, opts = {}) {
19837
19957
  }
19838
19958
  redactions += built.redactions;
19839
19959
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
19840
- await convBatch.add(built.conv);
19960
+ if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
19961
+ else await convBatch.add(built.conv);
19841
19962
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
19842
19963
  if (upload.sessionMeta) {
19843
19964
  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);
19844
19965
  }
19845
- if (upload.sessionMeta && sessionFileBytes(ref) <= FULL_BUILD_MAX_BYTES) {
19966
+ if (upload.sessionMeta && sessionFileBytes(ref) <= DERIVED_MAX_BYTES) {
19846
19967
  try {
19847
19968
  const count = { redactions: 0 };
19848
19969
  const offloaded = await scanPool().runDerived({
@@ -20022,35 +20143,70 @@ async function syncToTargetInScope(cred, opts = {}) {
20022
20143
  scanMs: Math.round(scanMs)
20023
20144
  };
20024
20145
  }
20146
+ async function scanTranscriptFindingsChunked(ref, tenantRules) {
20147
+ const backend = getBackendForId(ref.prefixedId);
20148
+ const findings = [];
20149
+ if (!backend?.readFromOffset) return findings;
20150
+ let offset = 0;
20151
+ let lineBase = 0;
20152
+ for (; ; ) {
20153
+ const chunk = await backend.readFromOffset(ref.prefixedId, offset, SYNC_CHUNK_BYTES);
20154
+ if (chunk.newOffset <= offset) break;
20155
+ offset = chunk.newOffset;
20156
+ if (chunk.skippedBytes) {
20157
+ lineBase += 1;
20158
+ continue;
20159
+ }
20160
+ try {
20161
+ for (const f of scanTextForFindings(chunk.text)) {
20162
+ findings.push({ session_id: ref.prefixedId, detector: "builtin", rule: f.rule, line: lineBase + f.line, preview: f.preview });
20163
+ }
20164
+ if (tenantRules && tenantRules.length > 0) {
20165
+ for (const f of scanTenantRules(chunk.text, tenantRules)) {
20166
+ findings.push({ session_id: ref.prefixedId, detector: f.detector, rule: f.rule, line: lineBase + f.line, preview: f.preview });
20167
+ }
20168
+ }
20169
+ } catch {
20170
+ }
20171
+ for (let i = chunk.text.indexOf("\n"); i >= 0; i = chunk.text.indexOf("\n", i + 1)) lineBase++;
20172
+ }
20173
+ return dropFuzzyFindings(findings, (f) => ({ detector: f.detector, rule: f.rule }));
20174
+ }
20025
20175
  function sessionFileBytes(ref) {
20026
20176
  const backend = getBackendForId(ref.prefixedId);
20027
20177
  if (!backend?.isAppendOnly?.()) return 0;
20028
20178
  return backend.fileSize?.(ref.prefixedId) ?? 0;
20029
20179
  }
20180
+ function sessionBuildBytes(ref) {
20181
+ const main = sessionFileBytes(ref);
20182
+ if (main === 0) return 0;
20183
+ return Math.max(main, shadowUncompressedBytes(ref.toolId, ref.rawId));
20184
+ }
20030
20185
  async function buildConversationSync(ref, mtime, opts = {}) {
20031
20186
  const mapPath = opts.mapPath ?? ((p) => p);
20032
20187
  let includeRaw = opts.includeRaw !== false;
20033
20188
  let includeMeta = opts.includeMeta !== false;
20034
20189
  let srcHash;
20035
- const fileBytes = sessionFileBytes(ref);
20036
- const oversized = fileBytes > FULL_BUILD_MAX_BYTES;
20037
- let tailText = "";
20038
- let tailOffsetEnd = 0;
20190
+ const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
20191
+ let headOffsetEnd = 0;
20192
+ let oversizedFindings = null;
20039
20193
  let transcript = null;
20040
20194
  let fullestContainer = null;
20041
20195
  if (oversized) {
20042
20196
  const backend = getBackendForId(ref.prefixedId);
20043
20197
  if (!backend?.readFromOffset) return null;
20044
20198
  try {
20045
- const tail = await backend.readFromOffset(ref.prefixedId, fileBytes - FULL_BUILD_TAIL_BYTES);
20046
- tailText = tail.text;
20047
- tailOffsetEnd = tail.newOffset;
20048
- const messages = parseTailMessages(ref.toolId, tailText);
20199
+ let head = await backend.readFromOffset(ref.prefixedId, 0, SYNC_CHUNK_BYTES);
20200
+ for (let hops = 0; head.skippedBytes && hops < 8; hops++) {
20201
+ head = await backend.readFromOffset(ref.prefixedId, head.newOffset, SYNC_CHUNK_BYTES);
20202
+ }
20203
+ headOffsetEnd = head.newOffset;
20204
+ const messages = parseTailMessages(ref.toolId, head.text);
20049
20205
  if (messages.length > 0) transcript = { messages, subagents: [] };
20050
20206
  } catch {
20051
20207
  }
20052
20208
  includeRaw = false;
20053
- includeMeta = false;
20209
+ oversizedFindings = await scanTranscriptFindingsChunked(ref, opts.tenantRules);
20054
20210
  } else {
20055
20211
  try {
20056
20212
  const backend = getBackendForId(ref.prefixedId) ?? getBackend("claude");
@@ -20111,7 +20267,6 @@ async function buildConversationSync(ref, mtime, opts = {}) {
20111
20267
  );
20112
20268
  let projectPath = ref.projectPath;
20113
20269
  const meta = { messageCount: textMessages.length };
20114
- if (oversized) meta.truncated = true;
20115
20270
  if (textMessages.filter((m) => m.role === "user").length <= 1) meta.oneShot = true;
20116
20271
  try {
20117
20272
  const cwd = ref.fullPath ? readCwdFromJsonl(ref.fullPath) : "";
@@ -20225,21 +20380,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
20225
20380
  }
20226
20381
  scanMs = performance.now() - t0;
20227
20382
  }
20228
- if (oversized && tailText) {
20229
- const t0 = performance.now();
20230
- try {
20231
- for (const f of scanTextForFindings(tailText)) {
20232
- findings.push({ session_id: ref.prefixedId, detector: "builtin", rule: f.rule, line: f.line, preview: f.preview });
20233
- }
20234
- if (opts.tenantRules && opts.tenantRules.length > 0) {
20235
- for (const f of scanTenantRules(tailText, opts.tenantRules)) {
20236
- findings.push({ session_id: ref.prefixedId, detector: f.detector, rule: f.rule, line: f.line, preview: f.preview });
20237
- }
20238
- }
20239
- } catch {
20240
- }
20241
- scanMs = performance.now() - t0;
20242
- }
20383
+ if (oversizedFindings) findings.push(...oversizedFindings);
20243
20384
  const keptFindings = dropFuzzyFindings(findings, (f) => ({ detector: f.detector, rule: f.rule }));
20244
20385
  findings.length = 0;
20245
20386
  findings.push(...keptFindings);
@@ -20285,7 +20426,10 @@ async function buildConversationSync(ref, mtime, opts = {}) {
20285
20426
  // (convBatch onFlush uses this, not a recomputed size) so the next
20286
20427
  // append's base_offset matches exactly — no spurious continuity misses on
20287
20428
  // an actively-growing session. undefined for non-append-only backends.
20288
- from_offset: oversized ? tailOffsetEnd : getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId),
20429
+ from_offset: oversized ? headOffsetEnd : getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId),
20430
+ // The head of a chunked session. The server reports back when its shrink
20431
+ // guard keeps a fuller stored copy, so the client appends from there.
20432
+ ...oversized ? { chunked: true } : {},
20289
20433
  meta,
20290
20434
  mtime
20291
20435
  },
@@ -20307,12 +20451,30 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
20307
20451
  const backend = getBackendForId(ref.prefixedId);
20308
20452
  if (!backend?.readFromOffset) return null;
20309
20453
  const mapPath = opts.mapPath ?? ((p) => p);
20310
- const { text: tailText, newOffset } = await backend.readFromOffset(ref.prefixedId, fromOffset);
20311
- if (!tailText.trim()) return null;
20312
- const messages = parseTailMessages(ref.toolId, tailText);
20313
- if (messages.length === 0) return null;
20454
+ const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
20455
+ if (newOffset <= fromOffset) return null;
20456
+ const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
20314
20457
  const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
20315
- if (textMessages.length === 0 && !messages.some((m) => m.toolCalls?.length)) return null;
20458
+ const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
20459
+ if (!hasContent) {
20460
+ return {
20461
+ conv: {
20462
+ session_id: ref.prefixedId,
20463
+ tool: ref.toolId,
20464
+ project_path: mapPath(ref.projectPath),
20465
+ append: true,
20466
+ base_offset: fromOffset,
20467
+ from_offset: newOffset,
20468
+ redacted_text: "",
20469
+ envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
20470
+ mtime: Math.floor(ref.mtime) || 0
20471
+ },
20472
+ redactions: 0,
20473
+ newOffset,
20474
+ findings: [],
20475
+ bytes: 0
20476
+ };
20477
+ }
20316
20478
  const firstUser = textMessages.find((m) => m.role === "user");
20317
20479
  if (isInternalToolPrompt(firstUser?.content)) return null;
20318
20480
  const count = { redactions: 0 };
@@ -20355,7 +20517,8 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
20355
20517
  },
20356
20518
  redactions: count.redactions,
20357
20519
  newOffset,
20358
- findings
20520
+ findings,
20521
+ bytes: Buffer.byteLength(tailText)
20359
20522
  };
20360
20523
  }
20361
20524
  function isSyncSkip(r) {
@@ -20385,7 +20548,7 @@ async function syncIncremental(opts = {}) {
20385
20548
  lock.release();
20386
20549
  }
20387
20550
  }
20388
- var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
20551
+ var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, DERIVED_MAX_BYTES, SYNC_CHUNK_BYTES, BATCH_SCAN_MAX_BYTES;
20389
20552
  var init_sync_client = __esm({
20390
20553
  "src/sync-client.ts"() {
20391
20554
  "use strict";
@@ -20430,7 +20593,7 @@ var init_sync_client = __esm({
20430
20593
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
20431
20594
  } catch {
20432
20595
  try {
20433
- if (true) ownVersion = "0.7.0";
20596
+ if (true) ownVersion = "0.7.1";
20434
20597
  } catch {
20435
20598
  }
20436
20599
  }
@@ -20456,8 +20619,9 @@ var init_sync_client = __esm({
20456
20619
  capsOk = /* @__PURE__ */ new Map();
20457
20620
  advertisedIngestConc = /* @__PURE__ */ new Map();
20458
20621
  advertisedGzipBody = /* @__PURE__ */ new Set();
20459
- FULL_BUILD_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_FULL_BUILD_MAX_MB || "64", 10)) * 1024 * 1024;
20460
- FULL_BUILD_TAIL_BYTES = 16 * 1024 * 1024;
20622
+ FULL_BUILD_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_FULL_BUILD_MAX_MB || "24", 10)) * 1024 * 1024;
20623
+ DERIVED_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_DERIVED_MAX_MB || "64", 10)) * 1024 * 1024;
20624
+ SYNC_CHUNK_BYTES = 8 * 1024 * 1024;
20461
20625
  BATCH_SCAN_MAX_BYTES = Math.max(1, parseInt(process.env.CHAT_RECALL_BATCHSCAN_MAX_MB || "64", 10)) * 1024 * 1024;
20462
20626
  }
20463
20627
  });
@@ -20606,7 +20770,7 @@ var init_artifact_codec = __esm({
20606
20770
  });
20607
20771
 
20608
20772
  // ../engine/dist/core/toolkit-sync.js
20609
- import { existsSync as existsSync41, mkdirSync as mkdirSync11, readFileSync as readFileSync37, writeFileSync as writeFileSync10, cpSync } from "fs";
20773
+ import { existsSync as existsSync42, mkdirSync as mkdirSync11, readFileSync as readFileSync37, writeFileSync as writeFileSync10, cpSync } from "fs";
20610
20774
  import { dirname as dirname20, join as join44, basename as basename22 } from "path";
20611
20775
  import { homedir as homedir17 } from "os";
20612
20776
  function rowFromMemoryItem(type, item) {
@@ -20640,11 +20804,11 @@ function skillsDirFor(tool) {
20640
20804
  }
20641
20805
  function copySkill(row, toTool) {
20642
20806
  const skillDir = row.extra.skillDir || dirname20(row.filePath);
20643
- if (!existsSync41(skillDir))
20807
+ if (!existsSync42(skillDir))
20644
20808
  return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
20645
20809
  const name = row.extra.skillName || basename22(skillDir);
20646
20810
  const targetDir = join44(skillsDirFor(toTool), name);
20647
- if (existsSync41(targetDir))
20811
+ if (existsSync42(targetDir))
20648
20812
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
20649
20813
  try {
20650
20814
  mkdirSync11(dirname20(targetDir), { recursive: true });
@@ -20676,7 +20840,7 @@ function readMcpEntry(tool, name) {
20676
20840
  return entry;
20677
20841
  }
20678
20842
  for (const t of tries) {
20679
- if (!existsSync41(t.path))
20843
+ if (!existsSync42(t.path))
20680
20844
  continue;
20681
20845
  try {
20682
20846
  const cfg = JSON.parse(readFileSync37(t.path, "utf-8"));
@@ -20689,7 +20853,7 @@ function readMcpEntry(tool, name) {
20689
20853
  return null;
20690
20854
  }
20691
20855
  function readCodexMcpEntry(path, target) {
20692
- if (!existsSync41(path))
20856
+ if (!existsSync42(path))
20693
20857
  return null;
20694
20858
  let content;
20695
20859
  try {
@@ -20743,7 +20907,7 @@ function readCodexMcpEntry(path, target) {
20743
20907
  }
20744
20908
  function writeCodexMcpEntry(path, name, entry) {
20745
20909
  let content = "";
20746
- if (existsSync41(path))
20910
+ if (existsSync42(path))
20747
20911
  content = readFileSync37(path, "utf-8");
20748
20912
  const header = `[mcp_servers.${name}]`;
20749
20913
  if (content.includes(header))
@@ -20801,7 +20965,7 @@ function writeMcpEntry(toTool, name, entry) {
20801
20965
  key2 = "mcp";
20802
20966
  }
20803
20967
  let cfg = {};
20804
- if (existsSync41(path)) {
20968
+ if (existsSync42(path)) {
20805
20969
  try {
20806
20970
  cfg = JSON.parse(readFileSync37(path, "utf-8"));
20807
20971
  } catch {
@@ -20865,7 +21029,7 @@ function copyCommandOrAgent(type, row, toTool) {
20865
21029
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
20866
21030
  }
20867
21031
  const out = emit(type, art, toTool);
20868
- if (existsSync41(out.path))
21032
+ if (existsSync42(out.path))
20869
21033
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
20870
21034
  try {
20871
21035
  mkdirSync11(dirname20(out.path), { recursive: true });
@@ -20885,7 +21049,7 @@ function copyInstructions(row, toTool) {
20885
21049
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
20886
21050
  }
20887
21051
  const out = emit("instructions", art, toTool, row.projectPath || void 0);
20888
- if (existsSync41(out.path))
21052
+ if (existsSync42(out.path))
20889
21053
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
20890
21054
  try {
20891
21055
  mkdirSync11(dirname20(out.path), { recursive: true });
@@ -21028,7 +21192,7 @@ var init_toolkit_sync = __esm({
21028
21192
  });
21029
21193
 
21030
21194
  // ../engine/dist/core/which.js
21031
- import { existsSync as existsSync43, statSync as statSync39, accessSync, constants } from "node:fs";
21195
+ import { existsSync as existsSync44, statSync as statSync39, accessSync, constants } from "node:fs";
21032
21196
  import { join as join46, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
21033
21197
  function executable(p) {
21034
21198
  try {
@@ -21069,7 +21233,7 @@ function resolveOnPath(name) {
21069
21233
  continue;
21070
21234
  for (const c of candidates(name)) {
21071
21235
  const full = join46(dir, c);
21072
- if (existsSync43(full) && executable(full))
21236
+ if (existsSync44(full) && executable(full))
21073
21237
  return full;
21074
21238
  }
21075
21239
  }
@@ -21094,7 +21258,7 @@ __export(toolkit_pull_exports, {
21094
21258
  planPull: () => planPull,
21095
21259
  portableCommand: () => portableCommand
21096
21260
  });
21097
- import { existsSync as existsSync44, mkdirSync as mkdirSync13, statSync as statSync40, writeFileSync as writeFileSync12 } from "node:fs";
21261
+ import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync40, writeFileSync as writeFileSync12 } from "node:fs";
21098
21262
  import { dirname as dirname22, join as join47, basename as basename23, isAbsolute as isAbsolute3 } from "node:path";
21099
21263
  function isExecutableFile(p) {
21100
21264
  try {
@@ -21247,7 +21411,7 @@ function executePull(rows, opts = {}) {
21247
21411
  for (const tool of tools) {
21248
21412
  const dir = join47(skillsDirFor(tool), name);
21249
21413
  const file = join47(dir, "SKILL.md");
21250
- if (existsSync44(file)) {
21414
+ if (existsSync45(file)) {
21251
21415
  outcomes.push({ type: "skill", name, tool, status: "present" });
21252
21416
  continue;
21253
21417
  }
@@ -21292,7 +21456,7 @@ function executePull(rows, opts = {}) {
21292
21456
  outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
21293
21457
  continue;
21294
21458
  }
21295
- if (existsSync44(out.path)) {
21459
+ if (existsSync45(out.path)) {
21296
21460
  outcomes.push({ type, name, tool, status: "present" });
21297
21461
  continue;
21298
21462
  }
@@ -21468,6 +21632,9 @@ async function repairSession(id, opts = {}) {
21468
21632
  note: "archive count is a lower bound; the rebuild also unions the on-disk tail"
21469
21633
  };
21470
21634
  }
21635
+ if (!opts.force && endpoints.every((ep) => (currentOnServer.get(ep.url) ?? 0) >= fullest.messages)) {
21636
+ return { sessionId: id, status: "already-full", fullestMessages: fullest.messages, fullestSource: fullest.source, pushed: [] };
21637
+ }
21471
21638
  seedShadow(rawId, fullest.container);
21472
21639
  const located = backend.findSession(rawId);
21473
21640
  const projectPath = located?.projectPath || cwdFromContainer(fullest.container);
@@ -21510,7 +21677,7 @@ async function repairSession(id, opts = {}) {
21510
21677
  try {
21511
21678
  const res = await fetchWithTimeout(`${ep.url}/api/sync`, { method: "POST", headers, body: JSON.stringify({ conversations: [built.conv] }) }, 6e4);
21512
21679
  if (!res.ok) {
21513
- pushed.push({ server: ep.url, before, after: before });
21680
+ pushed.push({ server: ep.url, before, after: before, error: `HTTP ${res.status}` });
21514
21681
  log6(`push to ${ep.url} FAILED: HTTP ${res.status}`);
21515
21682
  continue;
21516
21683
  }
@@ -21519,10 +21686,21 @@ async function repairSession(id, opts = {}) {
21519
21686
  pushed.push({ server: ep.url, before, after: afterN });
21520
21687
  log6(`pushed to ${ep.url}: ${before} \u2192 ${afterN} msg(s)`);
21521
21688
  } catch (e) {
21522
- pushed.push({ server: ep.url, before, after: before });
21523
- log6(`push to ${ep.url} errored: ${e instanceof Error ? e.message : e}`);
21689
+ const error = e instanceof Error ? e.message : String(e);
21690
+ pushed.push({ server: ep.url, before, after: before, error });
21691
+ log6(`push to ${ep.url} errored: ${error}`);
21524
21692
  }
21525
21693
  }
21694
+ if (pushed.every((p) => p.error)) {
21695
+ return {
21696
+ sessionId: id,
21697
+ status: "error",
21698
+ fullestMessages: builtCount,
21699
+ fullestSource: fullest.source,
21700
+ pushed,
21701
+ note: `every push failed: ${pushed.map((p) => `${p.server}: ${p.error}`).join("; ")}`
21702
+ };
21703
+ }
21526
21704
  return { sessionId: id, status: "repaired", fullestMessages: builtCount, fullestSource: fullest.source, pushed };
21527
21705
  }
21528
21706
  async function repairSessions(ids, opts = {}) {
@@ -21732,7 +21910,7 @@ var init_code_intel = __esm({
21732
21910
  });
21733
21911
 
21734
21912
  // ../engine/dist/core/companions.js
21735
- import { existsSync as existsSync47, mkdirSync as mkdirSync16, statSync as statSync43, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "node:fs";
21913
+ import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync43, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "node:fs";
21736
21914
  import { homedir as homedir19 } from "node:os";
21737
21915
  import { join as join51 } from "node:path";
21738
21916
  import { execSync as execSync3 } from "node:child_process";
@@ -21855,7 +22033,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
21855
22033
  renameSync5(tmpPath, CODEINDEX_BIN_PATH);
21856
22034
  } catch (err) {
21857
22035
  try {
21858
- if (existsSync47(tmpPath))
22036
+ if (existsSync48(tmpPath))
21859
22037
  unlinkSync4(tmpPath);
21860
22038
  } catch {
21861
22039
  }
@@ -22939,7 +23117,7 @@ __export(service_installer_exports, {
22939
23117
  uninstallService: () => uninstallService
22940
23118
  });
22941
23119
  import { execSync as execSync5 } from "child_process";
22942
- import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync44, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "fs";
23120
+ import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync44, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "fs";
22943
23121
  import { homedir as homedir20 } from "os";
22944
23122
  import { join as join53 } from "path";
22945
23123
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -22966,7 +23144,7 @@ function isServiceRunning() {
22966
23144
  return /com\.chat-recall\.watch/.test(tryCmd("launchctl list 2>/dev/null"));
22967
23145
  }
22968
23146
  if (process.platform === "linux") {
22969
- return /active/.test(tryCmd("systemctl --user is-active chat-recall-watch.service 2>/dev/null"));
23147
+ return /^active$/m.test(tryCmd("systemctl --user is-active chat-recall-watch.service 2>/dev/null"));
22970
23148
  }
22971
23149
  return false;
22972
23150
  }
@@ -22975,7 +23153,7 @@ function refreshServiceDefinition() {
22975
23153
  try {
22976
23154
  const { watchJs, node, logFile } = resolveDaemonPaths();
22977
23155
  const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
22978
- if (!existsSync48(unitPath)) return false;
23156
+ if (!existsSync49(unitPath)) return false;
22979
23157
  const current = readFileSync44(unitPath, "utf8");
22980
23158
  if (!current.includes(watchJs)) return false;
22981
23159
  const wanted = renderSystemdUnit(watchJs, node, logFile);
@@ -23042,7 +23220,12 @@ function renderSystemdUnit(watchJs, node, logFile) {
23042
23220
  // sawtooth. 1536MB keeps GC honest (768 OOMed on the largest subagent-fanout session) with plenty of headroom for the
23043
23221
  // largest transcripts.
23044
23222
  `ExecStart=${node} --max-old-space-size=1536 ${watchJs}`,
23045
- "Restart=on-failure",
23223
+ // ALWAYS: the collector never ends its own work, so any exit systemd did
23224
+ // not ask for is a fault. Under on-failure a clean exit left it dead: on
23225
+ // 2026-09-16 the unit went "inactive (dead) … status=0/SUCCESS" and stayed
23226
+ // down for 8 days, so nothing synced and the auto-update tick, which runs
23227
+ // inside the daemon, never ran. `systemctl stop` still stops it.
23228
+ "Restart=always",
23046
23229
  "RestartSec=10",
23047
23230
  "Nice=10",
23048
23231
  // NEVER STOP RETRYING. systemd's default start limiter gives up after a
@@ -23138,7 +23321,7 @@ function uninstallService() {
23138
23321
  } catch {
23139
23322
  }
23140
23323
  const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
23141
- if (existsSync48(unitPath)) {
23324
+ if (existsSync49(unitPath)) {
23142
23325
  rmSync7(unitPath, { force: true });
23143
23326
  removed = true;
23144
23327
  }
@@ -23155,7 +23338,7 @@ function uninstallService() {
23155
23338
  } catch {
23156
23339
  }
23157
23340
  let removed = false;
23158
- if (existsSync48(plistPath)) {
23341
+ if (existsSync49(plistPath)) {
23159
23342
  rmSync7(plistPath, { force: true });
23160
23343
  removed = true;
23161
23344
  }
@@ -23212,12 +23395,12 @@ init_codex2();
23212
23395
  init_cursor();
23213
23396
  init_opencode();
23214
23397
  init_agy();
23215
- import { existsSync as existsSync45, readFileSync as readFileSync39, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
23398
+ import { existsSync as existsSync46, readFileSync as readFileSync39, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
23216
23399
  import { join as join48 } from "node:path";
23217
23400
 
23218
23401
  // src/project-tasks.ts
23219
23402
  init_http();
23220
- import { existsSync as existsSync42, readFileSync as readFileSync38, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync38 } from "node:fs";
23403
+ import { existsSync as existsSync43, readFileSync as readFileSync38, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync38 } from "node:fs";
23221
23404
  import { join as join45, dirname as dirname21 } from "node:path";
23222
23405
  import { homedir as homedir18 } from "node:os";
23223
23406
 
@@ -23325,7 +23508,7 @@ async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
23325
23508
  }
23326
23509
  for (const project of projects) {
23327
23510
  const file = join45(project, fk.filename);
23328
- if (!existsSync42(file)) continue;
23511
+ if (!existsSync43(file)) continue;
23329
23512
  let mtime;
23330
23513
  try {
23331
23514
  mtime = statSync38(file).mtimeMs;
@@ -23386,7 +23569,7 @@ function instructionTargets(rootPath, isGlobal) {
23386
23569
  const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
23387
23570
  if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
23388
23571
  if (!rootPath) return [];
23389
- const present = INSTRUCTION_FILENAMES.map((f) => join48(rootPath, f)).filter((p) => existsSync45(p));
23572
+ const present = INSTRUCTION_FILENAMES.map((f) => join48(rootPath, f)).filter((p) => existsSync46(p));
23390
23573
  if (present.length) return present;
23391
23574
  return [...new Set(tools.map((t) => join48(rootPath, instructionsFilename(t.id))))];
23392
23575
  }
@@ -23414,7 +23597,7 @@ function applyCodeRecommendation(intent) {
23414
23597
  mkdirSync14(dir, { recursive: true });
23415
23598
  } catch {
23416
23599
  }
23417
- const existing = existsSync45(file) ? readFileSync39(file, "utf8") : "";
23600
+ const existing = existsSync46(file) ? readFileSync39(file, "utf8") : "";
23418
23601
  if (existing.includes(text)) {
23419
23602
  skipped.push(file);
23420
23603
  continue;
@@ -23516,7 +23699,7 @@ try {
23516
23699
  ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
23517
23700
  } catch {
23518
23701
  try {
23519
- if (true) ownVersion2 = "0.7.0";
23702
+ if (true) ownVersion2 = "0.7.1";
23520
23703
  } catch {
23521
23704
  }
23522
23705
  }
@@ -23820,8 +24003,8 @@ function rotateLogIfLarge(logPath, maxBytes = LOG_MAX_BYTES) {
23820
24003
 
23821
24004
  // src/collector-migrate.ts
23822
24005
  init_http();
23823
- import { existsSync as existsSync46 } from "node:fs";
23824
- function selectProjectsToReindex(projects, current, existsFn = existsSync46) {
24006
+ import { existsSync as existsSync47 } from "node:fs";
24007
+ function selectProjectsToReindex(projects, current, existsFn = existsSync47) {
23825
24008
  const reindex = [];
23826
24009
  const missing = [];
23827
24010
  for (const p of projects) {
@@ -23863,8 +24046,8 @@ async function runCollectorMigration(opts) {
23863
24046
  // auto-indexer/indexer.ts
23864
24047
  init_sync_ledger();
23865
24048
  init_collector_health();
23866
- import { existsSync as existsSync49, readFileSync as readFileSync45, statSync as statSync44 } from "fs";
23867
- var CLI_VERSION3 = true ? "0.7.0" : "0.0.0";
24049
+ import { existsSync as existsSync50, readFileSync as readFileSync45, statSync as statSync44 } from "fs";
24050
+ var CLI_VERSION3 = true ? "0.7.1" : "0.0.0";
23868
24051
  var DEBOUNCE_MS = 5e3;
23869
24052
  var CLAUDE_DIR = claudeBackend.projectsDir();
23870
24053
  var PLANS_DIR = claudeBackend.plansDir();
@@ -24269,7 +24452,7 @@ function discoverWorkspaces() {
24269
24452
  const isExcluded = (p) => excluded.some((x) => p.includes(x));
24270
24453
  const candidates2 = [...newest.entries()].filter(([p]) => !isExcluded(p)).filter(([p]) => !isPersonalPath(p, includeProjects)).filter(([p]) => {
24271
24454
  try {
24272
- return existsSync49(p);
24455
+ return existsSync50(p);
24273
24456
  } catch {
24274
24457
  return false;
24275
24458
  }
@@ -24301,7 +24484,7 @@ function isCodeWorkspace(p) {
24301
24484
  }
24302
24485
  return WORKSPACE_MARKERS.some((m) => {
24303
24486
  try {
24304
- return existsSync49(join54(p, m));
24487
+ return existsSync50(join54(p, m));
24305
24488
  } catch {
24306
24489
  return false;
24307
24490
  }