agentdb 1.5.8 → 1.6.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 (62) hide show
  1. package/README.md +11 -11
  2. package/dist/agentdb.min.js +4 -4
  3. package/dist/cli/agentdb-cli.d.ts +29 -0
  4. package/dist/cli/agentdb-cli.d.ts.map +1 -1
  5. package/dist/cli/agentdb-cli.js +1009 -34
  6. package/dist/cli/agentdb-cli.js.map +1 -1
  7. package/dist/controllers/ContextSynthesizer.d.ts +65 -0
  8. package/dist/controllers/ContextSynthesizer.d.ts.map +1 -0
  9. package/dist/controllers/ContextSynthesizer.js +208 -0
  10. package/dist/controllers/ContextSynthesizer.js.map +1 -0
  11. package/dist/controllers/MMRDiversityRanker.d.ts +50 -0
  12. package/dist/controllers/MMRDiversityRanker.d.ts.map +1 -0
  13. package/dist/controllers/MMRDiversityRanker.js +130 -0
  14. package/dist/controllers/MMRDiversityRanker.js.map +1 -0
  15. package/dist/controllers/MetadataFilter.d.ts +70 -0
  16. package/dist/controllers/MetadataFilter.d.ts.map +1 -0
  17. package/dist/controllers/MetadataFilter.js +243 -0
  18. package/dist/controllers/MetadataFilter.js.map +1 -0
  19. package/dist/controllers/QUICClient.d.ts +109 -0
  20. package/dist/controllers/QUICClient.d.ts.map +1 -0
  21. package/dist/controllers/QUICClient.js +299 -0
  22. package/dist/controllers/QUICClient.js.map +1 -0
  23. package/dist/controllers/QUICServer.d.ts +121 -0
  24. package/dist/controllers/QUICServer.d.ts.map +1 -0
  25. package/dist/controllers/QUICServer.js +383 -0
  26. package/dist/controllers/QUICServer.js.map +1 -0
  27. package/dist/controllers/SyncCoordinator.d.ts +120 -0
  28. package/dist/controllers/SyncCoordinator.d.ts.map +1 -0
  29. package/dist/controllers/SyncCoordinator.js +441 -0
  30. package/dist/controllers/SyncCoordinator.js.map +1 -0
  31. package/dist/controllers/WASMVectorSearch.d.ts.map +1 -1
  32. package/dist/controllers/WASMVectorSearch.js +10 -2
  33. package/dist/controllers/WASMVectorSearch.js.map +1 -1
  34. package/dist/controllers/index.d.ts +12 -0
  35. package/dist/controllers/index.d.ts.map +1 -1
  36. package/dist/controllers/index.js +6 -0
  37. package/dist/controllers/index.js.map +1 -1
  38. package/dist/db-fallback.d.ts.map +1 -1
  39. package/dist/db-fallback.js +14 -11
  40. package/dist/db-fallback.js.map +1 -1
  41. package/dist/examples/quic-sync-example.d.ts +9 -0
  42. package/dist/examples/quic-sync-example.d.ts.map +1 -0
  43. package/dist/examples/quic-sync-example.js +169 -0
  44. package/dist/examples/quic-sync-example.js.map +1 -0
  45. package/dist/types/quic.d.ts +518 -0
  46. package/dist/types/quic.d.ts.map +1 -0
  47. package/dist/types/quic.js +272 -0
  48. package/dist/types/quic.js.map +1 -0
  49. package/package.json +9 -3
  50. package/src/browser-entry.js +41 -6
  51. package/src/cli/agentdb-cli.ts +1114 -33
  52. package/src/controllers/ContextSynthesizer.ts +285 -0
  53. package/src/controllers/MMRDiversityRanker.ts +187 -0
  54. package/src/controllers/MetadataFilter.ts +280 -0
  55. package/src/controllers/QUICClient.ts +413 -0
  56. package/src/controllers/QUICServer.ts +498 -0
  57. package/src/controllers/SyncCoordinator.ts +597 -0
  58. package/src/controllers/WASMVectorSearch.ts +11 -2
  59. package/src/controllers/index.ts +12 -0
  60. package/src/db-fallback.ts +13 -10
  61. package/src/examples/quic-sync-example.ts +198 -0
  62. package/src/types/quic.ts +772 -0
