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/mcp/index.js
CHANGED
|
@@ -10410,13 +10410,22 @@ var tools = [
|
|
|
10410
10410
|
enum: ["off", "safe", "aggressive"],
|
|
10411
10411
|
description: "Optional context-pack compression mode. safe preserves source refs and high-signal errors/log lines; aggressive is more compact."
|
|
10412
10412
|
},
|
|
10413
|
+
retrievalMode: {
|
|
10414
|
+
type: "string",
|
|
10415
|
+
enum: ["session-event-hybrid", "event"],
|
|
10416
|
+
description: "Optional production context-pack retrieval mode. Defaults to session-event-hybrid (session-event hybrid), which rescues query-relevant sibling events from sessions already hit by direct retrieval. Use event for exact event-only debugging."
|
|
10417
|
+
},
|
|
10413
10418
|
maxChars: {
|
|
10414
10419
|
type: "number",
|
|
10415
|
-
|
|
10420
|
+
minimum: 1e3,
|
|
10421
|
+
maximum: 5e4,
|
|
10422
|
+
description: "Maximum final context-pack characters after the selected compression mode and final assembly trimming (default: no hard cap, max: 50000)"
|
|
10416
10423
|
},
|
|
10417
10424
|
maxTokens: {
|
|
10418
10425
|
type: "number",
|
|
10419
|
-
|
|
10426
|
+
minimum: 250,
|
|
10427
|
+
maximum: 12500,
|
|
10428
|
+
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
10429
|
},
|
|
10421
10430
|
refreshLatest: {
|
|
10422
10431
|
type: "boolean",
|
|
@@ -10563,6 +10572,16 @@ var tools = [
|
|
|
10563
10572
|
type: "number",
|
|
10564
10573
|
description: "Maximum recent events to scan for ID resolution (default: 10000, max: 50000)"
|
|
10565
10574
|
},
|
|
10575
|
+
includeNeighbors: {
|
|
10576
|
+
type: "boolean",
|
|
10577
|
+
description: "When true, include privacy-safe neighbor events from the same session around each resolved source reference."
|
|
10578
|
+
},
|
|
10579
|
+
neighborWindow: {
|
|
10580
|
+
type: "number",
|
|
10581
|
+
minimum: 0,
|
|
10582
|
+
maximum: 5,
|
|
10583
|
+
description: "Number of before/after session neighbor events to include when includeNeighbors is true (default: 1, max: 5)."
|
|
10584
|
+
},
|
|
10566
10585
|
projectPath: projectPathProperty
|
|
10567
10586
|
},
|
|
10568
10587
|
required: ["ids"]
|
|
@@ -13643,6 +13662,7 @@ var VectorOutbox = class {
|
|
|
13643
13662
|
// src/core/retrieval-debug-lanes.ts
|
|
13644
13663
|
var RETRIEVAL_DEBUG_LANE_NAMES = [
|
|
13645
13664
|
"raw_event",
|
|
13665
|
+
"session_event",
|
|
13646
13666
|
"session_summary",
|
|
13647
13667
|
"graph_path",
|
|
13648
13668
|
"facet_match"
|
|
@@ -21118,6 +21138,10 @@ var LOW_SIGNAL_CONTEXT_PATTERNS = [
|
|
|
21118
21138
|
/<turn_aborted>/i,
|
|
21119
21139
|
/^#\s*AGENTS\.md\s+instructions\b[\s\S]*<INSTRUCTIONS>/i,
|
|
21120
21140
|
/^\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,
|
|
21141
|
+
/^\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,
|
|
21142
|
+
/^\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,
|
|
21143
|
+
/^\s*---\s*END\s+OF\s+CONTEXT\s+SUMMARY\b/i,
|
|
21144
|
+
/^\s*\[Your\s+active\s+task\s+list\s+was\s+preserved\s+across\s+context\s+compression\]/i,
|
|
21121
21145
|
/^➜\s+\S+\s+git:\([^)]*\)\s+/i,
|
|
21122
21146
|
/^\$\s+\S+/i
|
|
21123
21147
|
];
|
|
@@ -21132,7 +21156,7 @@ var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
|
21132
21156
|
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
21133
21157
|
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
21134
21158
|
/\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|
|
|
21159
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|status)\b/i,
|
|
21136
21160
|
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
21137
21161
|
];
|
|
21138
21162
|
var STALE_CONTENT_PATTERNS = [
|
|
@@ -21310,6 +21334,16 @@ function isShortRepairFollowUpQuery(query) {
|
|
|
21310
21334
|
if (tokens.length > 8) return false;
|
|
21311
21335
|
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
21312
21336
|
}
|
|
21337
|
+
function isLowConfidenceContextFallbackQuery(query) {
|
|
21338
|
+
const trimmed = query.trim();
|
|
21339
|
+
if (!trimmed) return false;
|
|
21340
|
+
if (isGenericContinuationQuery(trimmed) || isShortRepairFollowUpQuery(trimmed)) return true;
|
|
21341
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
21342
|
+
if ((terms.has("compacted") || terms.has("compaction")) && terms.has("handoff")) return false;
|
|
21343
|
+
const hasContinuationRecall = /^(?:continue|resume)\b/i.test(trimmed) && (terms.has("work") || terms.has("step") || terms.has("task") || terms.has("last") || terms.has("completed"));
|
|
21344
|
+
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"));
|
|
21345
|
+
return hasContinuationRecall || hasValidationGateRecall;
|
|
21346
|
+
}
|
|
21313
21347
|
function isCurrentStateQuery(query) {
|
|
21314
21348
|
const trimmed = query.trim();
|
|
21315
21349
|
if (!trimmed) return false;
|
|
@@ -21483,6 +21517,7 @@ var Retriever = class {
|
|
|
21483
21517
|
}
|
|
21484
21518
|
async retrieve(query, options = {}) {
|
|
21485
21519
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
21520
|
+
const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
|
|
21486
21521
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
21487
21522
|
const fallbackTrace = [];
|
|
21488
21523
|
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
@@ -21513,6 +21548,7 @@ var Retriever = class {
|
|
|
21513
21548
|
decayPolicy: opts.decayPolicy,
|
|
21514
21549
|
intentRewrite: opts.intentRewrite === true,
|
|
21515
21550
|
graphHop: opts.graphHop,
|
|
21551
|
+
retrievalMode,
|
|
21516
21552
|
projectScopeMode: opts.projectScopeMode,
|
|
21517
21553
|
projectHash: opts.projectHash,
|
|
21518
21554
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21531,6 +21567,7 @@ var Retriever = class {
|
|
|
21531
21567
|
rerankWeights: opts.rerankWeights,
|
|
21532
21568
|
decayPolicy: opts.decayPolicy,
|
|
21533
21569
|
graphHop: opts.graphHop,
|
|
21570
|
+
retrievalMode,
|
|
21534
21571
|
projectScopeMode: opts.projectScopeMode,
|
|
21535
21572
|
projectHash: opts.projectHash,
|
|
21536
21573
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21550,6 +21587,7 @@ var Retriever = class {
|
|
|
21550
21587
|
rerankWeights: opts.rerankWeights,
|
|
21551
21588
|
decayPolicy: opts.decayPolicy,
|
|
21552
21589
|
graphHop: opts.graphHop,
|
|
21590
|
+
retrievalMode,
|
|
21553
21591
|
projectScopeMode: opts.projectScopeMode,
|
|
21554
21592
|
projectHash: opts.projectHash,
|
|
21555
21593
|
allowedProjectHashes: opts.allowedProjectHashes,
|
|
@@ -21570,14 +21608,37 @@ var Retriever = class {
|
|
|
21570
21608
|
query,
|
|
21571
21609
|
minScore: opts.minScore
|
|
21572
21610
|
});
|
|
21611
|
+
const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
|
|
21612
|
+
query: qualityQuery,
|
|
21613
|
+
currentStateQuery: query,
|
|
21614
|
+
limit: opts.topK * 4
|
|
21615
|
+
}) : filteredSummary;
|
|
21616
|
+
const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
|
|
21617
|
+
scope: opts.scope,
|
|
21618
|
+
projectScopeMode: opts.projectScopeMode,
|
|
21619
|
+
projectHash: opts.projectHash,
|
|
21620
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
21621
|
+
facets: opts.facets
|
|
21622
|
+
}) : expandedSummary;
|
|
21623
|
+
const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
|
|
21624
|
+
query,
|
|
21625
|
+
minScore: opts.minScore
|
|
21626
|
+
}) : scopedExpandedSummary;
|
|
21573
21627
|
current = {
|
|
21574
|
-
results:
|
|
21575
|
-
candidateResults:
|
|
21576
|
-
matchResult: this.matcher.matchSearchResults(
|
|
21628
|
+
results: finalSummary,
|
|
21629
|
+
candidateResults: finalSummary,
|
|
21630
|
+
matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
|
|
21577
21631
|
};
|
|
21578
21632
|
fallbackTrace.push("fallback:summary");
|
|
21579
21633
|
}
|
|
21580
|
-
const
|
|
21634
|
+
const selectedResults = current.results.slice(0, opts.topK).filter((result2) => {
|
|
21635
|
+
if (current.matchResult.confidence !== "none") return true;
|
|
21636
|
+
if (isLowConfidenceContextFallbackQuery(query)) {
|
|
21637
|
+
return (result2.semanticScore ?? result2.score) >= 0.5 || result2.score >= 0.5;
|
|
21638
|
+
}
|
|
21639
|
+
return (result2.semanticScore ?? result2.score) >= 0.62 || result2.score >= 0.62;
|
|
21640
|
+
});
|
|
21641
|
+
const memories = await this.enrichResults(selectedResults, opts, query);
|
|
21581
21642
|
const context = this.buildContext(memories, opts.maxTokens);
|
|
21582
21643
|
return {
|
|
21583
21644
|
memories,
|
|
@@ -21585,7 +21646,7 @@ var Retriever = class {
|
|
|
21585
21646
|
totalTokens: this.estimateTokens(context),
|
|
21586
21647
|
context,
|
|
21587
21648
|
fallbackTrace,
|
|
21588
|
-
selectedDebug:
|
|
21649
|
+
selectedDebug: selectedResults.map((r) => this.debugDetailForResult(r)),
|
|
21589
21650
|
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
21590
21651
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
21591
21652
|
effectiveQueryText: current.effectiveQueryText,
|
|
@@ -21652,13 +21713,18 @@ var Retriever = class {
|
|
|
21652
21713
|
initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
|
|
21653
21714
|
}
|
|
21654
21715
|
}
|
|
21655
|
-
const
|
|
21716
|
+
const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
21656
21717
|
query,
|
|
21657
21718
|
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
21658
21719
|
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
21659
21720
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
21660
21721
|
limit: input.topK * 4
|
|
21661
21722
|
});
|
|
21723
|
+
const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
|
|
21724
|
+
query: rerankQuery,
|
|
21725
|
+
currentStateQuery: query,
|
|
21726
|
+
limit: input.topK * 4
|
|
21727
|
+
}) : graphExpandedResults;
|
|
21662
21728
|
const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
|
|
21663
21729
|
const filtered = await this.applyScopeFilters(rerankedResults, {
|
|
21664
21730
|
scope: input.scope,
|
|
@@ -21680,6 +21746,7 @@ var Retriever = class {
|
|
|
21680
21746
|
if (isCurrentStateQuery(options.query)) {
|
|
21681
21747
|
filtered = filtered.filter((result2) => !isStaleOrSupersededContent(result2.content));
|
|
21682
21748
|
}
|
|
21749
|
+
filtered = filtered.filter((result2) => !isLowSignalContextContent(result2.content));
|
|
21683
21750
|
filtered = filtered.filter(
|
|
21684
21751
|
(result2) => this.isGraphPathResult(result2) || hasDiscriminativeTermOverlap(options.query, result2.content)
|
|
21685
21752
|
);
|
|
@@ -21705,6 +21772,47 @@ var Retriever = class {
|
|
|
21705
21772
|
}
|
|
21706
21773
|
return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
21707
21774
|
}
|
|
21775
|
+
async expandSessionEventHybrid(seeds, opts) {
|
|
21776
|
+
if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
|
|
21777
|
+
const queryTokens = this.tokenize(opts.query);
|
|
21778
|
+
if (queryTokens.length === 0) return seeds;
|
|
21779
|
+
const byId = /* @__PURE__ */ new Map();
|
|
21780
|
+
for (const seed of seeds) byId.set(seed.eventId, seed);
|
|
21781
|
+
const bestSeedBySession = /* @__PURE__ */ new Map();
|
|
21782
|
+
for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
|
|
21783
|
+
if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
|
|
21784
|
+
bestSeedBySession.set(seed.sessionId, seed);
|
|
21785
|
+
}
|
|
21786
|
+
const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
|
|
21787
|
+
for (const [sessionId, seed] of bestSeedBySession) {
|
|
21788
|
+
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
21789
|
+
for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
|
|
21790
|
+
if (byId.has(event.id)) continue;
|
|
21791
|
+
if (isLowSignalContextContent(event.content)) continue;
|
|
21792
|
+
if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
|
|
21793
|
+
const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
|
|
21794
|
+
if (lexicalScore <= 0) continue;
|
|
21795
|
+
if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
|
|
21796
|
+
const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
|
|
21797
|
+
const row = withRetrievalLane({
|
|
21798
|
+
id: `session-event-${seed.eventId}-${event.id}`,
|
|
21799
|
+
eventId: event.id,
|
|
21800
|
+
content: event.content,
|
|
21801
|
+
score,
|
|
21802
|
+
sessionId: event.sessionId,
|
|
21803
|
+
eventType: event.eventType,
|
|
21804
|
+
timestamp: event.timestamp.toISOString(),
|
|
21805
|
+
semanticScore: seed.semanticScore ?? seed.score,
|
|
21806
|
+
lexicalScore,
|
|
21807
|
+
recencyScore: seed.recencyScore
|
|
21808
|
+
}, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
|
|
21809
|
+
byId.set(row.eventId, row);
|
|
21810
|
+
if (byId.size >= opts.limit) break;
|
|
21811
|
+
}
|
|
21812
|
+
if (byId.size >= opts.limit) break;
|
|
21813
|
+
}
|
|
21814
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
21815
|
+
}
|
|
21708
21816
|
async expandGraphHops(seeds, opts) {
|
|
21709
21817
|
const byId = /* @__PURE__ */ new Map();
|
|
21710
21818
|
for (const s of seeds) byId.set(s.eventId, s);
|
|
@@ -22020,7 +22128,7 @@ var Retriever = class {
|
|
|
22020
22128
|
async retrieveRecent(limit = 100) {
|
|
22021
22129
|
return this.eventStore.getRecentEvents(limit);
|
|
22022
22130
|
}
|
|
22023
|
-
async enrichResults(results, options) {
|
|
22131
|
+
async enrichResults(results, options, query) {
|
|
22024
22132
|
const memories = [];
|
|
22025
22133
|
for (const result2 of results) {
|
|
22026
22134
|
const event = await this.eventStore.getEvent(result2.eventId);
|
|
@@ -22030,13 +22138,13 @@ var Retriever = class {
|
|
|
22030
22138
|
}
|
|
22031
22139
|
let sessionContext;
|
|
22032
22140
|
if (options.includeSessionContext) {
|
|
22033
|
-
sessionContext = await this.getSessionContext(event.sessionId, event.id);
|
|
22141
|
+
sessionContext = await this.getSessionContext(event.sessionId, event.id, query);
|
|
22034
22142
|
}
|
|
22035
22143
|
memories.push({ event, score: result2.score, sessionContext });
|
|
22036
22144
|
}
|
|
22037
22145
|
return memories;
|
|
22038
22146
|
}
|
|
22039
|
-
async getSessionContext(sessionId, eventId) {
|
|
22147
|
+
async getSessionContext(sessionId, eventId, query) {
|
|
22040
22148
|
const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
|
|
22041
22149
|
const eventIndex = sessionEvents.findIndex((e) => e.id === eventId);
|
|
22042
22150
|
if (eventIndex === -1) return void 0;
|
|
@@ -22044,7 +22152,9 @@ var Retriever = class {
|
|
|
22044
22152
|
const end = Math.min(sessionEvents.length, eventIndex + 2);
|
|
22045
22153
|
const contextEvents = sessionEvents.slice(start, end);
|
|
22046
22154
|
if (contextEvents.length <= 1) return void 0;
|
|
22047
|
-
|
|
22155
|
+
const suppressStaleState = isCurrentStateQuery(query);
|
|
22156
|
+
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)}...`);
|
|
22157
|
+
return contextLines.length > 0 ? contextLines.join("\n") : void 0;
|
|
22048
22158
|
}
|
|
22049
22159
|
buildUnifiedContext(projectResult, sharedMemories) {
|
|
22050
22160
|
let context = projectResult.context;
|
|
@@ -26689,10 +26799,11 @@ var ContextCompressor = class {
|
|
|
26689
26799
|
compress(content, options) {
|
|
26690
26800
|
const source = safeLabel(options.source) || "unknown";
|
|
26691
26801
|
const mode = options.mode;
|
|
26802
|
+
const sourceRef = safeSourceRef(options.sourceRef ?? sourceRefFromMetadata(options.metadata ?? {}));
|
|
26692
26803
|
const contentType = detectContextContentType(content, options.metadata ?? {});
|
|
26693
26804
|
const originalLines = nonEmptyLines(content).length;
|
|
26694
26805
|
if (mode === "off") {
|
|
26695
|
-
return result(content, {
|
|
26806
|
+
return withSourceRefHint(result(content, {
|
|
26696
26807
|
mode,
|
|
26697
26808
|
source,
|
|
26698
26809
|
contentType,
|
|
@@ -26701,21 +26812,21 @@ var ContextCompressor = class {
|
|
|
26701
26812
|
originalLines,
|
|
26702
26813
|
omittedLines: 0,
|
|
26703
26814
|
signalCount: 0
|
|
26704
|
-
});
|
|
26815
|
+
}), sourceRef);
|
|
26705
26816
|
}
|
|
26706
26817
|
if (contentType === "log") {
|
|
26707
|
-
return this.compressLog(content, mode, source, contentType);
|
|
26818
|
+
return withSourceRefHint(this.compressLog(content, mode, source, contentType), sourceRef);
|
|
26708
26819
|
}
|
|
26709
26820
|
if (contentType === "markdown") {
|
|
26710
|
-
return this.compressMarkdown(content, mode, source, contentType);
|
|
26821
|
+
return withSourceRefHint(this.compressMarkdown(content, mode, source, contentType), sourceRef);
|
|
26711
26822
|
}
|
|
26712
26823
|
if (contentType === "diff") {
|
|
26713
|
-
return this.compressDiff(content, mode, source, contentType);
|
|
26824
|
+
return withSourceRefHint(this.compressDiff(content, mode, source, contentType), sourceRef);
|
|
26714
26825
|
}
|
|
26715
26826
|
if (contentType === "code") {
|
|
26716
|
-
return this.compressCode(content, mode, source, contentType);
|
|
26827
|
+
return withSourceRefHint(this.compressCode(content, mode, source, contentType), sourceRef);
|
|
26717
26828
|
}
|
|
26718
|
-
return this.compressPlain(content, mode, source, contentType);
|
|
26829
|
+
return withSourceRefHint(this.compressPlain(content, mode, source, contentType), sourceRef);
|
|
26719
26830
|
}
|
|
26720
26831
|
compressLog(content, mode, source, contentType) {
|
|
26721
26832
|
const lines = nonEmptyLines(content);
|
|
@@ -26853,11 +26964,15 @@ function summarizeCompressionTelemetry(metadata) {
|
|
|
26853
26964
|
const totalOriginalChars = metadata.reduce((sum, item) => sum + item.originalChars, 0);
|
|
26854
26965
|
const totalCompressedChars = metadata.reduce((sum, item) => sum + item.compressedChars, 0);
|
|
26855
26966
|
const totalSavedChars = metadata.reduce((sum, item) => sum + item.savedChars, 0);
|
|
26967
|
+
const totalOmittedLines = metadata.reduce((sum, item) => sum + item.omittedLines, 0);
|
|
26968
|
+
const sourceRefsPreserved = metadata.filter((item) => item.sourceRefPreserved).length;
|
|
26856
26969
|
return {
|
|
26857
26970
|
totalItems: metadata.length,
|
|
26858
26971
|
totalOriginalChars,
|
|
26859
26972
|
totalCompressedChars,
|
|
26860
26973
|
totalSavedChars,
|
|
26974
|
+
totalOmittedLines,
|
|
26975
|
+
sourceRefsPreserved,
|
|
26861
26976
|
bySource: summarizeBy(metadata, "source"),
|
|
26862
26977
|
byStrategy: summarizeBy(metadata, "strategy")
|
|
26863
26978
|
};
|
|
@@ -26871,12 +26986,16 @@ function summarizeBy(metadata, key) {
|
|
|
26871
26986
|
items: 0,
|
|
26872
26987
|
originalChars: 0,
|
|
26873
26988
|
compressedChars: 0,
|
|
26874
|
-
savedChars: 0
|
|
26989
|
+
savedChars: 0,
|
|
26990
|
+
omittedLines: 0,
|
|
26991
|
+
sourceRefsPreserved: 0
|
|
26875
26992
|
};
|
|
26876
26993
|
existing.items += 1;
|
|
26877
26994
|
existing.originalChars += item.originalChars;
|
|
26878
26995
|
existing.compressedChars += item.compressedChars;
|
|
26879
26996
|
existing.savedChars += item.savedChars;
|
|
26997
|
+
existing.omittedLines += item.omittedLines;
|
|
26998
|
+
if (item.sourceRefPreserved) existing.sourceRefsPreserved += 1;
|
|
26880
26999
|
groups.set(groupKey, existing);
|
|
26881
27000
|
}
|
|
26882
27001
|
return Array.from(groups.values()).sort((a, b) => String(a[key] ?? "").localeCompare(String(b[key] ?? "")));
|
|
@@ -26891,7 +27010,8 @@ function result(text, base) {
|
|
|
26891
27010
|
...base,
|
|
26892
27011
|
compressedChars,
|
|
26893
27012
|
savedChars,
|
|
26894
|
-
compressionRatio
|
|
27013
|
+
compressionRatio,
|
|
27014
|
+
sourceRefPreserved: false
|
|
26895
27015
|
}
|
|
26896
27016
|
};
|
|
26897
27017
|
}
|
|
@@ -26940,6 +27060,65 @@ function safeLabel(value) {
|
|
|
26940
27060
|
const cleaned = value.replace(/[^A-Za-z0-9_.:-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80);
|
|
26941
27061
|
return cleaned || void 0;
|
|
26942
27062
|
}
|
|
27063
|
+
function sourceRefFromMetadata(metadata) {
|
|
27064
|
+
const explicit = firstString(metadata, ["sourceRef", "source_ref", "sourceReference", "source_reference"]);
|
|
27065
|
+
if (explicit) return explicit;
|
|
27066
|
+
const citation = firstString(metadata, ["citationId", "citation_id", "memoryCitationId", "memory_citation_id"]);
|
|
27067
|
+
if (citation) return citation.startsWith("mem:") ? citation : `mem:${citation}`;
|
|
27068
|
+
const eventId = firstString(metadata, ["eventId", "event_id"]);
|
|
27069
|
+
if (eventId) return eventId.startsWith("event:") ? eventId : `event:${eventId}`;
|
|
27070
|
+
return void 0;
|
|
27071
|
+
}
|
|
27072
|
+
function firstString(metadata, keys) {
|
|
27073
|
+
for (const key of keys) {
|
|
27074
|
+
const value = metadata[key];
|
|
27075
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
27076
|
+
}
|
|
27077
|
+
return void 0;
|
|
27078
|
+
}
|
|
27079
|
+
function safeSourceRef(value) {
|
|
27080
|
+
if (!value) return void 0;
|
|
27081
|
+
const normalized = value.trim().replace(/^\[?(mem|event):/i, (_, prefix) => `${prefix.toLowerCase()}:`).replace(/\]?$/g, "");
|
|
27082
|
+
if (!normalized || /(?:password|secret|api[_-]?key|token|bearer)/i.test(normalized)) return void 0;
|
|
27083
|
+
const cleaned = normalized.replace(/[^A-Za-z0-9_.:-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 140);
|
|
27084
|
+
return cleaned || void 0;
|
|
27085
|
+
}
|
|
27086
|
+
function withSourceRefHint(compression, sourceRef) {
|
|
27087
|
+
if (!sourceRef) return compression;
|
|
27088
|
+
if (compression.metadata.mode === "off" || compression.metadata.strategy === "none") {
|
|
27089
|
+
return {
|
|
27090
|
+
text: compression.text,
|
|
27091
|
+
metadata: {
|
|
27092
|
+
...compression.metadata,
|
|
27093
|
+
sourceRef,
|
|
27094
|
+
sourceRefPreserved: false
|
|
27095
|
+
}
|
|
27096
|
+
};
|
|
27097
|
+
}
|
|
27098
|
+
const hint = `sourceRef=${sourceRef} expand=mem-source-ref`;
|
|
27099
|
+
const text = compression.text.includes(hint) ? compression.text : addSourceRefHint(compression.text, hint);
|
|
27100
|
+
const compressedChars = text.length;
|
|
27101
|
+
const savedChars = Math.max(0, compression.metadata.originalChars - compressedChars);
|
|
27102
|
+
const compressionRatio = compression.metadata.originalChars > 0 ? compressedChars / compression.metadata.originalChars : 1;
|
|
27103
|
+
return {
|
|
27104
|
+
text,
|
|
27105
|
+
metadata: {
|
|
27106
|
+
...compression.metadata,
|
|
27107
|
+
sourceRef,
|
|
27108
|
+
sourceRefPreserved: text.includes(`sourceRef=${sourceRef}`) && text.includes("expand=mem-source-ref"),
|
|
27109
|
+
compressedChars,
|
|
27110
|
+
savedChars,
|
|
27111
|
+
compressionRatio
|
|
27112
|
+
}
|
|
27113
|
+
};
|
|
27114
|
+
}
|
|
27115
|
+
function addSourceRefHint(text, hint) {
|
|
27116
|
+
if (text.startsWith("[compressed ")) {
|
|
27117
|
+
return text.replace(/^\[([^\]]+)\]/, `[$1; ${hint}]`);
|
|
27118
|
+
}
|
|
27119
|
+
return `[${hint}]
|
|
27120
|
+
${text}`;
|
|
27121
|
+
}
|
|
26943
27122
|
|
|
26944
27123
|
// src/extensions/mcp/handlers.ts
|
|
26945
27124
|
function resolveMemoryService(args) {
|
|
@@ -26977,6 +27156,9 @@ async function handleToolCall(name, args) {
|
|
|
26977
27156
|
if (name === "mem-context-pack" && hasMemContextPackPerspectiveArgs(args)) {
|
|
26978
27157
|
validateMemContextPackPerspectiveArgs(args);
|
|
26979
27158
|
}
|
|
27159
|
+
if (name === "mem-context-pack") {
|
|
27160
|
+
validateMemContextPackBudgetArgs(args);
|
|
27161
|
+
}
|
|
26980
27162
|
if (MEMORY_OPERATION_TOOL_NAMES.has(name)) {
|
|
26981
27163
|
return await handleMemoryOperationTool(name, args);
|
|
26982
27164
|
}
|
|
@@ -27723,9 +27905,11 @@ async function handleExternalMarketContext(args) {
|
|
|
27723
27905
|
}
|
|
27724
27906
|
async function handleMemSearch(memoryService, args) {
|
|
27725
27907
|
const query = args.query;
|
|
27726
|
-
const topK =
|
|
27908
|
+
const topK = numberArg(args.topK, 5, 1, 20);
|
|
27909
|
+
const fetchTopK = Math.min(topK * 3, 20);
|
|
27727
27910
|
const sessionId = args.sessionId;
|
|
27728
|
-
const
|
|
27911
|
+
const eventType = eventTypeArg(args.eventType);
|
|
27912
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK, fetchTopK, sessionId, eventType });
|
|
27729
27913
|
const lines = [
|
|
27730
27914
|
"## Memory Search Results",
|
|
27731
27915
|
"",
|
|
@@ -27739,7 +27923,7 @@ async function handleMemSearch(memoryService, args) {
|
|
|
27739
27923
|
const m = search.memories[i];
|
|
27740
27924
|
const citationId = generateCitationId(m.event.id);
|
|
27741
27925
|
const date = m.event.timestamp.toISOString().split("T")[0];
|
|
27742
|
-
const preview = m.event.content
|
|
27926
|
+
const preview = sanitizeOperationString(m.event.content, 100);
|
|
27743
27927
|
lines.push(`### ${i + 1}. [mem:${citationId}] (score: ${m.score.toFixed(2)})`);
|
|
27744
27928
|
lines.push(`**Type**: ${m.event.eventType} | **Date**: ${date}`);
|
|
27745
27929
|
lines.push(`> ${preview}`);
|
|
@@ -27754,29 +27938,43 @@ async function handleMemSearch(memoryService, args) {
|
|
|
27754
27938
|
var SEMANTIC_VECTOR_FALLBACK_WARNING = "Warning: semantic/vector retrieval unavailable; used keyword fallback.";
|
|
27755
27939
|
var SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING = "Warning: semantic/vector retrieval unavailable; keyword fallback failed.";
|
|
27756
27940
|
async function retrieveMcpMemories(memoryService, query, options) {
|
|
27941
|
+
const fetchTopK = Math.max(options.topK, options.fetchTopK ?? options.topK);
|
|
27757
27942
|
try {
|
|
27758
|
-
const
|
|
27759
|
-
topK:
|
|
27943
|
+
const retrieveOptions = {
|
|
27944
|
+
topK: fetchTopK,
|
|
27760
27945
|
sessionId: options.sessionId,
|
|
27761
|
-
recordTrace: false
|
|
27762
|
-
|
|
27763
|
-
|
|
27946
|
+
recordTrace: false,
|
|
27947
|
+
...options.retrievalMode ? { retrievalMode: options.retrievalMode } : {}
|
|
27948
|
+
};
|
|
27949
|
+
const result2 = await memoryService.retrieveMemories(query, retrieveOptions);
|
|
27950
|
+
return { memories: selectMcpMemoryResults(result2.memories, options.topK, options.eventType) };
|
|
27764
27951
|
} catch (error) {
|
|
27765
27952
|
if (!isVectorSchemaMismatchError(error)) {
|
|
27766
27953
|
throw error;
|
|
27767
27954
|
}
|
|
27768
27955
|
try {
|
|
27769
|
-
const
|
|
27956
|
+
const candidates = options.sessionId ? rankSessionKeywordMatches(
|
|
27770
27957
|
query,
|
|
27771
27958
|
await memoryService.getSessionHistory(options.sessionId),
|
|
27772
|
-
|
|
27773
|
-
) : await memoryService.keywordSearch(query, { topK:
|
|
27774
|
-
return { memories, warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
27959
|
+
fetchTopK
|
|
27960
|
+
) : await memoryService.keywordSearch(query, { topK: fetchTopK });
|
|
27961
|
+
return { memories: selectMcpMemoryResults(candidates, options.topK, options.eventType), warning: SEMANTIC_VECTOR_FALLBACK_WARNING };
|
|
27775
27962
|
} catch {
|
|
27776
27963
|
return { memories: [], warning: SEMANTIC_VECTOR_FALLBACK_FAILED_WARNING };
|
|
27777
27964
|
}
|
|
27778
27965
|
}
|
|
27779
27966
|
}
|
|
27967
|
+
function selectMcpMemoryResults(memories, topK, eventType) {
|
|
27968
|
+
return memories.filter((memory) => !isLowSignalContextContent(memory.event.content || "")).filter((memory) => eventType === void 0 || memory.event.eventType === eventType).slice(0, topK);
|
|
27969
|
+
}
|
|
27970
|
+
function eventTypeArg(value) {
|
|
27971
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
27972
|
+
const normalized = String(value).trim();
|
|
27973
|
+
if (normalized === "user_prompt" || normalized === "agent_response" || normalized === "tool_observation" || normalized === "session_summary") {
|
|
27974
|
+
return normalized;
|
|
27975
|
+
}
|
|
27976
|
+
throw new Error("Invalid eventType: expected user_prompt, agent_response, tool_observation, or session_summary");
|
|
27977
|
+
}
|
|
27780
27978
|
function isVectorSchemaMismatchError(error) {
|
|
27781
27979
|
const message = error instanceof Error ? error.message : String(error);
|
|
27782
27980
|
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 +28010,7 @@ async function handleMemTimeline(memoryService, args) {
|
|
|
27812
28010
|
lines.push(`Event ${targetId} not found.`);
|
|
27813
28011
|
continue;
|
|
27814
28012
|
}
|
|
27815
|
-
const sessionEvents = recentEvents.filter((e) => e.sessionId === targetEvent.sessionId).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
28013
|
+
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
28014
|
const eventIndex = sessionEvents.findIndex((e) => e.id === targetEvent.id);
|
|
27817
28015
|
const start = Math.max(0, eventIndex - windowSize);
|
|
27818
28016
|
const end = Math.min(sessionEvents.length, eventIndex + windowSize + 1);
|
|
@@ -27823,7 +28021,8 @@ async function handleMemTimeline(memoryService, args) {
|
|
|
27823
28021
|
const isTarget = e.id === targetEvent.id;
|
|
27824
28022
|
const marker = isTarget ? "**\u2192**" : " ";
|
|
27825
28023
|
const time = e.timestamp.toLocaleTimeString();
|
|
27826
|
-
const
|
|
28024
|
+
const lowSignal = isLowSignalContextContent(e.content || "");
|
|
28025
|
+
const preview = lowSignal ? "[low-signal context artifact suppressed; use mem-details for raw source]" : sanitizeOperationString(e.content, 60);
|
|
27827
28026
|
const citationId = generateCitationId(e.id);
|
|
27828
28027
|
lines.push(`${marker} ${time} [${citationId}] ${e.eventType}: ${preview}`);
|
|
27829
28028
|
}
|
|
@@ -27875,6 +28074,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
27875
28074
|
const projectPath = optionalString(args.projectPath);
|
|
27876
28075
|
const maxContextChars = contextPackMaxCharsArg(args);
|
|
27877
28076
|
const compression = contextCompressionModeArg(args.compression, maxContextChars !== void 0);
|
|
28077
|
+
const retrievalMode = contextPackRetrievalModeArg(args.retrievalMode);
|
|
27878
28078
|
const compressionTelemetry = [];
|
|
27879
28079
|
const formatOptions = {
|
|
27880
28080
|
compression,
|
|
@@ -27895,7 +28095,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
27895
28095
|
sessionsDir: optionalString(args.sessionsDir),
|
|
27896
28096
|
stateDb: optionalString(args.stateDb)
|
|
27897
28097
|
}) : void 0;
|
|
27898
|
-
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId });
|
|
28098
|
+
const search = await retrieveMcpMemories(memoryService, query, { topK: retrievalTopK, sessionId, retrievalMode });
|
|
27899
28099
|
const recentEvents = await memoryService.getRecentEvents(recentLimit);
|
|
27900
28100
|
const timelineEvents = selectContextPackTimelineEvents(
|
|
27901
28101
|
recentEvents,
|
|
@@ -27955,6 +28155,7 @@ async function handleMemContextPack(memoryService, args) {
|
|
|
27955
28155
|
if (compression !== "off" || maxContextChars !== void 0) {
|
|
27956
28156
|
lines.push("### Compression Notice", "");
|
|
27957
28157
|
lines.push("- Context-pack compression is applied only to LLM-facing previews; original events remain available through source refs.");
|
|
28158
|
+
lines.push("- Privacy filters run before compression and final preview sanitization runs after compression.");
|
|
27958
28159
|
if (maxContextChars !== void 0) {
|
|
27959
28160
|
lines.push(`- Hard output budget: ${maxContextChars} characters; use follow-up lookups to expand trimmed sources.`);
|
|
27960
28161
|
}
|
|
@@ -28096,7 +28297,8 @@ async function handleMemProjectTimeline(memoryService, args) {
|
|
|
28096
28297
|
const limit = numberArg(args.limit, 50, 1, 500);
|
|
28097
28298
|
const sessionLimit = numberArg(args.sessionLimit, 10, 1, 50);
|
|
28098
28299
|
const recentEvents = await memoryService.getRecentEvents(limit);
|
|
28099
|
-
const
|
|
28300
|
+
const timelineEvents = recentEvents.filter((event) => !isLowSignalContextContent(event.content || ""));
|
|
28301
|
+
const sessions = summarizeSessions(timelineEvents, sessionLimit);
|
|
28100
28302
|
const lines = [
|
|
28101
28303
|
"## Project Memory Timeline",
|
|
28102
28304
|
"",
|
|
@@ -28117,7 +28319,10 @@ async function handleMemSourceRef(memoryService, args) {
|
|
|
28117
28319
|
const ids = Array.isArray(args.ids) ? args.ids.map((id) => String(id)).filter((id) => id.trim().length > 0) : [];
|
|
28118
28320
|
const maxContentChars = numberArg(args.maxContentChars, 500, 80, 2e3);
|
|
28119
28321
|
const lookupLimit = numberArg(args.lookupLimit, 1e4, 1, 5e4);
|
|
28322
|
+
const includeNeighbors = args.includeNeighbors === true;
|
|
28323
|
+
const neighborWindow = includeNeighbors ? numberArg(args.neighborWindow, 1, 0, 5) : 0;
|
|
28120
28324
|
const recentEvents = await memoryService.getRecentEvents(lookupLimit);
|
|
28325
|
+
const sessionEventCache = /* @__PURE__ */ new Map();
|
|
28121
28326
|
const lines = ["## Source References", ""];
|
|
28122
28327
|
if (ids.length === 0) {
|
|
28123
28328
|
lines.push("No IDs supplied.", "");
|
|
@@ -28145,6 +28350,19 @@ async function handleMemSourceRef(memoryService, args) {
|
|
|
28145
28350
|
}
|
|
28146
28351
|
lines.push("- Redacted Preview:");
|
|
28147
28352
|
lines.push(` > ${safeInline(event.content, maxContentChars)}`);
|
|
28353
|
+
if (neighborWindow > 0) {
|
|
28354
|
+
try {
|
|
28355
|
+
let sessionEventsPromise = sessionEventCache.get(event.sessionId);
|
|
28356
|
+
if (!sessionEventsPromise) {
|
|
28357
|
+
sessionEventsPromise = memoryService.getSessionHistory(event.sessionId);
|
|
28358
|
+
sessionEventCache.set(event.sessionId, sessionEventsPromise);
|
|
28359
|
+
}
|
|
28360
|
+
const sessionEvents = await sessionEventsPromise;
|
|
28361
|
+
appendSourceRefNeighborContext(lines, event, sessionEvents, neighborWindow, maxContentChars);
|
|
28362
|
+
} catch {
|
|
28363
|
+
lines.push("- Neighbor Context: unavailable (details suppressed)");
|
|
28364
|
+
}
|
|
28365
|
+
}
|
|
28148
28366
|
lines.push("");
|
|
28149
28367
|
}
|
|
28150
28368
|
return textResult(lines.join("\n"));
|
|
@@ -28374,12 +28592,38 @@ function sourceTypeForEvent(event) {
|
|
|
28374
28592
|
if (typeof event.metadata?.transcriptPath === "string") return "transcript";
|
|
28375
28593
|
return "raw_event";
|
|
28376
28594
|
}
|
|
28595
|
+
function appendSourceRefNeighborContext(lines, event, sessionEvents, neighborWindow, maxContentChars) {
|
|
28596
|
+
const sorted = sessionEvents.slice().sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime() || a.id.localeCompare(b.id));
|
|
28597
|
+
const targetIndex = sorted.findIndex((candidate) => candidate.id === event.id);
|
|
28598
|
+
lines.push("- Neighbor Context:");
|
|
28599
|
+
if (targetIndex < 0) {
|
|
28600
|
+
lines.push(" - unavailable (source event not found in session history)");
|
|
28601
|
+
return;
|
|
28602
|
+
}
|
|
28603
|
+
const start = Math.max(0, targetIndex - neighborWindow);
|
|
28604
|
+
const end = Math.min(sorted.length, targetIndex + neighborWindow + 1);
|
|
28605
|
+
const neighbors = sorted.map((candidate, index) => ({ event: candidate, index })).slice(start, end).filter((candidate) => candidate.event.id !== event.id);
|
|
28606
|
+
if (neighbors.length === 0) {
|
|
28607
|
+
lines.push(" - none within requested window");
|
|
28608
|
+
return;
|
|
28609
|
+
}
|
|
28610
|
+
const previewChars = Math.max(80, Math.min(maxContentChars, 300));
|
|
28611
|
+
for (const neighbor of neighbors) {
|
|
28612
|
+
const direction = neighbor.index < targetIndex ? "before" : "after";
|
|
28613
|
+
const citationId = generateCitationId(neighbor.event.id);
|
|
28614
|
+
lines.push(
|
|
28615
|
+
` - ${direction} [mem:${citationId}] sourceRef=event:${neighbor.event.id} type=${neighbor.event.eventType} timestamp=${neighbor.event.timestamp.toISOString()}`
|
|
28616
|
+
);
|
|
28617
|
+
lines.push(` > ${safeInline(neighbor.event.content, previewChars)}`);
|
|
28618
|
+
}
|
|
28619
|
+
}
|
|
28377
28620
|
function formatRelevantMemoryLine(event, score, index, formatOptions = DEFAULT_CONTEXT_PACK_FORMAT_OPTIONS) {
|
|
28378
28621
|
const citationId = generateCitationId(event.id);
|
|
28622
|
+
const previewBudget = formatOptions.compression === "off" ? 260 : 180;
|
|
28379
28623
|
return [
|
|
28380
28624
|
`${index}. [mem:${citationId}] score=${score.toFixed(2)} type=${event.eventType} date=${event.timestamp.toISOString()} session=${event.sessionId}`,
|
|
28381
28625
|
` source=${sourceForEvent(event)}`,
|
|
28382
|
-
` ${contextPackPreview(event,
|
|
28626
|
+
` ${contextPackPreview(event, previewBudget, formatOptions)}`,
|
|
28383
28627
|
""
|
|
28384
28628
|
].join("\n");
|
|
28385
28629
|
}
|
|
@@ -28456,6 +28700,7 @@ function contextPackPreview(event, maxLength, options = DEFAULT_CONTEXT_PACK_FOR
|
|
|
28456
28700
|
const compressed = MCP_CONTEXT_COMPRESSOR.compress(privacyFiltered, {
|
|
28457
28701
|
mode: options.compression,
|
|
28458
28702
|
source: sourceForEvent(event),
|
|
28703
|
+
sourceRef: `mem:${generateCitationId(event.id)}`,
|
|
28459
28704
|
metadata: {
|
|
28460
28705
|
...safeCompressionMetadata(event.metadata),
|
|
28461
28706
|
eventType: event.eventType
|
|
@@ -28483,8 +28728,9 @@ function appendCompressionTelemetry(lines, metadata, maxChars) {
|
|
|
28483
28728
|
const summary = summarizeCompressionTelemetry(metadata);
|
|
28484
28729
|
const sources = summary.bySource.map((item) => `${item.source}:${item.items}`).join(",");
|
|
28485
28730
|
const strategies = summary.byStrategy.map((item) => `${item.strategy}:${item.items}`).join(",");
|
|
28731
|
+
const sourceRefsAvailable = metadata.filter((item) => item.sourceRef !== void 0).length;
|
|
28486
28732
|
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"}`,
|
|
28733
|
+
`- 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
28734
|
""
|
|
28489
28735
|
);
|
|
28490
28736
|
}
|
|
@@ -28494,18 +28740,32 @@ function contextCompressionModeArg(value, hasBudget) {
|
|
|
28494
28740
|
if (normalized === "off" || normalized === "safe" || normalized === "aggressive") return normalized;
|
|
28495
28741
|
throw new Error("Invalid compression: expected off, safe, or aggressive");
|
|
28496
28742
|
}
|
|
28743
|
+
function contextPackRetrievalModeArg(value) {
|
|
28744
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
28745
|
+
const normalized = String(value).trim().toLowerCase();
|
|
28746
|
+
if (normalized === "session-event-hybrid") return "session-event-hybrid";
|
|
28747
|
+
if (normalized === "event") return "event";
|
|
28748
|
+
throw new Error("Invalid retrievalMode: expected session-event-hybrid or event");
|
|
28749
|
+
}
|
|
28750
|
+
function validateMemContextPackBudgetArgs(args) {
|
|
28751
|
+
const maxContextChars = contextPackMaxCharsArg(args);
|
|
28752
|
+
contextCompressionModeArg(args.compression, maxContextChars !== void 0);
|
|
28753
|
+
contextPackRetrievalModeArg(args.retrievalMode);
|
|
28754
|
+
}
|
|
28497
28755
|
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));
|
|
28756
|
+
const maxChars = optionalNumberArg(args.maxChars, "maxChars", CONTEXT_PACK_MIN_HARD_BUDGET_CHARS, CONTEXT_PACK_MAX_CHARS_CAP);
|
|
28757
|
+
const maxTokens = optionalNumberArg(args.maxTokens, "maxTokens", 250, Math.floor(CONTEXT_PACK_MAX_CHARS_CAP / 4));
|
|
28500
28758
|
const tokenChars = maxTokens === void 0 ? void 0 : maxTokens * 4;
|
|
28501
28759
|
if (maxChars !== void 0 && tokenChars !== void 0) return Math.min(maxChars, tokenChars);
|
|
28502
28760
|
return maxChars ?? tokenChars;
|
|
28503
28761
|
}
|
|
28504
|
-
function optionalNumberArg(value, min, max) {
|
|
28762
|
+
function optionalNumberArg(value, name, min, max) {
|
|
28505
28763
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
28506
28764
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
28507
|
-
if (!Number.isFinite(parsed)
|
|
28508
|
-
|
|
28765
|
+
if (!Number.isFinite(parsed) || parsed < min || parsed > max) {
|
|
28766
|
+
throw new Error(`Invalid ${name}: expected number between ${min} and ${max}`);
|
|
28767
|
+
}
|
|
28768
|
+
return Math.floor(parsed);
|
|
28509
28769
|
}
|
|
28510
28770
|
function applyContextPackBudget(text, maxChars) {
|
|
28511
28771
|
if (maxChars === void 0 || text.length <= maxChars) return text;
|