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.
@@ -2562,6 +2562,7 @@ var VectorOutbox = class {
2562
2562
  // src/core/retrieval-debug-lanes.ts
2563
2563
  var RETRIEVAL_DEBUG_LANE_NAMES = [
2564
2564
  "raw_event",
2565
+ "session_event",
2565
2566
  "session_summary",
2566
2567
  "graph_path",
2567
2568
  "facet_match"
@@ -8421,6 +8422,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
8421
8422
  /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
8422
8423
  /<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
8423
8424
  ];
8425
+ var LOW_SIGNAL_CONTEXT_PATTERNS = [
8426
+ /<environment_context\b[\s\S]*<\/environment_context>/i,
8427
+ /<turn_aborted>/i,
8428
+ /^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
8429
+ /^\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,
8430
+ /^\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,
8431
+ /^\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,
8432
+ /^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
8433
+ /^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
8434
+ /^➜\s+\S+\s+git:\([^)]*\)\s+/i,
8435
+ /^\$\s+\S+/i
8436
+ ];
8424
8437
  var CONTINUATION_QUERY_PATTERNS = [
8425
8438
  /^\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,
8426
8439
  /^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
@@ -8432,7 +8445,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
8432
8445
  var CURRENT_STATE_QUERY_PATTERNS = [
8433
8446
  /\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
8434
8447
  /\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
8435
- /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|state|status)\b/i,
8448
+ /\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
8436
8449
  /(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
8437
8450
  ];
8438
8451
  var STALE_CONTENT_PATTERNS = [
@@ -8578,6 +8591,21 @@ function isCommandArtifactQuery(query) {
8578
8591
  if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
8579
8592
  return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
8580
8593
  }
8594
+ function isCommandArtifactContent(content) {
8595
+ const trimmed = content.trim();
8596
+ if (!trimmed) return false;
8597
+ const normalized = trimmed.toLowerCase();
8598
+ if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
8599
+ if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
8600
+ return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
8601
+ }
8602
+ function isLowSignalContextContent(content) {
8603
+ const trimmed = content.trim();
8604
+ if (!trimmed) return true;
8605
+ if (isCommandArtifactContent(trimmed)) return true;
8606
+ if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
8607
+ return false;
8608
+ }
8581
8609
  function isGenericContinuationQuery(query) {
8582
8610
  const trimmed = query.trim();
8583
8611
  if (!trimmed) return false;
@@ -8595,6 +8623,16 @@ function isShortRepairFollowUpQuery(query) {
8595
8623
  if (tokens.length > 8) return false;
8596
8624
  return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
8597
8625
  }
8626
+ function isLowConfidenceContextFallbackQuery(query) {
8627
+ const trimmed = query.trim();
8628
+ if (!trimmed) return false;
8629
+ if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
8630
+ const terms = new Set(tokenizeQualityText(trimmed));
8631
+ if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
8632
+ const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
8633
+ 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"));
8634
+ return hasContinuationRecall || hasValidationGateRecall;
8635
+ }
8598
8636
  function isCurrentStateQuery(query) {
8599
8637
  const trimmed = query.trim();
8600
8638
  if (!trimmed) return false;
@@ -8768,6 +8806,7 @@ var Retriever = class {
8768
8806
  }
8769
8807
  async retrieve(query, options = {}) {
8770
8808
  const opts = { ...DEFAULT_OPTIONS, ...options };
8809
+ const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
8771
8810
  const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
8772
8811
  const fallbackTrace = [];
8773
8812
  const qualityQuery = buildRetrievalQualityQuery(query);
@@ -8798,6 +8837,7 @@ var Retriever = class {
8798
8837
  decayPolicy: opts.decayPolicy,
8799
8838
  intentRewrite: opts.intentRewrite === true,
8800
8839
  graphHop: opts.graphHop,
8840
+ retrievalMode,
8801
8841
  projectScopeMode: opts.projectScopeMode,
8802
8842
  projectHash: opts.projectHash,
8803
8843
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8816,6 +8856,7 @@ var Retriever = class {
8816
8856
  rerankWeights: opts.rerankWeights,
8817
8857
  decayPolicy: opts.decayPolicy,
8818
8858
  graphHop: opts.graphHop,
8859
+ retrievalMode,
8819
8860
  projectScopeMode: opts.projectScopeMode,
8820
8861
  projectHash: opts.projectHash,
8821
8862
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8835,6 +8876,7 @@ var Retriever = class {
8835
8876
  rerankWeights: opts.rerankWeights,
8836
8877
  decayPolicy: opts.decayPolicy,
8837
8878
  graphHop: opts.graphHop,
8879
+ retrievalMode,
8838
8880
  projectScopeMode: opts.projectScopeMode,
8839
8881
  projectHash: opts.projectHash,
8840
8882
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -8855,14 +8897,37 @@ var Retriever = class {
8855
8897
  query,
8856
8898
  minScore: opts.minScore
8857
8899
  });
8900
+ const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
8901
+ query: qualityQuery,
8902
+ currentStateQuery: query,
8903
+ limit: opts.topK * 4
8904
+ }) : filteredSummary;
8905
+ const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
8906
+ scope: opts.scope,
8907
+ projectScopeMode: opts.projectScopeMode,
8908
+ projectHash: opts.projectHash,
8909
+ allowedProjectHashes: opts.allowedProjectHashes,
8910
+ facets: opts.facets
8911
+ }) : expandedSummary;
8912
+ const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
8913
+ query,
8914
+ minScore: opts.minScore
8915
+ }) : scopedExpandedSummary;
8858
8916
  current = {
8859
- results: filteredSummary,
8860
- candidateResults: filteredSummary,
8861
- matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
8917
+ results: finalSummary,
8918
+ candidateResults: finalSummary,
8919
+ matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
8862
8920
  };
8863
8921
  fallbackTrace.push("fallback:summary");
8864
8922
  }
