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
package/dist/hooks/stop.js
CHANGED
|
@@ -2563,6 +2563,7 @@ var VectorOutbox = class {
|
|
|
2563
2563
|
// src/core/retrieval-debug-lanes.ts
|
|
2564
2564
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
2565
2565
|
"raw_event",
|
|
2566
|
+
"session_event",
|
|
2566
2567
|
"session_summary",
|
|
2567
2568
|
"graph_path",
|
|
2568
2569
|
"facet_match"
|
|
@@ -8602,6 +8603,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
8602
8603
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
8603
8604
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
8604
8605
|
];
|
|
8606
|
+
var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
8607
|
+
/<environment_context\b[\s\S]*<\/environment_context>/i,
|
|
8608
|
+
/<turn_aborted>/i,
|
|
8609
|
+
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
8610
|
+
/^\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,
|
|
8611
|
+
/^\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,
|
|
8612
|
+
/^\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,
|
|
8613
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
8614
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
8615
|
+
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
8616
|
+
/^\$\s+\S+/i
|
|
8617
|
+
];
|
|
8605
8618
|
var CONTINUATION_QUERY_PATTERNS = [
|
|
8606
8619
|
/^\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,
|
|
8607
8620
|
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
@@ -8613,7 +8626,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
8613
8626
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
8614
8627
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
8615
8628
|
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
8616
|
-
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|
|
|
8629
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
8617
8630
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
8618
8631
|
];
|
|
8619
8632
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -8759,6 +8772,21 @@ function isCommandArtifactQuery(query) {
|
|
|
8759
8772
|
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8760
8773
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8761
8774
|
}
|
|
8775
|
+
function isCommandArtifactContent(content) {
|
|
8776
|
+
const trimmed = content.trim();
|
|
8777
|
+
if (!trimmed) return false;
|
|
8778
|
+
const normalized = trimmed.toLowerCase();
|
|
8779
|
+
if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
|
|
8780
|
+
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8781
|
+
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8782
|
+
}
|
|
8783
|
+
function isLowSignalContextContent(content) {
|
|
8784
|
+
const trimmed = content.trim();
|
|
8785
|
+
if (!trimmed) return true;
|
|
8786
|
+
if (isCommandArtifactContent(trimmed)) return true;
|
|
8787
|
+
if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
8788
|
+
return false;
|
|
8789
|
+
}
|
|
8762
8790
|
function isGenericContinuationQuery(query) {
|
|
8763
8791
|
const trimmed = query.trim();
|
|
8764
8792
|
if (!trimmed) return false;
|
|
@@ -8776,6 +8804,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
8776
8804
|
if (tokens.length > 8) return false;
|
|
8777
8805
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8778
8806
|
}
|
|
8807
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
8808
|
+
const trimmed = query.trim();
|
|
8809
|
+
if (!trimmed) return false;
|
|
8810
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
8811
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
8812
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
8813
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
8814
|
+
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"));
|
|
8815
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
8816
|
+
}
|
|
8779
8817
|
function isCurrentStateQuery(query) {
|
|
8780
8818
|
const trimmed = query.trim();
|
|
8781
8819
|
if (!trimmed) return false;
|
|
@@ -8949,6 +8987,7 @@ var Retriever = class {
|
|
|
8949
8987
|
}
|
|
8950
8988
|
async retrieve(query, options = {}) {
|
|
8951
8989
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
8990
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
8952
8991
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
8953
8992
|
const fallbackTrace = [];
|
|
8954
8993
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -8979,6 +9018,7 @@ var Retriever = class {
|
|
|
8979
9018
|
decayPolicy: opts.decayPolicy,
|
|
8980
9019
|
intentRewrite: opts.intentRewrite === true,
|
|
8981
9020
|
graphHop: opts.graphHop,
|
|
9021
|
+
retrievalMode,
|
|
8982
9022
|
projectScopeMode: opts.projectScopeMode,
|
|
8983
9023
|
projectHash: opts.projectHash,
|
|
8984
9024
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8997,6 +9037,7 @@ var Retriever = class {
|
|
|
8997
9037
|
rerankWeights: opts.rerankWeights,
|
|
8998
9038
|
decayPolicy: opts.decayPolicy,
|
|
8999
9039
|
graphHop: opts.graphHop,
|
|
9040
|
+
retrievalMode,
|
|
9000
9041
|
projectScopeMode: opts.projectScopeMode,
|
|
9001
9042
|
projectHash: opts.projectHash,
|
|
9002
9043
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -9016,6 +9057,7 @@ var Retriever = class {
|
|
|
9016
9057
|
rerankWeights: opts.rerankWeights,
|
|
9017
9058
|
decayPolicy: opts.decayPolicy,
|
|
9018
9059
|
graphHop: opts.graphHop,
|
|
9060
|
+
retrievalMode,
|
|
9019
9061
|
projectScopeMode: opts.projectScopeMode,
|
|
9020
9062
|
projectHash: opts.projectHash,
|
|
9021
9063
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -9036,14 +9078,37 @@ var Retriever = class {
|
|
|
9036
9078
|
query,
|
|
9037
9079
|
minScore: opts.minScore
|
|
9038
9080
|
});
|
|
9081
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
9082
|
+
query: qualityQuery,
|
|
9083
|
+
currentStateQuery: query,
|
|
9084
|
+
limit: opts.topK * 4
|
|
9085
|
+
}) : filteredSummary;
|
|
9086
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
9087
|
+
scope: opts.scope,
|
|
9088
|
+
projectScopeMode: opts.projectScopeMode,
|
|
9089
|
+
projectHash: opts.projectHash,
|
|
9090
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
9091
|
+
facets: opts.facets
|
|
9092
|
+
}) : expandedSummary;
|
|
9093
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
9094
|
+
query,
|
|
9095
|
+
minScore: opts.minScore
|
|
9096
|
+
}) : scopedExpandedSummary;
|
|
9039
9097
|
current = {
|
|
9040
|
-
results:
|
|
9041
|
-
candidateResults:
|
|
9042
|
-
matchResult: this.matcher.matchSearchResults(
|
|
9098
|
+
results: finalSummary,
|
|
9099
|
+
candidateResults: finalSummary,
|
|
9100
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
9043
9101
|
};
|
|
9044
9102
|
fallbackTrace.push("fallback:summary");
|
|
9045
9103
|
}
|
|
9046
|
-
const
|
|
9104
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
|
|
9105
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
9106
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
9107
|
+
return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
|
|
9108
|
+
}
|
|
9109
|
+
return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
|
|
9110
|
+
});
|
|
9111
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
9047
9112
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
9048
9113
|
return {
|
|
9049
9114
|
memories,
|
|
@@ -9051,7 +9116,7 @@ var Retriever = class {
|
|
|
9051
9116
|
totalTokens: this.estimateTokens(context),
|
|
9052
9117
|
context,
|
|
9053
9118
|
fallbackTrace,
|
|
9054
|
-
selectedDebug:
|
|
9119
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
9055
9120
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
9056
9121
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
9057
9122
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -9118,13 +9183,18 @@ var Retriever = class {
|
|
|
9118
9183
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
9119
9184
|
}
|
|
9120
9185
|
}
|
|
9121
|
-
const
|
|
9186
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
9122
9187
|
query,
|
|
9123
9188
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
9124
9189
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
9125
9190
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
9126
9191
|
limit: input.topK * 4
|
|
9127
9192
|
});
|
|
9193
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
9194
|
+
query: rerankQuery,
|
|
9195
|
+
currentStateQuery: query,
|
|
9196
|
+
limit: input.topK * 4
|
|
9197
|
+
}) : graphExpandedResults;
|
|
9128
9198
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
9129
9199
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
9130
9200
|
scope: input.scope,
|
|
@@ -9146,6 +9216,7 @@ var Retriever = class {
|
|
|
9146
9216
|
if (isCurrentStateQuery(options.query)) {
|
|
9147
9217
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
9148
9218
|
}
|
|
9219
|
+
filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
|
|
9149
9220
|
filtered = filtered.filter(
|
|
9150
9221
|
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
9151
9222
|
);
|
|
@@ -9171,6 +9242,47 @@ var Retriever = class {
|
|
|
9171
9242
|
}
|
|
9172
9243
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
9173
9244
|
}
|
|
9245
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
9246
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
9247
|
+
const queryTokens = this.tokenize(opts.query);
|
|
9248
|
+
if (queryTokens.length === 0) return seeds;
|
|
9249
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9250
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
9251
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
9252
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
9253
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
9254
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
9255
|
+
}
|
|
9256
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
9257
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
9258
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9259
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
9260
|
+
if (byId.has(event.id)) continue;
|
|
9261
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
9262
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
9263
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
9264
|
+
if (lexicalScore <= 0) continue;
|
|
9265
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
9266
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
9267
|
+
const row = withRetrievalLane({
|
|
9268
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
9269
|
+
eventId: event.id,
|
|
9270
|
+
content: event.content,
|
|
9271
|
+
score,
|
|
9272
|
+
sessionId: event.sessionId,
|
|
9273
|
+
eventType: event.eventType,
|
|
9274
|
+
timestamp: event.timestamp.toISOString(),
|
|
9275
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
9276
|
+
lexicalScore,
|
|
9277
|
+
recencyScore: seed.recencyScore
|
|
9278
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
9279
|
+
byId.set(row.eventId, row);
|
|
9280
|
+
if (byId.size >= opts.limit) break;
|
|
9281
|
+
}
|
|
9282
|
+
if (byId.size >= opts.limit) break;
|
|
9283
|
+
}
|
|
9284
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
9285
|
+
}
|
|
9174
9286
|
async expandGraphHops(seeds, opts) {
|
|
9175
9287
|
const byId = /* @__PURE__ */ new Map();
|
|
9176
9288
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -9486,7 +9598,7 @@ var Retriever = class {
|
|
|
9486
9598
|
async retrieveRecent(limit = 100) {
|
|
9487
9599
|
return this.eventStore.getRecentEvents(limit);
|
|
9488
9600
|
}
|
|
9489
|
-
async enrichResults(results, options) {
|
|
9601
|
+
async enrichResults(results, options, query) {
|
|
9490
9602
|
const memories = [];
|
|
9491
9603
|
for (const result of results) {
|
|
9492
9604
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
@@ -9496,13 +9608,13 @@ var Retriever = class {
|
|
|
9496
9608
|
}
|
|
9497
9609
|
let sessionContext;
|
|
9498
9610
|
if (options.includeSessionContext) {
|
|
9499
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
9611
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
9500
9612
|
}
|
|
9501
9613
|
memories.push({ event, score: result.score, sessionContext });
|
|
9502
9614
|
}
|
|
9503
9615
|
return memories;
|
|
9504
9616
|
}
|
|
9505
|
-
async getSessionContext(sessionId, eventId) {
|
|
9617
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
9506
9618
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9507
9619
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
9508
9620
|
if (eventIndex === -1) return void 0;
|
|
@@ -9510,7 +9622,9 @@ var Retriever = class {
|
|
|
9510
9622
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
9511
9623
|
const contextEvents = sessionEvents.slice(start, end);
|
|
9512
9624
|
if (contextEvents.length <= 1) return void 0;
|
|
9513
|
-
|
|
9625
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
9626
|
+
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)}...`);
|
|
9627
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
9514
9628
|
}
|
|
9515
9629
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
9516
9630
|
let context = projectResult.context;
|