chat-agent-toolkit 1.2.33 → 1.2.35

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.
Files changed (42) hide show
  1. package/dist/research-agent.cjs.js.map +1 -1
  2. package/dist/research-agent.es.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/config/config-manager.ts +295 -295
  5. package/src/config/config-types.ts +65 -65
  6. package/src/config/environment-variables.ts +16 -16
  7. package/src/config/index.ts +26 -26
  8. package/src/config/language-models-database.ts +1434 -1434
  9. package/src/config/mcp-server-registry.ts +24 -24
  10. package/src/config/model-registry.ts +271 -271
  11. package/src/config/model-tester.ts +211 -211
  12. package/src/config/model-utils.ts +193 -193
  13. package/src/config/provider-ui-config.ts +196 -196
  14. package/src/connectors/open-connector.json +130 -130
  15. package/src/index.ts +26 -26
  16. package/src/memory/ARCHITECTURE.md +302 -302
  17. package/src/memory/README.md +224 -224
  18. package/src/memory/agent-memory-manager.ts +416 -416
  19. package/src/memory/example.ts +343 -343
  20. package/src/memory/index.ts +47 -47
  21. package/src/memory/mastra-integration.ts +604 -604
  22. package/src/memory/storage/drizzle-storage.ts +236 -236
  23. package/src/memory/storage/in-memory-storage.ts +551 -551
  24. package/src/memory/storage/storage-interface.ts +68 -68
  25. package/src/memory/types.ts +125 -125
  26. package/src/models/types.ts +4 -4
  27. package/src/tools/index.ts +13 -13
  28. package/src/tools/open-connector-mastra.ts +270 -270
  29. package/src/tools/open-connector-mcp.ts +170 -170
  30. package/src/tools/qwksearch-api-tools.ts +326 -326
  31. package/src/tools/search/doc-utils.ts +139 -139
  32. package/src/tools/search/document.ts +47 -47
  33. package/src/tools/search/index.ts +14 -14
  34. package/src/tools/search/link-summarizer.ts +112 -112
  35. package/src/tools/search/meta-search-types.ts +62 -62
  36. package/src/tools/search/metaSearchAgent.ts +465 -465
  37. package/src/tools/search/search-handlers.ts +97 -97
  38. package/src/tools/search/suggestionGeneratorAgent.ts +56 -56
  39. package/src/types.d.ts +137 -137
  40. package/src/utils/index.ts +2 -2
  41. package/src/utils/markdown-to-html.ts +53 -53
  42. package/src/utils/outputParser.ts +73 -73
