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/server/index.js
CHANGED
|
@@ -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|
|
|
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:
|
|
8899
|
-
candidateResults:
|
|
8900
|
-
matchResult: this.matcher.matchSearchResults(
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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;
|