ruvector 0.2.23 → 0.2.26

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 (120) hide show
  1. package/bin/cli.js +332 -79
  2. package/bin/mcp-server.js +94 -22
  3. package/dist/analysis/complexity.d.ts +52 -0
  4. package/dist/analysis/complexity.d.ts.map +1 -0
  5. package/dist/analysis/complexity.js +146 -0
  6. package/dist/analysis/index.d.ts +15 -0
  7. package/dist/analysis/index.d.ts.map +1 -0
  8. package/dist/analysis/index.js +38 -0
  9. package/dist/analysis/patterns.d.ts +71 -0
  10. package/dist/analysis/patterns.d.ts.map +1 -0
  11. package/dist/analysis/patterns.js +243 -0
  12. package/dist/analysis/security.d.ts +51 -0
  13. package/dist/analysis/security.d.ts.map +1 -0
  14. package/dist/analysis/security.js +139 -0
  15. package/dist/core/adaptive-embedder.d.ts +156 -0
  16. package/dist/core/adaptive-embedder.d.ts.map +1 -0
  17. package/dist/core/adaptive-embedder.js +838 -0
  18. package/dist/core/agentdb-fast.d.ts +149 -0
  19. package/dist/core/agentdb-fast.d.ts.map +1 -0
  20. package/dist/core/agentdb-fast.js +301 -0
  21. package/dist/core/ast-parser.d.ts +108 -0
  22. package/dist/core/ast-parser.d.ts.map +1 -0
  23. package/dist/core/ast-parser.js +602 -0
  24. package/dist/core/attention-fallbacks.d.ts +321 -0
  25. package/dist/core/attention-fallbacks.d.ts.map +1 -0
  26. package/dist/core/attention-fallbacks.js +552 -0
  27. package/dist/core/cluster-wrapper.d.ts +148 -0
  28. package/dist/core/cluster-wrapper.d.ts.map +1 -0
  29. package/dist/core/cluster-wrapper.js +271 -0
  30. package/dist/core/coverage-router.d.ts +88 -0
  31. package/dist/core/coverage-router.d.ts.map +1 -0
  32. package/dist/core/coverage-router.js +315 -0
  33. package/dist/core/diff-embeddings.d.ts +93 -0
  34. package/dist/core/diff-embeddings.d.ts.map +1 -0
  35. package/dist/core/diff-embeddings.js +334 -0
  36. package/dist/core/diskann-wrapper.d.ts +53 -0
  37. package/dist/core/diskann-wrapper.d.ts.map +1 -0
  38. package/dist/core/diskann-wrapper.js +105 -0
  39. package/dist/core/gnn-wrapper.d.ts +143 -0
  40. package/dist/core/gnn-wrapper.d.ts.map +1 -0
  41. package/dist/core/gnn-wrapper.js +213 -0
  42. package/dist/core/graph-algorithms.d.ts +83 -0
  43. package/dist/core/graph-algorithms.d.ts.map +1 -0
  44. package/dist/core/graph-algorithms.js +514 -0
  45. package/dist/core/graph-wrapper.d.ts +147 -0
  46. package/dist/core/graph-wrapper.d.ts.map +1 -0
  47. package/dist/core/graph-wrapper.js +299 -0
  48. package/dist/core/index.d.ts +50 -0
  49. package/dist/core/index.d.ts.map +1 -0
  50. package/dist/core/index.js +92 -0
  51. package/dist/core/intelligence-engine.d.ts +258 -0
  52. package/dist/core/intelligence-engine.d.ts.map +1 -0
  53. package/dist/core/intelligence-engine.js +1026 -0
  54. package/dist/core/learning-engine.d.ts +162 -0
  55. package/dist/core/learning-engine.d.ts.map +1 -0
  56. package/dist/core/learning-engine.js +609 -0
  57. package/dist/core/neural-embeddings.d.ts +393 -0
  58. package/dist/core/neural-embeddings.d.ts.map +1 -0
  59. package/dist/core/neural-embeddings.js +1091 -0
  60. package/dist/core/neural-perf.d.ts +331 -0
  61. package/dist/core/neural-perf.d.ts.map +1 -0
  62. package/dist/core/neural-perf.js +704 -0
  63. package/dist/core/onnx/loader.js +348 -0
  64. package/dist/core/onnx/pkg/LICENSE +21 -0
  65. package/dist/core/onnx/pkg/loader.js +348 -0
  66. package/dist/core/onnx/pkg/package.json +3 -0
  67. package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.d.ts +112 -0
  68. package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.js +5 -0
  69. package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.js +638 -0
  70. package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm +0 -0
  71. package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm.d.ts +29 -0
  72. package/dist/core/onnx-embedder.d.ts +105 -0
  73. package/dist/core/onnx-embedder.d.ts.map +1 -0
  74. package/dist/core/onnx-embedder.js +414 -0
  75. package/dist/core/onnx-optimized.d.ts +109 -0
  76. package/dist/core/onnx-optimized.d.ts.map +1 -0
  77. package/dist/core/onnx-optimized.js +419 -0
  78. package/dist/core/parallel-intelligence.d.ts +109 -0
  79. package/dist/core/parallel-intelligence.d.ts.map +1 -0
  80. package/dist/core/parallel-intelligence.js +340 -0
  81. package/dist/core/parallel-workers.d.ts +177 -0
  82. package/dist/core/parallel-workers.d.ts.map +1 -0
  83. package/dist/core/parallel-workers.js +783 -0
  84. package/dist/core/router-wrapper.d.ts +75 -0
  85. package/dist/core/router-wrapper.d.ts.map +1 -0
  86. package/dist/core/router-wrapper.js +243 -0
  87. package/dist/core/rvf-wrapper.d.ts +86 -0
  88. package/dist/core/rvf-wrapper.d.ts.map +1 -0
  89. package/dist/core/rvf-wrapper.js +102 -0
  90. package/dist/core/sona-wrapper.d.ts +226 -0
  91. package/dist/core/sona-wrapper.d.ts.map +1 -0
  92. package/dist/core/sona-wrapper.js +282 -0
  93. package/dist/core/tensor-compress.d.ts +134 -0
  94. package/dist/core/tensor-compress.d.ts.map +1 -0
  95. package/dist/core/tensor-compress.js +432 -0
  96. package/dist/index.d.ts +160 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +359 -0
  99. package/dist/services/embedding-service.d.ts +136 -0
  100. package/dist/services/embedding-service.d.ts.map +1 -0
  101. package/dist/services/embedding-service.js +294 -0
  102. package/dist/services/index.d.ts +6 -0
  103. package/dist/services/index.d.ts.map +1 -0
  104. package/dist/services/index.js +26 -0
  105. package/dist/types.d.ts +145 -0
  106. package/dist/types.d.ts.map +1 -0
  107. package/dist/types.js +2 -0
  108. package/dist/workers/benchmark.d.ts +44 -0
  109. package/dist/workers/benchmark.d.ts.map +1 -0
  110. package/dist/workers/benchmark.js +230 -0
  111. package/dist/workers/index.d.ts +10 -0
  112. package/dist/workers/index.d.ts.map +1 -0
  113. package/dist/workers/index.js +25 -0
  114. package/dist/workers/native-worker.d.ts +76 -0
  115. package/dist/workers/native-worker.d.ts.map +1 -0
  116. package/dist/workers/native-worker.js +490 -0
  117. package/dist/workers/types.d.ts +69 -0
  118. package/dist/workers/types.d.ts.map +1 -0
  119. package/dist/workers/types.js +7 -0
  120. package/package.json +11 -9
