@aitytech/agentkits-memory 1.0.1 → 2.0.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 (105) hide show
  1. package/README.md +54 -5
  2. package/dist/better-sqlite3-backend.d.ts +192 -0
  3. package/dist/better-sqlite3-backend.d.ts.map +1 -0
  4. package/dist/better-sqlite3-backend.js +801 -0
  5. package/dist/better-sqlite3-backend.js.map +1 -0
  6. package/dist/cli/save.js +0 -0
  7. package/dist/cli/setup.d.ts +6 -2
  8. package/dist/cli/setup.d.ts.map +1 -1
  9. package/dist/cli/setup.js +289 -42
  10. package/dist/cli/setup.js.map +1 -1
  11. package/dist/cli/viewer.js +25 -56
  12. package/dist/cli/viewer.js.map +1 -1
  13. package/dist/cli/web-viewer.d.ts +2 -1
  14. package/dist/cli/web-viewer.d.ts.map +1 -1
  15. package/dist/cli/web-viewer.js +791 -141
  16. package/dist/cli/web-viewer.js.map +1 -1
  17. package/dist/embeddings/embedding-cache.d.ts +131 -0
  18. package/dist/embeddings/embedding-cache.d.ts.map +1 -0
  19. package/dist/embeddings/embedding-cache.js +217 -0
  20. package/dist/embeddings/embedding-cache.js.map +1 -0
  21. package/dist/embeddings/index.d.ts +11 -0
  22. package/dist/embeddings/index.d.ts.map +1 -0
  23. package/dist/embeddings/index.js +11 -0
  24. package/dist/embeddings/index.js.map +1 -0
  25. package/dist/embeddings/local-embeddings.d.ts +140 -0
  26. package/dist/embeddings/local-embeddings.d.ts.map +1 -0
  27. package/dist/embeddings/local-embeddings.js +293 -0
  28. package/dist/embeddings/local-embeddings.js.map +1 -0
  29. package/dist/hooks/context.d.ts +6 -1
  30. package/dist/hooks/context.d.ts.map +1 -1
  31. package/dist/hooks/context.js +12 -2
  32. package/dist/hooks/context.js.map +1 -1
  33. package/dist/hooks/observation.d.ts +6 -1
  34. package/dist/hooks/observation.d.ts.map +1 -1
  35. package/dist/hooks/observation.js +12 -2
  36. package/dist/hooks/observation.js.map +1 -1
  37. package/dist/hooks/service.d.ts +1 -6
  38. package/dist/hooks/service.d.ts.map +1 -1
  39. package/dist/hooks/service.js +33 -85
  40. package/dist/hooks/service.js.map +1 -1
  41. package/dist/hooks/session-init.d.ts +6 -1
  42. package/dist/hooks/session-init.d.ts.map +1 -1
  43. package/dist/hooks/session-init.js +12 -2
  44. package/dist/hooks/session-init.js.map +1 -1
  45. package/dist/hooks/summarize.d.ts +6 -1
  46. package/dist/hooks/summarize.d.ts.map +1 -1
  47. package/dist/hooks/summarize.js +12 -2
  48. package/dist/hooks/summarize.js.map +1 -1
  49. package/dist/index.d.ts +10 -17
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +172 -94
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp/server.js +17 -3
  54. package/dist/mcp/server.js.map +1 -1
  55. package/dist/migration.js +3 -3
  56. package/dist/migration.js.map +1 -1
  57. package/dist/search/hybrid-search.d.ts +262 -0
  58. package/dist/search/hybrid-search.d.ts.map +1 -0
  59. package/dist/search/hybrid-search.js +688 -0
  60. package/dist/search/hybrid-search.js.map +1 -0
  61. package/dist/search/index.d.ts +13 -0
  62. package/dist/search/index.d.ts.map +1 -0
  63. package/dist/search/index.js +13 -0
  64. package/dist/search/index.js.map +1 -0
  65. package/dist/search/token-economics.d.ts +161 -0
  66. package/dist/search/token-economics.d.ts.map +1 -0
  67. package/dist/search/token-economics.js +239 -0
  68. package/dist/search/token-economics.js.map +1 -0
  69. package/dist/types.d.ts +0 -68
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/package.json +6 -4
  73. package/src/__tests__/better-sqlite3-backend.test.ts +1466 -0
  74. package/src/__tests__/cache-manager.test.ts +499 -0
  75. package/src/__tests__/embedding-integration.test.ts +481 -0
  76. package/src/__tests__/hnsw-index.test.ts +727 -0
  77. package/src/__tests__/index.test.ts +432 -0
  78. package/src/better-sqlite3-backend.ts +1000 -0
  79. package/src/cli/setup.ts +358 -47
  80. package/src/cli/viewer.ts +28 -63
  81. package/src/cli/web-viewer.ts +936 -182
  82. package/src/embeddings/__tests__/embedding-cache.test.ts +269 -0
  83. package/src/embeddings/__tests__/local-embeddings.test.ts +495 -0
  84. package/src/embeddings/embedding-cache.ts +318 -0
  85. package/src/embeddings/index.ts +20 -0
  86. package/src/embeddings/local-embeddings.ts +419 -0
  87. package/src/hooks/__tests__/handlers.test.ts +58 -17
  88. package/src/hooks/__tests__/integration.test.ts +77 -26
  89. package/src/hooks/context.ts +13 -2
  90. package/src/hooks/observation.ts +13 -2
  91. package/src/hooks/service.ts +39 -100
  92. package/src/hooks/session-init.ts +13 -2
  93. package/src/hooks/summarize.ts +13 -2
  94. package/src/index.ts +210 -116
  95. package/src/mcp/server.ts +20 -3
  96. package/src/search/__tests__/hybrid-search.test.ts +669 -0
  97. package/src/search/__tests__/token-economics.test.ts +276 -0
  98. package/src/search/hybrid-search.ts +968 -0
  99. package/src/search/index.ts +29 -0
  100. package/src/search/token-economics.ts +367 -0
  101. package/src/types.ts +0 -96
  102. package/src/__tests__/sqljs-backend.test.ts +0 -410
  103. package/src/migration.ts +0 -574
  104. package/src/sql.js.d.ts +0 -70
  105. package/src/sqljs-backend.ts +0 -789