@@ -0,0 +1,498 @@
1
+ /**
2
+ * QUICServer - QUIC Protocol Server for AgentDB Synchronization
3
+ *
4
+ * Implements a QUIC server for receiving and handling synchronization requests
5
+ * from remote AgentDB instances. Supports episodes, skills, and edge synchronization.
6
+ *
7
+ * Features:
8
+ * - Start/stop server lifecycle management
9
+ * - Client connection handling
10
+ * - Authentication and authorization
11
+ * - Rate limiting per client
12
+ * - Sync request processing (episodes, skills, edges)
13
+ * - Comprehensive error handling and logging
14
+ */
15
+
16
+ import chalk from 'chalk';
17
+
18
+ // Database type from db-fallback
19
+ type Database = any;
20
+
21
+ export interface QUICServerConfig {
22
+ host?: string;
23
+ port?: number;
24
+ maxConnections?: number;
25
+ authToken?: string;
26
+ rateLimit?: {
27
+ maxRequestsPerMinute: number;
28
+ maxBytesPerMinute: number;
29
+ };
30
+ tlsConfig?: {
31
+ cert?: string;
32
+ key?: string;
33
+ ca?: string;
34
+ };
35
+ }
36
+
37
+ export interface SyncRequest {
38
+ type: 'episodes' | 'skills' | 'edges' | 'full';
39
+ since?: number; // Timestamp for incremental sync
40
+ filters?: Record<string, any>;
41
+ batchSize?: number;
42
+ }
43
+
44
+ export interface SyncResponse {
45
+ success: boolean;
46
+ data?: any;
47
+ error?: string;
48
+ nextCursor?: number;
49
+ hasMore?: boolean;
50
+ count?: number;
51
+ }
52
+
53
+ interface ClientConnection {
54
+ id: string;
55
+ address: string;
56
+ connectedAt: number;
57
+ requestCount: number;
58
+ bytesReceived: number;
59
+ lastRequestAt: number;
60
+ }
61
+
62
+ interface RateLimitState {
63
+ requestCount: number;
64
+ bytesTransferred: number;
65
+ windowStart: number;
66
+ }
67
+
68
+ export class QUICServer {
69
+ private db: Database;
70
+ private config: Required<QUICServerConfig>;
71
+ private isRunning: boolean = false;
72
+ private connections: Map<string, ClientConnection> = new Map();
73
+ private rateLimitState: Map<string, RateLimitState> = new Map();
74
+ private server: any = null;
75
+ private cleanupInterval: NodeJS.Timeout | null = null;
76
+
77
+ constructor(db: Database, config: QUICServerConfig = {}) {
78
+ this.db = db;
79
+ this.config = {
80
+ host: config.host || '0.0.0.0',
81
+ port: config.port || 4433,
82
+ maxConnections: config.maxConnections || 100,
83
+ authToken: config.authToken || '',
84
+ rateLimit: config.rateLimit || {
85
+ maxRequestsPerMinute: 60,
86
+ maxBytesPerMinute: 10 * 1024 * 1024, // 10MB
87
+ },
88
+ tlsConfig: config.tlsConfig || {},
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Start the QUIC server
94
+ */
95
+ async start(): Promise<void> {
96
+ if (this.isRunning) {
97
+ console.log(chalk.yellow('⚠️ QUIC server is already running'));
98
+ return;
99
+ }
100
+
101
+ try {
102
+ console.log(chalk.blue('🚀 Starting QUIC server...'));
103
+ console.log(chalk.gray(` Host: ${this.config.host}`));
104
+ console.log(chalk.gray(` Port: ${this.config.port}`));
105
+
106
+ // Note: Actual QUIC implementation would use a library like @fails-components/webtransport
107
+ // or node-quic. This is a reference implementation showing the interface.
108
+
109
+ // Initialize server state
110
+ this.isRunning = true;
111
+ this.startCleanupInterval();
112
+
113
+ console.log(chalk.green('✓ QUIC server started successfully'));
114
+ console.log(chalk.gray(` Max connections: ${this.config.maxConnections}`));
115
+ console.log(chalk.gray(` Rate limit: ${this.config.rateLimit.maxRequestsPerMinute} req/min`));
116
+ } catch (error) {
117
+ const err = error as Error;
118
+ console.error(chalk.red('✗ Failed to start QUIC server:'), err.message);
119
+ throw new Error(`QUIC server start failed: ${err.message}`);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Stop the QUIC server
125
+ */
126
+ async stop(): Promise<void> {
127
+ if (!this.isRunning) {
128
+ console.log(chalk.yellow('⚠️ QUIC server is not running'));
129
+ return;
130
+ }
131
+
132
+ try {
133
+ console.log(chalk.blue('🛑 Stopping QUIC server...'));
134
+
135
+ // Close all connections
136
+ for (const [clientId, connection] of this.connections.entries()) {
137
+ console.log(chalk.gray(` Closing connection: ${clientId}`));
138
+ // Close connection logic here
139
+ }
140
+ this.connections.clear();
141
+ this.rateLimitState.clear();
142
+
143
+ // Stop cleanup interval
144
+ if (this.cleanupInterval) {
145
+ clearInterval(this.cleanupInterval);
146
+ this.cleanupInterval = null;
147
+ }
148
+
149
+ // Close server
150
+ if (this.server) {
151
+ // await this.server.close();
152
+ this.server = null;
153
+ }
154
+
155
+ this.isRunning = false;
156
+ console.log(chalk.green('✓ QUIC server stopped successfully'));
157
+ } catch (error) {
158
+ const err = error as Error;
159
+ console.error(chalk.red('✗ Error stopping QUIC server:'), err.message);
160
+ throw new Error(`QUIC server stop failed: ${err.message}`);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Handle incoming client connection
166
+ */
167
+ private async handleConnection(clientId: string, address: string): Promise<boolean> {
168
+ // Check max connections
169
+ if (this.connections.size >= this.config.maxConnections) {
170
+ console.log(chalk.yellow(`⚠️ Max connections reached, rejecting ${clientId}`));
171
+ return false;
172
+ }
173
+
174
+ // Register connection
175
+ const connection: ClientConnection = {
176
+ id: clientId,
177
+ address,
178
+ connectedAt: Date.now(),
179
+ requestCount: 0,
180
+ bytesReceived: 0,
181
+ lastRequestAt: 0,
182
+ };
183
+
184
+ this.connections.set(clientId, connection);
185
+ console.log(chalk.green(`✓ Client connected: ${clientId} from ${address}`));
186
+ console.log(chalk.gray(` Active connections: ${this.connections.size}`));
187
+
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * Authenticate client request
193
+ */
194
+ private authenticate(clientId: string, authToken: string): boolean {
195
+ if (!this.config.authToken) {
196
+ return true; // No auth required
197
+ }
198
+
199
+ const isValid = authToken === this.config.authToken;
200
+ if (!isValid) {
201
+ console.log(chalk.red(`✗ Authentication failed for client: ${clientId}`));
202
+ }
203
+ return isValid;
204
+ }
205
+
206
+ /**
207
+ * Check rate limits for client
208
+ */
209
+ private checkRateLimit(clientId: string, requestSize: number): boolean {
210
+ const now = Date.now();
211
+ let state = this.rateLimitState.get(clientId);
212
+
213
+ if (!state || now - state.windowStart > 60000) {
214
+ // New window
215
+ state = {
216
+ requestCount: 0,
217
+ bytesTransferred: 0,
218
+ windowStart: now,
219
+ };
220
+ this.rateLimitState.set(clientId, state);
221
+ }
222
+
223
+ // Check limits
224
+ if (state.requestCount >= this.config.rateLimit.maxRequestsPerMinute) {
225
+ console.log(chalk.yellow(`⚠️ Rate limit exceeded (requests) for ${clientId}`));
226
+ return false;
227
+ }
228
+
229
+ if (state.bytesTransferred + requestSize > this.config.rateLimit.maxBytesPerMinute) {
230
+ console.log(chalk.yellow(`⚠️ Rate limit exceeded (bytes) for ${clientId}`));
231
+ return false;
232
+ }
233
+
234
+ // Update state
235
+ state.requestCount++;
236
+ state.bytesTransferred += requestSize;
237
+
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * Process sync request from client
243
+ */
244
+ async processSyncRequest(
245
+ clientId: string,
246
+ request: SyncRequest,
247
+ authToken: string
248
+ ): Promise<SyncResponse> {
249
+ try {
250
+ // Authenticate
251
+ if (!this.authenticate(clientId, authToken)) {
252
+ return {
253
+ success: false,
254
+ error: 'Authentication failed',
255
+ };
256
+ }
257
+
258
+ // Check rate limit
259
+ const requestSize = JSON.stringify(request).length;
260
+ if (!this.checkRateLimit(clientId, requestSize)) {
261
+ return {
262
+ success: false,
263
+ error: 'Rate limit exceeded',
264
+ };
265
+ }
266
+
267
+ // Update connection stats
268
+ const connection = this.connections.get(clientId);
269
+ if (connection) {
270
+ connection.requestCount++;
271
+ connection.bytesReceived += requestSize;
272
+ connection.lastRequestAt = Date.now();
273
+ }
274
+
275
+ console.log(chalk.blue(`📥 Processing sync request from ${clientId}`));
276
+ console.log(chalk.gray(` Type: ${request.type}`));
277
+ console.log(chalk.gray(` Since: ${request.since || 'full sync'}`));
278
+
279
+ // Process based on type
280
+ let data: any;
281
+ let count = 0;
282
+
283
+ switch (request.type) {
284
+ case 'episodes':
285
+ data = await this.syncEpisodes(request);
286
+ count = data.length;
287
+ break;
288
+ case 'skills':
289
+ data = await this.syncSkills(request);
290
+ count = data.length;
291
+ break;
292
+ case 'edges':
293
+ data = await this.syncEdges(request);
294
+ count = data.length;
295
+ break;
296
+ case 'full':
297
+ data = await this.syncFull(request);
298
+ count = data.episodes?.length + data.skills?.length + data.edges?.length || 0;
299
+ break;
300
+ default:
301
+ return {
302
+ success: false,
303
+ error: `Unknown sync type: ${request.type}`,
304
+ };
305
+ }
306
+
307
+ console.log(chalk.green(`✓ Sync completed: ${count} items sent`));
308
+
309
+ return {
310
+ success: true,
311
+ data,
312
+ count,
313
+ hasMore: false, // Could implement pagination here
314
+ };
315
+ } catch (error) {
316
+ const err = error as Error;
317
+ console.error(chalk.red('✗ Sync request failed:'), err.message);
318
+ return {
319
+ success: false,
320
+ error: err.message,
321
+ };
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Sync episodes data
327
+ */
328
+ private async syncEpisodes(request: SyncRequest): Promise<any[]> {
329
+ const { since, filters, batchSize = 1000 } = request;
330
+
331
+ let query = 'SELECT * FROM episodes WHERE 1=1';
332
+ const params: any[] = [];
333
+
334
+ if (since) {
335
+ query += ' AND ts > ?';
336
+ params.push(since);
337
+ }
338
+
339
+ // Apply filters
340
+ if (filters) {
341
+ if (filters.sessionId) {
342
+ query += ' AND session_id = ?';
343
+ params.push(filters.sessionId);
344
+ }
345
+ if (filters.success !== undefined) {
346
+ query += ' AND success = ?';
347
+ params.push(filters.success ? 1 : 0);
348
+ }
349
+ }
350
+
351
+ query += ` ORDER BY ts DESC LIMIT ${batchSize}`;
352
+
353
+ const stmt = this.db.prepare(query);
354
+ const rows = stmt.all(...params);
355
+
356
+ return rows.map((row: any) => ({
357
+ id: row.id,
358
+ ts: row.ts,
359
+ sessionId: row.session_id,
360
+ task: row.task,
361
+ input: row.input,
362
+ output: row.output,
363
+ critique: row.critique,
364
+ reward: row.reward,
365
+ success: row.success === 1,
366
+ latencyMs: row.latency_ms,
367
+ tokensUsed: row.tokens_used,
368
+ tags: row.tags ? JSON.parse(row.tags) : [],
369
+ metadata: row.metadata ? JSON.parse(row.metadata) : {},
370
+ }));
371
+ }
372
+
373
+ /**
374
+ * Sync skills data
375
+ */
376
+ private async syncSkills(request: SyncRequest): Promise<any[]> {
377
+ const { since, batchSize = 1000 } = request;
378
+
379
+ let query = 'SELECT * FROM skills WHERE 1=1';
380
+ const params: any[] = [];
381
+
382
+ if (since) {
383
+ query += ' AND ts > ?';
384
+ params.push(since);
385
+ }
386
+
387
+ query += ` ORDER BY ts DESC LIMIT ${batchSize}`;
388
+
389
+ const stmt = this.db.prepare(query);
390
+ const rows = stmt.all(...params);
391
+
392
+ return rows.map((row: any) => ({
393
+ id: row.id,
394
+ ts: row.ts,
395
+ name: row.name,
396
+ description: row.description,
397
+ code: row.code,
398
+ successRate: row.success_rate,
399
+ usageCount: row.usage_count,
400
+ avgReward: row.avg_reward,
401
+ tags: row.tags ? JSON.parse(row.tags) : [],
402
+ metadata: row.metadata ? JSON.parse(row.metadata) : {},
403
+ }));
404
+ }
405
+
406
+ /**
407
+ * Sync edges (skill relationships)
408
+ */
409
+ private async syncEdges(request: SyncRequest): Promise<any[]> {
410
+ const { since, batchSize = 1000 } = request;
411
+
412
+ let query = 'SELECT * FROM skill_edges WHERE 1=1';
413
+ const params: any[] = [];
414
+
415
+ if (since) {
416
+ query += ' AND ts > ?';
417
+ params.push(since);
418
+ }
419
+
420
+ query += ` ORDER BY ts DESC LIMIT ${batchSize}`;
421
+
422
+ const stmt = this.db.prepare(query);
423
+ const rows = stmt.all(...params);
424
+
425
+ return rows.map((row: any) => ({
426
+ id: row.id,
427
+ ts: row.ts,
428
+ fromSkillId: row.from_skill_id,
429
+ toSkillId: row.to_skill_id,
430
+ weight: row.weight,
431
+ coOccurrences: row.co_occurrences,
432
+ }));
433
+ }
434
+
435
+ /**
436
+ * Full sync of all data
437
+ */
438
+ private async syncFull(request: SyncRequest): Promise<any> {
439
+ const [episodes, skills, edges] = await Promise.all([
440
+ this.syncEpisodes(request),
441
+ this.syncSkills(request),
442
+ this.syncEdges(request),
443
+ ]);
444
+
445
+ return {
446
+ episodes,
447
+ skills,
448
+ edges,
449
+ };
450
+ }
451
+
452
+ /**
453
+ * Start cleanup interval for stale connections
454
+ */
455
+ private startCleanupInterval(): void {
456
+ this.cleanupInterval = setInterval(() => {
457
+ const now = Date.now();
458
+ const staleThreshold = 5 * 60 * 1000; // 5 minutes
459
+
460
+ for (const [clientId, connection] of this.connections.entries()) {
461
+ if (now - connection.lastRequestAt > staleThreshold && connection.requestCount > 0) {
462
+ console.log(chalk.gray(`🧹 Removing stale connection: ${clientId}`));
463
+ this.connections.delete(clientId);
464
+ this.rateLimitState.delete(clientId);
465
+ }
466
+ }
467
+ }, 60000); // Run every minute
468
+ }
469
+
470
+ /**
471
+ * Get server status
472
+ */
473
+ getStatus(): {
474
+ isRunning: boolean;
475
+ activeConnections: number;
476
+ totalRequests: number;
477
+ config: QUICServerConfig;
478
+ } {
479
+ let totalRequests = 0;
480
+ for (const connection of this.connections.values()) {
481
+ totalRequests += connection.requestCount;
482
+ }
483
+
484
+ return {
485
+ isRunning: this.isRunning,
486
+ activeConnections: this.connections.size,
487
+ totalRequests,
488
+ config: this.config,
489
+ };
490
+ }
491
+
492
+ /**
493
+ * Get connection info
494
+ */
495
+ getConnections(): ClientConnection[] {
496
+ return Array.from(this.connections.values());
497
+ }
498
+ }