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/api/index.js
CHANGED
|
@@ -1615,6 +1615,33 @@ var SQLiteEventStore = class {
|
|
|
1615
1615
|
ids
|
|
1616
1616
|
);
|
|
1617
1617
|
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Clear embedding outbox (used for embedding model migration)
|
|
1620
|
+
*/
|
|
1621
|
+
async clearEmbeddingOutbox() {
|
|
1622
|
+
await this.initialize();
|
|
1623
|
+
sqliteRun(this.db, `DELETE FROM embedding_outbox`);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Count total events
|
|
1627
|
+
*/
|
|
1628
|
+
async countEvents() {
|
|
1629
|
+
await this.initialize();
|
|
1630
|
+
const row = sqliteGet(this.db, `SELECT COUNT(*) as count FROM events`);
|
|
1631
|
+
return row?.count || 0;
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Get events page in timestamp ascending order (stable migration/reindex scans)
|
|
1635
|
+
*/
|
|
1636
|
+
async getEventsPage(limit = 1e3, offset = 0) {
|
|
1637
|
+
await this.initialize();
|
|
1638
|
+
const rows = sqliteAll(
|
|
1639
|
+
this.db,
|
|
1640
|
+
`SELECT * FROM events ORDER BY timestamp ASC LIMIT ? OFFSET ?`,
|
|
1641
|
+
[limit, offset]
|
|
1642
|
+
);
|
|
1643
|
+
return rows.map(this.rowToEvent);
|
|
1644
|
+
}
|
|
1618
1645
|
/**
|
|
1619
1646
|
* Mark outbox items as failed
|
|
1620
1647
|
*/
|
|
@@ -2580,6 +2607,23 @@ var VectorStore = class {
|
|
|
2580
2607
|
const result = await this.table.countRows();
|
|
2581
2608
|
return result;
|
|
2582
2609
|
}
|
|
2610
|
+
/**
|
|
2611
|
+
* Clear all vectors (used for embedding model migration)
|
|
2612
|
+
*/
|
|
2613
|
+
async clearAll() {
|
|
2614
|
+
await this.initialize();
|
|
2615
|
+
if (!this.db)
|
|
2616
|
+
return;
|
|
2617
|
+
try {
|
|
2618
|
+
if (typeof this.db.dropTable === "function") {
|
|
2619
|
+
await this.db.dropTable(this.tableName);
|
|
2620
|
+
} else if (typeof this.db.drop_table === "function") {
|
|
2621
|
+
await this.db.drop_table(this.tableName);
|
|
2622
|
+
}
|
|
2623
|
+
} catch {
|
|
2624
|
+
}
|
|
2625
|
+
this.table = null;
|
|
2626
|
+
}
|
|
2583
2627
|
/**
|
|
2584
2628
|
* Check if vector exists for event
|
|
2585
2629
|
*/
|
|
@@ -2597,7 +2641,7 @@ var Embedder = class {
|
|
|
2597
2641
|
pipeline = null;
|
|
2598
2642
|
modelName;
|
|
2599
2643
|
initialized = false;
|
|
2600
|
-
constructor(modelName = "
|
|
2644
|
+
constructor(modelName = "jinaai/jina-embeddings-v5-text-nano") {
|
|
2601
2645
|
this.modelName = modelName;
|
|
2602
2646
|
}
|
|
2603
2647
|
/**
|
|
@@ -2677,8 +2721,9 @@ var Embedder = class {
|
|
|
2677
2721
|
};
|
|
2678
2722
|
var defaultEmbedder = null;
|
|
2679
2723
|
function getDefaultEmbedder() {
|
|
2724
|
+
const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
2680
2725
|
if (!defaultEmbedder) {
|
|
2681
|
-
defaultEmbedder = new Embedder();
|
|
2726
|
+
defaultEmbedder = new Embedder(envModel || void 0);
|
|
2682
2727
|
}
|
|
2683
2728
|
return defaultEmbedder;
|
|
2684
2729
|
}
|
|
@@ -5909,8 +5954,10 @@ var MemoryService = class {
|
|
|
5909
5954
|
readOnly;
|
|
5910
5955
|
lightweightMode;
|
|
5911
5956
|
mdMirror;
|
|
5957
|
+
storagePath;
|
|
5912
5958
|
constructor(config) {
|
|
5913
5959
|
const storagePath = this.expandPath(config.storagePath);
|
|
5960
|
+
this.storagePath = storagePath;
|
|
5914
5961
|
this.readOnly = config.readOnly ?? false;
|
|
5915
5962
|
this.lightweightMode = config.lightweightMode ?? false;
|
|
5916
5963
|
this.mdMirror = new MarkdownMirror2(process.cwd());
|
|
@@ -5946,7 +5993,8 @@ var MemoryService = class {
|
|
|
5946
5993
|
);
|
|
5947
5994
|
}
|
|
5948
5995
|
this.vectorStore = new VectorStore(path3.join(storagePath, "vectors"));
|
|
5949
|
-
|
|
5996
|
+
const embeddingModel = config.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
5997
|
+
this.embedder = embeddingModel ? new Embedder(embeddingModel) : getDefaultEmbedder();
|
|
5950
5998
|
this.matcher = getDefaultMatcher();
|
|
5951
5999
|
this.retriever = createRetriever(
|
|
5952
6000
|
this.sqliteStore,
|
|
@@ -6890,6 +6938,89 @@ var MemoryService = class {
|
|
|
6890
6938
|
recordMemoryAccess(eventId, sessionId, confidence = 1) {
|
|
6891
6939
|
this.graduation.recordAccess(eventId, sessionId, confidence);
|
|
6892
6940
|
}
|
|
6941
|
+
getEmbeddingModelName() {
|
|
6942
|
+
return this.embedder.getModelName();
|
|
6943
|
+
}
|
|
6944
|
+
/**
|
|
6945
|
+
* Ensure embedding model metadata is in sync and optionally migrate vectors.
|
|
6946
|
+
* Migration strategy: clear vector index + clear embedding outbox + re-enqueue all events.
|
|
6947
|
+
*/
|
|
6948
|
+
async ensureEmbeddingModelForImport(options) {
|
|
6949
|
+
await this.initialize();
|
|
6950
|
+
const currentModel = this.getEmbeddingModelName();
|
|
6951
|
+
const metaPath = path3.join(this.storagePath, "embedding-meta.json");
|
|
6952
|
+
let previousModel = null;
|
|
6953
|
+
try {
|
|
6954
|
+
if (fs4.existsSync(metaPath)) {
|
|
6955
|
+
const parsed = JSON.parse(fs4.readFileSync(metaPath, "utf-8"));
|
|
6956
|
+
previousModel = parsed?.model || null;
|
|
6957
|
+
}
|
|
6958
|
+
} catch {
|
|
6959
|
+
previousModel = null;
|
|
6960
|
+
}
|
|
6961
|
+
const stats = await this.getStats();
|
|
6962
|
+
const hasExistingVectors = (stats.vectorCount || 0) > 0;
|
|
6963
|
+
if (!previousModel && !hasExistingVectors) {
|
|
6964
|
+
fs4.writeFileSync(metaPath, JSON.stringify({ model: currentModel, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
6965
|
+
return { changed: false, previousModel: null, currentModel, enqueued: 0, reason: "initialized-meta" };
|
|
6966
|
+
}
|
|
6967
|
+
const modelChanged = previousModel !== currentModel;
|
|
6968
|
+
const legacyUnknownButVectorsExist = !previousModel && hasExistingVectors;
|
|
6969
|
+
if (!modelChanged && !legacyUnknownButVectorsExist) {
|
|
6970
|
+
return { changed: false, previousModel, currentModel, enqueued: 0 };
|
|
6971
|
+
}
|
|
6972
|
+
if (options?.autoMigrate === false) {
|
|
6973
|
+
return {
|
|
6974
|
+
changed: true,
|
|
6975
|
+
previousModel,
|
|
6976
|
+
currentModel,
|
|
6977
|
+
enqueued: 0,
|
|
6978
|
+
reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
|
|
6979
|
+
};
|
|
6980
|
+
}
|
|
6981
|
+
const wasRunning = this.vectorWorker?.isRunning() || false;
|
|
6982
|
+
if (wasRunning)
|
|
6983
|
+
this.vectorWorker?.stop();
|
|
6984
|
+
await this.vectorStore.clearAll();
|
|
6985
|
+
await this.sqliteStore.clearEmbeddingOutbox();
|
|
6986
|
+
const pageSize = 1e3;
|
|
6987
|
+
let offset = 0;
|
|
6988
|
+
let enqueued = 0;
|
|
6989
|
+
while (true) {
|
|
6990
|
+
const page = await this.sqliteStore.getEventsPage(pageSize, offset);
|
|
6991
|
+
if (page.length === 0)
|
|
6992
|
+
break;
|
|
6993
|
+
for (const event of page) {
|
|
6994
|
+
await this.sqliteStore.enqueueForEmbedding(event.id, event.content);
|
|
6995
|
+
enqueued += 1;
|
|
6996
|
+
}
|
|
6997
|
+
offset += page.length;
|
|
6998
|
+
if (page.length < pageSize)
|
|
6999
|
+
break;
|
|
7000
|
+
}
|
|
7001
|
+
fs4.writeFileSync(
|
|
7002
|
+
metaPath,
|
|
7003
|
+
JSON.stringify(
|
|
7004
|
+
{
|
|
7005
|
+
model: currentModel,
|
|
7006
|
+
previousModel,
|
|
7007
|
+
migratedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7008
|
+
enqueued
|
|
7009
|
+
},
|
|
7010
|
+
null,
|
|
7011
|
+
2
|
|
7012
|
+
)
|
|
7013
|
+
);
|
|
7014
|
+
if (wasRunning)
|
|
7015
|
+
this.vectorWorker?.start();
|
|
7016
|
+
return {
|
|
7017
|
+
changed: true,
|
|
7018
|
+
previousModel,
|
|
7019
|
+
currentModel,
|
|
7020
|
+
enqueued,
|
|
7021
|
+
reason: legacyUnknownButVectorsExist ? "legacy-vectors-without-meta" : "model-mismatch"
|
|
7022
|
+
};
|
|
7023
|
+
}
|
|
6893
7024
|
/**
|
|
6894
7025
|
* Backward-compatible alias used by some hooks
|
|
6895
7026
|
*/
|
|
@@ -7074,6 +7205,7 @@ eventsRouter.get("/", async (c) => {
|
|
|
7074
7205
|
const eventType = c.req.query("type");
|
|
7075
7206
|
const level = c.req.query("level");
|
|
7076
7207
|
const sort = c.req.query("sort") || "recent";
|
|
7208
|
+
const q = (c.req.query("q") || "").trim().toLowerCase();
|
|
7077
7209
|
const limit = parseInt(c.req.query("limit") || "100", 10);
|
|
7078
7210
|
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
7079
7211
|
const memoryService = getServiceFromQuery(c);
|
|
@@ -7091,6 +7223,9 @@ eventsRouter.get("/", async (c) => {
|
|
|
7091
7223
|
if (eventType) {
|
|
7092
7224
|
events = events.filter((e) => e.eventType === eventType);
|
|
7093
7225
|
}
|
|
7226
|
+
if (q) {
|
|
7227
|
+
events = events.filter((e) => (e.content || "").toLowerCase().includes(q));
|
|
7228
|
+
}
|
|
7094
7229
|
if (sort === "accessed") {
|
|
7095
7230
|
events.sort((a, b) => {
|
|
7096
7231
|
const aTime = a.last_accessed_at || "";
|