chat-recall 0.7.17 → 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/mcp.js CHANGED
@@ -2034,7 +2034,7 @@ var require_thread_stream = __commonJS({
2034
2034
  var { version: version2 } = require_package();
2035
2035
  var { EventEmitter } = __require("events");
2036
2036
  var { Worker: Worker2 } = __require("worker_threads");
2037
- var { join: join53 } = __require("path");
2037
+ var { join: join54 } = __require("path");
2038
2038
  var { pathToFileURL } = __require("url");
2039
2039
  var { wait } = require_wait();
2040
2040
  var {
@@ -2077,7 +2077,7 @@ var require_thread_stream = __commonJS({
2077
2077
  function createWorker(stream, opts) {
2078
2078
  const { filename, workerData } = opts;
2079
2079
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
2080
- const toExecute = bundlerOverrides["thread-stream-worker"] || join53(__dirname, "lib", "worker.js");
2080
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join54(__dirname, "lib", "worker.js");
2081
2081
  const worker = new Worker2(toExecute, {
2082
2082
  ...opts.workerOpts,
2083
2083
  trackUnmanagedFds: false,
@@ -2480,7 +2480,7 @@ var require_transport = __commonJS({
2480
2480
  "use strict";
2481
2481
  var { createRequire: createRequire3 } = __require("module");
2482
2482
  var getCallers = require_caller();
2483
- var { join: join53, isAbsolute: isAbsolute3, sep: sep4 } = __require("node:path");
2483
+ var { join: join54, isAbsolute: isAbsolute3, sep: sep4 } = __require("node:path");
2484
2484
  var sleep = require_atomic_sleep();
2485
2485
  var onExit = require_on_exit_leak_free();
2486
2486
  var ThreadStream = require_thread_stream();
@@ -2543,7 +2543,7 @@ var require_transport = __commonJS({
2543
2543
  throw new Error("only one of target or targets can be specified");
2544
2544
  }
2545
2545
  if (targets) {
2546
- target = bundlerOverrides["pino-worker"] || join53(__dirname, "worker.js");
2546
+ target = bundlerOverrides["pino-worker"] || join54(__dirname, "worker.js");
2547
2547
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
2548
2548
  return {
2549
2549
  ...dest,
@@ -2561,7 +2561,7 @@ var require_transport = __commonJS({
2561
2561
  });
2562
2562
  });
2563
2563
  } else if (pipeline) {
2564
- target = bundlerOverrides["pino-worker"] || join53(__dirname, "worker.js");
2564
+ target = bundlerOverrides["pino-worker"] || join54(__dirname, "worker.js");
2565
2565
  options.pipelines = [pipeline.map((dest) => {
2566
2566
  return {
2567
2567
  ...dest,
@@ -2583,7 +2583,7 @@ var require_transport = __commonJS({
2583
2583
  return origin;
2584
2584
  }
2585
2585
  if (origin === "pino/file") {
2586
- return join53(__dirname, "..", "file.js");
2586
+ return join54(__dirname, "..", "file.js");
2587
2587
  }
2588
2588
  let fixTarget2;
2589
2589
  for (const filePath of callers) {
@@ -3572,7 +3572,7 @@ var require_safe_stable_stringify = __commonJS({
3572
3572
  return circularValue;
3573
3573
  }
3574
3574
  let res = "";
3575
- let join53 = ",";
3575
+ let join54 = ",";
3576
3576
  const originalIndentation = indentation;
3577
3577
  if (Array.isArray(value)) {
3578
3578
  if (value.length === 0) {
@@ -3586,7 +3586,7 @@ var require_safe_stable_stringify = __commonJS({
3586
3586
  indentation += spacer;
3587
3587
  res += `
3588
3588
  ${indentation}`;
3589
- join53 = `,
3589
+ join54 = `,
3590
3590
  ${indentation}`;
3591
3591
  }
3592
3592
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3594,13 +3594,13 @@ ${indentation}`;
3594
3594
  for (; i < maximumValuesToStringify - 1; i++) {
3595
3595
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3596
3596
  res += tmp2 !== void 0 ? tmp2 : "null";
3597
- res += join53;
3597
+ res += join54;
3598
3598
  }
3599
3599
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3600
3600
  res += tmp !== void 0 ? tmp : "null";
3601
3601
  if (value.length - 1 > maximumBreadth) {
3602
3602
  const removedKeys = value.length - maximumBreadth - 1;
3603
- res += `${join53}"... ${getItemCount(removedKeys)} not stringified"`;
3603
+ res += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3604
3604
  }
3605
3605
  if (spacer !== "") {
3606
3606
  res += `
@@ -3621,7 +3621,7 @@ ${originalIndentation}`;
3621
3621
  let separator = "";
3622
3622
  if (spacer !== "") {
3623
3623
  indentation += spacer;
3624
- join53 = `,
3624
+ join54 = `,
3625
3625
  ${indentation}`;
3626
3626
  whitespace = " ";
3627
3627
  }
@@ -3635,13 +3635,13 @@ ${indentation}`;
3635
3635
  const tmp = stringifyFnReplacer(key3, value, stack, replacer, spacer, indentation);
3636
3636
  if (tmp !== void 0) {
3637
3637
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3638
- separator = join53;
3638
+ separator = join54;
3639
3639
  }
3640
3640
  }
3641
3641
  if (keyLength > maximumBreadth) {
3642
3642
  const removedKeys = keyLength - maximumBreadth;
3643
3643
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
3644
- separator = join53;
3644
+ separator = join54;
3645
3645
  }
3646
3646
  if (spacer !== "" && separator.length > 1) {
3647
3647
  res = `
@@ -3682,7 +3682,7 @@ ${originalIndentation}`;
3682
3682
  }
3683
3683
  const originalIndentation = indentation;
3684
3684
  let res = "";
3685
- let join53 = ",";
3685
+ let join54 = ",";
3686
3686
  if (Array.isArray(value)) {
3687
3687
  if (value.length === 0) {
3688
3688
  return "[]";
@@ -3695,7 +3695,7 @@ ${originalIndentation}`;
3695
3695
  indentation += spacer;
3696
3696
  res += `
3697
3697
  ${indentation}`;
3698
- join53 = `,
3698
+ join54 = `,
3699
3699
  ${indentation}`;
3700
3700
  }
3701
3701
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3703,13 +3703,13 @@ ${indentation}`;
3703
3703
  for (; i < maximumValuesToStringify - 1; i++) {
3704
3704
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3705
3705
  res += tmp2 !== void 0 ? tmp2 : "null";
3706
- res += join53;
3706
+ res += join54;
3707
3707
  }
3708
3708
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3709
3709
  res += tmp !== void 0 ? tmp : "null";
3710
3710
  if (value.length - 1 > maximumBreadth) {
3711
3711
  const removedKeys = value.length - maximumBreadth - 1;
3712
- res += `${join53}"... ${getItemCount(removedKeys)} not stringified"`;
3712
+ res += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3713
3713
  }
3714
3714
  if (spacer !== "") {
3715
3715
  res += `
@@ -3722,7 +3722,7 @@ ${originalIndentation}`;
3722
3722
  let whitespace = "";
3723
3723
  if (spacer !== "") {
3724
3724
  indentation += spacer;
3725
- join53 = `,
3725
+ join54 = `,
3726
3726
  ${indentation}`;
3727
3727
  whitespace = " ";
3728
3728
  }
@@ -3731,7 +3731,7 @@ ${indentation}`;
3731
3731
  const tmp = stringifyArrayReplacer(key3, value[key3], stack, replacer, spacer, indentation);
3732
3732
  if (tmp !== void 0) {
3733
3733
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3734
- separator = join53;
3734
+ separator = join54;
3735
3735
  }
3736
3736
  }
3737
3737
  if (spacer !== "" && separator.length > 1) {
@@ -3789,20 +3789,20 @@ ${originalIndentation}`;
3789
3789
  indentation += spacer;
3790
3790
  let res2 = `
3791
3791
  ${indentation}`;
3792
- const join54 = `,
3792
+ const join55 = `,
3793
3793
  ${indentation}`;
3794
3794
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
3795
3795
  let i = 0;
3796
3796
  for (; i < maximumValuesToStringify - 1; i++) {
3797
3797
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3798
3798
  res2 += tmp2 !== void 0 ? tmp2 : "null";
3799
- res2 += join54;
3799
+ res2 += join55;
3800
3800
  }
3801
3801
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3802
3802
  res2 += tmp !== void 0 ? tmp : "null";
3803
3803
  if (value.length - 1 > maximumBreadth) {
3804
3804
  const removedKeys = value.length - maximumBreadth - 1;
3805
- res2 += `${join54}"... ${getItemCount(removedKeys)} not stringified"`;
3805
+ res2 += `${join55}"... ${getItemCount(removedKeys)} not stringified"`;
3806
3806
  }
3807
3807
  res2 += `
3808
3808
  ${originalIndentation}`;
@@ -3818,16 +3818,16 @@ ${originalIndentation}`;
3818
3818
  return '"[Object]"';
3819
3819
  }
3820
3820
  indentation += spacer;
3821
- const join53 = `,
3821
+ const join54 = `,
3822
3822
  ${indentation}`;
3823
3823
  let res = "";
3824
3824
  let separator = "";
3825
3825
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
3826
3826
  if (isTypedArrayWithEntries(value)) {
3827
- res += stringifyTypedArray(value, join53, maximumBreadth);
3827
+ res += stringifyTypedArray(value, join54, maximumBreadth);
3828
3828
  keys = keys.slice(value.length);
3829
3829
  maximumPropertiesToStringify -= value.length;
3830
- separator = join53;
3830
+ separator = join54;
3831
3831
  }
3832
3832
  if (deterministic) {
3833
3833
  keys = sort(keys, comparator);
@@ -3838,13 +3838,13 @@ ${indentation}`;
3838
3838
  const tmp = stringifyIndent(key3, value[key3], stack, spacer, indentation);
3839
3839
  if (tmp !== void 0) {
3840
3840
  res += `${separator}${strEscape(key3)}: ${tmp}`;
3841
- separator = join53;
3841
+ separator = join54;
3842
3842
  }
3843
3843
  }
3844
3844
  if (keyLength > maximumBreadth) {
3845
3845
  const removedKeys = keyLength - maximumBreadth;
3846
3846
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
3847
- separator = join53;
3847
+ separator = join54;
3848
3848
  }
3849
3849
  if (separator !== "") {
3850
3850
  res = `
@@ -11680,6 +11680,7 @@ var init_from_events = __esm({
11680
11680
  // ../engine/dist/transcript/raw.js
11681
11681
  var raw_exports = {};
11682
11682
  __export(raw_exports, {
11683
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
11683
11684
  archiveRawSession: () => archiveRawSession,
11684
11685
  buildRawContainer: () => buildRawContainer,
11685
11686
  containerSrcHash: () => containerSrcHash,
@@ -11979,11 +11980,11 @@ async function archiveRawSession(store2, sessionId) {
11979
11980
  return await store2.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
11980
11981
  }
11981
11982
  function detectForkPredecessor(sessionPath) {
11982
- const { readFileSync: readFileSync44, readdirSync: readdirSync34 } = requireFs();
11983
- const { dirname: dirname21, join: join53, basename: basename24 } = requirePath();
11983
+ const { readFileSync: readFileSync45, readdirSync: readdirSync34 } = requireFs();
11984
+ const { dirname: dirname21, join: join54, basename: basename24 } = requirePath();
11984
11985
  let text;
11985
11986
  try {
11986
- text = readFileSync44(sessionPath, "utf-8");
11987
+ text = readFileSync45(sessionPath, "utf-8");
11987
11988
  } catch {
11988
11989
  return null;
11989
11990
  }
@@ -12017,7 +12018,7 @@ function detectForkPredecessor(sessionPath) {
12017
12018
  if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
12018
12019
  continue;
12019
12020
  try {
12020
- if (readFileSync44(join53(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
12021
+ if (readFileSync45(join54(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
12021
12022
  return basename24(f, ".jsonl");
12022
12023
  }
12023
12024
  } catch {
@@ -12031,7 +12032,7 @@ function requireFs() {
12031
12032
  function requirePath() {
12032
12033
  return pathMod;
12033
12034
  }
12034
- var MARKER_QUOTE;
12035
+ var RAW_PARSE_MAX_BYTES, MARKER_QUOTE;
12035
12036
  var init_raw = __esm({
12036
12037
  "../engine/dist/transcript/raw.js"() {
12037
12038
  "use strict";
@@ -12039,6 +12040,7 @@ var init_raw = __esm({
12039
12040
  init_claude2();
12040
12041
  init_codex2();
12041
12042
  init_from_events();
12043
+ RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
12042
12044
  MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
12043
12045
  }
12044
12046
  });
@@ -19734,6 +19736,11 @@ function markSynced(server, rows) {
19734
19736
  base2.s = s;
19735
19737
  }
19736
19738
  if (r.hash !== void 0) base2.h = r.hash;
19739
+ const completesChunked = cursorFromCaller && r.chunkFinal === true && prev?.k === 1;
19740
+ if (cursorFromCaller && (r.full || completesChunked) && o > 0) base2.F = o;
19741
+ else if (prev?.F !== void 0) base2.F = prev.F;
19742
+ if (cursorFromCaller && r.chunkedHead) base2.k = 1;
19743
+ else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base2.k = 1;
19737
19744
  forServer[r.id] = base2;
19738
19745
  }
19739
19746
  persist(data);
@@ -19790,6 +19797,14 @@ function markFieldFullPassDone(server, field) {
19790
19797
  delete st.force[field.name];
19791
19798
  persistReconcile(data);
19792
19799
  }
19800
+ function forgetFullCoverage(server, sessionId) {
19801
+ const data = load();
19802
+ const row = data[server]?.[sessionId];
19803
+ if (!row || typeof row !== "object") return;
19804
+ delete row.F;
19805
+ delete row.h;
19806
+ persist(data);
19807
+ }
19793
19808
  function markFullResync(server, sessionId) {
19794
19809
  const data = load();
19795
19810
  const forServer = data[server];
@@ -19798,7 +19813,11 @@ function markFullResync(server, sessionId) {
19798
19813
  delete forServer[sessionId];
19799
19814
  persist(data);
19800
19815
  }
19801
- function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19816
+ function fullCoveredOffset(row) {
19817
+ if (row.F !== void 0) return row.F;
19818
+ return row.h ? row.s ?? 0 : 0;
19819
+ }
19820
+ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly, nowMs = Date.now()) {
19802
19821
  if (!row) return "full";
19803
19822
  if (row.v < extractorVersion) return "full";
19804
19823
  const offset = row.o ?? 0;
@@ -19806,10 +19825,13 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19806
19825
  if (process.env.CHAT_RECALL_TAIL_APPEND !== "0" && isAppendOnly && fileSize > 0 && offset > 0 && fileSize > (row.s ?? 0)) {
19807
19826
  return "append";
19808
19827
  }
19828
+ if (isAppendOnly && offset > 0 && fileSize > 0 && fileSize === (row.s ?? 0) && !row.k && fullCoveredOffset(row) < offset && nowMs - Math.floor(mtime) >= SETTLE_QUIET_MS) {
19829
+ return "full";
19830
+ }
19809
19831
  if (row.m >= Math.floor(mtime)) return "skip";
19810
19832
  return "full";
19811
19833
  }
19812
- var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
19834
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache, SETTLE_QUIET_MS;
19813
19835
  var init_sync_ledger = __esm({
19814
19836
  "src/sync-ledger.ts"() {
19815
19837
  "use strict";
@@ -19829,6 +19851,7 @@ var init_sync_ledger = __esm({
19829
19851
  unackedCursorAttempts = 0;
19830
19852
  reconcilePath = () => join46(getDataDir(), "sync-field-reconcile.json");
19831
19853
  reconcileCache = null;
19854
+ SETTLE_QUIET_MS = 2 * 60 * 60 * 1e3;
19832
19855
  }
19833
19856
  });
19834
19857
 
@@ -19974,7 +19997,7 @@ var init_http = __esm({
19974
19997
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19975
19998
  } catch {
19976
19999
  try {
19977
- if (true) CLI_VERSION = "0.7.17";
20000
+ if (true) CLI_VERSION = "0.7.18";
19978
20001
  } catch {
19979
20002
  }
19980
20003
  }
@@ -20472,7 +20495,7 @@ var init_telemetry = __esm({
20472
20495
  init_sync_client();
20473
20496
  cliVersion2 = () => {
20474
20497
  try {
20475
- return true ? "0.7.17" : "dev";
20498
+ return true ? "0.7.18" : "dev";
20476
20499
  } catch {
20477
20500
  return "dev";
20478
20501
  }
@@ -20482,6 +20505,150 @@ var init_telemetry = __esm({
20482
20505
  }
20483
20506
  });
20484
20507
 
20508
+ // src/raw-archive-parts.ts
20509
+ import { createReadStream as createReadStream4, createWriteStream as createWriteStream2, openSync as openSync10, readSync as readSync8, closeSync as closeSync10, rmSync as rmSync5, statSync as statSync42 } from "node:fs";
20510
+ import { tmpdir as tmpdir3 } from "node:os";
20511
+ import { join as join48 } from "node:path";
20512
+ import { randomBytes } from "node:crypto";
20513
+ import { createGzip } from "node:zlib";
20514
+ import { once } from "node:events";
20515
+ import { readFileSync as readFileSync37 } from "node:fs";
20516
+ async function* fileLines(path2) {
20517
+ let rest = "";
20518
+ const decoder = new TextDecoder("utf-8");
20519
+ for await (const chunk of createReadStream4(path2, { highWaterMark: 1024 * 1024 })) {
20520
+ rest += decoder.decode(chunk, { stream: true });
20521
+ let nl = rest.indexOf("\n");
20522
+ while (nl >= 0) {
20523
+ yield rest.slice(0, nl + 1);
20524
+ rest = rest.slice(nl + 1);
20525
+ nl = rest.indexOf("\n");
20526
+ }
20527
+ }
20528
+ rest += decoder.decode();
20529
+ if (rest) yield rest;
20530
+ }
20531
+ function* textLines(text) {
20532
+ let at = 0;
20533
+ while (at < text.length) {
20534
+ const nl = text.indexOf("\n", at);
20535
+ const end = nl < 0 ? text.length : nl + 1;
20536
+ yield text.slice(at, end);
20537
+ at = end;
20538
+ }
20539
+ }
20540
+ async function writeArchiveFile(source, meta, redact, dir = tmpdir3()) {
20541
+ const path2 = join48(dir, `cr-archive-${randomBytes(8).toString("hex")}.json.gz`);
20542
+ const gzip = createGzip({ level: 6 });
20543
+ const out = createWriteStream2(path2, { mode: 384 });
20544
+ const done = once(out, "finish");
20545
+ gzip.pipe(out);
20546
+ let size = 0;
20547
+ const write = async (s) => {
20548
+ size += Buffer.byteLength(s);
20549
+ if (!gzip.write(s)) await once(gzip, "drain");
20550
+ };
20551
+ try {
20552
+ await write(`{"v":1,"tool":${JSON.stringify(meta.tool)},"mtime":${Math.floor(meta.mtime)},"files":[`);
20553
+ for (const [i, f] of source.files.entries()) {
20554
+ await write(`${i ? "," : ""}{"name":${JSON.stringify(f.name)},"text":"`);
20555
+ const whole = () => source.kind === "memory" ? f.text : readFileSync37(f.path, "utf-8");
20556
+ if (!f.name.endsWith(".jsonl")) {
20557
+ await write(escapeJson(redact(whole())));
20558
+ } else {
20559
+ const lines = source.kind === "memory" ? textLines(f.text) : fileLines(f.path);
20560
+ for await (const line of lines) {
20561
+ const nl = line.endsWith("\n");
20562
+ const body2 = nl ? line.slice(0, -1) : line;
20563
+ await write(escapeJson((body2 ? redactJsonLine(body2, redact) : body2) + (nl ? "\n" : "")));
20564
+ }
20565
+ }
20566
+ await write('"}');
20567
+ }
20568
+ await write("]}");
20569
+ gzip.end();
20570
+ await done;
20571
+ } catch (err) {
20572
+ gzip.destroy();
20573
+ out.destroy();
20574
+ rmSync5(path2, { force: true });
20575
+ throw err;
20576
+ }
20577
+ return { path: path2, gzSize: statSync42(path2).size, size };
20578
+ }
20579
+ async function putPart(url, body2) {
20580
+ let last;
20581
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
20582
+ const ac = new AbortController();
20583
+ const timer = setTimeout(() => ac.abort(), PART_TIMEOUT_MS);
20584
+ try {
20585
+ const r = await fetch(url, { method: "PUT", body: body2, signal: ac.signal });
20586
+ const etag = r.headers.get("etag");
20587
+ if (r.ok && etag) return etag;
20588
+ last = new Error(`part PUT answered ${r.status}`);
20589
+ if (r.status === 403 || r.status === 404) break;
20590
+ } catch (err) {
20591
+ last = err;
20592
+ } finally {
20593
+ clearTimeout(timer);
20594
+ }
20595
+ }
20596
+ throw last instanceof Error ? last : new Error(String(last));
20597
+ }
20598
+ async function uploadArchiveFile(base2, token, args) {
20599
+ const headers = { "content-type": "application/json" };
20600
+ if (token) headers.authorization = `Bearer ${token}`;
20601
+ const post = async (path2, body2) => {
20602
+ const r = await fetch(`${base2}/api/sync/raw-upload/${path2}`, { method: "POST", headers, body: JSON.stringify(body2) });
20603
+ const json = await r.json().catch(() => ({}));
20604
+ return { status: r.status, json };
20605
+ };
20606
+ const start = await post("start", {
20607
+ session_id: args.sessionId,
20608
+ tool: args.tool,
20609
+ mtime: Math.floor(args.mtime),
20610
+ size: args.file.size,
20611
+ gz_size: args.file.gzSize,
20612
+ project_id: args.projectId,
20613
+ project_path: args.projectPath
20614
+ });
20615
+ if (start.status === 501 || start.status === 404) return "unsupported";
20616
+ if (start.status !== 200) throw new Error(`raw upload start answered ${start.status}: ${start.json.error ?? ""}`);
20617
+ if (start.json.result === "unchanged" || start.json.result === "shrink-protected") return start.json.result;
20618
+ const urls = start.json.urls;
20619
+ const partBytes = Number(start.json.part_bytes);
20620
+ const parts = [];
20621
+ const fd = openSync10(args.file.path, "r");
20622
+ try {
20623
+ for (const [i, url] of urls.entries()) {
20624
+ const len = Math.min(partBytes, args.file.gzSize - i * partBytes);
20625
+ const buf = Buffer.allocUnsafe(len);
20626
+ let got = 0;
20627
+ while (got < len) {
20628
+ const n = readSync8(fd, buf, got, len - got, i * partBytes + got);
20629
+ if (n <= 0) break;
20630
+ got += n;
20631
+ }
20632
+ parts.push({ part_number: i + 1, etag: await putPart(url, buf.subarray(0, got)) });
20633
+ }
20634
+ } finally {
20635
+ closeSync10(fd);
20636
+ }
20637
+ const done = await post("complete", { session_id: args.sessionId, upload_id: start.json.upload_id, parts });
20638
+ if (done.status !== 200) throw new Error(`raw upload complete answered ${done.status}: ${done.json.error ?? ""}`);
20639
+ return done.json.result;
20640
+ }
20641
+ var escapeJson, PART_TIMEOUT_MS, PART_ATTEMPTS;
20642
+ var init_raw_archive_parts = __esm({
20643
+ "src/raw-archive-parts.ts"() {
20644
+ "use strict";
20645
+ init_transcript();
20646
+ escapeJson = (s) => JSON.stringify(s).slice(1, -1);
20647
+ PART_TIMEOUT_MS = 12e4;
20648
+ PART_ATTEMPTS = 3;
20649
+ }
20650
+ });
20651
+
20485
20652
  // src/server-probe.ts
20486
20653
  var server_probe_exports = {};
20487
20654
  __export(server_probe_exports, {
@@ -20643,11 +20810,11 @@ __export(auto_update_exports, {
20643
20810
  tarballUrl: () => tarballUrl
20644
20811
  });
20645
20812
  import { execSync as execSync4, execFileSync as execFileSync3 } from "node:child_process";
20646
- import { readFileSync as readFileSync37, existsSync as existsSync45 } from "node:fs";
20813
+ import { readFileSync as readFileSync38, existsSync as existsSync45 } from "node:fs";
20647
20814
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20648
- import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync42, mkdirSync as mkdirSync12 } from "node:fs";
20649
- import { join as join48, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
20650
- import { tmpdir as tmpdir3, homedir as homedir17 } from "node:os";
20815
+ import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync31, statSync as statSync43, mkdirSync as mkdirSync12 } from "node:fs";
20816
+ import { join as join49, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
20817
+ import { tmpdir as tmpdir4, homedir as homedir17 } from "node:os";
20651
20818
  import { createHash as createHash10 } from "node:crypto";
20652
20819
  function compareVersions(a, b) {
20653
20820
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -20671,7 +20838,7 @@ function newerOf(a, b) {
20671
20838
  function installedVersion() {
20672
20839
  try {
20673
20840
  const here = dirname19(fileURLToPath2(import.meta.url));
20674
- const raw = readFileSync37(join48(here, "..", "package.json"), "utf-8");
20841
+ const raw = readFileSync38(join49(here, "..", "package.json"), "utf-8");
20675
20842
  return JSON.parse(raw).version || null;
20676
20843
  } catch {
20677
20844
  return null;
@@ -20684,7 +20851,7 @@ function planAutoUpdate(base2, caps, ownVersion2, flag) {
20684
20851
  if (compareVersions(rel.version, ownVersion2) <= 0) return { update: false, reason: `already current (own ${ownVersion2} >= server ${rel.version})` };
20685
20852
  return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base2, rel.version), fallbackUrl: tarballUrl(base2), sha256: rel.sha256, from: ownVersion2, to: rel.version };
20686
20853
  }
20687
- function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20854
+ function sweepStaleStaging(root = tmpdir4(), now = Date.now()) {
20688
20855
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
20689
20856
  const COLD_MS = 60 * 60 * 1e3;
20690
20857
  let removed = 0;
@@ -20696,13 +20863,13 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20696
20863
  }
20697
20864
  for (const name of entries) {
20698
20865
  if (!MKDTEMP_SUFFIX.test(name)) continue;
20699
- const dir = join48(root, name);
20866
+ const dir = join49(root, name);
20700
20867
  try {
20701
- const st = statSync42(dir);
20868
+ const st = statSync43(dir);
20702
20869
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20703
20870
  const inside = readdirSync31(dir);
20704
20871
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20705
- rmSync5(dir, { recursive: true, force: true });
20872
+ rmSync6(dir, { recursive: true, force: true });
20706
20873
  removed++;
20707
20874
  } catch {
20708
20875
  }
@@ -20725,14 +20892,14 @@ async function executeAutoUpdate(plan, deps) {
20725
20892
  }
20726
20893
  const got = createHash10("sha256").update(buf).digest("hex");
20727
20894
  if (got !== plan.sha256) return { updated: false, reason: `checksum mismatch (expected ${plan.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026) \u2014 NOT installing` };
20728
- const dir = mkdtempSync3(join48(tmpdir3(), "cr-update-"));
20895
+ const dir = mkdtempSync3(join49(tmpdir4(), "cr-update-"));
20729
20896
  const discardStaging = () => {
20730
20897
  try {
20731
- rmSync5(dir, { recursive: true, force: true });
20898
+ rmSync6(dir, { recursive: true, force: true });
20732
20899
  } catch {
20733
20900
  }
20734
20901
  };
20735
- const tgz = join48(dir, "chat-recall.tgz");
20902
+ const tgz = join49(dir, "chat-recall.tgz");
20736
20903
  writeFileSync9(tgz, buf);
20737
20904
  if (deps.stopping?.()) {
20738
20905
  discardStaging();
@@ -20812,7 +20979,7 @@ function realInstall(tgz) {
20812
20979
  try {
20813
20980
  npmInstall(npm, ["install", "-g", tgz]);
20814
20981
  } catch {
20815
- npmInstall(npm, ["install", "-g", "--prefix", join48(homedir17(), ".local"), tgz]);
20982
+ npmInstall(npm, ["install", "-g", "--prefix", join49(homedir17(), ".local"), tgz]);
20816
20983
  }
20817
20984
  }
20818
20985
  function acquireUpdateLock() {
@@ -20829,12 +20996,12 @@ function realRestart(platform2) {
20829
20996
  if (c) execSync4(c.cmd, { stdio: "ignore", ...c.shell ? { shell: c.shell } : {} });
20830
20997
  }
20831
20998
  function updateStatePath() {
20832
- const dir = process.env.CHAT_RECALL_DATA_DIR || join48(homedir17(), ".chat-recall");
20833
- return join48(dir, "auto-update-state.json");
20999
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join49(homedir17(), ".chat-recall");
21000
+ return join49(dir, "auto-update-state.json");
20834
21001
  }
20835
21002
  function readUpdateState() {
20836
21003
  try {
20837
- return JSON.parse(readFileSync37(updateStatePath(), "utf8"));
21004
+ return JSON.parse(readFileSync38(updateStatePath(), "utf8"));
20838
21005
  } catch {
20839
21006
  return {};
20840
21007
  }
@@ -20975,9 +21142,9 @@ __export(sync_client_exports, {
20975
21142
  syncIncremental: () => syncIncremental,
20976
21143
  syncSessions: () => syncSessions
20977
21144
  });
20978
- import { join as join49, dirname as dirname20, basename as basename22 } from "node:path";
20979
- import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync43 } from "node:fs";
20980
- import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
21145
+ import { join as join50, dirname as dirname20, basename as basename22 } from "node:path";
21146
+ import { readFileSync as readFileSync39, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync7, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync44 } from "node:fs";
21147
+ import { tmpdir as tmpdir5, hostname as hostname2 } from "node:os";
20981
21148
  import { createHash as createHash11 } from "node:crypto";
20982
21149
  import { fileURLToPath as fileURLToPath3 } from "node:url";
20983
21150
  import { gzipSync as gzipSync3 } from "zlib";
@@ -21031,7 +21198,7 @@ function loadAllCredentials() {
21031
21198
  const fromEnv = envCredentials();
21032
21199
  if (fromEnv) return [{ serverUrl: fromEnv.serverUrl, token: fromEnv.token }];
21033
21200
  try {
21034
- const parsed = JSON.parse(readFileSync38(credPath(), "utf-8"));
21201
+ const parsed = JSON.parse(readFileSync39(credPath(), "utf-8"));
21035
21202
  if (Array.isArray(parsed?.targets)) return parsed.targets.filter((t) => t?.serverUrl).map((t) => ({ serverUrl: t.serverUrl, token: t.token || "" }));
21036
21203
  if (parsed?.serverUrl) return [{ serverUrl: parsed.serverUrl, token: parsed.token || "" }];
21037
21204
  } catch {
@@ -21093,11 +21260,11 @@ function _resetTenantSecurityConfigCache() {
21093
21260
  _securityConfigCache.clear();
21094
21261
  }
21095
21262
  function syncConfigCachePath() {
21096
- return join49(getDataDir(), "sync-config-cache.json");
21263
+ return join50(getDataDir(), "sync-config-cache.json");
21097
21264
  }
21098
21265
  function readPersistedSyncConfigs() {
21099
21266
  try {
21100
- return JSON.parse(readFileSync38(syncConfigCachePath(), "utf-8"));
21267
+ return JSON.parse(readFileSync39(syncConfigCachePath(), "utf-8"));
21101
21268
  } catch {
21102
21269
  return {};
21103
21270
  }
@@ -21375,7 +21542,7 @@ async function reconcileFields(opts = {}) {
21375
21542
  }
21376
21543
  return { sessions: fullRefs().length, scanned, pushed, absent, perTarget };
21377
21544
  }
21378
- function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now()) {
21545
+ function reapStaleScanDirs(dir = tmpdir5(), maxAgeMs = 6 * 36e5, now = Date.now()) {
21379
21546
  let reaped = 0;
21380
21547
  let names;
21381
21548
  try {
@@ -21385,10 +21552,10 @@ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now(
21385
21552
  }
21386
21553
  for (const name of names) {
21387
21554
  if (!name.startsWith("cr-batchscan-")) continue;
21388
- const p = join49(dir, name);
21555
+ const p = join50(dir, name);
21389
21556
  try {
21390
- if (now - statSync43(p).mtimeMs < maxAgeMs) continue;
21391
- rmSync6(p, { recursive: true, force: true });
21557
+ if (now - statSync44(p).mtimeMs < maxAgeMs) continue;
21558
+ rmSync7(p, { recursive: true, force: true });
21392
21559
  reaped++;
21393
21560
  } catch {
21394
21561
  }
@@ -21402,7 +21569,7 @@ function batchScanExternal(items, opts = {}) {
21402
21569
  let scanned = 0, slices = 0, scanMs = 0;
21403
21570
  if (items.length === 0) return { findings, scanned, slices, scanMs };
21404
21571
  reapStaleScanDirs();
21405
- let scanDir = mkdtempSync4(join49(tmpdir4(), "cr-batchscan-"));
21572
+ let scanDir = mkdtempSync4(join50(tmpdir5(), "cr-batchscan-"));
21406
21573
  let safeToId = /* @__PURE__ */ new Map();
21407
21574
  let bytesInDir = 0;
21408
21575
  const flushSlice = () => {
@@ -21420,7 +21587,7 @@ function batchScanExternal(items, opts = {}) {
21420
21587
  slices++;
21421
21588
  }
21422
21589
  try {
21423
- rmSync6(scanDir, { recursive: true, force: true });
21590
+ rmSync7(scanDir, { recursive: true, force: true });
21424
21591
  } catch {
21425
21592
  }
21426
21593
  safeToId = /* @__PURE__ */ new Map();
@@ -21433,19 +21600,19 @@ function batchScanExternal(items, opts = {}) {
21433
21600
  if (raw === null) continue;
21434
21601
  const safe = item.id.replace(/[^A-Za-z0-9_-]/g, "");
21435
21602
  safeToId.set(safe, item.id);
21436
- writeFileSync10(join49(scanDir, `${safe}.txt`), raw);
21603
+ writeFileSync10(join50(scanDir, `${safe}.txt`), raw);
21437
21604
  bytesInDir += Buffer.byteLength(raw);
21438
21605
  } catch {
21439
21606
  }
21440
21607
  if (bytesInDir >= maxBytes) {
21441
21608
  flushSlice();
21442
- scanDir = mkdtempSync4(join49(tmpdir4(), "cr-batchscan-"));
21609
+ scanDir = mkdtempSync4(join50(tmpdir5(), "cr-batchscan-"));
21443
21610
  }
21444
21611
  }
21445
21612
  flushSlice();
21446
21613
  } finally {
21447
21614
  try {
21448
- rmSync6(scanDir, { recursive: true, force: true });
21615
+ rmSync7(scanDir, { recursive: true, force: true });
21449
21616
  } catch {
21450
21617
  }
21451
21618
  }
@@ -21689,12 +21856,13 @@ async function syncToTargetInScope(cred, opts = {}) {
21689
21856
  markSynced(base2, rows.map((r) => {
21690
21857
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
21691
21858
  const hash = shippedHash.get(r.session_id);
21692
- return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true } : { id: r.session_id, mtime: r.mtime, hash };
21859
+ 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 };
21693
21860
  }));
21694
21861
  } catch {
21695
21862
  }
21696
21863
  });
21697
21864
  const appendResults = { uploaded: 0, fullResyncNeeded: /* @__PURE__ */ new Set() };
21865
+ const pendingArchives = [];
21698
21866
  const postAppend = async (convs) => {
21699
21867
  const headers = { "content-type": "application/json" };
21700
21868
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -21729,8 +21897,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21729
21897
  offset: c.from_offset,
21730
21898
  size: c.from_offset,
21731
21899
  // size = new offset (file grew to here)
21732
- acked: true
21900
+ acked: true,
21733
21901
  // server merged this tail
21902
+ chunkFinal: c.final === true
21903
+ // completes a chunked full sync
21734
21904
  })));
21735
21905
  } catch {
21736
21906
  }
@@ -21762,9 +21932,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21762
21932
  const mtime = conv.mtime;
21763
21933
  try {
21764
21934
  if (staged || !guarded) {
21765
- markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21935
+ markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true }]);
21766
21936
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21767
- markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
21937
+ markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true }]);
21768
21938
  } else {
21769
21939
  markSynced(base2, [{ id, mtime }]);
21770
21940
  }
@@ -21973,6 +22143,14 @@ async function syncToTargetInScope(cred, opts = {}) {
21973
22143
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
21974
22144
  if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
21975
22145
  else await convBatch.add(built.conv);
22146
+ if (built.archive) {
22147
+ pendingArchives.push({
22148
+ id: ref.prefixedId,
22149
+ ...built.archive,
22150
+ projectId: built.conv.project_id,
22151
+ projectPath: built.conv.project_path
22152
+ });
22153
+ }
21976
22154
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
21977
22155
  if (upload.sessionMeta) {
21978
22156
  await accumulateKg(extractEntities(built.kgText, { projectPath: built.projectPath, sourceType: "session", sessionId: ref.prefixedId, validFrom: ref.mtime ? new Date(ref.mtime).toISOString().slice(0, 10) : void 0 }), ref.prefixedId);
@@ -22125,6 +22303,30 @@ async function syncToTargetInScope(cred, opts = {}) {
22125
22303
  await kgTripleBatch.drain();
22126
22304
  await findingsBatch.drain();
22127
22305
  await drainInflight();
22306
+ let archivesUnsupported = false;
22307
+ for (const a of pendingArchives) {
22308
+ try {
22309
+ if (!archivesUnsupported) {
22310
+ const r = await uploadArchiveFile(base2, cred.token, {
22311
+ sessionId: a.id,
22312
+ tool: a.tool,
22313
+ mtime: a.mtime,
22314
+ projectId: a.projectId,
22315
+ projectPath: a.projectPath,
22316
+ file: a.file
22317
+ });
22318
+ if (r === "unsupported") archivesUnsupported = true;
22319
+ }
22320
+ } catch (err) {
22321
+ console.error(`[sync] raw archive for ${a.id} not uploaded (tried again when the session is next quiet): ${err instanceof Error ? err.message : err}`);
22322
+ try {
22323
+ forgetFullCoverage(base2, a.id);
22324
+ } catch {
22325
+ }
22326
+ } finally {
22327
+ rmSync7(a.file.path, { force: true });
22328
+ }
22329
+ }
22128
22330
  {
22129
22331
  const sentHashes = loadInventoryHashes(base2);
22130
22332
  const entries = [];
@@ -22230,6 +22432,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22230
22432
  let includeRaw = opts.includeRaw !== false;
22231
22433
  let includeMeta = opts.includeMeta !== false;
22232
22434
  let srcHash;
22435
+ const wantRaw = opts.includeRaw !== false;
22233
22436
  const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
22234
22437
  let headOffsetEnd = 0;
22235
22438
  let oversizedFindings = null;
@@ -22398,7 +22601,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22398
22601
  } else if (opts.scanSecrets) {
22399
22602
  let tmp = null;
22400
22603
  try {
22401
- tmp = join49(tmpdir4(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
22604
+ tmp = join50(tmpdir5(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
22402
22605
  writeFileSync10(tmp, rawText);
22403
22606
  for (const f of scanFileForSecrets(tmp, { verifyOnly: opts.verifySecrets, tenantRules: opts.tenantRules })) {
22404
22607
  const verifiedAt = f.verified ? (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -22450,6 +22653,25 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22450
22653
  }
22451
22654
  const envTexts = envelope.messages.filter((m) => m.content?.trim());
22452
22655
  const redactedText = envTexts.map((m) => m.content).join("\n");
22656
+ let archive;
22657
+ if (wantRaw && !raw_b64) {
22658
+ try {
22659
+ const redact = (t) => redactSecrets(t, { force: true, count });
22660
+ let source = null;
22661
+ let archiveMtime = Math.floor(ref.mtime) || 0;
22662
+ let archiveTool = ref.toolId;
22663
+ if (container) {
22664
+ source = { kind: "memory", files: container.files };
22665
+ archiveMtime = container.mtime;
22666
+ archiveTool = container.tool;
22667
+ } else if (oversized) {
22668
+ source = diskArchiveSource(ref);
22669
+ }
22670
+ if (source) archive = { file: await writeArchiveFile(source, { tool: archiveTool, mtime: archiveMtime }, redact), tool: archiveTool, mtime: archiveMtime };
22671
+ } catch (err) {
22672
+ console.error(`[sync] raw archive for ${ref.prefixedId} not written: ${err instanceof Error ? err.message : err}`);
22673
+ }
22674
+ }
22453
22675
  return {
22454
22676
  conv: {
22455
22677
  session_id: ref.prefixedId,
@@ -22481,7 +22703,17 @@ async function buildConversationSync(ref, mtime, opts = {}) {
22481
22703
  redactions: count.redactions,
22482
22704
  findings,
22483
22705
  scanMs,
22484
- srcHash
22706
+ srcHash,
22707
+ archive
22708
+ };
22709
+ }
22710
+ function diskArchiveSource(ref) {
22711
+ if (ref.toolId !== "claude") return null;
22712
+ const groups = resolveSessionContentGroups(ref.rawId);
22713
+ if (groups.length === 0 || groups.some((g) => g.paths.length !== 1)) return null;
22714
+ return {
22715
+ kind: "disk",
22716
+ files: groups.map((g) => ({ name: g.name === "main" ? `${ref.rawId}.jsonl` : g.name, path: g.paths[0] }))
22485
22717
  };
22486
22718
  }
22487
22719
  function parseTailMessages(tool, text) {
@@ -22637,14 +22869,15 @@ var init_sync_client = __esm({
22637
22869
  init_telemetry();
22638
22870
  init_collector_health();
22639
22871
  init_live_session_scan();
22872
+ init_raw_archive_parts();
22640
22873
  init_backends();
22641
22874
  ownVersion = "0.0.0";
22642
22875
  try {
22643
22876
  const dir = dirname20(fileURLToPath3(import.meta.url));
22644
- ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22877
+ ownVersion = JSON.parse(readFileSync39(join50(dir, "../package.json"), "utf-8")).version || ownVersion;
22645
22878
  } catch {
22646
22879
  try {
22647
- if (true) ownVersion = "0.7.17";
22880
+ if (true) ownVersion = "0.7.18";
22648
22881
  } catch {
22649
22882
  }
22650
22883
  }
@@ -22656,7 +22889,7 @@ var init_sync_client = __esm({
22656
22889
  ]);
22657
22890
  walkProgressForUpload = null;
22658
22891
  lastServerPackSpec = null;
22659
- credPath = () => join49(getDataDir(), "credentials.json");
22892
+ credPath = () => join50(getDataDir(), "credentials.json");
22660
22893
  _securityConfigCache = /* @__PURE__ */ new Map();
22661
22894
  SECURITY_CONFIG_TTL_MS = 5 * 60 * 1e3;
22662
22895
  _syncConfigCache = /* @__PURE__ */ new Map();
@@ -22685,7 +22918,7 @@ __export(client_events_exports, {
22685
22918
  isTelemetryEnabled: () => isTelemetryEnabled,
22686
22919
  reportClientEvent: () => reportClientEvent2
22687
22920
  });
22688
- import { readFileSync as readFileSync39 } from "node:fs";
22921
+ import { readFileSync as readFileSync40 } from "node:fs";
22689
22922
  function scrub(msg) {
22690
22923
  if (!msg) return "";
22691
22924
  const noHome = msg.replace(/\/(?:home|Users)\/[^/\s]+/g, "~");
@@ -22738,7 +22971,7 @@ var init_client_events = __esm({
22738
22971
  init_secret_redactor();
22739
22972
  CLI_VERSION2 = "0.0.0";
22740
22973
  try {
22741
- CLI_VERSION2 = JSON.parse(readFileSync39(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
22974
+ CLI_VERSION2 = JSON.parse(readFileSync40(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
22742
22975
  } catch {
22743
22976
  }
22744
22977
  }
@@ -22752,15 +22985,15 @@ __export(update_notice_exports, {
22752
22985
  refreshUpdateCheck: () => refreshUpdateCheck,
22753
22986
  updateNotice: () => updateNotice2
22754
22987
  });
22755
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync11 } from "node:fs";
22756
- import { join as join50 } from "node:path";
22988
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync11 } from "node:fs";
22989
+ import { join as join51 } from "node:path";
22757
22990
  function newestKnown(c) {
22758
22991
  const npm = c.npmVersion ?? "0.0.0";
22759
22992
  return compareVersions(npm, c.serverVersion) > 0 ? { version: npm, source: "npm" } : { version: c.serverVersion, source: "server" };
22760
22993
  }
22761
22994
  function readCheck() {
22762
22995
  try {
22763
- const c = JSON.parse(readFileSync40(checkPath(), "utf-8"));
22996
+ const c = JSON.parse(readFileSync41(checkPath(), "utf-8"));
22764
22997
  return c && typeof c.serverVersion === "string" ? c : null;
22765
22998
  } catch {
22766
22999
  return null;
@@ -22854,7 +23087,7 @@ var init_update_notice = __esm({
22854
23087
  init_http();
22855
23088
  CHECK_TTL_MS = 6 * 3600 * 1e3;
22856
23089
  NPM_LATEST_URL = "https://registry.npmjs.org/chat-recall";
22857
- checkPath = () => join50(getDataDir(), "update-check.json");
23090
+ checkPath = () => join51(getDataDir(), "update-check.json");
22858
23091
  printed = false;
22859
23092
  }
22860
23093
  });
@@ -22926,15 +23159,15 @@ __export(service_installer_exports, {
22926
23159
  uninstallService: () => uninstallService
22927
23160
  });
22928
23161
  import { execSync as execSync5 } from "child_process";
22929
- import { existsSync as existsSync46, mkdirSync as mkdirSync14, readFileSync as readFileSync41, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
23162
+ import { existsSync as existsSync46, mkdirSync as mkdirSync14, readFileSync as readFileSync42, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "fs";
22930
23163
  import { homedir as homedir18 } from "os";
22931
- import { join as join51 } from "path";
23164
+ import { join as join52 } from "path";
22932
23165
  import { fileURLToPath as fileURLToPath4 } from "url";
22933
23166
  function resolveDaemonPaths() {
22934
23167
  const watchJs = fileURLToPath4(new URL("./watch.js", import.meta.url));
22935
23168
  const node = process.execPath;
22936
- const dataDir = join51(homedir18(), ".chat-recall");
22937
- const logFile = join51(dataDir, "watch.log");
23169
+ const dataDir = join52(homedir18(), ".chat-recall");
23170
+ const logFile = join52(dataDir, "watch.log");
22938
23171
  return { watchJs, node, logFile, dataDir };
22939
23172
  }
22940
23173
  function tryCmd(cmd) {
@@ -22961,12 +23194,12 @@ function refreshServiceDefinition() {
22961
23194
  if (process.platform !== "linux") return false;
22962
23195
  try {
22963
23196
  const { watchJs, node, logFile } = resolveDaemonPaths();
22964
- const unitPath = join51(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23197
+ const unitPath = join52(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
22965
23198
  if (!existsSync46(unitPath)) return false;
22966
- const current = readFileSync41(unitPath, "utf8");
23199
+ const current = readFileSync42(unitPath, "utf8");
22967
23200
  if (!current.includes(watchJs)) return false;
22968
23201
  const wanted = renderSystemdUnit(watchJs, node, logFile);
22969
- if (readFileSync41(unitPath, "utf8") === wanted) return false;
23202
+ if (readFileSync42(unitPath, "utf8") === wanted) return false;
22970
23203
  writeFileSync12(unitPath, wanted);
22971
23204
  execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
22972
23205
  return true;
@@ -22992,8 +23225,8 @@ function installService() {
22992
23225
  );
22993
23226
  }
22994
23227
  function installSystemd(watchJs, node, logFile) {
22995
- const unitDir = join51(homedir18(), ".config", "systemd", "user");
22996
- const unitPath = join51(unitDir, "chat-recall-watch.service");
23228
+ const unitDir = join52(homedir18(), ".config", "systemd", "user");
23229
+ const unitPath = join52(unitDir, "chat-recall-watch.service");
22997
23230
  const unit = renderSystemdUnit(watchJs, node, logFile);
22998
23231
  mkdirSync14(unitDir, { recursive: true });
22999
23232
  writeFileSync12(unitPath, unit);
@@ -23072,8 +23305,8 @@ function renderSystemdUnit(watchJs, node, logFile) {
23072
23305
  }
23073
23306
  function installLaunchd(watchJs, node, logFile) {
23074
23307
  const label = "com.chat-recall.watch";
23075
- const plistDir = join51(homedir18(), "Library", "LaunchAgents");
23076
- const plistPath = join51(plistDir, `${label}.plist`);
23308
+ const plistDir = join52(homedir18(), "Library", "LaunchAgents");
23309
+ const plistPath = join52(plistDir, `${label}.plist`);
23077
23310
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
23078
23311
  mkdirSync14(plistDir, { recursive: true });
23079
23312
  writeFileSync12(plistPath, plist);
@@ -23129,9 +23362,9 @@ function uninstallService() {
23129
23362
  removed = true;
23130
23363
  } catch {
23131
23364
  }
23132
- const unitPath = join51(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23365
+ const unitPath = join52(homedir18(), ".config", "systemd", "user", "chat-recall-watch.service");
23133
23366
  if (existsSync46(unitPath)) {
23134
- rmSync7(unitPath, { force: true });
23367
+ rmSync8(unitPath, { force: true });
23135
23368
  removed = true;
23136
23369
  }
23137
23370
  try {
@@ -23141,14 +23374,14 @@ function uninstallService() {
23141
23374
  return removed;
23142
23375
  }
23143
23376
  if (process.platform === "darwin") {
23144
- const plistPath = join51(homedir18(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23377
+ const plistPath = join52(homedir18(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23145
23378
  try {
23146
23379
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
23147
23380
  } catch {
23148
23381
  }
23149
23382
  let removed = false;
23150
23383
  if (existsSync46(plistPath)) {
23151
- rmSync7(plistPath, { force: true });
23384
+ rmSync8(plistPath, { force: true });
23152
23385
  removed = true;
23153
23386
  }
23154
23387
  return removed;
@@ -23194,15 +23427,15 @@ __export(install_skills_exports, {
23194
23427
  skillsSourceDir: () => skillsSourceDir,
23195
23428
  uninstallSkills: () => uninstallSkills
23196
23429
  });
23197
- import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync44, realpathSync as realpathSync4 } from "fs";
23430
+ import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync9, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync43, statSync as statSync45, realpathSync as realpathSync4 } from "fs";
23198
23431
  import { createHash as createHash12 } from "crypto";
23199
- import { join as join52, basename as basename23 } from "path";
23432
+ import { join as join53, basename as basename23 } from "path";
23200
23433
  function skillsSourceDir() {
23201
- return join52(import.meta.dirname, "..", "skills");
23434
+ return join53(import.meta.dirname, "..", "skills");
23202
23435
  }
23203
23436
  function version() {
23204
23437
  try {
23205
- return JSON.parse(readFileSync42(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
23438
+ return JSON.parse(readFileSync43(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
23206
23439
  } catch {
23207
23440
  return "0.0.0";
23208
23441
  }
@@ -23211,9 +23444,9 @@ function skillsFingerprint() {
23211
23444
  const src = skillsSourceDir();
23212
23445
  const h = createHash12("sha256");
23213
23446
  for (const name of bundledSkillNames()) {
23214
- for (const rel of filesUnder(join52(src, name)).sort()) {
23447
+ for (const rel of filesUnder(join53(src, name)).sort()) {
23215
23448
  h.update(name + "/" + rel);
23216
- h.update(readFileSync42(join52(src, name, rel)));
23449
+ h.update(readFileSync43(join53(src, name, rel)));
23217
23450
  }
23218
23451
  }
23219
23452
  return h.digest("hex").slice(0, 12);
@@ -23223,7 +23456,7 @@ function filesUnder(dir, prefix = "") {
23223
23456
  for (const e of readdirSync33(dir, { withFileTypes: true })) {
23224
23457
  if (e.name === MARKER) continue;
23225
23458
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23226
- if (e.isDirectory()) out.push(...filesUnder(join52(dir, e.name), rel));
23459
+ if (e.isDirectory()) out.push(...filesUnder(join53(dir, e.name), rel));
23227
23460
  else out.push(rel);
23228
23461
  }
23229
23462
  return out;
@@ -23234,7 +23467,7 @@ function skillStamp() {
23234
23467
  function bundledSkillNames() {
23235
23468
  const src = skillsSourceDir();
23236
23469
  if (!existsSync47(src)) return [];
23237
- return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join52(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23470
+ return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join53(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23238
23471
  }
23239
23472
  function realPathOrSelf(p) {
23240
23473
  try {
@@ -23247,7 +23480,7 @@ function claudeSkillTargets() {
23247
23480
  const targets = [];
23248
23481
  const seen = /* @__PURE__ */ new Set();
23249
23482
  for (const home of claudeHomeDirs()) {
23250
- const dir = join52(home, "skills");
23483
+ const dir = join53(home, "skills");
23251
23484
  const key2 = realPathOrSelf(dir);
23252
23485
  if (seen.has(key2)) continue;
23253
23486
  seen.add(key2);
@@ -23258,7 +23491,7 @@ function claudeSkillTargets() {
23258
23491
  dir,
23259
23492
  // Judge each profile on ITS OWN transcripts, not the primary's: a home
23260
23493
  // with no projects/ is a directory someone made, not a Claude install.
23261
- available: existsSync47(join52(home, "projects"))
23494
+ available: existsSync47(join53(home, "projects"))
23262
23495
  });
23263
23496
  }
23264
23497
  if (targets.length === 0) {
@@ -23273,16 +23506,16 @@ function skillTargets() {
23273
23506
  // both produces "skill conflict" duplicate warnings. Every tool we support
23274
23507
  // has its own dir, so per-tool coverage is complete.
23275
23508
  ...claudeSkillTargets(),
23276
- { id: "agy", label: "Antigravity", dir: join52(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
23509
+ { id: "agy", label: "Antigravity", dir: join53(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
23277
23510
  { id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
23278
23511
  { id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
23279
23512
  // Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
23280
23513
  // it is Cursor's bundled set — read-only, never an install target.
23281
- { id: "cursor", label: "Cursor", dir: join52(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
23514
+ { id: "cursor", label: "Cursor", dir: join53(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
23282
23515
  ];
23283
23516
  }
23284
23517
  function isManaged(dir) {
23285
- return existsSync47(join52(dir, MARKER));
23518
+ return existsSync47(join53(dir, MARKER));
23286
23519
  }
23287
23520
  function installSkills(opts = {}) {
23288
23521
  const onlyAvailable = opts.onlyAvailable ?? true;
@@ -23299,15 +23532,15 @@ function installSkills(opts = {}) {
23299
23532
  continue;
23300
23533
  }
23301
23534
  for (const name of names) {
23302
- const dest = join52(t.dir, name);
23535
+ const dest = join53(t.dir, name);
23303
23536
  if (existsSync47(dest) && !isManaged(dest)) {
23304
23537
  skippedUserOwned.push(name);
23305
23538
  continue;
23306
23539
  }
23307
23540
  mkdirSync15(t.dir, { recursive: true });
23308
- rmSync8(dest, { recursive: true, force: true });
23309
- cpSync(join52(src, name), dest, { recursive: true });
23310
- writeFileSync13(join52(dest, MARKER), `${stamp}
23541
+ rmSync9(dest, { recursive: true, force: true });
23542
+ cpSync(join53(src, name), dest, { recursive: true });
23543
+ writeFileSync13(join53(dest, MARKER), `${stamp}
23311
23544
  `);
23312
23545
  installed.push(name);
23313
23546
  }
@@ -23326,9 +23559,9 @@ function uninstallSkills() {
23326
23559
  }
23327
23560
  for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
23328
23561
  if (!entry.isDirectory()) continue;
23329
- const dir = join52(t.dir, entry.name);
23562
+ const dir = join53(t.dir, entry.name);
23330
23563
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
23331
- rmSync8(dir, { recursive: true, force: true });
23564
+ rmSync9(dir, { recursive: true, force: true });
23332
23565
  removed.push(entry.name);
23333
23566
  }
23334
23567
  }
@@ -23343,10 +23576,10 @@ function skillsNeedRefresh() {
23343
23576
  for (const t of skillTargets()) {
23344
23577
  if (!t.available) continue;
23345
23578
  for (const name of names) {
23346
- const marker = join52(t.dir, name, MARKER);
23579
+ const marker = join53(t.dir, name, MARKER);
23347
23580
  if (!existsSync47(marker)) return true;
23348
23581
  try {
23349
- if (readFileSync42(marker, "utf-8").trim() !== cur) return true;
23582
+ if (readFileSync43(marker, "utf-8").trim() !== cur) return true;
23350
23583
  } catch {
23351
23584
  return true;
23352
23585
  }
@@ -23361,11 +23594,11 @@ function skillStatus() {
23361
23594
  let installed = 0;
23362
23595
  let ver = null;
23363
23596
  for (const name of names) {
23364
- const marker = join52(t.dir, name, MARKER);
23597
+ const marker = join53(t.dir, name, MARKER);
23365
23598
  if (existsSync47(marker)) {
23366
23599
  installed++;
23367
23600
  try {
23368
- ver = readFileSync42(marker, "utf-8").trim();
23601
+ ver = readFileSync43(marker, "utf-8").trim();
23369
23602
  } catch {
23370
23603
  }
23371
23604
  }
@@ -29460,9 +29693,9 @@ function openBrowser(url) {
29460
29693
  // src/mcp.ts
29461
29694
  init_client_events();
29462
29695
  init_update_notice();
29463
- import { readFileSync as readFileSync43 } from "node:fs";
29696
+ import { readFileSync as readFileSync44 } from "node:fs";
29464
29697
  setServerVersion(JSON.parse(
29465
- readFileSync43(new URL("../package.json", import.meta.url), "utf-8")
29698
+ readFileSync44(new URL("../package.json", import.meta.url), "utf-8")
29466
29699
  ).version);
29467
29700
  setEventReporter(reportClientEvent2);
29468
29701
  setIndexRunner(runIndexChild2);
@@ -29579,9 +29812,9 @@ var SYNC_TICK_MS = 3 * 6e4;
29579
29812
  function startBackgroundSync() {
29580
29813
  const tick = async (scope) => {
29581
29814
  try {
29582
- const { installedVersion: installedVersion2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
29815
+ const { installedVersion: installedVersion3 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
29583
29816
  const { installedIsNewer: installedIsNewer2, runInstalledSync: runInstalledSync2 } = await Promise.resolve().then(() => (init_sync_delegate(), sync_delegate_exports));
29584
- const installed = installedVersion2();
29817
+ const installed = installedVersion3();
29585
29818
  if (installedIsNewer2(RELAY_VERSION, installed)) {
29586
29819
  const code = await runInstalledSync2(fileURLToPath5(new URL("./cli.js", import.meta.url)));
29587
29820
  if (code !== 0) console.error(`[mcp] sync by the installed CLI ${installed} ended with ${code ?? "no exit code"}`);
@@ -29635,7 +29868,7 @@ function installCrashGuards() {
29635
29868
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29636
29869
  });
29637
29870
  }
29638
- var RELAY_VERSION = true ? "0.7.17" : "0.0.0";
29871
+ var RELAY_VERSION = true ? "0.7.18" : "0.0.0";
29639
29872
  function daemonIdleSecs() {
29640
29873
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29641
29874
  if (raw === void 0) return 900;
@@ -29731,8 +29964,25 @@ async function runDaemon() {
29731
29964
  };
29732
29965
  process.on("SIGTERM", shutdown);
29733
29966
  process.on("SIGINT", shutdown);
29967
+ const upgradeTimer = setInterval(() => {
29968
+ const installed = installedVersion2();
29969
+ if (installed && RELAY_VERSION !== "0.0.0" && installed !== RELAY_VERSION) {
29970
+ console.error(`[mcp] v${installed} is installed; daemon v${RELAY_VERSION} is leaving`);
29971
+ clearInterval(upgradeTimer);
29972
+ shutdown();
29973
+ }
29974
+ }, 3e4);
29975
+ upgradeTimer.unref();
29734
29976
  return true;
29735
29977
  }
29978
+ function installedVersion2() {
29979
+ try {
29980
+ const pkg = JSON.parse(readFileSync44(new URL("../package.json", import.meta.url), "utf-8"));
29981
+ return typeof pkg.version === "string" ? pkg.version : null;
29982
+ } catch {
29983
+ return null;
29984
+ }
29985
+ }
29736
29986
  async function main() {
29737
29987
  installCrashGuards();
29738
29988
  if (process.argv.includes("--daemon")) {