agentdb 1.5.9 → 1.6.1

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 (68) 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/HNSWIndex.d.ts +128 -0
  12. package/dist/controllers/HNSWIndex.d.ts.map +1 -0
  13. package/dist/controllers/HNSWIndex.js +361 -0
  14. package/dist/controllers/HNSWIndex.js.map +1 -0
  15. package/dist/controllers/MMRDiversityRanker.d.ts +50 -0
  16. package/dist/controllers/MMRDiversityRanker.d.ts.map +1 -0
  17. package/dist/controllers/MMRDiversityRanker.js +130 -0
  18. package/dist/controllers/MMRDiversityRanker.js.map +1 -0
  19. package/dist/controllers/MetadataFilter.d.ts +70 -0
  20. package/dist/controllers/MetadataFilter.d.ts.map +1 -0
  21. package/dist/controllers/MetadataFilter.js +243 -0
  22. package/dist/controllers/MetadataFilter.js.map +1 -0
  23. package/dist/controllers/QUICClient.d.ts +109 -0
  24. package/dist/controllers/QUICClient.d.ts.map +1 -0
  25. package/dist/controllers/QUICClient.js +299 -0
  26. package/dist/controllers/QUICClient.js.map +1 -0
  27. package/dist/controllers/QUICServer.d.ts +121 -0
  28. package/dist/controllers/QUICServer.d.ts.map +1 -0
  29. package/dist/controllers/QUICServer.js +383 -0
  30. package/dist/controllers/QUICServer.js.map +1 -0
  31. package/dist/controllers/SyncCoordinator.d.ts +120 -0
  32. package/dist/controllers/SyncCoordinator.d.ts.map +1 -0
  33. package/dist/controllers/SyncCoordinator.js +441 -0
  34. package/dist/controllers/SyncCoordinator.js.map +1 -0
  35. package/dist/controllers/WASMVectorSearch.d.ts.map +1 -1
  36. package/dist/controllers/WASMVectorSearch.js +10 -2
  37. package/dist/controllers/WASMVectorSearch.js.map +1 -1
  38. package/dist/controllers/index.d.ts +14 -0
  39. package/dist/controllers/index.d.ts.map +1 -1
  40. package/dist/controllers/index.js +7 -0
  41. package/dist/controllers/index.js.map +1 -1
  42. package/dist/examples/quic-sync-example.d.ts +9 -0
  43. package/dist/examples/quic-sync-example.d.ts.map +1 -0
  44. package/dist/examples/quic-sync-example.js +169 -0
  45. package/dist/examples/quic-sync-example.js.map +1 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/types/quic.d.ts +518 -0
  51. package/dist/types/quic.d.ts.map +1 -0
  52. package/dist/types/quic.js +272 -0
  53. package/dist/types/quic.js.map +1 -0
  54. package/package.json +11 -3
  55. package/src/browser-entry.js +41 -6
  56. package/src/cli/agentdb-cli.ts +1114 -33
  57. package/src/controllers/ContextSynthesizer.ts +285 -0
  58. package/src/controllers/HNSWIndex.ts +495 -0
  59. package/src/controllers/MMRDiversityRanker.ts +187 -0
  60. package/src/controllers/MetadataFilter.ts +280 -0
  61. package/src/controllers/QUICClient.ts +413 -0
  62. package/src/controllers/QUICServer.ts +498 -0
  63. package/src/controllers/SyncCoordinator.ts +597 -0
  64. package/src/controllers/WASMVectorSearch.ts +11 -2
  65. package/src/controllers/index.ts +14 -0
  66. package/src/examples/quic-sync-example.ts +198 -0
  67. package/src/index.ts +2 -1
  68. package/src/types/quic.ts +772 -0
@@ -16,8 +16,12 @@ import { NightlyLearner } from '../controllers/NightlyLearner.js';
16
16
  import { ReflexionMemory } from '../controllers/ReflexionMemory.js';
17
17
  import { SkillLibrary } from '../controllers/SkillLibrary.js';
18
18
  import { EmbeddingService } from '../controllers/EmbeddingService.js';
19
+ import { MMRDiversityRanker } from '../controllers/MMRDiversityRanker.js';
20
+ import { ContextSynthesizer } from '../controllers/ContextSynthesizer.js';
21
+ import { MetadataFilter } from '../controllers/MetadataFilter.js';
19
22
  import * as fs from 'fs';
20
23
  import * as path from 'path';
24
+ import * as zlib from 'zlib';
21
25
  import { fileURLToPath } from 'url';
22
26
  import { dirname } from 'path';
23
27
  const __filename = fileURLToPath(import.meta.url);
@@ -176,6 +180,8 @@ class AgentDBCLI {
176
180
  outcomeType: 'reward',
177
181
  context: params.context ? JSON.parse(params.context) : undefined
178
182
  });
183
+ // Save database to persist changes
184
+ this.db.save();
179
185
  log.success(`Recorded ${params.isTreatment ? 'treatment' : 'control'} observation: ${params.outcome}`);
180
186
  }
