memorix 1.6.0 → 1.6.2
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/CHANGELOG.md +46 -0
- package/dist/cli/index.js +1888 -1122
- package/dist/cli/memcode.js +12 -1
- package/dist/index.js +641 -113
- package/dist/maintenance-runner.js +618 -90
- package/dist/media-audio-runner.js +618 -90
- package/dist/media-video-runner.js +618 -90
- package/dist/memcode-runtime/CHANGELOG.md +46 -0
- package/dist/sdk.js +674 -117
- package/dist/vector-backfill-runner.js +618 -90
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +11 -3
- package/docs/API_REFERENCE.md +1 -1
- package/docs/DOCKER.md +5 -1
- package/docs/PERFORMANCE.md +17 -2
- package/docs/README.md +3 -1
- package/docs/SETUP.md +8 -2
- package/package.json +2 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/agent-integrations.ts +9 -3
- package/src/cli/commands/background.ts +34 -2
- package/src/cli/commands/doctor.ts +46 -15
- package/src/cli/commands/hook.ts +13 -1
- package/src/cli/commands/operator-shared.ts +6 -3
- package/src/cli/commands/serve-http.ts +72 -7
- package/src/cli/heap.ts +76 -0
- package/src/cli/invocation.ts +7 -4
- package/src/embedding/api-provider.ts +503 -27
- package/src/embedding/provider.ts +96 -9
- package/src/hooks/handler.ts +9 -18
- package/src/hooks/lifecycle.ts +174 -0
- package/src/memory/observations.ts +68 -18
- package/src/sdk.ts +12 -8
- package/src/store/orama-store.ts +21 -3
|
@@ -2352,8 +2352,8 @@ async function acquireLock(lockPath) {
|
|
|
2352
2352
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
2353
2353
|
if (code === "EEXIST" || code === "EPERM") {
|
|
2354
2354
|
try {
|
|
2355
|
-
const
|
|
2356
|
-
if (Date.now() -
|
|
2355
|
+
const stat5 = await fs2.stat(lockPath);
|
|
2356
|
+
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
2357
2357
|
await fs2.unlink(lockPath).catch(() => {
|
|
2358
2358
|
});
|
|
2359
2359
|
continue;
|
|
@@ -3464,24 +3464,131 @@ var init_transformers_provider = __esm({
|
|
|
3464
3464
|
// src/embedding/api-provider.ts
|
|
3465
3465
|
var api_provider_exports = {};
|
|
3466
3466
|
__export(api_provider_exports, {
|
|
3467
|
-
APIEmbeddingProvider: () => APIEmbeddingProvider
|
|
3467
|
+
APIEmbeddingProvider: () => APIEmbeddingProvider,
|
|
3468
|
+
convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
|
|
3469
|
+
ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
|
|
3470
|
+
getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
|
|
3471
|
+
getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
|
|
3472
|
+
getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
|
|
3473
|
+
getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
|
|
3474
|
+
migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
|
|
3475
|
+
parseCacheJsonInWorker: () => parseCacheJsonInWorker,
|
|
3476
|
+
resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
|
|
3477
|
+
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
3478
|
+
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
3468
3479
|
});
|
|
3469
3480
|
import { createHash as createHash2 } from "crypto";
|
|
3470
|
-
import {
|
|
3481
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
3482
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
3471
3483
|
import { join as join3 } from "path";
|
|
3472
3484
|
import { homedir as homedir7 } from "os";
|
|
3485
|
+
import { createInterface } from "readline";
|
|
3473
3486
|
function cacheDir() {
|
|
3474
3487
|
return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
|
|
3475
3488
|
}
|
|
3476
3489
|
function cacheFile() {
|
|
3477
3490
|
return join3(cacheDir(), ".embedding-api-cache.json");
|
|
3478
3491
|
}
|
|
3492
|
+
function cacheJsonlFile() {
|
|
3493
|
+
return join3(cacheDir(), ".embedding-api-cache.jsonl");
|
|
3494
|
+
}
|
|
3479
3495
|
function dimsCacheFile() {
|
|
3480
3496
|
return join3(cacheDir(), ".embedding-dims-cache.json");
|
|
3481
3497
|
}
|
|
3482
3498
|
function cacheMetaFile() {
|
|
3483
3499
|
return join3(cacheDir(), ".embedding-api-cache-meta.json");
|
|
3484
3500
|
}
|
|
3501
|
+
function cacheStatFile() {
|
|
3502
|
+
return join3(cacheDir(), ".embedding-api-cache-stat.json");
|
|
3503
|
+
}
|
|
3504
|
+
function payloadBytes(vector) {
|
|
3505
|
+
return vector.length * BYTES_PER_DIM;
|
|
3506
|
+
}
|
|
3507
|
+
function maxCachePayloadBytes() {
|
|
3508
|
+
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
3509
|
+
if (raw) {
|
|
3510
|
+
const parsed = Number.parseInt(raw, 10);
|
|
3511
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
3512
|
+
}
|
|
3513
|
+
return MAX_CACHE_PAYLOAD_BYTES;
|
|
3514
|
+
}
|
|
3515
|
+
function maxEntriesForDimensions(dimensions) {
|
|
3516
|
+
const dim = Math.max(1, Math.floor(dimensions));
|
|
3517
|
+
const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
|
|
3518
|
+
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
3519
|
+
}
|
|
3520
|
+
function rememberDimensions(vector) {
|
|
3521
|
+
if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
|
|
3522
|
+
cacheDimensions = vector.length;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
function evictOldest() {
|
|
3526
|
+
const firstKey = cache3.keys().next().value;
|
|
3527
|
+
if (firstKey === void 0) return;
|
|
3528
|
+
const old = cache3.get(firstKey);
|
|
3529
|
+
cache3.delete(firstKey);
|
|
3530
|
+
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
3531
|
+
}
|
|
3532
|
+
function evictToFit(incomingBytes, incomingDims) {
|
|
3533
|
+
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
3534
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
|
|
3535
|
+
evictOldest();
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
function isNumberArray(value) {
|
|
3539
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
3540
|
+
}
|
|
3541
|
+
function parseCacheRow(raw) {
|
|
3542
|
+
if (Array.isArray(raw)) {
|
|
3543
|
+
const hash4 = raw[0];
|
|
3544
|
+
const vector = raw[1];
|
|
3545
|
+
if (typeof hash4 === "string" && isNumberArray(vector)) {
|
|
3546
|
+
return { hash: hash4, vector };
|
|
3547
|
+
}
|
|
3548
|
+
return null;
|
|
3549
|
+
}
|
|
3550
|
+
if (raw && typeof raw === "object") {
|
|
3551
|
+
const row = raw;
|
|
3552
|
+
if (typeof row.h === "string" && isNumberArray(row.v)) {
|
|
3553
|
+
return { hash: row.h, vector: row.v };
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
return null;
|
|
3557
|
+
}
|
|
3558
|
+
function cacheInsert(hash4, value, dirty) {
|
|
3559
|
+
rememberDimensions(value);
|
|
3560
|
+
const incoming = payloadBytes(value);
|
|
3561
|
+
const existing = cache3.get(hash4);
|
|
3562
|
+
if (existing) {
|
|
3563
|
+
cachePayloadBytes -= payloadBytes(existing);
|
|
3564
|
+
cache3.delete(hash4);
|
|
3565
|
+
}
|
|
3566
|
+
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
3567
|
+
cache3.set(hash4, value);
|
|
3568
|
+
cachePayloadBytes += incoming;
|
|
3569
|
+
if (dirty) diskCacheDirty2 = true;
|
|
3570
|
+
}
|
|
3571
|
+
async function readCacheStat() {
|
|
3572
|
+
try {
|
|
3573
|
+
const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
|
|
3574
|
+
if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
|
|
3575
|
+
return {
|
|
3576
|
+
version: 1,
|
|
3577
|
+
entryCount: Math.floor(data.entryCount),
|
|
3578
|
+
dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
} catch {
|
|
3582
|
+
}
|
|
3583
|
+
return null;
|
|
3584
|
+
}
|
|
3585
|
+
async function writeCacheStat(stat5) {
|
|
3586
|
+
try {
|
|
3587
|
+
await mkdir2(cacheDir(), { recursive: true });
|
|
3588
|
+
await writeFile2(cacheStatFile(), JSON.stringify(stat5));
|
|
3589
|
+
} catch {
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3485
3592
|
function normalizeText(text2) {
|
|
3486
3593
|
return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
|
|
3487
3594
|
}
|
|
@@ -3567,29 +3674,300 @@ function toProviderInput(input, baseUrl) {
|
|
|
3567
3674
|
}
|
|
3568
3675
|
return { type: input.modality, url: input.url };
|
|
3569
3676
|
}
|
|
3677
|
+
function parseCacheJsonInWorker(filePath) {
|
|
3678
|
+
return new Promise((resolve, reject) => {
|
|
3679
|
+
void import("worker_threads").then(({ Worker }) => {
|
|
3680
|
+
const worker = new Worker(
|
|
3681
|
+
`
|
|
3682
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
3683
|
+
const { readFileSync } = require('node:fs');
|
|
3684
|
+
try {
|
|
3685
|
+
const raw = readFileSync(workerData.filePath, 'utf8');
|
|
3686
|
+
parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
|
|
3687
|
+
} catch (err) {
|
|
3688
|
+
parentPort.postMessage({
|
|
3689
|
+
ok: false,
|
|
3690
|
+
error: err instanceof Error ? err.message : String(err),
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
`,
|
|
3694
|
+
{ eval: true, workerData: { filePath } }
|
|
3695
|
+
);
|
|
3696
|
+
worker.once("message", (msg) => {
|
|
3697
|
+
void worker.terminate();
|
|
3698
|
+
if (msg?.ok && Array.isArray(msg.entries)) {
|
|
3699
|
+
resolve(msg.entries);
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
reject(new Error(msg?.error || "embedding cache worker parse failed"));
|
|
3703
|
+
});
|
|
3704
|
+
worker.once("error", reject);
|
|
3705
|
+
}).catch(reject);
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
function findJsonValueEnd(source) {
|
|
3709
|
+
let i2 = 0;
|
|
3710
|
+
while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
|
|
3711
|
+
if (i2 >= source.length) return -1;
|
|
3712
|
+
const start = source[i2];
|
|
3713
|
+
if (start === '"') {
|
|
3714
|
+
i2 += 1;
|
|
3715
|
+
while (i2 < source.length) {
|
|
3716
|
+
if (source[i2] === "\\") {
|
|
3717
|
+
i2 += 2;
|
|
3718
|
+
continue;
|
|
3719
|
+
}
|
|
3720
|
+
if (source[i2] === '"') return i2 + 1;
|
|
3721
|
+
i2 += 1;
|
|
3722
|
+
}
|
|
3723
|
+
return -1;
|
|
3724
|
+
}
|
|
3725
|
+
if (start === "{" || start === "[") {
|
|
3726
|
+
const close = start === "{" ? "}" : "]";
|
|
3727
|
+
let depth = 0;
|
|
3728
|
+
let inString = false;
|
|
3729
|
+
for (; i2 < source.length; i2 += 1) {
|
|
3730
|
+
const ch = source[i2];
|
|
3731
|
+
if (inString) {
|
|
3732
|
+
if (ch === "\\") {
|
|
3733
|
+
i2 += 1;
|
|
3734
|
+
continue;
|
|
3735
|
+
}
|
|
3736
|
+
if (ch === '"') inString = false;
|
|
3737
|
+
continue;
|
|
3738
|
+
}
|
|
3739
|
+
if (ch === '"') {
|
|
3740
|
+
inString = true;
|
|
3741
|
+
continue;
|
|
3742
|
+
}
|
|
3743
|
+
if (ch === start) depth += 1;
|
|
3744
|
+
else if (ch === close) {
|
|
3745
|
+
depth -= 1;
|
|
3746
|
+
if (depth === 0) return i2 + 1;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
return -1;
|
|
3750
|
+
}
|
|
3751
|
+
const rest = source.slice(i2);
|
|
3752
|
+
const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
|
|
3753
|
+
return match2 ? i2 + match2[0].length : -1;
|
|
3754
|
+
}
|
|
3755
|
+
async function streamJsonArrayToJsonl(filePath, tmpPath) {
|
|
3756
|
+
const input = createReadStream(filePath, { encoding: "utf8" });
|
|
3757
|
+
const out = createWriteStream(tmpPath);
|
|
3758
|
+
let buf = "";
|
|
3759
|
+
let started = false;
|
|
3760
|
+
let count2 = 0;
|
|
3761
|
+
const writeLine = async (line) => {
|
|
3762
|
+
if (!out.write(line)) {
|
|
3763
|
+
await new Promise((resolve, reject) => {
|
|
3764
|
+
out.once("drain", resolve);
|
|
3765
|
+
out.once("error", reject);
|
|
3766
|
+
});
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
try {
|
|
3770
|
+
for await (const chunk of input) {
|
|
3771
|
+
buf += chunk;
|
|
3772
|
+
if (!started) {
|
|
3773
|
+
const idx = buf.search(/\S/);
|
|
3774
|
+
if (idx < 0) continue;
|
|
3775
|
+
if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
|
|
3776
|
+
buf = buf.slice(idx + 1);
|
|
3777
|
+
started = true;
|
|
3778
|
+
}
|
|
3779
|
+
while (true) {
|
|
3780
|
+
buf = buf.replace(/^\s*,?\s*/, "");
|
|
3781
|
+
if (!buf) break;
|
|
3782
|
+
if (buf[0] === "]") {
|
|
3783
|
+
buf = "";
|
|
3784
|
+
break;
|
|
3785
|
+
}
|
|
3786
|
+
const end = findJsonValueEnd(buf);
|
|
3787
|
+
if (end < 0) break;
|
|
3788
|
+
const value = JSON.parse(buf.slice(0, end));
|
|
3789
|
+
buf = buf.slice(end);
|
|
3790
|
+
const row = parseCacheRow(value);
|
|
3791
|
+
if (row) {
|
|
3792
|
+
await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
|
|
3793
|
+
`);
|
|
3794
|
+
count2 += 1;
|
|
3795
|
+
if (count2 % 50 === 0) {
|
|
3796
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
|
|
3801
|
+
throw new Error("embedding cache entry too large to migrate safely");
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
} finally {
|
|
3805
|
+
await new Promise((resolve, reject) => {
|
|
3806
|
+
out.end(() => resolve());
|
|
3807
|
+
out.once("error", reject);
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
3810
|
+
return count2;
|
|
3811
|
+
}
|
|
3812
|
+
async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
|
|
3813
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
3814
|
+
try {
|
|
3815
|
+
const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
|
|
3816
|
+
await rename(tmpPath, jsonlPath);
|
|
3817
|
+
diskCacheEntryCount = count2;
|
|
3818
|
+
await writeCacheStat({
|
|
3819
|
+
version: 1,
|
|
3820
|
+
entryCount: count2,
|
|
3821
|
+
dimensions: cacheDimensions
|
|
3822
|
+
});
|
|
3823
|
+
return count2;
|
|
3824
|
+
} catch (err) {
|
|
3825
|
+
await unlink(tmpPath).catch(() => void 0);
|
|
3826
|
+
throw err;
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
function convertArrayCacheToJsonl(filePath, jsonlPath) {
|
|
3830
|
+
return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
3831
|
+
}
|
|
3832
|
+
async function loadCacheJsonl(filePath) {
|
|
3833
|
+
const rl = createInterface({
|
|
3834
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
3835
|
+
crlfDelay: Infinity
|
|
3836
|
+
});
|
|
3837
|
+
let loaded = 0;
|
|
3838
|
+
let onDiskCount = 0;
|
|
3839
|
+
for await (const line of rl) {
|
|
3840
|
+
const trimmed = line.trim();
|
|
3841
|
+
if (!trimmed) continue;
|
|
3842
|
+
try {
|
|
3843
|
+
const row = parseCacheRow(JSON.parse(trimmed));
|
|
3844
|
+
if (!row) continue;
|
|
3845
|
+
onDiskCount += 1;
|
|
3846
|
+
cacheInsert(row.hash, row.vector, false);
|
|
3847
|
+
loaded = cache3.size;
|
|
3848
|
+
} catch {
|
|
3849
|
+
}
|
|
3850
|
+
if (onDiskCount % 250 === 0) {
|
|
3851
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
diskCacheEntryCount = onDiskCount;
|
|
3855
|
+
await writeCacheStat({
|
|
3856
|
+
version: 1,
|
|
3857
|
+
entryCount: onDiskCount,
|
|
3858
|
+
dimensions: cacheDimensions
|
|
3859
|
+
});
|
|
3860
|
+
return loaded;
|
|
3861
|
+
}
|
|
3862
|
+
async function countJsonlLines(filePath) {
|
|
3863
|
+
jsonlLineCountCalls += 1;
|
|
3864
|
+
try {
|
|
3865
|
+
const rl = createInterface({
|
|
3866
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
3867
|
+
crlfDelay: Infinity
|
|
3868
|
+
});
|
|
3869
|
+
let count2 = 0;
|
|
3870
|
+
for await (const line of rl) {
|
|
3871
|
+
if (line.trim()) count2 += 1;
|
|
3872
|
+
}
|
|
3873
|
+
return count2;
|
|
3874
|
+
} catch {
|
|
3875
|
+
return 0;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
async function resolveOnDiskCount(jsonlPath) {
|
|
3879
|
+
if (diskCacheEntryCount > 0) return diskCacheEntryCount;
|
|
3880
|
+
const sidecar = await readCacheStat();
|
|
3881
|
+
if (sidecar) {
|
|
3882
|
+
diskCacheEntryCount = sidecar.entryCount;
|
|
3883
|
+
if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
|
|
3884
|
+
return sidecar.entryCount;
|
|
3885
|
+
}
|
|
3886
|
+
try {
|
|
3887
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
3888
|
+
} catch {
|
|
3889
|
+
return 0;
|
|
3890
|
+
}
|
|
3891
|
+
const counted = await countJsonlLines(jsonlPath);
|
|
3892
|
+
diskCacheEntryCount = counted;
|
|
3893
|
+
await writeCacheStat({
|
|
3894
|
+
version: 1,
|
|
3895
|
+
entryCount: counted,
|
|
3896
|
+
dimensions: cacheDimensions
|
|
3897
|
+
});
|
|
3898
|
+
return counted;
|
|
3899
|
+
}
|
|
3570
3900
|
async function loadDiskCache2() {
|
|
3571
|
-
if (diskCacheLoaded) return;
|
|
3901
|
+
if (diskCacheLoaded) return cache3.size;
|
|
3902
|
+
let loaded = 0;
|
|
3572
3903
|
try {
|
|
3573
|
-
const
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3904
|
+
const jsonlPath = cacheJsonlFile();
|
|
3905
|
+
let hasJsonl = false;
|
|
3906
|
+
try {
|
|
3907
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
3908
|
+
hasJsonl = true;
|
|
3909
|
+
} catch {
|
|
3910
|
+
hasJsonl = false;
|
|
3911
|
+
}
|
|
3912
|
+
if (hasJsonl) {
|
|
3913
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
3914
|
+
} else {
|
|
3915
|
+
const filePath = cacheFile();
|
|
3916
|
+
await stat(filePath);
|
|
3917
|
+
await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
3918
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
3919
|
+
}
|
|
3920
|
+
diskCacheLoadCount += 1;
|
|
3921
|
+
console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
|
|
3577
3922
|
} catch {
|
|
3578
3923
|
}
|
|
3579
3924
|
diskCacheLoaded = true;
|
|
3925
|
+
return loaded;
|
|
3580
3926
|
}
|
|
3581
3927
|
function startDiskCacheLoad() {
|
|
3582
3928
|
if (diskCacheLoaded || diskCacheLoadPromise) return;
|
|
3583
|
-
diskCacheLoadPromise = loadDiskCache2().catch(() => {
|
|
3929
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
3584
3930
|
});
|
|
3585
3931
|
}
|
|
3586
3932
|
async function ensureDiskCacheLoaded() {
|
|
3587
|
-
if (diskCacheLoaded) return;
|
|
3588
|
-
if (diskCacheLoadPromise) {
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
}
|
|
3592
|
-
await
|
|
3933
|
+
if (diskCacheLoaded) return cache3.size;
|
|
3934
|
+
if (!diskCacheLoadPromise) {
|
|
3935
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
3936
|
+
});
|
|
3937
|
+
}
|
|
3938
|
+
await diskCacheLoadPromise;
|
|
3939
|
+
return cache3.size;
|
|
3940
|
+
}
|
|
3941
|
+
function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
3942
|
+
return diskCacheLoadCount;
|
|
3943
|
+
}
|
|
3944
|
+
function resetApiEmbeddingCacheForTests() {
|
|
3945
|
+
cache3.clear();
|
|
3946
|
+
cachePayloadBytes = 0;
|
|
3947
|
+
cacheDimensions = null;
|
|
3948
|
+
diskCacheEntryCount = 0;
|
|
3949
|
+
diskCacheLoaded = false;
|
|
3950
|
+
diskCacheLoadPromise = null;
|
|
3951
|
+
diskCacheLoadCount = 0;
|
|
3952
|
+
diskCacheDirty2 = false;
|
|
3953
|
+
jsonlLineCountCalls = 0;
|
|
3954
|
+
}
|
|
3955
|
+
function seedApiEmbeddingCacheForTests(entries) {
|
|
3956
|
+
cache3.clear();
|
|
3957
|
+
cachePayloadBytes = 0;
|
|
3958
|
+
cacheDimensions = null;
|
|
3959
|
+
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
3960
|
+
diskCacheLoaded = true;
|
|
3961
|
+
diskCacheDirty2 = true;
|
|
3962
|
+
}
|
|
3963
|
+
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
3964
|
+
return cachePayloadBytes;
|
|
3965
|
+
}
|
|
3966
|
+
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
3967
|
+
return jsonlLineCountCalls;
|
|
3968
|
+
}
|
|
3969
|
+
function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
|
|
3970
|
+
return maxEntriesForDimensions(dimensions);
|
|
3593
3971
|
}
|
|
3594
3972
|
function dimsCacheKey(config) {
|
|
3595
3973
|
return [
|
|
@@ -3705,8 +4083,46 @@ async function saveDiskCacheNow() {
|
|
|
3705
4083
|
if (!diskCacheDirty2) return;
|
|
3706
4084
|
try {
|
|
3707
4085
|
await mkdir2(cacheDir(), { recursive: true });
|
|
3708
|
-
const
|
|
3709
|
-
await
|
|
4086
|
+
const jsonlPath = cacheJsonlFile();
|
|
4087
|
+
const existing = await resolveOnDiskCount(jsonlPath);
|
|
4088
|
+
if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
|
|
4089
|
+
console.error(
|
|
4090
|
+
`[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
|
|
4091
|
+
);
|
|
4092
|
+
diskCacheDirty2 = false;
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
4095
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
4096
|
+
await new Promise((resolve, reject) => {
|
|
4097
|
+
const out = createWriteStream(tmpPath);
|
|
4098
|
+
const entries = cache3.entries();
|
|
4099
|
+
const writeBatch = () => {
|
|
4100
|
+
for (let i2 = 0; i2 < 50; i2++) {
|
|
4101
|
+
const next = entries.next();
|
|
4102
|
+
if (next.done) {
|
|
4103
|
+
out.end();
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
4106
|
+
const [hash4, vector] = next.value;
|
|
4107
|
+
if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
|
|
4108
|
+
`)) {
|
|
4109
|
+
out.once("drain", writeBatch);
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
setImmediate(writeBatch);
|
|
4114
|
+
};
|
|
4115
|
+
out.on("finish", resolve);
|
|
4116
|
+
out.on("error", reject);
|
|
4117
|
+
writeBatch();
|
|
4118
|
+
});
|
|
4119
|
+
await rename(tmpPath, jsonlPath);
|
|
4120
|
+
diskCacheEntryCount = cache3.size;
|
|
4121
|
+
await writeCacheStat({
|
|
4122
|
+
version: 1,
|
|
4123
|
+
entryCount: cache3.size,
|
|
4124
|
+
dimensions: cacheDimensions
|
|
4125
|
+
});
|
|
3710
4126
|
diskCacheDirty2 = false;
|
|
3711
4127
|
} catch {
|
|
3712
4128
|
}
|
|
@@ -3720,12 +4136,7 @@ function scheduleDiskSave() {
|
|
|
3720
4136
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
3721
4137
|
}
|
|
3722
4138
|
function cacheSet(hash4, value) {
|
|
3723
|
-
|
|
3724
|
-
const firstKey = cache3.keys().next().value;
|
|
3725
|
-
if (firstKey !== void 0) cache3.delete(firstKey);
|
|
3726
|
-
}
|
|
3727
|
-
cache3.set(hash4, value);
|
|
3728
|
-
diskCacheDirty2 = true;
|
|
4139
|
+
cacheInsert(hash4, value, true);
|
|
3729
4140
|
}
|
|
3730
4141
|
function resolveEnvEmbeddingApiKey() {
|
|
3731
4142
|
return process.env.MEMORIX_EMBEDDING_API_KEY;
|
|
@@ -3844,18 +4255,27 @@ async function waitForRetry(waitMs, signal) {
|
|
|
3844
4255
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
3845
4256
|
});
|
|
3846
4257
|
}
|
|
3847
|
-
var cache3,
|
|
4258
|
+
var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES, cachePayloadBytes, cacheDimensions, diskCacheEntryCount, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, diskCacheLoadCount, jsonlLineCountCalls, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
|
|
3848
4259
|
var init_api_provider = __esm({
|
|
3849
4260
|
"src/embedding/api-provider.ts"() {
|
|
3850
4261
|
"use strict";
|
|
3851
4262
|
init_esm_shims();
|
|
3852
4263
|
init_provider();
|
|
3853
4264
|
cache3 = /* @__PURE__ */ new Map();
|
|
3854
|
-
|
|
4265
|
+
MAX_CACHE_ENTRIES = 2e5;
|
|
4266
|
+
MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
4267
|
+
BYTES_PER_DIM = 8;
|
|
4268
|
+
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
4269
|
+
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
4270
|
+
cachePayloadBytes = 0;
|
|
4271
|
+
cacheDimensions = null;
|
|
4272
|
+
diskCacheEntryCount = 0;
|
|
3855
4273
|
diskCacheDirty2 = false;
|
|
3856
4274
|
diskSaveTimer = null;
|
|
3857
4275
|
diskCacheLoaded = false;
|
|
3858
4276
|
diskCacheLoadPromise = null;
|
|
4277
|
+
diskCacheLoadCount = 0;
|
|
4278
|
+
jsonlLineCountCalls = 0;
|
|
3859
4279
|
MAX_INPUT_CHARS = 32e3;
|
|
3860
4280
|
MAX_CONCURRENCY = 4;
|
|
3861
4281
|
DISK_SAVE_DEBOUNCE_MS = 5e3;
|
|
@@ -3913,7 +4333,9 @@ var init_api_provider = __esm({
|
|
|
3913
4333
|
if (config.requestedDimensions) {
|
|
3914
4334
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
3915
4335
|
}
|
|
3916
|
-
|
|
4336
|
+
if (allowNetworkProbe) {
|
|
4337
|
+
startDiskCacheLoad();
|
|
4338
|
+
}
|
|
3917
4339
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
3918
4340
|
}
|
|
3919
4341
|
static resolveConfig() {
|
|
@@ -4189,6 +4611,7 @@ __export(provider_exports, {
|
|
|
4189
4611
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
4190
4612
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
4191
4613
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
4614
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
4192
4615
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
4193
4616
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
4194
4617
|
resetProvider: () => resetProvider,
|
|
@@ -4250,7 +4673,12 @@ function warnOnce(message) {
|
|
|
4250
4673
|
lastEmbeddingWarningAt = now3;
|
|
4251
4674
|
console.error(message);
|
|
4252
4675
|
}
|
|
4676
|
+
function embeddingModeToken() {
|
|
4677
|
+
return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
|
|
4678
|
+
}
|
|
4253
4679
|
function getEmbeddingMode2() {
|
|
4680
|
+
const token = embeddingModeToken();
|
|
4681
|
+
if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
|
|
4254
4682
|
try {
|
|
4255
4683
|
let cfg;
|
|
4256
4684
|
try {
|
|
@@ -4259,10 +4687,17 @@ function getEmbeddingMode2() {
|
|
|
4259
4687
|
cfg = (init_config(), __toCommonJS(config_exports));
|
|
4260
4688
|
}
|
|
4261
4689
|
const { getEmbeddingMode: cfgMode } = cfg;
|
|
4262
|
-
|
|
4690
|
+
cachedEmbeddingMode = cfgMode();
|
|
4691
|
+
cachedEmbeddingModeToken = token;
|
|
4692
|
+
return cachedEmbeddingMode;
|
|
4263
4693
|
} catch {
|
|
4264
|
-
|
|
4265
|
-
|
|
4694
|
+
if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
|
|
4695
|
+
cachedEmbeddingMode = token;
|
|
4696
|
+
cachedEmbeddingModeToken = token;
|
|
4697
|
+
return token;
|
|
4698
|
+
}
|
|
4699
|
+
cachedEmbeddingMode = "off";
|
|
4700
|
+
cachedEmbeddingModeToken = token;
|
|
4266
4701
|
return "off";
|
|
4267
4702
|
}
|
|
4268
4703
|
}
|
|
@@ -4296,13 +4731,36 @@ function isTemporaryEmbeddingFailure(error) {
|
|
|
4296
4731
|
if (!(error instanceof Error)) return false;
|
|
4297
4732
|
return /embedding api error \(401\)/i.test(error.message) || /invalid_api_key/i.test(error.message) || /incorrect api key/i.test(error.message) || /unauthorized/i.test(error.message) || /embedding api timeout/i.test(error.message) || /embedding api error \((402|429|5\d\d)\)/i.test(error.message) || /quota exceeded/i.test(error.message) || /account balance/i.test(error.message) || /fetch failed/i.test(error.message) || /econnreset/i.test(error.message) || /econnrefused/i.test(error.message) || /temporarily unavailable/i.test(error.message);
|
|
4298
4733
|
}
|
|
4734
|
+
function classifyEmbeddingFailure(error) {
|
|
4735
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4736
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
4737
|
+
return "authorization";
|
|
4738
|
+
}
|
|
4739
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
4740
|
+
return "billing";
|
|
4741
|
+
}
|
|
4742
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
4743
|
+
return "timeout";
|
|
4744
|
+
}
|
|
4745
|
+
return "upstream";
|
|
4746
|
+
}
|
|
4299
4747
|
function markTemporaryFailure(reason) {
|
|
4300
4748
|
provider = null;
|
|
4301
4749
|
initPromise = null;
|
|
4302
4750
|
lastInitWasTemporaryFailure = true;
|
|
4303
4751
|
lastFailureTimestamp = Date.now();
|
|
4752
|
+
lastEmbeddingFailure = {
|
|
4753
|
+
kind: classifyEmbeddingFailure(reason),
|
|
4754
|
+
occurredAt: lastFailureTimestamp
|
|
4755
|
+
};
|
|
4304
4756
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
4305
|
-
warnOnce(`[memorix] Embedding provider
|
|
4757
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
4758
|
+
}
|
|
4759
|
+
function markProviderReady(candidate) {
|
|
4760
|
+
provider = wrapProvider(candidate);
|
|
4761
|
+
lastInitWasTemporaryFailure = false;
|
|
4762
|
+
lastEmbeddingFailure = null;
|
|
4763
|
+
return provider;
|
|
4306
4764
|
}
|
|
4307
4765
|
async function createFastEmbedProvider() {
|
|
4308
4766
|
try {
|
|
@@ -4418,35 +4876,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
4418
4876
|
if (mode === "fastembed") {
|
|
4419
4877
|
const initialized = await createFastEmbedProvider();
|
|
4420
4878
|
if (!initialized) return null;
|
|
4421
|
-
provider =
|
|
4879
|
+
provider = markProviderReady(initialized);
|
|
4422
4880
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4423
4881
|
return provider;
|
|
4424
4882
|
}
|
|
4425
4883
|
if (mode === "transformers") {
|
|
4426
4884
|
const initialized = await createTransformersProvider();
|
|
4427
4885
|
if (!initialized) return null;
|
|
4428
|
-
provider =
|
|
4886
|
+
provider = markProviderReady(initialized);
|
|
4429
4887
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4430
4888
|
return provider;
|
|
4431
4889
|
}
|
|
4432
4890
|
if (mode === "api") {
|
|
4433
4891
|
const initialized = await createAPIProvider(options);
|
|
4434
4892
|
if (!initialized) return null;
|
|
4435
|
-
provider =
|
|
4893
|
+
provider = markProviderReady(initialized);
|
|
4436
4894
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4437
4895
|
return provider;
|
|
4438
4896
|
}
|
|
4439
4897
|
if (hasAPIEmbeddingConfig()) {
|
|
4440
4898
|
const initialized = await createAPIProvider(options);
|
|
4441
4899
|
if (initialized) {
|
|
4442
|
-
provider =
|
|
4900
|
+
provider = markProviderReady(initialized);
|
|
4443
4901
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4444
4902
|
return provider;
|
|
4445
4903
|
}
|
|
4446
4904
|
}
|
|
4447
4905
|
const localFallback = await createLocalFallbackProvider();
|
|
4448
4906
|
if (localFallback) {
|
|
4449
|
-
provider =
|
|
4907
|
+
provider = markProviderReady(localFallback);
|
|
4450
4908
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4451
4909
|
return provider;
|
|
4452
4910
|
}
|
|
@@ -4468,18 +4926,42 @@ async function isVectorSearchAvailable() {
|
|
|
4468
4926
|
const p = await getEmbeddingProvider();
|
|
4469
4927
|
return p !== null;
|
|
4470
4928
|
}
|
|
4929
|
+
function getEmbeddingRuntimeHealth() {
|
|
4930
|
+
const mode = getEmbeddingMode2();
|
|
4931
|
+
if (mode === "off") return { status: "disabled" };
|
|
4932
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
4933
|
+
return {
|
|
4934
|
+
status: "degraded",
|
|
4935
|
+
failure: {
|
|
4936
|
+
kind: lastEmbeddingFailure.kind,
|
|
4937
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
4938
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
4939
|
+
}
|
|
4940
|
+
};
|
|
4941
|
+
}
|
|
4942
|
+
if (provider) {
|
|
4943
|
+
return {
|
|
4944
|
+
status: "ready",
|
|
4945
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
4946
|
+
};
|
|
4947
|
+
}
|
|
4948
|
+
return { status: "uninitialized" };
|
|
4949
|
+
}
|
|
4471
4950
|
function isEmbeddingExplicitlyDisabled() {
|
|
4472
4951
|
return getEmbeddingMode2() === "off";
|
|
4473
4952
|
}
|
|
4474
4953
|
function resetProvider() {
|
|
4475
4954
|
provider = null;
|
|
4476
4955
|
initPromise = null;
|
|
4956
|
+
cachedEmbeddingMode = null;
|
|
4957
|
+
cachedEmbeddingModeToken = null;
|
|
4477
4958
|
lastInitWasTemporaryFailure = false;
|
|
4478
4959
|
lastFailureTimestamp = 0;
|
|
4960
|
+
lastEmbeddingFailure = null;
|
|
4479
4961
|
lastEmbeddingWarning = "";
|
|
4480
4962
|
lastEmbeddingWarningAt = 0;
|
|
4481
4963
|
}
|
|
4482
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
4964
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
4483
4965
|
var init_provider = __esm({
|
|
4484
4966
|
"src/embedding/provider.ts"() {
|
|
4485
4967
|
"use strict";
|
|
@@ -4503,7 +4985,10 @@ var init_provider = __esm({
|
|
|
4503
4985
|
MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
|
|
4504
4986
|
provider = null;
|
|
4505
4987
|
initPromise = null;
|
|
4988
|
+
cachedEmbeddingMode = null;
|
|
4989
|
+
cachedEmbeddingModeToken = null;
|
|
4506
4990
|
lastInitWasTemporaryFailure = false;
|
|
4991
|
+
lastEmbeddingFailure = null;
|
|
4507
4992
|
lastEmbeddingWarning = "";
|
|
4508
4993
|
lastEmbeddingWarningAt = 0;
|
|
4509
4994
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -6259,6 +6744,9 @@ async function attachCachedVectors(database, candidates) {
|
|
|
6259
6744
|
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
6260
6745
|
} catch {
|
|
6261
6746
|
}
|
|
6747
|
+
if (index % 50 === 0) {
|
|
6748
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6749
|
+
}
|
|
6262
6750
|
}
|
|
6263
6751
|
}
|
|
6264
6752
|
async function hydrateIndex(observations2, options = {}) {
|
|
@@ -6309,17 +6797,24 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6309
6797
|
await insert2(database, doc);
|
|
6310
6798
|
rememberObservationDoc(doc);
|
|
6311
6799
|
inserted++;
|
|
6800
|
+
if (inserted % 50 === 0) {
|
|
6801
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6802
|
+
}
|
|
6803
|
+
if (inserted % 200 === 0) {
|
|
6804
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6805
|
+
}
|
|
6312
6806
|
} catch {
|
|
6313
6807
|
}
|
|
6314
6808
|
}
|
|
6315
|
-
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6809
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6316
6810
|
}) : null;
|
|
6317
6811
|
return inserted;
|
|
6318
6812
|
}
|
|
6319
|
-
async function hydrateIndexForStartup(observations2) {
|
|
6813
|
+
async function hydrateIndexForStartup(observations2, options = {}) {
|
|
6320
6814
|
return hydrateIndex(observations2, {
|
|
6321
6815
|
allowNetworkProbe: false,
|
|
6322
|
-
deferCachedVectors: true
|
|
6816
|
+
deferCachedVectors: true,
|
|
6817
|
+
skipCachedVectors: options.skipCachedVectors
|
|
6323
6818
|
});
|
|
6324
6819
|
}
|
|
6325
6820
|
function getDeferredCachedVectorHydration() {
|
|
@@ -8410,7 +8905,7 @@ var init_types2 = __esm({
|
|
|
8410
8905
|
|
|
8411
8906
|
// src/media/asset-store.ts
|
|
8412
8907
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
8413
|
-
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
8908
|
+
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
8414
8909
|
import path9 from "path";
|
|
8415
8910
|
function mediaRoot(dataDir) {
|
|
8416
8911
|
return path9.resolve(dataDir, "media");
|
|
@@ -8483,7 +8978,7 @@ async function persistBuffer(bytes, destination) {
|
|
|
8483
8978
|
const temporary = `${destination}.${randomUUID5()}.tmp`;
|
|
8484
8979
|
try {
|
|
8485
8980
|
await writeFile3(temporary, bytes, { flag: "wx" });
|
|
8486
|
-
await
|
|
8981
|
+
await rename2(temporary, destination);
|
|
8487
8982
|
return true;
|
|
8488
8983
|
} catch (error) {
|
|
8489
8984
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -59563,9 +60058,9 @@ var require_node_domexception = __commonJS({
|
|
|
59563
60058
|
});
|
|
59564
60059
|
|
|
59565
60060
|
// node_modules/fetch-blob/from.js
|
|
59566
|
-
import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
|
|
60061
|
+
import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
59567
60062
|
import { basename } from "path";
|
|
59568
|
-
var import_node_domexception,
|
|
60063
|
+
var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
59569
60064
|
var init_from = __esm({
|
|
59570
60065
|
"node_modules/fetch-blob/from.js"() {
|
|
59571
60066
|
"use strict";
|
|
@@ -59573,23 +60068,23 @@ var init_from = __esm({
|
|
|
59573
60068
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
59574
60069
|
init_file();
|
|
59575
60070
|
init_fetch_blob();
|
|
59576
|
-
({ stat:
|
|
60071
|
+
({ stat: stat3 } = fs7);
|
|
59577
60072
|
blobFromSync = (path21, type) => fromBlob(statSync2(path21), path21, type);
|
|
59578
|
-
blobFrom = (path21, type) =>
|
|
59579
|
-
fileFrom = (path21, type) =>
|
|
60073
|
+
blobFrom = (path21, type) => stat3(path21).then((stat5) => fromBlob(stat5, path21, type));
|
|
60074
|
+
fileFrom = (path21, type) => stat3(path21).then((stat5) => fromFile(stat5, path21, type));
|
|
59580
60075
|
fileFromSync = (path21, type) => fromFile(statSync2(path21), path21, type);
|
|
59581
|
-
fromBlob = (
|
|
60076
|
+
fromBlob = (stat5, path21, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
59582
60077
|
path: path21,
|
|
59583
|
-
size:
|
|
59584
|
-
lastModified:
|
|
60078
|
+
size: stat5.size,
|
|
60079
|
+
lastModified: stat5.mtimeMs,
|
|
59585
60080
|
start: 0
|
|
59586
60081
|
})], { type });
|
|
59587
|
-
fromFile = (
|
|
60082
|
+
fromFile = (stat5, path21, type = "") => new file_default([new BlobDataItem({
|
|
59588
60083
|
path: path21,
|
|
59589
|
-
size:
|
|
59590
|
-
lastModified:
|
|
60084
|
+
size: stat5.size,
|
|
60085
|
+
lastModified: stat5.mtimeMs,
|
|
59591
60086
|
start: 0
|
|
59592
|
-
})], basename(path21), { type, lastModified:
|
|
60087
|
+
})], basename(path21), { type, lastModified: stat5.mtimeMs });
|
|
59593
60088
|
BlobDataItem = class _BlobDataItem {
|
|
59594
60089
|
#path;
|
|
59595
60090
|
#start;
|
|
@@ -59612,11 +60107,11 @@ var init_from = __esm({
|
|
|
59612
60107
|
});
|
|
59613
60108
|
}
|
|
59614
60109
|
async *stream() {
|
|
59615
|
-
const { mtimeMs } = await
|
|
60110
|
+
const { mtimeMs } = await stat3(this.#path);
|
|
59616
60111
|
if (mtimeMs > this.lastModified) {
|
|
59617
60112
|
throw new import_node_domexception.default("The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.", "NotReadableError");
|
|
59618
60113
|
}
|
|
59619
|
-
yield*
|
|
60114
|
+
yield* createReadStream2(this.#path, {
|
|
59620
60115
|
start: this.#start,
|
|
59621
60116
|
end: this.#start + this.size - 1
|
|
59622
60117
|
});
|
|
@@ -75246,7 +75741,7 @@ var init_wrapper = __esm({
|
|
|
75246
75741
|
});
|
|
75247
75742
|
|
|
75248
75743
|
// node_modules/@google/genai/dist/node/index.mjs
|
|
75249
|
-
import { createWriteStream } from "fs";
|
|
75744
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
75250
75745
|
import * as fs8 from "fs/promises";
|
|
75251
75746
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
75252
75747
|
import { Readable } from "stream";
|
|
@@ -92526,7 +93021,7 @@ ${underline}`);
|
|
|
92526
93021
|
if (params.downloadPath) {
|
|
92527
93022
|
const response = await downloadFile(params, apiClient);
|
|
92528
93023
|
if (response instanceof HttpResponse) {
|
|
92529
|
-
const writer =
|
|
93024
|
+
const writer = createWriteStream2(params.downloadPath);
|
|
92530
93025
|
const body = Readable.fromWeb(response.responseInternal.body);
|
|
92531
93026
|
body.pipe(writer);
|
|
92532
93027
|
await finished(writer);
|
|
@@ -142150,7 +142645,7 @@ function explainRetention(doc, referenceTime) {
|
|
|
142150
142645
|
};
|
|
142151
142646
|
}
|
|
142152
142647
|
function toRetentionDocument(obs, accessMap) {
|
|
142153
|
-
const
|
|
142648
|
+
const access2 = accessMap?.get(obs.id);
|
|
142154
142649
|
return {
|
|
142155
142650
|
id: `obs-${obs.id}`,
|
|
142156
142651
|
observationId: obs.id,
|
|
@@ -142164,8 +142659,8 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
142164
142659
|
tokens: obs.tokens,
|
|
142165
142660
|
createdAt: obs.createdAt,
|
|
142166
142661
|
projectId: obs.projectId,
|
|
142167
|
-
accessCount:
|
|
142168
|
-
lastAccessedAt:
|
|
142662
|
+
accessCount: access2?.accessCount ?? 0,
|
|
142663
|
+
lastAccessedAt: access2?.lastAccessedAt ?? "",
|
|
142169
142664
|
status: obs.status ?? "active",
|
|
142170
142665
|
source: obs.source ?? "agent",
|
|
142171
142666
|
sourceDetail: obs.sourceDetail ?? "",
|
|
@@ -144275,7 +144770,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
144275
144770
|
const rel = normalizeCodePath(relative(projectRoot, abs));
|
|
144276
144771
|
try {
|
|
144277
144772
|
const text2 = readFileSync6(abs, "utf-8");
|
|
144278
|
-
const
|
|
144773
|
+
const stat5 = fileStat ?? statSync3(abs);
|
|
144279
144774
|
const file = {
|
|
144280
144775
|
id: makeCodeFileId(projectId, rel),
|
|
144281
144776
|
projectId,
|
|
@@ -144284,8 +144779,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
144284
144779
|
contentHash: hashText(text2),
|
|
144285
144780
|
// Preserve filesystem precision. Rounding here made same-size edits in
|
|
144286
144781
|
// a short Windows timestamp window indistinguishable to refresh.
|
|
144287
|
-
mtimeMs: Number(
|
|
144288
|
-
sizeBytes: Number(
|
|
144782
|
+
mtimeMs: Number(stat5.mtimeMs),
|
|
144783
|
+
sizeBytes: Number(stat5.size),
|
|
144289
144784
|
indexedAt
|
|
144290
144785
|
};
|
|
144291
144786
|
return {
|
|
@@ -144312,18 +144807,18 @@ async function indexProjectLite(options) {
|
|
|
144312
144807
|
let skippedOversizedFiles = 0;
|
|
144313
144808
|
let unreadableFiles = 0;
|
|
144314
144809
|
for (const abs of paths) {
|
|
144315
|
-
let
|
|
144810
|
+
let stat5;
|
|
144316
144811
|
try {
|
|
144317
|
-
|
|
144812
|
+
stat5 = statSync3(abs);
|
|
144318
144813
|
} catch {
|
|
144319
144814
|
unreadableFiles++;
|
|
144320
144815
|
continue;
|
|
144321
144816
|
}
|
|
144322
|
-
if (Number(
|
|
144817
|
+
if (Number(stat5.size) > maxFileBytes) {
|
|
144323
144818
|
skippedOversizedFiles++;
|
|
144324
144819
|
continue;
|
|
144325
144820
|
}
|
|
144326
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
144821
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
144327
144822
|
if (!attempt.delta) {
|
|
144328
144823
|
if (attempt.unreadable) unreadableFiles++;
|
|
144329
144824
|
continue;
|
|
@@ -144351,16 +144846,16 @@ async function refreshProjectLite(store, options) {
|
|
|
144351
144846
|
for (const abs of paths) {
|
|
144352
144847
|
const rel = normalizeCodePath(relative(options.projectRoot, abs));
|
|
144353
144848
|
seenPaths.add(rel);
|
|
144354
|
-
let
|
|
144849
|
+
let stat5;
|
|
144355
144850
|
try {
|
|
144356
|
-
|
|
144851
|
+
stat5 = statSync3(abs);
|
|
144357
144852
|
} catch {
|
|
144358
144853
|
unreadableFiles++;
|
|
144359
144854
|
continue;
|
|
144360
144855
|
}
|
|
144361
144856
|
const existing = existingByPath.get(rel);
|
|
144362
|
-
const mtimeMs = Number(
|
|
144363
|
-
const sizeBytes = Number(
|
|
144857
|
+
const mtimeMs = Number(stat5.mtimeMs);
|
|
144858
|
+
const sizeBytes = Number(stat5.size);
|
|
144364
144859
|
if (sizeBytes > maxFileBytes) {
|
|
144365
144860
|
skippedOversizedFiles++;
|
|
144366
144861
|
if (existing) oversizedExistingFileIds.add(existing.id);
|
|
@@ -144370,7 +144865,7 @@ async function refreshProjectLite(store, options) {
|
|
|
144370
144865
|
unchangedFiles++;
|
|
144371
144866
|
continue;
|
|
144372
144867
|
}
|
|
144373
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
144868
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
144374
144869
|
if (!attempt.delta) {
|
|
144375
144870
|
if (attempt.unreadable) unreadableFiles++;
|
|
144376
144871
|
continue;
|
|
@@ -147767,12 +148262,25 @@ __export(observations_exports, {
|
|
|
147767
148262
|
prepareSearchIndex: () => prepareSearchIndex,
|
|
147768
148263
|
probeSearchIndex: () => probeSearchIndex,
|
|
147769
148264
|
reindexObservations: () => reindexObservations,
|
|
148265
|
+
resetObservationRuntime: () => resetObservationRuntime,
|
|
147770
148266
|
resolveObservations: () => resolveObservations,
|
|
147771
148267
|
storeObservation: () => storeObservation,
|
|
147772
148268
|
suggestTopicKey: () => suggestTopicKey,
|
|
147773
148269
|
updateObservationAdmission: () => updateObservationAdmission,
|
|
147774
148270
|
withFreshObservations: () => withFreshObservations
|
|
147775
148271
|
});
|
|
148272
|
+
function resetObservationRuntime() {
|
|
148273
|
+
observations = [];
|
|
148274
|
+
nextId = 1;
|
|
148275
|
+
projectDir = null;
|
|
148276
|
+
searchIndexPrepared = false;
|
|
148277
|
+
corpusLoaded = false;
|
|
148278
|
+
loadedObservationStore = null;
|
|
148279
|
+
embeddingWriteMode = "background";
|
|
148280
|
+
embeddingWorkerProjectRoot = void 0;
|
|
148281
|
+
vectorMissingIds.clear();
|
|
148282
|
+
vectorSchemaUpgradePromise = null;
|
|
148283
|
+
}
|
|
147776
148284
|
function logEmbeddingFailureOnce(key, message) {
|
|
147777
148285
|
const now3 = Date.now();
|
|
147778
148286
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
@@ -147881,23 +148389,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
|
147881
148389
|
async function initObservations(dir, options = {}) {
|
|
147882
148390
|
embeddingWriteMode = options.embeddingWriteMode ?? "background";
|
|
147883
148391
|
embeddingWorkerProjectRoot = options.projectRoot;
|
|
147884
|
-
if (projectDir === dir) return;
|
|
147885
148392
|
await initObservationStore(dir);
|
|
147886
148393
|
const store = getObservationStore();
|
|
147887
|
-
|
|
148394
|
+
if (projectDir === dir && loadedObservationStore === store) {
|
|
148395
|
+
if (options.skipCorpusLoad || corpusLoaded) return;
|
|
148396
|
+
}
|
|
147888
148397
|
nextId = await store.loadIdCounter();
|
|
147889
148398
|
projectDir = dir;
|
|
147890
148399
|
searchIndexPrepared = false;
|
|
147891
148400
|
vectorSchemaUpgradePromise = null;
|
|
148401
|
+
if (options.skipCorpusLoad) {
|
|
148402
|
+
observations = [];
|
|
148403
|
+
corpusLoaded = false;
|
|
148404
|
+
loadedObservationStore = store;
|
|
148405
|
+
return;
|
|
148406
|
+
}
|
|
148407
|
+
observations = await store.loadAll();
|
|
148408
|
+
corpusLoaded = true;
|
|
148409
|
+
loadedObservationStore = store;
|
|
147892
148410
|
}
|
|
147893
148411
|
function scheduleObservationEmbedding(input) {
|
|
147894
148412
|
const { observationId: observationId2, projectId, searchableText, doc } = input;
|
|
147895
148413
|
vectorMissingIds.add(observationId2);
|
|
148414
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
148415
|
+
vectorMissingIds.delete(observationId2);
|
|
148416
|
+
return;
|
|
148417
|
+
}
|
|
147896
148418
|
if (embeddingWriteMode === "deferred") {
|
|
147897
|
-
if (isEmbeddingExplicitlyDisabled()) {
|
|
147898
|
-
vectorMissingIds.delete(observationId2);
|
|
147899
|
-
return;
|
|
147900
|
-
}
|
|
147901
148419
|
queueVectorBackfill(projectId, {
|
|
147902
148420
|
detachedWorker: true,
|
|
147903
148421
|
projectRoot: embeddingWorkerProjectRoot
|
|
@@ -147944,7 +148462,7 @@ function scheduleObservationEmbedding(input) {
|
|
|
147944
148462
|
});
|
|
147945
148463
|
}
|
|
147946
148464
|
async function ensureFreshObservations() {
|
|
147947
|
-
if (!projectDir) return false;
|
|
148465
|
+
if (!projectDir || !corpusLoaded) return false;
|
|
147948
148466
|
try {
|
|
147949
148467
|
const store = getObservationStore();
|
|
147950
148468
|
const wasStale = await store.ensureFresh();
|
|
@@ -147985,7 +148503,7 @@ async function storeObservation(input) {
|
|
|
147985
148503
|
});
|
|
147986
148504
|
if (safeAttachments) input = { ...input, attachments: safeAttachments };
|
|
147987
148505
|
await ensureFreshObservations();
|
|
147988
|
-
if (input.topicKey) {
|
|
148506
|
+
if (input.topicKey && corpusLoaded) {
|
|
147989
148507
|
const existing = observations.find(
|
|
147990
148508
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
147991
148509
|
);
|
|
@@ -148082,13 +148600,17 @@ async function storeObservation(input) {
|
|
|
148082
148600
|
});
|
|
148083
148601
|
observation.writeGeneration = store.getGeneration();
|
|
148084
148602
|
if (upsertedInsideLock || reloadCacheAfterCommit) {
|
|
148085
|
-
|
|
148086
|
-
|
|
148087
|
-
|
|
148088
|
-
|
|
148603
|
+
if (corpusLoaded) {
|
|
148604
|
+
observations = await store.loadAll();
|
|
148605
|
+
nextId = await store.loadIdCounter();
|
|
148606
|
+
if (upsertedInsideLock) {
|
|
148607
|
+
observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
|
|
148608
|
+
}
|
|
148609
|
+
} else {
|
|
148610
|
+
nextId = await store.loadIdCounter();
|
|
148089
148611
|
}
|
|
148090
148612
|
} else {
|
|
148091
|
-
observations.push(observation);
|
|
148613
|
+
if (corpusLoaded) observations.push(observation);
|
|
148092
148614
|
nextId = observation.id + 1;
|
|
148093
148615
|
}
|
|
148094
148616
|
if (upsertedInsideLock) return;
|
|
@@ -148156,7 +148678,9 @@ async function storeObservation(input) {
|
|
|
148156
148678
|
createdByAgentId: input.createdByAgentId ?? "",
|
|
148157
148679
|
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
148158
148680
|
};
|
|
148159
|
-
|
|
148681
|
+
if (corpusLoaded) {
|
|
148682
|
+
await insertObservation(doc);
|
|
148683
|
+
}
|
|
148160
148684
|
};
|
|
148161
148685
|
await assignAndPersist();
|
|
148162
148686
|
if (upsertedInsideLock) {
|
|
@@ -148518,9 +149042,11 @@ async function reindexObservations() {
|
|
|
148518
149042
|
searchIndexPrepared = true;
|
|
148519
149043
|
return count2;
|
|
148520
149044
|
}
|
|
148521
|
-
async function prepareSearchIndex() {
|
|
149045
|
+
async function prepareSearchIndex(options = {}) {
|
|
148522
149046
|
if (searchIndexPrepared) return 0;
|
|
148523
|
-
const count2 = await hydrateIndexForStartup(observations
|
|
149047
|
+
const count2 = await hydrateIndexForStartup(observations, {
|
|
149048
|
+
skipCachedVectors: options.skipCachedVectors
|
|
149049
|
+
});
|
|
148524
149050
|
if (count2 === 0) {
|
|
148525
149051
|
searchIndexPrepared = true;
|
|
148526
149052
|
return 0;
|
|
@@ -148712,7 +149238,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
148712
149238
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
148713
149239
|
};
|
|
148714
149240
|
}
|
|
148715
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
149241
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
148716
149242
|
var init_observations = __esm({
|
|
148717
149243
|
"src/memory/observations.ts"() {
|
|
148718
149244
|
"use strict";
|
|
@@ -148731,6 +149257,8 @@ var init_observations = __esm({
|
|
|
148731
149257
|
projectDir = null;
|
|
148732
149258
|
searchIndexPrepared = false;
|
|
148733
149259
|
embeddingWriteMode = "background";
|
|
149260
|
+
corpusLoaded = false;
|
|
149261
|
+
loadedObservationStore = null;
|
|
148734
149262
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
148735
149263
|
vectorBackfillRunning = false;
|
|
148736
149264
|
vectorSchemaUpgradePromise = null;
|