@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,1530 @@
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/structures/index.ts
21
+ var structures_exports = {};
22
+ __export(structures_exports, {
23
+ EpisodicMemory: () => EpisodicMemory,
24
+ HierarchicalMemory: () => HierarchicalMemory,
25
+ LongTermMemory: () => LongTermMemory,
26
+ SemanticMemory: () => SemanticMemory,
27
+ WorkingMemory: () => WorkingMemory,
28
+ createEpisodicMemory: () => createEpisodicMemory,
29
+ createHierarchicalMemory: () => createHierarchicalMemory,
30
+ createLongTermMemory: () => createLongTermMemory,
31
+ createSemanticMemory: () => createSemanticMemory,
32
+ createWorkingMemory: () => createWorkingMemory
33
+ });
34
+ module.exports = __toCommonJS(structures_exports);
35
+
36
+ // src/structures/WorkingMemory.ts
37
+ var import_eventemitter3 = require("eventemitter3");
38
+ var WorkingMemory = class extends import_eventemitter3.EventEmitter {
39
+ store;
40
+ config;
41
+ contextWindow = [];
42
+ attentionBuffer = /* @__PURE__ */ new Map();
43
+ currentQuery = "";
44
+ constructor(store, config = {}) {
45
+ super();
46
+ this.store = store;
47
+ this.config = {
48
+ maxItems: config.maxItems ?? 20,
49
+ maxSize: config.maxSize ?? 20,
50
+ ttl: config.ttl ?? 3e5,
51
+ // 5 minutes default
52
+ importance: config.importance ?? "recency",
53
+ onEvict: config.onEvict ?? (() => {
54
+ }),
55
+ attentionWindow: config.attentionWindow ?? 5,
56
+ decayRate: config.decayRate ?? 0.1,
57
+ relevanceThreshold: config.relevanceThreshold ?? 0.3,
58
+ autoEvict: config.autoEvict ?? true
59
+ };
60
+ }
61
+ /**
62
+ * Add an entry to working memory
63
+ */
64
+ async add(entry) {
65
+ const existingIdx = this.contextWindow.findIndex((e) => e.id === entry.id);
66
+ if (existingIdx !== -1) {
67
+ this.contextWindow.splice(existingIdx, 1);
68
+ }
69
+ this.contextWindow.unshift(entry);
70
+ this.attentionBuffer.set(entry.id, 1);
71
+ if (this.contextWindow.length > this.config.maxSize) {
72
+ await this.evictLowestAttention();
73
+ }
74
+ this.emit("contextUpdate", this.contextWindow);
75
+ }
76
+ /**
77
+ * Get all entries in working memory
78
+ */
79
+ getContext() {
80
+ return [...this.contextWindow];
81
+ }
82
+ /**
83
+ * Get entries with attention scores
84
+ */
85
+ getContextWithAttention() {
86
+ return this.contextWindow.map((entry, index) => {
87
+ const recency = 1 - index / this.config.maxSize;
88
+ const attention = this.attentionBuffer.get(entry.id) ?? 0;
89
+ const relevance = this.calculateRelevance(entry);
90
+ const importance = entry.importance;
91
+ const total = recency * 0.3 + attention * 0.3 + relevance * 0.25 + importance * 0.15;
92
+ return {
93
+ entry,
94
+ relevance,
95
+ recency,
96
+ importance,
97
+ total
98
+ };
99
+ });
100
+ }
101
+ /**
102
+ * Update attention for an entry (e.g., when referenced)
103
+ */
104
+ attend(id) {
105
+ const current = this.attentionBuffer.get(id) ?? 0;
106
+ const newScore = Math.min(current + 0.2, 1);
107
+ this.attentionBuffer.set(id, newScore);
108
+ const entry = this.contextWindow.find((e) => e.id === id);
109
+ if (entry) {
110
+ this.emit("attention", entry, newScore);
111
+ }
112
+ }
113
+ /**
114
+ * Set the current query/context for relevance calculation
115
+ */
116
+ setQuery(query) {
117
+ this.currentQuery = query;
118
+ }
119
+ /**
120
+ * Decay attention scores
121
+ */
122
+ decay() {
123
+ for (const [id, score] of this.attentionBuffer) {
124
+ const newScore = score * (1 - this.config.decayRate);
125
+ if (newScore < 0.01) {
126
+ this.attentionBuffer.delete(id);
127
+ } else {
128
+ this.attentionBuffer.set(id, newScore);
129
+ }
130
+ }
131
+ }
132
+ /**
133
+ * Get the most attended entries
134
+ */
135
+ getFocused(limit = 5) {
136
+ const scored = this.getContextWithAttention();
137
+ scored.sort((a, b) => b.total - a.total);
138
+ return scored.slice(0, limit).map((s) => s.entry);
139
+ }
140
+ /**
141
+ * Clear working memory
142
+ */
143
+ clear() {
144
+ const evicted = [...this.contextWindow];
145
+ this.contextWindow = [];
146
+ this.attentionBuffer.clear();
147
+ this.emit("overflow", evicted);
148
+ this.emit("contextUpdate", []);
149
+ }
150
+ /**
151
+ * Remove a specific entry
152
+ */
153
+ remove(id) {
154
+ const idx = this.contextWindow.findIndex((e) => e.id === id);
155
+ if (idx === -1) return false;
156
+ this.contextWindow.splice(idx, 1);
157
+ this.attentionBuffer.delete(id);
158
+ this.emit("contextUpdate", this.contextWindow);
159
+ return true;
160
+ }
161
+ /**
162
+ * Get current size
163
+ */
164
+ get size() {
165
+ return this.contextWindow.length;
166
+ }
167
+ /**
168
+ * Check if at capacity
169
+ */
170
+ get isFull() {
171
+ return this.contextWindow.length >= this.config.maxSize;
172
+ }
173
+ /**
174
+ * Consolidate important items to long-term storage
175
+ */
176
+ async consolidate(targetStore) {
177
+ const scored = this.getContextWithAttention();
178
+ const toConsolidate = scored.filter(
179
+ (s) => s.total > this.config.relevanceThreshold
180
+ );
181
+ let count = 0;
182
+ for (const { entry } of toConsolidate) {
183
+ await targetStore.add(entry);
184
+ count++;
185
+ }
186
+ return count;
187
+ }
188
+ /**
189
+ * Load context from store
190
+ */
191
+ async loadFromStore(options) {
192
+ const { entries } = await this.store.query({
193
+ namespace: options?.namespace,
194
+ userId: options?.userId,
195
+ conversationId: options?.conversationId,
196
+ limit: options?.limit ?? this.config.maxSize
197
+ });
198
+ this.contextWindow = entries;
199
+ this.attentionBuffer.clear();
200
+ for (const entry of entries) {
201
+ this.attentionBuffer.set(entry.id, 0.5);
202
+ }
203
+ this.emit("contextUpdate", this.contextWindow);
204
+ }
205
+ /**
206
+ * Calculate relevance to current query
207
+ */
208
+ calculateRelevance(entry) {
209
+ if (!this.currentQuery) return 0.5;
210
+ const queryWords = this.currentQuery.toLowerCase().split(/\s+/);
211
+ const contentWords = entry.content.toLowerCase();
212
+ let matches = 0;
213
+ for (const word of queryWords) {
214
+ if (word.length > 2 && contentWords.includes(word)) {
215
+ matches++;
216
+ }
217
+ }
218
+ return queryWords.length > 0 ? matches / queryWords.length : 0;
219
+ }
220
+ /**
221
+ * Evict entry with lowest attention
222
+ */
223
+ async evictLowestAttention() {
224
+ const scored = this.getContextWithAttention();
225
+ scored.sort((a, b) => a.total - b.total);
226
+ const toEvict = scored[0];
227
+ if (toEvict) {
228
+ const idx = this.contextWindow.findIndex(
229
+ (e) => e.id === toEvict.entry.id
230
+ );
231
+ if (idx !== -1) {
232
+ const evicted = this.contextWindow.splice(idx, 1);
233
+ this.attentionBuffer.delete(toEvict.entry.id);
234
+ this.emit("overflow", evicted);
235
+ }
236
+ }
237
+ return Promise.resolve();
238
+ }
239
+ /**
240
+ * Get summary of working memory state
241
+ */
242
+ getSummary() {
243
+ const types = {};
244
+ let totalAttention = 0;
245
+ for (const entry of this.contextWindow) {
246
+ types[entry.type] = (types[entry.type] ?? 0) + 1;
247
+ totalAttention += this.attentionBuffer.get(entry.id) ?? 0;
248
+ }
249
+ return {
250
+ size: this.contextWindow.length,
251
+ maxSize: this.config.maxSize,
252
+ avgAttention: this.contextWindow.length > 0 ? totalAttention / this.contextWindow.length : 0,
253
+ topTypes: types
254
+ };
255
+ }
256
+ };
257
+ function createWorkingMemory(store, config) {
258
+ return new WorkingMemory(store, config);
259
+ }
260
+
261
+ // src/structures/EpisodicMemory.ts
262
+ var import_eventemitter32 = require("eventemitter3");
263
+ var EpisodicMemory = class extends import_eventemitter32.EventEmitter {
264
+ store;
265
+ config;
266
+ episodes = /* @__PURE__ */ new Map();
267
+ currentEpisode = null;
268
+ episodeTimeout = null;
269
+ constructor(store, config = {}) {
270
+ super();
271
+ this.store = store;
272
+ this.config = {
273
+ store: config.store ?? store,
274
+ consolidateAfter: config.consolidateAfter ?? 24 * 60 * 60 * 1e3,
275
+ // 24 hours
276
+ summarizeThreshold: config.summarizeThreshold ?? 10,
277
+ retentionDays: config.retentionDays ?? 90,
278
+ maxEpisodeLength: config.maxEpisodeLength ?? 50,
279
+ episodeTimeout: config.episodeTimeout ?? 30 * 60 * 1e3,
280
+ // 30 minutes
281
+ autoSummarize: config.autoSummarize ?? true,
282
+ minEventsForEpisode: config.minEventsForEpisode ?? 3,
283
+ emotionTracking: config.emotionTracking ?? false
284
+ };
285
+ }
286
+ /**
287
+ * Record an event
288
+ */
289
+ async recordEvent(entry) {
290
+ const eventEntry = {
291
+ ...entry,
292
+ type: "event"
293
+ };
294
+ await this.store.add(eventEntry);
295
+ let episode = this.currentEpisode;
296
+ if (!episode) {
297
+ episode = this.startEpisode();
298
+ }
299
+ episode.events.push(eventEntry);
300
+ this.resetEpisodeTimeout();
301
+ if (episode.events.length >= this.config.maxEpisodeLength) {
302
+ await this.endCurrentEpisode();
303
+ }
304
+ this.emit("eventAdded", eventEntry, episode);
305
+ return episode;
306
+ }
307
+ /**
308
+ * Start a new episode
309
+ */
310
+ startEpisode(metadata) {
311
+ if (this.currentEpisode) {
312
+ void this.endCurrentEpisode();
313
+ }
314
+ const episode = {
315
+ id: this.generateId(),
316
+ startTime: Date.now(),
317
+ events: [],
318
+ metadata: metadata ?? {}
319
+ };
320
+ this.currentEpisode = episode;
321
+ this.episodes.set(episode.id, episode);
322
+ this.emit("episodeStart", episode);
323
+ return episode;
324
+ }
325
+ /**
326
+ * End the current episode
327
+ */
328
+ async endCurrentEpisode() {
329
+ if (!this.currentEpisode) return null;
330
+ const episode = this.currentEpisode;
331
+ episode.endTime = Date.now();
332
+ if (this.config.autoSummarize && episode.events.length >= this.config.minEventsForEpisode) {
333
+ episode.summary = this.generateEpisodeSummary(episode);
334
+ }
335
+ if (episode.summary) {
336
+ await this.store.add({
337
+ id: `episode-summary-${episode.id}`,
338
+ content: episode.summary,
339
+ type: "summary",
340
+ importance: this.calculateEpisodeImportance(episode),
341
+ metadata: {
342
+ source: "system",
343
+ confidence: 0.9,
344
+ episodeId: episode.id,
345
+ eventCount: episode.events.length,
346
+ duration: episode.endTime - episode.startTime,
347
+ ...episode.metadata
348
+ },
349
+ timestamp: episode.startTime,
350
+ accessCount: 0,
351
+ createdAt: Date.now(),
352
+ updatedAt: Date.now()
353
+ });
354
+ }
355
+ this.currentEpisode = null;
356
+ if (this.episodeTimeout) {
357
+ clearTimeout(this.episodeTimeout);
358
+ this.episodeTimeout = null;
359
+ }
360
+ this.emit("episodeEnd", episode);
361
+ return episode;
362
+ }
363
+ /**
364
+ * Recall events from a time period
365
+ */
366
+ async recall(options) {
367
+ const { entries } = await this.store.query({
368
+ types: ["event"],
369
+ startTime: options.startTime,
370
+ endTime: options.endTime,
371
+ conversationId: options.conversationId,
372
+ userId: options.userId,
373
+ limit: options.limit ?? 50
374
+ });
375
+ return entries;
376
+ }
377
+ /**
378
+ * Search episodes by content
379
+ */
380
+ async searchEpisodes(query, limit = 10) {
381
+ const { entries } = await this.store.query({
382
+ query,
383
+ types: ["summary"],
384
+ limit
385
+ });
386
+ const results = [];
387
+ for (const entry of entries) {
388
+ const episodeId = entry.metadata.episodeId;
389
+ if (episodeId && this.episodes.has(episodeId)) {
390
+ results.push(this.episodes.get(episodeId));
391
+ }
392
+ }
393
+ return results;
394
+ }
395
+ /**
396
+ * Get episode by ID
397
+ */
398
+ getEpisode(id) {
399
+ return this.episodes.get(id);
400
+ }
401
+ /**
402
+ * Get current episode
403
+ */
404
+ getCurrentEpisode() {
405
+ return this.currentEpisode;
406
+ }
407
+ /**
408
+ * Get all episodes in a time range
409
+ */
410
+ getEpisodesByTimeRange(startTime, endTime) {
411
+ return Array.from(this.episodes.values()).filter(
412
+ (ep) => ep.startTime >= startTime && (ep.endTime ?? Date.now()) <= endTime
413
+ );
414
+ }
415
+ /**
416
+ * Get recent episodes
417
+ */
418
+ getRecentEpisodes(limit = 5) {
419
+ const sorted = Array.from(this.episodes.values()).sort(
420
+ (a, b) => b.startTime - a.startTime
421
+ );
422
+ return sorted.slice(0, limit);
423
+ }
424
+ /**
425
+ * Find similar episodes
426
+ */
427
+ async findSimilarEpisodes(episode, embedFn, limit = 5) {
428
+ if (!episode.summary) return [];
429
+ const embedding = await embedFn(episode.summary);
430
+ const results = await this.store.search(embedding, {
431
+ topK: limit + 1
432
+ // +1 to potentially exclude self
433
+ });
434
+ const similar = [];
435
+ for (const result of results) {
436
+ const episodeId = result.entry.metadata.episodeId;
437
+ if (episodeId && episodeId !== episode.id && this.episodes.has(episodeId)) {
438
+ similar.push({
439
+ episode: this.episodes.get(episodeId),
440
+ similarity: result.score
441
+ });
442
+ }
443
+ }
444
+ return Promise.resolve(similar.slice(0, limit));
445
+ }
446
+ /**
447
+ * Merge related episodes
448
+ */
449
+ mergeEpisodes(episodeIds, newTitle) {
450
+ const episodes = episodeIds.map((id) => this.episodes.get(id)).filter((ep) => ep !== void 0);
451
+ if (episodes.length < 2) return null;
452
+ episodes.sort((a, b) => a.startTime - b.startTime);
453
+ const merged = {
454
+ id: this.generateId(),
455
+ title: newTitle ?? `Merged episode (${episodes.length} episodes)`,
456
+ startTime: episodes[0].startTime,
457
+ endTime: episodes[episodes.length - 1].endTime,
458
+ events: episodes.flatMap((ep) => ep.events),
459
+ metadata: {
460
+ mergedFrom: episodeIds
461
+ }
462
+ };
463
+ merged.summary = this.generateEpisodeSummary(merged);
464
+ this.episodes.set(merged.id, merged);
465
+ return merged;
466
+ }
467
+ /**
468
+ * Reset episode timeout
469
+ */
470
+ resetEpisodeTimeout() {
471
+ if (this.episodeTimeout) {
472
+ clearTimeout(this.episodeTimeout);
473
+ }
474
+ this.episodeTimeout = setTimeout(() => {
475
+ void this.endCurrentEpisode();
476
+ }, this.config.episodeTimeout);
477
+ }
478
+ /**
479
+ * Generate episode summary
480
+ */
481
+ generateEpisodeSummary(episode) {
482
+ const eventSummaries = episode.events.slice(0, 10).map((e) => e.content.slice(0, 100)).join("; ");
483
+ const duration = episode.endTime ? Math.round((episode.endTime - episode.startTime) / 6e4) : "ongoing";
484
+ return `Episode with ${episode.events.length} events over ${duration} minutes: ${eventSummaries}`;
485
+ }
486
+ /**
487
+ * Calculate episode importance
488
+ */
489
+ calculateEpisodeImportance(episode) {
490
+ if (episode.events.length === 0) return 0;
491
+ const avgImportance = episode.events.reduce((sum, e) => sum + e.importance, 0) / episode.events.length;
492
+ const lengthBonus = Math.min(episode.events.length / 20, 0.2);
493
+ return Math.min(avgImportance + lengthBonus, 1);
494
+ }
495
+ /**
496
+ * Generate unique ID
497
+ */
498
+ generateId() {
499
+ return `ep-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
500
+ }
501
+ /**
502
+ * Get statistics
503
+ */
504
+ getStats() {
505
+ const totalEvents = Array.from(this.episodes.values()).reduce(
506
+ (sum, ep) => sum + ep.events.length,
507
+ 0
508
+ );
509
+ return {
510
+ totalEpisodes: this.episodes.size,
511
+ currentEpisodeSize: this.currentEpisode?.events.length ?? 0,
512
+ totalEvents,
513
+ avgEventsPerEpisode: this.episodes.size > 0 ? totalEvents / this.episodes.size : 0
514
+ };
515
+ }
516
+ };
517
+ function createEpisodicMemory(store, config) {
518
+ return new EpisodicMemory(store, config);
519
+ }
520
+
521
+ // src/structures/SemanticMemory.ts
522
+ var import_eventemitter33 = require("eventemitter3");
523
+ var SemanticMemory = class extends import_eventemitter33.EventEmitter {
524
+ store;
525
+ config;
526
+ concepts = /* @__PURE__ */ new Map();
527
+ relationships = /* @__PURE__ */ new Map();
528
+ conceptIndex = /* @__PURE__ */ new Map();
529
+ // category -> concept IDs
530
+ constructor(store, config = {}) {
531
+ super();
532
+ this.store = store;
533
+ this.config = {
534
+ store: config.store ?? store,
535
+ extractEntities: config.extractEntities ?? true,
536
+ extractRelations: config.extractRelations ?? true,
537
+ deduplication: config.deduplication ?? true,
538
+ deduplicationThreshold: config.deduplicationThreshold ?? 0.9,
539
+ maxConcepts: config.maxConcepts ?? 1e4,
540
+ enableInference: config.enableInference ?? true,
541
+ conflictResolution: config.conflictResolution ?? "newest",
542
+ minConfidence: config.minConfidence ?? 0.5
543
+ };
544
+ }
545
+ /**
546
+ * Learn a new fact
547
+ */
548
+ async learnFact(content, metadata) {
549
+ const fact = {
550
+ id: this.generateId("fact"),
551
+ content,
552
+ type: "fact",
553
+ importance: metadata?.importance ?? 0.5,
554
+ metadata: {
555
+ source: "explicit",
556
+ confidence: metadata?.confidence ?? 0.8,
557
+ ...metadata,
558
+ verified: metadata?.verified ?? false
559
+ },
560
+ timestamp: Date.now(),
561
+ accessCount: 0,
562
+ createdAt: Date.now(),
563
+ updatedAt: Date.now()
564
+ };
565
+ await this.store.add(fact);
566
+ this.emit("factLearned", fact);
567
+ return fact;
568
+ }
569
+ /**
570
+ * Add or update a concept
571
+ */
572
+ addConcept(concept) {
573
+ const existing = Array.from(this.concepts.values()).find(
574
+ (c) => c.name.toLowerCase() === concept.name.toLowerCase()
575
+ );
576
+ if (existing) {
577
+ const updated = {
578
+ ...existing,
579
+ ...concept,
580
+ id: existing.id,
581
+ createdAt: existing.createdAt,
582
+ updatedAt: Date.now()
583
+ };
584
+ this.concepts.set(existing.id, updated);
585
+ this.emit("conceptUpdated", updated);
586
+ return updated;
587
+ }
588
+ const newConcept = {
589
+ ...concept,
590
+ id: this.generateId("concept"),
591
+ createdAt: Date.now(),
592
+ updatedAt: Date.now()
593
+ };
594
+ this.concepts.set(newConcept.id, newConcept);
595
+ if (newConcept.category) {
596
+ if (!this.conceptIndex.has(newConcept.category)) {
597
+ this.conceptIndex.set(newConcept.category, /* @__PURE__ */ new Set());
598
+ }
599
+ this.conceptIndex.get(newConcept.category).add(newConcept.id);
600
+ }
601
+ this.emit("conceptAdded", newConcept);
602
+ return newConcept;
603
+ }
604
+ /**
605
+ * Create a relationship between concepts
606
+ */
607
+ addRelationship(sourceId, targetId, type, metadata) {
608
+ const source = this.concepts.get(sourceId);
609
+ const target = this.concepts.get(targetId);
610
+ if (!source || !target) {
611
+ return null;
612
+ }
613
+ const existing = Array.from(this.relationships.values()).find(
614
+ (r) => r.sourceId === sourceId && r.targetId === targetId && r.type === type
615
+ );
616
+ if (existing) {
617
+ existing.weight = Math.min(existing.weight + 0.1, 1);
618
+ return existing;
619
+ }
620
+ const relationship = {
621
+ id: this.generateId("rel"),
622
+ sourceId,
623
+ targetId,
624
+ type,
625
+ weight: metadata?.weight ?? 0.5,
626
+ metadata: metadata ?? {},
627
+ createdAt: Date.now()
628
+ };
629
+ this.relationships.set(relationship.id, relationship);
630
+ this.emit("relationshipAdded", relationship);
631
+ return relationship;
632
+ }
633
+ /**
634
+ * Get a concept by ID or name
635
+ */
636
+ getConcept(idOrName) {
637
+ const byId = this.concepts.get(idOrName);
638
+ if (byId) return byId;
639
+ return Array.from(this.concepts.values()).find(
640
+ (c) => c.name.toLowerCase() === idOrName.toLowerCase()
641
+ );
642
+ }
643
+ /**
644
+ * Get concepts by category
645
+ */
646
+ getConceptsByCategory(category) {
647
+ const ids = this.conceptIndex.get(category);
648
+ if (!ids) return [];
649
+ return Array.from(ids).map((id) => this.concepts.get(id)).filter((c) => c !== void 0);
650
+ }
651
+ /**
652
+ * Get relationships for a concept
653
+ */
654
+ getRelationships(conceptId, direction = "both") {
655
+ return Array.from(this.relationships.values()).filter((r) => {
656
+ if (direction === "outgoing") return r.sourceId === conceptId;
657
+ if (direction === "incoming") return r.targetId === conceptId;
658
+ return r.sourceId === conceptId || r.targetId === conceptId;
659
+ });
660
+ }
661
+ /**
662
+ * Get related concepts
663
+ */
664
+ getRelatedConcepts(conceptId, relationshipType, maxDepth = 1) {
665
+ const results = /* @__PURE__ */ new Map();
666
+ const visited = /* @__PURE__ */ new Set();
667
+ const queue = [{ id: conceptId, path: [], distance: 0 }];
668
+ while (queue.length > 0) {
669
+ const current = queue.shift();
670
+ if (visited.has(current.id) || current.distance > maxDepth) continue;
671
+ visited.add(current.id);
672
+ const relationships = this.getRelationships(current.id);
673
+ for (const rel of relationships) {
674
+ if (relationshipType && rel.type !== relationshipType) continue;
675
+ const relatedId = rel.sourceId === current.id ? rel.targetId : rel.sourceId;
676
+ if (relatedId === conceptId) continue;
677
+ const concept = this.concepts.get(relatedId);
678
+ if (!concept) continue;
679
+ const newPath = [...current.path, rel];
680
+ const existing = results.get(relatedId);
681
+ if (!existing || newPath.length < existing.path.length) {
682
+ results.set(relatedId, {
683
+ concept,
684
+ path: newPath,
685
+ distance: current.distance + 1
686
+ });
687
+ if (current.distance + 1 < maxDepth) {
688
+ queue.push({
689
+ id: relatedId,
690
+ path: newPath,
691
+ distance: current.distance + 1
692
+ });
693
+ }
694
+ }
695
+ }
696
+ }
697
+ return Array.from(results.values()).sort((a, b) => a.distance - b.distance);
698
+ }
699
+ /**
700
+ * Query facts
701
+ */
702
+ async queryFacts(query, limit = 10) {
703
+ const { entries } = await this.store.query({
704
+ query,
705
+ types: ["fact"],
706
+ limit
707
+ });
708
+ return entries;
709
+ }
710
+ /**
711
+ * Search facts by embedding
712
+ */
713
+ async searchFacts(embedding, options) {
714
+ const results = await this.store.search(embedding, {
715
+ topK: options?.topK ?? 10,
716
+ minScore: options?.minScore ?? this.config.minConfidence
717
+ });
718
+ return results.filter((r) => r.entry.type === "fact");
719
+ }
720
+ /**
721
+ * Find path between two concepts
722
+ */
723
+ findPath(sourceId, targetId, maxDepth = 5) {
724
+ const visited = /* @__PURE__ */ new Set();
725
+ const queue = [
726
+ { id: sourceId, path: [] }
727
+ ];
728
+ while (queue.length > 0) {
729
+ const current = queue.shift();
730
+ if (visited.has(current.id) || current.path.length >= maxDepth) continue;
731
+ visited.add(current.id);
732
+ const relationships = this.getRelationships(current.id);
733
+ for (const rel of relationships) {
734
+ const nextId = rel.sourceId === current.id ? rel.targetId : rel.sourceId;
735
+ const newPath = [...current.path, rel];
736
+ if (nextId === targetId) {
737
+ return newPath;
738
+ }
739
+ queue.push({ id: nextId, path: newPath });
740
+ }
741
+ }
742
+ return null;
743
+ }
744
+ /**
745
+ * Infer new relationships based on existing ones
746
+ */
747
+ inferRelationships() {
748
+ if (!this.config.enableInference) return [];
749
+ const inferred = [];
750
+ const transitiveTypes = ["is_a", "part_of", "related_to"];
751
+ for (const rel1 of this.relationships.values()) {
752
+ if (!transitiveTypes.includes(rel1.type)) continue;
753
+ for (const rel2 of this.relationships.values()) {
754
+ if (rel1.id === rel2.id) continue;
755
+ if (rel1.type !== rel2.type) continue;
756
+ if (rel1.targetId !== rel2.sourceId) continue;
757
+ const exists = Array.from(this.relationships.values()).some(
758
+ (r) => r.sourceId === rel1.sourceId && r.targetId === rel2.targetId && r.type === rel1.type
759
+ );
760
+ if (!exists) {
761
+ const newRel = this.addRelationship(
762
+ rel1.sourceId,
763
+ rel2.targetId,
764
+ rel1.type,
765
+ {
766
+ inferred: true,
767
+ weight: rel1.weight * rel2.weight * 0.8
768
+ }
769
+ );
770
+ if (newRel) {
771
+ inferred.push(newRel);
772
+ }
773
+ }
774
+ }
775
+ }
776
+ return inferred;
777
+ }
778
+ /**
779
+ * Get conflicting facts
780
+ */
781
+ async findConflicts(fact) {
782
+ const { entries } = await this.store.query({
783
+ query: fact.content,
784
+ types: ["fact"],
785
+ limit: 20
786
+ });
787
+ return entries.filter((e) => {
788
+ if (e.id === fact.id) return false;
789
+ const contentLower = e.content.toLowerCase();
790
+ const factLower = fact.content.toLowerCase();
791
+ const negationPatterns = ["not ", "isn't", "aren't", "never", "false"];
792
+ const hasNegation = negationPatterns.some(
793
+ (p) => contentLower.includes(p) && !factLower.includes(p) || !contentLower.includes(p) && factLower.includes(p)
794
+ );
795
+ return hasNegation;
796
+ });
797
+ }
798
+ /**
799
+ * Resolve conflict between facts
800
+ */
801
+ resolveConflict(fact1, fact2) {
802
+ switch (this.config.conflictResolution) {
803
+ case "newest":
804
+ return fact1.timestamp > fact2.timestamp ? fact1 : fact2;
805
+ case "highest-confidence": {
806
+ const conf1 = fact1.metadata.confidence ?? 0.5;
807
+ const conf2 = fact2.metadata.confidence ?? 0.5;
808
+ return conf1 > conf2 ? fact1 : fact2;
809
+ }
810
+ case "merge":
811
+ return {
812
+ ...fact1,
813
+ content: `${fact1.content} (Note: conflicting fact exists: ${fact2.content})`,
814
+ metadata: {
815
+ ...fact1.metadata,
816
+ hasConflict: true,
817
+ conflictingFactId: fact2.id
818
+ }
819
+ };
820
+ default:
821
+ return fact1;
822
+ }
823
+ }
824
+ /**
825
+ * Export knowledge graph
826
+ */
827
+ exportGraph() {
828
+ return {
829
+ concepts: Array.from(this.concepts.values()),
830
+ relationships: Array.from(this.relationships.values())
831
+ };
832
+ }
833
+ /**
834
+ * Import knowledge graph
835
+ */
836
+ importGraph(data) {
837
+ for (const concept of data.concepts) {
838
+ this.concepts.set(concept.id, concept);
839
+ if (concept.category) {
840
+ if (!this.conceptIndex.has(concept.category)) {
841
+ this.conceptIndex.set(concept.category, /* @__PURE__ */ new Set());
842
+ }
843
+ this.conceptIndex.get(concept.category).add(concept.id);
844
+ }
845
+ }
846
+ for (const relationship of data.relationships) {
847
+ this.relationships.set(relationship.id, relationship);
848
+ }
849
+ }
850
+ /**
851
+ * Generate unique ID
852
+ */
853
+ generateId(prefix) {
854
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
855
+ }
856
+ /**
857
+ * Get statistics
858
+ */
859
+ getStats() {
860
+ return {
861
+ conceptCount: this.concepts.size,
862
+ relationshipCount: this.relationships.size,
863
+ categoryCount: this.conceptIndex.size,
864
+ avgRelationshipsPerConcept: this.concepts.size > 0 ? this.relationships.size / this.concepts.size : 0
865
+ };
866
+ }
867
+ };
868
+ function createSemanticMemory(store, config) {
869
+ return new SemanticMemory(store, config);
870
+ }
871
+
872
+ // src/structures/LongTermMemory.ts
873
+ var import_eventemitter34 = require("eventemitter3");
874
+ var LongTermMemory = class extends import_eventemitter34.EventEmitter {
875
+ store;
876
+ config;
877
+ consolidatedMemories = /* @__PURE__ */ new Map();
878
+ constructor(store, config = {}) {
879
+ super();
880
+ this.store = store;
881
+ this.config = {
882
+ store: config.store ?? store,
883
+ indexing: config.indexing ?? "hybrid",
884
+ compression: config.compression ?? true,
885
+ compressionThreshold: config.compressionThreshold ?? 1e3,
886
+ consolidationThreshold: config.consolidationThreshold ?? 100,
887
+ compressionRatio: config.compressionRatio ?? 5,
888
+ minImportance: config.minImportance ?? 0.3,
889
+ retentionPeriod: config.retentionPeriod ?? 365 * 24 * 60 * 60 * 1e3,
890
+ // 1 year
891
+ autoConsolidate: config.autoConsolidate ?? true,
892
+ maxStorageSize: config.maxStorageSize ?? 1e5
893
+ };
894
+ }
895
+ /**
896
+ * Store a memory in long-term storage
897
+ */
898
+ async store_memory(entry) {
899
+ if (entry.importance < this.config.minImportance) {
900
+ return entry.id;
901
+ }
902
+ const entryWithExpiry = {
903
+ ...entry,
904
+ expiresAt: entry.expiresAt ?? Date.now() + this.config.retentionPeriod
905
+ };
906
+ await this.store.add(entryWithExpiry);
907
+ if (this.config.autoConsolidate) {
908
+ const count = await this.store.count();
909
+ if (count >= this.config.consolidationThreshold) {
910
+ await this.consolidateOldMemories();
911
+ }
912
+ }
913
+ return entry.id;
914
+ }
915
+ /**
916
+ * Retrieve memories by similarity
917
+ */
918
+ async retrieve(embedding, options) {
919
+ const results = await this.store.search(embedding, {
920
+ topK: options?.topK ?? 10,
921
+ minScore: options?.minScore ?? 0.5
922
+ });
923
+ this.emit(
924
+ "retrieved",
925
+ results.map((r) => r.entry)
926
+ );
927
+ return results;
928
+ }
929
+ /**
930
+ * Query long-term memories
931
+ */
932
+ async query(options) {
933
+ const { entries } = await this.store.query({
934
+ ...options,
935
+ types: options.types,
936
+ minImportance: options.minImportance ?? this.config.minImportance,
937
+ limit: options.limit ?? 100
938
+ });
939
+ return entries;
940
+ }
941
+ /**
942
+ * Consolidate old memories into summaries
943
+ */
944
+ async consolidateOldMemories(options) {
945
+ const olderThan = options?.olderThan ?? Date.now() - 7 * 24 * 60 * 60 * 1e3;
946
+ const { entries } = await this.store.query({
947
+ endTime: olderThan,
948
+ limit: 1e3
949
+ });
950
+ if (entries.length < this.config.compressionRatio) {
951
+ return [];
952
+ }
953
+ const groups = this.groupMemories(entries, options?.groupBy ?? "day");
954
+ const consolidated = [];
955
+ for (const [key, groupEntries] of groups) {
956
+ if (groupEntries.length < 2) continue;
957
+ const summary = options?.summarizeFn ? await options.summarizeFn(groupEntries) : this.generateSimpleSummary(groupEntries);
958
+ const consolidatedMemory = {
959
+ id: this.generateId(),
960
+ summary,
961
+ sourceIds: groupEntries.map((e) => e.id),
962
+ sourceCount: groupEntries.length,
963
+ avgImportance: groupEntries.reduce((sum, e) => sum + e.importance, 0) / groupEntries.length,
964
+ timeRange: {
965
+ start: Math.min(...groupEntries.map((e) => e.timestamp)),
966
+ end: Math.max(...groupEntries.map((e) => e.timestamp))
967
+ },
968
+ metadata: {
969
+ groupKey: key
970
+ },
971
+ createdAt: Date.now()
972
+ };
973
+ await this.store.add({
974
+ id: consolidatedMemory.id,
975
+ content: consolidatedMemory.summary,
976
+ type: "summary",
977
+ importance: consolidatedMemory.avgImportance,
978
+ metadata: {
979
+ source: "system",
980
+ confidence: 0.85,
981
+ consolidated: true,
982
+ sourceCount: consolidatedMemory.sourceCount,
983
+ ...consolidatedMemory.metadata
984
+ },
985
+ timestamp: consolidatedMemory.timeRange.start,
986
+ accessCount: 0,
987
+ createdAt: consolidatedMemory.createdAt,
988
+ updatedAt: consolidatedMemory.createdAt
989
+ });
990
+ this.consolidatedMemories.set(consolidatedMemory.id, consolidatedMemory);
991
+ for (const entry of groupEntries) {
992
+ await this.store.delete(entry.id);
993
+ }
994
+ consolidated.push(consolidatedMemory);
995
+ this.emit("consolidated", consolidatedMemory, groupEntries);
996
+ }
997
+ return consolidated;
998
+ }
999
+ /**
1000
+ * Prune low-importance memories
1001
+ */
1002
+ async prune(options) {
1003
+ const { entries } = await this.store.query({
1004
+ limit: 1e4
1005
+ });
1006
+ const now = Date.now();
1007
+ const maxAge = options?.maxAge ?? this.config.retentionPeriod;
1008
+ const maxImportance = options?.maxImportance ?? this.config.minImportance;
1009
+ const toPrune = entries.filter((entry) => {
1010
+ const age = now - entry.timestamp;
1011
+ const isOld = age > maxAge;
1012
+ const isLowImportance = entry.importance < maxImportance;
1013
+ const isLowAccess = entry.accessCount < 2;
1014
+ return isOld || isLowImportance && isLowAccess;
1015
+ });
1016
+ const limit = options?.maxCount ?? toPrune.length;
1017
+ const pruneList = toPrune.slice(0, limit);
1018
+ for (const entry of pruneList) {
1019
+ await this.store.delete(entry.id);
1020
+ }
1021
+ this.emit("pruned", pruneList);
1022
+ return pruneList.length;
1023
+ }
1024
+ /**
1025
+ * Reinforce a memory (increase importance)
1026
+ */
1027
+ async reinforce(id, amount = 0.1) {
1028
+ const entry = await this.store.get(id);
1029
+ if (!entry) return false;
1030
+ const newImportance = Math.min(entry.importance + amount, 1);
1031
+ return this.store.update(id, {
1032
+ importance: newImportance,
1033
+ expiresAt: Date.now() + this.config.retentionPeriod
1034
+ // Reset expiration
1035
+ });
1036
+ }
1037
+ /**
1038
+ * Get consolidated memory by ID
1039
+ */
1040
+ getConsolidated(id) {
1041
+ return this.consolidatedMemories.get(id);
1042
+ }
1043
+ /**
1044
+ * Get all consolidated memories
1045
+ */
1046
+ getAllConsolidated() {
1047
+ return Array.from(this.consolidatedMemories.values());
1048
+ }
1049
+ /**
1050
+ * Expand a consolidated memory to show original summaries
1051
+ */
1052
+ expandConsolidated(id) {
1053
+ const consolidated = this.consolidatedMemories.get(id);
1054
+ if (!consolidated) return null;
1055
+ return [
1056
+ {
1057
+ id: consolidated.id,
1058
+ content: consolidated.summary,
1059
+ type: "summary",
1060
+ importance: consolidated.avgImportance,
1061
+ metadata: {
1062
+ source: "system",
1063
+ confidence: 0.85,
1064
+ expanded: true,
1065
+ sourceCount: consolidated.sourceCount
1066
+ },
1067
+ timestamp: consolidated.timeRange.start,
1068
+ accessCount: 0,
1069
+ createdAt: consolidated.createdAt,
1070
+ updatedAt: consolidated.createdAt
1071
+ }
1072
+ ];
1073
+ }
1074
+ /**
1075
+ * Group memories by time or topic
1076
+ */
1077
+ groupMemories(entries, groupBy) {
1078
+ const groups = /* @__PURE__ */ new Map();
1079
+ for (const entry of entries) {
1080
+ let key;
1081
+ switch (groupBy) {
1082
+ case "day":
1083
+ key = new Date(entry.timestamp).toISOString().split("T")[0];
1084
+ break;
1085
+ case "week": {
1086
+ const date = new Date(entry.timestamp);
1087
+ const weekStart = new Date(date);
1088
+ weekStart.setDate(date.getDate() - date.getDay());
1089
+ key = weekStart.toISOString().split("T")[0];
1090
+ break;
1091
+ }
1092
+ case "topic":
1093
+ key = String(entry.metadata.topic ?? entry.type);
1094
+ break;
1095
+ default:
1096
+ key = "default";
1097
+ }
1098
+ if (!groups.has(key)) {
1099
+ groups.set(key, []);
1100
+ }
1101
+ groups.get(key).push(entry);
1102
+ }
1103
+ return groups;
1104
+ }
1105
+ /**
1106
+ * Generate simple summary from entries
1107
+ */
1108
+ generateSimpleSummary(entries) {
1109
+ const types = new Set(entries.map((e) => e.type));
1110
+ const snippets = entries.slice(0, 5).map((e) => e.content.slice(0, 50)).join("; ");
1111
+ return `Consolidated ${entries.length} memories (${Array.from(types).join(", ")}): ${snippets}...`;
1112
+ }
1113
+ /**
1114
+ * Generate unique ID
1115
+ */
1116
+ generateId() {
1117
+ return `ltm-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1118
+ }
1119
+ /**
1120
+ * Get statistics
1121
+ */
1122
+ async getStats() {
1123
+ const count = await this.store.count();
1124
+ const { entries } = await this.store.query({ limit: 1e3 });
1125
+ const avgImportance = entries.length > 0 ? entries.reduce((sum, e) => sum + e.importance, 0) / entries.length : 0;
1126
+ const timestamps = entries.map((e) => e.timestamp);
1127
+ return {
1128
+ totalMemories: count,
1129
+ consolidatedCount: this.consolidatedMemories.size,
1130
+ avgImportance,
1131
+ oldestMemory: timestamps.length > 0 ? Math.min(...timestamps) : null,
1132
+ newestMemory: timestamps.length > 0 ? Math.max(...timestamps) : null
1133
+ };
1134
+ }
1135
+ };
1136
+ function createLongTermMemory(store, config) {
1137
+ return new LongTermMemory(store, config);
1138
+ }
1139
+
1140
+ // src/structures/HierarchicalMemory.ts
1141
+ var import_eventemitter35 = require("eventemitter3");
1142
+ var HierarchicalMemory = class extends import_eventemitter35.EventEmitter {
1143
+ workingMemory;
1144
+ episodicMemory;
1145
+ semanticMemory;
1146
+ longTermMemory;
1147
+ config;
1148
+ embedFn;
1149
+ constructor(stores, config = {}) {
1150
+ super();
1151
+ this.config = {
1152
+ working: config.working ?? {},
1153
+ episodic: config.episodic ?? {},
1154
+ semantic: config.semantic ?? {},
1155
+ longTerm: config.longTerm ?? {},
1156
+ routing: config.routing ?? {},
1157
+ routingStrategy: config.routingStrategy ?? "auto",
1158
+ consolidationInterval: config.consolidationInterval ?? 60 * 60 * 1e3,
1159
+ // 1 hour
1160
+ workingMemorySize: config.workingMemorySize ?? 20,
1161
+ promotionThreshold: config.promotionThreshold ?? 0.7
1162
+ };
1163
+ this.workingMemory = new WorkingMemory(stores.working, {
1164
+ maxSize: this.config.workingMemorySize
1165
+ });
1166
+ this.episodicMemory = new EpisodicMemory(stores.episodic);
1167
+ this.semanticMemory = new SemanticMemory(stores.semantic);
1168
+ this.longTermMemory = new LongTermMemory(stores.longterm);
1169
+ this.setupEventHandlers();
1170
+ }
1171
+ /**
1172
+ * Set embedding function for semantic search
1173
+ */
1174
+ setEmbeddingFunction(fn) {
1175
+ this.embedFn = fn;
1176
+ }
1177
+ /**
1178
+ * Add a memory entry with automatic routing
1179
+ */
1180
+ async add(entry) {
1181
+ const layer = this.route(entry);
1182
+ switch (layer) {
1183
+ case "working":
1184
+ await this.workingMemory.add(entry);
1185
+ break;
1186
+ case "episodic":
1187
+ await this.episodicMemory.recordEvent(entry);
1188
+ break;
1189
+ case "semantic":
1190
+ await this.semanticMemory.learnFact(entry.content, entry.metadata);
1191
+ break;
1192
+ case "longterm":
1193
+ await this.longTermMemory.store_memory(entry);
1194
+ break;
1195
+ }
1196
+ this.emit("routed", entry, layer);
1197
+ return layer;
1198
+ }
1199
+ /**
1200
+ * Add to a specific layer
1201
+ */
1202
+ async addToLayer(entry, layer) {
1203
+ switch (layer) {
1204
+ case "working":
1205
+ await this.workingMemory.add(entry);
1206
+ break;
1207
+ case "episodic":
1208
+ await this.episodicMemory.recordEvent(entry);
1209
+ break;
1210
+ case "semantic":
1211
+ await this.semanticMemory.learnFact(entry.content, entry.metadata);
1212
+ break;
1213
+ case "longterm":
1214
+ await this.longTermMemory.store_memory(entry);
1215
+ break;
1216
+ }
1217
+ this.emit("routed", entry, layer);
1218
+ }
1219
+ /**
1220
+ * Search across all memory layers
1221
+ */
1222
+ async search(query, options) {
1223
+ const layers = options?.layers ?? [
1224
+ "working",
1225
+ "episodic",
1226
+ "semantic",
1227
+ "longterm"
1228
+ ];
1229
+ const topK = options?.topK ?? 10;
1230
+ const minScore = options?.minScore ?? 0.3;
1231
+ const results = [];
1232
+ let embedding;
1233
+ if (this.embedFn) {
1234
+ embedding = await this.embedFn(query);
1235
+ }
1236
+ for (const layer of layers) {
1237
+ const layerResults = await this.searchLayer(layer, query, embedding, {
1238
+ topK,
1239
+ minScore
1240
+ });
1241
+ results.push(...layerResults);
1242
+ }
1243
+ results.sort((a, b) => b.score - a.score);
1244
+ const seen = /* @__PURE__ */ new Set();
1245
+ const deduplicated = results.filter((r) => {
1246
+ if (seen.has(r.entry.id)) return false;
1247
+ seen.add(r.entry.id);
1248
+ return true;
1249
+ });
1250
+ this.emit(
1251
+ "retrieved",
1252
+ deduplicated.map((r) => r.entry),
1253
+ layers
1254
+ );
1255
+ return deduplicated.slice(0, topK);
1256
+ }
1257
+ /**
1258
+ * Search a specific layer
1259
+ */
1260
+ async searchLayer(layer, query, embedding, options) {
1261
+ const topK = options?.topK ?? 10;
1262
+ switch (layer) {
1263
+ case "working": {
1264
+ const context = this.workingMemory.getContextWithAttention();
1265
+ return context.filter(
1266
+ (c) => c.entry.content.toLowerCase().includes(query.toLowerCase())
1267
+ ).map((c) => ({
1268
+ entry: c.entry,
1269
+ score: c.total,
1270
+ layer: "working"
1271
+ })).slice(0, topK);
1272
+ }
1273
+ case "episodic": {
1274
+ const episodes = await this.episodicMemory.recall({ limit: topK * 2 });
1275
+ return episodes.filter((e) => e.content.toLowerCase().includes(query.toLowerCase())).map((entry) => ({
1276
+ entry,
1277
+ score: this.calculateTextScore(query, entry.content),
1278
+ layer: "episodic"
1279
+ })).slice(0, topK);
1280
+ }
1281
+ case "semantic": {
1282
+ const facts = await this.semanticMemory.queryFacts(query, topK);
1283
+ return facts.map((entry) => ({
1284
+ entry,
1285
+ score: this.calculateTextScore(query, entry.content),
1286
+ layer: "semantic"
1287
+ }));
1288
+ }
1289
+ case "longterm": {
1290
+ if (embedding) {
1291
+ const results = await this.longTermMemory.retrieve(embedding, {
1292
+ topK
1293
+ });
1294
+ return results.map((r) => ({
1295
+ entry: r.entry,
1296
+ score: r.score,
1297
+ layer: "longterm"
1298
+ }));
1299
+ }
1300
+ const entries = await this.longTermMemory.query({ query, limit: topK });
1301
+ return entries.map((entry) => ({
1302
+ entry,
1303
+ score: this.calculateTextScore(query, entry.content),
1304
+ layer: "longterm"
1305
+ }));
1306
+ }
1307
+ default:
1308
+ return [];
1309
+ }
1310
+ }
1311
+ /**
1312
+ * Get context from working memory
1313
+ */
1314
+ getWorkingContext() {
1315
+ return this.workingMemory.getContext();
1316
+ }
1317
+ /**
1318
+ * Promote a memory to a higher layer
1319
+ */
1320
+ async promote(entryId, from, to) {
1321
+ let entry = null;
1322
+ switch (from) {
1323
+ case "working":
1324
+ entry = this.workingMemory.getContext().find((e) => e.id === entryId) ?? null;
1325
+ break;
1326
+ case "episodic": {
1327
+ const events = await this.episodicMemory.recall({ limit: 1e3 });
1328
+ entry = events.find((e) => e.id === entryId) ?? null;
1329
+ break;
1330
+ }
1331
+ }
1332
+ if (!entry) return false;
1333
+ await this.addToLayer(entry, to);
1334
+ this.emit("promoted", entry, from, to);
1335
+ return true;
1336
+ }
1337
+ /**
1338
+ * Consolidate memories from one layer to another
1339
+ */
1340
+ async consolidate(from, to) {
1341
+ let count = 0;
1342
+ switch (from) {
1343
+ case "working":
1344
+ if (to === "episodic" || to === "longterm") {
1345
+ const context = this.workingMemory.getContext();
1346
+ const important = context.filter(
1347
+ (e) => e.importance >= this.config.promotionThreshold
1348
+ );
1349
+ for (const entry of important) {
1350
+ await this.addToLayer(entry, to);
1351
+ count++;
1352
+ }
1353
+ }
1354
+ break;
1355
+ case "episodic":
1356
+ if (to === "longterm") {
1357
+ const episodes = this.episodicMemory.getRecentEpisodes(10);
1358
+ for (const episode of episodes) {
1359
+ if (episode.summary) {
1360
+ await this.longTermMemory.store_memory({
1361
+ id: `consolidated-${episode.id}`,
1362
+ content: episode.summary,
1363
+ type: "summary",
1364
+ importance: 0.7,
1365
+ metadata: {
1366
+ source: "system",
1367
+ confidence: 0.85,
1368
+ sourceEpisodeId: episode.id,
1369
+ eventCount: episode.events.length
1370
+ },
1371
+ timestamp: episode.startTime,
1372
+ accessCount: 0,
1373
+ createdAt: Date.now(),
1374
+ updatedAt: Date.now()
1375
+ });
1376
+ count++;
1377
+ }
1378
+ }
1379
+ }
1380
+ break;
1381
+ }
1382
+ if (count > 0) {
1383
+ this.emit("consolidated", from, to, count);
1384
+ }
1385
+ return count;
1386
+ }
1387
+ /**
1388
+ * Route a memory entry to the appropriate layer
1389
+ */
1390
+ route(entry) {
1391
+ if (this.config.routingStrategy === "manual") {
1392
+ return entry.metadata.targetLayer ?? "working";
1393
+ }
1394
+ const decision = this.makeRoutingDecision(entry);
1395
+ return decision.layer;
1396
+ }
1397
+ /**
1398
+ * Make routing decision
1399
+ */
1400
+ makeRoutingDecision(entry) {
1401
+ if (entry.type === "event") {
1402
+ return {
1403
+ layer: "episodic",
1404
+ confidence: 0.9,
1405
+ reason: "Event type maps to episodic memory"
1406
+ };
1407
+ }
1408
+ if (entry.type === "fact") {
1409
+ return {
1410
+ layer: "semantic",
1411
+ confidence: 0.9,
1412
+ reason: "Fact type maps to semantic memory"
1413
+ };
1414
+ }
1415
+ if (entry.importance >= 0.8) {
1416
+ return {
1417
+ layer: "longterm",
1418
+ confidence: 0.8,
1419
+ reason: "High importance memory"
1420
+ };
1421
+ }
1422
+ if (entry.type === "context") {
1423
+ return {
1424
+ layer: "working",
1425
+ confidence: 0.9,
1426
+ reason: "Context type maps to working memory"
1427
+ };
1428
+ }
1429
+ if (entry.type === "summary") {
1430
+ return {
1431
+ layer: "longterm",
1432
+ confidence: 0.8,
1433
+ reason: "Summary type maps to long-term memory"
1434
+ };
1435
+ }
1436
+ return {
1437
+ layer: "working",
1438
+ confidence: 0.6,
1439
+ reason: "Default routing to working memory"
1440
+ };
1441
+ }
1442
+ /**
1443
+ * Calculate text similarity score
1444
+ */
1445
+ calculateTextScore(query, content) {
1446
+ const queryWords = query.toLowerCase().split(/\s+/);
1447
+ const contentLower = content.toLowerCase();
1448
+ let matches = 0;
1449
+ for (const word of queryWords) {
1450
+ if (word.length > 2 && contentLower.includes(word)) {
1451
+ matches++;
1452
+ }
1453
+ }
1454
+ return queryWords.length > 0 ? matches / queryWords.length : 0;
1455
+ }
1456
+ /**
1457
+ * Set up event handlers between layers
1458
+ */
1459
+ setupEventHandlers() {
1460
+ this.workingMemory.on("overflow", (evicted) => {
1461
+ void (async () => {
1462
+ for (const entry of evicted) {
1463
+ if (entry.importance >= this.config.promotionThreshold) {
1464
+ await this.longTermMemory.store_memory(entry);
1465
+ }
1466
+ }
1467
+ })();
1468
+ });
1469
+ this.episodicMemory.on("episodeEnd", (episode) => {
1470
+ void (async () => {
1471
+ if (episode.summary && episode.events.length >= 5) {
1472
+ await this.longTermMemory.store_memory({
1473
+ id: `episode-${episode.id}`,
1474
+ content: episode.summary,
1475
+ type: "summary",
1476
+ importance: 0.6,
1477
+ metadata: {
1478
+ source: "system",
1479
+ confidence: 0.8,
1480
+ episodeId: episode.id,
1481
+ eventCount: episode.events.length
1482
+ },
1483
+ timestamp: episode.startTime,
1484
+ accessCount: 0,
1485
+ createdAt: Date.now(),
1486
+ updatedAt: Date.now()
1487
+ });
1488
+ }
1489
+ })();
1490
+ });
1491
+ }
1492
+ /**
1493
+ * Get statistics for all layers
1494
+ */
1495
+ async getStats() {
1496
+ return {
1497
+ working: this.workingMemory.getSummary(),
1498
+ episodic: this.episodicMemory.getStats(),
1499
+ semantic: this.semanticMemory.getStats(),
1500
+ longterm: await this.longTermMemory.getStats()
1501
+ };
1502
+ }
1503
+ /**
1504
+ * Access individual memory layers
1505
+ */
1506
+ get layers() {
1507
+ return {
1508
+ working: this.workingMemory,
1509
+ episodic: this.episodicMemory,
1510
+ semantic: this.semanticMemory,
1511
+ longterm: this.longTermMemory
1512
+ };
1513
+ }
1514
+ };
1515
+ function createHierarchicalMemory(stores, config) {
1516
+ return new HierarchicalMemory(stores, config);
1517
+ }
1518
+ // Annotate the CommonJS export names for ESM import in node:
1519
+ 0 && (module.exports = {
1520
+ EpisodicMemory,
1521
+ HierarchicalMemory,
1522
+ LongTermMemory,
1523
+ SemanticMemory,
1524
+ WorkingMemory,
1525
+ createEpisodicMemory,
1526
+ createHierarchicalMemory,
1527
+ createLongTermMemory,
1528
+ createSemanticMemory,
1529
+ createWorkingMemory
1530
+ });