@@ -1,551 +1,551 @@
1
- // @ts-nocheck
2
- /**
3
- * Simple Memory Class
4
- *
5
- * Core memory management functionality with:
6
- * - Message deduplication
7
- * - Automatic summarization
8
- * - Vector-based relevance search
9
- * - Caching with TTL
10
- * - Batch processing
11
- * - Conflict resolution
12
- */
13
-
14
- import { writeLanguageResponse } from "write-language";
15
- import type { IMemoryStorage } from "./storage-interface";
16
- import type {
17
- MemoryRecord,
18
- Message,
19
- MemorySearchOptions,
20
- MemoryMetrics,
21
- MemoryOptions,
22
- MemoryContextOptions,
23
- ExtractedFact,
24
- MemoryType,
25
- } from "../types";
26
- import { MEMORY_CONFIG, MEMORY_TYPES } from "../types";
27
-
28
- export class SimpleMemory {
29
- private userId: string;
30
- private storage: IMemoryStorage;
31
- private maxMemories: number;
32
- private summaryThreshold: number;
33
- private cacheExpiry: number;
34
- private batchSize: number;
35
- private relevanceThreshold: number;
36
- private enableVectorSearch: boolean;
37
- private enableAutoSummarization: boolean;
38
- private recentMessages: Message[];
39
- private memoryCache: Map<string, { data: any; timestamp: number }>;
40
- private isProcessing: boolean;
41
- private processingQueue: any[];
42
- private summarizeTimeout?: NodeJS.Timeout;
43
- private metrics: {
44
- cacheHits: number;
45
- cacheMisses: number;
46
- vectorSearches: number;
47
- summarizations: number;
48
- errors: number;
49
- };
50
-
51
- /**
52
- * Initialize memory system for a user
53
- */
54
- constructor(
55
- userId: string,
56
- storage: IMemoryStorage,
57
- options: MemoryOptions = {},
58
- ) {
59
- if (!userId || !storage) {
60
- throw new Error("userId and storage are required parameters");
61
- }
62
-
63
- this.userId = userId;
64
- this.storage = storage;
65
- this.maxMemories =
66
- options.maxMemories || MEMORY_CONFIG.DEFAULT_MAX_MEMORIES;
67
- this.summaryThreshold =
68
- options.summaryThreshold || MEMORY_CONFIG.DEFAULT_SUMMARY_THRESHOLD;
69
- this.cacheExpiry =
70
- options.cacheExpiry || MEMORY_CONFIG.DEFAULT_CACHE_EXPIRY;
71
- this.batchSize = options.batchSize || MEMORY_CONFIG.DEFAULT_BATCH_SIZE;
72
- this.relevanceThreshold =
73
- options.relevanceThreshold || MEMORY_CONFIG.DEFAULT_RELEVANCE_THRESHOLD;
74
-
75
- // Feature flags
76
- this.enableVectorSearch =
77
- options.enableVectorSearch !== false &&
78
- MEMORY_CONFIG.VECTOR_SEARCH_ENABLED;
79
- this.enableAutoSummarization =
80
- options.enableAutoSummarization !== false &&
81
- MEMORY_CONFIG.AUTO_SUMMARIZATION_ENABLED;
82
-
83
- // State management
84
- this.recentMessages = [];
85
- this.memoryCache = new Map();
86
- this.isProcessing = false;
87
- this.processingQueue = [];
88
-
89
- // Performance metrics
90
- this.metrics = {
91
- cacheHits: 0,
92
- cacheMisses: 0,
93
- vectorSearches: 0,
94
- summarizations: 0,
95
- errors: 0,
96
- };
97
- }
98
-
99
- /**
100
- * Add a message to current session with intelligent deduplication
101
- */
102
- addMessage(
103
- role: "user" | "assistant",
104
- content: string,
105
- metadata: Record<string, any> = {},
106
- ): boolean {
107
- if (!role || !content || typeof content !== "string") {
108
- console.warn("Invalid message parameters:", { role, content });
109
- return false;
110
- }
111
-
112
- // Intelligent deduplication - check last few messages
113
- const lastMessages = this.recentMessages.slice(-3);
114
- const isDuplicate = lastMessages.some(
115
- (msg) =>
116
- msg.role === role &&
117
- msg.content === content &&
118
- Date.now() - msg.timestamp < 60000, // Within 1 minute
119
- );
120
-
121
- if (isDuplicate) {
122
- console.log("Duplicate message detected, skipping");
123
- return false;
124
- }
125
-
126
- // Add message with metadata
127
- const message: Message = {
128
- role,
129
- content: content.trim(),
130
- timestamp: metadata.timestamp || Date.now(),
131
- metadata: { ...metadata },
132
- };
133
-
134
- this.recentMessages.push(message);
135
-
136
- // Auto-summarization with debouncing
137
- if (
138
- this.enableAutoSummarization &&
139
- this.recentMessages.length >= this.summaryThreshold &&
140
- !this.isProcessing
141
- ) {
142
- this.debouncedSummarize();
143
- }
144
-
145
- return true;
146
- }
147
-
148
- /**
149
- * Debounced summarization to prevent excessive processing
150
- */
151
- private debouncedSummarize(): void {
152
- if (this.summarizeTimeout) {
153
- clearTimeout(this.summarizeTimeout);
154
- }
155
-
156
- this.summarizeTimeout = setTimeout(() => {
157
- this.summarizeAndStore().catch((error) => {
158
- console.error("Auto-summarization failed:", error);
159
- this.metrics.errors++;
160
- });
161
- }, 1000); // 1 second debounce
162
- }
163
-
164
- /**
165
- * Store important facts with validation and conflict resolution
166
- */
167
- async storeFact(
168
- content: string,
169
- importance: number = 1,
170
- category: MemoryType = MEMORY_TYPES.FACT,
171
- metadata: Record<string, any> = {},
172
- ): Promise<string> {
173
- // Input validation
174
- if (
175
- !content ||
176
- typeof content !== "string" ||
177
- content.trim().length === 0
178
- ) {
179
- throw new Error("Content cannot be empty");
180
- }
181
-
182
- if (
183
- importance < MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.min ||
184
- importance > MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.max
185
- ) {
186
- throw new Error(
187
- `Importance must be between ${MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.min} and ${MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.max}`,
188
- );
189
- }
190
-
191
- const normalizedContent = content.trim();
192
- const normalizedCategory = Object.values(MEMORY_TYPES).includes(category)
193
- ? category
194
- : MEMORY_TYPES.FACT;
195
-
196
- try {
197
- // Check for existing similar facts using fuzzy matching
198
- const existingFacts = await this.findSimilarFacts(normalizedContent);
199
-
200
- if (existingFacts.length > 0) {
201
- // Update existing fact if importance is higher
202
- const bestMatch = existingFacts[0];
203
- if (importance > bestMatch.importance) {
204
- await this.storage.updateMemory(bestMatch.id, {
205
- importance,
206
- updated_at: new Date(),
207
- access_count: { increment: 1 },
208
- metadata: { ...bestMatch.metadata, ...metadata },
209
- });
210
- }
211
- return bestMatch.id;
212
- }
213
-
214
- // Insert new fact
215
- const id = await this.storage.insertMemory(
216
- this.userId,
217
- normalizedCategory,
218
- normalizedContent,
219
- Math.max(0, Math.min(10, importance)),
220
- metadata,
221
- );
222
-
223
- // Clear cache after new insertion
224
- this.clearCache();
225
-
226
- return id;
227
- } catch (error) {
228
- console.error("Error storing fact:", error);
229
- this.metrics.errors++;
230
- throw error;
231
- }
232
- }
233
-
234
- /**
235
- * Find similar facts using content similarity
236
- */
237
- private async findSimilarFacts(content: string): Promise<MemoryRecord[]> {
238
- try {
239
- return await this.storage.findSimilarMemories(this.userId, content, 5);
240
- } catch (error) {
241
- console.error("Error finding similar facts:", error);
242
- return [];
243
- }
244
- }
245
-
246
- /**
247
- * Enhanced memory recall with caching and vector search
248
- */
249
- async recallRelevantMemories(
250
- query: string = "",
251
- limit: number = 10,
252
- options: MemorySearchOptions = {},
253
- ): Promise<MemoryRecord[]> {
254
- const cacheKey = `${query}-${limit}-${JSON.stringify(options)}`;
255
-
256
- // Check cache first
257
- if (this.memoryCache.has(cacheKey)) {
258
- const cached = this.memoryCache.get(cacheKey)!;
259
- if (Date.now() - cached.timestamp < this.cacheExpiry) {
260
- this.metrics.cacheHits++;
261
- return cached.data;
262
- }
263
- this.memoryCache.delete(cacheKey);
264
- }
265
-
266
- this.metrics.cacheMisses++;
267
-
268
- try {
269
- let memories = await this.storage.findMemories(
270
- this.userId,
271
- query,
272
- limit,
273
- options,
274
- );
275
-
276
- if (memories.length === 0) {
277
- return [];
278
- }
279
-
280
- // Apply vector search if enabled and query provided
281
- if (this.enableVectorSearch && query.trim() && memories.length > 0) {
282
- memories = await this.applyVectorSearch(query, memories, options);
283
- }
284
-
285
- // Apply relevance filtering
286
- if (options.minImportance) {
287
- memories = memories.filter(
288
- (m) => m.importance >= options.minImportance!,
289
- );
290
- }
291
-
292
- // Format results
293
- const result = memories.map((m) => ({
294
- ...m,
295
- relevance_score: m.relevance_score || 0,
296
- metadata: options.includeMetadata ? m.metadata : undefined,
297
- }));
298
-
299
- // Cache the result
300
- this.memoryCache.set(cacheKey, {
301
- data: result,
302
- timestamp: Date.now(),
303
- });
304
-
305
- return result;
306
- } catch (error) {
307
- console.error("Error retrieving memories:", error);
308
- this.metrics.errors++;
309
- return [];
310
- }
311
- }
312
-
313
- /**
314
- * Apply vector search to memories
315
- */
316
- private async applyVectorSearch(
317
- query: string,
318
- memories: MemoryRecord[],
319
- options: MemorySearchOptions,
320
- ): Promise<MemoryRecord[]> {
321
- // Placeholder for vector search implementation
322
- // In production, integrate with your vector similarity API
323
- // try {
324
- // this.metrics.vectorSearches++;
325
- // const sentencesByRelevance = await weighRelevanceConceptVectorAPI(
326
- // query,
327
- // memories.map(m => m.content)
328
- // );
329
- // await this.updateRelevanceScores(memories, sentencesByRelevance);
330
- // return memories
331
- // .map(memory => {
332
- // const relevanceData = sentencesByRelevance.find(s => s.sentence === memory.content);
333
- // return {
334
- // ...memory,
335
- // relevance_score: relevanceData?.relevance || 0
336
- // };
337
- // })
338
- // .sort((a, b) => b.relevance_score - a.relevance_score);
339
- // } catch (vectorError) {
340
- // console.warn('Vector search failed, falling back to basic search:', vectorError);
341
- // return memories;
342
- // }
343
- return memories;
344
- }
345
-
346
- /**
347
- * Update relevance scores for memories
348
- */
349
- private async updateRelevanceScores(
350
- memories: MemoryRecord[],
351
- sentencesByRelevance: Array<{ sentence: string; relevance: number }>,
352
- ): Promise<void> {
353
- const updates = sentencesByRelevance
354
- .filter((m) => m.relevance > this.relevanceThreshold)
355
- .map((m) => {
356
- const memory = memories.find((mem) => mem.content === m.sentence);
357
- if (memory) {
358
- return {
359
- id: memory.id,
360
- updates: {
361
- importance: Math.min(10, memory.importance + m.relevance * 0.5),
362
- access_count: { increment: 1 },
363
- updated_at: new Date(),
364
- },
365
- };
366
- }
367
- return null;
368
- })
369
- .filter(Boolean) as Array<{ id: string; updates: any }>;
370
-
371
- if (updates.length > 0) {
372
- await this.storage.batchUpdateMemories(updates);
373
- }
374
- }
375
-
376
- /**
377
- * Improved summarization with error handling and batch processing
378
- */
379
- async summarizeAndStore(): Promise<boolean> {
380
- if (this.recentMessages.length === 0 || this.isProcessing) {
381
- return false;
382
- }
383
-
384
- this.isProcessing = true;
385
- this.metrics.summarizations++;
386
-
387
- try {
388
- // Create conversation summary
389
- const conversationText = this.recentMessages
390
- .map((msg) => `${msg.role}: ${msg.content}`)
391
- .join("\n");
392
-
393
- // Extract facts using LLM
394
- const factsResponse =
395
- await this.extractFactsFromConversation(conversationText);
396
-
397
- if (!Array.isArray(factsResponse) || factsResponse.length === 0) {
398
- console.warn("No facts extracted from conversation");
399
- return false;
400
- }
401
-
402
- // Process facts in batches
403
- await this.processFactsInBatches(factsResponse);
404
-
405
- // Clear processed messages
406
- this.recentMessages = [];
407
-
408
- return true;
409
- } catch (error) {
410
- console.error("Error in summarizeAndStore:", error);
411
- this.metrics.errors++;
412
- return false;
413
- } finally {
414
- this.isProcessing = false;
415
- }
416
- }
417
-
418
- /**
419
- * Extract facts from conversation using LLM
420
- */
421
- private async extractFactsFromConversation(
422
- conversationText: string,
423
- ): Promise<ExtractedFact[]> {
424
- try {
425
- const { extract: factsResponse } = await writeLanguageResponse({
426
- agent: "remember-facts",
427
- chat_history: conversationText,
428
- provider: "groq",
429
- model: "mixtral-8x7b-32768",
430
- timeout: MEMORY_CONFIG.DEFAULT_TIMEOUT,
431
- });
432
-
433
- return Array.isArray(factsResponse) ? factsResponse : [];
434
- } catch (error) {
435
- console.error("Error extracting facts:", error);
436
- return [];
437
- }
438
- }
439
-
440
- /**
441
- * Process facts in batches to avoid overwhelming the database
442
- */
443
- private async processFactsInBatches(
444
- factsResponse: ExtractedFact[],
445
- ): Promise<void> {
446
- for (let i = 0; i < factsResponse.length; i += this.batchSize) {
447
- const batch = factsResponse.slice(i, i + this.batchSize);
448
-
449
- const storePromises = batch.map(async (fact) => {
450
- try {
451
- if (fact && typeof fact === "object" && fact.content) {
452
- return await this.storeFact(
453
- fact.content,
454
- fact.importance || 1,
455
- fact.category || MEMORY_TYPES.CONVERSATION,
456
- fact.metadata || {},
457
- );
458
- } else if (typeof fact === "string" && fact.trim()) {
459
- return await this.storeFact(
460
- fact.trim(),
461
- 1,
462
- MEMORY_TYPES.CONVERSATION,
463
- );
464
- }
465
- } catch (error) {
466
- console.error("Error storing individual fact:", error);
467
- return null;
468
- }
469
- });
470
-
471
- await Promise.allSettled(storePromises);
472
-
473
- // Small delay between batches
474
- if (i + this.batchSize < factsResponse.length) {
475
- await new Promise((resolve) => setTimeout(resolve, 100));
476
- }
477
- }
478
- }
479
-
480
- /**
481
- * Clear cache utility
482
- */
483
- clearCache(): void {
484
- this.memoryCache.clear();
485
- }
486
-
487
- /**
488
- * Get memory context with better formatting and relevance
489
- */
490
- async getMemoryContext(
491
- query: string = "",
492
- includeRecent: boolean = true,
493
- options: MemoryContextOptions = {},
494
- ): Promise<string> {
495
- const memories = await this.recallRelevantMemories(
496
- query,
497
- options.maxMemories || 8,
498
- { minImportance: options.minImportance || 0.5 },
499
- );
500
-
501
- if (memories.length === 0 && this.recentMessages.length === 0) {
502
- return "";
503
- }
504
-
505
- let context = "";
506
-
507
- // Add relevant memories
508
- if (memories.length > 0) {
509
- context += "What I remember about you:\n";
510
- memories
511
- .filter((memory) => memory.importance > (options.minImportance || 0.5))
512
- .forEach((memory) => {
513
- const importanceIndicator =
514
- memory.importance >= 8
515
- ? "\u2b50"
516
- : memory.importance >= 5
517
- ? "\u2022"
518
- : "-";
519
- context += `${importanceIndicator} ${memory.content}\n`;
520
- });
521
- }
522
-
523
- // Add recent conversation if requested
524
- if (includeRecent && this.recentMessages.length > 0) {
525
- context += context
526
- ? "\nRecent conversation:\n"
527
- : "Recent conversation:\n";
528
- this.recentMessages.slice(-3).forEach((msg) => {
529
- const truncatedContent =
530
- msg.content.length > 100
531
- ? msg.content.substring(0, 100) + "..."
532
- : msg.content;
533
- context += `${msg.role}: ${truncatedContent}\n`;
534
- });
535
- }
536
-
537
- return context;
538
- }
539
-
540
- /**
541
- * Get performance metrics
542
- */
543
- getMetrics(): MemoryMetrics {
544
- return {
545
- ...this.metrics,
546
- cacheSize: this.memoryCache.size,
547
- recentMessagesCount: this.recentMessages.length,
548
- isProcessing: this.isProcessing,
549
- };
550
- }
551
- }
1
+ // @ts-nocheck
2
+ /**
3
+ * Simple Memory Class
4
+ *
5
+ * Core memory management functionality with:
6
+ * - Message deduplication
7
+ * - Automatic summarization
8
+ * - Vector-based relevance search
9
+ * - Caching with TTL
10
+ * - Batch processing
11
+ * - Conflict resolution
12
+ */
13
+
14
+ import { writeLanguageResponse } from "write-language";
15
+ import type { IMemoryStorage } from "./storage-interface";
16
+ import type {
17
+ MemoryRecord,
18
+ Message,
19
+ MemorySearchOptions,
20
+ MemoryMetrics,
21
+ MemoryOptions,
22
+ MemoryContextOptions,
23
+ ExtractedFact,
24
+ MemoryType,
25
+ } from "../types";
26
+ import { MEMORY_CONFIG, MEMORY_TYPES } from "../types";
27
+
28
+ export class SimpleMemory {
29
+ private userId: string;
30
+ private storage: IMemoryStorage;
31
+ private maxMemories: number;
32
+ private summaryThreshold: number;
33
+ private cacheExpiry: number;
34
+ private batchSize: number;
35
+ private relevanceThreshold: number;
36
+ private enableVectorSearch: boolean;
37
+ private enableAutoSummarization: boolean;
38
+ private recentMessages: Message[];
39
+ private memoryCache: Map<string, { data: any; timestamp: number }>;
40
+ private isProcessing: boolean;
41
+ private processingQueue: any[];
42
+ private summarizeTimeout?: NodeJS.Timeout;
43
+ private metrics: {
44
+ cacheHits: number;
45
+ cacheMisses: number;
46
+ vectorSearches: number;
47
+ summarizations: number;
48
+ errors: number;
49
+ };
50
+
51
+ /**
52
+ * Initialize memory system for a user
53
+ */
54
+ constructor(
55
+ userId: string,
56
+ storage: IMemoryStorage,
57
+ options: MemoryOptions = {},
58
+ ) {
59
+ if (!userId || !storage) {
60
+ throw new Error("userId and storage are required parameters");
61
+ }
62
+
63
+ this.userId = userId;
64
+ this.storage = storage;
65
+ this.maxMemories =
66
+ options.maxMemories || MEMORY_CONFIG.DEFAULT_MAX_MEMORIES;
67
+ this.summaryThreshold =
68
+ options.summaryThreshold || MEMORY_CONFIG.DEFAULT_SUMMARY_THRESHOLD;
69
+ this.cacheExpiry =
70
+ options.cacheExpiry || MEMORY_CONFIG.DEFAULT_CACHE_EXPIRY;
71
+ this.batchSize = options.batchSize || MEMORY_CONFIG.DEFAULT_BATCH_SIZE;
72
+ this.relevanceThreshold =
73
+ options.relevanceThreshold || MEMORY_CONFIG.DEFAULT_RELEVANCE_THRESHOLD;
74
+
75
+ // Feature flags
76
+ this.enableVectorSearch =
77
+ options.enableVectorSearch !== false &&
78
+ MEMORY_CONFIG.VECTOR_SEARCH_ENABLED;
79
+ this.enableAutoSummarization =
80
+ options.enableAutoSummarization !== false &&
81
+ MEMORY_CONFIG.AUTO_SUMMARIZATION_ENABLED;
82
+
83
+ // State management
84
+ this.recentMessages = [];
85
+ this.memoryCache = new Map();
86
+ this.isProcessing = false;
87
+ this.processingQueue = [];
88
+
89
+ // Performance metrics
90
+ this.metrics = {
91
+ cacheHits: 0,
92
+ cacheMisses: 0,
93
+ vectorSearches: 0,
94
+ summarizations: 0,
95
+ errors: 0,
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Add a message to current session with intelligent deduplication
101
+ */
102
+ addMessage(
103
+ role: "user" | "assistant",
104
+ content: string,
105
+ metadata: Record<string, any> = {},
106
+ ): boolean {
107
+ if (!role || !content || typeof content !== "string") {
108
+ console.warn("Invalid message parameters:", { role, content });
109
+ return false;
110
+ }
111
+
112
+ // Intelligent deduplication - check last few messages
113
+ const lastMessages = this.recentMessages.slice(-3);
114
+ const isDuplicate = lastMessages.some(
115
+ (msg) =>
116
+ msg.role === role &&
117
+ msg.content === content &&
118
+ Date.now() - msg.timestamp < 60000, // Within 1 minute
119
+ );
120
+
121
+ if (isDuplicate) {
122
+ console.log("Duplicate message detected, skipping");
123
+ return false;
124
+ }
125
+
126
+ // Add message with metadata
127
+ const message: Message = {
128
+ role,
129
+ content: content.trim(),
130
+ timestamp: metadata.timestamp || Date.now(),
131
+ metadata: { ...metadata },
132
+ };
133
+
134
+ this.recentMessages.push(message);
135
+
136
+ // Auto-summarization with debouncing
137
+ if (
138
+ this.enableAutoSummarization &&
139
+ this.recentMessages.length >= this.summaryThreshold &&
140
+ !this.isProcessing
141
+ ) {
142
+ this.debouncedSummarize();
143
+ }
144
+
145
+ return true;
146
+ }
147
+
148
+ /**
149
+ * Debounced summarization to prevent excessive processing
150
+ */
151
+ private debouncedSummarize(): void {
152
+ if (this.summarizeTimeout) {
153
+ clearTimeout(this.summarizeTimeout);
154
+ }
155
+
156
+ this.summarizeTimeout = setTimeout(() => {
157
+ this.summarizeAndStore().catch((error) => {
158
+ console.error("Auto-summarization failed:", error);
159
+ this.metrics.errors++;
160
+ });
161
+ }, 1000); // 1 second debounce
162
+ }
163
+
164
+ /**
165
+ * Store important facts with validation and conflict resolution
166
+ */
167
+ async storeFact(
168
+ content: string,
169
+ importance: number = 1,
170
+ category: MemoryType = MEMORY_TYPES.FACT,
171
+ metadata: Record<string, any> = {},
172
+ ): Promise<string> {
173
+ // Input validation
174
+ if (
175
+ !content ||
176
+ typeof content !== "string" ||
177
+ content.trim().length === 0
178
+ ) {
179
+ throw new Error("Content cannot be empty");
180
+ }
181
+
182
+ if (
183
+ importance < MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.min ||
184
+ importance > MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.max
185
+ ) {
186
+ throw new Error(
187
+ `Importance must be between ${MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.min} and ${MEMORY_CONFIG.DEFAULT_IMPORTANCE_RANGE.max}`,
188
+ );
189
+ }
190
+
191
+ const normalizedContent = content.trim();
192
+ const normalizedCategory = Object.values(MEMORY_TYPES).includes(category)
193
+ ? category
194
+ : MEMORY_TYPES.FACT;
195
+
196
+ try {
197
+ // Check for existing similar facts using fuzzy matching
198
+ const existingFacts = await this.findSimilarFacts(normalizedContent);
199
+
200
+ if (existingFacts.length > 0) {
201
+ // Update existing fact if importance is higher
202
+ const bestMatch = existingFacts[0];
203
+ if (importance > bestMatch.importance) {
204
+ await this.storage.updateMemory(bestMatch.id, {
205
+ importance,
206
+ updated_at: new Date(),
207
+ access_count: { increment: 1 },
208
+ metadata: { ...bestMatch.metadata, ...metadata },
209
+ });
210
+ }
211
+ return bestMatch.id;
212
+ }
213
+
214
+ // Insert new fact
215
+ const id = await this.storage.insertMemory(
216
+ this.userId,
217
+ normalizedCategory,
218
+ normalizedContent,
219
+ Math.max(0, Math.min(10, importance)),
220
+ metadata,
221
+ );
222
+
223
+ // Clear cache after new insertion
224
+ this.clearCache();
225
+
226
+ return id;
227
+ } catch (error) {
228
+ console.error("Error storing fact:", error);
229
+ this.metrics.errors++;
230
+ throw error;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Find similar facts using content similarity
236
+ */
237
+ private async findSimilarFacts(content: string): Promise<MemoryRecord[]> {
238
+ try {
239
+ return await this.storage.findSimilarMemories(this.userId, content, 5);
240
+ } catch (error) {
241
+ console.error("Error finding similar facts:", error);
242
+ return [];
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Enhanced memory recall with caching and vector search
248
+ */
249
+ async recallRelevantMemories(
250
+ query: string = "",
251
+ limit: number = 10,
252
+ options: MemorySearchOptions = {},
253
+ ): Promise<MemoryRecord[]> {
254
+ const cacheKey = `${query}-${limit}-${JSON.stringify(options)}`;
255
+
256
+ // Check cache first
257
+ if (this.memoryCache.has(cacheKey)) {
258
+ const cached = this.memoryCache.get(cacheKey)!;
259
+ if (Date.now() - cached.timestamp < this.cacheExpiry) {
260
+ this.metrics.cacheHits++;
261
+ return cached.data;
262
+ }
263
+ this.memoryCache.delete(cacheKey);
264
+ }
265
+
266
+ this.metrics.cacheMisses++;
267
+
268
+ try {
269
+ let memories = await this.storage.findMemories(
270
+ this.userId,
271
+ query,
272
+ limit,
273
+ options,
274
+ );
275
+
276
+ if (memories.length === 0) {
277
+ return [];
278
+ }
279
+
280
+ // Apply vector search if enabled and query provided
281
+ if (this.enableVectorSearch && query.trim() && memories.length > 0) {
282
+ memories = await this.applyVectorSearch(query, memories, options);
283
+ }
284
+
285
+ // Apply relevance filtering
286
+ if (options.minImportance) {
287
+ memories = memories.filter(
288
+ (m) => m.importance >= options.minImportance!,
289
+ );
290
+ }
291
+
292
+ // Format results
293
+ const result = memories.map((m) => ({
294
+ ...m,
295
+ relevance_score: m.relevance_score || 0,
296
+ metadata: options.includeMetadata ? m.metadata : undefined,
297
+ }));
298
+
299
+ // Cache the result
300
+ this.memoryCache.set(cacheKey, {
301
+ data: result,
302
+ timestamp: Date.now(),
303
+ });
304
+
305
+ return result;
306
+ } catch (error) {
307
+ console.error("Error retrieving memories:", error);
308
+ this.metrics.errors++;
309
+ return [];
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Apply vector search to memories
315
+ */
316
+ private async applyVectorSearch(
317
+ query: string,
318
+ memories: MemoryRecord[],
319
+ options: MemorySearchOptions,
320
+ ): Promise<MemoryRecord[]> {
321
+ // Placeholder for vector search implementation
322
+ // In production, integrate with your vector similarity API
323
+ // try {
324
+ // this.metrics.vectorSearches++;
325
+ // const sentencesByRelevance = await weighRelevanceConceptVectorAPI(
326
+ // query,
327
+ // memories.map(m => m.content)
328
+ // );
329
+ // await this.updateRelevanceScores(memories, sentencesByRelevance);
330
+ // return memories
331
+ // .map(memory => {
332
+ // const relevanceData = sentencesByRelevance.find(s => s.sentence === memory.content);
333
+ // return {
334
+ // ...memory,
335
+ // relevance_score: relevanceData?.relevance || 0
336
+ // };
337
+ // })
338
+ // .sort((a, b) => b.relevance_score - a.relevance_score);
339
+ // } catch (vectorError) {
340
+ // console.warn('Vector search failed, falling back to basic search:', vectorError);
341
+ // return memories;
342
+ // }
343
+ return memories;
344
+ }
345
+
346
+ /**
347
+ * Update relevance scores for memories
348
+ */
349
+ private async updateRelevanceScores(
350
+ memories: MemoryRecord[],
351
+ sentencesByRelevance: Array<{ sentence: string; relevance: number }>,
352
+ ): Promise<void> {
353
+ const updates = sentencesByRelevance
354
+ .filter((m) => m.relevance > this.relevanceThreshold)
355
+ .map((m) => {
356
+ const memory = memories.find((mem) => mem.content === m.sentence);
357
+ if (memory) {
358
+ return {
359
+ id: memory.id,
360
+ updates: {
361
+ importance: Math.min(10, memory.importance + m.relevance * 0.5),
362
+ access_count: { increment: 1 },
363
+ updated_at: new Date(),
364
+ },
365
+ };
366
+ }
367
+ return null;
368
+ })
369
+ .filter(Boolean) as Array<{ id: string; updates: any }>;
370
+
371
+ if (updates.length > 0) {
372
+ await this.storage.batchUpdateMemories(updates);
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Improved summarization with error handling and batch processing
378
+ */
379
+ async summarizeAndStore(): Promise<boolean> {
380
+ if (this.recentMessages.length === 0 || this.isProcessing) {
381
+ return false;
382
+ }
383
+
384
+ this.isProcessing = true;
385
+ this.metrics.summarizations++;
386
+
387
+ try {
388
+ // Create conversation summary
389
+ const conversationText = this.recentMessages
390
+ .map((msg) => `${msg.role}: ${msg.content}`)
391
+ .join("\n");
392
+
393
+ // Extract facts using LLM
394
+ const factsResponse =
395
+ await this.extractFactsFromConversation(conversationText);
396
+
397
+ if (!Array.isArray(factsResponse) || factsResponse.length === 0) {
398
+ console.warn("No facts extracted from conversation");
399
+ return false;
400
+ }
401
+
402
+ // Process facts in batches
403
+ await this.processFactsInBatches(factsResponse);
404
+
405
+ // Clear processed messages
406
+ this.recentMessages = [];
407
+
408
+ return true;
409
+ } catch (error) {
410
+ console.error("Error in summarizeAndStore:", error);
411
+ this.metrics.errors++;
412
+ return false;
413
+ } finally {
414
+ this.isProcessing = false;
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Extract facts from conversation using LLM
420
+ */
421
+ private async extractFactsFromConversation(
422
+ conversationText: string,
423
+ ): Promise<ExtractedFact[]> {
424
+ try {
425
+ const { extract: factsResponse } = await writeLanguageResponse({
426
+ agent: "remember-facts",
427
+ chat_history: conversationText,
428
+ provider: "groq",
429
+ model: "mixtral-8x7b-32768",
430
+ timeout: MEMORY_CONFIG.DEFAULT_TIMEOUT,
431
+ });
432
+
433
+ return Array.isArray(factsResponse) ? factsResponse : [];
434
+ } catch (error) {
435
+ console.error("Error extracting facts:", error);
436
+ return [];
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Process facts in batches to avoid overwhelming the database
442
+ */
443
+ private async processFactsInBatches(
444
+ factsResponse: ExtractedFact[],
445
+ ): Promise<void> {
446
+ for (let i = 0; i < factsResponse.length; i += this.batchSize) {
447
+ const batch = factsResponse.slice(i, i + this.batchSize);
448
+
449
+ const storePromises = batch.map(async (fact) => {
450
+ try {
451
+ if (fact && typeof fact === "object" && fact.content) {
452
+ return await this.storeFact(
453
+ fact.content,
454
+ fact.importance || 1,
455
+ fact.category || MEMORY_TYPES.CONVERSATION,
456
+ fact.metadata || {},
457
+ );
458
+ } else if (typeof fact === "string" && fact.trim()) {
459
+ return await this.storeFact(
460
+ fact.trim(),
461
+ 1,
462
+ MEMORY_TYPES.CONVERSATION,
463
+ );
464
+ }
465
+ } catch (error) {
466
+ console.error("Error storing individual fact:", error);
467
+ return null;
468
+ }
469
+ });
470
+
471
+ await Promise.allSettled(storePromises);
472
+
473
+ // Small delay between batches
474
+ if (i + this.batchSize < factsResponse.length) {
475
+ await new Promise((resolve) => setTimeout(resolve, 100));
476
+ }
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Clear cache utility
482
+ */
483
+ clearCache(): void {
484
+ this.memoryCache.clear();
485
+ }
486
+
487
+ /**
488
+ * Get memory context with better formatting and relevance
489
+ */
490
+ async getMemoryContext(
491
+ query: string = "",
492
+ includeRecent: boolean = true,
493
+ options: MemoryContextOptions = {},
494
+ ): Promise<string> {
495
+ const memories = await this.recallRelevantMemories(
496
+ query,
497
+ options.maxMemories || 8,
498
+ { minImportance: options.minImportance || 0.5 },
499
+ );
500
+
501
+ if (memories.length === 0 && this.recentMessages.length === 0) {
502
+ return "";
503
+ }
504
+
505
+ let context = "";
506
+
507
+ // Add relevant memories
508
+ if (memories.length > 0) {
509
+ context += "What I remember about you:\n";
510
+ memories
511
+ .filter((memory) => memory.importance > (options.minImportance || 0.5))
512
+ .forEach((memory) => {
513
+ const importanceIndicator =
514
+ memory.importance >= 8
515
+ ? "\u2b50"
516
+ : memory.importance >= 5
517
+ ? "\u2022"
518
+ : "-";
519
+ context += `${importanceIndicator} ${memory.content}\n`;
520
+ });
521
+ }
522
+
523
+ // Add recent conversation if requested
524
+ if (includeRecent && this.recentMessages.length > 0) {
525
+ context += context
526
+ ? "\nRecent conversation:\n"
527
+ : "Recent conversation:\n";
528
+ this.recentMessages.slice(-3).forEach((msg) => {
529
+ const truncatedContent =
530
+ msg.content.length > 100
531
+ ? msg.content.substring(0, 100) + "..."
532
+ : msg.content;
533
+ context += `${msg.role}: ${truncatedContent}\n`;
534
+ });
535
+ }
536
+
537
+ return context;
538
+ }
539
+
540
+ /**
541
+ * Get performance metrics
542
+ */
543
+ getMetrics(): MemoryMetrics {
544
+ return {
545
+ ...this.metrics,
546
+ cacheSize: this.memoryCache.size,
547
+ recentMessagesCount: this.recentMessages.length,
548
+ isProcessing: this.isProcessing,
549
+ };
550
+ }
551
+ }