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
|
@@ -2569,6 +2569,7 @@ var VectorOutbox = class {
|
|
|
2569
2569
|
// src/core/retrieval-debug-lanes.ts
|
|
2570
2570
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
2571
2571
|
"raw_event",
|
|
2572
|
+
"session_event",
|
|
2572
2573
|
"session_summary",
|
|
2573
2574
|
"graph_path",
|
|
2574
2575
|
"facet_match"
|
|
@@ -8428,6 +8429,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
8428
8429
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
8429
8430
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
8430
8431
|
];
|
|
8432
|
+
var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
8433
|
+
/<environment_context\b[\s\S]*<\/environment_context>/i,
|
|
8434
|
+
/<turn_aborted>/i,
|
|
8435
|
+
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
8436
|
+
/^\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,
|
|
8437
|
+
/^\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,
|
|
8438
|
+
/^\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,
|
|
8439
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
8440
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
8441
|
+
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
8442
|
+
/^\$\s+\S+/i
|
|
8443
|
+
];
|
|
8431
8444
|
var CONTINUATION_QUERY_PATTERNS = [
|
|
8432
8445
|
/^\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,
|
|
8433
8446
|
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
@@ -8439,7 +8452,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
8439
8452
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
8440
8453
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
8441
8454
|
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
8442
|
-
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|
|
|
8455
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
8443
8456
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
8444
8457
|
];
|
|
8445
8458
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -8585,6 +8598,21 @@ function isCommandArtifactQuery(query) {
|
|
|
8585
8598
|
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8586
8599
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8587
8600
|
}
|
|
8601
|
+
function isCommandArtifactContent(content) {
|
|
8602
|
+
const trimmed = content.trim();
|
|
8603
|
+
if (!trimmed) return false;
|
|
8604
|
+
const normalized = trimmed.toLowerCase();
|
|
8605
|
+
if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
|
|
8606
|
+
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8607
|
+
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8608
|
+
}
|
|
8609
|
+
function isLowSignalContextContent(content) {
|
|
8610
|
+
const trimmed = content.trim();
|
|
8611
|
+
if (!trimmed) return true;
|
|
8612
|
+
if (isCommandArtifactContent(trimmed)) return true;
|
|
8613
|
+
if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
8614
|
+
return false;
|
|
8615
|
+
}
|
|
8588
8616
|
function isGenericContinuationQuery(query) {
|
|
8589
8617
|
const trimmed = query.trim();
|
|
8590
8618
|
if (!trimmed) return false;
|
|
@@ -8602,6 +8630,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
8602
8630
|
if (tokens.length > 8) return false;
|
|
8603
8631
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8604
8632
|
}
|
|
8633
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
8634
|
+
const trimmed = query.trim();
|
|
8635
|
+
if (!trimmed) return false;
|
|
8636
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
8637
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
8638
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
8639
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
8640
|
+
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"));
|
|
8641
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
8642
|
+
}
|
|
8605
8643
|
function isCurrentStateQuery(query) {
|
|
8606
8644
|
const trimmed = query.trim();
|
|
8607
8645
|
if (!trimmed) return false;
|
|
@@ -8775,6 +8813,7 @@ var Retriever = class {
|
|
|
8775
8813
|
}
|
|
8776
8814
|
async retrieve(query, options = {}) {
|
|
8777
8815
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
8816
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
8778
8817
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
8779
8818
|
const fallbackTrace = [];
|
|
8780
8819
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -8805,6 +8844,7 @@ var Retriever = class {
|
|
|
8805
8844
|
decayPolicy: opts.decayPolicy,
|
|
8806
8845
|
intentRewrite: opts.intentRewrite === true,
|
|
8807
8846
|
graphHop: opts.graphHop,
|
|
8847
|
+
retrievalMode,
|
|
8808
8848
|
projectScopeMode: opts.projectScopeMode,
|
|
8809
8849
|
projectHash: opts.projectHash,
|
|
8810
8850
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8823,6 +8863,7 @@ var Retriever = class {
|
|
|
8823
8863
|
rerankWeights: opts.rerankWeights,
|
|
8824
8864
|
decayPolicy: opts.decayPolicy,
|
|
8825
8865
|
graphHop: opts.graphHop,
|
|
8866
|
+
retrievalMode,
|
|
8826
8867
|
projectScopeMode: opts.projectScopeMode,
|
|
8827
8868
|
projectHash: opts.projectHash,
|
|
8828
8869
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8842,6 +8883,7 @@ var Retriever = class {
|
|
|
8842
8883
|
rerankWeights: opts.rerankWeights,
|
|
8843
8884
|
decayPolicy: opts.decayPolicy,
|
|
8844
8885
|
graphHop: opts.graphHop,
|
|
8886
|
+
retrievalMode,
|
|
8845
8887
|
projectScopeMode: opts.projectScopeMode,
|
|
8846
8888
|
projectHash: opts.projectHash,
|
|
8847
8889
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8862,14 +8904,37 @@ var Retriever = class {
|
|
|
8862
8904
|
query,
|
|
8863
8905
|
minScore: opts.minScore
|
|
8864
8906
|
});
|
|
8907
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
8908
|
+
query: qualityQuery,
|
|
8909
|
+
currentStateQuery: query,
|
|
8910
|
+
limit: opts.topK * 4
|
|
8911
|
+
}) : filteredSummary;
|
|
8912
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
8913
|
+
scope: opts.scope,
|
|
8914
|
+
projectScopeMode: opts.projectScopeMode,
|
|
8915
|
+
projectHash: opts.projectHash,
|
|
8916
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
8917
|
+
facets: opts.facets
|
|
8918
|
+
}) : expandedSummary;
|
|
8919
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
8920
|
+
query,
|
|
8921
|
+
minScore: opts.minScore
|
|
8922
|
+
}) : scopedExpandedSummary;
|
|
8865
8923
|
current = {
|
|
8866
|
-
results:
|
|
8867
|
-
candidateResults:
|
|
8868
|
-
matchResult: this.matcher.matchSearchResults(
|
|
8924
|
+
results: finalSummary,
|
|
8925
|
+
candidateResults: finalSummary,
|
|
8926
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
8869
8927
|
};
|
|
8870
8928
|
fallbackTrace.push("fallback:summary");
|
|
8871
8929
|
}
|
|
8872
|
-
const
|
|
8930
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
|
|
8931
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
8932
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
8933
|
+
return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
|
|
8934
|
+
}
|
|
8935
|
+
return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
|
|
8936
|
+
});
|
|
8937
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
8873
8938
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
8874
8939
|
return {
|
|
8875
8940
|
memories,
|
|
@@ -8877,7 +8942,7 @@ var Retriever = class {
|
|
|
8877
8942
|
totalTokens: this.estimateTokens(context),
|
|
8878
8943
|
context,
|
|
8879
8944
|
fallbackTrace,
|
|
8880
|
-
selectedDebug:
|
|
8945
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
8881
8946
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
8882
8947
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
8883
8948
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -8944,13 +9009,18 @@ var Retriever = class {
|
|
|
8944
9009
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
8945
9010
|
}
|
|
8946
9011
|
}
|
|
8947
|
-
const
|
|
9012
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
8948
9013
|
query,
|
|
8949
9014
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
8950
9015
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
8951
9016
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
8952
9017
|
limit: input.topK * 4
|
|
8953
9018
|
});
|
|
9019
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
9020
|
+
query: rerankQuery,
|
|
9021
|
+
currentStateQuery: query,
|
|
9022
|
+
limit: input.topK * 4
|
|
9023
|
+
}) : graphExpandedResults;
|
|
8954
9024
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
8955
9025
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
8956
9026
|
scope: input.scope,
|
|
@@ -8972,6 +9042,7 @@ var Retriever = class {
|
|
|
8972
9042
|
if (isCurrentStateQuery(options.query)) {
|
|
8973
9043
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
8974
9044
|
}
|
|
9045
|
+
filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
|
|
8975
9046
|
filtered = filtered.filter(
|
|
8976
9047
|
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
8977
9048
|
);
|
|
@@ -8997,6 +9068,47 @@ var Retriever = class {
|
|
|
8997
9068
|
}
|
|
8998
9069
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
8999
9070
|
}
|
|
9071
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
9072
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
9073
|
+
const queryTokens = this.tokenize(opts.query);
|
|
9074
|
+
if (queryTokens.length === 0) return seeds;
|
|
9075
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9076
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
9077
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
9078
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
9079
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
9080
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
9081
|
+
}
|
|
9082
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
9083
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
9084
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9085
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
9086
|
+
if (byId.has(event.id)) continue;
|
|
9087
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
9088
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
9089
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
9090
|
+
if (lexicalScore <= 0) continue;
|
|
9091
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
9092
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
9093
|
+
const row = withRetrievalLane({
|
|
9094
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
9095
|
+
eventId: event.id,
|
|
9096
|
+
content: event.content,
|
|
9097
|
+
score,
|
|
9098
|
+
sessionId: event.sessionId,
|
|
9099
|
+
eventType: event.eventType,
|
|
9100
|
+
timestamp: event.timestamp.toISOString(),
|
|
9101
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
9102
|
+
lexicalScore,
|
|
9103
|
+
recencyScore: seed.recencyScore
|
|
9104
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
9105
|
+
byId.set(row.eventId, row);
|
|
9106
|
+
if (byId.size >= opts.limit) break;
|
|
9107
|
+
}
|
|
9108
|
+
if (byId.size >= opts.limit) break;
|
|
9109
|
+
}
|
|
9110
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
9111
|
+
}
|
|
9000
9112
|
async expandGraphHops(seeds, opts) {
|
|
9001
9113
|
const byId = /* @__PURE__ */ new Map();
|
|
9002
9114
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -9312,7 +9424,7 @@ var Retriever = class {
|
|
|
9312
9424
|
async retrieveRecent(limit = 100) {
|
|
9313
9425
|
return this.eventStore.getRecentEvents(limit);
|
|
9314
9426
|
}
|
|
9315
|
-
async enrichResults(results, options) {
|
|
9427
|
+
async enrichResults(results, options, query) {
|
|
9316
9428
|
const memories = [];
|
|
9317
9429
|
for (const result of results) {
|
|
9318
9430
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
@@ -9322,13 +9434,13 @@ var Retriever = class {
|
|
|
9322
9434
|
}
|
|
9323
9435
|
let sessionContext;
|
|
9324
9436
|
if (options.includeSessionContext) {
|
|
9325
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
9437
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
9326
9438
|
}
|
|
9327
9439
|
memories.push({ event, score: result.score, sessionContext });
|
|
9328
9440
|
}
|
|
9329
9441
|
return memories;
|
|
9330
9442
|
}
|
|
9331
|
-
async getSessionContext(sessionId, eventId) {
|
|
9443
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
9332
9444
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9333
9445
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
9334
9446
|
if (eventIndex === -1) return void 0;
|
|
@@ -9336,7 +9448,9 @@ var Retriever = class {
|
|
|
9336
9448
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
9337
9449
|
const contextEvents = sessionEvents.slice(start, end);
|
|
9338
9450
|
if (contextEvents.length <= 1) return void 0;
|
|
9339
|
-
|
|
9451
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
9452
|
+
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)}...`);
|
|
9453
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
9340
9454
|
}
|
|
9341
9455
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
9342
9456
|
let context = projectResult.context;
|