@@ -0,0 +1,269 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import Database from 'better-sqlite3';
3
+ import type { Database as BetterDatabase } from 'better-sqlite3';
4
+ import {
5
+ PersistentEmbeddingCache,
6
+ createPersistentEmbeddingCache,
7
+ } from '../embedding-cache.js';
8
+
9
+ describe('PersistentEmbeddingCache', () => {
10
+ let db: BetterDatabase;
11
+ let cache: PersistentEmbeddingCache;
12
+
13
+ beforeEach(() => {
14
+ db = new Database(':memory:');
15
+ cache = new PersistentEmbeddingCache(db);
16
+ });
17
+
18
+ afterEach(() => {
19
+ db.close();
20
+ });
21
+
22
+ describe('basic operations', () => {
23
+ it('should store and retrieve embeddings', () => {
24
+ const embedding = new Float32Array([0.1, 0.2, 0.3, 0.4, 0.5]);
25
+ cache.set('Test content', embedding);
26
+
27
+ const retrieved = cache.get('Test content');
28
+
29
+ expect(retrieved).not.toBeNull();
30
+ expect(retrieved?.length).toBe(5);
31
+ for (let i = 0; i < embedding.length; i++) {
32
+ expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
33
+ }
34
+ });
35
+
36
+ it('should return null for missing content', () => {
37
+ const retrieved = cache.get('Non-existent content');
38
+ expect(retrieved).toBeNull();
39
+ });
40
+
41
+ it('should check if content exists', () => {
42
+ const embedding = new Float32Array([0.1, 0.2, 0.3]);
43
+ cache.set('Existing content', embedding);
44
+
45
+ expect(cache.has('Existing content')).toBe(true);
46
+ expect(cache.has('Missing content')).toBe(false);
47
+ });
48
+
49
+ it('should overwrite existing embeddings', () => {
50
+ const embedding1 = new Float32Array([0.1, 0.2, 0.3]);
51
+ const embedding2 = new Float32Array([0.4, 0.5, 0.6]);
52
+
53
+ cache.set('Content', embedding1);
54
+ cache.set('Content', embedding2);
55
+
56
+ const retrieved = cache.get('Content');
57
+ for (let i = 0; i < embedding2.length; i++) {
58
+ expect(retrieved![i]).toBeCloseTo(embedding2[i], 5);
59
+ }
60
+ });
61
+
62
+ it('should clear all entries', () => {
63
+ cache.set('Content 1', new Float32Array([0.1]));
64
+ cache.set('Content 2', new Float32Array([0.2]));
65
+
66
+ cache.clear();
67
+
68
+ expect(cache.get('Content 1')).toBeNull();
69
+ expect(cache.get('Content 2')).toBeNull();
70
+ });
71
+ });
72
+
73
+ describe('expiration', () => {
74
+ it('should expire entries after TTL', async () => {
75
+ const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 100 });
76
+ shortTtlCache.set('Expiring content', new Float32Array([0.1, 0.2]));
77
+
78
+ // Should exist immediately
79
+ expect(shortTtlCache.get('Expiring content')).not.toBeNull();
80
+
81
+ // Wait for expiration
82
+ await new Promise((resolve) => setTimeout(resolve, 150));
83
+
84
+ // Should be expired
85
+ expect(shortTtlCache.get('Expiring content')).toBeNull();
86
+ });
87
+
88
+ it('should evict expired entries', async () => {
89
+ const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 50 });
90
+ shortTtlCache.set('Expiring', new Float32Array([0.1]));
91
+
92
+ await new Promise((resolve) => setTimeout(resolve, 100));
93
+
94
+ const evicted = shortTtlCache.evictExpired();
95
+ expect(evicted).toBe(1);
96
+ });
97
+ });
98
+
99
+ describe('capacity management', () => {
100
+ it('should evict entries when over capacity', () => {
101
+ const smallCache = new PersistentEmbeddingCache(db, { maxSize: 5 });
102
+
103
+ // Add 5 entries (at capacity)
104
+ for (let i = 1; i <= 5; i++) {
105
+ smallCache.set(`Entry ${i}`, new Float32Array([i * 0.1]));
106
+ }
107
+
108
+ // Verify all 5 exist
109
+ const stats1 = smallCache.getStats();
110
+ expect(stats1.size).toBe(5);
111
+
112
+ // Add 2 more entries, should trigger eviction
113
+ smallCache.set('Entry 6', new Float32Array([0.6]));
114
+ smallCache.set('Entry 7', new Float32Array([0.7]));
115
+
116
+ // New entries should exist
117
+ expect(smallCache.get('Entry 6')).not.toBeNull();
118
+ expect(smallCache.get('Entry 7')).not.toBeNull();
119
+
120
+ // Size should not exceed maxSize
121
+ const stats2 = smallCache.getStats();
122
+ expect(stats2.size).toBeLessThanOrEqual(5);
123
+ });
124
+ });
125
+
126
+ describe('statistics', () => {
127
+ it('should track hits and misses', () => {
128
+ cache.set('Content', new Float32Array([0.1, 0.2, 0.3]));
129
+
130
+ cache.get('Content'); // Hit
131
+ cache.get('Content'); // Hit
132
+ cache.get('Missing'); // Miss
133
+
134
+ const stats = cache.getStats();
135
+
136
+ expect(stats.hits).toBe(2);
137
+ expect(stats.misses).toBe(1);
138
+ expect(stats.hitRate).toBeCloseTo(2 / 3);
139
+ });
140
+
141
+ it('should track size and bytes', () => {
142
+ cache.set('Content 1', new Float32Array([0.1, 0.2, 0.3])); // 12 bytes
143
+ cache.set('Content 2', new Float32Array([0.4, 0.5])); // 8 bytes
144
+
145
+ const stats = cache.getStats();
146
+
147
+ expect(stats.size).toBe(2);
148
+ expect(stats.bytesUsed).toBeGreaterThan(0);
149
+ });
150
+
151
+ it('should reset stats on clear', () => {
152
+ cache.set('Content', new Float32Array([0.1]));
153
+ cache.get('Content');
154
+
155
+ cache.clear();
156
+
157
+ const stats = cache.getStats();
158
+ expect(stats.hits).toBe(0);
159
+ expect(stats.misses).toBe(0);
160
+ });
161
+ });
162
+
163
+ describe('getAllEmbeddings', () => {
164
+ it('should return all non-expired embeddings', () => {
165
+ cache.set('Content 1', new Float32Array([0.1, 0.2]));
166
+ cache.set('Content 2', new Float32Array([0.3, 0.4]));
167
+ cache.set('Content 3', new Float32Array([0.5, 0.6]));
168
+
169
+ const all = cache.getAllEmbeddings();
170
+
171
+ expect(all.length).toBe(3);
172
+ all.forEach((item) => {
173
+ expect(item.hash).toBeDefined();
174
+ expect(item.embedding).toBeInstanceOf(Float32Array);
175
+ expect(item.embedding.length).toBe(2);
176
+ });
177
+ });
178
+
179
+ it('should not include expired embeddings', async () => {
180
+ const shortTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 50 });
181
+ shortTtlCache.set('Expiring', new Float32Array([0.1]));
182
+ shortTtlCache.set('Not expiring', new Float32Array([0.2]));
183
+
184
+ // Set longer TTL for second entry
185
+ const longTtlCache = new PersistentEmbeddingCache(db, { ttlMs: 10000 });
186
+ longTtlCache.set('Long TTL', new Float32Array([0.3]));
187
+
188
+ await new Promise((resolve) => setTimeout(resolve, 100));
189
+
190
+ const all = longTtlCache.getAllEmbeddings();
191
+
192
+ // Only non-expired entries should be returned
193
+ expect(all.length).toBe(1);
194
+ });
195
+ });
196
+
197
+ describe('createPersistentEmbeddingCache factory', () => {
198
+ it('should create cache with default config', () => {
199
+ const cache = createPersistentEmbeddingCache(db);
200
+ expect(cache).toBeInstanceOf(PersistentEmbeddingCache);
201
+ });
202
+
203
+ it('should accept custom config', () => {
204
+ const cache = createPersistentEmbeddingCache(db, {
205
+ maxSize: 500,
206
+ ttlMs: 1000,
207
+ dimensions: 768,
208
+ });
209
+
210
+ expect(cache).toBeInstanceOf(PersistentEmbeddingCache);
211
+ });
212
+ });
213
+
214
+ describe('content hashing', () => {
215
+ it('should use consistent hashing', () => {
216
+ const embedding = new Float32Array([0.1, 0.2, 0.3]);
217
+
218
+ cache.set('Same content', embedding);
219
+
220
+ // Different cache instance, same DB
221
+ const cache2 = new PersistentEmbeddingCache(db);
222
+ const retrieved = cache2.get('Same content');
223
+
224
+ expect(retrieved).not.toBeNull();
225
+ for (let i = 0; i < embedding.length; i++) {
226
+ expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
227
+ }
228
+ });
229
+
230
+ it('should differentiate similar content', () => {
231
+ const embeddingA = new Float32Array([0.1]);
232
+ const embeddingB = new Float32Array([0.2]);
233
+ cache.set('Content A', embeddingA);
234
+ cache.set('Content B', embeddingB);
235
+
236
+ expect(cache.get('Content A')![0]).toBeCloseTo(0.1, 5);
237
+ expect(cache.get('Content B')![0]).toBeCloseTo(0.2, 5);
238
+ });
239
+ });
240
+
241
+ describe('large embeddings', () => {
242
+ it('should handle 384-dimension embeddings', () => {
243
+ const embedding = new Float32Array(384);
244
+ for (let i = 0; i < 384; i++) {
245
+ embedding[i] = Math.random();
246
+ }
247
+
248
+ cache.set('Large embedding', embedding);
249
+ const retrieved = cache.get('Large embedding');
250
+
251
+ expect(retrieved?.length).toBe(384);
252
+ for (let i = 0; i < 384; i++) {
253
+ expect(retrieved![i]).toBeCloseTo(embedding[i], 5);
254
+ }
255
+ });
256
+
257
+ it('should handle 768-dimension embeddings', () => {
258
+ const embedding = new Float32Array(768);
259
+ for (let i = 0; i < 768; i++) {
260
+ embedding[i] = Math.random();
261
+ }
262
+
263
+ cache.set('Larger embedding', embedding);
264
+ const retrieved = cache.get('Larger embedding');
265
+
266
+ expect(retrieved?.length).toBe(768);
267
+ });
268
+ });
269
+ });