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
|
@@ -10,11 +10,14 @@ interface VectorBackfillLauncherOptions {
|
|
|
10
10
|
runnerPath?: string;
|
|
11
11
|
exists?: (path: string) => boolean;
|
|
12
12
|
spawn?: typeof spawn;
|
|
13
|
+
lockPath?: string;
|
|
13
14
|
}
|
|
14
15
|
/** Resolve the standalone worker next to either the library or CLI bundle. */
|
|
15
16
|
declare function resolveVectorBackfillRunnerPath(moduleUrl?: string): string;
|
|
16
17
|
/** Parse the internal request passed from a short-lived CLI process. */
|
|
17
18
|
declare function parseVectorBackfillRequest(raw: string): VectorBackfillRequest;
|
|
19
|
+
declare function vectorBackfillLockPath(dataDir: string): string;
|
|
20
|
+
declare function releaseVectorBackfillLock(lockPath?: string | undefined): void;
|
|
18
21
|
/**
|
|
19
22
|
* Start a detached one-shot worker. The caller has already persisted the
|
|
20
23
|
* observation and durable vector job, so failure to start is recoverable by a
|
|
@@ -28,4 +31,4 @@ declare function executeVectorBackfill(request: VectorBackfillRequest): Promise<
|
|
|
28
31
|
}>;
|
|
29
32
|
declare function main(): Promise<void>;
|
|
30
33
|
|
|
31
|
-
export { type VectorBackfillLauncherOptions, type VectorBackfillRequest, executeVectorBackfill, launchDetachedVectorBackfill, main, parseVectorBackfillRequest, resolveVectorBackfillRunnerPath };
|
|
34
|
+
export { type VectorBackfillLauncherOptions, type VectorBackfillRequest, executeVectorBackfill, launchDetachedVectorBackfill, main, parseVectorBackfillRequest, releaseVectorBackfillLock, resolveVectorBackfillRunnerPath, vectorBackfillLockPath };
|
|
@@ -5993,6 +5993,14 @@ __export(orama_store_exports, {
|
|
|
5993
5993
|
updateObservationMetadata: () => updateObservationMetadata
|
|
5994
5994
|
});
|
|
5995
5995
|
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5996
|
+
function rememberSearchMode(key, value) {
|
|
5997
|
+
if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
|
|
5998
|
+
const oldest = lastSearchModeByProject.keys().next().value;
|
|
5999
|
+
if (oldest !== void 0) lastSearchModeByProject.delete(oldest);
|
|
6000
|
+
}
|
|
6001
|
+
lastSearchModeByProject.delete(key);
|
|
6002
|
+
lastSearchModeByProject.set(key, value);
|
|
6003
|
+
}
|
|
5996
6004
|
function getLastSearchMode(projectId) {
|
|
5997
6005
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
5998
6006
|
}
|
|
@@ -6005,9 +6013,6 @@ function makeEntryKey(projectId, observationId2) {
|
|
|
6005
6013
|
function rememberObservationDoc(doc) {
|
|
6006
6014
|
const publicDoc = { ...doc };
|
|
6007
6015
|
delete publicDoc.embedding;
|
|
6008
|
-
if (doc.projectId && typeof doc.observationId === "number") {
|
|
6009
|
-
docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
|
|
6010
|
-
}
|
|
6011
6016
|
return publicDoc;
|
|
6012
6017
|
}
|
|
6013
6018
|
function isCommandLikeQuery(query) {
|
|
@@ -6111,7 +6116,6 @@ async function resetDb() {
|
|
|
6111
6116
|
indexEmbeddingProvider = null;
|
|
6112
6117
|
deferredCachedVectorHydration = null;
|
|
6113
6118
|
lastSearchModeByProject.clear();
|
|
6114
|
-
docByObservationKey.clear();
|
|
6115
6119
|
}
|
|
6116
6120
|
function isEmbeddingEnabled() {
|
|
6117
6121
|
return embeddingEnabled;
|
|
@@ -6174,78 +6178,72 @@ function isCompatibleCachedVector(vector) {
|
|
|
6174
6178
|
vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
|
|
6175
6179
|
);
|
|
6176
6180
|
}
|
|
6177
|
-
async function attachCachedVectors(database,
|
|
6178
|
-
const
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6181
|
+
async function attachCachedVectors(database, observations2) {
|
|
6182
|
+
const batchSize = 200;
|
|
6183
|
+
for (let start = 0; start < observations2.length; start += batchSize) {
|
|
6184
|
+
const batch = observations2.slice(start, start + batchSize);
|
|
6185
|
+
const cachedVectors = await getCachedEmbeddings(batch.map(observationEmbeddingText));
|
|
6186
|
+
if (db !== database) return;
|
|
6187
|
+
for (let index = 0; index < batch.length; index++) {
|
|
6188
|
+
const vector = cachedVectors[index];
|
|
6189
|
+
if (!isCompatibleCachedVector(vector)) continue;
|
|
6190
|
+
const id = makeOramaObservationId(batch[index].projectId, batch[index].id);
|
|
6191
|
+
const existing = getByID(database, id);
|
|
6192
|
+
if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(batch[index])) continue;
|
|
6193
|
+
try {
|
|
6194
|
+
await update(database, id, { ...existing, embedding: vector });
|
|
6195
|
+
} catch {
|
|
6196
|
+
}
|
|
6193
6197
|
}
|
|
6198
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
6194
6199
|
}
|
|
6195
6200
|
}
|
|
6196
6201
|
async function hydrateIndex(observations2, options = {}) {
|
|
6197
6202
|
const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
|
|
6198
|
-
const candidates = [];
|
|
6199
|
-
for (const observation of observations2) {
|
|
6200
|
-
if (!observation || !observation.id || !observation.projectId) continue;
|
|
6201
|
-
const id = makeOramaObservationId(observation.projectId, observation.id);
|
|
6202
|
-
if (getByID(database, id)) continue;
|
|
6203
|
-
candidates.push({ observation, id });
|
|
6204
|
-
}
|
|
6205
6203
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
6206
|
-
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
6207
|
-
const documents = [];
|
|
6208
|
-
for (let index = 0; index < candidates.length; index++) {
|
|
6209
|
-
const { observation: obs, id } = candidates[index];
|
|
6210
|
-
try {
|
|
6211
|
-
const vector = cachedVectors[index];
|
|
6212
|
-
const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
|
|
6213
|
-
const doc = {
|
|
6214
|
-
id,
|
|
6215
|
-
observationId: obs.id,
|
|
6216
|
-
entityName: obs.entityName || "",
|
|
6217
|
-
type: obs.type || "discovery",
|
|
6218
|
-
title: obs.title || "",
|
|
6219
|
-
narrative: obs.narrative || "",
|
|
6220
|
-
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
6221
|
-
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
6222
|
-
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
6223
|
-
tokens: obs.tokens ?? 0,
|
|
6224
|
-
createdAt: obs.createdAt || "",
|
|
6225
|
-
projectId: obs.projectId,
|
|
6226
|
-
accessCount: obs.accessCount ?? 0,
|
|
6227
|
-
lastAccessedAt: obs.lastAccessedAt || "",
|
|
6228
|
-
status: obs.status ?? "active",
|
|
6229
|
-
source: obs.source || "agent",
|
|
6230
|
-
sourceDetail: obs.sourceDetail ?? "",
|
|
6231
|
-
valueCategory: obs.valueCategory ?? "",
|
|
6232
|
-
admissionState: obs.admissionState ?? "",
|
|
6233
|
-
admissionReason: obs.admissionReason ?? "",
|
|
6234
|
-
visibility: obs.visibility ?? "project",
|
|
6235
|
-
createdByAgentId: obs.createdByAgentId ?? "",
|
|
6236
|
-
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
6237
|
-
documentType: "observation",
|
|
6238
|
-
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6239
|
-
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6240
|
-
};
|
|
6241
|
-
documents.push(doc);
|
|
6242
|
-
} catch {
|
|
6243
|
-
}
|
|
6244
|
-
}
|
|
6245
6204
|
let inserted = 0;
|
|
6246
6205
|
const hydrationBatchSize = 200;
|
|
6247
|
-
for (let start = 0; start <
|
|
6248
|
-
const
|
|
6206
|
+
for (let start = 0; start < observations2.length; start += hydrationBatchSize) {
|
|
6207
|
+
const batchObservations = observations2.slice(start, start + hydrationBatchSize).filter((observation) => observation && observation.id && observation.projectId).filter((observation) => !getByID(database, makeOramaObservationId(observation.projectId, observation.id)));
|
|
6208
|
+
if (batchObservations.length === 0) continue;
|
|
6209
|
+
const cachedVectors = deferCachedVectors || options.skipCachedVectors ? batchObservations.map(() => null) : await getCachedEmbeddings(batchObservations.map(observationEmbeddingText));
|
|
6210
|
+
const batch = [];
|
|
6211
|
+
for (let index = 0; index < batchObservations.length; index++) {
|
|
6212
|
+
const obs = batchObservations[index];
|
|
6213
|
+
try {
|
|
6214
|
+
const compatibleVector = isCompatibleCachedVector(cachedVectors[index]) ? cachedVectors[index] : null;
|
|
6215
|
+
batch.push({
|
|
6216
|
+
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
6217
|
+
observationId: obs.id,
|
|
6218
|
+
entityName: obs.entityName || "",
|
|
6219
|
+
type: obs.type || "discovery",
|
|
6220
|
+
title: obs.title || "",
|
|
6221
|
+
narrative: obs.narrative || "",
|
|
6222
|
+
facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
|
|
6223
|
+
filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
|
|
6224
|
+
concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
|
|
6225
|
+
tokens: obs.tokens ?? 0,
|
|
6226
|
+
createdAt: obs.createdAt || "",
|
|
6227
|
+
projectId: obs.projectId,
|
|
6228
|
+
accessCount: obs.accessCount ?? 0,
|
|
6229
|
+
lastAccessedAt: obs.lastAccessedAt || "",
|
|
6230
|
+
status: obs.status ?? "active",
|
|
6231
|
+
source: obs.source || "agent",
|
|
6232
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
6233
|
+
valueCategory: obs.valueCategory ?? "",
|
|
6234
|
+
admissionState: obs.admissionState ?? "",
|
|
6235
|
+
admissionReason: obs.admissionReason ?? "",
|
|
6236
|
+
visibility: obs.visibility ?? "project",
|
|
6237
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
6238
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
6239
|
+
documentType: "observation",
|
|
6240
|
+
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6241
|
+
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6242
|
+
});
|
|
6243
|
+
} catch {
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
if (batch.length === 0) continue;
|
|
6249
6247
|
try {
|
|
6250
6248
|
await insertMultiple(database, batch, batch.length);
|
|
6251
6249
|
for (const doc of batch) rememberObservationDoc(doc);
|
|
@@ -6267,7 +6265,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6267
6265
|
}
|
|
6268
6266
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
6269
6267
|
}
|
|
6270
|
-
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database,
|
|
6268
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, observations2).catch(() => {
|
|
6271
6269
|
}) : null;
|
|
6272
6270
|
return inserted;
|
|
6273
6271
|
}
|
|
@@ -6304,12 +6302,6 @@ async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
|
6304
6302
|
async function removeObservation(oramaId) {
|
|
6305
6303
|
const database = await getDb();
|
|
6306
6304
|
await remove2(database, oramaId);
|
|
6307
|
-
for (const [key, doc] of docByObservationKey.entries()) {
|
|
6308
|
-
if (doc.id === oramaId) {
|
|
6309
|
-
docByObservationKey.delete(key);
|
|
6310
|
-
break;
|
|
6311
|
-
}
|
|
6312
|
-
}
|
|
6313
6305
|
}
|
|
6314
6306
|
async function searchObservations(options) {
|
|
6315
6307
|
const perf = !!process.env.MEMORIX_PERF;
|
|
@@ -6324,7 +6316,7 @@ async function searchObservations(options) {
|
|
|
6324
6316
|
const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
|
|
6325
6317
|
const quality = options.quality ?? "balanced";
|
|
6326
6318
|
const database = await getDb();
|
|
6327
|
-
|
|
6319
|
+
rememberSearchMode(
|
|
6328
6320
|
modeKey,
|
|
6329
6321
|
quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
|
|
6330
6322
|
);
|
|
@@ -6391,7 +6383,7 @@ async function searchObservations(options) {
|
|
|
6391
6383
|
if (provider2) {
|
|
6392
6384
|
const activeVectorDimensions = getVectorDimensions();
|
|
6393
6385
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
6394
|
-
|
|
6386
|
+
rememberSearchMode(
|
|
6395
6387
|
modeKey,
|
|
6396
6388
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
6397
6389
|
);
|
|
@@ -6416,7 +6408,7 @@ async function searchObservations(options) {
|
|
|
6416
6408
|
mark("embedding");
|
|
6417
6409
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
6418
6410
|
const isCJKHeavy = cjkRatio > 0.3;
|
|
6419
|
-
|
|
6411
|
+
rememberSearchMode(modeKey, "hybrid");
|
|
6420
6412
|
searchParams = {
|
|
6421
6413
|
...searchParams,
|
|
6422
6414
|
mode: "hybrid",
|
|
@@ -6432,7 +6424,7 @@ async function searchObservations(options) {
|
|
|
6432
6424
|
}
|
|
6433
6425
|
}
|
|
6434
6426
|
} catch (error) {
|
|
6435
|
-
|
|
6427
|
+
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
6436
6428
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
6437
6429
|
}
|
|
6438
6430
|
}
|
|
@@ -6442,7 +6434,7 @@ async function searchObservations(options) {
|
|
|
6442
6434
|
results = await search(database, searchParams);
|
|
6443
6435
|
} catch (error) {
|
|
6444
6436
|
if (queryVector && isVectorDimensionMismatchError(error)) {
|
|
6445
|
-
|
|
6437
|
+
rememberSearchMode(modeKey, "fulltext (embedding dimension mismatch)");
|
|
6446
6438
|
console.error("[memorix] Vector search dimension mismatch detected, retrying without embeddings");
|
|
6447
6439
|
results = await search(database, stripVectorSearchParams(searchParams));
|
|
6448
6440
|
} else {
|
|
@@ -6464,7 +6456,7 @@ async function searchObservations(options) {
|
|
|
6464
6456
|
},
|
|
6465
6457
|
similarity: 0.25
|
|
6466
6458
|
};
|
|
6467
|
-
|
|
6459
|
+
rememberSearchMode(modeKey, "vector-only (hybrid empty fallback)");
|
|
6468
6460
|
results = await search(database, vectorOnlyParams);
|
|
6469
6461
|
} catch {
|
|
6470
6462
|
}
|
|
@@ -6705,7 +6697,7 @@ async function searchObservations(options) {
|
|
|
6705
6697
|
const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
6706
6698
|
if (rerankedTop.length > 0) {
|
|
6707
6699
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
6708
|
-
|
|
6700
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
|
|
6709
6701
|
mark("rerank(usedNeural=true)");
|
|
6710
6702
|
}
|
|
6711
6703
|
}
|
|
@@ -6722,7 +6714,7 @@ async function searchObservations(options) {
|
|
|
6722
6714
|
);
|
|
6723
6715
|
mark(`rerank(usedLLM=${usedLLM})`);
|
|
6724
6716
|
if (usedLLM) {
|
|
6725
|
-
|
|
6717
|
+
rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
|
|
6726
6718
|
const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
|
|
6727
6719
|
if (rerankedTop.length > 0) {
|
|
6728
6720
|
intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
|
|
@@ -6795,13 +6787,6 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
6795
6787
|
const database = await getDb();
|
|
6796
6788
|
const results = [];
|
|
6797
6789
|
for (const id of ids) {
|
|
6798
|
-
if (projectId) {
|
|
6799
|
-
const cached = docByObservationKey.get(makeEntryKey(projectId, id));
|
|
6800
|
-
if (cached) {
|
|
6801
|
-
results.push(cached);
|
|
6802
|
-
continue;
|
|
6803
|
-
}
|
|
6804
|
-
}
|
|
6805
6790
|
const searchResult = await search(database, {
|
|
6806
6791
|
term: "",
|
|
6807
6792
|
includeVectors: true,
|
|
@@ -6901,7 +6886,7 @@ function formatTime(isoDate) {
|
|
|
6901
6886
|
return isoDate;
|
|
6902
6887
|
}
|
|
6903
6888
|
}
|
|
6904
|
-
var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration,
|
|
6889
|
+
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;
|
|
6905
6890
|
var init_orama_store = __esm({
|
|
6906
6891
|
"src/store/orama-store.ts"() {
|
|
6907
6892
|
"use strict";
|
|
@@ -6922,10 +6907,10 @@ var init_orama_store = __esm({
|
|
|
6922
6907
|
embeddingDimensions = null;
|
|
6923
6908
|
indexEmbeddingProvider = null;
|
|
6924
6909
|
deferredCachedVectorHydration = null;
|
|
6925
|
-
docByObservationKey = /* @__PURE__ */ new Map();
|
|
6926
6910
|
NON_CJK_HYBRID_SIMILARITY = 0.45;
|
|
6927
6911
|
lastSearchModeByProject = /* @__PURE__ */ new Map();
|
|
6928
6912
|
SEARCH_MODE_DEFAULT_KEY = "__global__";
|
|
6913
|
+
MAX_SEARCH_MODE_KEYS = 4096;
|
|
6929
6914
|
COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
6930
6915
|
COMMAND_STYLE_TITLE = /(\bfindstr\b|\bSelect-String\b|\bGet-Content\b|\bnpx\s+vitest\b|\bnpx\s+tsc\b|\b2>&1\b)/i;
|
|
6931
6916
|
COMMAND_LIKE_QUERY2 = /\b(git|npm|npx|pnpm|yarn|node|bash|powershell|curl|memorix)\b/i;
|
|
@@ -9563,11 +9548,17 @@ function resetObservationRuntime() {
|
|
|
9563
9548
|
embeddingWorkerProjectRoot = void 0;
|
|
9564
9549
|
vectorMissingIds.clear();
|
|
9565
9550
|
vectorSchemaUpgradePromise = null;
|
|
9551
|
+
embeddingFailureLogTimestamps.clear();
|
|
9566
9552
|
}
|
|
9567
9553
|
function logEmbeddingFailureOnce(key, message) {
|
|
9568
9554
|
const now3 = Date.now();
|
|
9569
9555
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
9570
9556
|
if (now3 - last < EMBEDDING_FAILURE_LOG_COOLDOWN_MS) return;
|
|
9557
|
+
if (!embeddingFailureLogTimestamps.has(key) && embeddingFailureLogTimestamps.size >= MAX_EMBEDDING_FAILURE_KEYS) {
|
|
9558
|
+
const oldest = embeddingFailureLogTimestamps.keys().next().value;
|
|
9559
|
+
if (oldest !== void 0) embeddingFailureLogTimestamps.delete(oldest);
|
|
9560
|
+
}
|
|
9561
|
+
embeddingFailureLogTimestamps.delete(key);
|
|
9571
9562
|
embeddingFailureLogTimestamps.set(key, now3);
|
|
9572
9563
|
console.error(message);
|
|
9573
9564
|
}
|
|
@@ -9597,13 +9588,14 @@ function queueVectorBackfill(projectId, options = {}) {
|
|
|
9597
9588
|
const dataDir = projectDir;
|
|
9598
9589
|
if (!dataDir) return;
|
|
9599
9590
|
void Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)).then(({ MaintenanceJobStore: MaintenanceJobStore2 }) => {
|
|
9600
|
-
new MaintenanceJobStore2(dataDir).enqueue({
|
|
9591
|
+
const job = new MaintenanceJobStore2(dataDir).enqueue({
|
|
9601
9592
|
projectId,
|
|
9602
9593
|
kind: "vector-backfill",
|
|
9603
9594
|
dedupeKey: "vector-backfill",
|
|
9604
9595
|
payload: { limit: 12 }
|
|
9605
9596
|
});
|
|
9606
|
-
|
|
9597
|
+
const due = !job || job.status === "pending" && job.runAfter <= Date.now();
|
|
9598
|
+
if (due && options.detachedWorker && options.projectRoot) {
|
|
9607
9599
|
void Promise.resolve().then(() => (init_vector_backfill_runner(), vector_backfill_runner_exports)).then(({ launchDetachedVectorBackfill: launchDetachedVectorBackfill2 }) => {
|
|
9608
9600
|
launchDetachedVectorBackfill2({
|
|
9609
9601
|
projectId,
|
|
@@ -10534,7 +10526,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
10534
10526
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
10535
10527
|
};
|
|
10536
10528
|
}
|
|
10537
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
10529
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS, MAX_EMBEDDING_FAILURE_KEYS;
|
|
10538
10530
|
var init_observations = __esm({
|
|
10539
10531
|
"src/memory/observations.ts"() {
|
|
10540
10532
|
"use strict";
|
|
@@ -10562,6 +10554,57 @@ var init_observations = __esm({
|
|
|
10562
10554
|
lastVectorBackfill = null;
|
|
10563
10555
|
embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
|
|
10564
10556
|
EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
|
|
10557
|
+
MAX_EMBEDDING_FAILURE_KEYS = 1024;
|
|
10558
|
+
}
|
|
10559
|
+
});
|
|
10560
|
+
|
|
10561
|
+
// src/runtime/maintenance-targets.ts
|
|
10562
|
+
function rowToTarget(row) {
|
|
10563
|
+
return {
|
|
10564
|
+
projectId: row.project_id,
|
|
10565
|
+
projectRoot: row.project_root,
|
|
10566
|
+
dataDir: row.data_dir,
|
|
10567
|
+
updatedAt: Number(row.updated_at)
|
|
10568
|
+
};
|
|
10569
|
+
}
|
|
10570
|
+
var MaintenanceTargetStore;
|
|
10571
|
+
var init_maintenance_targets = __esm({
|
|
10572
|
+
"src/runtime/maintenance-targets.ts"() {
|
|
10573
|
+
"use strict";
|
|
10574
|
+
init_esm_shims();
|
|
10575
|
+
init_sqlite_db();
|
|
10576
|
+
MaintenanceTargetStore = class {
|
|
10577
|
+
db;
|
|
10578
|
+
constructor(dataDir) {
|
|
10579
|
+
this.db = getDatabase(dataDir);
|
|
10580
|
+
}
|
|
10581
|
+
register(input) {
|
|
10582
|
+
if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
|
|
10583
|
+
if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
|
|
10584
|
+
if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
|
|
10585
|
+
const now3 = input.now ?? Date.now();
|
|
10586
|
+
this.db.prepare(`
|
|
10587
|
+
INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
|
|
10588
|
+
VALUES (?, ?, ?, ?)
|
|
10589
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
10590
|
+
project_root = excluded.project_root,
|
|
10591
|
+
data_dir = excluded.data_dir,
|
|
10592
|
+
updated_at = excluded.updated_at
|
|
10593
|
+
`).run(input.projectId, input.projectRoot, input.dataDir, now3);
|
|
10594
|
+
return this.get(input.projectId);
|
|
10595
|
+
}
|
|
10596
|
+
get(projectId) {
|
|
10597
|
+
const row = this.db.prepare(`
|
|
10598
|
+
SELECT * FROM maintenance_targets WHERE project_id = ?
|
|
10599
|
+
`).get(projectId);
|
|
10600
|
+
return row ? rowToTarget(row) : void 0;
|
|
10601
|
+
}
|
|
10602
|
+
list() {
|
|
10603
|
+
return this.db.prepare(`
|
|
10604
|
+
SELECT * FROM maintenance_targets ORDER BY updated_at DESC
|
|
10605
|
+
`).all().map(rowToTarget);
|
|
10606
|
+
}
|
|
10607
|
+
};
|
|
10565
10608
|
}
|
|
10566
10609
|
});
|
|
10567
10610
|
|
|
@@ -151044,10 +151087,21 @@ __export(vector_backfill_runner_exports, {
|
|
|
151044
151087
|
launchDetachedVectorBackfill: () => launchDetachedVectorBackfill,
|
|
151045
151088
|
main: () => main,
|
|
151046
151089
|
parseVectorBackfillRequest: () => parseVectorBackfillRequest,
|
|
151047
|
-
|
|
151090
|
+
releaseVectorBackfillLock: () => releaseVectorBackfillLock,
|
|
151091
|
+
resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath,
|
|
151092
|
+
vectorBackfillLockPath: () => vectorBackfillLockPath
|
|
151048
151093
|
});
|
|
151049
151094
|
import { spawn as spawn3 } from "child_process";
|
|
151050
|
-
import {
|
|
151095
|
+
import {
|
|
151096
|
+
closeSync,
|
|
151097
|
+
existsSync as existsSync11,
|
|
151098
|
+
mkdirSync as mkdirSync2,
|
|
151099
|
+
openSync,
|
|
151100
|
+
readFileSync as readFileSync9,
|
|
151101
|
+
statSync as statSync4,
|
|
151102
|
+
unlinkSync,
|
|
151103
|
+
writeFileSync as writeFileSync2
|
|
151104
|
+
} from "fs";
|
|
151051
151105
|
import path20 from "path";
|
|
151052
151106
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
151053
151107
|
function isNonEmptyString(value) {
|
|
@@ -151074,10 +151128,104 @@ function parseVectorBackfillRequest(raw) {
|
|
|
151074
151128
|
dataDir: value.dataDir
|
|
151075
151129
|
};
|
|
151076
151130
|
}
|
|
151131
|
+
function vectorBackfillLockPath(dataDir) {
|
|
151132
|
+
return path20.join(dataDir, VECTOR_BACKFILL_LOCK_FILE);
|
|
151133
|
+
}
|
|
151134
|
+
function processIsAlive(pid) {
|
|
151135
|
+
if (!Number.isSafeInteger(pid) || pid == null || pid <= 0) return false;
|
|
151136
|
+
try {
|
|
151137
|
+
process.kill(pid, 0);
|
|
151138
|
+
return true;
|
|
151139
|
+
} catch (error) {
|
|
151140
|
+
return error.code === "EPERM";
|
|
151141
|
+
}
|
|
151142
|
+
}
|
|
151143
|
+
function readVectorBackfillLock(lockPath) {
|
|
151144
|
+
try {
|
|
151145
|
+
const value = JSON.parse(readFileSync9(lockPath, "utf8"));
|
|
151146
|
+
if (typeof value.parentPid !== "number" || !Number.isSafeInteger(value.parentPid) || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.projectId !== "string") return void 0;
|
|
151147
|
+
return {
|
|
151148
|
+
parentPid: value.parentPid,
|
|
151149
|
+
...typeof value.childPid === "number" ? { childPid: value.childPid } : {},
|
|
151150
|
+
startedAt: value.startedAt,
|
|
151151
|
+
projectId: value.projectId
|
|
151152
|
+
};
|
|
151153
|
+
} catch {
|
|
151154
|
+
return void 0;
|
|
151155
|
+
}
|
|
151156
|
+
}
|
|
151157
|
+
function writeVectorBackfillLock(lockPath, value) {
|
|
151158
|
+
writeFileSync2(lockPath, `${JSON.stringify(value)}
|
|
151159
|
+
`, "utf8");
|
|
151160
|
+
}
|
|
151161
|
+
function acquireVectorBackfillLock(dataDir, projectId, preferredPath) {
|
|
151162
|
+
const lockPath = preferredPath ?? vectorBackfillLockPath(dataDir);
|
|
151163
|
+
mkdirSync2(path20.dirname(lockPath), { recursive: true });
|
|
151164
|
+
const initial = {
|
|
151165
|
+
parentPid: process.pid,
|
|
151166
|
+
startedAt: Date.now(),
|
|
151167
|
+
projectId
|
|
151168
|
+
};
|
|
151169
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
151170
|
+
try {
|
|
151171
|
+
const fd = openSync(lockPath, "wx");
|
|
151172
|
+
try {
|
|
151173
|
+
writeFileSync2(fd, `${JSON.stringify(initial)}
|
|
151174
|
+
`, "utf8");
|
|
151175
|
+
} finally {
|
|
151176
|
+
closeSync(fd);
|
|
151177
|
+
}
|
|
151178
|
+
return {
|
|
151179
|
+
path: lockPath,
|
|
151180
|
+
setChildPid(pid) {
|
|
151181
|
+
try {
|
|
151182
|
+
writeVectorBackfillLock(lockPath, { ...initial, childPid: pid });
|
|
151183
|
+
} catch {
|
|
151184
|
+
}
|
|
151185
|
+
},
|
|
151186
|
+
release() {
|
|
151187
|
+
try {
|
|
151188
|
+
unlinkSync(lockPath);
|
|
151189
|
+
} catch {
|
|
151190
|
+
}
|
|
151191
|
+
}
|
|
151192
|
+
};
|
|
151193
|
+
} catch (error) {
|
|
151194
|
+
if (error.code !== "EEXIST") return void 0;
|
|
151195
|
+
const existing = readVectorBackfillLock(lockPath);
|
|
151196
|
+
const ownerAlive = existing && (processIsAlive(existing.childPid) || processIsAlive(existing.parentPid));
|
|
151197
|
+
if (ownerAlive) return void 0;
|
|
151198
|
+
let stale = false;
|
|
151199
|
+
try {
|
|
151200
|
+
stale = Date.now() - statSync4(lockPath).mtimeMs >= STALE_LOCK_RECOVERY_MS;
|
|
151201
|
+
} catch {
|
|
151202
|
+
stale = false;
|
|
151203
|
+
}
|
|
151204
|
+
if (!existing && !stale) return void 0;
|
|
151205
|
+
try {
|
|
151206
|
+
unlinkSync(lockPath);
|
|
151207
|
+
} catch {
|
|
151208
|
+
return void 0;
|
|
151209
|
+
}
|
|
151210
|
+
}
|
|
151211
|
+
}
|
|
151212
|
+
return void 0;
|
|
151213
|
+
}
|
|
151214
|
+
function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFILL_LOCK) {
|
|
151215
|
+
if (!lockPath) return;
|
|
151216
|
+
const lock = readVectorBackfillLock(lockPath);
|
|
151217
|
+
if (lock && lock.childPid !== process.pid) return;
|
|
151218
|
+
try {
|
|
151219
|
+
unlinkSync(lockPath);
|
|
151220
|
+
} catch {
|
|
151221
|
+
}
|
|
151222
|
+
}
|
|
151077
151223
|
function launchDetachedVectorBackfill(request, options = {}) {
|
|
151078
151224
|
const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
|
|
151079
151225
|
const exists = options.exists ?? existsSync11;
|
|
151080
151226
|
if (!exists(runnerPath)) return false;
|
|
151227
|
+
const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
|
|
151228
|
+
if (!lock) return false;
|
|
151081
151229
|
try {
|
|
151082
151230
|
const child = (options.spawn ?? spawn3)(process.execPath, [runnerPath], {
|
|
151083
151231
|
cwd: request.projectRoot,
|
|
@@ -151087,14 +151235,23 @@ function launchDetachedVectorBackfill(request, options = {}) {
|
|
|
151087
151235
|
stdio: "ignore",
|
|
151088
151236
|
// The request contains only local project metadata, never credentials.
|
|
151089
151237
|
// Environment transport avoids a live stdin pipe keeping the CLI alive.
|
|
151090
|
-
env: {
|
|
151238
|
+
env: {
|
|
151239
|
+
...process.env,
|
|
151240
|
+
MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request),
|
|
151241
|
+
MEMORIX_VECTOR_BACKFILL_LOCK: lock.path
|
|
151242
|
+
},
|
|
151091
151243
|
windowsHide: true
|
|
151092
151244
|
});
|
|
151245
|
+
if (child.pid) lock.setChildPid(child.pid);
|
|
151246
|
+
else lock.release();
|
|
151093
151247
|
child.once?.("error", () => {
|
|
151094
151248
|
});
|
|
151249
|
+
child.once?.("close", () => lock.release());
|
|
151250
|
+
if (typeof child.once !== "function") lock.release();
|
|
151095
151251
|
child.unref();
|
|
151096
|
-
return
|
|
151252
|
+
return Boolean(child.pid);
|
|
151097
151253
|
} catch {
|
|
151254
|
+
lock.release();
|
|
151098
151255
|
return false;
|
|
151099
151256
|
}
|
|
151100
151257
|
}
|
|
@@ -151106,12 +151263,32 @@ async function executeVectorBackfill(request) {
|
|
|
151106
151263
|
await prepareSearchIndex();
|
|
151107
151264
|
await getDeferredCachedVectorHydration()?.catch(() => {
|
|
151108
151265
|
});
|
|
151266
|
+
const queue = new MaintenanceJobStore(request.dataDir);
|
|
151267
|
+
const targets = new MaintenanceTargetStore(request.dataDir);
|
|
151268
|
+
let preparedRoot = request.projectRoot;
|
|
151269
|
+
const prepareTarget = async (target) => {
|
|
151270
|
+
if (target.projectRoot === preparedRoot) return;
|
|
151271
|
+
initProjectRoot(target.projectRoot);
|
|
151272
|
+
loadDotenv(target.projectRoot);
|
|
151273
|
+
preparedRoot = target.projectRoot;
|
|
151274
|
+
};
|
|
151275
|
+
const handler = async (job) => {
|
|
151276
|
+
const target = targets.get(job.projectId) ?? (job.projectId === request.projectId ? { projectId: request.projectId, projectRoot: request.projectRoot, dataDir: request.dataDir, updatedAt: Date.now() } : void 0);
|
|
151277
|
+
if (!target) return { action: "reschedule", delayMs: 3e4 };
|
|
151278
|
+
await prepareTarget(target);
|
|
151279
|
+
return createProjectMaintenanceHandler(job.projectId, target.dataDir, target.projectRoot)(job);
|
|
151280
|
+
};
|
|
151109
151281
|
const worker = new MaintenanceJobWorker(
|
|
151110
|
-
|
|
151111
|
-
|
|
151112
|
-
{
|
|
151282
|
+
queue,
|
|
151283
|
+
handler,
|
|
151284
|
+
{ kinds: ["vector-backfill"] }
|
|
151113
151285
|
);
|
|
151114
|
-
|
|
151286
|
+
let result = { state: "idle" };
|
|
151287
|
+
while (true) {
|
|
151288
|
+
result = await worker.runOnce();
|
|
151289
|
+
if (result.state === "idle") break;
|
|
151290
|
+
}
|
|
151291
|
+
return result;
|
|
151115
151292
|
}
|
|
151116
151293
|
async function readStdin() {
|
|
151117
151294
|
return new Promise((resolve2, reject) => {
|
|
@@ -151134,9 +151311,11 @@ async function main() {
|
|
|
151134
151311
|
`);
|
|
151135
151312
|
process.exitCode = 1;
|
|
151136
151313
|
} finally {
|
|
151314
|
+
releaseVectorBackfillLock();
|
|
151137
151315
|
closeAllDatabases();
|
|
151138
151316
|
}
|
|
151139
151317
|
}
|
|
151318
|
+
var VECTOR_BACKFILL_LOCK_FILE, STALE_LOCK_RECOVERY_MS;
|
|
151140
151319
|
var init_vector_backfill_runner = __esm({
|
|
151141
151320
|
"src/runtime/vector-backfill-runner.ts"() {
|
|
151142
151321
|
init_esm_shims();
|
|
@@ -151148,7 +151327,10 @@ var init_vector_backfill_runner = __esm({
|
|
|
151148
151327
|
init_orama_store();
|
|
151149
151328
|
init_sqlite_db();
|
|
151150
151329
|
init_maintenance_jobs();
|
|
151330
|
+
init_maintenance_targets();
|
|
151151
151331
|
init_project_maintenance();
|
|
151332
|
+
VECTOR_BACKFILL_LOCK_FILE = ".memorix-vector-backfill.lock";
|
|
151333
|
+
STALE_LOCK_RECOVERY_MS = 24 * 60 * 60 * 1e3;
|
|
151152
151334
|
if (process.argv[1] && process.argv[1].endsWith("vector-backfill-runner.js")) {
|
|
151153
151335
|
void main();
|
|
151154
151336
|
}
|
|
@@ -151160,7 +151342,9 @@ export {
|
|
|
151160
151342
|
launchDetachedVectorBackfill,
|
|
151161
151343
|
main,
|
|
151162
151344
|
parseVectorBackfillRequest,
|
|
151163
|
-
|
|
151345
|
+
releaseVectorBackfillLock,
|
|
151346
|
+
resolveVectorBackfillRunnerPath,
|
|
151347
|
+
vectorBackfillLockPath
|
|
151164
151348
|
};
|
|
151165
151349
|
/*! Bundled license information:
|
|
151166
151350
|
|
|
@@ -23,7 +23,7 @@ It supports:
|
|
|
23
23
|
- HTTP MCP service + dashboard (`memorix background start` or `memorix serve-http --port 3211`)
|
|
24
24
|
- bundled terminal agent (`memorix` or `memcode`) that uses the same shared memory pool
|
|
25
25
|
- local-first project-scoped memory
|
|
26
|
-
- cross-agent recall across Cursor, Claude Code, Codex, CodeBuddy Code, Windsurf, Gemini CLI, GitHub Copilot, OpenCode, OpenClaw, Hermes Agent, Oh-my-Pi, Pi, Kiro, Antigravity, Trae,
|
|
26
|
+
- cross-agent recall across Cursor, Claude Code, Codex, CodeBuddy Code, Windsurf, Gemini CLI, GitHub Copilot, OpenCode, Grok Build, OpenClaw, Hermes Agent, Oh-my-Pi, Pi, Kiro, Antigravity, Trae, DeepSeek Harness, and WorkBuddy
|
|
27
27
|
|
|
28
28
|
### Current 1.4 Baseline
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@ For the 1.4 release line, the visible product shape is:
|
|
|
41
41
|
- `memorix_project_context` / `memorix context "..."` is the normal black-box entry for non-trivial coding work: it assembles a bounded task Workset instead of injecting a generic memory dump; `memorix resume "..."` is the explicit CLI continuation projection
|
|
42
42
|
- Code State keeps local snapshots and freshness links; a healthy pre-existing local CodeGraph index can add a bounded semantic outline, but Memorix never initializes or synchronizes that external index itself
|
|
43
43
|
- `memorix knowledge` is an explicit review path for source-backed Markdown knowledge and canonical workflows. Do not initialize a versioned workspace or apply a proposal unless the user asks for that managed artifact
|
|
44
|
-
- integration surfaces are agent-specific: Claude Code, Codex, GitHub Copilot CLI, Antigravity, and Hermes receive plugin packages; OpenClaw receives a compatible bundle; Pi and Oh-my-Pi receive package entries; Gemini CLI receives an extension package; OpenCode receives a plugin file and skill; DeepSeek Harness receives an MCP patch row, AGENTS.md guidance, and skills; Cursor and other agents receive MCP/rules/hooks where supported
|
|
44
|
+
- integration surfaces are agent-specific: Claude Code, Codex, GitHub Copilot CLI, Antigravity, and Hermes receive plugin packages; OpenClaw receives a compatible bundle; Pi and Oh-my-Pi receive package entries; Gemini CLI receives an extension package; OpenCode receives a plugin file and skill; Grok Build receives native hooks and `AGENTS.md` guidance while keeping MCP host-owned; DeepSeek Harness receives an MCP patch row, AGENTS.md guidance, and skills; Cursor and other agents receive MCP/rules/hooks where supported
|
|
45
45
|
- privacy-safe diagnostics and receipts avoid raw chat, memory text, query text, tool payloads, and local file paths
|
|
46
46
|
|
|
47
47
|
---
|
|
@@ -245,6 +245,7 @@ memorix setup --agent opencode --global
|
|
|
245
245
|
memorix setup --agent openclaw --global
|
|
246
246
|
memorix setup --agent hermes --global
|
|
247
247
|
memorix setup --agent omp --global
|
|
248
|
+
memorix setup --agent grok --global
|
|
248
249
|
```
|
|
249
250
|
|
|
250
251
|
What this installs depends on the target agent:
|
|
@@ -256,6 +257,7 @@ What this installs depends on the target agent:
|
|
|
256
257
|
- Pi: user Pi package with extension-based hook capture and a Memorix skill, registered through `pi install <path> --approve`; project-local setup uses `-l`.
|
|
257
258
|
- Gemini CLI: local extension package under `~/.gemini/extensions/memorix`, extension-bundled stdio MCP, `GEMINI.md` context, hooks, commands, and skills. Antigravity CLI has an official Gemini CLI migration path, but Gemini CLI remains a separate target.
|
|
258
259
|
- OpenCode: local plugin file, `opencode.json` MCP config, OpenCode skill, plus `AGENTS.md` guidance.
|
|
260
|
+
- Grok Build: native hooks in `~/.grok/hooks/memorix.json` and guidance in `~/.grok/AGENTS.md`. Global hooks are trusted; project hooks require Grok `/hooks-trust` or `--trust`. Memorix does not write Grok's `config.toml` or MCP entries.
|
|
259
261
|
- Antigravity: native plugin under `~/.gemini/config/plugins/memorix` or `.agents/plugins/memorix`, with `plugin.json`, `mcp_config.json`, `hooks.json`, rules, and skills.
|
|
260
262
|
- OpenClaw: OpenClaw-compatible bundle under `~/.openclaw/extensions/memorix`, bundled `.mcp.json`, skills, and OpenClaw `HOOK.md`/`handler.ts` hook pack.
|
|
261
263
|
- Hermes Agent: Hermes plugin under Hermes home (`%LOCALAPPDATA%\hermes` on native Windows, `~/.hermes` elsewhere, or `HERMES_HOME`), enabled in `config.yaml`, with plugin hooks, slash command, CLI command, skills, and MCP config.
|