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
|
@@ -2681,6 +2681,7 @@ var VectorOutbox = class {
|
|
|
2681
2681
|
// src/core/retrieval-debug-lanes.ts
|
|
2682
2682
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
2683
2683
|
"raw_event",
|
|
2684
|
+
"session_event",
|
|
2684
2685
|
"session_summary",
|
|
2685
2686
|
"graph_path",
|
|
2686
2687
|
"facet_match"
|
|
@@ -8780,6 +8781,18 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
8780
8781
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
8781
8782
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
8782
8783
|
];
|
|
8784
|
+
var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
8785
|
+
/<environment_context\b[\s\S]*<\/environment_context>/i,
|
|
8786
|
+
/<turn_aborted>/i,
|
|
8787
|
+
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
8788
|
+
/^\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,
|
|
8789
|
+
/^\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,
|
|
8790
|
+
/^\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,
|
|
8791
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
8792
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
8793
|
+
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
8794
|
+
/^\$\s+\S+/i
|
|
8795
|
+
];
|
|
8783
8796
|
var CONTINUATION_QUERY_PATTERNS = [
|
|
8784
8797
|
/^\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,
|
|
8785
8798
|
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
@@ -8791,7 +8804,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
8791
8804
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
8792
8805
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
8793
8806
|
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
8794
|
-
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|
|
|
8807
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
8795
8808
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
8796
8809
|
];
|
|
8797
8810
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -8937,6 +8950,21 @@ function isCommandArtifactQuery(query) {
|
|
|
8937
8950
|
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8938
8951
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8939
8952
|
}
|
|
8953
|
+
function isCommandArtifactContent(content) {
|
|
8954
|
+
const trimmed = content.trim();
|
|
8955
|
+
if (!trimmed) return false;
|
|
8956
|
+
const normalized = trimmed.toLowerCase();
|
|
8957
|
+
if (normalized.includes("local-command-stdout") || normalized.includes("local-command-stderr")) return true;
|
|
8958
|
+
if (normalized.includes("command-name") || normalized.includes("command-message")) return true;
|
|
8959
|
+
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8960
|
+
}
|
|
8961
|
+
function isLowSignalContextContent(content) {
|
|
8962
|
+
const trimmed = content.trim();
|
|
8963
|
+
if (!trimmed) return true;
|
|
8964
|
+
if (isCommandArtifactContent(trimmed)) return true;
|
|
8965
|
+
if (LOW_SIGNAL_CONTEXT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
8966
|
+
return false;
|
|
8967
|
+
}
|
|
8940
8968
|
function isGenericContinuationQuery(query) {
|
|
8941
8969
|
const trimmed = query.trim();
|
|
8942
8970
|
if (!trimmed) return false;
|
|
@@ -8954,6 +8982,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
8954
8982
|
if (tokens.length > 8) return false;
|
|
8955
8983
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
8956
8984
|
}
|
|
8985
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
8986
|
+
const trimmed = query.trim();
|
|
8987
|
+
if (!trimmed) return false;
|
|
8988
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
8989
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
8990
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
8991
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
8992
|
+
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"));
|
|
8993
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
8994
|
+
}
|
|
8957
8995
|
function isCurrentStateQuery(query) {
|
|
8958
8996
|
const trimmed = query.trim();
|
|
8959
8997
|
if (!trimmed) return false;
|
|
@@ -9127,6 +9165,7 @@ var Retriever = class {
|
|
|
9127
9165
|
}
|
|
9128
9166
|
async retrieve(query, options = {}) {
|
|
9129
9167
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
9168
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
9130
9169
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
9131
9170
|
const fallbackTrace = [];
|
|
9132
9171
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -9157,6 +9196,7 @@ var Retriever = class {
|
|
|
9157
9196
|
decayPolicy: opts.decayPolicy,
|
|
9158
9197
|
intentRewrite: opts.intentRewrite === true,
|
|
9159
9198
|
graphHop: opts.graphHop,
|
|
9199
|
+
retrievalMode,
|
|
9160
9200
|
projectScopeMode: opts.projectScopeMode,
|
|
9161
9201
|
projectHash: opts.projectHash,
|
|
9162
9202
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -9175,6 +9215,7 @@ var Retriever = class {
|
|
|
9175
9215
|
rerankWeights: opts.rerankWeights,
|
|
9176
9216
|
decayPolicy: opts.decayPolicy,
|
|
9177
9217
|
graphHop: opts.graphHop,
|
|
9218
|
+
retrievalMode,
|
|
9178
9219
|
projectScopeMode: opts.projectScopeMode,
|
|
9179
9220
|
projectHash: opts.projectHash,
|
|
9180
9221
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -9194,6 +9235,7 @@ var Retriever = class {
|
|
|
9194
9235
|
rerankWeights: opts.rerankWeights,
|
|
9195
9236
|
decayPolicy: opts.decayPolicy,
|
|
9196
9237
|
graphHop: opts.graphHop,
|
|
9238
|
+
retrievalMode,
|
|
9197
9239
|
projectScopeMode: opts.projectScopeMode,
|
|
9198
9240
|
projectHash: opts.projectHash,
|
|
9199
9241
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -9214,14 +9256,37 @@ var Retriever = class {
|
|
|
9214
9256
|
query,
|
|
9215
9257
|
minScore: opts.minScore
|
|
9216
9258
|
});
|
|
9259
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
9260
|
+
query: qualityQuery,
|
|
9261
|
+
currentStateQuery: query,
|
|
9262
|
+
limit: opts.topK * 4
|
|
9263
|
+
}) : filteredSummary;
|
|
9264
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
9265
|
+
scope: opts.scope,
|
|
9266
|
+
projectScopeMode: opts.projectScopeMode,
|
|
9267
|
+
projectHash: opts.projectHash,
|
|
9268
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
9269
|
+
facets: opts.facets
|
|
9270
|
+
}) : expandedSummary;
|
|
9271
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
9272
|
+
query,
|
|
9273
|
+
minScore: opts.minScore
|
|
9274
|
+
}) : scopedExpandedSummary;
|
|
9217
9275
|
current = {
|
|
9218
|
-
results:
|
|
9219
|
-
candidateResults:
|
|
9220
|
-
matchResult: this.matcher.matchSearchResults(
|
|
9276
|
+
results: finalSummary,
|
|
9277
|
+
candidateResults: finalSummary,
|
|
9278
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
9221
9279
|
};
|
|
9222
9280
|
fallbackTrace.push("fallback:summary");
|
|
9223
9281
|
}
|
|
9224
|
-
const
|
|
9282
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result) => {
|
|
9283
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
9284
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
9285
|
+
return (result.semanticScore ?? result.score) >= 0.5 || result.score >= 0.5;
|
|
9286
|
+
}
|
|
9287
|
+
return (result.semanticScore ?? result.score) >= 0.62 || result.score >= 0.62;
|
|
9288
|
+
});
|
|
9289
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
9225
9290
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
9226
9291
|
return {
|
|
9227
9292
|
memories,
|
|
@@ -9229,7 +9294,7 @@ var Retriever = class {
|
|
|
9229
9294
|
totalTokens: this.estimateTokens(context),
|
|
9230
9295
|
context,
|
|
9231
9296
|
fallbackTrace,
|
|
9232
|
-
selectedDebug:
|
|
9297
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
9233
9298
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
9234
9299
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
9235
9300
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -9296,13 +9361,18 @@ var Retriever = class {
|
|
|
9296
9361
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
9297
9362
|
}
|
|
9298
9363
|
}
|
|
9299
|
-
const
|
|
9364
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
9300
9365
|
query,
|
|
9301
9366
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
9302
9367
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
9303
9368
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
9304
9369
|
limit: input.topK * 4
|
|
9305
9370
|
});
|
|
9371
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
9372
|
+
query: rerankQuery,
|
|
9373
|
+
currentStateQuery: query,
|
|
9374
|
+
limit: input.topK * 4
|
|
9375
|
+
}) : graphExpandedResults;
|
|
9306
9376
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
9307
9377
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
9308
9378
|
scope: input.scope,
|
|
@@ -9324,6 +9394,7 @@ var Retriever = class {
|
|
|
9324
9394
|
if (isCurrentStateQuery(options.query)) {
|
|
9325
9395
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
9326
9396
|
}
|
|
9397
|
+
filtered = filtered.filter((result) => !isLowSignalContextContent(result.content));
|
|
9327
9398
|
filtered = filtered.filter(
|
|
9328
9399
|
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
9329
9400
|
);
|
|
@@ -9349,6 +9420,47 @@ var Retriever = class {
|
|
|
9349
9420
|
}
|
|
9350
9421
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
9351
9422
|
}
|
|
9423
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
9424
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
9425
|
+
const queryTokens = this.tokenize(opts.query);
|
|
9426
|
+
if (queryTokens.length === 0) return seeds;
|
|
9427
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9428
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
9429
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
9430
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
9431
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
9432
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
9433
|
+
}
|
|
9434
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
9435
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
9436
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9437
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
9438
|
+
if (byId.has(event.id)) continue;
|
|
9439
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
9440
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
9441
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
9442
|
+
if (lexicalScore <= 0) continue;
|
|
9443
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
9444
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
9445
|
+
const row = withRetrievalLane({
|
|
9446
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
9447
|
+
eventId: event.id,
|
|
9448
|
+
content: event.content,
|
|
9449
|
+
score,
|
|
9450
|
+
sessionId: event.sessionId,
|
|
9451
|
+
eventType: event.eventType,
|
|
9452
|
+
timestamp: event.timestamp.toISOString(),
|
|
9453
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
9454
|
+
lexicalScore,
|
|
9455
|
+
recencyScore: seed.recencyScore
|
|
9456
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
9457
|
+
byId.set(row.eventId, row);
|
|
9458
|
+
if (byId.size >= opts.limit) break;
|
|
9459
|
+
}
|
|
9460
|
+
if (byId.size >= opts.limit) break;
|
|
9461
|
+
}
|
|
9462
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
9463
|
+
}
|
|
9352
9464
|
async expandGraphHops(seeds, opts) {
|
|
9353
9465
|
const byId = /* @__PURE__ */ new Map();
|
|
9354
9466
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -9664,7 +9776,7 @@ var Retriever = class {
|
|
|
9664
9776
|
async retrieveRecent(limit = 100) {
|
|
9665
9777
|
return this.eventStore.getRecentEvents(limit);
|
|
9666
9778
|
}
|
|
9667
|
-
async enrichResults(results, options) {
|
|
9779
|
+
async enrichResults(results, options, query) {
|
|
9668
9780
|
const memories = [];
|
|
9669
9781
|
for (const result of results) {
|
|
9670
9782
|
const event = await this.eventStore.getEvent(result.eventId);
|
|
@@ -9674,13 +9786,13 @@ var Retriever = class {
|
|
|
9674
9786
|
}
|
|
9675
9787
|
let sessionContext;
|
|
9676
9788
|
if (options.includeSessionContext) {
|
|
9677
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
9789
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
9678
9790
|
}
|
|
9679
9791
|
memories.push({ event, score: result.score, sessionContext });
|
|
9680
9792
|
}
|
|
9681
9793
|
return memories;
|
|
9682
9794
|
}
|
|
9683
|
-
async getSessionContext(sessionId, eventId) {
|
|
9795
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
9684
9796
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
9685
9797
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
9686
9798
|
if (eventIndex === -1) return void 0;
|
|
@@ -9688,7 +9800,9 @@ var Retriever = class {
|
|
|
9688
9800
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
9689
9801
|
const contextEvents = sessionEvents.slice(start, end);
|
|
9690
9802
|
if (contextEvents.length <= 1) return void 0;
|
|
9691
|
-
|
|
9803
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
9804
|
+
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)}...`);
|
|
9805
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
9692
9806
|
}
|
|
9693
9807
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
9694
9808
|
let context = projectResult.context;
|