claude-memory-layer 1.0.46 → 1.0.48
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/README.md +3 -2
- package/dist/cli/index.js +72 -5
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +1072 -4
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +71 -4
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +71 -4
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +71 -4
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +71 -4
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +71 -4
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +71 -4
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +1078 -10
- package/dist/index.js.map +4 -4
- package/dist/mcp/index.js +142 -8
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +71 -4
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +71 -4
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +71 -4
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +4 -2
package/dist/mcp/index.js
CHANGED
|
@@ -10410,6 +10410,11 @@ var tools = [
|
|
|
10410
10410
|
enum: ["off", "safe", "aggressive"],
|
|
10411
10411
|
description: "Optional context-pack compression mode. safe preserves source refs and high-signal errors/log lines; aggressive is more compact."
|
|
10412
10412
|
},
|
|
10413
|
+
retrievalMode: {
|
|
10414
|
+
type: "string",
|
|
10415
|
+
enum: ["session-event-hybrid", "event"],
|
|
10416
|
+
description: "Optional production context-pack retrieval mode. Defaults to session-event-hybrid (session-event hybrid), which rescues query-relevant sibling events from sessions already hit by direct retrieval. Use event for exact event-only debugging."
|
|
10417
|
+
},
|
|
10413
10418
|
maxChars: {
|
|
10414
10419
|
type: "number",
|
|
10415
10420
|
minimum: 1e3,
|
|
@@ -10567,6 +10572,16 @@ var tools = [
|
|
|
10567
10572
|
type: "number",
|
|
10568
10573
|
description: "Maximum recent events to scan for ID resolution (default: 10000, max: 50000)"
|
|
10569
10574
|
},
|
|
10575
|
+
includeNeighbors: {
|
|
10576
|
+
type: "boolean",
|
|
10577
|
+
description: "When true, include privacy-safe neighbor events from the same session around each resolved source reference."
|
|
10578
|
+
},
|
|
10579
|
+
neighborWindow: {
|
|
10580
|
+
type: "number",
|
|
10581
|
+
minimum: 0,
|
|
10582
|
+
maximum: 5,
|
|
10583
|
+
description: "Number of before/after session neighbor events to include when includeNeighbors is true (default: 1, max: 5)."
|
|
10584
|
+
},
|
|
10570
10585
|
projectPath: projectPathProperty
|
|
10571
10586
|
},
|
|
10572
10587
|
required: ["ids"]
|
|
@@ -13647,6 +13662,7 @@ var VectorOutbox = class {
|
|
|
13647
13662
|
// src/core/retrieval-debug-lanes.ts
|
|
13648
13663
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
13649
13664
|
"raw_event",
|
|
13665
|
+
"session_event",
|
|
13650
13666
|
"session_summary",
|
|
13651
13667
|
"graph_path",
|
|
13652
13668
|
"facet_match"
|
|
@@ -21501,6 +21517,7 @@ var Retriever = class {
|
|
|
21501
21517
|
}
|
|
21502
21518
|
async retrieve(query, options = {}) {
|
|
21503
21519
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
21520
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
21504
21521
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
21505
21522
|
const fallbackTrace = [];
|
|
21506
21523
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -21531,6 +21548,7 @@ var Retriever = class {
|
|
|
21531
21548
|
decayPolicy: opts.decayPolicy,
|
|
21532
21549
|
intentRewrite: opts.intentRewrite === true,
|
|
21533
21550
|
graphHop: opts.graphHop,
|
|
21551
|
+
retrievalMode,
|
|
21534
21552
|
projectScopeMode: opts.projectScopeMode,
|
|
21535
21553
|
projectHash: opts.projectHash,
|
|
21536
21554
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21549,6 +21567,7 @@ var Retriever = class {
|
|
|
21549
21567
|
rerankWeights: opts.rerankWeights,
|
|
21550
21568
|
decayPolicy: opts.decayPolicy,
|
|
21551
21569
|
graphHop: opts.graphHop,
|
|
21570
|
+
retrievalMode,
|
|
21552
21571
|
projectScopeMode: opts.projectScopeMode,
|
|
21553
21572
|
projectHash: opts.projectHash,
|
|
21554
21573
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21568,6 +21587,7 @@ var Retriever = class {
|
|
|
21568
21587
|
rerankWeights: opts.rerankWeights,
|
|
21569
21588
|
decayPolicy: opts.decayPolicy,
|
|
21570
21589
|
graphHop: opts.graphHop,
|
|
21590
|
+
retrievalMode,
|
|
21571
21591
|
projectScopeMode: opts.projectScopeMode,
|
|
21572
21592
|
projectHash: opts.projectHash,
|
|
21573
21593
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21588,10 +21608,26 @@ var Retriever = class {
|
|
|
21588
21608
|
query,
|
|
21589
21609
|
minScore: opts.minScore
|
|
21590
21610
|
});
|
|
21611
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
21612
|
+
query: qualityQuery,
|
|
21613
|
+
currentStateQuery: query,
|
|
21614
|
+
limit: opts.topK * 4
|
|
21615
|
+
}) : filteredSummary;
|
|
21616
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
21617
|
+
scope: opts.scope,
|
|
21618
|
+
projectScopeMode: opts.projectScopeMode,
|
|
21619
|
+
projectHash: opts.projectHash,
|
|
21620
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
21621
|
+
facets: opts.facets
|
|
21622
|
+
}) : expandedSummary;
|
|
21623
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
21624
|
+
query,
|
|
21625
|
+
minScore: opts.minScore
|
|
21626
|
+
}) : scopedExpandedSummary;
|
|
21591
21627
|
current = {
|
|
21592
|
-
results:
|
|
21593
|
-
candidateResults:
|
|
21594
|
-
matchResult: this.matcher.matchSearchResults(
|
|
21628
|
+
results: finalSummary,
|
|
21629
|
+
candidateResults: finalSummary,
|
|
21630
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
21595
21631
|
};
|
|
21596
21632
|
fallbackTrace.push("fallback:summary");
|
|
21597
21633
|
}
|
|
@@ -21677,13 +21713,18 @@ var Retriever = class {
|
|
|
21677
21713
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
21678
21714
|
}
|
|
21679
21715
|
}
|
|
21680
|
-
const
|
|
21716
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
21681
21717
|
query,
|
|
21682
21718
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
21683
21719
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
21684
21720
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
21685
21721
|
limit: input.topK * 4
|
|
21686
21722
|
});
|
|
21723
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
21724
|
+
query: rerankQuery,
|
|
21725
|
+
currentStateQuery: query,
|
|
21726
|
+
limit: input.topK * 4
|
|
21727
|
+
}) : graphExpandedResults;
|
|
21687
21728
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
21688
21729
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
21689
21730
|
scope: input.scope,
|
|
@@ -21731,6 +21772,47 @@ var Retriever = class {
|
|
|
21731
21772
|
}
|
|
21732
21773
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
21733
21774
|
}
|
|
21775
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
21776
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
21777
|
+
const queryTokens = this.tokenize(opts.query);
|
|
21778
|
+
if (queryTokens.length === 0) return seeds;
|
|
21779
|
+
const byId = /* @__PURE__ */ new Map();
|
|
21780
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
21781
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
21782
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
21783
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
21784
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
21785
|
+
}
|
|
21786
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
21787
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
21788
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
21789
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
21790
|
+
if (byId.has(event.id)) continue;
|
|
21791
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
21792
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
21793
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
21794
|
+
if (lexicalScore <= 0) continue;
|
|
21795
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
21796
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
21797
|
+
const row = withRetrievalLane({
|
|
21798
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
21799
|
+
eventId: event.id,
|
|
21800
|
+
content: event.content,
|
|
21801
|
+
score,
|
|
21802
|
+
sessionId: event.sessionId,
|
|
21803
|
+
eventType: event.eventType,
|
|
21804
|
+
timestamp: event.timestamp.toISOString(),
|
|
21805
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
21806
|
+
lexicalScore,
|
|
21807
|
+
recencyScore: seed.recencyScore
|
|
21808
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
21809
|
+
byId.set(row.eventId, row);
|
|
21810
|
+
if (byId.size >= opts.limit) break;
|
|
21811
|
+
}
|
|
21812
|
+
if (byId.size >= opts.limit) break;
|
|
21813
|
+
}
|
|
21814
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
21815
|
+
}
|
|
21734
21816
|
async expandGraphHops(seeds, opts) {
|
|
21735
21817
|
const byId = /* @__PURE__ */ new Map();
|
|
21736
21818
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -27858,11 +27940,13 @@ var SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING = "Warning: semantic/vector retrieva
|
|
|
27858
27940
|
async function retrieveMcpMemories(memoryService, query, options) {
|
|
27859
27941
|
const fetchTopK = Math.max(options.topK, options.fetchTopK ?? options.topK);
|
|
27860
27942
|
try {
|
|
27861
|
-
const
|
|
27943
|
+
const retrieveOptions = {
|
|
27862
27944
|
topK: fetchTopK,
|
|
27863
27945
|
sessionId: options.sessionId,
|
|
27864
|
-
recordTrace: false
|
|
27865
|
-
|
|
27946
|
+
recordTrace: false,
|
|
27947
|
+
...options.retrievalMode ? { retrievalMode: options.retrievalMode } : {}
|
|
27948
|
+
};
|
|
27949
|
+
const result2 = await memoryService.retrieveMemories(query, retrieveOptions);
|
|
27866
27950
|
return { memories: selectMcpMemoryResults(result2.memories, options.topK, options.eventType) };
|
|
27867
27951
|
} catch (error) {
|
|
27868
27952
|
if (!isVectorSchemaMismatchError(error)) {
|
|
@@ -27990,6 +28074,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
27990
28074
|
const projectPath = optionalString(args.projectPath);
|
|
27991
28075
|
const maxContextChars = contextPackMaxCharsArg(args);
|
|
27992
28076
|
const compression = contextCompressionModeArg(args.compression, maxContextChars !== void 0);
|
|
28077
|
+
const retrievalMode = contextPackRetrievalModeArg(args.retrievalMode);
|
|
27993
28078
|
const compressionTelemetry = [];
|
|
27994
28079
|
const formatOptions = {
|
|
27995
28080
|
compression,
|
|
@@ -28010,7 +28095,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
28010
28095
|
sessionsDir: optionalString(args.sessionsDir),
|
|
28011
28096
|
stateDb: optionalString(args.stateDb)
|
|
28012
28097
|
}) : void 0;
|
|
28013
|
-
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId });
|
|
28098
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId, retrievalMode });
|
|
28014
28099
|
const recentEvents = await memoryService.getRecentEvents(recentLimit);
|
|
28015
28100
|
const timelineEvents = selectContextPackTimelineEvents(
|
|
28016
28101
|
recentEvents,
|
|
@@ -28234,7 +28319,10 @@ async function handleMemSourceRef(memoryService, args) {
|
|
|
28234
28319
|
const ids = Array.isArray(args.ids) ? args.ids.map((id) => String(id)).filter((id) => id.trim().length > 0) : [];
|
|
28235
28320
|
const maxContentChars = numberArg(args.maxContentChars, 500, 80, 2e3);
|
|
28236
28321
|
const lookupLimit = numberArg(args.lookupLimit, 1e4, 1, 5e4);
|
|
28322
|
+
const includeNeighbors = args.includeNeighbors === true;
|
|
28323
|
+
const neighborWindow = includeNeighbors ? numberArg(args.neighborWindow, 1, 0, 5) : 0;
|
|
28237
28324
|
const recentEvents = await memoryService.getRecentEvents(lookupLimit);
|
|
28325
|
+
const sessionEventCache = /* @__PURE__ */ new Map();
|
|
28238
28326
|
const lines = ["## Source References", ""];
|
|
28239
28327
|
if (ids.length === 0) {
|
|
28240
28328
|
lines.push("No IDs supplied.", "");
|
|
@@ -28262,6 +28350,19 @@ async function handleMemSourceRef(memoryService, args) {
|
|
|
28262
28350
|
}
|
|
28263
28351
|
lines.push("- Redacted Preview:");
|
|
28264
28352
|
lines.push(` > ${safeInline(event.content, maxContentChars)}`);
|
|
28353
|
+
if (neighborWindow > 0) {
|
|
28354
|
+
try {
|
|
28355
|
+
let sessionEventsPromise = sessionEventCache.get(event.sessionId);
|
|
28356
|
+
if (!sessionEventsPromise) {
|
|
28357
|
+
sessionEventsPromise = memoryService.getSessionHistory(event.sessionId);
|
|
28358
|
+
sessionEventCache.set(event.sessionId, sessionEventsPromise);
|
|
28359
|
+
}
|
|
28360
|
+
const sessionEvents = await sessionEventsPromise;
|
|
28361
|
+
appendSourceRefNeighborContext(lines, event, sessionEvents, neighborWindow, maxContentChars);
|
|
28362
|
+
} catch {
|
|
28363
|
+
lines.push("- Neighbor Context: unavailable (details suppressed)");
|
|
28364
|
+
}
|
|
28365
|
+
}
|
|
28265
28366
|
lines.push("");
|
|
28266
28367
|
}
|
|
28267
28368
|
return textResult(lines.join("\n"));
|
|
@@ -28491,6 +28592,31 @@ function sourceTypeForEvent(event) {
|
|
|
28491
28592
|
if (typeof event.metadata?.transcriptPath === "string") return "transcript";
|
|
28492
28593
|
return "raw_event";
|
|
28493
28594
|
}
|
|
28595
|
+
function appendSourceRefNeighborContext(lines, event, sessionEvents, neighborWindow, maxContentChars) {
|
|
28596
|
+
const sorted = sessionEvents.slice().sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime() || a.id.localeCompare(b.id));
|
|
28597
|
+
const targetIndex = sorted.findIndex((candidate) => candidate.id === event.id);
|
|
28598
|
+
lines.push("- Neighbor Context:");
|
|
28599
|
+
if (targetIndex < 0) {
|
|
28600
|
+
lines.push(" - unavailable (source event not found in session history)");
|
|
28601
|
+
return;
|
|
28602
|
+
}
|
|
28603
|
+
const start = Math.max(0, targetIndex - neighborWindow);
|
|
28604
|
+
const end = Math.min(sorted.length, targetIndex + neighborWindow + 1);
|
|
28605
|
+
const neighbors = sorted.map((candidate, index) => ({ event: candidate, index })).slice(start, end).filter((candidate) => candidate.event.id !== event.id);
|
|
28606
|
+
if (neighbors.length === 0) {
|
|
28607
|
+
lines.push(" - none within requested window");
|
|
28608
|
+
return;
|
|
28609
|
+
}
|
|
28610
|
+
const previewChars = Math.max(80, Math.min(maxContentChars, 300));
|
|
28611
|
+
for (const neighbor of neighbors) {
|
|
28612
|
+
const direction = neighbor.index < targetIndex ? "before" : "after";
|
|
28613
|
+
const citationId = generateCitationId(neighbor.event.id);
|
|
28614
|
+
lines.push(
|
|
28615
|
+
` - ${direction} [mem:${citationId}] sourceRef=event:${neighbor.event.id} type=${neighbor.event.eventType} timestamp=${neighbor.event.timestamp.toISOString()}`
|
|
28616
|
+
);
|
|
28617
|
+
lines.push(` > ${safeInline(neighbor.event.content, previewChars)}`);
|
|
28618
|
+
}
|
|
28619
|
+
}
|
|
28494
28620
|
function formatRelevantMemoryLine(event, score, index, formatOptions = DEFAULT_CONTEXT_PACK_FORMAT_OPTIONS) {
|
|
28495
28621
|
const citationId = generateCitationId(event.id);
|
|
28496
28622
|
const previewBudget = formatOptions.compression === "off" ? 260 : 180;
|
|
@@ -28614,9 +28740,17 @@ function contextCompressionModeArg(value, hasBudget) {
|
|
|
28614
28740
|
if (normalized === "off" || normalized === "safe" || normalized === "aggressive") return normalized;
|
|
28615
28741
|
throw new Error("Invalid compression: expected off, safe, or aggressive");
|
|
28616
28742
|
}
|
|
28743
|
+
function contextPackRetrievalModeArg(value) {
|
|
28744
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
28745
|
+
const normalized = String(value).trim().toLowerCase();
|
|
28746
|
+
if (normalized === "session-event-hybrid") return "session-event-hybrid";
|
|
28747
|
+
if (normalized === "event") return "event";
|
|
28748
|
+
throw new Error("Invalid retrievalMode: expected session-event-hybrid or event");
|
|
28749
|
+
}
|
|
28617
28750
|
function validateMemContextPackBudgetArgs(args) {
|
|
28618
28751
|
const maxContextChars = contextPackMaxCharsArg(args);
|
|
28619
28752
|
contextCompressionModeArg(args.compression, maxContextChars !== void 0);
|
|
28753
|
+
contextPackRetrievalModeArg(args.retrievalMode);
|
|
28620
28754
|
}
|
|
28621
28755
|
function contextPackMaxCharsArg(args) {
|
|
28622
28756
|
const maxChars = optionalNumberArg(args.maxChars, "maxChars", CONTEXT_PACK_MIN_HARD_BUDGET_CHARS, CONTEXT_PACK_MAX_CHARS_CAP);
|