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
package/dist/sdk.js
CHANGED
|
@@ -249,6 +249,14 @@ var init_bun_sqlite_compat = __esm({
|
|
|
249
249
|
});
|
|
250
250
|
|
|
251
251
|
// src/store/sqlite-db.ts
|
|
252
|
+
var sqlite_db_exports = {};
|
|
253
|
+
__export(sqlite_db_exports, {
|
|
254
|
+
closeAllDatabases: () => closeAllDatabases,
|
|
255
|
+
closeDatabase: () => closeDatabase,
|
|
256
|
+
getDatabase: () => getDatabase,
|
|
257
|
+
isSqliteAvailable: () => isSqliteAvailable,
|
|
258
|
+
loadBetterSqlite3: () => loadBetterSqlite3
|
|
259
|
+
});
|
|
252
260
|
import "module";
|
|
253
261
|
import path2 from "path";
|
|
254
262
|
import fs from "fs";
|
|
@@ -354,6 +362,17 @@ function getDatabase(dataDir) {
|
|
|
354
362
|
_dbCache.set(normalized, db2);
|
|
355
363
|
return db2;
|
|
356
364
|
}
|
|
365
|
+
function closeDatabase(dataDir) {
|
|
366
|
+
const normalized = path2.resolve(dataDir);
|
|
367
|
+
const db2 = _dbCache.get(normalized);
|
|
368
|
+
if (db2) {
|
|
369
|
+
try {
|
|
370
|
+
db2.close();
|
|
371
|
+
} catch {
|
|
372
|
+
}
|
|
373
|
+
_dbCache.delete(normalized);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
357
376
|
function closeAllDatabases() {
|
|
358
377
|
for (const [key, db2] of _dbCache) {
|
|
359
378
|
try {
|
|
@@ -363,6 +382,14 @@ function closeAllDatabases() {
|
|
|
363
382
|
_dbCache.delete(key);
|
|
364
383
|
}
|
|
365
384
|
}
|
|
385
|
+
function isSqliteAvailable() {
|
|
386
|
+
try {
|
|
387
|
+
loadBetterSqlite3();
|
|
388
|
+
return true;
|
|
389
|
+
} catch {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
366
393
|
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
367
394
|
var init_sqlite_db = __esm({
|
|
368
395
|
"src/store/sqlite-db.ts"() {
|
|
@@ -1246,8 +1273,8 @@ async function acquireLock(lockPath) {
|
|
|
1246
1273
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
1247
1274
|
if (code === "EEXIST" || code === "EPERM") {
|
|
1248
1275
|
try {
|
|
1249
|
-
const
|
|
1250
|
-
if (Date.now() -
|
|
1276
|
+
const stat5 = await fs2.stat(lockPath);
|
|
1277
|
+
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
1251
1278
|
await fs2.unlink(lockPath).catch(() => {
|
|
1252
1279
|
});
|
|
1253
1280
|
continue;
|
|
@@ -3717,24 +3744,131 @@ var init_transformers_provider = __esm({
|
|
|
3717
3744
|
// src/embedding/api-provider.ts
|
|
3718
3745
|
var api_provider_exports = {};
|
|
3719
3746
|
__export(api_provider_exports, {
|
|
3720
|
-
APIEmbeddingProvider: () => APIEmbeddingProvider
|
|
3747
|
+
APIEmbeddingProvider: () => APIEmbeddingProvider,
|
|
3748
|
+
convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
|
|
3749
|
+
ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
|
|
3750
|
+
getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
|
|
3751
|
+
getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
|
|
3752
|
+
getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
|
|
3753
|
+
getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
|
|
3754
|
+
migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
|
|
3755
|
+
parseCacheJsonInWorker: () => parseCacheJsonInWorker,
|
|
3756
|
+
resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
|
|
3757
|
+
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
3758
|
+
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
3721
3759
|
});
|
|
3722
3760
|
import { createHash as createHash2 } from "crypto";
|
|
3723
|
-
import {
|
|
3761
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
3762
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
3724
3763
|
import { join as join3 } from "path";
|
|
3725
3764
|
import { homedir as homedir7 } from "os";
|
|
3765
|
+
import { createInterface } from "readline";
|
|
3726
3766
|
function cacheDir() {
|
|
3727
3767
|
return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
|
|
3728
3768
|
}
|
|
3729
3769
|
function cacheFile() {
|
|
3730
3770
|
return join3(cacheDir(), ".embedding-api-cache.json");
|
|
3731
3771
|
}
|
|
3772
|
+
function cacheJsonlFile() {
|
|
3773
|
+
return join3(cacheDir(), ".embedding-api-cache.jsonl");
|
|
3774
|
+
}
|
|
3732
3775
|
function dimsCacheFile() {
|
|
3733
3776
|
return join3(cacheDir(), ".embedding-dims-cache.json");
|
|
3734
3777
|
}
|
|
3735
3778
|
function cacheMetaFile() {
|
|
3736
3779
|
return join3(cacheDir(), ".embedding-api-cache-meta.json");
|
|
3737
3780
|
}
|
|
3781
|
+
function cacheStatFile() {
|
|
3782
|
+
return join3(cacheDir(), ".embedding-api-cache-stat.json");
|
|
3783
|
+
}
|
|
3784
|
+
function payloadBytes(vector) {
|
|
3785
|
+
return vector.length * BYTES_PER_DIM;
|
|
3786
|
+
}
|
|
3787
|
+
function maxCachePayloadBytes() {
|
|
3788
|
+
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
3789
|
+
if (raw) {
|
|
3790
|
+
const parsed = Number.parseInt(raw, 10);
|
|
3791
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
3792
|
+
}
|
|
3793
|
+
return MAX_CACHE_PAYLOAD_BYTES;
|
|
3794
|
+
}
|
|
3795
|
+
function maxEntriesForDimensions(dimensions) {
|
|
3796
|
+
const dim = Math.max(1, Math.floor(dimensions));
|
|
3797
|
+
const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
|
|
3798
|
+
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
3799
|
+
}
|
|
3800
|
+
function rememberDimensions(vector) {
|
|
3801
|
+
if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
|
|
3802
|
+
cacheDimensions = vector.length;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
function evictOldest() {
|
|
3806
|
+
const firstKey = cache3.keys().next().value;
|
|
3807
|
+
if (firstKey === void 0) return;
|
|
3808
|
+
const old = cache3.get(firstKey);
|
|
3809
|
+
cache3.delete(firstKey);
|
|
3810
|
+
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
3811
|
+
}
|
|
3812
|
+
function evictToFit(incomingBytes, incomingDims) {
|
|
3813
|
+
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
3814
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
|
|
3815
|
+
evictOldest();
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
function isNumberArray(value) {
|
|
3819
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
3820
|
+
}
|
|
3821
|
+
function parseCacheRow(raw) {
|
|
3822
|
+
if (Array.isArray(raw)) {
|
|
3823
|
+
const hash4 = raw[0];
|
|
3824
|
+
const vector = raw[1];
|
|
3825
|
+
if (typeof hash4 === "string" && isNumberArray(vector)) {
|
|
3826
|
+
return { hash: hash4, vector };
|
|
3827
|
+
}
|
|
3828
|
+
return null;
|
|
3829
|
+
}
|
|
3830
|
+
if (raw && typeof raw === "object") {
|
|
3831
|
+
const row = raw;
|
|
3832
|
+
if (typeof row.h === "string" && isNumberArray(row.v)) {
|
|
3833
|
+
return { hash: row.h, vector: row.v };
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
return null;
|
|
3837
|
+
}
|
|
3838
|
+
function cacheInsert(hash4, value, dirty) {
|
|
3839
|
+
rememberDimensions(value);
|
|
3840
|
+
const incoming = payloadBytes(value);
|
|
3841
|
+
const existing = cache3.get(hash4);
|
|
3842
|
+
if (existing) {
|
|
3843
|
+
cachePayloadBytes -= payloadBytes(existing);
|
|
3844
|
+
cache3.delete(hash4);
|
|
3845
|
+
}
|
|
3846
|
+
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
3847
|
+
cache3.set(hash4, value);
|
|
3848
|
+
cachePayloadBytes += incoming;
|
|
3849
|
+
if (dirty) diskCacheDirty2 = true;
|
|
3850
|
+
}
|
|
3851
|
+
async function readCacheStat() {
|
|
3852
|
+
try {
|
|
3853
|
+
const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
|
|
3854
|
+
if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
|
|
3855
|
+
return {
|
|
3856
|
+
version: 1,
|
|
3857
|
+
entryCount: Math.floor(data.entryCount),
|
|
3858
|
+
dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
|
|
3859
|
+
};
|
|
3860
|
+
}
|
|
3861
|
+
} catch {
|
|
3862
|
+
}
|
|
3863
|
+
return null;
|
|
3864
|
+
}
|
|
3865
|
+
async function writeCacheStat(stat5) {
|
|
3866
|
+
try {
|
|
3867
|
+
await mkdir2(cacheDir(), { recursive: true });
|
|
3868
|
+
await writeFile2(cacheStatFile(), JSON.stringify(stat5));
|
|
3869
|
+
} catch {
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3738
3872
|
function normalizeText(text2) {
|
|
3739
3873
|
return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
|
|
3740
3874
|
}
|
|
@@ -3820,29 +3954,300 @@ function toProviderInput(input, baseUrl) {
|
|
|
3820
3954
|
}
|
|
3821
3955
|
return { type: input.modality, url: input.url };
|
|
3822
3956
|
}
|
|
3957
|
+
function parseCacheJsonInWorker(filePath) {
|
|
3958
|
+
return new Promise((resolve2, reject) => {
|
|
3959
|
+
void import("worker_threads").then(({ Worker }) => {
|
|
3960
|
+
const worker = new Worker(
|
|
3961
|
+
`
|
|
3962
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
3963
|
+
const { readFileSync } = require('node:fs');
|
|
3964
|
+
try {
|
|
3965
|
+
const raw = readFileSync(workerData.filePath, 'utf8');
|
|
3966
|
+
parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
|
|
3967
|
+
} catch (err) {
|
|
3968
|
+
parentPort.postMessage({
|
|
3969
|
+
ok: false,
|
|
3970
|
+
error: err instanceof Error ? err.message : String(err),
|
|
3971
|
+
});
|
|
3972
|
+
}
|
|
3973
|
+
`,
|
|
3974
|
+
{ eval: true, workerData: { filePath } }
|
|
3975
|
+
);
|
|
3976
|
+
worker.once("message", (msg) => {
|
|
3977
|
+
void worker.terminate();
|
|
3978
|
+
if (msg?.ok && Array.isArray(msg.entries)) {
|
|
3979
|
+
resolve2(msg.entries);
|
|
3980
|
+
return;
|
|
3981
|
+
}
|
|
3982
|
+
reject(new Error(msg?.error || "embedding cache worker parse failed"));
|
|
3983
|
+
});
|
|
3984
|
+
worker.once("error", reject);
|
|
3985
|
+
}).catch(reject);
|
|
3986
|
+
});
|
|
3987
|
+
}
|
|
3988
|
+
function findJsonValueEnd(source) {
|
|
3989
|
+
let i2 = 0;
|
|
3990
|
+
while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
|
|
3991
|
+
if (i2 >= source.length) return -1;
|
|
3992
|
+
const start = source[i2];
|
|
3993
|
+
if (start === '"') {
|
|
3994
|
+
i2 += 1;
|
|
3995
|
+
while (i2 < source.length) {
|
|
3996
|
+
if (source[i2] === "\\") {
|
|
3997
|
+
i2 += 2;
|
|
3998
|
+
continue;
|
|
3999
|
+
}
|
|
4000
|
+
if (source[i2] === '"') return i2 + 1;
|
|
4001
|
+
i2 += 1;
|
|
4002
|
+
}
|
|
4003
|
+
return -1;
|
|
4004
|
+
}
|
|
4005
|
+
if (start === "{" || start === "[") {
|
|
4006
|
+
const close = start === "{" ? "}" : "]";
|
|
4007
|
+
let depth = 0;
|
|
4008
|
+
let inString = false;
|
|
4009
|
+
for (; i2 < source.length; i2 += 1) {
|
|
4010
|
+
const ch = source[i2];
|
|
4011
|
+
if (inString) {
|
|
4012
|
+
if (ch === "\\") {
|
|
4013
|
+
i2 += 1;
|
|
4014
|
+
continue;
|
|
4015
|
+
}
|
|
4016
|
+
if (ch === '"') inString = false;
|
|
4017
|
+
continue;
|
|
4018
|
+
}
|
|
4019
|
+
if (ch === '"') {
|
|
4020
|
+
inString = true;
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
4023
|
+
if (ch === start) depth += 1;
|
|
4024
|
+
else if (ch === close) {
|
|
4025
|
+
depth -= 1;
|
|
4026
|
+
if (depth === 0) return i2 + 1;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
return -1;
|
|
4030
|
+
}
|
|
4031
|
+
const rest = source.slice(i2);
|
|
4032
|
+
const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
|
|
4033
|
+
return match2 ? i2 + match2[0].length : -1;
|
|
4034
|
+
}
|
|
4035
|
+
async function streamJsonArrayToJsonl(filePath, tmpPath) {
|
|
4036
|
+
const input = createReadStream(filePath, { encoding: "utf8" });
|
|
4037
|
+
const out = createWriteStream(tmpPath);
|
|
4038
|
+
let buf = "";
|
|
4039
|
+
let started = false;
|
|
4040
|
+
let count2 = 0;
|
|
4041
|
+
const writeLine = async (line) => {
|
|
4042
|
+
if (!out.write(line)) {
|
|
4043
|
+
await new Promise((resolve2, reject) => {
|
|
4044
|
+
out.once("drain", resolve2);
|
|
4045
|
+
out.once("error", reject);
|
|
4046
|
+
});
|
|
4047
|
+
}
|
|
4048
|
+
};
|
|
4049
|
+
try {
|
|
4050
|
+
for await (const chunk of input) {
|
|
4051
|
+
buf += chunk;
|
|
4052
|
+
if (!started) {
|
|
4053
|
+
const idx = buf.search(/\S/);
|
|
4054
|
+
if (idx < 0) continue;
|
|
4055
|
+
if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
|
|
4056
|
+
buf = buf.slice(idx + 1);
|
|
4057
|
+
started = true;
|
|
4058
|
+
}
|
|
4059
|
+
while (true) {
|
|
4060
|
+
buf = buf.replace(/^\s*,?\s*/, "");
|
|
4061
|
+
if (!buf) break;
|
|
4062
|
+
if (buf[0] === "]") {
|
|
4063
|
+
buf = "";
|
|
4064
|
+
break;
|
|
4065
|
+
}
|
|
4066
|
+
const end = findJsonValueEnd(buf);
|
|
4067
|
+
if (end < 0) break;
|
|
4068
|
+
const value = JSON.parse(buf.slice(0, end));
|
|
4069
|
+
buf = buf.slice(end);
|
|
4070
|
+
const row = parseCacheRow(value);
|
|
4071
|
+
if (row) {
|
|
4072
|
+
await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
|
|
4073
|
+
`);
|
|
4074
|
+
count2 += 1;
|
|
4075
|
+
if (count2 % 50 === 0) {
|
|
4076
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
|
|
4081
|
+
throw new Error("embedding cache entry too large to migrate safely");
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
} finally {
|
|
4085
|
+
await new Promise((resolve2, reject) => {
|
|
4086
|
+
out.end(() => resolve2());
|
|
4087
|
+
out.once("error", reject);
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
return count2;
|
|
4091
|
+
}
|
|
4092
|
+
async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
|
|
4093
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
4094
|
+
try {
|
|
4095
|
+
const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
|
|
4096
|
+
await rename(tmpPath, jsonlPath);
|
|
4097
|
+
diskCacheEntryCount = count2;
|
|
4098
|
+
await writeCacheStat({
|
|
4099
|
+
version: 1,
|
|
4100
|
+
entryCount: count2,
|
|
4101
|
+
dimensions: cacheDimensions
|
|
4102
|
+
});
|
|
4103
|
+
return count2;
|
|
4104
|
+
} catch (err) {
|
|
4105
|
+
await unlink(tmpPath).catch(() => void 0);
|
|
4106
|
+
throw err;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
function convertArrayCacheToJsonl(filePath, jsonlPath) {
|
|
4110
|
+
return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
4111
|
+
}
|
|
4112
|
+
async function loadCacheJsonl(filePath) {
|
|
4113
|
+
const rl = createInterface({
|
|
4114
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
4115
|
+
crlfDelay: Infinity
|
|
4116
|
+
});
|
|
4117
|
+
let loaded = 0;
|
|
4118
|
+
let onDiskCount = 0;
|
|
4119
|
+
for await (const line of rl) {
|
|
4120
|
+
const trimmed = line.trim();
|
|
4121
|
+
if (!trimmed) continue;
|
|
4122
|
+
try {
|
|
4123
|
+
const row = parseCacheRow(JSON.parse(trimmed));
|
|
4124
|
+
if (!row) continue;
|
|
4125
|
+
onDiskCount += 1;
|
|
4126
|
+
cacheInsert(row.hash, row.vector, false);
|
|
4127
|
+
loaded = cache3.size;
|
|
4128
|
+
} catch {
|
|
4129
|
+
}
|
|
4130
|
+
if (onDiskCount % 250 === 0) {
|
|
4131
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
diskCacheEntryCount = onDiskCount;
|
|
4135
|
+
await writeCacheStat({
|
|
4136
|
+
version: 1,
|
|
4137
|
+
entryCount: onDiskCount,
|
|
4138
|
+
dimensions: cacheDimensions
|
|
4139
|
+
});
|
|
4140
|
+
return loaded;
|
|
4141
|
+
}
|
|
4142
|
+
async function countJsonlLines(filePath) {
|
|
4143
|
+
jsonlLineCountCalls += 1;
|
|
4144
|
+
try {
|
|
4145
|
+
const rl = createInterface({
|
|
4146
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
4147
|
+
crlfDelay: Infinity
|
|
4148
|
+
});
|
|
4149
|
+
let count2 = 0;
|
|
4150
|
+
for await (const line of rl) {
|
|
4151
|
+
if (line.trim()) count2 += 1;
|
|
4152
|
+
}
|
|
4153
|
+
return count2;
|
|
4154
|
+
} catch {
|
|
4155
|
+
return 0;
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
async function resolveOnDiskCount(jsonlPath) {
|
|
4159
|
+
if (diskCacheEntryCount > 0) return diskCacheEntryCount;
|
|
4160
|
+
const sidecar = await readCacheStat();
|
|
4161
|
+
if (sidecar) {
|
|
4162
|
+
diskCacheEntryCount = sidecar.entryCount;
|
|
4163
|
+
if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
|
|
4164
|
+
return sidecar.entryCount;
|
|
4165
|
+
}
|
|
4166
|
+
try {
|
|
4167
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
4168
|
+
} catch {
|
|
4169
|
+
return 0;
|
|
4170
|
+
}
|
|
4171
|
+
const counted = await countJsonlLines(jsonlPath);
|
|
4172
|
+
diskCacheEntryCount = counted;
|
|
4173
|
+
await writeCacheStat({
|
|
4174
|
+
version: 1,
|
|
4175
|
+
entryCount: counted,
|
|
4176
|
+
dimensions: cacheDimensions
|
|
4177
|
+
});
|
|
4178
|
+
return counted;
|
|
4179
|
+
}
|
|
3823
4180
|
async function loadDiskCache2() {
|
|
3824
|
-
if (diskCacheLoaded) return;
|
|
4181
|
+
if (diskCacheLoaded) return cache3.size;
|
|
4182
|
+
let loaded = 0;
|
|
3825
4183
|
try {
|
|
3826
|
-
const
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
4184
|
+
const jsonlPath = cacheJsonlFile();
|
|
4185
|
+
let hasJsonl = false;
|
|
4186
|
+
try {
|
|
4187
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
4188
|
+
hasJsonl = true;
|
|
4189
|
+
} catch {
|
|
4190
|
+
hasJsonl = false;
|
|
4191
|
+
}
|
|
4192
|
+
if (hasJsonl) {
|
|
4193
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
4194
|
+
} else {
|
|
4195
|
+
const filePath = cacheFile();
|
|
4196
|
+
await stat(filePath);
|
|
4197
|
+
await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
4198
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
4199
|
+
}
|
|
4200
|
+
diskCacheLoadCount += 1;
|
|
4201
|
+
console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
|
|
3830
4202
|
} catch {
|
|
3831
4203
|
}
|
|
3832
4204
|
diskCacheLoaded = true;
|
|
4205
|
+
return loaded;
|
|
3833
4206
|
}
|
|
3834
4207
|
function startDiskCacheLoad() {
|
|
3835
4208
|
if (diskCacheLoaded || diskCacheLoadPromise) return;
|
|
3836
|
-
diskCacheLoadPromise = loadDiskCache2().catch(() => {
|
|
4209
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
3837
4210
|
});
|
|
3838
4211
|
}
|
|
3839
4212
|
async function ensureDiskCacheLoaded() {
|
|
3840
|
-
if (diskCacheLoaded) return;
|
|
3841
|
-
if (diskCacheLoadPromise) {
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
}
|
|
3845
|
-
await
|
|
4213
|
+
if (diskCacheLoaded) return cache3.size;
|
|
4214
|
+
if (!diskCacheLoadPromise) {
|
|
4215
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
await diskCacheLoadPromise;
|
|
4219
|
+
return cache3.size;
|
|
4220
|
+
}
|
|
4221
|
+
function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
4222
|
+
return diskCacheLoadCount;
|
|
4223
|
+
}
|
|
4224
|
+
function resetApiEmbeddingCacheForTests() {
|
|
4225
|
+
cache3.clear();
|
|
4226
|
+
cachePayloadBytes = 0;
|
|
4227
|
+
cacheDimensions = null;
|
|
4228
|
+
diskCacheEntryCount = 0;
|
|
4229
|
+
diskCacheLoaded = false;
|
|
4230
|
+
diskCacheLoadPromise = null;
|
|
4231
|
+
diskCacheLoadCount = 0;
|
|
4232
|
+
diskCacheDirty2 = false;
|
|
4233
|
+
jsonlLineCountCalls = 0;
|
|
4234
|
+
}
|
|
4235
|
+
function seedApiEmbeddingCacheForTests(entries) {
|
|
4236
|
+
cache3.clear();
|
|
4237
|
+
cachePayloadBytes = 0;
|
|
4238
|
+
cacheDimensions = null;
|
|
4239
|
+
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
4240
|
+
diskCacheLoaded = true;
|
|
4241
|
+
diskCacheDirty2 = true;
|
|
4242
|
+
}
|
|
4243
|
+
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
4244
|
+
return cachePayloadBytes;
|
|
4245
|
+
}
|
|
4246
|
+
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
4247
|
+
return jsonlLineCountCalls;
|
|
4248
|
+
}
|
|
4249
|
+
function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
|
|
4250
|
+
return maxEntriesForDimensions(dimensions);
|
|
3846
4251
|
}
|
|
3847
4252
|
function dimsCacheKey(config) {
|
|
3848
4253
|
return [
|
|
@@ -3958,8 +4363,46 @@ async function saveDiskCacheNow() {
|
|
|
3958
4363
|
if (!diskCacheDirty2) return;
|
|
3959
4364
|
try {
|
|
3960
4365
|
await mkdir2(cacheDir(), { recursive: true });
|
|
3961
|
-
const
|
|
3962
|
-
await
|
|
4366
|
+
const jsonlPath = cacheJsonlFile();
|
|
4367
|
+
const existing = await resolveOnDiskCount(jsonlPath);
|
|
4368
|
+
if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
|
|
4369
|
+
console.error(
|
|
4370
|
+
`[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
|
|
4371
|
+
);
|
|
4372
|
+
diskCacheDirty2 = false;
|
|
4373
|
+
return;
|
|
4374
|
+
}
|
|
4375
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
4376
|
+
await new Promise((resolve2, reject) => {
|
|
4377
|
+
const out = createWriteStream(tmpPath);
|
|
4378
|
+
const entries = cache3.entries();
|
|
4379
|
+
const writeBatch = () => {
|
|
4380
|
+
for (let i2 = 0; i2 < 50; i2++) {
|
|
4381
|
+
const next = entries.next();
|
|
4382
|
+
if (next.done) {
|
|
4383
|
+
out.end();
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
const [hash4, vector] = next.value;
|
|
4387
|
+
if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
|
|
4388
|
+
`)) {
|
|
4389
|
+
out.once("drain", writeBatch);
|
|
4390
|
+
return;
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4393
|
+
setImmediate(writeBatch);
|
|
4394
|
+
};
|
|
4395
|
+
out.on("finish", resolve2);
|
|
4396
|
+
out.on("error", reject);
|
|
4397
|
+
writeBatch();
|
|
4398
|
+
});
|
|
4399
|
+
await rename(tmpPath, jsonlPath);
|
|
4400
|
+
diskCacheEntryCount = cache3.size;
|
|
4401
|
+
await writeCacheStat({
|
|
4402
|
+
version: 1,
|
|
4403
|
+
entryCount: cache3.size,
|
|
4404
|
+
dimensions: cacheDimensions
|
|
4405
|
+
});
|
|
3963
4406
|
diskCacheDirty2 = false;
|
|
3964
4407
|
} catch {
|
|
3965
4408
|
}
|
|
@@ -3973,12 +4416,7 @@ function scheduleDiskSave() {
|
|
|
3973
4416
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
3974
4417
|
}
|
|
3975
4418
|
function cacheSet(hash4, value) {
|
|
3976
|
-
|
|
3977
|
-
const firstKey = cache3.keys().next().value;
|
|
3978
|
-
if (firstKey !== void 0) cache3.delete(firstKey);
|
|
3979
|
-
}
|
|
3980
|
-
cache3.set(hash4, value);
|
|
3981
|
-
diskCacheDirty2 = true;
|
|
4419
|
+
cacheInsert(hash4, value, true);
|
|
3982
4420
|
}
|
|
3983
4421
|
function resolveEnvEmbeddingApiKey() {
|
|
3984
4422
|
return process.env.MEMORIX_EMBEDDING_API_KEY;
|
|
@@ -4097,18 +4535,27 @@ async function waitForRetry(waitMs, signal) {
|
|
|
4097
4535
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
4098
4536
|
});
|
|
4099
4537
|
}
|
|
4100
|
-
var cache3,
|
|
4538
|
+
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;
|
|
4101
4539
|
var init_api_provider = __esm({
|
|
4102
4540
|
"src/embedding/api-provider.ts"() {
|
|
4103
4541
|
"use strict";
|
|
4104
4542
|
init_esm_shims();
|
|
4105
4543
|
init_provider();
|
|
4106
4544
|
cache3 = /* @__PURE__ */ new Map();
|
|
4107
|
-
|
|
4545
|
+
MAX_CACHE_ENTRIES = 2e5;
|
|
4546
|
+
MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
4547
|
+
BYTES_PER_DIM = 8;
|
|
4548
|
+
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
4549
|
+
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
4550
|
+
cachePayloadBytes = 0;
|
|
4551
|
+
cacheDimensions = null;
|
|
4552
|
+
diskCacheEntryCount = 0;
|
|
4108
4553
|
diskCacheDirty2 = false;
|
|
4109
4554
|
diskSaveTimer = null;
|
|
4110
4555
|
diskCacheLoaded = false;
|
|
4111
4556
|
diskCacheLoadPromise = null;
|
|
4557
|
+
diskCacheLoadCount = 0;
|
|
4558
|
+
jsonlLineCountCalls = 0;
|
|
4112
4559
|
MAX_INPUT_CHARS = 32e3;
|
|
4113
4560
|
MAX_CONCURRENCY = 4;
|
|
4114
4561
|
DISK_SAVE_DEBOUNCE_MS = 5e3;
|
|
@@ -4166,7 +4613,9 @@ var init_api_provider = __esm({
|
|
|
4166
4613
|
if (config.requestedDimensions) {
|
|
4167
4614
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
4168
4615
|
}
|
|
4169
|
-
|
|
4616
|
+
if (allowNetworkProbe) {
|
|
4617
|
+
startDiskCacheLoad();
|
|
4618
|
+
}
|
|
4170
4619
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
4171
4620
|
}
|
|
4172
4621
|
static resolveConfig() {
|
|
@@ -4442,6 +4891,7 @@ __export(provider_exports, {
|
|
|
4442
4891
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
4443
4892
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
4444
4893
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
4894
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
4445
4895
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
4446
4896
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
4447
4897
|
resetProvider: () => resetProvider,
|
|
@@ -4503,7 +4953,12 @@ function warnOnce(message) {
|
|
|
4503
4953
|
lastEmbeddingWarningAt = now3;
|
|
4504
4954
|
console.error(message);
|
|
4505
4955
|
}
|
|
4956
|
+
function embeddingModeToken() {
|
|
4957
|
+
return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
|
|
4958
|
+
}
|
|
4506
4959
|
function getEmbeddingMode2() {
|
|
4960
|
+
const token = embeddingModeToken();
|
|
4961
|
+
if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
|
|
4507
4962
|
try {
|
|
4508
4963
|
let cfg;
|
|
4509
4964
|
try {
|
|
@@ -4512,10 +4967,17 @@ function getEmbeddingMode2() {
|
|
|
4512
4967
|
cfg = (init_config(), __toCommonJS(config_exports));
|
|
4513
4968
|
}
|
|
4514
4969
|
const { getEmbeddingMode: cfgMode } = cfg;
|
|
4515
|
-
|
|
4970
|
+
cachedEmbeddingMode = cfgMode();
|
|
4971
|
+
cachedEmbeddingModeToken = token;
|
|
4972
|
+
return cachedEmbeddingMode;
|
|
4516
4973
|
} catch {
|
|
4517
|
-
|
|
4518
|
-
|
|
4974
|
+
if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
|
|
4975
|
+
cachedEmbeddingMode = token;
|
|
4976
|
+
cachedEmbeddingModeToken = token;
|
|
4977
|
+
return token;
|
|
4978
|
+
}
|
|
4979
|
+
cachedEmbeddingMode = "off";
|
|
4980
|
+
cachedEmbeddingModeToken = token;
|
|
4519
4981
|
return "off";
|
|
4520
4982
|
}
|
|
4521
4983
|
}
|
|
@@ -4549,13 +5011,36 @@ function isTemporaryEmbeddingFailure(error) {
|
|
|
4549
5011
|
if (!(error instanceof Error)) return false;
|
|
4550
5012
|
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);
|
|
4551
5013
|
}
|
|
5014
|
+
function classifyEmbeddingFailure(error) {
|
|
5015
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5016
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
5017
|
+
return "authorization";
|
|
5018
|
+
}
|
|
5019
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
5020
|
+
return "billing";
|
|
5021
|
+
}
|
|
5022
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
5023
|
+
return "timeout";
|
|
5024
|
+
}
|
|
5025
|
+
return "upstream";
|
|
5026
|
+
}
|
|
4552
5027
|
function markTemporaryFailure(reason) {
|
|
4553
5028
|
provider = null;
|
|
4554
5029
|
initPromise = null;
|
|
4555
5030
|
lastInitWasTemporaryFailure = true;
|
|
4556
5031
|
lastFailureTimestamp = Date.now();
|
|
5032
|
+
lastEmbeddingFailure = {
|
|
5033
|
+
kind: classifyEmbeddingFailure(reason),
|
|
5034
|
+
occurredAt: lastFailureTimestamp
|
|
5035
|
+
};
|
|
4557
5036
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
4558
|
-
warnOnce(`[memorix] Embedding provider
|
|
5037
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
5038
|
+
}
|
|
5039
|
+
function markProviderReady(candidate) {
|
|
5040
|
+
provider = wrapProvider(candidate);
|
|
5041
|
+
lastInitWasTemporaryFailure = false;
|
|
5042
|
+
lastEmbeddingFailure = null;
|
|
5043
|
+
return provider;
|
|
4559
5044
|
}
|
|
4560
5045
|
async function createFastEmbedProvider() {
|
|
4561
5046
|
try {
|
|
@@ -4671,35 +5156,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
4671
5156
|
if (mode === "fastembed") {
|
|
4672
5157
|
const initialized = await createFastEmbedProvider();
|
|
4673
5158
|
if (!initialized) return null;
|
|
4674
|
-
provider =
|
|
5159
|
+
provider = markProviderReady(initialized);
|
|
4675
5160
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4676
5161
|
return provider;
|
|
4677
5162
|
}
|
|
4678
5163
|
if (mode === "transformers") {
|
|
4679
5164
|
const initialized = await createTransformersProvider();
|
|
4680
5165
|
if (!initialized) return null;
|
|
4681
|
-
provider =
|
|
5166
|
+
provider = markProviderReady(initialized);
|
|
4682
5167
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4683
5168
|
return provider;
|
|
4684
5169
|
}
|
|
4685
5170
|
if (mode === "api") {
|
|
4686
5171
|
const initialized = await createAPIProvider(options);
|
|
4687
5172
|
if (!initialized) return null;
|
|
4688
|
-
provider =
|
|
5173
|
+
provider = markProviderReady(initialized);
|
|
4689
5174
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4690
5175
|
return provider;
|
|
4691
5176
|
}
|
|
4692
5177
|
if (hasAPIEmbeddingConfig()) {
|
|
4693
5178
|
const initialized = await createAPIProvider(options);
|
|
4694
5179
|
if (initialized) {
|
|
4695
|
-
provider =
|
|
5180
|
+
provider = markProviderReady(initialized);
|
|
4696
5181
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4697
5182
|
return provider;
|
|
4698
5183
|
}
|
|
4699
5184
|
}
|
|
4700
5185
|
const localFallback = await createLocalFallbackProvider();
|
|
4701
5186
|
if (localFallback) {
|
|
4702
|
-
provider =
|
|
5187
|
+
provider = markProviderReady(localFallback);
|
|
4703
5188
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4704
5189
|
return provider;
|
|
4705
5190
|
}
|
|
@@ -4721,18 +5206,42 @@ async function isVectorSearchAvailable() {
|
|
|
4721
5206
|
const p = await getEmbeddingProvider();
|
|
4722
5207
|
return p !== null;
|
|
4723
5208
|
}
|
|
5209
|
+
function getEmbeddingRuntimeHealth() {
|
|
5210
|
+
const mode = getEmbeddingMode2();
|
|
5211
|
+
if (mode === "off") return { status: "disabled" };
|
|
5212
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
5213
|
+
return {
|
|
5214
|
+
status: "degraded",
|
|
5215
|
+
failure: {
|
|
5216
|
+
kind: lastEmbeddingFailure.kind,
|
|
5217
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
5218
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
5219
|
+
}
|
|
5220
|
+
};
|
|
5221
|
+
}
|
|
5222
|
+
if (provider) {
|
|
5223
|
+
return {
|
|
5224
|
+
status: "ready",
|
|
5225
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
5226
|
+
};
|
|
5227
|
+
}
|
|
5228
|
+
return { status: "uninitialized" };
|
|
5229
|
+
}
|
|
4724
5230
|
function isEmbeddingExplicitlyDisabled() {
|
|
4725
5231
|
return getEmbeddingMode2() === "off";
|
|
4726
5232
|
}
|
|
4727
5233
|
function resetProvider() {
|
|
4728
5234
|
provider = null;
|
|
4729
5235
|
initPromise = null;
|
|
5236
|
+
cachedEmbeddingMode = null;
|
|
5237
|
+
cachedEmbeddingModeToken = null;
|
|
4730
5238
|
lastInitWasTemporaryFailure = false;
|
|
4731
5239
|
lastFailureTimestamp = 0;
|
|
5240
|
+
lastEmbeddingFailure = null;
|
|
4732
5241
|
lastEmbeddingWarning = "";
|
|
4733
5242
|
lastEmbeddingWarningAt = 0;
|
|
4734
5243
|
}
|
|
4735
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
5244
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
4736
5245
|
var init_provider = __esm({
|
|
4737
5246
|
"src/embedding/provider.ts"() {
|
|
4738
5247
|
"use strict";
|
|
@@ -4756,7 +5265,10 @@ var init_provider = __esm({
|
|
|
4756
5265
|
MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
|
|
4757
5266
|
provider = null;
|
|
4758
5267
|
initPromise = null;
|
|
5268
|
+
cachedEmbeddingMode = null;
|
|
5269
|
+
cachedEmbeddingModeToken = null;
|
|
4759
5270
|
lastInitWasTemporaryFailure = false;
|
|
5271
|
+
lastEmbeddingFailure = null;
|
|
4760
5272
|
lastEmbeddingWarning = "";
|
|
4761
5273
|
lastEmbeddingWarningAt = 0;
|
|
4762
5274
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -6512,6 +7024,9 @@ async function attachCachedVectors(database, candidates) {
|
|
|
6512
7024
|
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
6513
7025
|
} catch {
|
|
6514
7026
|
}
|
|
7027
|
+
if (index % 50 === 0) {
|
|
7028
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7029
|
+
}
|
|
6515
7030
|
}
|
|
6516
7031
|
}
|
|
6517
7032
|
async function hydrateIndex(observations2, options = {}) {
|
|
@@ -6562,17 +7077,24 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6562
7077
|
await insert2(database, doc);
|
|
6563
7078
|
rememberObservationDoc(doc);
|
|
6564
7079
|
inserted++;
|
|
7080
|
+
if (inserted % 50 === 0) {
|
|
7081
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7082
|
+
}
|
|
7083
|
+
if (inserted % 200 === 0) {
|
|
7084
|
+
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
7085
|
+
}
|
|
6565
7086
|
} catch {
|
|
6566
7087
|
}
|
|
6567
7088
|
}
|
|
6568
|
-
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
7089
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6569
7090
|
}) : null;
|
|
6570
7091
|
return inserted;
|
|
6571
7092
|
}
|
|
6572
|
-
async function hydrateIndexForStartup(observations2) {
|
|
7093
|
+
async function hydrateIndexForStartup(observations2, options = {}) {
|
|
6573
7094
|
return hydrateIndex(observations2, {
|
|
6574
7095
|
allowNetworkProbe: false,
|
|
6575
|
-
deferCachedVectors: true
|
|
7096
|
+
deferCachedVectors: true,
|
|
7097
|
+
skipCachedVectors: options.skipCachedVectors
|
|
6576
7098
|
});
|
|
6577
7099
|
}
|
|
6578
7100
|
function getDeferredCachedVectorHydration() {
|
|
@@ -9895,7 +10417,7 @@ __export(asset_store_exports, {
|
|
|
9895
10417
|
removeMediaAsset: () => removeMediaAsset
|
|
9896
10418
|
});
|
|
9897
10419
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
9898
|
-
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
10420
|
+
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
9899
10421
|
import path13 from "path";
|
|
9900
10422
|
function mediaRoot(dataDir) {
|
|
9901
10423
|
return path13.resolve(dataDir, "media");
|
|
@@ -9985,7 +10507,7 @@ async function persistFile(source, destination) {
|
|
|
9985
10507
|
const temporary = `${destination}.${randomUUID5()}.tmp`;
|
|
9986
10508
|
try {
|
|
9987
10509
|
await copyFile(source, temporary);
|
|
9988
|
-
await
|
|
10510
|
+
await rename2(temporary, destination);
|
|
9989
10511
|
return true;
|
|
9990
10512
|
} catch (error) {
|
|
9991
10513
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -10001,7 +10523,7 @@ async function persistBuffer(bytes, destination) {
|
|
|
10001
10523
|
const temporary = `${destination}.${randomUUID5()}.tmp`;
|
|
10002
10524
|
try {
|
|
10003
10525
|
await writeFile3(temporary, bytes, { flag: "wx" });
|
|
10004
|
-
await
|
|
10526
|
+
await rename2(temporary, destination);
|
|
10005
10527
|
return true;
|
|
10006
10528
|
} catch (error) {
|
|
10007
10529
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -10041,11 +10563,11 @@ async function importKnownMedia(input) {
|
|
|
10041
10563
|
async function importMediaFile(input) {
|
|
10042
10564
|
const maxBytes = input.maxBytes ?? DEFAULT_MAX_MEDIA_BYTES;
|
|
10043
10565
|
const source = path13.resolve(input.filePath);
|
|
10044
|
-
const
|
|
10045
|
-
if (
|
|
10046
|
-
if (!
|
|
10047
|
-
if (
|
|
10048
|
-
if (
|
|
10566
|
+
const stat5 = await lstat(source);
|
|
10567
|
+
if (stat5.isSymbolicLink()) throw new Error("Media import does not follow symbolic links");
|
|
10568
|
+
if (!stat5.isFile()) throw new Error("Media import accepts regular files only");
|
|
10569
|
+
if (stat5.size <= 0) throw new Error("Media import rejects empty files");
|
|
10570
|
+
if (stat5.size > maxBytes) throw new Error(`Media file is too large (${stat5.size} bytes; limit ${maxBytes})`);
|
|
10049
10571
|
const header = await readHeader(source);
|
|
10050
10572
|
const detected = detectMedia(header);
|
|
10051
10573
|
const sha256 = await sha256File(source);
|
|
@@ -10053,7 +10575,7 @@ async function importMediaFile(input) {
|
|
|
10053
10575
|
dataDir: input.dataDir,
|
|
10054
10576
|
projectId: input.projectId,
|
|
10055
10577
|
sha256,
|
|
10056
|
-
bytes:
|
|
10578
|
+
bytes: stat5.size,
|
|
10057
10579
|
detected,
|
|
10058
10580
|
persist: (destination) => persistFile(source, destination),
|
|
10059
10581
|
sourceKind: input.sourceKind ?? "import",
|
|
@@ -10100,13 +10622,13 @@ async function removeMediaAsset(input) {
|
|
|
10100
10622
|
const assetPath = getMediaAssetPath(input.dataDir, asset);
|
|
10101
10623
|
const stagedPath = deletionStagingPath(mediaRoot(input.dataDir), asset.id);
|
|
10102
10624
|
await mkdir3(path13.dirname(stagedPath), { recursive: true });
|
|
10103
|
-
await
|
|
10625
|
+
await rename2(assetPath, stagedPath);
|
|
10104
10626
|
let detachedLinks;
|
|
10105
10627
|
try {
|
|
10106
10628
|
({ detachedLinks } = store.removeAssetMetadata(input.projectId, asset.id, { force: input.force }));
|
|
10107
10629
|
} catch (error) {
|
|
10108
10630
|
try {
|
|
10109
|
-
await
|
|
10631
|
+
await rename2(stagedPath, assetPath);
|
|
10110
10632
|
} catch (restoreError) {
|
|
10111
10633
|
const original = error instanceof Error ? error.message : String(error);
|
|
10112
10634
|
const restore = restoreError instanceof Error ? restoreError.message : String(restoreError);
|
|
@@ -10142,7 +10664,7 @@ async function cleanupMediaTrash(dataDir) {
|
|
|
10142
10664
|
} catch (error) {
|
|
10143
10665
|
if (error?.code === "ENOENT") continue;
|
|
10144
10666
|
try {
|
|
10145
|
-
pendingBytes += (await
|
|
10667
|
+
pendingBytes += (await stat2(candidate)).size;
|
|
10146
10668
|
} catch {
|
|
10147
10669
|
}
|
|
10148
10670
|
pendingFiles += 1;
|
|
@@ -61224,9 +61746,9 @@ var require_node_domexception = __commonJS({
|
|
|
61224
61746
|
});
|
|
61225
61747
|
|
|
61226
61748
|
// node_modules/fetch-blob/from.js
|
|
61227
|
-
import { statSync as statSync2, createReadStream, promises as fs10 } from "fs";
|
|
61749
|
+
import { statSync as statSync2, createReadStream as createReadStream2, promises as fs10 } from "fs";
|
|
61228
61750
|
import { basename } from "path";
|
|
61229
|
-
var import_node_domexception,
|
|
61751
|
+
var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
61230
61752
|
var init_from = __esm({
|
|
61231
61753
|
"node_modules/fetch-blob/from.js"() {
|
|
61232
61754
|
"use strict";
|
|
@@ -61234,23 +61756,23 @@ var init_from = __esm({
|
|
|
61234
61756
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
61235
61757
|
init_file();
|
|
61236
61758
|
init_fetch_blob();
|
|
61237
|
-
({ stat:
|
|
61759
|
+
({ stat: stat3 } = fs10);
|
|
61238
61760
|
blobFromSync = (path34, type) => fromBlob(statSync2(path34), path34, type);
|
|
61239
|
-
blobFrom = (path34, type) =>
|
|
61240
|
-
fileFrom = (path34, type) =>
|
|
61761
|
+
blobFrom = (path34, type) => stat3(path34).then((stat5) => fromBlob(stat5, path34, type));
|
|
61762
|
+
fileFrom = (path34, type) => stat3(path34).then((stat5) => fromFile(stat5, path34, type));
|
|
61241
61763
|
fileFromSync = (path34, type) => fromFile(statSync2(path34), path34, type);
|
|
61242
|
-
fromBlob = (
|
|
61764
|
+
fromBlob = (stat5, path34, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
61243
61765
|
path: path34,
|
|
61244
|
-
size:
|
|
61245
|
-
lastModified:
|
|
61766
|
+
size: stat5.size,
|
|
61767
|
+
lastModified: stat5.mtimeMs,
|
|
61246
61768
|
start: 0
|
|
61247
61769
|
})], { type });
|
|
61248
|
-
fromFile = (
|
|
61770
|
+
fromFile = (stat5, path34, type = "") => new file_default([new BlobDataItem({
|
|
61249
61771
|
path: path34,
|
|
61250
|
-
size:
|
|
61251
|
-
lastModified:
|
|
61772
|
+
size: stat5.size,
|
|
61773
|
+
lastModified: stat5.mtimeMs,
|
|
61252
61774
|
start: 0
|
|
61253
|
-
})], basename(path34), { type, lastModified:
|
|
61775
|
+
})], basename(path34), { type, lastModified: stat5.mtimeMs });
|
|
61254
61776
|
BlobDataItem = class _BlobDataItem {
|
|
61255
61777
|
#path;
|
|
61256
61778
|
#start;
|
|
@@ -61273,11 +61795,11 @@ var init_from = __esm({
|
|
|
61273
61795
|
});
|
|
61274
61796
|
}
|
|
61275
61797
|
async *stream() {
|
|
61276
|
-
const { mtimeMs } = await
|
|
61798
|
+
const { mtimeMs } = await stat3(this.#path);
|
|
61277
61799
|
if (mtimeMs > this.lastModified) {
|
|
61278
61800
|
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");
|
|
61279
61801
|
}
|
|
61280
|
-
yield*
|
|
61802
|
+
yield* createReadStream2(this.#path, {
|
|
61281
61803
|
start: this.#start,
|
|
61282
61804
|
end: this.#start + this.size - 1
|
|
61283
61805
|
});
|
|
@@ -76907,7 +77429,7 @@ var init_wrapper = __esm({
|
|
|
76907
77429
|
});
|
|
76908
77430
|
|
|
76909
77431
|
// node_modules/@google/genai/dist/node/index.mjs
|
|
76910
|
-
import { createWriteStream } from "fs";
|
|
77432
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
76911
77433
|
import * as fs11 from "fs/promises";
|
|
76912
77434
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
76913
77435
|
import { Readable } from "stream";
|
|
@@ -94187,7 +94709,7 @@ ${underline}`);
|
|
|
94187
94709
|
if (params.downloadPath) {
|
|
94188
94710
|
const response = await downloadFile(params, apiClient);
|
|
94189
94711
|
if (response instanceof HttpResponse) {
|
|
94190
|
-
const writer =
|
|
94712
|
+
const writer = createWriteStream2(params.downloadPath);
|
|
94191
94713
|
const body = Readable.fromWeb(response.responseInternal.body);
|
|
94192
94714
|
body.pipe(writer);
|
|
94193
94715
|
await finished(writer);
|
|
@@ -144221,7 +144743,7 @@ function explainRetention(doc, referenceTime) {
|
|
|
144221
144743
|
};
|
|
144222
144744
|
}
|
|
144223
144745
|
function toRetentionDocument(obs, accessMap) {
|
|
144224
|
-
const
|
|
144746
|
+
const access3 = accessMap?.get(obs.id);
|
|
144225
144747
|
return {
|
|
144226
144748
|
id: `obs-${obs.id}`,
|
|
144227
144749
|
observationId: obs.id,
|
|
@@ -144235,8 +144757,8 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
144235
144757
|
tokens: obs.tokens,
|
|
144236
144758
|
createdAt: obs.createdAt,
|
|
144237
144759
|
projectId: obs.projectId,
|
|
144238
|
-
accessCount:
|
|
144239
|
-
lastAccessedAt:
|
|
144760
|
+
accessCount: access3?.accessCount ?? 0,
|
|
144761
|
+
lastAccessedAt: access3?.lastAccessedAt ?? "",
|
|
144240
144762
|
status: obs.status ?? "active",
|
|
144241
144763
|
source: obs.source ?? "agent",
|
|
144242
144764
|
sourceDetail: obs.sourceDetail ?? "",
|
|
@@ -146350,7 +146872,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
146350
146872
|
const rel = normalizeCodePath(relative(projectRoot, abs));
|
|
146351
146873
|
try {
|
|
146352
146874
|
const text2 = readFileSync6(abs, "utf-8");
|
|
146353
|
-
const
|
|
146875
|
+
const stat5 = fileStat ?? statSync3(abs);
|
|
146354
146876
|
const file = {
|
|
146355
146877
|
id: makeCodeFileId(projectId, rel),
|
|
146356
146878
|
projectId,
|
|
@@ -146359,8 +146881,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
146359
146881
|
contentHash: hashText(text2),
|
|
146360
146882
|
// Preserve filesystem precision. Rounding here made same-size edits in
|
|
146361
146883
|
// a short Windows timestamp window indistinguishable to refresh.
|
|
146362
|
-
mtimeMs: Number(
|
|
146363
|
-
sizeBytes: Number(
|
|
146884
|
+
mtimeMs: Number(stat5.mtimeMs),
|
|
146885
|
+
sizeBytes: Number(stat5.size),
|
|
146364
146886
|
indexedAt
|
|
146365
146887
|
};
|
|
146366
146888
|
return {
|
|
@@ -146387,18 +146909,18 @@ async function indexProjectLite(options) {
|
|
|
146387
146909
|
let skippedOversizedFiles = 0;
|
|
146388
146910
|
let unreadableFiles = 0;
|
|
146389
146911
|
for (const abs of paths) {
|
|
146390
|
-
let
|
|
146912
|
+
let stat5;
|
|
146391
146913
|
try {
|
|
146392
|
-
|
|
146914
|
+
stat5 = statSync3(abs);
|
|
146393
146915
|
} catch {
|
|
146394
146916
|
unreadableFiles++;
|
|
146395
146917
|
continue;
|
|
146396
146918
|
}
|
|
146397
|
-
if (Number(
|
|
146919
|
+
if (Number(stat5.size) > maxFileBytes) {
|
|
146398
146920
|
skippedOversizedFiles++;
|
|
146399
146921
|
continue;
|
|
146400
146922
|
}
|
|
146401
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146923
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
146402
146924
|
if (!attempt.delta) {
|
|
146403
146925
|
if (attempt.unreadable) unreadableFiles++;
|
|
146404
146926
|
continue;
|
|
@@ -146426,16 +146948,16 @@ async function refreshProjectLite(store, options) {
|
|
|
146426
146948
|
for (const abs of paths) {
|
|
146427
146949
|
const rel = normalizeCodePath(relative(options.projectRoot, abs));
|
|
146428
146950
|
seenPaths.add(rel);
|
|
146429
|
-
let
|
|
146951
|
+
let stat5;
|
|
146430
146952
|
try {
|
|
146431
|
-
|
|
146953
|
+
stat5 = statSync3(abs);
|
|
146432
146954
|
} catch {
|
|
146433
146955
|
unreadableFiles++;
|
|
146434
146956
|
continue;
|
|
146435
146957
|
}
|
|
146436
146958
|
const existing = existingByPath.get(rel);
|
|
146437
|
-
const mtimeMs = Number(
|
|
146438
|
-
const sizeBytes = Number(
|
|
146959
|
+
const mtimeMs = Number(stat5.mtimeMs);
|
|
146960
|
+
const sizeBytes = Number(stat5.size);
|
|
146439
146961
|
if (sizeBytes > maxFileBytes) {
|
|
146440
146962
|
skippedOversizedFiles++;
|
|
146441
146963
|
if (existing) oversizedExistingFileIds.add(existing.id);
|
|
@@ -146445,7 +146967,7 @@ async function refreshProjectLite(store, options) {
|
|
|
146445
146967
|
unchangedFiles++;
|
|
146446
146968
|
continue;
|
|
146447
146969
|
}
|
|
146448
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146970
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
146449
146971
|
if (!attempt.delta) {
|
|
146450
146972
|
if (attempt.unreadable) unreadableFiles++;
|
|
146451
146973
|
continue;
|
|
@@ -150193,12 +150715,25 @@ __export(observations_exports, {
|
|
|
150193
150715
|
prepareSearchIndex: () => prepareSearchIndex,
|
|
150194
150716
|
probeSearchIndex: () => probeSearchIndex,
|
|
150195
150717
|
reindexObservations: () => reindexObservations,
|
|
150718
|
+
resetObservationRuntime: () => resetObservationRuntime,
|
|
150196
150719
|
resolveObservations: () => resolveObservations,
|
|
150197
150720
|
storeObservation: () => storeObservation,
|
|
150198
150721
|
suggestTopicKey: () => suggestTopicKey,
|
|
150199
150722
|
updateObservationAdmission: () => updateObservationAdmission,
|
|
150200
150723
|
withFreshObservations: () => withFreshObservations
|
|
150201
150724
|
});
|
|
150725
|
+
function resetObservationRuntime() {
|
|
150726
|
+
observations = [];
|
|
150727
|
+
nextId = 1;
|
|
150728
|
+
projectDir = null;
|
|
150729
|
+
searchIndexPrepared = false;
|
|
150730
|
+
corpusLoaded = false;
|
|
150731
|
+
loadedObservationStore = null;
|
|
150732
|
+
embeddingWriteMode = "background";
|
|
150733
|
+
embeddingWorkerProjectRoot = void 0;
|
|
150734
|
+
vectorMissingIds.clear();
|
|
150735
|
+
vectorSchemaUpgradePromise = null;
|
|
150736
|
+
}
|
|
150202
150737
|
function logEmbeddingFailureOnce(key, message) {
|
|
150203
150738
|
const now3 = Date.now();
|
|
150204
150739
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
@@ -150307,23 +150842,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
|
150307
150842
|
async function initObservations(dir, options = {}) {
|
|
150308
150843
|
embeddingWriteMode = options.embeddingWriteMode ?? "background";
|
|
150309
150844
|
embeddingWorkerProjectRoot = options.projectRoot;
|
|
150310
|
-
if (projectDir === dir) return;
|
|
150311
150845
|
await initObservationStore(dir);
|
|
150312
150846
|
const store = getObservationStore();
|
|
150313
|
-
|
|
150847
|
+
if (projectDir === dir && loadedObservationStore === store) {
|
|
150848
|
+
if (options.skipCorpusLoad || corpusLoaded) return;
|
|
150849
|
+
}
|
|
150314
150850
|
nextId = await store.loadIdCounter();
|
|
150315
150851
|
projectDir = dir;
|
|
150316
150852
|
searchIndexPrepared = false;
|
|
150317
150853
|
vectorSchemaUpgradePromise = null;
|
|
150854
|
+
if (options.skipCorpusLoad) {
|
|
150855
|
+
observations = [];
|
|
150856
|
+
corpusLoaded = false;
|
|
150857
|
+
loadedObservationStore = store;
|
|
150858
|
+
return;
|
|
150859
|
+
}
|
|
150860
|
+
observations = await store.loadAll();
|
|
150861
|
+
corpusLoaded = true;
|
|
150862
|
+
loadedObservationStore = store;
|
|
150318
150863
|
}
|
|
150319
150864
|
function scheduleObservationEmbedding(input) {
|
|
150320
150865
|
const { observationId: observationId2, projectId, searchableText, doc } = input;
|
|
150321
150866
|
vectorMissingIds.add(observationId2);
|
|
150867
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
150868
|
+
vectorMissingIds.delete(observationId2);
|
|
150869
|
+
return;
|
|
150870
|
+
}
|
|
150322
150871
|
if (embeddingWriteMode === "deferred") {
|
|
150323
|
-
if (isEmbeddingExplicitlyDisabled()) {
|
|
150324
|
-
vectorMissingIds.delete(observationId2);
|
|
150325
|
-
return;
|
|
150326
|
-
}
|
|
150327
150872
|
queueVectorBackfill(projectId, {
|
|
150328
150873
|
detachedWorker: true,
|
|
150329
150874
|
projectRoot: embeddingWorkerProjectRoot
|
|
@@ -150370,7 +150915,7 @@ function scheduleObservationEmbedding(input) {
|
|
|
150370
150915
|
});
|
|
150371
150916
|
}
|
|
150372
150917
|
async function ensureFreshObservations() {
|
|
150373
|
-
if (!projectDir) return false;
|
|
150918
|
+
if (!projectDir || !corpusLoaded) return false;
|
|
150374
150919
|
try {
|
|
150375
150920
|
const store = getObservationStore();
|
|
150376
150921
|
const wasStale = await store.ensureFresh();
|
|
@@ -150411,7 +150956,7 @@ async function storeObservation(input) {
|
|
|
150411
150956
|
});
|
|
150412
150957
|
if (safeAttachments) input = { ...input, attachments: safeAttachments };
|
|
150413
150958
|
await ensureFreshObservations();
|
|
150414
|
-
if (input.topicKey) {
|
|
150959
|
+
if (input.topicKey && corpusLoaded) {
|
|
150415
150960
|
const existing = observations.find(
|
|
150416
150961
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
150417
150962
|
);
|
|
@@ -150508,13 +151053,17 @@ async function storeObservation(input) {
|
|
|
150508
151053
|
});
|
|
150509
151054
|
observation.writeGeneration = store.getGeneration();
|
|
150510
151055
|
if (upsertedInsideLock || reloadCacheAfterCommit) {
|
|
150511
|
-
|
|
150512
|
-
|
|
150513
|
-
|
|
150514
|
-
|
|
151056
|
+
if (corpusLoaded) {
|
|
151057
|
+
observations = await store.loadAll();
|
|
151058
|
+
nextId = await store.loadIdCounter();
|
|
151059
|
+
if (upsertedInsideLock) {
|
|
151060
|
+
observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
|
|
151061
|
+
}
|
|
151062
|
+
} else {
|
|
151063
|
+
nextId = await store.loadIdCounter();
|
|
150515
151064
|
}
|
|
150516
151065
|
} else {
|
|
150517
|
-
observations.push(observation);
|
|
151066
|
+
if (corpusLoaded) observations.push(observation);
|
|
150518
151067
|
nextId = observation.id + 1;
|
|
150519
151068
|
}
|
|
150520
151069
|
if (upsertedInsideLock) return;
|
|
@@ -150582,7 +151131,9 @@ async function storeObservation(input) {
|
|
|
150582
151131
|
createdByAgentId: input.createdByAgentId ?? "",
|
|
150583
151132
|
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
150584
151133
|
};
|
|
150585
|
-
|
|
151134
|
+
if (corpusLoaded) {
|
|
151135
|
+
await insertObservation(doc);
|
|
151136
|
+
}
|
|
150586
151137
|
};
|
|
150587
151138
|
await assignAndPersist();
|
|
150588
151139
|
if (upsertedInsideLock) {
|
|
@@ -150944,9 +151495,11 @@ async function reindexObservations() {
|
|
|
150944
151495
|
searchIndexPrepared = true;
|
|
150945
151496
|
return count2;
|
|
150946
151497
|
}
|
|
150947
|
-
async function prepareSearchIndex() {
|
|
151498
|
+
async function prepareSearchIndex(options = {}) {
|
|
150948
151499
|
if (searchIndexPrepared) return 0;
|
|
150949
|
-
const count2 = await hydrateIndexForStartup(observations
|
|
151500
|
+
const count2 = await hydrateIndexForStartup(observations, {
|
|
151501
|
+
skipCachedVectors: options.skipCachedVectors
|
|
151502
|
+
});
|
|
150950
151503
|
if (count2 === 0) {
|
|
150951
151504
|
searchIndexPrepared = true;
|
|
150952
151505
|
return 0;
|
|
@@ -151138,7 +151691,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
151138
151691
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
151139
151692
|
};
|
|
151140
151693
|
}
|
|
151141
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
151694
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
151142
151695
|
var init_observations = __esm({
|
|
151143
151696
|
"src/memory/observations.ts"() {
|
|
151144
151697
|
"use strict";
|
|
@@ -151157,6 +151710,8 @@ var init_observations = __esm({
|
|
|
151157
151710
|
projectDir = null;
|
|
151158
151711
|
searchIndexPrepared = false;
|
|
151159
151712
|
embeddingWriteMode = "background";
|
|
151713
|
+
corpusLoaded = false;
|
|
151714
|
+
loadedObservationStore = null;
|
|
151160
151715
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
151161
151716
|
vectorBackfillRunning = false;
|
|
151162
151717
|
vectorSchemaUpgradePromise = null;
|
|
@@ -155352,9 +155907,9 @@ function existingLensCandidates(rootPath, lens) {
|
|
|
155352
155907
|
const absolute = path28.join(rootPath, candidate);
|
|
155353
155908
|
try {
|
|
155354
155909
|
if (!existsSync16(absolute)) continue;
|
|
155355
|
-
const
|
|
155356
|
-
if (
|
|
155357
|
-
if (
|
|
155910
|
+
const stat5 = statSync4(absolute);
|
|
155911
|
+
if (stat5.isFile()) out.push(candidate);
|
|
155912
|
+
if (stat5.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
|
|
155358
155913
|
} catch {
|
|
155359
155914
|
}
|
|
155360
155915
|
}
|
|
@@ -161174,13 +161729,13 @@ async function installHooks(agent, projectRoot, global2 = false) {
|
|
|
161174
161729
|
const agentPrev = prevFiles.filter((e2) => e2.agent === agent);
|
|
161175
161730
|
for (const entry of agentPrev) {
|
|
161176
161731
|
try {
|
|
161177
|
-
const { access:
|
|
161178
|
-
await
|
|
161732
|
+
const { access: access3, unlink: unlink3 } = await import("fs/promises");
|
|
161733
|
+
await access3(entry.path);
|
|
161179
161734
|
const basename5 = path32.basename(entry.path);
|
|
161180
161735
|
if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "CONTEXT.md") {
|
|
161181
161736
|
continue;
|
|
161182
161737
|
}
|
|
161183
|
-
await
|
|
161738
|
+
await unlink3(entry.path);
|
|
161184
161739
|
await removeFile2(projectRoot, entry.path);
|
|
161185
161740
|
} catch {
|
|
161186
161741
|
}
|
|
@@ -161700,11 +162255,11 @@ import path33 from "path";
|
|
|
161700
162255
|
function resolveGitDir(projectRoot) {
|
|
161701
162256
|
const dotGit = path33.join(projectRoot, ".git");
|
|
161702
162257
|
if (!existsSync19(dotGit)) return null;
|
|
161703
|
-
const
|
|
161704
|
-
if (
|
|
162258
|
+
const stat5 = statSync5(dotGit);
|
|
162259
|
+
if (stat5.isDirectory()) {
|
|
161705
162260
|
return dotGit;
|
|
161706
162261
|
}
|
|
161707
|
-
if (
|
|
162262
|
+
if (stat5.isFile()) {
|
|
161708
162263
|
try {
|
|
161709
162264
|
const content = readFileSync13(dotGit, "utf-8").trim();
|
|
161710
162265
|
const match2 = content.match(/^gitdir:\s*(.+)$/);
|
|
@@ -163848,8 +164403,8 @@ var RulesSyncer = class {
|
|
|
163848
164403
|
const dir = path25.dirname(pattern);
|
|
163849
164404
|
const fileGlob = path25.basename(pattern);
|
|
163850
164405
|
try {
|
|
163851
|
-
const
|
|
163852
|
-
if (!
|
|
164406
|
+
const stat5 = await fs17.stat(dir);
|
|
164407
|
+
if (!stat5.isDirectory()) {
|
|
163853
164408
|
try {
|
|
163854
164409
|
await fs17.access(pattern);
|
|
163855
164410
|
return [pattern];
|
|
@@ -165904,7 +166459,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165904
166459
|
};
|
|
165905
166460
|
const server = existingServer ?? new McpServer({
|
|
165906
166461
|
name: "memorix",
|
|
165907
|
-
version: true ? "1.6.
|
|
166462
|
+
version: true ? "1.6.2" : "1.0.1"
|
|
165908
166463
|
});
|
|
165909
166464
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165910
166465
|
server.registerTool = ((name, ...args) => {
|
|
@@ -170332,6 +170887,11 @@ var MemoryClient = class {
|
|
|
170332
170887
|
await this._oramaStore.resetDb();
|
|
170333
170888
|
} catch {
|
|
170334
170889
|
}
|
|
170890
|
+
try {
|
|
170891
|
+
const { closeDatabase: closeDatabase3 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
170892
|
+
closeDatabase3(this._dataDir);
|
|
170893
|
+
} catch {
|
|
170894
|
+
}
|
|
170335
170895
|
}
|
|
170336
170896
|
};
|
|
170337
170897
|
async function createMemoryClient(options) {
|
|
@@ -170343,11 +170903,8 @@ async function createMemoryClient(options) {
|
|
|
170343
170903
|
`[memorix-sdk] No Git repository found at "${projectRoot}". Memorix requires a Git-tracked project for identity resolution.`
|
|
170344
170904
|
);
|
|
170345
170905
|
}
|
|
170346
|
-
const
|
|
170347
|
-
const
|
|
170348
|
-
const dataDir = path34.join(os4.homedir(), ".memorix", "data", project.id.replace(/\//g, path34.sep));
|
|
170349
|
-
const fs22 = await import("fs");
|
|
170350
|
-
fs22.mkdirSync(dataDir, { recursive: true });
|
|
170906
|
+
const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
|
|
170907
|
+
const dataDir = await getProjectDataDir2(project.id);
|
|
170351
170908
|
const client = new MemoryClient(project.id, projectRoot, dataDir);
|
|
170352
170909
|
await client._init(silent);
|
|
170353
170910
|
return client;
|