claude-memory-layer 1.0.45 → 1.0.46
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 +1 -0
- package/dist/cli/index.js +55 -8
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +137 -20
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +54 -7
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +54 -7
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +54 -7
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +54 -7
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +54 -7
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +54 -7
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +137 -20
- package/dist/index.js.map +2 -2
- package/dist/mcp/index.js +163 -37
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +54 -7
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +54 -7
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +54 -7
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -10412,11 +10412,15 @@ var tools = [
|
|
|
10412
10412
|
},
|
|
10413
10413
|
maxChars: {
|
|
10414
10414
|
type: "number",
|
|
10415
|
-
|
|
10415
|
+
minimum: 1e3,
|
|
10416
|
+
maximum: 5e4,
|
|
10417
|
+
description: "Maximum final context-pack characters after the selected compression mode and final assembly trimming (default: no hard cap, max: 50000)"
|
|
10416
10418
|
},
|
|
10417
10419
|
maxTokens: {
|
|
10418
10420
|
type: "number",
|
|
10419
|
-
|
|
10421
|
+
minimum: 250,
|
|
10422
|
+
maximum: 12500,
|
|
10423
|
+
description: "Maximum final context-pack tokens, estimated at ~4 chars/token after the selected compression mode and final assembly trimming (default: no hard cap)"
|
|
10420
10424
|
},
|
|
10421
10425
|
refreshLatest: {
|
|
10422
10426
|
type: "boolean",
|
|
@@ -21118,6 +21122,10 @@ var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
|
21118
21122
|
/<turn_aborted>/i,
|
|
21119
21123
|
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
21120
21124
|
/^\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,
|
|
21125
|
+
/^\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,
|
|
21126
|
+
/^\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,
|
|
21127
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
21128
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
21121
21129
|
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
21122
21130
|
/^\$\s+\S+/i
|
|
21123
21131
|
];
|
|
@@ -21132,7 +21140,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
21132
21140
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
21133
21141
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
21134
21142
|
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
21135
|
-
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|
|
|
21143
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
21136
21144
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
21137
21145
|
];
|
|
21138
21146
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -21310,6 +21318,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
21310
21318
|
if (tokens.length > 8) return false;
|
|
21311
21319
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
21312
21320
|
}
|
|
21321
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
21322
|
+
const trimmed = query.trim();
|
|
21323
|
+
if (!trimmed) return false;
|
|
21324
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
21325
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
21326
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
21327
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
21328
|
+
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"));
|
|
21329
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
21330
|
+
}
|
|
21313
21331
|
function isCurrentStateQuery(query) {
|
|
21314
21332
|
const trimmed = query.trim();
|
|
21315
21333
|
if (!trimmed) return false;
|
|
@@ -21577,7 +21595,14 @@ var Retriever = class {
|
|
|
21577
21595
|
};
|
|
21578
21596
|
fallbackTrace.push("fallback:summary");
|
|
21579
21597
|
}
|
|
21580
|
-
const
|
|
21598
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result2) => {
|
|
21599
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
21600
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
21601
|
+
return (result2.semanticScore ?? result2.score) >= 0.5 || result2.score >= 0.5;
|
|
21602
|
+
}
|
|
21603
|
+
return (result2.semanticScore ?? result2.score) >= 0.62 || result2.score >= 0.62;
|
|
21604
|
+
});
|
|
21605
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
21581
21606
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
21582
21607
|
return {
|
|
21583
21608
|
memories,
|
|
@@ -21585,7 +21610,7 @@ var Retriever = class {
|
|
|
21585
21610
|
totalTokens: this.estimateTokens(context),
|
|
21586
21611
|
context,
|
|
21587
21612
|
fallbackTrace,
|
|
21588
|
-
selectedDebug:
|
|
21613
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
21589
21614
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
21590
21615
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
21591
21616
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -21680,6 +21705,7 @@ var Retriever = class {
|
|
|
21680
21705
|
if (isCurrentStateQuery(options.query)) {
|
|
21681
21706
|
filtered = filtered.filter((result2) => !isStaleOrSupersededContent(result2.content));
|
|
21682
21707
|
}
|
|
21708
|
+
filtered = filtered.filter((result2) => !isLowSignalContextContent(result2.content));
|
|
21683
21709
|
filtered = filtered.filter(
|
|
21684
21710
|
(result2) => this.isGraphPathResult(result2) || hasDiscriminativeTermOverlap(options.query, result2.content)
|
|
21685
21711
|
);
|
|
@@ -22020,7 +22046,7 @@ var Retriever = class {
|
|
|
22020
22046
|
async retrieveRecent(limit = 100) {
|
|
22021
22047
|
return this.eventStore.getRecentEvents(limit);
|
|
22022
22048
|
}
|
|
22023
|
-
async enrichResults(results, options) {
|
|
22049
|
+
async enrichResults(results, options, query) {
|
|
22024
22050
|
const memories = [];
|
|
22025
22051
|
for (const result2 of results) {
|
|
22026
22052
|
const event = await this.eventStore.getEvent(result2.eventId);
|
|
@@ -22030,13 +22056,13 @@ var Retriever = class {
|
|
|
22030
22056
|
}
|
|
22031
22057
|
let sessionContext;
|
|
22032
22058
|
if (options.includeSessionContext) {
|
|
22033
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
22059
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
22034
22060
|
}
|
|
22035
22061
|
memories.push({ event, score: result2.score, sessionContext });
|
|
22036
22062
|
}
|
|
22037
22063
|
return memories;
|
|
22038
22064
|
}
|
|
22039
|
-
async getSessionContext(sessionId, eventId) {
|
|
22065
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
22040
22066
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
22041
22067
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
22042
22068
|
if (eventIndex === -1) return void 0;
|
|
@@ -22044,7 +22070,9 @@ var Retriever = class {
|
|
|
22044
22070
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
22045
22071
|
const contextEvents = sessionEvents.slice(start, end);
|
|
22046
22072
|
if (contextEvents.length <= 1) return void 0;
|
|
22047
|
-
|
|
22073
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
22074
|
+
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)}...`);
|
|
22075
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
22048
22076
|
}
|
|
22049
22077
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
22050
22078
|
let context = projectResult.context;
|
|
@@ -26689,10 +26717,11 @@ var ContextCompressor = class {
|
|
|
26689
26717
|
compress(content, options) {
|
|
26690
26718
|
const source = safeLabel(options.source) || "unknown";
|
|
26691
26719
|
const mode = options.mode;
|
|
26720
|
+
const sourceRef = safeSourceRef(options.sourceRef ?? sourceRefFromMetadata(options.metadata ?? {}));
|
|
26692
26721
|
const contentType = detectContextContentType(content, options.metadata ?? {});
|
|
26693
26722
|
const originalLines = nonEmptyLines(content).length;
|
|
26694
26723
|
if (mode === "off") {
|
|
26695
|
-
return result(content, {
|
|
26724
|
+
return withSourceRefHint(result(content, {
|
|
26696
26725
|
mode,
|
|
26697
26726
|
source,
|
|
26698
26727
|
contentType,
|
|
@@ -26701,21 +26730,21 @@ var ContextCompressor = class {
|
|
|
26701
26730
|
originalLines,
|
|
26702
26731
|
omittedLines: 0,
|
|
26703
26732
|
signalCount: 0
|
|
26704
|
-
});
|
|
26733
|
+
}), sourceRef);
|
|
26705
26734
|
}
|
|
26706
26735
|
if (contentType === "log") {
|
|
26707
|
-
return this.compressLog(content, mode, source, contentType);
|
|
26736
|
+
return withSourceRefHint(this.compressLog(content, mode, source, contentType), sourceRef);
|
|
26708
26737
|
}
|
|
26709
26738
|
if (contentType === "markdown") {
|
|
26710
|
-
return this.compressMarkdown(content, mode, source, contentType);
|
|
26739
|
+
return withSourceRefHint(this.compressMarkdown(content, mode, source, contentType), sourceRef);
|
|
26711
26740
|
}
|
|
26712
26741
|
if (contentType === "diff") {
|
|
26713
|
-
return this.compressDiff(content, mode, source, contentType);
|
|
26742
|
+
return withSourceRefHint(this.compressDiff(content, mode, source, contentType), sourceRef);
|
|
26714
26743
|
}
|
|
26715
26744
|
if (contentType === "code") {
|
|
26716
|
-
return this.compressCode(content, mode, source, contentType);
|
|
26745
|
+
return withSourceRefHint(this.compressCode(content, mode, source, contentType), sourceRef);
|
|
26717
26746
|
}
|
|
26718
|
-
return this.compressPlain(content, mode, source, contentType);
|
|
26747
|
+
return withSourceRefHint(this.compressPlain(content, mode, source, contentType), sourceRef);
|
|
26719
26748
|
}
|
|
26720
26749
|
compressLog(content, mode, source, contentType) {
|
|
26721
26750
|
const lines = nonEmptyLines(content);
|
|
@@ -26853,11 +26882,15 @@ function summarizeCompressionTelemetry(metadata) {
|
|
|
26853
26882
|
const totalOriginalChars = metadata.reduce((sum, item) => sum + item.originalChars, 0);
|
|
26854
26883
|
const totalCompressedChars = metadata.reduce((sum, item) => sum + item.compressedChars, 0);
|
|
26855
26884
|
const totalSavedChars = metadata.reduce((sum, item) => sum + item.savedChars, 0);
|
|
26885
|
+
const totalOmittedLines = metadata.reduce((sum, item) => sum + item.omittedLines, 0);
|
|
26886
|
+
const sourceRefsPreserved = metadata.filter((item) => item.sourceRefPreserved).length;
|
|
26856
26887
|
return {
|
|
26857
26888
|
totalItems: metadata.length,
|
|
26858
26889
|
totalOriginalChars,
|
|
26859
26890
|
totalCompressedChars,
|
|
26860
26891
|
totalSavedChars,
|
|
26892
|
+
totalOmittedLines,
|
|
26893
|
+
sourceRefsPreserved,
|
|
26861
26894
|
bySource: summarizeBy(metadata, "source"),
|
|
26862
26895
|
byStrategy: summarizeBy(metadata, "strategy")
|
|
26863
26896
|
};
|
|
@@ -26871,12 +26904,16 @@ function summarizeBy(metadata, key) {
|
|
|
26871
26904
|
items: 0,
|
|
26872
26905
|
originalChars: 0,
|
|
26873
26906
|
compressedChars: 0,
|
|
26874
|
-
savedChars: 0
|
|
26907
|
+
savedChars: 0,
|
|
26908
|
+
omittedLines: 0,
|
|
26909
|
+
sourceRefsPreserved: 0
|
|
26875
26910
|
};
|
|
26876
26911
|
existing.items += 1;
|
|
26877
26912
|
existing.originalChars += item.originalChars;
|
|
26878
26913
|
existing.compressedChars += item.compressedChars;
|
|
26879
26914
|
existing.savedChars += item.savedChars;
|
|
26915
|
+
existing.omittedLines += item.omittedLines;
|
|
26916
|
+
if (item.sourceRefPreserved) existing.sourceRefsPreserved += 1;
|
|
26880
26917
|
groups.set(groupKey, existing);
|
|
26881
26918
|
}
|
|
26882
26919
|
return Array.from(groups.values()).sort((a, b) => String(a[key] ?? "").localeCompare(String(b[key] ?? "")));
|
|
@@ -26891,7 +26928,8 @@ function result(text, base) {
|
|
|
26891
26928
|
...base,
|
|
26892
26929
|
compressedChars,
|
|
26893
26930
|
savedChars,
|
|
26894
|
-
compressionRatio
|
|
26931
|
+
compressionRatio,
|
|
26932
|
+
sourceRefPreserved: false
|
|
26895
26933
|
}
|
|
26896
26934
|
};
|
|
26897
26935
|
}
|
|
@@ -26940,6 +26978,65 @@ function safeLabel(value) {
|
|
|
26940
26978
|
const cleaned = value.replace(/[^A-Za-z0-9_.:-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80);
|
|
26941
26979
|
return cleaned || void 0;
|
|
26942
26980
|
}
|
|
26981
|
+
function sourceRefFromMetadata(metadata) {
|
|
26982
|
+
const explicit = firstString(metadata, ["sourceRef", "source_ref", "sourceReference", "source_reference"]);
|
|
26983
|
+
if (explicit) return explicit;
|
|
26984
|
+
const citation = firstString(metadata, ["citationId", "citation_id", "memoryCitationId", "memory_citation_id"]);
|
|
26985
|
+
if (citation) return citation.startsWith("mem:") ? citation : `mem:${citation}`;
|
|
26986
|
+
const eventId = firstString(metadata, ["eventId", "event_id"]);
|
|
26987
|
+
if (eventId) return eventId.startsWith("event:") ? eventId : `event:${eventId}`;
|
|
26988
|
+
return void 0;
|
|
26989
|
+
}
|
|
26990
|
+
function firstString(metadata, keys) {
|
|
26991
|
+
for (const key of keys) {
|
|
26992
|
+
const value = metadata[key];
|
|
26993
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
26994
|
+
}
|
|
26995
|
+
return void 0;
|
|
26996
|
+
}
|
|
26997
|
+
function safeSourceRef(value) {
|
|
26998
|
+
if (!value) return void 0;
|
|
26999
|
+
const normalized = value.trim().replace(/^\[?(mem|event):/i, (_, prefix) => `${prefix.toLowerCase()}:`).replace(/\]?$/g, "");
|
|
27000
|
+
if (!normalized || /(?:password|secret|api[_-]?key|token|bearer)/i.test(normalized)) return void 0;
|
|
27001
|
+
const cleaned = normalized.replace(/[^A-Za-z0-9_.:-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 140);
|
|
27002
|
+
return cleaned || void 0;
|
|
27003
|
+
}
|
|
27004
|
+
function withSourceRefHint(compression, sourceRef) {
|
|
27005
|
+
if (!sourceRef) return compression;
|
|
27006
|
+
if (compression.metadata.mode === "off" || compression.metadata.strategy === "none") {
|
|
27007
|
+
return {
|
|
27008
|
+
text: compression.text,
|
|
27009
|
+
metadata: {
|
|
27010
|
+
...compression.metadata,
|
|
27011
|
+
sourceRef,
|
|
27012
|
+
sourceRefPreserved: false
|
|
27013
|
+
}
|
|
27014
|
+
};
|
|
27015
|
+
}
|
|
27016
|
+
const hint = `sourceRef=${sourceRef} expand=mem-source-ref`;
|
|
27017
|
+
const text = compression.text.includes(hint) ? compression.text : addSourceRefHint(compression.text, hint);
|
|
27018
|
+
const compressedChars = text.length;
|
|
27019
|
+
const savedChars = Math.max(0, compression.metadata.originalChars - compressedChars);
|
|
27020
|
+
const compressionRatio = compression.metadata.originalChars > 0 ? compressedChars / compression.metadata.originalChars : 1;
|
|
27021
|
+
return {
|
|
27022
|
+
text,
|
|
27023
|
+
metadata: {
|
|
27024
|
+
...compression.metadata,
|
|
27025
|
+
sourceRef,
|
|
27026
|
+
sourceRefPreserved: text.includes(`sourceRef=${sourceRef}`) && text.includes("expand=mem-source-ref"),
|
|
27027
|
+
compressedChars,
|
|
27028
|
+
savedChars,
|
|
27029
|
+
compressionRatio
|
|
27030
|
+
}
|
|
27031
|
+
};
|
|
27032
|
+
}
|
|
27033
|
+
function addSourceRefHint(text, hint) {
|
|
27034
|
+
if (text.startsWith("[compressed ")) {
|
|
27035
|
+
return text.replace(/^\[([^\]]+)\]/, `[$1; ${hint}]`);
|
|
27036
|
+
}
|
|
27037
|
+
return `[${hint}]
|
|
27038
|
+
${text}`;
|
|
27039
|
+
}
|
|
26943
27040
|
|
|
26944
27041
|
// src/extensions/mcp/handlers.ts
|
|
26945
27042
|
function resolveMemoryService(args) {
|
|
@@ -26977,6 +27074,9 @@ async function handleToolCall(name, args) {
|
|
|
26977
27074
|
if (name === "mem-context-pack" && hasMemContextPackPerspectiveArgs(args)) {
|
|
26978
27075
|
validateMemContextPackPerspectiveArgs(args);
|
|
26979
27076
|
}
|
|
27077
|
+
if (name === "mem-context-pack") {
|
|
27078
|
+
validateMemContextPackBudgetArgs(args);
|
|
27079
|
+
}
|
|
26980
27080
|
if (MEMORY_OPERATION_TOOL_NAMES.has(name)) {
|
|
26981
27081
|
return await handleMemoryOperationTool(name, args);
|
|
26982
27082
|
}
|
|
@@ -27723,9 +27823,11 @@ async function handleExternalMarketContext(args) {
|
|
|
27723
27823
|
}
|
|
27724
27824
|
async function handleMemSearch(memoryService, args) {
|
|
27725
27825
|
const query = args.query;
|
|
27726
|
-
const topK =
|
|
27826
|
+
const topK = numberArg(args.topK, 5, 1, 20);
|
|
27827
|
+
const fetchTopK = Math.min(topK * 3, 20);
|
|
27727
27828
|
const sessionId = args.sessionId;
|
|
27728
|
-
const
|
|
27829
|
+
const eventType = eventTypeArg(args.eventType);
|
|
27830
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK, fetchTopK, sessionId, eventType });
|
|
27729
27831
|
const lines = [
|
|
27730
27832
|
"## Memory Search Results",
|
|
27731
27833
|
"",
|
|
@@ -27739,7 +27841,7 @@ async function handleMemSearch(memoryService, args) {
|
|
|
27739
27841
|
const m = search.memories[i];
|
|
27740
27842
|
const citationId = generateCitationId(m.event.id);
|
|
27741
27843
|
const date = m.event.timestamp.toISOString().split("T")[0];
|
|
27742
|
-
const preview = m.event.content
|
|
27844
|
+
const preview = sanitizeOperationString(m.event.content, 100);
|
|
27743
27845
|
lines.push(`### ${i + 1}. [mem:${citationId}] (score: ${m.score.toFixed(2)})`);
|
|
27744
27846
|
lines.push(`**Type**: ${m.event.eventType} | **Date**: ${date}`);
|
|
27745
27847
|
lines.push(`> ${preview}`);
|
|
@@ -27754,29 +27856,41 @@ async function handleMemSearch(memoryService, args) {
|
|
|
27754
27856
|
var SEMANTIC_VECTOR_FALLBACK_WARNING = "Warning: semantic/vector retrieval unavailable; used keyword fallback.";
|
|
27755
27857
|
var SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING = "Warning: semantic/vector retrieval unavailable; keyword fallback failed.";
|
|
27756
27858
|
async function retrieveMcpMemories(memoryService, query, options) {
|
|
27859
|
+
const fetchTopK = Math.max(options.topK, options.fetchTopK ?? options.topK);
|
|
27757
27860
|
try {
|
|
27758
27861
|
const result2 = await memoryService.retrieveMemories(query, {
|
|
27759
|
-
topK:
|
|
27862
|
+
topK: fetchTopK,
|
|
27760
27863
|
sessionId: options.sessionId,
|
|
27761
27864
|
recordTrace: false
|
|
27762
27865
|
});
|
|
27763
|
-
return { memories: result2.memories };
|
|
27866
|
+
return { memories: selectMcpMemoryResults(result2.memories, options.topK, options.eventType) };
|
|
27764
27867
|
} catch (error) {
|
|
27765
27868
|
if (!isVectorSchemaMismatchError(error)) {
|
|
27766
27869
|
throw error;
|
|
27767
27870
|
}
|
|
27768
27871
|
try {
|
|
27769
|
-
const
|
|
27872
|
+
const candidates = options.sessionId ? rankSessionKeywordMatches(
|
|
27770
27873
|
query,
|
|
27771
27874
|
await memoryService.getSessionHistory(options.sessionId),
|
|
27772
|
-
|
|
27773
|
-
) : await memoryService.keywordSearch(query, { topK:
|
|
27774
|
-
return { memories, warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
27875
|
+
fetchTopK
|
|
27876
|
+
) : await memoryService.keywordSearch(query, { topK: fetchTopK });
|
|
27877
|
+
return { memories: selectMcpMemoryResults(candidates, options.topK, options.eventType), warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
27775
27878
|
} catch {
|
|
27776
27879
|
return { memories: [], warning: SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING };
|
|
27777
27880
|
}
|
|
27778
27881
|
}
|
|
27779
27882
|
}
|
|
27883
|
+
function selectMcpMemoryResults(memories, topK, eventType) {
|
|
27884
|
+
return memories.filter((memory) => !isLowSignalContextContent(memory.event.content || "")).filter((memory) => eventType === void 0 || memory.event.eventType === eventType).slice(0, topK);
|
|
27885
|
+
}
|
|
27886
|
+
function eventTypeArg(value) {
|
|
27887
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
27888
|
+
const normalized = String(value).trim();
|
|
27889
|
+
if (normalized === "user_prompt" || normalized === "agent_response" || normalized === "tool_observation" || normalized === "session_summary") {
|
|
27890
|
+
return normalized;
|
|
27891
|
+
}
|
|
27892
|
+
throw new Error("Invalid eventType: expected user_prompt, agent_response, tool_observation, or session_summary");
|
|
27893
|
+
}
|
|
27780
27894
|
function isVectorSchemaMismatchError(error) {
|
|
27781
27895
|
const message = error instanceof Error ? error.message : String(error);
|
|
27782
27896
|
return /no vector column/i.test(message) || /query vector dimension/i.test(message) || /vector[^\n]{0,80}dimension/i.test(message) || /dimension[^\n]{0,80}vector/i.test(message) || /lancedb[^\n]{0,120}schema/i.test(message);
|
|
@@ -27812,7 +27926,7 @@ async function handleMemTimeline(memoryService, args) {
|
|
|
27812
27926
|
lines.push(`Event ${targetId} not found.`);
|
|
27813
27927
|
continue;
|
|
27814
27928
|
}
|
|
27815
|
-
const sessionEvents = recentEvents.filter((e) => e.sessionId === targetEvent.sessionId).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
27929
|
+
const sessionEvents = recentEvents.filter((e) => e.sessionId === targetEvent.sessionId).filter((e) => e.id === targetEvent.id || !isLowSignalContextContent(e.content || "")).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
27816
27930
|
const eventIndex = sessionEvents.findIndex((e) => e.id === targetEvent.id);
|
|
27817
27931
|
const start = Math.max(0, eventIndex - windowSize);
|
|
27818
27932
|
const end = Math.min(sessionEvents.length, eventIndex + windowSize + 1);
|
|
@@ -27823,7 +27937,8 @@ async function handleMemTimeline(memoryService, args) {
|
|
|
27823
27937
|
const isTarget = e.id === targetEvent.id;
|
|
27824
27938
|
const marker = isTarget ? "**\u2192**" : " ";
|
|
27825
27939
|
const time = e.timestamp.toLocaleTimeString();
|
|
27826
|
-
const
|
|
27940
|
+
const lowSignal = isLowSignalContextContent(e.content || "");
|
|
27941
|
+
const preview = lowSignal ? "[low-signal context artifact suppressed; use mem-details for raw source]" : sanitizeOperationString(e.content, 60);
|
|
27827
27942
|
const citationId = generateCitationId(e.id);
|
|
27828
27943
|
lines.push(`${marker} ${time} [${citationId}] ${e.eventType}: ${preview}`);
|
|
27829
27944
|
}
|
|
@@ -27955,6 +28070,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
27955
28070
|
if (compression !== "off" || maxContextChars !== void 0) {
|
|
27956
28071
|
lines.push("### Compression Notice", "");
|
|
27957
28072
|
lines.push("- Context-pack compression is applied only to LLM-facing previews; original events remain available through source refs.");
|
|
28073
|
+
lines.push("- Privacy filters run before compression and final preview sanitization runs after compression.");
|
|
27958
28074
|
if (maxContextChars !== void 0) {
|
|
27959
28075
|
lines.push(`- Hard output budget: ${maxContextChars} characters; use follow-up lookups to expand trimmed sources.`);
|
|
27960
28076
|
}
|
|
@@ -28096,7 +28212,8 @@ async function handleMemProjectTimeline(memoryService, args) {
|
|
|
28096
28212
|
const limit = numberArg(args.limit, 50, 1, 500);
|
|
28097
28213
|
const sessionLimit = numberArg(args.sessionLimit, 10, 1, 50);
|
|
28098
28214
|
const recentEvents = await memoryService.getRecentEvents(limit);
|
|
28099
|
-
const
|
|
28215
|
+
const timelineEvents = recentEvents.filter((event) => !isLowSignalContextContent(event.content || ""));
|
|
28216
|
+
const sessions = summarizeSessions(timelineEvents, sessionLimit);
|
|
28100
28217
|
const lines = [
|
|
28101
28218
|
"## Project Memory Timeline",
|
|
28102
28219
|
"",
|
|
@@ -28376,10 +28493,11 @@ function sourceTypeForEvent(event) {
|
|
|
28376
28493
|
}
|
|
28377
28494
|
function formatRelevantMemoryLine(event, score, index, formatOptions = DEFAULT_CONTEXT_PACK_FORMAT_OPTIONS) {
|
|
28378
28495
|
const citationId = generateCitationId(event.id);
|
|
28496
|
+
const previewBudget = formatOptions.compression === "off" ? 260 : 180;
|
|
28379
28497
|
return [
|
|
28380
28498
|
`${index}. [mem:${citationId}] score=${score.toFixed(2)} type=${event.eventType} date=${event.timestamp.toISOString()} session=${event.sessionId}`,
|
|
28381
28499
|
` source=${sourceForEvent(event)}`,
|
|
28382
|
-
` ${contextPackPreview(event,
|
|
28500
|
+
` ${contextPackPreview(event, previewBudget, formatOptions)}`,
|
|
28383
28501
|
""
|
|
28384
28502
|
].join("\n");
|
|
28385
28503
|
}
|
|
@@ -28456,6 +28574,7 @@ function contextPackPreview(event, maxLength, options = DEFAULT_CONTEXT_PACK_FOR
|
|
|
28456
28574
|
const compressed = MCP_CONTEXT_COMPRESSOR.compress(privacyFiltered, {
|
|
28457
28575
|
mode: options.compression,
|
|
28458
28576
|
source: sourceForEvent(event),
|
|
28577
|
+
sourceRef: `mem:${generateCitationId(event.id)}`,
|
|
28459
28578
|
metadata: {
|
|
28460
28579
|
...safeCompressionMetadata(event.metadata),
|
|
28461
28580
|
eventType: event.eventType
|
|
@@ -28483,8 +28602,9 @@ function appendCompressionTelemetry(lines, metadata, maxChars) {
|
|
|
28483
28602
|
const summary = summarizeCompressionTelemetry(metadata);
|
|
28484
28603
|
const sources = summary.bySource.map((item) => `${item.source}:${item.items}`).join(",");
|
|
28485
28604
|
const strategies = summary.byStrategy.map((item) => `${item.strategy}:${item.items}`).join(",");
|
|
28605
|
+
const sourceRefsAvailable = metadata.filter((item) => item.sourceRef !== void 0).length;
|
|
28486
28606
|
lines.push(
|
|
28487
|
-
`- Compression telemetry: items=${summary.totalItems} originalChars=${summary.totalOriginalChars} compressedChars=${summary.totalCompressedChars} savedChars=${summary.totalSavedChars} sources=${sources || "n/a"} strategies=${strategies || "n/a"}`,
|
|
28607
|
+
`- Compression telemetry: items=${summary.totalItems} originalChars=${summary.totalOriginalChars} compressedChars=${summary.totalCompressedChars} savedChars=${summary.totalSavedChars} omittedLines=${summary.totalOmittedLines} sourceRefsPreserved=${summary.sourceRefsPreserved}/${sourceRefsAvailable} sources=${sources || "n/a"} strategies=${strategies || "n/a"}`,
|
|
28488
28608
|
""
|
|
28489
28609
|
);
|
|
28490
28610
|
}
|
|
@@ -28494,18 +28614,24 @@ function contextCompressionModeArg(value, hasBudget) {
|
|
|
28494
28614
|
if (normalized === "off" || normalized === "safe" || normalized === "aggressive") return normalized;
|
|
28495
28615
|
throw new Error("Invalid compression: expected off, safe, or aggressive");
|
|
28496
28616
|
}
|
|
28617
|
+
function validateMemContextPackBudgetArgs(args) {
|
|
28618
|
+
const maxContextChars = contextPackMaxCharsArg(args);
|
|
28619
|
+
contextCompressionModeArg(args.compression, maxContextChars !== void 0);
|
|
28620
|
+
}
|
|
28497
28621
|
function contextPackMaxCharsArg(args) {
|
|
28498
|
-
const maxChars = optionalNumberArg(args.maxChars, CONTEXT_PACK_MIN_HARD_BUDGET_CHARS, CONTEXT_PACK_MAX_CHARS_CAP);
|
|
28499
|
-
const maxTokens = optionalNumberArg(args.maxTokens, 250, Math.floor(CONTEXT_PACK_MAX_CHARS_CAP / 4));
|
|
28622
|
+
const maxChars = optionalNumberArg(args.maxChars, "maxChars", CONTEXT_PACK_MIN_HARD_BUDGET_CHARS, CONTEXT_PACK_MAX_CHARS_CAP);
|
|
28623
|
+
const maxTokens = optionalNumberArg(args.maxTokens, "maxTokens", 250, Math.floor(CONTEXT_PACK_MAX_CHARS_CAP / 4));
|
|
28500
28624
|
const tokenChars = maxTokens === void 0 ? void 0 : maxTokens * 4;
|
|
28501
28625
|
if (maxChars !== void 0 && tokenChars !== void 0) return Math.min(maxChars, tokenChars);
|
|
28502
28626
|
return maxChars ?? tokenChars;
|
|
28503
28627
|
}
|
|
28504
|
-
function optionalNumberArg(value, min, max) {
|
|
28628
|
+
function optionalNumberArg(value, name, min, max) {
|
|
28505
28629
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
28506
28630
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
28507
|
-
if (!Number.isFinite(parsed)
|
|
28508
|
-
|
|
28631
|
+
if (!Number.isFinite(parsed) || parsed < min || parsed > max) {
|
|
28632
|
+
throw new Error(`Invalid ${name}: expected number between ${min} and ${max}`);
|
|
28633
|
+
}
|
|
28634
|
+
return Math.floor(parsed);
|
|
28509
28635
|
}
|
|
28510
28636
|
function applyContextPackBudget(text, maxChars) {
|
|
28511
28637
|
if (maxChars === void 0 || text.length <= maxChars) return text;
|