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/index.js CHANGED
@@ -7292,6 +7292,14 @@ __export(orama_store_exports, {
7292
7292
  updateObservationMetadata: () => updateObservationMetadata
7293
7293
  });
7294
7294
  import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
7295
+ function rememberSearchMode(key, value) {
7296
+ if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
7297
+ const oldest = lastSearchModeByProject.keys().next().value;
7298
+ if (oldest !== void 0) lastSearchModeByProject.delete(oldest);
7299
+ }
7300
+ lastSearchModeByProject.delete(key);
7301
+ lastSearchModeByProject.set(key, value);
7302
+ }
7295
7303
  function getLastSearchMode(projectId) {
7296
7304
  return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
7297
7305
  }
@@ -7304,9 +7312,6 @@ function makeEntryKey(projectId, observationId2) {
7304
7312
  function rememberObservationDoc(doc) {
7305
7313
  const publicDoc = { ...doc };
7306
7314
  delete publicDoc.embedding;
7307
- if (doc.projectId && typeof doc.observationId === "number") {
7308
- docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
7309
- }
7310
7315
  return publicDoc;
7311
7316
  }
7312
7317
  function isCommandLikeQuery(query) {
@@ -7410,7 +7415,6 @@ async function resetDb() {
7410
7415
  indexEmbeddingProvider = null;
7411
7416
  deferredCachedVectorHydration = null;
7412
7417
  lastSearchModeByProject.clear();
7413
- docByObservationKey.clear();
7414
7418
  }
7415
7419
  function isEmbeddingEnabled() {
7416
7420
  return embeddingEnabled;
@@ -7473,78 +7477,72 @@ function isCompatibleCachedVector(vector) {
7473
7477
  vector && embeddingDimensions !== null && vector.length === embeddingDimensions && vector.every(Number.isFinite)
7474
7478
  );
7475
7479
  }
7476
- async function attachCachedVectors(database, candidates) {
7477
- const cachedVectors = await getCachedEmbeddings(
7478
- candidates.map(({ observation }) => observationEmbeddingText(observation))
7479
- );
7480
- if (db !== database) return;
7481
- for (let index = 0; index < candidates.length; index++) {
7482
- const vector = cachedVectors[index];
7483
- if (!isCompatibleCachedVector(vector)) continue;
7484
- const existing = getByID(database, candidates[index].id);
7485
- if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(candidates[index].observation)) continue;
7486
- try {
7487
- await update(database, candidates[index].id, { ...existing, embedding: vector });
7488
- } catch {
7489
- }
7490
- if (index % 50 === 0) {
7491
- await new Promise((resolve5) => setImmediate(resolve5));
7480
+ async function attachCachedVectors(database, observations2) {
7481
+ const batchSize = 200;
7482
+ for (let start = 0; start < observations2.length; start += batchSize) {
7483
+ const batch = observations2.slice(start, start + batchSize);
7484
+ const cachedVectors = await getCachedEmbeddings(batch.map(observationEmbeddingText));
7485
+ if (db !== database) return;
7486
+ for (let index = 0; index < batch.length; index++) {
7487
+ const vector = cachedVectors[index];
7488
+ if (!isCompatibleCachedVector(vector)) continue;
7489
+ const id = makeOramaObservationId(batch[index].projectId, batch[index].id);
7490
+ const existing = getByID(database, id);
7491
+ if (!existing || documentEmbeddingText(existing) !== observationEmbeddingText(batch[index])) continue;
7492
+ try {
7493
+ await update(database, id, { ...existing, embedding: vector });
7494
+ } catch {
7495
+ }
7492
7496
  }
7497
+ await new Promise((resolve5) => setImmediate(resolve5));
7493
7498
  }
7494
7499
  }
7495
7500
  async function hydrateIndex(observations2, options = {}) {
7496
7501
  const database = await getDb({ allowNetworkProbe: options.allowNetworkProbe });
7497
- const candidates = [];
7498
- for (const observation of observations2) {
7499
- if (!observation || !observation.id || !observation.projectId) continue;
7500
- const id = makeOramaObservationId(observation.projectId, observation.id);
7501
- if (getByID(database, id)) continue;
7502
- candidates.push({ observation, id });
7503
- }
7504
7502
  const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
7505
- const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
7506
- const documents = [];
7507
- for (let index = 0; index < candidates.length; index++) {
7508
- const { observation: obs, id } = candidates[index];
7509
- try {
7510
- const vector = cachedVectors[index];
7511
- const compatibleVector = isCompatibleCachedVector(vector) ? vector : null;
7512
- const doc = {
7513
- id,
7514
- observationId: obs.id,
7515
- entityName: obs.entityName || "",
7516
- type: obs.type || "discovery",
7517
- title: obs.title || "",
7518
- narrative: obs.narrative || "",
7519
- facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
7520
- filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
7521
- concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
7522
- tokens: obs.tokens ?? 0,
7523
- createdAt: obs.createdAt || "",
7524
- projectId: obs.projectId,
7525
- accessCount: obs.accessCount ?? 0,
7526
- lastAccessedAt: obs.lastAccessedAt || "",
7527
- status: obs.status ?? "active",
7528
- source: obs.source || "agent",
7529
- sourceDetail: obs.sourceDetail ?? "",
7530
- valueCategory: obs.valueCategory ?? "",
7531
- admissionState: obs.admissionState ?? "",
7532
- admissionReason: obs.admissionReason ?? "",
7533
- visibility: obs.visibility ?? "project",
7534
- createdByAgentId: obs.createdByAgentId ?? "",
7535
- sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
7536
- documentType: "observation",
7537
- knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
7538
- ...compatibleVector ? { embedding: compatibleVector } : {}
7539
- };
7540
- documents.push(doc);
7541
- } catch {
7542
- }
7543
- }
7544
7503
  let inserted = 0;
7545
7504
  const hydrationBatchSize = 200;
7546
- for (let start = 0; start < documents.length; start += hydrationBatchSize) {
7547
- const batch = documents.slice(start, start + hydrationBatchSize);
7505
+ for (let start = 0; start < observations2.length; start += hydrationBatchSize) {
7506
+ const batchObservations = observations2.slice(start, start + hydrationBatchSize).filter((observation) => observation && observation.id && observation.projectId).filter((observation) => !getByID(database, makeOramaObservationId(observation.projectId, observation.id)));
7507
+ if (batchObservations.length === 0) continue;
7508
+ const cachedVectors = deferCachedVectors || options.skipCachedVectors ? batchObservations.map(() => null) : await getCachedEmbeddings(batchObservations.map(observationEmbeddingText));
7509
+ const batch = [];
7510
+ for (let index = 0; index < batchObservations.length; index++) {
7511
+ const obs = batchObservations[index];
7512
+ try {
7513
+ const compatibleVector = isCompatibleCachedVector(cachedVectors[index]) ? cachedVectors[index] : null;
7514
+ batch.push({
7515
+ id: makeOramaObservationId(obs.projectId, obs.id),
7516
+ observationId: obs.id,
7517
+ entityName: obs.entityName || "",
7518
+ type: obs.type || "discovery",
7519
+ title: obs.title || "",
7520
+ narrative: obs.narrative || "",
7521
+ facts: Array.isArray(obs.facts) ? obs.facts.join(" ") : "",
7522
+ filesModified: Array.isArray(obs.filesModified) ? obs.filesModified.join(" ") : "",
7523
+ concepts: Array.isArray(obs.concepts) ? obs.concepts.join(" ") : "",
7524
+ tokens: obs.tokens ?? 0,
7525
+ createdAt: obs.createdAt || "",
7526
+ projectId: obs.projectId,
7527
+ accessCount: obs.accessCount ?? 0,
7528
+ lastAccessedAt: obs.lastAccessedAt || "",
7529
+ status: obs.status ?? "active",
7530
+ source: obs.source || "agent",
7531
+ sourceDetail: obs.sourceDetail ?? "",
7532
+ valueCategory: obs.valueCategory ?? "",
7533
+ admissionState: obs.admissionState ?? "",
7534
+ admissionReason: obs.admissionReason ?? "",
7535
+ visibility: obs.visibility ?? "project",
7536
+ createdByAgentId: obs.createdByAgentId ?? "",
7537
+ sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
7538
+ documentType: "observation",
7539
+ knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
7540
+ ...compatibleVector ? { embedding: compatibleVector } : {}
7541
+ });
7542
+ } catch {
7543
+ }
7544
+ }
7545
+ if (batch.length === 0) continue;
7548
7546
  try {
7549
7547
  await insertMultiple(database, batch, batch.length);
7550
7548
  for (const doc of batch) rememberObservationDoc(doc);
@@ -7566,7 +7564,7 @@ async function hydrateIndex(observations2, options = {}) {
7566
7564
  }
7567
7565
  await new Promise((resolve5) => setImmediate(resolve5));
7568
7566
  }
7569
- deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
7567
+ deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, observations2).catch(() => {
7570
7568
  }) : null;
7571
7569
  return inserted;
7572
7570
  }
@@ -7603,12 +7601,6 @@ async function updateObservationMetadata(projectId, observationId2, patch) {
7603
7601
  async function removeObservation(oramaId) {
7604
7602
  const database = await getDb();
7605
7603
  await remove2(database, oramaId);
7606
- for (const [key, doc] of docByObservationKey.entries()) {
7607
- if (doc.id === oramaId) {
7608
- docByObservationKey.delete(key);
7609
- break;
7610
- }
7611
- }
7612
7604
  }
7613
7605
  async function searchObservations(options) {
7614
7606
  const perf = !!process.env.MEMORIX_PERF;
@@ -7623,7 +7615,7 @@ async function searchObservations(options) {
7623
7615
  const modeKey = options.projectId ?? SEARCH_MODE_DEFAULT_KEY;
7624
7616
  const quality = options.quality ?? "balanced";
7625
7617
  const database = await getDb();
7626
- lastSearchModeByProject.set(
7618
+ rememberSearchMode(
7627
7619
  modeKey,
7628
7620
  quality === "fast" ? "fulltext (fast profile)" : embeddingEnabled ? "hybrid" : "fulltext"
7629
7621
  );
@@ -7690,7 +7682,7 @@ async function searchObservations(options) {
7690
7682
  if (provider2) {
7691
7683
  const activeVectorDimensions = getVectorDimensions();
7692
7684
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
7693
- lastSearchModeByProject.set(
7685
+ rememberSearchMode(
7694
7686
  modeKey,
7695
7687
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
7696
7688
  );
@@ -7715,7 +7707,7 @@ async function searchObservations(options) {
7715
7707
  mark("embedding");
7716
7708
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
7717
7709
  const isCJKHeavy = cjkRatio > 0.3;
7718
- lastSearchModeByProject.set(modeKey, "hybrid");
7710
+ rememberSearchMode(modeKey, "hybrid");
7719
7711
  searchParams = {
7720
7712
  ...searchParams,
7721
7713
  mode: "hybrid",
@@ -7731,7 +7723,7 @@ async function searchObservations(options) {
7731
7723
  }
7732
7724
  }
7733
7725
  } catch (error) {
7734
- lastSearchModeByProject.set(modeKey, "fulltext (embedding unavailable)");
7726
+ rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
7735
7727
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
7736
7728
  }
7737
7729
  }
@@ -7741,7 +7733,7 @@ async function searchObservations(options) {
7741
7733
  results = await search(database, searchParams);
7742
7734
  } catch (error) {
7743
7735
  if (queryVector && isVectorDimensionMismatchError(error)) {
7744
- lastSearchModeByProject.set(modeKey, "fulltext (embedding dimension mismatch)");
7736
+ rememberSearchMode(modeKey, "fulltext (embedding dimension mismatch)");
7745
7737
  console.error("[memorix] Vector search dimension mismatch detected, retrying without embeddings");
7746
7738
  results = await search(database, stripVectorSearchParams(searchParams));
7747
7739
  } else {
@@ -7763,7 +7755,7 @@ async function searchObservations(options) {
7763
7755
  },
7764
7756
  similarity: 0.25
7765
7757
  };
7766
- lastSearchModeByProject.set(modeKey, "vector-only (hybrid empty fallback)");
7758
+ rememberSearchMode(modeKey, "vector-only (hybrid empty fallback)");
7767
7759
  results = await search(database, vectorOnlyParams);
7768
7760
  } catch {
7769
7761
  }
@@ -8004,7 +7996,7 @@ async function searchObservations(options) {
8004
7996
  const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
8005
7997
  if (rerankedTop.length > 0) {
8006
7998
  intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
8007
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
7999
+ rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
8008
8000
  mark("rerank(usedNeural=true)");
8009
8001
  }
8010
8002
  }
@@ -8021,7 +8013,7 @@ async function searchObservations(options) {
8021
8013
  );
8022
8014
  mark(`rerank(usedLLM=${usedLLM})`);
8023
8015
  if (usedLLM) {
8024
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
8016
+ rememberSearchMode(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
8025
8017
  const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
8026
8018
  if (rerankedTop.length > 0) {
8027
8019
  intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
@@ -8094,13 +8086,6 @@ async function getObservationsByIds(ids, projectId) {
8094
8086
  const database = await getDb();
8095
8087
  const results = [];
8096
8088
  for (const id of ids) {
8097
- if (projectId) {
8098
- const cached = docByObservationKey.get(makeEntryKey(projectId, id));
8099
- if (cached) {
8100
- results.push(cached);
8101
- continue;
8102
- }
8103
- }
8104
8089
  const searchResult = await search(database, {
8105
8090
  term: "",
8106
8091
  includeVectors: true,
@@ -8200,7 +8185,7 @@ function formatTime(isoDate) {
8200
8185
  return isoDate;
8201
8186
  }
8202
8187
  }
8203
- 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;
8188
+ 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;
8204
8189
  var init_orama_store = __esm({
8205
8190
  "src/store/orama-store.ts"() {
8206
8191
  "use strict";
@@ -8221,10 +8206,10 @@ var init_orama_store = __esm({
8221
8206
  embeddingDimensions = null;
8222
8207
  indexEmbeddingProvider = null;
8223
8208
  deferredCachedVectorHydration = null;
8224
- docByObservationKey = /* @__PURE__ */ new Map();
8225
8209
  NON_CJK_HYBRID_SIMILARITY = 0.45;
8226
8210
  lastSearchModeByProject = /* @__PURE__ */ new Map();
8227
8211
  SEARCH_MODE_DEFAULT_KEY = "__global__";
8212
+ MAX_SEARCH_MODE_KEYS = 4096;
8228
8213
  COMMAND_LOG_TITLE2 = /^(Ran:|Command:|Executed:)\s/i;
8229
8214
  COMMAND_STYLE_TITLE = /(\bfindstr\b|\bSelect-String\b|\bGet-Content\b|\bnpx\s+vitest\b|\bnpx\s+tsc\b|\b2>&1\b)/i;
8230
8215
  COMMAND_LIKE_QUERY2 = /\b(git|npm|npx|pnpm|yarn|node|bash|powershell|curl|memorix)\b/i;
@@ -9652,6 +9637,60 @@ var init_evidence_store = __esm({
9652
9637
  }
9653
9638
  });
9654
9639
 
9640
+ // src/runtime/maintenance-targets.ts
9641
+ var maintenance_targets_exports = {};
9642
+ __export(maintenance_targets_exports, {
9643
+ MaintenanceTargetStore: () => MaintenanceTargetStore
9644
+ });
9645
+ function rowToTarget(row) {
9646
+ return {
9647
+ projectId: row.project_id,
9648
+ projectRoot: row.project_root,
9649
+ dataDir: row.data_dir,
9650
+ updatedAt: Number(row.updated_at)
9651
+ };
9652
+ }
9653
+ var MaintenanceTargetStore;
9654
+ var init_maintenance_targets = __esm({
9655
+ "src/runtime/maintenance-targets.ts"() {
9656
+ "use strict";
9657
+ init_esm_shims();
9658
+ init_sqlite_db();
9659
+ MaintenanceTargetStore = class {
9660
+ db;
9661
+ constructor(dataDir) {
9662
+ this.db = getDatabase(dataDir);
9663
+ }
9664
+ register(input) {
9665
+ if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
9666
+ if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
9667
+ if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
9668
+ const now3 = input.now ?? Date.now();
9669
+ this.db.prepare(`
9670
+ INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
9671
+ VALUES (?, ?, ?, ?)
9672
+ ON CONFLICT(project_id) DO UPDATE SET
9673
+ project_root = excluded.project_root,
9674
+ data_dir = excluded.data_dir,
9675
+ updated_at = excluded.updated_at
9676
+ `).run(input.projectId, input.projectRoot, input.dataDir, now3);
9677
+ return this.get(input.projectId);
9678
+ }
9679
+ get(projectId) {
9680
+ const row = this.db.prepare(`
9681
+ SELECT * FROM maintenance_targets WHERE project_id = ?
9682
+ `).get(projectId);
9683
+ return row ? rowToTarget(row) : void 0;
9684
+ }
9685
+ list() {
9686
+ return this.db.prepare(`
9687
+ SELECT * FROM maintenance_targets ORDER BY updated_at DESC
9688
+ `).all().map(rowToTarget);
9689
+ }
9690
+ };
9691
+ }
9692
+ });
9693
+
9655
9694
  // src/runtime/isolated-maintenance.ts
9656
9695
  import { spawn } from "child_process";
9657
9696
  import { existsSync as existsSync8 } from "fs";
@@ -152188,10 +152227,21 @@ __export(vector_backfill_runner_exports, {
152188
152227
  launchDetachedVectorBackfill: () => launchDetachedVectorBackfill,
152189
152228
  main: () => main,
152190
152229
  parseVectorBackfillRequest: () => parseVectorBackfillRequest,
152191
- resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath
152230
+ releaseVectorBackfillLock: () => releaseVectorBackfillLock,
152231
+ resolveVectorBackfillRunnerPath: () => resolveVectorBackfillRunnerPath,
152232
+ vectorBackfillLockPath: () => vectorBackfillLockPath
152192
152233
  });
152193
152234
  import { spawn as spawn4 } from "child_process";
152194
- import { existsSync as existsSync12 } from "fs";
152235
+ import {
152236
+ closeSync,
152237
+ existsSync as existsSync12,
152238
+ mkdirSync as mkdirSync2,
152239
+ openSync,
152240
+ readFileSync as readFileSync9,
152241
+ statSync as statSync4,
152242
+ unlinkSync,
152243
+ writeFileSync as writeFileSync2
152244
+ } from "fs";
152195
152245
  import path24 from "path";
152196
152246
  import { fileURLToPath as fileURLToPath5 } from "url";
152197
152247
  function isNonEmptyString(value) {
@@ -152218,10 +152268,104 @@ function parseVectorBackfillRequest(raw) {
152218
152268
  dataDir: value.dataDir
152219
152269
  };
152220
152270
  }
152271
+ function vectorBackfillLockPath(dataDir) {
152272
+ return path24.join(dataDir, VECTOR_BACKFILL_LOCK_FILE);
152273
+ }
152274
+ function processIsAlive(pid) {
152275
+ if (!Number.isSafeInteger(pid) || pid == null || pid <= 0) return false;
152276
+ try {
152277
+ process.kill(pid, 0);
152278
+ return true;
152279
+ } catch (error) {
152280
+ return error.code === "EPERM";
152281
+ }
152282
+ }
152283
+ function readVectorBackfillLock(lockPath) {
152284
+ try {
152285
+ const value = JSON.parse(readFileSync9(lockPath, "utf8"));
152286
+ if (typeof value.parentPid !== "number" || !Number.isSafeInteger(value.parentPid) || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.projectId !== "string") return void 0;
152287
+ return {
152288
+ parentPid: value.parentPid,
152289
+ ...typeof value.childPid === "number" ? { childPid: value.childPid } : {},
152290
+ startedAt: value.startedAt,
152291
+ projectId: value.projectId
152292
+ };
152293
+ } catch {
152294
+ return void 0;
152295
+ }
152296
+ }
152297
+ function writeVectorBackfillLock(lockPath, value) {
152298
+ writeFileSync2(lockPath, `${JSON.stringify(value)}
152299
+ `, "utf8");
152300
+ }
152301
+ function acquireVectorBackfillLock(dataDir, projectId, preferredPath) {
152302
+ const lockPath = preferredPath ?? vectorBackfillLockPath(dataDir);
152303
+ mkdirSync2(path24.dirname(lockPath), { recursive: true });
152304
+ const initial = {
152305
+ parentPid: process.pid,
152306
+ startedAt: Date.now(),
152307
+ projectId
152308
+ };
152309
+ for (let attempt = 0; attempt < 2; attempt++) {
152310
+ try {
152311
+ const fd = openSync(lockPath, "wx");
152312
+ try {
152313
+ writeFileSync2(fd, `${JSON.stringify(initial)}
152314
+ `, "utf8");
152315
+ } finally {
152316
+ closeSync(fd);
152317
+ }
152318
+ return {
152319
+ path: lockPath,
152320
+ setChildPid(pid) {
152321
+ try {
152322
+ writeVectorBackfillLock(lockPath, { ...initial, childPid: pid });
152323
+ } catch {
152324
+ }
152325
+ },
152326
+ release() {
152327
+ try {
152328
+ unlinkSync(lockPath);
152329
+ } catch {
152330
+ }
152331
+ }
152332
+ };
152333
+ } catch (error) {
152334
+ if (error.code !== "EEXIST") return void 0;
152335
+ const existing = readVectorBackfillLock(lockPath);
152336
+ const ownerAlive = existing && (processIsAlive(existing.childPid) || processIsAlive(existing.parentPid));
152337
+ if (ownerAlive) return void 0;
152338
+ let stale = false;
152339
+ try {
152340
+ stale = Date.now() - statSync4(lockPath).mtimeMs >= STALE_LOCK_RECOVERY_MS;
152341
+ } catch {
152342
+ stale = false;
152343
+ }
152344
+ if (!existing && !stale) return void 0;
152345
+ try {
152346
+ unlinkSync(lockPath);
152347
+ } catch {
152348
+ return void 0;
152349
+ }
152350
+ }
152351
+ }
152352
+ return void 0;
152353
+ }
152354
+ function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFILL_LOCK) {
152355
+ if (!lockPath) return;
152356
+ const lock = readVectorBackfillLock(lockPath);
152357
+ if (lock && lock.childPid !== process.pid) return;
152358
+ try {
152359
+ unlinkSync(lockPath);
152360
+ } catch {
152361
+ }
152362
+ }
152221
152363
  function launchDetachedVectorBackfill(request, options = {}) {
152222
152364
  const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
152223
152365
  const exists = options.exists ?? existsSync12;
152224
152366
  if (!exists(runnerPath)) return false;
152367
+ const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
152368
+ if (!lock) return false;
152225
152369
  try {
152226
152370
  const child = (options.spawn ?? spawn4)(process.execPath, [runnerPath], {
152227
152371
  cwd: request.projectRoot,
@@ -152231,14 +152375,23 @@ function launchDetachedVectorBackfill(request, options = {}) {
152231
152375
  stdio: "ignore",
152232
152376
  // The request contains only local project metadata, never credentials.
152233
152377
  // Environment transport avoids a live stdin pipe keeping the CLI alive.
152234
- env: { ...process.env, MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request) },
152378
+ env: {
152379
+ ...process.env,
152380
+ MEMORIX_VECTOR_BACKFILL_REQUEST: JSON.stringify(request),
152381
+ MEMORIX_VECTOR_BACKFILL_LOCK: lock.path
152382
+ },
152235
152383
  windowsHide: true
152236
152384
  });
152385
+ if (child.pid) lock.setChildPid(child.pid);
152386
+ else lock.release();
152237
152387
  child.once?.("error", () => {
152238
152388
  });
152389
+ child.once?.("close", () => lock.release());
152390
+ if (typeof child.once !== "function") lock.release();
152239
152391
  child.unref();
152240
- return true;
152392
+ return Boolean(child.pid);
152241
152393
  } catch {
152394
+ lock.release();
152242
152395
  return false;
152243
152396
  }
152244
152397
  }
@@ -152250,12 +152403,32 @@ async function executeVectorBackfill(request) {
152250
152403
  await prepareSearchIndex();
152251
152404
  await getDeferredCachedVectorHydration()?.catch(() => {
152252
152405
  });
152406
+ const queue = new MaintenanceJobStore(request.dataDir);
152407
+ const targets = new MaintenanceTargetStore(request.dataDir);
152408
+ let preparedRoot = request.projectRoot;
152409
+ const prepareTarget = async (target) => {
152410
+ if (target.projectRoot === preparedRoot) return;
152411
+ initProjectRoot(target.projectRoot);
152412
+ loadDotenv(target.projectRoot);
152413
+ preparedRoot = target.projectRoot;
152414
+ };
152415
+ const handler = async (job) => {
152416
+ const target = targets.get(job.projectId) ?? (job.projectId === request.projectId ? { projectId: request.projectId, projectRoot: request.projectRoot, dataDir: request.dataDir, updatedAt: Date.now() } : void 0);
152417
+ if (!target) return { action: "reschedule", delayMs: 3e4 };
152418
+ await prepareTarget(target);
152419
+ return createProjectMaintenanceHandler(job.projectId, target.dataDir, target.projectRoot)(job);
152420
+ };
152253
152421
  const worker = new MaintenanceJobWorker(
152254
- new MaintenanceJobStore(request.dataDir),
152255
- createProjectMaintenanceHandler(request.projectId, request.dataDir, request.projectRoot),
152256
- { projectId: request.projectId, kinds: ["vector-backfill"] }
152422
+ queue,
152423
+ handler,
152424
+ { kinds: ["vector-backfill"] }
152257
152425
  );
152258
- return worker.runOnce();
152426
+ let result = { state: "idle" };
152427
+ while (true) {
152428
+ result = await worker.runOnce();
152429
+ if (result.state === "idle") break;
152430
+ }
152431
+ return result;
152259
152432
  }
152260
152433
  async function readStdin() {
152261
152434
  return new Promise((resolve5, reject) => {
@@ -152278,9 +152451,11 @@ async function main() {
152278
152451
  `);
152279
152452
  process.exitCode = 1;
152280
152453
  } finally {
152454
+ releaseVectorBackfillLock();
152281
152455
  closeAllDatabases();
152282
152456
  }
152283
152457
  }
152458
+ var VECTOR_BACKFILL_LOCK_FILE, STALE_LOCK_RECOVERY_MS;
152284
152459
  var init_vector_backfill_runner = __esm({
152285
152460
  "src/runtime/vector-backfill-runner.ts"() {
152286
152461
  "use strict";
@@ -152293,7 +152468,10 @@ var init_vector_backfill_runner = __esm({
152293
152468
  init_orama_store();
152294
152469
  init_sqlite_db();
152295
152470
  init_maintenance_jobs();
152471
+ init_maintenance_targets();
152296
152472
  init_project_maintenance();
152473
+ VECTOR_BACKFILL_LOCK_FILE = ".memorix-vector-backfill.lock";
152474
+ STALE_LOCK_RECOVERY_MS = 24 * 60 * 60 * 1e3;
152297
152475
  if (process.argv[1] && process.argv[1].endsWith("vector-backfill-runner.js")) {
152298
152476
  void main();
152299
152477
  }
@@ -152335,11 +152513,17 @@ function resetObservationRuntime() {
152335
152513
  embeddingWorkerProjectRoot = void 0;
152336
152514
  vectorMissingIds.clear();
152337
152515
  vectorSchemaUpgradePromise = null;
152516
+ embeddingFailureLogTimestamps.clear();
152338
152517
  }
152339
152518
  function logEmbeddingFailureOnce(key, message) {
152340
152519
  const now3 = Date.now();
152341
152520
  const last = embeddingFailureLogTimestamps.get(key) ?? 0;
152342
152521
  if (now3 - last < EMBEDDING_FAILURE_LOG_COOLDOWN_MS) return;
152522
+ if (!embeddingFailureLogTimestamps.has(key) && embeddingFailureLogTimestamps.size >= MAX_EMBEDDING_FAILURE_KEYS) {
152523
+ const oldest = embeddingFailureLogTimestamps.keys().next().value;
152524
+ if (oldest !== void 0) embeddingFailureLogTimestamps.delete(oldest);
152525
+ }
152526
+ embeddingFailureLogTimestamps.delete(key);
152343
152527
  embeddingFailureLogTimestamps.set(key, now3);
152344
152528
  console.error(message);
152345
152529
  }
@@ -152369,13 +152553,14 @@ function queueVectorBackfill(projectId, options = {}) {
152369
152553
  const dataDir = projectDir;
152370
152554
  if (!dataDir) return;
152371
152555
  void Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)).then(({ MaintenanceJobStore: MaintenanceJobStore2 }) => {
152372
- new MaintenanceJobStore2(dataDir).enqueue({
152556
+ const job = new MaintenanceJobStore2(dataDir).enqueue({
152373
152557
  projectId,
152374
152558
  kind: "vector-backfill",
152375
152559
  dedupeKey: "vector-backfill",
152376
152560
  payload: { limit: 12 }
152377
152561
  });
152378
- if (options.detachedWorker && options.projectRoot) {
152562
+ const due = !job || job.status === "pending" && job.runAfter <= Date.now();
152563
+ if (due && options.detachedWorker && options.projectRoot) {
152379
152564
  void Promise.resolve().then(() => (init_vector_backfill_runner(), vector_backfill_runner_exports)).then(({ launchDetachedVectorBackfill: launchDetachedVectorBackfill2 }) => {
152380
152565
  launchDetachedVectorBackfill2({
152381
152566
  projectId,
@@ -153306,7 +153491,7 @@ async function backfillVectorEmbeddings(options = {}) {
153306
153491
  ...lastFailure ? { lastError: lastFailure } : {}
153307
153492
  };
153308
153493
  }
153309
- var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
153494
+ var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS, MAX_EMBEDDING_FAILURE_KEYS;
153310
153495
  var init_observations = __esm({
153311
153496
  "src/memory/observations.ts"() {
153312
153497
  "use strict";
@@ -153334,6 +153519,7 @@ var init_observations = __esm({
153334
153519
  lastVectorBackfill = null;
153335
153520
  embeddingFailureLogTimestamps = /* @__PURE__ */ new Map();
153336
153521
  EMBEDDING_FAILURE_LOG_COOLDOWN_MS = 3e4;
153522
+ MAX_EMBEDDING_FAILURE_KEYS = 1024;
153337
153523
  }
153338
153524
  });
153339
153525
 
@@ -157010,7 +157196,7 @@ var init_sanitizer = __esm({
157010
157196
  });
157011
157197
 
157012
157198
  // src/workspace/applier.ts
157013
- import { existsSync as existsSync13, mkdirSync as mkdirSync2, copyFileSync, writeFileSync as writeFileSync2, unlinkSync, renameSync } from "fs";
157199
+ import { existsSync as existsSync13, mkdirSync as mkdirSync3, copyFileSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, renameSync } from "fs";
157014
157200
  import { dirname } from "path";
157015
157201
  var WorkspaceSyncApplier;
157016
157202
  var init_applier = __esm({
@@ -157042,7 +157228,7 @@ var init_applier = __esm({
157042
157228
  try {
157043
157229
  const dir = dirname(file.filePath);
157044
157230
  if (!existsSync13(dir)) {
157045
- mkdirSync2(dir, { recursive: true });
157231
+ mkdirSync3(dir, { recursive: true });
157046
157232
  }
157047
157233
  } catch (err) {
157048
157234
  result.errors.push(`Cannot create directory for ${file.filePath}: ${err}`);
@@ -157067,7 +157253,7 @@ var init_applier = __esm({
157067
157253
  for (const file of files) {
157068
157254
  try {
157069
157255
  const tempPath = file.filePath + `.tmp-${Date.now()}`;
157070
- writeFileSync2(tempPath, file.content, "utf-8");
157256
+ writeFileSync3(tempPath, file.content, "utf-8");
157071
157257
  renameSync(tempPath, file.filePath);
157072
157258
  result.filesWritten.push(file.filePath);
157073
157259
  } catch (err) {
@@ -157102,7 +157288,7 @@ var init_applier = __esm({
157102
157288
  for (const backup of backups) {
157103
157289
  try {
157104
157290
  if (existsSync13(backup.backupPath)) {
157105
- unlinkSync(backup.backupPath);
157291
+ unlinkSync2(backup.backupPath);
157106
157292
  }
157107
157293
  } catch {
157108
157294
  }
@@ -157113,7 +157299,7 @@ var init_applier = __esm({
157113
157299
  });
157114
157300
 
157115
157301
  // src/workspace/engine.ts
157116
- import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync14, cpSync, mkdirSync as mkdirSync3 } from "fs";
157302
+ import { readFileSync as readFileSync10, readdirSync as readdirSync3, existsSync as existsSync14, cpSync, mkdirSync as mkdirSync4 } from "fs";
157117
157303
  import { join as join19 } from "path";
157118
157304
  import { homedir as homedir21 } from "os";
157119
157305
  var WorkspaceSyncEngine;
@@ -157195,7 +157381,7 @@ var init_engine4 = __esm({
157195
157381
  for (const path37 of pathsToCheck) {
157196
157382
  if (existsSync14(path37)) {
157197
157383
  try {
157198
- const content = readFileSync9(path37, "utf-8");
157384
+ const content = readFileSync10(path37, "utf-8");
157199
157385
  const servers = adapter.parse(content);
157200
157386
  for (const s2 of servers) {
157201
157387
  if (!merged.has(s2.name)) merged.set(s2.name, s2);
@@ -157249,7 +157435,7 @@ var init_engine4 = __esm({
157249
157435
  let configContent;
157250
157436
  if (target === "antigravity" && existsSync14(configPath)) {
157251
157437
  try {
157252
- const existing = JSON.parse(readFileSync9(configPath, "utf-8"));
157438
+ const existing = JSON.parse(readFileSync10(configPath, "utf-8"));
157253
157439
  const generated = JSON.parse(adapter.generate(result.mcpServers.scanned));
157254
157440
  existing.mcpServers = { ...existing.mcpServers ?? {}, ...generated.mcpServers };
157255
157441
  configContent = JSON.stringify(existing, null, 2);
@@ -157332,7 +157518,7 @@ var init_engine4 = __esm({
157332
157518
  if (!existsSync14(skillMd)) continue;
157333
157519
  let description = "";
157334
157520
  try {
157335
- const content = readFileSync9(skillMd, "utf-8");
157521
+ const content = readFileSync10(skillMd, "utf-8");
157336
157522
  const match2 = content.match(/^---[\s\S]*?description:\s*["']?(.+?)["']?\s*$/m);
157337
157523
  if (match2) description = match2[1];
157338
157524
  } catch {
@@ -157383,7 +157569,7 @@ var init_engine4 = __esm({
157383
157569
  continue;
157384
157570
  }
157385
157571
  try {
157386
- mkdirSync3(targetDir, { recursive: true });
157572
+ mkdirSync4(targetDir, { recursive: true });
157387
157573
  cpSync(skill2.sourcePath, dest, { recursive: true });
157388
157574
  copied.push(skill2.name);
157389
157575
  } catch {
@@ -157399,7 +157585,7 @@ var init_engine4 = __esm({
157399
157585
  const files = readdirSync3(wfDir).filter((f3) => f3.endsWith(".md"));
157400
157586
  for (const file of files) {
157401
157587
  try {
157402
- const content = readFileSync9(join19(wfDir, file), "utf-8");
157588
+ const content = readFileSync10(join19(wfDir, file), "utf-8");
157403
157589
  workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
157404
157590
  } catch {
157405
157591
  }
@@ -158792,7 +158978,7 @@ var init_request_context = __esm({
158792
158978
  function getMcpServerInfo() {
158793
158979
  return {
158794
158980
  name: "memorix",
158795
- version: true ? "1.8.9" : "1.0.1"
158981
+ version: true ? "1.9.0" : "1.0.1"
158796
158982
  };
158797
158983
  }
158798
158984
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -158854,62 +159040,13 @@ var init_mcp_compat = __esm({
158854
159040
  }
158855
159041
  });
158856
159042
 
158857
- // src/runtime/maintenance-targets.ts
158858
- var maintenance_targets_exports = {};
158859
- __export(maintenance_targets_exports, {
158860
- MaintenanceTargetStore: () => MaintenanceTargetStore
158861
- });
158862
- function rowToTarget(row) {
158863
- return {
158864
- projectId: row.project_id,
158865
- projectRoot: row.project_root,
158866
- dataDir: row.data_dir,
158867
- updatedAt: Number(row.updated_at)
158868
- };
158869
- }
158870
- var MaintenanceTargetStore;
158871
- var init_maintenance_targets = __esm({
158872
- "src/runtime/maintenance-targets.ts"() {
158873
- "use strict";
158874
- init_esm_shims();
158875
- init_sqlite_db();
158876
- MaintenanceTargetStore = class {
158877
- db;
158878
- constructor(dataDir) {
158879
- this.db = getDatabase(dataDir);
158880
- }
158881
- register(input) {
158882
- if (!input.projectId.trim()) throw new Error("Maintenance target requires a project ID");
158883
- if (!input.projectRoot.trim()) throw new Error("Maintenance target requires a project root");
158884
- if (!input.dataDir.trim()) throw new Error("Maintenance target requires a data directory");
158885
- const now3 = input.now ?? Date.now();
158886
- this.db.prepare(`
158887
- INSERT INTO maintenance_targets (project_id, project_root, data_dir, updated_at)
158888
- VALUES (?, ?, ?, ?)
158889
- ON CONFLICT(project_id) DO UPDATE SET
158890
- project_root = excluded.project_root,
158891
- data_dir = excluded.data_dir,
158892
- updated_at = excluded.updated_at
158893
- `).run(input.projectId, input.projectRoot, input.dataDir, now3);
158894
- return this.get(input.projectId);
158895
- }
158896
- get(projectId) {
158897
- const row = this.db.prepare(`
158898
- SELECT * FROM maintenance_targets WHERE project_id = ?
158899
- `).get(projectId);
158900
- return row ? rowToTarget(row) : void 0;
158901
- }
158902
- };
158903
- }
158904
- });
158905
-
158906
159043
  // src/config/behavior.ts
158907
159044
  var behavior_exports = {};
158908
159045
  __export(behavior_exports, {
158909
159046
  getBehaviorConfig: () => getBehaviorConfig,
158910
159047
  resetBehaviorConfigCache: () => resetBehaviorConfigCache
158911
159048
  });
158912
- import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
159049
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
158913
159050
  import { homedir as homedir22 } from "os";
158914
159051
  function isSessionInject(value) {
158915
159052
  return value === "full" || value === "minimal" || value === "silent";
@@ -158921,7 +159058,7 @@ function legacyBehavior(homeDir) {
158921
159058
  const configPath = getLegacyConfigJsonPath(homeDir);
158922
159059
  try {
158923
159060
  if (!existsSync15(configPath)) return {};
158924
- const parsed = JSON.parse(readFileSync10(configPath, "utf-8"));
159061
+ const parsed = JSON.parse(readFileSync11(configPath, "utf-8"));
158925
159062
  return parsed.behavior && typeof parsed.behavior === "object" && !Array.isArray(parsed.behavior) ? parsed.behavior : {};
158926
159063
  } catch {
158927
159064
  return {};
@@ -160274,12 +160411,12 @@ __export(current_facts_exports, {
160274
160411
  formatGitFact: () => formatGitFact
160275
160412
  });
160276
160413
  import { execFileSync } from "child_process";
160277
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
160414
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
160278
160415
  import path28 from "path";
160279
160416
  function readTextIfExists(filePath) {
160280
160417
  try {
160281
160418
  if (!existsSync16(filePath)) return null;
160282
- return readFileSync11(filePath, "utf8");
160419
+ return readFileSync12(filePath, "utf8");
160283
160420
  } catch {
160284
160421
  return null;
160285
160422
  }
@@ -160569,7 +160706,7 @@ __export(external_provider_exports, {
160569
160706
  runExternalCodeGraphLifecycle: () => runExternalCodeGraphLifecycle
160570
160707
  });
160571
160708
  import { spawn as spawn5 } from "child_process";
160572
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
160709
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
160573
160710
  import { delimiter, dirname as dirname2, extname, isAbsolute as isAbsolute3, join as join20, relative as relative4, resolve as resolve3, sep } from "path";
160574
160711
  function boundedTimeout(value) {
160575
160712
  if (!Number.isFinite(value)) return DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS;
@@ -160616,7 +160753,7 @@ function resolveLaunch(command) {
160616
160753
  return { executable: node, prefixArgs: ["--liftoff-only", entry] };
160617
160754
  }
160618
160755
  try {
160619
- const shim = readFileSync12(candidate, "utf8");
160756
+ const shim = readFileSync13(candidate, "utf8");
160620
160757
  const match2 = shim.match(/["']([^"']*%(?:~)?dp0%?[^"']*\.m?js)["']/i);
160621
160758
  if (match2?.[1]) {
160622
160759
  const base = dirname2(candidate).endsWith(sep) ? dirname2(candidate) : dirname2(candidate) + sep;
@@ -161876,7 +162013,7 @@ __export(auto_context_exports, {
161876
162013
  formatAutoProjectContextSummary: () => formatAutoProjectContextSummary,
161877
162014
  formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
161878
162015
  });
161879
- import { existsSync as existsSync18, statSync as statSync4 } from "fs";
162016
+ import { existsSync as existsSync18, statSync as statSync5 } from "fs";
161880
162017
  import path29 from "path";
161881
162018
  function activeProjectObservations(observations2, projectId) {
161882
162019
  return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
@@ -162223,7 +162360,7 @@ function existingLensCandidates(rootPath, lens) {
162223
162360
  const absolute = path29.join(rootPath, candidate);
162224
162361
  try {
162225
162362
  if (!existsSync18(absolute)) continue;
162226
- const stat5 = statSync4(absolute);
162363
+ const stat5 = statSync5(absolute);
162227
162364
  if (stat5.isFile()) out.push(candidate);
162228
162365
  if (stat5.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
162229
162366
  } catch {
@@ -162756,7 +162893,7 @@ var engine_exports2 = {};
162756
162893
  __export(engine_exports2, {
162757
162894
  SkillsEngine: () => SkillsEngine
162758
162895
  });
162759
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
162896
+ import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
162760
162897
  import { join as join21 } from "path";
162761
162898
  import { homedir as homedir23 } from "os";
162762
162899
  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;
@@ -162847,7 +162984,7 @@ var init_engine5 = __esm({
162847
162984
  const skillMd = join21(skillsRoot, name, "SKILL.md");
162848
162985
  if (!existsSync19(skillMd)) continue;
162849
162986
  try {
162850
- const content = readFileSync13(skillMd, "utf-8");
162987
+ const content = readFileSync14(skillMd, "utf-8");
162851
162988
  const description = this.parseDescription(content);
162852
162989
  skills.push({
162853
162990
  name,
@@ -162897,8 +163034,8 @@ var init_engine5 = __esm({
162897
163034
  if (!dirs || dirs.length === 0) return null;
162898
163035
  const targetDir = join21(this.projectRoot, dirs[0], skill2.name);
162899
163036
  try {
162900
- mkdirSync4(targetDir, { recursive: true });
162901
- writeFileSync3(join21(targetDir, "SKILL.md"), skill2.content, "utf-8");
163037
+ mkdirSync5(targetDir, { recursive: true });
163038
+ writeFileSync4(join21(targetDir, "SKILL.md"), skill2.content, "utf-8");
162902
163039
  return join21(dirs[0], skill2.name, "SKILL.md");
162903
163040
  } catch {
162904
163041
  return null;
@@ -166668,7 +166805,7 @@ var init_task_graph = __esm({
166668
166805
 
166669
166806
  // src/orchestrate/context-collector.ts
166670
166807
  import { execFileSync as execFileSync2 } from "child_process";
166671
- import { readFileSync as readFileSync14, existsSync as existsSync20 } from "fs";
166808
+ import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
166672
166809
  import { join as join22 } from "path";
166673
166810
  function normalizeEntryLimit(value, fallback) {
166674
166811
  if (!Number.isSafeInteger(value) || value < 1) return fallback;
@@ -166746,7 +166883,7 @@ function collectDependencies(projectDir2) {
166746
166883
  try {
166747
166884
  const pkgPath = join22(projectDir2, "package.json");
166748
166885
  if (!existsSync20(pkgPath)) return "";
166749
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
166886
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
166750
166887
  const parts = [];
166751
166888
  if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
166752
166889
  parts.push(`dependencies: ${Object.keys(pkg.dependencies).join(", ")}`);
@@ -169246,18 +169383,18 @@ __export(hooks_path_exports, {
169246
169383
  resolveGitDir: () => resolveGitDir,
169247
169384
  resolveHooksDir: () => resolveHooksDir
169248
169385
  });
169249
- import { existsSync as existsSync21, readFileSync as readFileSync15, statSync as statSync5, mkdirSync as mkdirSync5 } from "fs";
169386
+ import { existsSync as existsSync21, readFileSync as readFileSync16, statSync as statSync6, mkdirSync as mkdirSync6 } from "fs";
169250
169387
  import path34 from "path";
169251
169388
  function resolveGitDir(projectRoot) {
169252
169389
  const dotGit = path34.join(projectRoot, ".git");
169253
169390
  if (!existsSync21(dotGit)) return null;
169254
- const stat5 = statSync5(dotGit);
169391
+ const stat5 = statSync6(dotGit);
169255
169392
  if (stat5.isDirectory()) {
169256
169393
  return dotGit;
169257
169394
  }
169258
169395
  if (stat5.isFile()) {
169259
169396
  try {
169260
- const content = readFileSync15(dotGit, "utf-8").trim();
169397
+ const content = readFileSync16(dotGit, "utf-8").trim();
169261
169398
  const match2 = content.match(/^gitdir:\s*(.+)$/);
169262
169399
  if (match2) {
169263
169400
  const gitdir = match2[1].trim();
@@ -169283,7 +169420,7 @@ function resolveHooksDir(projectRoot) {
169283
169420
  function ensureHooksDir(projectRoot) {
169284
169421
  const resolved = resolveHooksDir(projectRoot);
169285
169422
  if (!resolved) return null;
169286
- mkdirSync5(resolved.hooksDir, { recursive: true });
169423
+ mkdirSync6(resolved.hooksDir, { recursive: true });
169287
169424
  return resolved;
169288
169425
  }
169289
169426
  var init_hooks_path = __esm({
@@ -173857,10 +173994,10 @@ Preview: ${analysis.description.slice(0, 300)}${analysis.description.length > 30
173857
173994
  const { ensureHooksDir: ensureHooksDir2 } = await Promise.resolve().then(() => (init_hooks_path(), hooks_path_exports));
173858
173995
  const resolved = ensureHooksDir2(project.rootPath);
173859
173996
  if (resolved) {
173860
- const { existsSync: existsSync22, readFileSync: readFileSync16, writeFileSync: writeFileSync4, chmodSync } = await import("fs");
173997
+ const { existsSync: existsSync22, readFileSync: readFileSync17, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
173861
173998
  const { hookPath } = resolved;
173862
173999
  const HOOK_MARKER = "# [memorix-git-hook]";
173863
- const needsInstall = !existsSync22(hookPath) || !readFileSync16(hookPath, "utf-8").includes(HOOK_MARKER);
174000
+ const needsInstall = !existsSync22(hookPath) || !readFileSync17(hookPath, "utf-8").includes(HOOK_MARKER);
173864
174001
  if (needsInstall) {
173865
174002
  const hookScript = `#!/bin/sh
173866
174003
  ${HOOK_MARKER}
@@ -173870,8 +174007,8 @@ if command -v memorix >/dev/null 2>&1; then
173870
174007
  fi
173871
174008
  `;
173872
174009
  if (existsSync22(hookPath)) {
173873
- const existing = readFileSync16(hookPath, "utf-8");
173874
- writeFileSync4(hookPath, existing.trimEnd() + `
174010
+ const existing = readFileSync17(hookPath, "utf-8");
174011
+ writeFileSync5(hookPath, existing.trimEnd() + `
173875
174012
 
173876
174013
  ${HOOK_MARKER}
173877
174014
  if command -v memorix >/dev/null 2>&1; then
@@ -173879,7 +174016,7 @@ if command -v memorix >/dev/null 2>&1; then
173879
174016
  fi
173880
174017
  `, "utf-8");
173881
174018
  } else {
173882
- writeFileSync4(hookPath, hookScript, "utf-8");
174019
+ writeFileSync5(hookPath, hookScript, "utf-8");
173883
174020
  }
173884
174021
  try {
173885
174022
  chmodSync(hookPath, 493);
@@ -174541,7 +174678,7 @@ var init_serve_shared = __esm({
174541
174678
 
174542
174679
  // src/cli/version.ts
174543
174680
  function getCliVersion() {
174544
- return true ? "1.8.9" : pkg.version;
174681
+ return true ? "1.9.0" : pkg.version;
174545
174682
  }
174546
174683
  var init_version3 = __esm({
174547
174684
  "src/cli/version.ts"() {