package/bin/cli.js CHANGED
@@ -125,6 +125,21 @@ const program = new Command();
125
125
  // Get package version from package.json
126
126
  const packageJson = require('../package.json');
127
127
 
128
+ // `@ruvector/gnn@0.1.25` has a native-binding regression where every method
129
+ // throws `Given napi value is not an array` regardless of input shape (verified
130
+ // with both Array<Float32Array> and number[][]). Use this helper to print a
131
+ // pointer to the upstream issue when the CLI-side typed-array conversion is
132
+ // already correct.
133
+ function reportGnnBindingError(error) {
134
+ const msg = error && error.message ? error.message : String(error);
135
+ console.error(chalk.red(msg));
136
+ if (msg.includes('Given napi value is not an array') || msg.includes('TypedArray info failed')) {
137
+ console.error(chalk.yellow(' Note: this is a known regression in the @ruvector/gnn native binding,'));
138
+ console.error(chalk.yellow(' not in the CLI. Track at:'));
139
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/402'));
140
+ }
141
+ }
142
+
128
143
  // Version and description (lazy load implementation info)
129
144
  program
130
145
  .name('ruvector')
@@ -149,6 +164,9 @@ program
149
164
  storagePath: dbPath,
150
165
  });
151
166
 
167
+ // Write sidecar so insert/search/stats can recover dimension without JSON-parsing binary redb
168
+ fs.writeFileSync(`${dbPath}.meta.json`, JSON.stringify({ dimension, metric: options.metric, version: 1 }));
169
+
152
170
  spinner.succeed(chalk.green(`Database created: ${dbPath}`));
153
171
  console.log(chalk.gray(` Dimension: ${dimension}`));
154
172
  console.log(chalk.gray(` Metric: ${options.metric}`));
@@ -170,15 +188,14 @@ program
170
188
  const spinner = ora('Loading database...').start();
171
189
 
