crispy-recall 0.2.0 → 0.3.0

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.
@@ -158,21 +158,21 @@ var require_file_uri_to_path = __commonJS({
158
158
  var rest = decodeURI(uri.substring(7));
159
159
  var firstSlash = rest.indexOf("/");
160
160
  var host = rest.substring(0, firstSlash);
161
- var path3 = rest.substring(firstSlash + 1);
161
+ var path2 = rest.substring(firstSlash + 1);
162
162
  if ("localhost" == host)
163
163
  host = "";
164
164
  if (host) {
165
165
  host = sep2 + sep2 + host;
166
166
  }
167
- path3 = path3.replace(/^(.+)\|/, "$1:");
167
+ path2 = path2.replace(/^(.+)\|/, "$1:");
168
168
  if (sep2 == "\\") {
169
- path3 = path3.replace(/\//g, "\\");
169
+ path2 = path2.replace(/\//g, "\\");
170
170
  }
171
- if (/^.+\:/.test(path3)) {
171
+ if (/^.+\:/.test(path2)) {
172
172
  } else {
173
- path3 = sep2 + path3;
173
+ path2 = sep2 + path2;
174
174
  }
175
- return host + path3;
175
+ return host + path2;
176
176
  }
177
177
  }
178
178
  });
@@ -181,18 +181,18 @@ var require_file_uri_to_path = __commonJS({
181
181
  var require_bindings = __commonJS({
182
182
  "node_modules/bindings/bindings.js"(exports2, module2) {
183
183
  var fs3 = require("fs");
184
- var path3 = require("path");
184
+ var path2 = require("path");
185
185
  var fileURLToPath3 = require_file_uri_to_path();
186
- var join8 = path3.join;
187
- var dirname3 = path3.dirname;
188
- var exists = fs3.accessSync && function(path4) {
186
+ var join7 = path2.join;
187
+ var dirname3 = path2.dirname;
188
+ var exists = fs3.accessSync && function(path3) {
189
189
  try {
190
- fs3.accessSync(path4);
190
+ fs3.accessSync(path3);
191
191
  } catch (e) {
192
192
  return false;
193
193
  }
194
194
  return true;
195
- } || fs3.existsSync || path3.existsSync;
195
+ } || fs3.existsSync || path2.existsSync;
196
196
  var defaults2 = {
197
197
  arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
198
198
  compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
@@ -238,13 +238,13 @@ var require_bindings = __commonJS({
238
238
  if (!opts.module_root) {
239
239
  opts.module_root = exports2.getRoot(exports2.getFileName());
240
240
  }
241
- if (path3.extname(opts.bindings) != ".node") {
241
+ if (path2.extname(opts.bindings) != ".node") {
242
242
  opts.bindings += ".node";
243
243
  }
244
244
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
245
245
  var tries = [], i = 0, l = opts.try.length, n, b, err;
246
246
  for (; i < l; i++) {
247
- n = join8.apply(
247
+ n = join7.apply(
248
248
  null,
249
249
  opts.try[i].map(function(p) {
250
250
  return opts[p] || p;
@@ -305,7 +305,7 @@ var require_bindings = __commonJS({
305
305
  if (dir === ".") {
306
306
  dir = process.cwd();
307
307
  }
308
- if (exists(join8(dir, "package.json")) || exists(join8(dir, "node_modules"))) {
308
+ if (exists(join7(dir, "package.json")) || exists(join7(dir, "node_modules"))) {
309
309
  return dir;
310
310
  }
311
311
  if (prev === dir) {
@@ -314,7 +314,7 @@ var require_bindings = __commonJS({
314
314
  );
315
315
  }
316
316
  prev = dir;
317
- dir = join8(dir, "..");
317
+ dir = join7(dir, "..");
318
318
  }
319
319
  };
320
320
  }
@@ -482,7 +482,7 @@ var require_backup = __commonJS({
482
482
  "node_modules/better-sqlite3/lib/methods/backup.js"(exports2, module2) {
483
483
  "use strict";
484
484
  var fs3 = require("fs");
485
- var path3 = require("path");
485
+ var path2 = require("path");
486
486
  var { promisify: promisify2 } = require("util");
487
487
  var { cppdb } = require_util();
488
488
  var fsAccess = promisify2(fs3.access);
@@ -506,7 +506,7 @@ var require_backup = __commonJS({
506
506
  throw new TypeError('The "attached" option cannot be an empty string');
507
507
  if (handler != null && typeof handler !== "function")
508
508
  throw new TypeError('Expected the "progress" option to be a function');
509
- await fsAccess(path3.dirname(filename)).catch(() => {
509
+ await fsAccess(path2.dirname(filename)).catch(() => {
510
510
  throw new TypeError("Cannot save backup because the directory does not exist");
511
511
  });
512
512
  const isNewFile = await fsAccess(filename).then(() => false, () => true);
@@ -837,7 +837,7 @@ var require_database = __commonJS({
837
837
  "node_modules/better-sqlite3/lib/database.js"(exports2, module2) {
838
838
  "use strict";
839
839
  var fs3 = require("fs");
840
- var path3 = require("path");
840
+ var path2 = require("path");
841
841
  var util = require_util();
842
842
  var SqliteError = require_sqlite_error();
843
843
  var DEFAULT_ADDON;
@@ -884,7 +884,7 @@ var require_database = __commonJS({
884
884
  addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
885
885
  } else if (typeof nativeBinding === "string") {
886
886
  const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : require;
887
- addon = requireFunc(path3.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
887
+ addon = requireFunc(path2.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
888
888
  } else {
889
889
  addon = nativeBinding;
890
890
  }
@@ -892,7 +892,7 @@ var require_database = __commonJS({
892
892
  addon.setErrorConstructor(SqliteError);
893
893
  addon.isInitialized = true;
894
894
  }
895
- if (!anonymous && !filename.startsWith("file:") && !fs3.existsSync(path3.dirname(filename))) {
895
+ if (!anonymous && !filename.startsWith("file:") && !fs3.existsSync(path2.dirname(filename))) {
896
896
  throw new TypeError("Cannot open database because the directory does not exist");
897
897
  }
898
898
  Object.defineProperties(this, {
@@ -2648,7 +2648,7 @@ var require_yauzl = __commonJS({
2648
2648
  exports2.ZipFile = ZipFile;
2649
2649
  exports2.Entry = Entry;
2650
2650
  exports2.RandomAccessReader = RandomAccessReader;
2651
- function open(path3, options, callback) {
2651
+ function open(path2, options, callback) {
2652
2652
  if (typeof options === "function") {
2653
2653
  callback = options;
2654
2654
  options = null;
@@ -2667,7 +2667,7 @@ var require_yauzl = __commonJS({
2667
2667
  options.strictFileNames = false;
2668
2668
  if (callback == null)
2669
2669
  callback = defaultCallback;
2670
- fs3.open(path3, "r", function(err, fd) {
2670
+ fs3.open(path2, "r", function(err, fd) {
2671
2671
  if (err)
2672
2672
  return callback(err);
2673
2673
  fromFd(fd, options, function(err2, zipfile) {
@@ -3334,7 +3334,7 @@ var require_extract_zip = __commonJS({
3334
3334
  var debug = require_src()("extract-zip");
3335
3335
  var { createWriteStream: createWriteStream2, promises: fs3 } = require("fs");
3336
3336
  var getStream = require_get_stream();
3337
- var path3 = require("path");
3337
+ var path2 = require("path");
3338
3338
  var { promisify: promisify2 } = require("util");
3339
3339
  var stream2 = require("stream");
3340
3340
  var yauzl = require_yauzl();
@@ -3371,12 +3371,12 @@ var require_extract_zip = __commonJS({
3371
3371
  this.zipfile.readEntry();
3372
3372
  return;
3373
3373
  }
3374
- const destDir = path3.dirname(path3.join(this.opts.dir, entry.fileName));
3374
+ const destDir = path2.dirname(path2.join(this.opts.dir, entry.fileName));
3375
3375
  try {
3376
3376
  await fs3.mkdir(destDir, { recursive: true });
3377
3377
  const canonicalDestDir = await fs3.realpath(destDir);
3378
- const relativeDestDir = path3.relative(this.opts.dir, canonicalDestDir);
3379
- if (relativeDestDir.split(path3.sep).includes("..")) {
3378
+ const relativeDestDir = path2.relative(this.opts.dir, canonicalDestDir);
3379
+ if (relativeDestDir.split(path2.sep).includes("..")) {
3380
3380
  throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`);
3381
3381
  }
3382
3382
  await this.extractEntry(entry);
@@ -3398,7 +3398,7 @@ var require_extract_zip = __commonJS({
3398
3398
  if (this.opts.onEntry) {
3399
3399
  this.opts.onEntry(entry, this.zipfile);
3400
3400
  }
3401
- const dest = path3.join(this.opts.dir, entry.fileName);
3401
+ const dest = path2.join(this.opts.dir, entry.fileName);
3402
3402
  const mode = entry.externalFileAttributes >> 16 & 65535;
3403
3403
  const IFMT2 = 61440;
3404
3404
  const IFDIR2 = 16384;
@@ -3413,7 +3413,7 @@ var require_extract_zip = __commonJS({
3413
3413
  isDir = madeBy === 0 && entry.externalFileAttributes === 16;
3414
3414
  debug("extracting entry", { filename: entry.fileName, isDir, isSymlink: symlink });
3415
3415
  const procMode = this.getExtractedMode(mode, isDir) & 511;
3416
- const destDir = isDir ? dest : path3.dirname(dest);
3416
+ const destDir = isDir ? dest : path2.dirname(dest);
3417
3417
  const mkdirOptions = { recursive: true };
3418
3418
  if (isDir) {
3419
3419
  mkdirOptions.mode = procMode;
@@ -3456,7 +3456,7 @@ var require_extract_zip = __commonJS({
3456
3456
  };
3457
3457
  module2.exports = async function(zipPath, opts) {
3458
3458
  debug("creating target directory", opts.dir);
3459
- if (!path3.isAbsolute(opts.dir)) {
3459
+ if (!path2.isAbsolute(opts.dir)) {
3460
3460
  throw new Error("Target directory is expected to be absolute");
3461
3461
  }
3462
3462
  await fs3.mkdir(opts.dir, { recursive: true });
@@ -3466,10 +3466,20 @@ var require_extract_zip = __commonJS({
3466
3466
  }
3467
3467
  });
3468
3468
 
3469
+ // src/installer/stable-node.ts
3470
+ var init_stable_node = __esm({
3471
+ "src/installer/stable-node.ts"() {
3472
+ "use strict";
3473
+ }
3474
+ });
3475
+
3469
3476
  // src/installer/settings-merge.ts
3470
3477
  var init_settings_merge = __esm({
3471
3478
  "src/installer/settings-merge.ts"() {
3472
3479
  "use strict";
3480
+ init_stable_node();
3481
+ init_config();
3482
+ init_paths();
3473
3483
  }
3474
3484
  });
3475
3485
 
@@ -3513,15 +3523,22 @@ var init_config = __esm({
3513
3523
  // src/recall/embedder.ts
3514
3524
  var embedder_exports = {};
3515
3525
  __export(embedder_exports, {
3526
+ LLAMA_RELEASE_TAG: () => LLAMA_RELEASE_TAG,
3527
+ MACOS_MIN_VERSION: () => MACOS_MIN_VERSION,
3528
+ buildUnzipInvocations: () => buildUnzipInvocations,
3516
3529
  disposeEmbedder: () => disposeEmbedder,
3517
3530
  embed: () => embed,
3518
3531
  embedBatch: () => embedBatch,
3532
+ embedBatchOneShot: () => embedBatchOneShot,
3519
3533
  ensureBinary: () => ensureBinary,
3520
3534
  ensureModel: () => ensureModel,
3535
+ extractArchive: () => extractArchive,
3521
3536
  getBinaryPath: () => getBinaryPath,
3522
3537
  getModelPath: () => getModelPath,
3523
3538
  hasNvidiaGpu: () => hasNvidiaGpu,
3524
- initEmbedder: () => initEmbedder
3539
+ initEmbedder: () => initEmbedder,
3540
+ installExtractedBinaries: () => installExtractedBinaries,
3541
+ withTimeout: () => withTimeout
3525
3542
  });
3526
3543
  function gpuRuntime() {
3527
3544
  if (_gpuRuntime === void 0) {
@@ -3695,6 +3712,82 @@ async function performBinaryDownload(binPath) {
3695
3712
  }
3696
3713
  throw new Error("No suitable llama-embedding binary found");
3697
3714
  }
3715
+ function withTimeout(promise, ms, label) {
3716
+ let timer;
3717
+ const timeout = new Promise((_, reject) => {
3718
+ timer = setTimeout(
3719
+ () => reject(new Error(`${label} timed out after ${Math.round(ms / 1e3)}s`)),
3720
+ ms
3721
+ );
3722
+ });
3723
+ promise.catch(() => {
3724
+ });
3725
+ return Promise.race([promise, timeout]).finally(() => {
3726
+ if (timer)
3727
+ clearTimeout(timer);
3728
+ });
3729
+ }
3730
+ function psQuote(p) {
3731
+ return p.replace(/'/g, "''");
3732
+ }
3733
+ function buildUnzipInvocations(plat, archivePath, dir) {
3734
+ if (plat === "win32") {
3735
+ return [{
3736
+ cmd: "powershell",
3737
+ args: [
3738
+ "-NoProfile",
3739
+ "-NonInteractive",
3740
+ "-Command",
3741
+ `Expand-Archive -LiteralPath '${psQuote(archivePath)}' -DestinationPath '${psQuote(dir)}' -Force`
3742
+ ]
3743
+ }];
3744
+ }
3745
+ const unzip = { cmd: "unzip", args: ["-o", "-q", archivePath, "-d", dir] };
3746
+ return plat === "darwin" ? [unzip, { cmd: "ditto", args: ["-x", "-k", archivePath, dir] }] : [unzip];
3747
+ }
3748
+ async function osUnzip(archivePath, dir) {
3749
+ (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
3750
+ const invocations = buildUnzipInvocations((0, import_node_os2.platform)(), archivePath, dir);
3751
+ let lastErr;
3752
+ for (const { cmd, args } of invocations) {
3753
+ try {
3754
+ await execFileAsync(cmd, args, { timeout: EXTRACT_TIMEOUT_MS, windowsHide: true });
3755
+ return;
3756
+ } catch (err) {
3757
+ lastErr = err;
3758
+ }
3759
+ }
3760
+ throw lastErr instanceof Error ? lastErr : new Error(`Native unzip failed for ${archivePath}`);
3761
+ }
3762
+ async function extractArchive(archivePath, baseDir, assetName, timeoutMs = EXTRACT_TIMEOUT_MS) {
3763
+ try {
3764
+ await withTimeout((0, import_extract_zip.default)(archivePath, { dir: baseDir }), timeoutMs, `Extracting ${assetName}`);
3765
+ return baseDir;
3766
+ } catch (err) {
3767
+ const msg = err instanceof Error ? err.message : String(err);
3768
+ log({
3769
+ source: "recall-catchup",
3770
+ level: "warn",
3771
+ summary: `Bundled unzip failed for ${assetName} (${msg}); falling back to system unzip`
3772
+ });
3773
+ const fallbackDir = (0, import_node_path4.join)(baseDir, "__os_unzip__");
3774
+ await osUnzip(archivePath, fallbackDir);
3775
+ return fallbackDir;
3776
+ }
3777
+ }
3778
+ async function installExtractedBinaries(extractedDir, destDir = binDir()) {
3779
+ const buildBinDir = (0, import_node_path4.join)(extractedDir, "build", "bin");
3780
+ const sourceDir = (0, import_node_fs4.existsSync)(buildBinDir) ? buildBinDir : extractedDir;
3781
+ (0, import_node_fs4.mkdirSync)(destDir, { recursive: true });
3782
+ let copiedCount = 0;
3783
+ for (const file of await (0, import_promises.readdir)(sourceDir)) {
3784
+ if (!isWantedFile(file))
3785
+ continue;
3786
+ await (0, import_promises.cp)((0, import_node_path4.join)(sourceDir, file), (0, import_node_path4.join)(destDir, file), { force: true });
3787
+ copiedCount++;
3788
+ }
3789
+ return copiedCount;
3790
+ }
3698
3791
  async function downloadAndExtract(assetName, binPath) {
3699
3792
  const url = `https://github.com/ggml-org/llama.cpp/releases/download/${LLAMA_RELEASE_TAG}/${assetName}`;
3700
3793
  const isGpu = assetName.includes("cuda") || assetName.includes("vulkan");
@@ -3715,18 +3808,8 @@ async function downloadAndExtract(assetName, binPath) {
3715
3808
  const tmpExtractDir = (0, import_node_path4.join)((0, import_node_os2.tmpdir)(), `llama-extract-${Date.now()}`);
3716
3809
  (0, import_node_fs4.mkdirSync)(tmpExtractDir, { recursive: true });
3717
3810
  try {
3718
- await (0, import_extract_zip.default)(archivePath, { dir: tmpExtractDir });
3719
- const buildBinDir = (0, import_node_path4.join)(tmpExtractDir, "build", "bin");
3720
- const sourceDir = (0, import_node_fs4.existsSync)(buildBinDir) ? buildBinDir : tmpExtractDir;
3721
- let copiedCount = 0;
3722
- for (const file of await (0, import_promises.readdir)(sourceDir)) {
3723
- if (!isWantedFile(file))
3724
- continue;
3725
- const src = (0, import_node_path4.join)(sourceDir, file);
3726
- const dest = (0, import_node_path4.join)(binDir(), file);
3727
- await (0, import_promises.cp)(src, dest, { force: true });
3728
- copiedCount++;
3729
- }
3811
+ const extractedDir = await extractArchive(archivePath, tmpExtractDir, assetName);
3812
+ const copiedCount = await installExtractedBinaries(extractedDir);
3730
3813
  if (copiedCount === 0) {
3731
3814
  throw new Error(`No binaries found in archive ${assetName}`);
3732
3815
  }
@@ -4147,13 +4230,13 @@ async function retryServer() {
4147
4230
  serverRetryPromise = null;
4148
4231
  }
4149
4232
  }
4150
- function httpPost(socketPath, path3, body, timeoutMs) {
4233
+ function httpPost(socketPath, path2, body, timeoutMs) {
4151
4234
  return new Promise((resolve, reject) => {
4152
4235
  const data = JSON.stringify(body);
4153
4236
  const req = (0, import_node_http.request)(
4154
4237
  {
4155
4238
  socketPath,
4156
- path: path3,
4239
+ path: path2,
4157
4240
  method: "POST",
4158
4241
  headers: {
4159
4242
  "Content-Type": "application/json",
@@ -4206,7 +4289,7 @@ async function embedViaHttp(socketPath, texts) {
4206
4289
  return new Float32Array(item.embedding);
4207
4290
  });
4208
4291
  }
4209
- async function embedViaProcess(texts, modelPath) {
4292
+ async function embedViaProcess(texts, modelPath, opts) {
4210
4293
  if (!binaryPath)
4211
4294
  throw new Error("llama-embedding binary not available");
4212
4295
  const sanitized = texts.map((t) => t.replaceAll(BATCH_SEPARATOR, " "));
@@ -4255,7 +4338,8 @@ async function embedViaProcess(texts, modelPath) {
4255
4338
  }
4256
4339
  const { stdout, stderr } = await execFileAsync(binaryPath, args, {
4257
4340
  maxBuffer: 2 * 1024 * 1024,
4258
- env
4341
+ env,
4342
+ ...opts?.timeoutMs ? { timeout: opts.timeoutMs, killSignal: "SIGKILL" } : {}
4259
4343
  });
4260
4344
  if (stderr) {
4261
4345
  log({
@@ -4297,6 +4381,19 @@ async function embedBatch(texts) {
4297
4381
  return [];
4298
4382
  return withEmbedMutex(() => embedBatchInner(texts));
4299
4383
  }
4384
+ async function embedBatchOneShot(texts, opts) {
4385
+ if (texts.length === 0)
4386
+ return [];
4387
+ return withEmbedMutex(async () => {
4388
+ if (!binaryPath) {
4389
+ await ensureBinary();
4390
+ }
4391
+ if (!binaryPath)
4392
+ throw new Error("llama-embedding binary not available");
4393
+ const modelPath = await ensureModel();
4394
+ return embedViaProcess(texts, modelPath, opts);
4395
+ });
4396
+ }
4300
4397
  async function embedBatchInner(texts) {
4301
4398
  if (!binaryPath) {
4302
4399
  await ensureBinary();
@@ -4350,7 +4447,7 @@ async function embedBatchInner(texts) {
4350
4447
  async function disposeEmbedder() {
4351
4448
  await killServer();
4352
4449
  }
4353
- var import_node_child_process, import_node_fs4, import_promises, import_node_http, import_node_path4, import_node_os2, import_node_util, import_extract_zip, execFileAsync, _gpuRuntime, MODEL_FILENAME, MODEL_URL, EXPECTED_DIMS, BATCH_SEPARATOR, MAX_ARG_BYTES, LLAMA_RELEASE_TAG, BIN_NAME, SERVER_BIN_NAME, SERVER_SUPPORTED, SERVER_THRESHOLD, IDLE_TIMEOUT_MS, HEALTH_POLL_INTERVAL_MS, HEALTH_POLL_TIMEOUT_MS, HTTP_REQUEST_TIMEOUT_MS, SERVER_KILL_TIMEOUT_MS, SERVER_COOLDOWN_MS, embedQueue, embedRunning, binaryPath, downloadPromise, binaryDownloadPromise, serverProcess, activeSocketPath, idleTimer, serverStartPromise, serverCooldownUntil, serverRetryPromise, activeServerRequests, WANTED_FILES, WANTED_LIB_PATTERNS;
4450
+ var import_node_child_process, import_node_fs4, import_promises, import_node_http, import_node_path4, import_node_os2, import_node_util, import_extract_zip, execFileAsync, _gpuRuntime, MODEL_FILENAME, MODEL_URL, EXPECTED_DIMS, BATCH_SEPARATOR, MAX_ARG_BYTES, LLAMA_RELEASE_TAG, MACOS_MIN_VERSION, BIN_NAME, SERVER_BIN_NAME, SERVER_SUPPORTED, SERVER_THRESHOLD, IDLE_TIMEOUT_MS, HEALTH_POLL_INTERVAL_MS, HEALTH_POLL_TIMEOUT_MS, HTTP_REQUEST_TIMEOUT_MS, SERVER_KILL_TIMEOUT_MS, SERVER_COOLDOWN_MS, EXTRACT_TIMEOUT_MS, embedQueue, embedRunning, binaryPath, downloadPromise, binaryDownloadPromise, serverProcess, activeSocketPath, idleTimer, serverStartPromise, serverCooldownUntil, serverRetryPromise, activeServerRequests, WANTED_FILES, WANTED_LIB_PATTERNS;
4354
4451
  var init_embedder = __esm({
4355
4452
  "src/recall/embedder.ts"() {
4356
4453
  "use strict";
@@ -4372,6 +4469,7 @@ var init_embedder = __esm({
4372
4469
  BATCH_SEPARATOR = "<#sep#>";
4373
4470
  MAX_ARG_BYTES = (0, import_node_os2.platform)() === "win32" ? 0 : 1e5;
4374
4471
  LLAMA_RELEASE_TAG = "b5300";
4472
+ MACOS_MIN_VERSION = { arm64: "14.0", x64: "13.7" };
4375
4473
  BIN_NAME = (0, import_node_os2.platform)() === "win32" ? "llama-embedding.exe" : "llama-embedding";
4376
4474
  SERVER_BIN_NAME = (0, import_node_os2.platform)() === "win32" ? "llama-server.exe" : "llama-server";
4377
4475
  SERVER_SUPPORTED = (0, import_node_os2.platform)() !== "win32";
@@ -4382,6 +4480,7 @@ var init_embedder = __esm({
4382
4480
  HTTP_REQUEST_TIMEOUT_MS = 12e4;
4383
4481
  SERVER_KILL_TIMEOUT_MS = 5e3;
4384
4482
  SERVER_COOLDOWN_MS = 6e4;
4483
+ EXTRACT_TIMEOUT_MS = 12e4;
4385
4484
  embedQueue = [];
4386
4485
  embedRunning = false;
4387
4486
  binaryPath = null;
@@ -4681,13 +4780,25 @@ var BindingLoadError = class extends Error {
4681
4780
  constructor(dbPath2, cause) {
4682
4781
  super(
4683
4782
  `recall: failed to load the better-sqlite3 native binding while opening ${dbPath2}: ${cause.message}
4684
- The binding is ABI-locked to the Node it was built for \u2014 run \`recall doctor\` (usually fixed by re-running \`recall install\`).`
4783
+ The native SQLite binding is ABI-locked to the Node it was built for. This is usually one of:
4784
+ \u2022 recall is running under a different Node than the one npm installed it with (e.g. Homebrew node vs nvm) \u2014 reinstall with \`npm install -g crispy-recall\` using the node on your PATH, then \`recall install\`.
4785
+ \u2022 no prebuilt binary matched your Node version and it could not compile \u2014 use Node 22 LTS or 24+ (Node 23 has no prebuilt SQLite binding), and on macOS make sure Xcode Command Line Tools are installed (\`xcode-select --install\`).
4786
+ Run \`recall doctor\` for details.`
4685
4787
  );
4686
4788
  this.dbPath = dbPath2;
4687
4789
  this.cause = cause;
4688
4790
  this.name = "BindingLoadError";
4689
4791
  }
4690
4792
  };
4793
+ var MigrationPendingError = class extends Error {
4794
+ constructor(dbPath2) {
4795
+ super(
4796
+ "recall: this database needs a one-time schema migration \u2014 run `recall install` to finish it. (Normal commands refuse to rewrite the index unattended.)"
4797
+ );
4798
+ this.dbPath = dbPath2;
4799
+ this.name = "MigrationPendingError";
4800
+ }
4801
+ };
4691
4802
  function isBindingLoadError(e) {
4692
4803
  if (!e || typeof e !== "object")
4693
4804
  return false;
@@ -4702,7 +4813,7 @@ function isBindingLoadError(e) {
4702
4813
  }
4703
4814
  var db = null;
4704
4815
  var currentDbPath = null;
4705
- function getDb(dbPath2) {
4816
+ function getDb(dbPath2, opts) {
4706
4817
  if (db && currentDbPath === dbPath2)
4707
4818
  return db;
4708
4819
  if (db) {
@@ -4717,12 +4828,45 @@ function getDb(dbPath2) {
4717
4828
  raw.close();
4718
4829
  throw e;
4719
4830
  }
4720
- db = createAdapter(raw);
4831
+ const adapter = createAdapter(raw);
4832
+ if (isRetrievalMigrationPending(adapter)) {
4833
+ if (!opts?.allowPendingMigration) {
4834
+ raw.close();
4835
+ throw new MigrationPendingError(dbPath2);
4836
+ }
4837
+ db = adapter;
4838
+ currentDbPath = dbPath2;
4839
+ log({ source: "db", level: "info", summary: `DB: opened pending-migration DB at ${dbPath2} (installer mode)` });
4840
+ return db;
4841
+ }
4842
+ db = adapter;
4721
4843
  currentDbPath = dbPath2;
4722
4844
  ensureSchema(db);
4723
4845
  log({ source: "db", level: "info", summary: `DB: initialized at ${dbPath2}` });
4724
4846
  return db;
4725
4847
  }
4848
+ var RETRIEVAL_MIGRATION_KEY = "retrieval_class_migration";
4849
+ function isRetrievalMigrationPending(d) {
4850
+ try {
4851
+ const hasMessages = d.get(
4852
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'messages'`
4853
+ );
4854
+ if (!hasMessages)
4855
+ return false;
4856
+ const hasMeta = d.get(
4857
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'schema_meta'`
4858
+ );
4859
+ if (!hasMeta)
4860
+ return true;
4861
+ const row = d.get(
4862
+ `SELECT value FROM schema_meta WHERE key = ?`,
4863
+ [RETRIEVAL_MIGRATION_KEY]
4864
+ );
4865
+ return row?.value !== "complete";
4866
+ } catch {
4867
+ return true;
4868
+ }
4869
+ }
4726
4870
  function closeDb() {
4727
4871
  if (db) {
4728
4872
  db.close();
@@ -4833,16 +4977,18 @@ function cleanupWasmArtifacts(dbPath2) {
4833
4977
  } catch {
4834
4978
  }
4835
4979
  }
4836
- function ensureSchema(db3) {
4837
- db3.exec(`
4980
+ var RETRIEVAL_SCHEMA_DDL = {
4981
+ /** messages + provenance tables + indexes (no FTS objects). */
4982
+ tables: `
4838
4983
  CREATE TABLE IF NOT EXISTS messages (
4839
- message_id TEXT PRIMARY KEY,
4840
- session_id TEXT NOT NULL,
4841
- message_seq INTEGER NOT NULL,
4842
- message_text TEXT NOT NULL,
4843
- project_id TEXT,
4844
- created_at INTEGER NOT NULL,
4845
- message_role TEXT,
4984
+ message_id TEXT PRIMARY KEY,
4985
+ session_id TEXT NOT NULL,
4986
+ message_seq INTEGER NOT NULL,
4987
+ message_text TEXT NOT NULL,
4988
+ project_id TEXT,
4989
+ created_at INTEGER NOT NULL,
4990
+ message_role TEXT,
4991
+ retrieval_class TEXT NOT NULL DEFAULT 'hot',
4846
4992
  UNIQUE(session_id, message_id)
4847
4993
  );
4848
4994
 
@@ -4854,68 +5000,134 @@ function ensureSchema(db3) {
4854
5000
  -- embed drain and every Stop-hook catch-up. This index serves the ORDER BY so
4855
5001
  -- the planner walks it and early-terminates at LIMIT instead.
4856
5002
  CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
4857
- `);
4858
- db3.exec(`
5003
+
5004
+ -- session_provenance \u2014 durable per-session classification evidence:
5005
+ -- canonical id, vendor, root-vs-agent kind, parent thread, hook/agent
5006
+ -- metadata, and the transcript path \u2192 canonical id mapping that lets
5007
+ -- later T1/mtime scans resolve a child to ONE identity.
5008
+ CREATE TABLE IF NOT EXISTS session_provenance (
5009
+ session_id TEXT PRIMARY KEY,
5010
+ vendor TEXT NOT NULL,
5011
+ kind TEXT NOT NULL,
5012
+ parent_session_id TEXT,
5013
+ agent_depth INTEGER,
5014
+ agent_meta TEXT,
5015
+ transcript_path TEXT,
5016
+ updated_at INTEGER NOT NULL
5017
+ );
5018
+ CREATE INDEX IF NOT EXISTS idx_provenance_path ON session_provenance(transcript_path);
5019
+
5020
+ -- session_aliases \u2014 alternate identifiers (e.g. a hook agent_id that
5021
+ -- differs from the child rollout's session-meta UUID) \u2192 canonical id.
5022
+ CREATE TABLE IF NOT EXISTS session_aliases (
5023
+ alias_id TEXT PRIMARY KEY,
5024
+ session_id TEXT NOT NULL,
5025
+ source TEXT,
5026
+ created_at INTEGER NOT NULL
5027
+ );
5028
+
5029
+ CREATE TABLE IF NOT EXISTS schema_meta (
5030
+ key TEXT PRIMARY KEY,
5031
+ value TEXT NOT NULL
5032
+ );
5033
+ `,
5034
+ /** Filtered external-content view + FTS + four-state triggers + vocab. */
5035
+ fts: `
5036
+ -- Filtered external-content source: FTS5 rebuild/integrity-check read
5037
+ -- THIS view, so 'rebuild' repopulates only hot rows and the (rank-1)
5038
+ -- integrity check compares against the filtered corpus.
5039
+ CREATE VIEW IF NOT EXISTS searchable_messages AS
5040
+ SELECT rowid, message_text FROM messages WHERE retrieval_class = 'hot';
5041
+
4859
5042
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
4860
5043
  message_text,
4861
- content=messages,
5044
+ content=searchable_messages,
4862
5045
  content_rowid=rowid,
4863
5046
  tokenize='porter unicode61'
4864
5047
  );
4865
5048
 
4866
- CREATE TRIGGER IF NOT EXISTS messages_fts_ai AFTER INSERT ON messages BEGIN
5049
+ -- Four-state trigger behavior:
5050
+ -- insert hot \u2192 add | insert agent \u2192 no-op
5051
+ -- delete hot \u2192 delete | delete agent \u2192 no-op
5052
+ -- update hot\u2192hot \u2192 delete old + add new
5053
+ -- update hot\u2192agent \u2192 delete old only
5054
+ -- update agent\u2192hot \u2192 add new only
5055
+ -- update agent\u2192agent \u2192 no-op
5056
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ai AFTER INSERT ON messages
5057
+ WHEN new.retrieval_class = 'hot'
5058
+ BEGIN
4867
5059
  INSERT INTO messages_fts(rowid, message_text) VALUES (new.rowid, new.message_text);
4868
5060
  END;
4869
5061
 
4870
- CREATE TRIGGER IF NOT EXISTS messages_fts_ad AFTER DELETE ON messages BEGIN
5062
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ad AFTER DELETE ON messages
5063
+ WHEN old.retrieval_class = 'hot'
5064
+ BEGIN
4871
5065
  INSERT INTO messages_fts(messages_fts, rowid, message_text)
4872
5066
  VALUES ('delete', old.rowid, old.message_text);
4873
5067
  END;
4874
5068
 
4875
5069
  CREATE TRIGGER IF NOT EXISTS messages_fts_au AFTER UPDATE ON messages BEGIN
4876
5070
  INSERT INTO messages_fts(messages_fts, rowid, message_text)
4877
- VALUES ('delete', old.rowid, old.message_text);
4878
- INSERT INTO messages_fts(rowid, message_text) VALUES (new.rowid, new.message_text);
5071
+ SELECT 'delete', old.rowid, old.message_text WHERE old.retrieval_class = 'hot';
5072
+ INSERT INTO messages_fts(rowid, message_text)
5073
+ SELECT new.rowid, new.message_text WHERE new.retrieval_class = 'hot';
4879
5074
  END;
4880
- `);
4881
- db3.exec(`
5075
+
4882
5076
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts_vocab
4883
5077
  USING fts5vocab(messages_fts, 'row');
4884
- `);
4885
- db3.exec(`
4886
- CREATE VIRTUAL TABLE IF NOT EXISTS _stem USING fts5(
4887
- t, tokenize='porter unicode61'
4888
- );
4889
- CREATE VIRTUAL TABLE IF NOT EXISTS _stem_vocab
4890
- USING fts5vocab(_stem, 'row');
4891
- `);
4892
- db3.exec(`
4893
- CREATE TABLE IF NOT EXISTS message_vectors (
4894
- message_id TEXT PRIMARY KEY REFERENCES messages(message_id) ON DELETE CASCADE,
4895
- embedding_q8 BLOB NOT NULL,
4896
- norm REAL NOT NULL,
4897
- quant_scale REAL NOT NULL,
4898
- embed_version INTEGER NOT NULL DEFAULT 1
4899
- );
4900
- `);
4901
- const hasEmbedVersion = () => db3.all(`PRAGMA table_info(message_vectors)`).some((c) => c.name === "embed_version");
4902
- if (!hasEmbedVersion()) {
5078
+ `
5079
+ };
5080
+ function ensureSchema(db3) {
5081
+ db3.exec("BEGIN IMMEDIATE");
5082
+ try {
5083
+ db3.exec(RETRIEVAL_SCHEMA_DDL.tables);
5084
+ db3.exec(RETRIEVAL_SCHEMA_DDL.fts);
5085
+ db3.exec(`
5086
+ CREATE VIRTUAL TABLE IF NOT EXISTS _stem USING fts5(
5087
+ t, tokenize='porter unicode61'
5088
+ );
5089
+ CREATE VIRTUAL TABLE IF NOT EXISTS _stem_vocab
5090
+ USING fts5vocab(_stem, 'row');
5091
+ `);
5092
+ db3.exec(`
5093
+ CREATE TABLE IF NOT EXISTS message_vectors (
5094
+ message_id TEXT PRIMARY KEY REFERENCES messages(message_id) ON DELETE CASCADE,
5095
+ embedding_q8 BLOB NOT NULL,
5096
+ norm REAL NOT NULL,
5097
+ quant_scale REAL NOT NULL,
5098
+ embed_version INTEGER NOT NULL DEFAULT 1
5099
+ );
5100
+ `);
5101
+ const hasEmbedVersion = () => db3.all(`PRAGMA table_info(message_vectors)`).some((c) => c.name === "embed_version");
5102
+ if (!hasEmbedVersion()) {
5103
+ try {
5104
+ db3.exec(`ALTER TABLE message_vectors ADD COLUMN embed_version INTEGER NOT NULL DEFAULT 1`);
5105
+ } catch (e) {
5106
+ if (!hasEmbedVersion())
5107
+ throw e;
5108
+ }
5109
+ }
5110
+ db3.exec(`
5111
+ CREATE TABLE IF NOT EXISTS ingest_watermark (
5112
+ transcript_path TEXT PRIMARY KEY,
5113
+ last_mtime INTEGER NOT NULL,
5114
+ last_size INTEGER NOT NULL,
5115
+ vendor TEXT NOT NULL
5116
+ );
5117
+ CREATE INDEX IF NOT EXISTS idx_watermark_vendor ON ingest_watermark(vendor);
5118
+ `);
5119
+ db3.exec(`
5120
+ INSERT OR IGNORE INTO schema_meta(key, value)
5121
+ VALUES ('${RETRIEVAL_MIGRATION_KEY}', 'complete');
5122
+ `);
5123
+ db3.exec("COMMIT");
5124
+ } catch (e) {
4903
5125
  try {
4904
- db3.exec(`ALTER TABLE message_vectors ADD COLUMN embed_version INTEGER NOT NULL DEFAULT 1`);
4905
- } catch (e) {
4906
- if (!hasEmbedVersion())
4907
- throw e;
5126
+ db3.exec("ROLLBACK");
5127
+ } catch {
4908
5128
  }
5129
+ throw e;
4909
5130
  }
4910
- db3.exec(`
4911
- CREATE TABLE IF NOT EXISTS ingest_watermark (
4912
- transcript_path TEXT PRIMARY KEY,
4913
- last_mtime INTEGER NOT NULL,
4914
- last_size INTEGER NOT NULL,
4915
- vendor TEXT NOT NULL
4916
- );
4917
- CREATE INDEX IF NOT EXISTS idx_watermark_vendor ON ingest_watermark(vendor);
4918
- `);
4919
5131
  }
4920
5132
 
4921
5133
  // src/recall/message-store.ts
@@ -4952,7 +5164,7 @@ function ensureDir2() {
4952
5164
  }
4953
5165
  }
4954
5166
  function insertMessages(messages, opts) {
4955
- if (messages.length === 0 && !opts?.replaceSessionId)
5167
+ if (messages.length === 0 && !opts?.replaceSessionId && !opts?.provenance)
4956
5168
  return;
4957
5169
  ensureDir2();
4958
5170
  const d = db2();
@@ -4968,8 +5180,8 @@ function insertMessages(messages, opts) {
4968
5180
  }
4969
5181
  const stmt = d.prepare(
4970
5182
  `INSERT OR IGNORE INTO messages
4971
- (message_id, session_id, message_seq, message_text, project_id, created_at, message_role)
4972
- VALUES (?, ?, ?, ?, ?, ?, ?)`
5183
+ (message_id, session_id, message_seq, message_text, project_id, created_at, message_role, retrieval_class)
5184
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
4973
5185
  );
4974
5186
  for (const m of messages) {
4975
5187
  stmt.run([
@@ -4979,9 +5191,55 @@ function insertMessages(messages, opts) {
4979
5191
  m.message_text,
4980
5192
  m.project_id,
4981
5193
  m.created_at,
4982
- m.message_role
5194
+ m.message_role,
5195
+ m.retrieval_class ?? "hot"
4983
5196
  ]);
4984
5197
  }
5198
+ if (opts?.provenance) {
5199
+ const p = opts.provenance;
5200
+ d.run(
5201
+ `INSERT INTO session_provenance
5202
+ (session_id, vendor, kind, parent_session_id, agent_depth, agent_meta, transcript_path, updated_at)
5203
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
5204
+ ON CONFLICT(session_id) DO UPDATE SET
5205
+ vendor = excluded.vendor,
5206
+ kind = excluded.kind,
5207
+ parent_session_id = COALESCE(excluded.parent_session_id, session_provenance.parent_session_id),
5208
+ agent_depth = COALESCE(excluded.agent_depth, session_provenance.agent_depth),
5209
+ agent_meta = COALESCE(excluded.agent_meta, session_provenance.agent_meta),
5210
+ transcript_path = COALESCE(excluded.transcript_path, session_provenance.transcript_path),
5211
+ updated_at = excluded.updated_at`,
5212
+ [
5213
+ p.sessionId,
5214
+ p.vendor,
5215
+ p.kind,
5216
+ p.parentSessionId,
5217
+ p.agentDepth,
5218
+ p.agentMeta ? JSON.stringify(p.agentMeta) : null,
5219
+ p.transcriptPath,
5220
+ Date.now()
5221
+ ]
5222
+ );
5223
+ if (p.kind === "agent") {
5224
+ d.run(
5225
+ `UPDATE messages SET retrieval_class = 'agent'
5226
+ WHERE session_id = ? AND retrieval_class != 'agent'`,
5227
+ [p.sessionId]
5228
+ );
5229
+ d.run(
5230
+ `DELETE FROM message_vectors WHERE message_id IN
5231
+ (SELECT message_id FROM messages WHERE session_id = ?)`,
5232
+ [p.sessionId]
5233
+ );
5234
+ }
5235
+ }
5236
+ for (const a of opts?.aliases ?? []) {
5237
+ d.run(
5238
+ `INSERT OR REPLACE INTO session_aliases (alias_id, session_id, source, created_at)
5239
+ VALUES (?, ?, ?, ?)`,
5240
+ [a.aliasId, a.sessionId, a.source, Date.now()]
5241
+ );
5242
+ }
4985
5243
  d.exec("COMMIT");
4986
5244
  } catch (e) {
4987
5245
  d.exec("ROLLBACK");
@@ -4998,15 +5256,17 @@ function insertMessageVectors(records) {
4998
5256
  const stmt = d.prepare(
4999
5257
  `INSERT OR REPLACE INTO message_vectors
5000
5258
  (message_id, embedding_q8, norm, quant_scale, embed_version)
5001
- VALUES (?, ?, ?, ?, ?)`
5259
+ SELECT m.message_id, ?, ?, ?, ?
5260
+ FROM messages m
5261
+ WHERE m.message_id = ? AND m.retrieval_class = 'hot'`
5002
5262
  );
5003
5263
  for (const r of records) {
5004
5264
  stmt.run([
5005
- r.messageId,
5006
5265
  Buffer.from(r.embeddingQ8.buffer, r.embeddingQ8.byteOffset, r.embeddingQ8.byteLength),
5007
5266
  r.norm,
5008
5267
  r.quantScale,
5009
- EMBED_VERSION
5268
+ EMBED_VERSION,
5269
+ r.messageId
5010
5270
  ]);
5011
5271
  }
5012
5272
  d.exec("COMMIT");
@@ -5021,12 +5281,13 @@ function getUnembeddedMessages(limit) {
5021
5281
  const rows = db2().all(
5022
5282
  `SELECT m.message_id, m.session_id, m.message_text,
5023
5283
  (SELECT p.message_text FROM messages p
5024
- WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq
5284
+ WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq AND p.retrieval_class = 'hot'
5025
5285
  ORDER BY p.message_seq DESC LIMIT 1) AS prev_text
5026
5286
  FROM messages m
5027
5287
  WHERE m.message_text != ''
5288
+ AND m.retrieval_class = 'hot'
5028
5289
  AND (LENGTH(m.message_text) >= ${MIN_EMBED_CHARS}
5029
- OR EXISTS (SELECT 1 FROM messages p2 WHERE p2.session_id = m.session_id AND p2.message_seq < m.message_seq))
5290
+ OR EXISTS (SELECT 1 FROM messages p2 WHERE p2.session_id = m.session_id AND p2.message_seq < m.message_seq AND p2.retrieval_class = 'hot'))
5030
5291
  AND NOT EXISTS (SELECT 1 FROM message_vectors mv WHERE mv.message_id = m.message_id AND mv.embed_version = ?)
5031
5292
  ORDER BY m.created_at DESC
5032
5293
  LIMIT ?`,
@@ -5047,6 +5308,260 @@ function getUnembeddedMessages(limit) {
5047
5308
  }
5048
5309
  }
5049
5310
 
5311
+ // src/recall/session-classifier.ts
5312
+ init_paths();
5313
+ init_log();
5314
+
5315
+ // src/adapters/codex/codex-jsonl-reader.ts
5316
+ var fs = __toESM(require("fs"));
5317
+ init_log();
5318
+ function parseCodexJsonlFile(filepath) {
5319
+ try {
5320
+ const content = fs.readFileSync(filepath, "utf-8");
5321
+ const lines = content.split("\n");
5322
+ const records = [];
5323
+ for (const line of lines) {
5324
+ const trimmed = line.trim();
5325
+ if (!trimmed)
5326
+ continue;
5327
+ try {
5328
+ const record = JSON.parse(trimmed);
5329
+ records.push(record);
5330
+ } catch (err) {
5331
+ log({ level: "warn", source: "codex-jsonl-reader", summary: `Skipping unparseable line: ${err.message}` });
5332
+ }
5333
+ }
5334
+ return records;
5335
+ } catch (error) {
5336
+ log({ level: "error", source: "codex-jsonl-reader", summary: `Failed to read ${filepath}: ${error instanceof Error ? error.message : String(error)}`, data: { filepath, error: String(error) } });
5337
+ return [];
5338
+ }
5339
+ }
5340
+ function extractCodexSessionMeta(filepath) {
5341
+ let fd = null;
5342
+ try {
5343
+ fd = fs.openSync(filepath, "r");
5344
+ const buffer = Buffer.alloc(8192);
5345
+ const bytesRead = fs.readSync(fd, buffer, 0, 8192, 0);
5346
+ if (bytesRead === 0)
5347
+ return null;
5348
+ const content = buffer.toString("utf-8", 0, bytesRead);
5349
+ const newlineIdx = content.indexOf("\n");
5350
+ const firstLine = newlineIdx >= 0 ? content.slice(0, newlineIdx) : content;
5351
+ const record = JSON.parse(firstLine.trim());
5352
+ if (record.type !== "session_meta")
5353
+ return null;
5354
+ const payload = record.payload;
5355
+ return {
5356
+ id: payload.id,
5357
+ cwd: payload.cwd,
5358
+ cli_version: payload.cli_version,
5359
+ git: payload.git,
5360
+ ...payload.source && typeof payload.source === "object" ? { source: payload.source } : {}
5361
+ };
5362
+ } catch {
5363
+ return null;
5364
+ } finally {
5365
+ if (fd !== null) {
5366
+ try {
5367
+ fs.closeSync(fd);
5368
+ } catch {
5369
+ }
5370
+ }
5371
+ }
5372
+ }
5373
+ var READ_BUFFER_SIZE = 64 * 1024;
5374
+ var TURN_CONTENT_CAP = 8 * 1024;
5375
+
5376
+ // src/recall/session-classifier.ts
5377
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5378
+ var ROLLOUT_UUID_RE = /rollout-.*-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
5379
+ function normalize(p) {
5380
+ return p.replace(/\\/g, "/");
5381
+ }
5382
+ function basenameNoExt(p) {
5383
+ return normalize(p).split("/").pop().replace(/\.jsonl$/i, "");
5384
+ }
5385
+ function lookupStoredProvenance(transcriptPath) {
5386
+ try {
5387
+ const row = getDb(dbPath()).get(
5388
+ `SELECT session_id, kind, parent_session_id FROM session_provenance
5389
+ WHERE transcript_path = ? LIMIT 1`,
5390
+ [normalize(transcriptPath)]
5391
+ );
5392
+ return row ?? null;
5393
+ } catch {
5394
+ return null;
5395
+ }
5396
+ }
5397
+ function claudeSubagentParent(normPath) {
5398
+ const segments = normPath.split("/");
5399
+ const idx = segments.lastIndexOf("subagents");
5400
+ if (idx <= 0)
5401
+ return null;
5402
+ const parent = segments[idx - 1];
5403
+ return parent || null;
5404
+ }
5405
+ function classifySession(args) {
5406
+ const { sessionId, transcriptPath, vendor, hook } = args;
5407
+ const normPath = normalize(transcriptPath);
5408
+ const base = basenameNoExt(transcriptPath);
5409
+ const stored = lookupStoredProvenance(transcriptPath);
5410
+ if (stored && !(stored.kind !== "agent" && hook?.isSubagent)) {
5411
+ return {
5412
+ kind: stored.kind === "agent" ? "agent" : "root",
5413
+ canonicalSessionId: stored.session_id,
5414
+ parentSessionId: stored.parent_session_id,
5415
+ agentDepth: null,
5416
+ agentMeta: null,
5417
+ aliases: aliasCandidates(stored.session_id, sessionId, hook),
5418
+ evidence: "stored"
5419
+ };
5420
+ }
5421
+ if (vendor === "claude")
5422
+ return classifyClaude(sessionId, normPath, base, hook);
5423
+ return classifyCodex(sessionId, transcriptPath, base, hook);
5424
+ }
5425
+ function aliasCandidates(canonical, proposed, hook) {
5426
+ const out = /* @__PURE__ */ new Set();
5427
+ if (hook?.agentId && hook.agentId !== canonical)
5428
+ out.add(hook.agentId);
5429
+ if (proposed && proposed !== canonical)
5430
+ out.add(proposed);
5431
+ return [...out];
5432
+ }
5433
+ function classifyClaude(sessionId, normPath, base, hook) {
5434
+ if (normPath.includes("/subagents/") && base.startsWith("agent-")) {
5435
+ return {
5436
+ kind: "agent",
5437
+ canonicalSessionId: base,
5438
+ parentSessionId: claudeSubagentParent(normPath) ?? hook?.payloadSessionId ?? null,
5439
+ agentDepth: null,
5440
+ agentMeta: hook?.agentId ? { hookAgentId: hook.agentId } : null,
5441
+ aliases: aliasCandidates(base, sessionId, hook),
5442
+ evidence: "claude-path"
5443
+ };
5444
+ }
5445
+ if (base.startsWith("agent-") || sessionId.startsWith("agent-")) {
5446
+ const canonical = base.startsWith("agent-") ? base : sessionId;
5447
+ return {
5448
+ kind: "agent",
5449
+ canonicalSessionId: canonical,
5450
+ parentSessionId: hook?.payloadSessionId ?? null,
5451
+ agentDepth: null,
5452
+ agentMeta: hook?.agentId ? { hookAgentId: hook.agentId } : null,
5453
+ aliases: aliasCandidates(canonical, sessionId, hook),
5454
+ evidence: "claude-name"
5455
+ };
5456
+ }
5457
+ if (hook?.isSubagent) {
5458
+ return {
5459
+ kind: "agent",
5460
+ canonicalSessionId: base || sessionId,
5461
+ parentSessionId: hook.payloadSessionId ?? null,
5462
+ agentDepth: null,
5463
+ agentMeta: hook.agentId ? { hookAgentId: hook.agentId } : null,
5464
+ aliases: aliasCandidates(base || sessionId, sessionId, hook),
5465
+ evidence: "hook"
5466
+ };
5467
+ }
5468
+ return {
5469
+ kind: "root",
5470
+ canonicalSessionId: sessionId,
5471
+ parentSessionId: null,
5472
+ agentDepth: null,
5473
+ agentMeta: null,
5474
+ aliases: [],
5475
+ evidence: "default-root"
5476
+ };
5477
+ }
5478
+ function classifyCodex(sessionId, transcriptPath, base, hook) {
5479
+ const meta = extractCodexSessionMeta(transcriptPath);
5480
+ const filenameUuid = ROLLOUT_UUID_RE.exec(normalize(transcriptPath))?.[1] ?? null;
5481
+ const canonical = meta?.id && typeof meta.id === "string" && meta.id.length > 0 ? meta.id : filenameUuid ?? (UUID_RE.test(sessionId) ? sessionId : null);
5482
+ const sub = parseSubagentSource(meta?.source, transcriptPath);
5483
+ if (sub.malformed) {
5484
+ log({
5485
+ source: "recall:classify",
5486
+ level: "warn",
5487
+ summary: `codex session_meta.source looks subagent-like but is malformed \u2014 leaving session hot (conservative): ${transcriptPath}`
5488
+ });
5489
+ }
5490
+ if (sub.isSubagent || hook?.isSubagent) {
5491
+ if (!canonical) {
5492
+ log({
5493
+ source: "recall:classify",
5494
+ level: "warn",
5495
+ summary: `codex subagent transcript has no derivable canonical id \u2014 skipping ingest (conservative): ${transcriptPath}`
5496
+ });
5497
+ return {
5498
+ kind: "agent",
5499
+ canonicalSessionId: base || sessionId,
5500
+ parentSessionId: sub.parentThreadId ?? hook?.payloadSessionId ?? null,
5501
+ agentDepth: sub.depth,
5502
+ agentMeta: sub.meta,
5503
+ aliases: [],
5504
+ evidence: sub.isSubagent ? "codex-meta" : "hook",
5505
+ unresolvable: true
5506
+ };
5507
+ }
5508
+ return {
5509
+ kind: "agent",
5510
+ canonicalSessionId: canonical,
5511
+ parentSessionId: sub.parentThreadId ?? hook?.payloadSessionId ?? null,
5512
+ agentDepth: sub.depth,
5513
+ agentMeta: mergeMeta(sub.meta, hook?.agentId),
5514
+ aliases: aliasCandidates(canonical, sessionId, hook),
5515
+ evidence: sub.isSubagent ? "codex-meta" : "hook"
5516
+ };
5517
+ }
5518
+ return {
5519
+ kind: "root",
5520
+ canonicalSessionId: canonical ?? sessionId,
5521
+ parentSessionId: null,
5522
+ agentDepth: null,
5523
+ agentMeta: null,
5524
+ aliases: canonical && canonical !== sessionId ? [sessionId] : [],
5525
+ evidence: "default-root"
5526
+ };
5527
+ }
5528
+ function mergeMeta(meta, hookAgentId) {
5529
+ if (!hookAgentId)
5530
+ return meta;
5531
+ return { ...meta ?? {}, hookAgentId };
5532
+ }
5533
+ function parseSubagentSource(source, _transcriptPath) {
5534
+ const none = { isSubagent: false, malformed: false, parentThreadId: null, depth: null, meta: null };
5535
+ if (!source || typeof source !== "object")
5536
+ return none;
5537
+ const subRaw = source.subagent;
5538
+ const typeTag = source.type;
5539
+ const looksSubagent = subRaw !== void 0 || typeTag === "subagent";
5540
+ if (!looksSubagent)
5541
+ return none;
5542
+ if (subRaw !== void 0 && (typeof subRaw !== "object" || subRaw === null)) {
5543
+ return { ...none, malformed: true };
5544
+ }
5545
+ const sub = subRaw ?? {};
5546
+ const spawnRaw = sub.thread_spawn ?? source.thread_spawn;
5547
+ const spawn2 = spawnRaw && typeof spawnRaw === "object" ? spawnRaw : {};
5548
+ const parentThreadId = typeof spawn2.parent_thread_id === "string" && spawn2.parent_thread_id ? spawn2.parent_thread_id : null;
5549
+ const depth = typeof spawn2.depth === "number" && Number.isFinite(spawn2.depth) ? spawn2.depth : null;
5550
+ const meta = {};
5551
+ for (const k of ["agent_path", "agent_type", "agent_name", "path", "type", "name"]) {
5552
+ const v = spawn2[k] ?? sub[k];
5553
+ if (typeof v === "string" || typeof v === "number")
5554
+ meta[k] = v;
5555
+ }
5556
+ return {
5557
+ isSubagent: true,
5558
+ malformed: false,
5559
+ parentThreadId,
5560
+ depth,
5561
+ meta: Object.keys(meta).length > 0 ? meta : null
5562
+ };
5563
+ }
5564
+
5050
5565
  // src/recall/message-ingest.ts
5051
5566
  init_paths();
5052
5567
 
@@ -5065,13 +5580,13 @@ function normalizePath(p) {
5065
5580
  init_log();
5066
5581
 
5067
5582
  // src/adapters/claude/jsonl-reader.ts
5068
- var fs = __toESM(require("fs"));
5583
+ var fs2 = __toESM(require("fs"));
5069
5584
  init_log();
5070
- var READ_BUFFER_SIZE = 64 * 1024;
5071
- var TURN_CONTENT_CAP = 8 * 1024;
5585
+ var READ_BUFFER_SIZE2 = 64 * 1024;
5586
+ var TURN_CONTENT_CAP2 = 8 * 1024;
5072
5587
  function parseJsonlFile(filepath) {
5073
5588
  try {
5074
- const content = fs.readFileSync(filepath, "utf-8");
5589
+ const content = fs2.readFileSync(filepath, "utf-8");
5075
5590
  const lines = content.split("\n");
5076
5591
  const entries = [];
5077
5592
  for (const line of lines) {
@@ -5260,37 +5775,6 @@ function adaptClaudeEntries(rawEntries) {
5260
5775
  return rawEntries.map(adaptClaudeEntry).filter((entry) => entry !== null);
5261
5776
  }
5262
5777
 
5263
- // src/adapters/codex/codex-jsonl-reader.ts
5264
- var fs2 = __toESM(require("fs"));
5265
- init_log();
5266
- var path = __toESM(require("path"));
5267
- var os = __toESM(require("os"));
5268
- var CODEX_SESSIONS_DIR = path.join(os.homedir(), ".codex", "sessions");
5269
- function parseCodexJsonlFile(filepath) {
5270
- try {
5271
- const content = fs2.readFileSync(filepath, "utf-8");
5272
- const lines = content.split("\n");
5273
- const records = [];
5274
- for (const line of lines) {
5275
- const trimmed = line.trim();
5276
- if (!trimmed)
5277
- continue;
5278
- try {
5279
- const record = JSON.parse(trimmed);
5280
- records.push(record);
5281
- } catch (err) {
5282
- log({ level: "warn", source: "codex-jsonl-reader", summary: `Skipping unparseable line: ${err.message}` });
5283
- }
5284
- }
5285
- return records;
5286
- } catch (error) {
5287
- log({ level: "error", source: "codex-jsonl-reader", summary: `Failed to read ${filepath}: ${error instanceof Error ? error.message : String(error)}`, data: { filepath, error: String(error) } });
5288
- return [];
5289
- }
5290
- }
5291
- var READ_BUFFER_SIZE2 = 64 * 1024;
5292
- var TURN_CONTENT_CAP2 = 8 * 1024;
5293
-
5294
5778
  // src/adapters/codex/codex-jsonl-adapter.ts
5295
5779
  function adaptCodexJsonlRecords(records, sessionId) {
5296
5780
  const outputIndex = /* @__PURE__ */ new Map();
@@ -5925,6 +6409,36 @@ function entriesCwd(entries) {
5925
6409
  return void 0;
5926
6410
  }
5927
6411
  async function ingestSessionMessages(sessionId, transcriptPath, vendor, options) {
6412
+ const classification = classifySession({
6413
+ sessionId,
6414
+ transcriptPath,
6415
+ vendor,
6416
+ ...options?.hook ? { hook: options.hook } : {}
6417
+ });
6418
+ if (classification.unresolvable) {
6419
+ return {
6420
+ sessionId,
6421
+ chunksCreated: 0,
6422
+ skipped: true,
6423
+ error: "subagent transcript has no derivable canonical identity \u2014 skipped (conservative)"
6424
+ };
6425
+ }
6426
+ const canonicalId = classification.canonicalSessionId;
6427
+ const retrievalClass = classification.kind === "agent" ? "agent" : "hot";
6428
+ const provenance = {
6429
+ sessionId: canonicalId,
6430
+ vendor,
6431
+ kind: classification.kind,
6432
+ parentSessionId: classification.parentSessionId,
6433
+ agentDepth: classification.agentDepth,
6434
+ agentMeta: classification.agentMeta,
6435
+ transcriptPath: transcriptPath.replace(/\\/g, "/")
6436
+ };
6437
+ const aliases = classification.aliases.map((aliasId) => ({
6438
+ aliasId,
6439
+ sessionId: canonicalId,
6440
+ source: options?.hook?.agentId === aliasId ? "hook-agent-id" : "ingest-request-id"
6441
+ }));
5928
6442
  let rawEntries;
5929
6443
  try {
5930
6444
  if (vendor === "claude") {
@@ -5932,18 +6446,25 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
5932
6446
  rawEntries = adaptClaudeEntries(raw);
5933
6447
  } else {
5934
6448
  const envelopes = parseCodexJsonlFile(transcriptPath);
5935
- rawEntries = adaptCodexJsonlRecords(envelopes, sessionId);
6449
+ rawEntries = adaptCodexJsonlRecords(envelopes, canonicalId);
5936
6450
  }
5937
6451
  } catch (err) {
5938
6452
  return {
5939
- sessionId,
6453
+ sessionId: canonicalId,
5940
6454
  chunksCreated: 0,
5941
6455
  skipped: false,
5942
- error: `Failed to load session: ${err instanceof Error ? err.message : String(err)}`
6456
+ error: `Failed to load session: ${err instanceof Error ? err.message : String(err)}`,
6457
+ retrievalClass
5943
6458
  };
5944
6459
  }
5945
6460
  if (rawEntries.length === 0) {
5946
- return { sessionId, chunksCreated: 0, skipped: true };
6461
+ if (options?.force) {
6462
+ try {
6463
+ insertMessages([], { replaceSessionId: canonicalId, provenance, aliases });
6464
+ } catch {
6465
+ }
6466
+ }
6467
+ return { sessionId: canonicalId, chunksCreated: 0, skipped: true, retrievalClass };
5947
6468
  }
5948
6469
  const rawProjectId = options?.projectId ?? entriesCwd(rawEntries);
5949
6470
  const projectId = rawProjectId ? normalizePath(rawProjectId) : null;
@@ -5960,31 +6481,44 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
5960
6481
  const createdAt = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();
5961
6482
  records.push({
5962
6483
  message_id: entry.uuid,
5963
- session_id: sessionId,
6484
+ session_id: canonicalId,
5964
6485
  message_seq: i,
5965
6486
  message_text: text,
5966
6487
  project_id: projectId,
5967
6488
  created_at: createdAt,
5968
- message_role: entry.message?.role ?? entry.type ?? null
6489
+ message_role: entry.message?.role ?? entry.type ?? null,
6490
+ retrieval_class: retrievalClass
5969
6491
  });
5970
6492
  }
5971
6493
  if (records.length === 0) {
5972
- return { sessionId, chunksCreated: 0, skipped: true };
6494
+ if (options?.force) {
6495
+ try {
6496
+ insertMessages([], { replaceSessionId: canonicalId, provenance, aliases });
6497
+ } catch {
6498
+ }
6499
+ }
6500
+ return { sessionId: canonicalId, chunksCreated: 0, skipped: true, retrievalClass };
5973
6501
  }
5974
6502
  try {
5975
- insertMessages(records, options?.force ? { replaceSessionId: sessionId } : void 0);
6503
+ insertMessages(records, {
6504
+ ...options?.force ? { replaceSessionId: canonicalId } : {},
6505
+ provenance,
6506
+ aliases
6507
+ });
5976
6508
  } catch (err) {
5977
6509
  return {
5978
- sessionId,
6510
+ sessionId: canonicalId,
5979
6511
  chunksCreated: 0,
5980
6512
  skipped: false,
5981
- error: `DB insert failed: ${err instanceof Error ? err.message : String(err)}`
6513
+ error: `DB insert failed: ${err instanceof Error ? err.message : String(err)}`,
6514
+ retrievalClass
5982
6515
  };
5983
6516
  }
5984
6517
  return {
5985
- sessionId,
6518
+ sessionId: canonicalId,
5986
6519
  chunksCreated: records.length,
5987
- skipped: false
6520
+ skipped: false,
6521
+ retrievalClass
5988
6522
  };
5989
6523
  }
5990
6524
  var MAX_EMBED_CHARS = 14e3;
@@ -6028,14 +6562,14 @@ async function embedSessionMessages(sessionId, force) {
6028
6562
  const rows = d.all(
6029
6563
  force ? `SELECT m.message_id, m.message_text,
6030
6564
  (SELECT p.message_text FROM messages p
6031
- WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq
6565
+ WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq AND p.retrieval_class = 'hot'
6032
6566
  ORDER BY p.message_seq DESC LIMIT 1) AS prev_text
6033
- FROM messages m WHERE m.session_id = ? ORDER BY m.message_seq ASC` : `SELECT m.message_id, m.message_text,
6567
+ FROM messages m WHERE m.session_id = ? AND m.retrieval_class = 'hot' ORDER BY m.message_seq ASC` : `SELECT m.message_id, m.message_text,
6034
6568
  (SELECT p.message_text FROM messages p
6035
- WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq
6569
+ WHERE p.session_id = m.session_id AND p.message_seq < m.message_seq AND p.retrieval_class = 'hot'
6036
6570
  ORDER BY p.message_seq DESC LIMIT 1) AS prev_text
6037
6571
  FROM messages m
6038
- WHERE m.session_id = ?
6572
+ WHERE m.session_id = ? AND m.retrieval_class = 'hot'
6039
6573
  AND NOT EXISTS (SELECT 1 FROM message_vectors mv WHERE mv.message_id = m.message_id AND mv.embed_version = ?)
6040
6574
  ORDER BY m.message_seq ASC`,
6041
6575
  force ? [sessionId] : [sessionId, EMBED_VERSION]
@@ -6088,7 +6622,9 @@ init_embedder();
6088
6622
  var import_node_fs5 = require("node:fs");
6089
6623
  var import_node_path5 = require("node:path");
6090
6624
  init_paths();
6091
- var LOCK_PATH = (0, import_node_path5.join)(runDir(), "embed.lock");
6625
+ function embedLockPath() {
6626
+ return (0, import_node_path5.join)(runDir(), "embed.lock");
6627
+ }
6092
6628
  var STALE_MS = 30 * 60 * 1e3;
6093
6629
  function isAlive(pid) {
6094
6630
  try {
@@ -6101,21 +6637,21 @@ function isAlive(pid) {
6101
6637
  function tryAcquireEmbedLock() {
6102
6638
  for (let attempt = 0; attempt < 2; attempt++) {
6103
6639
  try {
6104
- (0, import_node_fs5.writeFileSync)(LOCK_PATH, String(process.pid), { flag: "wx" });
6640
+ (0, import_node_fs5.writeFileSync)(embedLockPath(), String(process.pid), { flag: "wx" });
6105
6641
  return true;
6106
6642
  } catch {
6107
6643
  let heldPid;
6108
6644
  let ageMs;
6109
6645
  try {
6110
- heldPid = parseInt((0, import_node_fs5.readFileSync)(LOCK_PATH, "utf8"), 10);
6111
- ageMs = Date.now() - (0, import_node_fs5.statSync)(LOCK_PATH).mtimeMs;
6646
+ heldPid = parseInt((0, import_node_fs5.readFileSync)(embedLockPath(), "utf8"), 10);
6647
+ ageMs = Date.now() - (0, import_node_fs5.statSync)(embedLockPath()).mtimeMs;
6112
6648
  } catch {
6113
6649
  continue;
6114
6650
  }
6115
6651
  if (isAlive(heldPid) && ageMs < STALE_MS)
6116
6652
  return false;
6117
6653
  try {
6118
- (0, import_node_fs5.unlinkSync)(LOCK_PATH);
6654
+ (0, import_node_fs5.unlinkSync)(embedLockPath());
6119
6655
  } catch {
6120
6656
  }
6121
6657
  }
@@ -6124,9 +6660,9 @@ function tryAcquireEmbedLock() {
6124
6660
  }
6125
6661
  function releaseEmbedLock() {
6126
6662
  try {
6127
- const held = parseInt((0, import_node_fs5.readFileSync)(LOCK_PATH, "utf8"), 10);
6663
+ const held = parseInt((0, import_node_fs5.readFileSync)(embedLockPath(), "utf8"), 10);
6128
6664
  if (held === process.pid)
6129
- (0, import_node_fs5.unlinkSync)(LOCK_PATH);
6665
+ (0, import_node_fs5.unlinkSync)(embedLockPath());
6130
6666
  } catch {
6131
6667
  }
6132
6668
  }
@@ -6936,11 +7472,11 @@ var qmarksTestNoExtDot = ([$0]) => {
6936
7472
  return (f) => f.length === len && f !== "." && f !== "..";
6937
7473
  };
6938
7474
  var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
6939
- var path2 = {
7475
+ var path = {
6940
7476
  win32: { sep: "\\" },
6941
7477
  posix: { sep: "/" }
6942
7478
  };
6943
- var sep = defaultPlatform === "win32" ? path2.win32.sep : path2.posix.sep;
7479
+ var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
6944
7480
  minimatch.sep = sep;
6945
7481
  var GLOBSTAR = Symbol("globstar **");
6946
7482
  minimatch.GLOBSTAR = GLOBSTAR;
@@ -9972,7 +10508,7 @@ var ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
9972
10508
  var TYPEMASK = 1023;
9973
10509
  var entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
9974
10510
  var normalizeCache = /* @__PURE__ */ new Map();
9975
- var normalize = (s) => {
10511
+ var normalize2 = (s) => {
9976
10512
  const c = normalizeCache.get(s);
9977
10513
  if (c)
9978
10514
  return c;
@@ -9985,7 +10521,7 @@ var normalizeNocase = (s) => {
9985
10521
  const c = normalizeNocaseCache.get(s);
9986
10522
  if (c)
9987
10523
  return c;
9988
- const n = normalize(s.toLowerCase());
10524
+ const n = normalize2(s.toLowerCase());
9989
10525
  normalizeNocaseCache.set(s, n);
9990
10526
  return n;
9991
10527
  };
@@ -10152,7 +10688,7 @@ var PathBase = class {
10152
10688
  */
10153
10689
  constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
10154
10690
  this.name = name;
10155
- this.#matchName = nocase ? normalizeNocase(name) : normalize(name);
10691
+ this.#matchName = nocase ? normalizeNocase(name) : normalize2(name);
10156
10692
  this.#type = type & TYPEMASK;
10157
10693
  this.nocase = nocase;
10158
10694
  this.roots = roots;
@@ -10189,12 +10725,12 @@ var PathBase = class {
10189
10725
  /**
10190
10726
  * Get the Path object referenced by the string path, resolved from this Path
10191
10727
  */
10192
- resolve(path3) {
10193
- if (!path3) {
10728
+ resolve(path2) {
10729
+ if (!path2) {
10194
10730
  return this;
10195
10731
  }
10196
- const rootPath = this.getRootString(path3);
10197
- const dir = path3.substring(rootPath.length);
10732
+ const rootPath = this.getRootString(path2);
10733
+ const dir = path2.substring(rootPath.length);
10198
10734
  const dirParts = dir.split(this.splitSep);
10199
10735
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
10200
10736
  return result;
@@ -10245,7 +10781,7 @@ var PathBase = class {
10245
10781
  return this.parent || this;
10246
10782
  }
10247
10783
  const children = this.children();
10248
- const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
10784
+ const name = this.nocase ? normalizeNocase(pathPart) : normalize2(pathPart);
10249
10785
  for (const p of children) {
10250
10786
  if (p.#matchName === name) {
10251
10787
  return p;
@@ -10490,7 +11026,7 @@ var PathBase = class {
10490
11026
  * directly.
10491
11027
  */
10492
11028
  isNamed(n) {
10493
- return !this.nocase ? this.#matchName === normalize(n) : this.#matchName === normalizeNocase(n);
11029
+ return !this.nocase ? this.#matchName === normalize2(n) : this.#matchName === normalizeNocase(n);
10494
11030
  }
10495
11031
  /**
10496
11032
  * Return the Path object corresponding to the target of a symbolic link.
@@ -10629,7 +11165,7 @@ var PathBase = class {
10629
11165
  #readdirMaybePromoteChild(e, c) {
10630
11166
  for (let p = c.provisional; p < c.length; p++) {
10631
11167
  const pchild = c[p];
10632
- const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
11168
+ const name = this.nocase ? normalizeNocase(e.name) : normalize2(e.name);
10633
11169
  if (name !== pchild.#matchName) {
10634
11170
  continue;
10635
11171
  }
@@ -10946,8 +11482,8 @@ var PathWin32 = class _PathWin32 extends PathBase {
10946
11482
  /**
10947
11483
  * @internal
10948
11484
  */
10949
- getRootString(path3) {
10950
- return import_node_path6.win32.parse(path3).root;
11485
+ getRootString(path2) {
11486
+ return import_node_path6.win32.parse(path2).root;
10951
11487
  }
10952
11488
  /**
10953
11489
  * @internal
@@ -10993,8 +11529,8 @@ var PathPosix = class _PathPosix extends PathBase {
10993
11529
  /**
10994
11530
  * @internal
10995
11531
  */
10996
- getRootString(path3) {
10997
- return path3.startsWith("/") ? "/" : "";
11532
+ getRootString(path2) {
11533
+ return path2.startsWith("/") ? "/" : "";
10998
11534
  }
10999
11535
  /**
11000
11536
  * @internal
@@ -11083,11 +11619,11 @@ var PathScurryBase = class {
11083
11619
  /**
11084
11620
  * Get the depth of a provided path, string, or the cwd
11085
11621
  */
11086
- depth(path3 = this.cwd) {
11087
- if (typeof path3 === "string") {
11088
- path3 = this.cwd.resolve(path3);
11622
+ depth(path2 = this.cwd) {
11623
+ if (typeof path2 === "string") {
11624
+ path2 = this.cwd.resolve(path2);
11089
11625
  }
11090
- return path3.depth();
11626
+ return path2.depth();
11091
11627
  }
11092
11628
  /**
11093
11629
  * Return the cache of child entries. Exposed so subclasses can create
@@ -11574,9 +12110,9 @@ var PathScurryBase = class {
11574
12110
  process2();
11575
12111
  return results;
11576
12112
  }
11577
- chdir(path3 = this.cwd) {
12113
+ chdir(path2 = this.cwd) {
11578
12114
  const oldCwd = this.cwd;
11579
- this.cwd = typeof path3 === "string" ? this.cwd.resolve(path3) : path3;
12115
+ this.cwd = typeof path2 === "string" ? this.cwd.resolve(path2) : path2;
11580
12116
  this.cwd[setAsCwd](oldCwd);
11581
12117
  }
11582
12118
  };
@@ -11932,8 +12468,8 @@ var MatchRecord = class {
11932
12468
  }
11933
12469
  // match, absolute, ifdir
11934
12470
  entries() {
11935
- return [...this.store.entries()].map(([path3, n]) => [
11936
- path3,
12471
+ return [...this.store.entries()].map(([path2, n]) => [
12472
+ path2,
11937
12473
  !!(n & 2),
11938
12474
  !!(n & 1)
11939
12475
  ]);
@@ -12138,9 +12674,9 @@ var GlobUtil = class {
12138
12674
  signal;
12139
12675
  maxDepth;
12140
12676
  includeChildMatches;
12141
- constructor(patterns, path3, opts) {
12677
+ constructor(patterns, path2, opts) {
12142
12678
  this.patterns = patterns;
12143
- this.path = path3;
12679
+ this.path = path2;
12144
12680
  this.opts = opts;
12145
12681
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
12146
12682
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -12159,11 +12695,11 @@ var GlobUtil = class {
12159
12695
  });
12160
12696
  }
12161
12697
  }
12162
- #ignored(path3) {
12163
- return this.seen.has(path3) || !!this.#ignore?.ignored?.(path3);
12698
+ #ignored(path2) {
12699
+ return this.seen.has(path2) || !!this.#ignore?.ignored?.(path2);
12164
12700
  }
12165
- #childrenIgnored(path3) {
12166
- return !!this.#ignore?.childrenIgnored?.(path3);
12701
+ #childrenIgnored(path2) {
12702
+ return !!this.#ignore?.childrenIgnored?.(path2);
12167
12703
  }
12168
12704
  // backpressure mechanism
12169
12705
  pause() {
@@ -12378,8 +12914,8 @@ var GlobUtil = class {
12378
12914
  };
12379
12915
  var GlobWalker = class extends GlobUtil {
12380
12916
  matches = /* @__PURE__ */ new Set();
12381
- constructor(patterns, path3, opts) {
12382
- super(patterns, path3, opts);
12917
+ constructor(patterns, path2, opts) {
12918
+ super(patterns, path2, opts);
12383
12919
  }
12384
12920
  matchEmit(e) {
12385
12921
  this.matches.add(e);
@@ -12416,8 +12952,8 @@ var GlobWalker = class extends GlobUtil {
12416
12952
  };
12417
12953
  var GlobStream = class extends GlobUtil {
12418
12954
  results;
12419
- constructor(patterns, path3, opts) {
12420
- super(patterns, path3, opts);
12955
+ constructor(patterns, path2, opts) {
12956
+ super(patterns, path2, opts);
12421
12957
  this.results = new Minipass({
12422
12958
  signal: this.signal,
12423
12959
  objectMode: true
@@ -12805,7 +13341,7 @@ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
12805
13341
  process.on("SIGTERM", onSignal);
12806
13342
  const heartbeat = setInterval(() => {
12807
13343
  try {
12808
- (0, import_node_fs7.utimesSync)(LOCK_PATH, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
13344
+ (0, import_node_fs7.utimesSync)(embedLockPath(), /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
12809
13345
  } catch {
12810
13346
  }
12811
13347
  }, HEARTBEAT_INTERVAL_MS);
@@ -12852,4 +13388,6 @@ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
12852
13388
  }
12853
13389
  releaseEmbedLock();
12854
13390
  }
12855
- })();
13391
+ })().catch(() => {
13392
+ process.exit(0);
13393
+ });