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
|
@@ -6490,6 +6490,14 @@ __export(orama_store_exports, {
|
|
|
6490
6490
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6491
6491
|
});
|
|
6492
6492
|
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6493
|
+
function rememberSearchMode(key, value) {
|
|
6494
|
+
if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
|
|
6495
|
+
const oldest = lastSearchModeByProject.keys().next().value;
|
|
6496
|
+
if (oldest !== void 0) lastSearchModeByProject.delete(oldest);
|
|
6497
|
+
}
|
|
6498
|
+
lastSearchModeByProject.delete(key);
|
|
6499
|
+
lastSearchModeByProject.set(key, value);
|
|
6500
|
+
}
|
|
6493
6501
|
function getLastSearchMode(projectId) {
|
|
6494
6502
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6495
6503
|
}
|
|
@@ -6502,9 +6510,6 @@ function makeEntryKey(projectId, observationId2) {
|
|
|
6502
6510
|
function rememberObservationDoc(doc) {
|
|
6503
6511
|
const publicDoc = { ...doc };
|
|
6504
6512
|
delete publicDoc.embedding;
|
|
6505
|
-
if (doc.projectId && typeof doc.observationId === "number") {
|
|
6506
|
-
docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
|
|
6507
|
-
}
|
|
6508
6513
|
return publicDoc;
|
|
6509
6514
|
}
|
|
6510
6515
|
function isCommandLikeQuery(query) {
|
|
@@ -6608,7 +6613,6 @@ async function resetDb() {
|
|
|
6608
6613
|
indexEmbeddingProvider = null;
|
|
6609
6614
|
deferredCachedVectorHydration = null;
|
|
6610
6615
|
lastSearchModeByProject.clear();
|
|
6611
|
-
docByObservationKey.clear();
|
|
6612
6616
|
}
|
|
6613
6617
|
function isEmbeddingEnabled() {
|
|
6614
6618
|
return embeddingEnabled;
|
|
@@ -6671,78 +6675,72 @@ function isCompatibleCachedVector(vector) {
|
|
|
6671
6675
|
vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
|
|
6672
6676
|
);
|
|
6673
6677
|
}
|
|
6674
|
-
async function attachCachedVectors(database,
|
|
6675
|
-
const
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6678
|
+
async function attachCachedVectors(database, observations2) {
|
|
6679
|
+
const batchSize = 200;
|
|
6680
|
+
for (let start = 0; start < observations2.length; start += batchSize) {
|
|
6681
|
+
const batch = observations2.slice(start, start + batchSize);
|
|
6682
|
+
const cachedVectors = await getCachedEmbeddings(batch.map(observationEmbeddingText));
|
|
6683
|
+
if (db !== database) return;
|
|
6684
|
+
for (let index = 0; index < batch.length; index++) {
|
|
6685
|
+
const vector = cachedVectors[index];
|
|
6686
|
+
if (!isCompatibleCachedVector(vector)) continue;
|
|
6687
|
+
const id = makeOramaObservationId(batch[index].projectId, batch[index].id);
|
|
6688
|
+
const existing = getByID(database, id);
|
|
6689
|
+
if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(batch[index])) continue;
|
|
6690
|
+
try {
|
|
6691
|
+
await update(database, id, { ...existing, embedding: vector });
|
|
6692
|
+
} catch {
|
|
6693
|
+
}
|
|
6690
6694
|
}
|
|
6695
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
6691
6696
|
}
|
|
6692
6697
|
}
|
|
6693
6698
|
async function hydrateIndex(observations2, options = {}) {
|
|
6694
6699
|
const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
|
|
6695
|
-
const candidates = [];
|
|
6696
|
-
for (const observation of observations2) {
|
|
6697
|
-
if (!observation || !observation.id || !observation.projectId) continue;
|
|
6698
|
-
const id = makeOramaObservationId(observation.projectId, observation.id);
|
|
6699
|
-
if (getByID(database, id)) continue;
|
|
6700
|
-
candidates.push({ observation, id });
|
|
6701
|
-
}
|
|
6702
6700
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
6703
|
-
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
6704
|
-
const documents = [];
|
|
6705
|
-
for (let index = 0; index < candidates.length; index++) {
|
|
6706
|
-
const { observation: obs, id } = candidates[index];
|
|
6707
|
-
try {
|
|
6708
|
-
const vector = cachedVectors[index];
|
|
6709
|
-
const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
|
|
6710
|
-
const doc = {
|
|
6711
|
-
id,
|
|
6712
|
-
observationId: obs.id,
|
|
6713
|
-
entityName: obs.entityName || "",
|
|
6714
|
-
type: obs.type || "discovery",
|
|
6715
|
-
title: obs.title || "",
|
|
6716
|
-
narrative: obs.narrative || "",
|
|
6717
|
-
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
6718
|
-
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
6719
|
-
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
6720
|
-
tokens: obs.tokens ?? 0,
|
|
6721
|
-
createdAt: obs.createdAt || "",
|
|
6722
|
-
projectId: obs.projectId,
|
|
6723
|
-
accessCount: obs.accessCount ?? 0,
|
|
6724
|
-
lastAccessedAt: obs.lastAccessedAt || "",
|
|
6725
|
-
status: obs.status ?? "active",
|
|
6726
|
-
source: obs.source || "agent",
|
|
6727
|
-
sourceDetail: obs.sourceDetail ?? "",
|
|
6728
|
-
valueCategory: obs.valueCategory ?? "",
|
|
6729
|
-
admissionState: obs.admissionState ?? "",
|
|
6730
|
-
admissionReason: obs.admissionReason ?? "",
|
|
6731
|
-
visibility: obs.visibility ?? "project",
|
|
6732
|
-
createdByAgentId: obs.createdByAgentId ?? "",
|
|
6733
|
-
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
6734
|
-
documentType: "observation",
|
|
6735
|
-
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6736
|
-
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6737
|
-
};
|
|
6738
|
-
documents.push(doc);
|
|
6739
|
-
} catch {
|
|
6740
|
-
}
|
|
6741
|
-
}
|
|
6742
6701
|
let inserted = 0;
|
|
6743
6702
|
const hydrationBatchSize = 200;
|
|
6744
|
-
for (let start = 0; start <
|
|
6745
|
-
const
|
|
6703
|
+
for (let start = 0; start < observations2.length; start += hydrationBatchSize) {
|
|
6704
|
+
const batchObservations = observations2.slice(start, start + hydrationBatchSize).filter((observation) => observation && observation.id && observation.projectId).filter((observation) => !getByID(database, makeOramaObservationId(observation.projectId, observation.id)));
|
|
6705
|
+
if (batchObservations.length === 0) continue;
|
|
6706
|
+
const cachedVectors = deferCachedVectors || options.skipCachedVectors ? batchObservations.map(() => null) : await getCachedEmbeddings(batchObservations.map(observationEmbeddingText));
|
|
6707
|
+
const batch = [];
|
|
6708
|
+
for (let index = 0; index < batchObservations.length; index++) {
|
|
6709
|
+
const obs = batchObservations[index];
|
|
6710
|
+
try {
|
|
6711
|
+
const compatibleVector = isCompatibleCachedVector(cachedVectors[index]) ? cachedVectors[index] : null;
|
|
6712
|
+
batch.push({
|
|
6713
|
+
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
6714
|
+
observationId: obs.id,
|
|
6715
|
+
entityName: obs.entityName || "",
|
|
6716
|
+
type: obs.type || "discovery",
|
|
6717
|
+
title: obs.title || "",
|
|
6718
|
+
narrative: obs.narrative || "",
|
|
6719
|
+
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
6720
|
+
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
6721
|
+
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
6722
|
+
tokens: obs.tokens ?? 0,
|
|
6723
|
+
createdAt: obs.createdAt || "",
|
|
6724
|
+
projectId: obs.projectId,
|
|
6725
|
+
accessCount: obs.accessCount ?? 0,
|
|
6726
|
+
lastAccessedAt: obs.lastAccessedAt || "",
|
|
6727
|
+
status: obs.status ?? "active",
|
|
6728
|
+
source: obs.source || "agent",
|
|
6729
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
6730
|
+
valueCategory: obs.valueCategory ?? "",
|
|
6731
|
+
admissionState: obs.admissionState ?? "",
|
|
6732
|
+
admissionReason: obs.admissionReason ?? "",
|
|
6733
|
+
visibility: obs.visibility ?? "project",
|
|
6734
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
6735
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
6736
|
+
documentType: "observation",
|
|
6737
|
+
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6738
|
+
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6739
|
+
});
|
|
6740
|
+
} catch {
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
if (batch.length === 0) continue;
|
|
6746
6744
|
try {
|
|
6747
6745
|
await insertMultiple(database, batch, batch.length);
|
|
6748
6746
|
for (const doc of batch) rememberObservationDoc(doc);
|
|
@@ -6764,7 +6762,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6764
6762
|
}
|
|
6765
6763
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
6766
6764
|
}
|
|
6767
|
-
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database,
|
|
6765
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, observations2).catch(() => {
|
|
6768
6766
|
}) : null;
|
|
6769
6767
|
return inserted;
|
|
6770
6768
|
}
|
|
@@ -6801,12 +6799,6 @@ async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
|
6801
6799
|
async function removeObservation(oramaId) {
|
|
6802
6800
|
const database = await getDb();
|
|
6803
6801
|
await remove2(database, oramaId);
|
|
6804
|
-
for (const [key, doc] of docByObservationKey.entries()) {
|
|
6805
|
-
if (doc.id === oramaId) {
|
|
6806
|
-
docByObservationKey.delete(key);
|
|
6807
|
-
break;
|
|
6808
|
-
}
|
|
6809
|
-
}
|
|
6810
6802
|
}
|
|
6811
6803
|
async function searchObservations(options) {
|
|
6812
6804
|
const perf = !!process.env.MEMORIX_PERF;
|
|
@@ -6821,7 +6813,7 @@ async function searchObservations(options) {
|
|
|
6821
6813
|
const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
|
|
6822
6814
|
const quality = options.quality ?? "balanced";
|
|
6823
6815
|
const database = await getDb();
|
|
6824
|
-
|
|
6816
|
+
rememberSearchMode(
|
|
6825
6817
|
modeKey,
|
|
6826
6818
|
quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
|
|
6827
6819
|
);
|
|
@@ -6888,7 +6880,7 @@ async function searchObservations(options) {
|
|
|
6888
6880
|
if (provider2) {
|
|
6889
6881
|
const activeVectorDimensions = getVectorDimensions();
|
|
6890
6882
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
6891
|
-
|
|
6883
|
+
rememberSearchMode(
|
|
6892
6884
|
modeKey,
|
|
6893
6885
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
6894
6886
|
);
|
|
@@ -6913,7 +6905,7 @@ async function searchObservations(options) {
|
|
|
6913
6905
|
mark("embedding");
|
|
6914
6906
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
6915
6907
|
const isCJKHeavy = cjkRatio > 0.3;
|
|
6916
|
-
|
|
6908
|
+
rememberSearchMode(modeKey, "hybrid");
|
|
6917
6909
|
searchParams = {
|
|
6918
6910
|
...searchParams,
|
|
6919
6911
|
mode: "hybrid",
|
|
@@ -6929,7 +6921,7 @@ async function searchObservations(options) {
|
|
|
6929
6921
|
}
|
|
6930
6922
|
}
|
|
6931
6923
|
} catch (error) {
|
|
6932
|
-
|
|
6924
|
+
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
6933
6925
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
6934
6926
|
}
|
|
6935
6927
|
}
|
|
@@ -6939,7 +6931,7 @@ async function searchObservations(options) {
|
|
|
6939
6931
|
results = await search(database, searchParams);
|
|
6940
6932
|
} catch (error) {
|
|
6941
6933
|
if (queryVector && isVectorDimensionMismatchError(error)) {
|
|
6942
|
-
|
|
6934
|
+
rememberSearchMode(modeKey, "fulltext (embedding dimension mismatch)");
|
|
6943
6935
|
console.error("[memorix] Vector search dimension mismatch detected, retrying without embeddings");
|
|
6944
6936
|
results = await search(database, stripVectorSearchParams(searchParams));
|
|
6945
6937
|
} else {
|
|
@@ -6961,7 +6953,7 @@ async function searchObservations(options) {
|
|
|
6961
6953
|
},
|
|
6962
6954
|
similarity: 0.25
|
|
6963
6955
|
};
|
|
6964
|
-
|
|
6956
|
+
rememberSearchMode(modeKey, "vector-only (hybrid empty fallback)");
|
|
6965
6957
|
results = await search(database, vectorOnlyParams);
|
|
6966
6958
|
} catch {
|
|
6967
6959
|
}
|
|
@@ -7202,7 +7194,7 @@ async function searchObservations(options) {
|
|
|
7202
7194
|
const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7203
7195
|
if (rerankedTop.length > 0) {
|
|
7204
7196
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
7205
|
-
|
|
7197
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
|
|
7206
7198
|
mark("rerank(usedNeural=true)");
|
|
7207
7199
|
}
|
|
7208
7200
|
}
|
|
@@ -7219,7 +7211,7 @@ async function searchObservations(options) {
|
|
|
7219
7211
|
);
|
|
7220
7212
|
mark(`rerank(usedLLM=${usedLLM})`);
|
|
7221
7213
|
if (usedLLM) {
|
|
7222
|
-
|
|
7214
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
|
|
7223
7215
|
const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
7224
7216
|
if (rerankedTop.length > 0) {
|
|
7225
7217
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
@@ -7292,13 +7284,6 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
7292
7284
|
const database = await getDb();
|
|
7293
7285
|
const results = [];
|
|
7294
7286
|
for (const id of ids) {
|
|
7295
|
-
if (projectId) {
|
|
7296
|
-
const cached = docByObservationKey.get(makeEntryKey(projectId, id));
|
|
7297
|
-
if (cached) {
|
|
7298
|
-
results.push(cached);
|
|
7299
|
-
continue;
|
|
7300
|
-
}
|
|
7301
|
-
}
|
|
7302
7287
|
const searchResult = await search(database, {
|
|
7303
7288
|
term: "",
|
|
7304
7289
|
includeVectors: true,
|
|
@@ -7398,7 +7383,7 @@ function formatTime(isoDate) {
|
|
|
7398
7383
|
return isoDate;
|
|
7399
7384
|
}
|
|
7400
7385
|
}
|
|
7401
|
-
var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration,
|
|
7386
|
+
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;
|
|
7402
7387
|
var init_orama_store = __esm({
|
|
7403
7388
|
"src/store/orama-store.ts"() {
|
|
7404
7389
|
"use strict";
|
|
@@ -7419,10 +7404,10 @@ var init_orama_store = __esm({
|
|
|
7419
7404
|
embeddingDimensions = null;
|
|
7420
7405
|
indexEmbeddingProvider = null;
|
|
7421
7406
|
deferredCachedVectorHydration = null;
|
|
7422
|
-
docByObservationKey = /* @__PURE__ */ new Map();
|
|
7423
7407
|
NON_CJK_HYBRID_SIMILARITY = 0.45;
|
|
7424
7408
|
lastSearchModeByProject = /* @__PURE__ */ new Map();
|
|
7425
7409
|
SEARCH_MODE_DEFAULT_KEY = "__global__";
|
|
7410
|
+
MAX_SEARCH_MODE_KEYS = 4096;
|
|
7426
7411
|
COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
7427
7412
|
COMMAND_STYLE_TITLE = /(\bfindstr\b|\bSelect-String\b|\bGet-Content\b|\bnpx\s+vitest\b|\bnpx\s+tsc\b|\b2>&1\b)/i;
|
|
7428
7413
|
COMMAND_LIKE_QUERY2 = /\b(git|npm|npx|pnpm|yarn|node|bash|powershell|curl|memorix)\b/i;
|
|
@@ -8322,6 +8307,56 @@ var init_evidence_store = __esm({
|
|
|
8322
8307
|
}
|
|
8323
8308
|
});
|
|
8324
8309
|
|
|
8310
|
+
// src/runtime/maintenance-targets.ts
|
|
8311
|
+
function rowToTarget(row) {
|
|
8312
|
+
return {
|
|
8313
|
+
projectId: row.project_id,
|
|
8314
|
+
projectRoot: row.project_root,
|
|
8315
|
+
dataDir: row.data_dir,
|
|
8316
|
+
updatedAt: Number(row.updated_at)
|
|
8317
|
+
};
|
|
8318
|
+
}
|
|
8319
|
+
var MaintenanceTargetStore;
|
|
8320
|
+
var init_maintenance_targets = __esm({
|
|
8321
|
+
"src/runtime/maintenance-targets.ts"() {
|
|
8322
|
+
"use strict";
|
|
8323
|
+
init_esm_shims();
|
|
8324
|
+
init_sqlite_db();
|
|
8325
|
+
MaintenanceTargetStore = class {
|
|
8326
|
+
db;
|
|
8327
|
+
constructor(dataDir) {
|
|
8328
|
+
this.db = getDatabase(dataDir);
|
|
8329
|
+
}
|
|
8330
|
+
register(input) {
|
|
8331
|
+
if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
|
|
8332
|
+
if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
|
|
8333
|
+
if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
|
|
8334
|
+
const now3 = input.now ?? Date.now();
|
|
8335
|
+
this.db.prepare(`
|
|
8336
|
+
INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
|
|
8337
|
+
VALUES (?, ?, ?, ?)
|
|
8338
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
8339
|
+
project_root = excluded.project_root,
|
|
8340
|
+
data_dir = excluded.data_dir,
|
|
8341
|
+
updated_at = excluded.updated_at
|
|
8342
|
+
`).run(input.projectId, input.projectRoot, input.dataDir, now3);
|
|
8343
|
+
return this.get(input.projectId);
|
|
8344
|
+
}
|
|
8345
|
+
get(projectId) {
|
|
8346
|
+
const row = this.db.prepare(`
|
|
8347
|
+
SELECT * FROM maintenance_targets WHERE project_id = ?
|
|
8348
|
+
`).get(projectId);
|
|
8349
|
+
return row ? rowToTarget(row) : void 0;
|
|
8350
|
+
}
|
|
8351
|
+
list() {
|
|
8352
|
+
return this.db.prepare(`
|
|
8353
|
+
SELECT * FROM maintenance_targets ORDER BY updated_at DESC
|
|
8354
|
+
`).all().map(rowToTarget);
|
|
8355
|
+
}
|
|
8356
|
+
};
|
|
8357
|
+
}
|
|
8358
|
+
});
|
|
8359
|
+
|
|
8325
8360
|
// src/runtime/isolated-maintenance.ts
|
|
8326
8361
|
import { spawn } from "child_process";
|
|
8327
8362
|
import { existsSync as existsSync8 } from "fs";
|
|
@@ -150037,10 +150072,21 @@ __export(vector_backfill_runner_exports, {
|
|
|
150037
150072
|
launchDetachedVectorBackfill: () => launchDetachedVectorBackfill,
|
|
150038
150073
|
main: () => main,
|
|
150039
150074
|
parseVectorBackfillRequest: () => parseVectorBackfillRequest,
|
|
150040
|
-
|
|
150075
|
+
releaseVectorBackfillLock: () => releaseVectorBackfillLock,
|
|
150076
|
+
resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath,
|
|
150077
|
+
vectorBackfillLockPath: () => vectorBackfillLockPath
|
|
150041
150078
|
});
|
|
150042
150079
|
import { spawn as spawn4 } from "child_process";
|
|
150043
|
-
import {
|
|
150080
|
+
import {
|
|
150081
|
+
closeSync,
|
|
150082
|
+
existsSync as existsSync12,
|
|
150083
|
+
mkdirSync as mkdirSync2,
|
|
150084
|
+
openSync,
|
|
150085
|
+
readFileSync as readFileSync9,
|
|
150086
|
+
statSync as statSync4,
|
|
150087
|
+
unlinkSync,
|
|
150088
|
+
writeFileSync as writeFileSync2
|
|
150089
|
+
} from "fs";
|
|
150044
150090
|
import path21 from "path";
|
|
150045
150091
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
150046
150092
|
function isNonEmptyString(value) {
|
|
@@ -150067,10 +150113,104 @@ function parseVectorBackfillRequest(raw) {
|
|
|
150067
150113
|
dataDir: value.dataDir
|
|
150068
150114
|
};
|
|
150069
150115
|
}
|
|
150116
|
+
function vectorBackfillLockPath(dataDir) {
|
|
150117
|
+
return path21.join(dataDir, VECTOR_BACKFILL_LOCK_FILE);
|
|
150118
|
+
}
|
|
150119
|
+
function processIsAlive(pid) {
|
|
150120
|
+
if (!Number.isSafeInteger(pid) || pid == null || pid <= 0) return false;
|
|
150121
|
+
try {
|
|
150122
|
+
process.kill(pid, 0);
|
|
150123
|
+
return true;
|
|
150124
|
+
} catch (error) {
|
|
150125
|
+
return error.code === "EPERM";
|
|
150126
|
+
}
|
|
150127
|
+
}
|
|
150128
|
+
function readVectorBackfillLock(lockPath) {
|
|
150129
|
+
try {
|
|
150130
|
+
const value = JSON.parse(readFileSync9(lockPath, "utf8"));
|
|
150131
|
+
if (typeof value.parentPid !== "number" || !Number.isSafeInteger(value.parentPid) || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.projectId !== "string") return void 0;
|
|
150132
|
+
return {
|
|
150133
|
+
parentPid: value.parentPid,
|
|
150134
|
+
...typeof value.childPid === "number" ? { childPid: value.childPid } : {},
|
|
150135
|
+
startedAt: value.startedAt,
|
|
150136
|
+
projectId: value.projectId
|
|
150137
|
+
};
|
|
150138
|
+
} catch {
|
|
150139
|
+
return void 0;
|
|
150140
|
+
}
|
|
150141
|
+
}
|
|
150142
|
+
function writeVectorBackfillLock(lockPath, value) {
|
|
150143
|
+
writeFileSync2(lockPath, `${JSON.stringify(value)}
|
|
150144
|
+
`, "utf8");
|
|
150145
|
+
}
|
|
150146
|
+
function acquireVectorBackfillLock(dataDir, projectId, preferredPath) {
|
|
150147
|
+
const lockPath = preferredPath ?? vectorBackfillLockPath(dataDir);
|
|
150148
|
+
mkdirSync2(path21.dirname(lockPath), { recursive: true });
|
|
150149
|
+
const initial = {
|
|
150150
|
+
parentPid: process.pid,
|
|
150151
|
+
startedAt: Date.now(),
|
|
150152
|
+
projectId
|
|
150153
|
+
};
|
|
150154
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
150155
|
+
try {
|
|
150156
|
+
const fd = openSync(lockPath, "wx");
|
|
150157
|
+
try {
|
|
150158
|
+
writeFileSync2(fd, `${JSON.stringify(initial)}
|
|
150159
|
+
`, "utf8");
|
|
150160
|
+
} finally {
|
|
150161
|
+
closeSync(fd);
|
|
150162
|
+
}
|
|
150163
|
+
return {
|
|
150164
|
+
path: lockPath,
|
|
150165
|
+
setChildPid(pid) {
|
|
150166
|
+
try {
|
|
150167
|
+
writeVectorBackfillLock(lockPath, { ...initial, childPid: pid });
|
|
150168
|
+
} catch {
|
|
150169
|
+
}
|
|
150170
|
+
},
|
|
150171
|
+
release() {
|
|
150172
|
+
try {
|
|
150173
|
+
unlinkSync(lockPath);
|
|
150174
|
+
} catch {
|
|
150175
|
+
}
|
|
150176
|
+
}
|
|
150177
|
+
};
|
|
150178
|
+
} catch (error) {
|
|
150179
|
+
if (error.code !== "EEXIST") return void 0;
|
|
150180
|
+
const existing = readVectorBackfillLock(lockPath);
|
|
150181
|
+
const ownerAlive = existing && (processIsAlive(existing.childPid) || processIsAlive(existing.parentPid));
|
|
150182
|
+
if (ownerAlive) return void 0;
|
|
150183
|
+
let stale = false;
|
|
150184
|
+
try {
|
|
150185
|
+
stale = Date.now() - statSync4(lockPath).mtimeMs >= STALE_LOCK_RECOVERY_MS;
|
|
150186
|
+
} catch {
|
|
150187
|
+
stale = false;
|
|
150188
|
+
}
|
|
150189
|
+
if (!existing && !stale) return void 0;
|
|
150190
|
+
try {
|
|
150191
|
+
unlinkSync(lockPath);
|
|
150192
|
+
} catch {
|
|
150193
|
+
return void 0;
|
|
150194
|
+
}
|
|
150195
|
+
}
|
|
150196
|
+
}
|
|
150197
|
+
return void 0;
|
|
150198
|
+
}
|
|
150199
|
+
function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFILL_LOCK) {
|
|
150200
|
+
if (!lockPath) return;
|
|
150201
|
+
const lock = readVectorBackfillLock(lockPath);
|
|
150202
|
+
if (lock && lock.childPid !== process.pid) return;
|
|
150203
|
+
try {
|
|
150204
|
+
unlinkSync(lockPath);
|
|
150205
|
+
} catch {
|
|
150206
|
+
}
|
|
150207
|
+
}
|
|
150070
150208
|
function launchDetachedVectorBackfill(request, options = {}) {
|
|
150071
150209
|
const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
|
|
150072
150210
|
const exists = options.exists ?? existsSync12;
|
|
150073
150211
|
if (!exists(runnerPath)) return false;
|
|
150212
|
+
const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
|
|
150213
|
+
if (!lock) return false;
|
|
150074
150214
|
try {
|
|
150075
150215
|
const child = (options.spawn ?? spawn4)(process.execPath, [runnerPath], {
|
|
150076
150216
|
cwd: request.projectRoot,
|
|
@@ -150080,14 +150220,23 @@ function launchDetachedVectorBackfill(request, options = {}) {
|
|
|
150080
150220
|
stdio: "ignore",
|
|
150081
150221
|
// The request contains only local project metadata, never credentials.
|
|
150082
150222
|
// Environment transport avoids a live stdin pipe keeping the CLI alive.
|
|
150083
|
-
env: {
|
|
150223
|
+
env: {
|
|
150224
|
+
...process.env,
|
|
150225
|
+
MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request),
|
|
150226
|
+
MEMORIX_VECTOR_BACKFILL_LOCK: lock.path
|
|
150227
|
+
},
|
|
150084
150228
|
windowsHide: true
|
|
150085
150229
|
});
|
|
150230
|
+
if (child.pid) lock.setChildPid(child.pid);
|
|
150231
|
+
else lock.release();
|
|
150086
150232
|
child.once?.("error", () => {
|
|
150087
150233
|
});
|
|
150234
|
+
child.once?.("close", () => lock.release());
|
|
150235
|
+
if (typeof child.once !== "function") lock.release();
|
|
150088
150236
|
child.unref();
|
|
150089
|
-
return
|
|
150237
|
+
return Boolean(child.pid);
|
|
150090
150238
|
} catch {
|
|
150239
|
+
lock.release();
|
|
150091
150240
|
return false;
|
|
150092
150241
|
}
|
|
150093
150242
|
}
|
|
@@ -150099,12 +150248,32 @@ async function executeVectorBackfill(request) {
|
|
|
150099
150248
|
await prepareSearchIndex();
|
|
150100
150249
|
await getDeferredCachedVectorHydration()?.catch(() => {
|
|
150101
150250
|
});
|
|
150251
|
+
const queue = new MaintenanceJobStore(request.dataDir);
|
|
150252
|
+
const targets = new MaintenanceTargetStore(request.dataDir);
|
|
150253
|
+
let preparedRoot = request.projectRoot;
|
|
150254
|
+
const prepareTarget = async (target) => {
|
|
150255
|
+
if (target.projectRoot === preparedRoot) return;
|
|
150256
|
+
initProjectRoot(target.projectRoot);
|
|
150257
|
+
loadDotenv(target.projectRoot);
|
|
150258
|
+
preparedRoot = target.projectRoot;
|
|
150259
|
+
};
|
|
150260
|
+
const handler = async (job) => {
|
|
150261
|
+
const target = targets.get(job.projectId) ?? (job.projectId === request.projectId ? { projectId: request.projectId, projectRoot: request.projectRoot, dataDir: request.dataDir, updatedAt: Date.now() } : void 0);
|
|
150262
|
+
if (!target) return { action: "reschedule", delayMs: 3e4 };
|
|
150263
|
+
await prepareTarget(target);
|
|
150264
|
+
return createProjectMaintenanceHandler(job.projectId, target.dataDir, target.projectRoot)(job);
|
|
150265
|
+
};
|
|
150102
150266
|
const worker = new MaintenanceJobWorker(
|
|
150103
|
-
|
|
150104
|
-
|
|
150105
|
-
{
|
|
150267
|
+
queue,
|
|
150268
|
+
handler,
|
|
150269
|
+
{ kinds: ["vector-backfill"] }
|
|
150106
150270
|
);
|
|
150107
|
-
|
|
150271
|
+
let result = { state: "idle" };
|
|
150272
|
+
while (true) {
|
|
150273
|
+
result = await worker.runOnce();
|
|
150274
|
+
if (result.state === "idle") break;
|
|
150275
|
+
}
|
|
150276
|
+
return result;
|
|
150108
150277
|
}
|
|
150109
150278
|
async function readStdin() {
|
|
150110
150279
|
return new Promise((resolve2, reject) => {
|
|
@@ -150127,9 +150296,11 @@ async function main() {
|
|
|
150127
150296
|
`);
|
|
150128
150297
|
process.exitCode = 1;
|
|
150129
150298
|
} finally {
|
|
150299
|
+
releaseVectorBackfillLock();
|
|
150130
150300
|
closeAllDatabases();
|
|
150131
150301
|
}
|
|
150132
150302
|
}
|
|
150303
|
+
var VECTOR_BACKFILL_LOCK_FILE, STALE_LOCK_RECOVERY_MS;
|
|
150133
150304
|
var init_vector_backfill_runner = __esm({
|
|
150134
150305
|
"src/runtime/vector-backfill-runner.ts"() {
|
|
150135
150306
|
"use strict";
|
|
@@ -150142,7 +150313,10 @@ var init_vector_backfill_runner = __esm({
|
|
|
150142
150313
|
init_orama_store();
|
|
150143
150314
|
init_sqlite_db();
|
|
150144
150315
|
init_maintenance_jobs();
|
|
150316
|
+
init_maintenance_targets();
|
|
150145
150317
|
init_project_maintenance();
|
|
150318
|
+
VECTOR_BACKFILL_LOCK_FILE = ".memorix-vector-backfill.lock";
|
|
150319
|
+
STALE_LOCK_RECOVERY_MS = 24 * 60 * 60 * 1e3;
|
|
150146
150320
|
if (process.argv[1] && process.argv[1].endsWith("vector-backfill-runner.js")) {
|
|
150147
150321
|
void main();
|
|
150148
150322
|
}
|
|
@@ -150184,11 +150358,17 @@ function resetObservationRuntime() {
|
|
|
150184
150358
|
embeddingWorkerProjectRoot = void 0;
|
|
150185
150359
|
vectorMissingIds.clear();
|
|
150186
150360
|
vectorSchemaUpgradePromise = null;
|
|
150361
|
+
embeddingFailureLogTimestamps.clear();
|
|
150187
150362
|
}
|
|
150188
150363
|
function logEmbeddingFailureOnce(key, message) {
|
|
150189
150364
|
const now3 = Date.now();
|
|
150190
150365
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
150191
150366
|
if (now3 - last < EMBEDDING_FAILURE_LOG_COOLDOWN_MS) return;
|
|
150367
|
+
if (!embeddingFailureLogTimestamps.has(key) && embeddingFailureLogTimestamps.size >= MAX_EMBEDDING_FAILURE_KEYS) {
|
|
150368
|
+
const oldest = embeddingFailureLogTimestamps.keys().next().value;
|
|
150369
|
+
if (oldest !== void 0) embeddingFailureLogTimestamps.delete(oldest);
|
|
150370
|
+
}
|
|
150371
|
+
embeddingFailureLogTimestamps.delete(key);
|
|
150192
150372
|
embeddingFailureLogTimestamps.set(key, now3);
|
|
150193
150373
|
console.error(message);
|
|
150194
150374
|
}
|
|
@@ -150218,13 +150398,14 @@ function queueVectorBackfill(projectId, options = {}) {
|
|
|
150218
150398
|
const dataDir = projectDir;
|
|
150219
150399
|
if (!dataDir) return;
|
|
150220
150400
|
void Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)).then(({ MaintenanceJobStore: MaintenanceJobStore2 }) => {
|
|
150221
|
-
new MaintenanceJobStore2(dataDir).enqueue({
|
|
150401
|
+
const job = new MaintenanceJobStore2(dataDir).enqueue({
|
|
150222
150402
|
projectId,
|
|
150223
150403
|
kind: "vector-backfill",
|
|
150224
150404
|
dedupeKey: "vector-backfill",
|
|
150225
150405
|
payload: { limit: 12 }
|
|
150226
150406
|
});
|
|
150227
|
-
|
|
150407
|
+
const due = !job || job.status === "pending" && job.runAfter <= Date.now();
|
|
150408
|
+
if (due && options.detachedWorker && options.projectRoot) {
|
|
150228
150409
|
void Promise.resolve().then(() => (init_vector_backfill_runner(), vector_backfill_runner_exports)).then(({ launchDetachedVectorBackfill: launchDetachedVectorBackfill2 }) => {
|
|
150229
150410
|
launchDetachedVectorBackfill2({
|
|
150230
150411
|
projectId,
|
|
@@ -151155,7 +151336,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
151155
151336
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
151156
151337
|
};
|
|
151157
151338
|
}
|
|
151158
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
151339
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS, MAX_EMBEDDING_FAILURE_KEYS;
|
|
151159
151340
|
var init_observations = __esm({
|
|
151160
151341
|
"src/memory/observations.ts"() {
|
|
151161
151342
|
"use strict";
|
|
@@ -151183,6 +151364,7 @@ var init_observations = __esm({
|
|
|
151183
151364
|
lastVectorBackfill = null;
|
|
151184
151365
|
embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
|
|
151185
151366
|
EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
|
|
151367
|
+
MAX_EMBEDDING_FAILURE_KEYS = 1024;
|
|
151186
151368
|
}
|
|
151187
151369
|
});
|
|
151188
151370
|
|