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.
- package/dist/cli/index.js +161 -6
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +49 -4
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +134 -3
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/session-end.js +134 -3
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +134 -3
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +134 -3
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +134 -3
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/server/api/index.js +138 -3
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +138 -3
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +134 -3
- package/dist/services/memory-service.js.map +2 -2
- package/dist/ui/app.js +126 -0
- package/dist/ui/index.html +39 -0
- package/dist/ui/style.css +7 -0
- package/package.json +1 -1
- package/src/cli/index.ts +23 -1
- package/src/core/embedder.ts +3 -2
- package/src/core/sqlite-event-store.ts +32 -0
- package/src/core/types.ts +2 -2
- package/src/core/vector-store.ts +20 -0
- package/src/server/api/events.ts +6 -0
- package/src/services/memory-service.ts +112 -2
- package/src/ui/app.js +126 -0
- package/src/ui/index.html +39 -0
- package/src/ui/style.css +7 -0
package/dist/server/index.js
CHANGED
|
@@ -1624,6 +1624,33 @@ var SQLiteEventStore = class {
|
|
|
1624
1624
|
ids
|
|
1625
1625
|
);
|
|
1626
1626
|
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Clear embedding outbox (used for embedding model migration)
|
|
1629
|
+
*/
|
|
1630
|
+
async clearEmbeddingOutbox() {
|
|
1631
|
+
await this.initialize();
|
|
1632
|
+
sqliteRun(this.db, `DELETE FROM embedding_outbox`);
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Count total events
|
|
1636
|
+
*/
|
|
1637
|
+
async countEvents() {
|
|
1638
|
+
await this.initialize();
|
|
1639
|
+
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
|
|
1640
|
+
return row?.count || 0;
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
1644
|
+
*/
|
|
1645
|
+
async getEventsPage(limit = 1e3, offset = 0) {
|
|
1646
|
+
await this.initialize();
|
|
1647
|
+
const rows = sqliteAll(
|
|
1648
|
+
this.db,
|
|
1649
|
+
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
1650
|
+
[limit, offset]
|
|
1651
|
+
);
|
|
1652
|
+
return rows.map(this.rowToEvent);
|
|
1653
|
+
}
|
|
1627
1654
|
/**
|
|
1628
1655
|
* Mark outbox items as failed
|
|
1629
1656
|
*/
|
|
@@ -2589,6 +2616,23 @@ var VectorStore = class {
|
|
|
2589
2616
|
const result = await this.table.countRows();
|
|
2590
2617
|
return result;
|
|
2591
2618
|
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Clear all vectors (used for embedding model migration)
|
|
2621
|
+
*/
|
|
2622
|
+
async clearAll() {
|
|
2623
|
+
await this.initialize();
|
|
2624
|
+
if (!this.db)
|
|
2625
|
+
return;
|
|
2626
|
+
try {
|
|
2627
|
+
if (typeof this.db.dropTable === "function") {
|
|
2628
|
+
await this.db.dropTable(this.tableName);
|
|
2629
|
+
} else if (typeof this.db.drop_table === "function") {
|
|
2630
|
+
await this.db.drop_table(this.tableName);
|
|
2631
|
+
}
|
|
2632
|
+
} catch {
|
|
2633
|
+
}
|
|
2634
|
+
this.table = null;
|
|
2635
|
+
}
|
|
2592
2636
|
/**
|
|
2593
2637
|
* Check if vector exists for event
|
|
2594
2638
|
*/
|
|
@@ -2606,7 +2650,7 @@ var Embedder = class {
|
|
|
2606
2650
|
pipeline = null;
|
|
2607
2651
|
modelName;
|
|
2608
2652
|
initialized = false;
|
|
2609
|
-
constructor(modelName = "
|
|
2653
|
+
constructor(modelName = "jinaai/jina-embeddings-v5-text-nano") {
|
|
2610
2654
|
this.modelName = modelName;
|
|
2611
2655
|
}
|
|
2612
2656
|
/**
|
|
@@ -2686,8 +2730,9 @@ var Embedder = class {
|
|
|
2686
2730
|
};
|
|
2687
2731
|
var defaultEmbedder = null;
|
|
2688
2732
|
function getDefaultEmbedder() {
|
|
2733
|
+
const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
2689
2734
|
if (!defaultEmbedder) {
|
|
2690
|
-
defaultEmbedder = new Embedder();
|
|
2735
|
+
defaultEmbedder = new Embedder(envModel || void 0);
|
|
2691
2736
|
}
|
|
2692
2737
|
return defaultEmbedder;
|
|
2693
2738
|
}
|
|
@@ -5918,8 +5963,10 @@ var MemoryService = class {
|
|
|
5918
5963
|
readOnly;
|
|
5919
5964
|
lightweightMode;
|
|
5920
5965
|
mdMirror;
|
|
5966
|
+
storagePath;
|
|
5921
5967
|
constructor(config) {
|
|
5922
5968
|
const storagePath = this.expandPath(config.storagePath);
|
|
5969
|
+
this.storagePath = storagePath;
|
|
5923
5970
|
this.readOnly = config.readOnly ?? false;
|
|
5924
5971
|
this.lightweightMode = config.lightweightMode ?? false;
|
|
5925
5972
|
this.mdMirror = new MarkdownMirror2(process.cwd());
|
|
@@ -5955,7 +6002,8 @@ var MemoryService = class {
|
|
|
5955
6002
|
);
|
|
5956
6003
|
}
|
|
5957
6004
|
this.vectorStore = new VectorStore(path3.join(storagePath, "vectors"));
|
|
5958
|
-
|
|
6005
|
+
const embeddingModel = config.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
6006
|
+
this.embedder = embeddingModel ? new Embedder(embeddingModel) : getDefaultEmbedder();
|
|
5959
6007
|
this.matcher = getDefaultMatcher();
|
|
5960
6008
|
this.retriever = createRetriever(
|
|
5961
6009
|
this.sqliteStore,
|
|
@@ -6899,6 +6947,89 @@ var MemoryService = class {
|
|
|
6899
6947
|
recordMemoryAccess(eventId, sessionId, confidence = 1) {
|
|
6900
6948
|
this.graduation.recordAccess(eventId, sessionId, confidence);
|
|
6901
6949
|
}
|
|
6950
|
+
getEmbeddingModelName() {
|
|
6951
|
+
return this.embedder.getModelName();
|
|
6952
|
+
}
|
|
6953
|
+
/**
|
|
6954
|
+
* Ensure embedding model metadata is in sync and optionally migrate vectors.
|
|
6955
|
+
* Migration strategy: clear vector index + clear embedding outbox + re-enqueue all events.
|
|
6956
|
+
*/
|
|
6957
|
+
async ensureEmbeddingModelForImport(options) {
|
|
6958
|
+
await this.initialize();
|
|
6959
|
+
const currentModel = this.getEmbeddingModelName();
|
|
6960
|
+
const metaPath = path3.join(this.storagePath, "embedding-meta.json");
|
|
6961
|
+
let previousModel = null;
|
|
6962
|
+
try {
|
|
6963
|
+
if (fs4.existsSync(metaPath)) {
|
|
6964
|
+
const parsed = JSON.parse(fs4.readFileSync(metaPath, "utf-8"));
|
|
6965
|
+
previousModel = parsed?.model || null;
|
|
6966
|
+
}
|
|
6967
|
+
} catch {
|
|
6968
|
+
previousModel = null;
|
|
6969
|
+
}
|
|
6970
|
+
const stats = await this.getStats();
|
|
6971
|
+
const hasExistingVectors = (stats.vectorCount || 0) > 0;
|
|
6972
|
+
if (!previousModel && !hasExistingVectors) {
|
|
6973
|
+
fs4.writeFileSync(metaPath, JSON.stringify({ model: currentModel, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
6974
|
+
return { changed: false, previousModel: null, currentModel, enqueued: 0, reason: "initialized-meta" };
|
|
6975
|
+
}
|
|
6976
|
+
const modelChanged = previousModel !== currentModel;
|
|
6977
|
+
const legacyUnknownButVectorsExist = !previousModel && hasExistingVectors;
|
|
6978
|
+
if (!modelChanged && !legacyUnknownButVectorsExist) {
|
|
6979
|
+
return { changed: false, previousModel, currentModel, enqueued: 0 };
|
|
6980
|
+
}
|
|
6981
|
+
if (options?.autoMigrate === false) {
|
|
6982
|
+
return {
|
|
6983
|
+
changed: true,
|
|
6984
|
+
previousModel,
|
|
6985
|
+
currentModel,
|
|
6986
|
+
enqueued: 0,
|
|
6987
|
+
reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
|
|
6988
|
+
};
|
|
6989
|
+
}
|
|
6990
|
+
const wasRunning = this.vectorWorker?.isRunning() || false;
|
|
6991
|
+
if (wasRunning)
|
|
6992
|
+
this.vectorWorker?.stop();
|
|
6993
|
+
await this.vectorStore.clearAll();
|
|
6994
|
+
await this.sqliteStore.clearEmbeddingOutbox();
|
|
6995
|
+
const pageSize = 1e3;
|
|
6996
|
+
let offset = 0;
|
|
6997
|
+
let enqueued = 0;
|
|
6998
|
+
while (true) {
|
|
6999
|
+
const page = await this.sqliteStore.getEventsPage(pageSize, offset);
|
|
7000
|
+
if (page.length === 0)
|
|
7001
|
+
break;
|
|
7002
|
+
for (const event of page) {
|
|
7003
|
+
await this.sqliteStore.enqueueForEmbedding(event.id, event.content);
|
|
7004
|
+
enqueued += 1;
|
|
7005
|
+
}
|
|
7006
|
+
offset += page.length;
|
|
7007
|
+
if (page.length < pageSize)
|
|
7008
|
+
break;
|
|
7009
|
+
}
|
|
7010
|
+
fs4.writeFileSync(
|
|
7011
|
+
metaPath,
|
|
7012
|
+
JSON.stringify(
|
|
7013
|
+
{
|
|
7014
|
+
model: currentModel,
|
|
7015
|
+
previousModel,
|
|
7016
|
+
migratedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7017
|
+
enqueued
|
|
7018
|
+
},
|
|
7019
|
+
null,
|
|
7020
|
+
2
|
|
7021
|
+
)
|
|
7022
|
+
);
|
|
7023
|
+
if (wasRunning)
|
|
7024
|
+
this.vectorWorker?.start();
|
|
7025
|
+
return {
|
|
7026
|
+
changed: true,
|
|
7027
|
+
previousModel,
|
|
7028
|
+
currentModel,
|
|
7029
|
+
enqueued,
|
|
7030
|
+
reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
|
|
7031
|
+
};
|
|
7032
|
+
}
|
|
6902
7033
|
/**
|
|
6903
7034
|
* Backward-compatible alias used by some hooks
|
|
6904
7035
|
*/
|
|
@@ -7083,6 +7214,7 @@ eventsRouter.get("/", async (c) => {
|
|
|
7083
7214
|
const eventType = c.req.query("type");
|
|
7084
7215
|
const level = c.req.query("level");
|
|
7085
7216
|
const sort = c.req.query("sort") || "recent";
|
|
7217
|
+
const q = (c.req.query("q") || "").trim().toLowerCase();
|
|
7086
7218
|
const limit = parseInt(c.req.query("limit") || "100", 10);
|
|
7087
7219
|
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
7088
7220
|
const memoryService = getServiceFromQuery(c);
|
|
@@ -7100,6 +7232,9 @@ eventsRouter.get("/", async (c) => {
|
|
|
7100
7232
|
if (eventType) {
|
|
7101
7233
|
events = events.filter((e) => e.eventType === eventType);
|
|
7102
7234
|
}
|
|
7235
|
+
if (q) {
|
|
7236
|
+
events = events.filter((e) => (e.content || "").toLowerCase().includes(q));
|
|
7237
|
+
}
|
|
7103
7238
|
if (sort === "accessed") {
|
|
7104
7239
|
events.sort((a, b) => {
|
|
7105
7240
|
const aTime = a.last_accessed_at || "";
|