@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,1262 @@
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/retrieval/index.ts
21
+ var retrieval_exports = {};
22
+ __export(retrieval_exports, {
23
+ HybridRetrieval: () => HybridRetrieval,
24
+ PipelineBuilder: () => PipelineBuilder,
25
+ RetrievalPipeline: () => RetrievalPipeline,
26
+ SemanticRetrieval: () => SemanticRetrieval,
27
+ TemporalRetrieval: () => TemporalRetrieval,
28
+ TimeWindows: () => TimeWindows,
29
+ createHybridRetrieval: () => createHybridRetrieval,
30
+ createPipelineBuilder: () => createPipelineBuilder,
31
+ createRetrievalPipeline: () => createRetrievalPipeline,
32
+ createSemanticRetrieval: () => createSemanticRetrieval,
33
+ createTemporalRetrieval: () => createTemporalRetrieval
34
+ });
35
+ module.exports = __toCommonJS(retrieval_exports);
36
+
37
+ // src/retrieval/strategies/SemanticRetrieval.ts
38
+ var SemanticRetrieval = class {
39
+ store;
40
+ embedFn;
41
+ config;
42
+ constructor(store, embedFn, config = {}) {
43
+ this.store = store;
44
+ this.embedFn = embedFn;
45
+ this.config = {
46
+ topK: config.topK ?? 10,
47
+ minScore: config.minScore ?? 0.7,
48
+ reranking: config.reranking ?? false,
49
+ maxCandidates: config.maxCandidates ?? 100,
50
+ ...config
51
+ };
52
+ }
53
+ /**
54
+ * Retrieve memories semantically similar to the query
55
+ */
56
+ async retrieve(options) {
57
+ const startTime = Date.now();
58
+ const queryEmbedding = await this.embedFn(options.query);
59
+ const results = await this.store.search(queryEmbedding, {
60
+ topK: options.topK ?? this.config.topK,
61
+ minScore: options.minScore ?? this.config.minScore,
62
+ namespace: options.namespace,
63
+ filter: options.filter
64
+ });
65
+ let finalResults = results;
66
+ if (this.config.reranking && this.config.rerankFn) {
67
+ finalResults = await this.rerank(options.query, results);
68
+ }
69
+ const memories = finalResults.map((r) => {
70
+ const entry = { ...r.entry };
71
+ if (!options.includeEmbeddings) {
72
+ delete entry.embedding;
73
+ }
74
+ return entry;
75
+ });
76
+ return {
77
+ memories,
78
+ scores: finalResults.map((r) => r.score),
79
+ totalCandidates: results.length,
80
+ retrievalTimeMs: Date.now() - startTime,
81
+ strategy: "semantic"
82
+ };
83
+ }
84
+ /**
85
+ * Retrieve with context - includes surrounding memories
86
+ */
87
+ async retrieveWithContext(options, contextWindow = 2) {
88
+ const result = await this.retrieve(options);
89
+ const contextMemories = [];
90
+ for (const memory of result.memories) {
91
+ const surrounding = await this.getSurroundingMemories(
92
+ memory,
93
+ contextWindow
94
+ );
95
+ contextMemories.push(surrounding);
96
+ }
97
+ return {
98
+ ...result,
99
+ contextMemories
100
+ };
101
+ }
102
+ /**
103
+ * Get memories surrounding a given memory by timestamp
104
+ */
105
+ async getSurroundingMemories(memory, windowSize) {
106
+ const before = await this.store.query({
107
+ endTime: memory.timestamp - 1,
108
+ limit: windowSize,
109
+ namespace: memory.metadata.namespace
110
+ });
111
+ const after = await this.store.query({
112
+ startTime: memory.timestamp + 1,
113
+ limit: windowSize,
114
+ namespace: memory.metadata.namespace
115
+ });
116
+ return [...before.entries.reverse(), ...after.entries];
117
+ }
118
+ /**
119
+ * Rerank results using provided rerank function
120
+ */
121
+ async rerank(query, results) {
122
+ if (!this.config.rerankFn) {
123
+ return results;
124
+ }
125
+ const reranked = await this.config.rerankFn(query, results);
126
+ return Promise.resolve(
127
+ reranked.sort((a, b) => b.score - a.score)
128
+ );
129
+ }
130
+ /**
131
+ * Find memories similar to a given memory
132
+ */
133
+ async findSimilar(memory, options) {
134
+ if (!memory.embedding) {
135
+ const embedding = await this.embedFn(memory.content);
136
+ return this.store.search(embedding, {
137
+ topK: options?.topK ?? this.config.topK,
138
+ minScore: options?.minScore ?? this.config.minScore,
139
+ namespace: options?.namespace ?? memory.metadata.namespace,
140
+ filter: {
141
+ ...options?.filter,
142
+ // Exclude the source memory
143
+ id: { $ne: memory.id }
144
+ }
145
+ });
146
+ }
147
+ return this.store.search(memory.embedding, {
148
+ topK: (options?.topK ?? this.config.topK) + 1,
149
+ // +1 to exclude self
150
+ minScore: options?.minScore ?? this.config.minScore,
151
+ namespace: options?.namespace ?? memory.metadata.namespace,
152
+ filter: options?.filter
153
+ }).then((results) => results.filter((r) => r.entry.id !== memory.id));
154
+ }
155
+ /**
156
+ * Cluster memories by semantic similarity
157
+ */
158
+ async cluster(memories, numClusters = 5) {
159
+ const clusters = /* @__PURE__ */ new Map();
160
+ const memoriesWithEmbeddings = await Promise.all(
161
+ memories.map(async (m) => {
162
+ if (m.embedding) return m;
163
+ return {
164
+ ...m,
165
+ embedding: await this.embedFn(m.content)
166
+ };
167
+ })
168
+ );
169
+ const centerIndices = this.randomSample(
170
+ memoriesWithEmbeddings.length,
171
+ numClusters
172
+ );
173
+ const centers = centerIndices.map(
174
+ (i) => memoriesWithEmbeddings[i].embedding
175
+ );
176
+ for (let iteration = 0; iteration < 10; iteration++) {
177
+ for (let i = 0; i < numClusters; i++) {
178
+ clusters.set(i, []);
179
+ }
180
+ for (const memory of memoriesWithEmbeddings) {
181
+ let bestCluster = 0;
182
+ let bestSimilarity = -Infinity;
183
+ for (let i = 0; i < centers.length; i++) {
184
+ const similarity = this.cosineSimilarity(
185
+ memory.embedding,
186
+ centers[i]
187
+ );
188
+ if (similarity > bestSimilarity) {
189
+ bestSimilarity = similarity;
190
+ bestCluster = i;
191
+ }
192
+ }
193
+ clusters.get(bestCluster).push(memory);
194
+ }
195
+ for (let i = 0; i < numClusters; i++) {
196
+ const clusterMemories = clusters.get(i);
197
+ if (clusterMemories.length > 0) {
198
+ centers[i] = this.averageEmbedding(
199
+ clusterMemories.map((m) => m.embedding)
200
+ );
201
+ }
202
+ }
203
+ }
204
+ return clusters;
205
+ }
206
+ /**
207
+ * Calculate cosine similarity between two vectors
208
+ */
209
+ cosineSimilarity(a, b) {
210
+ if (a.length !== b.length) return 0;
211
+ let dotProduct = 0;
212
+ let normA = 0;
213
+ let normB = 0;
214
+ for (let i = 0; i < a.length; i++) {
215
+ dotProduct += a[i] * b[i];
216
+ normA += a[i] * a[i];
217
+ normB += b[i] * b[i];
218
+ }
219
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
220
+ return magnitude === 0 ? 0 : dotProduct / magnitude;
221
+ }
222
+ /**
223
+ * Calculate average embedding
224
+ */
225
+ averageEmbedding(embeddings) {
226
+ const dim = embeddings[0].length;
227
+ const avg = new Array(dim).fill(0);
228
+ for (const emb of embeddings) {
229
+ for (let i = 0; i < dim; i++) {
230
+ avg[i] += emb[i];
231
+ }
232
+ }
233
+ for (let i = 0; i < dim; i++) {
234
+ avg[i] /= embeddings.length;
235
+ }
236
+ return avg;
237
+ }
238
+ /**
239
+ * Random sample without replacement
240
+ */
241
+ randomSample(max, count) {
242
+ const result = [];
243
+ const used = /* @__PURE__ */ new Set();
244
+ while (result.length < count && result.length < max) {
245
+ const index = Math.floor(Math.random() * max);
246
+ if (!used.has(index)) {
247
+ used.add(index);
248
+ result.push(index);
249
+ }
250
+ }
251
+ return result;
252
+ }
253
+ /**
254
+ * Update configuration
255
+ */
256
+ configure(config) {
257
+ this.config = { ...this.config, ...config };
258
+ }
259
+ /**
260
+ * Get current configuration
261
+ */
262
+ getConfig() {
263
+ return { ...this.config };
264
+ }
265
+ };
266
+ function createSemanticRetrieval(store, embedFn, config) {
267
+ return new SemanticRetrieval(store, embedFn, config);
268
+ }
269
+
270
+ // src/retrieval/strategies/HybridRetrieval.ts
271
+ var HybridRetrieval = class {
272
+ store;
273
+ semanticRetrieval;
274
+ config;
275
+ constructor(store, embedFn, config = {}) {
276
+ this.store = store;
277
+ this.config = {
278
+ semanticWeight: config.semanticWeight ?? 0.7,
279
+ keywordWeight: config.keywordWeight ?? 0.3,
280
+ topK: config.topK ?? 10,
281
+ minScore: config.minScore ?? 0.5,
282
+ fusionMethod: config.fusionMethod ?? "rrf",
283
+ ...config
284
+ };
285
+ this.semanticRetrieval = new SemanticRetrieval(store, embedFn, {
286
+ topK: config.topK ?? 10,
287
+ minScore: 0
288
+ // Lower threshold for candidates
289
+ });
290
+ }
291
+ /**
292
+ * Retrieve memories using hybrid search
293
+ */
294
+ async retrieve(options) {
295
+ const startTime = Date.now();
296
+ const topK = options.topK ?? this.config.topK;
297
+ const semanticWeight = options.semanticWeight ?? this.config.semanticWeight;
298
+ const keywordWeight = options.keywordWeight ?? this.config.keywordWeight;
299
+ const candidateMultiplier = 3;
300
+ const semanticResult = await this.semanticRetrieval.retrieve({
301
+ query: options.query,
302
+ topK: topK * candidateMultiplier,
303
+ minScore: 0,
304
+ // Get all candidates
305
+ namespace: options.namespace,
306
+ filter: options.filter
307
+ });
308
+ const keywordResult = await this.keywordSearch(options.query, {
309
+ limit: topK * candidateMultiplier,
310
+ namespace: options.namespace
311
+ });
312
+ const fusedResults = this.fuseResults(
313
+ semanticResult.memories.map((m, i) => ({
314
+ entry: m,
315
+ score: semanticResult.scores?.[i] ?? 0
316
+ })),
317
+ keywordResult,
318
+ semanticWeight,
319
+ keywordWeight
320
+ );
321
+ const filtered = fusedResults.filter((r) => r.score >= (options.minScore ?? this.config.minScore)).slice(0, topK);
322
+ return {
323
+ memories: filtered.map((r) => r.entry),
324
+ scores: filtered.map((r) => r.score),
325
+ totalCandidates: semanticResult.memories.length + keywordResult.length,
326
+ retrievalTimeMs: Date.now() - startTime,
327
+ strategy: "hybrid",
328
+ metadata: {
329
+ semanticCandidates: semanticResult.memories.length,
330
+ keywordCandidates: keywordResult.length,
331
+ fusionMethod: this.config.fusionMethod
332
+ }
333
+ };
334
+ }
335
+ /**
336
+ * Keyword-based search using text matching
337
+ */
338
+ async keywordSearch(query, options) {
339
+ const queryTokens = this.tokenize(query);
340
+ const { entries } = await this.store.query({
341
+ query,
342
+ limit: options.limit * 2,
343
+ // Get more for better recall
344
+ namespace: options.namespace
345
+ });
346
+ const results = [];
347
+ for (const entry of entries) {
348
+ const score = this.calculateKeywordScore(queryTokens, entry.content);
349
+ if (score > 0) {
350
+ results.push({ entry, score });
351
+ }
352
+ }
353
+ results.sort((a, b) => b.score - a.score);
354
+ return Promise.resolve(results.slice(0, options.limit));
355
+ }
356
+ /**
357
+ * Tokenize text into terms
358
+ */
359
+ tokenize(text) {
360
+ return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 2);
361
+ }
362
+ /**
363
+ * Calculate BM25-like keyword score
364
+ */
365
+ calculateKeywordScore(queryTokens, content) {
366
+ const contentTokens = this.tokenize(content);
367
+ const contentTokenSet = new Set(contentTokens);
368
+ const termFreq = /* @__PURE__ */ new Map();
369
+ for (const token of contentTokens) {
370
+ termFreq.set(token, (termFreq.get(token) ?? 0) + 1);
371
+ }
372
+ const k1 = 1.2;
373
+ const b = 0.75;
374
+ const avgDocLength = 100;
375
+ let score = 0;
376
+ const docLength = contentTokens.length;
377
+ for (const token of queryTokens) {
378
+ if (contentTokenSet.has(token)) {
379
+ const tf = termFreq.get(token) ?? 0;
380
+ const idf = 1.5;
381
+ const tfNorm = tf * (k1 + 1) / (tf + k1 * (1 - b + b * docLength / avgDocLength));
382
+ score += idf * tfNorm;
383
+ }
384
+ }
385
+ return score / queryTokens.length;
386
+ }
387
+ /**
388
+ * Fuse semantic and keyword results
389
+ */
390
+ fuseResults(semanticResults, keywordResults, semanticWeight, keywordWeight) {
391
+ if (this.config.fusionMethod === "rrf") {
392
+ return this.reciprocalRankFusion(semanticResults, keywordResults);
393
+ } else {
394
+ return this.weightedFusion(
395
+ semanticResults,
396
+ keywordResults,
397
+ semanticWeight,
398
+ keywordWeight
399
+ );
400
+ }
401
+ }
402
+ /**
403
+ * Reciprocal Rank Fusion (RRF)
404
+ */
405
+ reciprocalRankFusion(list1, list2) {
406
+ const k = 60;
407
+ const scoreMap = /* @__PURE__ */ new Map();
408
+ for (let i = 0; i < list1.length; i++) {
409
+ const id = list1[i].entry.id;
410
+ const rrfScore = 1 / (k + i + 1);
411
+ scoreMap.set(id, {
412
+ entry: list1[i].entry,
413
+ score: rrfScore
414
+ });
415
+ }
416
+ for (let i = 0; i < list2.length; i++) {
417
+ const id = list2[i].entry.id;
418
+ const rrfScore = 1 / (k + i + 1);
419
+ const existing = scoreMap.get(id);
420
+ if (existing) {
421
+ existing.score += rrfScore;
422
+ } else {
423
+ scoreMap.set(id, {
424
+ entry: list2[i].entry,
425
+ score: rrfScore
426
+ });
427
+ }
428
+ }
429
+ const results = Array.from(scoreMap.values()).map((item) => ({
430
+ entry: item.entry,
431
+ score: item.score
432
+ }));
433
+ results.sort((a, b) => b.score - a.score);
434
+ return results;
435
+ }
436
+ /**
437
+ * Weighted score fusion
438
+ */
439
+ weightedFusion(semanticResults, keywordResults, semanticWeight, keywordWeight) {
440
+ const scoreMap = /* @__PURE__ */ new Map();
441
+ const maxSemantic = Math.max(...semanticResults.map((r) => r.score), 1);
442
+ for (const result of semanticResults) {
443
+ const normalizedScore = result.score / maxSemantic;
444
+ scoreMap.set(result.entry.id, {
445
+ entry: result.entry,
446
+ semanticScore: normalizedScore,
447
+ keywordScore: 0
448
+ });
449
+ }
450
+ const maxKeyword = Math.max(...keywordResults.map((r) => r.score), 1);
451
+ for (const result of keywordResults) {
452
+ const normalizedScore = result.score / maxKeyword;
453
+ const existing = scoreMap.get(result.entry.id);
454
+ if (existing) {
455
+ existing.keywordScore = normalizedScore;
456
+ } else {
457
+ scoreMap.set(result.entry.id, {
458
+ entry: result.entry,
459
+ semanticScore: 0,
460
+ keywordScore: normalizedScore
461
+ });
462
+ }
463
+ }
464
+ const results = Array.from(scoreMap.values()).map((item) => ({
465
+ entry: item.entry,
466
+ score: item.semanticScore * semanticWeight + item.keywordScore * keywordWeight
467
+ }));
468
+ results.sort((a, b) => b.score - a.score);
469
+ return results;
470
+ }
471
+ /**
472
+ * Retrieve with explanation of why each result matched
473
+ */
474
+ async retrieveWithExplanation(options) {
475
+ const startTime = Date.now();
476
+ const topK = options.topK ?? this.config.topK;
477
+ const semanticResult = await this.semanticRetrieval.retrieve({
478
+ query: options.query,
479
+ topK: topK * 3,
480
+ minScore: 0,
481
+ namespace: options.namespace
482
+ });
483
+ const keywordResult = await this.keywordSearch(options.query, {
484
+ limit: topK * 3,
485
+ namespace: options.namespace
486
+ });
487
+ const semanticScores = /* @__PURE__ */ new Map();
488
+ semanticResult.memories.forEach((m, i) => {
489
+ semanticScores.set(m.id, semanticResult.scores?.[i] ?? 0);
490
+ });
491
+ const keywordScores = /* @__PURE__ */ new Map();
492
+ keywordResult.forEach((r) => {
493
+ keywordScores.set(r.entry.id, r.score);
494
+ });
495
+ const fusedResults = this.fuseResults(
496
+ semanticResult.memories.map((m, i) => ({
497
+ entry: m,
498
+ score: semanticResult.scores?.[i] ?? 0
499
+ })),
500
+ keywordResult,
501
+ options.semanticWeight ?? this.config.semanticWeight,
502
+ options.keywordWeight ?? this.config.keywordWeight
503
+ ).slice(0, topK);
504
+ const queryTokens = this.tokenize(options.query);
505
+ const explanations = fusedResults.map((r) => {
506
+ const semScore = semanticScores.get(r.entry.id);
507
+ const kwScore = keywordScores.get(r.entry.id);
508
+ const parts = [];
509
+ if (semScore !== void 0 && semScore > 0.5) {
510
+ parts.push(`semantically similar (${(semScore * 100).toFixed(0)}%)`);
511
+ }
512
+ if (kwScore !== void 0 && kwScore > 0) {
513
+ const matchingTokens = queryTokens.filter(
514
+ (t) => r.entry.content.toLowerCase().includes(t)
515
+ );
516
+ if (matchingTokens.length > 0) {
517
+ parts.push(`keyword matches: "${matchingTokens.join('", "')}"`);
518
+ }
519
+ }
520
+ return parts.length > 0 ? parts.join("; ") : "matched via fuzzy matching";
521
+ });
522
+ return {
523
+ memories: fusedResults.map((r) => r.entry),
524
+ scores: fusedResults.map((r) => r.score),
525
+ totalCandidates: semanticResult.memories.length + keywordResult.length,
526
+ retrievalTimeMs: Date.now() - startTime,
527
+ strategy: "hybrid",
528
+ explanations
529
+ };
530
+ }
531
+ /**
532
+ * Update configuration
533
+ */
534
+ configure(config) {
535
+ this.config = { ...this.config, ...config };
536
+ }
537
+ /**
538
+ * Get current configuration
539
+ */
540
+ getConfig() {
541
+ return { ...this.config };
542
+ }
543
+ };
544
+ function createHybridRetrieval(store, embedFn, config) {
545
+ return new HybridRetrieval(store, embedFn, config);
546
+ }
547
+
548
+ // src/retrieval/strategies/TemporalRetrieval.ts
549
+ var TemporalRetrieval = class {
550
+ store;
551
+ config;
552
+ constructor(store, config = {}) {
553
+ this.store = store;
554
+ this.config = {
555
+ recencyWeight: config.recencyWeight ?? 0.5,
556
+ importanceWeight: config.importanceWeight ?? 0.3,
557
+ accessWeight: config.accessWeight ?? 0.2,
558
+ decayFunction: config.decayFunction ?? "exponential",
559
+ decayHalfLife: config.decayHalfLife ?? 24 * 60 * 60 * 1e3,
560
+ // 24 hours
561
+ topK: config.topK ?? 10,
562
+ ...config
563
+ };
564
+ }
565
+ /**
566
+ * Retrieve memories with temporal scoring
567
+ */
568
+ async retrieve(options) {
569
+ const startTime = Date.now();
570
+ const { entries } = await this.store.query({
571
+ startTime: options.startTime,
572
+ endTime: options.endTime,
573
+ namespace: options.namespace,
574
+ types: options.types,
575
+ limit: 1e3
576
+ // Get many candidates for scoring
577
+ });
578
+ let filtered = entries;
579
+ if (options.filter) {
580
+ filtered = this.applyFilter(entries, options.filter);
581
+ }
582
+ const now = Date.now();
583
+ const scored = filtered.map((entry) => ({
584
+ entry,
585
+ score: this.calculateTemporalScore(entry, now, options)
586
+ }));
587
+ scored.sort((a, b) => b.score - a.score);
588
+ const topK = options.topK ?? this.config.topK;
589
+ const results = scored.slice(0, topK);
590
+ return {
591
+ memories: results.map((r) => r.entry),
592
+ scores: results.map((r) => r.score),
593
+ totalCandidates: filtered.length,
594
+ retrievalTimeMs: Date.now() - startTime,
595
+ strategy: "temporal"
596
+ };
597
+ }
598
+ /**
599
+ * Retrieve memories from specific time windows
600
+ */
601
+ async retrieveFromWindows(windows, options) {
602
+ const results = /* @__PURE__ */ new Map();
603
+ for (const window of windows) {
604
+ const label = window.label ?? `${window.start}-${window.end}`;
605
+ const result = await this.retrieve({
606
+ ...options,
607
+ startTime: window.start,
608
+ endTime: window.end
609
+ });
610
+ results.set(label, result);
611
+ }
612
+ return results;
613
+ }
614
+ /**
615
+ * Get memories from relative time periods
616
+ */
617
+ async retrieveRecent(period, options) {
618
+ const now = Date.now();
619
+ const periodMs = {
620
+ hour: 60 * 60 * 1e3,
621
+ day: 24 * 60 * 60 * 1e3,
622
+ week: 7 * 24 * 60 * 60 * 1e3,
623
+ month: 30 * 24 * 60 * 60 * 1e3
624
+ };
625
+ return this.retrieve({
626
+ ...options,
627
+ startTime: now - periodMs[period],
628
+ endTime: now
629
+ });
630
+ }
631
+ /**
632
+ * Get trending memories (high access in recent time)
633
+ */
634
+ async retrieveTrending(windowMs = 24 * 60 * 60 * 1e3, options) {
635
+ const startTime = Date.now();
636
+ const windowStart = startTime - windowMs;
637
+ const { entries } = await this.store.query({
638
+ startTime: windowStart,
639
+ namespace: options?.namespace,
640
+ limit: 1e3
641
+ });
642
+ const scored = entries.map((entry) => {
643
+ const age = startTime - entry.timestamp;
644
+ const accessRate = entry.accessCount / Math.max(age / (60 * 60 * 1e3), 1);
645
+ return {
646
+ entry,
647
+ score: accessRate * entry.importance
648
+ };
649
+ });
650
+ scored.sort((a, b) => b.score - a.score);
651
+ const topK = options?.topK ?? this.config.topK;
652
+ const results = scored.slice(0, topK);
653
+ return {
654
+ memories: results.map((r) => r.entry),
655
+ scores: results.map((r) => r.score),
656
+ totalCandidates: entries.length,
657
+ retrievalTimeMs: Date.now() - startTime,
658
+ strategy: "temporal-trending"
659
+ };
660
+ }
661
+ /**
662
+ * Get memories matching a temporal pattern
663
+ */
664
+ async retrieveByPattern(pattern, lookbackPeriods = 4, options) {
665
+ const startTime = Date.now();
666
+ const now = Date.now();
667
+ const windows = [];
668
+ for (let i = 0; i < lookbackPeriods; i++) {
669
+ const periodStart = now - (i + 1) * pattern.interval;
670
+ const periodEnd = now - i * pattern.interval;
671
+ if (pattern.peakHours || pattern.peakDays) {
672
+ const date = new Date(periodStart);
673
+ const hour = date.getHours();
674
+ const day = date.getDay();
675
+ if (pattern.peakHours && !pattern.peakHours.includes(hour)) {
676
+ continue;
677
+ }
678
+ if (pattern.peakDays && !pattern.peakDays.includes(day)) {
679
+ continue;
680
+ }
681
+ }
682
+ windows.push({ start: periodStart, end: periodEnd });
683
+ }
684
+ const allEntries = [];
685
+ for (const window of windows) {
686
+ const { entries } = await this.store.query({
687
+ startTime: window.start,
688
+ endTime: window.end,
689
+ namespace: options?.namespace,
690
+ limit: 100
691
+ });
692
+ allEntries.push(...entries);
693
+ }
694
+ const uniqueEntries = this.deduplicateEntries(allEntries);
695
+ const scored = uniqueEntries.map((entry) => ({
696
+ entry,
697
+ score: this.calculateTemporalScore(entry, now, options ?? {})
698
+ }));
699
+ scored.sort((a, b) => b.score - a.score);
700
+ const topK = options?.topK ?? this.config.topK;
701
+ const results = scored.slice(0, topK);
702
+ return {
703
+ memories: results.map((r) => r.entry),
704
+ scores: results.map((r) => r.score),
705
+ totalCandidates: uniqueEntries.length,
706
+ retrievalTimeMs: Date.now() - startTime,
707
+ strategy: "temporal-pattern",
708
+ metadata: {
709
+ pattern: pattern.type,
710
+ windowsMatched: windows.length
711
+ }
712
+ };
713
+ }
714
+ /**
715
+ * Get timeline of memories
716
+ */
717
+ async getTimeline(options) {
718
+ const bucketMs = {
719
+ hour: 60 * 60 * 1e3,
720
+ day: 24 * 60 * 60 * 1e3,
721
+ week: 7 * 24 * 60 * 60 * 1e3
722
+ };
723
+ const { entries } = await this.store.query({
724
+ startTime: options.startTime,
725
+ endTime: options.endTime,
726
+ namespace: options.namespace,
727
+ limit: 1e4
728
+ });
729
+ const timeline = /* @__PURE__ */ new Map();
730
+ const size = bucketMs[options.bucketSize];
731
+ for (const entry of entries) {
732
+ const bucketStart = Math.floor(entry.timestamp / size) * size;
733
+ const key = new Date(bucketStart).toISOString();
734
+ if (!timeline.has(key)) {
735
+ timeline.set(key, []);
736
+ }
737
+ timeline.get(key).push(entry);
738
+ }
739
+ return timeline;
740
+ }
741
+ /**
742
+ * Calculate temporal score for a memory
743
+ */
744
+ calculateTemporalScore(entry, now, options) {
745
+ const recencyWeight = options.recencyWeight ?? this.config.recencyWeight;
746
+ const importanceWeight = options.importanceWeight ?? this.config.importanceWeight;
747
+ const accessWeight = options.accessWeight ?? this.config.accessWeight;
748
+ const recencyScore = this.calculateDecay(now - entry.timestamp);
749
+ const importanceScore = entry.importance;
750
+ const maxAccessCount = 100;
751
+ const accessScore = Math.min(entry.accessCount / maxAccessCount, 1);
752
+ return recencyScore * recencyWeight + importanceScore * importanceWeight + accessScore * accessWeight;
753
+ }
754
+ /**
755
+ * Calculate decay based on configured function
756
+ */
757
+ calculateDecay(ageMs) {
758
+ const halfLife = this.config.decayHalfLife;
759
+ switch (this.config.decayFunction) {
760
+ case "exponential":
761
+ return Math.exp(-Math.LN2 * ageMs / halfLife);
762
+ case "linear":
763
+ return Math.max(0, 1 - ageMs / (halfLife * 2));
764
+ case "step":
765
+ return ageMs <= halfLife ? 1 : 0;
766
+ case "logarithmic":
767
+ return 1 / (1 + Math.log2(1 + ageMs / halfLife));
768
+ default:
769
+ return Math.exp(-Math.LN2 * ageMs / halfLife);
770
+ }
771
+ }
772
+ /**
773
+ * Apply metadata filter to entries
774
+ */
775
+ applyFilter(entries, filter) {
776
+ return entries.filter((entry) => {
777
+ for (const [key, value] of Object.entries(filter)) {
778
+ const entryValue = entry.metadata[key];
779
+ if (Array.isArray(value)) {
780
+ if (!value.includes(entryValue)) return false;
781
+ } else if (entryValue !== value) {
782
+ return false;
783
+ }
784
+ }
785
+ return true;
786
+ });
787
+ }
788
+ /**
789
+ * Deduplicate entries by ID
790
+ */
791
+ deduplicateEntries(entries) {
792
+ const seen = /* @__PURE__ */ new Set();
793
+ return entries.filter((entry) => {
794
+ if (seen.has(entry.id)) return false;
795
+ seen.add(entry.id);
796
+ return true;
797
+ });
798
+ }
799
+ /**
800
+ * Update configuration
801
+ */
802
+ configure(config) {
803
+ this.config = { ...this.config, ...config };
804
+ }
805
+ /**
806
+ * Get current configuration
807
+ */
808
+ getConfig() {
809
+ return { ...this.config };
810
+ }
811
+ };
812
+ function createTemporalRetrieval(store, config) {
813
+ return new TemporalRetrieval(store, config);
814
+ }
815
+ var TimeWindows = {
816
+ lastHour: () => ({
817
+ start: Date.now() - 60 * 60 * 1e3,
818
+ end: Date.now(),
819
+ label: "last-hour"
820
+ }),
821
+ lastDay: () => ({
822
+ start: Date.now() - 24 * 60 * 60 * 1e3,
823
+ end: Date.now(),
824
+ label: "last-day"
825
+ }),
826
+ lastWeek: () => ({
827
+ start: Date.now() - 7 * 24 * 60 * 60 * 1e3,
828
+ end: Date.now(),
829
+ label: "last-week"
830
+ }),
831
+ lastMonth: () => ({
832
+ start: Date.now() - 30 * 24 * 60 * 60 * 1e3,
833
+ end: Date.now(),
834
+ label: "last-month"
835
+ }),
836
+ today: () => {
837
+ const now = /* @__PURE__ */ new Date();
838
+ const startOfDay = new Date(
839
+ now.getFullYear(),
840
+ now.getMonth(),
841
+ now.getDate()
842
+ ).getTime();
843
+ return {
844
+ start: startOfDay,
845
+ end: Date.now(),
846
+ label: "today"
847
+ };
848
+ },
849
+ yesterday: () => {
850
+ const now = /* @__PURE__ */ new Date();
851
+ const startOfYesterday = new Date(
852
+ now.getFullYear(),
853
+ now.getMonth(),
854
+ now.getDate() - 1
855
+ ).getTime();
856
+ const endOfYesterday = new Date(
857
+ now.getFullYear(),
858
+ now.getMonth(),
859
+ now.getDate()
860
+ ).getTime();
861
+ return {
862
+ start: startOfYesterday,
863
+ end: endOfYesterday,
864
+ label: "yesterday"
865
+ };
866
+ },
867
+ custom: (startDate, endDate, label) => ({
868
+ start: startDate.getTime(),
869
+ end: endDate.getTime(),
870
+ label
871
+ })
872
+ };
873
+
874
+ // src/retrieval/strategies/RetrievalPipeline.ts
875
+ var RetrievalPipeline = class {
876
+ stages = /* @__PURE__ */ new Map();
877
+ config;
878
+ constructor(config = { stages: [] }) {
879
+ this.config = {
880
+ maxCandidates: config.maxCandidates ?? 100,
881
+ minScore: config.minScore ?? 0,
882
+ timeout: config.timeout ?? 3e4,
883
+ ...config
884
+ };
885
+ this.registerBuiltInStages();
886
+ }
887
+ /**
888
+ * Register built-in stages
889
+ */
890
+ registerBuiltInStages() {
891
+ this.register("filter", (ctx, config) => {
892
+ const filters = config.params?.filters;
893
+ if (!filters) return Promise.resolve(ctx);
894
+ const filtered = ctx.candidates.filter((c) => {
895
+ for (const [key, value] of Object.entries(filters)) {
896
+ const entryValue = c.entry.metadata[key] ?? c.entry[key];
897
+ if (Array.isArray(value)) {
898
+ if (!value.includes(entryValue)) return false;
899
+ } else if (entryValue !== value) {
900
+ return false;
901
+ }
902
+ }
903
+ return true;
904
+ });
905
+ return Promise.resolve({ ...ctx, candidates: filtered });
906
+ });
907
+ this.register("boost", (ctx, config) => {
908
+ const boosts = config.params?.boosts;
909
+ if (!boosts) return Promise.resolve(ctx);
910
+ const boosted = ctx.candidates.map((c) => {
911
+ let newScore = c.score;
912
+ for (const boost of boosts) {
913
+ const fieldValue = c.entry.metadata[boost.field] ?? c.entry[boost.field];
914
+ if (fieldValue === boost.value) {
915
+ newScore *= boost.factor;
916
+ }
917
+ }
918
+ return { ...c, score: newScore };
919
+ });
920
+ boosted.sort((a, b) => b.score - a.score);
921
+ return Promise.resolve({ ...ctx, candidates: boosted });
922
+ });
923
+ this.register("rerank", (ctx, config) => {
924
+ const weights = config.params?.weights;
925
+ if (!weights) return Promise.resolve(ctx);
926
+ const reranked = ctx.candidates.map((c) => {
927
+ let newScore = 0;
928
+ let totalWeight = 0;
929
+ for (const [field, weight] of Object.entries(weights)) {
930
+ if (field === "originalScore") {
931
+ newScore += c.score * weight;
932
+ } else {
933
+ const value = c.entry.metadata[field] ?? c.entry[field];
934
+ if (typeof value === "number") {
935
+ newScore += value * weight;
936
+ }
937
+ }
938
+ totalWeight += weight;
939
+ }
940
+ return {
941
+ ...c,
942
+ score: totalWeight > 0 ? newScore / totalWeight : c.score
943
+ };
944
+ });
945
+ reranked.sort((a, b) => b.score - a.score);
946
+ return Promise.resolve({ ...ctx, candidates: reranked });
947
+ });
948
+ this.register("dedupe", (ctx, config) => {
949
+ const field = config.params?.field ?? "content";
950
+ const seen = /* @__PURE__ */ new Map();
951
+ const deduped = [];
952
+ for (const candidate of ctx.candidates) {
953
+ const key = this.getDedupeKey(candidate.entry, field);
954
+ const existing = seen.get(key);
955
+ if (!existing) {
956
+ seen.set(key, candidate);
957
+ deduped.push(candidate);
958
+ } else if (candidate.score > existing.score) {
959
+ const idx = deduped.indexOf(existing);
960
+ if (idx !== -1) {
961
+ deduped[idx] = candidate;
962
+ }
963
+ seen.set(key, candidate);
964
+ }
965
+ }
966
+ return Promise.resolve({ ...ctx, candidates: deduped });
967
+ });
968
+ this.register("diversify", (ctx, config) => {
969
+ const field = config.params?.field ?? "type";
970
+ const maxPerCategory = config.params?.maxPerCategory ?? 3;
971
+ const categoryCounts = /* @__PURE__ */ new Map();
972
+ const diversified = [];
973
+ for (const candidate of ctx.candidates) {
974
+ const category = String(
975
+ candidate.entry.metadata[field] ?? candidate.entry[field] ?? "unknown"
976
+ );
977
+ const count = categoryCounts.get(category) ?? 0;
978
+ if (count < maxPerCategory) {
979
+ diversified.push(candidate);
980
+ categoryCounts.set(category, count + 1);
981
+ }
982
+ }
983
+ return Promise.resolve({ ...ctx, candidates: diversified });
984
+ });
985
+ this.register("truncate", (ctx, config) => {
986
+ const limit = config.params?.limit ?? this.config.maxCandidates;
987
+ return Promise.resolve({
988
+ ...ctx,
989
+ candidates: ctx.candidates.slice(0, limit)
990
+ });
991
+ });
992
+ this.register("enrich", async (ctx, config) => {
993
+ const enrichFn = config.params?.enrichFn;
994
+ if (!enrichFn) return ctx;
995
+ const enriched = await Promise.all(
996
+ ctx.candidates.map(async (c) => {
997
+ const enrichment = await enrichFn(c.entry);
998
+ return {
999
+ ...c,
1000
+ entry: {
1001
+ ...c.entry,
1002
+ metadata: { ...c.entry.metadata, ...enrichment }
1003
+ }
1004
+ };
1005
+ })
1006
+ );
1007
+ return { ...ctx, candidates: enriched };
1008
+ });
1009
+ }
1010
+ /**
1011
+ * Register a custom stage
1012
+ */
1013
+ register(name, stage) {
1014
+ this.stages.set(name, stage);
1015
+ }
1016
+ /**
1017
+ * Execute the pipeline
1018
+ */
1019
+ async execute(query, initialCandidates) {
1020
+ const startTime = Date.now();
1021
+ let context = {
1022
+ query,
1023
+ candidates: initialCandidates,
1024
+ metadata: {},
1025
+ timing: {}
1026
+ };
1027
+ for (const stageConfig of this.config.stages) {
1028
+ if (stageConfig.enabled === false) continue;
1029
+ const stage = this.stages.get(stageConfig.name);
1030
+ if (!stage) {
1031
+ console.warn(
1032
+ `Pipeline stage "${stageConfig.name}" not found, skipping`
1033
+ );
1034
+ continue;
1035
+ }
1036
+ const stageStart = Date.now();
1037
+ try {
1038
+ context = await this.executeWithTimeout(
1039
+ stage(context, stageConfig),
1040
+ this.config.timeout
1041
+ );
1042
+ context.timing[stageConfig.name] = Date.now() - stageStart;
1043
+ } catch (error) {
1044
+ console.error(`Pipeline stage "${stageConfig.name}" failed:`, error);
1045
+ }
1046
+ }
1047
+ if (this.config.minScore > 0) {
1048
+ context.candidates = context.candidates.filter(
1049
+ (c) => c.score >= this.config.minScore
1050
+ );
1051
+ }
1052
+ context.candidates = context.candidates.slice(0, this.config.maxCandidates);
1053
+ return {
1054
+ memories: context.candidates.map((c) => c.entry),
1055
+ scores: context.candidates.map((c) => c.score),
1056
+ totalCandidates: initialCandidates.length,
1057
+ retrievalTimeMs: Date.now() - startTime,
1058
+ strategy: "pipeline",
1059
+ metadata: {
1060
+ ...context.metadata,
1061
+ timing: context.timing,
1062
+ stagesExecuted: this.config.stages.filter((s) => s.enabled !== false).length
1063
+ }
1064
+ };
1065
+ }
1066
+ /**
1067
+ * Execute promise with timeout
1068
+ */
1069
+ async executeWithTimeout(promise, timeout) {
1070
+ return Promise.race([
1071
+ promise,
1072
+ new Promise(
1073
+ (_, reject) => setTimeout(() => reject(new Error("Stage timeout")), timeout)
1074
+ )
1075
+ ]);
1076
+ }
1077
+ /**
1078
+ * Get dedupe key for an entry
1079
+ */
1080
+ getDedupeKey(entry, field) {
1081
+ if (field === "id") return entry.id;
1082
+ if (field === "content") {
1083
+ return entry.content.toLowerCase().trim().slice(0, 200);
1084
+ }
1085
+ return String(
1086
+ entry.metadata[field] ?? entry[field] ?? entry.id
1087
+ );
1088
+ }
1089
+ /**
1090
+ * Add a stage to the pipeline
1091
+ */
1092
+ addStage(config) {
1093
+ this.config.stages.push(config);
1094
+ return this;
1095
+ }
1096
+ /**
1097
+ * Remove a stage from the pipeline
1098
+ */
1099
+ removeStage(name) {
1100
+ this.config.stages = this.config.stages.filter((s) => s.name !== name);
1101
+ return this;
1102
+ }
1103
+ /**
1104
+ * Update pipeline configuration
1105
+ */
1106
+ configure(config) {
1107
+ this.config = { ...this.config, ...config };
1108
+ }
1109
+ /**
1110
+ * Get current configuration
1111
+ */
1112
+ getConfig() {
1113
+ return { ...this.config };
1114
+ }
1115
+ /**
1116
+ * Get registered stage names
1117
+ */
1118
+ getStageNames() {
1119
+ return Array.from(this.stages.keys());
1120
+ }
1121
+ };
1122
+ var PipelineBuilder = class {
1123
+ stages = [];
1124
+ maxCandidates = 100;
1125
+ minScore = 0;
1126
+ timeout = 3e4;
1127
+ customStages = /* @__PURE__ */ new Map();
1128
+ /**
1129
+ * Add a filter stage
1130
+ */
1131
+ filter(filters) {
1132
+ this.stages.push({
1133
+ name: "filter",
1134
+ params: { filters }
1135
+ });
1136
+ return this;
1137
+ }
1138
+ /**
1139
+ * Add a boost stage
1140
+ */
1141
+ boost(boosts) {
1142
+ this.stages.push({
1143
+ name: "boost",
1144
+ params: { boosts }
1145
+ });
1146
+ return this;
1147
+ }
1148
+ /**
1149
+ * Add a rerank stage
1150
+ */
1151
+ rerank(weights) {
1152
+ this.stages.push({
1153
+ name: "rerank",
1154
+ params: { weights }
1155
+ });
1156
+ return this;
1157
+ }
1158
+ /**
1159
+ * Add a dedupe stage
1160
+ */
1161
+ dedupe(field = "content", similarity = 0.95) {
1162
+ this.stages.push({
1163
+ name: "dedupe",
1164
+ params: { field, similarity }
1165
+ });
1166
+ return this;
1167
+ }
1168
+ /**
1169
+ * Add a diversify stage
1170
+ */
1171
+ diversify(field = "type", maxPerCategory = 3) {
1172
+ this.stages.push({
1173
+ name: "diversify",
1174
+ params: { field, maxPerCategory }
1175
+ });
1176
+ return this;
1177
+ }
1178
+ /**
1179
+ * Add a truncate stage
1180
+ */
1181
+ truncate(limit) {
1182
+ this.stages.push({
1183
+ name: "truncate",
1184
+ params: { limit }
1185
+ });
1186
+ return this;
1187
+ }
1188
+ /**
1189
+ * Add an enrich stage
1190
+ */
1191
+ enrich(enrichFn) {
1192
+ this.stages.push({
1193
+ name: "enrich",
1194
+ params: { enrichFn }
1195
+ });
1196
+ return this;
1197
+ }
1198
+ /**
1199
+ * Add a custom stage
1200
+ */
1201
+ custom(name, stage, params) {
1202
+ this.customStages.set(name, stage);
1203
+ this.stages.push({ name, params });
1204
+ return this;
1205
+ }
1206
+ /**
1207
+ * Set maximum candidates
1208
+ */
1209
+ withMaxCandidates(max) {
1210
+ this.maxCandidates = max;
1211
+ return this;
1212
+ }
1213
+ /**
1214
+ * Set minimum score
1215
+ */
1216
+ withMinScore(min) {
1217
+ this.minScore = min;
1218
+ return this;
1219
+ }
1220
+ /**
1221
+ * Set timeout
1222
+ */
1223
+ withTimeout(ms) {
1224
+ this.timeout = ms;
1225
+ return this;
1226
+ }
1227
+ /**
1228
+ * Build the pipeline
1229
+ */
1230
+ build() {
1231
+ const pipeline = new RetrievalPipeline({
1232
+ stages: this.stages,
1233
+ maxCandidates: this.maxCandidates,
1234
+ minScore: this.minScore,
1235
+ timeout: this.timeout
1236
+ });
1237
+ for (const [name, stage] of this.customStages) {
1238
+ pipeline.register(name, stage);
1239
+ }
1240
+ return pipeline;
1241
+ }
1242
+ };
1243
+ function createPipelineBuilder() {
1244
+ return new PipelineBuilder();
1245
+ }
1246
+ function createRetrievalPipeline(config) {
1247
+ return new RetrievalPipeline(config);
1248
+ }
1249
+ // Annotate the CommonJS export names for ESM import in node:
1250
+ 0 && (module.exports = {
1251
+ HybridRetrieval,
1252
+ PipelineBuilder,
1253
+ RetrievalPipeline,
1254
+ SemanticRetrieval,
1255
+ TemporalRetrieval,
1256
+ TimeWindows,
1257
+ createHybridRetrieval,
1258
+ createPipelineBuilder,
1259
+ createRetrievalPipeline,
1260
+ createSemanticRetrieval,
1261
+ createTemporalRetrieval
1262
+ });