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