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
|
@@ -3126,8 +3126,8 @@ async function acquireLock(lockPath) {
|
|
|
3126
3126
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
3127
3127
|
if (code === "EEXIST" || code === "EPERM") {
|
|
3128
3128
|
try {
|
|
3129
|
-
const
|
|
3130
|
-
if (Date.now() -
|
|
3129
|
+
const stat5 = await fs3.stat(lockPath);
|
|
3130
|
+
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
3131
3131
|
await fs3.unlink(lockPath).catch(() => {
|
|
3132
3132
|
});
|
|
3133
3133
|
continue;
|
|
@@ -3583,24 +3583,131 @@ var init_transformers_provider = __esm({
|
|
|
3583
3583
|
// src/embedding/api-provider.ts
|
|
3584
3584
|
var api_provider_exports = {};
|
|
3585
3585
|
__export(api_provider_exports, {
|
|
3586
|
-
APIEmbeddingProvider: () => APIEmbeddingProvider
|
|
3586
|
+
APIEmbeddingProvider: () => APIEmbeddingProvider,
|
|
3587
|
+
convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
|
|
3588
|
+
ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
|
|
3589
|
+
getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
|
|
3590
|
+
getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
|
|
3591
|
+
getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
|
|
3592
|
+
getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
|
|
3593
|
+
migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
|
|
3594
|
+
parseCacheJsonInWorker: () => parseCacheJsonInWorker,
|
|
3595
|
+
resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
|
|
3596
|
+
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
3597
|
+
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
3587
3598
|
});
|
|
3588
3599
|
import { createHash as createHash2 } from "crypto";
|
|
3589
|
-
import {
|
|
3600
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
3601
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
3590
3602
|
import { join as join3 } from "path";
|
|
3591
3603
|
import { homedir as homedir7 } from "os";
|
|
3604
|
+
import { createInterface } from "readline";
|
|
3592
3605
|
function cacheDir() {
|
|
3593
3606
|
return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
|
|
3594
3607
|
}
|
|
3595
3608
|
function cacheFile() {
|
|
3596
3609
|
return join3(cacheDir(), ".embedding-api-cache.json");
|
|
3597
3610
|
}
|
|
3611
|
+
function cacheJsonlFile() {
|
|
3612
|
+
return join3(cacheDir(), ".embedding-api-cache.jsonl");
|
|
3613
|
+
}
|
|
3598
3614
|
function dimsCacheFile() {
|
|
3599
3615
|
return join3(cacheDir(), ".embedding-dims-cache.json");
|
|
3600
3616
|
}
|
|
3601
3617
|
function cacheMetaFile() {
|
|
3602
3618
|
return join3(cacheDir(), ".embedding-api-cache-meta.json");
|
|
3603
3619
|
}
|
|
3620
|
+
function cacheStatFile() {
|
|
3621
|
+
return join3(cacheDir(), ".embedding-api-cache-stat.json");
|
|
3622
|
+
}
|
|
3623
|
+
function payloadBytes(vector) {
|
|
3624
|
+
return vector.length * BYTES_PER_DIM;
|
|
3625
|
+
}
|
|
3626
|
+
function maxCachePayloadBytes() {
|
|
3627
|
+
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
3628
|
+
if (raw) {
|
|
3629
|
+
const parsed = Number.parseInt(raw, 10);
|
|
3630
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
3631
|
+
}
|
|
3632
|
+
return MAX_CACHE_PAYLOAD_BYTES;
|
|
3633
|
+
}
|
|
3634
|
+
function maxEntriesForDimensions(dimensions) {
|
|
3635
|
+
const dim = Math.max(1, Math.floor(dimensions));
|
|
3636
|
+
const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
|
|
3637
|
+
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
3638
|
+
}
|
|
3639
|
+
function rememberDimensions(vector) {
|
|
3640
|
+
if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
|
|
3641
|
+
cacheDimensions = vector.length;
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
function evictOldest() {
|
|
3645
|
+
const firstKey = cache3.keys().next().value;
|
|
3646
|
+
if (firstKey === void 0) return;
|
|
3647
|
+
const old = cache3.get(firstKey);
|
|
3648
|
+
cache3.delete(firstKey);
|
|
3649
|
+
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
3650
|
+
}
|
|
3651
|
+
function evictToFit(incomingBytes, incomingDims) {
|
|
3652
|
+
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
3653
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
|
|
3654
|
+
evictOldest();
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
function isNumberArray(value) {
|
|
3658
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
3659
|
+
}
|
|
3660
|
+
function parseCacheRow(raw) {
|
|
3661
|
+
if (Array.isArray(raw)) {
|
|
3662
|
+
const hash4 = raw[0];
|
|
3663
|
+
const vector = raw[1];
|
|
3664
|
+
if (typeof hash4 === "string" && isNumberArray(vector)) {
|
|
3665
|
+
return { hash: hash4, vector };
|
|
3666
|
+
}
|
|
3667
|
+
return null;
|
|
3668
|
+
}
|
|
3669
|
+
if (raw && typeof raw === "object") {
|
|
3670
|
+
const row = raw;
|
|
3671
|
+
if (typeof row.h === "string" && isNumberArray(row.v)) {
|
|
3672
|
+
return { hash: row.h, vector: row.v };
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
return null;
|
|
3676
|
+
}
|
|
3677
|
+
function cacheInsert(hash4, value, dirty) {
|
|
3678
|
+
rememberDimensions(value);
|
|
3679
|
+
const incoming = payloadBytes(value);
|
|
3680
|
+
const existing = cache3.get(hash4);
|
|
3681
|
+
if (existing) {
|
|
3682
|
+
cachePayloadBytes -= payloadBytes(existing);
|
|
3683
|
+
cache3.delete(hash4);
|
|
3684
|
+
}
|
|
3685
|
+
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
3686
|
+
cache3.set(hash4, value);
|
|
3687
|
+
cachePayloadBytes += incoming;
|
|
3688
|
+
if (dirty) diskCacheDirty2 = true;
|
|
3689
|
+
}
|
|
3690
|
+
async function readCacheStat() {
|
|
3691
|
+
try {
|
|
3692
|
+
const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
|
|
3693
|
+
if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
|
|
3694
|
+
return {
|
|
3695
|
+
version: 1,
|
|
3696
|
+
entryCount: Math.floor(data.entryCount),
|
|
3697
|
+
dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
} catch {
|
|
3701
|
+
}
|
|
3702
|
+
return null;
|
|
3703
|
+
}
|
|
3704
|
+
async function writeCacheStat(stat5) {
|
|
3705
|
+
try {
|
|
3706
|
+
await mkdir2(cacheDir(), { recursive: true });
|
|
3707
|
+
await writeFile2(cacheStatFile(), JSON.stringify(stat5));
|
|
3708
|
+
} catch {
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3604
3711
|
function normalizeText(text2) {
|
|
3605
3712
|
return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
|
|
3606
3713
|
}
|
|
@@ -3686,29 +3793,300 @@ function toProviderInput(input, baseUrl) {
|
|
|
3686
3793
|
}
|
|
3687
3794
|
return { type: input.modality, url: input.url };
|
|
3688
3795
|
}
|
|
3796
|
+
function parseCacheJsonInWorker(filePath) {
|
|
3797
|
+
return new Promise((resolve, reject) => {
|
|
3798
|
+
void import("worker_threads").then(({ Worker }) => {
|
|
3799
|
+
const worker = new Worker(
|
|
3800
|
+
`
|
|
3801
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
3802
|
+
const { readFileSync } = require('node:fs');
|
|
3803
|
+
try {
|
|
3804
|
+
const raw = readFileSync(workerData.filePath, 'utf8');
|
|
3805
|
+
parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
|
|
3806
|
+
} catch (err) {
|
|
3807
|
+
parentPort.postMessage({
|
|
3808
|
+
ok: false,
|
|
3809
|
+
error: err instanceof Error ? err.message : String(err),
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
`,
|
|
3813
|
+
{ eval: true, workerData: { filePath } }
|
|
3814
|
+
);
|
|
3815
|
+
worker.once("message", (msg) => {
|
|
3816
|
+
void worker.terminate();
|
|
3817
|
+
if (msg?.ok && Array.isArray(msg.entries)) {
|
|
3818
|
+
resolve(msg.entries);
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3821
|
+
reject(new Error(msg?.error || "embedding cache worker parse failed"));
|
|
3822
|
+
});
|
|
3823
|
+
worker.once("error", reject);
|
|
3824
|
+
}).catch(reject);
|
|
3825
|
+
});
|
|
3826
|
+
}
|
|
3827
|
+
function findJsonValueEnd(source) {
|
|
3828
|
+
let i2 = 0;
|
|
3829
|
+
while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
|
|
3830
|
+
if (i2 >= source.length) return -1;
|
|
3831
|
+
const start = source[i2];
|
|
3832
|
+
if (start === '"') {
|
|
3833
|
+
i2 += 1;
|
|
3834
|
+
while (i2 < source.length) {
|
|
3835
|
+
if (source[i2] === "\\") {
|
|
3836
|
+
i2 += 2;
|
|
3837
|
+
continue;
|
|
3838
|
+
}
|
|
3839
|
+
if (source[i2] === '"') return i2 + 1;
|
|
3840
|
+
i2 += 1;
|
|
3841
|
+
}
|
|
3842
|
+
return -1;
|
|
3843
|
+
}
|
|
3844
|
+
if (start === "{" || start === "[") {
|
|
3845
|
+
const close = start === "{" ? "}" : "]";
|
|
3846
|
+
let depth = 0;
|
|
3847
|
+
let inString = false;
|
|
3848
|
+
for (; i2 < source.length; i2 += 1) {
|
|
3849
|
+
const ch = source[i2];
|
|
3850
|
+
if (inString) {
|
|
3851
|
+
if (ch === "\\") {
|
|
3852
|
+
i2 += 1;
|
|
3853
|
+
continue;
|
|
3854
|
+
}
|
|
3855
|
+
if (ch === '"') inString = false;
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
if (ch === '"') {
|
|
3859
|
+
inString = true;
|
|
3860
|
+
continue;
|
|
3861
|
+
}
|
|
3862
|
+
if (ch === start) depth += 1;
|
|
3863
|
+
else if (ch === close) {
|
|
3864
|
+
depth -= 1;
|
|
3865
|
+
if (depth === 0) return i2 + 1;
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
return -1;
|
|
3869
|
+
}
|
|
3870
|
+
const rest = source.slice(i2);
|
|
3871
|
+
const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
|
|
3872
|
+
return match2 ? i2 + match2[0].length : -1;
|
|
3873
|
+
}
|
|
3874
|
+
async function streamJsonArrayToJsonl(filePath, tmpPath) {
|
|
3875
|
+
const input = createReadStream(filePath, { encoding: "utf8" });
|
|
3876
|
+
const out = createWriteStream(tmpPath);
|
|
3877
|
+
let buf = "";
|
|
3878
|
+
let started = false;
|
|
3879
|
+
let count2 = 0;
|
|
3880
|
+
const writeLine = async (line) => {
|
|
3881
|
+
if (!out.write(line)) {
|
|
3882
|
+
await new Promise((resolve, reject) => {
|
|
3883
|
+
out.once("drain", resolve);
|
|
3884
|
+
out.once("error", reject);
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
};
|
|
3888
|
+
try {
|
|
3889
|
+
for await (const chunk of input) {
|
|
3890
|
+
buf += chunk;
|
|
3891
|
+
if (!started) {
|
|
3892
|
+
const idx = buf.search(/\S/);
|
|
3893
|
+
if (idx < 0) continue;
|
|
3894
|
+
if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
|
|
3895
|
+
buf = buf.slice(idx + 1);
|
|
3896
|
+
started = true;
|
|
3897
|
+
}
|
|
3898
|
+
while (true) {
|
|
3899
|
+
buf = buf.replace(/^\s*,?\s*/, "");
|
|
3900
|
+
if (!buf) break;
|
|
3901
|
+
if (buf[0] === "]") {
|
|
3902
|
+
buf = "";
|
|
3903
|
+
break;
|
|
3904
|
+
}
|
|
3905
|
+
const end = findJsonValueEnd(buf);
|
|
3906
|
+
if (end < 0) break;
|
|
3907
|
+
const value = JSON.parse(buf.slice(0, end));
|
|
3908
|
+
buf = buf.slice(end);
|
|
3909
|
+
const row = parseCacheRow(value);
|
|
3910
|
+
if (row) {
|
|
3911
|
+
await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
|
|
3912
|
+
`);
|
|
3913
|
+
count2 += 1;
|
|
3914
|
+
if (count2 % 50 === 0) {
|
|
3915
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
|
|
3920
|
+
throw new Error("embedding cache entry too large to migrate safely");
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
} finally {
|
|
3924
|
+
await new Promise((resolve, reject) => {
|
|
3925
|
+
out.end(() => resolve());
|
|
3926
|
+
out.once("error", reject);
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
return count2;
|
|
3930
|
+
}
|
|
3931
|
+
async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
|
|
3932
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
3933
|
+
try {
|
|
3934
|
+
const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
|
|
3935
|
+
await rename(tmpPath, jsonlPath);
|
|
3936
|
+
diskCacheEntryCount = count2;
|
|
3937
|
+
await writeCacheStat({
|
|
3938
|
+
version: 1,
|
|
3939
|
+
entryCount: count2,
|
|
3940
|
+
dimensions: cacheDimensions
|
|
3941
|
+
});
|
|
3942
|
+
return count2;
|
|
3943
|
+
} catch (err) {
|
|
3944
|
+
await unlink(tmpPath).catch(() => void 0);
|
|
3945
|
+
throw err;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
function convertArrayCacheToJsonl(filePath, jsonlPath) {
|
|
3949
|
+
return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
3950
|
+
}
|
|
3951
|
+
async function loadCacheJsonl(filePath) {
|
|
3952
|
+
const rl = createInterface({
|
|
3953
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
3954
|
+
crlfDelay: Infinity
|
|
3955
|
+
});
|
|
3956
|
+
let loaded = 0;
|
|
3957
|
+
let onDiskCount = 0;
|
|
3958
|
+
for await (const line of rl) {
|
|
3959
|
+
const trimmed = line.trim();
|
|
3960
|
+
if (!trimmed) continue;
|
|
3961
|
+
try {
|
|
3962
|
+
const row = parseCacheRow(JSON.parse(trimmed));
|
|
3963
|
+
if (!row) continue;
|
|
3964
|
+
onDiskCount += 1;
|
|
3965
|
+
cacheInsert(row.hash, row.vector, false);
|
|
3966
|
+
loaded = cache3.size;
|
|
3967
|
+
} catch {
|
|
3968
|
+
}
|
|
3969
|
+
if (onDiskCount % 250 === 0) {
|
|
3970
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
diskCacheEntryCount = onDiskCount;
|
|
3974
|
+
await writeCacheStat({
|
|
3975
|
+
version: 1,
|
|
3976
|
+
entryCount: onDiskCount,
|
|
3977
|
+
dimensions: cacheDimensions
|
|
3978
|
+
});
|
|
3979
|
+
return loaded;
|
|
3980
|
+
}
|
|
3981
|
+
async function countJsonlLines(filePath) {
|
|
3982
|
+
jsonlLineCountCalls += 1;
|
|
3983
|
+
try {
|
|
3984
|
+
const rl = createInterface({
|
|
3985
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
3986
|
+
crlfDelay: Infinity
|
|
3987
|
+
});
|
|
3988
|
+
let count2 = 0;
|
|
3989
|
+
for await (const line of rl) {
|
|
3990
|
+
if (line.trim()) count2 += 1;
|
|
3991
|
+
}
|
|
3992
|
+
return count2;
|
|
3993
|
+
} catch {
|
|
3994
|
+
return 0;
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
async function resolveOnDiskCount(jsonlPath) {
|
|
3998
|
+
if (diskCacheEntryCount > 0) return diskCacheEntryCount;
|
|
3999
|
+
const sidecar = await readCacheStat();
|
|
4000
|
+
if (sidecar) {
|
|
4001
|
+
diskCacheEntryCount = sidecar.entryCount;
|
|
4002
|
+
if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
|
|
4003
|
+
return sidecar.entryCount;
|
|
4004
|
+
}
|
|
4005
|
+
try {
|
|
4006
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
4007
|
+
} catch {
|
|
4008
|
+
return 0;
|
|
4009
|
+
}
|
|
4010
|
+
const counted = await countJsonlLines(jsonlPath);
|
|
4011
|
+
diskCacheEntryCount = counted;
|
|
4012
|
+
await writeCacheStat({
|
|
4013
|
+
version: 1,
|
|
4014
|
+
entryCount: counted,
|
|
4015
|
+
dimensions: cacheDimensions
|
|
4016
|
+
});
|
|
4017
|
+
return counted;
|
|
4018
|
+
}
|
|
3689
4019
|
async function loadDiskCache2() {
|
|
3690
|
-
if (diskCacheLoaded) return;
|
|
4020
|
+
if (diskCacheLoaded) return cache3.size;
|
|
4021
|
+
let loaded = 0;
|
|
3691
4022
|
try {
|
|
3692
|
-
const
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
4023
|
+
const jsonlPath = cacheJsonlFile();
|
|
4024
|
+
let hasJsonl = false;
|
|
4025
|
+
try {
|
|
4026
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
4027
|
+
hasJsonl = true;
|
|
4028
|
+
} catch {
|
|
4029
|
+
hasJsonl = false;
|
|
4030
|
+
}
|
|
4031
|
+
if (hasJsonl) {
|
|
4032
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
4033
|
+
} else {
|
|
4034
|
+
const filePath = cacheFile();
|
|
4035
|
+
await stat(filePath);
|
|
4036
|
+
await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
4037
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
4038
|
+
}
|
|
4039
|
+
diskCacheLoadCount += 1;
|
|
4040
|
+
console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
|
|
3696
4041
|
} catch {
|
|
3697
4042
|
}
|
|
3698
4043
|
diskCacheLoaded = true;
|
|
4044
|
+
return loaded;
|
|
3699
4045
|
}
|
|
3700
4046
|
function startDiskCacheLoad() {
|
|
3701
4047
|
if (diskCacheLoaded || diskCacheLoadPromise) return;
|
|
3702
|
-
diskCacheLoadPromise = loadDiskCache2().catch(() => {
|
|
4048
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
3703
4049
|
});
|
|
3704
4050
|
}
|
|
3705
4051
|
async function ensureDiskCacheLoaded() {
|
|
3706
|
-
if (diskCacheLoaded) return;
|
|
3707
|
-
if (diskCacheLoadPromise) {
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
}
|
|
3711
|
-
await
|
|
4052
|
+
if (diskCacheLoaded) return cache3.size;
|
|
4053
|
+
if (!diskCacheLoadPromise) {
|
|
4054
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
4055
|
+
});
|
|
4056
|
+
}
|
|
4057
|
+
await diskCacheLoadPromise;
|
|
4058
|
+
return cache3.size;
|
|
4059
|
+
}
|
|
4060
|
+
function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
4061
|
+
return diskCacheLoadCount;
|
|
4062
|
+
}
|
|
4063
|
+
function resetApiEmbeddingCacheForTests() {
|
|
4064
|
+
cache3.clear();
|
|
4065
|
+
cachePayloadBytes = 0;
|
|
4066
|
+
cacheDimensions = null;
|
|
4067
|
+
diskCacheEntryCount = 0;
|
|
4068
|
+
diskCacheLoaded = false;
|
|
4069
|
+
diskCacheLoadPromise = null;
|
|
4070
|
+
diskCacheLoadCount = 0;
|
|
4071
|
+
diskCacheDirty2 = false;
|
|
4072
|
+
jsonlLineCountCalls = 0;
|
|
4073
|
+
}
|
|
4074
|
+
function seedApiEmbeddingCacheForTests(entries) {
|
|
4075
|
+
cache3.clear();
|
|
4076
|
+
cachePayloadBytes = 0;
|
|
4077
|
+
cacheDimensions = null;
|
|
4078
|
+
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
4079
|
+
diskCacheLoaded = true;
|
|
4080
|
+
diskCacheDirty2 = true;
|
|
4081
|
+
}
|
|
4082
|
+
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
4083
|
+
return cachePayloadBytes;
|
|
4084
|
+
}
|
|
4085
|
+
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
4086
|
+
return jsonlLineCountCalls;
|
|
4087
|
+
}
|
|
4088
|
+
function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
|
|
4089
|
+
return maxEntriesForDimensions(dimensions);
|
|
3712
4090
|
}
|
|
3713
4091
|
function dimsCacheKey(config) {
|
|
3714
4092
|
return [
|
|
@@ -3824,8 +4202,46 @@ async function saveDiskCacheNow() {
|
|
|
3824
4202
|
if (!diskCacheDirty2) return;
|
|
3825
4203
|
try {
|
|
3826
4204
|
await mkdir2(cacheDir(), { recursive: true });
|
|
3827
|
-
const
|
|
3828
|
-
await
|
|
4205
|
+
const jsonlPath = cacheJsonlFile();
|
|
4206
|
+
const existing = await resolveOnDiskCount(jsonlPath);
|
|
4207
|
+
if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
|
|
4208
|
+
console.error(
|
|
4209
|
+
`[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
|
|
4210
|
+
);
|
|
4211
|
+
diskCacheDirty2 = false;
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
4215
|
+
await new Promise((resolve, reject) => {
|
|
4216
|
+
const out = createWriteStream(tmpPath);
|
|
4217
|
+
const entries = cache3.entries();
|
|
4218
|
+
const writeBatch = () => {
|
|
4219
|
+
for (let i2 = 0; i2 < 50; i2++) {
|
|
4220
|
+
const next = entries.next();
|
|
4221
|
+
if (next.done) {
|
|
4222
|
+
out.end();
|
|
4223
|
+
return;
|
|
4224
|
+
}
|
|
4225
|
+
const [hash4, vector] = next.value;
|
|
4226
|
+
if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
|
|
4227
|
+
`)) {
|
|
4228
|
+
out.once("drain", writeBatch);
|
|
4229
|
+
return;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
setImmediate(writeBatch);
|
|
4233
|
+
};
|
|
4234
|
+
out.on("finish", resolve);
|
|
4235
|
+
out.on("error", reject);
|
|
4236
|
+
writeBatch();
|
|
4237
|
+
});
|
|
4238
|
+
await rename(tmpPath, jsonlPath);
|
|
4239
|
+
diskCacheEntryCount = cache3.size;
|
|
4240
|
+
await writeCacheStat({
|
|
4241
|
+
version: 1,
|
|
4242
|
+
entryCount: cache3.size,
|
|
4243
|
+
dimensions: cacheDimensions
|
|
4244
|
+
});
|
|
3829
4245
|
diskCacheDirty2 = false;
|
|
3830
4246
|
} catch {
|
|
3831
4247
|
}
|
|
@@ -3839,12 +4255,7 @@ function scheduleDiskSave() {
|
|
|
3839
4255
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
3840
4256
|
}
|
|
3841
4257
|
function cacheSet(hash4, value) {
|
|
3842
|
-
|
|
3843
|
-
const firstKey = cache3.keys().next().value;
|
|
3844
|
-
if (firstKey !== void 0) cache3.delete(firstKey);
|
|
3845
|
-
}
|
|
3846
|
-
cache3.set(hash4, value);
|
|
3847
|
-
diskCacheDirty2 = true;
|
|
4258
|
+
cacheInsert(hash4, value, true);
|
|
3848
4259
|
}
|
|
3849
4260
|
function resolveEnvEmbeddingApiKey() {
|
|
3850
4261
|
return process.env.MEMORIX_EMBEDDING_API_KEY;
|
|
@@ -3963,18 +4374,27 @@ async function waitForRetry(waitMs, signal) {
|
|
|
3963
4374
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
3964
4375
|
});
|
|
3965
4376
|
}
|
|
3966
|
-
var cache3,
|
|
4377
|
+
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;
|
|
3967
4378
|
var init_api_provider = __esm({
|
|
3968
4379
|
"src/embedding/api-provider.ts"() {
|
|
3969
4380
|
"use strict";
|
|
3970
4381
|
init_esm_shims();
|
|
3971
4382
|
init_provider2();
|
|
3972
4383
|
cache3 = /* @__PURE__ */ new Map();
|
|
3973
|
-
|
|
4384
|
+
MAX_CACHE_ENTRIES = 2e5;
|
|
4385
|
+
MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
4386
|
+
BYTES_PER_DIM = 8;
|
|
4387
|
+
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
4388
|
+
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
4389
|
+
cachePayloadBytes = 0;
|
|
4390
|
+
cacheDimensions = null;
|
|
4391
|
+
diskCacheEntryCount = 0;
|
|
3974
4392
|
diskCacheDirty2 = false;
|
|
3975
4393
|
diskSaveTimer = null;
|
|
3976
4394
|
diskCacheLoaded = false;
|
|
3977
4395
|
diskCacheLoadPromise = null;
|
|
4396
|
+
diskCacheLoadCount = 0;
|
|
4397
|
+
jsonlLineCountCalls = 0;
|
|
3978
4398
|
MAX_INPUT_CHARS = 32e3;
|
|
3979
4399
|
MAX_CONCURRENCY = 4;
|
|
3980
4400
|
DISK_SAVE_DEBOUNCE_MS = 5e3;
|
|
@@ -4032,7 +4452,9 @@ var init_api_provider = __esm({
|
|
|
4032
4452
|
if (config.requestedDimensions) {
|
|
4033
4453
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
4034
4454
|
}
|
|
4035
|
-
|
|
4455
|
+
if (allowNetworkProbe) {
|
|
4456
|
+
startDiskCacheLoad();
|
|
4457
|
+
}
|
|
4036
4458
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
4037
4459
|
}
|
|
4038
4460
|
static resolveConfig() {
|
|
@@ -4308,6 +4730,7 @@ __export(provider_exports2, {
|
|
|
4308
4730
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
4309
4731
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
4310
4732
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
4733
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
4311
4734
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
4312
4735
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
4313
4736
|
resetProvider: () => resetProvider,
|
|
@@ -4369,7 +4792,12 @@ function warnOnce(message) {
|
|
|
4369
4792
|
lastEmbeddingWarningAt = now3;
|
|
4370
4793
|
console.error(message);
|
|
4371
4794
|
}
|
|
4795
|
+
function embeddingModeToken() {
|
|
4796
|
+
return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
|
|
4797
|
+
}
|
|
4372
4798
|
function getEmbeddingMode2() {
|
|
4799
|
+
const token = embeddingModeToken();
|
|
4800
|
+
if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
|
|
4373
4801
|
try {
|
|
4374
4802
|
let cfg;
|
|
4375
4803
|
try {
|
|
@@ -4378,10 +4806,17 @@ function getEmbeddingMode2() {
|
|
|
4378
4806
|
cfg = (init_config(), __toCommonJS(config_exports));
|
|
4379
4807
|
}
|
|
4380
4808
|
const { getEmbeddingMode: cfgMode } = cfg;
|
|
4381
|
-
|
|
4809
|
+
cachedEmbeddingMode = cfgMode();
|
|
4810
|
+
cachedEmbeddingModeToken = token;
|
|
4811
|
+
return cachedEmbeddingMode;
|
|
4382
4812
|
} catch {
|
|
4383
|
-
|
|
4384
|
-
|
|
4813
|
+
if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
|
|
4814
|
+
cachedEmbeddingMode = token;
|
|
4815
|
+
cachedEmbeddingModeToken = token;
|
|
4816
|
+
return token;
|
|
4817
|
+
}
|
|
4818
|
+
cachedEmbeddingMode = "off";
|
|
4819
|
+
cachedEmbeddingModeToken = token;
|
|
4385
4820
|
return "off";
|
|
4386
4821
|
}
|
|
4387
4822
|
}
|
|
@@ -4415,13 +4850,36 @@ function isTemporaryEmbeddingFailure(error) {
|
|
|
4415
4850
|
if (!(error instanceof Error)) return false;
|
|
4416
4851
|
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);
|
|
4417
4852
|
}
|
|
4853
|
+
function classifyEmbeddingFailure(error) {
|
|
4854
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4855
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
4856
|
+
return "authorization";
|
|
4857
|
+
}
|
|
4858
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
4859
|
+
return "billing";
|
|
4860
|
+
}
|
|
4861
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
4862
|
+
return "timeout";
|
|
4863
|
+
}
|
|
4864
|
+
return "upstream";
|
|
4865
|
+
}
|
|
4418
4866
|
function markTemporaryFailure(reason) {
|
|
4419
4867
|
provider = null;
|
|
4420
4868
|
initPromise = null;
|
|
4421
4869
|
lastInitWasTemporaryFailure = true;
|
|
4422
4870
|
lastFailureTimestamp = Date.now();
|
|
4871
|
+
lastEmbeddingFailure = {
|
|
4872
|
+
kind: classifyEmbeddingFailure(reason),
|
|
4873
|
+
occurredAt: lastFailureTimestamp
|
|
4874
|
+
};
|
|
4423
4875
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
4424
|
-
warnOnce(`[memorix] Embedding provider
|
|
4876
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
4877
|
+
}
|
|
4878
|
+
function markProviderReady(candidate) {
|
|
4879
|
+
provider = wrapProvider(candidate);
|
|
4880
|
+
lastInitWasTemporaryFailure = false;
|
|
4881
|
+
lastEmbeddingFailure = null;
|
|
4882
|
+
return provider;
|
|
4425
4883
|
}
|
|
4426
4884
|
async function createFastEmbedProvider() {
|
|
4427
4885
|
try {
|
|
@@ -4537,35 +4995,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
4537
4995
|
if (mode === "fastembed") {
|
|
4538
4996
|
const initialized = await createFastEmbedProvider();
|
|
4539
4997
|
if (!initialized) return null;
|
|
4540
|
-
provider =
|
|
4998
|
+
provider = markProviderReady(initialized);
|
|
4541
4999
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4542
5000
|
return provider;
|
|
4543
5001
|
}
|
|
4544
5002
|
if (mode === "transformers") {
|
|
4545
5003
|
const initialized = await createTransformersProvider();
|
|
4546
5004
|
if (!initialized) return null;
|
|
4547
|
-
provider =
|
|
5005
|
+
provider = markProviderReady(initialized);
|
|
4548
5006
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4549
5007
|
return provider;
|
|
4550
5008
|
}
|
|
4551
5009
|
if (mode === "api") {
|
|
4552
5010
|
const initialized = await createAPIProvider(options);
|
|
4553
5011
|
if (!initialized) return null;
|
|
4554
|
-
provider =
|
|
5012
|
+
provider = markProviderReady(initialized);
|
|
4555
5013
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4556
5014
|
return provider;
|
|
4557
5015
|
}
|
|
4558
5016
|
if (hasAPIEmbeddingConfig()) {
|
|
4559
5017
|
const initialized = await createAPIProvider(options);
|
|
4560
5018
|
if (initialized) {
|
|
4561
|
-
provider =
|
|
5019
|
+
provider = markProviderReady(initialized);
|
|
4562
5020
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4563
5021
|
return provider;
|
|
4564
5022
|
}
|
|
4565
5023
|
}
|
|
4566
5024
|
const localFallback = await createLocalFallbackProvider();
|
|
4567
5025
|
if (localFallback) {
|
|
4568
|
-
provider =
|
|
5026
|
+
provider = markProviderReady(localFallback);
|
|
4569
5027
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4570
5028
|
return provider;
|
|
4571
5029
|
}
|
|
@@ -4587,18 +5045,42 @@ async function isVectorSearchAvailable() {
|
|
|
4587
5045
|
const p = await getEmbeddingProvider();
|
|
4588
5046
|
return p !== null;
|
|
4589
5047
|
}
|
|
5048
|
+
function getEmbeddingRuntimeHealth() {
|
|
5049
|
+
const mode = getEmbeddingMode2();
|
|
5050
|
+
if (mode === "off") return { status: "disabled" };
|
|
5051
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
5052
|
+
return {
|
|
5053
|
+
status: "degraded",
|
|
5054
|
+
failure: {
|
|
5055
|
+
kind: lastEmbeddingFailure.kind,
|
|
5056
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
5057
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
5058
|
+
}
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
if (provider) {
|
|
5062
|
+
return {
|
|
5063
|
+
status: "ready",
|
|
5064
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
5065
|
+
};
|
|
5066
|
+
}
|
|
5067
|
+
return { status: "uninitialized" };
|
|
5068
|
+
}
|
|
4590
5069
|
function isEmbeddingExplicitlyDisabled() {
|
|
4591
5070
|
return getEmbeddingMode2() === "off";
|
|
4592
5071
|
}
|
|
4593
5072
|
function resetProvider() {
|
|
4594
5073
|
provider = null;
|
|
4595
5074
|
initPromise = null;
|
|
5075
|
+
cachedEmbeddingMode = null;
|
|
5076
|
+
cachedEmbeddingModeToken = null;
|
|
4596
5077
|
lastInitWasTemporaryFailure = false;
|
|
4597
5078
|
lastFailureTimestamp = 0;
|
|
5079
|
+
lastEmbeddingFailure = null;
|
|
4598
5080
|
lastEmbeddingWarning = "";
|
|
4599
5081
|
lastEmbeddingWarningAt = 0;
|
|
4600
5082
|
}
|
|
4601
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
5083
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
4602
5084
|
var init_provider2 = __esm({
|
|
4603
5085
|
"src/embedding/provider.ts"() {
|
|
4604
5086
|
"use strict";
|
|
@@ -4622,7 +5104,10 @@ var init_provider2 = __esm({
|
|
|
4622
5104
|
MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
|
|
4623
5105
|
provider = null;
|
|
4624
5106
|
initPromise = null;
|
|
5107
|
+
cachedEmbeddingMode = null;
|
|
5108
|
+
cachedEmbeddingModeToken = null;
|
|
4625
5109
|
lastInitWasTemporaryFailure = false;
|
|
5110
|
+
lastEmbeddingFailure = null;
|
|
4626
5111
|
lastEmbeddingWarning = "";
|
|
4627
5112
|
lastEmbeddingWarningAt = 0;
|
|
4628
5113
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -5762,6 +6247,9 @@ async function attachCachedVectors(database, candidates) {
|
|
|
5762
6247
|
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
5763
6248
|
} catch {
|
|
5764
6249
|
}
|
|
6250
|
+
if (index % 50 === 0) {
|
|
6251
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6252
|
+
}
|
|
5765
6253
|
}
|
|
5766
6254
|
}
|
|
5767
6255
|
async function hydrateIndex(observations2, options = {}) {
|
|
@@ -5812,17 +6300,24 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5812
6300
|
await insert2(database, doc);
|
|
5813
6301
|
rememberObservationDoc(doc);
|
|
5814
6302
|
inserted++;
|
|
6303
|
+
if (inserted % 50 === 0) {
|
|
6304
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6305
|
+
}
|
|
6306
|
+
if (inserted % 200 === 0) {
|
|
6307
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6308
|
+
}
|
|
5815
6309
|
} catch {
|
|
5816
6310
|
}
|
|
5817
6311
|
}
|
|
5818
|
-
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6312
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5819
6313
|
}) : null;
|
|
5820
6314
|
return inserted;
|
|
5821
6315
|
}
|
|
5822
|
-
async function hydrateIndexForStartup(observations2) {
|
|
6316
|
+
async function hydrateIndexForStartup(observations2, options = {}) {
|
|
5823
6317
|
return hydrateIndex(observations2, {
|
|
5824
6318
|
allowNetworkProbe: false,
|
|
5825
|
-
deferCachedVectors: true
|
|
6319
|
+
deferCachedVectors: true,
|
|
6320
|
+
skipCachedVectors: options.skipCachedVectors
|
|
5826
6321
|
});
|
|
5827
6322
|
}
|
|
5828
6323
|
function getDeferredCachedVectorHydration() {
|
|
@@ -8484,7 +8979,7 @@ var init_types2 = __esm({
|
|
|
8484
8979
|
|
|
8485
8980
|
// src/media/asset-store.ts
|
|
8486
8981
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
8487
|
-
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
8982
|
+
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
8488
8983
|
import path10 from "path";
|
|
8489
8984
|
function mediaRoot(dataDir) {
|
|
8490
8985
|
return path10.resolve(dataDir, "media");
|
|
@@ -8557,7 +9052,7 @@ async function persistBuffer(bytes, destination) {
|
|
|
8557
9052
|
const temporary = `${destination}.${randomUUID5()}.tmp`;
|
|
8558
9053
|
try {
|
|
8559
9054
|
await writeFile3(temporary, bytes, { flag: "wx" });
|
|
8560
|
-
await
|
|
9055
|
+
await rename2(temporary, destination);
|
|
8561
9056
|
return true;
|
|
8562
9057
|
} catch (error) {
|
|
8563
9058
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -59637,9 +60132,9 @@ var require_node_domexception = __commonJS({
|
|
|
59637
60132
|
});
|
|
59638
60133
|
|
|
59639
60134
|
// node_modules/fetch-blob/from.js
|
|
59640
|
-
import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
|
|
60135
|
+
import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
59641
60136
|
import { basename } from "path";
|
|
59642
|
-
var import_node_domexception,
|
|
60137
|
+
var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
59643
60138
|
var init_from = __esm({
|
|
59644
60139
|
"node_modules/fetch-blob/from.js"() {
|
|
59645
60140
|
"use strict";
|
|
@@ -59647,23 +60142,23 @@ var init_from = __esm({
|
|
|
59647
60142
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
59648
60143
|
init_file();
|
|
59649
60144
|
init_fetch_blob();
|
|
59650
|
-
({ stat:
|
|
60145
|
+
({ stat: stat3 } = fs7);
|
|
59651
60146
|
blobFromSync = (path21, type) => fromBlob(statSync2(path21), path21, type);
|
|
59652
|
-
blobFrom = (path21, type) =>
|
|
59653
|
-
fileFrom = (path21, type) =>
|
|
60147
|
+
blobFrom = (path21, type) => stat3(path21).then((stat5) => fromBlob(stat5, path21, type));
|
|
60148
|
+
fileFrom = (path21, type) => stat3(path21).then((stat5) => fromFile(stat5, path21, type));
|
|
59654
60149
|
fileFromSync = (path21, type) => fromFile(statSync2(path21), path21, type);
|
|
59655
|
-
fromBlob = (
|
|
60150
|
+
fromBlob = (stat5, path21, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
59656
60151
|
path: path21,
|
|
59657
|
-
size:
|
|
59658
|
-
lastModified:
|
|
60152
|
+
size: stat5.size,
|
|
60153
|
+
lastModified: stat5.mtimeMs,
|
|
59659
60154
|
start: 0
|
|
59660
60155
|
})], { type });
|
|
59661
|
-
fromFile = (
|
|
60156
|
+
fromFile = (stat5, path21, type = "") => new file_default([new BlobDataItem({
|
|
59662
60157
|
path: path21,
|
|
59663
|
-
size:
|
|
59664
|
-
lastModified:
|
|
60158
|
+
size: stat5.size,
|
|
60159
|
+
lastModified: stat5.mtimeMs,
|
|
59665
60160
|
start: 0
|
|
59666
|
-
})], basename(path21), { type, lastModified:
|
|
60161
|
+
})], basename(path21), { type, lastModified: stat5.mtimeMs });
|
|
59667
60162
|
BlobDataItem = class _BlobDataItem {
|
|
59668
60163
|
#path;
|
|
59669
60164
|
#start;
|
|
@@ -59686,11 +60181,11 @@ var init_from = __esm({
|
|
|
59686
60181
|
});
|
|
59687
60182
|
}
|
|
59688
60183
|
async *stream() {
|
|
59689
|
-
const { mtimeMs } = await
|
|
60184
|
+
const { mtimeMs } = await stat3(this.#path);
|
|
59690
60185
|
if (mtimeMs > this.lastModified) {
|
|
59691
60186
|
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");
|
|
59692
60187
|
}
|
|
59693
|
-
yield*
|
|
60188
|
+
yield* createReadStream2(this.#path, {
|
|
59694
60189
|
start: this.#start,
|
|
59695
60190
|
end: this.#start + this.size - 1
|
|
59696
60191
|
});
|
|
@@ -75320,7 +75815,7 @@ var init_wrapper = __esm({
|
|
|
75320
75815
|
});
|
|
75321
75816
|
|
|
75322
75817
|
// node_modules/@google/genai/dist/node/index.mjs
|
|
75323
|
-
import { createWriteStream } from "fs";
|
|
75818
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
75324
75819
|
import * as fs8 from "fs/promises";
|
|
75325
75820
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
75326
75821
|
import { Readable } from "stream";
|
|
@@ -92600,7 +93095,7 @@ ${underline}`);
|
|
|
92600
93095
|
if (params.downloadPath) {
|
|
92601
93096
|
const response = await downloadFile(params, apiClient);
|
|
92602
93097
|
if (response instanceof HttpResponse) {
|
|
92603
|
-
const writer =
|
|
93098
|
+
const writer = createWriteStream2(params.downloadPath);
|
|
92604
93099
|
const body = Readable.fromWeb(response.responseInternal.body);
|
|
92605
93100
|
body.pipe(writer);
|
|
92606
93101
|
await finished(writer);
|
|
@@ -142518,7 +143013,7 @@ function explainRetention(doc, referenceTime) {
|
|
|
142518
143013
|
};
|
|
142519
143014
|
}
|
|
142520
143015
|
function toRetentionDocument(obs, accessMap) {
|
|
142521
|
-
const
|
|
143016
|
+
const access2 = accessMap?.get(obs.id);
|
|
142522
143017
|
return {
|
|
142523
143018
|
id: `obs-${obs.id}`,
|
|
142524
143019
|
observationId: obs.id,
|
|
@@ -142532,8 +143027,8 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
142532
143027
|
tokens: obs.tokens,
|
|
142533
143028
|
createdAt: obs.createdAt,
|
|
142534
143029
|
projectId: obs.projectId,
|
|
142535
|
-
accessCount:
|
|
142536
|
-
lastAccessedAt:
|
|
143030
|
+
accessCount: access2?.accessCount ?? 0,
|
|
143031
|
+
lastAccessedAt: access2?.lastAccessedAt ?? "",
|
|
142537
143032
|
status: obs.status ?? "active",
|
|
142538
143033
|
source: obs.source ?? "agent",
|
|
142539
143034
|
sourceDetail: obs.sourceDetail ?? "",
|
|
@@ -144643,7 +145138,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
144643
145138
|
const rel = normalizeCodePath(relative(projectRoot, abs));
|
|
144644
145139
|
try {
|
|
144645
145140
|
const text2 = readFileSync6(abs, "utf-8");
|
|
144646
|
-
const
|
|
145141
|
+
const stat5 = fileStat ?? statSync3(abs);
|
|
144647
145142
|
const file = {
|
|
144648
145143
|
id: makeCodeFileId(projectId, rel),
|
|
144649
145144
|
projectId,
|
|
@@ -144652,8 +145147,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
144652
145147
|
contentHash: hashText(text2),
|
|
144653
145148
|
// Preserve filesystem precision. Rounding here made same-size edits in
|
|
144654
145149
|
// a short Windows timestamp window indistinguishable to refresh.
|
|
144655
|
-
mtimeMs: Number(
|
|
144656
|
-
sizeBytes: Number(
|
|
145150
|
+
mtimeMs: Number(stat5.mtimeMs),
|
|
145151
|
+
sizeBytes: Number(stat5.size),
|
|
144657
145152
|
indexedAt
|
|
144658
145153
|
};
|
|
144659
145154
|
return {
|
|
@@ -144680,18 +145175,18 @@ async function indexProjectLite(options) {
|
|
|
144680
145175
|
let skippedOversizedFiles = 0;
|
|
144681
145176
|
let unreadableFiles = 0;
|
|
144682
145177
|
for (const abs of paths) {
|
|
144683
|
-
let
|
|
145178
|
+
let stat5;
|
|
144684
145179
|
try {
|
|
144685
|
-
|
|
145180
|
+
stat5 = statSync3(abs);
|
|
144686
145181
|
} catch {
|
|
144687
145182
|
unreadableFiles++;
|
|
144688
145183
|
continue;
|
|
144689
145184
|
}
|
|
144690
|
-
if (Number(
|
|
145185
|
+
if (Number(stat5.size) > maxFileBytes) {
|
|
144691
145186
|
skippedOversizedFiles++;
|
|
144692
145187
|
continue;
|
|
144693
145188
|
}
|
|
144694
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
145189
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
144695
145190
|
if (!attempt.delta) {
|
|
144696
145191
|
if (attempt.unreadable) unreadableFiles++;
|
|
144697
145192
|
continue;
|
|
@@ -144719,16 +145214,16 @@ async function refreshProjectLite(store, options) {
|
|
|
144719
145214
|
for (const abs of paths) {
|
|
144720
145215
|
const rel = normalizeCodePath(relative(options.projectRoot, abs));
|
|
144721
145216
|
seenPaths.add(rel);
|
|
144722
|
-
let
|
|
145217
|
+
let stat5;
|
|
144723
145218
|
try {
|
|
144724
|
-
|
|
145219
|
+
stat5 = statSync3(abs);
|
|
144725
145220
|
} catch {
|
|
144726
145221
|
unreadableFiles++;
|
|
144727
145222
|
continue;
|
|
144728
145223
|
}
|
|
144729
145224
|
const existing = existingByPath.get(rel);
|
|
144730
|
-
const mtimeMs = Number(
|
|
144731
|
-
const sizeBytes = Number(
|
|
145225
|
+
const mtimeMs = Number(stat5.mtimeMs);
|
|
145226
|
+
const sizeBytes = Number(stat5.size);
|
|
144732
145227
|
if (sizeBytes > maxFileBytes) {
|
|
144733
145228
|
skippedOversizedFiles++;
|
|
144734
145229
|
if (existing) oversizedExistingFileIds.add(existing.id);
|
|
@@ -144738,7 +145233,7 @@ async function refreshProjectLite(store, options) {
|
|
|
144738
145233
|
unchangedFiles++;
|
|
144739
145234
|
continue;
|
|
144740
145235
|
}
|
|
144741
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
145236
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
144742
145237
|
if (!attempt.delta) {
|
|
144743
145238
|
if (attempt.unreadable) unreadableFiles++;
|
|
144744
145239
|
continue;
|
|
@@ -148135,12 +148630,25 @@ __export(observations_exports, {
|
|
|
148135
148630
|
prepareSearchIndex: () => prepareSearchIndex,
|
|
148136
148631
|
probeSearchIndex: () => probeSearchIndex,
|
|
148137
148632
|
reindexObservations: () => reindexObservations,
|
|
148633
|
+
resetObservationRuntime: () => resetObservationRuntime,
|
|
148138
148634
|
resolveObservations: () => resolveObservations,
|
|
148139
148635
|
storeObservation: () => storeObservation,
|
|
148140
148636
|
suggestTopicKey: () => suggestTopicKey,
|
|
148141
148637
|
updateObservationAdmission: () => updateObservationAdmission,
|
|
148142
148638
|
withFreshObservations: () => withFreshObservations
|
|
148143
148639
|
});
|
|
148640
|
+
function resetObservationRuntime() {
|
|
148641
|
+
observations = [];
|
|
148642
|
+
nextId = 1;
|
|
148643
|
+
projectDir = null;
|
|
148644
|
+
searchIndexPrepared = false;
|
|
148645
|
+
corpusLoaded = false;
|
|
148646
|
+
loadedObservationStore = null;
|
|
148647
|
+
embeddingWriteMode = "background";
|
|
148648
|
+
embeddingWorkerProjectRoot = void 0;
|
|
148649
|
+
vectorMissingIds.clear();
|
|
148650
|
+
vectorSchemaUpgradePromise = null;
|
|
148651
|
+
}
|
|
148144
148652
|
function logEmbeddingFailureOnce(key, message) {
|
|
148145
148653
|
const now3 = Date.now();
|
|
148146
148654
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
@@ -148249,23 +148757,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
|
148249
148757
|
async function initObservations(dir, options = {}) {
|
|
148250
148758
|
embeddingWriteMode = options.embeddingWriteMode ?? "background";
|
|
148251
148759
|
embeddingWorkerProjectRoot = options.projectRoot;
|
|
148252
|
-
if (projectDir === dir) return;
|
|
148253
148760
|
await initObservationStore(dir);
|
|
148254
148761
|
const store = getObservationStore();
|
|
148255
|
-
|
|
148762
|
+
if (projectDir === dir && loadedObservationStore === store) {
|
|
148763
|
+
if (options.skipCorpusLoad || corpusLoaded) return;
|
|
148764
|
+
}
|
|
148256
148765
|
nextId = await store.loadIdCounter();
|
|
148257
148766
|
projectDir = dir;
|
|
148258
148767
|
searchIndexPrepared = false;
|
|
148259
148768
|
vectorSchemaUpgradePromise = null;
|
|
148769
|
+
if (options.skipCorpusLoad) {
|
|
148770
|
+
observations = [];
|
|
148771
|
+
corpusLoaded = false;
|
|
148772
|
+
loadedObservationStore = store;
|
|
148773
|
+
return;
|
|
148774
|
+
}
|
|
148775
|
+
observations = await store.loadAll();
|
|
148776
|
+
corpusLoaded = true;
|
|
148777
|
+
loadedObservationStore = store;
|
|
148260
148778
|
}
|
|
148261
148779
|
function scheduleObservationEmbedding(input) {
|
|
148262
148780
|
const { observationId: observationId2, projectId, searchableText, doc } = input;
|
|
148263
148781
|
vectorMissingIds.add(observationId2);
|
|
148782
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
148783
|
+
vectorMissingIds.delete(observationId2);
|
|
148784
|
+
return;
|
|
148785
|
+
}
|
|
148264
148786
|
if (embeddingWriteMode === "deferred") {
|
|
148265
|
-
if (isEmbeddingExplicitlyDisabled()) {
|
|
148266
|
-
vectorMissingIds.delete(observationId2);
|
|
148267
|
-
return;
|
|
148268
|
-
}
|
|
148269
148787
|
queueVectorBackfill(projectId, {
|
|
148270
148788
|
detachedWorker: true,
|
|
148271
148789
|
projectRoot: embeddingWorkerProjectRoot
|
|
@@ -148312,7 +148830,7 @@ function scheduleObservationEmbedding(input) {
|
|
|
148312
148830
|
});
|
|
148313
148831
|
}
|
|
148314
148832
|
async function ensureFreshObservations() {
|
|
148315
|
-
if (!projectDir) return false;
|
|
148833
|
+
if (!projectDir || !corpusLoaded) return false;
|
|
148316
148834
|
try {
|
|
148317
148835
|
const store = getObservationStore();
|
|
148318
148836
|
const wasStale = await store.ensureFresh();
|
|
@@ -148353,7 +148871,7 @@ async function storeObservation(input) {
|
|
|
148353
148871
|
});
|
|
148354
148872
|
if (safeAttachments) input = { ...input, attachments: safeAttachments };
|
|
148355
148873
|
await ensureFreshObservations();
|
|
148356
|
-
if (input.topicKey) {
|
|
148874
|
+
if (input.topicKey && corpusLoaded) {
|
|
148357
148875
|
const existing = observations.find(
|
|
148358
148876
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
148359
148877
|
);
|
|
@@ -148450,13 +148968,17 @@ async function storeObservation(input) {
|
|
|
148450
148968
|
});
|
|
148451
148969
|
observation.writeGeneration = store.getGeneration();
|
|
148452
148970
|
if (upsertedInsideLock || reloadCacheAfterCommit) {
|
|
148453
|
-
|
|
148454
|
-
|
|
148455
|
-
|
|
148456
|
-
|
|
148971
|
+
if (corpusLoaded) {
|
|
148972
|
+
observations = await store.loadAll();
|
|
148973
|
+
nextId = await store.loadIdCounter();
|
|
148974
|
+
if (upsertedInsideLock) {
|
|
148975
|
+
observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
|
|
148976
|
+
}
|
|
148977
|
+
} else {
|
|
148978
|
+
nextId = await store.loadIdCounter();
|
|
148457
148979
|
}
|
|
148458
148980
|
} else {
|
|
148459
|
-
observations.push(observation);
|
|
148981
|
+
if (corpusLoaded) observations.push(observation);
|
|
148460
148982
|
nextId = observation.id + 1;
|
|
148461
148983
|
}
|
|
148462
148984
|
if (upsertedInsideLock) return;
|
|
@@ -148524,7 +149046,9 @@ async function storeObservation(input) {
|
|
|
148524
149046
|
createdByAgentId: input.createdByAgentId ?? "",
|
|
148525
149047
|
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
148526
149048
|
};
|
|
148527
|
-
|
|
149049
|
+
if (corpusLoaded) {
|
|
149050
|
+
await insertObservation(doc);
|
|
149051
|
+
}
|
|
148528
149052
|
};
|
|
148529
149053
|
await assignAndPersist();
|
|
148530
149054
|
if (upsertedInsideLock) {
|
|
@@ -148886,9 +149410,11 @@ async function reindexObservations() {
|
|
|
148886
149410
|
searchIndexPrepared = true;
|
|
148887
149411
|
return count2;
|
|
148888
149412
|
}
|
|
148889
|
-
async function prepareSearchIndex() {
|
|
149413
|
+
async function prepareSearchIndex(options = {}) {
|
|
148890
149414
|
if (searchIndexPrepared) return 0;
|
|
148891
|
-
const count2 = await hydrateIndexForStartup(observations
|
|
149415
|
+
const count2 = await hydrateIndexForStartup(observations, {
|
|
149416
|
+
skipCachedVectors: options.skipCachedVectors
|
|
149417
|
+
});
|
|
148892
149418
|
if (count2 === 0) {
|
|
148893
149419
|
searchIndexPrepared = true;
|
|
148894
149420
|
return 0;
|
|
@@ -149080,7 +149606,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
149080
149606
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
149081
149607
|
};
|
|
149082
149608
|
}
|
|
149083
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
149609
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
149084
149610
|
var init_observations = __esm({
|
|
149085
149611
|
"src/memory/observations.ts"() {
|
|
149086
149612
|
"use strict";
|
|
@@ -149099,6 +149625,8 @@ var init_observations = __esm({
|
|
|
149099
149625
|
projectDir = null;
|
|
149100
149626
|
searchIndexPrepared = false;
|
|
149101
149627
|
embeddingWriteMode = "background";
|
|
149628
|
+
corpusLoaded = false;
|
|
149629
|
+
loadedObservationStore = null;
|
|
149102
149630
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
149103
149631
|
vectorBackfillRunning = false;
|
|
149104
149632
|
vectorSchemaUpgradePromise = null;
|