@hashgraphonline/conversational-agent 0.1.206 → 0.1.207

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.
@@ -1,233 +1,663 @@
1
- class MCPContentProcessor {
2
- constructor(contentStorage, logger) {
3
- this.contentStorage = contentStorage;
4
- this.logger = logger;
5
- }
6
- analyzeResponse(response) {
7
- const contents = [];
8
- let totalSize = 0;
9
- this.extractContentFromResponse(response, contents);
10
- totalSize = contents.reduce((sum, content) => sum + content.sizeBytes, 0);
11
- const largestContentSize = contents.reduce((max, content) => Math.max(max, content.sizeBytes), 0);
12
- const shouldProcess = contents.some(
13
- (content) => this.contentStorage.shouldUseReference(
14
- typeof content.content === "string" ? content.content : JSON.stringify(content.content)
15
- )
16
- );
1
+ import { ReferenceIdGenerator } from "./index24.js";
2
+ import { DEFAULT_CONTENT_REFERENCE_CONFIG, ContentReferenceError } from "./index25.js";
3
+ const _ContentStorage = class _ContentStorage {
4
+ constructor(maxStorage = _ContentStorage.DEFAULT_MAX_STORAGE, referenceConfig) {
5
+ this.messages = [];
6
+ this.idCounter = 0;
7
+ this.contentStore = /* @__PURE__ */ new Map();
8
+ this.maxStorage = maxStorage;
9
+ this.referenceConfig = { ...DEFAULT_CONTENT_REFERENCE_CONFIG, ...referenceConfig };
10
+ this.referenceStats = {
11
+ activeReferences: 0,
12
+ totalStorageBytes: 0,
13
+ recentlyCleanedUp: 0,
14
+ totalResolutions: 0,
15
+ failedResolutions: 0,
16
+ averageContentSize: 0,
17
+ storageUtilization: 0,
18
+ performanceMetrics: {
19
+ averageCreationTimeMs: 0,
20
+ averageResolutionTimeMs: 0,
21
+ averageCleanupTimeMs: 0,
22
+ creationTimes: [],
23
+ resolutionTimes: [],
24
+ cleanupTimes: []
25
+ }
26
+ };
27
+ if (this.referenceConfig.enableAutoCleanup) {
28
+ this.startReferenceCleanupTimer();
29
+ }
30
+ }
31
+ /**
32
+ * Store messages in the content storage
33
+ * Automatically drops oldest messages if storage limit is exceeded
34
+ * @param messages - Messages to store
35
+ * @returns Result indicating how many messages were stored and dropped
36
+ */
37
+ storeMessages(messages) {
38
+ if (messages.length === 0) {
39
+ return { stored: 0, dropped: 0 };
40
+ }
41
+ const now = /* @__PURE__ */ new Date();
42
+ let dropped = 0;
43
+ const storedMessages = messages.map((message) => ({
44
+ message,
45
+ storedAt: now,
46
+ id: this.generateId()
47
+ }));
48
+ this.messages.push(...storedMessages);
49
+ while (this.messages.length > this.maxStorage) {
50
+ this.messages.shift();
51
+ dropped++;
52
+ }
53
+ return {
54
+ stored: storedMessages.length,
55
+ dropped
56
+ };
57
+ }
58
+ /**
59
+ * Get the most recent messages from storage
60
+ * @param count - Number of recent messages to retrieve
61
+ * @returns Array of recent messages in chronological order
62
+ */
63
+ getRecentMessages(count) {
64
+ if (count <= 0 || this.messages.length === 0) {
65
+ return [];
66
+ }
67
+ const startIndex = Math.max(0, this.messages.length - count);
68
+ return this.messages.slice(startIndex).map((stored) => stored.message);
69
+ }
70
+ /**
71
+ * Search for messages containing specific text or patterns
72
+ * @param query - Search term or regex pattern
73
+ * @param options - Search configuration options
74
+ * @returns Array of matching messages
75
+ */
76
+ searchMessages(query, options = {}) {
77
+ if (!query || this.messages.length === 0) {
78
+ return [];
79
+ }
80
+ const {
81
+ caseSensitive = false,
82
+ limit,
83
+ useRegex = false
84
+ } = options;
85
+ let matches = [];
86
+ if (useRegex) {
87
+ try {
88
+ const regex = new RegExp(query, caseSensitive ? "g" : "gi");
89
+ matches = this.messages.filter((stored) => regex.test(stored.message.content)).map((stored) => stored.message);
90
+ } catch (error) {
91
+ console.warn("Invalid regex pattern:", query, error);
92
+ return [];
93
+ }
94
+ } else {
95
+ const searchTerm = caseSensitive ? query : query.toLowerCase();
96
+ matches = this.messages.filter((stored) => {
97
+ const content = stored.message.content;
98
+ const searchContent = caseSensitive ? content : content.toLowerCase();
99
+ return searchContent.includes(searchTerm);
100
+ }).map((stored) => stored.message);
101
+ }
102
+ return limit ? matches.slice(0, limit) : matches;
103
+ }
104
+ /**
105
+ * Get messages from a specific time range
106
+ * @param startTime - Start of time range (inclusive)
107
+ * @param endTime - End of time range (inclusive)
108
+ * @returns Array of messages within the time range
109
+ */
110
+ getMessagesFromTimeRange(startTime, endTime) {
111
+ if (startTime > endTime || this.messages.length === 0) {
112
+ return [];
113
+ }
114
+ return this.messages.filter(
115
+ (stored) => stored.storedAt >= startTime && stored.storedAt <= endTime
116
+ ).map((stored) => stored.message);
117
+ }
118
+ /**
119
+ * Get storage statistics and usage information
120
+ * @returns Current storage statistics
121
+ */
122
+ getStorageStats() {
123
+ const totalMessages = this.messages.length;
124
+ const usagePercentage = totalMessages > 0 ? Math.round(totalMessages / this.maxStorage * 100) : 0;
125
+ let oldestMessageTime;
126
+ let newestMessageTime;
127
+ if (totalMessages > 0) {
128
+ oldestMessageTime = this.messages[0].storedAt;
129
+ newestMessageTime = this.messages[totalMessages - 1].storedAt;
130
+ }
131
+ return {
132
+ totalMessages,
133
+ maxStorageLimit: this.maxStorage,
134
+ usagePercentage,
135
+ oldestMessageTime,
136
+ newestMessageTime
137
+ };
138
+ }
139
+ /**
140
+ * Clear all stored messages
141
+ */
142
+ clear() {
143
+ this.messages = [];
144
+ this.idCounter = 0;
145
+ }
146
+ /**
147
+ * Get total number of stored messages
148
+ * @returns Number of messages currently in storage
149
+ */
150
+ getTotalStoredMessages() {
151
+ return this.messages.length;
152
+ }
153
+ /**
154
+ * Update the maximum storage limit
155
+ * @param newLimit - New maximum storage limit
156
+ */
157
+ updateStorageLimit(newLimit) {
158
+ if (newLimit <= 0) {
159
+ throw new Error("Storage limit must be greater than 0");
160
+ }
161
+ this.maxStorage = newLimit;
162
+ while (this.messages.length > this.maxStorage) {
163
+ this.messages.shift();
164
+ }
165
+ }
166
+ /**
167
+ * Get messages by message type
168
+ * @param messageType - Type of messages to retrieve ('human', 'ai', 'system', etc.)
169
+ * @param limit - Maximum number of messages to return
170
+ * @returns Array of messages of the specified type
171
+ */
172
+ getMessagesByType(messageType, limit) {
173
+ const filtered = this.messages.filter((stored) => stored.message._getType() === messageType).map((stored) => stored.message);
174
+ return limit ? filtered.slice(0, limit) : filtered;
175
+ }
176
+ /**
177
+ * Get the current storage configuration
178
+ * @returns Storage configuration object
179
+ */
180
+ getConfig() {
17
181
  return {
18
- shouldProcess,
19
- contents,
20
- totalSize,
21
- largestContentSize
182
+ maxStorage: this.maxStorage,
183
+ currentUsage: this.messages.length,
184
+ utilizationPercentage: this.messages.length / this.maxStorage * 100
22
185
  };
23
186
  }
24
- async processResponse(response, serverName, toolName) {
187
+ /**
188
+ * Generate a unique ID for stored messages
189
+ * @returns Unique string identifier
190
+ */
191
+ generateId() {
192
+ return `msg_${++this.idCounter}_${Date.now()}`;
193
+ }
194
+ /**
195
+ * Get messages stored within the last N minutes
196
+ * @param minutes - Number of minutes to look back
197
+ * @returns Array of messages from the last N minutes
198
+ */
199
+ getRecentMessagesByTime(minutes) {
200
+ if (minutes <= 0 || this.messages.length === 0) {
201
+ return [];
202
+ }
203
+ const cutoffTime = new Date(Date.now() - minutes * 60 * 1e3);
204
+ return this.messages.filter((stored) => stored.storedAt >= cutoffTime).map((stored) => stored.message);
205
+ }
206
+ /**
207
+ * Export messages to a JSON-serializable format
208
+ * @returns Serializable representation of stored messages
209
+ */
210
+ exportMessages() {
211
+ return this.messages.map((stored) => ({
212
+ content: stored.message.content,
213
+ type: stored.message._getType(),
214
+ storedAt: stored.storedAt.toISOString(),
215
+ id: stored.id
216
+ }));
217
+ }
218
+ // ========== Reference-Based Content Storage Methods ==========
219
+ /**
220
+ * Determine if content should be stored as a reference based on size
221
+ */
222
+ shouldUseReference(content) {
223
+ const size = Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, "utf8");
224
+ return size > this.referenceConfig.sizeThresholdBytes;
225
+ }
226
+ /**
227
+ * Store content and return a reference if it exceeds the size threshold
228
+ * Otherwise returns null to indicate direct content should be used
229
+ */
230
+ async storeContentIfLarge(content, metadata) {
231
+ const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content, "utf8");
232
+ if (!this.shouldUseReference(buffer)) {
233
+ return null;
234
+ }
235
+ const storeMetadata = {
236
+ contentType: metadata.contentType || this.detectContentType(buffer, metadata.mimeType),
237
+ sizeBytes: buffer.length,
238
+ source: metadata.source,
239
+ tags: []
240
+ };
241
+ if (metadata.mimeType !== void 0) {
242
+ storeMetadata.mimeType = metadata.mimeType;
243
+ }
244
+ if (metadata.mcpToolName !== void 0) {
245
+ storeMetadata.mcpToolName = metadata.mcpToolName;
246
+ }
247
+ if (metadata.fileName !== void 0) {
248
+ storeMetadata.fileName = metadata.fileName;
249
+ }
250
+ if (metadata.tags !== void 0) {
251
+ storeMetadata.tags = metadata.tags;
252
+ }
253
+ if (metadata.customMetadata !== void 0) {
254
+ storeMetadata.customMetadata = metadata.customMetadata;
255
+ }
256
+ return await this.storeContent(buffer, storeMetadata);
257
+ }
258
+ /**
259
+ * Store content and return a reference (implements ContentReferenceStore)
260
+ */
261
+ async storeContent(content, metadata) {
262
+ const startTime = Date.now();
25
263
  try {
26
- const analysis = this.analyzeResponse(response);
27
- if (!analysis.shouldProcess) {
264
+ const now = /* @__PURE__ */ new Date();
265
+ const referenceId = ReferenceIdGenerator.generateId(content);
266
+ const fullMetadata = {
267
+ ...metadata,
268
+ createdAt: now,
269
+ lastAccessedAt: now,
270
+ accessCount: 0
271
+ };
272
+ const storedContent = {
273
+ content,
274
+ metadata: fullMetadata,
275
+ state: "active"
276
+ };
277
+ const expirationTime = this.calculateExpirationTime(metadata.source);
278
+ if (expirationTime !== void 0) {
279
+ storedContent.expiresAt = expirationTime;
280
+ }
281
+ this.contentStore.set(referenceId, storedContent);
282
+ this.updateStatsAfterStore(content.length);
283
+ await this.enforceReferenceStorageLimits();
284
+ const preview = this.createContentPreview(content, fullMetadata.contentType);
285
+ const referenceMetadata = {
286
+ contentType: fullMetadata.contentType,
287
+ sizeBytes: fullMetadata.sizeBytes,
288
+ source: fullMetadata.source
289
+ };
290
+ if (fullMetadata.fileName !== void 0) {
291
+ referenceMetadata.fileName = fullMetadata.fileName;
292
+ }
293
+ if (fullMetadata.mimeType !== void 0) {
294
+ referenceMetadata.mimeType = fullMetadata.mimeType;
295
+ }
296
+ const reference = {
297
+ referenceId,
298
+ state: "active",
299
+ preview,
300
+ metadata: referenceMetadata,
301
+ createdAt: now,
302
+ format: "ref://{id}"
303
+ };
304
+ const duration = Date.now() - startTime;
305
+ this.recordPerformanceMetric("creation", duration);
306
+ console.log(`[ContentStorage] Stored content with reference ID: ${referenceId} (${content.length} bytes)`);
307
+ return reference;
308
+ } catch (error) {
309
+ const duration = Date.now() - startTime;
310
+ this.recordPerformanceMetric("creation", duration);
311
+ console.error("[ContentStorage] Failed to store content:", error);
312
+ throw new ContentReferenceError(
313
+ `Failed to store content: ${error instanceof Error ? error.message : "Unknown error"}`,
314
+ "system_error",
315
+ void 0,
316
+ ["Try again", "Check storage limits", "Contact administrator"]
317
+ );
318
+ }
319
+ }
320
+ /**
321
+ * Resolve a reference to its content (implements ContentReferenceStore)
322
+ */
323
+ async resolveReference(referenceId) {
324
+ const startTime = Date.now();
325
+ try {
326
+ if (!ReferenceIdGenerator.isValidReferenceId(referenceId)) {
327
+ this.referenceStats.failedResolutions++;
28
328
  return {
29
- content: response,
30
- wasProcessed: false
329
+ success: false,
330
+ error: "Invalid reference ID format",
331
+ errorType: "not_found",
332
+ suggestedActions: ["Check the reference ID format", "Ensure the reference ID is complete"]
31
333
  };
32
334
  }
33
- const processedResponse = await this.createReferencedResponse(
34
- response,
35
- analysis,
36
- serverName,
37
- toolName
38
- );
39
- return processedResponse;
335
+ const storedContent = this.contentStore.get(referenceId);
336
+ if (!storedContent) {
337
+ this.referenceStats.failedResolutions++;
338
+ return {
339
+ success: false,
340
+ error: "Reference not found",
341
+ errorType: "not_found",
342
+ suggestedActions: ["Verify the reference ID", "Check if the content has expired", "Request fresh content"]
343
+ };
344
+ }
345
+ if (storedContent.expiresAt && storedContent.expiresAt < /* @__PURE__ */ new Date()) {
346
+ storedContent.state = "expired";
347
+ this.referenceStats.failedResolutions++;
348
+ return {
349
+ success: false,
350
+ error: "Reference has expired",
351
+ errorType: "expired",
352
+ suggestedActions: ["Request fresh content", "Use alternative content source"]
353
+ };
354
+ }
355
+ if (storedContent.state !== "active") {
356
+ this.referenceStats.failedResolutions++;
357
+ return {
358
+ success: false,
359
+ error: `Reference is ${storedContent.state}`,
360
+ errorType: storedContent.state === "expired" ? "expired" : "corrupted",
361
+ suggestedActions: ["Request fresh content", "Check reference validity"]
362
+ };
363
+ }
364
+ storedContent.metadata.lastAccessedAt = /* @__PURE__ */ new Date();
365
+ storedContent.metadata.accessCount++;
366
+ this.referenceStats.totalResolutions++;
367
+ const duration = Date.now() - startTime;
368
+ this.recordPerformanceMetric("resolution", duration);
369
+ console.log(`[ContentStorage] Resolved reference ${referenceId} (${storedContent.content.length} bytes, access count: ${storedContent.metadata.accessCount})`);
370
+ return {
371
+ success: true,
372
+ content: storedContent.content,
373
+ metadata: storedContent.metadata
374
+ };
40
375
  } catch (error) {
41
- this.logger.error("Error processing MCP response:", error);
376
+ const duration = Date.now() - startTime;
377
+ this.recordPerformanceMetric("resolution", duration);
378
+ this.referenceStats.failedResolutions++;
379
+ console.error(`[ContentStorage] Error resolving reference ${referenceId}:`, error);
42
380
  return {
43
- content: response,
44
- wasProcessed: false,
45
- errors: [error instanceof Error ? error.message : "Unknown processing error"]
381
+ success: false,
382
+ error: `System error resolving reference: ${error instanceof Error ? error.message : "Unknown error"}`,
383
+ errorType: "system_error",
384
+ suggestedActions: ["Try again", "Contact administrator"]
46
385
  };
47
386
  }
48
387
  }
49
- extractContentFromResponse(obj, contents) {
50
- if (obj === null || obj === void 0) {
51
- return;
52
- }
53
- if (Array.isArray(obj)) {
54
- obj.forEach((item) => this.extractContentFromResponse(item, contents));
55
- return;
56
- }
57
- if (typeof obj === "object") {
58
- const record = obj;
59
- if (record.type === "text" && typeof record.text === "string") {
60
- contents.push({
61
- content: record.text,
62
- type: "text",
63
- sizeBytes: Buffer.byteLength(record.text, "utf8"),
64
- mimeType: "text/plain"
65
- });
66
- return;
67
- }
68
- if (record.type === "image" && typeof record.data === "string") {
69
- contents.push({
70
- content: record.data,
71
- type: "image",
72
- sizeBytes: Math.ceil(record.data.length * 0.75),
73
- mimeType: record.mimeType || "image/jpeg"
74
- });
75
- return;
76
- }
77
- if (record.type === "resource" && record.resource) {
78
- const resourceStr = JSON.stringify(record.resource);
79
- contents.push({
80
- content: resourceStr,
81
- type: "resource",
82
- sizeBytes: Buffer.byteLength(resourceStr, "utf8"),
83
- mimeType: "application/json"
84
- });
85
- return;
86
- }
87
- Object.values(record).forEach((value) => this.extractContentFromResponse(value, contents));
88
- return;
89
- }
90
- if (typeof obj === "string") {
91
- if (obj.length > 1e3) {
92
- contents.push({
93
- content: obj,
94
- type: "text",
95
- sizeBytes: Buffer.byteLength(obj, "utf8"),
96
- mimeType: this.detectMimeType(obj)
97
- });
98
- }
99
- }
100
- }
101
- async createReferencedResponse(originalResponse, analysis, serverName, toolName) {
102
- const processedResponse = this.deepClone(originalResponse);
103
- const errors = [];
104
- let referenceCreated = false;
105
- let totalReferenceSize = 0;
106
- for (const contentInfo of analysis.contents) {
107
- if (this.contentStorage.shouldUseReference(
108
- typeof contentInfo.content === "string" ? contentInfo.content : JSON.stringify(contentInfo.content)
109
- )) {
110
- try {
111
- const contentBuffer = Buffer.from(
112
- typeof contentInfo.content === "string" ? contentInfo.content : JSON.stringify(contentInfo.content),
113
- "utf8"
114
- );
115
- const contentType = this.mapMimeTypeToContentType(contentInfo.mimeType);
116
- const metadata = {
117
- contentType,
118
- source: "mcp_tool",
119
- mcpToolName: `${serverName}::${toolName}`,
120
- tags: ["mcp_response", serverName, toolName]
121
- };
122
- if (contentInfo.mimeType !== void 0) {
123
- metadata.mimeType = contentInfo.mimeType;
124
- }
125
- const reference = await this.contentStorage.storeContentIfLarge(
126
- contentBuffer,
127
- metadata
128
- );
129
- if (reference) {
130
- this.replaceContentInResponse(
131
- processedResponse,
132
- contentInfo.content,
133
- this.createLightweightReference(reference)
134
- );
135
- referenceCreated = true;
136
- totalReferenceSize += contentBuffer.length;
137
- }
138
- } catch (error) {
139
- errors.push(`Failed to create reference: ${error instanceof Error ? error.message : "Unknown error"}`);
140
- }
141
- }
388
+ /**
389
+ * Check if a reference exists and is valid
390
+ */
391
+ async hasReference(referenceId) {
392
+ if (!ReferenceIdGenerator.isValidReferenceId(referenceId)) {
393
+ return false;
142
394
  }
143
- const result = {
144
- content: processedResponse,
145
- wasProcessed: true,
146
- referenceCreated,
147
- originalSize: totalReferenceSize
148
- };
149
- if (errors.length > 0) {
150
- result.errors = errors;
395
+ const storedContent = this.contentStore.get(referenceId);
396
+ if (!storedContent) {
397
+ return false;
398
+ }
399
+ if (storedContent.expiresAt && storedContent.expiresAt < /* @__PURE__ */ new Date()) {
400
+ storedContent.state = "expired";
401
+ return false;
151
402
  }
152
- return result;
403
+ return storedContent.state === "active";
153
404
  }
154
- createLightweightReference(reference) {
405
+ /**
406
+ * Mark a reference for cleanup
407
+ */
408
+ async cleanupReference(referenceId) {
409
+ const storedContent = this.contentStore.get(referenceId);
410
+ if (!storedContent) {
411
+ return false;
412
+ }
413
+ this.referenceStats.totalStorageBytes -= storedContent.content.length;
414
+ this.referenceStats.activeReferences--;
415
+ this.referenceStats.recentlyCleanedUp++;
416
+ this.contentStore.delete(referenceId);
417
+ console.log(`[ContentStorage] Cleaned up reference ${referenceId} (${storedContent.content.length} bytes)`);
418
+ return true;
419
+ }
420
+ /**
421
+ * Get current reference storage statistics (implements ContentReferenceStore)
422
+ */
423
+ async getStats() {
424
+ this.updateReferenceStorageStats();
155
425
  return {
156
- type: "content_reference",
157
- referenceId: reference.referenceId,
158
- preview: reference.preview,
159
- size: reference.metadata.sizeBytes,
160
- contentType: reference.metadata.contentType,
161
- format: "ref://{id}",
162
- _isReference: true
426
+ ...this.referenceStats,
427
+ performanceMetrics: {
428
+ averageCreationTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.creationTimes),
429
+ averageResolutionTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.resolutionTimes),
430
+ averageCleanupTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.cleanupTimes)
431
+ }
163
432
  };
164
433
  }
165
- replaceContentInResponse(obj, oldContent, newContent) {
166
- if (obj === null || obj === void 0) {
167
- return;
434
+ /**
435
+ * Update reference configuration
436
+ */
437
+ async updateConfig(config) {
438
+ this.referenceConfig = { ...this.referenceConfig, ...config };
439
+ if (this.cleanupTimer) {
440
+ clearInterval(this.cleanupTimer);
441
+ delete this.cleanupTimer;
168
442
  }
169
- if (Array.isArray(obj)) {
170
- for (let i = 0; i < obj.length; i++) {
171
- if (obj[i] === oldContent) {
172
- obj[i] = newContent;
173
- } else {
174
- this.replaceContentInResponse(obj[i], oldContent, newContent);
443
+ if (this.referenceConfig.enableAutoCleanup) {
444
+ this.startReferenceCleanupTimer();
445
+ }
446
+ console.log("[ContentStorage] Reference configuration updated");
447
+ }
448
+ /**
449
+ * Perform cleanup based on current policies (implements ContentReferenceStore)
450
+ */
451
+ async performCleanup() {
452
+ const startTime = Date.now();
453
+ const errors = [];
454
+ let cleanedUp = 0;
455
+ try {
456
+ console.log("[ContentStorage] Starting reference cleanup process...");
457
+ const now = /* @__PURE__ */ new Date();
458
+ const toCleanup = [];
459
+ for (const [referenceId, storedContent] of this.contentStore.entries()) {
460
+ let shouldCleanup = false;
461
+ if (storedContent.expiresAt && storedContent.expiresAt < now) {
462
+ shouldCleanup = true;
463
+ storedContent.state = "expired";
464
+ }
465
+ const ageMs = now.getTime() - storedContent.metadata.createdAt.getTime();
466
+ const policy = this.getCleanupPolicy(storedContent.metadata.source);
467
+ if (ageMs > policy.maxAgeMs) {
468
+ shouldCleanup = true;
469
+ }
470
+ if (storedContent.state === "cleanup_pending") {
471
+ shouldCleanup = true;
472
+ }
473
+ if (shouldCleanup) {
474
+ toCleanup.push(referenceId);
175
475
  }
176
476
  }
177
- return;
178
- }
179
- if (typeof obj === "object") {
180
- const record = obj;
181
- for (const key in record) {
182
- if (record[key] === oldContent) {
183
- record[key] = newContent;
184
- } else {
185
- this.replaceContentInResponse(record[key], oldContent, newContent);
477
+ toCleanup.sort((a, b) => {
478
+ const aContent = this.contentStore.get(a);
479
+ const bContent = this.contentStore.get(b);
480
+ const aPriority = this.getCleanupPolicy(aContent.metadata.source).priority;
481
+ const bPriority = this.getCleanupPolicy(bContent.metadata.source).priority;
482
+ return bPriority - aPriority;
483
+ });
484
+ for (const referenceId of toCleanup) {
485
+ try {
486
+ const success = await this.cleanupReference(referenceId);
487
+ if (success) {
488
+ cleanedUp++;
489
+ }
490
+ } catch (error) {
491
+ errors.push(`Failed to cleanup ${referenceId}: ${error instanceof Error ? error.message : "Unknown error"}`);
492
+ }
493
+ }
494
+ if (this.contentStore.size > this.referenceConfig.maxReferences) {
495
+ const sortedByAge = Array.from(this.contentStore.entries()).sort(([, a], [, b]) => a.metadata.lastAccessedAt.getTime() - b.metadata.lastAccessedAt.getTime());
496
+ const excessCount = this.contentStore.size - this.referenceConfig.maxReferences;
497
+ for (let i = 0; i < excessCount && i < sortedByAge.length; i++) {
498
+ const [referenceId] = sortedByAge[i];
499
+ try {
500
+ const success = await this.cleanupReference(referenceId);
501
+ if (success) {
502
+ cleanedUp++;
503
+ }
504
+ } catch (error) {
505
+ errors.push(`Failed to cleanup excess reference ${referenceId}: ${error instanceof Error ? error.message : "Unknown error"}`);
506
+ }
186
507
  }
187
508
  }
509
+ const duration = Date.now() - startTime;
510
+ this.recordPerformanceMetric("cleanup", duration);
511
+ console.log(`[ContentStorage] Reference cleanup completed: ${cleanedUp} references cleaned up, ${errors.length} errors`);
512
+ return { cleanedUp, errors };
513
+ } catch (error) {
514
+ const duration = Date.now() - startTime;
515
+ this.recordPerformanceMetric("cleanup", duration);
516
+ const errorMessage = `Cleanup process failed: ${error instanceof Error ? error.message : "Unknown error"}`;
517
+ console.error("[ContentStorage]", errorMessage);
518
+ errors.push(errorMessage);
519
+ return { cleanedUp, errors };
188
520
  }
189
521
  }
190
- detectMimeType(content) {
191
- if (content.trim().startsWith("{") || content.trim().startsWith("[")) {
192
- return "application/json";
522
+ /**
523
+ * Get reference configuration for debugging
524
+ */
525
+ getReferenceConfig() {
526
+ return { ...this.referenceConfig };
527
+ }
528
+ // ========== Private Reference Storage Helper Methods ==========
529
+ async enforceReferenceStorageLimits() {
530
+ if (this.contentStore.size >= this.referenceConfig.maxReferences) {
531
+ await this.performCleanup();
193
532
  }
194
- if (content.includes("<html>") || content.includes("<!DOCTYPE")) {
195
- return "text/html";
533
+ if (this.referenceStats.totalStorageBytes >= this.referenceConfig.maxTotalStorageBytes) {
534
+ await this.performCleanup();
196
535
  }
197
- if (content.includes("# ") || content.includes("## ")) {
198
- return "text/markdown";
536
+ }
537
+ calculateExpirationTime(source) {
538
+ const policy = this.getCleanupPolicy(source);
539
+ return new Date(Date.now() + policy.maxAgeMs);
540
+ }
541
+ getCleanupPolicy(source) {
542
+ switch (source) {
543
+ case "mcp_tool":
544
+ return this.referenceConfig.cleanupPolicies.recent;
545
+ case "user_upload":
546
+ return this.referenceConfig.cleanupPolicies.userContent;
547
+ case "agent_generated":
548
+ return this.referenceConfig.cleanupPolicies.agentGenerated;
549
+ default:
550
+ return this.referenceConfig.cleanupPolicies.default;
199
551
  }
200
- return "text/plain";
201
552
  }
202
- mapMimeTypeToContentType(mimeType) {
203
- if (!mimeType) return "text";
204
- if (mimeType.startsWith("text/plain")) return "text";
205
- if (mimeType === "application/json") return "json";
206
- if (mimeType === "text/html") return "html";
207
- if (mimeType === "text/markdown") return "markdown";
208
- if (mimeType.startsWith("text/")) return "text";
209
- return "binary";
553
+ detectContentType(content, mimeType) {
554
+ if (mimeType) {
555
+ if (mimeType === "text/html") return "html";
556
+ if (mimeType === "text/markdown") return "markdown";
557
+ if (mimeType === "application/json") return "json";
558
+ if (mimeType.startsWith("text/")) return "text";
559
+ return "binary";
560
+ }
561
+ const contentStr = content.toString("utf8", 0, Math.min(content.length, 1e3));
562
+ if (contentStr.startsWith("{") || contentStr.startsWith("[")) return "json";
563
+ if (contentStr.includes("<html>") || contentStr.includes("<!DOCTYPE")) return "html";
564
+ if (contentStr.includes("#") && contentStr.includes("\n")) return "markdown";
565
+ return "text";
210
566
  }
211
- deepClone(obj) {
212
- if (obj === null || typeof obj !== "object") {
213
- return obj;
567
+ createContentPreview(content, contentType) {
568
+ const maxLength = 200;
569
+ let preview = content.toString("utf8", 0, Math.min(content.length, maxLength * 2));
570
+ if (contentType === "html") {
571
+ preview = preview.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
572
+ } else if (contentType === "json") {
573
+ try {
574
+ const parsed = JSON.parse(preview);
575
+ preview = JSON.stringify(parsed, null, 0);
576
+ } catch {
577
+ }
214
578
  }
215
- if (obj instanceof Date) {
216
- return new Date(obj.getTime());
579
+ preview = preview.trim();
580
+ if (preview.length > maxLength) {
581
+ preview = preview.substring(0, maxLength) + "...";
217
582
  }
218
- if (Array.isArray(obj)) {
219
- return obj.map((item) => this.deepClone(item));
583
+ return preview || "[Binary content]";
584
+ }
585
+ updateStatsAfterStore(sizeBytes) {
586
+ this.referenceStats.activeReferences++;
587
+ this.referenceStats.totalStorageBytes += sizeBytes;
588
+ this.updateReferenceStorageStats();
589
+ }
590
+ updateReferenceStorageStats() {
591
+ if (this.referenceStats.activeReferences > 0) {
592
+ this.referenceStats.averageContentSize = this.referenceStats.totalStorageBytes / this.referenceStats.activeReferences;
220
593
  }
221
- const cloned = {};
222
- for (const key in obj) {
223
- if (obj.hasOwnProperty(key)) {
224
- cloned[key] = this.deepClone(obj[key]);
594
+ this.referenceStats.storageUtilization = this.referenceStats.totalStorageBytes / this.referenceConfig.maxTotalStorageBytes * 100;
595
+ let mostAccessedId;
596
+ let maxAccess = 0;
597
+ for (const [referenceId, storedContent] of this.contentStore.entries()) {
598
+ if (storedContent.metadata.accessCount > maxAccess) {
599
+ maxAccess = storedContent.metadata.accessCount;
600
+ mostAccessedId = referenceId;
225
601
  }
226
602
  }
227
- return cloned;
603
+ if (mostAccessedId !== void 0) {
604
+ this.referenceStats.mostAccessedReferenceId = mostAccessedId;
605
+ } else {
606
+ delete this.referenceStats.mostAccessedReferenceId;
607
+ }
608
+ }
609
+ recordPerformanceMetric(type, timeMs) {
610
+ const metrics = this.referenceStats.performanceMetrics;
611
+ const maxRecords = 100;
612
+ switch (type) {
613
+ case "creation":
614
+ metrics.creationTimes.push(timeMs);
615
+ if (metrics.creationTimes.length > maxRecords) {
616
+ metrics.creationTimes.shift();
617
+ }
618
+ break;
619
+ case "resolution":
620
+ metrics.resolutionTimes.push(timeMs);
621
+ if (metrics.resolutionTimes.length > maxRecords) {
622
+ metrics.resolutionTimes.shift();
623
+ }
624
+ break;
625
+ case "cleanup":
626
+ metrics.cleanupTimes.push(timeMs);
627
+ if (metrics.cleanupTimes.length > maxRecords) {
628
+ metrics.cleanupTimes.shift();
629
+ }
630
+ break;
631
+ }
228
632
  }
229
- }
633
+ calculateAverage(times) {
634
+ if (times.length === 0) return 0;
635
+ return times.reduce((sum, time) => sum + time, 0) / times.length;
636
+ }
637
+ startReferenceCleanupTimer() {
638
+ this.cleanupTimer = setInterval(async () => {
639
+ try {
640
+ await this.performCleanup();
641
+ } catch (error) {
642
+ console.error("[ContentStorage] Error in scheduled reference cleanup:", error);
643
+ }
644
+ }, this.referenceConfig.cleanupIntervalMs);
645
+ }
646
+ /**
647
+ * Clean up resources (enhanced to include reference cleanup)
648
+ */
649
+ async dispose() {
650
+ if (this.cleanupTimer) {
651
+ clearInterval(this.cleanupTimer);
652
+ delete this.cleanupTimer;
653
+ }
654
+ this.contentStore.clear();
655
+ this.clear();
656
+ }
657
+ };
658
+ _ContentStorage.DEFAULT_MAX_STORAGE = 1e3;
659
+ let ContentStorage = _ContentStorage;
230
660
  export {
231
- MCPContentProcessor
661
+ ContentStorage
232
662
  };
233
663
  //# sourceMappingURL=index20.js.map