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.
- package/README.md +4 -2
- package/dist/cli/index.js +126 -12
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +1209 -24
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +125 -11
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +125 -11
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +125 -11
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +125 -11
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +125 -11
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +125 -11
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +1215 -30
- package/dist/index.js.map +4 -4
- package/dist/mcp/index.js +305 -45
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +125 -11
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +125 -11
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +125 -11
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +3 -1
|
@@ -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|
|
|
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:
|
|
8860
|
-
candidateResults:
|
|
8861
|
-
matchResult: this.matcher.matchSearchResults(
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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;
|