memorix 1.8.9 → 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.
Files changed (53) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +7 -0
  3. package/README.zh-CN.md +6 -0
  4. package/dist/cli/index.js +3261 -1348
  5. package/dist/index.js +324 -187
  6. package/dist/maintenance-runner.js +285 -103
  7. package/dist/media-audio-runner.js +285 -103
  8. package/dist/media-video-runner.js +285 -103
  9. package/dist/memcode-runtime/CHANGELOG.md +18 -0
  10. package/dist/sdk.js +323 -186
  11. package/dist/vector-backfill-runner.d.ts +4 -1
  12. package/dist/vector-backfill-runner.js +288 -104
  13. package/docs/API_REFERENCE.md +14 -0
  14. package/docs/CONFIGURATION.md +46 -0
  15. package/docs/DEVELOPMENT.md +2 -2
  16. package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +344 -0
  17. package/docs/PERFORMANCE.md +32 -0
  18. package/docs/README.md +4 -2
  19. package/llms-full.txt +1 -0
  20. package/llms.txt +1 -0
  21. package/package.json +1 -1
  22. package/plugins/antigravity/memorix/plugin.json +1 -1
  23. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  24. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  25. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  26. package/plugins/copilot/memorix/plugin.json +1 -1
  27. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  28. package/plugins/hermes/memorix/plugin.yaml +1 -1
  29. package/plugins/omp/memorix/package.json +1 -1
  30. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  31. package/plugins/pi/memorix/package.json +1 -1
  32. package/server.json +2 -2
  33. package/src/cli/command-guide.ts +9 -1
  34. package/src/cli/commands/serve-http.ts +11 -0
  35. package/src/cli/commands/sync-store.ts +191 -0
  36. package/src/cli/commands/sync.ts +8 -0
  37. package/src/memory/observations.ts +15 -2
  38. package/src/runtime/maintenance-targets.ts +6 -0
  39. package/src/runtime/vector-backfill-runner.ts +185 -7
  40. package/src/store/orama-store.ts +85 -102
  41. package/src/sync/adapters/fs.ts +179 -0
  42. package/src/sync/adapters/github.ts +258 -0
  43. package/src/sync/adapters/object-store.ts +267 -0
  44. package/src/sync/adapters/postgres.ts +269 -0
  45. package/src/sync/engine.ts +353 -0
  46. package/src/sync/journal.ts +162 -0
  47. package/src/sync/merge.ts +116 -0
  48. package/src/sync/namespace.ts +15 -0
  49. package/src/sync/paging.ts +45 -0
  50. package/src/sync/policy.ts +37 -0
  51. package/src/sync/remote-factory.ts +49 -0
  52. package/src/sync/store-port.ts +288 -0
  53. package/src/sync/types.ts +183 -0
package/dist/sdk.js CHANGED
@@ -7280,6 +7280,14 @@ __export(orama_store_exports, {
7280
7280
  updateObservationMetadata: () => updateObservationMetadata
7281
7281
  });
7282
7282
  import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
7283
+ function rememberSearchMode(key, value) {
7284
+ if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
7285
+ const oldest = lastSearchModeByProject.keys().next().value;
7286
+ if (oldest !== void 0) lastSearchModeByProject.delete(oldest);
7287
+ }
7288
+ lastSearchModeByProject.delete(key);
7289
+ lastSearchModeByProject.set(key, value);
7290
+ }
7283
7291
  function getLastSearchMode(projectId) {
7284
7292
  return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
7285
7293
  }
