chat-recall 0.7.16 → 0.7.18

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
@@ -2233,7 +2233,7 @@ var require_thread_stream = __commonJS({
2233
2233
  var { version } = require_package();
2234
2234
  var { EventEmitter } = __require("events");
2235
2235
  var { Worker: Worker2 } = __require("worker_threads");
2236
- var { join: join55 } = __require("path");
2236
+ var { join: join56 } = __require("path");
2237
2237
  var { pathToFileURL } = __require("url");
2238
2238
  var { wait } = require_wait();
2239
2239
  var {
@@ -2276,7 +2276,7 @@ var require_thread_stream = __commonJS({
2276
2276
  function createWorker(stream, opts) {
2277
2277
  const { filename, workerData } = opts;
2278
2278
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
2279
- const toExecute = bundlerOverrides["thread-stream-worker"] || join55(__dirname, "lib", "worker.js");
2279
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join56(__dirname, "lib", "worker.js");
2280
2280
  const worker = new Worker2(toExecute, {
2281
2281
  ...opts.workerOpts,
2282
2282
  trackUnmanagedFds: false,
@@ -2679,7 +2679,7 @@ var require_transport = __commonJS({
2679
2679
  "use strict";
2680
2680
  var { createRequire: createRequire3 } = __require("module");
2681
2681
  var getCallers = require_caller();
2682
- var { join: join55, isAbsolute: isAbsolute4, sep: sep5 } = __require("node:path");
2682
+ var { join: join56, isAbsolute: isAbsolute4, sep: sep5 } = __require("node:path");
2683
2683
  var sleep = require_atomic_sleep();
2684
2684
  var onExit = require_on_exit_leak_free();
2685
2685
  var ThreadStream = require_thread_stream();
@@ -2742,7 +2742,7 @@ var require_transport = __commonJS({
2742
2742
  throw new Error("only one of target or targets can be specified");
2743
2743
  }
2744
2744
  if (targets) {
2745
- target = bundlerOverrides["pino-worker"] || join55(__dirname, "worker.js");
2745
+ target = bundlerOverrides["pino-worker"] || join56(__dirname, "worker.js");
2746
2746
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
2747
2747
  return {
2748
2748
  ...dest,
@@ -2760,7 +2760,7 @@ var require_transport = __commonJS({
2760
2760
  });
2761
2761
  });
2762
2762
  } else if (pipeline) {
2763
- target = bundlerOverrides["pino-worker"] || join55(__dirname, "worker.js");
2763
+ target = bundlerOverrides["pino-worker"] || join56(__dirname, "worker.js");
2764
2764
  options.pipelines = [pipeline.map((dest) => {
2765
2765
  return {
2766
2766
  ...dest,
@@ -2782,7 +2782,7 @@ var require_transport = __commonJS({
2782
2782
  return origin;
2783
2783
  }
2784
2784
  if (origin === "pino/file") {
2785
- return join55(__dirname, "..", "file.js");
2785
+ return join56(__dirname, "..", "file.js");
2786
2786
  }
2787
2787
  let fixTarget2;
2788
2788
  for (const filePath of callers) {
@@ -3771,7 +3771,7 @@ var require_safe_stable_stringify = __commonJS({
3771
3771
  return circularValue;
3772
3772
  }
3773
3773
  let res = "";
3774
- let join55 = ",";
3774
+ let join56 = ",";
3775
3775
  const originalIndentation = indentation;
3776
3776
  if (Array.isArray(value)) {
3777
3777
  if (value.length === 0) {
@@ -3785,7 +3785,7 @@ var require_safe_stable_stringify = __commonJS({
3785
3785
  indentation += spacer;
3786
3786
  res += `
3787
3787
  ${indentation}`;
3788
- join55 = `,
3788
+ join56 = `,
3789
3789
  ${indentation}`;
3790
3790
  }
3791
3791
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3793,13 +3793,13 @@ ${indentation}`;
3793
3793
  for (; i < maximumValuesToStringify - 1; i++) {
3794
3794
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3795
3795
  res += tmp2 !== void 0 ? tmp2 : "null";
3796
- res += join55;
3796
+ res += join56;
3797
3797
  }
3798
3798
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3799
3799
  res += tmp !== void 0 ? tmp : "null";
3800
3800
  if (value.length - 1 > maximumBreadth) {
3801
3801
  const removedKeys = value.length - maximumBreadth - 1;
3802
- res += `${join55}"... ${getItemCount(removedKeys)} not stringified"`;
3802
+ res += `${join56}"... ${getItemCount(removedKeys)} not stringified"`;
3803
3803
  }
3804
3804
  if (spacer !== "") {
3805
3805
  res += `
@@ -3820,7 +3820,7 @@ ${originalIndentation}`;
3820
3820
  let separator = "";
3821
3821
  if (spacer !== "") {
3822
3822
  indentation += spacer;
3823
- join55 = `,
3823
+ join56 = `,
3824
3824
  ${indentation}`;
3825
3825
  whitespace = " ";
3826
3826
  }
@@ -3834,13 +3834,13 @@ ${indentation}`;
3834
3834
  const tmp = stringifyFnReplacer(key3, value, stack, replacer, spacer, indentation);
3835
3835
  if (tmp !== void 0) {
3836
3836
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3837
- separator = join55;
3837
+ separator = join56;
3838
3838
  }
3839
3839
  }
3840
3840
  if (keyLength > maximumBreadth) {
3841
3841
  const removedKeys = keyLength - maximumBreadth;
3842
3842
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
3843
- separator = join55;
3843
+ separator = join56;
3844
3844
  }
3845
3845
  if (spacer !== "" && separator.length > 1) {
3846
3846
  res = `
@@ -3881,7 +3881,7 @@ ${originalIndentation}`;
3881
3881
  }
3882
3882
  const originalIndentation = indentation;
3883
3883
  let res = "";
3884
- let join55 = ",";
3884
+ let join56 = ",";
3885
3885
  if (Array.isArray(value)) {
3886
3886
  if (value.length === 0) {
3887
3887
  return "[]";
@@ -3894,7 +3894,7 @@ ${originalIndentation}`;
3894
3894
  indentation += spacer;
3895
3895
  res += `
3896
3896
  ${indentation}`;
3897
- join55 = `,
3897
+ join56 = `,
3898
3898
  ${indentation}`;
3899
3899
  }
3900
3900
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3902,13 +3902,13 @@ ${indentation}`;
3902
3902
  for (; i < maximumValuesToStringify - 1; i++) {
3903
3903
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3904
3904
  res += tmp2 !== void 0 ? tmp2 : "null";
3905
- res += join55;
3905
+ res += join56;
3906
3906
  }
3907
3907
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3908
3908
  res += tmp !== void 0 ? tmp : "null";
3909
3909
  if (value.length - 1 > maximumBreadth) {
3910
3910
  const removedKeys = value.length - maximumBreadth - 1;
3911
- res += `${join55}"... ${getItemCount(removedKeys)} not stringified"`;
3911
+ res += `${join56}"... ${getItemCount(removedKeys)} not stringified"`;
3912
3912
  }
3913
3913
  if (spacer !== "") {
3914
3914
  res += `
@@ -3921,7 +3921,7 @@ ${originalIndentation}`;
3921
3921
  let whitespace = "";
3922
3922
  if (spacer !== "") {
3923
3923
  indentation += spacer;
3924
- join55 = `,
3924
+ join56 = `,
3925
3925
  ${indentation}`;
3926
3926
  whitespace = " ";
3927
3927
  }
@@ -3930,7 +3930,7 @@ ${indentation}`;
3930
3930
  const tmp = stringifyArrayReplacer(key3, value[key3], stack, replacer, spacer, indentation);
3931
3931
  if (tmp !== void 0) {
3932
3932
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3933
- separator = join55;
3933
+ separator = join56;
3934
3934
  }
3935
3935
  }
3936
3936
  if (spacer !== "" && separator.length > 1) {
@@ -3988,20 +3988,20 @@ ${originalIndentation}`;
3988
3988
  indentation += spacer;
3989
3989
  let res2 = `
3990
3990
  ${indentation}`;
3991
- const join56 = `,
3991
+ const join57 = `,
3992
3992
  ${indentation}`;
3993
3993
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
3994
3994
  let i = 0;
3995
3995
  for (; i < maximumValuesToStringify - 1; i++) {
3996
3996
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3997
3997
  res2 += tmp2 !== void 0 ? tmp2 : "null";
3998
- res2 += join56;
3998
+ res2 += join57;
3999
3999
  }
4000
4000
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
4001
4001
  res2 += tmp !== void 0 ? tmp : "null";
4002
4002
  if (value.length - 1 > maximumBreadth) {
4003
4003
  const removedKeys = value.length - maximumBreadth - 1;
4004
- res2 += `${join56}"... ${getItemCount(removedKeys)} not stringified"`;
4004
+ res2 += `${join57}"... ${getItemCount(removedKeys)} not stringified"`;
4005
4005
  }
4006
4006
  res2 += `
4007
4007
  ${originalIndentation}`;
@@ -4017,16 +4017,16 @@ ${originalIndentation}`;
4017
4017
  return '"[Object]"';
4018
4018
  }
4019
4019
  indentation += spacer;
4020
- const join55 = `,
4020
+ const join56 = `,
4021
4021
  ${indentation}`;
4022
4022
  let res = "";
4023
4023
  let separator = "";
4024
4024
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
4025
4025
  if (isTypedArrayWithEntries(value)) {
4026
- res += stringifyTypedArray(value, join55, maximumBreadth);
4026
+ res += stringifyTypedArray(value, join56, maximumBreadth);
4027
4027
  keys = keys.slice(value.length);
4028
4028
  maximumPropertiesToStringify -= value.length;
4029
- separator = join55;
4029
+ separator = join56;
4030
4030
  }
4031
4031
  if (deterministic) {
4032
4032
  keys = sort(keys, comparator);
@@ -4037,13 +4037,13 @@ ${indentation}`;
4037
4037
  const tmp = stringifyIndent(key3, value[key3], stack, spacer, indentation);
4038
4038
  if (tmp !== void 0) {
4039
4039
  res += `${separator}${strEscape(key3)}: ${tmp}`;
4040
- separator = join55;
4040
+ separator = join56;
4041
4041
  }
4042
4042
  }
4043
4043
  if (keyLength > maximumBreadth) {
4044
4044
  const removedKeys = keyLength - maximumBreadth;
4045
4045
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
4046
- separator = join55;
4046
+ separator = join56;
4047
4047
  }
4048
4048
  if (separator !== "") {
4049
4049
  res = `
@@ -6448,6 +6448,7 @@ var init_from_events = __esm({
6448
6448
  // ../engine/dist/transcript/raw.js
6449
6449
  var raw_exports = {};
6450
6450
  __export(raw_exports, {
6451
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
6451
6452
  archiveRawSession: () => archiveRawSession,
6452
6453
  buildRawContainer: () => buildRawContainer,
6453
6454
  containerSrcHash: () => containerSrcHash,
@@ -6747,11 +6748,11 @@ async function archiveRawSession(store, sessionId) {
6747
6748
  return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
6748
6749
  }
6749
6750
  function detectForkPredecessor(sessionPath) {
6750
- const { readFileSync: readFileSync46, readdirSync: readdirSync33 } = requireFs();
6751
- const { dirname: dirname26, join: join55, basename: basename26 } = requirePath();
6751
+ const { readFileSync: readFileSync47, readdirSync: readdirSync33 } = requireFs();
6752
+ const { dirname: dirname26, join: join56, basename: basename26 } = requirePath();
6752
6753
  let text;
6753
6754
  try {
6754
- text = readFileSync46(sessionPath, "utf-8");
6755
+ text = readFileSync47(sessionPath, "utf-8");
6755
6756
  } catch {
6756
6757
  return null;
6757
6758
  }
@@ -6785,7 +6786,7 @@ function detectForkPredecessor(sessionPath) {
6785
6786
  if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
6786
6787
  continue;
6787
6788
  try {
6788
- if (readFileSync46(join55(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6789
+ if (readFileSync47(join56(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6789
6790
  return basename26(f, ".jsonl");
6790
6791
  }
6791
6792
  } catch {
@@ -6799,7 +6800,7 @@ function requireFs() {
6799
6800
  function requirePath() {
6800
6801
  return pathMod;
6801
6802
  }
6802
- var MARKER_QUOTE;
6803
+ var RAW_PARSE_MAX_BYTES, MARKER_QUOTE;
6803
6804
  var init_raw = __esm({
6804
6805
  "../engine/dist/transcript/raw.js"() {
6805
6806
  "use strict";
@@ -6807,6 +6808,7 @@ var init_raw = __esm({
6807
6808
  init_claude();
6808
6809
  init_codex();
6809
6810
  init_from_events();
6811
+ RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
6810
6812
  MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
6811
6813
  }
6812
6814
  });
@@ -6963,18 +6965,20 @@ function updateShadow(sessionId, exp, prebuilt) {
6963
6965
  const curHash = containerSrcHash(current);
6964
6966
  if (!prior) {
6965
6967
  current.srcHash = curHash;
6968
+ current.mergeVersion = SHADOW_MERGE_VERSION;
6966
6969
  try {
6967
6970
  writeShadowContainer(tool, rawId, current);
6968
6971
  } catch {
6969
6972
  }
6970
6973
  return { status: "created", sessionId, tool, container: current, recovered: 0, path };
6971
6974
  }
6972
- if (prior.srcHash && prior.srcHash === curHash) {
6975
+ if (prior.srcHash && prior.srcHash === curHash && prior.mergeVersion === SHADOW_MERGE_VERSION) {
6973
6976
  return { status: "unchanged", sessionId, tool, container: prior, recovered: 0, path };
6974
6977
  }
6975
6978
  const merged = mergeContainer(prior, current);
6976
- if (merged.status !== "unchanged" || prior.srcHash !== curHash) {
6979
+ if (merged.status !== "unchanged" || prior.srcHash !== curHash || prior.mergeVersion !== SHADOW_MERGE_VERSION) {
6977
6980
  merged.container.srcHash = curHash;
6981
+ merged.container.mergeVersion = SHADOW_MERGE_VERSION;
6978
6982
  try {
6979
6983
  writeShadowContainer(tool, rawId, merged.container);
6980
6984
  } catch {
@@ -7067,7 +7071,7 @@ function shadowSizeOnDisk(tool, rawId) {
7067
7071
  return 0;
7068
7072
  }
7069
7073
  }
7070
- var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
7074
+ var WHOLE_FILE_TOOLS, STATE_META, SHADOW_MERGE_VERSION, lastLiveFingerprint;
7071
7075
  var init_shadow = __esm({
7072
7076
  "../engine/dist/transcript/shadow.js"() {
7073
7077
  "use strict";
@@ -7075,6 +7079,7 @@ var init_shadow = __esm({
7075
7079
  init_raw();
7076
7080
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
7077
7081
  STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
7082
+ SHADOW_MERGE_VERSION = 2;
7078
7083
  lastLiveFingerprint = /* @__PURE__ */ new Map();
7079
7084
  }
7080
7085
  });
@@ -13522,6 +13527,7 @@ var init_opencode2 = __esm({
13522
13527
  // ../engine/dist/transcript/index.js
13523
13528
  var transcript_exports = {};
13524
13529
  __export(transcript_exports, {
13530
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
13525
13531
  TRANSCRIPT_VERSION: () => TRANSCRIPT_VERSION,
13526
13532
  archiveRawSession: () => archiveRawSession,
13527
13533
  buildRawContainer: () => buildRawContainer,
@@ -18101,12 +18107,15 @@ var init_extractor_version = __esm({
18101
18107
  // src/sync-ledger.ts
18102
18108
  var sync_ledger_exports = {};
18103
18109
  __export(sync_ledger_exports, {
18110
+ SETTLE_QUIET_MS: () => SETTLE_QUIET_MS,
18104
18111
  _resetLedgerCacheForTests: () => _resetLedgerCacheForTests,
18105
18112
  _resetUnackedCursorCount: () => _resetUnackedCursorCount,
18106
18113
  fieldNeedsFullPass: () => fieldNeedsFullPass,
18107
18114
  fieldNeedsScan: () => fieldNeedsScan,
18108
18115
  flushLedger: () => flushLedger,
18109
18116
  forceFieldRescan: () => forceFieldRescan,
18117
+ forgetFullCoverage: () => forgetFullCoverage,
18118
+ fullCoveredOffset: () => fullCoveredOffset,
18110
18119
  getLedgerData: () => getLedgerData,
18111
18120
  getSyncedMtimes: () => getSyncedMtimes,
18112
18121
  getSyncedRows: () => getSyncedRows,
@@ -18358,6 +18367,11 @@ function markSynced(server, rows) {
18358
18367
  base3.s = s;
18359
18368
  }
18360
18369
  if (r.hash !== void 0) base3.h = r.hash;
18370
+ const completesChunked = cursorFromCaller && r.chunkFinal === true && prev?.k === 1;
18371
+ if (cursorFromCaller && (r.full || completesChunked) && o > 0) base3.F = o;
18372
+ else if (prev?.F !== void 0) base3.F = prev.F;
18373
+ if (cursorFromCaller && r.chunkedHead) base3.k = 1;
18374
+ else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base3.k = 1;
18361
18375
  forServer[r.id] = base3;
18362
18376
  }
18363
18377
  persist(data);
@@ -18431,6 +18445,14 @@ function _resetLedgerCacheForTests() {
18431
18445
  diskStamp = null;
18432
18446
  reconcileCache = null;
18433
18447
  }
18448
+ function forgetFullCoverage(server, sessionId) {
18449
+ const data = load();
18450
+ const row = data[server]?.[sessionId];
18451
+ if (!row || typeof row !== "object") return;
18452
+ delete row.F;
18453
+ delete row.h;
18454
+ persist(data);
18455
+ }
18434
18456
  function markFullResync(server, sessionId) {
18435
18457
  const data = load();
18436
18458
  const forServer = data[server];
@@ -18439,7 +18461,11 @@ function markFullResync(server, sessionId) {
18439
18461
  delete forServer[sessionId];
18440
18462
  persist(data);
18441
18463
  }
18442
- function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
18464
+ function fullCoveredOffset(row) {
18465
+ if (row.F !== void 0) return row.F;
18466
+ return row.h ? row.s ?? 0 : 0;
18467
+ }
18468
+ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly, nowMs = Date.now()) {
18443
18469
  if (!row) return "full";
18444
18470
  if (row.v < extractorVersion) return "full";
18445
18471
  const offset = row.o ?? 0;
@@ -18447,10 +18473,13 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
18447
18473
  if (process.env.CHAT_RECALL_TAIL_APPEND !== "0" && isAppendOnly && fileSize > 0 && offset > 0 && fileSize > (row.s ?? 0)) {
18448
18474
  return "append";
18449
18475
  }
18476
+ if (isAppendOnly && offset > 0 && fileSize > 0 && fileSize === (row.s ?? 0) && !row.k && fullCoveredOffset(row) < offset && nowMs - Math.floor(mtime) >= SETTLE_QUIET_MS) {
18477
+ return "full";
18478
+ }
18450
18479
  if (row.m >= Math.floor(mtime)) return "skip";
18451
18480
  return "full";
18452
18481
  }
18453
- var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
18482
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache, SETTLE_QUIET_MS;
18454
18483
  var init_sync_ledger = __esm({
18455
18484
  "src/sync-ledger.ts"() {
18456
18485
  "use strict";
@@ -18470,6 +18499,7 @@ var init_sync_ledger = __esm({
18470
18499
  unackedCursorAttempts = 0;
18471
18500
  reconcilePath = () => join39(getDataDir(), "sync-field-reconcile.json");
18472
18501
  reconcileCache = null;
18502
+ SETTLE_QUIET_MS = 2 * 60 * 60 * 1e3;
18473
18503
  }
18474
18504
  });
18475
18505
 
@@ -18612,7 +18642,7 @@ var init_http = __esm({
18612
18642
  CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18613
18643
  } catch {
18614
18644
  try {
18615
- if (true) CLI_VERSION = "0.7.16";
18645
+ if (true) CLI_VERSION = "0.7.18";
18616
18646
  } catch {
18617
18647
  }
18618
18648
  }
@@ -19106,7 +19136,7 @@ var init_telemetry = __esm({
19106
19136
  init_sync_client();
19107
19137
  cliVersion = () => {
19108
19138
  try {
19109
- return true ? "0.7.16" : "dev";
19139
+ return true ? "0.7.18" : "dev";
19110
19140
  } catch {
19111
19141
  return "dev";
19112
19142
  }
@@ -19116,6 +19146,150 @@ var init_telemetry = __esm({
19116
19146
  }
19117
19147
  });
19118
19148
 
19149
+ // src/raw-archive-parts.ts
19150
+ import { createReadStream as createReadStream4, createWriteStream, openSync as openSync9, readSync as readSync8, closeSync as closeSync9, rmSync as rmSync5, statSync as statSync37 } from "node:fs";
19151
+ import { tmpdir as tmpdir2 } from "node:os";
19152
+ import { join as join41 } from "node:path";
19153
+ import { randomBytes } from "node:crypto";
19154
+ import { createGzip } from "node:zlib";
19155
+ import { once } from "node:events";
19156
+ import { readFileSync as readFileSync33 } from "node:fs";
19157
+ async function* fileLines(path) {
19158
+ let rest = "";
19159
+ const decoder = new TextDecoder("utf-8");
19160
+ for await (const chunk of createReadStream4(path, { highWaterMark: 1024 * 1024 })) {
19161
+ rest += decoder.decode(chunk, { stream: true });
19162
+ let nl = rest.indexOf("\n");
19163
+ while (nl >= 0) {
19164
+ yield rest.slice(0, nl + 1);
19165
+ rest = rest.slice(nl + 1);
19166
+ nl = rest.indexOf("\n");
19167
+ }
19168
+ }
19169
+ rest += decoder.decode();
19170
+ if (rest) yield rest;
19171
+ }
19172
+ function* textLines(text) {
19173
+ let at = 0;
19174
+ while (at < text.length) {
19175
+ const nl = text.indexOf("\n", at);
19176
+ const end = nl < 0 ? text.length : nl + 1;
19177
+ yield text.slice(at, end);
19178
+ at = end;
19179
+ }
19180
+ }
19181
+ async function writeArchiveFile(source, meta, redact, dir = tmpdir2()) {
19182
+ const path = join41(dir, `cr-archive-${randomBytes(8).toString("hex")}.json.gz`);
19183
+ const gzip = createGzip({ level: 6 });
19184
+ const out = createWriteStream(path, { mode: 384 });
19185
+ const done = once(out, "finish");
19186
+ gzip.pipe(out);
19187
+ let size = 0;
19188
+ const write = async (s) => {
19189
+ size += Buffer.byteLength(s);
19190
+ if (!gzip.write(s)) await once(gzip, "drain");
19191
+ };
19192
+ try {
19193
+ await write(`{"v":1,"tool":${JSON.stringify(meta.tool)},"mtime":${Math.floor(meta.mtime)},"files":[`);
19194
+ for (const [i, f] of source.files.entries()) {
19195
+ await write(`${i ? "," : ""}{"name":${JSON.stringify(f.name)},"text":"`);
19196
+ const whole = () => source.kind === "memory" ? f.text : readFileSync33(f.path, "utf-8");
19197
+ if (!f.name.endsWith(".jsonl")) {
19198
+ await write(escapeJson(redact(whole())));
19199
+ } else {
19200
+ const lines = source.kind === "memory" ? textLines(f.text) : fileLines(f.path);
19201
+ for await (const line of lines) {
19202
+ const nl = line.endsWith("\n");
19203
+ const body = nl ? line.slice(0, -1) : line;
19204
+ await write(escapeJson((body ? redactJsonLine(body, redact) : body) + (nl ? "\n" : "")));
19205
+ }
19206
+ }
19207
+ await write('"}');
19208
+ }
19209
+ await write("]}");
19210
+ gzip.end();
19211
+ await done;
19212
+ } catch (err) {
19213
+ gzip.destroy();
19214
+ out.destroy();
19215
+ rmSync5(path, { force: true });
19216
+ throw err;
19217
+ }
19218
+ return { path, gzSize: statSync37(path).size, size };
19219
+ }
19220
+ async function putPart(url, body) {
19221
+ let last;
19222
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
19223
+ const ac = new AbortController();
19224
+ const timer = setTimeout(() => ac.abort(), PART_TIMEOUT_MS);
19225
+ try {
19226
+ const r = await fetch(url, { method: "PUT", body, signal: ac.signal });
19227
+ const etag = r.headers.get("etag");
19228
+ if (r.ok && etag) return etag;
19229
+ last = new Error(`part PUT answered ${r.status}`);
19230
+ if (r.status === 403 || r.status === 404) break;
19231
+ } catch (err) {
19232
+ last = err;
19233
+ } finally {
19234
+ clearTimeout(timer);
19235
+ }
19236
+ }
19237
+ throw last instanceof Error ? last : new Error(String(last));
19238
+ }
19239
+ async function uploadArchiveFile(base3, token, args) {
19240
+ const headers = { "content-type": "application/json" };
19241
+ if (token) headers.authorization = `Bearer ${token}`;
19242
+ const post = async (path, body) => {
19243
+ const r = await fetch(`${base3}/api/sync/raw-upload/${path}`, { method: "POST", headers, body: JSON.stringify(body) });
19244
+ const json = await r.json().catch(() => ({}));
19245
+ return { status: r.status, json };
19246
+ };
19247
+ const start = await post("start", {
19248
+ session_id: args.sessionId,
19249
+ tool: args.tool,
19250
+ mtime: Math.floor(args.mtime),
19251
+ size: args.file.size,
19252
+ gz_size: args.file.gzSize,
19253
+ project_id: args.projectId,
19254
+ project_path: args.projectPath
19255
+ });
19256
+ if (start.status === 501 || start.status === 404) return "unsupported";
19257
+ if (start.status !== 200) throw new Error(`raw upload start answered ${start.status}: ${start.json.error ?? ""}`);
19258
+ if (start.json.result === "unchanged" || start.json.result === "shrink-protected") return start.json.result;
19259
+ const urls = start.json.urls;
19260
+ const partBytes = Number(start.json.part_bytes);
19261
+ const parts = [];
19262
+ const fd = openSync9(args.file.path, "r");
19263
+ try {
19264
+ for (const [i, url] of urls.entries()) {
19265
+ const len = Math.min(partBytes, args.file.gzSize - i * partBytes);
19266
+ const buf = Buffer.allocUnsafe(len);
19267
+ let got = 0;
19268
+ while (got < len) {
19269
+ const n = readSync8(fd, buf, got, len - got, i * partBytes + got);
19270
+ if (n <= 0) break;
19271
+ got += n;
19272
+ }
19273
+ parts.push({ part_number: i + 1, etag: await putPart(url, buf.subarray(0, got)) });
19274
+ }
19275
+ } finally {
19276
+ closeSync9(fd);
19277
+ }
19278
+ const done = await post("complete", { session_id: args.sessionId, upload_id: start.json.upload_id, parts });
19279
+ if (done.status !== 200) throw new Error(`raw upload complete answered ${done.status}: ${done.json.error ?? ""}`);
19280
+ return done.json.result;
19281
+ }
19282
+ var escapeJson, PART_TIMEOUT_MS, PART_ATTEMPTS;
19283
+ var init_raw_archive_parts = __esm({
19284
+ "src/raw-archive-parts.ts"() {
19285
+ "use strict";
19286
+ init_transcript();
19287
+ escapeJson = (s) => JSON.stringify(s).slice(1, -1);
19288
+ PART_TIMEOUT_MS = 12e4;
19289
+ PART_ATTEMPTS = 3;
19290
+ }
19291
+ });
19292
+
19119
19293
  // src/server-probe.ts
19120
19294
  var server_probe_exports = {};
19121
19295
  __export(server_probe_exports, {
@@ -19259,7 +19433,7 @@ __export(client_events_exports, {
19259
19433
  isTelemetryEnabled: () => isTelemetryEnabled,
19260
19434
  reportClientEvent: () => reportClientEvent
19261
19435
  });
19262
- import { readFileSync as readFileSync33 } from "node:fs";
19436
+ import { readFileSync as readFileSync34 } from "node:fs";
19263
19437
  function scrub(msg) {
19264
19438
  if (!msg) return "";
19265
19439
  const noHome = msg.replace(/\/(?:home|Users)\/[^/\s]+/g, "~");
@@ -19312,7 +19486,7 @@ var init_client_events = __esm({
19312
19486
  init_secret_redactor();
19313
19487
  CLI_VERSION2 = "0.0.0";
19314
19488
  try {
19315
- CLI_VERSION2 = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
19489
+ CLI_VERSION2 = JSON.parse(readFileSync34(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
19316
19490
  } catch {
19317
19491
  }
19318
19492
  }
@@ -19342,11 +19516,11 @@ __export(auto_update_exports, {
19342
19516
  tarballUrl: () => tarballUrl
19343
19517
  });
19344
19518
  import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
19345
- import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
19519
+ import { readFileSync as readFileSync35, existsSync as existsSync41 } from "node:fs";
19346
19520
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19347
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as statSync37, mkdirSync as mkdirSync9 } from "node:fs";
19348
- import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
19349
- import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
19521
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync30, statSync as statSync38, mkdirSync as mkdirSync9 } from "node:fs";
19522
+ import { join as join42, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
19523
+ import { tmpdir as tmpdir3, homedir as homedir15 } from "node:os";
19350
19524
  import { createHash as createHash7 } from "node:crypto";
19351
19525
  function compareVersions(a, b) {
19352
19526
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -19370,7 +19544,7 @@ function newerOf(a, b) {
19370
19544
  function installedVersion() {
19371
19545
  try {
19372
19546
  const here = dirname18(fileURLToPath2(import.meta.url));
19373
- const raw = readFileSync34(join41(here, "..", "package.json"), "utf-8");
19547
+ const raw = readFileSync35(join42(here, "..", "package.json"), "utf-8");
19374
19548
  return JSON.parse(raw).version || null;
19375
19549
  } catch {
19376
19550
  return null;
@@ -19383,7 +19557,7 @@ function planAutoUpdate(base3, caps, ownVersion3, flag) {
19383
19557
  if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
19384
19558
  return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base3, rel.version), fallbackUrl: tarballUrl(base3), sha256: rel.sha256, from: ownVersion3, to: rel.version };
19385
19559
  }
19386
- function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
19560
+ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
19387
19561
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
19388
19562
  const COLD_MS = 60 * 60 * 1e3;
19389
19563
  let removed = 0;
@@ -19395,13 +19569,13 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
19395
19569
  }
19396
19570
  for (const name of entries) {
19397
19571
  if (!MKDTEMP_SUFFIX.test(name)) continue;
19398
- const dir = join41(root, name);
19572
+ const dir = join42(root, name);
19399
19573
  try {
19400
- const st = statSync37(dir);
19574
+ const st = statSync38(dir);
19401
19575
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
19402
19576
  const inside = readdirSync30(dir);
19403
19577
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
19404
- rmSync5(dir, { recursive: true, force: true });
19578
+ rmSync6(dir, { recursive: true, force: true });
19405
19579
  removed++;
19406
19580
  } catch {
19407
19581
  }
@@ -19424,14 +19598,14 @@ async function executeAutoUpdate(plan, deps) {
19424
19598
  }
19425
19599
  const got = createHash7("sha256").update(buf).digest("hex");
19426
19600
  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` };
19427
- const dir = mkdtempSync3(join41(tmpdir2(), "cr-update-"));
19601
+ const dir = mkdtempSync3(join42(tmpdir3(), "cr-update-"));
19428
19602
  const discardStaging = () => {
19429
19603
  try {
19430
- rmSync5(dir, { recursive: true, force: true });
19604
+ rmSync6(dir, { recursive: true, force: true });
19431
19605
  } catch {
19432
19606
  }
19433
19607
  };
19434
- const tgz = join41(dir, "chat-recall.tgz");
19608
+ const tgz = join42(dir, "chat-recall.tgz");
19435
19609
  writeFileSync8(tgz, buf);
19436
19610
  if (deps.stopping?.()) {
19437
19611
  discardStaging();
@@ -19511,7 +19685,7 @@ function realInstall(tgz) {
19511
19685
  try {
19512
19686
  npmInstall(npm, ["install", "-g", tgz]);
19513
19687
  } catch {
19514
- npmInstall(npm, ["install", "-g", "--prefix", join41(homedir15(), ".local"), tgz]);
19688
+ npmInstall(npm, ["install", "-g", "--prefix", join42(homedir15(), ".local"), tgz]);
19515
19689
  }
19516
19690
  }
19517
19691
  function acquireUpdateLock() {
@@ -19528,12 +19702,12 @@ function realRestart(platform2) {
19528
19702
  if (c) execSync2(c.cmd, { stdio: "ignore", ...c.shell ? { shell: c.shell } : {} });
19529
19703
  }
19530
19704
  function updateStatePath() {
19531
- const dir = process.env.CHAT_RECALL_DATA_DIR || join41(homedir15(), ".chat-recall");
19532
- return join41(dir, "auto-update-state.json");
19705
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join42(homedir15(), ".chat-recall");
19706
+ return join42(dir, "auto-update-state.json");
19533
19707
  }
19534
19708
  function readUpdateState() {
19535
19709
  try {
19536
- return JSON.parse(readFileSync34(updateStatePath(), "utf8"));
19710
+ return JSON.parse(readFileSync35(updateStatePath(), "utf8"));
19537
19711
  } catch {
19538
19712
  return {};
19539
19713
  }
@@ -19640,9 +19814,9 @@ var init_auto_update = __esm({
19640
19814
  });
19641
19815
 
19642
19816
  // src/sync-client.ts
19643
- import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
19644
- 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";
19645
- import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
19817
+ import { join as join43, dirname as dirname19, basename as basename20 } from "node:path";
19818
+ 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";
19819
+ import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
19646
19820
  import { createHash as createHash8 } from "node:crypto";
19647
19821
  import { fileURLToPath as fileURLToPath3 } from "node:url";
19648
19822
  import { gzipSync as gzipSync3 } from "zlib";
@@ -19678,7 +19852,7 @@ function loadAllCredentials() {
19678
19852
  const fromEnv = envCredentials();
19679
19853
  if (fromEnv) return [{ serverUrl: fromEnv.serverUrl, token: fromEnv.token }];
19680
19854
  try {
19681
- const parsed = JSON.parse(readFileSync35(credPath(), "utf-8"));
19855
+ const parsed = JSON.parse(readFileSync36(credPath(), "utf-8"));
19682
19856
  if (Array.isArray(parsed?.targets)) return parsed.targets.filter((t) => t?.serverUrl).map((t) => ({ serverUrl: t.serverUrl, token: t.token || "" }));
19683
19857
  if (parsed?.serverUrl) return [{ serverUrl: parsed.serverUrl, token: parsed.token || "" }];
19684
19858
  } catch {
@@ -19737,11 +19911,11 @@ async function fetchTenantSecurityConfig(cred) {
19737
19911
  }
19738
19912
  }
19739
19913
  function syncConfigCachePath() {
19740
- return join42(getDataDir(), "sync-config-cache.json");
19914
+ return join43(getDataDir(), "sync-config-cache.json");
19741
19915
  }
19742
19916
  function readPersistedSyncConfigs() {
19743
19917
  try {
19744
- return JSON.parse(readFileSync35(syncConfigCachePath(), "utf-8"));
19918
+ return JSON.parse(readFileSync36(syncConfigCachePath(), "utf-8"));
19745
19919
  } catch {
19746
19920
  return {};
19747
19921
  }
@@ -19984,7 +20158,7 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
19984
20158
  }
19985
20159
  return { pushed, absent, scanned };
19986
20160
  }
19987
- function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now()) {
20161
+ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now()) {
19988
20162
  let reaped = 0;
19989
20163
  let names;
19990
20164
  try {
@@ -19994,10 +20168,10 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
19994
20168
  }
19995
20169
  for (const name of names) {
19996
20170
  if (!name.startsWith("cr-batchscan-")) continue;
19997
- const p = join42(dir, name);
20171
+ const p = join43(dir, name);
19998
20172
  try {
19999
- if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
20000
- rmSync6(p, { recursive: true, force: true });
20173
+ if (now - statSync39(p).mtimeMs < maxAgeMs) continue;
20174
+ rmSync7(p, { recursive: true, force: true });
20001
20175
  reaped++;
20002
20176
  } catch {
20003
20177
  }
@@ -20011,7 +20185,7 @@ function batchScanExternal(items, opts = {}) {
20011
20185
  let scanned = 0, slices = 0, scanMs = 0;
20012
20186
  if (items.length === 0) return { findings, scanned, slices, scanMs };
20013
20187
  reapStaleScanDirs();
20014
- let scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
20188
+ let scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20015
20189
  let safeToId = /* @__PURE__ */ new Map();
20016
20190
  let bytesInDir = 0;
20017
20191
  const flushSlice = () => {
@@ -20029,7 +20203,7 @@ function batchScanExternal(items, opts = {}) {
20029
20203
  slices++;
20030
20204
  }
20031
20205
  try {
20032
- rmSync6(scanDir, { recursive: true, force: true });
20206
+ rmSync7(scanDir, { recursive: true, force: true });
20033
20207
  } catch {
20034
20208
  }
20035
20209
  safeToId = /* @__PURE__ */ new Map();
@@ -20042,19 +20216,19 @@ function batchScanExternal(items, opts = {}) {
20042
20216
  if (raw === null) continue;
20043
20217
  const safe = item.id.replace(/[^A-Za-z0-9_-]/g, "");
20044
20218
  safeToId.set(safe, item.id);
20045
- writeFileSync9(join42(scanDir, `${safe}.txt`), raw);
20219
+ writeFileSync9(join43(scanDir, `${safe}.txt`), raw);
20046
20220
  bytesInDir += Buffer.byteLength(raw);
20047
20221
  } catch {
20048
20222
  }
20049
20223
  if (bytesInDir >= maxBytes) {
20050
20224
  flushSlice();
20051
- scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
20225
+ scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20052
20226
  }
20053
20227
  }
20054
20228
  flushSlice();
20055
20229
  } finally {
20056
20230
  try {
20057
- rmSync6(scanDir, { recursive: true, force: true });
20231
+ rmSync7(scanDir, { recursive: true, force: true });
20058
20232
  } catch {
20059
20233
  }
20060
20234
  }
@@ -20293,12 +20467,13 @@ async function syncToTargetInScope(cred, opts = {}) {
20293
20467
  markSynced(base3, rows.map((r) => {
20294
20468
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
20295
20469
  const hash = shippedHash.get(r.session_id);
20296
- 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 };
20470
+ return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true, full: true } : { id: r.session_id, mtime: r.mtime, hash };
20297
20471
  }));
20298
20472
  } catch {
20299
20473
  }
20300
20474
  });
20301
20475
  const appendResults = { uploaded: 0, fullResyncNeeded: /* @__PURE__ */ new Set() };
20476
+ const pendingArchives = [];
20302
20477
  const postAppend = async (convs) => {
20303
20478
  const headers = { "content-type": "application/json" };
20304
20479
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -20333,8 +20508,10 @@ async function syncToTargetInScope(cred, opts = {}) {
20333
20508
  offset: c.from_offset,
20334
20509
  size: c.from_offset,
20335
20510
  // size = new offset (file grew to here)
20336
- acked: true
20511
+ acked: true,
20337
20512
  // server merged this tail
20513
+ chunkFinal: c.final === true
20514
+ // completes a chunked full sync
20338
20515
  })));
20339
20516
  } catch {
20340
20517
  }
@@ -20366,9 +20543,9 @@ async function syncToTargetInScope(cred, opts = {}) {
20366
20543
  const mtime = conv.mtime;
20367
20544
  try {
20368
20545
  if (staged || !guarded) {
20369
- markSynced(base3, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
20546
+ markSynced(base3, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true }]);
20370
20547
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
20371
- markSynced(base3, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
20548
+ markSynced(base3, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true }]);
20372
20549
  } else {
20373
20550
  markSynced(base3, [{ id, mtime }]);
20374
20551
  }
@@ -20577,6 +20754,14 @@ async function syncToTargetInScope(cred, opts = {}) {
20577
20754
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
20578
20755
  if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
20579
20756
  else await convBatch.add(built.conv);
20757
+ if (built.archive) {
20758
+ pendingArchives.push({
20759
+ id: ref.prefixedId,
20760
+ ...built.archive,
20761
+ projectId: built.conv.project_id,
20762
+ projectPath: built.conv.project_path
20763
+ });
20764
+ }
20580
20765
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
20581
20766
  if (upload.sessionMeta) {
20582
20767
  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);
@@ -20729,6 +20914,30 @@ async function syncToTargetInScope(cred, opts = {}) {
20729
20914
  await kgTripleBatch.drain();
20730
20915
  await findingsBatch.drain();
20731
20916
  await drainInflight();
20917
+ let archivesUnsupported = false;
20918
+ for (const a of pendingArchives) {
20919
+ try {
20920
+ if (!archivesUnsupported) {
20921
+ const r = await uploadArchiveFile(base3, cred.token, {
20922
+ sessionId: a.id,
20923
+ tool: a.tool,
20924
+ mtime: a.mtime,
20925
+ projectId: a.projectId,
20926
+ projectPath: a.projectPath,
20927
+ file: a.file
20928
+ });
20929
+ if (r === "unsupported") archivesUnsupported = true;
20930
+ }
20931
+ } catch (err) {
20932
+ console.error(`[sync] raw archive for ${a.id} not uploaded (tried again when the session is next quiet): ${err instanceof Error ? err.message : err}`);
20933
+ try {
20934
+ forgetFullCoverage(base3, a.id);
20935
+ } catch {
20936
+ }
20937
+ } finally {
20938
+ rmSync7(a.file.path, { force: true });
20939
+ }
20940
+ }
20732
20941
  {
20733
20942
  const sentHashes = loadInventoryHashes(base3);
20734
20943
  const entries = [];
@@ -20834,6 +21043,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
20834
21043
  let includeRaw = opts.includeRaw !== false;
20835
21044
  let includeMeta = opts.includeMeta !== false;
20836
21045
  let srcHash;
21046
+ const wantRaw = opts.includeRaw !== false;
20837
21047
  const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
20838
21048
  let headOffsetEnd = 0;
20839
21049
  let oversizedFindings = null;
@@ -21002,7 +21212,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21002
21212
  } else if (opts.scanSecrets) {
21003
21213
  let tmp = null;
21004
21214
  try {
21005
- tmp = join42(tmpdir3(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
21215
+ tmp = join43(tmpdir4(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
21006
21216
  writeFileSync9(tmp, rawText);
21007
21217
  for (const f of scanFileForSecrets(tmp, { verifyOnly: opts.verifySecrets, tenantRules: opts.tenantRules })) {
21008
21218
  const verifiedAt = f.verified ? (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -21054,6 +21264,25 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21054
21264
  }
21055
21265
  const envTexts = envelope.messages.filter((m) => m.content?.trim());
21056
21266
  const redactedText = envTexts.map((m) => m.content).join("\n");
21267
+ let archive;
21268
+ if (wantRaw && !raw_b64) {
21269
+ try {
21270
+ const redact = (t) => redactSecrets(t, { force: true, count });
21271
+ let source = null;
21272
+ let archiveMtime = Math.floor(ref.mtime) || 0;
21273
+ let archiveTool = ref.toolId;
21274
+ if (container) {
21275
+ source = { kind: "memory", files: container.files };
21276
+ archiveMtime = container.mtime;
21277
+ archiveTool = container.tool;
21278
+ } else if (oversized) {
21279
+ source = diskArchiveSource(ref);
21280
+ }
21281
+ if (source) archive = { file: await writeArchiveFile(source, { tool: archiveTool, mtime: archiveMtime }, redact), tool: archiveTool, mtime: archiveMtime };
21282
+ } catch (err) {
21283
+ console.error(`[sync] raw archive for ${ref.prefixedId} not written: ${err instanceof Error ? err.message : err}`);
21284
+ }
21285
+ }
21057
21286
  return {
21058
21287
  conv: {
21059
21288
  session_id: ref.prefixedId,
@@ -21085,7 +21314,17 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21085
21314
  redactions: count.redactions,
21086
21315
  findings,
21087
21316
  scanMs,
21088
- srcHash
21317
+ srcHash,
21318
+ archive
21319
+ };
21320
+ }
21321
+ function diskArchiveSource(ref) {
21322
+ if (ref.toolId !== "claude") return null;
21323
+ const groups = resolveSessionContentGroups(ref.rawId);
21324
+ if (groups.length === 0 || groups.some((g) => g.paths.length !== 1)) return null;
21325
+ return {
21326
+ kind: "disk",
21327
+ files: groups.map((g) => ({ name: g.name === "main" ? `${ref.rawId}.jsonl` : g.name, path: g.paths[0] }))
21089
21328
  };
21090
21329
  }
21091
21330
  function parseTailMessages(tool, text) {
@@ -21241,14 +21480,15 @@ var init_sync_client = __esm({
21241
21480
  init_telemetry();
21242
21481
  init_collector_health();
21243
21482
  init_live_session_scan();
21483
+ init_raw_archive_parts();
21244
21484
  init_backends();
21245
21485
  ownVersion = "0.0.0";
21246
21486
  try {
21247
21487
  const dir = dirname19(fileURLToPath3(import.meta.url));
21248
- ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
21488
+ ownVersion = JSON.parse(readFileSync36(join43(dir, "../package.json"), "utf-8")).version || ownVersion;
21249
21489
  } catch {
21250
21490
  try {
21251
- if (true) ownVersion = "0.7.16";
21491
+ if (true) ownVersion = "0.7.18";
21252
21492
  } catch {
21253
21493
  }
21254
21494
  }
@@ -21260,7 +21500,7 @@ var init_sync_client = __esm({
21260
21500
  ]);
21261
21501
  walkProgressForUpload = null;
21262
21502
  lastServerPackSpec = null;
21263
- credPath = () => join42(getDataDir(), "credentials.json");
21503
+ credPath = () => join43(getDataDir(), "credentials.json");
21264
21504
  _securityConfigCache = /* @__PURE__ */ new Map();
21265
21505
  SECURITY_CONFIG_TTL_MS = 5 * 60 * 1e3;
21266
21506
  _syncConfigCache = /* @__PURE__ */ new Map();
@@ -21283,8 +21523,8 @@ var init_sync_client = __esm({
21283
21523
  });
21284
21524
 
21285
21525
  // ../engine/dist/core/artifact-codec.js
21286
- import { readFileSync as readFileSync36 } from "fs";
21287
- import { basename as basename21, join as join43 } from "path";
21526
+ import { readFileSync as readFileSync37 } from "fs";
21527
+ import { basename as basename21, join as join44 } from "path";
21288
21528
  import { homedir as homedir16 } from "os";
21289
21529
  function parseCommandText(content, format, fallbackName) {
21290
21530
  if (format === "toml") {
@@ -21307,41 +21547,41 @@ function parseAgentText(content, format, fallbackName) {
21307
21547
  return { name: fm.name || fallbackName, description: fm.description || "", tools: fm.tools, body: body.trim() };
21308
21548
  }
21309
21549
  function readCommand(filePath, format) {
21310
- return parseCommandText(readFileSync36(filePath, "utf-8"), format, base2(filePath));
21550
+ return parseCommandText(readFileSync37(filePath, "utf-8"), format, base2(filePath));
21311
21551
  }
21312
21552
  function readAgent(filePath, format) {
21313
- return parseAgentText(readFileSync36(filePath, "utf-8"), format, base2(filePath));
21553
+ return parseAgentText(readFileSync37(filePath, "utf-8"), format, base2(filePath));
21314
21554
  }
21315
21555
  function readInstructions(filePath, name) {
21316
- const content = readFileSync36(filePath, "utf-8");
21556
+ const content = readFileSync37(filePath, "utf-8");
21317
21557
  return { name, description: "", body: content };
21318
21558
  }
21319
21559
  function commandPath(tool, name) {
21320
21560
  switch (tool) {
21321
21561
  case "claude":
21322
- return join43(claudeBackend.commandsDir(), `${name}.md`);
21562
+ return join44(claudeBackend.commandsDir(), `${name}.md`);
21323
21563
  case "agy":
21324
- return join43(join43(agyBackend.homeDir(), "commands"), `${name}.toml`);
21564
+ return join44(join44(agyBackend.homeDir(), "commands"), `${name}.toml`);
21325
21565
  case "opencode":
21326
- return join43(opencodeBackend.commandsDir(), `${name}.md`);
21566
+ return join44(opencodeBackend.commandsDir(), `${name}.md`);
21327
21567
  case "codex":
21328
- return join43(codexBackend.promptsDir(), `${name}.md`);
21568
+ return join44(codexBackend.promptsDir(), `${name}.md`);
21329
21569
  case "cursor":
21330
- return join43(cursorHomeDir(), "commands", `${name}.md`);
21570
+ return join44(cursorHomeDir(), "commands", `${name}.md`);
21331
21571
  }
21332
21572
  }
21333
21573
  function agentPath(tool, name) {
21334
21574
  switch (tool) {
21335
21575
  case "claude":
21336
- return join43(claudeBackend.agentsDir(), `${name}.md`);
21576
+ return join44(claudeBackend.agentsDir(), `${name}.md`);
21337
21577
  case "agy":
21338
- return join43(join43(agyBackend.homeDir(), "agents"), `${name}.md`);
21578
+ return join44(join44(agyBackend.homeDir(), "agents"), `${name}.md`);
21339
21579
  case "opencode":
21340
- return join43(opencodeBackend.agentsDir(), `${name}.md`);
21580
+ return join44(opencodeBackend.agentsDir(), `${name}.md`);
21341
21581
  case "codex":
21342
- return join43(codexBackend.agentsDir(), `${name}.toml`);
21582
+ return join44(codexBackend.agentsDir(), `${name}.toml`);
21343
21583
  case "cursor":
21344
- return join43(cursorHomeDir(), "agents", `${name}.md`);
21584
+ return join44(cursorHomeDir(), "agents", `${name}.md`);
21345
21585
  }
21346
21586
  }
21347
21587
  function instructionsFilename(tool) {
@@ -21366,21 +21606,21 @@ function instructionsFilename(tool) {
21366
21606
  function instructionsPath(tool, projectPath) {
21367
21607
  const file = instructionsFilename(tool);
21368
21608
  if (projectPath)
21369
- return join43(projectPath, file);
21609
+ return join44(projectPath, file);
21370
21610
  switch (tool) {
21371
21611
  case "claude":
21372
- return join43(claudeBackend.homeDir(), file);
21612
+ return join44(claudeBackend.homeDir(), file);
21373
21613
  case "agy":
21374
- return join43(agyBackend.homeDir(), file);
21614
+ return join44(agyBackend.homeDir(), file);
21375
21615
  case "codex":
21376
- return join43(codexBackend.homeDir(), file);
21616
+ return join44(codexBackend.homeDir(), file);
21377
21617
  case "opencode":
21378
- return join43(homedir16(), ".config", "opencode", file);
21618
+ return join44(homedir16(), ".config", "opencode", file);
21379
21619
  // Cursor has no single user-scope instructions FILE — user-level guidance
21380
21620
  // is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
21381
21621
  // the closest equivalent and is what a mirror can actually write.
21382
21622
  case "cursor":
21383
- return join43(cursorHomeDir(), file);
21623
+ return join44(cursorHomeDir(), file);
21384
21624
  }
21385
21625
  }
21386
21626
  function emit(type, art, toTool, projectPath) {
@@ -21426,8 +21666,8 @@ var init_artifact_codec = __esm({
21426
21666
  });
21427
21667
 
21428
21668
  // ../engine/dist/core/toolkit-sync.js
21429
- import { existsSync as existsSync42, mkdirSync as mkdirSync11, readFileSync as readFileSync37, writeFileSync as writeFileSync10, cpSync } from "fs";
21430
- import { dirname as dirname20, join as join44, basename as basename22 } from "path";
21669
+ import { existsSync as existsSync42, mkdirSync as mkdirSync11, readFileSync as readFileSync38, writeFileSync as writeFileSync10, cpSync } from "fs";
21670
+ import { dirname as dirname20, join as join45, basename as basename22 } from "path";
21431
21671
  import { homedir as homedir17 } from "os";
21432
21672
  function rowFromMemoryItem(type, item) {
21433
21673
  const extra = item.extra || {};
@@ -21448,14 +21688,14 @@ function skillsDirFor(tool) {
21448
21688
  case "claude":
21449
21689
  return claudeBackend.skillsDir();
21450
21690
  case "agy":
21451
- return join44(agyBackend.homeDir(), "skills");
21691
+ return join45(agyBackend.homeDir(), "skills");
21452
21692
  case "opencode":
21453
21693
  return opencodeBackend.skillsDir();
21454
21694
  case "codex":
21455
21695
  return codexBackend.skillsDir();
21456
21696
  // user skills, NOT .system
21457
21697
  case "cursor":
21458
- return join44(cursorHomeDir(), "skills");
21698
+ return join45(cursorHomeDir(), "skills");
21459
21699
  }
21460
21700
  }
21461
21701
  function copySkill(row, toTool) {
@@ -21463,7 +21703,7 @@ function copySkill(row, toTool) {
21463
21703
  if (!existsSync42(skillDir))
21464
21704
  return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
21465
21705
  const name = row.extra.skillName || basename22(skillDir);
21466
- const targetDir = join44(skillsDirFor(toTool), name);
21706
+ const targetDir = join45(skillsDirFor(toTool), name);
21467
21707
  if (existsSync42(targetDir))
21468
21708
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
21469
21709
  try {
@@ -21478,20 +21718,20 @@ function readMcpEntry(tool, name) {
21478
21718
  const home = homedir17();
21479
21719
  const tries = [];
21480
21720
  if (tool === "claude") {
21481
- tries.push({ path: join44(home, ".mcp.json"), key: "mcpServers" });
21482
- tries.push({ path: join44(home, ".claude.json"), key: "mcpServers" });
21721
+ tries.push({ path: join45(home, ".mcp.json"), key: "mcpServers" });
21722
+ tries.push({ path: join45(home, ".claude.json"), key: "mcpServers" });
21483
21723
  } else if (tool === "gemini") {
21484
- tries.push({ path: join44(home, ".gemini", "settings.json"), key: "mcpServers" });
21724
+ tries.push({ path: join45(home, ".gemini", "settings.json"), key: "mcpServers" });
21485
21725
  } else if (tool === "agy") {
21486
- tries.push({ path: join44(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
21726
+ tries.push({ path: join45(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
21487
21727
  } else if (tool === "cursor") {
21488
- tries.push({ path: join44(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
21728
+ tries.push({ path: join45(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
21489
21729
  } else if (tool === "opencode") {
21490
- tries.push({ path: join44(home, ".config", "opencode", "opencode.json"), key: "mcp" });
21491
- tries.push({ path: join44(home, ".config", "opencode", "config.json"), key: "mcp" });
21492
- tries.push({ path: join44(home, ".opencode", "config.json"), key: "mcp" });
21730
+ tries.push({ path: join45(home, ".config", "opencode", "opencode.json"), key: "mcp" });
21731
+ tries.push({ path: join45(home, ".config", "opencode", "config.json"), key: "mcp" });
21732
+ tries.push({ path: join45(home, ".opencode", "config.json"), key: "mcp" });
21493
21733
  } else if (tool === "codex") {
21494
- const entry = readCodexMcpEntry(join44(home, ".codex", "config.toml"), name);
21734
+ const entry = readCodexMcpEntry(join45(home, ".codex", "config.toml"), name);
21495
21735
  if (entry)
21496
21736
  return entry;
21497
21737
  }
@@ -21499,7 +21739,7 @@ function readMcpEntry(tool, name) {
21499
21739
  if (!existsSync42(t.path))
21500
21740
  continue;
21501
21741
  try {
21502
- const cfg = JSON.parse(readFileSync37(t.path, "utf-8"));
21742
+ const cfg = JSON.parse(readFileSync38(t.path, "utf-8"));
21503
21743
  const block = cfg[t.key] || {};
21504
21744
  if (block[name])
21505
21745
  return block[name];
@@ -21513,7 +21753,7 @@ function readCodexMcpEntry(path, target) {
21513
21753
  return null;
21514
21754
  let content;
21515
21755
  try {
21516
- content = readFileSync37(path, "utf-8");
21756
+ content = readFileSync38(path, "utf-8");
21517
21757
  } catch {
21518
21758
  return null;
21519
21759
  }
@@ -21564,7 +21804,7 @@ function readCodexMcpEntry(path, target) {
21564
21804
  function writeCodexMcpEntry(path, name, entry) {
21565
21805
  let content = "";
21566
21806
  if (existsSync42(path))
21567
- content = readFileSync37(path, "utf-8");
21807
+ content = readFileSync38(path, "utf-8");
21568
21808
  const header = `[mcp_servers.${name}]`;
21569
21809
  if (content.includes(header))
21570
21810
  return false;
@@ -21600,7 +21840,7 @@ function writeCodexMcpEntry(path, name, entry) {
21600
21840
  function writeMcpEntry(toTool, name, entry) {
21601
21841
  const home = homedir17();
21602
21842
  if (toTool === "codex") {
21603
- const path2 = join44(home, ".codex", "config.toml");
21843
+ const path2 = join45(home, ".codex", "config.toml");
21604
21844
  if (!writeCodexMcpEntry(path2, name, entry))
21605
21845
  return { ok: false, status: 409, error: `MCP "${name}" already exists in ${path2}.` };
21606
21846
  return { ok: true, targetPath: path2 };
@@ -21608,22 +21848,22 @@ function writeMcpEntry(toTool, name, entry) {
21608
21848
  let path;
21609
21849
  let key2;
21610
21850
  if (toTool === "claude") {
21611
- path = join44(home, ".mcp.json");
21851
+ path = join45(home, ".mcp.json");
21612
21852
  key2 = "mcpServers";
21613
21853
  } else if (toTool === "agy") {
21614
- path = join44(home, ".gemini", "config", "mcp_config.json");
21854
+ path = join45(home, ".gemini", "config", "mcp_config.json");
21615
21855
  key2 = "mcpServers";
21616
21856
  } else if (toTool === "cursor") {
21617
- path = join44(cursorHomeDir(), "mcp.json");
21857
+ path = join45(cursorHomeDir(), "mcp.json");
21618
21858
  key2 = "mcpServers";
21619
21859
  } else {
21620
- path = join44(home, ".config", "opencode", "opencode.json");
21860
+ path = join45(home, ".config", "opencode", "opencode.json");
21621
21861
  key2 = "mcp";
21622
21862
  }
21623
21863
  let cfg = {};
21624
21864
  if (existsSync42(path)) {
21625
21865
  try {
21626
- cfg = JSON.parse(readFileSync37(path, "utf-8"));
21866
+ cfg = JSON.parse(readFileSync38(path, "utf-8"));
21627
21867
  } catch {
21628
21868
  return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
21629
21869
  }
@@ -21848,11 +22088,11 @@ var init_toolkit_sync = __esm({
21848
22088
  });
21849
22089
 
21850
22090
  // ../engine/dist/core/which.js
21851
- import { existsSync as existsSync44, statSync as statSync40, accessSync, constants } from "node:fs";
21852
- import { join as join46, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22091
+ import { existsSync as existsSync44, statSync as statSync41, accessSync, constants } from "node:fs";
22092
+ import { join as join47, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
21853
22093
  function executable(p) {
21854
22094
  try {
21855
- if (!statSync40(p).isFile())
22095
+ if (!statSync41(p).isFile())
21856
22096
  return false;
21857
22097
  } catch {
21858
22098
  return false;
@@ -21888,7 +22128,7 @@ function resolveOnPath(name) {
21888
22128
  if (!dir)
21889
22129
  continue;
21890
22130
  for (const c of candidates(name)) {
21891
- const full = join46(dir, c);
22131
+ const full = join47(dir, c);
21892
22132
  if (existsSync44(full) && executable(full))
21893
22133
  return full;
21894
22134
  }
@@ -21915,11 +22155,11 @@ __export(toolkit_pull_exports, {
21915
22155
  platformRefusal: () => platformRefusal,
21916
22156
  portableCommand: () => portableCommand
21917
22157
  });
21918
- import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync41, writeFileSync as writeFileSync12 } from "node:fs";
21919
- import { dirname as dirname22, join as join47, basename as basename23, isAbsolute as isAbsolute3 } from "node:path";
22158
+ import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync42, writeFileSync as writeFileSync12 } from "node:fs";
22159
+ import { dirname as dirname22, join as join48, basename as basename23, isAbsolute as isAbsolute3 } from "node:path";
21920
22160
  function isExecutableFile(p) {
21921
22161
  try {
21922
- return statSync41(p).isFile();
22162
+ return statSync42(p).isFile();
21923
22163
  } catch {
21924
22164
  return false;
21925
22165
  }
@@ -22086,8 +22326,8 @@ function executePull(rows, opts = {}) {
22086
22326
  const outcomes = [];
22087
22327
  for (const { name, body, truncated, redacted, tools } of skills) {
22088
22328
  for (const tool of tools) {
22089
- const dir = join47(skillsDirFor(tool), name);
22090
- const file = join47(dir, "SKILL.md");
22329
+ const dir = join48(skillsDirFor(tool), name);
22330
+ const file = join48(dir, "SKILL.md");
22091
22331
  if (existsSync45(file)) {
22092
22332
  outcomes.push({ type: "skill", name, tool, status: "present" });
22093
22333
  continue;
@@ -22420,11 +22660,16 @@ async function repairAll(opts = {}) {
22420
22660
  scanned++;
22421
22661
  if (opts.verbose && scanned % 50 === 0) log6(`checked ${scanned}/${ids.length}\u2026`);
22422
22662
  const arch = await fetchJson(`${primary.url}/api/conversations/${id}/raw-archive?count=1`, primary.token);
22423
- const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
22424
- if (archiveMsgs < minMessages) continue;
22425
- const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
22426
- const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
22427
- if (archiveMsgs <= current) continue;
22663
+ const counted = arch && arch.messages !== null;
22664
+ if (counted) {
22665
+ const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
22666
+ if (archiveMsgs < minMessages) continue;
22667
+ const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
22668
+ const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
22669
+ if (archiveMsgs <= current) continue;
22670
+ } else if (!arch) {
22671
+ continue;
22672
+ }
22428
22673
  const r = await repairSession(id, { dryRun: !opts.apply, verbose: opts.verbose, server: opts.server });
22429
22674
  if (r.status === "already-full") continue;
22430
22675
  candidates2.push(r);
@@ -22452,8 +22697,8 @@ __export(shadow_prune_exports, {
22452
22697
  DEFAULT_MAX_TOTAL_BYTES: () => DEFAULT_MAX_TOTAL_BYTES,
22453
22698
  pruneShadow: () => pruneShadow
22454
22699
  });
22455
- import { readdirSync as readdirSync32, statSync as statSync44, unlinkSync as unlinkSync3 } from "fs";
22456
- import { join as join50 } from "path";
22700
+ import { readdirSync as readdirSync32, statSync as statSync45, unlinkSync as unlinkSync3 } from "fs";
22701
+ import { join as join51 } from "path";
22457
22702
  function listShadows(isFullyAcked) {
22458
22703
  const out = [];
22459
22704
  const root = shadowRoot();
@@ -22464,7 +22709,7 @@ function listShadows(isFullyAcked) {
22464
22709
  return out;
22465
22710
  }
22466
22711
  for (const tool of tools) {
22467
- const dir = join50(root, tool);
22712
+ const dir = join51(root, tool);
22468
22713
  let names;
22469
22714
  try {
22470
22715
  names = readdirSync32(dir);
@@ -22474,10 +22719,10 @@ function listShadows(isFullyAcked) {
22474
22719
  for (const name of names) {
22475
22720
  if (!name.endsWith(".gz"))
22476
22721
  continue;
22477
- const path = join50(dir, name);
22722
+ const path = join51(dir, name);
22478
22723
  let st;
22479
22724
  try {
22480
- st = statSync44(path);
22725
+ st = statSync45(path);
22481
22726
  } catch {
22482
22727
  continue;
22483
22728
  }
@@ -22592,9 +22837,9 @@ var init_code_intel = __esm({
22592
22837
  });
22593
22838
 
22594
22839
  // ../engine/dist/core/companions.js
22595
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync45, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "node:fs";
22840
+ import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync46, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream as createWriteStream2, readFileSync as readFileSync43, writeFileSync as writeFileSync15 } from "node:fs";
22596
22841
  import { homedir as homedir19 } from "node:os";
22597
- import { join as join51 } from "node:path";
22842
+ import { join as join52 } from "node:path";
22598
22843
  import { execSync as execSync3 } from "node:child_process";
22599
22844
  import { createHash as createHash13 } from "node:crypto";
22600
22845
  function expectedCodeindexSha256(artifact) {
@@ -22643,7 +22888,7 @@ function checkCodeindexStatus() {
22643
22888
  status.installed = true;
22644
22889
  status.path = foundPath;
22645
22890
  try {
22646
- status.size = statSync45(foundPath).size;
22891
+ status.size = statSync46(foundPath).size;
22647
22892
  } catch {
22648
22893
  }
22649
22894
  try {
@@ -22669,7 +22914,7 @@ async function downloadArtifact(artifact, dest) {
22669
22914
  throw new Error(`Download failed: ${res.status} ${res.statusText}`);
22670
22915
  if (!res.body)
22671
22916
  throw new Error("Download returned empty body");
22672
- const file = createWriteStream(dest);
22917
+ const file = createWriteStream2(dest);
22673
22918
  await new Promise((resolve3, reject) => {
22674
22919
  const reader = res.body.getReader();
22675
22920
  const pump = async () => {
@@ -22704,7 +22949,7 @@ async function installCodeindex(opts = {}) {
22704
22949
  if (!expected) {
22705
22950
  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.`);
22706
22951
  }
22707
- const actual = createHash13("sha256").update(readFileSync42(tmpPath)).digest("hex");
22952
+ const actual = createHash13("sha256").update(readFileSync43(tmpPath)).digest("hex");
22708
22953
  if (actual !== expected) {
22709
22954
  throw new Error(`Checksum mismatch for ${status.artifactName}.
22710
22955
  expected ${expected}
@@ -22738,9 +22983,9 @@ var init_companions = __esm({
22738
22983
  "x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
22739
22984
  "aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
22740
22985
  };
22741
- CODEINDEX_INSTALL_DIR = join51(homedir19(), ".local", "bin");
22986
+ CODEINDEX_INSTALL_DIR = join52(homedir19(), ".local", "bin");
22742
22987
  CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
22743
- CODEINDEX_BIN_PATH = join51(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22988
+ CODEINDEX_BIN_PATH = join52(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22744
22989
  }
22745
22990
  });
22746
22991
 
@@ -22855,8 +23100,8 @@ __export(collector_exports, {
22855
23100
  });
22856
23101
  import { spawn as spawn2 } from "node:child_process";
22857
23102
  import { execSync as execSync4 } from "node:child_process";
22858
- import { readFileSync as readFileSync43 } from "node:fs";
22859
- import { join as join52, basename as basename24 } from "node:path";
23103
+ import { readFileSync as readFileSync44 } from "node:fs";
23104
+ import { join as join53, basename as basename24 } from "node:path";
22860
23105
  async function resolveCodeindexBin(autoInstall = true) {
22861
23106
  let st = checkCodeindexStatus();
22862
23107
  if (!st.installed && autoInstall)
@@ -23038,7 +23283,7 @@ async function collectCode(opts) {
23038
23283
  }
23039
23284
  const complexity = (r) => {
23040
23285
  try {
23041
- const txt = readFileSync43(join52(ws, r), "utf8");
23286
+ const txt = readFileSync44(join53(ws, r), "utf8");
23042
23287
  if (txt.length > 2e6)
23043
23288
  return 1;
23044
23289
  const m = txt.match(CF_RE);
@@ -23802,15 +24047,15 @@ __export(service_installer_exports, {
23802
24047
  uninstallService: () => uninstallService
23803
24048
  });
23804
24049
  import { execSync as execSync5 } from "child_process";
23805
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync44, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "fs";
24050
+ import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync45, rmSync as rmSync8, writeFileSync as writeFileSync16 } from "fs";
23806
24051
  import { homedir as homedir20 } from "os";
23807
- import { join as join53 } from "path";
24052
+ import { join as join54 } from "path";
23808
24053
  import { fileURLToPath as fileURLToPath5 } from "url";
23809
24054
  function resolveDaemonPaths() {
23810
24055
  const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
23811
24056
  const node = process.execPath;
23812
- const dataDir = join53(homedir20(), ".chat-recall");
23813
- const logFile = join53(dataDir, "watch.log");
24057
+ const dataDir = join54(homedir20(), ".chat-recall");
24058
+ const logFile = join54(dataDir, "watch.log");
23814
24059
  return { watchJs, node, logFile, dataDir };
23815
24060
  }
23816
24061
  function tryCmd(cmd) {
@@ -23837,12 +24082,12 @@ function refreshServiceDefinition() {
23837
24082
  if (process.platform !== "linux") return false;
23838
24083
  try {
23839
24084
  const { watchJs, node, logFile } = resolveDaemonPaths();
23840
- const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
24085
+ const unitPath = join54(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
23841
24086
  if (!existsSync49(unitPath)) return false;
23842
- const current = readFileSync44(unitPath, "utf8");
24087
+ const current = readFileSync45(unitPath, "utf8");
23843
24088
  if (!current.includes(watchJs)) return false;
23844
24089
  const wanted = renderSystemdUnit(watchJs, node, logFile);
23845
- if (readFileSync44(unitPath, "utf8") === wanted) return false;
24090
+ if (readFileSync45(unitPath, "utf8") === wanted) return false;
23846
24091
  writeFileSync16(unitPath, wanted);
23847
24092
  execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
23848
24093
  return true;
@@ -23868,8 +24113,8 @@ function installService() {
23868
24113
  );
23869
24114
  }
23870
24115
  function installSystemd(watchJs, node, logFile) {
23871
- const unitDir = join53(homedir20(), ".config", "systemd", "user");
23872
- const unitPath = join53(unitDir, "chat-recall-watch.service");
24116
+ const unitDir = join54(homedir20(), ".config", "systemd", "user");
24117
+ const unitPath = join54(unitDir, "chat-recall-watch.service");
23873
24118
  const unit = renderSystemdUnit(watchJs, node, logFile);
23874
24119
  mkdirSync17(unitDir, { recursive: true });
23875
24120
  writeFileSync16(unitPath, unit);
@@ -23948,8 +24193,8 @@ function renderSystemdUnit(watchJs, node, logFile) {
23948
24193
  }
23949
24194
  function installLaunchd(watchJs, node, logFile) {
23950
24195
  const label = "com.chat-recall.watch";
23951
- const plistDir = join53(homedir20(), "Library", "LaunchAgents");
23952
- const plistPath = join53(plistDir, `${label}.plist`);
24196
+ const plistDir = join54(homedir20(), "Library", "LaunchAgents");
24197
+ const plistPath = join54(plistDir, `${label}.plist`);
23953
24198
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
23954
24199
  mkdirSync17(plistDir, { recursive: true });
23955
24200
  writeFileSync16(plistPath, plist);
@@ -24005,9 +24250,9 @@ function uninstallService() {
24005
24250
  removed = true;
24006
24251
  } catch {
24007
24252
  }
24008
- const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
24253
+ const unitPath = join54(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
24009
24254
  if (existsSync49(unitPath)) {
24010
- rmSync7(unitPath, { force: true });
24255
+ rmSync8(unitPath, { force: true });
24011
24256
  removed = true;
24012
24257
  }
24013
24258
  try {
@@ -24017,14 +24262,14 @@ function uninstallService() {
24017
24262
  return removed;
24018
24263
  }
24019
24264
  if (process.platform === "darwin") {
24020
- const plistPath = join53(homedir20(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
24265
+ const plistPath = join54(homedir20(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
24021
24266
  try {
24022
24267
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
24023
24268
  } catch {
24024
24269
  }
24025
24270
  let removed = false;
24026
24271
  if (existsSync49(plistPath)) {
24027
- rmSync7(plistPath, { force: true });
24272
+ rmSync8(plistPath, { force: true });
24028
24273
  removed = true;
24029
24274
  }
24030
24275
  return removed;
@@ -24068,7 +24313,7 @@ init_settings();
24068
24313
  init_sync_client();
24069
24314
  init_http();
24070
24315
  import chokidar from "chokidar";
24071
- import { dirname as dirname25, basename as basename25, join as join54, resolve as resolve2, sep as sep4 } from "path";
24316
+ import { dirname as dirname25, basename as basename25, join as join55, resolve as resolve2, sep as sep4 } from "path";
24072
24317
  import { homedir as homedir21 } from "os";
24073
24318
 
24074
24319
  // src/intent-drain.ts
@@ -24081,13 +24326,13 @@ init_codex2();
24081
24326
  init_cursor();
24082
24327
  init_opencode();
24083
24328
  init_agy();
24084
- import { existsSync as existsSync46, readFileSync as readFileSync39, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
24085
- import { join as join48 } from "node:path";
24329
+ import { existsSync as existsSync46, readFileSync as readFileSync40, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
24330
+ import { join as join49 } from "node:path";
24086
24331
 
24087
24332
  // src/project-tasks.ts
24088
24333
  init_http();
24089
- import { existsSync as existsSync43, readFileSync as readFileSync38, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync39 } from "node:fs";
24090
- import { join as join45, dirname as dirname21 } from "node:path";
24334
+ import { existsSync as existsSync43, readFileSync as readFileSync39, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync40 } from "node:fs";
24335
+ import { join as join46, dirname as dirname21 } from "node:path";
24091
24336
  import { homedir as homedir18 } from "node:os";
24092
24337
 
24093
24338
  // ../engine/dist/core/secret-task-status.js
@@ -24156,11 +24401,11 @@ function parseTaskFile(content) {
24156
24401
  return out;
24157
24402
  }
24158
24403
  function ledgerPath2() {
24159
- return join45(homedir18(), ".chat-recall", "project-tasks-ledger.json");
24404
+ return join46(homedir18(), ".chat-recall", "project-tasks-ledger.json");
24160
24405
  }
24161
24406
  function readLedger() {
24162
24407
  try {
24163
- return JSON.parse(readFileSync38(ledgerPath2(), "utf8"));
24408
+ return JSON.parse(readFileSync39(ledgerPath2(), "utf8"));
24164
24409
  } catch {
24165
24410
  return {};
24166
24411
  }
@@ -24193,18 +24438,18 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
24193
24438
  continue;
24194
24439
  }
24195
24440
  for (const project of projects) {
24196
- const file = join45(project, fk.filename);
24441
+ const file = join46(project, fk.filename);
24197
24442
  if (!existsSync43(file)) continue;
24198
24443
  let mtime;
24199
24444
  try {
24200
- mtime = statSync39(file).mtimeMs;
24445
+ mtime = statSync40(file).mtimeMs;
24201
24446
  } catch {
24202
24447
  continue;
24203
24448
  }
24204
24449
  if (ledger[file] === mtime) continue;
24205
24450
  let changed;
24206
24451
  try {
24207
- changed = parseTaskFile(readFileSync38(file, "utf8")).filter((t) => t.kind === fk.kind);
24452
+ changed = parseTaskFile(readFileSync39(file, "utf8")).filter((t) => t.kind === fk.kind);
24208
24453
  } catch {
24209
24454
  continue;
24210
24455
  }
@@ -24255,16 +24500,16 @@ function instructionTargets(rootPath, isGlobal) {
24255
24500
  const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
24256
24501
  if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
24257
24502
  if (!rootPath) return [];
24258
- const present = INSTRUCTION_FILENAMES.map((f) => join48(rootPath, f)).filter((p) => existsSync46(p));
24503
+ const present = INSTRUCTION_FILENAMES.map((f) => join49(rootPath, f)).filter((p) => existsSync46(p));
24259
24504
  if (present.length) return present;
24260
- return [...new Set(tools.map((t) => join48(rootPath, instructionsFilename(t.id))))];
24505
+ return [...new Set(tools.map((t) => join49(rootPath, instructionsFilename(t.id))))];
24261
24506
  }
24262
24507
  function applyCodeRecommendation(intent) {
24263
24508
  try {
24264
24509
  const meta = JSON.parse(intent.name || "{}");
24265
24510
  if (intent.artifact_type === "write_tasks_file") {
24266
24511
  if (!meta.rootPath || !meta.content) return { status: "error", result: JSON.stringify({ error: "missing rootPath or content" }) };
24267
- const file = join48(meta.rootPath, meta.filename || "CODE_TASKS.md");
24512
+ const file = join49(meta.rootPath, meta.filename || "CODE_TASKS.md");
24268
24513
  writeFileSync13(file, meta.content);
24269
24514
  return { status: "done", result: JSON.stringify({ wrote: file }) };
24270
24515
  }
@@ -24283,7 +24528,7 @@ function applyCodeRecommendation(intent) {
24283
24528
  mkdirSync14(dir, { recursive: true });
24284
24529
  } catch {
24285
24530
  }
24286
- const existing = existsSync46(file) ? readFileSync39(file, "utf8") : "";
24531
+ const existing = existsSync46(file) ? readFileSync40(file, "utf8") : "";
24287
24532
  if (existing.includes(text)) {
24288
24533
  skipped.push(file);
24289
24534
  continue;
@@ -24382,10 +24627,10 @@ async function runIntent(intent, ctx) {
24382
24627
  var ownVersion2 = "0.0.0";
24383
24628
  try {
24384
24629
  const dir = dirname23(fileURLToPath4(import.meta.url));
24385
- ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
24630
+ ownVersion2 = JSON.parse(readFileSync40(join49(dir, "../package.json"), "utf-8")).version || ownVersion2;
24386
24631
  } catch {
24387
24632
  try {
24388
- if (true) ownVersion2 = "0.7.16";
24633
+ if (true) ownVersion2 = "0.7.18";
24389
24634
  } catch {
24390
24635
  }
24391
24636
  }
@@ -24450,14 +24695,14 @@ init_auto_update();
24450
24695
 
24451
24696
  // src/code-index-cursor.ts
24452
24697
  init_paths();
24453
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15, statSync as statSync42 } from "fs";
24698
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15, statSync as statSync43 } from "fs";
24454
24699
  import { execFileSync as execFileSync4 } from "child_process";
24455
24700
  import { createHash as createHash10 } from "crypto";
24456
- import { join as join49, dirname as dirname24 } from "path";
24457
- var cursorPath = () => join49(getDataDir(), "code-index-cursor.json");
24701
+ import { join as join50, dirname as dirname24 } from "path";
24702
+ var cursorPath = () => join50(getDataDir(), "code-index-cursor.json");
24458
24703
  function readCursor() {
24459
24704
  try {
24460
- const raw = JSON.parse(readFileSync40(cursorPath(), "utf-8"));
24705
+ const raw = JSON.parse(readFileSync41(cursorPath(), "utf-8"));
24461
24706
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
24462
24707
  const out = {};
24463
24708
  for (const [k, v] of Object.entries(raw)) {
@@ -24501,7 +24746,7 @@ function workspaceFingerprint(workspace, collectorVersion) {
24501
24746
  const rel = entry.slice(3);
24502
24747
  let stamp2 = "gone";
24503
24748
  try {
24504
- const s = statSync42(join49(workspace, rel));
24749
+ const s = statSync43(join50(workspace, rel));
24505
24750
  stamp2 = `${s.size}:${Math.floor(s.mtimeMs)}`;
24506
24751
  } catch {
24507
24752
  }
@@ -24637,13 +24882,13 @@ init_telemetry();
24637
24882
 
24638
24883
  // src/self-restart.ts
24639
24884
  import { spawn } from "child_process";
24640
- import { readFileSync as readFileSync41 } from "fs";
24885
+ import { readFileSync as readFileSync42 } from "fs";
24641
24886
  import { createHash as createHash11 } from "crypto";
24642
24887
  var EXIT_CODE_CODE_CHANGED = 75;
24643
24888
  function codeFingerprint(entry = process.argv[1]) {
24644
24889
  if (!entry) return null;
24645
24890
  try {
24646
- const buf = readFileSync41(entry);
24891
+ const buf = readFileSync42(entry);
24647
24892
  return {
24648
24893
  path: entry,
24649
24894
  size: buf.byteLength,
@@ -24710,11 +24955,11 @@ function checkSelfRestart(opts) {
24710
24955
  }
24711
24956
 
24712
24957
  // src/log-rotate.ts
24713
- import { statSync as statSync43, copyFileSync as copyFileSync3, truncateSync } from "fs";
24958
+ import { statSync as statSync44, copyFileSync as copyFileSync3, truncateSync } from "fs";
24714
24959
  var LOG_MAX_BYTES = 32 * 1024 * 1024;
24715
24960
  function rotateLogIfLarge(logPath, maxBytes = LOG_MAX_BYTES) {
24716
24961
  try {
24717
- const size = statSync43(logPath).size;
24962
+ const size = statSync44(logPath).size;
24718
24963
  if (size < maxBytes) return { rotated: false, sizeBefore: 0 };
24719
24964
  copyFileSync3(logPath, `${logPath}.1`);
24720
24965
  truncateSync(logPath, 0);
@@ -24769,8 +25014,8 @@ async function runCollectorMigration(opts) {
24769
25014
  // auto-indexer/indexer.ts
24770
25015
  init_sync_ledger();
24771
25016
  init_collector_health();
24772
- import { existsSync as existsSync50, readFileSync as readFileSync45, statSync as statSync46 } from "fs";
24773
- var CLI_VERSION3 = true ? "0.7.16" : "0.0.0";
25017
+ import { existsSync as existsSync50, readFileSync as readFileSync46, statSync as statSync47 } from "fs";
25018
+ var CLI_VERSION3 = true ? "0.7.18" : "0.0.0";
24774
25019
  var DEBOUNCE_MS = 5e3;
24775
25020
  var PLANS_DIR = claudeBackend.plansDir();
24776
25021
  var TASKS_DIR = claudeBackend.tasksDir();
@@ -24779,8 +25024,8 @@ var DIARY_DIR = getDiaryDir();
24779
25024
  var CODEX_SESSIONS_DIR = codexBackend.sessionsDir();
24780
25025
  var OPENCODE_DB_PATH = opencodeBackend.dbPath();
24781
25026
  var OPENCODE_DB_DIR = dirname25(OPENCODE_DB_PATH);
24782
- var CURRENT_RESUME_PATH = join54(claudeBackend.homeDir(), "current-resume");
24783
- var LAST_CLEANUP_PATH = join54(claudeBackend.homeDir(), ".last-cleanup");
25027
+ var CURRENT_RESUME_PATH = join55(claudeBackend.homeDir(), "current-resume");
25028
+ var LAST_CLEANUP_PATH = join55(claudeBackend.homeDir(), ".last-cleanup");
24784
25029
  var debounceTimer = null;
24785
25030
  var ticks = new TickQueue({
24786
25031
  onError: (kind, err) => daemonLog.error(`${kind} tick failed: ${err instanceof Error ? err.message : err}`)
@@ -24829,7 +25074,7 @@ var lastResumeAt = /* @__PURE__ */ new Map();
24829
25074
  var RESUME_REARM_MS = 6e4;
24830
25075
  async function onResumeSignal(path) {
24831
25076
  try {
24832
- const txt = readFileSync45(path, "utf-8");
25077
+ const txt = readFileSync46(path, "utf-8");
24833
25078
  const m = /--resume\s+([0-9a-fA-F-]{36})/.exec(txt);
24834
25079
  if (!m) return;
24835
25080
  const id = m[1];
@@ -24875,7 +25120,7 @@ var CLAUDE_SESSION_ROOTS = claudeProjectDirs();
24875
25120
  var sessionWatcher = watchWithFallback("sessions", CLAUDE_SESSION_ROOTS.map((root) => `${root}/**/*.jsonl`), {
24876
25121
  ignored: (p, stats) => {
24877
25122
  if (/agent-/.test(p) || /^\./.test(basename25(p))) return true;
24878
- const st = stats ?? statSync46(p, { throwIfNoEntry: false });
25123
+ const st = stats ?? statSync47(p, { throwIfNoEntry: false });
24879
25124
  if (!st || st.isDirectory()) return false;
24880
25125
  return st.mtimeMs < Date.now() - WATCH_WINDOW_MS;
24881
25126
  },
@@ -25017,7 +25262,7 @@ if (BOOT_CODE) {
25017
25262
  daemonLog.info(`running bundle ${BOOT_CODE.path} (${BOOT_CODE.size} bytes)`);
25018
25263
  }
25019
25264
  {
25020
- const r = rotateLogIfLarge(join54(getDataDir(), "watch.log"));
25265
+ const r = rotateLogIfLarge(join55(getDataDir(), "watch.log"));
25021
25266
  if (r.rotated) {
25022
25267
  daemonLog.info(`rotated watch.log at ${(r.sizeBefore / 1048576).toFixed(0)}MB \u2192 watch.log.1 (copy+truncate; the path is preserved)`);
25023
25268
  }
@@ -25203,7 +25448,7 @@ function isCodeWorkspace(p) {
25203
25448
  }
25204
25449
  return WORKSPACE_MARKERS.some((m) => {
25205
25450
  try {
25206
- return existsSync50(join54(p, m));
25451
+ return existsSync50(join55(p, m));
25207
25452
  } catch {
25208
25453
  return false;
25209
25454
  }