claude-memory-layer 1.0.46 → 1.0.47

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/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: filteredSummary,
21593
- candidateResults: filteredSummary,
21594
- matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
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 expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
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 result2 = await memoryService.retrieveMemories(query, {
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);