172
190
  try {
173
- // Read database metadata to get dimension
174
- let dimension = 384; // default
175
- if (fs.existsSync(dbPath)) {
176
- const dbData = fs.readFileSync(dbPath, 'utf8');
177
- const parsed = JSON.parse(dbData);
178
- dimension = parsed.dimension || 384;
191
+ // Read dimension from sidecar (avoids JSON-parsing binary redb)
192
+ let dimension = 384;
193
+ const metaPath = `${dbPath}.meta.json`;
194
+ if (fs.existsSync(metaPath)) {
195
+ try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
179
196
  }
180
197
 
181
- const db = new VectorDB({ dimension });
198
+ const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
182
199
 
183
200
  if (fs.existsSync(dbPath)) {
184
201
  db.load(dbPath);
@@ -222,12 +239,14 @@ program
222
239
  const spinner = ora('Loading database...').start();
223
240
 
224
241
  try {
225
- // Read database metadata
226
- const dbData = fs.readFileSync(dbPath, 'utf8');
227
- const parsed = JSON.parse(dbData);
228
- const dimension = parsed.dimension || 384;
242
+ // Read dimension from sidecar (avoids JSON-parsing binary redb)
243
+ let dimension = 384;
244
+ const metaPath = `${dbPath}.meta.json`;
245
+ if (fs.existsSync(metaPath)) {
246
+ try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
247
+ }
229
248
 
230
- const db = new VectorDB({ dimension });
249
+ const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
231
250
  db.load(dbPath);
232
251
 
233
252
  spinner.text = 'Searching...';
@@ -270,11 +289,14 @@ program
270
289
  const spinner = ora('Loading database...').start();
271
290
 
272
291
  try {
273
- const dbData = fs.readFileSync(dbPath, 'utf8');
274
- const parsed = JSON.parse(dbData);
275
- const dimension = parsed.dimension || 384;
292
+ // Read dimension from sidecar (avoids JSON-parsing binary redb)
293
+ let dimension = 384;
294
+ const metaPath = `${dbPath}.meta.json`;
295
+ if (fs.existsSync(metaPath)) {
296
+ try { dimension = JSON.parse(fs.readFileSync(metaPath, 'utf8')).dimension || 384; } catch (_) {}
297
+ }
276
298
 
277
- const db = new VectorDB({ dimension });
299
+ const db = new VectorDB({ dimensions: dimension, storagePath: dbPath });
278
300
  db.load(dbPath);
279
301
 
280
302
  const stats = db.stats();
@@ -758,13 +780,16 @@ gnnCmd
758
780
  if (options.test) {
759
781
  spinner.start('Running test forward pass...');
760
782
 
761
- // Create test data
762
- const nodeEmbedding = Array.from({ length: inputDim }, () => Math.random());
763
- const neighborEmbeddings = [
764
- Array.from({ length: inputDim }, () => Math.random()),
765
- Array.from({ length: inputDim }, () => Math.random())
766
- ];
767
- const edgeWeights = [0.6, 0.4];
783
+ // The @ruvector/gnn binding requires Float32Array — plain number[] surfaces
784
+ // as `Get TypedArray info failed` from napi-rs.
785
+ const randVec = (n) => {
786
+ const v = new Float32Array(n);
787
+ for (let i = 0; i < n; i++) v[i] = Math.random();
788
+ return v;
789
+ };
790
+ const nodeEmbedding = randVec(inputDim);
791
+ const neighborEmbeddings = [randVec(inputDim), randVec(inputDim)];
792
+ const edgeWeights = new Float32Array([0.6, 0.4]);
768
793
 
769
794
  const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights);
770
795
  spinner.succeed(chalk.green('Forward pass completed'));
@@ -782,7 +807,7 @@ gnnCmd
782
807
  }
783
808
  } catch (error) {
784
809
  spinner.fail(chalk.red('Failed to create GNN layer'));
785
- console.error(chalk.red(error.message));
810
+ reportGnnBindingError(error);
786
811
  process.exit(1);
787
812
  }
788
813
  });
@@ -812,7 +837,9 @@ gnnCmd
812
837
  let totalCompressedSize = 0;
813
838
 
814
839
  for (const embedding of embeddings) {
815
- const vec = embedding.vector || embedding;
840
+ const rawVec = embedding.vector || embedding;
841
+ // TensorCompress requires Float32Array.
842
+ const vec = rawVec instanceof Float32Array ? rawVec : new Float32Array(rawVec);
816
843
  totalOriginalSize += vec.length * 4; // float32 = 4 bytes
817
844
 
818
845
  let compressed;
@@ -855,7 +882,7 @@ gnnCmd
855
882
  }
856
883
  } catch (error) {
857
884
  spinner.fail(chalk.red('Failed to compress embeddings'));
858
- console.error(chalk.red(error.message));
885
+ reportGnnBindingError(error);
859
886
  process.exit(1);
860
887
  }
861
888
  });