8865
- const memories = await this.enrichResults(current.results.slice(0, opts.topK), opts);
8923
+ const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
8924
+ if (current.matchResult.confidence !== "none") return true;
8925
+ if (isLowConfidenceContextFallbackQuery(query)) {
8926
+ return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
8927
+ }
8928
+ return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
8929
+ });
8930
+ const memories = await this.enrichResults(selectedResults, opts, query);
8866
8931
  const context = this.buildContext(memories, opts.maxTokens);
8867
8932
  return {
8868
8933
  memories,
@@ -8870,7 +8935,7 @@ var Retriever = class {
8870
8935
  totalTokens: this.estimateTokens(context),
8871
8936
  context,
8872
8937
  fallbackTrace,
8873
- selectedDebug: current.results.slice(0, opts.topK).map((r) => this.debugDetailForResult(r)),
8938
+ selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
8874
8939
  candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
8875
8940
  rawQueryText: current.queryRewriteKind ? query : void 0,
8876
8941
  effectiveQueryText: current.effectiveQueryText,
@@ -8937,13 +9002,18 @@ var Retriever = class {
8937
9002
  initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
8938
9003
  }
8939
9004
  }
8940
- const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
9005
+ const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
8941
9006
  query,
8942
9007
  queryGraphEnabled: this.queryGraphExpansionEnabled,
8943
9008
  maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
8944
9009
  hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
8945
9010
  limit: input.topK * 4
8946
9011
  });
9012
+ const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
9013
+ query: rerankQuery,
9014
+ currentStateQuery: query,
9015
+ limit: input.topK * 4
9016
+ }) : graphExpandedResults;
8947
9017
  const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
