chat-recall 0.7.14 → 0.7.16

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
@@ -4259,7 +4259,7 @@ var require_pino = __commonJS({
4259
4259
  messageKey,
4260
4260
  errorKey,
4261
4261
  nestedKey,
4262
- base,
4262
+ base: base2,
4263
4263
  name,
4264
4264
  level: level2,
4265
4265
  customLevels,
@@ -4298,11 +4298,11 @@ var require_pino = __commonJS({
4298
4298
  [formattersSym]: allFormatters
4299
4299
  });
4300
4300
  let chindings = "";
4301
- if (base !== null) {
4301
+ if (base2 !== null) {
4302
4302
  if (name === void 0) {
4303
- chindings = coreChindings(base);
4303
+ chindings = coreChindings(base2);
4304
4304
  } else {
4305
- chindings = coreChindings(Object.assign({}, base, { name }));
4305
+ chindings = coreChindings(Object.assign({}, base2, { name }));
4306
4306
  }
4307
4307
  }
4308
4308
  const time2 = timestamp instanceof Function ? timestamp : timestamp ? epochTime : nullTime;
@@ -5271,57 +5271,57 @@ function freshDefaults() {
5271
5271
  team: defaultTeam()
5272
5272
  };
5273
5273
  }
5274
- function mergeSources(base, partial) {
5274
+ function mergeSources(base2, partial) {
5275
5275
  if (!partial)
5276
- return base;
5276
+ return base2;
5277
5277
  const legacyGemini = partial.enabled?.gemini;
5278
5278
  const inherited = legacyGemini && typeof legacyGemini === "object" ? (({ sessions: _s, plans: _p, ...rest }) => rest)(legacyGemini) : {};
5279
5279
  const enabled = {
5280
- claude: { ...base.enabled.claude, ...partial.enabled?.claude ?? {} },
5281
- opencode: { ...base.enabled.opencode, ...partial.enabled?.opencode ?? {} },
5282
- codex: { ...base.enabled.codex, ...partial.enabled?.codex ?? {} },
5283
- agy: { ...base.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
5284
- cursor: { ...base.enabled.cursor, ...partial.enabled?.cursor ?? {} },
5285
- shared: { ...base.enabled.shared, ...partial.enabled?.shared ?? {} },
5286
- common: { ...base.enabled.common, ...partial.enabled?.common ?? {} }
5280
+ claude: { ...base2.enabled.claude, ...partial.enabled?.claude ?? {} },
5281
+ opencode: { ...base2.enabled.opencode, ...partial.enabled?.opencode ?? {} },
5282
+ codex: { ...base2.enabled.codex, ...partial.enabled?.codex ?? {} },
5283
+ agy: { ...base2.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
5284
+ cursor: { ...base2.enabled.cursor, ...partial.enabled?.cursor ?? {} },
5285
+ shared: { ...base2.enabled.shared, ...partial.enabled?.shared ?? {} },
5286
+ common: { ...base2.enabled.common, ...partial.enabled?.common ?? {} }
5287
5287
  };
5288
5288
  return {
5289
- claudeHome: partial.claudeHome ?? base.claudeHome,
5290
- geminiHome: partial.geminiHome ?? base.geminiHome,
5291
- codexHome: partial.codexHome ?? base.codexHome,
5292
- opencodeDbPath: partial.opencodeDbPath ?? base.opencodeDbPath,
5293
- agyHome: partial.agyHome ?? base.agyHome,
5294
- cursorHome: partial.cursorHome ?? base.cursorHome,
5295
- cursorIdeHome: partial.cursorIdeHome ?? base.cursorIdeHome,
5296
- extraClaudeHomes: partial.extraClaudeHomes ?? base.extraClaudeHomes,
5297
- approvedHomes: partial.approvedHomes ?? base.approvedHomes,
5298
- declinedHomes: partial.declinedHomes ?? base.declinedHomes,
5299
- homesGrandfathered: partial.homesGrandfathered ?? base.homesGrandfathered,
5289
+ claudeHome: partial.claudeHome ?? base2.claudeHome,
5290
+ geminiHome: partial.geminiHome ?? base2.geminiHome,
5291
+ codexHome: partial.codexHome ?? base2.codexHome,
5292
+ opencodeDbPath: partial.opencodeDbPath ?? base2.opencodeDbPath,
5293
+ agyHome: partial.agyHome ?? base2.agyHome,
5294
+ cursorHome: partial.cursorHome ?? base2.cursorHome,
5295
+ cursorIdeHome: partial.cursorIdeHome ?? base2.cursorIdeHome,
5296
+ extraClaudeHomes: partial.extraClaudeHomes ?? base2.extraClaudeHomes,
5297
+ approvedHomes: partial.approvedHomes ?? base2.approvedHomes,
5298
+ declinedHomes: partial.declinedHomes ?? base2.declinedHomes,
5299
+ homesGrandfathered: partial.homesGrandfathered ?? base2.homesGrandfathered,
5300
5300
  enabled
5301
5301
  };
5302
5302
  }
5303
- function mergePrivacy(base, partial) {
5303
+ function mergePrivacy(base2, partial) {
5304
5304
  if (!partial)
5305
- return base;
5306
- return { ...base, ...partial };
5305
+ return base2;
5306
+ return { ...base2, ...partial };
5307
5307
  }
5308
- function mergeSync(base, partial) {
5308
+ function mergeSync(base2, partial) {
5309
5309
  if (!partial)
5310
- return base;
5310
+ return base2;
5311
5311
  return {
5312
- ...base,
5312
+ ...base2,
5313
5313
  ...partial,
5314
- upload: { ...base.upload, ...partial.upload ?? {} }
5314
+ upload: { ...base2.upload, ...partial.upload ?? {} }
5315
5315
  };
5316
5316
  }
5317
- function mergeTeam(base, partial) {
5317
+ function mergeTeam(base2, partial) {
5318
5318
  if (!partial)
5319
- return base;
5319
+ return base2;
5320
5320
  return {
5321
- ...base,
5321
+ ...base2,
5322
5322
  ...partial,
5323
- publishAllowed: { ...base.publishAllowed, ...partial.publishAllowed ?? {} },
5324
- vault: { ...base.vault, ...partial.vault ?? {} }
5323
+ publishAllowed: { ...base2.publishAllowed, ...partial.publishAllowed ?? {} },
5324
+ vault: { ...base2.vault, ...partial.vault ?? {} }
5325
5325
  };
5326
5326
  }
5327
5327
  function settingsFilePath() {
@@ -5439,15 +5439,15 @@ function loadSettings() {
5439
5439
  }
5440
5440
  try {
5441
5441
  const parsed = JSON.parse(readFileSync3(path2, "utf-8"));
5442
- const base = freshDefaults();
5442
+ const base2 = freshDefaults();
5443
5443
  const result = {
5444
5444
  v: SCHEMA_VERSION,
5445
- embedding: { ...base.embedding, ...parsed.embedding ?? {} },
5446
- summary: { ...base.summary, ...parsed.summary ?? {} },
5447
- sources: mergeSources(base.sources, parsed.sources),
5448
- privacy: mergePrivacy(base.privacy, parsed.privacy),
5449
- sync: mergeSync(base.sync, parsed.sync),
5450
- team: mergeTeam(base.team, parsed.team)
5445
+ embedding: { ...base2.embedding, ...parsed.embedding ?? {} },
5446
+ summary: { ...base2.summary, ...parsed.summary ?? {} },
5447
+ sources: mergeSources(base2.sources, parsed.sources),
5448
+ privacy: mergePrivacy(base2.privacy, parsed.privacy),
5449
+ sync: mergeSync(base2.sync, parsed.sync),
5450
+ team: mergeTeam(base2.team, parsed.team)
5451
5451
  };
5452
5452
  if (mtimeMs >= 0)
5453
5453
  _settingsCache = { mtimeMs, size, value: structuredClone(result) };
@@ -5643,10 +5643,10 @@ function legacyAutoSyncedHomes() {
5643
5643
  { base: join6(home, ".local", "share", "opencode"), marker: "opencode.db" },
5644
5644
  { base: join6(home, ".cursor"), marker: "chats" }
5645
5645
  ];
5646
- for (const { base, marker } of bases) {
5647
- add(base, marker);
5648
- const parent = dirname5(base);
5649
- const leaf = base.slice(parent.length + 1);
5646
+ for (const { base: base2, marker } of bases) {
5647
+ add(base2, marker);
5648
+ const parent = dirname5(base2);
5649
+ const leaf = base2.slice(parent.length + 1);
5650
5650
  try {
5651
5651
  for (const e of readdirSync4(parent, { withFileTypes: true })) {
5652
5652
  if (!e.isDirectory())
@@ -5827,17 +5827,17 @@ function cursorHomeDir() {
5827
5827
  function cursorIdeHomeDir() {
5828
5828
  return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join7(homedir3(), "Library", "Application Support", "Cursor") : join7(homedir3(), ".config", "Cursor"));
5829
5829
  }
5830
- function siblingHomes(base, overridden, suffix) {
5830
+ function siblingHomes(base2, overridden, suffix) {
5831
5831
  const roots = [];
5832
5832
  const add = (home) => {
5833
5833
  const root = suffix ? join7(home, suffix) : home;
5834
5834
  if (existsSync5(root) && !roots.includes(root))
5835
5835
  roots.push(root);
5836
5836
  };
5837
- add(base);
5837
+ add(base2);
5838
5838
  if (!overridden) {
5839
- const parent = dirname6(base);
5840
- const leaf = basename3(base);
5839
+ const parent = dirname6(base2);
5840
+ const leaf = basename3(base2);
5841
5841
  try {
5842
5842
  for (const entry of readdirSync5(parent, { withFileTypes: true })) {
5843
5843
  if (!entry.isDirectory())
@@ -7108,7 +7108,64 @@ var init_opencode = __esm({
7108
7108
  });
7109
7109
 
7110
7110
  // ../engine/dist/core/backends/tail-read.js
7111
- import { openSync, readSync, closeSync, statSync as statSync6 } from "node:fs";
7111
+ import { openSync, readSync, closeSync, statSync as statSync6, fstatSync } from "node:fs";
7112
+ function readAt(fd, pos, len) {
7113
+ const buf = Buffer.alloc(len);
7114
+ let got = 0;
7115
+ while (got < len) {
7116
+ const n = readSync(fd, buf, got, len - got, pos + got);
7117
+ if (n <= 0)
7118
+ break;
7119
+ got += n;
7120
+ }
7121
+ return buf.subarray(0, got);
7122
+ }
7123
+ function linesBeforeOffset(path2, offset) {
7124
+ if (offset <= 0)
7125
+ return 0;
7126
+ let fd = null;
7127
+ try {
7128
+ fd = openSync(path2, "r");
7129
+ const ino = fstatSync(fd).ino;
7130
+ const known = lineCounts.get(path2);
7131
+ let pos = 0;
7132
+ let lines = 0;
7133
+ if (known && known.ino === ino && known.offset <= offset) {
7134
+ const edgeStart2 = Math.max(0, known.offset - EDGE_BYTES);
7135
+ if (readAt(fd, edgeStart2, known.offset - edgeStart2).equals(known.edge)) {
7136
+ pos = known.offset;
7137
+ lines = known.lines;
7138
+ }
7139
+ }
7140
+ const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
7141
+ while (pos < offset) {
7142
+ const n = readSync(fd, block, 0, Math.min(SKIP_SCAN_BYTES, offset - pos), pos);
7143
+ if (n <= 0)
7144
+ return void 0;
7145
+ for (let i = block.indexOf(10, 0); i >= 0 && i < n; i = block.indexOf(10, i + 1))
7146
+ lines++;
7147
+ pos += n;
7148
+ }
7149
+ const edgeStart = Math.max(0, offset - EDGE_BYTES);
7150
+ const edge = readAt(fd, edgeStart, offset - edgeStart);
7151
+ if (lineCounts.size >= LINE_COUNT_CACHE_MAX && !lineCounts.has(path2)) {
7152
+ const oldest = lineCounts.keys().next().value;
7153
+ if (oldest !== void 0)
7154
+ lineCounts.delete(oldest);
7155
+ }
7156
+ lineCounts.set(path2, { offset, lines, ino, edge });
7157
+ return lines;
7158
+ } catch {
7159
+ return void 0;
7160
+ } finally {
7161
+ if (fd !== null) {
7162
+ try {
7163
+ closeSync(fd);
7164
+ } catch {
7165
+ }
7166
+ }
7167
+ }
7168
+ }
7112
7169
  function readTailFromOffset(path2, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7113
7170
  let size = 0;
7114
7171
  try {
@@ -7136,7 +7193,7 @@ function readTailFromOffset(path2, offset, maxBytes = TAIL_READ_MAX_BYTES) {
7136
7193
  const lastNl = slice.lastIndexOf(10);
7137
7194
  if (lastNl >= 0) {
7138
7195
  const text = slice.subarray(0, lastNl + 1).toString("utf-8");
7139
- return { text, newOffset: offset + lastNl + 1 };
7196
+ return { text, newOffset: offset + lastNl + 1, baseLine: linesBeforeOffset(path2, offset) };
7140
7197
  }
7141
7198
  if (offset + got >= size)
7142
7199
  return { text: "", newOffset: offset };
@@ -7169,12 +7226,15 @@ function findNewline(fd, from, size) {
7169
7226
  }
7170
7227
  return -1;
7171
7228
  }
7172
- var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES;
7229
+ var TAIL_READ_MAX_BYTES, SKIP_SCAN_BYTES, lineCounts, LINE_COUNT_CACHE_MAX, EDGE_BYTES;
7173
7230
  var init_tail_read = __esm({
7174
7231
  "../engine/dist/core/backends/tail-read.js"() {
7175
7232
  "use strict";
7176
7233
  TAIL_READ_MAX_BYTES = 16 * 1024 * 1024;
7177
7234
  SKIP_SCAN_BYTES = 1024 * 1024;
7235
+ lineCounts = /* @__PURE__ */ new Map();
7236
+ LINE_COUNT_CACHE_MAX = 2048;
7237
+ EDGE_BYTES = 64;
7178
7238
  }
7179
7239
  });
7180
7240
 
@@ -11019,9 +11079,9 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
11019
11079
  content.metadata.durationMs += obj.durationMs;
11020
11080
  }
11021
11081
  if (msgType === "file-history-snapshot") {
11022
- const snapshot = obj.snapshot;
11023
- if (snapshot && typeof snapshot === "object") {
11024
- const backups = snapshot.trackedFileBackups;
11082
+ const snapshot2 = obj.snapshot;
11083
+ if (snapshot2 && typeof snapshot2 === "object") {
11084
+ const backups = snapshot2.trackedFileBackups;
11025
11085
  if (backups && typeof backups === "object") {
11026
11086
  for (const filePath2 of Object.keys(backups)) {
11027
11087
  filesModified.add(filePath2);
@@ -13290,8 +13350,8 @@ function serverRulePackVersion() {
13290
13350
  return serverPack ? serverPack.version || "unversioned" : null;
13291
13351
  }
13292
13352
  function activeRules() {
13293
- const base = settingsView().rules;
13294
- return serverPack && serverPack.rules.length > 0 ? [...base, ...serverPack.rules] : base;
13353
+ const base2 = settingsView().rules;
13354
+ return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
13295
13355
  }
13296
13356
  function isRedactionEnabled() {
13297
13357
  const env = isEnvRedactionRequested();
@@ -14154,17 +14214,17 @@ async function collectCode(opts) {
14154
14214
  return r;
14155
14215
  };
14156
14216
  log6(`indexing ${ws} + analyses \u2026`);
14157
- const base = await runMcp(bin, ws, [
14217
+ const base2 = await runMcp(bin, ws, [
14158
14218
  call(1, "index_workspace", { path: ws }),
14159
14219
  call(2, "status"),
14160
14220
  call(3, "get_tree"),
14161
14221
  ...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
14162
14222
  ]);
14163
- const st = parseJson(base, 2, {});
14164
- const tree = parseJson(base, 3, []);
14223
+ const st = parseJson(base2, 2, {});
14224
+ const tree = parseJson(base2, 3, []);
14165
14225
  const A = {};
14166
14226
  ANALYSES.forEach((a, i) => {
14167
- A[a] = parseJson(base, 10 + i, {});
14227
+ A[a] = parseJson(base2, 10 + i, {});
14168
14228
  });
14169
14229
  const { health, security: sec, duplication: dup, clones, literal_scan: lit, coupling: coup, cycles: cyc, dead_code: dead, unwrap_audit: unwrap, test_coverage: coverage, architecture: arch, crossref, type_drift: typeDrift, db_schema: dbSchema, migration_parity: migParity, manifest_compliance: manifest } = A;
14170
14230
  if (tree.length === 0) {
@@ -19452,38 +19512,100 @@ var init_extractor_version = __esm({
19452
19512
  });
19453
19513
 
19454
19514
  // src/sync-ledger.ts
19455
- import { readFileSync as readFileSync34, writeFileSync as writeFileSync7, renameSync as renameSync5, mkdirSync as mkdirSync10 } from "node:fs";
19515
+ import { readFileSync as readFileSync34, writeFileSync as writeFileSync7, renameSync as renameSync5, mkdirSync as mkdirSync10, statSync as statSync40 } from "node:fs";
19456
19516
  import { join as join46, dirname as dirname17 } from "node:path";
19457
19517
  function toRow(entry) {
19458
19518
  if (entry === void 0) return void 0;
19459
19519
  return typeof entry === "number" ? { m: entry, v: 0 } : entry;
19460
19520
  }
19461
- function load() {
19462
- if (cache3) return cache3;
19521
+ function stampOf(path2) {
19463
19522
  try {
19464
- const parsed = JSON.parse(readFileSync34(ledgerPath(), "utf-8"));
19465
- cache3 = parsed && typeof parsed === "object" ? parsed : {};
19523
+ const st = statSync40(path2);
19524
+ return `${st.mtimeMs}:${st.size}`;
19466
19525
  } catch {
19467
- cache3 = {};
19526
+ return null;
19468
19527
  }
19528
+ }
19529
+ function readDisk(path2) {
19530
+ try {
19531
+ const parsed = JSON.parse(readFileSync34(path2, "utf-8"));
19532
+ return parsed && typeof parsed === "object" ? parsed : {};
19533
+ } catch {
19534
+ return {};
19535
+ }
19536
+ }
19537
+ function snapshot(data) {
19538
+ const out = /* @__PURE__ */ new Map();
19539
+ for (const [server, rows] of Object.entries(data)) {
19540
+ for (const [id, entry] of Object.entries(rows)) out.set(rowKey(server, id), JSON.stringify(entry));
19541
+ }
19542
+ return out;
19543
+ }
19544
+ function mergeOnto(disk) {
19545
+ const out = {};
19546
+ const put = (server, id, entry) => {
19547
+ (out[server] ??= {})[id] = entry;
19548
+ };
19549
+ const mine = cache3 ?? {};
19550
+ for (const [server, rows] of Object.entries(disk)) {
19551
+ for (const [id, entry] of Object.entries(rows)) {
19552
+ const k = rowKey(server, id);
19553
+ const own = mine[server]?.[id];
19554
+ const was = base.get(k);
19555
+ if (own === void 0) {
19556
+ if (was === void 0) put(server, id, entry);
19557
+ } else {
19558
+ put(server, id, JSON.stringify(own) === was ? entry : own);
19559
+ }
19560
+ }
19561
+ }
19562
+ for (const [server, rows] of Object.entries(mine)) {
19563
+ for (const [id, entry] of Object.entries(rows)) {
19564
+ if (disk[server]?.[id] !== void 0) continue;
19565
+ if (JSON.stringify(entry) !== base.get(rowKey(server, id))) put(server, id, entry);
19566
+ }
19567
+ }
19568
+ return out;
19569
+ }
19570
+ function load() {
19571
+ const path2 = ledgerPath();
19572
+ const stamp = stampOf(path2);
19573
+ if (cache3 && stamp === diskStamp) return cache3;
19574
+ const disk = readDisk(path2);
19575
+ const merged = cache3 ? mergeOnto(disk) : disk;
19576
+ if (cache3) {
19577
+ for (const server of Object.keys(cache3)) if (!(server in merged)) delete cache3[server];
19578
+ for (const [server, rows] of Object.entries(merged)) {
19579
+ const target = cache3[server] ?? (cache3[server] = {});
19580
+ for (const id of Object.keys(target)) if (!(id in rows)) delete target[id];
19581
+ Object.assign(target, rows);
19582
+ }
19583
+ } else {
19584
+ cache3 = merged;
19585
+ }
19586
+ base = snapshot(disk);
19587
+ diskStamp = stamp;
19469
19588
  return cache3;
19470
19589
  }
19471
- function persistNow(data) {
19590
+ function persistNow() {
19472
19591
  const path2 = ledgerPath();
19473
19592
  mkdirSync10(dirname17(path2), { recursive: true });
19474
- const tmp = `${path2}.tmp`;
19475
- writeFileSync7(tmp, JSON.stringify(data));
19593
+ load();
19594
+ const tmp = `${path2}.tmp-${process.pid}`;
19595
+ writeFileSync7(tmp, JSON.stringify(cache3 ?? {}));
19476
19596
  renameSync5(tmp, path2);
19597
+ base = snapshot(cache3 ?? {});
19598
+ diskStamp = stampOf(path2);
19477
19599
  pendingWrite = false;
19478
19600
  }
19479
- function persist(data) {
19601
+ function persist(_data) {
19480
19602
  pendingWrite = true;
19481
19603
  if (flushTimer) return;
19482
19604
  flushTimer = setTimeout(() => {
19483
19605
  flushTimer = null;
19484
19606
  if (pendingWrite) {
19485
19607
  try {
19486
- persistNow(data);
19608
+ persistNow();
19487
19609
  } catch {
19488
19610
  }
19489
19611
  }
@@ -19517,7 +19639,7 @@ function pruneLedgerTargets(configuredServers) {
19517
19639
  }
19518
19640
  const rows = names.reduce((n, s) => n + Object.keys(orphans[s]).length, 0);
19519
19641
  console.error(`[sync] tidied local bookkeeping: ${rows} already-synced marker(s) for ${names.length} server(s) you no longer sync to (${names.join(", ")}) moved to ${side}. Nothing was uploaded there; the file is kept so logging back in re-ships nothing.`);
19520
- persistNow(data);
19642
+ persistNow();
19521
19643
  return names.length;
19522
19644
  }
19523
19645
  function flushLedger() {
@@ -19526,7 +19648,7 @@ function flushLedger() {
19526
19648
  flushTimer = null;
19527
19649
  }
19528
19650
  if (!pendingWrite || !cache3) return;
19529
- persistNow(cache3);
19651
+ persistNow();
19530
19652
  }
19531
19653
  function loadItemVersionsAll() {
19532
19654
  if (itemVersionsCache) return itemVersionsCache;
@@ -19586,6 +19708,7 @@ function persistLedgerData(server, serverData) {
19586
19708
  const data = load();
19587
19709
  data[server] = serverData;
19588
19710
  persist(data);
19711
+ flushLedger();
19589
19712
  }
19590
19713
  function markSynced(server, rows) {
19591
19714
  if (rows.length === 0) return;
@@ -19602,13 +19725,13 @@ function markSynced(server, rows) {
19602
19725
  const o = (cursorFromCaller ? r.offset : void 0) ?? prevOS.o ?? 0;
19603
19726
  const s = (cursorFromCaller ? r.size : void 0) ?? prevOS.s ?? 0;
19604
19727
  const ev = extractorVersionForId(r.id);
19605
- const base = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
19728
+ const base2 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
19606
19729
  if (o > 0 || s > 0) {
19607
- base.o = o;
19608
- base.s = s;
19730
+ base2.o = o;
19731
+ base2.s = s;
19609
19732
  }
19610
- if (r.hash !== void 0) base.h = r.hash;
19611
- forServer[r.id] = base;
19733
+ if (r.hash !== void 0) base2.h = r.hash;
19734
+ forServer[r.id] = base2;
19612
19735
  }
19613
19736
  persist(data);
19614
19737
  }
@@ -19683,7 +19806,7 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19683
19806
  if (row.m >= Math.floor(mtime)) return "skip";
19684
19807
  return "full";
19685
19808
  }
19686
- var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
19809
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
19687
19810
  var init_sync_ledger = __esm({
19688
19811
  "src/sync-ledger.ts"() {
19689
19812
  "use strict";
@@ -19691,6 +19814,9 @@ var init_sync_ledger = __esm({
19691
19814
  init_extractor_version();
19692
19815
  ledgerPath = () => join46(getDataDir(), "sync-ledger.json");
19693
19816
  cache3 = null;
19817
+ base = /* @__PURE__ */ new Map();
19818
+ diskStamp = null;
19819
+ rowKey = (server, id) => `${server}\0${id}`;
19694
19820
  LEDGER_FLUSH_MS = 2e3;
19695
19821
  pendingWrite = false;
19696
19822
  flushTimer = null;
@@ -19729,7 +19855,7 @@ var init_sync_fields = __esm({
19729
19855
  });
19730
19856
 
19731
19857
  // ../engine/dist/core/index-lock.js
19732
- import { existsSync as existsSync43, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as statSync40, closeSync as closeSync9 } from "fs";
19858
+ import { existsSync as existsSync43, mkdirSync as mkdirSync11, openSync as openSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync35, rmSync as rmSync4, statSync as statSync41, closeSync as closeSync9 } from "fs";
19733
19859
  import { dirname as dirname18, join as join47 } from "path";
19734
19860
  function acquireIndexLock(opts) {
19735
19861
  const lockPath = lockFilePath(opts.file);
@@ -19779,7 +19905,7 @@ function lockFilePath(file = ".compact.lock") {
19779
19905
  }
19780
19906
  function stealStaleLock(lockPath, staleAfterMs) {
19781
19907
  try {
19782
- const s = statSync40(lockPath);
19908
+ const s = statSync41(lockPath);
19783
19909
  const age = Date.now() - s.mtimeMs;
19784
19910
  let payload;
19785
19911
  try {
@@ -19845,7 +19971,7 @@ var init_http = __esm({
19845
19971
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19846
19972
  } catch {
19847
19973
  try {
19848
- if (true) CLI_VERSION = "0.7.14";
19974
+ if (true) CLI_VERSION = "0.7.16";
19849
19975
  } catch {
19850
19976
  }
19851
19977
  }
@@ -20343,7 +20469,7 @@ var init_telemetry = __esm({
20343
20469
  init_sync_client();
20344
20470
  cliVersion2 = () => {
20345
20471
  try {
20346
- return true ? "0.7.14" : "dev";
20472
+ return true ? "0.7.16" : "dev";
20347
20473
  } catch {
20348
20474
  return "dev";
20349
20475
  }
@@ -20366,11 +20492,11 @@ __export(server_probe_exports, {
20366
20492
  function probeOk(r) {
20367
20493
  return r.kind === "ok" && r.apiVersion >= 2;
20368
20494
  }
20369
- async function probeServer(base, opts = {}) {
20495
+ async function probeServer(base2, opts = {}) {
20370
20496
  const f = opts.fetchImpl || fetch;
20371
20497
  let res;
20372
20498
  try {
20373
- res = await f(`${base.replace(/\/+$/, "")}/api/capabilities`, {
20499
+ res = await f(`${base2.replace(/\/+$/, "")}/api/capabilities`, {
20374
20500
  signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
20375
20501
  });
20376
20502
  } catch (err) {
@@ -20407,34 +20533,34 @@ async function probeServer(base, opts = {}) {
20407
20533
  raw: parsed
20408
20534
  };
20409
20535
  }
20410
- function probeAdvice(r, base) {
20536
+ function probeAdvice(r, base2) {
20411
20537
  switch (r.kind) {
20412
20538
  case "ok":
20413
20539
  return r.apiVersion >= 2 ? `apiVersion ${r.apiVersion}${r.edition ? `, edition ${r.edition}` : ""}` : `apiVersion ${r.apiVersion} is too old for this CLI \u2014 upgrade the server`;
20414
20540
  case "moved":
20415
- return `HTTP ${r.status} \u2014 nothing is served at ${base}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
20541
+ return `HTTP ${r.status} \u2014 nothing is served at ${base2}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
20416
20542
  case "unauthorized":
20417
- return `HTTP ${r.status} \u2014 ${base} rejected the credential. Re-login: \`chat-recall login ` + base + "`";
20543
+ return `HTTP ${r.status} \u2014 ${base2} rejected the credential. Re-login: \`chat-recall login ` + base2 + "`";
20418
20544
  case "server_error":
20419
- return `HTTP ${r.status} \u2014 ${base} is up but failing. Nothing to fix locally; check the server.`;
20545
+ return `HTTP ${r.status} \u2014 ${base2} is up but failing. Nothing to fix locally; check the server.`;
20420
20546
  case "not_api":
20421
- return `${base} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
20547
+ return `${base2} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
20422
20548
  case "unreachable":
20423
20549
  switch (r.reason) {
20424
20550
  case "dns":
20425
- return `cannot resolve the host in ${base} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
20551
+ return `cannot resolve the host in ${base2} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
20426
20552
  case "refused":
20427
- return `${base} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
20553
+ return `${base2} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
20428
20554
  case "tls":
20429
- return `the TLS certificate at ${base} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
20555
+ return `the TLS certificate at ${base2} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
20430
20556
  case "timeout":
20431
- return `${base} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
20557
+ return `${base2} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
20432
20558
  case "reset":
20433
- return `the connection to ${base} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
20559
+ return `the connection to ${base2} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
20434
20560
  case "bad_port":
20435
- return `${base} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
20561
+ return `${base2} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
20436
20562
  default:
20437
- return `no HTTP response from ${base} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
20563
+ return `no HTTP response from ${base2} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
20438
20564
  }
20439
20565
  }
20440
20566
  }
@@ -20467,8 +20593,8 @@ function describeCause(err) {
20467
20593
  const code = e.code;
20468
20594
  if (typeof code === "string" && code && !parts.includes(code)) parts.push(code);
20469
20595
  }
20470
- const base = err instanceof Error ? err.message : String(err);
20471
- return parts.length ? `${base}: ${parts.join(" \u2192 ")}` : base;
20596
+ const base2 = err instanceof Error ? err.message : String(err);
20597
+ return parts.length ? `${base2}: ${parts.join(" \u2192 ")}` : base2;
20472
20598
  }
20473
20599
  function describeUnreachable(err) {
20474
20600
  const reason = classifyUnreachable(err);
@@ -20516,7 +20642,7 @@ __export(auto_update_exports, {
20516
20642
  import { execSync as execSync4, execFileSync as execFileSync3 } from "node:child_process";
20517
20643
  import { readFileSync as readFileSync37, existsSync as existsSync45 } from "node:fs";
20518
20644
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20519
- import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync41, mkdirSync as mkdirSync12 } from "node:fs";
20645
+ import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync42, mkdirSync as mkdirSync12 } from "node:fs";
20520
20646
  import { join as join48, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
20521
20647
  import { tmpdir as tmpdir3, homedir as homedir17 } from "node:os";
20522
20648
  import { createHash as createHash10 } from "node:crypto";
@@ -20548,12 +20674,12 @@ function installedVersion() {
20548
20674
  return null;
20549
20675
  }
20550
20676
  }
20551
- function planAutoUpdate(base, caps, ownVersion2, flag) {
20677
+ function planAutoUpdate(base2, caps, ownVersion2, flag) {
20552
20678
  const rel = caps.cli;
20553
20679
  if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
20554
20680
  if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
20555
20681
  if (compareVersions(rel.version, ownVersion2) <= 0) return { update: false, reason: `already current (own ${ownVersion2} >= server ${rel.version})` };
20556
- return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base, rel.version), fallbackUrl: tarballUrl(base), sha256: rel.sha256, from: ownVersion2, to: rel.version };
20682
+ 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 };
20557
20683
  }
20558
20684
  function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20559
20685
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
@@ -20569,7 +20695,7 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20569
20695
  if (!MKDTEMP_SUFFIX.test(name)) continue;
20570
20696
  const dir = join48(root, name);
20571
20697
  try {
20572
- const st = statSync41(dir);
20698
+ const st = statSync42(dir);
20573
20699
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20574
20700
  const inside = readdirSync31(dir);
20575
20701
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
@@ -20733,20 +20859,20 @@ function clearUpdateFailures() {
20733
20859
  function stopAutoUpdates() {
20734
20860
  stopRequested = true;
20735
20861
  }
20736
- async function runAutoUpdate(base, authHeaders, ownVersion2, deps) {
20862
+ async function runAutoUpdate(base2, authHeaders, ownVersion2, deps) {
20737
20863
  let caps;
20738
20864
  try {
20739
- const res = await fetchWithTimeout(`${base.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
20865
+ const res = await fetchWithTimeout(`${base2.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
20740
20866
  if (!res.ok) return { updated: false, reason: `capabilities HTTP ${res.status}` };
20741
20867
  caps = await res.json();
20742
20868
  } catch (e) {
20743
20869
  return { updated: false, reason: `capabilities unreachable: ${e instanceof Error ? e.message : e}` };
20744
20870
  }
20745
20871
  const effectiveOwn = newerOf(ownVersion2, installedVersion());
20746
- const plan = planAutoUpdate(base, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
20872
+ const plan = planAutoUpdate(base2, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
20747
20873
  if (!plan.update) {
20748
- if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base)) {
20749
- reportedStandingSkip.add(base);
20874
+ if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base2)) {
20875
+ reportedStandingSkip.add(base2);
20750
20876
  try {
20751
20877
  const { reportClientEvent: reportClientEvent3 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
20752
20878
  reportClientEvent3("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
@@ -20802,7 +20928,7 @@ var init_auto_update = __esm({
20802
20928
  "use strict";
20803
20929
  init_http();
20804
20930
  init_index_lock();
20805
- tarballUrl = (base, version2) => `${base.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
20931
+ tarballUrl = (base2, version2) => `${base2.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
20806
20932
  GIVE_UP_AFTER = 3;
20807
20933
  reportedStandingSkip = /* @__PURE__ */ new Set();
20808
20934
  STOPPING_REASON = "not installed: this process is shutting down";
@@ -20847,7 +20973,7 @@ __export(sync_client_exports, {
20847
20973
  syncSessions: () => syncSessions
20848
20974
  });
20849
20975
  import { join as join49, dirname as dirname20, basename as basename22 } from "node:path";
20850
- 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 statSync42 } from "node:fs";
20976
+ 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";
20851
20977
  import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
20852
20978
  import { createHash as createHash11 } from "node:crypto";
20853
20979
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -20910,15 +21036,15 @@ function loadAllCredentials() {
20910
21036
  return [];
20911
21037
  }
20912
21038
  async function fetchTenantSecurityConfig(cred) {
20913
- const base = cred.serverUrl.replace(/\/+$/, "");
20914
- const cached = _securityConfigCache.get(base);
21039
+ const base2 = cred.serverUrl.replace(/\/+$/, "");
21040
+ const cached = _securityConfigCache.get(base2);
20915
21041
  if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
20916
21042
  const headers = { "content-type": "application/json" };
20917
21043
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
20918
21044
  try {
20919
21045
  const [rulesRes, settingsRes] = await Promise.all([
20920
- fetchWithTimeout(`${base}/api/secrets/rules`, { headers }),
20921
- fetchWithTimeout(`${base}/api/teams/security-config`, { headers }).catch(() => null)
21046
+ fetchWithTimeout(`${base2}/api/secrets/rules`, { headers }),
21047
+ fetchWithTimeout(`${base2}/api/teams/security-config`, { headers }).catch(() => null)
20922
21048
  ]);
20923
21049
  let tenantRules = [];
20924
21050
  let rulePackVersion = null;
@@ -20943,18 +21069,18 @@ async function fetchTenantSecurityConfig(cred) {
20943
21069
  const body2 = await settingsRes.json().catch(() => ({}));
20944
21070
  if (typeof body2.verifySecrets === "boolean") verifySecrets = body2.verifySecrets;
20945
21071
  if (typeof body2.telemetry === "boolean") {
20946
- setTelemetryEligible(base, body2.telemetry);
21072
+ setTelemetryEligible(base2, body2.telemetry);
20947
21073
  try {
20948
21074
  const prior = readCollectorHealth()?.telemetryEligible ?? {};
20949
21075
  updateCollectorHealth({
20950
- telemetryEligible: { ...prior, [base]: { allowed: body2.telemetry, at: Date.now() } }
21076
+ telemetryEligible: { ...prior, [base2]: { allowed: body2.telemetry, at: Date.now() } }
20951
21077
  });
20952
21078
  } catch {
20953
21079
  }
20954
21080
  }
20955
21081
  }
20956
21082
  const config2 = { tenantRules, verifySecrets, rulePackVersion };
20957
- _securityConfigCache.set(base, { fetchedAt: Date.now(), config: config2 });
21083
+ _securityConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
20958
21084
  return config2;
20959
21085
  } catch {
20960
21086
  return { tenantRules: [], verifySecrets: true, rulePackVersion: null };
@@ -20973,18 +21099,18 @@ function readPersistedSyncConfigs() {
20973
21099
  return {};
20974
21100
  }
20975
21101
  }
20976
- function persistSyncConfig(base, config2) {
21102
+ function persistSyncConfig(base2, config2) {
20977
21103
  try {
20978
21104
  const all = readPersistedSyncConfigs();
20979
- all[base] = { fetchedAt: Date.now(), config: config2 };
21105
+ all[base2] = { fetchedAt: Date.now(), config: config2 };
20980
21106
  mkdirSync13(dirname20(syncConfigCachePath()), { recursive: true });
20981
21107
  writeFileSync10(syncConfigCachePath(), JSON.stringify(all, null, 2));
20982
21108
  } catch {
20983
21109
  }
20984
21110
  }
20985
21111
  async function fetchTenantSyncConfig(cred) {
20986
- const base = cred.serverUrl.replace(/\/+$/, "");
20987
- const cached = _syncConfigCache.get(base);
21112
+ const base2 = cred.serverUrl.replace(/\/+$/, "");
21113
+ const cached = _syncConfigCache.get(base2);
20988
21114
  if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
20989
21115
  const headers = { "content-type": "application/json" };
20990
21116
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -20992,7 +21118,7 @@ async function fetchTenantSyncConfig(cred) {
20992
21118
  try {
20993
21119
  const sources2 = discoverSessionSources();
20994
21120
  if (sources2.length > 0) {
20995
- await fetchWithTimeout(`${base}/api/sync-config/sources`, {
21121
+ await fetchWithTimeout(`${base2}/api/sync-config/sources`, {
20996
21122
  method: "POST",
20997
21123
  headers,
20998
21124
  body: JSON.stringify({ device: deviceLabel(), sources: sources2 })
@@ -21000,12 +21126,12 @@ async function fetchTenantSyncConfig(cred) {
21000
21126
  }
21001
21127
  } catch {
21002
21128
  }
21003
- const res = await fetchWithTimeout(`${base}/api/sync-config`, { headers });
21129
+ const res = await fetchWithTimeout(`${base2}/api/sync-config`, { headers });
21004
21130
  if (res.status === 404) {
21005
- _syncConfigCache.set(base, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
21131
+ _syncConfigCache.set(base2, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
21006
21132
  return EMPTY_SYNC_CONFIG;
21007
21133
  }
21008
- if (!res.ok) return lastKnownSyncConfig(base, `HTTP ${res.status}`);
21134
+ if (!res.ok) return lastKnownSyncConfig(base2, `HTTP ${res.status}`);
21009
21135
  const body2 = await res.json().catch(() => ({}));
21010
21136
  const config2 = {
21011
21137
  excludeTools: Array.isArray(body2.excludeTools) ? body2.excludeTools.filter((t) => typeof t === "string") : [],
@@ -21042,22 +21168,22 @@ async function fetchTenantSyncConfig(cred) {
21042
21168
  if (decided > 0) console.error(`[sync] applied ${decided} folder decision(s) from the dashboard`);
21043
21169
  } catch {
21044
21170
  }
21045
- _syncConfigCache.set(base, { fetchedAt: Date.now(), config: config2 });
21046
- persistSyncConfig(base, config2);
21171
+ _syncConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
21172
+ persistSyncConfig(base2, config2);
21047
21173
  return config2;
21048
21174
  } catch (err) {
21049
21175
  if (isSyncRulesRefusal(err)) throw err;
21050
- return lastKnownSyncConfig(base, err instanceof Error ? err.message : "request failed");
21176
+ return lastKnownSyncConfig(base2, err instanceof Error ? err.message : "request failed");
21051
21177
  }
21052
21178
  }
21053
- function lastKnownSyncConfig(base, reason) {
21054
- const remembered = _syncConfigCache.get(base) ?? readPersistedSyncConfigs()[base];
21179
+ function lastKnownSyncConfig(base2, reason) {
21180
+ const remembered = _syncConfigCache.get(base2) ?? readPersistedSyncConfigs()[base2];
21055
21181
  if (remembered) {
21056
21182
  console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
21057
21183
  return remembered.config;
21058
21184
  }
21059
21185
  const refusal = new Error(
21060
- `Cannot read the server-side sync rules from ${base} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
21186
+ `Cannot read the server-side sync rules from ${base2} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
21061
21187
  );
21062
21188
  refusal[SYNC_RULES_REFUSAL] = true;
21063
21189
  throw refusal;
@@ -21178,25 +21304,25 @@ async function syncSessions(opts = {}) {
21178
21304
  ${errors.join("\n ")}`);
21179
21305
  return { ...agg, perTarget };
21180
21306
  }
21181
- async function postFieldBatch(base, token, rows) {
21307
+ async function postFieldBatch(base2, token, rows) {
21182
21308
  const headers = { "content-type": "application/json" };
21183
21309
  if (token) headers.authorization = `Bearer ${token}`;
21184
21310
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
21185
21311
  for (let attempt = 0; ; attempt++) {
21186
- const res = await fetchWithTimeout(`${base}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
21312
+ const res = await fetchWithTimeout(`${base2}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
21187
21313
  if (res.ok) return;
21188
21314
  const retryable = res.status === 429 || res.status >= 500;
21189
- if (!retryable || attempt >= 5) throw new Error(`${base}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
21315
+ if (!retryable || attempt >= 5) throw new Error(`${base2}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
21190
21316
  const ra = Number(res.headers.get("retry-after")) || 0;
21191
21317
  await sleep(ra > 0 ? ra * 1e3 : [500, 1500, 4e3, 8e3, 15e3][Math.min(attempt, 4)]);
21192
21318
  }
21193
21319
  }
21194
21320
  async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
21195
- const base = cred.serverUrl.replace(/\/+$/, "");
21196
- const ledger = getSyncedRows(base);
21321
+ const base2 = cred.serverUrl.replace(/\/+$/, "");
21322
+ const ledger = getSyncedRows(base2);
21197
21323
  let pushed = 0, absent = 0, scanned = 0;
21198
21324
  for (const field of SYNC_FIELDS) {
21199
- const needFull = !!opts.force || fieldNeedsFullPass(base, field);
21325
+ const needFull = !!opts.force || fieldNeedsFullPass(base2, field);
21200
21326
  const candidates2 = needFull ? fullRefs() : opts.convRefs ?? fullRefs();
21201
21327
  const send = [];
21202
21328
  const mtimeById = /* @__PURE__ */ new Map();
@@ -21212,15 +21338,15 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
21212
21338
  }
21213
21339
  for (let i = 0; i < send.length; i += 200) {
21214
21340
  const chunk = send.slice(i, i + 200);
21215
- await postFieldBatch(base, cred.token, chunk);
21216
- markFieldCoverage(base, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
21341
+ await postFieldBatch(base2, cred.token, chunk);
21342
+ markFieldCoverage(base2, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
21217
21343
  pushed += chunk.length;
21218
21344
  }
21219
21345
  if (absentIds.length) {
21220
- markFieldCoverage(base, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
21346
+ markFieldCoverage(base2, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
21221
21347
  absent += absentIds.length;
21222
21348
  }
21223
- if (needFull) markFieldFullPassDone(base, field);
21349
+ if (needFull) markFieldFullPassDone(base2, field);
21224
21350
  }
21225
21351
  return { pushed, absent, scanned };
21226
21352
  }
@@ -21258,7 +21384,7 @@ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now(
21258
21384
  if (!name.startsWith("cr-batchscan-")) continue;
21259
21385
  const p = join49(dir, name);
21260
21386
  try {
21261
- if (now - statSync42(p).mtimeMs < maxAgeMs) continue;
21387
+ if (now - statSync43(p).mtimeMs < maxAgeMs) continue;
21262
21388
  rmSync6(p, { recursive: true, force: true });
21263
21389
  reaped++;
21264
21390
  } catch {
@@ -21337,25 +21463,25 @@ function serverIngestConcurrency(serverUrl) {
21337
21463
  return advertisedIngestConc.get(serverUrl.replace(/\/$/, "")) ?? null;
21338
21464
  }
21339
21465
  async function verifyServerApi(serverUrl, minApi) {
21340
- const base = serverUrl.replace(/\/$/, "");
21341
- const seen = capsOk.get(base);
21466
+ const base2 = serverUrl.replace(/\/$/, "");
21467
+ const seen = capsOk.get(base2);
21342
21468
  if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
21343
21469
  const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
21344
- const probe = await probeServer2(base, {
21470
+ const probe = await probeServer2(base2, {
21345
21471
  fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
21346
21472
  });
21347
- if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base));
21473
+ if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base2));
21348
21474
  const caps = probe.raw;
21349
21475
  if ((caps.apiVersion ?? 0) < minApi) {
21350
21476
  throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
21351
21477
  }
21352
21478
  const adv = caps.limits?.ingestConcurrencyPerTenant;
21353
21479
  if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
21354
- advertisedIngestConc.set(base, Math.floor(adv));
21480
+ advertisedIngestConc.set(base2, Math.floor(adv));
21355
21481
  }
21356
- if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base);
21357
- else advertisedGzipBody.delete(base);
21358
- capsOk.set(base, Date.now());
21482
+ if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base2);
21483
+ else advertisedGzipBody.delete(base2);
21484
+ capsOk.set(base2, Date.now());
21359
21485
  return true;
21360
21486
  }
21361
21487
  async function syncToTarget(cred, opts = {}) {
@@ -21420,7 +21546,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21420
21546
  const ac = new AbortController();
21421
21547
  const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
21422
21548
  try {
21423
- const res = await fetch(`${base}/api/sync`, {
21549
+ const res = await fetch(`${base2}/api/sync`, {
21424
21550
  method: "POST",
21425
21551
  // No token ⇒ no auth header: a no-auth local server (AUTH_PROVIDER=none)
21426
21552
  // accepts it as the single 'default' tenant.
@@ -21436,26 +21562,26 @@ async function syncToTargetInScope(cred, opts = {}) {
21436
21562
  acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
21437
21563
  const body3 = await res.json().catch(() => ({}));
21438
21564
  if (typeof body3.telemetry === "boolean") {
21439
- setTelemetryEligible(base, body3.telemetry);
21565
+ setTelemetryEligible(base2, body3.telemetry);
21440
21566
  try {
21441
21567
  const prior = readCollectorHealth()?.telemetryEligible ?? {};
21442
21568
  updateCollectorHealth({
21443
- telemetryEligible: { ...prior, [base]: { allowed: body3.telemetry, at: Date.now() } }
21569
+ telemetryEligible: { ...prior, [base2]: { allowed: body3.telemetry, at: Date.now() } }
21444
21570
  });
21445
21571
  } catch {
21446
21572
  }
21447
21573
  }
21448
21574
  const adv = body3.limits?.ingestConcurrencyPerTenant;
21449
21575
  if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
21450
- setServerIngestConcurrency(base, Math.floor(adv));
21576
+ setServerIngestConcurrency(base2, Math.floor(adv));
21451
21577
  }
21452
21578
  if (body3.cli && body3.cli.version) {
21453
21579
  const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21454
21580
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21455
- const plan = planAutoUpdate2(base, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21581
+ const plan = planAutoUpdate2(base2, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21456
21582
  sweepStaleStaging2();
21457
21583
  if (plan.update) {
21458
- void runAutoUpdate2(base, authHeaders, ownVersion).catch(() => {
21584
+ void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21459
21585
  });
21460
21586
  }
21461
21587
  }
@@ -21464,7 +21590,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21464
21590
  const retryable = res.status === 429 || res.status >= 500;
21465
21591
  const text = await res.text().catch(() => "");
21466
21592
  if (res.status === 401 || res.status === 403) {
21467
- throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base}`);
21593
+ throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base2}`);
21468
21594
  }
21469
21595
  if (res.status === 402) {
21470
21596
  let detail = text;
@@ -21485,7 +21611,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21485
21611
  const retryAfterS = Number(res.headers.get("retry-after"));
21486
21612
  const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
21487
21613
  const why = res.status === 524 ? ` \u2014 the proxy cut the request at its time limit; the batch is too big for one request` : "";
21488
- console.error(`[sync] HTTP ${res.status} from ${base}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
21614
+ console.error(`[sync] HTTP ${res.status} from ${base2}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
21489
21615
  await sleep(waitMs);
21490
21616
  continue;
21491
21617
  } catch (err) {
@@ -21553,11 +21679,11 @@ async function syncToTargetInScope(cred, opts = {}) {
21553
21679
  }
21554
21680
  };
21555
21681
  };
21556
- const base = cred.serverUrl.replace(/\/$/, "");
21557
- const ledger = opts.useLedger ? getSyncedRows(base) : null;
21682
+ const base2 = cred.serverUrl.replace(/\/$/, "");
21683
+ const ledger = opts.useLedger ? getSyncedRows(base2) : null;
21558
21684
  const convBatch = makeBatcher("conversations", 25, false, (rows) => {
21559
21685
  try {
21560
- markSynced(base, rows.map((r) => {
21686
+ markSynced(base2, rows.map((r) => {
21561
21687
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
21562
21688
  const hash = shippedHash.get(r.session_id);
21563
21689
  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 };
@@ -21572,7 +21698,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21572
21698
  const ac = new AbortController();
21573
21699
  const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
21574
21700
  try {
21575
- const res = await fetch(`${base}/api/sync`, {
21701
+ const res = await fetch(`${base2}/api/sync`, {
21576
21702
  method: "POST",
21577
21703
  headers,
21578
21704
  body: JSON.stringify({ conversations: convs }),
@@ -21583,10 +21709,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21583
21709
  if (body2.cli && body2.cli.version) {
21584
21710
  const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21585
21711
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21586
- const plan = planAutoUpdate2(base, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21712
+ const plan = planAutoUpdate2(base2, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21587
21713
  sweepStaleStaging2();
21588
21714
  if (plan.update) {
21589
- void runAutoUpdate2(base, authHeaders, ownVersion).catch(() => {
21715
+ void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21590
21716
  });
21591
21717
  }
21592
21718
  }
@@ -21594,7 +21720,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21594
21720
  const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
21595
21721
  if (acked.length > 0) {
21596
21722
  try {
21597
- markSynced(base, acked.map((c) => ({
21723
+ markSynced(base2, acked.map((c) => ({
21598
21724
  id: c.session_id,
21599
21725
  mtime: c.mtime,
21600
21726
  offset: c.from_offset,
@@ -21612,13 +21738,14 @@ async function syncToTargetInScope(cred, opts = {}) {
21612
21738
  clearTimeout(timer);
21613
21739
  }
21614
21740
  };
21615
- const postChunkedHead = async (conv, fileSize) => {
21741
+ const postChunkedHead = async (head, fileSize) => {
21742
+ const conv = { ...head, chunk_head: true };
21616
21743
  const headers = { "content-type": "application/json" };
21617
21744
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
21618
21745
  const ac = new AbortController();
21619
21746
  const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
21620
21747
  try {
21621
- const res = await fetch(`${base}/api/sync`, {
21748
+ const res = await fetch(`${base2}/api/sync`, {
21622
21749
  method: "POST",
21623
21750
  headers,
21624
21751
  body: JSON.stringify({ conversations: [conv] }),
@@ -21628,14 +21755,15 @@ async function syncToTargetInScope(cred, opts = {}) {
21628
21755
  const body2 = await res.json().catch(() => ({}));
21629
21756
  const id = conv.session_id;
21630
21757
  const guarded = (body2.shrink_guarded ?? []).find((g) => g.session_id === id);
21758
+ const staged = (body2.rebuild_staged ?? []).includes(id);
21631
21759
  const mtime = conv.mtime;
21632
21760
  try {
21633
- if (!guarded) {
21634
- markSynced(base, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21761
+ if (staged || !guarded) {
21762
+ markSynced(base2, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21635
21763
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21636
- markSynced(base, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
21764
+ markSynced(base2, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
21637
21765
  } else {
21638
- markSynced(base, [{ id, mtime }]);
21766
+ markSynced(base2, [{ id, mtime }]);
21639
21767
  }
21640
21768
  } catch {
21641
21769
  }
@@ -21701,7 +21829,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21701
21829
  const m = process.memoryUsage();
21702
21830
  console.error(`[sync-trace] ${msg} \xB7 heap=${(m.heapUsed / 1048576).toFixed(0)}MB rss=${(m.rss / 1048576).toFixed(0)}MB`);
21703
21831
  } : null;
21704
- trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base}`);
21832
+ trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base2}`);
21705
21833
  if (upload.sessionMeta) {
21706
21834
  try {
21707
21835
  await reconcileFieldsForTarget(
@@ -21716,7 +21844,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21716
21844
  { convRefs: slice }
21717
21845
  );
21718
21846
  } catch (e) {
21719
- console.error(`[sync] field reconcile (${base}): ${e instanceof Error ? e.message : e}`);
21847
+ console.error(`[sync] field reconcile (${base2}): ${e instanceof Error ? e.message : e}`);
21720
21848
  }
21721
21849
  }
21722
21850
  trace?.("field reconcile done");
@@ -21758,7 +21886,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21758
21886
  }
21759
21887
  trace?.("conversation walk start");
21760
21888
  const walkStartedAt = Date.now();
21761
- const firstWalk = (ledger ?? getSyncedRows(base)).size === 0;
21889
+ const firstWalk = (ledger ?? getSyncedRows(base2)).size === 0;
21762
21890
  reportWalkProgress({ done: 0, total: slice.length, startedAt: walkStartedAt, complete: false, first: firstWalk });
21763
21891
  walkProgressForUpload = { done: 0, total: slice.length, complete: false };
21764
21892
  let traceN = 0;
@@ -21798,7 +21926,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21798
21926
  continue;
21799
21927
  } else {
21800
21928
  try {
21801
- markSynced(base, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21929
+ markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21802
21930
  } catch {
21803
21931
  }
21804
21932
  skipped++;
@@ -21821,7 +21949,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21821
21949
  const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
21822
21950
  const priorCursor = ackRow?.s ?? 0;
21823
21951
  const safeToAck = off > 0 && off <= priorCursor;
21824
- markSynced(base, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21952
+ markSynced(base2, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21825
21953
  } catch {
21826
21954
  }
21827
21955
  }
@@ -21831,7 +21959,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21831
21959
  if (!built) {
21832
21960
  if (ledger) {
21833
21961
  try {
21834
- markSynced(base, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21962
+ markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21835
21963
  } catch {
21836
21964
  }
21837
21965
  }
@@ -21888,7 +22016,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21888
22016
  walked++;
21889
22017
  if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
21890
22018
  }
21891
- const itemVersions = loadItemVersions(base);
22019
+ const itemVersions = loadItemVersions(base2);
21892
22020
  const seenTools = /* @__PURE__ */ new Set();
21893
22021
  const seenItemKeys = /* @__PURE__ */ new Map();
21894
22022
  const registry = buildSourceRegistry();
@@ -21982,7 +22110,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21982
22110
  await appendFlush();
21983
22111
  for (const id of appendResults.fullResyncNeeded) {
21984
22112
  try {
21985
- markFullResync(base, id);
22113
+ markFullResync(base2, id);
21986
22114
  } catch {
21987
22115
  }
21988
22116
  }
@@ -21995,7 +22123,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21995
22123
  await findingsBatch.drain();
21996
22124
  await drainInflight();
21997
22125
  {
21998
- const sentHashes = loadInventoryHashes(base);
22126
+ const sentHashes = loadInventoryHashes(base2);
21999
22127
  const entries = [];
22000
22128
  const hashes = {};
22001
22129
  for (const [sourceType, ids] of inventory) {
@@ -22009,7 +22137,7 @@ async function syncToTargetInScope(cred, opts = {}) {
22009
22137
  if (entries.length > 0) {
22010
22138
  try {
22011
22139
  await post({ inventory: entries });
22012
- saveInventoryHashes(base, hashes);
22140
+ saveInventoryHashes(base2, hashes);
22013
22141
  } catch (err) {
22014
22142
  console.error(`[sync] toolkit inventory not sent (it is retried next sync): ${err instanceof Error ? err.message : err}`);
22015
22143
  }
@@ -22020,7 +22148,7 @@ async function syncToTargetInScope(cred, opts = {}) {
22020
22148
  const rec = {};
22021
22149
  for (const t of seenTools) rec[t] = extractorVersionForTool(t);
22022
22150
  for (const [k, v] of seenItemKeys) rec[k] = v;
22023
- saveItemVersions(base, rec);
22151
+ saveItemVersions(base2, rec);
22024
22152
  } catch {
22025
22153
  }
22026
22154
  }
@@ -22033,9 +22161,9 @@ async function syncToTargetInScope(cred, opts = {}) {
22033
22161
  }
22034
22162
  if (tombstones.length > 0) {
22035
22163
  await post({ tombstones });
22036
- const data = getLedgerData(base);
22164
+ const data = getLedgerData(base2);
22037
22165
  for (const t of tombstones) delete data[t.session_id];
22038
- persistLedgerData(base, data);
22166
+ persistLedgerData(base2, data);
22039
22167
  }
22040
22168
  }
22041
22169
  if (opts.prune) await post({ prune_empty_sessions: true });
@@ -22363,8 +22491,13 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22363
22491
  const backend = getBackendForId(ref.prefixedId);
22364
22492
  if (!backend?.readFromOffset) return null;
22365
22493
  const mapPath = opts.mapPath ?? ((p) => p);
22366
- const { text: tailText, newOffset, skippedBytes } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
22494
+ const { text: tailText, newOffset, skippedBytes, baseLine } = await backend.readFromOffset(ref.prefixedId, fromOffset, SYNC_CHUNK_BYTES);
22367
22495
  if (newOffset <= fromOffset) return null;
22496
+ const fileSize = backend.fileSize?.(ref.prefixedId) ?? 0;
22497
+ const position = {
22498
+ ...typeof baseLine === "number" ? { base_line: baseLine } : {},
22499
+ final: fileSize > 0 && newOffset >= fileSize
22500
+ };
22368
22501
  const messages = skippedBytes ? [] : parseTailMessages(ref.toolId, tailText);
22369
22502
  const textMessages = messages.filter((m) => m.role !== "summary" && m.content?.trim());
22370
22503
  const hasContent = textMessages.length > 0 || messages.some((m) => m.toolCalls?.length);
@@ -22377,6 +22510,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22377
22510
  append: true,
22378
22511
  base_offset: fromOffset,
22379
22512
  from_offset: newOffset,
22513
+ ...position,
22380
22514
  redacted_text: "",
22381
22515
  envelope: { v: TRANSCRIPT_VERSION, messages: [], subagents: [] },
22382
22516
  mtime: Math.floor(ref.mtime) || 0
@@ -22423,6 +22557,7 @@ async function buildConversationTail(ref, fromOffset, opts = {}) {
22423
22557
  // equals its stored synced-through offset.
22424
22558
  from_offset: newOffset,
22425
22559
  // where it ends → new synced-through offset.
22560
+ ...position,
22426
22561
  redacted_text: redactedText,
22427
22562
  envelope,
22428
22563
  mtime: Math.floor(ref.mtime) || 0
@@ -22506,7 +22641,7 @@ var init_sync_client = __esm({
22506
22641
  ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22507
22642
  } catch {
22508
22643
  try {
22509
- if (true) ownVersion = "0.7.14";
22644
+ if (true) ownVersion = "0.7.16";
22510
22645
  } catch {
22511
22646
  }
22512
22647
  }
@@ -22665,9 +22800,9 @@ async function refreshUpdateCheck(force = false) {
22665
22800
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
22666
22801
  for (const cred of loadAllCredentials2()) {
22667
22802
  if (!cred.serverUrl) continue;
22668
- const base = cred.serverUrl.replace(/\/+$/, "");
22803
+ const base2 = cred.serverUrl.replace(/\/+$/, "");
22669
22804
  try {
22670
- const res = await fetchWithTimeout(`${base}/api/capabilities`, {
22805
+ const res = await fetchWithTimeout(`${base2}/api/capabilities`, {
22671
22806
  headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
22672
22807
  }, 5e3);
22673
22808
  if (!res.ok) continue;
@@ -23056,7 +23191,7 @@ __export(install_skills_exports, {
23056
23191
  skillsSourceDir: () => skillsSourceDir,
23057
23192
  uninstallSkills: () => uninstallSkills
23058
23193
  });
23059
- import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync43, realpathSync as realpathSync4 } from "fs";
23194
+ 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";
23060
23195
  import { createHash as createHash12 } from "crypto";
23061
23196
  import { join as join52, basename as basename23 } from "path";
23062
23197
  function skillsSourceDir() {
@@ -23297,18 +23432,18 @@ function usableDir(dir) {
23297
23432
  if (!isDir(dir)) {
23298
23433
  const missing = [];
23299
23434
  let cur = dir;
23300
- let base = false;
23435
+ let base2 = false;
23301
23436
  while (missing.length < MAX_MISSING_LEVELS) {
23302
23437
  const parent = dirname(cur);
23303
23438
  if (parent === cur) break;
23304
23439
  missing.push(cur);
23305
23440
  if (isDir(parent)) {
23306
- base = true;
23441
+ base2 = true;
23307
23442
  break;
23308
23443
  }
23309
23444
  cur = parent;
23310
23445
  }
23311
- if (!base) return false;
23446
+ if (!base2) return false;
23312
23447
  for (let i = missing.length - 1; i >= 0; i--) {
23313
23448
  try {
23314
23449
  mkdirSync(missing[i]);
@@ -29497,7 +29632,7 @@ function installCrashGuards() {
29497
29632
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
29498
29633
  });
29499
29634
  }
29500
- var RELAY_VERSION = true ? "0.7.14" : "0.0.0";
29635
+ var RELAY_VERSION = true ? "0.7.16" : "0.0.0";
29501
29636
  function daemonIdleSecs() {
29502
29637
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
29503
29638
  if (raw === void 0) return 900;