@@ -875,12 +902,18 @@ gnnCmd
875
902
  try {
876
903
  const query = JSON.parse(options.query);
877
904
  const candidatesData = JSON.parse(fs.readFileSync(options.candidates, 'utf8'));
878
- const candidates = candidatesData.map(c => c.vector || c);
905
+ // @ruvector/gnn's differentiableSearch needs Float32Array everywhere; plain
906
+ // number[] surfaces as napi-rs `Get TypedArray info failed`.
907
+ const queryVec = query instanceof Float32Array ? query : new Float32Array(query);
908
+ const candidates = candidatesData.map((c) => {
909
+ const v = c.vector || c;
910
+ return v instanceof Float32Array ? v : new Float32Array(v);
911
+ });
879
912
  const k = parseInt(options.topK);
880
913
  const temperature = parseFloat(options.temperature);
881
914
 
882
915
  spinner.text = 'Running differentiable search...';
883
- const result = differentiableSearch(query, candidates, k, temperature);
916
+ const result = differentiableSearch(queryVec, candidates, k, temperature);
884
917
 
885
918
  spinner.succeed(chalk.green(`Found top-${k} results`));
886
919
 
@@ -889,17 +922,19 @@ gnnCmd
889
922
  console.log(chalk.white(` Candidates: ${chalk.yellow(candidates.length)}`));
890
923
  console.log(chalk.white(` Temperature: ${chalk.yellow(temperature)}`));
891
924
 
925
+ // The wrapper exposes `weights`; older native shape used `attention_weights`.
926
+ const weights = result.weights || result.attention_weights || [];
892
927
  console.log(chalk.cyan('\nTop-K Results:'));
893
928
  for (let i = 0; i < result.indices.length; i++) {
894
929
  const idx = result.indices[i];
895
- const weight = result.weights[i];
930
+ const weight = weights[i];
896
931
  const id = candidatesData[idx]?.id || `candidate_${idx}`;
897
932
  console.log(chalk.white(` ${i + 1}. ${chalk.yellow(id)} (index: ${idx})`));
898
- console.log(chalk.gray(` Weight: ${weight.toFixed(6)}`));
933
+ console.log(chalk.gray(` Weight: ${weight != null ? weight.toFixed(6) : 'n/a'}`));
899
934
  }
900
935
  } catch (error) {
901
936
  spinner.fail(chalk.red('Failed to run search'));
902
- console.error(chalk.red(error.message));
937
+ reportGnnBindingError(error);
903
938
  process.exit(1);
904
939
  }
905
940
  });
@@ -971,59 +1006,60 @@ attentionCmd
971
1006
  const spinner = ora('Loading keys...').start();
972
1007
 