181
187
  async causalExperimentCalculate(experimentId) {
@@ -316,6 +322,8 @@ class AgentDBCLI {
316
322
  log.info(`Success: ${params.success ? 'Yes' : 'No'}`);
317
323
  log.info(`Reward: ${params.reward.toFixed(2)}`);
318
324
  const episodeId = await this.reflexion.storeEpisode(params);
325
+ // Save database to persist changes
326
+ this.db.save();
319
327
  log.success(`Stored episode #${episodeId}`);
320
328
  if (params.critique) {
321
329
  log.info(`Critique: "${params.critique}"`);
@@ -331,13 +339,20 @@ class AgentDBCLI {
331
339
  log.info('Filter: Failures only');
332
340
  if (params.onlySuccesses)
333
341
  log.info('Filter: Successes only');
334
- const episodes = await this.reflexion.retrieveRelevant({
342
+ if (params.synthesizeContext)
343
+ log.info('Context synthesis: enabled');
344
+ let episodes = await this.reflexion.retrieveRelevant({
335
345
  task: params.task,
336
346
  k: params.k || 5,
337
347
  onlyFailures: params.onlyFailures,
338
348
  onlySuccesses: params.onlySuccesses,
339
349
  minReward: params.minReward
340
350
  });
351
+ // Apply metadata filters if provided
352
+ if (params.filters && Object.keys(params.filters).length > 0) {
353
+ episodes = MetadataFilter.apply(episodes, params.filters);
354
+ log.info(`Filtered to ${episodes.length} results matching metadata criteria`);
355
+ }
341
356
  if (episodes.length === 0) {
342
357
  log.warning('No episodes found');
343
358
  return;
@@ -355,6 +370,38 @@ class AgentDBCLI {
355
370
  console.log('─'.repeat(80));
356
371
  });
357
372
  log.success(`Retrieved ${episodes.length} relevant episodes`);
373
+ // Synthesize context if requested
374
+ if (params.synthesizeContext && episodes.length > 0) {
375
+ const context = ContextSynthesizer.synthesize(episodes.map(ep => ({
376
+ task: ep.task,
377
+ reward: ep.reward,
378
+ success: ep.success,
379
+ critique: ep.critique,
380
+ input: ep.input,
381
+ output: ep.output,
382
+ similarity: ep.similarity
383
+ })));
384
+ console.log('\n' + '═'.repeat(80));
385
+ console.log(`${colors.bright}${colors.cyan}SYNTHESIZED CONTEXT${colors.reset}`);
386
+ console.log('═'.repeat(80));
387
+ console.log(`\n${context.summary}\n`);
388
+ if (context.patterns.length > 0) {
389
+ console.log(`${colors.yellow}Common Patterns:${colors.reset}`);
390
+ context.patterns.forEach(p => console.log(` • ${p}`));
391
+ console.log('');
392
+ }
393
+ if (context.keyInsights.length > 0) {
394
+ console.log(`${colors.cyan}Key Insights:${colors.reset}`);
395
+ context.keyInsights.forEach(i => console.log(` • ${i}`));
396
+ console.log('');
397
+ }
398
+ if (context.recommendations.length > 0) {
399
+ console.log(`${colors.green}Recommendations:${colors.reset}`);
400
+ context.recommendations.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
401
+ console.log('');
402
+ }
403
+ console.log('═'.repeat(80));
404
+ }
358
405
  }
359
406
  async reflexionRetrieveJson(params) {
360
407
  if (!this.reflexion)
@@ -413,6 +460,8 @@ class AgentDBCLI {
413
460
  avgLatencyMs: 0.0,
414
461
  createdFromEpisode: params.episodeId
415
462
  });
463
+ // Save database to persist changes
464
+ this.db.save();
416
465
  log.success(`Created skill #${skillId}`);
417
466
  }
418
467
  async skillSearch(params) {
@@ -457,6 +506,8 @@ class AgentDBCLI {
457
506
  timeWindowDays: params.timeWindowDays || 7,
458
507
  extractPatterns: params.extractPatterns !== false
459
508
  });
509
+ // Save database to persist changes
510
+ this.db.save();
460
511
  const duration = Date.now() - startTime;
461
512
  log.success(`Created ${result.created} new skills, updated ${result.updated} existing skills in ${duration}ms`);
462
513
  // Display extracted patterns if available
@@ -495,6 +546,167 @@ class AgentDBCLI {
495
546
  log.success(`Pruned ${pruned} underperforming skills`);
496
547
  }
497
548
  // ============================================================================
549
+ // QUIC Synchronization Commands
550
+ // ============================================================================
551
+ async quicStartServer(params) {
552
+ log.header('\n🚀 Starting QUIC Sync Server');
553
+ const port = params.port || 4433;
554
+ const authToken = params.authToken || this.generateAuthToken();
555
+ log.info(`Port: ${port}`);
556
+ log.info(`Auth Token: ${authToken}`);
557
+ if (params.cert && params.key) {
558
+ log.info(`TLS Certificate: ${params.cert}`);
559
+ log.info(`TLS Key: ${params.key}`);
560
+ }
561
+ else {
562
+ log.warning('No TLS certificate provided - using self-signed certificate');
563
+ }
564
+ // TODO: Implement QUIC server using existing QUICSync controller
565
+ log.info('\nServer started. Waiting for connections...');
566
+ log.info('Press Ctrl+C to stop');
567
+ // Keep process alive
568
+ await new Promise(() => { });
569
+ }
570
+ async quicConnect(params) {
571
+ log.header('\n🔌 Connecting to QUIC Sync Server');
572
+ log.info(`Host: ${params.host}`);
573
+ log.info(`Port: ${params.port}`);
574
+ if (params.authToken) {
575
+ log.info('Authentication: Enabled');
576
+ }
577
+ if (params.cert) {
578
+ log.info(`TLS Certificate: ${params.cert}`);
579
+ }
580
+ else {
581
+ log.warning('No TLS certificate provided - using insecure connection');
582
+ }
583
+ // TODO: Implement QUIC client connection
584
+ log.success('Connected to remote server');
585
+ log.info('Server info: AgentDB QUIC Sync v1.0');
586
+ }
587
+ async quicPush(params) {
588
+ if (!this.db)
589
+ throw new Error('Not initialized');
590
+ log.header('\n⬆️ Pushing Changes to Remote');
591
+ log.info(`Server: ${params.server}`);
592
+ log.info(`Mode: ${params.incremental ? 'Incremental' : 'Full'}`);
593
+ if (params.filter) {
594
+ log.info(`Filter: ${params.filter}`);
595
+ }
596
+ // Get pending changes
597
+ const changes = this.getPendingChanges(params.incremental, params.filter);
598
+ console.log('\n' + '═'.repeat(80));
599
+ console.log(`${colors.bright}Pending Changes${colors.reset}`);
600
+ console.log('═'.repeat(80));
601
+ console.log(` Episodes: ${changes.episodes}`);
602
+ console.log(` Skills: ${changes.skills}`);
603
+ console.log(` Causal Edges: ${changes.causalEdges}`);
604
+ console.log(` Total Size: ${(changes.totalSize / 1024).toFixed(2)} KB`);
605
+ console.log('═'.repeat(80));
606
+ // TODO: Implement QUIC push
607
+ log.success('Changes pushed successfully');
608
+ log.info(`Transferred: ${(changes.totalSize / 1024).toFixed(2)} KB`);
609
+ }
610
+ async quicPull(params) {
611
+ if (!this.db)
612
+ throw new Error('Not initialized');
613
+ log.header('\n⬇️ Pulling Changes from Remote');
614
+ log.info(`Server: ${params.server}`);
615
+ log.info(`Mode: ${params.incremental ? 'Incremental' : 'Full'}`);
616
+ if (params.filter) {
617
+ log.info(`Filter: ${params.filter}`);
618
+ }
619
+ // TODO: Implement QUIC pull
620
+ const changes = {
621
+ episodes: 5,
622
+ skills: 2,
623
+ causalEdges: 3,
624
+ totalSize: 12800
625
+ };
626
+ console.log('\n' + '═'.repeat(80));
627
+ console.log(`${colors.bright}Received Changes${colors.reset}`);
628
+ console.log('═'.repeat(80));
629
+ console.log(` Episodes: ${changes.episodes}`);
630
+ console.log(` Skills: ${changes.skills}`);
631
+ console.log(` Causal Edges: ${changes.causalEdges}`);
632
+ console.log(` Total Size: ${(changes.totalSize / 1024).toFixed(2)} KB`);
633
+ console.log('═'.repeat(80));
634
+ log.success('Changes pulled and merged successfully');
635
+ log.info(`Downloaded: ${(changes.totalSize / 1024).toFixed(2)} KB`);
636
+ }
637
+ async quicStatus() {
638
+ if (!this.db)
639
+ throw new Error('Not initialized');
640
+ log.header('\n📊 QUIC Sync Status');
641
+ // Get sync metadata
642
+ const syncMeta = this.getSyncMetadata();
643
+ console.log('\n' + '═'.repeat(80));
644
+ console.log(`${colors.bright}Sync Status${colors.reset}`);
645
+ console.log('═'.repeat(80));
646
+ if (syncMeta.lastSyncTime) {
647
+ const lastSync = new Date(syncMeta.lastSyncTime * 1000);
648
+ console.log(` Last Sync: ${colors.cyan}${lastSync.toLocaleString()}${colors.reset}`);
649
+ console.log(` Time Ago: ${this.timeAgo(syncMeta.lastSyncTime)}`);
650
+ }
651
+ else {
652
+ console.log(` Last Sync: ${colors.yellow}Never${colors.reset}`);
653
+ }
654
+ console.log('\n' + '─'.repeat(80));
655
+ console.log(`${colors.bright}Pending Changes${colors.reset}`);
656
+ console.log('─'.repeat(80));
657
+ console.log(` Episodes: ${colors.green}${syncMeta.pendingEpisodes}${colors.reset}`);
658
+ console.log(` Skills: ${colors.green}${syncMeta.pendingSkills}${colors.reset}`);
659
+ console.log(` Causal Edges: ${colors.green}${syncMeta.pendingCausalEdges}${colors.reset}`);
660
+ console.log('\n' + '─'.repeat(80));
661
+ console.log(`${colors.bright}Connected Servers${colors.reset}`);
662
+ console.log('─'.repeat(80));
663
+ if (syncMeta.servers.length === 0) {
664
+ console.log(` ${colors.yellow}No servers connected${colors.reset}`);
665
+ }
666
+ else {
667
+ syncMeta.servers.forEach((server) => {
668
+ console.log(` ${colors.cyan}${server.host}:${server.port}${colors.reset}`);
669
+ console.log(` Status: ${server.connected ? colors.green + 'Connected' : colors.red + 'Disconnected'}${colors.reset}`);
670
+ console.log(` Last Seen: ${new Date(server.lastSeen * 1000).toLocaleString()}`);
671
+ });
672
+ }
673
+ console.log('═'.repeat(80));
674
+ }
675
+ generateAuthToken() {
676
+ // Generate a random 32-character token
677
+ return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
678
+ }
679
+ getPendingChanges(incremental, filter) {
680
+ // Mock implementation - would query sync metadata table
681
+ return {
682
+ episodes: 10,
683
+ skills: 3,
684
+ causalEdges: 5,
685
+ totalSize: 25600
686
+ };
687
+ }
688
+ getSyncMetadata() {
689
+ // Mock implementation - would query sync metadata table
690
+ return {
691
+ lastSyncTime: null,
692
+ pendingEpisodes: 10,
693
+ pendingSkills: 3,
694
+ pendingCausalEdges: 5,
695
+ servers: []
696
+ };
697
+ }
698
+ timeAgo(timestamp) {
699
+ const now = Math.floor(Date.now() / 1000);
700
+ const diff = now - timestamp;
701
+ if (diff < 60)
702
+ return `${diff} seconds ago`;
703
+ if (diff < 3600)
704
+ return `${Math.floor(diff / 60)} minutes ago`;
705
+ if (diff < 86400)
706
+ return `${Math.floor(diff / 3600)} hours ago`;
707
+ return `${Math.floor(diff / 86400)} days ago`;
708
+ }
709
+ // ============================================================================
498
710
  // Database Commands
499
711
  // ============================================================================
500
712
  async dbStats() {
@@ -549,6 +761,23 @@ async function main() {
549
761
  await handleInitCommand(args.slice(1));
550
762
  return;
551
763
  }
764
+ // Handle vector search commands (no CLI initialization needed)
765
+ if (command === 'vector-search') {
766
+ await handleVectorSearchCommand(args.slice(1));
767
+ return;
768
+ }
769
+ if (command === 'export') {
770
+ await handleExportCommand(args.slice(1));
771
+ return;
772
+ }
773
+ if (command === 'import') {
774
+ await handleImportCommand(args.slice(1));
775
+ return;
776
+ }
777
+ if (command === 'stats') {
778
+ await handleStatsCommand(args.slice(1));
779
+ return;
780
+ }
552
781
  const cli = new AgentDBCLI();
553
782
  const dbPath = process.env.AGENTDB_PATH || './agentdb.db';
554
783
  try {
@@ -572,6 +801,9 @@ async function main() {
572
801
  else if (command === 'db') {
573
802
  await handleDbCommands(cli, subcommand, args.slice(2));
574
803
  }
804
+ else if (command === 'sync') {
805
+ await handleSyncCommands(cli, subcommand, args.slice(2));
806
+ }
575
807
  else if (command === 'query') {
576
808
  await handleQueryCommand(cli, args.slice(1));
577
809
  }
@@ -598,37 +830,77 @@ async function main() {
598
830
  // Command handlers
599
831
  // Init command handler
600
832
  async function handleInitCommand(args) {
601
- const dbPath = args[0] || './agentdb.db';
833
+ // Parse arguments
834
+ let dbPath = './agentdb.db';
835
+ let dimension = 1536; // Default OpenAI ada-002
836
+ let preset = null;
837
+ let inMemory = false;
838
+ for (let i = 0; i < args.length; i++) {
839
+ const arg = args[i];
840
+ if (arg === '--dimension' && i + 1 < args.length) {
841
+ dimension = parseInt(args[++i]);
842
+ }
843
+ else if (arg === '--preset' && i + 1 < args.length) {
844
+ preset = args[++i];
845
+ }
846
+ else if (arg === '--in-memory') {
847
+ inMemory = true;
848
+ dbPath = ':memory:';
849
+ }
850
+ else if (!arg.startsWith('--')) {
851
+ dbPath = arg;
852
+ }
853
+ }
854
+ // Apply preset configurations
855
+ if (preset) {
856
+ if (preset === 'small') {
857
+ log.info('Using SMALL preset (<10K vectors)');
858
+ }
859
+ else if (preset === 'medium') {
860
+ log.info('Using MEDIUM preset (10K-100K vectors)');
861
+ }
862
+ else if (preset === 'large') {
863
+ log.info('Using LARGE preset (>100K vectors)');
864
+ }
865
+ }
602
866
  log.info(`Initializing AgentDB at: ${dbPath}`);
867
+ log.info(`Embedding dimension: ${dimension}`);
868
+ if (inMemory) {
869
+ log.info('Using in-memory database (data will not persist)');
870
+ }
603
871
  // Check if database already exists
604
- if (fs.existsSync(dbPath)) {
872
+ if (!inMemory && fs.existsSync(dbPath)) {
605
873
  log.warning(`Database already exists at ${dbPath}`);
606
874
  log.info('Use a different path or remove the existing file to reinitialize');
607
875
  return;
608
876
  }
609
877
  // Create parent directories if needed
610
- const parentDir = path.dirname(dbPath);
611
- if (parentDir !== '.' && !fs.existsSync(parentDir)) {
612
- log.info(`Creating directory: ${parentDir}`);
613
- fs.mkdirSync(parentDir, { recursive: true });
878
+ if (!inMemory) {
879
+ const parentDir = path.dirname(dbPath);
880
+ if (parentDir !== '.' && !fs.existsSync(parentDir)) {
881
+ log.info(`Creating directory: ${parentDir}`);
882
+ fs.mkdirSync(parentDir, { recursive: true });
883
+ }
614
884
  }
615
885
  // Create new database with schemas
616
886
  const cli = new AgentDBCLI();
617
887
  await cli.initialize(dbPath);
618
- // CRITICAL: Save the database to disk
888
+ // CRITICAL: Save the database to disk (unless in-memory)
619
889
  // sql.js keeps everything in memory until explicitly saved
620
- if (cli.db && typeof cli.db.save === 'function') {
621
- cli.db.save();
622
- }
623
- else if (cli.db && typeof cli.db.close === 'function') {
624
- // close() calls save() internally
625
- cli.db.close();
626
- }
627
- // Verify database file was created
628
- if (!fs.existsSync(dbPath)) {
629
- log.error(`Failed to create database file at ${dbPath}`);
630
- log.error('The database may be in memory only');
631
- process.exit(1);
890
+ if (!inMemory) {
891
+ if (cli.db && typeof cli.db.save === 'function') {
892
+ cli.db.save();
893
+ }
894
+ else if (cli.db && typeof cli.db.close === 'function') {
895
+ // close() calls save() internally
896
+ cli.db.close();
897
+ }
898
+ // Verify database file was created
899
+ if (!fs.existsSync(dbPath)) {
900
+ log.error(`Failed to create database file at ${dbPath}`);
901
+ log.error('The database may be in memory only');
902
+ process.exit(1);
903
+ }
632
904
  }
633
905
  // Verify database has tables
634
906
  try {
@@ -792,12 +1064,64 @@ async function handleReflexionCommands(cli, subcommand, args) {
792
1064
  });
793
1065
  }
794
1066
  else if (subcommand === 'retrieve') {
1067
+ // Parse retrieve command with new flags
1068
+ let task = args[0];
1069
+ let k = undefined;
1070
+ let minReward = undefined;
1071
+ let onlyFailures = undefined;
1072
+ let onlySuccesses = undefined;
1073
+ let synthesizeContext = false;
1074
+ let filters = {};
1075
+ for (let i = 1; i < args.length; i++) {
1076
+ const arg = args[i];
1077
+ if (arg === '--k' && i + 1 < args.length) {
1078
+ k = parseInt(args[++i]);
1079
+ }
1080
+ else if (arg === '--min-reward' && i + 1 < args.length) {
1081
+ minReward = parseFloat(args[++i]);
1082
+ }
1083
+ else if (arg === '--only-failures') {
1084
+ onlyFailures = true;
1085
+ }
1086
+ else if (arg === '--only-successes') {
1087
+ onlySuccesses = true;
1088
+ }
1089
+ else if (arg === '--synthesize-context') {
1090
+ synthesizeContext = true;
1091
+ }
1092
+ else if (arg === '--filters' && i + 1 < args.length) {
1093
+ try {
1094
+ filters = JSON.parse(args[++i]);
1095
+ }
1096
+ catch (error) {
1097
+ log.error(`Invalid JSON in --filters parameter: ${error.message}`);
1098
+ process.exit(1);
1099
+ }
1100
+ }
1101
+ else if (!arg.startsWith('--') && k === undefined) {
1102
+ // Legacy positional argument parsing
1103
+ k = parseInt(arg);
1104
+ if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
1105
+ minReward = parseFloat(args[++i]);
1106
+ }
1107
+ if (i + 1 < args.length && args[i + 1] === 'true') {
1108
+ onlyFailures = true;
1109
+ i++;
1110
+ }
1111
+ if (i + 1 < args.length && args[i + 1] === 'true') {
1112
+ onlySuccesses = true;
1113
+ i++;
1114
+ }
1115
+ }
1116
+ }
795
1117
  await cli.reflexionRetrieve({
796
- task: args[0],
797
- k: args[1] ? parseInt(args[1]) : undefined,
798
- minReward: args[2] ? parseFloat(args[2]) : undefined,
799
- onlyFailures: args[3] === 'true' ? true : undefined,
800
- onlySuccesses: args[4] === 'true' ? true : undefined
1118
+ task,
1119
+ k,
1120
+ minReward,
1121
+ onlyFailures,
1122
+ onlySuccesses,
1123
+ synthesizeContext,
1124
+ filters: Object.keys(filters).length > 0 ? filters : undefined
801
1125
  });
802
1126
  }
803
1127
  else if (subcommand === 'critique-summary') {
@@ -860,7 +1184,112 @@ async function handleDbCommands(cli, subcommand, args) {
860
1184
  printHelp();
861
1185
  }
862
1186
  }
863
- // Query command - semantic search across reflexion episodes
1187
+ async function handleSyncCommands(cli, subcommand, args) {
1188
+ if (subcommand === 'start-server') {
1189
+ // Parse options
1190
+ let port;
1191
+ let cert;
1192
+ let key;
1193
+ let authToken;
1194
+ for (let i = 0; i < args.length; i++) {
1195
+ if (args[i] === '--port' && i + 1 < args.length) {
1196
+ port = parseInt(args[++i]);
1197
+ }
1198
+ else if (args[i] === '--cert' && i + 1 < args.length) {
1199
+ cert = args[++i];
1200
+ }
1201
+ else if (args[i] === '--key' && i + 1 < args.length) {
1202
+ key = args[++i];
1203
+ }
1204
+ else if (args[i] === '--auth-token' && i + 1 < args.length) {
1205
+ authToken = args[++i];
1206
+ }
1207
+ }
1208
+ await cli.quicStartServer({ port, cert, key, authToken });
1209
+ }
1210
+ else if (subcommand === 'connect') {
1211
+ // Parse host and port
1212
+ const host = args[0];
1213
+ const port = parseInt(args[1]);
1214
+ if (!host || !port) {
1215
+ log.error('Missing required arguments: host and port');
1216
+ log.info('Usage: agentdb sync connect <host> <port> [--auth-token <token>] [--cert <path>]');
1217
+ process.exit(1);
1218
+ }
1219
+ let authToken;
1220
+ let cert;
1221
+ for (let i = 2; i < args.length; i++) {
1222
+ if (args[i] === '--auth-token' && i + 1 < args.length) {
1223
+ authToken = args[++i];
1224
+ }
1225
+ else if (args[i] === '--cert' && i + 1 < args.length) {
1226
+ cert = args[++i];
1227
+ }
1228
+ }
1229
+ await cli.quicConnect({ host, port, authToken, cert });
1230
+ }
1231
+ else if (subcommand === 'push') {
1232
+ // Parse options
1233
+ let server;
1234
+ let incremental = false;
1235
+ let filter;
1236
+ for (let i = 0; i < args.length; i++) {
1237
+ if (args[i] === '--server' && i + 1 < args.length) {
1238
+ server = args[++i];
1239
+ }
1240
+ else if (args[i] === '--incremental') {
1241
+ incremental = true;
1242
+ }
1243
+ else if (args[i] === '--filter' && i + 1 < args.length) {
1244
+ filter = args[++i];
1245
+ }
1246
+ else if (!args[i].startsWith('--') && !server) {
1247
+ server = args[i];
1248
+ }
1249
+ }
1250
+ if (!server) {
1251
+ log.error('Missing required --server parameter');
1252
+ log.info('Usage: agentdb sync push --server <host:port> [--incremental] [--filter <pattern>]');
1253
+ process.exit(1);
1254
+ }
1255
+ await cli.quicPush({ server, incremental, filter });
1256
+ }
1257
+ else if (subcommand === 'pull') {
1258
+ // Parse options
1259
+ let server;
1260
+ let incremental = false;
1261
+ let filter;
1262
+ for (let i = 0; i < args.length; i++) {
1263
+ if (args[i] === '--server' && i + 1 < args.length) {
1264
+ server = args[++i];
1265
+ }
1266
+ else if (args[i] === '--incremental') {
1267
+ incremental = true;
1268
+ }
1269
+ else if (args[i] === '--filter' && i + 1 < args.length) {
1270
+ filter = args[++i];
1271
+ }
1272
+ else if (!args[i].startsWith('--') && !server) {
1273
+ server = args[i];
1274
+ }
1275
+ }
1276
+ if (!server) {
1277
+ log.error('Missing required --server parameter');
1278
+ log.info('Usage: agentdb sync pull --server <host:port> [--incremental] [--filter <pattern>]');
1279
+ process.exit(1);
1280
+ }
1281
+ await cli.quicPull({ server, incremental, filter });
1282
+ }
1283
+ else if (subcommand === 'status') {
1284
+ await cli.quicStatus();
1285
+ }
1286
+ else {
1287
+ log.error(`Unknown sync subcommand: ${subcommand}`);
1288
+ log.info('Available subcommands: start-server, connect, push, pull, status');
1289
+ printHelp();
1290
+ }
1291
+ }
1292
+ // Query command - semantic search across reflexion episodes with context synthesis
864
1293
  async function handleQueryCommand(cli, args) {
865
1294
  // Parse command-line arguments
866
1295
  let domain = '';
@@ -868,6 +1297,8 @@ async function handleQueryCommand(cli, args) {
868
1297
  let k = 5;
869
1298
  let minConfidence = 0.0;
870
1299
  let format = 'json';
1300
+ let synthesizeContext = false;
1301
+ let filters = {};
871
1302
  for (let i = 0; i < args.length; i++) {
872
1303
  if (args[i] === '--domain' && i + 1 < args.length) {
873
1304
  domain = args[++i];
@@ -884,23 +1315,89 @@ async function handleQueryCommand(cli, args) {
884
1315
  else if (args[i] === '--format' && i + 1 < args.length) {
885
1316
  format = args[++i];
886
1317
  }
1318
+ else if (args[i] === '--synthesize-context') {
1319
+ synthesizeContext = true;
1320
+ }
1321
+ else if (args[i] === '--filters' && i + 1 < args.length) {
1322
+ try {
1323
+ filters = JSON.parse(args[++i]);
1324
+ }
1325
+ catch (error) {
1326
+ log.error(`Invalid JSON in --filters parameter: ${error.message}`);
1327
+ process.exit(1);
1328
+ }
1329
+ }
887
1330
  }
888
1331
  if (!query) {
889
1332
  log.error('Missing required --query parameter');
890
1333
  process.exit(1);
891
1334
  }
1335
+ // Validate filters if provided
1336
+ if (Object.keys(filters).length > 0) {
1337
+ const validation = MetadataFilter.validate(filters);
1338
+ if (!validation.valid) {
1339
+ log.error('Invalid filters:');
1340
+ validation.errors.forEach(err => log.error(` - ${err}`));
1341
+ process.exit(1);
1342
+ }
1343
+ }
892
1344
  // Use reflexionRetrieveJson method for JSON output
893
- const results = await cli.reflexionRetrieveJson({
1345
+ let results = await cli.reflexionRetrieveJson({
894
1346
  task: query,
895
1347
  k,
896
1348
  minReward: minConfidence,
897
1349
  onlySuccesses: domain === 'successful-edits'
898
1350
  });
899
- if (format === 'json') {
900
- console.log(JSON.stringify(results, null, 2));
1351
+ // Apply metadata filters if provided
1352
+ if (Object.keys(filters).length > 0) {
1353
+ results = MetadataFilter.apply(results, filters);
1354
+ log.info(`Filtered to ${results.length} results matching metadata criteria`);
1355
+ }
1356
+ // Synthesize context if requested
1357
+ if (synthesizeContext && results.length > 0) {
1358
+ const context = ContextSynthesizer.synthesize(results.map((r) => ({
1359
+ task: r.task,
1360
+ reward: r.reward,
1361
+ success: r.success,
1362
+ critique: r.critique,
1363
+ input: r.input,
1364
+ output: r.output,
1365
+ similarity: r.similarity
1366
+ })));
1367
+ if (format === 'json') {
1368
+ console.log(JSON.stringify({ results, synthesizedContext: context }, null, 2));
1369
+ }
1370
+ else {
1371
+ console.log('\n' + '═'.repeat(80));
1372
+ console.log('SYNTHESIZED CONTEXT');
1373
+ console.log('═'.repeat(80));
1374
+ console.log(`\n${context.summary}\n`);
1375
+ if (context.patterns.length > 0) {
1376
+ console.log('Common Patterns:');
1377
+ context.patterns.forEach(p => console.log(` • ${p}`));
1378
+ console.log('');
1379
+ }
1380
+ if (context.keyInsights.length > 0) {
1381
+ console.log('Key Insights:');
1382
+ context.keyInsights.forEach(i => console.log(` • ${i}`));
1383
+ console.log('');
1384
+ }
1385
+ if (context.recommendations.length > 0) {
1386
+ console.log('Recommendations:');
1387
+ context.recommendations.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
1388
+ console.log('');
1389
+ }
1390
+ console.log('═'.repeat(80));
1391
+ console.log(`\nMatched ${results.length} memories\n`);
1392
+ }
901
1393
  }
902
1394
  else {
903
- console.log(results);
1395
+ if (format === 'json') {
1396
+ console.log(JSON.stringify(results, null, 2));
1397
+ }
1398
+ else {
1399
+ console.log(results);
1400
+ }
904
1401
  }
905
1402
  }
906
1403
  // Store-pattern command - store learned patterns
@@ -1028,6 +1525,366 @@ async function handleOptimizeMemoryCommand(cli, args) {
1028
1525
  });
1029
1526
  console.log(JSON.stringify({ success: true, message: 'Memory optimization completed' }, null, 2));
1030
1527
  }
1528
+ // Vector-search command - direct vector similarity search with MMR diversity ranking
1529
+ async function handleVectorSearchCommand(args) {
1530
+ // Parse arguments
1531
+ let dbPath = './agentdb.db';
1532
+ let vector = [];
1533
+ let k = 10;
1534
+ let threshold = 0.0;
1535
+ let metric = 'cosine';
1536
+ let format = 'json';
1537
+ let verbose = false;
1538
+ let useMmr = false;
1539
+ let mmrLambda = 0.5;
1540
+ for (let i = 0; i < args.length; i++) {
1541
+ const arg = args[i];
1542
+ if (arg === '-k' && i + 1 < args.length) {
1543
+ k = parseInt(args[++i]);
1544
+ }
1545
+ else if (arg === '-t' && i + 1 < args.length) {
1546
+ threshold = parseFloat(args[++i]);
1547
+ }
1548
+ else if (arg === '-m' && i + 1 < args.length) {
1549
+ metric = args[++i];
1550
+ }
1551
+ else if (arg === '-f' && i + 1 < args.length) {
1552
+ format = args[++i];
1553
+ }
1554
+ else if (arg === '-v' || arg === '--verbose') {
1555
+ verbose = true;
1556
+ }
1557
+ else if (arg === '--mmr') {
1558
+ useMmr = true;
1559
+ if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
1560
+ mmrLambda = parseFloat(args[++i]);
1561
+ }
1562
+ }
1563
+ else if (!dbPath.endsWith('.db') && !arg.startsWith('[')) {
1564
+ dbPath = arg;
1565
+ }
1566
+ else if (arg.startsWith('[') || (!isNaN(parseFloat(arg)))) {
1567
+ // Parse vector - either JSON array or space-separated numbers
1568
+ try {
1569
+ if (arg.startsWith('[')) {
1570
+ vector = JSON.parse(arg);
1571
+ }
1572
+ else {
1573
+ // Collect space-separated numbers
1574
+ while (i < args.length && !isNaN(parseFloat(args[i]))) {
1575
+ vector.push(parseFloat(args[i++]));
1576
+ }
1577
+ i--; // Back up one since loop will increment
1578
+ }
1579
+ }
1580
+ catch (e) {
1581
+ log.error('Invalid vector format. Use JSON array: "[0.1,0.2,0.3]" or space-separated: "0.1 0.2 0.3"');
1582
+ process.exit(1);
1583
+ }
1584
+ }
1585
+ }
1586
+ if (vector.length === 0) {
1587
+ log.error('Missing required vector parameter');
1588
+ log.info('Usage: agentdb vector-search <db-path> <vector> [-k 10] [-t 0.75] [-m cosine] [-f json] [-v] [--mmr [lambda]]');
1589
+ process.exit(1);
1590
+ }
1591
+ if (useMmr) {
1592
+ log.info(`Using MMR diversity ranking (λ=${mmrLambda})`);
1593
+ }
1594
+ // Initialize database
1595
+ const cli = new AgentDBCLI();
1596
+ await cli.initialize(dbPath);
1597
+ // Perform vector search using reflexion's retrieveRelevant (which uses embeddings)
1598
+ // We'll need to search episode_embeddings table directly
1599
+ const query = `
1600
+ SELECT
1601
+ e.id,
1602
+ e.session_id,
1603
+ e.task,
1604
+ e.reward,
1605
+ e.success,
1606
+ ee.embedding
1607
+ FROM episodes e
1608
+ JOIN episode_embeddings ee ON e.id = ee.episode_id
1609
+ LIMIT ?
1610
+ `;
1611
+ const results = cli.db.prepare(query).all(k * 10); // Get more for filtering
1612
+ // Calculate similarities
1613
+ let scored = results.map((row) => {
1614
+ const embedding = new Float32Array(row.embedding);
1615
+ const similarity = calculateSimilarity(vector, Array.from(embedding), metric);
1616
+ return {
1617
+ id: row.id,
1618
+ session_id: row.session_id,
1619
+ task: row.task,
1620
+ reward: row.reward,
1621
+ success: row.success,
1622
+ similarity,
1623
+ embedding: Array.from(embedding)
1624
+ };
1625
+ }).filter(r => r.similarity >= threshold)
1626
+ .sort((a, b) => b.similarity - a.similarity);
1627
+ // Apply MMR diversity ranking if requested
1628
+ if (useMmr && scored.length > k) {
1629
+ scored = MMRDiversityRanker.selectDiverse(scored, vector, {
1630
+ lambda: mmrLambda,
1631
+ k,
1632
+ metric: metric
1633
+ });
1634
+ }
1635
+ else {
1636
+ scored = scored.slice(0, k);
1637
+ }
1638
+ // Remove embedding from output
1639
+ const output = scored.map(({ embedding, ...rest }) => rest);
1640
+ if (format === 'json') {
1641
+ console.log(JSON.stringify(output, null, 2));
1642
+ }
1643
+ else {
1644
+ console.log(`Found ${output.length} results:`);
1645
+ output.forEach((r, i) => {
1646
+ console.log(`${i + 1}. [${r.id}] ${r.task} (similarity: ${r.similarity.toFixed(4)})`);
1647
+ if (verbose) {
1648
+ console.log(` Session: ${r.session_id}, Reward: ${r.reward}, Success: ${r.success}`);
1649
+ }
1650
+ });
1651
+ }
1652
+ }
1653
+ // Helper function to calculate similarity
1654
+ function calculateSimilarity(v1, v2, metric) {
1655
+ if (v1.length !== v2.length) {
1656
+ throw new Error(`Vector dimension mismatch: ${v1.length} vs ${v2.length}`);
1657
+ }
1658
+ if (metric === 'cosine') {
1659
+ let dot = 0, mag1 = 0, mag2 = 0;
1660
+ for (let i = 0; i < v1.length; i++) {
1661
+ dot += v1[i] * v2[i];
1662
+ mag1 += v1[i] * v1[i];
1663
+ mag2 += v2[i] * v2[i];
1664
+ }
1665
+ return dot / (Math.sqrt(mag1) * Math.sqrt(mag2));
1666
+ }
1667
+ else if (metric === 'euclidean') {
1668
+ let sum = 0;
1669
+ for (let i = 0; i < v1.length; i++) {
1670
+ const diff = v1[i] - v2[i];
1671
+ sum += diff * diff;
1672
+ }
1673
+ return 1 / (1 + Math.sqrt(sum)); // Normalize to 0-1 range
1674
+ }
1675
+ else if (metric === 'dot') {
1676
+ let dot = 0;
1677
+ for (let i = 0; i < v1.length; i++) {
1678
+ dot += v1[i] * v2[i];
1679
+ }
1680
+ return dot;
1681
+ }
1682
+ return 0;
1683
+ }
1684
+ // Export command - export vectors to JSON with optional compression
1685
+ async function handleExportCommand(args) {
1686
+ let dbPath = './agentdb.db';
1687
+ let outputPath = './agentdb-export.json';
1688
+ let compress = false;
1689
+ // Parse arguments
1690
+ for (let i = 0; i < args.length; i++) {
1691
+ const arg = args[i];
1692
+ if (arg === '--compress') {
1693
+ compress = true;
1694
+ }
1695
+ else if (arg === '--output' && i + 1 < args.length) {
1696
+ outputPath = args[++i];
1697
+ }
1698
+ else if (!arg.startsWith('--')) {
1699
+ if (dbPath === './agentdb.db') {
1700
+ dbPath = arg;
1701
+ }
1702
+ else if (outputPath === './agentdb-export.json') {
1703
+ outputPath = arg;
1704
+ }
1705
+ }
1706
+ }
1707
+ // Add .gz extension if compressing and not already present
1708
+ if (compress && !outputPath.endsWith('.gz')) {
1709
+ outputPath += '.gz';
1710
+ }
1711
+ log.info(`Exporting vectors from: ${dbPath}`);
1712
+ if (compress) {
1713
+ log.info('Compression: enabled');
1714
+ }
1715
+ const cli = new AgentDBCLI();
1716
+ await cli.initialize(dbPath);
1717
+ // Export all episodes with embeddings
1718
+ const query = `
1719
+ SELECT
1720
+ e.*,
1721
+ ee.embedding
1722
+ FROM episodes e
1723
+ LEFT JOIN episode_embeddings ee ON e.id = ee.episode_id
1724
+ `;
1725
+ const results = cli.db.prepare(query).all();
1726
+ const jsonData = JSON.stringify(results, null, 2);
1727
+ try {
1728
+ if (compress) {
1729
+ // Compress with gzip
1730
+ const compressed = zlib.gzipSync(jsonData);
1731
+ fs.writeFileSync(outputPath, compressed);
1732
+ const originalSize = Buffer.byteLength(jsonData);
1733
+ const compressedSize = compressed.length;
1734
+ const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(1);
1735
+ log.success(`Exported ${results.length} episodes to ${outputPath}`);
1736
+ log.info(`Original size: ${(originalSize / 1024).toFixed(2)} KB`);
1737
+ log.info(`Compressed size: ${(compressedSize / 1024).toFixed(2)} KB (${ratio}% reduction)`);
1738
+ }
1739
+ else {
1740
+ fs.writeFileSync(outputPath, jsonData);
1741
+ log.success(`Exported ${results.length} episodes to ${outputPath}`);
1742
+ }
1743
+ }
1744
+ catch (error) {
1745
+ log.error(`Failed to export: ${error.message}`);
1746
+ process.exit(1);
1747
+ }
1748
+ }
1749
+ // Import command - import vectors from JSON with optional decompression
1750
+ async function handleImportCommand(args) {
1751
+ let inputPath = '';
1752
+ let dbPath = './agentdb.db';
1753
+ let decompress = false;
1754
+ // Parse arguments
1755
+ for (let i = 0; i < args.length; i++) {
1756
+ const arg = args[i];
1757
+ if (arg === '--decompress') {
1758
+ decompress = true;
1759
+ }
1760
+ else if (arg === '--db' && i + 1 < args.length) {
1761
+ dbPath = args[++i];
1762
+ }
1763
+ else if (!arg.startsWith('--')) {
1764
+ if (!inputPath) {
1765
+ inputPath = arg;
1766
+ }
1767
+ else if (dbPath === './agentdb.db') {
1768
+ dbPath = arg;
1769
+ }
1770
+ }
1771
+ }
1772
+ // Auto-detect compression from .gz extension
1773
+ if (inputPath.endsWith('.gz')) {
1774
+ decompress = true;
1775
+ }
1776
+ if (!inputPath) {
1777
+ log.error('Missing required input file');
1778
+ log.info('Usage: agentdb import <input-file.json> [db-path] [--decompress]');
1779
+ process.exit(1);
1780
+ }
1781
+ log.info(`Importing vectors from: ${inputPath}`);
1782
+ if (decompress) {
1783
+ log.info('Decompression: enabled');
1784
+ }
1785
+ let data;
1786
+ try {
1787
+ if (decompress) {
1788
+ // Decompress with gunzip
1789
+ const compressed = fs.readFileSync(inputPath);
1790
+ const decompressed = zlib.gunzipSync(compressed);
1791
+ data = JSON.parse(decompressed.toString('utf-8'));
1792
+ log.info(`Decompressed ${(compressed.length / 1024).toFixed(2)} KB to ${(decompressed.length / 1024).toFixed(2)} KB`);
1793
+ }
1794
+ else {
1795
+ data = JSON.parse(fs.readFileSync(inputPath, 'utf-8'));
1796
+ }
1797
+ }
1798
+ catch (error) {
1799
+ log.error(`Failed to read/parse input file: ${error.message}`);
1800
+ process.exit(1);
1801
+ }
1802
+ const cli = new AgentDBCLI();
1803
+ await cli.initialize(dbPath);
1804
+ let imported = 0;
1805
+ for (const item of data) {
1806
+ try {
1807
+ // Import episode
1808
+ const episodeQuery = `
1809
+ INSERT INTO episodes (session_id, task, input, output, critique, reward, success, latency_ms, tokens_used, tags, metadata)
1810
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1811
+ `;
1812
+ const result = cli.db.prepare(episodeQuery).run(item.session_id, item.task, item.input, item.output, item.critique, item.reward, item.success, item.latency_ms, item.tokens_used, item.tags, item.metadata);
1813
+ // Import embedding if exists
1814
+ if (item.embedding) {
1815
+ const embeddingQuery = `INSERT INTO episode_embeddings (episode_id, embedding) VALUES (?, ?)`;
1816
+ cli.db.prepare(embeddingQuery).run(result.lastInsertRowid, item.embedding);
1817
+ }
1818
+ imported++;
1819
+ }
1820
+ catch (error) {
1821
+ log.warning(`Failed to import item ${imported + 1}: ${error.message}`);
1822
+ }
1823
+ }
1824
+ // Save database
1825
+ if (cli.db && typeof cli.db.save === 'function') {
1826
+ cli.db.save();
1827
+ }
1828
+ log.success(`Imported ${imported} episodes`);
1829
+ }
1830
+ // Stats command - show database statistics
1831
+ async function handleStatsCommand(args) {
1832
+ const dbPath = args[0] || './agentdb.db';
1833
+ log.info(`Getting statistics for: ${dbPath}`);
1834
+ const cli = new AgentDBCLI();
1835
+ await cli.initialize(dbPath);
1836
+ // Get counts (with fallback for missing tables)
1837
+ const episodeCount = cli.db.prepare('SELECT COUNT(*) as count FROM episodes').get()?.count || 0;
1838
+ const embeddingCount = cli.db.prepare('SELECT COUNT(*) as count FROM episode_embeddings').get()?.count || 0;
1839
+ let skillCount = 0;
1840
+ try {
1841
+ skillCount = cli.db.prepare('SELECT COUNT(*) as count FROM skill_library').get()?.count || 0;
1842
+ }
1843
+ catch (e) {
1844
+ // skill_library table may not exist in older databases
1845
+ }
1846
+ let causalEdges = 0;
1847
+ try {
1848
+ causalEdges = cli.db.prepare('SELECT COUNT(*) as count FROM causal_edges').get()?.count || 0;
1849
+ }
1850
+ catch (e) {
1851
+ // causal_edges table may not exist in older databases
1852
+ }
1853
+ // Get database file size
1854
+ let dbSize = 0;
1855
+ if (dbPath !== ':memory:' && fs.existsSync(dbPath)) {
1856
+ dbSize = fs.statSync(dbPath).size;
1857
+ }
1858
+ // Get average confidence
1859
+ const avgConfidence = cli.db.prepare('SELECT AVG(reward) as avg FROM episodes').get().avg || 0;
1860
+ // Get domains
1861
+ const domains = cli.db.prepare(`
1862
+ SELECT task, COUNT(*) as count
1863
+ FROM episodes
1864
+ GROUP BY task
1865
+ ORDER BY count DESC
1866
+ LIMIT 5
1867
+ `).all();
1868
+ console.log(`
1869
+ 📊 AgentDB Statistics
1870
+
1871
+ Database: ${dbPath}
1872
+ Size: ${(dbSize / 1024).toFixed(2)} KB
1873
+
1874
+ 📈 Counts:
1875
+ Episodes: ${episodeCount}
1876
+ Embeddings: ${embeddingCount}
1877
+ Skills: ${skillCount}
1878
+ Causal Edges: ${causalEdges}
1879
+
1880
+ 📊 Metrics:
1881
+ Average Reward: ${avgConfidence.toFixed(3)}
1882
+ Embedding Coverage: ${episodeCount > 0 ? ((embeddingCount / episodeCount) * 100).toFixed(1) : 0}%
1883
+
1884
+ 🏷️ Top Domains:
1885
+ ${domains.map(d => ` • ${d.task}: ${d.count}`).join('\n')}
1886
+ `);
1887
+ }
1031
1888
  function printHelp() {
1032
1889
  console.log(`
1033
1890
  ${colors.bright}${colors.cyan}█▀█ █▀▀ █▀▀ █▄░█ ▀█▀ █▀▄ █▄▄
@@ -1039,13 +1896,96 @@ ${colors.bright}USAGE:${colors.reset}
1039
1896
  agentdb <command> <subcommand> [options]
1040
1897
 
1041
1898
  ${colors.bright}SETUP COMMANDS:${colors.reset}
1042
- agentdb init [db-path]
1899
+ agentdb init [db-path] [--dimension 1536] [--preset small|medium|large] [--in-memory]
1043
1900
  Initialize a new AgentDB database (default: ./agentdb.db)
1901
+ Options:
1902
+ --dimension <n> Vector dimension (default: 1536 for OpenAI, 768 for sentence-transformers)
1903
+ --preset <size> small (<10K), medium (10K-100K), large (>100K vectors)
1904
+ --in-memory Use temporary in-memory database (:memory:)
1905
+
1906
+ ${colors.bright}VECTOR SEARCH COMMANDS:${colors.reset}
1907
+ agentdb vector-search <db-path> <vector> [-k 10] [-t 0.75] [-m cosine] [-f json] [-v] [--mmr [lambda]]
1908
+ Direct vector similarity search without text embeddings
1909
+ Arguments:
1910
+ <db-path> Database file path (or :memory:)
1911
+ <vector> Vector as JSON array [0.1,0.2,...] or space-separated numbers
1912
+ Options:
1913
+ -k <n> Number of results (default: 10)
1914
+ -t <threshold> Minimum similarity threshold (default: 0.0)
1915
+ -m <metric> Similarity metric: cosine|euclidean|dot (default: cosine)
1916
+ -f <format> Output format: json|table (default: json)
1917
+ -v Verbose mode with similarity scores
1918
+ --mmr [lambda] Enable MMR diversity ranking (lambda: 0-1, default: 0.5)
1919
+ 0 = max diversity, 1 = max relevance
1920
+ Example: agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" -k 10 -m cosine
1921
+ Example: agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" --mmr 0.7
1922
+
1923
+ agentdb export <db-path> [output-file] [--compress]
1924
+ Export all vectors and episodes to JSON file
1925
+ Options:
1926
+ --compress Compress output with gzip (adds .gz extension)
1927
+ --output <file> Output file path
1928
+ Example: agentdb export ./agentdb.db ./backup.json
1929
+ Example: agentdb export ./agentdb.db --compress --output backup.json.gz
1930
+
1931
+ agentdb import <input-file> [db-path] [--decompress]
1932
+ Import vectors and episodes from JSON file
1933
+ Options:
1934
+ --decompress Decompress gzip input (auto-detected for .gz files)
1935
+ --db <path> Database file path
1936
+ Example: agentdb import ./backup.json ./new-db.db
1937
+ Example: agentdb import ./backup.json.gz --decompress
1938
+
1939
+ agentdb stats [db-path]
1940
+ Show detailed database statistics and metrics
1941
+ Example: agentdb stats ./agentdb.db
1044
1942
 
1045
1943
  ${colors.bright}MCP COMMANDS:${colors.reset}
1046
1944
  agentdb mcp start
1047
1945
  Start the MCP server for Claude Desktop integration
1048
1946
 
1947
+ ${colors.bright}QUIC SYNC COMMANDS:${colors.reset}
1948
+ agentdb sync start-server [--port 4433] [--cert <path>] [--key <path>] [--auth-token <token>]
1949
+ Start a QUIC synchronization server for multi-agent coordination
1950
+ Options:
1951
+ --port <n> Server port (default: 4433)
1952
+ --cert <path> TLS certificate file path
1953
+ --key <path> TLS key file path
1954
+ --auth-token <token> Authentication token (auto-generated if not provided)
1955
+ Example: agentdb sync start-server --port 4433 --cert ./cert.pem --key ./key.pem
1956
+
1957
+ agentdb sync connect <host> <port> [--auth-token <token>] [--cert <path>]
1958
+ Connect to a remote QUIC sync server
1959
+ Arguments:
1960
+ <host> Remote server hostname or IP
1961
+ <port> Remote server port
1962
+ Options:
1963
+ --auth-token <token> Authentication token
1964
+ --cert <path> TLS certificate for verification
1965
+ Example: agentdb sync connect 192.168.1.100 4433 --auth-token abc123
1966
+
1967
+ agentdb sync push --server <host:port> [--incremental] [--filter <pattern>]
1968
+ Push local changes to remote server
1969
+ Options:
1970
+ --server <host:port> Remote server address (e.g., 192.168.1.100:4433)
1971
+ --incremental Only push changes since last sync
1972
+ --filter <pattern> Filter changes by pattern (e.g., "episodes", "skills")
1973
+ Example: agentdb sync push --server 192.168.1.100:4433 --incremental
1974
+ Example: agentdb sync push --server localhost:4433 --filter "episodes"
1975
+
1976
+ agentdb sync pull --server <host:port> [--incremental] [--filter <pattern>]
1977
+ Pull remote changes from server
1978
+ Options:
1979
+ --server <host:port> Remote server address (e.g., 192.168.1.100:4433)
1980
+ --incremental Only pull changes since last sync
1981
+ --filter <pattern> Filter changes by pattern (e.g., "skills", "causal_edges")
1982
+ Example: agentdb sync pull --server 192.168.1.100:4433 --incremental
1983
+ Example: agentdb sync pull --server localhost:4433 --filter "skills"
1984
+
1985
+ agentdb sync status
1986
+ Show synchronization status, pending changes, and connected servers
1987
+ Example: agentdb sync status
1988
+
1049
1989
  ${colors.bright}CAUSAL COMMANDS:${colors.reset}
1050
1990
  agentdb causal add-edge <cause> <effect> <uplift> [confidence] [sample-size]
1051
1991
  Add a causal edge manually
@@ -1080,8 +2020,17 @@ ${colors.bright}REFLEXION COMMANDS:${colors.reset}
1080
2020
  agentdb reflexion store <session-id> <task> <reward> <success> [critique] [input] [output] [latency-ms] [tokens]
1081
2021
  Store episode with self-critique
1082
2022
 
1083
- agentdb reflexion retrieve <task> [k] [min-reward] [only-failures] [only-successes]
2023
+ agentdb reflexion retrieve <task> [--k <n>] [--min-reward <r>] [--only-failures] [--only-successes] [--synthesize-context] [--filters <json>]
1084
2024
  Retrieve relevant past episodes
2025
+ Options:
2026
+ --k <n> Number of results (default: 5)
2027
+ --min-reward <r> Minimum reward threshold
2028
+ --only-failures Return only failed episodes
2029
+ --only-successes Return only successful episodes
2030
+ --synthesize-context Generate coherent summary with patterns and insights
2031
+ --filters <json> MongoDB-style metadata filters (e.g., '{"metadata.year":{"$gte":2024}}')
2032
+ Example: agentdb reflexion retrieve "authentication" --k 10 --synthesize-context
2033
+ Example: agentdb reflexion retrieve "bug-fix" --filters '{"success":true,"reward":{"$gte":0.8}}'
1085
2034
 
1086
2035
  agentdb reflexion critique-summary <task> [only-failures]
1087
2036
  Get aggregated critique lessons
@@ -1109,9 +2058,18 @@ ${colors.bright}DATABASE COMMANDS:${colors.reset}
1109
2058
  Show database statistics
1110
2059
 
1111
2060
  ${colors.bright}HOOKS INTEGRATION COMMANDS:${colors.reset}
1112
- agentdb query --domain <domain> --query <query> --k <k> --min-confidence <conf> --format json
2061
+ agentdb query --query <query> [--domain <domain>] [--k <k>] [--min-confidence <conf>] [--format json] [--synthesize-context] [--filters <json>]
1113
2062
  Semantic search across stored episodes and patterns
1114
- Example: agentdb query --domain "successful-edits" --query "file:utils.ts" --k 5 --min-confidence 0.8
2063
+ Options:
2064
+ --query <q> Query string (required)
2065
+ --domain <d> Domain filter (e.g., "successful-edits")
2066
+ --k <n> Number of results (default: 5)
2067
+ --min-confidence <c> Minimum confidence threshold (default: 0.0)
2068
+ --format <f> Output format: json|text (default: json)
2069
+ --synthesize-context Generate coherent summary with patterns and insights
2070
+ --filters <json> MongoDB-style metadata filters
2071
+ Example: agentdb query --query "authentication" --k 5 --min-confidence 0.8 --synthesize-context
2072
+ Example: agentdb query --query "bug-fix" --filters '{"metadata.priority":"high"}' --synthesize-context
1115
2073
 
1116
2074
  agentdb store-pattern --type <type> --domain <domain> --pattern <json> --confidence <conf>
1117
2075
  Store a learned pattern for future retrieval
@@ -1129,6 +2087,23 @@ ${colors.bright}ENVIRONMENT:${colors.reset}
1129
2087
  AGENTDB_PATH Database file path (default: ./agentdb.db)
1130
2088
 
1131
2089
  ${colors.bright}EXAMPLES:${colors.reset}
2090
+ # QUIC Sync: Multi-agent coordination
2091
+ # On server machine:
2092
+ agentdb sync start-server --port 4433 --auth-token secret123
2093
+
2094
+ # On client machines:
2095
+ agentdb sync connect 192.168.1.100 4433 --auth-token secret123
2096
+ agentdb sync push --server 192.168.1.100:4433 --incremental
2097
+ agentdb sync pull --server 192.168.1.100:4433 --incremental
2098
+ agentdb sync status
2099
+
2100
+ # Vector Search: Direct similarity queries
2101
+ agentdb init ./vectors.db --dimension 768 --preset medium
2102
+ agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" -k 10 -m cosine -f json
2103
+ agentdb export ./vectors.db ./backup.json
2104
+ agentdb import ./backup.json ./new-vectors.db
2105
+ agentdb stats ./vectors.db
2106
+
1132
2107
  # Reflexion: Store and retrieve episodes
1133
2108
  agentdb reflexion store "session-1" "implement_auth" 0.95 true "Used OAuth2"
1134
2109
  agentdb reflexion retrieve "authentication" 10 0.8