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/api/index.js
CHANGED
|
@@ -2572,6 +2572,7 @@ var VectorOutbox = class {
|
|
|
2572
2572
|
// src/core/retrieval-debug-lanes.ts
|
|
2573
2573
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
2574
2574
|
"raw_event",
|
|
2575
|
+
"session_event",
|
|
2575
2576
|
"session_summary",
|
|
2576
2577
|
"graph_path",
|
|
2577
2578
|
"facet_match"
|
|
@@ -8448,6 +8449,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
8448
8449
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
8449
8450
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
8450
8451
|
];
|
|
8452
|
+
var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
8453
|
+
/<environment_context\b[\s\S]*<\/environment_context>/i,
|
|
8454
|
+
/<turn_aborted>/i,
|
|
8455
|
+
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
8456
|
+
/^\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,
|
|
8457
|
+
/^\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,
|
|
8458
|
+
/^\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,
|
|
8459
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
8460
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
8461
|
+
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
8462
|
+
/^\$\s+\S+/i
|
|
8463
|
+
];
|
|
8451
8464
|
var CONTINUATION_QUERY_PATTERNS = [
|
|
8452
8465
|
/^\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,
|
|
8453
8466
|
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
@@ -8459,7 +8472,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
8459
8472
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
8460
8473
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
8461
8474
|
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
8462
|
-
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|
|
|
8475
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
8463
8476
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
8464
8477
|
];
|
|
8465
8478
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -8605,6 +8618,21 @@ function isCommandArtifactQuery(query) {
|
|
|
8605
8618
|
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8606
8619
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8607
8620
|
}
|
|
8621
|
+
function isCommandArtifactContent(content) {
|
|
8622
|
+
const trimmed = content.trim();
|
|
8623
|
+
if (!trimmed) return false;
|
|
8624
|
+
const normalized = trimmed.toLowerCase();
|
|
8625
|
+
if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
|
|
8626
|
+
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8627
|
+
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8628
|
+
}
|
|
8629
|
+
function isLowSignalContextContent(content) {
|
|
8630
|
+
const trimmed = content.trim();
|
|
8631
|
+
if (!trimmed) return true;
|
|
8632
|
+
if (isCommandArtifactContent(trimmed)) return true;
|
|
8633
|
+
if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
8634
|
+
return false;
|
|
8635
|
+
}
|
|
8608
8636
|
function isGenericContinuationQuery(query) {
|
|
8609
8637
|
const trimmed = query.trim();
|
|
8610
8638
|
if (!trimmed) return false;
|
|
@@ -8622,6 +8650,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
8622
8650
|
if (tokens.length > 8) return false;
|
|
8623
8651
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8624
8652
|
}
|
|
8653
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
8654
|
+
const trimmed = query.trim();
|
|
8655
|
+
if (!trimmed) return false;
|
|
8656
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
8657
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
8658
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
8659
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
8660
|
+
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"));
|
|
8661
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
8662
|
+
}
|
|
8625
8663
|
function isCurrentStateQuery(query) {
|
|
8626
8664
|
const trimmed = query.trim();
|
|
8627
8665
|
if (!trimmed) return false;
|
|
@@ -8795,6 +8833,7 @@ var Retriever = class {
|
|
|
8795
8833
|
}
|
|
8796
8834
|
async retrieve(query, options = {}) {
|
|
8797
8835
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
8836
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
8798
8837
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
8799
8838
|
const fallbackTrace = [];
|
|
8800
8839
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -8825,6 +8864,7 @@ var Retriever = class {
|
|
|
8825
8864
|
decayPolicy: opts.decayPolicy,
|
|
8826
8865
|
intentRewrite: opts.intentRewrite === true,
|
|
8827
8866
|
graphHop: opts.graphHop,
|
|
8867
|
+
retrievalMode,
|
|
8828
8868
|
projectScopeMode: opts.projectScopeMode,
|
|
8829
8869
|
projectHash: opts.projectHash,
|
|
8830
8870
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8843,6 +8883,7 @@ var Retriever = class {
|
|
|
8843
8883
|
rerankWeights: opts.rerankWeights,
|
|
8844
8884
|
decayPolicy: opts.decayPolicy,
|
|
8845
8885
|
graphHop: opts.graphHop,
|
|
8886
|
+
retrievalMode,
|
|
8846
8887
|
projectScopeMode: opts.projectScopeMode,
|
|
8847
8888
|
projectHash: opts.projectHash,
|
|
8848
8889
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8862,6 +8903,7 @@ var Retriever = class {
|
|
|
8862
8903
|
rerankWeights: opts.rerankWeights,
|
|
8863
8904
|
decayPolicy: opts.decayPolicy,
|
|
8864
8905
|
graphHop: opts.graphHop,
|
|
8906
|
+
retrievalMode,
|
|
8865
8907
|
projectScopeMode: opts.projectScopeMode,
|
|
8866
8908
|
projectHash: opts.projectHash,
|
|
8867
8909
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -8882,14 +8924,37 @@ var Retriever = class {
|
|
|
8882
8924
|
query,
|
|
8883
8925
|
minScore: opts.minScore
|
|
8884
8926
|
});
|
|
8927
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
8928
|
+
query: qualityQuery,
|
|
8929
|
+
currentStateQuery: query,
|
|
8930
|
+
limit: opts.topK * 4
|
|
8931
|
+
}) : filteredSummary;
|
|
8932
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
8933
|
+
scope: opts.scope,
|
|
8934
|
+
projectScopeMode: opts.projectScopeMode,
|
|
8935
|
+
projectHash: opts.projectHash,
|
|
8936
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
8937
|
+
facets: opts.facets
|
|
8938
|
+
}) : expandedSummary;
|
|
8939
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
8940
|
+
query,
|
|
8941
|
+
minScore: opts.minScore
|
|
8942
|
+
}) : scopedExpandedSummary;
|
|
8885
8943
|
current = {
|
|
8886
|
-
results:
|
|
8887
|
-
candidateResults:
|
|
8888
|
-
matchResult: this.matcher.matchSearchResults(
|
|
8944
|
+
results: finalSummary,
|
|
8945
|
+
candidateResults: finalSummary,
|
|
8946
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
8889
8947
|
};
|
|
8890
8948
|
fallbackTrace.push("fallback:summary");
|
|
8891
8949
|
}
|
|
8892
|
-
const
|
|
8950
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
|
|
8951
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
8952
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
8953
|
+
return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
|
|
8954
|
+
}
|
|
8955
|
+
return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
|
|
8956
|
+
});
|
|
8957
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
8893
8958
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
8894
8959
|
return {
|
|
8895
8960
|
memories,
|
|
@@ -8897,7 +8962,7 @@ var Retriever = class {
|
|
|
8897
8962
|
totalTokens: this.estimateTokens(context),
|
|
8898
8963
|
context,
|
|
8899
8964
|
fallbackTrace,
|
|
8900
|
-
selectedDebug:
|
|
8965
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
8901
8966
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
8902
8967
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
8903
8968
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -8964,13 +9029,18 @@ var Retriever = class {
|
|
|
8964
9029
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
8965
9030
|
}
|
|
8966
9031
|
}
|
|
8967
|
-
const
|
|
9032
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
8968
9033
|
query,
|
|
8969
9034
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
8970
9035
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
8971
9036
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
8972
9037
|
limit: input.topK * 4
|
|
8973
9038
|
});
|
|
9039
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
9040
|
+
query: rerankQuery,
|
|
9041
|
+
currentStateQuery: query,
|
|
9042
|
+
limit: input.topK * 4
|
|
9043
|
+
}) : graphExpandedResults;
|
|
8974
9044
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
8975
9045
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
8976
9046
|
scope: input.scope,
|
|
@@ -8992,6 +9062,7 @@ var Retriever = class {
|
|
|
8992
9062
|
if (isCurrentStateQuery(options.query)) {
|
|
8993
9063
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
8994
9064
|
}
|
|
9065
|
+
filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
|
|
8995
9066
|
filtered = filtered.filter(
|
|
8996
9067
|
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
8997
9068
|
);
|
|
@@ -9017,6 +9088,47 @@ var Retriever = class {
|
|
|
9017
9088
|
}
|
|
9018
9089
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
9019
9090
|
}
|
|
9091
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
9092
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
9093
|
+
const queryTokens = this.tokenize(opts.query);
|
|
9094
|
+
if (queryTokens.length === 0) return seeds;
|
|
9095
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9096
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
9097
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
9098
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
9099
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
9100
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
9101
|
+
}
|
|
9102
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
9103
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
9104
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9105
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
9106
|
+
if (byId.has(event.id)) continue;
|
|
9107
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
9108
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
9109
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
9110
|
+
if (lexicalScore <= 0) continue;
|
|
9111
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
9112
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
9113
|
+
const row = withRetrievalLane({
|
|
9114
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
9115
|
+
eventId: event.id,
|
|
9116
|
+
content: event.content,
|
|
9117
|
+
score,
|
|
9118
|
+
sessionId: event.sessionId,
|
|
9119
|
+
eventType: event.eventType,
|
|
9120
|
+
timestamp: event.timestamp.toISOString(),
|
|
9121
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
9122
|
+
lexicalScore,
|
|
9123
|
+
recencyScore: seed.recencyScore
|
|
9124
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
9125
|
+
byId.set(row.eventId, row);
|
|
9126
|
+
if (byId.size >= opts.limit) break;
|
|
9127
|
+
}
|
|
9128
|
+
if (byId.size >= opts.limit) break;
|
|
9129
|
+
}
|
|
9130
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
9131
|
+
}
|
|
9020
9132
|
async expandGraphHops(seeds, opts) {
|
|
9021
9133
|
const byId = /* @__PURE__ */ new Map();
|
|
9022
9134
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -9332,7 +9444,7 @@ var Retriever = class {
|
|
|
9332
9444
|
async retrieveRecent(limit = 100) {
|
|
9333
9445
|
return this.eventStore.getRecentEvents(limit);
|
|
9334
9446
|
}
|
|
9335
|
-
async enrichResults(results, options) {
|
|
9447
|
+
async enrichResults(results, options, query) {
|
|
9336
9448
|
const memories = [];
|
|
9337
9449
|
for (const result of results) {
|
|
9338
9450
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
@@ -9342,13 +9454,13 @@ var Retriever = class {
|
|
|
9342
9454
|
}
|
|
9343
9455
|
let sessionContext;
|
|
9344
9456
|
if (options.includeSessionContext) {
|
|
9345
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
9457
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
9346
9458
|
}
|
|
9347
9459
|
memories.push({ event, score: result.score, sessionContext });
|
|
9348
9460
|
}
|
|
9349
9461
|
return memories;
|
|
9350
9462
|
}
|
|
9351
|
-
async getSessionContext(sessionId, eventId) {
|
|
9463
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
9352
9464
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9353
9465
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
9354
9466
|
if (eventIndex === -1) return void 0;
|
|
@@ -9356,7 +9468,9 @@ var Retriever = class {
|
|
|
9356
9468
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
9357
9469
|
const contextEvents = sessionEvents.slice(start, end);
|
|
9358
9470
|
if (contextEvents.length <= 1) return void 0;
|
|
9359
|
-
|
|
9471
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
9472
|
+
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)}...`);
|
|
9473
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
9360
9474
|
}
|
|
9361
9475
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
9362
9476
|
let context = projectResult.context;
|