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