claude-memory-layer 1.0.45 → 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.
@@ -2584,6 +2584,7 @@ var VectorOutbox = class {
2584
2584
  // src/core/retrieval-debug-lanes.ts
2585
2585
  var RETRIEVAL_DEBUG_LANE_NAMES = [
2586
2586
  "raw_event",
2587
+ "session_event",
2587
2588
  "session_summary",
2588
2589
  "graph_path",
2589
2590
  "facet_match"
@@ -8460,6 +8461,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
8460
8461
  /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
8461
8462
  /<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
8462
8463
  ];
8464
+ var LOW_SIGNAL_CONTEXT_PATTERNS = [
8465
+ /<environment_context\b[\s\S]*<\/environment_context>/i,
8466
+ /<turn_aborted>/i,
8467
+ /^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
8468
+ /^\s*(?:understood[,\s.]*)?(?:stopping|stopped|pausing|paused)\s+here\b[\s\S]{0,180}\blet\s+me\s+know\s+when\s+you(?:'d|\s+would)?\s+like\s+to\s+continue\b/i,
8469
+ /^\s*\[?CONTEXT\s+COMPACTION\s*[—-]\s*REFERENCE\s+ONLY\]?\b[\s\S]{0,600}\b(?:earlier\s+turns\s+were\s+compacted|handoff\s+from\s+a\s+previous\s+context\s+window|active\s+task)\b/i,
8470
+ /^\s*Summary\s+generation\s+was\s+unavailable\.\s*\d+\s+message\(s\)\s+were\s+removed\s+to\s+free\s+context\s+space\b/i,
8471
+ /^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
8472
+ /^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
8473
+ /^➜\s+\S+\s+git:\([^)]*\)\s+/i,
8474
+ /^\$\s+\S+/i
8475
+ ];
8463
8476
  var CONTINUATION_QUERY_PATTERNS = [
8464
8477
  /^\s*(?:continue|resume|next|what(?:'s| is)? next|next\s+(?:step|task|action)|recommended\s+(?:next\s+)?(?:step|task|action)|what should (?:we|i) do next)\??\s*$/i,
8465
8478
  /^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
@@ -8471,7 +8484,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
8471
8484
  var CURRENT_STATE_QUERY_PATTERNS = [
8472
8485
  /\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
8473
8486
  /\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
8474
- /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|state|status)\b/i,
8487
+ /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
8475
8488
  /(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
8476
8489
  ];
8477
8490
  var STALE_CONTENT_PATTERNS = [
@@ -8617,6 +8630,21 @@ function isCommandArtifactQuery(query) {
8617
8630
  if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
8618
8631
  return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
8619
8632
  }
8633
+ function isCommandArtifactContent(content) {
8634
+ const trimmed = content.trim();
8635
+ if (!trimmed) return false;
8636
+ const normalized = trimmed.toLowerCase();
8637
+ if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
8638
+ if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
8639
+ return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
8640
+ }
8641
+ function isLowSignalContextContent(content) {
8642
+ const trimmed = content.trim();
8643
+ if (!trimmed) return true;
8644
+ if (isCommandArtifactContent(trimmed)) return true;
8645
+ if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
8646
+ return false;
8647
+ }
8620
8648
  function isGenericContinuationQuery(query) {
8621
8649
  const trimmed = query.trim();
8622
8650
  if (!trimmed) return false;
@@ -8634,6 +8662,16 @@ function isShortRepairFollowUpQuery(query) {
8634
8662
  if (tokens.length > 8) return false;
8635
8663
  return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
8636
8664
  }
8665
+ function isLowConfidenceContextFallbackQuery(query) {
8666
+ const trimmed = query.trim();
8667
+ if (!trimmed) return false;
8668
+ if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
8669
+ const terms = new Set(tokenizeQualityText(trimmed));
8670
+ if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
8671
+ const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
8672
+ const hasValidationGateRecall = terms.has("validation") && (terms.has("gate") || terms.has("check")) && (terms.has("run") || terms.has("before") || terms.has("commit") || terms.has("committing") || terms.has("change"));
8673
+ return hasContinuationRecall || hasValidationGateRecall;
8674
+ }
8637
8675
  function isCurrentStateQuery(query) {
8638
8676
  const trimmed = query.trim();
8639
8677
  if (!trimmed) return false;
@@ -8807,6 +8845,7 @@ var Retriever = class {
8807
8845
  }
8808
8846
  async retrieve(query, options = {}) {
8809
8847
  const opts = { ...DEFAULT_OPTIONS, ...options };
8848
+ const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
8810
8849
  const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
8811
8850
  const fallbackTrace = [];
8812
8851
  const qualityQuery = buildRetrievalQualityQuery(query);
@@ -8837,6 +8876,7 @@ var Retriever = class {
8837
8876
  decayPolicy: opts.decayPolicy,
8838
8877
  intentRewrite: opts.intentRewrite === true,
8839
8878
  graphHop: opts.graphHop,
8879
+ retrievalMode,
8840
8880
  projectScopeMode: opts.projectScopeMode,
8841
8881
  projectHash: opts.projectHash,
8842
8882
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8855,6 +8895,7 @@ var Retriever = class {
8855
8895
  rerankWeights: opts.rerankWeights,
8856
8896
  decayPolicy: opts.decayPolicy,
8857
8897
  graphHop: opts.graphHop,
8898
+ retrievalMode,
8858
8899
  projectScopeMode: opts.projectScopeMode,
8859
8900
  projectHash: opts.projectHash,
8860
8901
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8874,6 +8915,7 @@ var Retriever = class {
8874
8915
  rerankWeights: opts.rerankWeights,
8875
8916
  decayPolicy: opts.decayPolicy,
8876
8917
  graphHop: opts.graphHop,
8918
+ retrievalMode,
8877
8919
  projectScopeMode: opts.projectScopeMode,
8878
8920
  projectHash: opts.projectHash,
8879
8921
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8894,14 +8936,37 @@ var Retriever = class {
8894
8936
  query,
8895
8937
  minScore: opts.minScore
8896
8938
  });
8939
+ const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
8940
+ query: qualityQuery,
8941
+ currentStateQuery: query,
8942
+ limit: opts.topK * 4
8943
+ }) : filteredSummary;
8944
+ const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
8945
+ scope: opts.scope,
8946
+ projectScopeMode: opts.projectScopeMode,
8947
+ projectHash: opts.projectHash,
8948
+ allowedProjectHashes: opts.allowedProjectHashes,
8949
+ facets: opts.facets
8950
+ }) : expandedSummary;
8951
+ const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
8952
+ query,
8953
+ minScore: opts.minScore
8954
+ }) : scopedExpandedSummary;
8897
8955
  current = {
8898
- results: filteredSummary,
8899
- candidateResults: filteredSummary,
8900
- matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
8956
+ results: finalSummary,
8957
+ candidateResults: finalSummary,
8958
+ matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
8901
8959
  };
8902
8960
  fallbackTrace.push("fallback:summary");
8903
8961
  }
8904
- const memories = await this.enrichResults(current.results.slice(0, opts.topK), opts);
8962
+ const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
8963
+ if (current.matchResult.confidence !== "none") return true;
8964
+ if (isLowConfidenceContextFallbackQuery(query)) {
8965
+ return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
8966
+ }
8967
+ return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
8968
+ });
8969
+ const memories = await this.enrichResults(selectedResults, opts, query);
8905
8970
  const context = this.buildContext(memories, opts.maxTokens);
8906
8971
  return {
8907
8972
  memories,
@@ -8909,7 +8974,7 @@ var Retriever = class {
8909
8974
  totalTokens: this.estimateTokens(context),
8910
8975
  context,
8911
8976
  fallbackTrace,
8912
- selectedDebug: current.results.slice(0, opts.topK).map((r) => this.debugDetailForResult(r)),
8977
+ selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
8913
8978
  candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
8914
8979
  rawQueryText: current.queryRewriteKind ? query : void 0,
8915
8980
  effectiveQueryText: current.effectiveQueryText,
@@ -8976,13 +9041,18 @@ var Retriever = class {
8976
9041
  initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
8977
9042
  }
8978
9043
  }
8979
- const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
9044
+ const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
8980
9045
  query,
8981
9046
  queryGraphEnabled: this.queryGraphExpansionEnabled,
8982
9047
  maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
8983
9048
  hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
8984
9049
  limit: input.topK * 4
8985
9050
  });
