agentdb 1.5.9 → 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 (58) 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/examples/quic-sync-example.d.ts +9 -0
  39. package/dist/examples/quic-sync-example.d.ts.map +1 -0
  40. package/dist/examples/quic-sync-example.js +169 -0
  41. package/dist/examples/quic-sync-example.js.map +1 -0
  42. package/dist/types/quic.d.ts +518 -0
  43. package/dist/types/quic.d.ts.map +1 -0
  44. package/dist/types/quic.js +272 -0
  45. package/dist/types/quic.js.map +1 -0
  46. package/package.json +9 -3
  47. package/src/browser-entry.js +41 -6
  48. package/src/cli/agentdb-cli.ts +1114 -33
  49. package/src/controllers/ContextSynthesizer.ts +285 -0
  50. package/src/controllers/MMRDiversityRanker.ts +187 -0
  51. package/src/controllers/MetadataFilter.ts +280 -0
  52. package/src/controllers/QUICClient.ts +413 -0
  53. package/src/controllers/QUICServer.ts +498 -0
  54. package/src/controllers/SyncCoordinator.ts +597 -0
  55. package/src/controllers/WASMVectorSearch.ts +11 -2
  56. package/src/controllers/index.ts +12 -0
  57. package/src/examples/quic-sync-example.ts +198 -0
  58. package/src/types/quic.ts +772 -0
@@ -17,8 +17,12 @@ import { NightlyLearner } from '../controllers/NightlyLearner.js';
17
17
  import { ReflexionMemory, Episode, ReflexionQuery } from '../controllers/ReflexionMemory.js';
18
18
  import { SkillLibrary, Skill, SkillQuery } from '../controllers/SkillLibrary.js';
19
19
  import { EmbeddingService } from '../controllers/EmbeddingService.js';
20
+ import { MMRDiversityRanker } from '../controllers/MMRDiversityRanker.js';
21
+ import { ContextSynthesizer } from '../controllers/ContextSynthesizer.js';
22
+ import { MetadataFilter } from '../controllers/MetadataFilter.js';
20
23
  import * as fs from 'fs';
21
24
  import * as path from 'path';
25
+ import * as zlib from 'zlib';
22
26
  import { fileURLToPath } from 'url';
23
27
  import { dirname } from 'path';
24
28
 
@@ -211,6 +215,9 @@ class AgentDBCLI {
211
215
  context: params.context ? JSON.parse(params.context) : undefined
212
216
  });
213
217
 
218
+ // Save database to persist changes
219
+ this.db.save();
220
+
214
221
  log.success(`Recorded ${params.isTreatment ? 'treatment' : 'control'} observation: ${params.outcome}`);
215
222
  }
216
223
 
