@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,29 @@
1
+ /**
2
+ * Search Module
3
+ *
4
+ * Enterprise-grade search capabilities for AgentKits Memory:
5
+ * - Hybrid search (FTS5 keyword + vector semantic)
6
+ * - 3-layer token-efficient retrieval
7
+ * - Token economics tracking
8
+ *
9
+ * @module @aitytech/agentkits-memory/search
10
+ */
11
+
12
+ export {
13
+ HybridSearchEngine,
14
+ createHybridSearchEngine,
15
+ type HybridSearchConfig,
16
+ type CompactSearchResult,
17
+ type TimelineResult,
18
+ type TokenEconomics,
19
+ type HybridSearchResult,
20
+ } from './hybrid-search.js';
21
+
22
+ export {
23
+ TokenEconomicsTracker,
24
+ createTokenEconomicsTracker,
25
+ type TokenStats,
26
+ type EntryTokenMetrics,
27
+ type SessionTokenSummary,
28
+ type TokenEconomicsConfig,
29
+ } from './token-economics.js';
@@ -0,0 +1,367 @@
1
+ /**
2
+ * Token Economics Module
3
+ *
4
+ * Tracks token usage and savings for memory operations.
5
+ * Provides ROI metrics for the memory system.
6
+ *
7
+ * Key metrics:
8
+ * - Discovery tokens: Input tokens used to create memories
9
+ * - Read tokens: Tokens when retrieving memories
10
+ * - Savings: Discovery - Read tokens (compression benefit)
11
+ *
12
+ * @module @aitytech/agentkits-memory/search
13
+ */
14
+
15
+ /**
16
+ * Token economics statistics
17
+ */
18
+ export interface TokenStats {
19
+ /** Total discovery tokens (input that created memories) */
20
+ totalDiscoveryTokens: number;
21
+
22
+ /** Total read tokens (output when retrieved) */
23
+ totalReadTokens: number;
24
+
25
+ /** Total tokens saved (discovery - read) */
26
+ totalSavings: number;
27
+
28
+ /** Savings as percentage */
29
+ savingsPercent: number;
30
+
31
+ /** Number of observations/memories */
32
+ totalObservations: number;
33
+
34
+ /** Average tokens per observation */
35
+ avgTokensPerObservation: number;
36
+
37
+ /** Token efficiency score (0-100) */
38
+ efficiencyScore: number;
39
+ }
40
+
41
+ /**
42
+ * Per-entry token metrics
43
+ */
44
+ export interface EntryTokenMetrics {
45
+ /** Entry ID */
46
+ entryId: string;
47
+
48
+ /** Tokens used to create this entry */
49
+ discoveryTokens: number;
50
+
51
+ /** Tokens when retrieved */
52
+ readTokens: number;
53
+
54
+ /** Savings for this entry */
55
+ savings: number;
56
+
57
+ /** Number of times accessed */
58
+ accessCount: number;
59
+
60
+ /** Value score (savings * accessCount) */
61
+ valueScore: number;
62
+ }
63
+
64
+ /**
65
+ * Session token summary
66
+ */
67
+ export interface SessionTokenSummary {
68
+ /** Session ID */
69
+ sessionId: string;
70
+
71
+ /** Tokens input during session */
72
+ inputTokens: number;
73
+
74
+ /** Tokens saved by memory recall */
75
+ savedTokens: number;
76
+
77
+ /** Number of memories created */
78
+ memoriesCreated: number;
79
+
80
+ /** Number of memories recalled */
81
+ memoriesRecalled: number;
82
+
83
+ /** Net efficiency */
84
+ netEfficiency: number;
85
+ }
86
+
87
+ /**
88
+ * Token economics configuration
89
+ */
90
+ export interface TokenEconomicsConfig {
91
+ /** Characters per token estimate (default: 4) */
92
+ charsPerToken: number;
93
+
94
+ /** Include metadata in token count (default: false) */
95
+ includeMetadata: boolean;
96
+
97
+ /** Track per-entry metrics (default: true) */
98
+ trackPerEntry: boolean;
99
+ }
100
+
101
+ /**
102
+ * Default configuration
103
+ */
104
+ const DEFAULT_CONFIG: TokenEconomicsConfig = {
105
+ charsPerToken: 4,
106
+ includeMetadata: false,
107
+ trackPerEntry: true,
108
+ };
109
+
110
+ /**
111
+ * Token Economics Tracker
112
+ *
113
+ * Provides detailed token usage metrics for the memory system.
114
+ * Helps users understand the ROI of persistent memory.
115
+ */
116
+ export class TokenEconomicsTracker {
117
+ private config: TokenEconomicsConfig;
118
+ private entryMetrics: Map<string, EntryTokenMetrics> = new Map();
119
+ private sessionMetrics: Map<string, SessionTokenSummary> = new Map();
120
+
121
+ // Aggregate stats
122
+ private totalDiscoveryTokens = 0;
123
+ private totalReadTokens = 0;
124
+ private totalObservations = 0;
125
+
126
+ constructor(config: Partial<TokenEconomicsConfig> = {}) {
127
+ this.config = { ...DEFAULT_CONFIG, ...config };
128
+ }
129
+
130
+ /**
131
+ * Estimate token count for text
132
+ */
133
+ estimateTokens(text: string): number {
134
+ return Math.ceil(text.length / this.config.charsPerToken);
135
+ }
136
+
137
+ /**
138
+ * Record memory creation (discovery tokens)
139
+ */
140
+ recordCreation(
141
+ entryId: string,
142
+ content: string,
143
+ metadata?: Record<string, unknown>
144
+ ): void {
145
+ const contentTokens = this.estimateTokens(content);
146
+ const metadataTokens = this.config.includeMetadata && metadata
147
+ ? this.estimateTokens(JSON.stringify(metadata))
148
+ : 0;
149
+ const discoveryTokens = contentTokens + metadataTokens;
150
+
151
+ this.totalDiscoveryTokens += discoveryTokens;
152
+ this.totalObservations++;
153
+
154
+ if (this.config.trackPerEntry) {
155
+ this.entryMetrics.set(entryId, {
156
+ entryId,
157
+ discoveryTokens,
158
+ readTokens: 0,
159
+ savings: discoveryTokens,
160
+ accessCount: 0,
161
+ valueScore: 0,
162
+ });
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Record memory retrieval (read tokens)
168
+ */
169
+ recordRetrieval(
170
+ entryId: string,
171
+ content: string,
172
+ sessionId?: string
173
+ ): void {
174
+ const readTokens = this.estimateTokens(content);
175
+ this.totalReadTokens += readTokens;
176
+
177
+ if (this.config.trackPerEntry) {
178
+ const metrics = this.entryMetrics.get(entryId);
179
+ if (metrics) {
180
+ metrics.readTokens += readTokens;
181
+ metrics.accessCount++;
182
+ metrics.savings = metrics.discoveryTokens - metrics.readTokens;
183
+ metrics.valueScore = metrics.savings * metrics.accessCount;
184
+ }
185
+ }
186
+
187
+ if (sessionId) {
188
+ const session = this.sessionMetrics.get(sessionId) || {
189
+ sessionId,
190
+ inputTokens: 0,
191
+ savedTokens: 0,
192
+ memoriesCreated: 0,
193
+ memoriesRecalled: 0,
194
+ netEfficiency: 0,
195
+ };
196
+ session.memoriesRecalled++;
197
+ session.savedTokens += readTokens;
198
+ this.sessionMetrics.set(sessionId, session);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Record session input tokens
204
+ */
205
+ recordSessionInput(sessionId: string, inputTokens: number): void {
206
+ const session = this.sessionMetrics.get(sessionId) || {
207
+ sessionId,
208
+ inputTokens: 0,
209
+ savedTokens: 0,
210
+ memoriesCreated: 0,
211
+ memoriesRecalled: 0,
212
+ netEfficiency: 0,
213
+ };
214
+ session.inputTokens += inputTokens;
215
+ session.netEfficiency = session.inputTokens > 0
216
+ ? session.savedTokens / session.inputTokens
217
+ : 0;
218
+ this.sessionMetrics.set(sessionId, session);
219
+ }
220
+
221
+ /**
222
+ * Get aggregate statistics
223
+ */
224
+ getStats(): TokenStats {
225
+ const totalSavings = Math.max(0, this.totalDiscoveryTokens - this.totalReadTokens);
226
+ const savingsPercent = this.totalDiscoveryTokens > 0
227
+ ? (totalSavings / this.totalDiscoveryTokens) * 100
228
+ : 0;
229
+
230
+ // Efficiency score: 0-100 based on savings and usage
231
+ const usageRatio = this.totalReadTokens > 0
232
+ ? this.totalDiscoveryTokens / this.totalReadTokens
233
+ : 0;
234
+ const efficiencyScore = Math.min(100, usageRatio * 10);
235
+
236
+ return {
237
+ totalDiscoveryTokens: this.totalDiscoveryTokens,
238
+ totalReadTokens: this.totalReadTokens,
239
+ totalSavings,
240
+ savingsPercent,
241
+ totalObservations: this.totalObservations,
242
+ avgTokensPerObservation: this.totalObservations > 0
243
+ ? this.totalDiscoveryTokens / this.totalObservations
244
+ : 0,
245
+ efficiencyScore,
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Get per-entry metrics
251
+ */
252
+ getEntryMetrics(entryId?: string): EntryTokenMetrics[] {
253
+ if (entryId) {
254
+ const metrics = this.entryMetrics.get(entryId);
255
+ return metrics ? [metrics] : [];
256
+ }
257
+ return Array.from(this.entryMetrics.values());
258
+ }
259
+
260
+ /**
261
+ * Get top entries by value score
262
+ */
263
+ getTopValueEntries(limit: number = 10): EntryTokenMetrics[] {
264
+ return Array.from(this.entryMetrics.values())
265
+ .sort((a, b) => b.valueScore - a.valueScore)
266
+ .slice(0, limit);
267
+ }
268
+
269
+ /**
270
+ * Get low-value entries (candidates for archival)
271
+ */
272
+ getLowValueEntries(limit: number = 10): EntryTokenMetrics[] {
273
+ return Array.from(this.entryMetrics.values())
274
+ .filter((e) => e.accessCount === 0 || e.valueScore < 0)
275
+ .sort((a, b) => a.valueScore - b.valueScore)
276
+ .slice(0, limit);
277
+ }
278
+
279
+ /**
280
+ * Get session summary
281
+ */
282
+ getSessionSummary(sessionId: string): SessionTokenSummary | undefined {
283
+ return this.sessionMetrics.get(sessionId);
284
+ }
285
+
286
+ /**
287
+ * Get all session summaries
288
+ */
289
+ getAllSessionSummaries(): SessionTokenSummary[] {
290
+ return Array.from(this.sessionMetrics.values());
291
+ }
292
+
293
+ /**
294
+ * Reset all metrics
295
+ */
296
+ reset(): void {
297
+ this.entryMetrics.clear();
298
+ this.sessionMetrics.clear();
299
+ this.totalDiscoveryTokens = 0;
300
+ this.totalReadTokens = 0;
301
+ this.totalObservations = 0;
302
+ }
303
+
304
+ /**
305
+ * Export metrics as JSON
306
+ */
307
+ export(): {
308
+ stats: TokenStats;
309
+ entries: EntryTokenMetrics[];
310
+ sessions: SessionTokenSummary[];
311
+ } {
312
+ return {
313
+ stats: this.getStats(),
314
+ entries: this.getEntryMetrics(),
315
+ sessions: this.getAllSessionSummaries(),
316
+ };
317
+ }
318
+
319
+ /**
320
+ * Import metrics from JSON
321
+ */
322
+ import(data: {
323
+ entries?: EntryTokenMetrics[];
324
+ sessions?: SessionTokenSummary[];
325
+ }): void {
326
+ if (data.entries) {
327
+ for (const entry of data.entries) {
328
+ this.entryMetrics.set(entry.entryId, entry);
329
+ this.totalDiscoveryTokens += entry.discoveryTokens;
330
+ this.totalReadTokens += entry.readTokens;
331
+ this.totalObservations++;
332
+ }
333
+ }
334
+ if (data.sessions) {
335
+ for (const session of data.sessions) {
336
+ this.sessionMetrics.set(session.sessionId, session);
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Format stats for display
343
+ */
344
+ formatStats(): string {
345
+ const stats = this.getStats();
346
+ return [
347
+ `📊 Token Economics`,
348
+ `━━━━━━━━━━━━━━━━━━`,
349
+ `Observations: ${stats.totalObservations.toLocaleString()}`,
350
+ `Discovery Tokens: ${stats.totalDiscoveryTokens.toLocaleString()}`,
351
+ `Read Tokens: ${stats.totalReadTokens.toLocaleString()}`,
352
+ `Tokens Saved: ${stats.totalSavings.toLocaleString()} (${stats.savingsPercent.toFixed(1)}%)`,
353
+ `Efficiency Score: ${stats.efficiencyScore.toFixed(0)}/100`,
354
+ ].join('\n');
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Create a token economics tracker
360
+ */
361
+ export function createTokenEconomicsTracker(
362
+ config?: Partial<TokenEconomicsConfig>
363
+ ): TokenEconomicsTracker {
364
+ return new TokenEconomicsTracker(config);
365
+ }
366
+
367
+ export default TokenEconomicsTracker;
package/src/types.ts CHANGED
@@ -482,102 +482,6 @@ export interface CachedEntry<T> {
482
482
  accessCount: number;
483
483
  }
484
484
 
485
- // ===== Migration Types =====
486
-
487
- /**
488
- * Migration source type
489
- */
490
- export type MigrationSource =
491
- | 'markdown' // .claude/memory/*.md files
492
- | 'json' // JSON exports
493
- | 'sqlite'; // Existing SQLite databases
494
-
495
- /**
496
- * Migration configuration
497
- */
498
- export interface MigrationConfig {
499
- /** Source backend type */
500
- source: MigrationSource;
501
-
502
- /** Source path or connection string */
503
- sourcePath: string;
504
-
505
- /** Batch size for migration */
506
- batchSize: number;
507
-
508
- /** Generate embeddings during migration */
509
- generateEmbeddings: boolean;
510
-
511
- /** Validate data during migration */
512
- validateData: boolean;
513
-
514
- /** Continue on error */
515
- continueOnError: boolean;
516
-
517
- /** Namespace mapping */
518
- namespaceMapping?: Record<string, string>;
519
-
520
- /** Type mapping */
521
- typeMapping?: Record<string, MemoryType>;
522
- }
523
-
524
- /**
525
- * Migration progress
526
- */
527
- export interface MigrationProgress {
528
- /** Total entries to migrate */
529
- total: number;
530
-
531
- /** Entries migrated so far */
532
- migrated: number;
533
-
534
- /** Entries failed */
535
- failed: number;
536
-
537
- /** Entries skipped */
538
- skipped: number;
539
-
540
- /** Progress percentage (0-100) */
541
- percentage: number;
542
-
543
- /** Errors encountered */
544
- errors: MigrationError[];
545
- }
546
-
547
- /**
548
- * Migration error
549
- */
550
- export interface MigrationError {
551
- /** Entry ID or key that failed */
552
- entryId: string;
553
-
554
- /** Error message */
555
- message: string;
556
-
557
- /** Error code */
558
- code: string;
559
-
560
- /** Whether the error is recoverable */
561
- recoverable: boolean;
562
- }
563
-
564
- /**
565
- * Migration result
566
- */
567
- export interface MigrationResult {
568
- /** Whether migration completed successfully */
569
- success: boolean;
570
-
571
- /** Final progress state */
572
- progress: MigrationProgress;
573
-
574
- /** Total time taken in milliseconds */
575
- duration: number;
576
-
577
- /** Summary message */
578
- summary: string;
579
- }
580
-
581
485
  // ===== Session Types =====
582
486
 
583
487
  /**