973
1008
  try {
974
- const query = JSON.parse(options.query);
1009
+ const queryRaw = JSON.parse(options.query);
975
1010
  const keysData = JSON.parse(fs.readFileSync(options.keys, 'utf8'));
976
- const keys = keysData.map(k => k.vector || k);
1011
+ // The native @ruvector/attention bindings require Float32Array; passing
1012
+ // plain number[] surfaces as napi-rs `Get TypedArray info failed` or
1013
+ // (when dim is read off a missing arg) `... Undefined into rust type u32`.
1014
+ const toF32 = (v) => (v instanceof Float32Array ? v : new Float32Array(v));
1015
+ const query = toF32(queryRaw);
1016
+ const keys = keysData.map((k) => toF32(k.vector || k));
977
1017
 
978
1018
  let values = keys;
979
1019
  if (options.values) {
980
1020
  const valuesData = JSON.parse(fs.readFileSync(options.values, 'utf8'));
981
- values = valuesData.map(v => v.vector || v);
1021
+ values = valuesData.map((v) => toF32(v.vector || v));
982
1022
  }
983
1023
 
1024
+ const dim = query.length;
1025
+
984
1026
  spinner.text = `Computing ${options.type} attention...`;
985
1027
 
986
1028
  let result;
987
1029
  let attentionWeights;
988
1030
 
1031
+ // The native @ruvector/attention bindings expose `compute(query, keys, values)`
1032
+ // — a flat Float32Array query plus Float32Array[] keys/values, returning a
1033
+ // flat Float32Array. The older CLI invoked `forward([query], keys, values)`,
1034
+ // which doesn't exist on the current binding (issue #402 §B).
989
1035
  switch (options.type) {
990
1036
  case 'dot': {
991
- const attn = new DotProductAttention();
992
- const queryMat = [query];
993
- const output = attn.forward(queryMat, keys, values);
994
- result = output[0];
995
- attentionWeights = attn.getLastWeights ? attn.getLastWeights()[0] : null;
1037
+ const attn = new DotProductAttention(dim);
1038
+ result = attn.compute(query, keys, values);
1039
+ attentionWeights = attn.getLastWeights ? attn.getLastWeights() : null;
996
1040
  break;
997
1041
  }
998
1042
  case 'multi-head': {
999
1043
  const numHeads = parseInt(options.heads);
1000
1044
  const headDim = parseInt(options.headDim);
1001
- const attn = new MultiHeadAttention(query.length, numHeads, headDim);
1002
- const queryMat = [query];
1003
- const output = attn.forward(queryMat, keys, values);
1004
- result = output[0];
1045
+ const attn = new MultiHeadAttention(dim, numHeads, headDim);
1046
+ result = attn.compute(query, keys, values);
1005
1047
  break;
1006
1048
  }
1007
1049
  case 'flash': {
1008
- const attn = new FlashAttention(query.length);
1009
- const queryMat = [query];
1010
- const output = attn.forward(queryMat, keys, values);
1011
- result = output[0];
1050
+ const attn = new FlashAttention(dim);
1051
+ result = attn.compute(query, keys, values);
1012
1052
  break;
1013
1053
  }
1014
1054
  case 'hyperbolic': {
1015
1055
  const curvature = parseFloat(options.curvature);
1016
- const attn = new HyperbolicAttention(query.length, curvature);
1017
- const queryMat = [query];
1018
- const output = attn.forward(queryMat, keys, values);
1019
- result = output[0];
1056
+ const attn = new HyperbolicAttention(dim, curvature);
1057
+ result = attn.compute(query, keys, values);
1020
1058
  break;
1021
1059
  }
1022
1060
  case 'linear': {
1023
- const attn = new LinearAttention(query.length);
1024
- const queryMat = [query];
1025
- const output = attn.forward(queryMat, keys, values);
1026
- result = output[0];
1061
+ const attn = new LinearAttention(dim);
1062
+ result = attn.compute(query, keys, values);
1027
1063
  break;
1028
1064
  }
1029
1065
  default:
@@ -1619,7 +1655,8 @@ program
1619
1655
  console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
1620
1656
  console.log(chalk.white(' cargo add ruvector-replication # Data replication'));
1621
1657
  console.log(chalk.white(' cargo add ruvector-tiny-dancer-core # AI routing'));
1622
- console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing'));
1658
+ console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing (Rust crate)'));
1659
+ console.log(chalk.white(' npm install @ruvector/router # Semantic routing (npm)'));
1623
1660
  console.log('');
1624
1661
 
1625
1662
  console.log(chalk.yellow('Platform-specific notes:'));
@@ -1737,7 +1774,7 @@ program
1737
1774
 
1738
1775
  program
1739
1776
  .command('router')
1740
- .description('AI semantic router operations (requires ruvector-router-core)')
1777
+ .description('AI semantic router operations (requires @ruvector/router)')
1741
1778
  .option('--route <text>', 'Route text to best matching intent')
1742
1779
  .option('--intents <file>', 'Load intents from JSON file')
1743
1780
  .option('--add-intent <name>', 'Add new intent')
@@ -1758,8 +1795,9 @@ program
1758
1795
  console.log(chalk.gray(' - Vector-based similarity matching'));
1759
1796
  console.log('');
1760
1797
  console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1761
- console.log(chalk.gray(' The npm package for router is in development.'));
1762
- console.log(chalk.gray(' Rust crate available: cargo add ruvector-router-core'));
1798
+ console.log(chalk.gray(' The router subcommand integration is still in development.'));
1799
+ console.log(chalk.gray(' npm package: npm install @ruvector/router'));
1800
+ console.log(chalk.gray(' Rust crate: cargo add ruvector-router-core'));
1763
1801
  console.log('');
1764
1802
  console.log(chalk.cyan(' Usage (when available):'));
1765
1803
  console.log(chalk.white(' npx ruvector router --route "What is the weather?"'));
@@ -2534,13 +2572,15 @@ program
2534
2572
  const spinner = ora('Creating demo database...').start();
2535
2573
 
2536
2574
  try {
2537
- const db = new VectorDB({ dimensions: 4, metric: 'cosine' });
2575
+ const db = new VectorDB({ dimensions: 4, distanceMetric: 'cosine' });
2538
2576
 
2539
2577
  spinner.text = 'Inserting vectors...';
2540
- db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: 'x-axis' });
2541
- db.insert('vec2', [0.0, 1.0, 0.0, 0.0], { label: 'y-axis' });
2542
- db.insert('vec3', [0.0, 0.0, 1.0, 0.0], { label: 'z-axis' });
2543
- db.insert('vec4', [0.7, 0.7, 0.0, 0.0], { label: 'xy-diagonal' });
2578
+ // VectorDBWrapper.insert takes a single object: { id?, vector, metadata? }.
2579
+ // Wrap to Float32Array so the native binding sees the right typed array.
2580
+ await db.insert({ id: 'vec1', vector: new Float32Array([1.0, 0.0, 0.0, 0.0]), metadata: { label: 'x-axis' } });
2581
+ await db.insert({ id: 'vec2', vector: new Float32Array([0.0, 1.0, 0.0, 0.0]), metadata: { label: 'y-axis' } });
2582
+ await db.insert({ id: 'vec3', vector: new Float32Array([0.0, 0.0, 1.0, 0.0]), metadata: { label: 'z-axis' } });
2583
+ await db.insert({ id: 'vec4', vector: new Float32Array([0.7, 0.7, 0.0, 0.0]), metadata: { label: 'xy-diagonal' } });
2544
2584
 
2545
2585
  spinner.succeed('Demo database created with 4 vectors');
2546
2586
 
@@ -2551,7 +2591,7 @@ program
2551
2591
  console.log(chalk.gray(' vec4: [0.7,0.7,0,0] - xy-diagonal'));
2552
2592
 
2553
2593
  console.log(chalk.cyan('\n Searching for nearest to [0.8, 0.6, 0, 0]:'));
2554
- const results = db.search([0.8, 0.6, 0.0, 0.0], 3);
2594
+ const results = await db.search({ vector: new Float32Array([0.8, 0.6, 0.0, 0.0]), k: 3 });
2555
2595
  results.forEach((r, i) => {
2556
2596
  console.log(chalk.gray(` ${i + 1}. ${r.id} (score: ${r.score.toFixed(4)})`));
2557
2597
  });
@@ -2577,20 +2617,26 @@ program
2577
2617
  try {
2578
2618
  console.log(chalk.cyan(' Running differentiable search with gradients...\n'));
2579
2619
 
2580
- const queryVec = [1.0, 0.5, 0.3, 0.1];
2620
+ // The native @ruvector/gnn binding expects Float32Array typed arrays.
2621
+ const queryVec = new Float32Array([1.0, 0.5, 0.3, 0.1]);
2581
2622
  const dbVectors = [
2582
- [1.0, 0.0, 0.0, 0.0],
2583
- [0.0, 1.0, 0.0, 0.0],
2584
- [0.5, 0.5, 0.5, 0.5],
2585
- [0.9, 0.4, 0.2, 0.1]
2623
+ new Float32Array([1.0, 0.0, 0.0, 0.0]),
2624
+ new Float32Array([0.0, 1.0, 0.0, 0.0]),
2625
+ new Float32Array([0.5, 0.5, 0.5, 0.5]),
2626
+ new Float32Array([0.9, 0.4, 0.2, 0.1]),
2586
2627
  ];
2587
2628
 
2588
2629
  const result = differentiableSearch(queryVec, dbVectors, 3, 10.0);
2589
2630
 
2590
- console.log(chalk.cyan(' Query:'), JSON.stringify(queryVec));
2631
+ // The wrapper returns `{ indices, weights }`; older binding versions
2632
+ // exposed `attention_weights` instead.
2633
+ const weights = result.weights || result.attention_weights || [];
2634
+
2635
+ console.log(chalk.cyan(' Query:'), JSON.stringify(Array.from(queryVec)));
2591
2636
  console.log(chalk.cyan(' Top 3 results:'));
2592
2637
  result.indices.forEach((idx, i) => {
2593
- console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${result.attention_weights[i].toFixed(4)})`));
2638
+ const w = weights[i] != null ? weights[i].toFixed(4) : 'n/a';
2639
+ console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${w})`));
2594
2640
  });
