@lov3kaizen/agentsea-memory 0.5.1

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +450 -0
  3. package/dist/chunk-GACX3FPR.js +1402 -0
  4. package/dist/chunk-M44NB53O.js +1226 -0
  5. package/dist/chunk-MQDWBPZU.js +972 -0
  6. package/dist/chunk-TPC7MYWK.js +1495 -0
  7. package/dist/chunk-XD2CQGSD.js +1540 -0
  8. package/dist/chunk-YI7RPDEV.js +1215 -0
  9. package/dist/core.types-lkxKv-bW.d.cts +242 -0
  10. package/dist/core.types-lkxKv-bW.d.ts +242 -0
  11. package/dist/debug/index.cjs +1248 -0
  12. package/dist/debug/index.d.cts +3 -0
  13. package/dist/debug/index.d.ts +3 -0
  14. package/dist/debug/index.js +20 -0
  15. package/dist/index-7SsAJ4et.d.ts +525 -0
  16. package/dist/index-BGxYqpFb.d.cts +601 -0
  17. package/dist/index-BX62efZu.d.ts +565 -0
  18. package/dist/index-Bbc3COw0.d.cts +748 -0
  19. package/dist/index-Bczz1Eyk.d.ts +637 -0
  20. package/dist/index-C7pEiT8L.d.cts +637 -0
  21. package/dist/index-CHetLTb0.d.ts +389 -0
  22. package/dist/index-CloeiFyx.d.ts +748 -0
  23. package/dist/index-DNOhq-3y.d.cts +525 -0
  24. package/dist/index-Da-M8FOV.d.cts +389 -0
  25. package/dist/index-Dy8UjRFz.d.cts +565 -0
  26. package/dist/index-aVcITW0B.d.ts +601 -0
  27. package/dist/index.cjs +8554 -0
  28. package/dist/index.d.cts +293 -0
  29. package/dist/index.d.ts +293 -0
  30. package/dist/index.js +742 -0
  31. package/dist/processing/index.cjs +1575 -0
  32. package/dist/processing/index.d.cts +2 -0
  33. package/dist/processing/index.d.ts +2 -0
  34. package/dist/processing/index.js +24 -0
  35. package/dist/retrieval/index.cjs +1262 -0
  36. package/dist/retrieval/index.d.cts +2 -0
  37. package/dist/retrieval/index.d.ts +2 -0
  38. package/dist/retrieval/index.js +26 -0
  39. package/dist/sharing/index.cjs +1003 -0
  40. package/dist/sharing/index.d.cts +3 -0
  41. package/dist/sharing/index.d.ts +3 -0
  42. package/dist/sharing/index.js +16 -0
  43. package/dist/stores/index.cjs +1445 -0
  44. package/dist/stores/index.d.cts +2 -0
  45. package/dist/stores/index.d.ts +2 -0
  46. package/dist/stores/index.js +20 -0
  47. package/dist/structures/index.cjs +1530 -0
  48. package/dist/structures/index.d.cts +3 -0
  49. package/dist/structures/index.d.ts +3 -0
  50. package/dist/structures/index.js +24 -0
  51. package/package.json +141 -0