9051
+ const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
9052
+ query: rerankQuery,
9053
+ currentStateQuery: query,
9054
+ limit: input.topK * 4
9055
+ }) : graphExpandedResults;
8986
9056
  const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
8987
9057
  const filtered = await this.applyScopeFilters(rerankedResults, {
8988
9058
  scope: input.scope,
@@ -9004,6 +9074,7 @@ var Retriever = class {
9004
9074
  if (isCurrentStateQuery(options.query)) {
9005
9075
  filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
9006
9076
  }
9077
+ filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
9007
9078
  filtered = filtered.filter(
9008
9079
  (result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
9009
9080
  );
@@ -9029,6 +9100,47 @@ var Retriever = class {
9029
9100
  }
9030
9101
  return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
9031
9102
  }
9103
+ async expandSessionEventHybrid(seeds, opts) {
9104
+ if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
9105
+ const queryTokens = this.tokenize(opts.query);
9106
+ if (queryTokens.length === 0) return seeds;
9107
+ const byId = /* @__PURE__ */ new Map();
9108
+ for (const seed of seeds) byId.set(seed.eventId, seed);
9109
+ const bestSeedBySession = /* @__PURE__ */ new Map();
9110
+ for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
9111
+ if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
9112
+ bestSeedBySession.set(seed.sessionId, seed);
9113
+ }
9114
+ const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
9115
+ for (const [sessionId, seed] of bestSeedBySession) {
9116
+ const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
9117
+ for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
9118
+ if (byId.has(event.id)) continue;
9119
+ if (isLowSignalContextContent(event.content)) continue;
9120
+ if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
9121
+ const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
9122
+ if (lexicalScore <= 0) continue;
9123
+ if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
9124
+ const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
9125
+ const row = withRetrievalLane({
9126
+ id: `session-event-${seed.eventId}-${event.id}`,
9127
+ eventId: event.id,
9128
+ content: event.content,
9129
+ score,
9130
+ sessionId: event.sessionId,
9131
+ eventType: event.eventType,
9132
+ timestamp: event.timestamp.toISOString(),
9133
+ semanticScore: seed.semanticScore ?? seed.score,
9134
+ lexicalScore,
9135
+ recencyScore: seed.recencyScore
9136
+ }, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
9137
+ byId.set(row.eventId, row);
9138
+ if (byId.size >= opts.limit) break;
9139
+ }
9140
+ if (byId.size >= opts.limit) break;
9141
+ }
9142
+ return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
9143
+ }
9032
9144
  async expandGraphHops(seeds, opts) {
9033
9145
  const byId = /* @__PURE__ */ new Map();
9034
9146
  for (const s of seeds) byId.set(s.eventId, s);
@@ -9344,7 +9456,7 @@ var Retriever = class {
9344
9456
  async retrieveRecent(limit = 100) {
9345
9457
  return this.eventStore.getRecentEvents(limit);
9346
9458
  }
9347
- async enrichResults(results, options) {
9459
+ async enrichResults(results, options, query) {
9348
9460
  const memories = [];
9349
9461
  for (const result of results) {
9350
9462
  const event = await this.eventStore.getEvent(result.eventId);
@@ -9354,13 +9466,13 @@ var Retriever = class {
9354
9466
  }
9355
9467
  let sessionContext;
9356
9468
  if (options.includeSessionContext) {
9357
- sessionContext = await this.getSessionContext(event.sessionId, event.id);
9469
+ sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
9358
9470
  }
9359
9471
  memories.push({ event, score: result.score, sessionContext });
9360
9472
  }
9361
9473
  return memories;
9362
9474
  }
9363
- async getSessionContext(sessionId, eventId) {
9475
+ async getSessionContext(sessionId, eventId, query) {
9364
9476
  const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
9365
9477
  const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
9366
9478
  if (eventIndex === -1) return void 0;
@@ -9368,7 +9480,9 @@ var Retriever = class {
9368
9480
  const end = Math.min(sessionEvents.length, eventIndex + 2);
9369
9481
  const contextEvents = sessionEvents.slice(start, end);
9370
9482
  if (contextEvents.length <= 1) return void 0;
9371
- return contextEvents.filter((e) => e.id !== eventId).map((e) => `[${e.eventType}]: ${e.content.slice(0, 200)}...`).join("\n");
9483
+ const suppressStaleState = isCurrentStateQuery(query);
9484
+ const contextLines = contextEvents.filter((e) => e.id !== eventId).filter((e) => !isLowSignalContextContent(e.content)).filter((e) => !(suppressStaleState && isStaleOrSupersededContent(e.content))).map((e) => `[${e.eventType}]: ${e.content.slice(0, 200)}...`);
9485
+ return contextLines.length > 0 ? contextLines.join("\n") : void 0;
9372
9486
  }
9373
9487
  buildUnifiedContext(projectResult, sharedMemories) {
9374
9488
  let context = projectResult.context;