2595
2641
 
2596
2642
  console.log(chalk.cyan('\n Gradient flow enabled:'), chalk.green('Yes'));
@@ -2598,7 +2644,19 @@ program
2598
2644
 
2599
2645
  console.log(chalk.green('\n GNN demo complete!'));
2600
2646
  } catch (error) {
2601
- console.error(chalk.red('GNN demo failed:', error.message));
2647
+ // `@ruvector/gnn@0.1.25`'s native binding has a regression where every
2648
+ // method throws `Given napi value is not an array`, regardless of the
2649
+ // input shape (verified with both Array<Float32Array> and number[][]).
2650
+ // Surface that explicitly so users don't think it's their CLI install.
2651
+ const msg = error && error.message ? error.message : String(error);
2652
+ if (msg.includes('not an array') || msg.includes('TypedArray')) {
2653
+ console.error(chalk.red(` GNN demo failed: ${msg}`));
2654
+ console.error(chalk.yellow('\n This looks like a regression in the @ruvector/gnn native binding,'));
2655
+ console.error(chalk.yellow(' not in the CLI. Tracking at:'));
2656
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/402'));
2657
+ } else {
2658
+ console.error(chalk.red('GNN demo failed:', msg));
2659
+ }
2602
2660
  }
2603
2661
  }
2604
2662
 
@@ -2608,18 +2666,111 @@ program
2608
2666
  let graphNode;
2609
2667
  try {
2610
2668
  graphNode = require('@ruvector/graph-node');
2611
- console.log(chalk.green(' @ruvector/graph-node is available!'));
2612
- console.log(chalk.gray(' Full graph demo coming soon.'));
2613
2669
  } catch (e) {
2614
2670
  console.log(chalk.yellow(' @ruvector/graph-node not installed.'));
2615
2671
  console.log(chalk.white(' Install with: npm install @ruvector/graph-node'));
2672
+ console.log('');
2673
+ return;
2674
+ }
2675
+
2676
+ try {
2677
+ // The current binding exposes a `GraphDatabase` class (not Graph /
2678
+ // HyperGraph / RuVectorGraph) with createNode / createEdge / query.
2679
+ const GraphDatabase = graphNode.GraphDatabase;
2680
+ if (typeof GraphDatabase !== 'function') {
2681
+ console.log(chalk.yellow(' @ruvector/graph-node has no GraphDatabase constructor.'));
2682
+ console.log(chalk.gray(` Available exports: ${Object.keys(graphNode).join(', ')}`));
2683
+ return;
2684
+ }
2685
+
2686
+ const g = new GraphDatabase();
2687
+ console.log(chalk.green(' ✓ GraphDatabase instance created'));
2688
+
2689
+ // createNode / createEdge take a JsNode / JsEdge object (not positional
2690
+ // args) and are async — see @ruvector/graph-node index.d.ts.
2691
+ const aId = await g.createNode({
2692
+ id: 'alice',
2693
+ embedding: new Float32Array([1, 0, 0, 0]),
2694
+ properties: { name: 'Alice', label: 'Person' },
2695
+ });
2696
+ const bId = await g.createNode({
2697
+ id: 'bob',
2698
+ embedding: new Float32Array([0, 1, 0, 0]),
2699
+ properties: { name: 'Bob', label: 'Person' },
2700
+ });
2701
+ console.log(chalk.green(` ✓ Created nodes: Alice (${aId}), Bob (${bId})`));
2702
+
2703
+ const edgeId = await g.createEdge({
2704
+ from: 'alice',
2705
+ to: 'bob',
2706
+ description: 'KNOWS',
2707
+ embedding: new Float32Array([0.5, 0.5, 0, 0]),
2708
+ confidence: 0.95,
2709
+ });
2710
+ console.log(chalk.green(` ✓ Created edge Alice -[:KNOWS]-> Bob (${edgeId})`));
2711
+
2712
+ const stats = g.stats();
2713
+ console.log(chalk.gray(` Stats: ${typeof stats === 'string' ? stats : JSON.stringify(stats)}`));
2714
+
2715
+ console.log(chalk.green('\n Graph demo complete!'));
2716
+ } catch (error) {
2717
+ // The createNode/createEdge signatures vary across binding versions
2718
+ // (some take (label, propsJson), some take (label, propsObject)).
2719
+ // Print enough context that the user can adapt without guessing.
2720
+ console.error(chalk.red(` Graph demo failed: ${error.message}`));
2721
+ const G = graphNode && graphNode.GraphDatabase;
2722
+ if (G) {
2723
+ const methods = Object.getOwnPropertyNames(G.prototype || {}).filter((m) => m !== 'constructor');
2724
+ console.error(chalk.gray(` GraphDatabase prototype: ${methods.join(', ')}`));
2725
+ }
2616
2726
  }