@@ -0,0 +1,1575 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/processing/index.ts
21
+ var processing_exports = {};
22
+ __export(processing_exports, {
23
+ Compressor: () => Compressor,
24
+ Consolidator: () => Consolidator,
25
+ Extractor: () => Extractor,
26
+ Forgetter: () => Forgetter,
27
+ Summarizer: () => Summarizer,
28
+ createCompressor: () => createCompressor,
29
+ createConsolidator: () => createConsolidator,
30
+ createExtractor: () => createExtractor,
31
+ createForgetter: () => createForgetter,
32
+ createSummarizer: () => createSummarizer
33
+ });
34
+ module.exports = __toCommonJS(processing_exports);
35
+
36
+ // src/processing/Summarizer.ts
37
+ var Summarizer = class {
38
+ config;
39
+ summaryFn;
40
+ constructor(config = {}) {
41
+ this.config = {
42
+ provider: config.provider ?? void 0,
43
+ model: config.model ?? "default",
44
+ strategy: config.strategy ?? "abstractive",
45
+ maxLength: config.maxLength ?? 500,
46
+ preserveEntities: config.preserveEntities ?? true,
47
+ focusPrompt: config.focusPrompt ?? "",
48
+ maxSummaryLength: config.maxSummaryLength ?? 500,
49
+ minEntriesForSummary: config.minEntriesForSummary ?? 3,
50
+ preserveKeyEntities: config.preserveKeyEntities ?? true,
51
+ summaryStyle: config.summaryStyle ?? "concise"
52
+ };
53
+ }
54
+ /**
55
+ * Set custom summary function (for LLM integration)
56
+ */
57
+ setSummaryFunction(fn) {
58
+ this.summaryFn = fn;
59
+ }
60
+ /**
61
+ * Summarize a collection of memories
62
+ */
63
+ async summarize(entries) {
64
+ if (entries.length < this.config.minEntriesForSummary) {
65
+ return Promise.resolve(this.createSimpleSummary(entries));
66
+ }
67
+ if (this.summaryFn) {
68
+ const summary = await this.summaryFn(entries, {
69
+ maxLength: this.config.maxSummaryLength,
70
+ style: this.config.summaryStyle
71
+ });
72
+ return {
73
+ summary,
74
+ keyPoints: this.extractKeyPoints(entries),
75
+ sourceCount: entries.length,
76
+ compressionRatio: this.calculateCompressionRatio(entries, summary),
77
+ metadata: {
78
+ method: "llm",
79
+ style: this.config.summaryStyle
80
+ }
81
+ };
82
+ }
83
+ return Promise.resolve(this.heuristicSummarize(entries));
84
+ }
85
+ /**
86
+ * Summarize memories by time period
87
+ */
88
+ async summarizeByPeriod(entries, period) {
89
+ const grouped = this.groupByPeriod(entries, period);
90
+ const results = /* @__PURE__ */ new Map();
91
+ for (const [key, groupEntries] of grouped) {
92
+ const summary = await this.summarize(groupEntries);
93
+ results.set(key, summary);
94
+ }
95
+ return results;
96
+ }
97
+ /**
98
+ * Summarize memories by topic/type
99
+ */
100
+ async summarizeByTopic(entries) {
101
+ const grouped = this.groupByTopic(entries);
102
+ const results = /* @__PURE__ */ new Map();
103
+ for (const [topic, topicEntries] of grouped) {
104
+ const summary = await this.summarize(topicEntries);
105
+ results.set(topic, summary);
106
+ }
107
+ return results;
108
+ }
109
+ /**
110
+ * Create incremental summary (add to existing summary)
111
+ */
112
+ async incrementalSummarize(existingSummary, newEntries) {
113
+ if (this.summaryFn) {
114
+ const contextEntry = {
115
+ id: "context",
116
+ content: `Previous summary: ${existingSummary}`,
117
+ type: "summary",
118
+ importance: 0.8,
119
+ metadata: {
120
+ source: "system",
121
+ confidence: 1
122
+ },
123
+ timestamp: Date.now(),
124
+ accessCount: 0,
125
+ createdAt: Date.now(),
126
+ updatedAt: Date.now()
127
+ };
128
+ const summary = await this.summaryFn([contextEntry, ...newEntries], {
129
+ maxLength: this.config.maxSummaryLength,
130
+ style: this.config.summaryStyle
131
+ });
132
+ return {
133
+ summary,
134
+ keyPoints: this.extractKeyPoints(newEntries),
135
+ sourceCount: newEntries.length,
136
+ compressionRatio: this.calculateCompressionRatio(newEntries, summary),
137
+ metadata: {
138
+ method: "incremental",
139
+ hadExistingSummary: true
140
+ }
141
+ };
142
+ }
143
+ const newPoints = this.extractKeyPoints(newEntries);
144
+ const combinedSummary = `${existingSummary} Additionally: ${newPoints.join("; ")}.`;
145
+ return {
146
+ summary: combinedSummary.slice(0, this.config.maxSummaryLength),
147
+ keyPoints: newPoints,
148
+ sourceCount: newEntries.length,
149
+ compressionRatio: this.calculateCompressionRatio(
150
+ newEntries,
151
+ combinedSummary
152
+ ),
153
+ metadata: {
154
+ method: "heuristic-incremental"
155
+ }
156
+ };
157
+ }
158
+ /**
159
+ * Heuristic-based summarization
160
+ */
161
+ heuristicSummarize(entries) {
162
+ const sorted = [...entries].sort((a, b) => b.importance - a.importance);
163
+ const keyPoints = this.extractKeyPoints(sorted);
164
+ const typeGroups = /* @__PURE__ */ new Map();
165
+ for (const entry of entries) {
166
+ typeGroups.set(entry.type, (typeGroups.get(entry.type) ?? 0) + 1);
167
+ }
168
+ const typesSummary = Array.from(typeGroups.entries()).map(([type, count]) => `${count} ${type}(s)`).join(", ");
169
+ const timeRange = this.getTimeRange(entries);
170
+ const summary = `Summary of ${entries.length} memories (${typesSummary}) from ${timeRange}: ${keyPoints.slice(0, 3).join(". ")}.`;
171
+ return {
172
+ summary: summary.slice(0, this.config.maxSummaryLength),
173
+ keyPoints,
174
+ sourceCount: entries.length,
175
+ compressionRatio: this.calculateCompressionRatio(entries, summary),
176
+ metadata: {
177
+ method: "heuristic",
178
+ typeDistribution: Object.fromEntries(typeGroups)
179
+ }
180
+ };
181
+ }
182
+ /**
183
+ * Create simple summary for few entries
184
+ */
185
+ createSimpleSummary(entries) {
186
+ const summary = entries.map((e) => e.content.slice(0, 100)).join("; ");
187
+ return {
188
+ summary: summary.slice(0, this.config.maxSummaryLength),
189
+ keyPoints: entries.map((e) => e.content.slice(0, 50)),
190
+ sourceCount: entries.length,
191
+ compressionRatio: 1,
192
+ metadata: {
193
+ method: "simple"
194
+ }
195
+ };
196
+ }
197
+ /**
198
+ * Extract key points from entries
199
+ */
200
+ extractKeyPoints(entries) {
201
+ const points = [];
202
+ for (const entry of entries.slice(0, 10)) {
203
+ const firstSentence = entry.content.split(/[.!?]/)[0];
204
+ if (firstSentence && firstSentence.length > 10) {
205
+ points.push(firstSentence.trim());
206
+ }
207
+ }
208
+ return this.deduplicateStrings(points);
209
+ }
210
+ /**
211
+ * Group entries by time period
212
+ */
213
+ groupByPeriod(entries, period) {
214
+ const groups = /* @__PURE__ */ new Map();
215
+ for (const entry of entries) {
216
+ const date = new Date(entry.timestamp);
217
+ let key;
218
+ switch (period) {
219
+ case "hour":
220
+ key = `${date.toISOString().slice(0, 13)}:00`;
221
+ break;
222
+ case "day":
223
+ key = date.toISOString().slice(0, 10);
224
+ break;
225
+ case "week": {
226
+ const weekStart = new Date(date);
227
+ weekStart.setDate(date.getDate() - date.getDay());
228
+ key = `week-${weekStart.toISOString().slice(0, 10)}`;
229
+ break;
230
+ }
231
+ }
232
+ if (!groups.has(key)) {
233
+ groups.set(key, []);
234
+ }
235
+ groups.get(key).push(entry);
236
+ }
237
+ return groups;
238
+ }
239
+ /**
240
+ * Group entries by topic/type
241
+ */
242
+ groupByTopic(entries) {
243
+ const groups = /* @__PURE__ */ new Map();
244
+ for (const entry of entries) {
245
+ const topic = String(entry.metadata.topic ?? entry.type);
246
+ if (!groups.has(topic)) {
247
+ groups.set(topic, []);
248
+ }
249
+ groups.get(topic).push(entry);
250
+ }
251
+ return groups;
252
+ }
253
+ /**
254
+ * Get time range description
255
+ */
256
+ getTimeRange(entries) {
257
+ if (entries.length === 0) return "unknown period";
258
+ const timestamps = entries.map((e) => e.timestamp);
259
+ const start = new Date(Math.min(...timestamps));
260
+ const end = new Date(Math.max(...timestamps));
261
+ const diffMs = end.getTime() - start.getTime();
262
+ const diffHours = diffMs / (1e3 * 60 * 60);
263
+ if (diffHours < 1) {
264
+ return "the last hour";
265
+ } else if (diffHours < 24) {
266
+ return "today";
267
+ } else if (diffHours < 168) {
268
+ return "this week";
269
+ } else {
270
+ return `${start.toLocaleDateString()} to ${end.toLocaleDateString()}`;
271
+ }
272
+ }
273
+ /**
274
+ * Calculate compression ratio
275
+ */
276
+ calculateCompressionRatio(entries, summary) {
277
+ const originalLength = entries.reduce(
278
+ (sum, e) => sum + e.content.length,
279
+ 0
280
+ );
281
+ return originalLength > 0 ? originalLength / summary.length : 1;
282
+ }
283
+ /**
284
+ * Deduplicate similar strings
285
+ */
286
+ deduplicateStrings(strings) {
287
+ const result = [];
288
+ for (const str of strings) {
289
+ const isDuplicate = result.some(
290
+ (existing) => this.stringSimilarity(str, existing) > 0.8
291
+ );
292
+ if (!isDuplicate) {
293
+ result.push(str);
294
+ }
295
+ }
296
+ return result;
297
+ }
298
+ /**
299
+ * Simple string similarity (Jaccard)
300
+ */
301
+ stringSimilarity(a, b) {
302
+ const setA = new Set(a.toLowerCase().split(/\s+/));
303
+ const setB = new Set(b.toLowerCase().split(/\s+/));
304
+ const intersection = new Set([...setA].filter((x) => setB.has(x)));
305
+ const union = /* @__PURE__ */ new Set([...setA, ...setB]);
306
+ return intersection.size / union.size;
307
+ }
308
+ /**
309
+ * Update configuration
310
+ */
311
+ configure(config) {
312
+ this.config = { ...this.config, ...config };
313
+ }
314
+ };
315
+ function createSummarizer(config) {
316
+ return new Summarizer(config);
317
+ }
318
+
319
+ // src/processing/Compressor.ts
320
+ var Compressor = class {
321
+ config;
322
+ constructor(config = {}) {
323
+ this.config = {
324
+ targetRatio: config.targetRatio ?? 0.5,
325
+ preserveImportant: config.preserveImportant ?? true,
326
+ strategy: config.strategy ?? "importance-weighted",
327
+ minImportance: config.minImportance ?? 0.3,
328
+ minContentLength: config.minContentLength ?? 50,
329
+ removeEmbeddings: config.removeEmbeddings ?? false,
330
+ truncateMetadata: config.truncateMetadata ?? true
331
+ };
332
+ }
333
+ /**
334
+ * Compress a single memory entry
335
+ */
336
+ compress(entry) {
337
+ const originalSize = this.calculateSize(entry);
338
+ const preservedFields = [];
339
+ const compressed = { ...entry };
340
+ if (entry.content.length > this.config.minContentLength) {
341
+ compressed.content = this.truncateContent(
342
+ entry.content,
343
+ entry.importance
344
+ );
345
+ preservedFields.push("content (truncated)");
346
+ } else {
347
+ preservedFields.push("content");
348
+ }
349
+ if (this.config.removeEmbeddings && entry.embedding) {
350
+ delete compressed.embedding;
351
+ } else if (entry.embedding) {
352
+ preservedFields.push("embedding");
353
+ }
354
+ if (this.config.truncateMetadata) {
355
+ compressed.metadata = this.truncateMetadata(entry.metadata);
356
+ preservedFields.push("metadata (truncated)");
357
+ } else {
358
+ preservedFields.push("metadata");
359
+ }
360
+ const compressedSize = this.calculateSize(compressed);
361
+ return {
362
+ compressed,
363
+ originalSize,
364
+ compressedSize,
365
+ ratio: compressedSize / originalSize,
366
+ preservedFields
367
+ };
368
+ }
369
+ /**
370
+ * Compress multiple entries
371
+ */
372
+ compressBatch(entries) {
373
+ let totalOriginalSize = 0;
374
+ let totalCompressedSize = 0;
375
+ let removedCount = 0;
376
+ const compressedEntries = [];
377
+ for (const entry of entries) {
378
+ const originalSize = this.calculateSize(entry);
379
+ totalOriginalSize += originalSize;
380
+ if (this.config.preserveImportant && entry.importance < 0.2) {
381
+ removedCount++;
382
+ continue;
383
+ }
384
+ const result = this.compress(entry);
385
+ compressedEntries.push(result.compressed);
386
+ totalCompressedSize += result.compressedSize;
387
+ }
388
+ return {
389
+ entries: compressedEntries,
390
+ totalOriginalSize,
391
+ totalCompressedSize,
392
+ avgRatio: totalOriginalSize > 0 ? totalCompressedSize / totalOriginalSize : 1,
393
+ removedCount
394
+ };
395
+ }
396
+ /**
397
+ * Compress entries to target size
398
+ */
399
+ compressToSize(entries, targetSize) {
400
+ const sorted = [...entries].sort((a, b) => b.importance - a.importance);
401
+ let currentSize = 0;
402
+ const result = [];
403
+ let removedCount = 0;
404
+ for (const entry of sorted) {
405
+ const compressed = this.compress(entry);
406
+ if (currentSize + compressed.compressedSize <= targetSize) {
407
+ result.push(compressed.compressed);
408
+ currentSize += compressed.compressedSize;
409
+ } else {
410
+ const aggressive = this.aggressiveCompress(entry);
411
+ if (currentSize + this.calculateSize(aggressive) <= targetSize) {
412
+ result.push(aggressive);
413
+ currentSize += this.calculateSize(aggressive);
414
+ } else {
415
+ removedCount++;
416
+ }
417
+ }
418
+ }
419
+ return {
420
+ entries: result,
421
+ totalOriginalSize: entries.reduce(
422
+ (sum, e) => sum + this.calculateSize(e),
423
+ 0
424
+ ),
425
+ totalCompressedSize: currentSize,
426
+ avgRatio: currentSize / Math.max(
427
+ entries.reduce((sum, e) => sum + this.calculateSize(e), 0),
428
+ 1
429
+ ),
430
+ removedCount
431
+ };
432
+ }
433
+ /**
434
+ * Deduplicate and compress similar entries
435
+ */
436
+ deduplicateAndCompress(entries) {
437
+ const seen = /* @__PURE__ */ new Map();
438
+ let removedCount = 0;
439
+ for (const entry of entries) {
440
+ const key = this.getDedupeKey(entry);
441
+ const existing = seen.get(key);
442
+ if (!existing) {
443
+ seen.set(key, entry);
444
+ } else {
445
+ if (entry.importance > existing.importance || entry.importance === existing.importance && entry.timestamp > existing.timestamp) {
446
+ seen.set(key, entry);
447
+ }
448
+ removedCount++;
449
+ }
450
+ }
451
+ const unique = Array.from(seen.values());
452
+ const batchResult = this.compressBatch(unique);
453
+ return {
454
+ ...batchResult,
455
+ removedCount: removedCount + batchResult.removedCount
456
+ };
457
+ }
458
+ /**
459
+ * Truncate content based on importance
460
+ */
461
+ truncateContent(content, importance) {
462
+ const keepRatio = 0.3 + importance * 0.5;
463
+ const targetLength = Math.max(
464
+ this.config.minContentLength,
465
+ Math.floor(content.length * keepRatio)
466
+ );
467
+ if (content.length <= targetLength) {
468
+ return content;
469
+ }
470
+ const truncated = content.slice(0, targetLength);
471
+ const lastSentenceEnd = Math.max(
472
+ truncated.lastIndexOf("."),
473
+ truncated.lastIndexOf("!"),
474
+ truncated.lastIndexOf("?")
475
+ );
476
+ if (lastSentenceEnd > targetLength * 0.5) {
477
+ return truncated.slice(0, lastSentenceEnd + 1);
478
+ }
479
+ const lastSpace = truncated.lastIndexOf(" ");
480
+ if (lastSpace > targetLength * 0.8) {
481
+ return truncated.slice(0, lastSpace) + "...";
482
+ }
483
+ return truncated + "...";
484
+ }
485
+ /**
486
+ * Truncate metadata
487
+ */
488
+ truncateMetadata(metadata) {
489
+ const result = {
490
+ source: metadata.source,
491
+ confidence: metadata.confidence
492
+ };
493
+ const essentialFields = [
494
+ "userId",
495
+ "agentId",
496
+ "conversationId",
497
+ "namespace",
498
+ "tags"
499
+ ];
500
+ for (const field of essentialFields) {
501
+ if (field in metadata) {
502
+ result[field] = metadata[field];
503
+ }
504
+ }
505
+ return result;
506
+ }
507
+ /**
508
+ * Aggressive compression for tight space constraints
509
+ */
510
+ aggressiveCompress(entry) {
511
+ return {
512
+ ...entry,
513
+ content: entry.content.slice(0, 100) + (entry.content.length > 100 ? "..." : ""),
514
+ embedding: void 0,
515
+ metadata: {
516
+ source: entry.metadata.source,
517
+ confidence: entry.metadata.confidence,
518
+ namespace: entry.metadata.namespace
519
+ }
520
+ };
521
+ }
522
+ /**
523
+ * Get deduplication key for entry
524
+ */
525
+ getDedupeKey(entry) {
526
+ const contentKey = entry.content.toLowerCase().replace(/[^\w\s]/g, "").slice(0, 100);
527
+ return `${entry.type}:${contentKey}`;
528
+ }
529
+ /**
530
+ * Calculate approximate size of entry in bytes
531
+ */
532
+ calculateSize(entry) {
533
+ let size = 0;
534
+ size += entry.content.length * 2;
535
+ if (entry.embedding) {
536
+ size += entry.embedding.length * 8;
537
+ }
538
+ size += JSON.stringify(entry.metadata).length * 2;
539
+ size += 200;
540
+ return size;
541
+ }
542
+ /**
543
+ * Update configuration
544
+ */
545
+ configure(config) {
546
+ this.config = { ...this.config, ...config };
547
+ }
548
+ };
549
+ function createCompressor(config) {
550
+ return new Compressor(config);
551
+ }
552
+
553
+ // src/processing/Consolidator.ts
554
+ var Consolidator = class {
555
+ config;
556
+ summarizer;
557
+ embedFn;
558
+ constructor(config = {}) {
559
+ this.config = {
560
+ similarityThreshold: config.similarityThreshold ?? 0.8,
561
+ mergeStrategy: config.mergeStrategy ?? "confidence-weighted",
562
+ extractRelations: config.extractRelations ?? false,
563
+ maxBatchSize: config.maxBatchSize ?? 100,
564
+ minGroupSize: config.minGroupSize ?? 2,
565
+ maxGroupSize: config.maxGroupSize ?? 20,
566
+ groupingStrategy: config.groupingStrategy ?? "semantic",
567
+ preserveOriginals: config.preserveOriginals ?? false
568
+ };
569
+ this.summarizer = new Summarizer();
570
+ }
571
+ /**
572
+ * Set embedding function for semantic grouping
573
+ */
574
+ setEmbeddingFunction(fn) {
575
+ this.embedFn = fn;
576
+ }
577
+ /**
578
+ * Set summarizer function
579
+ */
580
+ setSummarizerFunction(fn) {
581
+ this.summarizer.setSummaryFunction(fn);
582
+ }
583
+ /**
584
+ * Find and consolidate similar memories
585
+ */
586
+ async consolidate(entries, store) {
587
+ const groups = await this.groupSimilar(entries);
588
+ const results = [];
589
+ for (const group of groups) {
590
+ if (group.entries.length < this.config.minGroupSize) {
591
+ continue;
592
+ }
593
+ const consolidated = await this.consolidateGroup(group);
594
+ results.push(consolidated);
595
+ if (store) {
596
+ await store.add(consolidated.consolidated);
597
+ if (!this.config.preserveOriginals) {
598
+ for (const id of consolidated.sourceIds) {
599
+ await store.delete(id);
600
+ }
601
+ }
602
+ }
603
+ }
604
+ return results;
605
+ }
606
+ /**
607
+ * Group similar entries
608
+ */
609
+ async groupSimilar(entries) {
610
+ switch (this.config.groupingStrategy) {
611
+ case "semantic":
612
+ return Promise.resolve(this.groupBySemantic(entries));
613
+ case "temporal":
614
+ return Promise.resolve(this.groupByTemporal(entries));
615
+ case "type":
616
+ return Promise.resolve(this.groupByType(entries));
617
+ default:
618
+ return Promise.resolve(this.groupBySemantic(entries));
619
+ }
620
+ }
621
+ /**
622
+ * Group by semantic similarity
623
+ */
624
+ async groupBySemantic(entries) {
625
+ if (!this.embedFn) {
626
+ return Promise.resolve(this.groupByTextSimilarity(entries));
627
+ }
628
+ const withEmbeddings = await Promise.all(
629
+ entries.map(async (entry) => {
630
+ if (entry.embedding) return entry;
631
+ return {
632
+ ...entry,
633
+ embedding: await this.embedFn(entry.content)
634
+ };
635
+ })
636
+ );
637
+ const groups = [];
638
+ const assigned = /* @__PURE__ */ new Set();
639
+ for (let i = 0; i < withEmbeddings.length; i++) {
640
+ if (assigned.has(withEmbeddings[i].id)) continue;
641
+ const group = [withEmbeddings[i]];
642
+ assigned.add(withEmbeddings[i].id);
643
+ for (let j = i + 1; j < withEmbeddings.length; j++) {
644
+ if (assigned.has(withEmbeddings[j].id)) continue;
645
+ if (group.length >= this.config.maxGroupSize) break;
646
+ const similarity = this.cosineSimilarity(
647
+ withEmbeddings[i].embedding,
648
+ withEmbeddings[j].embedding
649
+ );
650
+ if (similarity >= this.config.similarityThreshold) {
651
+ group.push(withEmbeddings[j]);
652
+ assigned.add(withEmbeddings[j].id);
653
+ }
654
+ }
655
+ if (group.length >= this.config.minGroupSize) {
656
+ groups.push({
657
+ id: this.generateId(),
658
+ entries: group,
659
+ similarity: this.calculateGroupSimilarity(group),
660
+ groupKey: `semantic-${i}`
661
+ });
662
+ }
663
+ }
664
+ return groups;
665
+ }
666
+ /**
667
+ * Group by text similarity (fallback)
668
+ */
669
+ groupByTextSimilarity(entries) {
670
+ const groups = [];
671
+ const assigned = /* @__PURE__ */ new Set();
672
+ for (let i = 0; i < entries.length; i++) {
673
+ if (assigned.has(entries[i].id)) continue;
674
+ const group = [entries[i]];
675
+ assigned.add(entries[i].id);
676
+ for (let j = i + 1; j < entries.length; j++) {
677
+ if (assigned.has(entries[j].id)) continue;
678
+ if (group.length >= this.config.maxGroupSize) break;
679
+ const similarity = this.textSimilarity(
680
+ entries[i].content,
681
+ entries[j].content
682
+ );
683
+ if (similarity >= this.config.similarityThreshold) {
684
+ group.push(entries[j]);
685
+ assigned.add(entries[j].id);
686
+ }
687
+ }
688
+ if (group.length >= this.config.minGroupSize) {
689
+ groups.push({
690
+ id: this.generateId(),
691
+ entries: group,
692
+ similarity: 0.8,
693
+ groupKey: `text-${i}`
694
+ });
695
+ }
696
+ }
697
+ return groups;
698
+ }
699
+ /**
700
+ * Group by temporal proximity
701
+ */
702
+ groupByTemporal(entries) {
703
+ const sorted = [...entries].sort((a, b) => a.timestamp - b.timestamp);
704
+ const groups = [];
705
+ const windowMs = 60 * 60 * 1e3;
706
+ let currentGroup = [];
707
+ let windowStart = sorted[0]?.timestamp ?? 0;
708
+ for (const entry of sorted) {
709
+ if (entry.timestamp - windowStart <= windowMs && currentGroup.length < this.config.maxGroupSize) {
710
+ currentGroup.push(entry);
711
+ } else {
712
+ if (currentGroup.length >= this.config.minGroupSize) {
713
+ groups.push({
714
+ id: this.generateId(),
715
+ entries: currentGroup,
716
+ similarity: 1,
717
+ groupKey: `temporal-${new Date(windowStart).toISOString()}`
718
+ });
719
+ }
720
+ currentGroup = [entry];
721
+ windowStart = entry.timestamp;
722
+ }
723
+ }
724
+ if (currentGroup.length >= this.config.minGroupSize) {
725
+ groups.push({
726
+ id: this.generateId(),
727
+ entries: currentGroup,
728
+ similarity: 1,
729
+ groupKey: `temporal-${new Date(windowStart).toISOString()}`
730
+ });
731
+ }
732
+ return groups;
733
+ }
734
+ /**
735
+ * Group by memory type
736
+ */
737
+ groupByType(entries) {
738
+ const typeGroups = /* @__PURE__ */ new Map();
739
+ for (const entry of entries) {
740
+ if (!typeGroups.has(entry.type)) {
741
+ typeGroups.set(entry.type, []);
742
+ }
743
+ typeGroups.get(entry.type).push(entry);
744
+ }
745
+ const groups = [];
746
+ for (const [type, typeEntries] of typeGroups) {
747
+ for (let i = 0; i < typeEntries.length; i += this.config.maxGroupSize) {
748
+ const chunk = typeEntries.slice(i, i + this.config.maxGroupSize);
749
+ if (chunk.length >= this.config.minGroupSize) {
750
+ groups.push({
751
+ id: this.generateId(),
752
+ entries: chunk,
753
+ similarity: 1,
754
+ groupKey: `type-${type}-${i}`
755
+ });
756
+ }
757
+ }
758
+ }
759
+ return groups;
760
+ }
761
+ /**
762
+ * Consolidate a group into a single entry
763
+ */
764
+ async consolidateGroup(group) {
765
+ const entries = group.entries;
766
+ const summaryResult = await this.summarizer.summarize(entries);
767
+ const avgImportance = entries.reduce((sum, e) => sum + e.importance, 0) / entries.length;
768
+ const maxImportance = Math.max(...entries.map((e) => e.importance));
769
+ const combinedImportance = avgImportance * 0.7 + maxImportance * 0.3;
770
+ const timestamps = entries.map((e) => e.timestamp);
771
+ const timeSpan = {
772
+ start: Math.min(...timestamps),
773
+ end: Math.max(...timestamps)
774
+ };
775
+ const allTags = /* @__PURE__ */ new Set();
776
+ for (const entry of entries) {
777
+ const tags = entry.metadata.tags;
778
+ if (tags) {
779
+ tags.forEach((t) => allTags.add(t));
780
+ }
781
+ }
782
+ const consolidated = {
783
+ id: `consolidated-${group.id}`,
784
+ content: summaryResult.summary,
785
+ type: "summary",
786
+ importance: Math.min(combinedImportance + 0.1, 1),
787
+ // Boost for consolidation
788
+ metadata: {
789
+ source: "system",
790
+ confidence: 0.9,
791
+ consolidated: true,
792
+ sourceCount: entries.length,
793
+ groupKey: group.groupKey,
794
+ tags: Array.from(allTags),
795
+ keyPoints: summaryResult.keyPoints
796
+ },
797
+ timestamp: timeSpan.start,
798
+ accessCount: entries.reduce((sum, e) => sum + e.accessCount, 0),
799
+ createdAt: Date.now(),
800
+ updatedAt: Date.now()
801
+ };
802
+ if (this.embedFn) {
803
+ consolidated.embedding = await this.embedFn(consolidated.content);
804
+ }
805
+ return {
806
+ consolidated,
807
+ sourceIds: entries.map((e) => e.id),
808
+ groupKey: group.groupKey,
809
+ avgImportance,
810
+ timeSpan
811
+ };
812
+ }
813
+ /**
814
+ * Calculate cosine similarity
815
+ */
816
+ cosineSimilarity(a, b) {
817
+ if (a.length !== b.length) return 0;
818
+ let dotProduct = 0;
819
+ let normA = 0;
820
+ let normB = 0;
821
+ for (let i = 0; i < a.length; i++) {
822
+ dotProduct += a[i] * b[i];
823
+ normA += a[i] * a[i];
824
+ normB += b[i] * b[i];
825
+ }
826
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
827
+ return magnitude === 0 ? 0 : dotProduct / magnitude;
828
+ }
829
+ /**
830
+ * Calculate text similarity (Jaccard)
831
+ */
832
+ textSimilarity(a, b) {
833
+ const wordsA = new Set(a.toLowerCase().split(/\s+/));
834
+ const wordsB = new Set(b.toLowerCase().split(/\s+/));
835
+ const intersection = new Set([...wordsA].filter((x) => wordsB.has(x)));
836
+ const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
837
+ return intersection.size / union.size;
838
+ }
839
+ /**
840
+ * Calculate average similarity within a group
841
+ */
842
+ calculateGroupSimilarity(entries) {
843
+ if (entries.length < 2) return 1;
844
+ if (!entries[0].embedding) return 0.8;
845
+ let totalSimilarity = 0;
846
+ let comparisons = 0;
847
+ for (let i = 0; i < entries.length; i++) {
848
+ for (let j = i + 1; j < entries.length; j++) {
849
+ if (entries[i].embedding && entries[j].embedding) {
850
+ totalSimilarity += this.cosineSimilarity(
851
+ entries[i].embedding,
852
+ entries[j].embedding
853
+ );
854
+ comparisons++;
855
+ }
856
+ }
857
+ }
858
+ return comparisons > 0 ? totalSimilarity / comparisons : 0.8;
859
+ }
860
+ /**
861
+ * Generate unique ID
862
+ */
863
+ generateId() {
864
+ return `group-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
865
+ }
866
+ /**
867
+ * Update configuration
868
+ */
869
+ configure(config) {
870
+ this.config = { ...this.config, ...config };
871
+ }
872
+ };
873
+ function createConsolidator(config) {
874
+ return new Consolidator(config);
875
+ }
876
+
877
+ // src/processing/Forgetter.ts
878
+ var Forgetter = class {
879
+ config;
880
+ constructor(config = {}) {
881
+ this.config = {
882
+ retentionPolicy: config.retentionPolicy ?? {},
883
+ importanceThreshold: config.importanceThreshold ?? 0.3,
884
+ maxAge: config.maxAge ?? 30 * 24 * 60 * 60 * 1e3,
885
+ // 30 days
886
+ preserveTypes: config.preserveTypes ?? [],
887
+ curve: config.curve ?? "exponential",
888
+ halfLife: config.halfLife ?? 7 * 24 * 60 * 60 * 1e3,
889
+ // 7 days
890
+ minRetention: config.minRetention ?? 0.1,
891
+ accessBoost: config.accessBoost ?? 0.1,
892
+ importanceWeight: config.importanceWeight ?? 0.5,
893
+ forgetThreshold: config.forgetThreshold ?? 0.05
894
+ };
895
+ }
896
+ /**
897
+ * Calculate retention score for a memory
898
+ */
899
+ calculateRetention(entry, now = Date.now()) {
900
+ const ageMs = now - entry.timestamp;
901
+ const baseRetention = this.calculateBaseRetention(ageMs);
902
+ const accessBoost = Math.min(
903
+ entry.accessCount * this.config.accessBoost,
904
+ 0.5
905
+ );
906
+ const importanceBoost = entry.importance * this.config.importanceWeight;
907
+ let retention = baseRetention + accessBoost + importanceBoost;
908
+ if (entry.importance >= 0.8) {
909
+ retention = Math.max(retention, 0.5);
910
+ }
911
+ retention = Math.min(Math.max(retention, this.config.minRetention), 1);
912
+ return {
913
+ entryId: entry.id,
914
+ retention,
915
+ ageMs,
916
+ accessCount: entry.accessCount,
917
+ importance: entry.importance,
918
+ shouldForget: retention < this.config.forgetThreshold
919
+ };
920
+ }
921
+ /**
922
+ * Calculate base retention using the configured curve
923
+ */
924
+ calculateBaseRetention(ageMs) {
925
+ const halfLife = this.config.halfLife;
926
+ switch (this.config.curve) {
927
+ case "exponential":
928
+ return Math.exp(-Math.LN2 * ageMs / halfLife);
929
+ case "power": {
930
+ const c = halfLife;
931
+ const b = 0.5;
932
+ return Math.pow(1 + ageMs / c, -b);
933
+ }
934
+ case "ebbinghaus": {
935
+ const stability = halfLife;
936
+ return Math.exp(-ageMs / stability);
937
+ }
938
+ default:
939
+ return Math.exp(-Math.LN2 * ageMs / halfLife);
940
+ }
941
+ }
942
+ /**
943
+ * Apply forgetting to memories in a store
944
+ */
945
+ async applyForgetting(store) {
946
+ const { entries } = await store.query({ limit: 1e4 });
947
+ const now = Date.now();
948
+ const forgotten = [];
949
+ const decayed = [];
950
+ const retained = [];
951
+ let totalRetention = 0;
952
+ for (const entry of entries) {
953
+ const score = this.calculateRetention(entry, now);
954
+ totalRetention += score.retention;
955
+ if (score.shouldForget) {
956
+ await store.delete(entry.id);
957
+ forgotten.push(entry.id);
958
+ } else if (score.retention < 0.5 && entry.importance > 0.3) {
959
+ const newImportance = Math.max(
960
+ entry.importance * score.retention,
961
+ this.config.minRetention
962
+ );
963
+ if (newImportance < entry.importance) {
964
+ await store.update(entry.id, { importance: newImportance });
965
+ decayed.push({
966
+ id: entry.id,
967
+ oldImportance: entry.importance,
968
+ newImportance
969
+ });
970
+ }
971
+ retained.push(entry.id);
972
+ } else {
973
+ retained.push(entry.id);
974
+ }
975
+ }
976
+ return {
977
+ forgotten,
978
+ decayed,
979
+ retained,
980
+ avgRetention: entries.length > 0 ? totalRetention / entries.length : 1
981
+ };
982
+ }
983
+ /**
984
+ * Simulate forgetting over time (for testing/visualization)
985
+ */
986
+ simulateForgetting(entries, timePeriodMs, steps = 10) {
987
+ const results = [];
988
+ const stepMs = timePeriodMs / steps;
989
+ const baseTime = Date.now();
990
+ for (let i = 0; i <= steps; i++) {
991
+ const simulatedNow = baseTime + i * stepMs;
992
+ let totalRetention = 0;
993
+ let forgottenCount = 0;
994
+ for (const entry of entries) {
995
+ const score = this.calculateRetention(entry, simulatedNow);
996
+ totalRetention += score.retention;
997
+ if (score.shouldForget) forgottenCount++;
998
+ }
999
+ results.push({
1000
+ time: i * stepMs,
1001
+ avgRetention: entries.length > 0 ? totalRetention / entries.length : 1,
1002
+ forgottenCount
1003
+ });
1004
+ }
1005
+ return results;
1006
+ }
1007
+ /**
1008
+ * Get memories at risk of being forgotten
1009
+ */
1010
+ async getAtRiskMemories(store, threshold = 0.2) {
1011
+ const { entries } = await store.query({ limit: 1e3 });
1012
+ const now = Date.now();
1013
+ const atRisk = [];
1014
+ for (const entry of entries) {
1015
+ const score = this.calculateRetention(entry, now);
1016
+ if (score.retention < threshold && score.retention >= this.config.forgetThreshold) {
1017
+ atRisk.push({ entry, retention: score.retention });
1018
+ }
1019
+ }
1020
+ return Promise.resolve(atRisk.sort((a, b) => a.retention - b.retention));
1021
+ }
1022
+ /**
1023
+ * Reinforce a memory (simulate rehearsal)
1024
+ */
1025
+ async reinforce(store, id, boost = 0.2) {
1026
+ const entry = await store.get(id);
1027
+ if (!entry) return false;
1028
+ const newImportance = Math.min(entry.importance + boost, 1);
1029
+ return store.update(id, {
1030
+ importance: newImportance,
1031
+ lastAccessedAt: Date.now()
1032
+ });
1033
+ }
1034
+ /**
1035
+ * Batch reinforce multiple memories
1036
+ */
1037
+ async reinforceBatch(store, ids, boost = 0.1) {
1038
+ let count = 0;
1039
+ for (const id of ids) {
1040
+ if (await this.reinforce(store, id, boost)) {
1041
+ count++;
1042
+ }
1043
+ }
1044
+ return count;
1045
+ }
1046
+ /**
1047
+ * Calculate optimal review schedule for a memory
1048
+ */
1049
+ calculateReviewSchedule(entry, targetRetention = 0.8) {
1050
+ const baseInterval = this.config.halfLife / 10;
1051
+ const intervals = [];
1052
+ let _currentInterval = baseInterval;
1053
+ let simulatedTime = 0;
1054
+ const maxReviews = 10;
1055
+ for (let i = 0; i < maxReviews; i++) {
1056
+ let retention = 1;
1057
+ let checkTime = simulatedTime;
1058
+ while (retention > targetRetention) {
1059
+ checkTime += baseInterval / 10;
1060
+ const ageMs = checkTime - entry.timestamp;
1061
+ retention = this.calculateBaseRetention(ageMs);
1062
+ }
1063
+ intervals.push(checkTime);
1064
+ simulatedTime = checkTime;
1065
+ _currentInterval *= 2;
1066
+ }
1067
+ return intervals;
1068
+ }
1069
+ /**
1070
+ * Get statistics about memory retention
1071
+ */
1072
+ async getRetentionStats(store) {
1073
+ const { entries } = await store.query({ limit: 1e4 });
1074
+ const now = Date.now();
1075
+ let totalRetention = 0;
1076
+ let atRiskCount = 0;
1077
+ let forgettableCount = 0;
1078
+ let healthyCount = 0;
1079
+ const distribution = {
1080
+ "0-20%": 0,
1081
+ "20-40%": 0,
1082
+ "40-60%": 0,
1083
+ "60-80%": 0,
1084
+ "80-100%": 0
1085
+ };
1086
+ for (const entry of entries) {
1087
+ const score = this.calculateRetention(entry, now);
1088
+ totalRetention += score.retention;
1089
+ if (score.shouldForget) {
1090
+ forgettableCount++;
1091
+ } else if (score.retention < 0.3) {
1092
+ atRiskCount++;
1093
+ } else {
1094
+ healthyCount++;
1095
+ }
1096
+ if (score.retention < 0.2) distribution["0-20%"]++;
1097
+ else if (score.retention < 0.4) distribution["20-40%"]++;
1098
+ else if (score.retention < 0.6) distribution["40-60%"]++;
1099
+ else if (score.retention < 0.8) distribution["60-80%"]++;
1100
+ else distribution["80-100%"]++;
1101
+ }
1102
+ return {
1103
+ avgRetention: entries.length > 0 ? totalRetention / entries.length : 1,
1104
+ atRiskCount,
1105
+ forgettableCount,
1106
+ healthyCount,
1107
+ distribution
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Update configuration
1112
+ */
1113
+ configure(config) {
1114
+ this.config = { ...this.config, ...config };
1115
+ }
1116
+ };
1117
+ function createForgetter(config) {
1118
+ return new Forgetter(config);
1119
+ }
1120
+
1121
+ // src/processing/Extractor.ts
1122
+ var Extractor = class {
1123
+ config;
1124
+ extractFn;
1125
+ constructor(config = {}) {
1126
+ this.config = {
1127
+ provider: config.provider ?? void 0,
1128
+ model: config.model ?? "default",
1129
+ extractTypes: config.extractTypes ?? [],
1130
+ customPrompt: config.customPrompt ?? "",
1131
+ confidence: config.confidence ?? 0.5,
1132
+ extractEntities: config.extractEntities ?? true,
1133
+ extractRelations: config.extractRelations ?? true,
1134
+ extractKeywords: config.extractKeywords ?? true,
1135
+ extractSentiment: config.extractSentiment ?? false,
1136
+ minConfidence: config.minConfidence ?? 0.5,
1137
+ maxEntitiesPerEntry: config.maxEntitiesPerEntry ?? 20
1138
+ };
1139
+ }
1140
+ /**
1141
+ * Set custom extraction function (for LLM integration)
1142
+ */
1143
+ setExtractionFunction(fn) {
1144
+ this.extractFn = fn;
1145
+ }
1146
+ /**
1147
+ * Extract information from a memory entry
1148
+ */
1149
+ async extract(entry) {
1150
+ if (this.extractFn) {
1151
+ return this.extractFn(entry.content, {
1152
+ extractRelations: this.config.extractRelations,
1153
+ extractSentiment: this.config.extractSentiment
1154
+ });
1155
+ }
1156
+ return Promise.resolve(this.heuristicExtract(entry.content));
1157
+ }
1158
+ /**
1159
+ * Extract from multiple entries
1160
+ */
1161
+ async extractBatch(entries) {
1162
+ const results = /* @__PURE__ */ new Map();
1163
+ for (const entry of entries) {
1164
+ const result = await this.extract(entry);
1165
+ results.set(entry.id, result);
1166
+ }
1167
+ return results;
1168
+ }
1169
+ /**
1170
+ * Extract and aggregate across multiple entries
1171
+ */
1172
+ async extractAggregate(entries) {
1173
+ const entityMap = /* @__PURE__ */ new Map();
1174
+ const allRelations = [];
1175
+ const keywordCounts = /* @__PURE__ */ new Map();
1176
+ let sentimentSum = 0;
1177
+ let sentimentCount = 0;
1178
+ for (const entry of entries) {
1179
+ const result = await this.extract(entry);
1180
+ for (const entity of result.entities) {
1181
+ const key = `${entity.type}:${entity.text.toLowerCase()}`;
1182
+ const existing = entityMap.get(key);
1183
+ if (existing) {
1184
+ existing.count++;
1185
+ existing.entity.confidence = Math.max(
1186
+ existing.entity.confidence,
1187
+ entity.confidence
1188
+ );
1189
+ } else {
1190
+ entityMap.set(key, { entity, count: 1 });
1191
+ }
1192
+ }
1193
+ allRelations.push(...result.relations);
1194
+ for (const keyword of result.keywords) {
1195
+ const lower = keyword.toLowerCase();
1196
+ keywordCounts.set(lower, (keywordCounts.get(lower) ?? 0) + 1);
1197
+ }
1198
+ if (result.sentiment) {
1199
+ sentimentSum += result.sentiment.score;
1200
+ sentimentCount++;
1201
+ }
1202
+ }
1203
+ const topKeywords = Array.from(keywordCounts.entries()).map(([keyword, count]) => ({ keyword, count })).sort((a, b) => b.count - a.count).slice(0, 20);
1204
+ return {
1205
+ allEntities: entityMap,
1206
+ allRelations,
1207
+ topKeywords,
1208
+ avgSentiment: sentimentCount > 0 ? sentimentSum / sentimentCount : null
1209
+ };
1210
+ }
1211
+ /**
1212
+ * Heuristic-based extraction (fallback)
1213
+ */
1214
+ heuristicExtract(content) {
1215
+ const entities = [];
1216
+ const relations = [];
1217
+ const keywords = [];
1218
+ if (this.config.extractEntities) {
1219
+ entities.push(...this.extractEntities(content));
1220
+ }
1221
+ if (this.config.extractRelations) {
1222
+ relations.push(...this.extractRelations(content));
1223
+ }
1224
+ if (this.config.extractKeywords) {
1225
+ keywords.push(...this.extractKeywords(content));
1226
+ }
1227
+ const sentiment = this.config.extractSentiment ? this.extractSentiment(content) : void 0;
1228
+ return {
1229
+ entities: entities.slice(0, this.config.maxEntitiesPerEntry),
1230
+ relations,
1231
+ keywords,
1232
+ sentiment
1233
+ };
1234
+ }
1235
+ /**
1236
+ * Extract entities using patterns
1237
+ */
1238
+ extractEntities(content) {
1239
+ const entities = [];
1240
+ const datePatterns = [
1241
+ /\b\d{1,2}\/\d{1,2}\/\d{2,4}\b/g,
1242
+ /\b\d{4}-\d{2}-\d{2}\b/g,
1243
+ /\b(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}(?:,\s*\d{4})?\b/gi,
1244
+ /\b(?:today|yesterday|tomorrow|last\s+week|next\s+month)\b/gi
1245
+ ];
1246
+ for (const pattern of datePatterns) {
1247
+ let match2;
1248
+ while ((match2 = pattern.exec(content)) !== null) {
1249
+ entities.push({
1250
+ text: match2[0],
1251
+ type: "date",
1252
+ confidence: 0.9,
1253
+ position: { start: match2.index, end: match2.index + match2[0].length }
1254
+ });
1255
+ }
1256
+ }
1257
+ const numberPattern = /\b\d+(?:\.\d+)?(?:\s*(?:percent|%|dollars|\$|euros|€|pounds|£))?\b/gi;
1258
+ let match;
1259
+ while ((match = numberPattern.exec(content)) !== null) {
1260
+ entities.push({
1261
+ text: match[0],
1262
+ type: "number",
1263
+ confidence: 0.8,
1264
+ position: { start: match.index, end: match.index + match[0].length }
1265
+ });
1266
+ }
1267
+ const capitalizedPattern = /\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+\b/g;
1268
+ while ((match = capitalizedPattern.exec(content)) !== null) {
1269
+ const text = match[0];
1270
+ if (!this.isSentenceStarter(text, content, match.index)) {
1271
+ const type = this.guessEntityType(text);
1272
+ entities.push({
1273
+ text,
1274
+ type,
1275
+ confidence: 0.6,
1276
+ position: { start: match.index, end: match.index + text.length }
1277
+ });
1278
+ }
1279
+ }
1280
+ const emailPattern = /\b[\w.-]+@[\w.-]+\.\w+\b/g;
1281
+ while ((match = emailPattern.exec(content)) !== null) {
1282
+ entities.push({
1283
+ text: match[0],
1284
+ type: "other",
1285
+ confidence: 0.95,
1286
+ position: { start: match.index, end: match.index + match[0].length },
1287
+ metadata: { subtype: "email" }
1288
+ });
1289
+ }
1290
+ const urlPattern = /https?:\/\/[^\s]+/g;
1291
+ while ((match = urlPattern.exec(content)) !== null) {
1292
+ entities.push({
1293
+ text: match[0],
1294
+ type: "other",
1295
+ confidence: 0.95,
1296
+ position: { start: match.index, end: match.index + match[0].length },
1297
+ metadata: { subtype: "url" }
1298
+ });
1299
+ }
1300
+ return entities.filter((e) => e.confidence >= this.config.minConfidence);
1301
+ }
1302
+ /**
1303
+ * Extract relations using patterns
1304
+ */
1305
+ extractRelations(content) {
1306
+ const relations = [];
1307
+ const patterns = [
1308
+ // "X is a/an Y"
1309
+ {
1310
+ regex: /(\w+(?:\s+\w+)?)\s+is\s+a(?:n)?\s+(\w+(?:\s+\w+)?)/gi,
1311
+ predicate: "is_a"
1312
+ },
1313
+ // "X works at Y"
1314
+ {
1315
+ regex: /(\w+(?:\s+\w+)?)\s+works\s+at\s+(\w+(?:\s+\w+)?)/gi,
1316
+ predicate: "works_at"
1317
+ },
1318
+ // "X is located in Y"
1319
+ {
1320
+ regex: /(\w+(?:\s+\w+)?)\s+is\s+located\s+in\s+(\w+(?:\s+\w+)?)/gi,
1321
+ predicate: "located_in"
1322
+ },
1323
+ // "X belongs to Y"
1324
+ {
1325
+ regex: /(\w+(?:\s+\w+)?)\s+belongs\s+to\s+(\w+(?:\s+\w+)?)/gi,
1326
+ predicate: "belongs_to"
1327
+ },
1328
+ // "X created Y"
1329
+ {
1330
+ regex: /(\w+(?:\s+\w+)?)\s+created\s+(\w+(?:\s+\w+)?)/gi,
1331
+ predicate: "created"
1332
+ },
1333
+ // "X contains Y"
1334
+ {
1335
+ regex: /(\w+(?:\s+\w+)?)\s+contains\s+(\w+(?:\s+\w+)?)/gi,
1336
+ predicate: "contains"
1337
+ }
1338
+ ];
1339
+ for (const { regex, predicate } of patterns) {
1340
+ let match;
1341
+ while ((match = regex.exec(content)) !== null) {
1342
+ relations.push({
1343
+ subject: match[1].trim(),
1344
+ predicate,
1345
+ object: match[2].trim(),
1346
+ confidence: 0.7,
1347
+ sourceText: match[0]
1348
+ });
1349
+ }
1350
+ }
1351
+ return relations.filter((r) => r.confidence >= this.config.minConfidence);
1352
+ }
1353
+ /**
1354
+ * Extract keywords
1355
+ */
1356
+ extractKeywords(content) {
1357
+ const stopWords = /* @__PURE__ */ new Set([
1358
+ "the",
1359
+ "a",
1360
+ "an",
1361
+ "and",
1362
+ "or",
1363
+ "but",
1364
+ "in",
1365
+ "on",
1366
+ "at",
1367
+ "to",
1368
+ "for",
1369
+ "of",
1370
+ "with",
1371
+ "by",
1372
+ "from",
1373
+ "as",
1374
+ "is",
1375
+ "was",
1376
+ "are",
1377
+ "were",
1378
+ "been",
1379
+ "be",
1380
+ "have",
1381
+ "has",
1382
+ "had",
1383
+ "do",
1384
+ "does",
1385
+ "did",
1386
+ "will",
1387
+ "would",
1388
+ "could",
1389
+ "should",
1390
+ "may",
1391
+ "might",
1392
+ "must",
1393
+ "shall",
1394
+ "can",
1395
+ "need",
1396
+ "it",
1397
+ "its",
1398
+ "this",
1399
+ "that",
1400
+ "these",
1401
+ "those",
1402
+ "i",
1403
+ "you",
1404
+ "he",
1405
+ "she",
1406
+ "we",
1407
+ "they",
1408
+ "what",
1409
+ "which",
1410
+ "who",
1411
+ "when",
1412
+ "where",
1413
+ "why",
1414
+ "how",
1415
+ "all",
1416
+ "each",
1417
+ "every",
1418
+ "both",
1419
+ "few",
1420
+ "more",
1421
+ "most",
1422
+ "other",
1423
+ "some",
1424
+ "such",
1425
+ "no",
1426
+ "not",
1427
+ "only",
1428
+ "own",
1429
+ "same",
1430
+ "so",
1431
+ "than",
1432
+ "too",
1433
+ "very"
1434
+ ]);
1435
+ const words = content.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((word) => word.length > 3 && !stopWords.has(word));
1436
+ const counts = /* @__PURE__ */ new Map();
1437
+ for (const word of words) {
1438
+ counts.set(word, (counts.get(word) ?? 0) + 1);
1439
+ }
1440
+ return Array.from(counts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([word]) => word);
1441
+ }
1442
+ /**
1443
+ * Extract sentiment
1444
+ */
1445
+ extractSentiment(content) {
1446
+ const positiveWords = /* @__PURE__ */ new Set([
1447
+ "good",
1448
+ "great",
1449
+ "excellent",
1450
+ "amazing",
1451
+ "wonderful",
1452
+ "fantastic",
1453
+ "happy",
1454
+ "love",
1455
+ "best",
1456
+ "awesome",
1457
+ "nice",
1458
+ "beautiful",
1459
+ "perfect",
1460
+ "success",
1461
+ "successful",
1462
+ "pleased",
1463
+ "delighted",
1464
+ "enjoy",
1465
+ "enjoyed",
1466
+ "helpful",
1467
+ "thanks",
1468
+ "thank"
1469
+ ]);
1470
+ const negativeWords = /* @__PURE__ */ new Set([
1471
+ "bad",
1472
+ "terrible",
1473
+ "awful",
1474
+ "horrible",
1475
+ "poor",
1476
+ "worst",
1477
+ "hate",
1478
+ "sad",
1479
+ "angry",
1480
+ "disappointed",
1481
+ "frustrating",
1482
+ "annoying",
1483
+ "problem",
1484
+ "issue",
1485
+ "error",
1486
+ "fail",
1487
+ "failed",
1488
+ "failure",
1489
+ "wrong",
1490
+ "broken",
1491
+ "difficult",
1492
+ "hard"
1493
+ ]);
1494
+ const words = content.toLowerCase().split(/\s+/);
1495
+ let positiveCount = 0;
1496
+ let negativeCount = 0;
1497
+ for (const word of words) {
1498
+ if (positiveWords.has(word)) positiveCount++;
1499
+ if (negativeWords.has(word)) negativeCount++;
1500
+ }
1501
+ const total = positiveCount + negativeCount;
1502
+ if (total === 0) {
1503
+ return { score: 0, label: "neutral" };
1504
+ }
1505
+ const score = (positiveCount - negativeCount) / total;
1506
+ let label;
1507
+ if (score > 0.2) label = "positive";
1508
+ else if (score < -0.2) label = "negative";
1509
+ else label = "neutral";
1510
+ return { score, label };
1511
+ }
1512
+ /**
1513
+ * Check if text is a sentence starter
1514
+ */
1515
+ isSentenceStarter(_text, content, index) {
1516
+ if (index === 0) return true;
1517
+ const prevChar = content[index - 1];
1518
+ return prevChar === "." || prevChar === "!" || prevChar === "?" || prevChar === "\n";
1519
+ }
1520
+ /**
1521
+ * Guess entity type from text
1522
+ */
1523
+ guessEntityType(text) {
1524
+ const lowerText = text.toLowerCase();
1525
+ const locationIndicators = [
1526
+ "city",
1527
+ "state",
1528
+ "country",
1529
+ "street",
1530
+ "avenue",
1531
+ "road"
1532
+ ];
1533
+ if (locationIndicators.some((ind) => lowerText.includes(ind))) {
1534
+ return "location";
1535
+ }
1536
+ const orgIndicators = [
1537
+ "inc",
1538
+ "corp",
1539
+ "company",
1540
+ "organization",
1541
+ "foundation",
1542
+ "institute"
1543
+ ];
1544
+ if (orgIndicators.some((ind) => lowerText.includes(ind))) {
1545
+ return "organization";
1546
+ }
1547
+ const wordCount = text.split(/\s+/).length;
1548
+ if (wordCount >= 2 && wordCount <= 3) {
1549
+ return "person";
1550
+ }
1551
+ return "concept";
1552
+ }
1553
+ /**
1554
+ * Update configuration
1555
+ */
1556
+ configure(config) {
1557
+ this.config = { ...this.config, ...config };
1558
+ }
1559
+ };
1560
+ function createExtractor(config) {
1561
+ return new Extractor(config);
1562
+ }
1563
+ // Annotate the CommonJS export names for ESM import in node:
1564
+ 0 && (module.exports = {
1565
+ Compressor,
1566
+ Consolidator,
1567
+ Extractor,
1568
+ Forgetter,
1569
+ Summarizer,
1570
+ createCompressor,
1571
+ createConsolidator,
1572
+ createExtractor,
1573
+ createForgetter,
1574
+ createSummarizer
1575
+ });