8948
9018
  const filtered = await this.applyScopeFilters(rerankedResults, {
8949
9019
  scope: input.scope,
@@ -8965,6 +9035,7 @@ var Retriever = class {
8965
9035
  if (isCurrentStateQuery(options.query)) {
8966
9036
  filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
8967
9037
  }
9038
+ filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
8968
9039
  filtered = filtered.filter(
8969
9040
  (result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
8970
9041
  );
@@ -8990,6 +9061,47 @@ var Retriever = class {
8990
9061
  }
8991
9062
  return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
8992
9063
  }
9064
+ async expandSessionEventHybrid(seeds, opts) {
9065
+ if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
9066
+ const queryTokens = this.tokenize(opts.query);
9067
+ if (queryTokens.length === 0) return seeds;
9068
+ const byId = /* @__PURE__ */ new Map();
9069
+ for (const seed of seeds) byId.set(seed.eventId, seed);
9070
+ const bestSeedBySession = /* @__PURE__ */ new Map();
9071
+ for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
9072
+ if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
9073
+ bestSeedBySession.set(seed.sessionId, seed);
9074
+ }
9075
+ const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
9076
+ for (const [sessionId, seed] of bestSeedBySession) {
9077
+ const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
9078
+ for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
9079
+ if (byId.has(event.id)) continue;
9080
+ if (isLowSignalContextContent(event.content)) continue;
9081
+ if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
9082
+ const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
9083
+ if (lexicalScore <= 0) continue;
9084
+ if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
9085
+ const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
9086
+ const row = withRetrievalLane({
9087
+ id: `session-event-${seed.eventId}-${event.id}`,
9088
+ eventId: event.id,
9089
+ content: event.content,
9090
+ score,
9091
+ sessionId: event.sessionId,
9092
+ eventType: event.eventType,
9093
+ timestamp: event.timestamp.toISOString(),
9094
+ semanticScore: seed.semanticScore ?? seed.score,
9095
+ lexicalScore,
9096
+ recencyScore: seed.recencyScore
9097
+ }, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
9098
+ byId.set(row.eventId, row);
9099
+ if (byId.size >= opts.limit) break;
9100
+ }
9101
+ if (byId.size >= opts.limit) break;
9102
+ }
9103
+ return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
9104
+ }
8993
9105
  async expandGraphHops(seeds, opts) {
8994
9106
  const byId = /* @__PURE__ */ new Map();
8995
9107
  for (const s of seeds) byId.set(s.eventId, s);
@@ -9305,7 +9417,7 @@ var Retriever = class {
9305
9417
  async retrieveRecent(limit = 100) {
9306
9418
  return this.eventStore.getRecentEvents(limit);
9307
9419
  }
9308
- async enrichResults(results, options) {
9420
+ async enrichResults(results, options, query) {
9309
9421
  const memories = [];
9310
9422
  for (const result of results) {
9311
9423
  const event = await this.eventStore.getEvent(result.eventId);
@@ -9315,13 +9427,13 @@ var Retriever = class {
9315
9427
  }
9316
9428
  let sessionContext;
9317
9429
  if (options.includeSessionContext) {
9318
- sessionContext = await this.getSessionContext(event.sessionId, event.id);
9430
+ sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
9319
9431
  }
9320
9432
  memories.push({ event, score: result.score, sessionContext });
9321
9433
  }
9322
9434
  return memories;
9323
9435
  }
9324
- async getSessionContext(sessionId, eventId) {
9436
+ async getSessionContext(sessionId, eventId, query) {
9325
9437
  const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
9326
9438
  const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
9327
9439
  if (eventIndex === -1) return void 0;
@@ -9329,7 +9441,9 @@ var Retriever = class {
9329
9441
  const end = Math.min(sessionEvents.length, eventIndex + 2);
9330
9442
  const contextEvents = sessionEvents.slice(start, end);
9331
9443
  if (contextEvents.length <= 1) return void 0;
9332
- return contextEvents.filter((e) => e.id !== eventId).map((e) => `[${e.eventType}]: ${e.content.slice(0, 200)}...`).join("\n");
9444
+ const suppressStaleState = isCurrentStateQuery(query);
9445
+ 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)}...`);
9446
+ return contextLines.length > 0 ? contextLines.join("\n") : void 0;
9333
9447
  }
9334
9448
  buildUnifiedContext(projectResult, sharedMemories) {
9335
9449
  let context = projectResult.context;