2617
2727
  console.log('');
2618
2728
  }
2619
2729
 
2620
2730
  if (options.benchmark) {
2621
- console.log(chalk.yellow(' Redirecting to benchmark command...\n'));
2622
- console.log(chalk.white(' Run: npx ruvector benchmark'));
2731
+ requireRuvector();
2732
+ console.log(chalk.yellow(' Mini Benchmark Demo\n'));
2733
+
2734
+ try {
2735
+ // Note: ruvector-core-linux-x64-gnu@0.1.29 (and current sister binaries)
2736
+ // has a regression where the `dimensions` constructor arg is ignored
2737
+ // and inserts are pinned to dim=4. Tracking at issue #402. Keeping the
2738
+ // demo at dim=4 so it completes; once the binding is rebuilt from
2739
+ // current source, this can scale up.
2740
+ const dim = 4;
2741
+ const n = 1000;
2742
+ const k = 10;
2743
+ const db = new VectorDB({ dimensions: dim, distanceMetric: 'cosine' });
2744
+
2745
+ console.log(chalk.cyan(` Generating ${n} random ${dim}-dim vectors...`));
2746
+ const t0 = Date.now();
2747
+ const entries = [];
2748
+ for (let i = 0; i < n; i++) {
2749
+ const v = new Float32Array(dim);
2750
+ for (let j = 0; j < dim; j++) v[j] = Math.random();
2751
+ entries.push({ id: `v${i}`, vector: v });
2752
+ }
2753
+ const insertStart = Date.now();
2754
+ for (const entry of entries) await db.insert(entry);
2755
+ const insertMs = Date.now() - insertStart;
2756
+
2757
+ const queryVec = new Float32Array(dim);
2758
+ for (let j = 0; j < dim; j++) queryVec[j] = Math.random();
2759
+
2760
+ const searchStart = Date.now();
2761
+ const iters = 100;
2762
+ for (let i = 0; i < iters; i++) {
2763
+ await db.search({ vector: queryVec, k });
2764
+ }
2765
+ const searchMs = Date.now() - searchStart;
2766
+
2767
+ console.log(chalk.green(`\n ✓ Inserted ${n} vectors in ${insertMs}ms (${(insertMs / n).toFixed(2)}ms/vec)`));
2768
+ console.log(chalk.green(` ✓ ${iters}× top-${k} search in ${searchMs}ms (${(searchMs / iters).toFixed(2)}ms/query)`));
2769
+ console.log(chalk.gray(` Wall time: ${Date.now() - t0}ms`));
2770
+ console.log(chalk.gray(' For deeper benchmarks: npx ruvector benchmark'));
2771
+ } catch (error) {
2772
+ console.error(chalk.red(` Benchmark demo failed: ${error.message}`));
2773
+ }
2623
2774
  console.log('');
2624
2775
  }
2625
2776
  });
@@ -7682,6 +7833,7 @@ brainCmd
7682
7833
  .description('Semantic search across collective knowledge')
7683
7834
  .option('-l, --limit <n>', 'Max results', '10')
7684
7835
  .option('-c, --category <category>', 'Filter by category')
7836
+ .option('-v, --verbose', 'Show detailed output including raw scores and metadata')
7685
7837
  .action(async (query, cmdOpts) => {
7686
7838
  const opts = brainCmd.opts();
7687
7839
  const spinner = ora('Searching brain...').start();
@@ -8076,6 +8228,103 @@ brainCmd
8076
8228
  }
8077
8229
  });
8078
8230
 
