claude-memory-layer 1.0.33 → 1.0.35
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 +21 -8
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +16 -5
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +16 -5
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +16 -5
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +16 -5
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +16 -5
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +16 -5
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +16 -5
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +16 -5
- package/dist/index.js.map +2 -2
- package/dist/mcp/index.js +176 -24
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +16 -5
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +16 -5
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +16 -5
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -13999,6 +13999,16 @@ var SQLiteEventStore = class {
|
|
|
13999
13999
|
getDatabase() {
|
|
14000
14000
|
return this.db;
|
|
14001
14001
|
}
|
|
14002
|
+
hasTableColumn(tableName, columnName) {
|
|
14003
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tableName))
|
|
14004
|
+
return false;
|
|
14005
|
+
try {
|
|
14006
|
+
const rows = sqliteAll(this.db, `PRAGMA table_info("${tableName}")`, []);
|
|
14007
|
+
return rows.some((row) => row.name === columnName);
|
|
14008
|
+
} catch {
|
|
14009
|
+
return false;
|
|
14010
|
+
}
|
|
14011
|
+
}
|
|
14002
14012
|
async recordRetrievalTrace(input) {
|
|
14003
14013
|
await this.initialize();
|
|
14004
14014
|
const traceId = randomUUID5();
|
|
@@ -14064,17 +14074,18 @@ var SQLiteEventStore = class {
|
|
|
14064
14074
|
async getRetrievalTraceStats() {
|
|
14065
14075
|
await this.initialize();
|
|
14066
14076
|
try {
|
|
14077
|
+
const rewrittenQueryRewriteKindSql = this.hasTableColumn("retrieval_traces", "query_rewrite_kind") ? REWRITTEN_QUERY_REWRITE_KIND_SQL : "0";
|
|
14067
14078
|
const row = sqliteGet(
|
|
14068
14079
|
this.db,
|
|
14069
14080
|
`SELECT
|
|
14070
14081
|
COUNT(*) as total_queries,
|
|
14071
14082
|
AVG(candidate_count) as avg_candidate_count,
|
|
14072
14083
|
AVG(selected_count) as avg_selected_count,
|
|
14073
|
-
SUM(CASE WHEN ${
|
|
14074
|
-
SUM(CASE WHEN ${
|
|
14075
|
-
SUM(CASE WHEN NOT (${
|
|
14076
|
-
AVG(CASE WHEN ${
|
|
14077
|
-
AVG(CASE WHEN NOT (${
|
|
14084
|
+
SUM(CASE WHEN ${rewrittenQueryRewriteKindSql} THEN 1 ELSE 0 END) as rewritten_queries,
|
|
14085
|
+
SUM(CASE WHEN ${rewrittenQueryRewriteKindSql} AND selected_count > 0 THEN 1 ELSE 0 END) as rewritten_queries_with_selection,
|
|
14086
|
+
SUM(CASE WHEN NOT (${rewrittenQueryRewriteKindSql}) AND selected_count > 0 THEN 1 ELSE 0 END) as raw_queries_with_selection,
|
|
14087
|
+
AVG(CASE WHEN ${rewrittenQueryRewriteKindSql} THEN selected_count END) as avg_selected_count_for_rewritten_queries,
|
|
14088
|
+
AVG(CASE WHEN NOT (${rewrittenQueryRewriteKindSql}) THEN selected_count END) as avg_selected_count_for_raw_queries,
|
|
14078
14089
|
CASE
|
|
14079
14090
|
WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
|
|
14080
14091
|
ELSE 0
|
|
@@ -19859,7 +19870,8 @@ var HermesSessionHistoryImporter = class {
|
|
|
19859
19870
|
source: "hermes",
|
|
19860
19871
|
hermesSource: session.source,
|
|
19861
19872
|
sourceSessionId: session.id,
|
|
19862
|
-
sourceSessionHash: hashLabel(session.id)
|
|
19873
|
+
sourceSessionHash: hashLabel(session.id),
|
|
19874
|
+
projectPath: effectiveProjectPath
|
|
19863
19875
|
}
|
|
19864
19876
|
);
|
|
19865
19877
|
if (appendResult.success && appendResult.isDuplicate) {
|
|
@@ -19896,7 +19908,8 @@ var HermesSessionHistoryImporter = class {
|
|
|
19896
19908
|
source: "hermes",
|
|
19897
19909
|
hermesSource: session.source,
|
|
19898
19910
|
sourceSessionId: session.id,
|
|
19899
|
-
sourceSessionHash: hashLabel(session.id)
|
|
19911
|
+
sourceSessionHash: hashLabel(session.id),
|
|
19912
|
+
projectPath: effectiveProjectPath
|
|
19900
19913
|
}
|
|
19901
19914
|
);
|
|
19902
19915
|
if (appendResult.success && appendResult.isDuplicate) {
|
|
@@ -20445,7 +20458,7 @@ async function handleToolCall(name, args) {
|
|
|
20445
20458
|
case "mem-details":
|
|
20446
20459
|
return await handleMemDetails(memoryService, args);
|
|
20447
20460
|
case "mem-stats":
|
|
20448
|
-
return await handleMemStats(memoryService);
|
|
20461
|
+
return await handleMemStats(memoryService, args);
|
|
20449
20462
|
case "mem-context-pack":
|
|
20450
20463
|
return await handleMemContextPack(memoryService, args);
|
|
20451
20464
|
case "mem-import-latest":
|
|
@@ -20481,19 +20494,19 @@ async function handleExternalMarketContext(args) {
|
|
|
20481
20494
|
async function handleMemSearch(memoryService, args) {
|
|
20482
20495
|
const query = args.query;
|
|
20483
20496
|
const topK = Math.min(args.topK || 5, 20);
|
|
20484
|
-
const
|
|
20485
|
-
|
|
20486
|
-
sessionId: args.sessionId,
|
|
20487
|
-
recordTrace: false
|
|
20488
|
-
});
|
|
20497
|
+
const sessionId = args.sessionId;
|
|
20498
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK, sessionId });
|
|
20489
20499
|
const lines = [
|
|
20490
20500
|
"## Memory Search Results",
|
|
20491
20501
|
"",
|
|
20492
|
-
`Found ${
|
|
20502
|
+
`Found ${search.memories.length} relevant memories:`,
|
|
20493
20503
|
""
|
|
20494
20504
|
];
|
|
20495
|
-
|
|
20496
|
-
|
|
20505
|
+
if (search.warning) {
|
|
20506
|
+
lines.push(search.warning, "");
|
|
20507
|
+
}
|
|
20508
|
+
for (let i = 0; i < search.memories.length; i++) {
|
|
20509
|
+
const m = search.memories[i];
|
|
20497
20510
|
const citationId = generateCitationId(m.event.id);
|
|
20498
20511
|
const date = m.event.timestamp.toISOString().split("T")[0];
|
|
20499
20512
|
const preview = m.event.content.slice(0, 100) + (m.event.content.length > 100 ? "..." : "");
|
|
@@ -20508,6 +20521,52 @@ async function handleMemSearch(memoryService, args) {
|
|
|
20508
20521
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
20509
20522
|
};
|
|
20510
20523
|
}
|
|
20524
|
+
var SEMANTIC_VECTOR_FALLBACK_WARNING = "Warning: semantic/vector retrieval unavailable; used keyword fallback.";
|
|
20525
|
+
var SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING = "Warning: semantic/vector retrieval unavailable; keyword fallback failed.";
|
|
20526
|
+
async function retrieveMcpMemories(memoryService, query, options) {
|
|
20527
|
+
try {
|
|
20528
|
+
const result = await memoryService.retrieveMemories(query, {
|
|
20529
|
+
topK: options.topK,
|
|
20530
|
+
sessionId: options.sessionId,
|
|
20531
|
+
recordTrace: false
|
|
20532
|
+
});
|
|
20533
|
+
return { memories: result.memories };
|
|
20534
|
+
} catch (error) {
|
|
20535
|
+
if (!isVectorSchemaMismatchError(error)) {
|
|
20536
|
+
throw error;
|
|
20537
|
+
}
|
|
20538
|
+
try {
|
|
20539
|
+
const memories = options.sessionId ? rankSessionKeywordMatches(
|
|
20540
|
+
query,
|
|
20541
|
+
await memoryService.getSessionHistory(options.sessionId),
|
|
20542
|
+
options.topK
|
|
20543
|
+
) : await memoryService.keywordSearch(query, { topK: options.topK });
|
|
20544
|
+
return { memories, warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
20545
|
+
} catch {
|
|
20546
|
+
return { memories: [], warning: SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING };
|
|
20547
|
+
}
|
|
20548
|
+
}
|
|
20549
|
+
}
|
|
20550
|
+
function isVectorSchemaMismatchError(error) {
|
|
20551
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20552
|
+
return /no vector column/i.test(message) || /query vector dimension/i.test(message) || /vector[^\n]{0,80}dimension/i.test(message) || /dimension[^\n]{0,80}vector/i.test(message) || /lancedb[^\n]{0,120}schema/i.test(message);
|
|
20553
|
+
}
|
|
20554
|
+
function rankSessionKeywordMatches(query, events, topK) {
|
|
20555
|
+
const queryTokens = tokenizeKeywordQuery(query);
|
|
20556
|
+
if (queryTokens.length === 0)
|
|
20557
|
+
return [];
|
|
20558
|
+
return events.map((event) => ({ event, score: scoreKeywordMatch(event.content, queryTokens) })).filter((match) => match.score > 0).sort((a, b) => b.score - a.score || b.event.timestamp.getTime() - a.event.timestamp.getTime()).slice(0, topK);
|
|
20559
|
+
}
|
|
20560
|
+
function tokenizeKeywordQuery(value) {
|
|
20561
|
+
return Array.from(new Set(
|
|
20562
|
+
value.toLowerCase().split(/[^a-z0-9가-힣_]+/).map((token) => token.trim()).filter((token) => token.length > 1)
|
|
20563
|
+
));
|
|
20564
|
+
}
|
|
20565
|
+
function scoreKeywordMatch(content, queryTokens) {
|
|
20566
|
+
const haystack = content.toLowerCase();
|
|
20567
|
+
const hits = queryTokens.filter((token) => haystack.includes(token)).length;
|
|
20568
|
+
return hits / queryTokens.length;
|
|
20569
|
+
}
|
|
20511
20570
|
async function handleMemTimeline(memoryService, args) {
|
|
20512
20571
|
const ids = args.ids;
|
|
20513
20572
|
const windowSize = args.windowSize || 3;
|
|
@@ -20599,10 +20658,8 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20599
20658
|
sessionsDir: optionalString(args.sessionsDir),
|
|
20600
20659
|
stateDb: optionalString(args.stateDb)
|
|
20601
20660
|
}) : void 0;
|
|
20602
|
-
const
|
|
20603
|
-
|
|
20604
|
-
memoryService.getRecentEvents(recentLimit)
|
|
20605
|
-
]);
|
|
20661
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId });
|
|
20662
|
+
const recentEvents = await memoryService.getRecentEvents(recentLimit);
|
|
20606
20663
|
const timelineEvents = selectContextPackTimelineEvents(
|
|
20607
20664
|
recentEvents,
|
|
20608
20665
|
projectPath,
|
|
@@ -20610,7 +20667,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20610
20667
|
);
|
|
20611
20668
|
const sessions = summarizeSessions(timelineEvents, sessionLimit);
|
|
20612
20669
|
const recentSessionIds = new Set(sessions.map((session) => session.sessionId));
|
|
20613
|
-
const relevantMemories = selectContextPackMemories(
|
|
20670
|
+
const relevantMemories = selectContextPackMemories(search.memories, {
|
|
20614
20671
|
genericContinuationQuery,
|
|
20615
20672
|
topK,
|
|
20616
20673
|
recentSessionIds,
|
|
@@ -20631,6 +20688,9 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20631
20688
|
`- Refresh limits: sessions=${freshnessRun.sessionLimit} messages=${freshnessRun.messageLimit} force=${freshnessRun.force ? "yes" : "no"} embeddings=${freshnessRun.processEmbeddings ? `processed ${freshnessRun.embeddingsProcessed ?? 0}` : "skipped"}`
|
|
20632
20689
|
);
|
|
20633
20690
|
}
|
|
20691
|
+
if (search.warning) {
|
|
20692
|
+
lines.push(`- ${search.warning}`);
|
|
20693
|
+
}
|
|
20634
20694
|
if (genericContinuationQuery) {
|
|
20635
20695
|
lines.push("- Generic continuation query: recent project timeline prioritized.");
|
|
20636
20696
|
}
|
|
@@ -20870,7 +20930,7 @@ function shouldShowContextPackTimelineEvent(event, projectPath, genericContinuat
|
|
|
20870
20930
|
return false;
|
|
20871
20931
|
if (event.eventType === "tool_observation")
|
|
20872
20932
|
return false;
|
|
20873
|
-
if (
|
|
20933
|
+
if (eventBelongsToDifferentProject(event, projectPath))
|
|
20874
20934
|
return false;
|
|
20875
20935
|
if (genericContinuationQuery && isGenericContinuationQuery(content))
|
|
20876
20936
|
return false;
|
|
@@ -20882,7 +20942,7 @@ function shouldShowContextPackMemory(memory, options) {
|
|
|
20882
20942
|
return false;
|
|
20883
20943
|
if (memory.event.eventType === "tool_observation")
|
|
20884
20944
|
return false;
|
|
20885
|
-
if (
|
|
20945
|
+
if (eventBelongsToDifferentProject(memory.event, options.projectPath))
|
|
20886
20946
|
return false;
|
|
20887
20947
|
if (!options.genericContinuationQuery)
|
|
20888
20948
|
return true;
|
|
@@ -20894,6 +20954,54 @@ function shouldShowContextPackMemory(memory, options) {
|
|
|
20894
20954
|
return memory.score >= GENERIC_RECENT_MEMORY_MIN_SCORE;
|
|
20895
20955
|
return memory.score >= GENERIC_STALE_MEMORY_MIN_SCORE;
|
|
20896
20956
|
}
|
|
20957
|
+
function eventBelongsToDifferentProject(event, projectPath) {
|
|
20958
|
+
if (!projectPath)
|
|
20959
|
+
return false;
|
|
20960
|
+
const metadata = event.metadata || {};
|
|
20961
|
+
const metadataProjectRefs = metadataProjectReferenceValues(metadata);
|
|
20962
|
+
if (metadataProjectRefs.length > 0) {
|
|
20963
|
+
return !metadataProjectRefs.some((value) => projectReferenceMatches(value, projectPath));
|
|
20964
|
+
}
|
|
20965
|
+
if (isUnscopedImportedHistory(metadata))
|
|
20966
|
+
return true;
|
|
20967
|
+
return mentionsDifferentWorkspaceProject(event.content || "", projectPath);
|
|
20968
|
+
}
|
|
20969
|
+
function isUnscopedImportedHistory(metadata) {
|
|
20970
|
+
return typeof metadata.importedFrom === "string" || typeof metadata.sourceSessionId === "string" || typeof metadata.sourceSessionHash === "string" || typeof metadata.transcriptPath === "string";
|
|
20971
|
+
}
|
|
20972
|
+
var PROJECT_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
20973
|
+
"projectPath",
|
|
20974
|
+
"sourceProjectPath",
|
|
20975
|
+
"workspacePath",
|
|
20976
|
+
"sourceWorkspacePath",
|
|
20977
|
+
"cwd",
|
|
20978
|
+
"sourceCwd",
|
|
20979
|
+
"currentWorkingDirectory",
|
|
20980
|
+
"projectRoot",
|
|
20981
|
+
"repoPath",
|
|
20982
|
+
"repositoryPath"
|
|
20983
|
+
]);
|
|
20984
|
+
function metadataProjectReferenceValues(metadata) {
|
|
20985
|
+
const values = [];
|
|
20986
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
20987
|
+
if (!PROJECT_METADATA_KEYS.has(key))
|
|
20988
|
+
continue;
|
|
20989
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
20990
|
+
values.push(value.trim());
|
|
20991
|
+
}
|
|
20992
|
+
}
|
|
20993
|
+
return values;
|
|
20994
|
+
}
|
|
20995
|
+
function projectReferenceMatches(reference, projectPath) {
|
|
20996
|
+
const normalizedReference = normalizeProjectReference(reference);
|
|
20997
|
+
const normalizedProjectPath = normalizeProjectReference(projectPath);
|
|
20998
|
+
if (normalizedReference === normalizedProjectPath)
|
|
20999
|
+
return true;
|
|
21000
|
+
return normalizedReference.startsWith(`${normalizedProjectPath}/`);
|
|
21001
|
+
}
|
|
21002
|
+
function normalizeProjectReference(value) {
|
|
21003
|
+
return value.trim().replace(/\\/g, "/").replace(/\/+$/g, "").toLowerCase();
|
|
21004
|
+
}
|
|
20897
21005
|
function mentionsDifferentWorkspaceProject(content, projectPath) {
|
|
20898
21006
|
const currentProject = basenameOfPath(projectPath);
|
|
20899
21007
|
if (!currentProject)
|
|
@@ -21124,9 +21232,11 @@ function formatMetadataValue(value) {
|
|
|
21124
21232
|
function textResult(text) {
|
|
21125
21233
|
return { content: [{ type: "text", text }] };
|
|
21126
21234
|
}
|
|
21127
|
-
async function handleMemStats(memoryService) {
|
|
21235
|
+
async function handleMemStats(memoryService, args) {
|
|
21128
21236
|
const stats = await memoryService.getStats();
|
|
21129
21237
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
21238
|
+
const outboxStats = await readMcpOutboxStats(memoryService);
|
|
21239
|
+
const storageView = buildMcpStatsStorageView(optionalString(args.projectPath));
|
|
21130
21240
|
const uniqueSessions = new Set(recentEvents.map((e) => e.sessionId));
|
|
21131
21241
|
const lines = [
|
|
21132
21242
|
"## Memory Statistics",
|
|
@@ -21135,6 +21245,19 @@ async function handleMemStats(memoryService) {
|
|
|
21135
21245
|
`- **Total Vectors**: ${stats.vectorCount}`,
|
|
21136
21246
|
`- **Sessions**: ${uniqueSessions.size}`,
|
|
21137
21247
|
"",
|
|
21248
|
+
"### Storage View / Freshness",
|
|
21249
|
+
"",
|
|
21250
|
+
`- Storage View: ${storageView.storageView}`,
|
|
21251
|
+
`- Storage Path Label: ${storageView.storagePathLabel}`,
|
|
21252
|
+
`- Embedder Model: ${storageView.embedderModel}`,
|
|
21253
|
+
`- Vector Table Dimension: ${storageView.vectorTableDimension}`,
|
|
21254
|
+
`- Pending Embeddings: ${outboxStats.embedding.pending}`,
|
|
21255
|
+
`- Embedding Outbox: pending=${outboxStats.embedding.pending}, processing=${outboxStats.embedding.processing}, failed=${outboxStats.embedding.failed}, total=${outboxStats.embedding.total}`,
|
|
21256
|
+
`- Vector Outbox Pending: ${outboxStats.vector.pending}`,
|
|
21257
|
+
`- Vector Outbox: pending=${outboxStats.vector.pending}, processing=${outboxStats.vector.processing}, failed=${outboxStats.vector.failed}, total=${outboxStats.vector.total}`,
|
|
21258
|
+
"- MCP/CLI parity: CLI `stats -p <project>` and MCP `mem-stats(projectPath=...)` should use this same storage view label.",
|
|
21259
|
+
"- Restart guidance: if CLI and MCP counts differ for this storage view after import/build, restart the long-lived MCP/Hermes gateway process.",
|
|
21260
|
+
"",
|
|
21138
21261
|
"### Events by Type",
|
|
21139
21262
|
""
|
|
21140
21263
|
];
|
|
@@ -21149,6 +21272,35 @@ async function handleMemStats(memoryService) {
|
|
|
21149
21272
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
21150
21273
|
};
|
|
21151
21274
|
}
|
|
21275
|
+
async function readMcpOutboxStats(memoryService) {
|
|
21276
|
+
try {
|
|
21277
|
+
return await memoryService.getOutboxStats();
|
|
21278
|
+
} catch {
|
|
21279
|
+
return {
|
|
21280
|
+
embedding: { pending: 0, processing: 0, failed: 0, total: 0 },
|
|
21281
|
+
vector: { pending: 0, processing: 0, failed: 0, total: 0 }
|
|
21282
|
+
};
|
|
21283
|
+
}
|
|
21284
|
+
}
|
|
21285
|
+
function buildMcpStatsStorageView(projectPath) {
|
|
21286
|
+
const embedderModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL || DEFAULT_EMBEDDING_MODEL;
|
|
21287
|
+
const requestedProjectPath = projectPath?.trim();
|
|
21288
|
+
if (requestedProjectPath) {
|
|
21289
|
+
const projectHash = hashProjectPath(requestedProjectPath);
|
|
21290
|
+
return {
|
|
21291
|
+
storageView: `project:${projectHash}`,
|
|
21292
|
+
storagePathLabel: `~/.claude-code/memory/projects/${projectHash}`,
|
|
21293
|
+
embedderModel,
|
|
21294
|
+
vectorTableDimension: "unknown (not recorded in current vector metadata)"
|
|
21295
|
+
};
|
|
21296
|
+
}
|
|
21297
|
+
return {
|
|
21298
|
+
storageView: "global",
|
|
21299
|
+
storagePathLabel: "~/.claude-code/memory",
|
|
21300
|
+
embedderModel,
|
|
21301
|
+
vectorTableDimension: "unknown (not recorded in current vector metadata)"
|
|
21302
|
+
};
|
|
21303
|
+
}
|
|
21152
21304
|
|
|
21153
21305
|
// src/extensions/mcp/index.ts
|
|
21154
21306
|
var server = new Server(
|