@aitytech/agentkits-memory 1.0.0 → 2.0.0

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -149
  3. package/assets/agentkits-memory-add-memory.png +0 -0
  4. package/assets/agentkits-memory-memory-detail.png +0 -0
  5. package/assets/agentkits-memory-memory-list.png +0 -0
  6. package/assets/logo.svg +24 -0
  7. package/dist/better-sqlite3-backend.d.ts +192 -0
  8. package/dist/better-sqlite3-backend.d.ts.map +1 -0
  9. package/dist/better-sqlite3-backend.js +801 -0
  10. package/dist/better-sqlite3-backend.js.map +1 -0
  11. package/dist/cli/save.js +0 -0
  12. package/dist/cli/setup.d.ts +6 -2
  13. package/dist/cli/setup.d.ts.map +1 -1
  14. package/dist/cli/setup.js +289 -42
  15. package/dist/cli/setup.js.map +1 -1
  16. package/dist/cli/viewer.js +25 -56
  17. package/dist/cli/viewer.js.map +1 -1
  18. package/dist/cli/web-viewer.d.ts +14 -0
  19. package/dist/cli/web-viewer.d.ts.map +1 -0
  20. package/dist/cli/web-viewer.js +1769 -0
  21. package/dist/cli/web-viewer.js.map +1 -0
  22. package/dist/embeddings/embedding-cache.d.ts +131 -0
  23. package/dist/embeddings/embedding-cache.d.ts.map +1 -0
  24. package/dist/embeddings/embedding-cache.js +217 -0
  25. package/dist/embeddings/embedding-cache.js.map +1 -0
  26. package/dist/embeddings/index.d.ts +11 -0
  27. package/dist/embeddings/index.d.ts.map +1 -0
  28. package/dist/embeddings/index.js +11 -0
  29. package/dist/embeddings/index.js.map +1 -0
  30. package/dist/embeddings/local-embeddings.d.ts +140 -0
  31. package/dist/embeddings/local-embeddings.d.ts.map +1 -0
  32. package/dist/embeddings/local-embeddings.js +293 -0
  33. package/dist/embeddings/local-embeddings.js.map +1 -0
  34. package/dist/hooks/context.d.ts +6 -1
  35. package/dist/hooks/context.d.ts.map +1 -1
  36. package/dist/hooks/context.js +12 -2
  37. package/dist/hooks/context.js.map +1 -1
  38. package/dist/hooks/observation.d.ts +6 -1
  39. package/dist/hooks/observation.d.ts.map +1 -1
  40. package/dist/hooks/observation.js +12 -2
  41. package/dist/hooks/observation.js.map +1 -1
  42. package/dist/hooks/service.d.ts +1 -6
  43. package/dist/hooks/service.d.ts.map +1 -1
  44. package/dist/hooks/service.js +33 -85
  45. package/dist/hooks/service.js.map +1 -1
  46. package/dist/hooks/session-init.d.ts +6 -1
  47. package/dist/hooks/session-init.d.ts.map +1 -1
  48. package/dist/hooks/session-init.js +12 -2
  49. package/dist/hooks/session-init.js.map +1 -1
  50. package/dist/hooks/summarize.d.ts +6 -1
  51. package/dist/hooks/summarize.d.ts.map +1 -1
  52. package/dist/hooks/summarize.js +12 -2
  53. package/dist/hooks/summarize.js.map +1 -1
  54. package/dist/index.d.ts +10 -17
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +172 -94
  57. package/dist/index.js.map +1 -1
  58. package/dist/mcp/server.js +17 -3
  59. package/dist/mcp/server.js.map +1 -1
  60. package/dist/migration.js +3 -3
  61. package/dist/migration.js.map +1 -1
  62. package/dist/search/hybrid-search.d.ts +262 -0
  63. package/dist/search/hybrid-search.d.ts.map +1 -0
  64. package/dist/search/hybrid-search.js +688 -0
  65. package/dist/search/hybrid-search.js.map +1 -0
  66. package/dist/search/index.d.ts +13 -0
  67. package/dist/search/index.d.ts.map +1 -0
  68. package/dist/search/index.js +13 -0
  69. package/dist/search/index.js.map +1 -0
  70. package/dist/search/token-economics.d.ts +161 -0
  71. package/dist/search/token-economics.d.ts.map +1 -0
  72. package/dist/search/token-economics.js +239 -0
  73. package/dist/search/token-economics.js.map +1 -0
  74. package/dist/types.d.ts +0 -68
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/types.js.map +1 -1
  77. package/package.json +23 -8
  78. package/src/__tests__/better-sqlite3-backend.test.ts +1466 -0
  79. package/src/__tests__/cache-manager.test.ts +499 -0
  80. package/src/__tests__/embedding-integration.test.ts +481 -0
  81. package/src/__tests__/hnsw-index.test.ts +727 -0
  82. package/src/__tests__/index.test.ts +432 -0
  83. package/src/better-sqlite3-backend.ts +1000 -0
  84. package/src/cli/setup.ts +358 -47
  85. package/src/cli/viewer.ts +28 -63
  86. package/src/cli/web-viewer.ts +1956 -0
  87. package/src/embeddings/__tests__/embedding-cache.test.ts +269 -0
  88. package/src/embeddings/__tests__/local-embeddings.test.ts +495 -0
  89. package/src/embeddings/embedding-cache.ts +318 -0
  90. package/src/embeddings/index.ts +20 -0
  91. package/src/embeddings/local-embeddings.ts +419 -0
  92. package/src/hooks/__tests__/handlers.test.ts +58 -17
  93. package/src/hooks/__tests__/integration.test.ts +77 -26
  94. package/src/hooks/context.ts +13 -2
  95. package/src/hooks/observation.ts +13 -2
  96. package/src/hooks/service.ts +39 -100
  97. package/src/hooks/session-init.ts +13 -2
  98. package/src/hooks/summarize.ts +13 -2
  99. package/src/index.ts +210 -116
  100. package/src/mcp/server.ts +20 -3
  101. package/src/search/__tests__/hybrid-search.test.ts +669 -0
  102. package/src/search/__tests__/token-economics.test.ts +276 -0
  103. package/src/search/hybrid-search.ts +968 -0
  104. package/src/search/index.ts +29 -0
  105. package/src/search/token-economics.ts +367 -0
  106. package/src/types.ts +0 -96
  107. package/src/__tests__/sqljs-backend.test.ts +0 -410
  108. package/src/migration.ts +0 -574
  109. package/src/sql.js.d.ts +0 -70
  110. package/src/sqljs-backend.ts +0 -789
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Persistent Embedding Cache
3
+ *
4
+ * SQLite-backed cache for embeddings to avoid re-computing
5
+ * embeddings for the same text content.
6
+ *
7
+ * @module @aitytech/agentkits-memory/embeddings
8
+ */
9
+
10
+ import { createHash } from 'crypto';
11
+ import type { Database as BetterDatabase } from 'better-sqlite3';
12
+
13
+ /**
14
+ * Embedding cache configuration
15
+ */
16
+ export interface PersistentEmbeddingCacheConfig {
17
+ /** Maximum number of entries (default: 10000) */
18
+ maxSize?: number;
19
+
20
+ /** TTL in milliseconds (default: 7 days) */
21
+ ttlMs?: number;
22
+
23
+ /** Vector dimensions (default: 384) */
24
+ dimensions?: number;
25
+ }
26
+
27
+ /**
28
+ * Cached embedding entry
29
+ */
30
+ export interface CachedEmbedding {
31
+ /** Content hash (primary key) */
32
+ hash: string;
33
+
34
+ /** The embedding vector */
35
+ embedding: Float32Array;
36
+
37
+ /** When the entry was created */
38
+ createdAt: number;
39
+
40
+ /** When the entry expires */
41
+ expiresAt: number;
42
+
43
+ /** Access count */
44
+ accessCount: number;
45
+ }
46
+
47
+ /**
48
+ * Cache statistics
49
+ */
50
+ export interface PersistentEmbeddingCacheStats {
51
+ /** Total entries in cache */
52
+ size: number;
53
+
54
+ /** Cache hits */
55
+ hits: number;
56
+
57
+ /** Cache misses */
58
+ misses: number;
59
+
60
+ /** Hit rate (0-1) */
61
+ hitRate: number;
62
+
63
+ /** Total bytes used */
64
+ bytesUsed: number;
65
+
66
+ /** Oldest entry age in ms */
67
+ oldestEntryAge: number;
68
+ }
69
+
70
+ /**
71
+ * Persistent Embedding Cache using SQLite
72
+ */
73
+ export class PersistentEmbeddingCache {
74
+ private db: BetterDatabase;
75
+ private config: Required<PersistentEmbeddingCacheConfig>;
76
+ private stats = {
77
+ hits: 0,
78
+ misses: 0,
79
+ };
80
+
81
+ constructor(db: BetterDatabase, config: PersistentEmbeddingCacheConfig = {}) {
82
+ this.db = db;
83
+ this.config = {
84
+ maxSize: config.maxSize || 10000,
85
+ ttlMs: config.ttlMs || 7 * 24 * 60 * 60 * 1000, // 7 days
86
+ dimensions: config.dimensions || 384,
87
+ };
88
+
89
+ this.initializeSchema();
90
+ }
91
+
92
+ /**
93
+ * Initialize the cache table
94
+ */
95
+ private initializeSchema(): void {
96
+ this.db.exec(`
97
+ CREATE TABLE IF NOT EXISTS embedding_cache (
98
+ hash TEXT PRIMARY KEY,
99
+ embedding BLOB NOT NULL,
100
+ created_at INTEGER NOT NULL,
101
+ expires_at INTEGER NOT NULL,
102
+ access_count INTEGER DEFAULT 1,
103
+ last_accessed_at INTEGER NOT NULL
104
+ )
105
+ `);
106
+
107
+ this.db.exec(`
108
+ CREATE INDEX IF NOT EXISTS idx_embedding_cache_expires
109
+ ON embedding_cache(expires_at)
110
+ `);
111
+
112
+ this.db.exec(`
113
+ CREATE INDEX IF NOT EXISTS idx_embedding_cache_accessed
114
+ ON embedding_cache(last_accessed_at)
115
+ `);
116
+ }
117
+
118
+ /**
119
+ * Hash content for cache key
120
+ */
121
+ private hashContent(content: string): string {
122
+ return createHash('sha256').update(content).digest('hex').substring(0, 32);
123
+ }
124
+
125
+ /**
126
+ * Convert Float32Array to Buffer for storage
127
+ */
128
+ private toBuffer(embedding: Float32Array): Buffer {
129
+ return Buffer.from(embedding.buffer);
130
+ }
131
+
132
+ /**
133
+ * Convert Buffer back to Float32Array
134
+ */
135
+ private fromBuffer(buffer: Buffer): Float32Array {
136
+ return new Float32Array(buffer.buffer.slice(
137
+ buffer.byteOffset,
138
+ buffer.byteOffset + buffer.byteLength
139
+ ));
140
+ }
141
+
142
+ /**
143
+ * Get embedding from cache
144
+ */
145
+ get(content: string): Float32Array | null {
146
+ const hash = this.hashContent(content);
147
+ const now = Date.now();
148
+
149
+ const row = this.db.prepare(`
150
+ SELECT embedding, expires_at
151
+ FROM embedding_cache
152
+ WHERE hash = ? AND expires_at > ?
153
+ `).get(hash, now) as { embedding: Buffer; expires_at: number } | undefined;
154
+
155
+ if (row) {
156
+ // Update access stats
157
+ this.db.prepare(`
158
+ UPDATE embedding_cache
159
+ SET access_count = access_count + 1,
160
+ last_accessed_at = ?
161
+ WHERE hash = ?
162
+ `).run(now, hash);
163
+
164
+ this.stats.hits++;
165
+ return this.fromBuffer(row.embedding);
166
+ }
167
+
168
+ this.stats.misses++;
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * Store embedding in cache
174
+ */
175
+ set(content: string, embedding: Float32Array): void {
176
+ const hash = this.hashContent(content);
177
+ const now = Date.now();
178
+ const expiresAt = now + this.config.ttlMs;
179
+
180
+ // Check if we need to evict entries
181
+ this.evictIfNeeded();
182
+
183
+ // Insert or replace
184
+ this.db.prepare(`
185
+ INSERT OR REPLACE INTO embedding_cache
186
+ (hash, embedding, created_at, expires_at, access_count, last_accessed_at)
187
+ VALUES (?, ?, ?, ?, 1, ?)
188
+ `).run(hash, this.toBuffer(embedding), now, expiresAt, now);
189
+ }
190
+
191
+ /**
192
+ * Check if content is in cache
193
+ */
194
+ has(content: string): boolean {
195
+ const hash = this.hashContent(content);
196
+ const now = Date.now();
197
+
198
+ const row = this.db.prepare(`
199
+ SELECT 1 FROM embedding_cache
200
+ WHERE hash = ? AND expires_at > ?
201
+ `).get(hash, now);
202
+
203
+ return !!row;
204
+ }
205
+
206
+ /**
207
+ * Delete expired entries
208
+ */
209
+ evictExpired(): number {
210
+ const now = Date.now();
211
+ const result = this.db.prepare(`DELETE FROM embedding_cache WHERE expires_at <= ?`).run(now);
212
+ return result.changes;
213
+ }
214
+
215
+ /**
216
+ * Evict oldest entries if over capacity
217
+ */
218
+ private evictIfNeeded(): void {
219
+ const countRow = this.db.prepare(`SELECT COUNT(*) as count FROM embedding_cache`).get() as { count: number };
220
+
221
+ if (countRow.count >= this.config.maxSize) {
222
+ // Delete oldest 10% of entries
223
+ const deleteCount = Math.max(1, Math.floor(this.config.maxSize * 0.1));
224
+ this.db.prepare(`
225
+ DELETE FROM embedding_cache
226
+ WHERE hash IN (
227
+ SELECT hash FROM embedding_cache
228
+ ORDER BY last_accessed_at ASC
229
+ LIMIT ?
230
+ )
231
+ `).run(deleteCount);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Clear all cache entries
237
+ */
238
+ clear(): void {
239
+ this.db.exec(`DELETE FROM embedding_cache`);
240
+ this.stats.hits = 0;
241
+ this.stats.misses = 0;
242
+ }
243
+
244
+ /**
245
+ * Get cache statistics
246
+ */
247
+ getStats(): PersistentEmbeddingCacheStats {
248
+ const now = Date.now();
249
+
250
+ // Get size and bytes
251
+ const result = this.db.prepare(`
252
+ SELECT
253
+ COUNT(*) as size,
254
+ COALESCE(SUM(LENGTH(embedding)), 0) as bytes,
255
+ MIN(created_at) as oldest
256
+ FROM embedding_cache
257
+ WHERE expires_at > ?
258
+ `).get(now) as {
259
+ size: number;
260
+ bytes: number;
261
+ oldest: number | null;
262
+ };
263
+
264
+ const totalRequests = this.stats.hits + this.stats.misses;
265
+
266
+ return {
267
+ size: result.size,
268
+ hits: this.stats.hits,
269
+ misses: this.stats.misses,
270
+ hitRate: totalRequests > 0 ? this.stats.hits / totalRequests : 0,
271
+ bytesUsed: result.bytes,
272
+ oldestEntryAge: result.oldest ? now - result.oldest : 0,
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Get all cached embeddings (for warm-starting HNSW index)
278
+ */
279
+ getAllEmbeddings(): Array<{ hash: string; embedding: Float32Array }> {
280
+ const now = Date.now();
281
+
282
+ const rows = this.db.prepare(`
283
+ SELECT hash, embedding
284
+ FROM embedding_cache
285
+ WHERE expires_at > ?
286
+ `).all(now) as { hash: string; embedding: Buffer }[];
287
+
288
+ return rows.map(row => ({
289
+ hash: row.hash,
290
+ embedding: this.fromBuffer(row.embedding),
291
+ }));
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Create a persistent embedding cache
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * import Database from 'better-sqlite3';
301
+ * import { createPersistentEmbeddingCache } from '@aitytech/agentkits-memory/embeddings';
302
+ *
303
+ * const db = new Database(':memory:');
304
+ *
305
+ * const cache = createPersistentEmbeddingCache(db, {
306
+ * maxSize: 10000,
307
+ * ttlMs: 7 * 24 * 60 * 60 * 1000, // 7 days
308
+ * });
309
+ * ```
310
+ */
311
+ export function createPersistentEmbeddingCache(
312
+ db: BetterDatabase,
313
+ config?: PersistentEmbeddingCacheConfig
314
+ ): PersistentEmbeddingCache {
315
+ return new PersistentEmbeddingCache(db, config);
316
+ }
317
+
318
+ export default PersistentEmbeddingCache;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Local Embeddings Module
3
+ *
4
+ * 100% offline text embeddings for semantic search.
5
+ * Uses Transformers.js (WASM) - no external API calls.
6
+ *
7
+ * @module @aitytech/agentkits-memory/embeddings
8
+ */
9
+
10
+ export {
11
+ LocalEmbeddingsService,
12
+ createLocalEmbeddings,
13
+ createEmbeddingGenerator,
14
+ type LocalEmbeddingsConfig,
15
+ type EmbeddingProvider,
16
+ type EmbeddingResult,
17
+ type EmbeddingsStats,
18
+ } from './local-embeddings.js';
19
+
20
+ export { PersistentEmbeddingCache, createPersistentEmbeddingCache } from './embedding-cache.js';