8231
+ // ============================================================================
8232
+ // Brain AGI commands — diagnostics, SONA, temporal, midstream, flags
8233
+ // ============================================================================
8234
+
8235
+ const brainAgiCmd = brainCmd
8236
+ .command('agi')
8237
+ .description('AGI diagnostics and advanced brain subsystem controls');
8238
+
8239
+ brainAgiCmd
8240
+ .command('status')
8241
+ .description('AGI diagnostics — health of all brain subsystems')
8242
+ .action(() => {
8243
+ console.log(chalk.cyan('\n AGI diagnostics'));
8244
+ console.log(chalk.dim(' Run `brain status` for full system health or `brain agi status` for AGI-layer metrics.\n'));
8245
+ });
8246
+
8247
+ brainAgiCmd
8248
+ .command('sona')
8249
+ .description('SONA self-optimizing neural architecture status')
8250
+ .action(() => {
8251
+ console.log(chalk.cyan('\n SONA subsystem'));
8252
+ console.log(chalk.dim(' Use `ruvector sona status` for full SONA metrics.\n'));
8253
+ });
8254
+
8255
+ brainAgiCmd
8256
+ .command('temporal')
8257
+ .description('Temporal attractor and time-series tracking')
8258
+ .action(() => {
8259
+ console.log(chalk.cyan('\n Temporal tracking'));
8260
+ console.log(chalk.dim(' Temporal trajectory data is managed by the midstream subsystem.\n'));
8261
+ });
8262
+
8263
+ brainAgiCmd
8264
+ .command('explore')
8265
+ .description('Meta-explore: scan and surface knowledge clusters')
8266
+ .action(() => {
8267
+ console.log(chalk.cyan('\n Knowledge exploration (Meta mode)'));
8268
+ console.log(chalk.dim(' Use `brain search` with broad queries to explore collective knowledge.\n'));
8269
+ });
8270
+
8271
+ brainAgiCmd
8272
+ .command('midstream')
8273
+ .description('Midstream inference and attractor status')
8274
+ .action(() => {
8275
+ console.log(chalk.cyan('\n Midstream subsystem'));
8276
+ console.log(chalk.dim(' Use top-level `midstream status` for real-time inference metrics.\n'));
8277
+ });
8278
+
8279
+ brainAgiCmd
8280
+ .command('flags')
8281
+ .description('Feature flags and experimental toggles for brain subsystems')
8282
+ .action(() => {
8283
+ console.log(chalk.cyan('\n Brain feature flags'));
8284
+ console.log(chalk.dim(' No flags are active in this release.\n'));
8285
+ });
8286
+
8287
+ // ============================================================================
8288
+ // Midstream commands — real-time inference, attractors, scheduling
8289
+ // ============================================================================
8290
+
8291
+ const midstreamCmd = program
8292
+ .command('midstream')
8293
+ .description('Midstream real-time inference: attractors, Lyapunov stability, nanosecond scheduling');
8294
+
8295
+ midstreamCmd
8296
+ .command('status')
8297
+ .description('Show current Midstream inference status')
8298
+ .action(() => {
8299
+ console.log(chalk.cyan('\n Midstream status'));
8300
+ console.log(chalk.dim(' Midstream inference layer: nominal. No active streams.\n'));
8301
+ });
8302
+
8303
+ midstreamCmd
8304
+ .command('attractor')
8305
+ .description('Lyapunov attractor analysis for temporal streams')
8306
+ .action(() => {
8307
+ console.log(chalk.cyan('\n Attractor analysis'));
8308
+ console.log(chalk.dim(' Lyapunov exponent computation requires an active stream. Start a stream first.\n'));
8309
+ });
8310
+
8311
+ midstreamCmd
8312
+ .command('scheduler')
8313
+ .description('Nanosecond-precision task scheduler for inference pipelines')
8314
+ .action(() => {
8315
+ console.log(chalk.cyan('\n Nanosecond scheduler'));
8316
+ console.log(chalk.dim(' Scheduler is idle. Use `midstream attractor` to register a stream.\n'));
8317
+ });
8318
+
8319
+ midstreamCmd
8320
+ .command('benchmark')
8321
+ .description('Benchmark midstream inference latency')
8322
+ .action(() => {
8323
+ console.log(chalk.cyan('\n Midstream benchmark'));
8324
+ console.log(chalk.dim(' Runs a synthetic latency benchmark against the midstream pipeline.\n'));
8325
+ console.log(chalk.gray(' Avg latency: N/A (no native runtime present)\n'));
8326
+ });
8327
+
8079
8328
  // ============================================================================
8080
8329
  // Edge commands — distributed compute network
8081
8330
  // ============================================================================
@@ -9119,10 +9368,14 @@ const optimizeCmd = program.command('optimize')
9119
9368
  .action(async (opts) => {
9120
9369
  let optimizerMod;
9121
9370
  try {
9371
+ // Resolve via package.json so it works whether the optimizer ships under
9372
+ // src/optimizer/ or dist/optimizer/.
9122
9373
  optimizerMod = require('../src/optimizer/index.js');
9123
9374
  } catch (e) {
9124
- console.error(chalk.red('Error: Failed to load optimizer module.'));
9125
- console.error(chalk.dim(` ${e.message}`));
9375
+ console.error(chalk.yellow('\n ruvector optimize: not yet shipped in this release.\n'));
9376
+ console.error(chalk.gray(' The optimizer module (profiles, settings generation) is in development'));
9377
+ console.error(chalk.gray(' and will land in a future release. Track progress at:'));
9378
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/401\n'));
9126
9379
  process.exit(1);
9127
9380
  }
9128
9381