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

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 (163) 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/hybrid-backend.d.ts.map +1 -1
  27. package/dist/hybrid-backend.js +29 -4
  28. package/dist/hybrid-backend.js.map +1 -1
  29. package/dist/index.d.ts +8 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +8 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/learning-bridge.d.ts +137 -0
  34. package/dist/learning-bridge.d.ts.map +1 -0
  35. package/dist/learning-bridge.js +335 -0
  36. package/dist/learning-bridge.js.map +1 -0
  37. package/dist/learning-bridge.test.d.ts +8 -0
  38. package/dist/learning-bridge.test.d.ts.map +1 -0
  39. package/dist/learning-bridge.test.js +578 -0
  40. package/dist/learning-bridge.test.js.map +1 -0
  41. package/dist/memory-graph.d.ts +100 -0
  42. package/dist/memory-graph.d.ts.map +1 -0
  43. package/dist/memory-graph.js +333 -0
  44. package/dist/memory-graph.js.map +1 -0
  45. package/dist/memory-graph.test.d.ts +8 -0
  46. package/dist/memory-graph.test.d.ts.map +1 -0
  47. package/dist/memory-graph.test.js +609 -0
  48. package/dist/memory-graph.test.js.map +1 -0
  49. package/dist/sqlite-backend.js +3 -3
  50. package/dist/sqljs-backend.d.ts.map +1 -1
  51. package/dist/sqljs-backend.js +5 -2
  52. package/dist/sqljs-backend.js.map +1 -1
  53. package/dist/types.d.ts +3 -0
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +20 -5
  56. package/.agentic-flow/intelligence.json +0 -16
  57. package/__tests__/coverage/base.css +0 -224
  58. package/__tests__/coverage/block-navigation.js +0 -87
  59. package/__tests__/coverage/coverage-final.json +0 -19
  60. package/__tests__/coverage/favicon.png +0 -0
  61. package/__tests__/coverage/index.html +0 -206
  62. package/__tests__/coverage/lcov-report/base.css +0 -224
  63. package/__tests__/coverage/lcov-report/block-navigation.js +0 -87
  64. package/__tests__/coverage/lcov-report/favicon.png +0 -0
  65. package/__tests__/coverage/lcov-report/index.html +0 -206
  66. package/__tests__/coverage/lcov-report/prettify.css +0 -1
  67. package/__tests__/coverage/lcov-report/prettify.js +0 -2
  68. package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  69. package/__tests__/coverage/lcov-report/sorter.js +0 -210
  70. package/__tests__/coverage/lcov-report/src/agentdb-adapter.ts.html +0 -2737
  71. package/__tests__/coverage/lcov-report/src/agentdb-backend.ts.html +0 -3130
  72. package/__tests__/coverage/lcov-report/src/application/commands/delete-memory.command.ts.html +0 -601
  73. package/__tests__/coverage/lcov-report/src/application/commands/index.html +0 -131
  74. package/__tests__/coverage/lcov-report/src/application/commands/store-memory.command.ts.html +0 -394
  75. package/__tests__/coverage/lcov-report/src/application/queries/index.html +0 -116
  76. package/__tests__/coverage/lcov-report/src/application/queries/search-memory.query.ts.html +0 -796
  77. package/__tests__/coverage/lcov-report/src/application/services/index.html +0 -116
  78. package/__tests__/coverage/lcov-report/src/application/services/memory-application-service.ts.html +0 -793
  79. package/__tests__/coverage/lcov-report/src/cache-manager.ts.html +0 -1633
  80. package/__tests__/coverage/lcov-report/src/database-provider.ts.html +0 -1618
  81. package/__tests__/coverage/lcov-report/src/domain/entities/index.html +0 -116
  82. package/__tests__/coverage/lcov-report/src/domain/entities/memory-entry.ts.html +0 -952
  83. package/__tests__/coverage/lcov-report/src/domain/services/index.html +0 -116
  84. package/__tests__/coverage/lcov-report/src/domain/services/memory-domain-service.ts.html +0 -1294
  85. package/__tests__/coverage/lcov-report/src/hnsw-index.ts.html +0 -3124
  86. package/__tests__/coverage/lcov-report/src/hybrid-backend.ts.html +0 -2167
  87. package/__tests__/coverage/lcov-report/src/index.html +0 -266
  88. package/__tests__/coverage/lcov-report/src/infrastructure/repositories/hybrid-memory-repository.ts.html +0 -1633
  89. package/__tests__/coverage/lcov-report/src/infrastructure/repositories/index.html +0 -116
  90. package/__tests__/coverage/lcov-report/src/migration.ts.html +0 -2092
  91. package/__tests__/coverage/lcov-report/src/query-builder.ts.html +0 -1711
  92. package/__tests__/coverage/lcov-report/src/sqlite-backend.ts.html +0 -2281
  93. package/__tests__/coverage/lcov-report/src/sqljs-backend.ts.html +0 -2374
  94. package/__tests__/coverage/lcov-report/src/types.ts.html +0 -2266
  95. package/__tests__/coverage/lcov.info +0 -10238
  96. package/__tests__/coverage/prettify.css +0 -1
  97. package/__tests__/coverage/prettify.js +0 -2
  98. package/__tests__/coverage/sort-arrow-sprite.png +0 -0
  99. package/__tests__/coverage/sorter.js +0 -210
  100. package/__tests__/coverage/src/agentdb-adapter.ts.html +0 -2737
  101. package/__tests__/coverage/src/agentdb-backend.ts.html +0 -3130
  102. package/__tests__/coverage/src/application/commands/delete-memory.command.ts.html +0 -601
  103. package/__tests__/coverage/src/application/commands/index.html +0 -131
  104. package/__tests__/coverage/src/application/commands/store-memory.command.ts.html +0 -394
  105. package/__tests__/coverage/src/application/queries/index.html +0 -116
  106. package/__tests__/coverage/src/application/queries/search-memory.query.ts.html +0 -796
  107. package/__tests__/coverage/src/application/services/index.html +0 -116
  108. package/__tests__/coverage/src/application/services/memory-application-service.ts.html +0 -793
  109. package/__tests__/coverage/src/cache-manager.ts.html +0 -1633
  110. package/__tests__/coverage/src/database-provider.ts.html +0 -1618
  111. package/__tests__/coverage/src/domain/entities/index.html +0 -116
  112. package/__tests__/coverage/src/domain/entities/memory-entry.ts.html +0 -952
  113. package/__tests__/coverage/src/domain/services/index.html +0 -116
  114. package/__tests__/coverage/src/domain/services/memory-domain-service.ts.html +0 -1294
  115. package/__tests__/coverage/src/hnsw-index.ts.html +0 -3124
  116. package/__tests__/coverage/src/hybrid-backend.ts.html +0 -2167
  117. package/__tests__/coverage/src/index.html +0 -266
  118. package/__tests__/coverage/src/infrastructure/repositories/hybrid-memory-repository.ts.html +0 -1633
  119. package/__tests__/coverage/src/infrastructure/repositories/index.html +0 -116
  120. package/__tests__/coverage/src/migration.ts.html +0 -2092
  121. package/__tests__/coverage/src/query-builder.ts.html +0 -1711
  122. package/__tests__/coverage/src/sqlite-backend.ts.html +0 -2281
  123. package/__tests__/coverage/src/sqljs-backend.ts.html +0 -2374
  124. package/__tests__/coverage/src/types.ts.html +0 -2266
  125. package/benchmarks/cache-hit-rate.bench.ts +0 -535
  126. package/benchmarks/hnsw-indexing.bench.ts +0 -552
  127. package/benchmarks/memory-write.bench.ts +0 -469
  128. package/benchmarks/vector-search.bench.ts +0 -449
  129. package/docs/AGENTDB-INTEGRATION.md +0 -388
  130. package/docs/CROSS_PLATFORM.md +0 -505
  131. package/docs/WINDOWS_SUPPORT.md +0 -422
  132. package/examples/agentdb-example.ts +0 -345
  133. package/examples/cross-platform-usage.ts +0 -326
  134. package/framework/benchmark.ts +0 -112
  135. package/src/agentdb-adapter.ts +0 -884
  136. package/src/agentdb-backend.test.ts +0 -339
  137. package/src/agentdb-backend.ts +0 -1016
  138. package/src/application/commands/delete-memory.command.ts +0 -172
  139. package/src/application/commands/store-memory.command.ts +0 -103
  140. package/src/application/index.ts +0 -36
  141. package/src/application/queries/search-memory.query.ts +0 -237
  142. package/src/application/services/memory-application-service.ts +0 -236
  143. package/src/cache-manager.ts +0 -516
  144. package/src/database-provider.test.ts +0 -364
  145. package/src/database-provider.ts +0 -511
  146. package/src/domain/entities/memory-entry.ts +0 -289
  147. package/src/domain/index.ts +0 -35
  148. package/src/domain/repositories/memory-repository.interface.ts +0 -120
  149. package/src/domain/services/memory-domain-service.ts +0 -403
  150. package/src/hnsw-index.ts +0 -1013
  151. package/src/hybrid-backend.test.ts +0 -399
  152. package/src/hybrid-backend.ts +0 -694
  153. package/src/index.ts +0 -515
  154. package/src/infrastructure/index.ts +0 -23
  155. package/src/infrastructure/repositories/hybrid-memory-repository.ts +0 -516
  156. package/src/migration.ts +0 -669
  157. package/src/query-builder.ts +0 -542
  158. package/src/sqlite-backend.ts +0 -732
  159. package/src/sqljs-backend.ts +0 -763
  160. package/src/types.ts +0 -727
  161. package/tsconfig.json +0 -9
  162. package/tsconfig.tsbuildinfo +0 -1
  163. 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;