@claude-flow/memory 3.0.0-alpha.1 → 3.0.0-alpha.7

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 (156) hide show
  1. package/README.md +356 -18
  2. package/dist/agent-memory-scope.d.ts +131 -0
  3. package/dist/agent-memory-scope.d.ts.map +1 -0
  4. package/dist/agent-memory-scope.js +215 -0
  5. package/dist/agent-memory-scope.js.map +1 -0
  6. package/dist/agent-memory-scope.test.d.ts +8 -0
  7. package/dist/agent-memory-scope.test.d.ts.map +1 -0
  8. package/dist/agent-memory-scope.test.js +463 -0
  9. package/dist/agent-memory-scope.test.js.map +1 -0
  10. package/dist/agentdb-adapter.d.ts +22 -3
  11. package/dist/agentdb-adapter.d.ts.map +1 -1
  12. package/dist/agentdb-adapter.js +135 -8
  13. package/dist/agentdb-adapter.js.map +1 -1
  14. package/dist/auto-memory-bridge.d.ts +226 -0
  15. package/dist/auto-memory-bridge.d.ts.map +1 -0
  16. package/dist/auto-memory-bridge.js +709 -0
  17. package/dist/auto-memory-bridge.js.map +1 -0
  18. package/dist/auto-memory-bridge.test.d.ts +8 -0
  19. package/dist/auto-memory-bridge.test.d.ts.map +1 -0
  20. package/dist/auto-memory-bridge.test.js +754 -0
  21. package/dist/auto-memory-bridge.test.js.map +1 -0
  22. package/dist/benchmark.test.d.ts +2 -0
  23. package/dist/benchmark.test.d.ts.map +1 -0
  24. package/dist/benchmark.test.js +277 -0
  25. package/dist/benchmark.test.js.map +1 -0
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +8 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/learning-bridge.d.ts +137 -0
  31. package/dist/learning-bridge.d.ts.map +1 -0
  32. package/dist/learning-bridge.js +335 -0
  33. package/dist/learning-bridge.js.map +1 -0
  34. package/dist/learning-bridge.test.d.ts +8 -0
  35. package/dist/learning-bridge.test.d.ts.map +1 -0
  36. package/dist/learning-bridge.test.js +578 -0
  37. package/dist/learning-bridge.test.js.map +1 -0
  38. package/dist/memory-graph.d.ts +100 -0
  39. package/dist/memory-graph.d.ts.map +1 -0
  40. package/dist/memory-graph.js +333 -0
  41. package/dist/memory-graph.js.map +1 -0
  42. package/dist/memory-graph.test.d.ts +8 -0
  43. package/dist/memory-graph.test.d.ts.map +1 -0
  44. package/dist/memory-graph.test.js +609 -0
  45. package/dist/memory-graph.test.js.map +1 -0
  46. package/dist/types.d.ts +3 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/package.json +19 -4
  49. package/.agentic-flow/intelligence.json +0 -16
  50. package/__tests__/coverage/base.css +0 -224
  51. package/__tests__/coverage/block-navigation.js +0 -87
  52. package/__tests__/coverage/coverage-final.json +0 -19
  53. package/__tests__/coverage/favicon.png +0 -0
  54. package/__tests__/coverage/index.html +0 -206
  55. package/__tests__/coverage/lcov-report/base.css +0 -224
  56. package/__tests__/coverage/lcov-report/block-navigation.js +0 -87
  57. package/__tests__/coverage/lcov-report/favicon.png +0 -0
  58. package/__tests__/coverage/lcov-report/index.html +0 -206
  59. package/__tests__/coverage/lcov-report/prettify.css +0 -1
  60. package/__tests__/coverage/lcov-report/prettify.js +0 -2
  61. package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  62. package/__tests__/coverage/lcov-report/sorter.js +0 -210
  63. package/__tests__/coverage/lcov-report/src/agentdb-adapter.ts.html +0 -2737
  64. package/__tests__/coverage/lcov-report/src/agentdb-backend.ts.html +0 -3130
  65. package/__tests__/coverage/lcov-report/src/application/commands/delete-memory.command.ts.html +0 -601
  66. package/__tests__/coverage/lcov-report/src/application/commands/index.html +0 -131
  67. package/__tests__/coverage/lcov-report/src/application/commands/store-memory.command.ts.html +0 -394
  68. package/__tests__/coverage/lcov-report/src/application/queries/index.html +0 -116
  69. package/__tests__/coverage/lcov-report/src/application/queries/search-memory.query.ts.html +0 -796
  70. package/__tests__/coverage/lcov-report/src/application/services/index.html +0 -116
  71. package/__tests__/coverage/lcov-report/src/application/services/memory-application-service.ts.html +0 -793
  72. package/__tests__/coverage/lcov-report/src/cache-manager.ts.html +0 -1633
  73. package/__tests__/coverage/lcov-report/src/database-provider.ts.html +0 -1618
  74. package/__tests__/coverage/lcov-report/src/domain/entities/index.html +0 -116
  75. package/__tests__/coverage/lcov-report/src/domain/entities/memory-entry.ts.html +0 -952
  76. package/__tests__/coverage/lcov-report/src/domain/services/index.html +0 -116
  77. package/__tests__/coverage/lcov-report/src/domain/services/memory-domain-service.ts.html +0 -1294
  78. package/__tests__/coverage/lcov-report/src/hnsw-index.ts.html +0 -3124
  79. package/__tests__/coverage/lcov-report/src/hybrid-backend.ts.html +0 -2167
  80. package/__tests__/coverage/lcov-report/src/index.html +0 -266
  81. package/__tests__/coverage/lcov-report/src/infrastructure/repositories/hybrid-memory-repository.ts.html +0 -1633
  82. package/__tests__/coverage/lcov-report/src/infrastructure/repositories/index.html +0 -116
  83. package/__tests__/coverage/lcov-report/src/migration.ts.html +0 -2092
  84. package/__tests__/coverage/lcov-report/src/query-builder.ts.html +0 -1711
  85. package/__tests__/coverage/lcov-report/src/sqlite-backend.ts.html +0 -2281
  86. package/__tests__/coverage/lcov-report/src/sqljs-backend.ts.html +0 -2374
  87. package/__tests__/coverage/lcov-report/src/types.ts.html +0 -2266
  88. package/__tests__/coverage/lcov.info +0 -10238
  89. package/__tests__/coverage/prettify.css +0 -1
  90. package/__tests__/coverage/prettify.js +0 -2
  91. package/__tests__/coverage/sort-arrow-sprite.png +0 -0
  92. package/__tests__/coverage/sorter.js +0 -210
  93. package/__tests__/coverage/src/agentdb-adapter.ts.html +0 -2737
  94. package/__tests__/coverage/src/agentdb-backend.ts.html +0 -3130
  95. package/__tests__/coverage/src/application/commands/delete-memory.command.ts.html +0 -601
  96. package/__tests__/coverage/src/application/commands/index.html +0 -131
  97. package/__tests__/coverage/src/application/commands/store-memory.command.ts.html +0 -394
  98. package/__tests__/coverage/src/application/queries/index.html +0 -116
  99. package/__tests__/coverage/src/application/queries/search-memory.query.ts.html +0 -796
  100. package/__tests__/coverage/src/application/services/index.html +0 -116
  101. package/__tests__/coverage/src/application/services/memory-application-service.ts.html +0 -793
  102. package/__tests__/coverage/src/cache-manager.ts.html +0 -1633
  103. package/__tests__/coverage/src/database-provider.ts.html +0 -1618
  104. package/__tests__/coverage/src/domain/entities/index.html +0 -116
  105. package/__tests__/coverage/src/domain/entities/memory-entry.ts.html +0 -952
  106. package/__tests__/coverage/src/domain/services/index.html +0 -116
  107. package/__tests__/coverage/src/domain/services/memory-domain-service.ts.html +0 -1294
  108. package/__tests__/coverage/src/hnsw-index.ts.html +0 -3124
  109. package/__tests__/coverage/src/hybrid-backend.ts.html +0 -2167
  110. package/__tests__/coverage/src/index.html +0 -266
  111. package/__tests__/coverage/src/infrastructure/repositories/hybrid-memory-repository.ts.html +0 -1633
  112. package/__tests__/coverage/src/infrastructure/repositories/index.html +0 -116
  113. package/__tests__/coverage/src/migration.ts.html +0 -2092
  114. package/__tests__/coverage/src/query-builder.ts.html +0 -1711
  115. package/__tests__/coverage/src/sqlite-backend.ts.html +0 -2281
  116. package/__tests__/coverage/src/sqljs-backend.ts.html +0 -2374
  117. package/__tests__/coverage/src/types.ts.html +0 -2266
  118. package/benchmarks/cache-hit-rate.bench.ts +0 -535
  119. package/benchmarks/hnsw-indexing.bench.ts +0 -552
  120. package/benchmarks/memory-write.bench.ts +0 -469
  121. package/benchmarks/vector-search.bench.ts +0 -449
  122. package/docs/AGENTDB-INTEGRATION.md +0 -388
  123. package/docs/CROSS_PLATFORM.md +0 -505
  124. package/docs/WINDOWS_SUPPORT.md +0 -422
  125. package/examples/agentdb-example.ts +0 -345
  126. package/examples/cross-platform-usage.ts +0 -326
  127. package/framework/benchmark.ts +0 -112
  128. package/src/agentdb-adapter.ts +0 -884
  129. package/src/agentdb-backend.test.ts +0 -339
  130. package/src/agentdb-backend.ts +0 -1016
  131. package/src/application/commands/delete-memory.command.ts +0 -172
  132. package/src/application/commands/store-memory.command.ts +0 -103
  133. package/src/application/index.ts +0 -36
  134. package/src/application/queries/search-memory.query.ts +0 -237
  135. package/src/application/services/memory-application-service.ts +0 -236
  136. package/src/cache-manager.ts +0 -516
  137. package/src/database-provider.test.ts +0 -364
  138. package/src/database-provider.ts +0 -511
  139. package/src/domain/entities/memory-entry.ts +0 -289
  140. package/src/domain/index.ts +0 -35
  141. package/src/domain/repositories/memory-repository.interface.ts +0 -120
  142. package/src/domain/services/memory-domain-service.ts +0 -403
  143. package/src/hnsw-index.ts +0 -1013
  144. package/src/hybrid-backend.test.ts +0 -399
  145. package/src/hybrid-backend.ts +0 -694
  146. package/src/index.ts +0 -515
  147. package/src/infrastructure/index.ts +0 -23
  148. package/src/infrastructure/repositories/hybrid-memory-repository.ts +0 -516
  149. package/src/migration.ts +0 -669
  150. package/src/query-builder.ts +0 -542
  151. package/src/sqlite-backend.ts +0 -732
  152. package/src/sqljs-backend.ts +0 -763
  153. package/src/types.ts +0 -727
  154. package/tsconfig.json +0 -9
  155. package/tsconfig.tsbuildinfo +0 -1
  156. package/verify-cross-platform.ts +0 -170
