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
|
@@ -1369,8 +1369,8 @@ async function acquireLock(lockPath) {
|
|
|
1369
1369
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
1370
1370
|
if (code === "EEXIST" || code === "EPERM") {
|
|
1371
1371
|
try {
|
|
1372
|
-
const
|
|
1373
|
-
if (Date.now() -
|
|
1372
|
+
const stat5 = await fs2.stat(lockPath);
|
|
1373
|
+
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
1374
1374
|
await fs2.unlink(lockPath).catch(() => {
|
|
1375
1375
|
});
|
|
1376
1376
|
continue;
|
|
@@ -2481,24 +2481,131 @@ var init_transformers_provider = __esm({
|
|
|
2481
2481
|
// src/embedding/api-provider.ts
|
|
2482
2482
|
var api_provider_exports = {};
|
|
2483
2483
|
__export(api_provider_exports, {
|
|
2484
|
-
APIEmbeddingProvider: () => APIEmbeddingProvider
|
|
2484
|
+
APIEmbeddingProvider: () => APIEmbeddingProvider,
|
|
2485
|
+
convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
|
|
2486
|
+
ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
|
|
2487
|
+
getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
|
|
2488
|
+
getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
|
|
2489
|
+
getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
|
|
2490
|
+
getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
|
|
2491
|
+
migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
|
|
2492
|
+
parseCacheJsonInWorker: () => parseCacheJsonInWorker,
|
|
2493
|
+
resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
|
|
2494
|
+
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
2495
|
+
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
2485
2496
|
});
|
|
2486
2497
|
import { createHash as createHash2 } from "crypto";
|
|
2487
|
-
import {
|
|
2498
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
2499
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
2488
2500
|
import { join as join3 } from "path";
|
|
2489
2501
|
import { homedir as homedir7 } from "os";
|
|
2502
|
+
import { createInterface } from "readline";
|
|
2490
2503
|
function cacheDir() {
|
|
2491
2504
|
return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
|
|
2492
2505
|
}
|
|
2493
2506
|
function cacheFile() {
|
|
2494
2507
|
return join3(cacheDir(), ".embedding-api-cache.json");
|
|
2495
2508
|
}
|
|
2509
|
+
function cacheJsonlFile() {
|
|
2510
|
+
return join3(cacheDir(), ".embedding-api-cache.jsonl");
|
|
2511
|
+
}
|
|
2496
2512
|
function dimsCacheFile() {
|
|
2497
2513
|
return join3(cacheDir(), ".embedding-dims-cache.json");
|
|
2498
2514
|
}
|
|
2499
2515
|
function cacheMetaFile() {
|
|
2500
2516
|
return join3(cacheDir(), ".embedding-api-cache-meta.json");
|
|
2501
2517
|
}
|
|
2518
|
+
function cacheStatFile() {
|
|
2519
|
+
return join3(cacheDir(), ".embedding-api-cache-stat.json");
|
|
2520
|
+
}
|
|
2521
|
+
function payloadBytes(vector) {
|
|
2522
|
+
return vector.length * BYTES_PER_DIM;
|
|
2523
|
+
}
|
|
2524
|
+
function maxCachePayloadBytes() {
|
|
2525
|
+
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
2526
|
+
if (raw) {
|
|
2527
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2528
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
2529
|
+
}
|
|
2530
|
+
return MAX_CACHE_PAYLOAD_BYTES;
|
|
2531
|
+
}
|
|
2532
|
+
function maxEntriesForDimensions(dimensions) {
|
|
2533
|
+
const dim = Math.max(1, Math.floor(dimensions));
|
|
2534
|
+
const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
|
|
2535
|
+
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
2536
|
+
}
|
|
2537
|
+
function rememberDimensions(vector) {
|
|
2538
|
+
if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
|
|
2539
|
+
cacheDimensions = vector.length;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
function evictOldest() {
|
|
2543
|
+
const firstKey = cache3.keys().next().value;
|
|
2544
|
+
if (firstKey === void 0) return;
|
|
2545
|
+
const old = cache3.get(firstKey);
|
|
2546
|
+
cache3.delete(firstKey);
|
|
2547
|
+
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
2548
|
+
}
|
|
2549
|
+
function evictToFit(incomingBytes, incomingDims) {
|
|
2550
|
+
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
2551
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
|
|
2552
|
+
evictOldest();
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
function isNumberArray(value) {
|
|
2556
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
2557
|
+
}
|
|
2558
|
+
function parseCacheRow(raw) {
|
|
2559
|
+
if (Array.isArray(raw)) {
|
|
2560
|
+
const hash4 = raw[0];
|
|
2561
|
+
const vector = raw[1];
|
|
2562
|
+
if (typeof hash4 === "string" && isNumberArray(vector)) {
|
|
2563
|
+
return { hash: hash4, vector };
|
|
2564
|
+
}
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
if (raw && typeof raw === "object") {
|
|
2568
|
+
const row = raw;
|
|
2569
|
+
if (typeof row.h === "string" && isNumberArray(row.v)) {
|
|
2570
|
+
return { hash: row.h, vector: row.v };
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
function cacheInsert(hash4, value, dirty) {
|
|
2576
|
+
rememberDimensions(value);
|
|
2577
|
+
const incoming = payloadBytes(value);
|
|
2578
|
+
const existing = cache3.get(hash4);
|
|
2579
|
+
if (existing) {
|
|
2580
|
+
cachePayloadBytes -= payloadBytes(existing);
|
|
2581
|
+
cache3.delete(hash4);
|
|
2582
|
+
}
|
|
2583
|
+
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
2584
|
+
cache3.set(hash4, value);
|
|
2585
|
+
cachePayloadBytes += incoming;
|
|
2586
|
+
if (dirty) diskCacheDirty2 = true;
|
|
2587
|
+
}
|
|
2588
|
+
async function readCacheStat() {
|
|
2589
|
+
try {
|
|
2590
|
+
const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
|
|
2591
|
+
if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
|
|
2592
|
+
return {
|
|
2593
|
+
version: 1,
|
|
2594
|
+
entryCount: Math.floor(data.entryCount),
|
|
2595
|
+
dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
} catch {
|
|
2599
|
+
}
|
|
2600
|
+
return null;
|
|
2601
|
+
}
|
|
2602
|
+
async function writeCacheStat(stat5) {
|
|
2603
|
+
try {
|
|
2604
|
+
await mkdir2(cacheDir(), { recursive: true });
|
|
2605
|
+
await writeFile2(cacheStatFile(), JSON.stringify(stat5));
|
|
2606
|
+
} catch {
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2502
2609
|
function normalizeText(text2) {
|
|
2503
2610
|
return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
|
|
2504
2611
|
}
|
|
@@ -2584,29 +2691,300 @@ function toProviderInput(input, baseUrl) {
|
|
|
2584
2691
|
}
|
|
2585
2692
|
return { type: input.modality, url: input.url };
|
|
2586
2693
|
}
|
|
2694
|
+
function parseCacheJsonInWorker(filePath) {
|
|
2695
|
+
return new Promise((resolve, reject) => {
|
|
2696
|
+
void import("worker_threads").then(({ Worker }) => {
|
|
2697
|
+
const worker = new Worker(
|
|
2698
|
+
`
|
|
2699
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
2700
|
+
const { readFileSync } = require('node:fs');
|
|
2701
|
+
try {
|
|
2702
|
+
const raw = readFileSync(workerData.filePath, 'utf8');
|
|
2703
|
+
parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
|
|
2704
|
+
} catch (err) {
|
|
2705
|
+
parentPort.postMessage({
|
|
2706
|
+
ok: false,
|
|
2707
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
`,
|
|
2711
|
+
{ eval: true, workerData: { filePath } }
|
|
2712
|
+
);
|
|
2713
|
+
worker.once("message", (msg) => {
|
|
2714
|
+
void worker.terminate();
|
|
2715
|
+
if (msg?.ok && Array.isArray(msg.entries)) {
|
|
2716
|
+
resolve(msg.entries);
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
reject(new Error(msg?.error || "embedding cache worker parse failed"));
|
|
2720
|
+
});
|
|
2721
|
+
worker.once("error", reject);
|
|
2722
|
+
}).catch(reject);
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
function findJsonValueEnd(source) {
|
|
2726
|
+
let i2 = 0;
|
|
2727
|
+
while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
|
|
2728
|
+
if (i2 >= source.length) return -1;
|
|
2729
|
+
const start = source[i2];
|
|
2730
|
+
if (start === '"') {
|
|
2731
|
+
i2 += 1;
|
|
2732
|
+
while (i2 < source.length) {
|
|
2733
|
+
if (source[i2] === "\\") {
|
|
2734
|
+
i2 += 2;
|
|
2735
|
+
continue;
|
|
2736
|
+
}
|
|
2737
|
+
if (source[i2] === '"') return i2 + 1;
|
|
2738
|
+
i2 += 1;
|
|
2739
|
+
}
|
|
2740
|
+
return -1;
|
|
2741
|
+
}
|
|
2742
|
+
if (start === "{" || start === "[") {
|
|
2743
|
+
const close = start === "{" ? "}" : "]";
|
|
2744
|
+
let depth = 0;
|
|
2745
|
+
let inString = false;
|
|
2746
|
+
for (; i2 < source.length; i2 += 1) {
|
|
2747
|
+
const ch = source[i2];
|
|
2748
|
+
if (inString) {
|
|
2749
|
+
if (ch === "\\") {
|
|
2750
|
+
i2 += 1;
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
if (ch === '"') inString = false;
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
if (ch === '"') {
|
|
2757
|
+
inString = true;
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
if (ch === start) depth += 1;
|
|
2761
|
+
else if (ch === close) {
|
|
2762
|
+
depth -= 1;
|
|
2763
|
+
if (depth === 0) return i2 + 1;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return -1;
|
|
2767
|
+
}
|
|
2768
|
+
const rest = source.slice(i2);
|
|
2769
|
+
const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
|
|
2770
|
+
return match2 ? i2 + match2[0].length : -1;
|
|
2771
|
+
}
|
|
2772
|
+
async function streamJsonArrayToJsonl(filePath, tmpPath) {
|
|
2773
|
+
const input = createReadStream(filePath, { encoding: "utf8" });
|
|
2774
|
+
const out = createWriteStream(tmpPath);
|
|
2775
|
+
let buf = "";
|
|
2776
|
+
let started = false;
|
|
2777
|
+
let count2 = 0;
|
|
2778
|
+
const writeLine = async (line) => {
|
|
2779
|
+
if (!out.write(line)) {
|
|
2780
|
+
await new Promise((resolve, reject) => {
|
|
2781
|
+
out.once("drain", resolve);
|
|
2782
|
+
out.once("error", reject);
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
try {
|
|
2787
|
+
for await (const chunk of input) {
|
|
2788
|
+
buf += chunk;
|
|
2789
|
+
if (!started) {
|
|
2790
|
+
const idx = buf.search(/\S/);
|
|
2791
|
+
if (idx < 0) continue;
|
|
2792
|
+
if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
|
|
2793
|
+
buf = buf.slice(idx + 1);
|
|
2794
|
+
started = true;
|
|
2795
|
+
}
|
|
2796
|
+
while (true) {
|
|
2797
|
+
buf = buf.replace(/^\s*,?\s*/, "");
|
|
2798
|
+
if (!buf) break;
|
|
2799
|
+
if (buf[0] === "]") {
|
|
2800
|
+
buf = "";
|
|
2801
|
+
break;
|
|
2802
|
+
}
|
|
2803
|
+
const end = findJsonValueEnd(buf);
|
|
2804
|
+
if (end < 0) break;
|
|
2805
|
+
const value = JSON.parse(buf.slice(0, end));
|
|
2806
|
+
buf = buf.slice(end);
|
|
2807
|
+
const row = parseCacheRow(value);
|
|
2808
|
+
if (row) {
|
|
2809
|
+
await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
|
|
2810
|
+
`);
|
|
2811
|
+
count2 += 1;
|
|
2812
|
+
if (count2 % 50 === 0) {
|
|
2813
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
|
|
2818
|
+
throw new Error("embedding cache entry too large to migrate safely");
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
} finally {
|
|
2822
|
+
await new Promise((resolve, reject) => {
|
|
2823
|
+
out.end(() => resolve());
|
|
2824
|
+
out.once("error", reject);
|
|
2825
|
+
});
|
|
2826
|
+
}
|
|
2827
|
+
return count2;
|
|
2828
|
+
}
|
|
2829
|
+
async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
|
|
2830
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
2831
|
+
try {
|
|
2832
|
+
const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
|
|
2833
|
+
await rename(tmpPath, jsonlPath);
|
|
2834
|
+
diskCacheEntryCount = count2;
|
|
2835
|
+
await writeCacheStat({
|
|
2836
|
+
version: 1,
|
|
2837
|
+
entryCount: count2,
|
|
2838
|
+
dimensions: cacheDimensions
|
|
2839
|
+
});
|
|
2840
|
+
return count2;
|
|
2841
|
+
} catch (err) {
|
|
2842
|
+
await unlink(tmpPath).catch(() => void 0);
|
|
2843
|
+
throw err;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
function convertArrayCacheToJsonl(filePath, jsonlPath) {
|
|
2847
|
+
return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
2848
|
+
}
|
|
2849
|
+
async function loadCacheJsonl(filePath) {
|
|
2850
|
+
const rl = createInterface({
|
|
2851
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
2852
|
+
crlfDelay: Infinity
|
|
2853
|
+
});
|
|
2854
|
+
let loaded = 0;
|
|
2855
|
+
let onDiskCount = 0;
|
|
2856
|
+
for await (const line of rl) {
|
|
2857
|
+
const trimmed = line.trim();
|
|
2858
|
+
if (!trimmed) continue;
|
|
2859
|
+
try {
|
|
2860
|
+
const row = parseCacheRow(JSON.parse(trimmed));
|
|
2861
|
+
if (!row) continue;
|
|
2862
|
+
onDiskCount += 1;
|
|
2863
|
+
cacheInsert(row.hash, row.vector, false);
|
|
2864
|
+
loaded = cache3.size;
|
|
2865
|
+
} catch {
|
|
2866
|
+
}
|
|
2867
|
+
if (onDiskCount % 250 === 0) {
|
|
2868
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
diskCacheEntryCount = onDiskCount;
|
|
2872
|
+
await writeCacheStat({
|
|
2873
|
+
version: 1,
|
|
2874
|
+
entryCount: onDiskCount,
|
|
2875
|
+
dimensions: cacheDimensions
|
|
2876
|
+
});
|
|
2877
|
+
return loaded;
|
|
2878
|
+
}
|
|
2879
|
+
async function countJsonlLines(filePath) {
|
|
2880
|
+
jsonlLineCountCalls += 1;
|
|
2881
|
+
try {
|
|
2882
|
+
const rl = createInterface({
|
|
2883
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
2884
|
+
crlfDelay: Infinity
|
|
2885
|
+
});
|
|
2886
|
+
let count2 = 0;
|
|
2887
|
+
for await (const line of rl) {
|
|
2888
|
+
if (line.trim()) count2 += 1;
|
|
2889
|
+
}
|
|
2890
|
+
return count2;
|
|
2891
|
+
} catch {
|
|
2892
|
+
return 0;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
async function resolveOnDiskCount(jsonlPath) {
|
|
2896
|
+
if (diskCacheEntryCount > 0) return diskCacheEntryCount;
|
|
2897
|
+
const sidecar = await readCacheStat();
|
|
2898
|
+
if (sidecar) {
|
|
2899
|
+
diskCacheEntryCount = sidecar.entryCount;
|
|
2900
|
+
if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
|
|
2901
|
+
return sidecar.entryCount;
|
|
2902
|
+
}
|
|
2903
|
+
try {
|
|
2904
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
2905
|
+
} catch {
|
|
2906
|
+
return 0;
|
|
2907
|
+
}
|
|
2908
|
+
const counted = await countJsonlLines(jsonlPath);
|
|
2909
|
+
diskCacheEntryCount = counted;
|
|
2910
|
+
await writeCacheStat({
|
|
2911
|
+
version: 1,
|
|
2912
|
+
entryCount: counted,
|
|
2913
|
+
dimensions: cacheDimensions
|
|
2914
|
+
});
|
|
2915
|
+
return counted;
|
|
2916
|
+
}
|
|
2587
2917
|
async function loadDiskCache2() {
|
|
2588
|
-
if (diskCacheLoaded) return;
|
|
2918
|
+
if (diskCacheLoaded) return cache3.size;
|
|
2919
|
+
let loaded = 0;
|
|
2589
2920
|
try {
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2921
|
+
const jsonlPath = cacheJsonlFile();
|
|
2922
|
+
let hasJsonl = false;
|
|
2923
|
+
try {
|
|
2924
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
2925
|
+
hasJsonl = true;
|
|
2926
|
+
} catch {
|
|
2927
|
+
hasJsonl = false;
|
|
2928
|
+
}
|
|
2929
|
+
if (hasJsonl) {
|
|
2930
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
2931
|
+
} else {
|
|
2932
|
+
const filePath = cacheFile();
|
|
2933
|
+
await stat(filePath);
|
|
2934
|
+
await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
2935
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
2936
|
+
}
|
|
2937
|
+
diskCacheLoadCount += 1;
|
|
2938
|
+
console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
|
|
2594
2939
|
} catch {
|
|
2595
2940
|
}
|
|
2596
2941
|
diskCacheLoaded = true;
|
|
2942
|
+
return loaded;
|
|
2597
2943
|
}
|
|
2598
2944
|
function startDiskCacheLoad() {
|
|
2599
2945
|
if (diskCacheLoaded || diskCacheLoadPromise) return;
|
|
2600
|
-
diskCacheLoadPromise = loadDiskCache2().catch(() => {
|
|
2946
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
2601
2947
|
});
|
|
2602
2948
|
}
|
|
2603
2949
|
async function ensureDiskCacheLoaded() {
|
|
2604
|
-
if (diskCacheLoaded) return;
|
|
2605
|
-
if (diskCacheLoadPromise) {
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
}
|
|
2609
|
-
await
|
|
2950
|
+
if (diskCacheLoaded) return cache3.size;
|
|
2951
|
+
if (!diskCacheLoadPromise) {
|
|
2952
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
await diskCacheLoadPromise;
|
|
2956
|
+
return cache3.size;
|
|
2957
|
+
}
|
|
2958
|
+
function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
2959
|
+
return diskCacheLoadCount;
|
|
2960
|
+
}
|
|
2961
|
+
function resetApiEmbeddingCacheForTests() {
|
|
2962
|
+
cache3.clear();
|
|
2963
|
+
cachePayloadBytes = 0;
|
|
2964
|
+
cacheDimensions = null;
|
|
2965
|
+
diskCacheEntryCount = 0;
|
|
2966
|
+
diskCacheLoaded = false;
|
|
2967
|
+
diskCacheLoadPromise = null;
|
|
2968
|
+
diskCacheLoadCount = 0;
|
|
2969
|
+
diskCacheDirty2 = false;
|
|
2970
|
+
jsonlLineCountCalls = 0;
|
|
2971
|
+
}
|
|
2972
|
+
function seedApiEmbeddingCacheForTests(entries) {
|
|
2973
|
+
cache3.clear();
|
|
2974
|
+
cachePayloadBytes = 0;
|
|
2975
|
+
cacheDimensions = null;
|
|
2976
|
+
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
2977
|
+
diskCacheLoaded = true;
|
|
2978
|
+
diskCacheDirty2 = true;
|
|
2979
|
+
}
|
|
2980
|
+
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
2981
|
+
return cachePayloadBytes;
|
|
2982
|
+
}
|
|
2983
|
+
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
2984
|
+
return jsonlLineCountCalls;
|
|
2985
|
+
}
|
|
2986
|
+
function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
|
|
2987
|
+
return maxEntriesForDimensions(dimensions);
|
|
2610
2988
|
}
|
|
2611
2989
|
function dimsCacheKey(config) {
|
|
2612
2990
|
return [
|
|
@@ -2722,8 +3100,46 @@ async function saveDiskCacheNow() {
|
|
|
2722
3100
|
if (!diskCacheDirty2) return;
|
|
2723
3101
|
try {
|
|
2724
3102
|
await mkdir2(cacheDir(), { recursive: true });
|
|
2725
|
-
const
|
|
2726
|
-
await
|
|
3103
|
+
const jsonlPath = cacheJsonlFile();
|
|
3104
|
+
const existing = await resolveOnDiskCount(jsonlPath);
|
|
3105
|
+
if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
|
|
3106
|
+
console.error(
|
|
3107
|
+
`[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
|
|
3108
|
+
);
|
|
3109
|
+
diskCacheDirty2 = false;
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
3113
|
+
await new Promise((resolve, reject) => {
|
|
3114
|
+
const out = createWriteStream(tmpPath);
|
|
3115
|
+
const entries = cache3.entries();
|
|
3116
|
+
const writeBatch = () => {
|
|
3117
|
+
for (let i2 = 0; i2 < 50; i2++) {
|
|
3118
|
+
const next = entries.next();
|
|
3119
|
+
if (next.done) {
|
|
3120
|
+
out.end();
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
const [hash4, vector] = next.value;
|
|
3124
|
+
if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
|
|
3125
|
+
`)) {
|
|
3126
|
+
out.once("drain", writeBatch);
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
setImmediate(writeBatch);
|
|
3131
|
+
};
|
|
3132
|
+
out.on("finish", resolve);
|
|
3133
|
+
out.on("error", reject);
|
|
3134
|
+
writeBatch();
|
|
3135
|
+
});
|
|
3136
|
+
await rename(tmpPath, jsonlPath);
|
|
3137
|
+
diskCacheEntryCount = cache3.size;
|
|
3138
|
+
await writeCacheStat({
|
|
3139
|
+
version: 1,
|
|
3140
|
+
entryCount: cache3.size,
|
|
3141
|
+
dimensions: cacheDimensions
|
|
3142
|
+
});
|
|
2727
3143
|
diskCacheDirty2 = false;
|
|
2728
3144
|
} catch {
|
|
2729
3145
|
}
|
|
@@ -2737,12 +3153,7 @@ function scheduleDiskSave() {
|
|
|
2737
3153
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
2738
3154
|
}
|
|
2739
3155
|
function cacheSet(hash4, value) {
|
|
2740
|
-
|
|
2741
|
-
const firstKey = cache3.keys().next().value;
|
|
2742
|
-
if (firstKey !== void 0) cache3.delete(firstKey);
|
|
2743
|
-
}
|
|
2744
|
-
cache3.set(hash4, value);
|
|
2745
|
-
diskCacheDirty2 = true;
|
|
3156
|
+
cacheInsert(hash4, value, true);
|
|
2746
3157
|
}
|
|
2747
3158
|
function resolveEnvEmbeddingApiKey() {
|
|
2748
3159
|
return process.env.MEMORIX_EMBEDDING_API_KEY;
|
|
@@ -2861,18 +3272,27 @@ async function waitForRetry(waitMs, signal) {
|
|
|
2861
3272
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2862
3273
|
});
|
|
2863
3274
|
}
|
|
2864
|
-
var cache3,
|
|
3275
|
+
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;
|
|
2865
3276
|
var init_api_provider = __esm({
|
|
2866
3277
|
"src/embedding/api-provider.ts"() {
|
|
2867
3278
|
"use strict";
|
|
2868
3279
|
init_esm_shims();
|
|
2869
3280
|
init_provider();
|
|
2870
3281
|
cache3 = /* @__PURE__ */ new Map();
|
|
2871
|
-
|
|
3282
|
+
MAX_CACHE_ENTRIES = 2e5;
|
|
3283
|
+
MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
3284
|
+
BYTES_PER_DIM = 8;
|
|
3285
|
+
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
3286
|
+
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
3287
|
+
cachePayloadBytes = 0;
|
|
3288
|
+
cacheDimensions = null;
|
|
3289
|
+
diskCacheEntryCount = 0;
|
|
2872
3290
|
diskCacheDirty2 = false;
|
|
2873
3291
|
diskSaveTimer = null;
|
|
2874
3292
|
diskCacheLoaded = false;
|
|
2875
3293
|
diskCacheLoadPromise = null;
|
|
3294
|
+
diskCacheLoadCount = 0;
|
|
3295
|
+
jsonlLineCountCalls = 0;
|
|
2876
3296
|
MAX_INPUT_CHARS = 32e3;
|
|
2877
3297
|
MAX_CONCURRENCY = 4;
|
|
2878
3298
|
DISK_SAVE_DEBOUNCE_MS = 5e3;
|
|
@@ -2930,7 +3350,9 @@ var init_api_provider = __esm({
|
|
|
2930
3350
|
if (config.requestedDimensions) {
|
|
2931
3351
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
2932
3352
|
}
|
|
2933
|
-
|
|
3353
|
+
if (allowNetworkProbe) {
|
|
3354
|
+
startDiskCacheLoad();
|
|
3355
|
+
}
|
|
2934
3356
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
2935
3357
|
}
|
|
2936
3358
|
static resolveConfig() {
|
|
@@ -3206,6 +3628,7 @@ __export(provider_exports, {
|
|
|
3206
3628
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
3207
3629
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
3208
3630
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
3631
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
3209
3632
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
3210
3633
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
3211
3634
|
resetProvider: () => resetProvider,
|
|
@@ -3267,7 +3690,12 @@ function warnOnce(message) {
|
|
|
3267
3690
|
lastEmbeddingWarningAt = now3;
|
|
3268
3691
|
console.error(message);
|
|
3269
3692
|
}
|
|
3693
|
+
function embeddingModeToken() {
|
|
3694
|
+
return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
|
|
3695
|
+
}
|
|
3270
3696
|
function getEmbeddingMode2() {
|
|
3697
|
+
const token = embeddingModeToken();
|
|
3698
|
+
if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
|
|
3271
3699
|
try {
|
|
3272
3700
|
let cfg;
|
|
3273
3701
|
try {
|
|
@@ -3276,10 +3704,17 @@ function getEmbeddingMode2() {
|
|
|
3276
3704
|
cfg = (init_config(), __toCommonJS(config_exports));
|
|
3277
3705
|
}
|
|
3278
3706
|
const { getEmbeddingMode: cfgMode } = cfg;
|
|
3279
|
-
|
|
3707
|
+
cachedEmbeddingMode = cfgMode();
|
|
3708
|
+
cachedEmbeddingModeToken = token;
|
|
3709
|
+
return cachedEmbeddingMode;
|
|
3280
3710
|
} catch {
|
|
3281
|
-
|
|
3282
|
-
|
|
3711
|
+
if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
|
|
3712
|
+
cachedEmbeddingMode = token;
|
|
3713
|
+
cachedEmbeddingModeToken = token;
|
|
3714
|
+
return token;
|
|
3715
|
+
}
|
|
3716
|
+
cachedEmbeddingMode = "off";
|
|
3717
|
+
cachedEmbeddingModeToken = token;
|
|
3283
3718
|
return "off";
|
|
3284
3719
|
}
|
|
3285
3720
|
}
|
|
@@ -3313,13 +3748,36 @@ function isTemporaryEmbeddingFailure(error) {
|
|
|
3313
3748
|
if (!(error instanceof Error)) return false;
|
|
3314
3749
|
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);
|
|
3315
3750
|
}
|
|
3751
|
+
function classifyEmbeddingFailure(error) {
|
|
3752
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3753
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
3754
|
+
return "authorization";
|
|
3755
|
+
}
|
|
3756
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
3757
|
+
return "billing";
|
|
3758
|
+
}
|
|
3759
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
3760
|
+
return "timeout";
|
|
3761
|
+
}
|
|
3762
|
+
return "upstream";
|
|
3763
|
+
}
|
|
3316
3764
|
function markTemporaryFailure(reason) {
|
|
3317
3765
|
provider = null;
|
|
3318
3766
|
initPromise = null;
|
|
3319
3767
|
lastInitWasTemporaryFailure = true;
|
|
3320
3768
|
lastFailureTimestamp = Date.now();
|
|
3769
|
+
lastEmbeddingFailure = {
|
|
3770
|
+
kind: classifyEmbeddingFailure(reason),
|
|
3771
|
+
occurredAt: lastFailureTimestamp
|
|
3772
|
+
};
|
|
3321
3773
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
3322
|
-
warnOnce(`[memorix] Embedding provider
|
|
3774
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
3775
|
+
}
|
|
3776
|
+
function markProviderReady(candidate) {
|
|
3777
|
+
provider = wrapProvider(candidate);
|
|
3778
|
+
lastInitWasTemporaryFailure = false;
|
|
3779
|
+
lastEmbeddingFailure = null;
|
|
3780
|
+
return provider;
|
|
3323
3781
|
}
|
|
3324
3782
|
async function createFastEmbedProvider() {
|
|
3325
3783
|
try {
|
|
@@ -3435,35 +3893,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
3435
3893
|
if (mode === "fastembed") {
|
|
3436
3894
|
const initialized = await createFastEmbedProvider();
|
|
3437
3895
|
if (!initialized) return null;
|
|
3438
|
-
provider =
|
|
3896
|
+
provider = markProviderReady(initialized);
|
|
3439
3897
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3440
3898
|
return provider;
|
|
3441
3899
|
}
|
|
3442
3900
|
if (mode === "transformers") {
|
|
3443
3901
|
const initialized = await createTransformersProvider();
|
|
3444
3902
|
if (!initialized) return null;
|
|
3445
|
-
provider =
|
|
3903
|
+
provider = markProviderReady(initialized);
|
|
3446
3904
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3447
3905
|
return provider;
|
|
3448
3906
|
}
|
|
3449
3907
|
if (mode === "api") {
|
|
3450
3908
|
const initialized = await createAPIProvider(options);
|
|
3451
3909
|
if (!initialized) return null;
|
|
3452
|
-
provider =
|
|
3910
|
+
provider = markProviderReady(initialized);
|
|
3453
3911
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3454
3912
|
return provider;
|
|
3455
3913
|
}
|
|
3456
3914
|
if (hasAPIEmbeddingConfig()) {
|
|
3457
3915
|
const initialized = await createAPIProvider(options);
|
|
3458
3916
|
if (initialized) {
|
|
3459
|
-
provider =
|
|
3917
|
+
provider = markProviderReady(initialized);
|
|
3460
3918
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3461
3919
|
return provider;
|
|
3462
3920
|
}
|
|
3463
3921
|
}
|
|
3464
3922
|
const localFallback = await createLocalFallbackProvider();
|
|
3465
3923
|
if (localFallback) {
|
|
3466
|
-
provider =
|
|
3924
|
+
provider = markProviderReady(localFallback);
|
|
3467
3925
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3468
3926
|
return provider;
|
|
3469
3927
|
}
|
|
@@ -3485,18 +3943,42 @@ async function isVectorSearchAvailable() {
|
|
|
3485
3943
|
const p = await getEmbeddingProvider();
|
|
3486
3944
|
return p !== null;
|
|
3487
3945
|
}
|
|
3946
|
+
function getEmbeddingRuntimeHealth() {
|
|
3947
|
+
const mode = getEmbeddingMode2();
|
|
3948
|
+
if (mode === "off") return { status: "disabled" };
|
|
3949
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
3950
|
+
return {
|
|
3951
|
+
status: "degraded",
|
|
3952
|
+
failure: {
|
|
3953
|
+
kind: lastEmbeddingFailure.kind,
|
|
3954
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
3955
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
3956
|
+
}
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3959
|
+
if (provider) {
|
|
3960
|
+
return {
|
|
3961
|
+
status: "ready",
|
|
3962
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3965
|
+
return { status: "uninitialized" };
|
|
3966
|
+
}
|
|
3488
3967
|
function isEmbeddingExplicitlyDisabled() {
|
|
3489
3968
|
return getEmbeddingMode2() === "off";
|
|
3490
3969
|
}
|
|
3491
3970
|
function resetProvider() {
|
|
3492
3971
|
provider = null;
|
|
3493
3972
|
initPromise = null;
|
|
3973
|
+
cachedEmbeddingMode = null;
|
|
3974
|
+
cachedEmbeddingModeToken = null;
|
|
3494
3975
|
lastInitWasTemporaryFailure = false;
|
|
3495
3976
|
lastFailureTimestamp = 0;
|
|
3977
|
+
lastEmbeddingFailure = null;
|
|
3496
3978
|
lastEmbeddingWarning = "";
|
|
3497
3979
|
lastEmbeddingWarningAt = 0;
|
|
3498
3980
|
}
|
|
3499
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3981
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3500
3982
|
var init_provider = __esm({
|
|
3501
3983
|
"src/embedding/provider.ts"() {
|
|
3502
3984
|
"use strict";
|
|
@@ -3520,7 +4002,10 @@ var init_provider = __esm({
|
|
|
3520
4002
|
MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
|
|
3521
4003
|
provider = null;
|
|
3522
4004
|
initPromise = null;
|
|
4005
|
+
cachedEmbeddingMode = null;
|
|
4006
|
+
cachedEmbeddingModeToken = null;
|
|
3523
4007
|
lastInitWasTemporaryFailure = false;
|
|
4008
|
+
lastEmbeddingFailure = null;
|
|
3524
4009
|
lastEmbeddingWarning = "";
|
|
3525
4010
|
lastEmbeddingWarningAt = 0;
|
|
3526
4011
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -5276,6 +5761,9 @@ async function attachCachedVectors(database, candidates) {
|
|
|
5276
5761
|
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
5277
5762
|
} catch {
|
|
5278
5763
|
}
|
|
5764
|
+
if (index % 50 === 0) {
|
|
5765
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5766
|
+
}
|
|
5279
5767
|
}
|
|
5280
5768
|
}
|
|
5281
5769
|
async function hydrateIndex(observations2, options = {}) {
|
|
@@ -5326,17 +5814,24 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5326
5814
|
await insert2(database, doc);
|
|
5327
5815
|
rememberObservationDoc(doc);
|
|
5328
5816
|
inserted++;
|
|
5817
|
+
if (inserted % 50 === 0) {
|
|
5818
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5819
|
+
}
|
|
5820
|
+
if (inserted % 200 === 0) {
|
|
5821
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
5822
|
+
}
|
|
5329
5823
|
} catch {
|
|
5330
5824
|
}
|
|
5331
5825
|
}
|
|
5332
|
-
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5826
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5333
5827
|
}) : null;
|
|
5334
5828
|
return inserted;
|
|
5335
5829
|
}
|
|
5336
|
-
async function hydrateIndexForStartup(observations2) {
|
|
5830
|
+
async function hydrateIndexForStartup(observations2, options = {}) {
|
|
5337
5831
|
return hydrateIndex(observations2, {
|
|
5338
5832
|
allowNetworkProbe: false,
|
|
5339
|
-
deferCachedVectors: true
|
|
5833
|
+
deferCachedVectors: true,
|
|
5834
|
+
skipCachedVectors: options.skipCachedVectors
|
|
5340
5835
|
});
|
|
5341
5836
|
}
|
|
5342
5837
|
function getDeferredCachedVectorHydration() {
|
|
@@ -8124,12 +8619,25 @@ __export(observations_exports, {
|
|
|
8124
8619
|
prepareSearchIndex: () => prepareSearchIndex,
|
|
8125
8620
|
probeSearchIndex: () => probeSearchIndex,
|
|
8126
8621
|
reindexObservations: () => reindexObservations,
|
|
8622
|
+
resetObservationRuntime: () => resetObservationRuntime,
|
|
8127
8623
|
resolveObservations: () => resolveObservations,
|
|
8128
8624
|
storeObservation: () => storeObservation,
|
|
8129
8625
|
suggestTopicKey: () => suggestTopicKey,
|
|
8130
8626
|
updateObservationAdmission: () => updateObservationAdmission,
|
|
8131
8627
|
withFreshObservations: () => withFreshObservations
|
|
8132
8628
|
});
|
|
8629
|
+
function resetObservationRuntime() {
|
|
8630
|
+
observations = [];
|
|
8631
|
+
nextId = 1;
|
|
8632
|
+
projectDir = null;
|
|
8633
|
+
searchIndexPrepared = false;
|
|
8634
|
+
corpusLoaded = false;
|
|
8635
|
+
loadedObservationStore = null;
|
|
8636
|
+
embeddingWriteMode = "background";
|
|
8637
|
+
embeddingWorkerProjectRoot = void 0;
|
|
8638
|
+
vectorMissingIds.clear();
|
|
8639
|
+
vectorSchemaUpgradePromise = null;
|
|
8640
|
+
}
|
|
8133
8641
|
function logEmbeddingFailureOnce(key, message) {
|
|
8134
8642
|
const now3 = Date.now();
|
|
8135
8643
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
@@ -8238,23 +8746,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
|
8238
8746
|
async function initObservations(dir, options = {}) {
|
|
8239
8747
|
embeddingWriteMode = options.embeddingWriteMode ?? "background";
|
|
8240
8748
|
embeddingWorkerProjectRoot = options.projectRoot;
|
|
8241
|
-
if (projectDir === dir) return;
|
|
8242
8749
|
await initObservationStore(dir);
|
|
8243
8750
|
const store = getObservationStore();
|
|
8244
|
-
|
|
8751
|
+
if (projectDir === dir && loadedObservationStore === store) {
|
|
8752
|
+
if (options.skipCorpusLoad || corpusLoaded) return;
|
|
8753
|
+
}
|
|
8245
8754
|
nextId = await store.loadIdCounter();
|
|
8246
8755
|
projectDir = dir;
|
|
8247
8756
|
searchIndexPrepared = false;
|
|
8248
8757
|
vectorSchemaUpgradePromise = null;
|
|
8758
|
+
if (options.skipCorpusLoad) {
|
|
8759
|
+
observations = [];
|
|
8760
|
+
corpusLoaded = false;
|
|
8761
|
+
loadedObservationStore = store;
|
|
8762
|
+
return;
|
|
8763
|
+
}
|
|
8764
|
+
observations = await store.loadAll();
|
|
8765
|
+
corpusLoaded = true;
|
|
8766
|
+
loadedObservationStore = store;
|
|
8249
8767
|
}
|
|
8250
8768
|
function scheduleObservationEmbedding(input) {
|
|
8251
8769
|
const { observationId: observationId2, projectId, searchableText, doc } = input;
|
|
8252
8770
|
vectorMissingIds.add(observationId2);
|
|
8771
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
8772
|
+
vectorMissingIds.delete(observationId2);
|
|
8773
|
+
return;
|
|
8774
|
+
}
|
|
8253
8775
|
if (embeddingWriteMode === "deferred") {
|
|
8254
|
-
if (isEmbeddingExplicitlyDisabled()) {
|
|
8255
|
-
vectorMissingIds.delete(observationId2);
|
|
8256
|
-
return;
|
|
8257
|
-
}
|
|
8258
8776
|
queueVectorBackfill(projectId, {
|
|
8259
8777
|
detachedWorker: true,
|
|
8260
8778
|
projectRoot: embeddingWorkerProjectRoot
|
|
@@ -8301,7 +8819,7 @@ function scheduleObservationEmbedding(input) {
|
|
|
8301
8819
|
});
|
|
8302
8820
|
}
|
|
8303
8821
|
async function ensureFreshObservations() {
|
|
8304
|
-
if (!projectDir) return false;
|
|
8822
|
+
if (!projectDir || !corpusLoaded) return false;
|
|
8305
8823
|
try {
|
|
8306
8824
|
const store = getObservationStore();
|
|
8307
8825
|
const wasStale = await store.ensureFresh();
|
|
@@ -8342,7 +8860,7 @@ async function storeObservation(input) {
|
|
|
8342
8860
|
});
|
|
8343
8861
|
if (safeAttachments) input = { ...input, attachments: safeAttachments };
|
|
8344
8862
|
await ensureFreshObservations();
|
|
8345
|
-
if (input.topicKey) {
|
|
8863
|
+
if (input.topicKey && corpusLoaded) {
|
|
8346
8864
|
const existing = observations.find(
|
|
8347
8865
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8348
8866
|
);
|
|
@@ -8439,13 +8957,17 @@ async function storeObservation(input) {
|
|
|
8439
8957
|
});
|
|
8440
8958
|
observation.writeGeneration = store.getGeneration();
|
|
8441
8959
|
if (upsertedInsideLock || reloadCacheAfterCommit) {
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8960
|
+
if (corpusLoaded) {
|
|
8961
|
+
observations = await store.loadAll();
|
|
8962
|
+
nextId = await store.loadIdCounter();
|
|
8963
|
+
if (upsertedInsideLock) {
|
|
8964
|
+
observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
|
|
8965
|
+
}
|
|
8966
|
+
} else {
|
|
8967
|
+
nextId = await store.loadIdCounter();
|
|
8446
8968
|
}
|
|
8447
8969
|
} else {
|
|
8448
|
-
observations.push(observation);
|
|
8970
|
+
if (corpusLoaded) observations.push(observation);
|
|
8449
8971
|
nextId = observation.id + 1;
|
|
8450
8972
|
}
|
|
8451
8973
|
if (upsertedInsideLock) return;
|
|
@@ -8513,7 +9035,9 @@ async function storeObservation(input) {
|
|
|
8513
9035
|
createdByAgentId: input.createdByAgentId ?? "",
|
|
8514
9036
|
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8515
9037
|
};
|
|
8516
|
-
|
|
9038
|
+
if (corpusLoaded) {
|
|
9039
|
+
await insertObservation(doc);
|
|
9040
|
+
}
|
|
8517
9041
|
};
|
|
8518
9042
|
await assignAndPersist();
|
|
8519
9043
|
if (upsertedInsideLock) {
|
|
@@ -8875,9 +9399,11 @@ async function reindexObservations() {
|
|
|
8875
9399
|
searchIndexPrepared = true;
|
|
8876
9400
|
return count2;
|
|
8877
9401
|
}
|
|
8878
|
-
async function prepareSearchIndex() {
|
|
9402
|
+
async function prepareSearchIndex(options = {}) {
|
|
8879
9403
|
if (searchIndexPrepared) return 0;
|
|
8880
|
-
const count2 = await hydrateIndexForStartup(observations
|
|
9404
|
+
const count2 = await hydrateIndexForStartup(observations, {
|
|
9405
|
+
skipCachedVectors: options.skipCachedVectors
|
|
9406
|
+
});
|
|
8881
9407
|
if (count2 === 0) {
|
|
8882
9408
|
searchIndexPrepared = true;
|
|
8883
9409
|
return 0;
|
|
@@ -9069,7 +9595,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9069
9595
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
9070
9596
|
};
|
|
9071
9597
|
}
|
|
9072
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9598
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9073
9599
|
var init_observations = __esm({
|
|
9074
9600
|
"src/memory/observations.ts"() {
|
|
9075
9601
|
"use strict";
|
|
@@ -9088,6 +9614,8 @@ var init_observations = __esm({
|
|
|
9088
9614
|
projectDir = null;
|
|
9089
9615
|
searchIndexPrepared = false;
|
|
9090
9616
|
embeddingWriteMode = "background";
|
|
9617
|
+
corpusLoaded = false;
|
|
9618
|
+
loadedObservationStore = null;
|
|
9091
9619
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
9092
9620
|
vectorBackfillRunning = false;
|
|
9093
9621
|
vectorSchemaUpgradePromise = null;
|
|
@@ -10433,7 +10961,7 @@ var init_types2 = __esm({
|
|
|
10433
10961
|
|
|
10434
10962
|
// src/media/asset-store.ts
|
|
10435
10963
|
import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
|
|
10436
|
-
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
10964
|
+
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
10437
10965
|
import path9 from "path";
|
|
10438
10966
|
function mediaRoot(dataDir) {
|
|
10439
10967
|
return path9.resolve(dataDir, "media");
|
|
@@ -10506,7 +11034,7 @@ async function persistBuffer(bytes, destination) {
|
|
|
10506
11034
|
const temporary = `${destination}.${randomUUID6()}.tmp`;
|
|
10507
11035
|
try {
|
|
10508
11036
|
await writeFile3(temporary, bytes, { flag: "wx" });
|
|
10509
|
-
await
|
|
11037
|
+
await rename2(temporary, destination);
|
|
10510
11038
|
return true;
|
|
10511
11039
|
} catch (error) {
|
|
10512
11040
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -61586,9 +62114,9 @@ var require_node_domexception = __commonJS({
|
|
|
61586
62114
|
});
|
|
61587
62115
|
|
|
61588
62116
|
// node_modules/fetch-blob/from.js
|
|
61589
|
-
import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
|
|
62117
|
+
import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
61590
62118
|
import { basename } from "path";
|
|
61591
|
-
var import_node_domexception,
|
|
62119
|
+
var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
61592
62120
|
var init_from = __esm({
|
|
61593
62121
|
"node_modules/fetch-blob/from.js"() {
|
|
61594
62122
|
"use strict";
|
|
@@ -61596,23 +62124,23 @@ var init_from = __esm({
|
|
|
61596
62124
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
61597
62125
|
init_file();
|
|
61598
62126
|
init_fetch_blob();
|
|
61599
|
-
({ stat:
|
|
62127
|
+
({ stat: stat3 } = fs7);
|
|
61600
62128
|
blobFromSync = (path20, type) => fromBlob(statSync2(path20), path20, type);
|
|
61601
|
-
blobFrom = (path20, type) =>
|
|
61602
|
-
fileFrom = (path20, type) =>
|
|
62129
|
+
blobFrom = (path20, type) => stat3(path20).then((stat5) => fromBlob(stat5, path20, type));
|
|
62130
|
+
fileFrom = (path20, type) => stat3(path20).then((stat5) => fromFile(stat5, path20, type));
|
|
61603
62131
|
fileFromSync = (path20, type) => fromFile(statSync2(path20), path20, type);
|
|
61604
|
-
fromBlob = (
|
|
62132
|
+
fromBlob = (stat5, path20, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
61605
62133
|
path: path20,
|
|
61606
|
-
size:
|
|
61607
|
-
lastModified:
|
|
62134
|
+
size: stat5.size,
|
|
62135
|
+
lastModified: stat5.mtimeMs,
|
|
61608
62136
|
start: 0
|
|
61609
62137
|
})], { type });
|
|
61610
|
-
fromFile = (
|
|
62138
|
+
fromFile = (stat5, path20, type = "") => new file_default([new BlobDataItem({
|
|
61611
62139
|
path: path20,
|
|
61612
|
-
size:
|
|
61613
|
-
lastModified:
|
|
62140
|
+
size: stat5.size,
|
|
62141
|
+
lastModified: stat5.mtimeMs,
|
|
61614
62142
|
start: 0
|
|
61615
|
-
})], basename(path20), { type, lastModified:
|
|
62143
|
+
})], basename(path20), { type, lastModified: stat5.mtimeMs });
|
|
61616
62144
|
BlobDataItem = class _BlobDataItem {
|
|
61617
62145
|
#path;
|
|
61618
62146
|
#start;
|
|
@@ -61635,11 +62163,11 @@ var init_from = __esm({
|
|
|
61635
62163
|
});
|
|
61636
62164
|
}
|
|
61637
62165
|
async *stream() {
|
|
61638
|
-
const { mtimeMs } = await
|
|
62166
|
+
const { mtimeMs } = await stat3(this.#path);
|
|
61639
62167
|
if (mtimeMs > this.lastModified) {
|
|
61640
62168
|
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");
|
|
61641
62169
|
}
|
|
61642
|
-
yield*
|
|
62170
|
+
yield* createReadStream2(this.#path, {
|
|
61643
62171
|
start: this.#start,
|
|
61644
62172
|
end: this.#start + this.size - 1
|
|
61645
62173
|
});
|
|
@@ -77269,7 +77797,7 @@ var init_wrapper = __esm({
|
|
|
77269
77797
|
});
|
|
77270
77798
|
|
|
77271
77799
|
// node_modules/@google/genai/dist/node/index.mjs
|
|
77272
|
-
import { createWriteStream } from "fs";
|
|
77800
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
77273
77801
|
import * as fs8 from "fs/promises";
|
|
77274
77802
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
77275
77803
|
import { Readable } from "stream";
|
|
@@ -94549,7 +95077,7 @@ ${underline}`);
|
|
|
94549
95077
|
if (params.downloadPath) {
|
|
94550
95078
|
const response = await downloadFile(params, apiClient);
|
|
94551
95079
|
if (response instanceof HttpResponse) {
|
|
94552
|
-
const writer =
|
|
95080
|
+
const writer = createWriteStream2(params.downloadPath);
|
|
94553
95081
|
const body = Readable.fromWeb(response.responseInternal.body);
|
|
94554
95082
|
body.pipe(writer);
|
|
94555
95083
|
await finished(writer);
|
|
@@ -144467,7 +144995,7 @@ function explainRetention(doc, referenceTime) {
|
|
|
144467
144995
|
};
|
|
144468
144996
|
}
|
|
144469
144997
|
function toRetentionDocument(obs, accessMap) {
|
|
144470
|
-
const
|
|
144998
|
+
const access2 = accessMap?.get(obs.id);
|
|
144471
144999
|
return {
|
|
144472
145000
|
id: `obs-${obs.id}`,
|
|
144473
145001
|
observationId: obs.id,
|
|
@@ -144481,8 +145009,8 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
144481
145009
|
tokens: obs.tokens,
|
|
144482
145010
|
createdAt: obs.createdAt,
|
|
144483
145011
|
projectId: obs.projectId,
|
|
144484
|
-
accessCount:
|
|
144485
|
-
lastAccessedAt:
|
|
145012
|
+
accessCount: access2?.accessCount ?? 0,
|
|
145013
|
+
lastAccessedAt: access2?.lastAccessedAt ?? "",
|
|
144486
145014
|
status: obs.status ?? "active",
|
|
144487
145015
|
source: obs.source ?? "agent",
|
|
144488
145016
|
sourceDetail: obs.sourceDetail ?? "",
|
|
@@ -145761,7 +146289,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
145761
146289
|
const rel = normalizeCodePath(relative(projectRoot, abs));
|
|
145762
146290
|
try {
|
|
145763
146291
|
const text2 = readFileSync6(abs, "utf-8");
|
|
145764
|
-
const
|
|
146292
|
+
const stat5 = fileStat ?? statSync3(abs);
|
|
145765
146293
|
const file = {
|
|
145766
146294
|
id: makeCodeFileId(projectId, rel),
|
|
145767
146295
|
projectId,
|
|
@@ -145770,8 +146298,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
145770
146298
|
contentHash: hashText(text2),
|
|
145771
146299
|
// Preserve filesystem precision. Rounding here made same-size edits in
|
|
145772
146300
|
// a short Windows timestamp window indistinguishable to refresh.
|
|
145773
|
-
mtimeMs: Number(
|
|
145774
|
-
sizeBytes: Number(
|
|
146301
|
+
mtimeMs: Number(stat5.mtimeMs),
|
|
146302
|
+
sizeBytes: Number(stat5.size),
|
|
145775
146303
|
indexedAt
|
|
145776
146304
|
};
|
|
145777
146305
|
return {
|
|
@@ -145798,18 +146326,18 @@ async function indexProjectLite(options) {
|
|
|
145798
146326
|
let skippedOversizedFiles = 0;
|
|
145799
146327
|
let unreadableFiles = 0;
|
|
145800
146328
|
for (const abs of paths) {
|
|
145801
|
-
let
|
|
146329
|
+
let stat5;
|
|
145802
146330
|
try {
|
|
145803
|
-
|
|
146331
|
+
stat5 = statSync3(abs);
|
|
145804
146332
|
} catch {
|
|
145805
146333
|
unreadableFiles++;
|
|
145806
146334
|
continue;
|
|
145807
146335
|
}
|
|
145808
|
-
if (Number(
|
|
146336
|
+
if (Number(stat5.size) > maxFileBytes) {
|
|
145809
146337
|
skippedOversizedFiles++;
|
|
145810
146338
|
continue;
|
|
145811
146339
|
}
|
|
145812
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146340
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
145813
146341
|
if (!attempt.delta) {
|
|
145814
146342
|
if (attempt.unreadable) unreadableFiles++;
|
|
145815
146343
|
continue;
|
|
@@ -145837,16 +146365,16 @@ async function refreshProjectLite(store, options) {
|
|
|
145837
146365
|
for (const abs of paths) {
|
|
145838
146366
|
const rel = normalizeCodePath(relative(options.projectRoot, abs));
|
|
145839
146367
|
seenPaths.add(rel);
|
|
145840
|
-
let
|
|
146368
|
+
let stat5;
|
|
145841
146369
|
try {
|
|
145842
|
-
|
|
146370
|
+
stat5 = statSync3(abs);
|
|
145843
146371
|
} catch {
|
|
145844
146372
|
unreadableFiles++;
|
|
145845
146373
|
continue;
|
|
145846
146374
|
}
|
|
145847
146375
|
const existing = existingByPath.get(rel);
|
|
145848
|
-
const mtimeMs = Number(
|
|
145849
|
-
const sizeBytes = Number(
|
|
146376
|
+
const mtimeMs = Number(stat5.mtimeMs);
|
|
146377
|
+
const sizeBytes = Number(stat5.size);
|
|
145850
146378
|
if (sizeBytes > maxFileBytes) {
|
|
145851
146379
|
skippedOversizedFiles++;
|
|
145852
146380
|
if (existing) oversizedExistingFileIds.add(existing.id);
|
|
@@ -145856,7 +146384,7 @@ async function refreshProjectLite(store, options) {
|
|
|
145856
146384
|
unchangedFiles++;
|
|
145857
146385
|
continue;
|
|
145858
146386
|
}
|
|
145859
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146387
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
145860
146388
|
if (!attempt.delta) {
|
|
145861
146389
|
if (attempt.unreadable) unreadableFiles++;
|
|
145862
146390
|
continue;
|