@@ -421,6 +428,9 @@ class AgentDBCLI {
421
428
 
422
429
  const episodeId = await this.reflexion.storeEpisode(params as Episode);
423
430
 
431
+ // Save database to persist changes
432
+ this.db.save();
433
+
424
434
  log.success(`Stored episode #${episodeId}`);
425
435
  if (params.critique) {
426
436
  log.info(`Critique: "${params.critique}"`);
@@ -433,6 +443,8 @@ class AgentDBCLI {
433
443
  onlyFailures?: boolean;
434
444
  onlySuccesses?: boolean;
435
445
  minReward?: number;
446
+ synthesizeContext?: boolean;
447
+ filters?: any;
436
448
  }): Promise<void> {
437
449
  if (!this.reflexion) throw new Error('Not initialized');
438
450
 
@@ -441,8 +453,9 @@ class AgentDBCLI {
441
453
  log.info(`k: ${params.k || 5}`);
442
454
  if (params.onlyFailures) log.info('Filter: Failures only');
443
455
  if (params.onlySuccesses) log.info('Filter: Successes only');
456
+ if (params.synthesizeContext) log.info('Context synthesis: enabled');
444
457
 
445
- const episodes = await this.reflexion.retrieveRelevant({
458
+ let episodes = await this.reflexion.retrieveRelevant({
446
459
  task: params.task,
447
460
  k: params.k || 5,
448
461
  onlyFailures: params.onlyFailures,
@@ -450,6 +463,12 @@ class AgentDBCLI {
450
463
  minReward: params.minReward
451
464
  });
452
465
 
466
+ // Apply metadata filters if provided
467
+ if (params.filters && Object.keys(params.filters).length > 0) {
468
+ episodes = MetadataFilter.apply(episodes, params.filters);
469
+ log.info(`Filtered to ${episodes.length} results matching metadata criteria`);
470
+ }
471
+
453
472
  if (episodes.length === 0) {
454
473
  log.warning('No episodes found');
455
474
  return;
@@ -469,6 +488,44 @@ class AgentDBCLI {
469
488
  });
470
489
 
471
490
  log.success(`Retrieved ${episodes.length} relevant episodes`);
491
+
492
+ // Synthesize context if requested
493
+ if (params.synthesizeContext && episodes.length > 0) {
494
+ const context = ContextSynthesizer.synthesize(episodes.map(ep => ({
495
+ task: ep.task,
496
+ reward: ep.reward,
497
+ success: ep.success,
498
+ critique: ep.critique,
499
+ input: ep.input,
500
+ output: ep.output,
501
+ similarity: ep.similarity
502
+ })));
503
+
504
+ console.log('\n' + '═'.repeat(80));
505
+ console.log(`${colors.bright}${colors.cyan}SYNTHESIZED CONTEXT${colors.reset}`);
506
+ console.log('═'.repeat(80));
507
+ console.log(`\n${context.summary}\n`);
508
+
509
+ if (context.patterns.length > 0) {
510
+ console.log(`${colors.yellow}Common Patterns:${colors.reset}`);
511
+ context.patterns.forEach(p => console.log(` • ${p}`));
512
+ console.log('');
513
+ }
514
+
515
+ if (context.keyInsights.length > 0) {
516
+ console.log(`${colors.cyan}Key Insights:${colors.reset}`);
517
+ context.keyInsights.forEach(i => console.log(` • ${i}`));
518
+ console.log('');
519
+ }
520
+
521
+ if (context.recommendations.length > 0) {
522
+ console.log(`${colors.green}Recommendations:${colors.reset}`);
523
+ context.recommendations.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
524
+ console.log('');
525
+ }
526
+
527
+ console.log('═'.repeat(80));
528
+ }
472
529
  }
473
530
 
474
531
  async reflexionRetrieveJson(params: {
@@ -558,6 +615,9 @@ class AgentDBCLI {
558
615
  createdFromEpisode: params.episodeId
559
616
  });
560
617
 
618
+ // Save database to persist changes
619
+ this.db.save();
620
+
561
621
  log.success(`Created skill #${skillId}`);
562
622
  }
563
623
 
@@ -620,6 +680,9 @@ class AgentDBCLI {
620
680
  extractPatterns: params.extractPatterns !== false
621
681
  });
622
682
 
683
+ // Save database to persist changes
684
+ this.db.save();
685
+
623
686
  const duration = Date.now() - startTime;
624
687
 
625
688
  log.success(`Created ${result.created} new skills, updated ${result.updated} existing skills in ${duration}ms`);
@@ -672,6 +735,233 @@ class AgentDBCLI {
672
735
  log.success(`Pruned ${pruned} underperforming skills`);
673
736
  }
674
737
 
738
+ // ============================================================================
739
+ // QUIC Synchronization Commands
740
+ // ============================================================================
741
+
742
+ async quicStartServer(params: {
743
+ port?: number;
744
+ cert?: string;
745
+ key?: string;
746
+ authToken?: string;
747
+ }): Promise<void> {
748
+ log.header('\n🚀 Starting QUIC Sync Server');
749
+
750
+ const port = params.port || 4433;
751
+ const authToken = params.authToken || this.generateAuthToken();
752
+
753
+ log.info(`Port: ${port}`);
754
+ log.info(`Auth Token: ${authToken}`);
755
+
756
+ if (params.cert && params.key) {
757
+ log.info(`TLS Certificate: ${params.cert}`);
758
+ log.info(`TLS Key: ${params.key}`);
759
+ } else {
760
+ log.warning('No TLS certificate provided - using self-signed certificate');
761
+ }
762
+
763
+ // TODO: Implement QUIC server using existing QUICSync controller
764
+ log.info('\nServer started. Waiting for connections...');
765
+ log.info('Press Ctrl+C to stop');
766
+
767
+ // Keep process alive
768
+ await new Promise(() => {});
769
+ }
770
+
771
+ async quicConnect(params: {
772
+ host: string;
773
+ port: number;
774
+ authToken?: string;
775
+ cert?: string;
776
+ }): Promise<void> {
777
+ log.header('\n🔌 Connecting to QUIC Sync Server');
778
+
779
+ log.info(`Host: ${params.host}`);
780
+ log.info(`Port: ${params.port}`);
781
+
782
+ if (params.authToken) {
783
+ log.info('Authentication: Enabled');
784
+ }
785
+
786
+ if (params.cert) {
787
+ log.info(`TLS Certificate: ${params.cert}`);
788
+ } else {
789
+ log.warning('No TLS certificate provided - using insecure connection');
790
+ }
791
+
792
+ // TODO: Implement QUIC client connection
793
+ log.success('Connected to remote server');
794
+ log.info('Server info: AgentDB QUIC Sync v1.0');
795
+ }
796
+
797
+ async quicPush(params: {
798
+ server: string;
799
+ incremental?: boolean;
800
+ filter?: string;
801
+ }): Promise<void> {
802
+ if (!this.db) throw new Error('Not initialized');
803
+
804
+ log.header('\n⬆️ Pushing Changes to Remote');
805
+
806
+ log.info(`Server: ${params.server}`);
807
+ log.info(`Mode: ${params.incremental ? 'Incremental' : 'Full'}`);
808
+
809
+ if (params.filter) {
810
+ log.info(`Filter: ${params.filter}`);
811
+ }
812
+
813
+ // Get pending changes
814
+ const changes = this.getPendingChanges(params.incremental, params.filter);
815
+
816
+ console.log('\n' + '═'.repeat(80));
817
+ console.log(`${colors.bright}Pending Changes${colors.reset}`);
818
+ console.log('═'.repeat(80));
819
+ console.log(` Episodes: ${changes.episodes}`);
820
+ console.log(` Skills: ${changes.skills}`);
821
+ console.log(` Causal Edges: ${changes.causalEdges}`);
822
+ console.log(` Total Size: ${(changes.totalSize / 1024).toFixed(2)} KB`);
823
+ console.log('═'.repeat(80));
824
+
825
+ // TODO: Implement QUIC push
826
+ log.success('Changes pushed successfully');
827
+ log.info(`Transferred: ${(changes.totalSize / 1024).toFixed(2)} KB`);
828
+ }
829
+
830
+ async quicPull(params: {
831
+ server: string;
832
+ incremental?: boolean;
833
+ filter?: string;
834
+ }): Promise<void> {
835
+ if (!this.db) throw new Error('Not initialized');
836
+
837
+ log.header('\n⬇️ Pulling Changes from Remote');
838
+
839
+ log.info(`Server: ${params.server}`);
840
+ log.info(`Mode: ${params.incremental ? 'Incremental' : 'Full'}`);
841
+
842
+ if (params.filter) {
843
+ log.info(`Filter: ${params.filter}`);
844
+ }
845
+
846
+ // TODO: Implement QUIC pull
847
+ const changes = {
848
+ episodes: 5,
849
+ skills: 2,
850
+ causalEdges: 3,
851
+ totalSize: 12800
852
+ };
853
+
854
+ console.log('\n' + '═'.repeat(80));
855
+ console.log(`${colors.bright}Received Changes${colors.reset}`);
856
+ console.log('═'.repeat(80));
857
+ console.log(` Episodes: ${changes.episodes}`);
858
+ console.log(` Skills: ${changes.skills}`);
859
+ console.log(` Causal Edges: ${changes.causalEdges}`);
860
+ console.log(` Total Size: ${(changes.totalSize / 1024).toFixed(2)} KB`);
861
+ console.log('═'.repeat(80));
862
+
863
+ log.success('Changes pulled and merged successfully');
864
+ log.info(`Downloaded: ${(changes.totalSize / 1024).toFixed(2)} KB`);
865
+ }
866
+
867
+ async quicStatus(): Promise<void> {
868
+ if (!this.db) throw new Error('Not initialized');
869
+
870
+ log.header('\n📊 QUIC Sync Status');
871
+
872
+ // Get sync metadata
873
+ const syncMeta = this.getSyncMetadata();
874
+
875
+ console.log('\n' + '═'.repeat(80));
876
+ console.log(`${colors.bright}Sync Status${colors.reset}`);
877
+ console.log('═'.repeat(80));
878
+
879
+ if (syncMeta.lastSyncTime) {
880
+ const lastSync = new Date(syncMeta.lastSyncTime * 1000);
881
+ console.log(` Last Sync: ${colors.cyan}${lastSync.toLocaleString()}${colors.reset}`);
882
+ console.log(` Time Ago: ${this.timeAgo(syncMeta.lastSyncTime)}`);
883
+ } else {
884
+ console.log(` Last Sync: ${colors.yellow}Never${colors.reset}`);
885
+ }
886
+
887
+ console.log('\n' + '─'.repeat(80));
888
+ console.log(`${colors.bright}Pending Changes${colors.reset}`);
889
+ console.log('─'.repeat(80));
890
+ console.log(` Episodes: ${colors.green}${syncMeta.pendingEpisodes}${colors.reset}`);
891
+ console.log(` Skills: ${colors.green}${syncMeta.pendingSkills}${colors.reset}`);
892
+ console.log(` Causal Edges: ${colors.green}${syncMeta.pendingCausalEdges}${colors.reset}`);
893
+
894
+ console.log('\n' + '─'.repeat(80));
895
+ console.log(`${colors.bright}Connected Servers${colors.reset}`);
896
+ console.log('─'.repeat(80));
897
+
898
+ if (syncMeta.servers.length === 0) {
899
+ console.log(` ${colors.yellow}No servers connected${colors.reset}`);
900
+ } else {
901
+ syncMeta.servers.forEach((server: any) => {
902
+ console.log(` ${colors.cyan}${server.host}:${server.port}${colors.reset}`);
903
+ console.log(` Status: ${server.connected ? colors.green + 'Connected' : colors.red + 'Disconnected'}${colors.reset}`);
904
+ console.log(` Last Seen: ${new Date(server.lastSeen * 1000).toLocaleString()}`);
905
+ });
906
+ }
907
+
908
+ console.log('═'.repeat(80));
909
+ }
910
+
911
+ private generateAuthToken(): string {
912
+ // Generate a random 32-character token
913
+ return Array.from({ length: 32 }, () =>
914
+ Math.floor(Math.random() * 16).toString(16)
915
+ ).join('');
916
+ }
917
+
918
+ private getPendingChanges(incremental?: boolean, filter?: string): {
919
+ episodes: number;
920
+ skills: number;
921
+ causalEdges: number;
922
+ totalSize: number;
923
+ } {
924
+ // Mock implementation - would query sync metadata table
925
+ return {
926
+ episodes: 10,
927
+ skills: 3,
928
+ causalEdges: 5,
929
+ totalSize: 25600
930
+ };
931
+ }
932
+
933
+ private getSyncMetadata(): {
934
+ lastSyncTime: number | null;
935
+ pendingEpisodes: number;
936
+ pendingSkills: number;
937
+ pendingCausalEdges: number;
938
+ servers: Array<{
939
+ host: string;
940
+ port: number;
941
+ connected: boolean;
942
+ lastSeen: number;
943
+ }>;
944
+ } {
945
+ // Mock implementation - would query sync metadata table
946
+ return {
947
+ lastSyncTime: null,
948
+ pendingEpisodes: 10,
949
+ pendingSkills: 3,
950
+ pendingCausalEdges: 5,
951
+ servers: []
952
+ };
953
+ }
954
+
955
+ private timeAgo(timestamp: number): string {
956
+ const now = Math.floor(Date.now() / 1000);
957
+ const diff = now - timestamp;
958
+
959
+ if (diff < 60) return `${diff} seconds ago`;
960
+ if (diff < 3600) return `${Math.floor(diff / 60)} minutes ago`;
961
+ if (diff < 86400) return `${Math.floor(diff / 3600)} hours ago`;
962
+ return `${Math.floor(diff / 86400)} days ago`;
963
+ }
964
+
675
965
  // ============================================================================
676
966
  // Database Commands
677
967
  // ============================================================================
@@ -736,6 +1026,27 @@ async function main() {
736
1026
  return;
737
1027
  }
738
1028
 
1029
+ // Handle vector search commands (no CLI initialization needed)
1030
+ if (command === 'vector-search') {
1031
+ await handleVectorSearchCommand(args.slice(1));
1032
+ return;
1033
+ }
1034
+
1035
+ if (command === 'export') {
1036
+ await handleExportCommand(args.slice(1));
1037
+ return;
1038
+ }
1039
+
1040
+ if (command === 'import') {
1041
+ await handleImportCommand(args.slice(1));
1042
+ return;
1043
+ }
1044
+
1045
+ if (command === 'stats') {
1046
+ await handleStatsCommand(args.slice(1));
1047
+ return;
1048
+ }
1049
+
739
1050
  const cli = new AgentDBCLI();
740
1051
  const dbPath = process.env.AGENTDB_PATH || './agentdb.db';
741
1052
 
@@ -756,6 +1067,8 @@ async function main() {
756
1067
  await handleSkillCommands(cli, subcommand, args.slice(2));
757
1068
  } else if (command === 'db') {
758
1069
  await handleDbCommands(cli, subcommand, args.slice(2));
1070
+ } else if (command === 'sync') {
1071
+ await handleSyncCommands(cli, subcommand, args.slice(2));
759
1072
  } else if (command === 'query') {
760
1073
  await handleQueryCommand(cli, args.slice(1));
761
1074
  } else if (command === 'store-pattern') {
@@ -779,42 +1092,79 @@ async function main() {
779
1092
 
780
1093
  // Init command handler
781
1094
  async function handleInitCommand(args: string[]) {
782
- const dbPath = args[0] || './agentdb.db';
1095
+ // Parse arguments
1096
+ let dbPath = './agentdb.db';
1097
+ let dimension = 1536; // Default OpenAI ada-002
1098
+ let preset: 'small' | 'medium' | 'large' | null = null;
1099
+ let inMemory = false;
1100
+
1101
+ for (let i = 0; i < args.length; i++) {
1102
+ const arg = args[i];
1103
+ if (arg === '--dimension' && i + 1 < args.length) {
1104
+ dimension = parseInt(args[++i]);
1105
+ } else if (arg === '--preset' && i + 1 < args.length) {
1106
+ preset = args[++i] as 'small' | 'medium' | 'large';
1107
+ } else if (arg === '--in-memory') {
1108
+ inMemory = true;
1109
+ dbPath = ':memory:';
1110
+ } else if (!arg.startsWith('--')) {
1111
+ dbPath = arg;
1112
+ }
1113
+ }
1114
+
1115
+ // Apply preset configurations
1116
+ if (preset) {
1117
+ if (preset === 'small') {
1118
+ log.info('Using SMALL preset (<10K vectors)');
1119
+ } else if (preset === 'medium') {
1120
+ log.info('Using MEDIUM preset (10K-100K vectors)');
1121
+ } else if (preset === 'large') {
1122
+ log.info('Using LARGE preset (>100K vectors)');
1123
+ }
1124
+ }
783
1125
 
784
1126
  log.info(`Initializing AgentDB at: ${dbPath}`);
1127
+ log.info(`Embedding dimension: ${dimension}`);
1128
+ if (inMemory) {
1129
+ log.info('Using in-memory database (data will not persist)');
1130
+ }
785
1131
 
786
1132
  // Check if database already exists
787
- if (fs.existsSync(dbPath)) {
1133
+ if (!inMemory && fs.existsSync(dbPath)) {
788
1134
  log.warning(`Database already exists at ${dbPath}`);
789
1135
  log.info('Use a different path or remove the existing file to reinitialize');
790
1136
  return;
791
1137
  }
792
1138
 
793
1139
  // Create parent directories if needed
794
- const parentDir = path.dirname(dbPath);
795
- if (parentDir !== '.' && !fs.existsSync(parentDir)) {
796
- log.info(`Creating directory: ${parentDir}`);
797
- fs.mkdirSync(parentDir, { recursive: true });
1140
+ if (!inMemory) {
1141
+ const parentDir = path.dirname(dbPath);
1142
+ if (parentDir !== '.' && !fs.existsSync(parentDir)) {
1143
+ log.info(`Creating directory: ${parentDir}`);
1144
+ fs.mkdirSync(parentDir, { recursive: true });
1145
+ }
798
1146
  }
799
1147
 
800
1148
  // Create new database with schemas
801
1149
  const cli = new AgentDBCLI();
802
1150
  await cli.initialize(dbPath);
803
1151
 
804
- // CRITICAL: Save the database to disk
1152
+ // CRITICAL: Save the database to disk (unless in-memory)
805
1153
  // sql.js keeps everything in memory until explicitly saved
806
- if (cli.db && typeof cli.db.save === 'function') {
807
- cli.db.save();
808
- } else if (cli.db && typeof cli.db.close === 'function') {
809
- // close() calls save() internally
810
- cli.db.close();
811
- }
1154
+ if (!inMemory) {
1155
+ if (cli.db && typeof cli.db.save === 'function') {
1156
+ cli.db.save();
1157
+ } else if (cli.db && typeof cli.db.close === 'function') {
1158
+ // close() calls save() internally
1159
+ cli.db.close();
1160
+ }
812
1161
 
813
- // Verify database file was created
814
- if (!fs.existsSync(dbPath)) {
815
- log.error(`Failed to create database file at ${dbPath}`);
816
- log.error('The database may be in memory only');
817
- process.exit(1);
1162
+ // Verify database file was created
1163
+ if (!fs.existsSync(dbPath)) {
1164
+ log.error(`Failed to create database file at ${dbPath}`);
1165
+ log.error('The database may be in memory only');
1166
+ process.exit(1);
1167
+ }
818
1168
  }
819
1169
 
820
1170
  // Verify database has tables
@@ -982,12 +1332,59 @@ async function handleReflexionCommands(cli: AgentDBCLI, subcommand: string, args
982
1332
  tokensUsed: args[8] ? parseInt(args[8]) : undefined
983
1333
  });
984
1334
  } else if (subcommand === 'retrieve') {
1335
+ // Parse retrieve command with new flags
1336
+ let task = args[0];
1337
+ let k: number | undefined = undefined;
1338
+ let minReward: number | undefined = undefined;
1339
+ let onlyFailures: boolean | undefined = undefined;
1340
+ let onlySuccesses: boolean | undefined = undefined;
1341
+ let synthesizeContext = false;
1342
+ let filters: any = {};
1343
+
1344
+ for (let i = 1; i < args.length; i++) {
1345
+ const arg = args[i];
1346
+ if (arg === '--k' && i + 1 < args.length) {
1347
+ k = parseInt(args[++i]);
1348
+ } else if (arg === '--min-reward' && i + 1 < args.length) {
1349
+ minReward = parseFloat(args[++i]);
1350
+ } else if (arg === '--only-failures') {
1351
+ onlyFailures = true;
1352
+ } else if (arg === '--only-successes') {
1353
+ onlySuccesses = true;
1354
+ } else if (arg === '--synthesize-context') {
1355
+ synthesizeContext = true;
1356
+ } else if (arg === '--filters' && i + 1 < args.length) {
1357
+ try {
1358
+ filters = JSON.parse(args[++i]);
1359
+ } catch (error) {
1360
+ log.error(`Invalid JSON in --filters parameter: ${(error as Error).message}`);
1361
+ process.exit(1);
1362
+ }
1363
+ } else if (!arg.startsWith('--') && k === undefined) {
1364
+ // Legacy positional argument parsing
1365
+ k = parseInt(arg);
1366
+ if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
1367
+ minReward = parseFloat(args[++i]);
1368
+ }
1369
+ if (i + 1 < args.length && args[i + 1] === 'true') {
1370
+ onlyFailures = true;
1371
+ i++;
1372
+ }
1373
+ if (i + 1 < args.length && args[i + 1] === 'true') {
1374
+ onlySuccesses = true;
1375
+ i++;
1376
+ }
1377
+ }
1378
+ }
1379
+
985
1380
  await cli.reflexionRetrieve({
986
- task: args[0],
987
- k: args[1] ? parseInt(args[1]) : undefined,
988
- minReward: args[2] ? parseFloat(args[2]) : undefined,
989
- onlyFailures: args[3] === 'true' ? true : undefined,
990
- onlySuccesses: args[4] === 'true' ? true : undefined
1381
+ task,
1382
+ k,
1383
+ minReward,
1384
+ onlyFailures,
1385
+ onlySuccesses,
1386
+ synthesizeContext,
1387
+ filters: Object.keys(filters).length > 0 ? filters : undefined
991
1388
  });
992
1389
  } else if (subcommand === 'critique-summary') {
993
1390
  await cli.reflexionGetCritiqueSummary({
@@ -1045,7 +1442,110 @@ async function handleDbCommands(cli: AgentDBCLI, subcommand: string, args: strin
1045
1442
  }
1046
1443
  }
1047
1444
 
1048
- // Query command - semantic search across reflexion episodes
1445
+ async function handleSyncCommands(cli: AgentDBCLI, subcommand: string, args: string[]) {
1446
+ if (subcommand === 'start-server') {
1447
+ // Parse options
1448
+ let port: number | undefined;
1449
+ let cert: string | undefined;
1450
+ let key: string | undefined;
1451
+ let authToken: string | undefined;
1452
+
1453
+ for (let i = 0; i < args.length; i++) {
1454
+ if (args[i] === '--port' && i + 1 < args.length) {
1455
+ port = parseInt(args[++i]);
1456
+ } else if (args[i] === '--cert' && i + 1 < args.length) {
1457
+ cert = args[++i];
1458
+ } else if (args[i] === '--key' && i + 1 < args.length) {
1459
+ key = args[++i];
1460
+ } else if (args[i] === '--auth-token' && i + 1 < args.length) {
1461
+ authToken = args[++i];
1462
+ }
1463
+ }
1464
+
1465
+ await cli.quicStartServer({ port, cert, key, authToken });
1466
+ } else if (subcommand === 'connect') {
1467
+ // Parse host and port
1468
+ const host = args[0];
1469
+ const port = parseInt(args[1]);
1470
+
1471
+ if (!host || !port) {
1472
+ log.error('Missing required arguments: host and port');
1473
+ log.info('Usage: agentdb sync connect <host> <port> [--auth-token <token>] [--cert <path>]');
1474
+ process.exit(1);
1475
+ }
1476
+
1477
+ let authToken: string | undefined;
1478
+ let cert: string | undefined;
1479
+
1480
+ for (let i = 2; i < args.length; i++) {
1481
+ if (args[i] === '--auth-token' && i + 1 < args.length) {
1482
+ authToken = args[++i];
1483
+ } else if (args[i] === '--cert' && i + 1 < args.length) {
1484
+ cert = args[++i];
1485
+ }
1486
+ }
1487
+
1488
+ await cli.quicConnect({ host, port, authToken, cert });
1489
+ } else if (subcommand === 'push') {
1490
+ // Parse options
1491
+ let server: string | undefined;
1492
+ let incremental = false;
1493
+ let filter: string | undefined;
1494
+
1495
+ for (let i = 0; i < args.length; i++) {
1496
+ if (args[i] === '--server' && i + 1 < args.length) {
1497
+ server = args[++i];
1498
+ } else if (args[i] === '--incremental') {
1499
+ incremental = true;
1500
+ } else if (args[i] === '--filter' && i + 1 < args.length) {
1501
+ filter = args[++i];
1502
+ } else if (!args[i].startsWith('--') && !server) {
1503
+ server = args[i];
1504
+ }
1505
+ }
1506
+
1507
+ if (!server) {
1508
+ log.error('Missing required --server parameter');
1509
+ log.info('Usage: agentdb sync push --server <host:port> [--incremental] [--filter <pattern>]');
1510
+ process.exit(1);
1511
+ }
1512
+
1513
+ await cli.quicPush({ server, incremental, filter });
1514
+ } else if (subcommand === 'pull') {
1515
+ // Parse options
1516
+ let server: string | undefined;
1517
+ let incremental = false;
1518
+ let filter: string | undefined;
1519
+
1520
+ for (let i = 0; i < args.length; i++) {
1521
+ if (args[i] === '--server' && i + 1 < args.length) {
1522
+ server = args[++i];
1523
+ } else if (args[i] === '--incremental') {
1524
+ incremental = true;
1525
+ } else if (args[i] === '--filter' && i + 1 < args.length) {
1526
+ filter = args[++i];
1527
+ } else if (!args[i].startsWith('--') && !server) {
1528
+ server = args[i];
1529
+ }
1530
+ }
1531
+
1532
+ if (!server) {
1533
+ log.error('Missing required --server parameter');
1534
+ log.info('Usage: agentdb sync pull --server <host:port> [--incremental] [--filter <pattern>]');
1535
+ process.exit(1);
1536
+ }
1537
+
1538
+ await cli.quicPull({ server, incremental, filter });
1539
+ } else if (subcommand === 'status') {
1540
+ await cli.quicStatus();
1541
+ } else {
1542
+ log.error(`Unknown sync subcommand: ${subcommand}`);
1543
+ log.info('Available subcommands: start-server, connect, push, pull, status');
1544
+ printHelp();
1545
+ }
1546
+ }
1547
+
1548
+ // Query command - semantic search across reflexion episodes with context synthesis
1049
1549
  async function handleQueryCommand(cli: AgentDBCLI, args: string[]) {
1050
1550
  // Parse command-line arguments
1051
1551
  let domain = '';
@@ -1053,6 +1553,8 @@ async function handleQueryCommand(cli: AgentDBCLI, args: string[]) {
1053
1553
  let k = 5;
1054
1554
  let minConfidence = 0.0;
1055
1555
  let format = 'json';
1556
+ let synthesizeContext = false;
1557
+ let filters: any = {};
1056
1558
 
1057
1559
  for (let i = 0; i < args.length; i++) {
1058
1560
  if (args[i] === '--domain' && i + 1 < args.length) {
@@ -1065,6 +1567,15 @@ async function handleQueryCommand(cli: AgentDBCLI, args: string[]) {
1065
1567
  minConfidence = parseFloat(args[++i]);
1066
1568
  } else if (args[i] === '--format' && i + 1 < args.length) {
1067
1569
  format = args[++i];
1570
+ } else if (args[i] === '--synthesize-context') {
1571
+ synthesizeContext = true;
1572
+ } else if (args[i] === '--filters' && i + 1 < args.length) {
1573
+ try {
1574
+ filters = JSON.parse(args[++i]);
1575
+ } catch (error) {
1576
+ log.error(`Invalid JSON in --filters parameter: ${(error as Error).message}`);
1577
+ process.exit(1);
1578
+ }
1068
1579
  }
1069
1580
  }
1070
1581
 
@@ -1073,18 +1584,77 @@ async function handleQueryCommand(cli: AgentDBCLI, args: string[]) {
1073
1584
  process.exit(1);
1074
1585
  }
1075
1586
 
1587
+ // Validate filters if provided
1588
+ if (Object.keys(filters).length > 0) {
1589
+ const validation = MetadataFilter.validate(filters);
1590
+ if (!validation.valid) {
1591
+ log.error('Invalid filters:');
1592
+ validation.errors.forEach(err => log.error(` - ${err}`));
1593
+ process.exit(1);
1594
+ }
1595
+ }
1596
+
1076
1597
  // Use reflexionRetrieveJson method for JSON output
1077
- const results = await cli.reflexionRetrieveJson({
1598
+ let results = await cli.reflexionRetrieveJson({
1078
1599
  task: query,
1079
1600
  k,
1080
1601
  minReward: minConfidence,
1081
1602
  onlySuccesses: domain === 'successful-edits'
1082
1603
  });
1083
1604
 
1084
- if (format === 'json') {
1085
- console.log(JSON.stringify(results, null, 2));
1605
+ // Apply metadata filters if provided
1606
+ if (Object.keys(filters).length > 0) {
1607
+ results = MetadataFilter.apply(results, filters);
1608
+ log.info(`Filtered to ${results.length} results matching metadata criteria`);
1609
+ }
1610
+
1611
+ // Synthesize context if requested
1612
+ if (synthesizeContext && results.length > 0) {
1613
+ const context = ContextSynthesizer.synthesize(results.map((r: any) => ({
1614
+ task: r.task,
1615
+ reward: r.reward,
1616
+ success: r.success,
1617
+ critique: r.critique,
1618
+ input: r.input,
1619
+ output: r.output,
1620
+ similarity: r.similarity
1621
+ })));
1622
+
1623
+ if (format === 'json') {
1624
+ console.log(JSON.stringify({ results, synthesizedContext: context }, null, 2));
1625
+ } else {
1626
+ console.log('\n' + '═'.repeat(80));
1627
+ console.log('SYNTHESIZED CONTEXT');
1628
+ console.log('═'.repeat(80));
1629
+ console.log(`\n${context.summary}\n`);
1630
+
1631
+ if (context.patterns.length > 0) {
1632
+ console.log('Common Patterns:');
1633
+ context.patterns.forEach(p => console.log(` • ${p}`));
1634
+ console.log('');
1635
+ }
1636
+
1637
+ if (context.keyInsights.length > 0) {
1638
+ console.log('Key Insights:');
1639
+ context.keyInsights.forEach(i => console.log(` • ${i}`));
1640
+ console.log('');
1641
+ }
1642
+
1643
+ if (context.recommendations.length > 0) {
1644
+ console.log('Recommendations:');
1645
+ context.recommendations.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
1646
+ console.log('');
1647
+ }
1648
+
1649
+ console.log('═'.repeat(80));
1650
+ console.log(`\nMatched ${results.length} memories\n`);
1651
+ }
1086
1652
  } else {
1087
- console.log(results);
1653
+ if (format === 'json') {
1654
+ console.log(JSON.stringify(results, null, 2));
1655
+ } else {
1656
+ console.log(results);
1657
+ }
1088
1658
  }
1089
1659
  }
1090
1660
 
@@ -1226,6 +1796,399 @@ async function handleOptimizeMemoryCommand(cli: AgentDBCLI, args: string[]) {
1226
1796
  console.log(JSON.stringify({ success: true, message: 'Memory optimization completed' }, null, 2));
1227
1797
  }
1228
1798
 
1799
+ // Vector-search command - direct vector similarity search with MMR diversity ranking
1800
+ async function handleVectorSearchCommand(args: string[]) {
1801
+ // Parse arguments
1802
+ let dbPath = './agentdb.db';
1803
+ let vector: number[] = [];
1804
+ let k = 10;
1805
+ let threshold = 0.0;
1806
+ let metric = 'cosine';
1807
+ let format = 'json';
1808
+ let verbose = false;
1809
+ let useMmr = false;
1810
+ let mmrLambda = 0.5;
1811
+
1812
+ for (let i = 0; i < args.length; i++) {
1813
+ const arg = args[i];
1814
+ if (arg === '-k' && i + 1 < args.length) {
1815
+ k = parseInt(args[++i]);
1816
+ } else if (arg === '-t' && i + 1 < args.length) {
1817
+ threshold = parseFloat(args[++i]);
1818
+ } else if (arg === '-m' && i + 1 < args.length) {
1819
+ metric = args[++i];
1820
+ } else if (arg === '-f' && i + 1 < args.length) {
1821
+ format = args[++i];
1822
+ } else if (arg === '-v' || arg === '--verbose') {
1823
+ verbose = true;
1824
+ } else if (arg === '--mmr') {
1825
+ useMmr = true;
1826
+ if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
1827
+ mmrLambda = parseFloat(args[++i]);
1828
+ }
1829
+ } else if (!dbPath.endsWith('.db') && !arg.startsWith('[')) {
1830
+ dbPath = arg;
1831
+ } else if (arg.startsWith('[') || (!isNaN(parseFloat(arg)))) {
1832
+ // Parse vector - either JSON array or space-separated numbers
1833
+ try {
1834
+ if (arg.startsWith('[')) {
1835
+ vector = JSON.parse(arg);
1836
+ } else {
1837
+ // Collect space-separated numbers
1838
+ while (i < args.length && !isNaN(parseFloat(args[i]))) {
1839
+ vector.push(parseFloat(args[i++]));
1840
+ }
1841
+ i--; // Back up one since loop will increment
1842
+ }
1843
+ } catch (e) {
1844
+ log.error('Invalid vector format. Use JSON array: "[0.1,0.2,0.3]" or space-separated: "0.1 0.2 0.3"');
1845
+ process.exit(1);
1846
+ }
1847
+ }
1848
+ }
1849
+
1850
+ if (vector.length === 0) {
1851
+ log.error('Missing required vector parameter');
1852
+ log.info('Usage: agentdb vector-search <db-path> <vector> [-k 10] [-t 0.75] [-m cosine] [-f json] [-v] [--mmr [lambda]]');
1853
+ process.exit(1);
1854
+ }
1855
+
1856
+ if (useMmr) {
1857
+ log.info(`Using MMR diversity ranking (λ=${mmrLambda})`);
1858
+ }
1859
+
1860
+ // Initialize database
1861
+ const cli = new AgentDBCLI();
1862
+ await cli.initialize(dbPath);
1863
+
1864
+ // Perform vector search using reflexion's retrieveRelevant (which uses embeddings)
1865
+ // We'll need to search episode_embeddings table directly
1866
+ const query = `
1867
+ SELECT
1868
+ e.id,
1869
+ e.session_id,
1870
+ e.task,
1871
+ e.reward,
1872
+ e.success,
1873
+ ee.embedding
1874
+ FROM episodes e
1875
+ JOIN episode_embeddings ee ON e.id = ee.episode_id
1876
+ LIMIT ?
1877
+ `;
1878
+
1879
+ const results = cli.db.prepare(query).all(k * 10); // Get more for filtering
1880
+
1881
+ // Calculate similarities
1882
+ let scored = results.map((row: any) => {
1883
+ const embedding = new Float32Array(row.embedding);
1884
+ const similarity = calculateSimilarity(vector, Array.from(embedding), metric);
1885
+ return {
1886
+ id: row.id,
1887
+ session_id: row.session_id,
1888
+ task: row.task,
1889
+ reward: row.reward,
1890
+ success: row.success,
1891
+ similarity,
1892
+ embedding: Array.from(embedding)
1893
+ };
1894
+ }).filter(r => r.similarity >= threshold)
1895
+ .sort((a, b) => b.similarity - a.similarity);
1896
+
1897
+ // Apply MMR diversity ranking if requested
1898
+ if (useMmr && scored.length > k) {
1899
+ scored = MMRDiversityRanker.selectDiverse(scored, vector, {
1900
+ lambda: mmrLambda,
1901
+ k,
1902
+ metric: metric as 'cosine' | 'euclidean' | 'dot'
1903
+ });
1904
+ } else {
1905
+ scored = scored.slice(0, k);
1906
+ }
1907
+
1908
+ // Remove embedding from output
1909
+ const output = scored.map(({ embedding, ...rest }) => rest);
1910
+
1911
+ if (format === 'json') {
1912
+ console.log(JSON.stringify(output, null, 2));
1913
+ } else {
1914
+ console.log(`Found ${output.length} results:`);
1915
+ output.forEach((r, i) => {
1916
+ console.log(`${i + 1}. [${r.id}] ${r.task} (similarity: ${r.similarity.toFixed(4)})`);
1917
+ if (verbose) {
1918
+ console.log(` Session: ${r.session_id}, Reward: ${r.reward}, Success: ${r.success}`);
1919
+ }
1920
+ });
1921
+ }
1922
+ }
1923
+
1924
+ // Helper function to calculate similarity
1925
+ function calculateSimilarity(v1: number[], v2: number[], metric: string): number {
1926
+ if (v1.length !== v2.length) {
1927
+ throw new Error(`Vector dimension mismatch: ${v1.length} vs ${v2.length}`);
1928
+ }
1929
+
1930
+ if (metric === 'cosine') {
1931
+ let dot = 0, mag1 = 0, mag2 = 0;
1932
+ for (let i = 0; i < v1.length; i++) {
1933
+ dot += v1[i] * v2[i];
1934
+ mag1 += v1[i] * v1[i];
1935
+ mag2 += v2[i] * v2[i];
1936
+ }
1937
+ return dot / (Math.sqrt(mag1) * Math.sqrt(mag2));
1938
+ } else if (metric === 'euclidean') {
1939
+ let sum = 0;
1940
+ for (let i = 0; i < v1.length; i++) {
1941
+ const diff = v1[i] - v2[i];
1942
+ sum += diff * diff;
1943
+ }
1944
+ return 1 / (1 + Math.sqrt(sum)); // Normalize to 0-1 range
1945
+ } else if (metric === 'dot') {
1946
+ let dot = 0;
1947
+ for (let i = 0; i < v1.length; i++) {
1948
+ dot += v1[i] * v2[i];
1949
+ }
1950
+ return dot;
1951
+ }
1952
+
1953
+ return 0;
1954
+ }
1955
+
1956
+ // Export command - export vectors to JSON with optional compression
1957
+ async function handleExportCommand(args: string[]) {
1958
+ let dbPath = './agentdb.db';
1959
+ let outputPath = './agentdb-export.json';
1960
+ let compress = false;
1961
+
1962
+ // Parse arguments
1963
+ for (let i = 0; i < args.length; i++) {
1964
+ const arg = args[i];
1965
+ if (arg === '--compress') {
1966
+ compress = true;
1967
+ } else if (arg === '--output' && i + 1 < args.length) {
1968
+ outputPath = args[++i];
1969
+ } else if (!arg.startsWith('--')) {
1970
+ if (dbPath === './agentdb.db') {
1971
+ dbPath = arg;
1972
+ } else if (outputPath === './agentdb-export.json') {
1973
+ outputPath = arg;
1974
+ }
1975
+ }
1976
+ }
1977
+
1978
+ // Add .gz extension if compressing and not already present
1979
+ if (compress && !outputPath.endsWith('.gz')) {
1980
+ outputPath += '.gz';
1981
+ }
1982
+
1983
+ log.info(`Exporting vectors from: ${dbPath}`);
1984
+ if (compress) {
1985
+ log.info('Compression: enabled');
1986
+ }
1987
+
1988
+ const cli = new AgentDBCLI();
1989
+ await cli.initialize(dbPath);
1990
+
1991
+ // Export all episodes with embeddings
1992
+ const query = `
1993
+ SELECT
1994
+ e.*,
1995
+ ee.embedding
1996
+ FROM episodes e
1997
+ LEFT JOIN episode_embeddings ee ON e.id = ee.episode_id
1998
+ `;
1999
+
2000
+ const results = cli.db.prepare(query).all();
2001
+
2002
+ const jsonData = JSON.stringify(results, null, 2);
2003
+
2004
+ try {
2005
+ if (compress) {
2006
+ // Compress with gzip
2007
+ const compressed = zlib.gzipSync(jsonData);
2008
+ fs.writeFileSync(outputPath, compressed);
2009
+ const originalSize = Buffer.byteLength(jsonData);
2010
+ const compressedSize = compressed.length;
2011
+ const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(1);
2012
+ log.success(`Exported ${results.length} episodes to ${outputPath}`);
2013
+ log.info(`Original size: ${(originalSize / 1024).toFixed(2)} KB`);
2014
+ log.info(`Compressed size: ${(compressedSize / 1024).toFixed(2)} KB (${ratio}% reduction)`);
2015
+ } else {
2016
+ fs.writeFileSync(outputPath, jsonData);
2017
+ log.success(`Exported ${results.length} episodes to ${outputPath}`);
2018
+ }
2019
+ } catch (error) {
2020
+ log.error(`Failed to export: ${(error as Error).message}`);
2021
+ process.exit(1);
2022
+ }
2023
+ }
2024
+
2025
+ // Import command - import vectors from JSON with optional decompression
2026
+ async function handleImportCommand(args: string[]) {
2027
+ let inputPath = '';
2028
+ let dbPath = './agentdb.db';
2029
+ let decompress = false;
2030
+
2031
+ // Parse arguments
2032
+ for (let i = 0; i < args.length; i++) {
2033
+ const arg = args[i];
2034
+ if (arg === '--decompress') {
2035
+ decompress = true;
2036
+ } else if (arg === '--db' && i + 1 < args.length) {
2037
+ dbPath = args[++i];
2038
+ } else if (!arg.startsWith('--')) {
2039
+ if (!inputPath) {
2040
+ inputPath = arg;
2041
+ } else if (dbPath === './agentdb.db') {
2042
+ dbPath = arg;
2043
+ }
2044
+ }
2045
+ }
2046
+
2047
+ // Auto-detect compression from .gz extension
2048
+ if (inputPath.endsWith('.gz')) {
2049
+ decompress = true;
2050
+ }
2051
+
2052
+ if (!inputPath) {
2053
+ log.error('Missing required input file');
2054
+ log.info('Usage: agentdb import <input-file.json> [db-path] [--decompress]');
2055
+ process.exit(1);
2056
+ }
2057
+
2058
+ log.info(`Importing vectors from: ${inputPath}`);
2059
+ if (decompress) {
2060
+ log.info('Decompression: enabled');
2061
+ }
2062
+
2063
+ let data: any[];
2064
+
2065
+ try {
2066
+ if (decompress) {
2067
+ // Decompress with gunzip
2068
+ const compressed = fs.readFileSync(inputPath);
2069
+ const decompressed = zlib.gunzipSync(compressed);
2070
+ data = JSON.parse(decompressed.toString('utf-8'));
2071
+ log.info(`Decompressed ${(compressed.length / 1024).toFixed(2)} KB to ${(decompressed.length / 1024).toFixed(2)} KB`);
2072
+ } else {
2073
+ data = JSON.parse(fs.readFileSync(inputPath, 'utf-8'));
2074
+ }
2075
+ } catch (error) {
2076
+ log.error(`Failed to read/parse input file: ${(error as Error).message}`);
2077
+ process.exit(1);
2078
+ }
2079
+
2080
+ const cli = new AgentDBCLI();
2081
+ await cli.initialize(dbPath);
2082
+
2083
+ let imported = 0;
2084
+ for (const item of data) {
2085
+ try {
2086
+ // Import episode
2087
+ const episodeQuery = `
2088
+ INSERT INTO episodes (session_id, task, input, output, critique, reward, success, latency_ms, tokens_used, tags, metadata)
2089
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2090
+ `;
2091
+
2092
+ const result = cli.db.prepare(episodeQuery).run(
2093
+ item.session_id,
2094
+ item.task,
2095
+ item.input,
2096
+ item.output,
2097
+ item.critique,
2098
+ item.reward,
2099
+ item.success,
2100
+ item.latency_ms,
2101
+ item.tokens_used,
2102
+ item.tags,
2103
+ item.metadata
2104
+ );
2105
+
2106
+ // Import embedding if exists
2107
+ if (item.embedding) {
2108
+ const embeddingQuery = `INSERT INTO episode_embeddings (episode_id, embedding) VALUES (?, ?)`;
2109
+ cli.db.prepare(embeddingQuery).run(result.lastInsertRowid, item.embedding);
2110
+ }
2111
+
2112
+ imported++;
2113
+ } catch (error) {
2114
+ log.warning(`Failed to import item ${imported + 1}: ${(error as Error).message}`);
2115
+ }
2116
+ }
2117
+
2118
+ // Save database
2119
+ if (cli.db && typeof cli.db.save === 'function') {
2120
+ cli.db.save();
2121
+ }
2122
+
2123
+ log.success(`Imported ${imported} episodes`);
2124
+ }
2125
+
2126
+ // Stats command - show database statistics
2127
+ async function handleStatsCommand(args: string[]) {
2128
+ const dbPath = args[0] || './agentdb.db';
2129
+
2130
+ log.info(`Getting statistics for: ${dbPath}`);
2131
+
2132
+ const cli = new AgentDBCLI();
2133
+ await cli.initialize(dbPath);
2134
+
2135
+ // Get counts (with fallback for missing tables)
2136
+ const episodeCount = cli.db.prepare('SELECT COUNT(*) as count FROM episodes').get()?.count || 0;
2137
+ const embeddingCount = cli.db.prepare('SELECT COUNT(*) as count FROM episode_embeddings').get()?.count || 0;
2138
+
2139
+ let skillCount = 0;
2140
+ try {
2141
+ skillCount = cli.db.prepare('SELECT COUNT(*) as count FROM skill_library').get()?.count || 0;
2142
+ } catch (e) {
2143
+ // skill_library table may not exist in older databases
2144
+ }
2145
+
2146
+ let causalEdges = 0;
2147
+ try {
2148
+ causalEdges = cli.db.prepare('SELECT COUNT(*) as count FROM causal_edges').get()?.count || 0;
2149
+ } catch (e) {
2150
+ // causal_edges table may not exist in older databases
2151
+ }
2152
+
2153
+ // Get database file size
2154
+ let dbSize = 0;
2155
+ if (dbPath !== ':memory:' && fs.existsSync(dbPath)) {
2156
+ dbSize = fs.statSync(dbPath).size;
2157
+ }
2158
+
2159
+ // Get average confidence
2160
+ const avgConfidence = cli.db.prepare('SELECT AVG(reward) as avg FROM episodes').get().avg || 0;
2161
+
2162
+ // Get domains
2163
+ const domains = cli.db.prepare(`
2164
+ SELECT task, COUNT(*) as count
2165
+ FROM episodes
2166
+ GROUP BY task
2167
+ ORDER BY count DESC
2168
+ LIMIT 5
2169
+ `).all();
2170
+
2171
+ console.log(`
2172
+ 📊 AgentDB Statistics
2173
+
2174
+ Database: ${dbPath}
2175
+ Size: ${(dbSize / 1024).toFixed(2)} KB
2176
+
2177
+ 📈 Counts:
2178
+ Episodes: ${episodeCount}
2179
+ Embeddings: ${embeddingCount}
2180
+ Skills: ${skillCount}
2181
+ Causal Edges: ${causalEdges}
2182
+
2183
+ 📊 Metrics:
2184
+ Average Reward: ${avgConfidence.toFixed(3)}
2185
+ Embedding Coverage: ${episodeCount > 0 ? ((embeddingCount / episodeCount) * 100).toFixed(1) : 0}%
2186
+
2187
+ 🏷️ Top Domains:
2188
+ ${domains.map(d => ` • ${d.task}: ${d.count}`).join('\n')}
2189
+ `);
2190
+ }
2191
+
1229
2192
  function printHelp() {
1230
2193
  console.log(`
1231
2194
  ${colors.bright}${colors.cyan}█▀█ █▀▀ █▀▀ █▄░█ ▀█▀ █▀▄ █▄▄
@@ -1237,13 +2200,96 @@ ${colors.bright}USAGE:${colors.reset}
1237
2200
  agentdb <command> <subcommand> [options]
1238
2201
 
1239
2202
  ${colors.bright}SETUP COMMANDS:${colors.reset}
1240
- agentdb init [db-path]
2203
+ agentdb init [db-path] [--dimension 1536] [--preset small|medium|large] [--in-memory]
1241
2204
  Initialize a new AgentDB database (default: ./agentdb.db)
2205
+ Options:
2206
+ --dimension <n> Vector dimension (default: 1536 for OpenAI, 768 for sentence-transformers)
2207
+ --preset <size> small (<10K), medium (10K-100K), large (>100K vectors)
2208
+ --in-memory Use temporary in-memory database (:memory:)
2209
+
2210
+ ${colors.bright}VECTOR SEARCH COMMANDS:${colors.reset}
2211
+ agentdb vector-search <db-path> <vector> [-k 10] [-t 0.75] [-m cosine] [-f json] [-v] [--mmr [lambda]]
2212
+ Direct vector similarity search without text embeddings
2213
+ Arguments:
2214
+ <db-path> Database file path (or :memory:)
2215
+ <vector> Vector as JSON array [0.1,0.2,...] or space-separated numbers
2216
+ Options:
2217
+ -k <n> Number of results (default: 10)
2218
+ -t <threshold> Minimum similarity threshold (default: 0.0)
2219
+ -m <metric> Similarity metric: cosine|euclidean|dot (default: cosine)
2220
+ -f <format> Output format: json|table (default: json)
2221
+ -v Verbose mode with similarity scores
2222
+ --mmr [lambda] Enable MMR diversity ranking (lambda: 0-1, default: 0.5)
2223
+ 0 = max diversity, 1 = max relevance
2224
+ Example: agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" -k 10 -m cosine
2225
+ Example: agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" --mmr 0.7
2226
+
2227
+ agentdb export <db-path> [output-file] [--compress]
2228
+ Export all vectors and episodes to JSON file
2229
+ Options:
2230
+ --compress Compress output with gzip (adds .gz extension)
2231
+ --output <file> Output file path
2232
+ Example: agentdb export ./agentdb.db ./backup.json
2233
+ Example: agentdb export ./agentdb.db --compress --output backup.json.gz
2234
+
2235
+ agentdb import <input-file> [db-path] [--decompress]
2236
+ Import vectors and episodes from JSON file
2237
+ Options:
2238
+ --decompress Decompress gzip input (auto-detected for .gz files)
2239
+ --db <path> Database file path
2240
+ Example: agentdb import ./backup.json ./new-db.db
2241
+ Example: agentdb import ./backup.json.gz --decompress
2242
+
2243
+ agentdb stats [db-path]
2244
+ Show detailed database statistics and metrics
2245
+ Example: agentdb stats ./agentdb.db
1242
2246
 
1243
2247
  ${colors.bright}MCP COMMANDS:${colors.reset}
1244
2248
  agentdb mcp start
1245
2249
  Start the MCP server for Claude Desktop integration
1246
2250
 
2251
+ ${colors.bright}QUIC SYNC COMMANDS:${colors.reset}
2252
+ agentdb sync start-server [--port 4433] [--cert <path>] [--key <path>] [--auth-token <token>]
2253
+ Start a QUIC synchronization server for multi-agent coordination
2254
+ Options:
2255
+ --port <n> Server port (default: 4433)
2256
+ --cert <path> TLS certificate file path
2257
+ --key <path> TLS key file path
2258
+ --auth-token <token> Authentication token (auto-generated if not provided)
2259
+ Example: agentdb sync start-server --port 4433 --cert ./cert.pem --key ./key.pem
2260
+
2261
+ agentdb sync connect <host> <port> [--auth-token <token>] [--cert <path>]
2262
+ Connect to a remote QUIC sync server
2263
+ Arguments:
2264
+ <host> Remote server hostname or IP
2265
+ <port> Remote server port
2266
+ Options:
2267
+ --auth-token <token> Authentication token
2268
+ --cert <path> TLS certificate for verification
2269
+ Example: agentdb sync connect 192.168.1.100 4433 --auth-token abc123
2270
+
2271
+ agentdb sync push --server <host:port> [--incremental] [--filter <pattern>]
2272
+ Push local changes to remote server
2273
+ Options:
2274
+ --server <host:port> Remote server address (e.g., 192.168.1.100:4433)
2275
+ --incremental Only push changes since last sync
2276
+ --filter <pattern> Filter changes by pattern (e.g., "episodes", "skills")
2277
+ Example: agentdb sync push --server 192.168.1.100:4433 --incremental
2278
+ Example: agentdb sync push --server localhost:4433 --filter "episodes"
2279
+
2280
+ agentdb sync pull --server <host:port> [--incremental] [--filter <pattern>]
2281
+ Pull remote changes from server
2282
+ Options:
2283
+ --server <host:port> Remote server address (e.g., 192.168.1.100:4433)
2284
+ --incremental Only pull changes since last sync
2285
+ --filter <pattern> Filter changes by pattern (e.g., "skills", "causal_edges")
2286
+ Example: agentdb sync pull --server 192.168.1.100:4433 --incremental
2287
+ Example: agentdb sync pull --server localhost:4433 --filter "skills"
2288
+
2289
+ agentdb sync status
2290
+ Show synchronization status, pending changes, and connected servers
2291
+ Example: agentdb sync status
2292
+
1247
2293
  ${colors.bright}CAUSAL COMMANDS:${colors.reset}
1248
2294
  agentdb causal add-edge <cause> <effect> <uplift> [confidence] [sample-size]
1249
2295
  Add a causal edge manually
@@ -1278,8 +2324,17 @@ ${colors.bright}REFLEXION COMMANDS:${colors.reset}
1278
2324
  agentdb reflexion store <session-id> <task> <reward> <success> [critique] [input] [output] [latency-ms] [tokens]
1279
2325
  Store episode with self-critique
1280
2326
 
1281
- agentdb reflexion retrieve <task> [k] [min-reward] [only-failures] [only-successes]
2327
+ agentdb reflexion retrieve <task> [--k <n>] [--min-reward <r>] [--only-failures] [--only-successes] [--synthesize-context] [--filters <json>]
1282
2328
  Retrieve relevant past episodes
2329
+ Options:
2330
+ --k <n> Number of results (default: 5)
2331
+ --min-reward <r> Minimum reward threshold
2332
+ --only-failures Return only failed episodes
2333
+ --only-successes Return only successful episodes
2334
+ --synthesize-context Generate coherent summary with patterns and insights
2335
+ --filters <json> MongoDB-style metadata filters (e.g., '{"metadata.year":{"$gte":2024}}')
2336
+ Example: agentdb reflexion retrieve "authentication" --k 10 --synthesize-context
2337
+ Example: agentdb reflexion retrieve "bug-fix" --filters '{"success":true,"reward":{"$gte":0.8}}'
1283
2338
 
1284
2339
  agentdb reflexion critique-summary <task> [only-failures]
1285
2340
  Get aggregated critique lessons
@@ -1307,9 +2362,18 @@ ${colors.bright}DATABASE COMMANDS:${colors.reset}
1307
2362
  Show database statistics
1308
2363
 
1309
2364
  ${colors.bright}HOOKS INTEGRATION COMMANDS:${colors.reset}
1310
- agentdb query --domain <domain> --query <query> --k <k> --min-confidence <conf> --format json
2365
+ agentdb query --query <query> [--domain <domain>] [--k <k>] [--min-confidence <conf>] [--format json] [--synthesize-context] [--filters <json>]
1311
2366
  Semantic search across stored episodes and patterns
1312
- Example: agentdb query --domain "successful-edits" --query "file:utils.ts" --k 5 --min-confidence 0.8
2367
+ Options:
2368
+ --query <q> Query string (required)
2369
+ --domain <d> Domain filter (e.g., "successful-edits")
2370
+ --k <n> Number of results (default: 5)
2371
+ --min-confidence <c> Minimum confidence threshold (default: 0.0)
2372
+ --format <f> Output format: json|text (default: json)
2373
+ --synthesize-context Generate coherent summary with patterns and insights
2374
+ --filters <json> MongoDB-style metadata filters
2375
+ Example: agentdb query --query "authentication" --k 5 --min-confidence 0.8 --synthesize-context
2376
+ Example: agentdb query --query "bug-fix" --filters '{"metadata.priority":"high"}' --synthesize-context
1313
2377
 
1314
2378
  agentdb store-pattern --type <type> --domain <domain> --pattern <json> --confidence <conf>
1315
2379
  Store a learned pattern for future retrieval
@@ -1327,6 +2391,23 @@ ${colors.bright}ENVIRONMENT:${colors.reset}
1327
2391
  AGENTDB_PATH Database file path (default: ./agentdb.db)
1328
2392
 
1329
2393
  ${colors.bright}EXAMPLES:${colors.reset}
2394
+ # QUIC Sync: Multi-agent coordination
2395
+ # On server machine:
2396
+ agentdb sync start-server --port 4433 --auth-token secret123
2397
+
2398
+ # On client machines:
2399
+ agentdb sync connect 192.168.1.100 4433 --auth-token secret123
2400
+ agentdb sync push --server 192.168.1.100:4433 --incremental
2401
+ agentdb sync pull --server 192.168.1.100:4433 --incremental
2402
+ agentdb sync status
2403
+
2404
+ # Vector Search: Direct similarity queries
2405
+ agentdb init ./vectors.db --dimension 768 --preset medium
2406
+ agentdb vector-search ./vectors.db "[0.1,0.2,0.3]" -k 10 -m cosine -f json
2407
+ agentdb export ./vectors.db ./backup.json
2408
+ agentdb import ./backup.json ./new-vectors.db
2409
+ agentdb stats ./vectors.db
2410
+
1330
2411
  # Reflexion: Store and retrieve episodes
1331
2412
  agentdb reflexion store "session-1" "implement_auth" 0.95 true "Used OAuth2"
1332
2413
  agentdb reflexion retrieve "authentication" 10 0.8