claude-memory-layer 1.0.33 → 1.0.34
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 +5 -3
- package/dist/cli/index.js.map +2 -2
- package/dist/mcp/index.js +160 -19
- package/dist/mcp/index.js.map +2 -2
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -19859,7 +19859,8 @@ var HermesSessionHistoryImporter = class {
|
|
|
19859
19859
|
source: "hermes",
|
|
19860
19860
|
hermesSource: session.source,
|
|
19861
19861
|
sourceSessionId: session.id,
|
|
19862
|
-
sourceSessionHash: hashLabel(session.id)
|
|
19862
|
+
sourceSessionHash: hashLabel(session.id),
|
|
19863
|
+
projectPath: effectiveProjectPath
|
|
19863
19864
|
}
|
|
19864
19865
|
);
|
|
19865
19866
|
if (appendResult.success && appendResult.isDuplicate) {
|
|
@@ -19896,7 +19897,8 @@ var HermesSessionHistoryImporter = class {
|
|
|
19896
19897
|
source: "hermes",
|
|
19897
19898
|
hermesSource: session.source,
|
|
19898
19899
|
sourceSessionId: session.id,
|
|
19899
|
-
sourceSessionHash: hashLabel(session.id)
|
|
19900
|
+
sourceSessionHash: hashLabel(session.id),
|
|
19901
|
+
projectPath: effectiveProjectPath
|
|
19900
19902
|
}
|
|
19901
19903
|
);
|
|
19902
19904
|
if (appendResult.success && appendResult.isDuplicate) {
|
|
@@ -20445,7 +20447,7 @@ async function handleToolCall(name, args) {
|
|
|
20445
20447
|
case "mem-details":
|
|
20446
20448
|
return await handleMemDetails(memoryService, args);
|
|
20447
20449
|
case "mem-stats":
|
|
20448
|
-
return await handleMemStats(memoryService);
|
|
20450
|
+
return await handleMemStats(memoryService, args);
|
|
20449
20451
|
case "mem-context-pack":
|
|
20450
20452
|
return await handleMemContextPack(memoryService, args);
|
|
20451
20453
|
case "mem-import-latest":
|
|
@@ -20481,19 +20483,19 @@ async function handleExternalMarketContext(args) {
|
|
|
20481
20483
|
async function handleMemSearch(memoryService, args) {
|
|
20482
20484
|
const query = args.query;
|
|
20483
20485
|
const topK = Math.min(args.topK || 5, 20);
|
|
20484
|
-
const
|
|
20485
|
-
|
|
20486
|
-
sessionId: args.sessionId,
|
|
20487
|
-
recordTrace: false
|
|
20488
|
-
});
|
|
20486
|
+
const sessionId = args.sessionId;
|
|
20487
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK, sessionId });
|
|
20489
20488
|
const lines = [
|
|
20490
20489
|
"## Memory Search Results",
|
|
20491
20490
|
"",
|
|
20492
|
-
`Found ${
|
|
20491
|
+
`Found ${search.memories.length} relevant memories:`,
|
|
20493
20492
|
""
|
|
20494
20493
|
];
|
|
20495
|
-
|
|
20496
|
-
|
|
20494
|
+
if (search.warning) {
|
|
20495
|
+
lines.push(search.warning, "");
|
|
20496
|
+
}
|
|
20497
|
+
for (let i = 0; i < search.memories.length; i++) {
|
|
20498
|
+
const m = search.memories[i];
|
|
20497
20499
|
const citationId = generateCitationId(m.event.id);
|
|
20498
20500
|
const date = m.event.timestamp.toISOString().split("T")[0];
|
|
20499
20501
|
const preview = m.event.content.slice(0, 100) + (m.event.content.length > 100 ? "..." : "");
|
|
@@ -20508,6 +20510,52 @@ async function handleMemSearch(memoryService, args) {
|
|
|
20508
20510
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
20509
20511
|
};
|
|
20510
20512
|
}
|
|
20513
|
+
var SEMANTIC_VECTOR_FALLBACK_WARNING = "Warning: semantic/vector retrieval unavailable; used keyword fallback.";
|
|
20514
|
+
var SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING = "Warning: semantic/vector retrieval unavailable; keyword fallback failed.";
|
|
20515
|
+
async function retrieveMcpMemories(memoryService, query, options) {
|
|
20516
|
+
try {
|
|
20517
|
+
const result = await memoryService.retrieveMemories(query, {
|
|
20518
|
+
topK: options.topK,
|
|
20519
|
+
sessionId: options.sessionId,
|
|
20520
|
+
recordTrace: false
|
|
20521
|
+
});
|
|
20522
|
+
return { memories: result.memories };
|
|
20523
|
+
} catch (error) {
|
|
20524
|
+
if (!isVectorSchemaMismatchError(error)) {
|
|
20525
|
+
throw error;
|
|
20526
|
+
}
|
|
20527
|
+
try {
|
|
20528
|
+
const memories = options.sessionId ? rankSessionKeywordMatches(
|
|
20529
|
+
query,
|
|
20530
|
+
await memoryService.getSessionHistory(options.sessionId),
|
|
20531
|
+
options.topK
|
|
20532
|
+
) : await memoryService.keywordSearch(query, { topK: options.topK });
|
|
20533
|
+
return { memories, warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
20534
|
+
} catch {
|
|
20535
|
+
return { memories: [], warning: SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING };
|
|
20536
|
+
}
|
|
20537
|
+
}
|
|
20538
|
+
}
|
|
20539
|
+
function isVectorSchemaMismatchError(error) {
|
|
20540
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20541
|
+
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);
|
|
20542
|
+
}
|
|
20543
|
+
function rankSessionKeywordMatches(query, events, topK) {
|
|
20544
|
+
const queryTokens = tokenizeKeywordQuery(query);
|
|
20545
|
+
if (queryTokens.length === 0)
|
|
20546
|
+
return [];
|
|
20547
|
+
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);
|
|
20548
|
+
}
|
|
20549
|
+
function tokenizeKeywordQuery(value) {
|
|
20550
|
+
return Array.from(new Set(
|
|
20551
|
+
value.toLowerCase().split(/[^a-z0-9가-힣_]+/).map((token) => token.trim()).filter((token) => token.length > 1)
|
|
20552
|
+
));
|
|
20553
|
+
}
|
|
20554
|
+
function scoreKeywordMatch(content, queryTokens) {
|
|
20555
|
+
const haystack = content.toLowerCase();
|
|
20556
|
+
const hits = queryTokens.filter((token) => haystack.includes(token)).length;
|
|
20557
|
+
return hits / queryTokens.length;
|
|
20558
|
+
}
|
|
20511
20559
|
async function handleMemTimeline(memoryService, args) {
|
|
20512
20560
|
const ids = args.ids;
|
|
20513
20561
|
const windowSize = args.windowSize || 3;
|
|
@@ -20599,10 +20647,8 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20599
20647
|
sessionsDir: optionalString(args.sessionsDir),
|
|
20600
20648
|
stateDb: optionalString(args.stateDb)
|
|
20601
20649
|
}) : void 0;
|
|
20602
|
-
const
|
|
20603
|
-
|
|
20604
|
-
memoryService.getRecentEvents(recentLimit)
|
|
20605
|
-
]);
|
|
20650
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId });
|
|
20651
|
+
const recentEvents = await memoryService.getRecentEvents(recentLimit);
|
|
20606
20652
|
const timelineEvents = selectContextPackTimelineEvents(
|
|
20607
20653
|
recentEvents,
|
|
20608
20654
|
projectPath,
|
|
@@ -20610,7 +20656,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20610
20656
|
);
|
|
20611
20657
|
const sessions = summarizeSessions(timelineEvents, sessionLimit);
|
|
20612
20658
|
const recentSessionIds = new Set(sessions.map((session) => session.sessionId));
|
|
20613
|
-
const relevantMemories = selectContextPackMemories(
|
|
20659
|
+
const relevantMemories = selectContextPackMemories(search.memories, {
|
|
20614
20660
|
genericContinuationQuery,
|
|
20615
20661
|
topK,
|
|
20616
20662
|
recentSessionIds,
|
|
@@ -20631,6 +20677,9 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
20631
20677
|
`- Refresh limits: sessions=${freshnessRun.sessionLimit} messages=${freshnessRun.messageLimit} force=${freshnessRun.force ? "yes" : "no"} embeddings=${freshnessRun.processEmbeddings ? `processed ${freshnessRun.embeddingsProcessed ?? 0}` : "skipped"}`
|
|
20632
20678
|
);
|
|
20633
20679
|
}
|
|
20680
|
+
if (search.warning) {
|
|
20681
|
+
lines.push(`- ${search.warning}`);
|
|
20682
|
+
}
|
|
20634
20683
|
if (genericContinuationQuery) {
|
|
20635
20684
|
lines.push("- Generic continuation query: recent project timeline prioritized.");
|
|
20636
20685
|
}
|
|
@@ -20870,7 +20919,7 @@ function shouldShowContextPackTimelineEvent(event, projectPath, genericContinuat
|
|
|
20870
20919
|
return false;
|
|
20871
20920
|
if (event.eventType === "tool_observation")
|
|
20872
20921
|
return false;
|
|
20873
|
-
if (
|
|
20922
|
+
if (eventBelongsToDifferentProject(event, projectPath))
|
|
20874
20923
|
return false;
|
|
20875
20924
|
if (genericContinuationQuery && isGenericContinuationQuery(content))
|
|
20876
20925
|
return false;
|
|
@@ -20882,7 +20931,7 @@ function shouldShowContextPackMemory(memory, options) {
|
|
|
20882
20931
|
return false;
|
|
20883
20932
|
if (memory.event.eventType === "tool_observation")
|
|
20884
20933
|
return false;
|
|
20885
|
-
if (
|
|
20934
|
+
if (eventBelongsToDifferentProject(memory.event, options.projectPath))
|
|
20886
20935
|
return false;
|
|
20887
20936
|
if (!options.genericContinuationQuery)
|
|
20888
20937
|
return true;
|
|
@@ -20894,6 +20943,54 @@ function shouldShowContextPackMemory(memory, options) {
|
|
|
20894
20943
|
return memory.score >= GENERIC_RECENT_MEMORY_MIN_SCORE;
|
|
20895
20944
|
return memory.score >= GENERIC_STALE_MEMORY_MIN_SCORE;
|
|
20896
20945
|
}
|
|
20946
|
+
function eventBelongsToDifferentProject(event, projectPath) {
|
|
20947
|
+
if (!projectPath)
|
|
20948
|
+
return false;
|
|
20949
|
+
const metadata = event.metadata || {};
|
|
20950
|
+
const metadataProjectRefs = metadataProjectReferenceValues(metadata);
|
|
20951
|
+
if (metadataProjectRefs.length > 0) {
|
|
20952
|
+
return !metadataProjectRefs.some((value) => projectReferenceMatches(value, projectPath));
|
|
20953
|
+
}
|
|
20954
|
+
if (isUnscopedImportedHistory(metadata))
|
|
20955
|
+
return true;
|
|
20956
|
+
return mentionsDifferentWorkspaceProject(event.content || "", projectPath);
|
|
20957
|
+
}
|
|
20958
|
+
function isUnscopedImportedHistory(metadata) {
|
|
20959
|
+
return typeof metadata.importedFrom === "string" || typeof metadata.sourceSessionId === "string" || typeof metadata.sourceSessionHash === "string" || typeof metadata.transcriptPath === "string";
|
|
20960
|
+
}
|
|
20961
|
+
var PROJECT_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
20962
|
+
"projectPath",
|
|
20963
|
+
"sourceProjectPath",
|
|
20964
|
+
"workspacePath",
|
|
20965
|
+
"sourceWorkspacePath",
|
|
20966
|
+
"cwd",
|
|
20967
|
+
"sourceCwd",
|
|
20968
|
+
"currentWorkingDirectory",
|
|
20969
|
+
"projectRoot",
|
|
20970
|
+
"repoPath",
|
|
20971
|
+
"repositoryPath"
|
|
20972
|
+
]);
|
|
20973
|
+
function metadataProjectReferenceValues(metadata) {
|
|
20974
|
+
const values = [];
|
|
20975
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
20976
|
+
if (!PROJECT_METADATA_KEYS.has(key))
|
|
20977
|
+
continue;
|
|
20978
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
20979
|
+
values.push(value.trim());
|
|
20980
|
+
}
|
|
20981
|
+
}
|
|
20982
|
+
return values;
|
|
20983
|
+
}
|
|
20984
|
+
function projectReferenceMatches(reference, projectPath) {
|
|
20985
|
+
const normalizedReference = normalizeProjectReference(reference);
|
|
20986
|
+
const normalizedProjectPath = normalizeProjectReference(projectPath);
|
|
20987
|
+
if (normalizedReference === normalizedProjectPath)
|
|
20988
|
+
return true;
|
|
20989
|
+
return normalizedReference.startsWith(`${normalizedProjectPath}/`);
|
|
20990
|
+
}
|
|
20991
|
+
function normalizeProjectReference(value) {
|
|
20992
|
+
return value.trim().replace(/\\/g, "/").replace(/\/+$/g, "").toLowerCase();
|
|
20993
|
+
}
|
|
20897
20994
|
function mentionsDifferentWorkspaceProject(content, projectPath) {
|
|
20898
20995
|
const currentProject = basenameOfPath(projectPath);
|
|
20899
20996
|
if (!currentProject)
|
|
@@ -21124,9 +21221,11 @@ function formatMetadataValue(value) {
|
|
|
21124
21221
|
function textResult(text) {
|
|
21125
21222
|
return { content: [{ type: "text", text }] };
|
|
21126
21223
|
}
|
|
21127
|
-
async function handleMemStats(memoryService) {
|
|
21224
|
+
async function handleMemStats(memoryService, args) {
|
|
21128
21225
|
const stats = await memoryService.getStats();
|
|
21129
21226
|
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
21227
|
+
const outboxStats = await readMcpOutboxStats(memoryService);
|
|
21228
|
+
const storageView = buildMcpStatsStorageView(optionalString(args.projectPath));
|
|
21130
21229
|
const uniqueSessions = new Set(recentEvents.map((e) => e.sessionId));
|
|
21131
21230
|
const lines = [
|
|
21132
21231
|
"## Memory Statistics",
|
|
@@ -21135,6 +21234,19 @@ async function handleMemStats(memoryService) {
|
|
|
21135
21234
|
`- **Total Vectors**: ${stats.vectorCount}`,
|
|
21136
21235
|
`- **Sessions**: ${uniqueSessions.size}`,
|
|
21137
21236
|
"",
|
|
21237
|
+
"### Storage View / Freshness",
|
|
21238
|
+
"",
|
|
21239
|
+
`- Storage View: ${storageView.storageView}`,
|
|
21240
|
+
`- Storage Path Label: ${storageView.storagePathLabel}`,
|
|
21241
|
+
`- Embedder Model: ${storageView.embedderModel}`,
|
|
21242
|
+
`- Vector Table Dimension: ${storageView.vectorTableDimension}`,
|
|
21243
|
+
`- Pending Embeddings: ${outboxStats.embedding.pending}`,
|
|
21244
|
+
`- Embedding Outbox: pending=${outboxStats.embedding.pending}, processing=${outboxStats.embedding.processing}, failed=${outboxStats.embedding.failed}, total=${outboxStats.embedding.total}`,
|
|
21245
|
+
`- Vector Outbox Pending: ${outboxStats.vector.pending}`,
|
|
21246
|
+
`- Vector Outbox: pending=${outboxStats.vector.pending}, processing=${outboxStats.vector.processing}, failed=${outboxStats.vector.failed}, total=${outboxStats.vector.total}`,
|
|
21247
|
+
"- MCP/CLI parity: CLI `stats -p <project>` and MCP `mem-stats(projectPath=...)` should use this same storage view label.",
|
|
21248
|
+
"- Restart guidance: if CLI and MCP counts differ for this storage view after import/build, restart the long-lived MCP/Hermes gateway process.",
|
|
21249
|
+
"",
|
|
21138
21250
|
"### Events by Type",
|
|
21139
21251
|
""
|
|
21140
21252
|
];
|
|
@@ -21149,6 +21261,35 @@ async function handleMemStats(memoryService) {
|
|
|
21149
21261
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
21150
21262
|
};
|
|
21151
21263
|
}
|
|
21264
|
+
async function readMcpOutboxStats(memoryService) {
|
|
21265
|
+
try {
|
|
21266
|
+
return await memoryService.getOutboxStats();
|
|
21267
|
+
} catch {
|
|
21268
|
+
return {
|
|
21269
|
+
embedding: { pending: 0, processing: 0, failed: 0, total: 0 },
|
|
21270
|
+
vector: { pending: 0, processing: 0, failed: 0, total: 0 }
|
|
21271
|
+
};
|
|
21272
|
+
}
|
|
21273
|
+
}
|
|
21274
|
+
function buildMcpStatsStorageView(projectPath) {
|
|
21275
|
+
const embedderModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL || DEFAULT_EMBEDDING_MODEL;
|
|
21276
|
+
const requestedProjectPath = projectPath?.trim();
|
|
21277
|
+
if (requestedProjectPath) {
|
|
21278
|
+
const projectHash = hashProjectPath(requestedProjectPath);
|
|
21279
|
+
return {
|
|
21280
|
+
storageView: `project:${projectHash}`,
|
|
21281
|
+
storagePathLabel: `~/.claude-code/memory/projects/${projectHash}`,
|
|
21282
|
+
embedderModel,
|
|
21283
|
+
vectorTableDimension: "unknown (not recorded in current vector metadata)"
|
|
21284
|
+
};
|
|
21285
|
+
}
|
|
21286
|
+
return {
|
|
21287
|
+
storageView: "global",
|
|
21288
|
+
storagePathLabel: "~/.claude-code/memory",
|
|
21289
|
+
embedderModel,
|
|
21290
|
+
vectorTableDimension: "unknown (not recorded in current vector metadata)"
|
|
21291
|
+
};
|
|
21292
|
+
}
|
|
21152
21293
|
|
|
21153
21294
|
// src/extensions/mcp/index.ts
|
|
21154
21295
|
var server = new Server(
|