claude-memory-layer 1.0.19 → 1.0.20

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.
@@ -1603,6 +1603,33 @@ var SQLiteEventStore = class {
1603
1603
  ids
1604
1604
  );
1605
1605
  }
1606
+ /**
1607
+ * Clear embedding outbox (used for embedding model migration)
1608
+ */
1609
+ async clearEmbeddingOutbox() {
1610
+ await this.initialize();
1611
+ sqliteRun(this.db, `DELETE FROM embedding_outbox`);
1612
+ }
1613
+ /**
1614
+ * Count total events
1615
+ */
1616
+ async countEvents() {
1617
+ await this.initialize();
1618
+ const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
1619
+ return row?.count || 0;
1620
+ }
1621
+ /**
1622
+ * Get events page in timestamp ascending order (stable migration/reindex scans)
1623
+ */
1624
+ async getEventsPage(limit = 1e3, offset = 0) {
1625
+ await this.initialize();
1626
+ const rows = sqliteAll(
1627
+ this.db,
1628
+ `SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
1629
+ [limit, offset]
1630
+ );
1631
+ return rows.map(this.rowToEvent);
1632
+ }
1606
1633
  /**
1607
1634
  * Mark outbox items as failed
1608
1635
  */
@@ -2568,6 +2595,23 @@ var VectorStore = class {
2568
2595
  const result = await this.table.countRows();
2569
2596
  return result;
2570
2597
  }
2598
+ /**
2599
+ * Clear all vectors (used for embedding model migration)
2600
+ */
2601
+ async clearAll() {
2602
+ await this.initialize();
2603
+ if (!this.db)
2604
+ return;
2605
+ try {
2606
+ if (typeof this.db.dropTable === "function") {
2607
+ await this.db.dropTable(this.tableName);
2608
+ } else if (typeof this.db.drop_table === "function") {
2609
+ await this.db.drop_table(this.tableName);
2610
+ }
2611
+ } catch {
2612
+ }
2613
+ this.table = null;
2614
+ }
2571
2615
  /**
2572
2616
  * Check if vector exists for event
2573
2617
  */
@@ -2585,7 +2629,7 @@ var Embedder = class {
2585
2629
  pipeline = null;
2586
2630
  modelName;
2587
2631
  initialized = false;
2588
- constructor(modelName = "Xenova/all-MiniLM-L6-v2") {
2632
+ constructor(modelName = "jinaai/jina-embeddings-v5-text-nano") {
2589
2633
  this.modelName = modelName;
2590
2634
  }
2591
2635
  /**
@@ -2665,8 +2709,9 @@ var Embedder = class {
2665
2709
  };
2666
2710
  var defaultEmbedder = null;
2667
2711
  function getDefaultEmbedder() {
2712
+ const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
2668
2713
  if (!defaultEmbedder) {
2669
- defaultEmbedder = new Embedder();
2714
+ defaultEmbedder = new Embedder(envModel || void 0);
2670
2715
  }
2671
2716
  return defaultEmbedder;
2672
2717
  }
@@ -5901,8 +5946,10 @@ var MemoryService = class {
5901
5946
  readOnly;
5902
5947
  lightweightMode;
5903
5948
  mdMirror;
5949
+ storagePath;
5904
5950
  constructor(config) {
5905
5951
  const storagePath = this.expandPath(config.storagePath);
5952
+ this.storagePath = storagePath;
5906
5953
  this.readOnly = config.readOnly ?? false;
5907
5954
  this.lightweightMode = config.lightweightMode ?? false;
5908
5955
  this.mdMirror = new MarkdownMirror2(process.cwd());
@@ -5938,7 +5985,8 @@ var MemoryService = class {
5938
5985
  );
5939
5986
  }
5940
5987
  this.vectorStore = new VectorStore(path3.join(storagePath, "vectors"));
5941
- this.embedder = config.embeddingModel ? new Embedder(config.embeddingModel) : getDefaultEmbedder();
5988
+ const embeddingModel = config.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
5989
+ this.embedder = embeddingModel ? new Embedder(embeddingModel) : getDefaultEmbedder();
5942
5990
  this.matcher = getDefaultMatcher();
5943
5991
  this.retriever = createRetriever(
5944
5992
  this.sqliteStore,
@@ -6882,6 +6930,89 @@ var MemoryService = class {
6882
6930
  recordMemoryAccess(eventId, sessionId, confidence = 1) {
6883
6931
  this.graduation.recordAccess(eventId, sessionId, confidence);
6884
6932
  }
6933
+ getEmbeddingModelName() {
6934
+ return this.embedder.getModelName();
6935
+ }
6936
+ /**
6937
+ * Ensure embedding model metadata is in sync and optionally migrate vectors.
6938
+ * Migration strategy: clear vector index + clear embedding outbox + re-enqueue all events.
6939
+ */
6940
+ async ensureEmbeddingModelForImport(options) {
6941
+ await this.initialize();
6942
+ const currentModel = this.getEmbeddingModelName();
6943
+ const metaPath = path3.join(this.storagePath, "embedding-meta.json");
6944
+ let previousModel = null;
6945
+ try {
6946
+ if (fs4.existsSync(metaPath)) {
6947
+ const parsed = JSON.parse(fs4.readFileSync(metaPath, "utf-8"));
6948
+ previousModel = parsed?.model || null;
6949
+ }
6950
+ } catch {
6951
+ previousModel = null;
6952
+ }
6953
+ const stats = await this.getStats();
6954
+ const hasExistingVectors = (stats.vectorCount || 0) > 0;
6955
+ if (!previousModel && !hasExistingVectors) {
6956
+ fs4.writeFileSync(metaPath, JSON.stringify({ model: currentModel, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
6957
+ return { changed: false, previousModel: null, currentModel, enqueued: 0, reason: "initialized-meta" };
6958
+ }
6959
+ const modelChanged = previousModel !== currentModel;
6960
+ const legacyUnknownButVectorsExist = !previousModel && hasExistingVectors;
6961
+ if (!modelChanged && !legacyUnknownButVectorsExist) {
6962
+ return { changed: false, previousModel, currentModel, enqueued: 0 };
6963
+ }
6964
+ if (options?.autoMigrate === false) {
6965
+ return {
6966
+ changed: true,
6967
+ previousModel,
6968
+ currentModel,
6969
+ enqueued: 0,
6970
+ reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
6971
+ };
6972
+ }
6973
+ const wasRunning = this.vectorWorker?.isRunning() || false;
6974
+ if (wasRunning)
6975
+ this.vectorWorker?.stop();
6976
+ await this.vectorStore.clearAll();
6977
+ await this.sqliteStore.clearEmbeddingOutbox();
6978
+ const pageSize = 1e3;
6979
+ let offset = 0;
6980
+ let enqueued = 0;
6981
+ while (true) {
6982
+ const page = await this.sqliteStore.getEventsPage(pageSize, offset);
6983
+ if (page.length === 0)
6984
+ break;
6985
+ for (const event of page) {
6986
+ await this.sqliteStore.enqueueForEmbedding(event.id, event.content);
6987
+ enqueued += 1;
6988
+ }
6989
+ offset += page.length;
6990
+ if (page.length < pageSize)
6991
+ break;
6992
+ }
6993
+ fs4.writeFileSync(
6994
+ metaPath,
6995
+ JSON.stringify(
6996
+ {
6997
+ model: currentModel,
6998
+ previousModel,
6999
+ migratedAt: (/* @__PURE__ */ new Date()).toISOString(),
7000
+ enqueued
7001
+ },
7002
+ null,
7003
+ 2
7004
+ )
7005
+ );
7006
+ if (wasRunning)
7007
+ this.vectorWorker?.start();
7008
+ return {
7009
+ changed: true,
7010
+ previousModel,
7011
+ currentModel,
7012
+ enqueued,
7013
+ reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
7014
+ };
7015
+ }
6885
7016
  /**
6886
7017
  * Backward-compatible alias used by some hooks
6887
7018
  */