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.
- package/README.md +230 -169
- package/THIRD-PARTY-LICENSES +43 -145
- package/dist/SKILL.md.template +7 -0
- package/dist/embed-pending.js +742 -204
- package/dist/recall.js +3029 -941
- package/dist/statusline.js +180 -0
- package/dist/stop-hook.js +611 -144
- package/package.json +3 -2
package/dist/embed-pending.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
167
|
+
path2 = path2.replace(/^(.+)\|/, "$1:");
|
|
168
168
|
if (sep2 == "\\") {
|
|
169
|
-
|
|
169
|
+
path2 = path2.replace(/\//g, "\\");
|
|
170
170
|
}
|
|
171
|
-
if (/^.+\:/.test(
|
|
171
|
+
if (/^.+\:/.test(path2)) {
|
|
172
172
|
} else {
|
|
173
|
-
|
|
173
|
+
path2 = sep2 + path2;
|
|
174
174
|
}
|
|
175
|
-
return host +
|
|
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
|
|
184
|
+
var path2 = require("path");
|
|
185
185
|
var fileURLToPath3 = require_file_uri_to_path();
|
|
186
|
-
var
|
|
187
|
-
var dirname3 =
|
|
188
|
-
var exists = fs3.accessSync && function(
|
|
186
|
+
var join7 = path2.join;
|
|
187
|
+
var dirname3 = path2.dirname;
|
|
188
|
+
var exists = fs3.accessSync && function(path3) {
|
|
189
189
|
try {
|
|
190
|
-
fs3.accessSync(
|
|
190
|
+
fs3.accessSync(path3);
|
|
191
191
|
} catch (e) {
|
|
192
192
|
return false;
|
|
193
193
|
}
|
|
194
194
|
return true;
|
|
195
|
-
} || fs3.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 (
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
3379
|
-
if (relativeDestDir.split(
|
|
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 =
|
|
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 :
|
|
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 (!
|
|
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 (
|
|
3719
|
-
const
|
|
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,
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4837
|
-
|
|
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
|
|
4840
|
-
session_id
|
|
4841
|
-
message_seq
|
|
4842
|
-
message_text
|
|
4843
|
-
project_id
|
|
4844
|
-
created_at
|
|
4845
|
-
message_role
|
|
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
|
-
|
|
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=
|
|
5044
|
+
content=searchable_messages,
|
|
4862
5045
|
content_rowid=rowid,
|
|
4863
5046
|
tokenize='porter unicode61'
|
|
4864
5047
|
);
|
|
4865
5048
|
|
|
4866
|
-
|
|
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
|
|
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
|
-
|
|
4878
|
-
INSERT INTO messages_fts(rowid, 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
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
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(
|
|
4905
|
-
} catch
|
|
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
|
-
|
|
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
|
|
5583
|
+
var fs2 = __toESM(require("fs"));
|
|
5069
5584
|
init_log();
|
|
5070
|
-
var
|
|
5071
|
-
var
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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)(
|
|
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)(
|
|
6111
|
-
ageMs = Date.now() - (0, import_node_fs5.statSync)(
|
|
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)(
|
|
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)(
|
|
6663
|
+
const held = parseInt((0, import_node_fs5.readFileSync)(embedLockPath(), "utf8"), 10);
|
|
6128
6664
|
if (held === process.pid)
|
|
6129
|
-
(0, import_node_fs5.unlinkSync)(
|
|
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
|
|
7475
|
+
var path = {
|
|
6940
7476
|
win32: { sep: "\\" },
|
|
6941
7477
|
posix: { sep: "/" }
|
|
6942
7478
|
};
|
|
6943
|
-
var sep = defaultPlatform === "win32" ?
|
|
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
|
|
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 =
|
|
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) :
|
|
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(
|
|
10193
|
-
if (!
|
|
10728
|
+
resolve(path2) {
|
|
10729
|
+
if (!path2) {
|
|
10194
10730
|
return this;
|
|
10195
10731
|
}
|
|
10196
|
-
const rootPath = this.getRootString(
|
|
10197
|
-
const dir =
|
|
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) :
|
|
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 ===
|
|
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) :
|
|
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(
|
|
10950
|
-
return import_node_path6.win32.parse(
|
|
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(
|
|
10997
|
-
return
|
|
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(
|
|
11087
|
-
if (typeof
|
|
11088
|
-
|
|
11622
|
+
depth(path2 = this.cwd) {
|
|
11623
|
+
if (typeof path2 === "string") {
|
|
11624
|
+
path2 = this.cwd.resolve(path2);
|
|
11089
11625
|
}
|
|
11090
|
-
return
|
|
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(
|
|
12113
|
+
chdir(path2 = this.cwd) {
|
|
11578
12114
|
const oldCwd = this.cwd;
|
|
11579
|
-
this.cwd = typeof
|
|
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(([
|
|
11936
|
-
|
|
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,
|
|
12677
|
+
constructor(patterns, path2, opts) {
|
|
12142
12678
|
this.patterns = patterns;
|
|
12143
|
-
this.path =
|
|
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(
|
|
12163
|
-
return this.seen.has(
|
|
12698
|
+
#ignored(path2) {
|
|
12699
|
+
return this.seen.has(path2) || !!this.#ignore?.ignored?.(path2);
|
|
12164
12700
|
}
|
|
12165
|
-
#childrenIgnored(
|
|
12166
|
-
return !!this.#ignore?.childrenIgnored?.(
|
|
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,
|
|
12382
|
-
super(patterns,
|
|
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,
|
|
12420
|
-
super(patterns,
|
|
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)(
|
|
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
|
+
});
|