@@ -7292,9 +7300,6 @@ function makeEntryKey(projectId, observationId2) {
7292
7300
  function rememberObservationDoc(doc) {
7293
7301
  const publicDoc = { ...doc };
7294
7302
  delete publicDoc.embedding;
7295
- if (doc.projectId && typeof doc.observationId === "number") {
7296
- docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
7297
- }
7298
7303
  return publicDoc;
7299
7304
  }
7300
7305
  function isCommandLikeQuery(query) {
@@ -7398,7 +7403,6 @@ async function resetDb() {
7398
7403
  indexEmbeddingProvider = null;
7399
7404
  deferredCachedVectorHydration = null;
7400
7405
  lastSearchModeByProject.clear();
7401
- docByObservationKey.clear();
7402
7406
  }
7403
7407
  function isEmbeddingEnabled() {
7404
7408
  return embeddingEnabled;
@@ -7461,78 +7465,72 @@ function isCompatibleCachedVector(vector) {
7461
7465
  vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
7462
7466
  );
7463
7467
  }
7464
- async function attachCachedVectors(database, candidates) {
7465
- const cachedVectors = await getCachedEmbeddings(
7466
- candidates.map(({ observation }) => observationEmbeddingText(observation))
7467
- );
7468
- if (db !== database) return;
7469
- for (let index = 0; index < candidates.length; index++) {
7470
- const vector = cachedVectors[index];
7471
- if (!isCompatibleCachedVector(vector)) continue;
7472
- const existing = getByID(database, candidates[index].id);
7473
- if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(candidates[index].observation)) continue;
7474
- try {
7475
- await update(database, candidates[index].id, { ...existing, embedding: vector });
7476
- } catch {
7477
- }
7478
- if (index % 50 === 0) {
7479
- await new Promise((resolve5) => setImmediate(resolve5));
7468
+ async function attachCachedVectors(database, observations2) {
7469
+ const batchSize = 200;
7470
+ for (let start = 0; start < observations2.length; start += batchSize) {
7471
+ const batch = observations2.slice(start, start + batchSize);
7472
+ const cachedVectors = await getCachedEmbeddings(batch.map(observationEmbeddingText));
7473
+ if (db !== database) return;
7474
+ for (let index = 0; index < batch.length; index++) {
7475
+ const vector = cachedVectors[index];
7476
+ if (!isCompatibleCachedVector(vector)) continue;
7477
+ const id = makeOramaObservationId(batch[index].projectId, batch[index].id);
7478
+ const existing = getByID(database, id);
7479
+ if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(batch[index])) continue;
7480
+ try {
7481
+ await update(database, id, { ...existing, embedding: vector });
7482
+ } catch {
7483
+ }
7480
7484
  }
7485
+ await new Promise((resolve5) => setImmediate(resolve5));
7481
7486
  }
7482
7487
  }
7483
7488
  async function hydrateIndex(observations2, options = {}) {
7484
7489
  const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
7485
- const candidates = [];
7486
- for (const observation of observations2) {
7487
- if (!observation || !observation.id || !observation.projectId) continue;
7488
- const id = makeOramaObservationId(observation.projectId, observation.id);
7489
- if (getByID(database, id)) continue;
7490
- candidates.push({ observation, id });
7491
- }
7492
7490
  const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
7493
- const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
7494
- const documents = [];
7495
- for (let index = 0; index < candidates.length; index++) {
7496
- const { observation: obs, id } = candidates[index];
7497
- try {
7498
- const vector = cachedVectors[index];
7499
- const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
7500
- const doc = {
7501
- id,
7502
- observationId: obs.id,
7503
- entityName: obs.entityName || "",
7504
- type: obs.type || "discovery",
7505
- title: obs.title || "",
7506
- narrative: obs.narrative || "",
7507
- facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
7508
- filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
7509
- concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
7510
- tokens: obs.tokens ?? 0,
7511
- createdAt: obs.createdAt || "",
7512
- projectId: obs.projectId,
7513
- accessCount: obs.accessCount ?? 0,
7514
- lastAccessedAt: obs.lastAccessedAt || "",
7515
- status: obs.status ?? "active",
7516
- source: obs.source || "agent",
7517
- sourceDetail: obs.sourceDetail ?? "",
7518
- valueCategory: obs.valueCategory ?? "",
7519
- admissionState: obs.admissionState ?? "",
7520
- admissionReason: obs.admissionReason ?? "",
7521
- visibility: obs.visibility ?? "project",
7522
- createdByAgentId: obs.createdByAgentId ?? "",
7523
- sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
7524
- documentType: "observation",
7525
- knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
7526
- ...compatibleVector ? { embedding: compatibleVector } : {}
7527
- };
7528
- documents.push(doc);
7529
- } catch {
7530
- }
7531
- }
7532
7491
  let inserted = 0;
7533
7492
  const hydrationBatchSize = 200;
7534
- for (let start = 0; start < documents.length; start += hydrationBatchSize) {
7535
- const batch = documents.slice(start, start + hydrationBatchSize);
7493
+ for (let start = 0; start < observations2.length; start += hydrationBatchSize) {
7494
+ const batchObservations = observations2.slice(start, start + hydrationBatchSize).filter((observation) => observation && observation.id && observation.projectId).filter((observation) => !getByID(database, makeOramaObservationId(observation.projectId, observation.id)));
7495
+ if (batchObservations.length === 0) continue;
7496
+ const cachedVectors = deferCachedVectors || options.skipCachedVectors ? batchObservations.map(() => null) : await getCachedEmbeddings(batchObservations.map(observationEmbeddingText));
7497
+ const batch = [];
7498
+ for (let index = 0; index < batchObservations.length; index++) {
7499
+ const obs = batchObservations[index];
7500
+ try {
7501
+ const compatibleVector = isCompatibleCachedVector(cachedVectors[index]) ? cachedVectors[index] : null;
7502
+ batch.push({
7503
+ id: makeOramaObservationId(obs.projectId, obs.id),
7504
+ observationId: obs.id,
7505
+ entityName: obs.entityName || "",
7506
+ type: obs.type || "discovery",
7507
+ title: obs.title || "",
7508
+ narrative: obs.narrative || "",
7509
+ facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
7510
+ filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
7511
+ concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
7512
+ tokens: obs.tokens ?? 0,
7513
+ createdAt: obs.createdAt || "",
7514
+ projectId: obs.projectId,
7515
+ accessCount: obs.accessCount ?? 0,
7516
+ lastAccessedAt: obs.lastAccessedAt || "",
7517
+ status: obs.status ?? "active",
7518
+ source: obs.source || "agent",
7519
+ sourceDetail: obs.sourceDetail ?? "",
7520
+ valueCategory: obs.valueCategory ?? "",
7521
+ admissionState: obs.admissionState ?? "",
7522
+ admissionReason: obs.admissionReason ?? "",
7523
+ visibility: obs.visibility ?? "project",
7524
+ createdByAgentId: obs.createdByAgentId ?? "",
7525
+ sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
7526
+ documentType: "observation",
7527
+ knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
7528
+ ...compatibleVector ? { embedding: compatibleVector } : {}
7529
+ });
7530
+ } catch {
7531
+ }
7532
+ }
7533
+ if (batch.length === 0) continue;
7536
7534
  try {
7537
7535
  await insertMultiple(database, batch, batch.length);
7538
7536
  for (const doc of batch) rememberObservationDoc(doc);
@@ -7554,7 +7552,7 @@ async function hydrateIndex(observations2, options = {}) {
7554
7552
  }
7555
7553
  await new Promise((resolve5) => setImmediate(resolve5));
7556
7554
  }
7557
- deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
7555
+ deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, observations2).catch(() => {
7558
7556
  }) : null;
7559
7557
  return inserted;
7560
7558
  }
@@ -7591,12 +7589,6 @@ async function updateObservationMetadata(projectId, observationId2, patch) {
7591
7589
  async function removeObservation(oramaId) {
7592
7590
  const database = await getDb();
7593
7591
  await remove2(database, oramaId);
7594
- for (const [key, doc] of docByObservationKey.entries()) {
7595
- if (doc.id === oramaId) {
7596
- docByObservationKey.delete(key);
7597
- break;
7598
- }
7599
- }
7600
7592
  }
7601
7593
  async function searchObservations(options) {
7602
7594
  const perf = !!process.env.MEMORIX_PERF;
@@ -7611,7 +7603,7 @@ async function searchObservations(options) {
7611
7603
  const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
7612
7604
  const quality = options.quality ?? "balanced";
7613
7605
  const database = await getDb();
7614
- lastSearchModeByProject.set(
7606
+ rememberSearchMode(
7615
7607
  modeKey,
7616
7608
  quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
7617
7609
  );
@@ -7678,7 +7670,7 @@ async function searchObservations(options) {
7678
7670
  if (provider2) {
7679
7671
  const activeVectorDimensions = getVectorDimensions();
7680
7672
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
7681
- lastSearchModeByProject.set(
7673
+ rememberSearchMode(
7682
7674
  modeKey,
7683
7675
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
7684
7676
  );
@@ -7703,7 +7695,7 @@ async function searchObservations(options) {
7703
7695
  mark("embedding");
7704
7696
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
7705
7697
  const isCJKHeavy = cjkRatio > 0.3;
7706
- lastSearchModeByProject.set(modeKey, "hybrid");
7698
+ rememberSearchMode(modeKey, "hybrid");
7707
7699
  searchParams = {
7708
7700
  ...searchParams,
7709
7701
  mode: "hybrid",
@@ -7719,7 +7711,7 @@ async function searchObservations(options) {
7719
7711
  }
7720
7712
  }
7721
7713
  } catch (error) {
7722
- lastSearchModeByProject.set(modeKey, "fulltext (embedding unavailable)");
7714
+ rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
7723
7715
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
7724
7716
  }
7725
7717
  }
@@ -7729,7 +7721,7 @@ async function searchObservations(options) {
7729
7721
  results = await search(database, searchParams);
7730
7722
  } catch (error) {
7731
7723
  if (queryVector && isVectorDimensionMismatchError(error)) {
7732
- lastSearchModeByProject.set(modeKey, "fulltext (embedding dimension mismatch)");
7724
+ rememberSearchMode(modeKey, "fulltext (embedding dimension mismatch)");
7733
7725
  console.error("[memorix] Vector search dimension mismatch detected, retrying without embeddings");
7734
7726
  results = await search(database, stripVectorSearchParams(searchParams));
7735
7727
  } else {
@@ -7751,7 +7743,7 @@ async function searchObservations(options) {
7751
7743
  },
7752
7744
  similarity: 0.25
7753
7745
  };
7754
- lastSearchModeByProject.set(modeKey, "vector-only (hybrid empty fallback)");
7746
+ rememberSearchMode(modeKey, "vector-only (hybrid empty fallback)");
7755
7747
  results = await search(database, vectorOnlyParams);
7756
7748
  } catch {
7757
7749
  }
@@ -7992,7 +7984,7 @@ async function searchObservations(options) {
7992
7984
  const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
7993
7985
  if (rerankedTop.length > 0) {
7994
7986
  intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
7995
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
7987
+ rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
7996
7988
  mark("rerank(usedNeural=true)");
7997
7989
  }
7998
7990
  }
@@ -8009,7 +8001,7 @@ async function searchObservations(options) {
8009
8001
  );
8010
8002
  mark(`rerank(usedLLM=${usedLLM})`);
8011
8003
  if (usedLLM) {
8012
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
8004
+ rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
8013
8005
  const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
8014
8006
  if (rerankedTop.length > 0) {
8015
8007
  intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
@@ -8082,13 +8074,6 @@ async function getObservationsByIds(ids, projectId) {
8082
8074
  const database = await getDb();
8083
8075
  const results = [];
8084
8076
  for (const id of ids) {
8085
- if (projectId) {
8086
- const cached = docByObservationKey.get(makeEntryKey(projectId, id));
8087
- if (cached) {
8088
- results.push(cached);
8089
- continue;
8090
- }
8091
- }
8092
8077
  const searchResult = await search(database, {
8093
8078
  term: "",
8094
8079
  includeVectors: true,
@@ -8188,7 +8173,7 @@ function formatTime(isoDate) {
8188
8173
  return isoDate;
8189
8174
  }
8190
8175
  }
8191
- var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, docByObservationKey, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, COMMAND_LOG_TITLE2, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
8176
+ var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, MAX_SEARCH_MODE_KEYS, COMMAND_LOG_TITLE2, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
8192
8177
  var init_orama_store = __esm({
8193
8178
  "src/store/orama-store.ts"() {
8194
8179
  "use strict";
@@ -8209,10 +8194,10 @@ var init_orama_store = __esm({
8209
8194
  embeddingDimensions = null;
8210
8195
  indexEmbeddingProvider = null;
8211
8196
  deferredCachedVectorHydration = null;
8212
- docByObservationKey = /* @__PURE__ */ new Map();
8213
8197
  NON_CJK_HYBRID_SIMILARITY = 0.45;
8214
8198
  lastSearchModeByProject = /* @__PURE__ */ new Map();
8215
8199
  SEARCH_MODE_DEFAULT_KEY = "__global__";
8200
+ MAX_SEARCH_MODE_KEYS = 4096;
8216
8201
  COMMAND_LOG_TITLE2 = /^(Ran:|Command:|Executed:)\s/i;
8217
8202
  COMMAND_STYLE_TITLE = /(\bfindstr\b|\bSelect-String\b|\bGet-Content\b|\bnpx\s+vitest\b|\bnpx\s+tsc\b|\b2>&1\b)/i;
8218
8203
  COMMAND_LIKE_QUERY2 = /\b(git|npm|npx|pnpm|yarn|node|bash|powershell|curl|memorix)\b/i;
@@ -9640,6 +9625,60 @@ var init_evidence_store = __esm({
9640
9625
  }
9641
9626
  });
9642
9627
 
9628
+ // src/runtime/maintenance-targets.ts
9629
+ var maintenance_targets_exports = {};
9630
+ __export(maintenance_targets_exports, {
9631
+ MaintenanceTargetStore: () => MaintenanceTargetStore
9632
+ });
9633
+ function rowToTarget(row) {
9634
+ return {
9635
+ projectId: row.project_id,
9636
+ projectRoot: row.project_root,
9637
+ dataDir: row.data_dir,
9638
+ updatedAt: Number(row.updated_at)
9639
+ };
9640
+ }
9641
+ var MaintenanceTargetStore;
9642
+ var init_maintenance_targets = __esm({
9643
+ "src/runtime/maintenance-targets.ts"() {
9644
+ "use strict";
9645
+ init_esm_shims();
9646
+ init_sqlite_db();
9647
+ MaintenanceTargetStore = class {
9648
+ db;
9649
+ constructor(dataDir) {
9650
+ this.db = getDatabase(dataDir);
9651
+ }
9652
+ register(input) {
9653
+ if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
9654
+ if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
9655
+ if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
9656
+ const now3 = input.now ?? Date.now();
9657
+ this.db.prepare(`
9658
+ INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
9659
+ VALUES (?, ?, ?, ?)
9660
+ ON CONFLICT(project_id) DO UPDATE SET
9661
+ project_root = excluded.project_root,
9662
+ data_dir = excluded.data_dir,
9663
+ updated_at = excluded.updated_at
9664
+ `).run(input.projectId, input.projectRoot, input.dataDir, now3);
9665
+ return this.get(input.projectId);
9666
+ }
9667
+ get(projectId) {
9668
+ const row = this.db.prepare(`
9669
+ SELECT * FROM maintenance_targets WHERE project_id = ?
9670
+ `).get(projectId);
9671
+ return row ? rowToTarget(row) : void 0;
9672
+ }
9673
+ list() {
9674
+ return this.db.prepare(`
9675
+ SELECT * FROM maintenance_targets ORDER BY updated_at DESC
9676
+ `).all().map(rowToTarget);
9677
+ }
9678
+ };
9679
+ }
9680
+ });
9681
+
9643
9682
  // src/runtime/isolated-maintenance.ts
9644
9683
  import { spawn } from "child_process";
9645
9684
  import { existsSync as existsSync8 } from "fs";
@@ -152176,10 +152215,21 @@ __export(vector_backfill_runner_exports, {
152176
152215
  launchDetachedVectorBackfill: () => launchDetachedVectorBackfill,
152177
152216
  main: () => main,
152178
152217
  parseVectorBackfillRequest: () => parseVectorBackfillRequest,
152179
- resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath
152218
+ releaseVectorBackfillLock: () => releaseVectorBackfillLock,
152219
+ resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath,
152220
+ vectorBackfillLockPath: () => vectorBackfillLockPath
152180
152221
  });
152181
152222
  import { spawn as spawn4 } from "child_process";
152182
- import { existsSync as existsSync12 } from "fs";
152223
+ import {
152224
+ closeSync,
152225
+ existsSync as existsSync12,
152226
+ mkdirSync as mkdirSync2,
152227
+ openSync,
152228
+ readFileSync as readFileSync9,
152229
+ statSync as statSync4,
152230
+ unlinkSync,
152231
+ writeFileSync as writeFileSync2
152232
+ } from "fs";
152183
152233
  import path24 from "path";
152184
152234
  import { fileURLToPath as fileURLToPath5 } from "url";
152185
152235
  function isNonEmptyString(value) {
@@ -152206,10 +152256,104 @@ function parseVectorBackfillRequest(raw) {
152206
152256
  dataDir: value.dataDir
152207
152257
  };
152208
152258
  }
152259
+ function vectorBackfillLockPath(dataDir) {
152260
+ return path24.join(dataDir, VECTOR_BACKFILL_LOCK_FILE);
152261
+ }
152262
+ function processIsAlive(pid) {
152263
+ if (!Number.isSafeInteger(pid) || pid == null || pid <= 0) return false;
152264
+ try {
152265
+ process.kill(pid, 0);
152266
+ return true;
152267
+ } catch (error) {
152268
+ return error.code === "EPERM";
152269
+ }
152270
+ }
152271
+ function readVectorBackfillLock(lockPath) {
152272
+ try {
152273
+ const value = JSON.parse(readFileSync9(lockPath, "utf8"));
152274
+ if (typeof value.parentPid !== "number" || !Number.isSafeInteger(value.parentPid) || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.projectId !== "string") return void 0;
152275
+ return {
152276
+ parentPid: value.parentPid,
152277
+ ...typeof value.childPid === "number" ? { childPid: value.childPid } : {},
152278
+ startedAt: value.startedAt,
152279
+ projectId: value.projectId
152280
+ };
152281
+ } catch {
152282
+ return void 0;
152283
+ }
152284
+ }
152285
+ function writeVectorBackfillLock(lockPath, value) {
152286
+ writeFileSync2(lockPath, `${JSON.stringify(value)}
152287
+ `, "utf8");
152288
+ }
152289
+ function acquireVectorBackfillLock(dataDir, projectId, preferredPath) {
152290
+ const lockPath = preferredPath ?? vectorBackfillLockPath(dataDir);
152291
+ mkdirSync2(path24.dirname(lockPath), { recursive: true });
152292
+ const initial = {
152293
+ parentPid: process.pid,
152294
+ startedAt: Date.now(),
152295
+ projectId
152296
+ };
152297
+ for (let attempt = 0; attempt < 2; attempt++) {
152298
+ try {
152299
+ const fd = openSync(lockPath, "wx");
152300
+ try {
152301
+ writeFileSync2(fd, `${JSON.stringify(initial)}
152302
+ `, "utf8");
152303
+ } finally {
152304
+ closeSync(fd);
152305
+ }
152306
+ return {
152307
+ path: lockPath,
152308
+ setChildPid(pid) {
152309
+ try {
152310
+ writeVectorBackfillLock(lockPath, { ...initial, childPid: pid });
152311
+ } catch {
152312
+ }
152313
+ },
152314
+ release() {
152315
+ try {
152316
+ unlinkSync(lockPath);
152317
+ } catch {
152318
+ }
152319
+ }
152320
+ };
152321
+ } catch (error) {
152322
+ if (error.code !== "EEXIST") return void 0;
152323
+ const existing = readVectorBackfillLock(lockPath);
152324
+ const ownerAlive = existing && (processIsAlive(existing.childPid) || processIsAlive(existing.parentPid));
152325
+ if (ownerAlive) return void 0;
152326
+ let stale = false;
152327
+ try {
152328
+ stale = Date.now() - statSync4(lockPath).mtimeMs >= STALE_LOCK_RECOVERY_MS;
152329
+ } catch {
152330
+ stale = false;
152331
+ }
152332
+ if (!existing && !stale) return void 0;
152333
+ try {
152334
+ unlinkSync(lockPath);
152335
+ } catch {
152336
+ return void 0;
152337
+ }
152338
+ }
152339
+ }
152340
+ return void 0;
152341
+ }
152342
+ function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFILL_LOCK) {
152343
+ if (!lockPath) return;
152344
+ const lock = readVectorBackfillLock(lockPath);
152345
+ if (lock && lock.childPid !== process.pid) return;
152346
+ try {
152347
+ unlinkSync(lockPath);
152348
+ } catch {
152349
+ }
152350
+ }
152209
152351
  function launchDetachedVectorBackfill(request, options = {}) {
152210
152352
  const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
152211
152353
  const exists = options.exists ?? existsSync12;
152212
152354
  if (!exists(runnerPath)) return false;
152355
+ const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
152356
+ if (!lock) return false;
152213
152357
  try {
152214
152358
  const child = (options.spawn ?? spawn4)(process.execPath, [runnerPath], {
152215
152359
  cwd: request.projectRoot,
@@ -152219,14 +152363,23 @@ function launchDetachedVectorBackfill(request, options = {}) {
152219
152363
  stdio: "ignore",
152220
152364
  // The request contains only local project metadata, never credentials.
152221
152365
  // Environment transport avoids a live stdin pipe keeping the CLI alive.
152222
- env: { ...process.env, MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request) },
152366
+ env: {
152367
+ ...process.env,
152368
+ MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request),
152369
+ MEMORIX_VECTOR_BACKFILL_LOCK: lock.path
152370
+ },
152223
152371
  windowsHide: true
152224
152372
  });
152373
+ if (child.pid) lock.setChildPid(child.pid);
152374
+ else lock.release();
152225
152375
  child.once?.("error", () => {
152226
152376
  });
152377
+ child.once?.("close", () => lock.release());
152378
+ if (typeof child.once !== "function") lock.release();
152227
152379
  child.unref();
152228
- return true;
152380
+ return Boolean(child.pid);
152229
152381
  } catch {
152382
+ lock.release();
152230
152383
  return false;
152231
152384
  }
152232
152385
  }
@@ -152238,12 +152391,32 @@ async function executeVectorBackfill(request) {
152238
152391
  await prepareSearchIndex();
152239
152392
  await getDeferredCachedVectorHydration()?.catch(() => {
152240
152393
  });
152394
+ const queue = new MaintenanceJobStore(request.dataDir);
152395
+ const targets = new MaintenanceTargetStore(request.dataDir);
152396
+ let preparedRoot = request.projectRoot;
152397
+ const prepareTarget = async (target) => {
152398
+ if (target.projectRoot === preparedRoot) return;
152399
+ initProjectRoot(target.projectRoot);
152400
+ loadDotenv(target.projectRoot);
152401
+ preparedRoot = target.projectRoot;
152402
+ };
152403
+ const handler = async (job) => {
152404
+ const target = targets.get(job.projectId) ?? (job.projectId === request.projectId ? { projectId: request.projectId, projectRoot: request.projectRoot, dataDir: request.dataDir, updatedAt: Date.now() } : void 0);
152405
+ if (!target) return { action: "reschedule", delayMs: 3e4 };
152406
+ await prepareTarget(target);
152407
+ return createProjectMaintenanceHandler(job.projectId, target.dataDir, target.projectRoot)(job);
152408
+ };
152241
152409
  const worker = new MaintenanceJobWorker(
152242
- new MaintenanceJobStore(request.dataDir),
152243
- createProjectMaintenanceHandler(request.projectId, request.dataDir, request.projectRoot),
152244
- { projectId: request.projectId, kinds: ["vector-backfill"] }
152410
+ queue,
152411
+ handler,
152412
+ { kinds: ["vector-backfill"] }
152245
152413
  );
152246
- return worker.runOnce();
152414
+ let result = { state: "idle" };
152415
+ while (true) {
152416
+ result = await worker.runOnce();
152417
+ if (result.state === "idle") break;
152418
+ }
152419
+ return result;
152247
152420
  }
152248
152421
  async function readStdin() {
152249
152422
  return new Promise((resolve5, reject) => {
@@ -152266,9 +152439,11 @@ async function main() {
152266
152439
  `);
152267
152440
  process.exitCode = 1;
152268
152441
  } finally {
152442
+ releaseVectorBackfillLock();
152269
152443
  closeAllDatabases();
152270
152444
  }
152271
152445
  }
152446
+ var VECTOR_BACKFILL_LOCK_FILE, STALE_LOCK_RECOVERY_MS;
152272
152447
  var init_vector_backfill_runner = __esm({
152273
152448
  "src/runtime/vector-backfill-runner.ts"() {
152274
152449
  "use strict";
@@ -152281,7 +152456,10 @@ var init_vector_backfill_runner = __esm({
152281
152456
  init_orama_store();
152282
152457
  init_sqlite_db();
152283
152458
  init_maintenance_jobs();
152459
+ init_maintenance_targets();
152284
152460
  init_project_maintenance();
152461
+ VECTOR_BACKFILL_LOCK_FILE = ".memorix-vector-backfill.lock";
152462
+ STALE_LOCK_RECOVERY_MS = 24 * 60 * 60 * 1e3;
152285
152463
  if (process.argv[1] && process.argv[1].endsWith("vector-backfill-runner.js")) {
152286
152464
  void main();
152287
152465
  }
@@ -152323,11 +152501,17 @@ function resetObservationRuntime() {
152323
152501
  embeddingWorkerProjectRoot = void 0;
152324
152502
  vectorMissingIds.clear();
152325
152503
  vectorSchemaUpgradePromise = null;
152504
+ embeddingFailureLogTimestamps.clear();
152326
152505
  }
152327
152506
  function logEmbeddingFailureOnce(key, message) {
152328
152507
  const now3 = Date.now();
152329
152508
  const last = embeddingFailureLogTimestamps.get(key) ?? 0;
152330
152509
  if (now3 - last < EMBEDDING_FAILURE_LOG_COOLDOWN_MS) return;
152510
+ if (!embeddingFailureLogTimestamps.has(key) && embeddingFailureLogTimestamps.size >= MAX_EMBEDDING_FAILURE_KEYS) {
152511
+ const oldest = embeddingFailureLogTimestamps.keys().next().value;
152512
+ if (oldest !== void 0) embeddingFailureLogTimestamps.delete(oldest);
152513
+ }
152514
+ embeddingFailureLogTimestamps.delete(key);
152331
152515
  embeddingFailureLogTimestamps.set(key, now3);
152332
152516
  console.error(message);
152333
152517
  }
@@ -152357,13 +152541,14 @@ function queueVectorBackfill(projectId, options = {}) {
152357
152541
  const dataDir = projectDir;
152358
152542
  if (!dataDir) return;
152359
152543
  void Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)).then(({ MaintenanceJobStore: MaintenanceJobStore2 }) => {
152360
- new MaintenanceJobStore2(dataDir).enqueue({
152544
+ const job = new MaintenanceJobStore2(dataDir).enqueue({
152361
152545
  projectId,
152362
152546
  kind: "vector-backfill",
152363
152547
  dedupeKey: "vector-backfill",
152364
152548
  payload: { limit: 12 }
152365
152549
  });
152366
- if (options.detachedWorker && options.projectRoot) {
152550
+ const due = !job || job.status === "pending" && job.runAfter <= Date.now();
152551
+ if (due && options.detachedWorker && options.projectRoot) {
152367
152552
  void Promise.resolve().then(() => (init_vector_backfill_runner(), vector_backfill_runner_exports)).then(({ launchDetachedVectorBackfill: launchDetachedVectorBackfill2 }) => {
152368
152553
  launchDetachedVectorBackfill2({
152369
152554
  projectId,
@@ -153294,7 +153479,7 @@ async function backfillVectorEmbeddings(options = {}) {
153294
153479
  ...lastFailure ? { lastError: lastFailure } : {}
153295
153480
  };
153296
153481
  }
153297
- var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
153482
+ var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS, MAX_EMBEDDING_FAILURE_KEYS;
153298
153483
  var init_observations = __esm({
153299
153484
  "src/memory/observations.ts"() {
153300
153485
  "use strict";
@@ -153322,6 +153507,7 @@ var init_observations = __esm({
153322
153507
  lastVectorBackfill = null;
153323
153508
  embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
153324
153509
  EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
153510
+ MAX_EMBEDDING_FAILURE_KEYS = 1024;
153325
153511
  }
153326
153512
  });
153327
153513
 
@@ -154759,62 +154945,13 @@ var init_formation = __esm({
154759
154945
  }
154760
154946
  });
154761
154947
 
154762
- // src/runtime/maintenance-targets.ts
154763
- var maintenance_targets_exports = {};
154764
- __export(maintenance_targets_exports, {
154765
- MaintenanceTargetStore: () => MaintenanceTargetStore
154766
- });
154767
- function rowToTarget(row) {
154768
- return {
154769
- projectId: row.project_id,
154770
- projectRoot: row.project_root,
154771
- dataDir: row.data_dir,
154772
- updatedAt: Number(row.updated_at)
154773
- };
154774
- }
154775
- var MaintenanceTargetStore;
154776
- var init_maintenance_targets = __esm({
154777
- "src/runtime/maintenance-targets.ts"() {
154778
- "use strict";
154779
- init_esm_shims();
154780
- init_sqlite_db();
154781
- MaintenanceTargetStore = class {
154782
- db;
154783
- constructor(dataDir) {
154784
- this.db = getDatabase(dataDir);
154785
- }
154786
- register(input) {
154787
- if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
154788
- if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
154789
- if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
154790
- const now3 = input.now ?? Date.now();
154791
- this.db.prepare(`
154792
- INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
154793
- VALUES (?, ?, ?, ?)
154794
- ON CONFLICT(project_id) DO UPDATE SET
154795
- project_root = excluded.project_root,
154796
- data_dir = excluded.data_dir,
154797
- updated_at = excluded.updated_at
154798
- `).run(input.projectId, input.projectRoot, input.dataDir, now3);
154799
- return this.get(input.projectId);
154800
- }
154801
- get(projectId) {
154802
- const row = this.db.prepare(`
154803
- SELECT * FROM maintenance_targets WHERE project_id = ?
154804
- `).get(projectId);
154805
- return row ? rowToTarget(row) : void 0;
154806
- }
154807
- };
154808
- }
154809
- });
154810
-
154811
154948
  // src/config/behavior.ts
154812
154949
  var behavior_exports = {};
154813
154950
  __export(behavior_exports, {
154814
154951
  getBehaviorConfig: () => getBehaviorConfig,
154815
154952
  resetBehaviorConfigCache: () => resetBehaviorConfigCache
154816
154953
  });
154817
- import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
154954
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
154818
154955
  import { homedir as homedir22 } from "os";
154819
154956
  function isSessionInject(value) {
154820
154957
  return value === "full" || value === "minimal" || value === "silent";
@@ -154826,7 +154963,7 @@ function legacyBehavior(homeDir) {
154826
154963
  const configPath = getLegacyConfigJsonPath(homeDir);
154827
154964
  try {
154828
154965
  if (!existsSync15(configPath)) return {};
154829
- const parsed = JSON.parse(readFileSync10(configPath, "utf-8"));
154966
+ const parsed = JSON.parse(readFileSync11(configPath, "utf-8"));
154830
154967
  return parsed.behavior && typeof parsed.behavior === "object" && !Array.isArray(parsed.behavior) ? parsed.behavior : {};
154831
154968
  } catch {
154832
154969
  return {};
@@ -156179,12 +156316,12 @@ __export(current_facts_exports, {
156179
156316
  formatGitFact: () => formatGitFact
156180
156317
  });
156181
156318
  import { execFileSync } from "child_process";
156182
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
156319
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
156183
156320
  import path28 from "path";
156184
156321
  function readTextIfExists(filePath) {
156185
156322
  try {
156186
156323
  if (!existsSync16(filePath)) return null;
156187
- return readFileSync11(filePath, "utf8");
156324
+ return readFileSync12(filePath, "utf8");
156188
156325
  } catch {
156189
156326
  return null;
156190
156327
  }
@@ -156474,7 +156611,7 @@ __export(external_provider_exports, {
156474
156611
  runExternalCodeGraphLifecycle: () => runExternalCodeGraphLifecycle
156475
156612
  });
156476
156613
  import { spawn as spawn5 } from "child_process";
156477
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
156614
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
156478
156615
  import { delimiter, dirname as dirname2, extname, isAbsolute as isAbsolute3, join as join20, relative as relative4, resolve as resolve3, sep } from "path";
156479
156616
  function boundedTimeout(value) {
156480
156617
  if (!Number.isFinite(value)) return DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS;
@@ -156521,7 +156658,7 @@ function resolveLaunch(command) {
156521
156658
  return { executable: node, prefixArgs: ["--liftoff-only", entry] };
156522
156659
  }
156523
156660
  try {
156524
- const shim = readFileSync12(candidate, "utf8");
156661
+ const shim = readFileSync13(candidate, "utf8");
156525
156662
  const match2 = shim.match(/["']([^"']*%(?:~)?dp0%?[^"']*\.m?js)["']/i);
156526
156663
  if (match2?.[1]) {
156527
156664
  const base = dirname2(candidate).endsWith(sep) ? dirname2(candidate) : dirname2(candidate) + sep;
@@ -157781,7 +157918,7 @@ __export(auto_context_exports, {
157781
157918
  formatAutoProjectContextSummary: () => formatAutoProjectContextSummary,
157782
157919
  formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
157783
157920
  });
157784
- import { existsSync as existsSync18, statSync as statSync4 } from "fs";
157921
+ import { existsSync as existsSync18, statSync as statSync5 } from "fs";
157785
157922
  import path29 from "path";
157786
157923
  function activeProjectObservations(observations2, projectId) {
157787
157924
  return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
@@ -158128,7 +158265,7 @@ function existingLensCandidates(rootPath, lens) {
158128
158265
  const absolute = path29.join(rootPath, candidate);
158129
158266
  try {
158130
158267
  if (!existsSync18(absolute)) continue;
158131
- const stat5 = statSync4(absolute);
158268
+ const stat5 = statSync5(absolute);
158132
158269
  if (stat5.isFile()) out.push(candidate);
158133
158270
  if (stat5.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
158134
158271
  } catch {
@@ -158661,7 +158798,7 @@ var engine_exports2 = {};
158661
158798
  __export(engine_exports2, {
158662
158799
  SkillsEngine: () => SkillsEngine
158663
158800
  });
158664
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
158801
+ import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
158665
158802
  import { join as join21 } from "path";
158666
158803
  import { homedir as homedir23 } from "os";
158667
158804
  var SKILLS_DIRS, SKILL_WORTHY_TYPES, MIN_OBS_FOR_SKILL, MIN_SCORE_FOR_SKILL, LOW_SIGNAL_TITLE_PATTERNS, LOW_SIGNAL_ENTITY_PATTERNS, COMMAND_TRACE_PATTERNS2, SkillsEngine;
@@ -158752,7 +158889,7 @@ var init_engine3 = __esm({
158752
158889
  const skillMd = join21(skillsRoot, name, "SKILL.md");
158753
158890
  if (!existsSync19(skillMd)) continue;
158754
158891
  try {
158755
- const content = readFileSync13(skillMd, "utf-8");
158892
+ const content = readFileSync14(skillMd, "utf-8");
158756
158893
  const description = this.parseDescription(content);
158757
158894
  skills.push({
158758
158895
  name,
@@ -158802,8 +158939,8 @@ var init_engine3 = __esm({
158802
158939
  if (!dirs || dirs.length === 0) return null;
158803
158940
  const targetDir = join21(this.projectRoot, dirs[0], skill2.name);
158804
158941
  try {
158805
- mkdirSync4(targetDir, { recursive: true });
158806
- writeFileSync3(join21(targetDir, "SKILL.md"), skill2.content, "utf-8");
158942
+ mkdirSync5(targetDir, { recursive: true });
158943
+ writeFileSync4(join21(targetDir, "SKILL.md"), skill2.content, "utf-8");
158807
158944
  return join21(dirs[0], skill2.name, "SKILL.md");
158808
158945
  } catch {
158809
158946
  return null;
@@ -162573,7 +162710,7 @@ var init_task_graph = __esm({
162573
162710
 
162574
162711
  // src/orchestrate/context-collector.ts
162575
162712
  import { execFileSync as execFileSync2 } from "child_process";
162576
- import { readFileSync as readFileSync14, existsSync as existsSync20 } from "fs";
162713
+ import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
162577
162714
  import { join as join22 } from "path";
162578
162715
  function normalizeEntryLimit(value, fallback) {
162579
162716
  if (!Number.isSafeInteger(value) || value < 1) return fallback;
@@ -162651,7 +162788,7 @@ function collectDependencies(projectDir2) {
162651
162788
  try {
162652
162789
  const pkgPath = join22(projectDir2, "package.json");
162653
162790
  if (!existsSync20(pkgPath)) return "";
162654
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
162791
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
162655
162792
  const parts = [];
162656
162793
  if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
162657
162794
  parts.push(`dependencies: ${Object.keys(pkg.dependencies).join(", ")}`);
@@ -165151,18 +165288,18 @@ __export(hooks_path_exports, {
165151
165288
  resolveGitDir: () => resolveGitDir,
165152
165289
  resolveHooksDir: () => resolveHooksDir
165153
165290
  });
165154
- import { existsSync as existsSync21, readFileSync as readFileSync15, statSync as statSync5, mkdirSync as mkdirSync5 } from "fs";
165291
+ import { existsSync as existsSync21, readFileSync as readFileSync16, statSync as statSync6, mkdirSync as mkdirSync6 } from "fs";
165155
165292
  import path34 from "path";
165156
165293
  function resolveGitDir(projectRoot) {
165157
165294
  const dotGit = path34.join(projectRoot, ".git");
165158
165295
  if (!existsSync21(dotGit)) return null;
165159
- const stat5 = statSync5(dotGit);
165296
+ const stat5 = statSync6(dotGit);
165160
165297
  if (stat5.isDirectory()) {
165161
165298
  return dotGit;
165162
165299
  }
165163
165300
  if (stat5.isFile()) {
165164
165301
  try {
165165
- const content = readFileSync15(dotGit, "utf-8").trim();
165302
+ const content = readFileSync16(dotGit, "utf-8").trim();
165166
165303
  const match2 = content.match(/^gitdir:\s*(.+)$/);
165167
165304
  if (match2) {
165168
165305
  const gitdir = match2[1].trim();
@@ -165188,7 +165325,7 @@ function resolveHooksDir(projectRoot) {
165188
165325
  function ensureHooksDir(projectRoot) {
165189
165326
  const resolved = resolveHooksDir(projectRoot);
165190
165327
  if (!resolved) return null;
165191
- mkdirSync5(resolved.hooksDir, { recursive: true });
165328
+ mkdirSync6(resolved.hooksDir, { recursive: true });
165192
165329
  return resolved;
165193
165330
  }
165194
165331
  var init_hooks_path = __esm({
@@ -167373,7 +167510,7 @@ var RulesSyncer = class {
167373
167510
 
167374
167511
  // src/workspace/engine.ts
167375
167512
  init_esm_shims();
167376
- import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync14, cpSync, mkdirSync as mkdirSync3 } from "fs";
167513
+ import { readFileSync as readFileSync10, readdirSync as readdirSync3, existsSync as existsSync14, cpSync, mkdirSync as mkdirSync4 } from "fs";
167377
167514
  import { join as join19 } from "path";
167378
167515
  import { homedir as homedir21 } from "os";
167379
167516
 
@@ -168325,7 +168462,7 @@ function sanitize(input) {
168325
168462
 
168326
168463
  // src/workspace/applier.ts
168327
168464
  init_esm_shims();
168328
- import { existsSync as existsSync13, mkdirSync as mkdirSync2, copyFileSync, writeFileSync as writeFileSync2, unlinkSync, renameSync } from "fs";
168465
+ import { existsSync as existsSync13, mkdirSync as mkdirSync3, copyFileSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, renameSync } from "fs";
168329
168466
  import { dirname } from "path";
168330
168467
  var WorkspaceSyncApplier = class {
168331
168468
  /**
@@ -168352,7 +168489,7 @@ var WorkspaceSyncApplier = class {
168352
168489
  try {
168353
168490
  const dir = dirname(file.filePath);
168354
168491
  if (!existsSync13(dir)) {
168355
- mkdirSync2(dir, { recursive: true });
168492
+ mkdirSync3(dir, { recursive: true });
168356
168493
  }
168357
168494
  } catch (err) {
168358
168495
  result.errors.push(`Cannot create directory for ${file.filePath}: ${err}`);
@@ -168377,7 +168514,7 @@ var WorkspaceSyncApplier = class {
168377
168514
  for (const file of files) {
168378
168515
  try {
168379
168516
  const tempPath = file.filePath + `.tmp-${Date.now()}`;
168380
- writeFileSync2(tempPath, file.content, "utf-8");
168517
+ writeFileSync3(tempPath, file.content, "utf-8");
168381
168518
  renameSync(tempPath, file.filePath);
168382
168519
  result.filesWritten.push(file.filePath);
168383
168520
  } catch (err) {
@@ -168412,7 +168549,7 @@ var WorkspaceSyncApplier = class {
168412
168549
  for (const backup of backups) {
168413
168550
  try {
168414
168551
  if (existsSync13(backup.backupPath)) {
168415
- unlinkSync(backup.backupPath);
168552
+ unlinkSync2(backup.backupPath);
168416
168553
  }
168417
168554
  } catch {
168418
168555
  }
@@ -168478,7 +168615,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
168478
168615
  for (const path35 of pathsToCheck) {
168479
168616
  if (existsSync14(path35)) {
168480
168617
  try {
168481
- const content = readFileSync9(path35, "utf-8");
168618
+ const content = readFileSync10(path35, "utf-8");
168482
168619
  const servers = adapter.parse(content);
168483
168620
  for (const s2 of servers) {
168484
168621
  if (!merged.has(s2.name)) merged.set(s2.name, s2);
@@ -168532,7 +168669,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
168532
168669
  let configContent;
168533
168670
  if (target === "antigravity" && existsSync14(configPath)) {
168534
168671
  try {
168535
- const existing = JSON.parse(readFileSync9(configPath, "utf-8"));
168672
+ const existing = JSON.parse(readFileSync10(configPath, "utf-8"));
168536
168673
  const generated = JSON.parse(adapter.generate(result.mcpServers.scanned));
168537
168674
  existing.mcpServers = { ...existing.mcpServers ?? {}, ...generated.mcpServers };
168538
168675
  configContent = JSON.stringify(existing, null, 2);
@@ -168615,7 +168752,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
168615
168752
  if (!existsSync14(skillMd)) continue;
168616
168753
  let description = "";
168617
168754
  try {
168618
- const content = readFileSync9(skillMd, "utf-8");
168755
+ const content = readFileSync10(skillMd, "utf-8");
168619
168756
  const match2 = content.match(/^---[\s\S]*?description:\s*["']?(.+?)["']?\s*$/m);
168620
168757
  if (match2) description = match2[1];
168621
168758
  } catch {
@@ -168666,7 +168803,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
168666
168803
  continue;
168667
168804
  }
168668
168805
  try {
168669
- mkdirSync3(targetDir, { recursive: true });
168806
+ mkdirSync4(targetDir, { recursive: true });
168670
168807
  cpSync(skill2.sourcePath, dest, { recursive: true });
168671
168808
  copied.push(skill2.name);
168672
168809
  } catch {
@@ -168682,7 +168819,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
168682
168819
  const files = readdirSync3(wfDir).filter((f3) => f3.endsWith(".md"));
168683
168820
  for (const file of files) {
168684
168821
  try {
168685
- const content = readFileSync9(join19(wfDir, file), "utf-8");
168822
+ const content = readFileSync10(join19(wfDir, file), "utf-8");
168686
168823
  workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
168687
168824
  } catch {
168688
168825
  }
@@ -169022,7 +169159,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
169022
169159
  function getMcpServerInfo() {
169023
169160
  return {
169024
169161
  name: "memorix",
169025
- version: true ? "1.8.9" : "1.0.1"
169162
+ version: true ? "1.9.0" : "1.0.1"
169026
169163
  };
169027
169164
  }
169028
169165
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -173639,10 +173776,10 @@ Preview: ${analysis.description.slice(0, 300)}${analysis.description.length > 30
173639
173776
  const { ensureHooksDir: ensureHooksDir2 } = await Promise.resolve().then(() => (init_hooks_path(), hooks_path_exports));
173640
173777
  const resolved = ensureHooksDir2(project.rootPath);
173641
173778
  if (resolved) {
173642
- const { existsSync: existsSync22, readFileSync: readFileSync16, writeFileSync: writeFileSync4, chmodSync } = await import("fs");
173779
+ const { existsSync: existsSync22, readFileSync: readFileSync17, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
173643
173780
  const { hookPath } = resolved;
173644
173781
  const HOOK_MARKER = "# [memorix-git-hook]";
173645
- const needsInstall = !existsSync22(hookPath) || !readFileSync16(hookPath, "utf-8").includes(HOOK_MARKER);
173782
+ const needsInstall = !existsSync22(hookPath) || !readFileSync17(hookPath, "utf-8").includes(HOOK_MARKER);
173646
173783
  if (needsInstall) {
173647
173784
  const hookScript = `#!/bin/sh
173648
173785
  ${HOOK_MARKER}
@@ -173652,8 +173789,8 @@ if command -v memorix >/dev/null 2>&1; then
173652
173789
  fi
173653
173790
  `;
173654
173791
  if (existsSync22(hookPath)) {
173655
- const existing = readFileSync16(hookPath, "utf-8");
173656
- writeFileSync4(hookPath, existing.trimEnd() + `
173792
+ const existing = readFileSync17(hookPath, "utf-8");
173793
+ writeFileSync5(hookPath, existing.trimEnd() + `
173657
173794
 
173658
173795
  ${HOOK_MARKER}
173659
173796
  if command -v memorix >/dev/null 2>&1; then
@@ -173661,7 +173798,7 @@ if command -v memorix >/dev/null 2>&1; then
173661
173798
  fi
173662
173799
  `, "utf-8");
173663
173800
  } else {
173664
- writeFileSync4(hookPath, hookScript, "utf-8");
173801
+ writeFileSync5(hookPath, hookScript, "utf-8");
173665
173802
  }
173666
173803
  try {
173667
173804
  chmodSync(hookPath, 493);