@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,1402 @@
1
+ // src/stores/implementations/InMemoryStore.ts
2
+ import { LRUCache } from "lru-cache";
3
+ var InMemoryStore = class {
4
+ cache;
5
+ constructor(config = {}) {
6
+ this.cache = new LRUCache({
7
+ max: config.maxSize ?? 1e4,
8
+ ttl: config.ttl,
9
+ updateAgeOnGet: true
10
+ });
11
+ }
12
+ /**
13
+ * Add a memory entry
14
+ */
15
+ async add(entry) {
16
+ this.cache.set(entry.id, entry);
17
+ return Promise.resolve(entry.id);
18
+ }
19
+ /**
20
+ * Get a memory entry by ID
21
+ */
22
+ async get(id) {
23
+ const entry = this.cache.get(id);
24
+ if (entry) {
25
+ entry.accessCount++;
26
+ entry.lastAccessedAt = Date.now();
27
+ this.cache.set(id, entry);
28
+ }
29
+ return Promise.resolve(entry ?? null);
30
+ }
31
+ /**
32
+ * Update a memory entry
33
+ */
34
+ async update(id, updates) {
35
+ const entry = this.cache.get(id);
36
+ if (!entry) {
37
+ return Promise.resolve(false);
38
+ }
39
+ const updated = {
40
+ ...entry,
41
+ ...updates,
42
+ metadata: {
43
+ ...entry.metadata,
44
+ ...updates.metadata
45
+ },
46
+ updatedAt: Date.now()
47
+ };
48
+ this.cache.set(id, updated);
49
+ return Promise.resolve(true);
50
+ }
51
+ /**
52
+ * Delete a memory entry
53
+ */
54
+ async delete(id) {
55
+ return Promise.resolve(this.cache.delete(id));
56
+ }
57
+ /**
58
+ * Query memory entries
59
+ */
60
+ async query(options) {
61
+ const entries = [];
62
+ const limit = options.limit ?? 100;
63
+ const offset = options.offset ?? 0;
64
+ for (const entry of this.cache.values()) {
65
+ if (this.matchesQuery(entry, options)) {
66
+ entries.push(entry);
67
+ }
68
+ }
69
+ entries.sort((a, b) => b.timestamp - a.timestamp);
70
+ const paginated = entries.slice(offset, offset + limit);
71
+ return Promise.resolve({
72
+ entries: paginated,
73
+ total: entries.length,
74
+ hasMore: offset + limit < entries.length
75
+ });
76
+ }
77
+ /**
78
+ * Search by vector similarity
79
+ */
80
+ async search(embedding, options) {
81
+ const results = [];
82
+ for (const entry of this.cache.values()) {
83
+ if (!entry.embedding) {
84
+ continue;
85
+ }
86
+ if (options.filter) {
87
+ if (!this.matchesFilter(entry, options.filter)) {
88
+ continue;
89
+ }
90
+ }
91
+ if (options.namespace && entry.metadata.namespace !== options.namespace) {
92
+ continue;
93
+ }
94
+ const score = this.cosineSimilarity(embedding, entry.embedding);
95
+ if (options.minScore !== void 0 && score < options.minScore) {
96
+ continue;
97
+ }
98
+ results.push({ entry, score });
99
+ }
100
+ results.sort((a, b) => b.score - a.score);
101
+ return Promise.resolve(results.slice(0, options.topK));
102
+ }
103
+ /**
104
+ * Clear entries
105
+ */
106
+ async clear(options) {
107
+ if (!options) {
108
+ const size = this.cache.size;
109
+ this.cache.clear();
110
+ return Promise.resolve(size);
111
+ }
112
+ let deleted = 0;
113
+ const toDelete = [];
114
+ for (const [id, entry] of this.cache.entries()) {
115
+ let shouldDelete = true;
116
+ if (options.namespace && entry.metadata.namespace !== options.namespace) {
117
+ shouldDelete = false;
118
+ }
119
+ if (options.userId && entry.metadata.userId !== options.userId) {
120
+ shouldDelete = false;
121
+ }
122
+ if (shouldDelete) {
123
+ toDelete.push(id);
124
+ }
125
+ }
126
+ for (const id of toDelete) {
127
+ this.cache.delete(id);
128
+ deleted++;
129
+ }
130
+ return Promise.resolve(deleted);
131
+ }
132
+ /**
133
+ * Count entries
134
+ */
135
+ async count(options) {
136
+ if (!options) {
137
+ return Promise.resolve(this.cache.size);
138
+ }
139
+ let count = 0;
140
+ for (const entry of this.cache.values()) {
141
+ if (this.matchesQuery(entry, options)) {
142
+ count++;
143
+ }
144
+ }
145
+ return Promise.resolve(count);
146
+ }
147
+ /**
148
+ * Close the store (no-op for in-memory)
149
+ */
150
+ async close() {
151
+ }
152
+ /**
153
+ * Check if entry matches query
154
+ */
155
+ matchesQuery(entry, options) {
156
+ if (options.query) {
157
+ const queryLower = options.query.toLowerCase();
158
+ if (!entry.content.toLowerCase().includes(queryLower)) {
159
+ return false;
160
+ }
161
+ }
162
+ if (options.userId && entry.metadata.userId !== options.userId) {
163
+ return false;
164
+ }
165
+ if (options.agentId && entry.metadata.agentId !== options.agentId) {
166
+ return false;
167
+ }
168
+ if (options.conversationId && entry.metadata.conversationId !== options.conversationId) {
169
+ return false;
170
+ }
171
+ if (options.sessionId && entry.metadata.sessionId !== options.sessionId) {
172
+ return false;
173
+ }
174
+ if (options.namespace && entry.metadata.namespace !== options.namespace) {
175
+ return false;
176
+ }
177
+ if (options.types && options.types.length > 0) {
178
+ if (!options.types.includes(entry.type)) {
179
+ return false;
180
+ }
181
+ }
182
+ if (options.tags && options.tags.length > 0) {
183
+ const entryTags = entry.metadata.tags ?? [];
184
+ const hasAllTags = options.tags.every((tag) => entryTags.includes(tag));
185
+ if (!hasAllTags) {
186
+ return false;
187
+ }
188
+ }
189
+ if (options.minImportance !== void 0 && entry.importance < options.minImportance) {
190
+ return false;
191
+ }
192
+ if (options.startTime !== void 0 && entry.timestamp < options.startTime) {
193
+ return false;
194
+ }
195
+ if (options.endTime !== void 0 && entry.timestamp > options.endTime) {
196
+ return false;
197
+ }
198
+ if (!options.includeExpired && entry.expiresAt) {
199
+ if (entry.expiresAt < Date.now()) {
200
+ return false;
201
+ }
202
+ }
203
+ return true;
204
+ }
205
+ /**
206
+ * Check if entry matches filter
207
+ */
208
+ matchesFilter(entry, filter) {
209
+ for (const [key, value] of Object.entries(filter)) {
210
+ if (value === void 0) continue;
211
+ if (key in entry.metadata) {
212
+ if (Array.isArray(value)) {
213
+ if (!value.includes(entry.metadata[key])) {
214
+ return false;
215
+ }
216
+ } else if (entry.metadata[key] !== value) {
217
+ return false;
218
+ }
219
+ } else if (key in entry) {
220
+ const entryValue = entry[key];
221
+ if (Array.isArray(value)) {
222
+ if (!value.includes(entryValue)) {
223
+ return false;
224
+ }
225
+ } else if (entryValue !== value) {
226
+ return false;
227
+ }
228
+ }
229
+ }
230
+ return true;
231
+ }
232
+ /**
233
+ * Calculate cosine similarity
234
+ */
235
+ cosineSimilarity(a, b) {
236
+ if (a.length !== b.length) {
237
+ throw new Error("Vectors must have the same length");
238
+ }
239
+ let dotProduct = 0;
240
+ let normA = 0;
241
+ let normB = 0;
242
+ for (let i = 0; i < a.length; i++) {
243
+ dotProduct += a[i] * b[i];
244
+ normA += a[i] * a[i];
245
+ normB += b[i] * b[i];
246
+ }
247
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
248
+ if (magnitude === 0) {
249
+ return 0;
250
+ }
251
+ return dotProduct / magnitude;
252
+ }
253
+ /**
254
+ * Get all entries (for debugging)
255
+ */
256
+ getAllEntries() {
257
+ return Array.from(this.cache.values());
258
+ }
259
+ /**
260
+ * Get cache size
261
+ */
262
+ get size() {
263
+ return this.cache.size;
264
+ }
265
+ };
266
+ function createInMemoryStore(config) {
267
+ return new InMemoryStore(config);
268
+ }
269
+
270
+ // src/stores/implementations/SQLiteStore.ts
271
+ var SQLiteStore = class {
272
+ db = null;
273
+ config;
274
+ tableName;
275
+ constructor(config) {
276
+ this.config = config;
277
+ this.tableName = config.tableName ?? "memories";
278
+ }
279
+ /**
280
+ * Initialize the database
281
+ */
282
+ async initialize() {
283
+ const BetterSqlite3 = (await import("better-sqlite3")).default;
284
+ this.db = new BetterSqlite3(this.config.path);
285
+ if (this.config.enableWAL !== false) {
286
+ this.db.pragma("journal_mode = WAL");
287
+ }
288
+ this.db.exec(`
289
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
290
+ id TEXT PRIMARY KEY,
291
+ content TEXT NOT NULL,
292
+ embedding BLOB,
293
+ type TEXT NOT NULL,
294
+ importance REAL NOT NULL,
295
+ metadata TEXT NOT NULL,
296
+ timestamp INTEGER NOT NULL,
297
+ expires_at INTEGER,
298
+ parent_id TEXT,
299
+ access_count INTEGER DEFAULT 0,
300
+ last_accessed_at INTEGER,
301
+ created_at INTEGER NOT NULL,
302
+ updated_at INTEGER NOT NULL
303
+ )
304
+ `);
305
+ this.db.exec(`
306
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_timestamp ON ${this.tableName}(timestamp);
307
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_type ON ${this.tableName}(type);
308
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_importance ON ${this.tableName}(importance);
309
+ `);
310
+ }
311
+ /**
312
+ * Ensure database is initialized
313
+ */
314
+ async ensureInitialized() {
315
+ if (!this.db) {
316
+ await this.initialize();
317
+ }
318
+ return this.db;
319
+ }
320
+ /**
321
+ * Add a memory entry
322
+ */
323
+ async add(entry) {
324
+ const db = await this.ensureInitialized();
325
+ const stmt = db.prepare(`
326
+ INSERT INTO ${this.tableName} (
327
+ id, content, embedding, type, importance, metadata,
328
+ timestamp, expires_at, parent_id, access_count,
329
+ last_accessed_at, created_at, updated_at
330
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
331
+ `);
332
+ stmt.run(
333
+ entry.id,
334
+ entry.content,
335
+ entry.embedding ? this.serializeVector(entry.embedding) : null,
336
+ entry.type,
337
+ entry.importance,
338
+ JSON.stringify(entry.metadata),
339
+ entry.timestamp,
340
+ entry.expiresAt ?? null,
341
+ entry.parentId ?? null,
342
+ entry.accessCount,
343
+ entry.lastAccessedAt ?? null,
344
+ entry.createdAt,
345
+ entry.updatedAt
346
+ );
347
+ return entry.id;
348
+ }
349
+ /**
350
+ * Get a memory entry by ID
351
+ */
352
+ async get(id) {
353
+ const db = await this.ensureInitialized();
354
+ const stmt = db.prepare(`SELECT * FROM ${this.tableName} WHERE id = ?`);
355
+ const row = stmt.get(id);
356
+ if (!row) {
357
+ return null;
358
+ }
359
+ const updateStmt = db.prepare(`
360
+ UPDATE ${this.tableName}
361
+ SET access_count = access_count + 1, last_accessed_at = ?
362
+ WHERE id = ?
363
+ `);
364
+ updateStmt.run(Date.now(), id);
365
+ return Promise.resolve(this.rowToEntry(row));
366
+ }
367
+ /**
368
+ * Update a memory entry
369
+ */
370
+ async update(id, updates) {
371
+ const db = await this.ensureInitialized();
372
+ const existing = await this.get(id);
373
+ if (!existing) {
374
+ return false;
375
+ }
376
+ const updated = {
377
+ ...existing,
378
+ ...updates,
379
+ metadata: {
380
+ ...existing.metadata,
381
+ ...updates.metadata
382
+ },
383
+ updatedAt: Date.now()
384
+ };
385
+ const stmt = db.prepare(`
386
+ UPDATE ${this.tableName}
387
+ SET content = ?, embedding = ?, type = ?, importance = ?,
388
+ metadata = ?, expires_at = ?, updated_at = ?
389
+ WHERE id = ?
390
+ `);
391
+ const result = stmt.run(
392
+ updated.content,
393
+ updated.embedding ? this.serializeVector(updated.embedding) : null,
394
+ updated.type,
395
+ updated.importance,
396
+ JSON.stringify(updated.metadata),
397
+ updated.expiresAt ?? null,
398
+ updated.updatedAt,
399
+ id
400
+ );
401
+ return result.changes > 0;
402
+ }
403
+ /**
404
+ * Delete a memory entry
405
+ */
406
+ async delete(id) {
407
+ const db = await this.ensureInitialized();
408
+ const stmt = db.prepare(`DELETE FROM ${this.tableName} WHERE id = ?`);
409
+ const result = stmt.run(id);
410
+ return result.changes > 0;
411
+ }
412
+ /**
413
+ * Query memory entries
414
+ */
415
+ async query(options) {
416
+ const db = await this.ensureInitialized();
417
+ const conditions = [];
418
+ const params = [];
419
+ if (options.query) {
420
+ conditions.push("content LIKE ?");
421
+ params.push(`%${options.query}%`);
422
+ }
423
+ if (options.userId) {
424
+ conditions.push("json_extract(metadata, '$.userId') = ?");
425
+ params.push(options.userId);
426
+ }
427
+ if (options.agentId) {
428
+ conditions.push("json_extract(metadata, '$.agentId') = ?");
429
+ params.push(options.agentId);
430
+ }
431
+ if (options.conversationId) {
432
+ conditions.push("json_extract(metadata, '$.conversationId') = ?");
433
+ params.push(options.conversationId);
434
+ }
435
+ if (options.namespace) {
436
+ conditions.push("json_extract(metadata, '$.namespace') = ?");
437
+ params.push(options.namespace);
438
+ }
439
+ if (options.types && options.types.length > 0) {
440
+ const placeholders = options.types.map(() => "?").join(", ");
441
+ conditions.push(`type IN (${placeholders})`);
442
+ params.push(...options.types);
443
+ }
444
+ if (options.minImportance !== void 0) {
445
+ conditions.push("importance >= ?");
446
+ params.push(options.minImportance);
447
+ }
448
+ if (options.startTime !== void 0) {
449
+ conditions.push("timestamp >= ?");
450
+ params.push(options.startTime);
451
+ }
452
+ if (options.endTime !== void 0) {
453
+ conditions.push("timestamp <= ?");
454
+ params.push(options.endTime);
455
+ }
456
+ if (!options.includeExpired) {
457
+ conditions.push("(expires_at IS NULL OR expires_at > ?)");
458
+ params.push(Date.now());
459
+ }
460
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
461
+ const countStmt = db.prepare(
462
+ `SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`
463
+ );
464
+ const countResult = countStmt.get(...params);
465
+ const total = countResult.count;
466
+ const limit = options.limit ?? 100;
467
+ const offset = options.offset ?? 0;
468
+ const queryStmt = db.prepare(`
469
+ SELECT * FROM ${this.tableName}
470
+ ${whereClause}
471
+ ORDER BY timestamp DESC
472
+ LIMIT ? OFFSET ?
473
+ `);
474
+ const rows = queryStmt.all(...params, limit, offset);
475
+ const entries = rows.map((row) => this.rowToEntry(row));
476
+ return {
477
+ entries,
478
+ total,
479
+ hasMore: offset + limit < total
480
+ };
481
+ }
482
+ /**
483
+ * Search by vector similarity
484
+ */
485
+ async search(embedding, options) {
486
+ const db = await this.ensureInitialized();
487
+ const conditions = ["embedding IS NOT NULL"];
488
+ const params = [];
489
+ if (options.namespace) {
490
+ conditions.push("json_extract(metadata, '$.namespace') = ?");
491
+ params.push(options.namespace);
492
+ }
493
+ if (options.filter) {
494
+ for (const [key, value] of Object.entries(options.filter)) {
495
+ if (value !== void 0) {
496
+ if (Array.isArray(value)) {
497
+ const placeholders = value.map(() => "?").join(", ");
498
+ conditions.push(
499
+ `json_extract(metadata, '$.${key}') IN (${placeholders})`
500
+ );
501
+ params.push(...value);
502
+ } else {
503
+ conditions.push(`json_extract(metadata, '$.${key}') = ?`);
504
+ params.push(value);
505
+ }
506
+ }
507
+ }
508
+ }
509
+ const whereClause = `WHERE ${conditions.join(" AND ")}`;
510
+ const stmt = db.prepare(`SELECT * FROM ${this.tableName} ${whereClause}`);
511
+ const rows = stmt.all(...params);
512
+ const results = [];
513
+ for (const row of rows) {
514
+ const entry = this.rowToEntry(row);
515
+ if (!entry.embedding) continue;
516
+ const score = this.cosineSimilarity(embedding, entry.embedding);
517
+ if (options.minScore === void 0 || score >= options.minScore) {
518
+ results.push({ entry, score });
519
+ }
520
+ }
521
+ results.sort((a, b) => b.score - a.score);
522
+ return Promise.resolve(results.slice(0, options.topK));
523
+ }
524
+ /**
525
+ * Clear entries
526
+ */
527
+ async clear(options) {
528
+ const db = await this.ensureInitialized();
529
+ if (!options) {
530
+ const stmt2 = db.prepare(`DELETE FROM ${this.tableName}`);
531
+ const result2 = stmt2.run();
532
+ return result2.changes;
533
+ }
534
+ const conditions = [];
535
+ const params = [];
536
+ if (options.namespace) {
537
+ conditions.push("json_extract(metadata, '$.namespace') = ?");
538
+ params.push(options.namespace);
539
+ }
540
+ if (options.userId) {
541
+ conditions.push("json_extract(metadata, '$.userId') = ?");
542
+ params.push(options.userId);
543
+ }
544
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
545
+ const stmt = db.prepare(`DELETE FROM ${this.tableName} ${whereClause}`);
546
+ const result = stmt.run(...params);
547
+ return result.changes;
548
+ }
549
+ /**
550
+ * Count entries
551
+ */
552
+ async count(options) {
553
+ const db = await this.ensureInitialized();
554
+ if (!options) {
555
+ const stmt = db.prepare(
556
+ `SELECT COUNT(*) as count FROM ${this.tableName}`
557
+ );
558
+ const result = stmt.get();
559
+ return result.count;
560
+ }
561
+ const { total } = await this.query({ ...options, limit: 0 });
562
+ return total;
563
+ }
564
+ /**
565
+ * Close the database
566
+ */
567
+ async close() {
568
+ if (this.db) {
569
+ this.db.close();
570
+ this.db = null;
571
+ }
572
+ return Promise.resolve();
573
+ }
574
+ /**
575
+ * Convert database row to MemoryEntry
576
+ */
577
+ rowToEntry(row) {
578
+ return {
579
+ id: row.id,
580
+ content: row.content,
581
+ embedding: row.embedding ? this.deserializeVector(row.embedding) : void 0,
582
+ type: row.type,
583
+ importance: row.importance,
584
+ metadata: JSON.parse(row.metadata),
585
+ timestamp: row.timestamp,
586
+ expiresAt: row.expires_at,
587
+ parentId: row.parent_id,
588
+ accessCount: row.access_count,
589
+ lastAccessedAt: row.last_accessed_at,
590
+ createdAt: row.created_at,
591
+ updatedAt: row.updated_at
592
+ };
593
+ }
594
+ /**
595
+ * Serialize vector to buffer
596
+ */
597
+ serializeVector(vector) {
598
+ const buffer = Buffer.alloc(vector.length * 4);
599
+ for (let i = 0; i < vector.length; i++) {
600
+ buffer.writeFloatLE(vector[i], i * 4);
601
+ }
602
+ return buffer;
603
+ }
604
+ /**
605
+ * Deserialize vector from buffer
606
+ */
607
+ deserializeVector(buffer) {
608
+ const vector = [];
609
+ for (let i = 0; i < buffer.length / 4; i++) {
610
+ vector.push(buffer.readFloatLE(i * 4));
611
+ }
612
+ return vector;
613
+ }
614
+ /**
615
+ * Calculate cosine similarity
616
+ */
617
+ cosineSimilarity(a, b) {
618
+ if (a.length !== b.length) {
619
+ throw new Error("Vectors must have the same length");
620
+ }
621
+ let dotProduct = 0;
622
+ let normA = 0;
623
+ let normB = 0;
624
+ for (let i = 0; i < a.length; i++) {
625
+ dotProduct += a[i] * b[i];
626
+ normA += a[i] * a[i];
627
+ normB += b[i] * b[i];
628
+ }
629
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
630
+ if (magnitude === 0) {
631
+ return 0;
632
+ }
633
+ return dotProduct / magnitude;
634
+ }
635
+ };
636
+ async function createSQLiteStore(config) {
637
+ const store = new SQLiteStore(config);
638
+ await store.initialize();
639
+ return store;
640
+ }
641
+
642
+ // src/stores/implementations/PostgresStore.ts
643
+ var PostgresStore = class {
644
+ pool = null;
645
+ config;
646
+ tableName;
647
+ vectorDimensions;
648
+ initialized = false;
649
+ constructor(config) {
650
+ this.config = config;
651
+ this.tableName = config.tableName ?? "memories";
652
+ this.vectorDimensions = config.vectorDimensions ?? 1536;
653
+ }
654
+ /**
655
+ * Initialize the database
656
+ */
657
+ async initialize() {
658
+ if (this.initialized) return;
659
+ const { Pool } = await import("pg");
660
+ if (this.config.connectionString) {
661
+ this.pool = new Pool({
662
+ connectionString: this.config.connectionString,
663
+ max: this.config.poolSize ?? 10,
664
+ ssl: this.config.ssl
665
+ });
666
+ } else {
667
+ this.pool = new Pool({
668
+ host: this.config.host ?? "localhost",
669
+ port: this.config.port ?? 5432,
670
+ database: this.config.database ?? "agentsea",
671
+ user: this.config.user ?? "postgres",
672
+ password: this.config.password,
673
+ max: this.config.poolSize ?? 10,
674
+ ssl: this.config.ssl
675
+ });
676
+ }
677
+ await this.pool.query("CREATE EXTENSION IF NOT EXISTS vector");
678
+ await this.pool.query(`
679
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
680
+ id TEXT PRIMARY KEY,
681
+ content TEXT NOT NULL,
682
+ embedding vector(${this.vectorDimensions}),
683
+ type TEXT NOT NULL,
684
+ importance REAL NOT NULL,
685
+ metadata JSONB NOT NULL,
686
+ timestamp BIGINT NOT NULL,
687
+ expires_at BIGINT,
688
+ parent_id TEXT,
689
+ access_count INTEGER DEFAULT 0,
690
+ last_accessed_at BIGINT,
691
+ created_at BIGINT NOT NULL,
692
+ updated_at BIGINT NOT NULL
693
+ )
694
+ `);
695
+ await this.pool.query(`
696
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_timestamp ON ${this.tableName}(timestamp);
697
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_type ON ${this.tableName}(type);
698
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_importance ON ${this.tableName}(importance);
699
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_metadata ON ${this.tableName} USING GIN(metadata);
700
+ `);
701
+ await this.pool.query(
702
+ `
703
+ CREATE INDEX IF NOT EXISTS idx_${this.tableName}_embedding
704
+ ON ${this.tableName} USING ivfflat (embedding vector_cosine_ops)
705
+ WITH (lists = 100)
706
+ `
707
+ ).catch(() => {
708
+ });
709
+ this.initialized = true;
710
+ }
711
+ /**
712
+ * Ensure database is initialized
713
+ */
714
+ async ensureInitialized() {
715
+ if (!this.initialized) {
716
+ await this.initialize();
717
+ }
718
+ return this.pool;
719
+ }
720
+ /**
721
+ * Add a memory entry
722
+ */
723
+ async add(entry) {
724
+ const pool = await this.ensureInitialized();
725
+ const embeddingValue = entry.embedding ? `[${entry.embedding.join(",")}]` : null;
726
+ await pool.query(
727
+ `INSERT INTO ${this.tableName} (
728
+ id, content, embedding, type, importance, metadata,
729
+ timestamp, expires_at, parent_id, access_count,
730
+ last_accessed_at, created_at, updated_at
731
+ ) VALUES ($1, $2, $3::vector, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,
732
+ [
733
+ entry.id,
734
+ entry.content,
735
+ embeddingValue,
736
+ entry.type,
737
+ entry.importance,
738
+ JSON.stringify(entry.metadata),
739
+ entry.timestamp,
740
+ entry.expiresAt ?? null,
741
+ entry.parentId ?? null,
742
+ entry.accessCount,
743
+ entry.lastAccessedAt ?? null,
744
+ entry.createdAt,
745
+ entry.updatedAt
746
+ ]
747
+ );
748
+ return entry.id;
749
+ }
750
+ /**
751
+ * Get a memory entry by ID
752
+ */
753
+ async get(id) {
754
+ const pool = await this.ensureInitialized();
755
+ const result = await pool.query(
756
+ `SELECT * FROM ${this.tableName} WHERE id = $1`,
757
+ [id]
758
+ );
759
+ if (result.rows.length === 0) {
760
+ return null;
761
+ }
762
+ await pool.query(
763
+ `UPDATE ${this.tableName}
764
+ SET access_count = access_count + 1, last_accessed_at = $1
765
+ WHERE id = $2`,
766
+ [Date.now(), id]
767
+ );
768
+ return Promise.resolve(this.rowToEntry(result.rows[0]));
769
+ }
770
+ /**
771
+ * Update a memory entry
772
+ */
773
+ async update(id, updates) {
774
+ const pool = await this.ensureInitialized();
775
+ const existing = await this.get(id);
776
+ if (!existing) {
777
+ return false;
778
+ }
779
+ const updated = {
780
+ ...existing,
781
+ ...updates,
782
+ metadata: {
783
+ ...existing.metadata,
784
+ ...updates.metadata
785
+ },
786
+ updatedAt: Date.now()
787
+ };
788
+ const embeddingValue = updated.embedding ? `[${updated.embedding.join(",")}]` : null;
789
+ const result = await pool.query(
790
+ `UPDATE ${this.tableName}
791
+ SET content = $1, embedding = $2::vector, type = $3, importance = $4,
792
+ metadata = $5, expires_at = $6, updated_at = $7
793
+ WHERE id = $8`,
794
+ [
795
+ updated.content,
796
+ embeddingValue,
797
+ updated.type,
798
+ updated.importance,
799
+ JSON.stringify(updated.metadata),
800
+ updated.expiresAt ?? null,
801
+ updated.updatedAt,
802
+ id
803
+ ]
804
+ );
805
+ return result.rowCount !== null && result.rowCount > 0;
806
+ }
807
+ /**
808
+ * Delete a memory entry
809
+ */
810
+ async delete(id) {
811
+ const pool = await this.ensureInitialized();
812
+ const result = await pool.query(
813
+ `DELETE FROM ${this.tableName} WHERE id = $1`,
814
+ [id]
815
+ );
816
+ return result.rowCount !== null && result.rowCount > 0;
817
+ }
818
+ /**
819
+ * Query memory entries
820
+ */
821
+ async query(options) {
822
+ const pool = await this.ensureInitialized();
823
+ const conditions = [];
824
+ const params = [];
825
+ let paramIndex = 1;
826
+ if (options.query) {
827
+ conditions.push(`content ILIKE $${paramIndex}`);
828
+ params.push(`%${options.query}%`);
829
+ paramIndex++;
830
+ }
831
+ if (options.userId) {
832
+ conditions.push(`metadata->>'userId' = $${paramIndex}`);
833
+ params.push(options.userId);
834
+ paramIndex++;
835
+ }
836
+ if (options.agentId) {
837
+ conditions.push(`metadata->>'agentId' = $${paramIndex}`);
838
+ params.push(options.agentId);
839
+ paramIndex++;
840
+ }
841
+ if (options.conversationId) {
842
+ conditions.push(`metadata->>'conversationId' = $${paramIndex}`);
843
+ params.push(options.conversationId);
844
+ paramIndex++;
845
+ }
846
+ if (options.namespace) {
847
+ conditions.push(`metadata->>'namespace' = $${paramIndex}`);
848
+ params.push(options.namespace);
849
+ paramIndex++;
850
+ }
851
+ if (options.types && options.types.length > 0) {
852
+ conditions.push(`type = ANY($${paramIndex})`);
853
+ params.push(options.types);
854
+ paramIndex++;
855
+ }
856
+ if (options.minImportance !== void 0) {
857
+ conditions.push(`importance >= $${paramIndex}`);
858
+ params.push(options.minImportance);
859
+ paramIndex++;
860
+ }
861
+ if (options.startTime !== void 0) {
862
+ conditions.push(`timestamp >= $${paramIndex}`);
863
+ params.push(options.startTime);
864
+ paramIndex++;
865
+ }
866
+ if (options.endTime !== void 0) {
867
+ conditions.push(`timestamp <= $${paramIndex}`);
868
+ params.push(options.endTime);
869
+ paramIndex++;
870
+ }
871
+ if (!options.includeExpired) {
872
+ conditions.push(`(expires_at IS NULL OR expires_at > $${paramIndex})`);
873
+ params.push(Date.now());
874
+ paramIndex++;
875
+ }
876
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
877
+ const countResult = await pool.query(
878
+ `SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`,
879
+ params
880
+ );
881
+ const total = parseInt(countResult.rows[0].count, 10);
882
+ const limit = options.limit ?? 100;
883
+ const offset = options.offset ?? 0;
884
+ const queryResult = await pool.query(
885
+ `SELECT * FROM ${this.tableName}
886
+ ${whereClause}
887
+ ORDER BY timestamp DESC
888
+ LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
889
+ [...params, limit, offset]
890
+ );
891
+ const entries = queryResult.rows.map((row) => this.rowToEntry(row));
892
+ return {
893
+ entries,
894
+ total,
895
+ hasMore: offset + limit < total
896
+ };
897
+ }
898
+ /**
899
+ * Search by vector similarity using pgvector
900
+ */
901
+ async search(embedding, options) {
902
+ const pool = await this.ensureInitialized();
903
+ const conditions = ["embedding IS NOT NULL"];
904
+ const params = [`[${embedding.join(",")}]`];
905
+ let paramIndex = 2;
906
+ if (options.namespace) {
907
+ conditions.push(`metadata->>'namespace' = $${paramIndex}`);
908
+ params.push(options.namespace);
909
+ paramIndex++;
910
+ }
911
+ if (options.filter) {
912
+ for (const [key, value] of Object.entries(options.filter)) {
913
+ if (value !== void 0) {
914
+ if (Array.isArray(value)) {
915
+ conditions.push(`metadata->>'${key}' = ANY($${paramIndex})`);
916
+ params.push(value);
917
+ } else {
918
+ conditions.push(`metadata->>'${key}' = $${paramIndex}`);
919
+ params.push(value);
920
+ }
921
+ paramIndex++;
922
+ }
923
+ }
924
+ }
925
+ const whereClause = `WHERE ${conditions.join(" AND ")}`;
926
+ const minScore = options.minScore ?? 0;
927
+ const result = await pool.query(
928
+ `SELECT *, 1 - (embedding <=> $1::vector) as score
929
+ FROM ${this.tableName}
930
+ ${whereClause}
931
+ AND 1 - (embedding <=> $1::vector) >= $${paramIndex}
932
+ ORDER BY embedding <=> $1::vector
933
+ LIMIT $${paramIndex + 1}`,
934
+ [...params, minScore, options.topK]
935
+ );
936
+ return result.rows.map((row) => ({
937
+ entry: this.rowToEntry(row),
938
+ score: parseFloat(row.score)
939
+ }));
940
+ }
941
+ /**
942
+ * Clear entries
943
+ */
944
+ async clear(options) {
945
+ const pool = await this.ensureInitialized();
946
+ if (!options) {
947
+ const result2 = await pool.query(`DELETE FROM ${this.tableName}`);
948
+ return result2.rowCount ?? 0;
949
+ }
950
+ const conditions = [];
951
+ const params = [];
952
+ let paramIndex = 1;
953
+ if (options.namespace) {
954
+ conditions.push(`metadata->>'namespace' = $${paramIndex}`);
955
+ params.push(options.namespace);
956
+ paramIndex++;
957
+ }
958
+ if (options.userId) {
959
+ conditions.push(`metadata->>'userId' = $${paramIndex}`);
960
+ params.push(options.userId);
961
+ }
962
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
963
+ const result = await pool.query(
964
+ `DELETE FROM ${this.tableName} ${whereClause}`,
965
+ params
966
+ );
967
+ return result.rowCount ?? 0;
968
+ }
969
+ /**
970
+ * Count entries
971
+ */
972
+ async count(options) {
973
+ const pool = await this.ensureInitialized();
974
+ if (!options) {
975
+ const result = await pool.query(
976
+ `SELECT COUNT(*) as count FROM ${this.tableName}`
977
+ );
978
+ return Promise.resolve(parseInt(result.rows[0].count, 10));
979
+ }
980
+ const { total } = await this.query({ ...options, limit: 0 });
981
+ return total;
982
+ }
983
+ /**
984
+ * Close the connection pool
985
+ */
986
+ async close() {
987
+ if (this.pool) {
988
+ await this.pool.end();
989
+ this.pool = null;
990
+ this.initialized = false;
991
+ }
992
+ }
993
+ /**
994
+ * Convert database row to MemoryEntry
995
+ */
996
+ rowToEntry(row) {
997
+ const embedding = row.embedding;
998
+ let embeddingArray;
999
+ if (embedding) {
1000
+ const cleaned = embedding.replace(/[[\]]/g, "");
1001
+ embeddingArray = cleaned.split(",").map((v) => parseFloat(v));
1002
+ }
1003
+ return {
1004
+ id: row.id,
1005
+ content: row.content,
1006
+ embedding: embeddingArray,
1007
+ type: row.type,
1008
+ importance: parseFloat(row.importance),
1009
+ metadata: row.metadata,
1010
+ timestamp: parseInt(row.timestamp, 10),
1011
+ expiresAt: row.expires_at ? parseInt(row.expires_at, 10) : void 0,
1012
+ parentId: row.parent_id,
1013
+ accessCount: parseInt(row.access_count, 10),
1014
+ lastAccessedAt: row.last_accessed_at ? parseInt(row.last_accessed_at, 10) : void 0,
1015
+ createdAt: parseInt(row.created_at, 10),
1016
+ updatedAt: parseInt(row.updated_at, 10)
1017
+ };
1018
+ }
1019
+ };
1020
+ async function createPostgresStore(config) {
1021
+ const store = new PostgresStore(config);
1022
+ await store.initialize();
1023
+ return store;
1024
+ }
1025
+
1026
+ // src/stores/implementations/RedisStore.ts
1027
+ var RedisStore = class {
1028
+ redis = null;
1029
+ config;
1030
+ keyPrefix;
1031
+ ttl;
1032
+ initialized = false;
1033
+ constructor(config) {
1034
+ this.config = config;
1035
+ this.keyPrefix = config.keyPrefix ?? "memory:";
1036
+ this.ttl = config.ttl;
1037
+ }
1038
+ /**
1039
+ * Initialize Redis connection
1040
+ */
1041
+ async initialize() {
1042
+ if (this.initialized) return;
1043
+ const Redis = (await import("ioredis")).default;
1044
+ if (this.config.url) {
1045
+ this.redis = new Redis(this.config.url);
1046
+ } else {
1047
+ this.redis = new Redis({
1048
+ host: this.config.host ?? "localhost",
1049
+ port: this.config.port ?? 6379,
1050
+ password: this.config.password,
1051
+ db: this.config.db ?? 0
1052
+ });
1053
+ }
1054
+ this.initialized = true;
1055
+ }
1056
+ /**
1057
+ * Ensure Redis is initialized
1058
+ */
1059
+ async ensureInitialized() {
1060
+ if (!this.initialized) {
1061
+ await this.initialize();
1062
+ }
1063
+ return this.redis;
1064
+ }
1065
+ /**
1066
+ * Get key for memory entry
1067
+ */
1068
+ getKey(id) {
1069
+ return `${this.keyPrefix}${id}`;
1070
+ }
1071
+ /**
1072
+ * Get index key
1073
+ */
1074
+ getIndexKey(index) {
1075
+ return `${this.keyPrefix}index:${index}`;
1076
+ }
1077
+ /**
1078
+ * Add a memory entry
1079
+ */
1080
+ async add(entry) {
1081
+ const redis = await this.ensureInitialized();
1082
+ const key = this.getKey(entry.id);
1083
+ const data = JSON.stringify(entry);
1084
+ if (this.ttl || entry.expiresAt) {
1085
+ const ttlMs = entry.expiresAt ? entry.expiresAt - Date.now() : this.ttl * 1e3;
1086
+ if (ttlMs > 0) {
1087
+ await redis.setex(key, Math.ceil(ttlMs / 1e3), data);
1088
+ } else {
1089
+ await redis.set(key, data);
1090
+ }
1091
+ } else {
1092
+ await redis.set(key, data);
1093
+ }
1094
+ await this.addToIndexes(entry);
1095
+ return entry.id;
1096
+ }
1097
+ /**
1098
+ * Add entry to indexes
1099
+ */
1100
+ async addToIndexes(entry) {
1101
+ const redis = await this.ensureInitialized();
1102
+ const score = entry.timestamp;
1103
+ await redis.zadd(this.getIndexKey("all"), score, entry.id);
1104
+ await redis.zadd(this.getIndexKey(`type:${entry.type}`), score, entry.id);
1105
+ const namespace = entry.metadata.namespace ?? "default";
1106
+ await redis.zadd(
1107
+ this.getIndexKey(`namespace:${namespace}`),
1108
+ score,
1109
+ entry.id
1110
+ );
1111
+ if (entry.metadata.userId) {
1112
+ await redis.zadd(
1113
+ this.getIndexKey(`user:${entry.metadata.userId}`),
1114
+ score,
1115
+ entry.id
1116
+ );
1117
+ }
1118
+ if (entry.metadata.conversationId) {
1119
+ await redis.zadd(
1120
+ this.getIndexKey(`conversation:${entry.metadata.conversationId}`),
1121
+ score,
1122
+ entry.id
1123
+ );
1124
+ }
1125
+ }
1126
+ /**
1127
+ * Remove entry from indexes
1128
+ */
1129
+ async removeFromIndexes(entry) {
1130
+ const redis = await this.ensureInitialized();
1131
+ await redis.zrem(this.getIndexKey("all"), entry.id);
1132
+ await redis.zrem(this.getIndexKey(`type:${entry.type}`), entry.id);
1133
+ const namespace = entry.metadata.namespace ?? "default";
1134
+ await redis.zrem(this.getIndexKey(`namespace:${namespace}`), entry.id);
1135
+ if (entry.metadata.userId) {
1136
+ await redis.zrem(
1137
+ this.getIndexKey(`user:${entry.metadata.userId}`),
1138
+ entry.id
1139
+ );
1140
+ }
1141
+ if (entry.metadata.conversationId) {
1142
+ await redis.zrem(
1143
+ this.getIndexKey(`conversation:${entry.metadata.conversationId}`),
1144
+ entry.id
1145
+ );
1146
+ }
1147
+ }
1148
+ /**
1149
+ * Get a memory entry by ID
1150
+ */
1151
+ async get(id) {
1152
+ const redis = await this.ensureInitialized();
1153
+ const key = this.getKey(id);
1154
+ const data = await redis.get(key);
1155
+ if (!data) {
1156
+ return null;
1157
+ }
1158
+ const entry = JSON.parse(data);
1159
+ entry.accessCount++;
1160
+ entry.lastAccessedAt = Date.now();
1161
+ await redis.set(key, JSON.stringify(entry));
1162
+ return entry;
1163
+ }
1164
+ /**
1165
+ * Update a memory entry
1166
+ */
1167
+ async update(id, updates) {
1168
+ const redis = await this.ensureInitialized();
1169
+ const existing = await this.get(id);
1170
+ if (!existing) {
1171
+ return false;
1172
+ }
1173
+ await this.removeFromIndexes(existing);
1174
+ const updated = {
1175
+ ...existing,
1176
+ ...updates,
1177
+ metadata: {
1178
+ ...existing.metadata,
1179
+ ...updates.metadata
1180
+ },
1181
+ updatedAt: Date.now()
1182
+ };
1183
+ const key = this.getKey(id);
1184
+ await redis.set(key, JSON.stringify(updated));
1185
+ await this.addToIndexes(updated);
1186
+ return true;
1187
+ }
1188
+ /**
1189
+ * Delete a memory entry
1190
+ */
1191
+ async delete(id) {
1192
+ const redis = await this.ensureInitialized();
1193
+ const existing = await this.get(id);
1194
+ if (!existing) {
1195
+ return false;
1196
+ }
1197
+ await this.removeFromIndexes(existing);
1198
+ const result = await redis.del(this.getKey(id));
1199
+ return result > 0;
1200
+ }
1201
+ /**
1202
+ * Query memory entries
1203
+ */
1204
+ async query(options) {
1205
+ const redis = await this.ensureInitialized();
1206
+ let indexKey = this.getIndexKey("all");
1207
+ if (options.conversationId) {
1208
+ indexKey = this.getIndexKey(`conversation:${options.conversationId}`);
1209
+ } else if (options.userId) {
1210
+ indexKey = this.getIndexKey(`user:${options.userId}`);
1211
+ } else if (options.namespace) {
1212
+ indexKey = this.getIndexKey(`namespace:${options.namespace}`);
1213
+ } else if (options.types && options.types.length === 1) {
1214
+ indexKey = this.getIndexKey(`type:${options.types[0]}`);
1215
+ }
1216
+ const minScore = options.startTime ?? "-inf";
1217
+ const maxScore = options.endTime ?? "+inf";
1218
+ const ids = await redis.zrevrangebyscore(
1219
+ indexKey,
1220
+ maxScore,
1221
+ minScore,
1222
+ "LIMIT",
1223
+ 0,
1224
+ 1e3
1225
+ // Get more than needed for filtering
1226
+ );
1227
+ const entries = [];
1228
+ const pipeline = redis.pipeline();
1229
+ for (const id of ids) {
1230
+ pipeline.get(this.getKey(id));
1231
+ }
1232
+ const results = await pipeline.exec();
1233
+ if (results) {
1234
+ for (const [err, data] of results) {
1235
+ if (!err && data) {
1236
+ const entry = JSON.parse(data);
1237
+ if (this.matchesQuery(entry, options)) {
1238
+ entries.push(entry);
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ const limit = options.limit ?? 100;
1244
+ const offset = options.offset ?? 0;
1245
+ const paginated = entries.slice(offset, offset + limit);
1246
+ return {
1247
+ entries: paginated,
1248
+ total: entries.length,
1249
+ hasMore: offset + limit < entries.length
1250
+ };
1251
+ }
1252
+ /**
1253
+ * Check if entry matches query
1254
+ */
1255
+ matchesQuery(entry, options) {
1256
+ if (options.query) {
1257
+ if (!entry.content.toLowerCase().includes(options.query.toLowerCase())) {
1258
+ return false;
1259
+ }
1260
+ }
1261
+ if (options.userId && entry.metadata.userId !== options.userId) {
1262
+ return false;
1263
+ }
1264
+ if (options.agentId && entry.metadata.agentId !== options.agentId) {
1265
+ return false;
1266
+ }
1267
+ if (options.conversationId && entry.metadata.conversationId !== options.conversationId) {
1268
+ return false;
1269
+ }
1270
+ if (options.namespace && entry.metadata.namespace !== options.namespace) {
1271
+ return false;
1272
+ }
1273
+ if (options.types && options.types.length > 0 && !options.types.includes(entry.type)) {
1274
+ return false;
1275
+ }
1276
+ if (options.minImportance !== void 0 && entry.importance < options.minImportance) {
1277
+ return false;
1278
+ }
1279
+ if (options.tags && options.tags.length > 0) {
1280
+ const entryTags = entry.metadata.tags ?? [];
1281
+ if (!options.tags.every((tag) => entryTags.includes(tag))) {
1282
+ return false;
1283
+ }
1284
+ }
1285
+ if (!options.includeExpired && entry.expiresAt && entry.expiresAt < Date.now()) {
1286
+ return false;
1287
+ }
1288
+ return true;
1289
+ }
1290
+ /**
1291
+ * Search by vector similarity
1292
+ */
1293
+ async search(embedding, options) {
1294
+ const { entries } = await this.query({
1295
+ limit: 1e4,
1296
+ namespace: options.namespace
1297
+ });
1298
+ const results = [];
1299
+ for (const entry of entries) {
1300
+ if (!entry.embedding) continue;
1301
+ if (options.filter) {
1302
+ let matches = true;
1303
+ for (const [key, value] of Object.entries(options.filter)) {
1304
+ if (value !== void 0) {
1305
+ const metaValue = entry.metadata[key];
1306
+ if (Array.isArray(value)) {
1307
+ if (!value.includes(metaValue)) {
1308
+ matches = false;
1309
+ break;
1310
+ }
1311
+ } else if (metaValue !== value) {
1312
+ matches = false;
1313
+ break;
1314
+ }
1315
+ }
1316
+ }
1317
+ if (!matches) continue;
1318
+ }
1319
+ const score = this.cosineSimilarity(embedding, entry.embedding);
1320
+ if (options.minScore === void 0 || score >= options.minScore) {
1321
+ results.push({ entry, score });
1322
+ }
1323
+ }
1324
+ results.sort((a, b) => b.score - a.score);
1325
+ return Promise.resolve(results.slice(0, options.topK));
1326
+ }
1327
+ /**
1328
+ * Calculate cosine similarity
1329
+ */
1330
+ cosineSimilarity(a, b) {
1331
+ if (a.length !== b.length) return 0;
1332
+ let dotProduct = 0;
1333
+ let normA = 0;
1334
+ let normB = 0;
1335
+ for (let i = 0; i < a.length; i++) {
1336
+ dotProduct += a[i] * b[i];
1337
+ normA += a[i] * a[i];
1338
+ normB += b[i] * b[i];
1339
+ }
1340
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
1341
+ return magnitude === 0 ? 0 : dotProduct / magnitude;
1342
+ }
1343
+ /**
1344
+ * Clear entries
1345
+ */
1346
+ async clear(options) {
1347
+ const redis = await this.ensureInitialized();
1348
+ if (!options) {
1349
+ const keys = await redis.keys(`${this.keyPrefix}*`);
1350
+ if (keys.length > 0) {
1351
+ await redis.del(...keys);
1352
+ }
1353
+ return keys.length;
1354
+ }
1355
+ const { entries } = await this.query({
1356
+ namespace: options.namespace,
1357
+ userId: options.userId,
1358
+ limit: 1e5
1359
+ });
1360
+ for (const entry of entries) {
1361
+ await this.delete(entry.id);
1362
+ }
1363
+ return entries.length;
1364
+ }
1365
+ /**
1366
+ * Count entries
1367
+ */
1368
+ async count(options) {
1369
+ const redis = await this.ensureInitialized();
1370
+ if (!options) {
1371
+ return Promise.resolve(redis.zcard(this.getIndexKey("all")));
1372
+ }
1373
+ const { total } = await this.query({ ...options, limit: 0 });
1374
+ return total;
1375
+ }
1376
+ /**
1377
+ * Close Redis connection
1378
+ */
1379
+ async close() {
1380
+ if (this.redis) {
1381
+ await this.redis.quit();
1382
+ this.redis = null;
1383
+ this.initialized = false;
1384
+ }
1385
+ }
1386
+ };
1387
+ async function createRedisStore(config) {
1388
+ const store = new RedisStore(config);
1389
+ await store.initialize();
1390
+ return store;
1391
+ }
1392
+
1393
+ export {
1394
+ InMemoryStore,
1395
+ createInMemoryStore,
1396
+ SQLiteStore,
1397
+ createSQLiteStore,
1398
+ PostgresStore,
1399
+ createPostgresStore,
1400
+ RedisStore,
1401
+ createRedisStore
1402
+ };