@@ -1,694 +0,0 @@
1
- /**
2
- * HybridBackend - Combines SQLite (structured queries) + AgentDB (vector search)
3
- *
4
- * Per ADR-009: "HybridBackend (SQLite + AgentDB) as default"
5
- * - SQLite for: Structured queries, ACID transactions, exact matches
6
- * - AgentDB for: Semantic search, vector similarity, RAG
7
- *
8
- * @module v3/memory/hybrid-backend
9
- */
10
-
11
- import { EventEmitter } from 'node:events';
12
- import {
13
- IMemoryBackend,
14
- MemoryEntry,
15
- MemoryEntryInput,
16
- MemoryEntryUpdate,
17
- MemoryQuery,
18
- SearchOptions,
19
- SearchResult,
20
- BackendStats,
21
- HealthCheckResult,
22
- ComponentHealth,
23
- EmbeddingGenerator,
24
- createDefaultEntry,
25
- QueryType,
26
- } from './types.js';
27
- import { SQLiteBackend, SQLiteBackendConfig } from './sqlite-backend.js';
28
- import { AgentDBBackend, AgentDBBackendConfig } from './agentdb-backend.js';
29
-
30
- /**
31
- * Configuration for HybridBackend
32
- */
33
- export interface HybridBackendConfig {
34
- /** SQLite configuration */
35
- sqlite?: Partial<SQLiteBackendConfig>;
36
-
37
- /** AgentDB configuration */
38
- agentdb?: Partial<AgentDBBackendConfig>;
39
-
40
- /** Default namespace */
41
- defaultNamespace?: string;
42
-
43
- /** Embedding generator function */
44
- embeddingGenerator?: EmbeddingGenerator;
45
-
46
- /** Query routing strategy */
47
- routingStrategy?: 'auto' | 'sqlite-first' | 'agentdb-first';
48
-
49
- /** Enable dual-write (write to both backends) */
50
- dualWrite?: boolean;
51
-
52
- /** Semantic search threshold for hybrid queries */
53
- semanticThreshold?: number;
54
-
55
- /** Maximum results to fetch from each backend in hybrid queries */
56
- hybridMaxResults?: number;
57
- }
58
-
59
- /**
60
- * Default configuration
61
- */
62
- const DEFAULT_CONFIG: Required<HybridBackendConfig> = {
63
- sqlite: {},
64
- agentdb: {},
65
- defaultNamespace: 'default',
66
- embeddingGenerator: undefined as any,
67
- routingStrategy: 'auto',
68
- dualWrite: true,
69
- semanticThreshold: 0.7,
70
- hybridMaxResults: 100,
71
- };
72
-
73
- /**
74
- * Structured Query Interface
75
- * Optimized for SQLite's strengths
76
- */
77
- export interface StructuredQuery {
78
- /** Exact key match */
79
- key?: string;
80
-
81
- /** Key prefix match */
82
- keyPrefix?: string;
83
-
84
- /** Namespace filter */
85
- namespace?: string;
86
-
87
- /** Owner filter */
88
- ownerId?: string;
89
-
90
- /** Type filter */
91
- type?: string;
92
-
93
- /** Time range filters */
94
- createdAfter?: number;
95
- createdBefore?: number;
96
- updatedAfter?: number;
97
- updatedBefore?: number;
98
-
99
- /** Pagination */
100
- limit?: number;
101
- offset?: number;
102
- }
103
-
104
- /**
105
- * Semantic Query Interface
106
- * Optimized for AgentDB's vector search
107
- */
108
- export interface SemanticQuery {
109
- /** Content to search for (will be embedded) */
110
- content?: string;
111
-
112
- /** Pre-computed embedding */
113
- embedding?: Float32Array;
114
-
115
- /** Number of results */
116
- k?: number;
117
-
118
- /** Similarity threshold (0-1) */
119
- threshold?: number;
120
-
121
- /** Additional filters */
122
- filters?: Partial<MemoryQuery>;
123
- }
124
-
125
- /**
126
- * Hybrid Query Interface
127
- * Combines structured + semantic search
128
- */
129
- export interface HybridQuery {
130
- /** Semantic component */
131
- semantic: SemanticQuery;
132
-
133
- /** Structured component */
134
- structured?: StructuredQuery;
135
-
136
- /** How to combine results */
137
- combineStrategy?: 'union' | 'intersection' | 'semantic-first' | 'structured-first';
138
-
139
- /** Weights for score combination */
140
- weights?: {
141
- semantic: number;
142
- structured: number;
143
- };
144
- }
145
-
146
- /**
147
- * HybridBackend Implementation
148
- *
149
- * Intelligently routes queries between SQLite and AgentDB:
150
- * - Exact matches, prefix queries → SQLite
151
- * - Semantic search, similarity → AgentDB
152
- * - Complex hybrid queries → Both backends with intelligent merging
153
- */
154
- export class HybridBackend extends EventEmitter implements IMemoryBackend {
155
- private sqlite: SQLiteBackend;
156
- private agentdb: AgentDBBackend;
157
- private config: Required<HybridBackendConfig>;
158
- private initialized: boolean = false;
159
-
160
- // Performance tracking
161
- private stats = {
162
- sqliteQueries: 0,
163
- agentdbQueries: 0,
164
- hybridQueries: 0,
165
- totalQueryTime: 0,
166
- };
167
-
168
- constructor(config: HybridBackendConfig = {}) {
169
- super();
170
- this.config = { ...DEFAULT_CONFIG, ...config };
171
-
172
- // Initialize SQLite backend
173
- this.sqlite = new SQLiteBackend({
174
- ...this.config.sqlite,
175
- defaultNamespace: this.config.defaultNamespace,
176
- embeddingGenerator: this.config.embeddingGenerator,
177
- });
178
-
179
- // Initialize AgentDB backend
180
- this.agentdb = new AgentDBBackend({
181
- ...this.config.agentdb,
182
- namespace: this.config.defaultNamespace,
183
- embeddingGenerator: this.config.embeddingGenerator,
184
- });
185
-
186
- // Forward events from both backends
187
- this.sqlite.on('entry:stored', (data) => this.emit('sqlite:stored', data));
188
- this.sqlite.on('entry:updated', (data) => this.emit('sqlite:updated', data));
189
- this.sqlite.on('entry:deleted', (data) => this.emit('sqlite:deleted', data));
190
-
191
- this.agentdb.on('entry:stored', (data) => this.emit('agentdb:stored', data));
192
- this.agentdb.on('entry:updated', (data) => this.emit('agentdb:updated', data));
193
- this.agentdb.on('entry:deleted', (data) => this.emit('agentdb:deleted', data));
194
- this.agentdb.on('cache:hit', (data) => this.emit('cache:hit', data));
195
- this.agentdb.on('cache:miss', (data) => this.emit('cache:miss', data));
196
- }
197
-
198
- /**
199
- * Initialize both backends
200
- */
201
- async initialize(): Promise<void> {
202
- if (this.initialized) return;
203
-
204
- await Promise.all([this.sqlite.initialize(), this.agentdb.initialize()]);
205
-
206
- this.initialized = true;
207
- this.emit('initialized');
208
- }
209
-
210
- /**
211
- * Shutdown both backends
212
- */
213
- async shutdown(): Promise<void> {
214
- if (!this.initialized) return;
215
-
216
- await Promise.all([this.sqlite.shutdown(), this.agentdb.shutdown()]);
217
-
218
- this.initialized = false;
219
- this.emit('shutdown');
220
- }
221
-
222
- /**
223
- * Store in both backends (dual-write for consistency)
224
- */
225
- async store(entry: MemoryEntry): Promise<void> {
226
- if (this.config.dualWrite) {
227
- // Write to both backends in parallel
228
- await Promise.all([this.sqlite.store(entry), this.agentdb.store(entry)]);
229
- } else {
230
- // Write to primary backend only (AgentDB has vector search)
231
- await this.agentdb.store(entry);
232
- }
233
-
234
- this.emit('entry:stored', { id: entry.id });
235
- }
236
-
237
- /**
238
- * Get from AgentDB (has caching enabled)
239
- */
240
- async get(id: string): Promise<MemoryEntry | null> {
241
- return this.agentdb.get(id);
242
- }
243
-
244
- /**
245
- * Get by key (SQLite optimized for exact matches)
246
- */
247
- async getByKey(namespace: string, key: string): Promise<MemoryEntry | null> {
248
- return this.sqlite.getByKey(namespace, key);
249
- }
250
-
251
- /**
252
- * Update in both backends
253
- */
254
- async update(id: string, update: MemoryEntryUpdate): Promise<MemoryEntry | null> {
255
- if (this.config.dualWrite) {
256
- // Update both backends
257
- const [sqliteResult, agentdbResult] = await Promise.all([
258
- this.sqlite.update(id, update),
259
- this.agentdb.update(id, update),
260
- ]);
261
- return agentdbResult || sqliteResult;
262
- } else {
263
- return this.agentdb.update(id, update);
264
- }
265
- }
266
-
267
- /**
268
- * Delete from both backends
269
- */
270
- async delete(id: string): Promise<boolean> {
271
- if (this.config.dualWrite) {
272
- const [sqliteResult, agentdbResult] = await Promise.all([
273
- this.sqlite.delete(id),
274
- this.agentdb.delete(id),
275
- ]);
276
- return sqliteResult || agentdbResult;
277
- } else {
278
- return this.agentdb.delete(id);
279
- }
280
- }
281
-
282
- /**
283
- * Query routing - semantic goes to AgentDB, structured to SQLite
284
- */
285
- async query(query: MemoryQuery): Promise<MemoryEntry[]> {
286
- const startTime = performance.now();
287
-
288
- let results: MemoryEntry[];
289
-
290
- // Route based on query type
291
- switch (query.type) {
292
- case 'exact':
293
- // SQLite optimized for exact matches
294
- this.stats.sqliteQueries++;
295
- results = await this.sqlite.query(query);
296
- break;
297
-
298
- case 'prefix':
299
- // SQLite optimized for prefix queries
300
- this.stats.sqliteQueries++;
301
- results = await this.sqlite.query(query);
302
- break;
303
-
304
- case 'tag':
305
- // Both can handle tags, use SQLite for structured filtering
306
- this.stats.sqliteQueries++;
307
- results = await this.sqlite.query(query);
308
- break;
309
-
310
- case 'semantic':
311
- // AgentDB optimized for semantic search
312
- this.stats.agentdbQueries++;
313
- results = await this.agentdb.query(query);
314
- break;
315
-
316
- case 'hybrid':
317
- // Use hybrid query combining both backends
318
- this.stats.hybridQueries++;
319
- results = await this.queryHybridInternal(query);
320
- break;
321
-
322
- default:
323
- // Auto-routing based on query properties
324
- results = await this.autoRoute(query);
325
- }
326
-
327
- const duration = performance.now() - startTime;
328
- this.stats.totalQueryTime += duration;
329
-
330
- this.emit('query:completed', { type: query.type, duration, count: results.length });
331
- return results;
332
- }
333
-
334
- /**
335
- * Structured queries (SQL)
336
- * Routes to SQLite for optimal performance
337
- */
338
- async queryStructured(query: StructuredQuery): Promise<MemoryEntry[]> {
339
- this.stats.sqliteQueries++;
340
-
341
- const memoryQuery: MemoryQuery = {
342
- type: query.key ? 'exact' : query.keyPrefix ? 'prefix' : 'hybrid',
343
- key: query.key,
344
- keyPrefix: query.keyPrefix,
345
- namespace: query.namespace,
346
- ownerId: query.ownerId,
347
- memoryType: query.type as any,
348
- createdAfter: query.createdAfter,
349
- createdBefore: query.createdBefore,
350
- updatedAfter: query.updatedAfter,
351
- updatedBefore: query.updatedBefore,
352
- limit: query.limit || 100,
353
- offset: query.offset || 0,
354
- };
355
-
356
- return this.sqlite.query(memoryQuery);
357
- }
358
-
359
- /**
360
- * Semantic queries (vector)
361
- * Routes to AgentDB for HNSW-based vector search
362
- */
363
- async querySemantic(query: SemanticQuery): Promise<MemoryEntry[]> {
364
- this.stats.agentdbQueries++;
365
-
366
- let embedding = query.embedding;
367
-
368
- // Generate embedding if content provided
369
- if (!embedding && query.content && this.config.embeddingGenerator) {
370
- embedding = await this.config.embeddingGenerator(query.content);
371
- }
372
-
373
- if (!embedding) {
374
- throw new Error('SemanticQuery requires either content or embedding');
375
- }
376
-
377
- const searchResults = await this.agentdb.search(embedding, {
378
- k: query.k || 10,
379
- threshold: query.threshold || this.config.semanticThreshold,
380
- filters: query.filters as MemoryQuery | undefined,
381
- });
382
-
383
- return searchResults.map((r) => r.entry);
384
- }
385
-
386
- /**
387
- * Hybrid queries (combine both)
388
- * Intelligently merges results from both backends
389
- */
390
- async queryHybrid(query: HybridQuery): Promise<MemoryEntry[]> {
391
- this.stats.hybridQueries++;
392
-
393
- const strategy = query.combineStrategy || 'semantic-first';
394
- const weights = query.weights || { semantic: 0.7, structured: 0.3 };
395
-
396
- // Execute both queries in parallel
397
- const [semanticResults, structuredResults] = await Promise.all([
398
- this.querySemantic(query.semantic),
399
- query.structured ? this.queryStructured(query.structured) : Promise.resolve([]),
400
- ]);
401
-
402
- // Combine results based on strategy
403
- switch (strategy) {
404
- case 'union':
405
- return this.combineUnion(semanticResults, structuredResults);
406
-
407
- case 'intersection':
408
- return this.combineIntersection(semanticResults, structuredResults);
409
-
410
- case 'semantic-first':
411
- return this.combineSemanticFirst(semanticResults, structuredResults);
412
-
413
- case 'structured-first':
414
- return this.combineStructuredFirst(semanticResults, structuredResults);
415
-
416
- default:
417
- return this.combineUnion(semanticResults, structuredResults);
418
- }
419
- }
420
-
421
- /**
422
- * Semantic vector search (routes to AgentDB)
423
- */
424
- async search(embedding: Float32Array, options: SearchOptions): Promise<SearchResult[]> {
425
- this.stats.agentdbQueries++;
426
- return this.agentdb.search(embedding, options);
427
- }
428
-
429
- /**
430
- * Bulk insert to both backends
431
- */
432
- async bulkInsert(entries: MemoryEntry[]): Promise<void> {
433
- if (this.config.dualWrite) {
434
- await Promise.all([this.sqlite.bulkInsert(entries), this.agentdb.bulkInsert(entries)]);
435
- } else {
436
- await this.agentdb.bulkInsert(entries);
437
- }
438
- }
439
-
440
- /**
441
- * Bulk delete from both backends
442
- */
443
- async bulkDelete(ids: string[]): Promise<number> {
444
- if (this.config.dualWrite) {
445
- const [sqliteCount, agentdbCount] = await Promise.all([
446
- this.sqlite.bulkDelete(ids),
447
- this.agentdb.bulkDelete(ids),
448
- ]);
449
- return Math.max(sqliteCount, agentdbCount);
450
- } else {
451
- return this.agentdb.bulkDelete(ids);
452
- }
453
- }
454
-
455
- /**
456
- * Count entries (use SQLite for efficiency)
457
- */
458
- async count(namespace?: string): Promise<number> {
459
- return this.sqlite.count(namespace);
460
- }
461
-
462
- /**
463
- * List namespaces (use SQLite)
464
- */
465
- async listNamespaces(): Promise<string[]> {
466
- return this.sqlite.listNamespaces();
467
- }
468
-
469
- /**
470
- * Clear namespace in both backends
471
- */
472
- async clearNamespace(namespace: string): Promise<number> {
473
- if (this.config.dualWrite) {
474
- const [sqliteCount, agentdbCount] = await Promise.all([
475
- this.sqlite.clearNamespace(namespace),
476
- this.agentdb.clearNamespace(namespace),
477
- ]);
478
- return Math.max(sqliteCount, agentdbCount);
479
- } else {
480
- return this.agentdb.clearNamespace(namespace);
481
- }
482
- }
483
-
484
- /**
485
- * Get combined statistics from both backends
486
- */
487
- async getStats(): Promise<BackendStats> {
488
- const [sqliteStats, agentdbStats] = await Promise.all([
489
- this.sqlite.getStats(),
490
- this.agentdb.getStats(),
491
- ]);
492
-
493
- return {
494
- totalEntries: Math.max(sqliteStats.totalEntries, agentdbStats.totalEntries),
495
- entriesByNamespace: agentdbStats.entriesByNamespace,
496
- entriesByType: agentdbStats.entriesByType,
497
- memoryUsage: sqliteStats.memoryUsage + agentdbStats.memoryUsage,
498
- hnswStats: agentdbStats.hnswStats,
499
- cacheStats: agentdbStats.cacheStats,
500
- avgQueryTime:
501
- this.stats.hybridQueries + this.stats.sqliteQueries + this.stats.agentdbQueries > 0
502
- ? this.stats.totalQueryTime /
503
- (this.stats.hybridQueries + this.stats.sqliteQueries + this.stats.agentdbQueries)
504
- : 0,
505
- avgSearchTime: agentdbStats.avgSearchTime,
506
- };
507
- }
508
-
509
- /**
510
- * Health check for both backends
511
- */
512
- async healthCheck(): Promise<HealthCheckResult> {
513
- const [sqliteHealth, agentdbHealth] = await Promise.all([
514
- this.sqlite.healthCheck(),
515
- this.agentdb.healthCheck(),
516
- ]);
517
-
518
- const allIssues = [...sqliteHealth.issues, ...agentdbHealth.issues];
519
- const allRecommendations = [
520
- ...sqliteHealth.recommendations,
521
- ...agentdbHealth.recommendations,
522
- ];
523
-
524
- // Determine overall status
525
- let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';
526
- if (
527
- sqliteHealth.status === 'unhealthy' ||
528
- agentdbHealth.status === 'unhealthy'
529
- ) {
530
- status = 'unhealthy';
531
- } else if (
532
- sqliteHealth.status === 'degraded' ||
533
- agentdbHealth.status === 'degraded'
534
- ) {
535
- status = 'degraded';
536
- }
537
-
538
- return {
539
- status,
540
- components: {
541
- storage: sqliteHealth.components.storage,
542
- index: agentdbHealth.components.index,
543
- cache: agentdbHealth.components.cache,
544
- },
545
- timestamp: Date.now(),
546
- issues: allIssues,
547
- recommendations: allRecommendations,
548
- };
549
- }
550
-
551
- // ===== Private Methods =====
552
-
553
- /**
554
- * Auto-route queries based on properties
555
- */
556
- private async autoRoute(query: MemoryQuery): Promise<MemoryEntry[]> {
557
- // If has embedding or content, use semantic search (AgentDB)
558
- const hasEmbeddingGenerator = typeof this.config.embeddingGenerator === 'function';
559
- if (query.embedding || (query.content && hasEmbeddingGenerator)) {
560
- this.stats.agentdbQueries++;
561
- return this.agentdb.query(query);
562
- }
563
-
564
- // If has exact key or prefix, use structured search (SQLite)
565
- if (query.key || query.keyPrefix) {
566
- this.stats.sqliteQueries++;
567
- return this.sqlite.query(query);
568
- }
569
-
570
- // For other filters, use routing strategy
571
- switch (this.config.routingStrategy) {
572
- case 'sqlite-first':
573
- this.stats.sqliteQueries++;
574
- return this.sqlite.query(query);
575
-
576
- case 'agentdb-first':
577
- this.stats.agentdbQueries++;
578
- return this.agentdb.query(query);
579
-
580
- case 'auto':
581
- default:
582
- // Default to AgentDB (has caching)
583
- this.stats.agentdbQueries++;
584
- return this.agentdb.query(query);
585
- }
586
- }
587
-
588
- /**
589
- * Internal hybrid query implementation
590
- */
591
- private async queryHybridInternal(query: MemoryQuery): Promise<MemoryEntry[]> {
592
- // If semantic component exists, use hybrid
593
- if (query.embedding || query.content) {
594
- const semanticQuery: SemanticQuery = {
595
- content: query.content,
596
- embedding: query.embedding,
597
- k: query.limit || 10,
598
- threshold: query.threshold,
599
- filters: query,
600
- };
601
-
602
- const structuredQuery: StructuredQuery = {
603
- namespace: query.namespace,
604
- key: query.key,
605
- keyPrefix: query.keyPrefix,
606
- ownerId: query.ownerId,
607
- type: query.memoryType,
608
- createdAfter: query.createdAfter,
609
- createdBefore: query.createdBefore,
610
- updatedAfter: query.updatedAfter,
611
- updatedBefore: query.updatedBefore,
612
- limit: query.limit,
613
- offset: query.offset,
614
- };
615
-
616
- return this.queryHybrid({
617
- semantic: semanticQuery,
618
- structured: structuredQuery,
619
- combineStrategy: 'semantic-first',
620
- });
621
- }
622
-
623
- // Otherwise, route to structured
624
- return this.autoRoute(query);
625
- }
626
-
627
- /**
628
- * Combine results using union (all unique results)
629
- */
630
- private combineUnion(
631
- semanticResults: MemoryEntry[],
632
- structuredResults: MemoryEntry[]
633
- ): MemoryEntry[] {
634
- const seen = new Set<string>();
635
- const combined: MemoryEntry[] = [];
636
-
637
- for (const entry of [...semanticResults, ...structuredResults]) {
638
- if (!seen.has(entry.id)) {
639
- seen.add(entry.id);
640
- combined.push(entry);
641
- }
642
- }
643
-
644
- return combined;
645
- }
646
-
647
- /**
648
- * Combine results using intersection (only common results)
649
- */
650
- private combineIntersection(
651
- semanticResults: MemoryEntry[],
652
- structuredResults: MemoryEntry[]
653
- ): MemoryEntry[] {
654
- const semanticIds = new Set(semanticResults.map((e) => e.id));
655
- return structuredResults.filter((e) => semanticIds.has(e.id));
656
- }
657
-
658
- /**
659
- * Semantic-first: Prefer semantic results, add structured if not present
660
- */
661
- private combineSemanticFirst(
662
- semanticResults: MemoryEntry[],
663
- structuredResults: MemoryEntry[]
664
- ): MemoryEntry[] {
665
- const semanticIds = new Set(semanticResults.map((e) => e.id));
666
- const additional = structuredResults.filter((e) => !semanticIds.has(e.id));
667
- return [...semanticResults, ...additional];
668
- }
669
-
670
- /**
671
- * Structured-first: Prefer structured results, add semantic if not present
672
- */
673
- private combineStructuredFirst(
674
- semanticResults: MemoryEntry[],
675
- structuredResults: MemoryEntry[]
676
- ): MemoryEntry[] {
677
- const structuredIds = new Set(structuredResults.map((e) => e.id));
678
- const additional = semanticResults.filter((e) => !structuredIds.has(e.id));
679
- return [...structuredResults, ...additional];
680
- }
681
-
682
- /**
683
- * Get underlying backends for advanced operations
684
- */
685
- getSQLiteBackend(): SQLiteBackend {
686
- return this.sqlite;
687
- }
688
-
689
- getAgentDBBackend(): AgentDBBackend {
690
- return this.agentdb;
691
- }
692
- }
693
-
694
- export default HybridBackend;