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