memorix 1.8.8 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +15 -1
- package/README.zh-CN.md +15 -1
- package/dist/cli/index.js +3722 -1599
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +634 -363
- package/dist/maintenance-runner.js +285 -103
- package/dist/media-audio-runner.js +285 -103
- package/dist/media-video-runner.js +285 -103
- package/dist/memcode-runtime/CHANGELOG.md +36 -0
- package/dist/sdk.js +627 -356
- package/dist/vector-backfill-runner.d.ts +4 -1
- package/dist/vector-backfill-runner.js +288 -104
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -2
- package/docs/API_REFERENCE.md +14 -0
- package/docs/CONFIGURATION.md +46 -0
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/INTEGRATIONS.md +1 -0
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +344 -0
- package/docs/PERFORMANCE.md +32 -0
- package/docs/README.md +4 -2
- package/docs/hooks-architecture.md +18 -13
- package/llms-full.txt +4 -1
- package/llms.txt +3 -1
- package/package.json +2 -1
- package/plugins/antigravity/memorix/plugin.json +1 -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/hermes/memorix/plugin.yaml +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/command-guide.ts +9 -1
- package/src/cli/commands/agent-integrations.ts +2 -1
- package/src/cli/commands/hooks-install.ts +2 -0
- package/src/cli/commands/integrate.ts +2 -0
- package/src/cli/commands/serve-http.ts +11 -0
- package/src/cli/commands/setup.ts +11 -4
- package/src/cli/commands/sync-store.ts +191 -0
- package/src/cli/commands/sync.ts +8 -0
- package/src/hooks/handler.ts +6 -0
- package/src/hooks/installers/index.ts +181 -6
- package/src/hooks/normalizer.ts +60 -1
- package/src/hooks/types.ts +3 -1
- package/src/integrations/registry.ts +9 -0
- package/src/memory/observations.ts +15 -2
- package/src/runtime/maintenance-targets.ts +6 -0
- package/src/runtime/vector-backfill-runner.ts +185 -7
- package/src/store/orama-store.ts +85 -102
- package/src/sync/adapters/fs.ts +179 -0
- package/src/sync/adapters/github.ts +258 -0
- package/src/sync/adapters/object-store.ts +267 -0
- package/src/sync/adapters/postgres.ts +269 -0
- package/src/sync/engine.ts +353 -0
- package/src/sync/journal.ts +162 -0
- package/src/sync/merge.ts +116 -0
- package/src/sync/namespace.ts +15 -0
- package/src/sync/paging.ts +45 -0
- package/src/sync/policy.ts +37 -0
- package/src/sync/remote-factory.ts +49 -0
- package/src/sync/store-port.ts +288 -0
- package/src/sync/types.ts +183 -0
|
@@ -6976,6 +6976,14 @@ __export(orama_store_exports, {
|
|
|
6976
6976
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6977
6977
|
});
|
|
6978
6978
|
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6979
|
+
function rememberSearchMode(key, value) {
|
|
6980
|
+
if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
|
|
6981
|
+
const oldest = lastSearchModeByProject.keys().next().value;
|
|
6982
|
+
if (oldest !== void 0) lastSearchModeByProject.delete(oldest);
|
|
6983
|
+
}
|
|
6984
|
+
lastSearchModeByProject.delete(key);
|
|
6985
|
+
lastSearchModeByProject.set(key, value);
|
|
6986
|
+
}
|
|
6979
6987
|
function getLastSearchMode(projectId) {
|
|
6980
6988
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6981
6989
|
}
|
|
@@ -6988,9 +6996,6 @@ function makeEntryKey(projectId, observationId2) {
|
|
|
6988
6996
|
function rememberObservationDoc(doc) {
|
|
6989
6997
|
const publicDoc = { ...doc };
|
|
6990
6998
|
delete publicDoc.embedding;
|
|
6991
|
-
if (doc.projectId && typeof doc.observationId === "number") {
|
|
6992
|
-
docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
|
|
6993
|
-
}
|
|
6994
6999
|
return publicDoc;
|
|
6995
7000
|
}
|
|
6996
7001
|
function isCommandLikeQuery(query) {
|
|
@@ -7094,7 +7099,6 @@ async function resetDb() {
|
|
|
7094
7099
|
indexEmbeddingProvider = null;
|
|
7095
7100
|
deferredCachedVectorHydration = null;
|
|
7096
7101
|
lastSearchModeByProject.clear();
|
|
7097
|
-
docByObservationKey.clear();
|
|
7098
7102
|
}
|
|
7099
7103
|
function isEmbeddingEnabled() {
|
|
7100
7104
|
return embeddingEnabled;
|
|
@@ -7157,78 +7161,72 @@ function isCompatibleCachedVector(vector) {
|
|
|
7157
7161
|
vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
|
|
7158
7162
|
);
|
|
7159
7163
|
}
|
|
7160
|
-
async function attachCachedVectors(database,
|
|
7161
|
-
const
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7164
|
+
async function attachCachedVectors(database, observations2) {
|
|
7165
|
+
const batchSize = 200;
|
|
7166
|
+
for (let start = 0; start < observations2.length; start += batchSize) {
|
|
7167
|
+
const batch = observations2.slice(start, start + batchSize);
|
|
7168
|
+
const cachedVectors = await getCachedEmbeddings(batch.map(observationEmbeddingText));
|
|
7169
|
+
if (db !== database) return;
|
|
7170
|
+
for (let index = 0; index < batch.length; index++) {
|
|
7171
|
+
const vector = cachedVectors[index];
|
|
7172
|
+
if (!isCompatibleCachedVector(vector)) continue;
|
|
7173
|
+
const id = makeOramaObservationId(batch[index].projectId, batch[index].id);
|
|
7174
|
+
const existing = getByID(database, id);
|
|
7175
|
+
if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(batch[index])) continue;
|
|
7176
|
+
try {
|
|
7177
|
+
await update(database, id, { ...existing, embedding: vector });
|
|
7178
|
+
} catch {
|
|
7179
|
+
}
|
|
7176
7180
|
}
|
|
7181
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7177
7182
|
}
|
|
7178
7183
|
}
|
|
7179
7184
|
async function hydrateIndex(observations2, options = {}) {
|
|
7180
7185
|
const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
|
|
7181
|
-
const candidates = [];
|
|
7182
|
-
for (const observation of observations2) {
|
|
7183
|
-
if (!observation || !observation.id || !observation.projectId) continue;
|
|
7184
|
-
const id = makeOramaObservationId(observation.projectId, observation.id);
|
|
7185
|
-
if (getByID(database, id)) continue;
|
|
7186
|
-
candidates.push({ observation, id });
|
|
7187
|
-
}
|
|
7188
7186
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
7189
|
-
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
7190
|
-
const documents = [];
|
|
7191
|
-
for (let index = 0; index < candidates.length; index++) {
|
|
7192
|
-
const { observation: obs, id } = candidates[index];
|
|
7193
|
-
try {
|
|
7194
|
-
const vector = cachedVectors[index];
|
|
7195
|
-
const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
|
|
7196
|
-
const doc = {
|
|
7197
|
-
id,
|
|
7198
|
-
observationId: obs.id,
|
|
7199
|
-
entityName: obs.entityName || "",
|
|
7200
|
-
type: obs.type || "discovery",
|
|
7201
|
-
title: obs.title || "",
|
|
7202
|
-
narrative: obs.narrative || "",
|
|
7203
|
-
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
7204
|
-
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
7205
|
-
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
7206
|
-
tokens: obs.tokens ?? 0,
|
|
7207
|
-
createdAt: obs.createdAt || "",
|
|
7208
|
-
projectId: obs.projectId,
|
|
7209
|
-
accessCount: obs.accessCount ?? 0,
|
|
7210
|
-
lastAccessedAt: obs.lastAccessedAt || "",
|
|
7211
|
-
status: obs.status ?? "active",
|
|
7212
|
-
source: obs.source || "agent",
|
|
7213
|
-
sourceDetail: obs.sourceDetail ?? "",
|
|
7214
|
-
valueCategory: obs.valueCategory ?? "",
|
|
7215
|
-
admissionState: obs.admissionState ?? "",
|
|
7216
|
-
admissionReason: obs.admissionReason ?? "",
|
|
7217
|
-
visibility: obs.visibility ?? "project",
|
|
7218
|
-
createdByAgentId: obs.createdByAgentId ?? "",
|
|
7219
|
-
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
7220
|
-
documentType: "observation",
|
|
7221
|
-
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
7222
|
-
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
7223
|
-
};
|
|
7224
|
-
documents.push(doc);
|
|
7225
|
-
} catch {
|
|
7226
|
-
}
|
|
7227
|
-
}
|
|
7228
7187
|
let inserted = 0;
|
|
7229
7188
|
const hydrationBatchSize = 200;
|
|
7230
|
-
for (let start = 0; start <
|
|
7231
|
-
const
|
|
7189
|
+
for (let start = 0; start < observations2.length; start += hydrationBatchSize) {
|
|
7190
|
+
const batchObservations = observations2.slice(start, start + hydrationBatchSize).filter((observation) => observation && observation.id && observation.projectId).filter((observation) => !getByID(database, makeOramaObservationId(observation.projectId, observation.id)));
|
|
7191
|
+
if (batchObservations.length === 0) continue;
|
|
7192
|
+
const cachedVectors = deferCachedVectors || options.skipCachedVectors ? batchObservations.map(() => null) : await getCachedEmbeddings(batchObservations.map(observationEmbeddingText));
|
|
7193
|
+
const batch = [];
|
|
7194
|
+
for (let index = 0; index < batchObservations.length; index++) {
|
|
7195
|
+
const obs = batchObservations[index];
|
|
7196
|
+
try {
|
|
7197
|
+
const compatibleVector = isCompatibleCachedVector(cachedVectors[index]) ? cachedVectors[index] : null;
|
|
7198
|
+
batch.push({
|
|
7199
|
+
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
7200
|
+
observationId: obs.id,
|
|
7201
|
+
entityName: obs.entityName || "",
|
|
7202
|
+
type: obs.type || "discovery",
|
|
7203
|
+
title: obs.title || "",
|
|
7204
|
+
narrative: obs.narrative || "",
|
|
7205
|
+
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
7206
|
+
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
7207
|
+
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
7208
|
+
tokens: obs.tokens ?? 0,
|
|
7209
|
+
createdAt: obs.createdAt || "",
|
|
7210
|
+
projectId: obs.projectId,
|
|
7211
|
+
accessCount: obs.accessCount ?? 0,
|
|
7212
|
+
lastAccessedAt: obs.lastAccessedAt || "",
|
|
7213
|
+
status: obs.status ?? "active",
|
|
7214
|
+
source: obs.source || "agent",
|
|
7215
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
7216
|
+
valueCategory: obs.valueCategory ?? "",
|
|
7217
|
+
admissionState: obs.admissionState ?? "",
|
|
7218
|
+
admissionReason: obs.admissionReason ?? "",
|
|
7219
|
+
visibility: obs.visibility ?? "project",
|
|
7220
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
7221
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
7222
|
+
documentType: "observation",
|
|
7223
|
+
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
7224
|
+
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
7225
|
+
});
|
|
7226
|
+
} catch {
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
if (batch.length === 0) continue;
|
|
7232
7230
|
try {
|
|
7233
7231
|
await insertMultiple(database, batch, batch.length);
|
|
7234
7232
|
for (const doc of batch) rememberObservationDoc(doc);
|
|
@@ -7250,7 +7248,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
7250
7248
|
}
|
|
7251
7249
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7252
7250
|
}
|
|
7253
|
-
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database,
|
|
7251
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, observations2).catch(() => {
|
|
7254
7252
|
}) : null;
|
|
7255
7253
|
return inserted;
|
|
7256
7254
|
}
|
|
@@ -7287,12 +7285,6 @@ async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
|
7287
7285
|
async function removeObservation(oramaId) {
|
|
7288
7286
|
const database = await getDb();
|
|
7289
7287
|
await remove2(database, oramaId);
|
|
7290
|
-
for (const [key, doc] of docByObservationKey.entries()) {
|
|
7291
|
-
if (doc.id === oramaId) {
|
|
7292
|
-
docByObservationKey.delete(key);
|
|
7293
|
-
break;
|
|
7294
|
-
}
|
|
7295
|
-
}
|
|
7296
7288
|
}
|
|
7297
7289
|
async function searchObservations(options) {
|
|
7298
7290
|
const perf = !!process.env.MEMORIX_PERF;
|
|
@@ -7307,7 +7299,7 @@ async function searchObservations(options) {
|
|
|
7307
7299
|
const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
|
|
7308
7300
|
const quality = options.quality ?? "balanced";
|
|
7309
7301
|
const database = await getDb();
|
|
7310
|
-
|
|
7302
|
+
rememberSearchMode(
|
|
7311
7303
|
modeKey,
|
|
7312
7304
|
quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
|
|
7313
7305
|
);
|
|
@@ -7374,7 +7366,7 @@ async function searchObservations(options) {
|
|
|
7374
7366
|
if (provider2) {
|
|
7375
7367
|
const activeVectorDimensions = getVectorDimensions();
|
|
7376
7368
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
7377
|
-
|
|
7369
|
+
rememberSearchMode(
|
|
7378
7370
|
modeKey,
|
|
7379
7371
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
7380
7372
|
);
|
|
@@ -7399,7 +7391,7 @@ async function searchObservations(options) {
|
|
|
7399
7391
|
mark("embedding");
|
|
7400
7392
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
7401
7393
|
const isCJKHeavy = cjkRatio > 0.3;
|
|
7402
|
-
|
|
7394
|
+
rememberSearchMode(modeKey, "hybrid");
|
|
7403
7395
|
searchParams = {
|
|
7404
7396
|
...searchParams,
|
|
7405
7397
|
mode: "hybrid",
|
|
@@ -7415,7 +7407,7 @@ async function searchObservations(options) {
|
|
|
7415
7407
|
}
|
|
7416
7408
|
}
|
|
7417
7409
|
} catch (error) {
|
|
7418
|
-
|
|
7410
|
+
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
7419
7411
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
7420
7412
|
}
|
|
7421
7413
|
}
|
|
@@ -7425,7 +7417,7 @@ async function searchObservations(options) {
|
|
|
7425
7417
|
results = await search(database, searchParams);
|
|
7426
7418
|
} catch (error) {
|
|
7427
7419
|
if (queryVector && isVectorDimensionMismatchError(error)) {
|
|
7428
|
-
|
|
7420
|
+
rememberSearchMode(modeKey, "fulltext (embedding dimension mismatch)");
|
|
7429
7421
|
console.error("[memorix] Vector search dimension mismatch detected, retrying without embeddings");
|
|
7430
7422
|
results = await search(database, stripVectorSearchParams(searchParams));
|
|
7431
7423
|
} else {
|
|
@@ -7447,7 +7439,7 @@ async function searchObservations(options) {
|
|
|
7447
7439
|
},
|
|
7448
7440
|
similarity: 0.25
|
|
7449
7441
|
};
|
|
7450
|
-
|
|
7442
|
+
rememberSearchMode(modeKey, "vector-only (hybrid empty fallback)");
|
|
7451
7443
|
results = await search(database, vectorOnlyParams);
|
|
7452
7444
|
} catch {
|
|
7453
7445
|
}
|
|
@@ -7688,7 +7680,7 @@ async function searchObservations(options) {
|
|
|
7688
7680
|
const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7689
7681
|
if (rerankedTop.length > 0) {
|
|
7690
7682
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
7691
|
-
|
|
7683
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
|
|
7692
7684
|
mark("rerank(usedNeural=true)");
|
|
7693
7685
|
}
|
|
7694
7686
|
}
|
|
@@ -7705,7 +7697,7 @@ async function searchObservations(options) {
|
|
|
7705
7697
|
);
|
|
7706
7698
|
mark(`rerank(usedLLM=${usedLLM})`);
|
|
7707
7699
|
if (usedLLM) {
|
|
7708
|
-
|
|
7700
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
|
|
7709
7701
|
const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7710
7702
|
if (rerankedTop.length > 0) {
|
|
7711
7703
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
@@ -7778,13 +7770,6 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
7778
7770
|
const database = await getDb();
|
|
7779
7771
|
const results = [];
|
|
7780
7772
|
for (const id of ids) {
|
|
7781
|
-
if (projectId) {
|
|
7782
|
-
const cached = docByObservationKey.get(makeEntryKey(projectId, id));
|
|
7783
|
-
if (cached) {
|
|
7784
|
-
results.push(cached);
|
|
7785
|
-
continue;
|
|
7786
|
-
}
|
|
7787
|
-
}
|
|
7788
7773
|
const searchResult = await search(database, {
|
|
7789
7774
|
term: "",
|
|
7790
7775
|
includeVectors: true,
|
|
@@ -7884,7 +7869,7 @@ function formatTime(isoDate) {
|
|
|
7884
7869
|
return isoDate;
|
|
7885
7870
|
}
|
|
7886
7871
|
}
|
|
7887
|
-
var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration,
|
|
7872
|
+
var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, MAX_SEARCH_MODE_KEYS, COMMAND_LOG_TITLE, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
|
|
7888
7873
|
var init_orama_store = __esm({
|
|
7889
7874
|
"src/store/orama-store.ts"() {
|
|
7890
7875
|
"use strict";
|
|
@@ -7905,10 +7890,10 @@ var init_orama_store = __esm({
|
|
|
7905
7890
|
embeddingDimensions = null;
|
|
7906
7891
|
indexEmbeddingProvider = null;
|
|
7907
7892
|
deferredCachedVectorHydration = null;
|
|
7908
|
-
docByObservationKey = /* @__PURE__ */ new Map();
|
|
7909
7893
|
NON_CJK_HYBRID_SIMILARITY = 0.45;
|
|
7910
7894
|
lastSearchModeByProject = /* @__PURE__ */ new Map();
|
|
7911
7895
|
SEARCH_MODE_DEFAULT_KEY = "__global__";
|
|
7896
|
+
MAX_SEARCH_MODE_KEYS = 4096;
|
|
7912
7897
|
COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
7913
7898
|
COMMAND_STYLE_TITLE = /(\bfindstr\b|\bSelect-String\b|\bGet-Content\b|\bnpx\s+vitest\b|\bnpx\s+tsc\b|\b2>&1\b)/i;
|
|
7914
7899
|
COMMAND_LIKE_QUERY2 = /\b(git|npm|npx|pnpm|yarn|node|bash|powershell|curl|memorix)\b/i;
|
|
@@ -8804,6 +8789,56 @@ var init_evidence_store = __esm({
|
|
|
8804
8789
|
}
|
|
8805
8790
|
});
|
|
8806
8791
|
|
|
8792
|
+
// src/runtime/maintenance-targets.ts
|
|
8793
|
+
function rowToTarget(row) {
|
|
8794
|
+
return {
|
|
8795
|
+
projectId: row.project_id,
|
|
8796
|
+
projectRoot: row.project_root,
|
|
8797
|
+
dataDir: row.data_dir,
|
|
8798
|
+
updatedAt: Number(row.updated_at)
|
|
8799
|
+
};
|
|
8800
|
+
}
|
|
8801
|
+
var MaintenanceTargetStore;
|
|
8802
|
+
var init_maintenance_targets = __esm({
|
|
8803
|
+
"src/runtime/maintenance-targets.ts"() {
|
|
8804
|
+
"use strict";
|
|
8805
|
+
init_esm_shims();
|
|
8806
|
+
init_sqlite_db();
|
|
8807
|
+
MaintenanceTargetStore = class {
|
|
8808
|
+
db;
|
|
8809
|
+
constructor(dataDir) {
|
|
8810
|
+
this.db = getDatabase(dataDir);
|
|
8811
|
+
}
|
|
8812
|
+
register(input) {
|
|
8813
|
+
if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
|
|
8814
|
+
if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
|
|
8815
|
+
if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
|
|
8816
|
+
const now3 = input.now ?? Date.now();
|
|
8817
|
+
this.db.prepare(`
|
|
8818
|
+
INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
|
|
8819
|
+
VALUES (?, ?, ?, ?)
|
|
8820
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
8821
|
+
project_root = excluded.project_root,
|
|
8822
|
+
data_dir = excluded.data_dir,
|
|
8823
|
+
updated_at = excluded.updated_at
|
|
8824
|
+
`).run(input.projectId, input.projectRoot, input.dataDir, now3);
|
|
8825
|
+
return this.get(input.projectId);
|
|
8826
|
+
}
|
|
8827
|
+
get(projectId) {
|
|
8828
|
+
const row = this.db.prepare(`
|
|
8829
|
+
SELECT * FROM maintenance_targets WHERE project_id = ?
|
|
8830
|
+
`).get(projectId);
|
|
8831
|
+
return row ? rowToTarget(row) : void 0;
|
|
8832
|
+
}
|
|
8833
|
+
list() {
|
|
8834
|
+
return this.db.prepare(`
|
|
8835
|
+
SELECT * FROM maintenance_targets ORDER BY updated_at DESC
|
|
8836
|
+
`).all().map(rowToTarget);
|
|
8837
|
+
}
|
|
8838
|
+
};
|
|
8839
|
+
}
|
|
8840
|
+
});
|
|
8841
|
+
|
|
8807
8842
|
// src/knowledge/workspace-store.ts
|
|
8808
8843
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
8809
8844
|
function arrayValue(raw) {
|
|
@@ -149668,10 +149703,21 @@ __export(vector_backfill_runner_exports, {
|
|
|
149668
149703
|
launchDetachedVectorBackfill: () => launchDetachedVectorBackfill,
|
|
149669
149704
|
main: () => main,
|
|
149670
149705
|
parseVectorBackfillRequest: () => parseVectorBackfillRequest,
|
|
149671
|
-
|
|
149706
|
+
releaseVectorBackfillLock: () => releaseVectorBackfillLock,
|
|
149707
|
+
resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath,
|
|
149708
|
+
vectorBackfillLockPath: () => vectorBackfillLockPath
|
|
149672
149709
|
});
|
|
149673
149710
|
import { spawn as spawn2 } from "child_process";
|
|
149674
|
-
import {
|
|
149711
|
+
import {
|
|
149712
|
+
closeSync,
|
|
149713
|
+
existsSync as existsSync10,
|
|
149714
|
+
mkdirSync as mkdirSync2,
|
|
149715
|
+
openSync,
|
|
149716
|
+
readFileSync as readFileSync9,
|
|
149717
|
+
statSync as statSync4,
|
|
149718
|
+
unlinkSync,
|
|
149719
|
+
writeFileSync as writeFileSync2
|
|
149720
|
+
} from "fs";
|
|
149675
149721
|
import path19 from "path";
|
|
149676
149722
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
149677
149723
|
function isNonEmptyString(value) {
|
|
@@ -149698,10 +149744,104 @@ function parseVectorBackfillRequest(raw) {
|
|
|
149698
149744
|
dataDir: value.dataDir
|
|
149699
149745
|
};
|
|
149700
149746
|
}
|
|
149747
|
+
function vectorBackfillLockPath(dataDir) {
|
|
149748
|
+
return path19.join(dataDir, VECTOR_BACKFILL_LOCK_FILE);
|
|
149749
|
+
}
|
|
149750
|
+
function processIsAlive(pid) {
|
|
149751
|
+
if (!Number.isSafeInteger(pid) || pid == null || pid <= 0) return false;
|
|
149752
|
+
try {
|
|
149753
|
+
process.kill(pid, 0);
|
|
149754
|
+
return true;
|
|
149755
|
+
} catch (error) {
|
|
149756
|
+
return error.code === "EPERM";
|
|
149757
|
+
}
|
|
149758
|
+
}
|
|
149759
|
+
function readVectorBackfillLock(lockPath) {
|
|
149760
|
+
try {
|
|
149761
|
+
const value = JSON.parse(readFileSync9(lockPath, "utf8"));
|
|
149762
|
+
if (typeof value.parentPid !== "number" || !Number.isSafeInteger(value.parentPid) || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.projectId !== "string") return void 0;
|
|
149763
|
+
return {
|
|
149764
|
+
parentPid: value.parentPid,
|
|
149765
|
+
...typeof value.childPid === "number" ? { childPid: value.childPid } : {},
|
|
149766
|
+
startedAt: value.startedAt,
|
|
149767
|
+
projectId: value.projectId
|
|
149768
|
+
};
|
|
149769
|
+
} catch {
|
|
149770
|
+
return void 0;
|
|
149771
|
+
}
|
|
149772
|
+
}
|
|
149773
|
+
function writeVectorBackfillLock(lockPath, value) {
|
|
149774
|
+
writeFileSync2(lockPath, `${JSON.stringify(value)}
|
|
149775
|
+
`, "utf8");
|
|
149776
|
+
}
|
|
149777
|
+
function acquireVectorBackfillLock(dataDir, projectId, preferredPath) {
|
|
149778
|
+
const lockPath = preferredPath ?? vectorBackfillLockPath(dataDir);
|
|
149779
|
+
mkdirSync2(path19.dirname(lockPath), { recursive: true });
|
|
149780
|
+
const initial = {
|
|
149781
|
+
parentPid: process.pid,
|
|
149782
|
+
startedAt: Date.now(),
|
|
149783
|
+
projectId
|
|
149784
|
+
};
|
|
149785
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
149786
|
+
try {
|
|
149787
|
+
const fd = openSync(lockPath, "wx");
|
|
149788
|
+
try {
|
|
149789
|
+
writeFileSync2(fd, `${JSON.stringify(initial)}
|
|
149790
|
+
`, "utf8");
|
|
149791
|
+
} finally {
|
|
149792
|
+
closeSync(fd);
|
|
149793
|
+
}
|
|
149794
|
+
return {
|
|
149795
|
+
path: lockPath,
|
|
149796
|
+
setChildPid(pid) {
|
|
149797
|
+
try {
|
|
149798
|
+
writeVectorBackfillLock(lockPath, { ...initial, childPid: pid });
|
|
149799
|
+
} catch {
|
|
149800
|
+
}
|
|
149801
|
+
},
|
|
149802
|
+
release() {
|
|
149803
|
+
try {
|
|
149804
|
+
unlinkSync(lockPath);
|
|
149805
|
+
} catch {
|
|
149806
|
+
}
|
|
149807
|
+
}
|
|
149808
|
+
};
|
|
149809
|
+
} catch (error) {
|
|
149810
|
+
if (error.code !== "EEXIST") return void 0;
|
|
149811
|
+
const existing = readVectorBackfillLock(lockPath);
|
|
149812
|
+
const ownerAlive = existing && (processIsAlive(existing.childPid) || processIsAlive(existing.parentPid));
|
|
149813
|
+
if (ownerAlive) return void 0;
|
|
149814
|
+
let stale = false;
|
|
149815
|
+
try {
|
|
149816
|
+
stale = Date.now() - statSync4(lockPath).mtimeMs >= STALE_LOCK_RECOVERY_MS;
|
|
149817
|
+
} catch {
|
|
149818
|
+
stale = false;
|
|
149819
|
+
}
|
|
149820
|
+
if (!existing && !stale) return void 0;
|
|
149821
|
+
try {
|
|
149822
|
+
unlinkSync(lockPath);
|
|
149823
|
+
} catch {
|
|
149824
|
+
return void 0;
|
|
149825
|
+
}
|
|
149826
|
+
}
|
|
149827
|
+
}
|
|
149828
|
+
return void 0;
|
|
149829
|
+
}
|
|
149830
|
+
function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFILL_LOCK) {
|
|
149831
|
+
if (!lockPath) return;
|
|
149832
|
+
const lock = readVectorBackfillLock(lockPath);
|
|
149833
|
+
if (lock && lock.childPid !== process.pid) return;
|
|
149834
|
+
try {
|
|
149835
|
+
unlinkSync(lockPath);
|
|
149836
|
+
} catch {
|
|
149837
|
+
}
|
|
149838
|
+
}
|
|
149701
149839
|
function launchDetachedVectorBackfill(request, options = {}) {
|
|
149702
149840
|
const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
|
|
149703
149841
|
const exists = options.exists ?? existsSync10;
|
|
149704
149842
|
if (!exists(runnerPath)) return false;
|
|
149843
|
+
const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
|
|
149844
|
+
if (!lock) return false;
|
|
149705
149845
|
try {
|
|
149706
149846
|
const child = (options.spawn ?? spawn2)(process.execPath, [runnerPath], {
|
|
149707
149847
|
cwd: request.projectRoot,
|
|
@@ -149711,14 +149851,23 @@ function launchDetachedVectorBackfill(request, options = {}) {
|
|
|
149711
149851
|
stdio: "ignore",
|
|
149712
149852
|
// The request contains only local project metadata, never credentials.
|
|
149713
149853
|
// Environment transport avoids a live stdin pipe keeping the CLI alive.
|
|
149714
|
-
env: {
|
|
149854
|
+
env: {
|
|
149855
|
+
...process.env,
|
|
149856
|
+
MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request),
|
|
149857
|
+
MEMORIX_VECTOR_BACKFILL_LOCK: lock.path
|
|
149858
|
+
},
|
|
149715
149859
|
windowsHide: true
|
|
149716
149860
|
});
|
|
149861
|
+
if (child.pid) lock.setChildPid(child.pid);
|
|
149862
|
+
else lock.release();
|
|
149717
149863
|
child.once?.("error", () => {
|
|
149718
149864
|
});
|
|
149865
|
+
child.once?.("close", () => lock.release());
|
|
149866
|
+
if (typeof child.once !== "function") lock.release();
|
|
149719
149867
|
child.unref();
|
|
149720
|
-
return
|
|
149868
|
+
return Boolean(child.pid);
|
|
149721
149869
|
} catch {
|
|
149870
|
+
lock.release();
|
|
149722
149871
|
return false;
|
|
149723
149872
|
}
|
|
149724
149873
|
}
|
|
@@ -149730,12 +149879,32 @@ async function executeVectorBackfill(request) {
|
|
|
149730
149879
|
await prepareSearchIndex();
|
|
149731
149880
|
await getDeferredCachedVectorHydration()?.catch(() => {
|
|
149732
149881
|
});
|
|
149882
|
+
const queue = new MaintenanceJobStore(request.dataDir);
|
|
149883
|
+
const targets = new MaintenanceTargetStore(request.dataDir);
|
|
149884
|
+
let preparedRoot = request.projectRoot;
|
|
149885
|
+
const prepareTarget = async (target) => {
|
|
149886
|
+
if (target.projectRoot === preparedRoot) return;
|
|
149887
|
+
initProjectRoot(target.projectRoot);
|
|
149888
|
+
loadDotenv(target.projectRoot);
|
|
149889
|
+
preparedRoot = target.projectRoot;
|
|
149890
|
+
};
|
|
149891
|
+
const handler = async (job) => {
|
|
149892
|
+
const target = targets.get(job.projectId) ?? (job.projectId === request.projectId ? { projectId: request.projectId, projectRoot: request.projectRoot, dataDir: request.dataDir, updatedAt: Date.now() } : void 0);
|
|
149893
|
+
if (!target) return { action: "reschedule", delayMs: 3e4 };
|
|
149894
|
+
await prepareTarget(target);
|
|
149895
|
+
return createProjectMaintenanceHandler(job.projectId, target.dataDir, target.projectRoot)(job);
|
|
149896
|
+
};
|
|
149733
149897
|
const worker = new MaintenanceJobWorker(
|
|
149734
|
-
|
|
149735
|
-
|
|
149736
|
-
{
|
|
149898
|
+
queue,
|
|
149899
|
+
handler,
|
|
149900
|
+
{ kinds: ["vector-backfill"] }
|
|
149737
149901
|
);
|
|
149738
|
-
|
|
149902
|
+
let result = { state: "idle" };
|
|
149903
|
+
while (true) {
|
|
149904
|
+
result = await worker.runOnce();
|
|
149905
|
+
if (result.state === "idle") break;
|
|
149906
|
+
}
|
|
149907
|
+
return result;
|
|
149739
149908
|
}
|
|
149740
149909
|
async function readStdin() {
|
|
149741
149910
|
return new Promise((resolve2, reject) => {
|
|
@@ -149758,9 +149927,11 @@ async function main() {
|
|
|
149758
149927
|
`);
|
|
149759
149928
|
process.exitCode = 1;
|
|
149760
149929
|
} finally {
|
|
149930
|
+
releaseVectorBackfillLock();
|
|
149761
149931
|
closeAllDatabases();
|
|
149762
149932
|
}
|
|
149763
149933
|
}
|
|
149934
|
+
var VECTOR_BACKFILL_LOCK_FILE, STALE_LOCK_RECOVERY_MS;
|
|
149764
149935
|
var init_vector_backfill_runner = __esm({
|
|
149765
149936
|
"src/runtime/vector-backfill-runner.ts"() {
|
|
149766
149937
|
"use strict";
|
|
@@ -149773,7 +149944,10 @@ var init_vector_backfill_runner = __esm({
|
|
|
149773
149944
|
init_orama_store();
|
|
149774
149945
|
init_sqlite_db();
|
|
149775
149946
|
init_maintenance_jobs();
|
|
149947
|
+
init_maintenance_targets();
|
|
149776
149948
|
init_project_maintenance();
|
|
149949
|
+
VECTOR_BACKFILL_LOCK_FILE = ".memorix-vector-backfill.lock";
|
|
149950
|
+
STALE_LOCK_RECOVERY_MS = 24 * 60 * 60 * 1e3;
|
|
149777
149951
|
if (process.argv[1] && process.argv[1].endsWith("vector-backfill-runner.js")) {
|
|
149778
149952
|
void main();
|
|
149779
149953
|
}
|
|
@@ -149815,11 +149989,17 @@ function resetObservationRuntime() {
|
|
|
149815
149989
|
embeddingWorkerProjectRoot = void 0;
|
|
149816
149990
|
vectorMissingIds.clear();
|
|
149817
149991
|
vectorSchemaUpgradePromise = null;
|
|
149992
|
+
embeddingFailureLogTimestamps.clear();
|
|
149818
149993
|
}
|
|
149819
149994
|
function logEmbeddingFailureOnce(key, message) {
|
|
149820
149995
|
const now3 = Date.now();
|
|
149821
149996
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
149822
149997
|
if (now3 - last < EMBEDDING_FAILURE_LOG_COOLDOWN_MS) return;
|
|
149998
|
+
if (!embeddingFailureLogTimestamps.has(key) && embeddingFailureLogTimestamps.size >= MAX_EMBEDDING_FAILURE_KEYS) {
|
|
149999
|
+
const oldest = embeddingFailureLogTimestamps.keys().next().value;
|
|
150000
|
+
if (oldest !== void 0) embeddingFailureLogTimestamps.delete(oldest);
|
|
150001
|
+
}
|
|
150002
|
+
embeddingFailureLogTimestamps.delete(key);
|
|
149823
150003
|
embeddingFailureLogTimestamps.set(key, now3);
|
|
149824
150004
|
console.error(message);
|
|
149825
150005
|
}
|
|
@@ -149849,13 +150029,14 @@ function queueVectorBackfill(projectId, options = {}) {
|
|
|
149849
150029
|
const dataDir = projectDir;
|
|
149850
150030
|
if (!dataDir) return;
|
|
149851
150031
|
void Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)).then(({ MaintenanceJobStore: MaintenanceJobStore2 }) => {
|
|
149852
|
-
new MaintenanceJobStore2(dataDir).enqueue({
|
|
150032
|
+
const job = new MaintenanceJobStore2(dataDir).enqueue({
|
|
149853
150033
|
projectId,
|
|
149854
150034
|
kind: "vector-backfill",
|
|
149855
150035
|
dedupeKey: "vector-backfill",
|
|
149856
150036
|
payload: { limit: 12 }
|
|
149857
150037
|
});
|
|
149858
|
-
|
|
150038
|
+
const due = !job || job.status === "pending" && job.runAfter <= Date.now();
|
|
150039
|
+
if (due && options.detachedWorker && options.projectRoot) {
|
|
149859
150040
|
void Promise.resolve().then(() => (init_vector_backfill_runner(), vector_backfill_runner_exports)).then(({ launchDetachedVectorBackfill: launchDetachedVectorBackfill2 }) => {
|
|
149860
150041
|
launchDetachedVectorBackfill2({
|
|
149861
150042
|
projectId,
|
|
@@ -150786,7 +150967,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
150786
150967
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
150787
150968
|
};
|
|
150788
150969
|
}
|
|
150789
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
150970
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS, MAX_EMBEDDING_FAILURE_KEYS;
|
|
150790
150971
|
var init_observations = __esm({
|
|
150791
150972
|
"src/memory/observations.ts"() {
|
|
150792
150973
|
"use strict";
|
|
@@ -150814,6 +150995,7 @@ var init_observations = __esm({
|
|
|
150814
150995
|
lastVectorBackfill = null;
|
|
150815
150996
|
embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
|
|
150816
150997
|
EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
|
|
150998
|
+
MAX_EMBEDDING_FAILURE_KEYS = 1024;
|
|
150817
150999
|
}
|
|
150818
151000
|
});
|
|
150819
151001
|
|