@claude-flow/cli 3.10.46 → 3.11.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 (551) hide show
  1. package/.claude/agents/analysis/analyze-code-quality.md +178 -178
  2. package/.claude/agents/analysis/code-analyzer.md +209 -209
  3. package/.claude/agents/analysis/code-review/analyze-code-quality.md +178 -178
  4. package/.claude/agents/architecture/arch-system-design.md +156 -156
  5. package/.claude/agents/architecture/system-design/arch-system-design.md +154 -154
  6. package/.claude/agents/browser/browser-agent.yaml +182 -182
  7. package/.claude/agents/consensus/byzantine-coordinator.md +62 -62
  8. package/.claude/agents/consensus/crdt-synchronizer.md +996 -996
  9. package/.claude/agents/consensus/gossip-coordinator.md +62 -62
  10. package/.claude/agents/consensus/performance-benchmarker.md +850 -850
  11. package/.claude/agents/consensus/quorum-manager.md +822 -822
  12. package/.claude/agents/consensus/raft-manager.md +62 -62
  13. package/.claude/agents/consensus/security-manager.md +621 -621
  14. package/.claude/agents/core/planner.md +374 -374
  15. package/.claude/agents/custom/test-long-runner.md +44 -44
  16. package/.claude/agents/data/data-ml-model.md +444 -444
  17. package/.claude/agents/data/ml/data-ml-model.md +192 -192
  18. package/.claude/agents/development/backend/dev-backend-api.md +141 -141
  19. package/.claude/agents/development/dev-backend-api.md +344 -344
  20. package/.claude/agents/devops/ci-cd/ops-cicd-github.md +163 -163
  21. package/.claude/agents/devops/ops-cicd-github.md +164 -164
  22. package/.claude/agents/documentation/api-docs/docs-api-openapi.md +173 -173
  23. package/.claude/agents/documentation/docs-api-openapi.md +354 -354
  24. package/.claude/agents/flow-nexus/app-store.md +87 -87
  25. package/.claude/agents/flow-nexus/authentication.md +68 -68
  26. package/.claude/agents/flow-nexus/challenges.md +80 -80
  27. package/.claude/agents/flow-nexus/neural-network.md +87 -87
  28. package/.claude/agents/flow-nexus/payments.md +82 -82
  29. package/.claude/agents/flow-nexus/sandbox.md +75 -75
  30. package/.claude/agents/flow-nexus/swarm.md +75 -75
  31. package/.claude/agents/flow-nexus/user-tools.md +95 -95
  32. package/.claude/agents/flow-nexus/workflow.md +83 -83
  33. package/.claude/agents/github/code-review-swarm.md +377 -377
  34. package/.claude/agents/github/github-modes.md +172 -172
  35. package/.claude/agents/github/issue-tracker.md +575 -575
  36. package/.claude/agents/github/multi-repo-swarm.md +552 -552
  37. package/.claude/agents/github/pr-manager.md +437 -437
  38. package/.claude/agents/github/project-board-sync.md +508 -508
  39. package/.claude/agents/github/release-manager.md +604 -604
  40. package/.claude/agents/github/release-swarm.md +582 -582
  41. package/.claude/agents/github/repo-architect.md +397 -397
  42. package/.claude/agents/github/swarm-issue.md +572 -572
  43. package/.claude/agents/github/swarm-pr.md +427 -427
  44. package/.claude/agents/github/sync-coordinator.md +451 -451
  45. package/.claude/agents/github/workflow-automation.md +902 -902
  46. package/.claude/agents/goal/agent.md +815 -815
  47. package/.claude/agents/optimization/benchmark-suite.md +664 -664
  48. package/.claude/agents/optimization/load-balancer.md +430 -430
  49. package/.claude/agents/optimization/performance-monitor.md +671 -671
  50. package/.claude/agents/optimization/resource-allocator.md +673 -673
  51. package/.claude/agents/optimization/topology-optimizer.md +807 -807
  52. package/.claude/agents/payments/agentic-payments.md +126 -126
  53. package/.claude/agents/sona/sona-learning-optimizer.md +74 -74
  54. package/.claude/agents/sparc/architecture.md +698 -698
  55. package/.claude/agents/sparc/pseudocode.md +519 -519
  56. package/.claude/agents/sparc/refinement.md +801 -801
  57. package/.claude/agents/sparc/specification.md +477 -477
  58. package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +224 -224
  59. package/.claude/agents/specialized/spec-mobile-react-native.md +226 -226
  60. package/.claude/agents/sublinear/consensus-coordinator.md +337 -337
  61. package/.claude/agents/sublinear/matrix-optimizer.md +184 -184
  62. package/.claude/agents/sublinear/pagerank-analyzer.md +298 -298
  63. package/.claude/agents/sublinear/performance-optimizer.md +367 -367
  64. package/.claude/agents/sublinear/trading-predictor.md +245 -245
  65. package/.claude/agents/swarm/adaptive-coordinator.md +1126 -1126
  66. package/.claude/agents/swarm/hierarchical-coordinator.md +709 -709
  67. package/.claude/agents/swarm/mesh-coordinator.md +962 -962
  68. package/.claude/agents/templates/automation-smart-agent.md +204 -204
  69. package/.claude/agents/templates/base-template-generator.md +289 -289
  70. package/.claude/agents/templates/coordinator-swarm-init.md +89 -89
  71. package/.claude/agents/templates/github-pr-manager.md +176 -176
  72. package/.claude/agents/templates/implementer-sparc-coder.md +258 -258
  73. package/.claude/agents/templates/memory-coordinator.md +186 -186
  74. package/.claude/agents/templates/orchestrator-task.md +138 -138
  75. package/.claude/agents/templates/performance-analyzer.md +198 -198
  76. package/.claude/agents/templates/sparc-coordinator.md +513 -513
  77. package/.claude/agents/testing/production-validator.md +394 -394
  78. package/.claude/agents/testing/tdd-london-swarm.md +243 -243
  79. package/.claude/agents/v3/aidefence-guardian.md +282 -282
  80. package/.claude/agents/v3/claims-authorizer.md +208 -208
  81. package/.claude/agents/v3/collective-intelligence-coordinator.md +993 -993
  82. package/.claude/agents/v3/ddd-domain-expert.md +220 -220
  83. package/.claude/agents/v3/injection-analyst.md +236 -236
  84. package/.claude/agents/v3/performance-engineer.md +1233 -1233
  85. package/.claude/agents/v3/pii-detector.md +151 -151
  86. package/.claude/agents/v3/reasoningbank-learner.md +213 -213
  87. package/.claude/agents/v3/security-architect-aidefence.md +410 -410
  88. package/.claude/agents/v3/security-architect.md +867 -867
  89. package/.claude/agents/v3/swarm-memory-manager.md +157 -157
  90. package/.claude/agents/v3/v3-integration-architect.md +205 -205
  91. package/.claude/commands/agents/README.md +50 -50
  92. package/.claude/commands/agents/agent-capabilities.md +140 -140
  93. package/.claude/commands/agents/agent-coordination.md +28 -28
  94. package/.claude/commands/agents/agent-spawning.md +28 -28
  95. package/.claude/commands/agents/agent-types.md +216 -216
  96. package/.claude/commands/agents/health.md +139 -139
  97. package/.claude/commands/agents/list.md +100 -100
  98. package/.claude/commands/agents/logs.md +130 -130
  99. package/.claude/commands/agents/metrics.md +122 -122
  100. package/.claude/commands/agents/pool.md +127 -127
  101. package/.claude/commands/agents/spawn.md +140 -140
  102. package/.claude/commands/agents/status.md +115 -115
  103. package/.claude/commands/agents/stop.md +102 -102
  104. package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
  105. package/.claude/commands/analysis/README.md +9 -9
  106. package/.claude/commands/analysis/bottleneck-detect.md +162 -162
  107. package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
  108. package/.claude/commands/analysis/performance-report.md +25 -25
  109. package/.claude/commands/analysis/token-efficiency.md +44 -44
  110. package/.claude/commands/analysis/token-usage.md +25 -25
  111. package/.claude/commands/automation/README.md +9 -9
  112. package/.claude/commands/automation/auto-agent.md +122 -122
  113. package/.claude/commands/automation/self-healing.md +105 -105
  114. package/.claude/commands/automation/session-memory.md +89 -89
  115. package/.claude/commands/automation/smart-agents.md +72 -72
  116. package/.claude/commands/automation/smart-spawn.md +25 -25
  117. package/.claude/commands/automation/workflow-select.md +25 -25
  118. package/.claude/commands/claude-flow-help.md +103 -103
  119. package/.claude/commands/claude-flow-memory.md +107 -107
  120. package/.claude/commands/claude-flow-swarm.md +205 -205
  121. package/.claude/commands/coordination/README.md +9 -9
  122. package/.claude/commands/coordination/agent-spawn.md +25 -25
  123. package/.claude/commands/coordination/init.md +44 -44
  124. package/.claude/commands/coordination/orchestrate.md +43 -43
  125. package/.claude/commands/coordination/spawn.md +45 -45
  126. package/.claude/commands/coordination/swarm-init.md +85 -85
  127. package/.claude/commands/coordination/task-orchestrate.md +25 -25
  128. package/.claude/commands/github/README.md +11 -11
  129. package/.claude/commands/github/code-review-swarm.md +513 -513
  130. package/.claude/commands/github/code-review.md +25 -25
  131. package/.claude/commands/github/github-modes.md +146 -146
  132. package/.claude/commands/github/github-swarm.md +121 -121
  133. package/.claude/commands/github/issue-tracker.md +291 -291
  134. package/.claude/commands/github/issue-triage.md +25 -25
  135. package/.claude/commands/github/multi-repo-swarm.md +518 -518
  136. package/.claude/commands/github/pr-enhance.md +26 -26
  137. package/.claude/commands/github/pr-manager.md +169 -169
  138. package/.claude/commands/github/project-board-sync.md +470 -470
  139. package/.claude/commands/github/release-manager.md +339 -339
  140. package/.claude/commands/github/release-swarm.md +543 -543
  141. package/.claude/commands/github/repo-analyze.md +25 -25
  142. package/.claude/commands/github/repo-architect.md +366 -366
  143. package/.claude/commands/github/swarm-issue.md +484 -484
  144. package/.claude/commands/github/swarm-pr.md +287 -287
  145. package/.claude/commands/github/sync-coordinator.md +302 -302
  146. package/.claude/commands/github/workflow-automation.md +441 -441
  147. package/.claude/commands/hive-mind/README.md +17 -17
  148. package/.claude/commands/hive-mind/hive-mind-consensus.md +8 -8
  149. package/.claude/commands/hive-mind/hive-mind-init.md +18 -18
  150. package/.claude/commands/hive-mind/hive-mind-memory.md +8 -8
  151. package/.claude/commands/hive-mind/hive-mind-metrics.md +8 -8
  152. package/.claude/commands/hive-mind/hive-mind-resume.md +8 -8
  153. package/.claude/commands/hive-mind/hive-mind-sessions.md +8 -8
  154. package/.claude/commands/hive-mind/hive-mind-spawn.md +21 -21
  155. package/.claude/commands/hive-mind/hive-mind-status.md +8 -8
  156. package/.claude/commands/hive-mind/hive-mind-stop.md +8 -8
  157. package/.claude/commands/hive-mind/hive-mind-wizard.md +8 -8
  158. package/.claude/commands/hive-mind/hive-mind.md +27 -27
  159. package/.claude/commands/hooks/README.md +11 -11
  160. package/.claude/commands/hooks/overview.md +57 -57
  161. package/.claude/commands/hooks/post-edit.md +117 -117
  162. package/.claude/commands/hooks/post-task.md +112 -112
  163. package/.claude/commands/hooks/pre-edit.md +113 -113
  164. package/.claude/commands/hooks/pre-task.md +111 -111
  165. package/.claude/commands/hooks/session-end.md +118 -118
  166. package/.claude/commands/hooks/setup.md +102 -102
  167. package/.claude/commands/memory/README.md +9 -9
  168. package/.claude/commands/memory/memory-persist.md +25 -25
  169. package/.claude/commands/memory/memory-search.md +25 -25
  170. package/.claude/commands/memory/memory-usage.md +25 -25
  171. package/.claude/commands/memory/neural.md +47 -47
  172. package/.claude/commands/monitoring/README.md +9 -9
  173. package/.claude/commands/monitoring/agent-metrics.md +25 -25
  174. package/.claude/commands/monitoring/agents.md +44 -44
  175. package/.claude/commands/monitoring/real-time-view.md +25 -25
  176. package/.claude/commands/monitoring/status.md +46 -46
  177. package/.claude/commands/monitoring/swarm-monitor.md +25 -25
  178. package/.claude/commands/optimization/README.md +9 -9
  179. package/.claude/commands/optimization/auto-topology.md +61 -61
  180. package/.claude/commands/optimization/cache-manage.md +25 -25
  181. package/.claude/commands/optimization/parallel-execute.md +25 -25
  182. package/.claude/commands/optimization/parallel-execution.md +49 -49
  183. package/.claude/commands/optimization/topology-optimize.md +25 -25
  184. package/.claude/commands/pair/README.md +260 -260
  185. package/.claude/commands/pair/commands.md +545 -545
  186. package/.claude/commands/pair/config.md +509 -509
  187. package/.claude/commands/pair/examples.md +511 -511
  188. package/.claude/commands/pair/modes.md +347 -347
  189. package/.claude/commands/pair/session.md +406 -406
  190. package/.claude/commands/pair/start.md +208 -208
  191. package/.claude/commands/sparc/analyzer.md +51 -51
  192. package/.claude/commands/sparc/architect.md +53 -53
  193. package/.claude/commands/sparc/ask.md +97 -97
  194. package/.claude/commands/sparc/batch-executor.md +54 -54
  195. package/.claude/commands/sparc/code.md +89 -89
  196. package/.claude/commands/sparc/coder.md +54 -54
  197. package/.claude/commands/sparc/debug.md +83 -83
  198. package/.claude/commands/sparc/debugger.md +54 -54
  199. package/.claude/commands/sparc/designer.md +53 -53
  200. package/.claude/commands/sparc/devops.md +109 -109
  201. package/.claude/commands/sparc/docs-writer.md +80 -80
  202. package/.claude/commands/sparc/documenter.md +54 -54
  203. package/.claude/commands/sparc/innovator.md +54 -54
  204. package/.claude/commands/sparc/integration.md +83 -83
  205. package/.claude/commands/sparc/mcp.md +117 -117
  206. package/.claude/commands/sparc/memory-manager.md +54 -54
  207. package/.claude/commands/sparc/optimizer.md +54 -54
  208. package/.claude/commands/sparc/orchestrator.md +131 -131
  209. package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
  210. package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
  211. package/.claude/commands/sparc/researcher.md +54 -54
  212. package/.claude/commands/sparc/reviewer.md +54 -54
  213. package/.claude/commands/sparc/security-review.md +80 -80
  214. package/.claude/commands/sparc/sparc-modes.md +174 -174
  215. package/.claude/commands/sparc/sparc.md +111 -111
  216. package/.claude/commands/sparc/spec-pseudocode.md +80 -80
  217. package/.claude/commands/sparc/supabase-admin.md +348 -348
  218. package/.claude/commands/sparc/swarm-coordinator.md +54 -54
  219. package/.claude/commands/sparc/tdd.md +54 -54
  220. package/.claude/commands/sparc/tester.md +54 -54
  221. package/.claude/commands/sparc/tutorial.md +79 -79
  222. package/.claude/commands/sparc/workflow-manager.md +54 -54
  223. package/.claude/commands/sparc.md +166 -166
  224. package/.claude/commands/stream-chain/pipeline.md +120 -120
  225. package/.claude/commands/stream-chain/run.md +69 -69
  226. package/.claude/commands/swarm/README.md +15 -15
  227. package/.claude/commands/swarm/analysis.md +95 -95
  228. package/.claude/commands/swarm/development.md +96 -96
  229. package/.claude/commands/swarm/examples.md +168 -168
  230. package/.claude/commands/swarm/maintenance.md +102 -102
  231. package/.claude/commands/swarm/optimization.md +117 -117
  232. package/.claude/commands/swarm/research.md +136 -136
  233. package/.claude/commands/swarm/swarm-analysis.md +8 -8
  234. package/.claude/commands/swarm/swarm-background.md +8 -8
  235. package/.claude/commands/swarm/swarm-init.md +19 -19
  236. package/.claude/commands/swarm/swarm-modes.md +8 -8
  237. package/.claude/commands/swarm/swarm-monitor.md +8 -8
  238. package/.claude/commands/swarm/swarm-spawn.md +19 -19
  239. package/.claude/commands/swarm/swarm-status.md +8 -8
  240. package/.claude/commands/swarm/swarm-strategies.md +8 -8
  241. package/.claude/commands/swarm/swarm.md +87 -87
  242. package/.claude/commands/swarm/testing.md +131 -131
  243. package/.claude/commands/training/README.md +9 -9
  244. package/.claude/commands/training/model-update.md +25 -25
  245. package/.claude/commands/training/neural-patterns.md +107 -107
  246. package/.claude/commands/training/neural-train.md +75 -75
  247. package/.claude/commands/training/pattern-learn.md +25 -25
  248. package/.claude/commands/training/specialization.md +62 -62
  249. package/.claude/commands/truth/start.md +142 -142
  250. package/.claude/commands/verify/check.md +49 -49
  251. package/.claude/commands/verify/start.md +127 -127
  252. package/.claude/commands/workflows/README.md +9 -9
  253. package/.claude/commands/workflows/development.md +77 -77
  254. package/.claude/commands/workflows/research.md +62 -62
  255. package/.claude/commands/workflows/workflow-create.md +25 -25
  256. package/.claude/commands/workflows/workflow-execute.md +25 -25
  257. package/.claude/commands/workflows/workflow-export.md +25 -25
  258. package/.claude/helpers/README.md +96 -96
  259. package/.claude/helpers/adr-compliance.sh +186 -186
  260. package/.claude/helpers/auto-commit.sh +178 -178
  261. package/.claude/helpers/auto-memory-hook.mjs +368 -368
  262. package/.claude/helpers/checkpoint-manager.sh +251 -251
  263. package/.claude/helpers/daemon-manager.sh +252 -252
  264. package/.claude/helpers/ddd-tracker.sh +144 -144
  265. package/.claude/helpers/github-safe.js +156 -156
  266. package/.claude/helpers/github-setup.sh +45 -45
  267. package/.claude/helpers/guidance-hook.sh +13 -13
  268. package/.claude/helpers/guidance-hooks.sh +102 -102
  269. package/.claude/helpers/health-monitor.sh +108 -108
  270. package/.claude/helpers/hook-handler.cjs +286 -286
  271. package/.claude/helpers/intelligence.cjs +1031 -1031
  272. package/.claude/helpers/learning-hooks.sh +329 -329
  273. package/.claude/helpers/learning-optimizer.sh +127 -127
  274. package/.claude/helpers/learning-service.mjs +1144 -1144
  275. package/.claude/helpers/memory.js +83 -83
  276. package/.claude/helpers/metrics-db.mjs +488 -488
  277. package/.claude/helpers/pattern-consolidator.sh +86 -86
  278. package/.claude/helpers/perf-worker.sh +160 -160
  279. package/.claude/helpers/post-commit +16 -16
  280. package/.claude/helpers/pre-commit +26 -26
  281. package/.claude/helpers/quick-start.sh +19 -19
  282. package/.claude/helpers/router.js +105 -105
  283. package/.claude/helpers/security-scanner.sh +127 -127
  284. package/.claude/helpers/session.js +157 -157
  285. package/.claude/helpers/setup-mcp.sh +18 -18
  286. package/.claude/helpers/standard-checkpoint-hooks.sh +189 -189
  287. package/.claude/helpers/statusline-hook.sh +21 -21
  288. package/.claude/helpers/statusline.cjs +604 -604
  289. package/.claude/helpers/statusline.js +352 -352
  290. package/.claude/helpers/swarm-comms.sh +353 -353
  291. package/.claude/helpers/swarm-hooks.sh +761 -761
  292. package/.claude/helpers/swarm-monitor.sh +210 -210
  293. package/.claude/helpers/sync-v3-metrics.sh +245 -245
  294. package/.claude/helpers/update-v3-progress.sh +165 -165
  295. package/.claude/helpers/v3-quick-status.sh +57 -57
  296. package/.claude/helpers/v3.sh +110 -110
  297. package/.claude/helpers/validate-v3-config.sh +215 -215
  298. package/.claude/helpers/worker-manager.sh +170 -170
  299. package/.claude/settings.json +182 -182
  300. package/.claude/skills/agentdb-advanced/SKILL.md +550 -550
  301. package/.claude/skills/agentdb-learning/SKILL.md +545 -545
  302. package/.claude/skills/agentdb-memory-patterns/SKILL.md +339 -339
  303. package/.claude/skills/agentdb-optimization/SKILL.md +509 -509
  304. package/.claude/skills/agentdb-vector-search/SKILL.md +339 -339
  305. package/.claude/skills/browser/SKILL.md +204 -204
  306. package/.claude/skills/dual-mode/README.md +71 -71
  307. package/.claude/skills/dual-mode/dual-collect.md +103 -103
  308. package/.claude/skills/dual-mode/dual-coordinate.md +85 -85
  309. package/.claude/skills/dual-mode/dual-spawn.md +81 -81
  310. package/.claude/skills/flow-nexus-neural/SKILL.md +727 -727
  311. package/.claude/skills/flow-nexus-platform/SKILL.md +1154 -1154
  312. package/.claude/skills/flow-nexus-swarm/SKILL.md +604 -604
  313. package/.claude/skills/github-code-review/SKILL.md +1125 -1125
  314. package/.claude/skills/github-multi-repo/SKILL.md +862 -862
  315. package/.claude/skills/github-project-management/SKILL.md +1262 -1262
  316. package/.claude/skills/github-release-management/SKILL.md +1064 -1064
  317. package/.claude/skills/github-workflow-automation/SKILL.md +1047 -1047
  318. package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
  319. package/.claude/skills/pair-programming/SKILL.md +1202 -1202
  320. package/.claude/skills/reasoningbank-agentdb/SKILL.md +446 -446
  321. package/.claude/skills/reasoningbank-intelligence/SKILL.md +201 -201
  322. package/.claude/skills/skill-builder/SKILL.md +910 -910
  323. package/.claude/skills/sparc-methodology/SKILL.md +1106 -1106
  324. package/.claude/skills/stream-chain/SKILL.md +560 -560
  325. package/.claude/skills/swarm-advanced/SKILL.md +970 -970
  326. package/.claude/skills/swarm-orchestration/SKILL.md +179 -179
  327. package/.claude/skills/v3-cli-modernization/SKILL.md +871 -871
  328. package/.claude/skills/v3-core-implementation/SKILL.md +796 -796
  329. package/.claude/skills/v3-ddd-architecture/SKILL.md +441 -441
  330. package/.claude/skills/v3-integration-deep/SKILL.md +240 -240
  331. package/.claude/skills/v3-mcp-optimization/SKILL.md +776 -776
  332. package/.claude/skills/v3-memory-unification/SKILL.md +173 -173
  333. package/.claude/skills/v3-performance-optimization/SKILL.md +389 -389
  334. package/.claude/skills/v3-security-overhaul/SKILL.md +81 -81
  335. package/.claude/skills/v3-swarm-coordination/SKILL.md +339 -339
  336. package/.claude/skills/verification-quality/SKILL.md +691 -691
  337. package/README.md +412 -412
  338. package/bin/cli.js +314 -314
  339. package/bin/mcp-server.js +224 -224
  340. package/bin/preinstall.cjs +2 -2
  341. package/dist/src/benchmarks/capability-tasks.d.ts +126 -0
  342. package/dist/src/benchmarks/capability-tasks.d.ts.map +1 -0
  343. package/dist/src/benchmarks/capability-tasks.js +147 -0
  344. package/dist/src/benchmarks/capability-tasks.js.map +1 -0
  345. package/dist/src/benchmarks/gaia-agent-contrastive.d.ts +53 -0
  346. package/dist/src/benchmarks/gaia-agent-contrastive.d.ts.map +1 -0
  347. package/dist/src/benchmarks/gaia-agent-contrastive.js +177 -0
  348. package/dist/src/benchmarks/gaia-agent-contrastive.js.map +1 -0
  349. package/dist/src/benchmarks/gaia-agent-gemini.d.ts +51 -0
  350. package/dist/src/benchmarks/gaia-agent-gemini.d.ts.map +1 -0
  351. package/dist/src/benchmarks/gaia-agent-gemini.js +352 -0
  352. package/dist/src/benchmarks/gaia-agent-gemini.js.map +1 -0
  353. package/dist/src/benchmarks/gaia-claude-p.d.ts +105 -0
  354. package/dist/src/benchmarks/gaia-claude-p.d.ts.map +1 -0
  355. package/dist/src/benchmarks/gaia-claude-p.js +269 -0
  356. package/dist/src/benchmarks/gaia-claude-p.js.map +1 -0
  357. package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts +20 -0
  358. package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts.map +1 -0
  359. package/dist/src/benchmarks/gaia-claude-p.smoke.js +168 -0
  360. package/dist/src/benchmarks/gaia-claude-p.smoke.js.map +1 -0
  361. package/dist/src/benchmarks/gaia-codeagent-runner.py +556 -0
  362. package/dist/src/benchmarks/gaia-codeagent.d.ts +129 -0
  363. package/dist/src/benchmarks/gaia-codeagent.d.ts.map +1 -0
  364. package/dist/src/benchmarks/gaia-codeagent.js +590 -0
  365. package/dist/src/benchmarks/gaia-codeagent.js.map +1 -0
  366. package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts +33 -0
  367. package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts.map +1 -0
  368. package/dist/src/benchmarks/gaia-codeagent.smoke.js +241 -0
  369. package/dist/src/benchmarks/gaia-codeagent.smoke.js.map +1 -0
  370. package/dist/src/benchmarks/gaia-critic.js +24 -24
  371. package/dist/src/benchmarks/gaia-dag-pilot.d.ts +22 -0
  372. package/dist/src/benchmarks/gaia-dag-pilot.d.ts.map +1 -0
  373. package/dist/src/benchmarks/gaia-dag-pilot.js +158 -0
  374. package/dist/src/benchmarks/gaia-dag-pilot.js.map +1 -0
  375. package/dist/src/benchmarks/gaia-dag.d.ts +110 -0
  376. package/dist/src/benchmarks/gaia-dag.d.ts.map +1 -0
  377. package/dist/src/benchmarks/gaia-dag.js +658 -0
  378. package/dist/src/benchmarks/gaia-dag.js.map +1 -0
  379. package/dist/src/benchmarks/gaia-dag.smoke.d.ts +21 -0
  380. package/dist/src/benchmarks/gaia-dag.smoke.d.ts.map +1 -0
  381. package/dist/src/benchmarks/gaia-dag.smoke.js +290 -0
  382. package/dist/src/benchmarks/gaia-dag.smoke.js.map +1 -0
  383. package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts +17 -0
  384. package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts.map +1 -0
  385. package/dist/src/benchmarks/gaia-ensemble-pilot.js +106 -0
  386. package/dist/src/benchmarks/gaia-ensemble-pilot.js.map +1 -0
  387. package/dist/src/benchmarks/gaia-ensemble.d.ts +103 -0
  388. package/dist/src/benchmarks/gaia-ensemble.d.ts.map +1 -0
  389. package/dist/src/benchmarks/gaia-ensemble.js +462 -0
  390. package/dist/src/benchmarks/gaia-ensemble.js.map +1 -0
  391. package/dist/src/benchmarks/gaia-gemini-pilot.d.ts +17 -0
  392. package/dist/src/benchmarks/gaia-gemini-pilot.d.ts.map +1 -0
  393. package/dist/src/benchmarks/gaia-gemini-pilot.js +168 -0
  394. package/dist/src/benchmarks/gaia-gemini-pilot.js.map +1 -0
  395. package/dist/src/benchmarks/gaia-mode-router.d.ts +67 -0
  396. package/dist/src/benchmarks/gaia-mode-router.d.ts.map +1 -0
  397. package/dist/src/benchmarks/gaia-mode-router.js +126 -0
  398. package/dist/src/benchmarks/gaia-mode-router.js.map +1 -0
  399. package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts +15 -0
  400. package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts.map +1 -0
  401. package/dist/src/benchmarks/gaia-mode-router.smoke.js +131 -0
  402. package/dist/src/benchmarks/gaia-mode-router.smoke.js.map +1 -0
  403. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts +24 -0
  404. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts.map +1 -0
  405. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js +276 -0
  406. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js.map +1 -0
  407. package/dist/src/benchmarks/gaia-tools/image_describe.d.ts +45 -0
  408. package/dist/src/benchmarks/gaia-tools/image_describe.d.ts.map +1 -0
  409. package/dist/src/benchmarks/gaia-tools/image_describe.js +222 -0
  410. package/dist/src/benchmarks/gaia-tools/image_describe.js.map +1 -0
  411. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts +24 -0
  412. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts.map +1 -0
  413. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js +169 -0
  414. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js.map +1 -0
  415. package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts +27 -0
  416. package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts.map +1 -0
  417. package/dist/src/benchmarks/gaia-tools/pdf_read.js +226 -0
  418. package/dist/src/benchmarks/gaia-tools/pdf_read.js.map +1 -0
  419. package/dist/src/benchmarks/gaia-tools/python_exec.d.ts +31 -0
  420. package/dist/src/benchmarks/gaia-tools/python_exec.d.ts.map +1 -0
  421. package/dist/src/benchmarks/gaia-tools/python_exec.js +94 -0
  422. package/dist/src/benchmarks/gaia-tools/python_exec.js.map +1 -0
  423. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts +13 -0
  424. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts.map +1 -0
  425. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js +98 -0
  426. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js.map +1 -0
  427. package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts +52 -0
  428. package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts.map +1 -0
  429. package/dist/src/benchmarks/gaia-tools/visit_webpage.js +304 -0
  430. package/dist/src/benchmarks/gaia-tools/visit_webpage.js.map +1 -0
  431. package/dist/src/benchmarks/gaia-tools/web_browse.d.ts +61 -0
  432. package/dist/src/benchmarks/gaia-tools/web_browse.d.ts.map +1 -0
  433. package/dist/src/benchmarks/gaia-tools/web_browse.js +217 -0
  434. package/dist/src/benchmarks/gaia-tools/web_browse.js.map +1 -0
  435. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts +26 -0
  436. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts.map +1 -0
  437. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js +152 -0
  438. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js.map +1 -0
  439. package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts +24 -0
  440. package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts.map +1 -0
  441. package/dist/src/benchmarks/gaia-tools/web_search.smoke.js +311 -0
  442. package/dist/src/benchmarks/gaia-tools/web_search.smoke.js.map +1 -0
  443. package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts +25 -0
  444. package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts.map +1 -0
  445. package/dist/src/benchmarks/gaia-visit-webpage.smoke.js +304 -0
  446. package/dist/src/benchmarks/gaia-visit-webpage.smoke.js.map +1 -0
  447. package/dist/src/commands/completions.js +409 -409
  448. package/dist/src/commands/daemon.js +44 -44
  449. package/dist/src/commands/embeddings.js +26 -26
  450. package/dist/src/commands/hive-mind.js +97 -97
  451. package/dist/src/commands/hooks.js +9 -9
  452. package/dist/src/commands/neural.d.ts.map +1 -1
  453. package/dist/src/commands/neural.js +2351 -1
  454. package/dist/src/commands/neural.js.map +1 -1
  455. package/dist/src/commands/performance-capability.d.ts +30 -0
  456. package/dist/src/commands/performance-capability.d.ts.map +1 -0
  457. package/dist/src/commands/performance-capability.js +349 -0
  458. package/dist/src/commands/performance-capability.js.map +1 -0
  459. package/dist/src/commands/ruvector/backup.js +23 -23
  460. package/dist/src/commands/ruvector/benchmark.js +31 -31
  461. package/dist/src/commands/ruvector/import.js +14 -14
  462. package/dist/src/commands/ruvector/init.js +115 -115
  463. package/dist/src/commands/ruvector/migrate.js +99 -99
  464. package/dist/src/commands/ruvector/optimize.js +51 -51
  465. package/dist/src/commands/ruvector/setup.js +624 -624
  466. package/dist/src/commands/ruvector/status.js +38 -38
  467. package/dist/src/init/claudemd-generator.js +231 -231
  468. package/dist/src/init/executor.js +453 -453
  469. package/dist/src/init/helpers-generator.js +727 -727
  470. package/dist/src/init/statusline-generator.js +717 -717
  471. package/dist/src/mcp-tools/agent-execute-core.d.ts +23 -1
  472. package/dist/src/mcp-tools/agent-execute-core.d.ts.map +1 -1
  473. package/dist/src/mcp-tools/agent-execute-core.js +136 -9
  474. package/dist/src/mcp-tools/agent-execute-core.js.map +1 -1
  475. package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
  476. package/dist/src/mcp-tools/agent-tools.js +48 -4
  477. package/dist/src/mcp-tools/agent-tools.js.map +1 -1
  478. package/dist/src/mcp-tools/agentdb-tools.js +15 -15
  479. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  480. package/dist/src/mcp-tools/hooks-tools.js +196 -0
  481. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  482. package/dist/src/memory/graph-edge-writer.js +18 -18
  483. package/dist/src/memory/memory-bridge.js +84 -84
  484. package/dist/src/memory/memory-initializer.js +393 -393
  485. package/dist/src/memory/rabitq-index.js +5 -5
  486. package/dist/src/runtime/headless.js +28 -28
  487. package/dist/src/ruvector/enhanced-model-router.d.ts +5 -0
  488. package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
  489. package/dist/src/ruvector/enhanced-model-router.js +18 -1
  490. package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
  491. package/dist/src/ruvector/model-prices.d.ts +50 -0
  492. package/dist/src/ruvector/model-prices.d.ts.map +1 -0
  493. package/dist/src/ruvector/model-prices.js +72 -0
  494. package/dist/src/ruvector/model-prices.js.map +1 -0
  495. package/dist/src/ruvector/model-router.d.ts +147 -8
  496. package/dist/src/ruvector/model-router.d.ts.map +1 -1
  497. package/dist/src/ruvector/model-router.js +418 -18
  498. package/dist/src/ruvector/model-router.js.map +1 -1
  499. package/dist/src/ruvector/neural-router.d.ts +172 -39
  500. package/dist/src/ruvector/neural-router.d.ts.map +1 -1
  501. package/dist/src/ruvector/neural-router.js +780 -91
  502. package/dist/src/ruvector/neural-router.js.map +1 -1
  503. package/dist/src/ruvector/router-calibrator.d.ts +65 -0
  504. package/dist/src/ruvector/router-calibrator.d.ts.map +1 -0
  505. package/dist/src/ruvector/router-calibrator.js +120 -0
  506. package/dist/src/ruvector/router-calibrator.js.map +1 -0
  507. package/dist/src/ruvector/router-trajectory.d.ts +137 -45
  508. package/dist/src/ruvector/router-trajectory.d.ts.map +1 -1
  509. package/dist/src/ruvector/router-trajectory.js +226 -63
  510. package/dist/src/ruvector/router-trajectory.js.map +1 -1
  511. package/dist/src/ruvector/task-embedder.d.ts +56 -0
  512. package/dist/src/ruvector/task-embedder.d.ts.map +1 -0
  513. package/dist/src/ruvector/task-embedder.js +237 -0
  514. package/dist/src/ruvector/task-embedder.js.map +1 -0
  515. package/dist/src/services/headless-worker-executor.js +84 -84
  516. package/dist/src/transfer/deploy-seraphine.js +23 -23
  517. package/dist/tsconfig.tsbuildinfo +1 -1
  518. package/package.json +136 -134
  519. package/scripts/beir-bootstrap-significance.mjs +162 -162
  520. package/scripts/bench-rvagent.mjs +312 -312
  521. package/scripts/benchmark-codemods.mjs +127 -127
  522. package/scripts/benchmark-cross-repo.mjs +206 -206
  523. package/scripts/benchmark-pretrained-retrieval.mjs +305 -305
  524. package/scripts/benchmark-self-learning.mjs +241 -241
  525. package/scripts/benchmark-trajectory-mrr.mjs +191 -191
  526. package/scripts/deploy-ipfs-node.sh +153 -153
  527. package/scripts/grid-search-retrieval.mjs +216 -216
  528. package/scripts/postinstall.cjs +153 -153
  529. package/scripts/pretrain-from-github.mjs +358 -358
  530. package/scripts/publish-registry.ts +345 -345
  531. package/scripts/publish.sh +57 -57
  532. package/scripts/run-beir-bge.mjs +324 -324
  533. package/scripts/run-beir-hybrid.mjs +261 -261
  534. package/scripts/run-beir-lucene-bm25.mjs +154 -154
  535. package/scripts/run-beir-nfcorpus.mjs +231 -231
  536. package/scripts/run-beir-rrf-ablation.mjs +221 -221
  537. package/scripts/setup-ipfs-registry.md +366 -366
  538. package/dist/src/ruvector/flash-attention.d.ts +0 -195
  539. package/dist/src/ruvector/flash-attention.d.ts.map +0 -1
  540. package/dist/src/ruvector/flash-attention.js +0 -643
  541. package/dist/src/ruvector/flash-attention.js.map +0 -1
  542. package/dist/src/ruvector/moe-router.d.ts +0 -206
  543. package/dist/src/ruvector/moe-router.d.ts.map +0 -1
  544. package/dist/src/ruvector/moe-router.js +0 -626
  545. package/dist/src/ruvector/moe-router.js.map +0 -1
  546. package/dist/src/services/event-stream.d.ts +0 -25
  547. package/dist/src/services/event-stream.js +0 -27
  548. package/dist/src/services/loop-worker-runner.d.ts +0 -16
  549. package/dist/src/services/loop-worker-runner.js +0 -34
  550. package/dist/src/services/runtime-capabilities.d.ts +0 -22
  551. package/dist/src/services/runtime-capabilities.js +0 -45
@@ -1,132 +1,821 @@
1
1
  /**
2
- * Neural routing scaffold — `@ruvector/tiny-dancer` FastGRNN seam (#2334 Phase 1)
2
+ * neural-router.ts Optional cost-optimal neural routing path (ADR-148).
3
3
  *
4
- * Wires the optional neural path that ADR-026 originally described, behind a
5
- * double gate that is OFF by default:
4
+ * Wires `@metaharness/router` (pure-TS k-NN/KRR + optional FastGRNN via
5
+ * `@ruvector/tiny-dancer`) into the model-routing path as a graceful, gated
6
+ * addition. The shipped heuristic + Thompson bandit stays as the default;
7
+ * this module only contributes a decision when:
6
8
  *
7
- * CLAUDE_FLOW_ROUTER_NEURAL=1 opt in to the neural path
8
- * CLAUDE_FLOW_ROUTER_MODEL_PATH=<x.safetensors> — trained FastGRNN artifact
9
+ * 1. `CLAUDE_FLOW_ROUTER_NEURAL=1` is set
10
+ * 2. Either a trained artifact path resolves (`CLAUDE_FLOW_ROUTER_MODEL_PATH`)
11
+ * OR the bundled seed corpus loads
12
+ * 3. The dynamic `import('@metaharness/router')` succeeds
9
13
  *
10
- * Both must be set; otherwise `tryNeuralRoute` returns `null` immediately and
11
- * the caller stays on the shipped heuristic + Thompson-bandit path. When the
12
- * gate is open but anything fails (package not installed — it is an
13
- * optionalDependency per ADR-124 — artifact missing/incompatible, runtime
14
- * error), this module degrades gracefully: it returns `null`, never throws,
15
- * and the caller reports `routedBy: 'bandit-fallback'` so the active path is
16
- * observable rather than inferred from import success (ADR-086/074).
14
+ * Otherwise `tryCostOptimalRoute(...)` returns `null` and the caller falls
15
+ * back to the bandit path with `routedBy: 'bandit-fallback'`.
17
16
  *
18
- * Candidate modeling (#2334 Q3, provisional): the 3 model tiers are encoded as
19
- * fixed candidates with deterministic placeholder embeddings (orthogonal-ish
20
- * one-hot-block vectors). This is explicitly provisional — the trained Phase 2
21
- * artifact defines what candidate embeddings mean, and this encoding is the
22
- * scaffolding default until the maintainers answer #2334's candidate-modeling
23
- * question. Until a real artifact exists the gate stays closed in practice, so
24
- * the placeholder never influences routing.
17
+ * Observability `routedBy` is returned on every result and must never be
18
+ * inferred from "did the import resolve?" (ADR-074, ADR-086). It carries
19
+ * exactly one of:
20
+ * - 'metaharness-knn' pure-TS k-NN, no training (uses raw seed examples)
21
+ * - 'metaharness-krr' pure-TS KRR with LOO-CV λ (TrainedRouter JSON)
22
+ * - 'fastgrnn' native FastGRNN via tiny-dancer (NativeRouter)
23
+ *
24
+ * Performance — module-level caches resolve the backend, seed corpus and
25
+ * router once per process. Hot path is a single `route(embedding)` call.
25
26
  *
26
27
  * @module neural-router
27
28
  */
28
- import { existsSync } from 'fs';
29
+ import { existsSync, readFileSync } from 'node:fs';
30
+ import { fileURLToPath } from 'node:url';
31
+ import { dirname, join, resolve as resolvePath } from 'node:path';
32
+ let _config = null;
33
+ let _backend = null;
34
+ let _initPromise = null;
35
+ const PRICES = {
36
+ haiku: 1, sonnet: 3, opus: 15, inherit: 3,
37
+ };
38
+ // ADR-149 iter 12 — lazy load measured per-model latency (mean ms) from the
39
+ // most-recent FULL seed-corpus measurement file. Cached per-process.
40
+ // Returns an empty map if no measurement is available.
41
+ let _latencyMapPromise = null;
42
+ function loadLatencyMap() {
43
+ if (_latencyMapPromise !== null)
44
+ return _latencyMapPromise;
45
+ _latencyMapPromise = (async () => {
46
+ const result = {};
47
+ try {
48
+ const fs = await import('node:fs');
49
+ const path = await import('node:path');
50
+ const benchDir = path.resolve(process.cwd(), 'docs', 'benchmarks', 'runs');
51
+ if (!fs.existsSync(benchDir))
52
+ return result;
53
+ const files = fs.readdirSync(benchDir)
54
+ .filter(f => f.startsWith('seed-corpus-') && f.endsWith('.json'))
55
+ .sort().reverse();
56
+ // Prefer a file with all three tiers populated (full measurement run);
57
+ // fall back to newest if none qualify.
58
+ let chosen = null;
59
+ for (const f of files) {
60
+ try {
61
+ const data = JSON.parse(fs.readFileSync(path.join(benchDir, f), 'utf8'));
62
+ const sample = data.perCandidate?.[0];
63
+ if (sample && sample.cheap_avg_score != null && sample.mid_avg_score != null && sample.strong_avg_score != null) {
64
+ chosen = data;
65
+ break;
66
+ }
67
+ if (!chosen)
68
+ chosen = data;
69
+ }
70
+ catch { /* skip */ }
71
+ }
72
+ for (const r of chosen?.perCandidate ?? []) {
73
+ if (typeof r.latency_mean_ms === 'number' && r.latency_mean_ms > 0) {
74
+ result[r.id] = r.latency_mean_ms;
75
+ }
76
+ }
77
+ }
78
+ catch { /* best-effort */ }
79
+ return result;
80
+ })();
81
+ return _latencyMapPromise;
82
+ }
83
+ /**
84
+ * ADR-149 — map a concrete OpenRouter / Anthropic model id back to the
85
+ * closest ClaudeModel tier label for back-compat. Used to populate the
86
+ * `model: ClaudeModel` field when the actual picked `modelId` is e.g.
87
+ * `openai/gpt-4.1` or `inclusionai/ling-2.6-flash`.
88
+ *
89
+ * Mapping rule: substring-based. Anthropic ids carry the tier name;
90
+ * other providers map to the tier whose role they play (cheap, mid, strong).
91
+ * The map is intentionally non-exhaustive — unknown ids default to 'sonnet'
92
+ * (the safest middle ground for an unrecognised candidate).
93
+ */
94
+ function tierLabelForModelId(modelId) {
95
+ const id = modelId.toLowerCase();
96
+ if (id.includes('haiku') || id.includes('ling-') || id.includes('flash-lite')
97
+ || id.includes('nemotron-nano') || id.includes('ministral')
98
+ || id.includes('llama-3.2-3b') || id.includes('llama-3.1-8b')) {
99
+ return 'haiku';
100
+ }
101
+ if (id.includes('opus'))
102
+ return 'opus';
103
+ // Mid-tier: sonnet, gpt-4.1, gemini-flash, llama-70b, nemotron-super, etc.
104
+ return 'sonnet';
105
+ }
29
106
  // ============================================================================
30
- // Gate & lifecycle
107
+ // Config resolution
31
108
  // ============================================================================
32
- /** True when the user has opted in AND pointed at a model artifact. */
33
- export function neuralRoutingEnabled() {
34
- return process.env.CLAUDE_FLOW_ROUTER_NEURAL === '1'
35
- && !!process.env.CLAUDE_FLOW_ROUTER_MODEL_PATH;
36
- }
37
- // Cached router instance + a sticky failure latch so a broken install/artifact
38
- // costs one failed load, not one per routing call.
39
- let routerInstance = null;
40
- let loadFailed = false;
41
- /** Reset cached state — for tests. */
42
- export function resetNeuralRouter() {
43
- routerInstance = null;
44
- loadFailed = false;
109
+ function getConfig() {
110
+ if (_config !== null)
111
+ return _config;
112
+ // Default seed-corpus path: bundled with the package. We resolve relative to
113
+ // this file's location so it works both in src (tsc dev) and in the dist.
114
+ // dist layout: dist/src/ruvector/neural-router.js assets at dist/assets/...
115
+ // src layout: src/ruvector/neural-router.ts → assets at assets/...
116
+ // We probe both candidate locations.
117
+ let assetsDir;
118
+ try {
119
+ const here = dirname(fileURLToPath(import.meta.url));
120
+ const candidates = [
121
+ resolvePath(here, '..', '..', 'assets', 'model-router'), // src/ruvector → src/assets/...
122
+ resolvePath(here, '..', '..', '..', 'assets', 'model-router'), // dist/src/ruvector → dist/assets/...
123
+ resolvePath(here, '..', '..', '..', '..', 'assets', 'model-router'), // safety net
124
+ ];
125
+ assetsDir = candidates.find(existsSync) ?? candidates[0];
126
+ }
127
+ catch {
128
+ assetsDir = resolvePath(process.cwd(), 'v3', '@claude-flow', 'cli', 'assets', 'model-router');
129
+ }
130
+ _config = {
131
+ enabled: process.env.CLAUDE_FLOW_ROUTER_NEURAL === '1',
132
+ modelPath: process.env.CLAUDE_FLOW_ROUTER_MODEL_PATH || undefined,
133
+ bundledKrrPath: join(assetsDir, 'seed-router.krr.json'),
134
+ // ADR-149 v2 — measured against the richer code-context corpus
135
+ // (gen-seed-corpus-v2.mjs). On that corpus, cheap models (Ling 2.6
136
+ // Flash) deliver 75-93% on cheap/mid tasks and ~54% on strong tasks;
137
+ // expensive models deliver 56-89% across tiers. Bar=0.50 lets cheap
138
+ // models win cheap+mid (cost-optimal) but escalates to capable models
139
+ // on strong queries where cheap predictions fall below the bar.
140
+ // Override per-installation; 0.25 = always-cheapest, 0.70 = quality-strict.
141
+ qualityBar: parseFloat(process.env.CLAUDE_FLOW_ROUTER_QUALITY_BAR ?? '0.50') || 0.50,
142
+ seedCorpusPath: process.env.CLAUDE_FLOW_ROUTER_SEED_CORPUS
143
+ ?? join(assetsDir, 'seed-rows.json'),
144
+ k: parseInt(process.env.CLAUDE_FLOW_ROUTER_KNN_K ?? '5', 10) || 5,
145
+ latencyBudgetMs: Math.max(0, parseInt(process.env.CLAUDE_FLOW_ROUTER_LATENCY_BUDGET_MS ?? '0', 10) || 0),
146
+ // ADR-149 iter 24 — DEFAULT ON. Opt out with `=0`. Iter 23 OOS
147
+ // validation: ECE 0.1604 → 0.0335 (-79%), well-calibrated.
148
+ calibrateEnabled: process.env.CLAUDE_FLOW_ROUTER_CALIBRATE !== '0',
149
+ // ADR-149 iter 29 — orthogonal selector mode. Blended $/Mtok ceiling;
150
+ // 0 disables (preserves cost-optimal-above-bar). Typical values:
151
+ // $5 → cheap+mid tier only (Ling 2.6, Gemini Flash Lite, Llama 3.3, GPT-4.1)
152
+ // $20 → exclude Sonnet ($48 blended) and Opus ($240)
153
+ // $50 → exclude Opus only
154
+ costCeilingPerMTok: Math.max(0, parseFloat(process.env.CLAUDE_FLOW_ROUTER_COST_CEILING_USD_PER_MTOK ?? '0') || 0),
155
+ // iter 44 — ensemble disagreement threshold; 0 disables.
156
+ ensembleUncertaintyThreshold: Math.max(0, parseFloat(process.env.CLAUDE_FLOW_ROUTER_ENSEMBLE_UNCERTAINTY_THRESHOLD ?? '0') || 0),
157
+ calibratorPath: process.env.CLAUDE_FLOW_ROUTER_CALIBRATOR_PATH
158
+ ?? join(assetsDir, 'seed-router.calibrator.json'),
159
+ };
160
+ return _config;
45
161
  }
46
- async function loadRouter() {
47
- if (routerInstance)
48
- return routerInstance;
49
- if (loadFailed)
162
+ function loadSeedCorpus(path) {
163
+ if (!existsSync(path))
50
164
  return null;
51
- const modelPath = process.env.CLAUDE_FLOW_ROUTER_MODEL_PATH;
52
- if (!modelPath || !existsSync(modelPath)) {
53
- loadFailed = true;
54
- return null;
55
- }
56
165
  try {
57
- // Dynamic import of an optionalDependency (ADR-124): absent on installs
58
- // where the native binding failed or was skipped — degrade, don't throw.
59
- const mod = await import('@ruvector/tiny-dancer');
60
- const RouterCtor = mod.Router;
61
- if (!RouterCtor) {
62
- loadFailed = true;
166
+ const raw = readFileSync(path, 'utf8');
167
+ const data = JSON.parse(raw);
168
+ if (!Array.isArray(data))
63
169
  return null;
170
+ // Light-touch validation: each row must have a numeric-array embedding and
171
+ // a non-empty scores map. We do not coerce types — bad data should be
172
+ // visible as a config bug, not silently routed around.
173
+ for (const row of data) {
174
+ if (!row || !Array.isArray(row.embedding) || row.embedding.length === 0)
175
+ return null;
176
+ if (!row.scores || typeof row.scores !== 'object')
177
+ return null;
64
178
  }
65
- routerInstance = new RouterCtor({ modelPath });
66
- return routerInstance;
179
+ return data;
67
180
  }
68
181
  catch {
69
- loadFailed = true;
70
182
  return null;
71
183
  }
72
184
  }
73
- // ============================================================================
74
- // Candidate encoding (provisional — see header + #2334 Q3)
75
- // ============================================================================
76
- const TIER_ORDER = ['haiku', 'sonnet', 'opus'];
77
- /**
78
- * Deterministic placeholder embedding for a tier candidate: a block one-hot
79
- * over the embedding dimensionality. Replaced by whatever the Phase 2 trained
80
- * artifact defines as candidate space.
81
- */
82
- function tierCandidateEmbedding(tierIndex, dim) {
83
- const v = new Array(dim).fill(0);
84
- const block = Math.max(1, Math.floor(dim / TIER_ORDER.length));
85
- const start = tierIndex * block;
86
- for (let i = start; i < Math.min(start + block, dim); i++)
87
- v[i] = 1 / Math.sqrt(block);
88
- return v;
185
+ async function resolveBackend(cfg) {
186
+ // 1. Optional dep present?
187
+ let mh;
188
+ try {
189
+ mh = await import('@metaharness/router');
190
+ }
191
+ catch {
192
+ return { available: false, router: null, routedBy: null, reason: '@metaharness/router not installed' };
193
+ }
194
+ // 2. If a trained-model path is set AND tiny-dancer is loadable, prefer FastGRNN.
195
+ // Load the NativeRouter ONCE here; reuse on every route() call.
196
+ if (cfg.modelPath && existsSync(cfg.modelPath)) {
197
+ const backend = await mh.resolveRouterBackend('auto');
198
+ if (backend === 'native') {
199
+ try {
200
+ const nativeRouter = await mh.NativeRouter.load({ modelPath: cfg.modelPath });
201
+ // Build per-candidate embeddings once. NativeRouter requires non-empty
202
+ // candidate embeddings; we use a one-hot signature so the three tiers
203
+ // are distinct in the FastGRNN's feature engineering. Dim is probed
204
+ // from the seed corpus or falls back to 384 (MiniLM default).
205
+ const seed = loadSeedCorpus(cfg.seedCorpusPath);
206
+ const dim = seed?.[0]?.embedding.length ?? 384;
207
+ const candidates = ['haiku', 'sonnet', 'opus'].map((id, i) => {
208
+ const v = new Array(dim).fill(0);
209
+ v[i % dim] = 1;
210
+ return { id, embedding: v, costPerMTok: PRICES[id] };
211
+ });
212
+ return {
213
+ available: true,
214
+ router: null, // The native path uses the `native` field below, not `router`.
215
+ native: { router: nativeRouter, candidates },
216
+ routedBy: 'fastgrnn',
217
+ reason: `native router loaded from ${cfg.modelPath}`,
218
+ };
219
+ }
220
+ catch (e) {
221
+ // Fall through to pure-TS paths
222
+ }
223
+ }
224
+ }
225
+ // 3. Trained KRR JSON artifact path?
226
+ if (cfg.modelPath && existsSync(cfg.modelPath) && cfg.modelPath.endsWith('.json')) {
227
+ try {
228
+ const json = JSON.parse(readFileSync(cfg.modelPath, 'utf8'));
229
+ const trained = mh.TrainedRouter.fromJSON(json);
230
+ // Pre-extract candidate ids+costs for predictAll
231
+ const cands = json.candidates.map((c) => ({ id: c.id, costPerMTok: c.costPerMTok }));
232
+ return {
233
+ available: true,
234
+ router: {
235
+ route: (e) => {
236
+ const r = trained.route(e);
237
+ return { id: r.id, predictedQuality: r.predictedQuality, costPerMTok: r.costPerMTok, metBar: r.metBar };
238
+ },
239
+ predictAll: (e) => cands.map((c) => ({
240
+ id: c.id, predictedQuality: trained.predict(c.id, e), costPerMTok: c.costPerMTok,
241
+ })).sort((a, b) => a.costPerMTok - b.costPerMTok),
242
+ },
243
+ routedBy: 'metaharness-krr',
244
+ reason: `KRR artifact loaded from ${cfg.modelPath}`,
245
+ };
246
+ }
247
+ catch {
248
+ // Fall through to k-NN
249
+ }
250
+ }
251
+ // 3.5. No user artifact → try the bundled pre-trained KRR (~96kB, ~0.020 ms/route).
252
+ if (existsSync(cfg.bundledKrrPath)) {
253
+ try {
254
+ let unifiedCalibrator = null;
255
+ const calibratorByBucket = {};
256
+ const loadedCalibrators = [];
257
+ if (cfg.calibrateEnabled) {
258
+ const { IsotonicCalibrator } = await import('./router-calibrator.js');
259
+ const loadCal = (path) => {
260
+ if (!existsSync(path))
261
+ return null;
262
+ try {
263
+ return IsotonicCalibrator.fromJSON(JSON.parse(readFileSync(path, 'utf8')));
264
+ }
265
+ catch {
266
+ return null;
267
+ }
268
+ };
269
+ unifiedCalibrator = loadCal(cfg.calibratorPath);
270
+ if (unifiedCalibrator)
271
+ loadedCalibrators.push('unified');
272
+ // Per-tier — paths follow the iter 16 KRR specialist convention.
273
+ const calDir = cfg.calibratorPath.replace(/seed-router\.calibrator\.json$/, '');
274
+ for (const bucket of ['low', 'med', 'high']) {
275
+ const c = loadCal(`${calDir}seed-router.calibrator.${bucket}.json`);
276
+ if (c) {
277
+ calibratorByBucket[bucket] = c;
278
+ loadedCalibrators.push(bucket);
279
+ }
280
+ }
281
+ }
282
+ const wrapWithCalibrator = (r, cal) => {
283
+ if (!cal)
284
+ return r;
285
+ return {
286
+ route: (e) => {
287
+ const result = r.route(e);
288
+ return { ...result, predictedQuality: cal.transform(result.predictedQuality) };
289
+ },
290
+ predictAll: (e) => r.predictAll(e).map(c => ({ ...c, predictedQuality: cal.transform(c.predictedQuality) })),
291
+ };
292
+ };
293
+ // Helper: load a TrainedRouter from a path and wrap it as a PureRouter.
294
+ const loadKrr = (path) => {
295
+ if (!existsSync(path))
296
+ return null;
297
+ try {
298
+ const json = JSON.parse(readFileSync(path, 'utf8'));
299
+ const trained = mh.TrainedRouter.fromJSON(json);
300
+ const cands = json.candidates.map((c) => ({ id: c.id, costPerMTok: c.costPerMTok }));
301
+ return {
302
+ route: (e) => {
303
+ const r = trained.route(e);
304
+ return { id: r.id, predictedQuality: r.predictedQuality, costPerMTok: r.costPerMTok, metBar: r.metBar };
305
+ },
306
+ predictAll: (e) => cands.map((c) => ({
307
+ id: c.id, predictedQuality: trained.predict(c.id, e), costPerMTok: c.costPerMTok,
308
+ })).sort((a, b) => a.costPerMTok - b.costPerMTok),
309
+ };
310
+ }
311
+ catch {
312
+ return null;
313
+ }
314
+ };
315
+ const unifiedRaw = loadKrr(cfg.bundledKrrPath);
316
+ if (!unifiedRaw)
317
+ throw new Error('failed to load unified KRR');
318
+ const unified = wrapWithCalibrator(unifiedRaw, unifiedCalibrator);
319
+ // ADR-149 iter 16 — load per-bucket specialists if present. Each is a
320
+ // KRR fit only to its tier's rows (cheap → low.json, mid → med.json,
321
+ // strong → high.json). When tryCostOptimalRoute is called with a
322
+ // complexityBucket, the matching specialist is preferred over the
323
+ // unified router.
324
+ const bucketDir = cfg.bundledKrrPath.replace(/seed-router\.krr\.json$/, '');
325
+ const routerByBucket = {};
326
+ const loadedBuckets = [];
327
+ for (const bucket of ['low', 'med', 'high']) {
328
+ const r = loadKrr(`${bucketDir}seed-router.krr.${bucket}.json`);
329
+ if (r) {
330
+ // iter 25 — prefer tier-specific calibrator for this bucket;
331
+ // fall back to the unified calibrator when no specialist exists.
332
+ routerByBucket[bucket] = wrapWithCalibrator(r, calibratorByBucket[bucket] ?? unifiedCalibrator);
333
+ loadedBuckets.push(bucket);
334
+ }
335
+ }
336
+ const calibratedNote = loadedCalibrators.length > 0
337
+ ? ` (calibrated: ${loadedCalibrators.join('+')})`
338
+ : '';
339
+ const reason = loadedBuckets.length > 0
340
+ ? `bundled KRR loaded from ${cfg.bundledKrrPath} + ${loadedBuckets.length} bucket specialist(s): ${loadedBuckets.join(', ')}${calibratedNote}`
341
+ : `bundled KRR artifact loaded from ${cfg.bundledKrrPath}${calibratedNote}`;
342
+ return {
343
+ available: true,
344
+ router: unified,
345
+ ...(loadedBuckets.length > 0 ? { routerByBucket } : {}),
346
+ routedBy: 'metaharness-krr',
347
+ reason,
348
+ };
349
+ }
350
+ catch {
351
+ // Fall through to k-NN
352
+ }
353
+ }
354
+ // 4. Pure-TS k-NN over the bundled seed corpus.
355
+ const seed = loadSeedCorpus(cfg.seedCorpusPath);
356
+ if (!seed || seed.length === 0) {
357
+ return { available: true, router: null, routedBy: null, reason: `seed corpus missing or invalid at ${cfg.seedCorpusPath}` };
358
+ }
359
+ const router = mh.Router.fromExamples(seed, PRICES, { qualityBar: cfg.qualityBar, k: cfg.k });
360
+ // Pre-build per-candidate views ONCE so predictAll() doesn't re-allocate
361
+ // O(seed.length) objects per call. Sort by cost so the result is already in
362
+ // cheapest-first order.
363
+ const candIds = Object.keys(PRICES).filter(id => id !== 'inherit');
364
+ const candidateViews = candIds
365
+ .map(id => ({
366
+ id,
367
+ costPerMTok: PRICES[id],
368
+ examples: seed.map(r => ({ embedding: r.embedding, quality: r.scores[id] ?? 0 })),
369
+ }))
370
+ .sort((a, b) => a.costPerMTok - b.costPerMTok);
371
+ return {
372
+ available: true,
373
+ router: {
374
+ route: (e) => {
375
+ const r = router.route(e);
376
+ return { id: r.id, predictedQuality: r.predictedQuality, costPerMTok: r.costPerMTok, metBar: r.metBar };
377
+ },
378
+ predictAll: (e) => candidateViews.map(c => ({
379
+ id: c.id, predictedQuality: router.predict(c, e), costPerMTok: c.costPerMTok,
380
+ })),
381
+ },
382
+ routedBy: 'metaharness-knn',
383
+ reason: `k-NN over ${seed.length} seed rows`,
384
+ };
385
+ }
386
+ async function getBackend() {
387
+ if (_backend !== null)
388
+ return _backend;
389
+ if (_initPromise !== null)
390
+ return _initPromise;
391
+ const cfg = getConfig();
392
+ _initPromise = resolveBackend(cfg).then(b => { _backend = b; return b; });
393
+ return _initPromise;
89
394
  }
90
395
  // ============================================================================
91
- // Routing
396
+ // Public function
92
397
  // ============================================================================
93
398
  /**
94
- * Attempt a neural routing decision for the given task embedding.
399
+ * Cost-optimal route via the optional neural backend. Returns `null` when the
400
+ * neural path is disabled (gate closed), unavailable (deps missing), or
401
+ * unconfigured (no corpus / artifact). Callers must fall back to the
402
+ * heuristic+bandit path on null and tag the result `routedBy: 'bandit-fallback'`.
95
403
  *
96
- * Returns `null` (never throws) when the gate is closed, the package or
97
- * artifact is unavailable, or inference fails callers fall back to the
98
- * bandit and report `routedBy: 'bandit-fallback'` (when the gate was open)
99
- * or `'heuristic'` (when it never was).
404
+ * @param embedding 384-dim (or matching corpus dim) query embedding
405
+ * @returns NeuralRouteResult on success, or `null` when the path is inactive
100
406
  */
101
- export async function tryNeuralRoute(embedding) {
102
- if (!neuralRoutingEnabled())
407
+ export async function tryCostOptimalRoute(embedding, opts) {
408
+ const cfg = getConfig();
409
+ if (!cfg.enabled)
103
410
  return null;
104
- if (!embedding || embedding.length === 0)
411
+ if (!Array.isArray(embedding) || embedding.length === 0)
105
412
  return null;
106
- const router = await loadRouter();
107
- if (!router)
413
+ const backend = await getBackend();
414
+ if (!backend.available || backend.routedBy === null)
108
415
  return null;
416
+ const t0 = Number(process.hrtime.bigint() / 1000n); // microseconds
109
417
  try {
110
- const response = await router.route({
111
- queryEmbedding: embedding,
112
- candidates: TIER_ORDER.map((tier, i) => ({
113
- id: tier,
114
- embedding: tierCandidateEmbedding(i, embedding.length),
115
- metadata: JSON.stringify({ tier }),
418
+ if (backend.routedBy === 'fastgrnn') {
419
+ // Native path: NativeRouter was loaded once at resolveBackend time;
420
+ // candidates were precomputed. Hot path is one .route() call.
421
+ if (!backend.native)
422
+ return null;
423
+ const res = await backend.native.router.route(embedding, backend.native.candidates);
424
+ const modelId = res.id;
425
+ const t1 = Number(process.hrtime.bigint() / 1000n);
426
+ return {
427
+ model: tierLabelForModelId(modelId),
428
+ modelId,
429
+ predictedQuality: 1 - res.uncertainty, // FastGRNN reports uncertainty, not quality directly
430
+ metBar: !res.useLightweight && res.confidence >= cfg.qualityBar,
431
+ alternatives: backend.native.candidates.map(c => ({
432
+ model: tierLabelForModelId(c.id),
433
+ modelId: c.id,
434
+ predictedQuality: c.id === modelId ? res.confidence : 0,
435
+ costPerMTok: c.costPerMTok,
436
+ })),
437
+ routedBy: 'fastgrnn',
438
+ inferenceTimeUs: t1 - t0,
439
+ };
440
+ }
441
+ // Pure-TS paths (k-NN or KRR) — ADR-149: ids are arbitrary strings,
442
+ // not ClaudeModel tier names. `tierLabelForModelId` derives the
443
+ // back-compat tier; `modelId` carries the concrete pick.
444
+ //
445
+ // ADR-149 iter 16 — when the caller supplies a complexity bucket AND a
446
+ // per-bucket specialist KRR was loaded at backend resolution, prefer
447
+ // the specialist over the unified router. Each specialist is trained
448
+ // only on its tier's rows (looQuality 0.94 for cheap vs 0.71 unified)
449
+ // so its predictions are sharper for queries in that band.
450
+ const activeRouter = (opts?.complexityBucket && backend.routerByBucket?.[opts.complexityBucket])
451
+ ?? backend.router;
452
+ if (!activeRouter)
453
+ return null;
454
+ const allRaw = activeRouter.predictAll(embedding);
455
+ // ADR-149 iter 14 — per-modelId Thompson sampling. When
456
+ // CLAUDE_FLOW_ROUTER_BANDIT_PER_MODEL=1, perturb each candidate's
457
+ // predicted quality by a Beta sample from its persisted per-modelId
458
+ // prior. Lets the bandit learn "Ling outperforms Haiku-4.5 within
459
+ // the cheap tier" without changing the neural backend's prediction.
460
+ //
461
+ // Density guard: only apply the perturbation when (α+β) > 4 for that
462
+ // modelId (≥2 outcomes accumulated; cold-start Beta(1,1) gives α+β=2).
463
+ // Otherwise the bandit's signal is uninformative noise and we'd
464
+ // sabotage the well-trained neural prediction.
465
+ let all = allRaw;
466
+ if (process.env.CLAUDE_FLOW_ROUTER_BANDIT_PER_MODEL === '1') {
467
+ try {
468
+ const { getModelRouterPriorsById, sampleBeta } = await import('./model-router.js');
469
+ const priorsById = getModelRouterPriorsById();
470
+ const bucket = opts?.complexityBucket; // ADR-149 iter 15
471
+ if (priorsById) {
472
+ // Cross-bucket shrinkage configuration (iter 57). When λ > 0,
473
+ // bucket-specific priors with few samples blend toward the
474
+ // marginal-across-buckets prior. Classic James-Stein-style
475
+ // shrinkage: rich cells trust themselves, thin cells borrow
476
+ // strength from neighbors. Default λ = 4 (set =0 to disable).
477
+ const shrinkageLambda = Math.max(0, parseFloat(process.env.CLAUDE_FLOW_ROUTER_BANDIT_SHRINKAGE_LAMBDA ?? '4') || 0);
478
+ all = allRaw.map(a => {
479
+ // Compute marginal-across-buckets prior for this modelId ONCE
480
+ // (used either as the primary prior OR as the shrinkage anchor).
481
+ let alphaMarginal = 1, betaMarginal = 1;
482
+ let marginalFound = false;
483
+ for (const b of ['low', 'med', 'high']) {
484
+ const p = priorsById[b]?.[a.id];
485
+ if (p) {
486
+ alphaMarginal += p.alpha - 1;
487
+ betaMarginal += p.beta - 1;
488
+ marginalFound = true;
489
+ }
490
+ }
491
+ let alpha, beta;
492
+ if (bucket && priorsById[bucket]?.[a.id]) {
493
+ // Bucket-specific prior exists — use it as the primary signal
494
+ // (iter 15 sharpness). Iter 57: when shrinkage is enabled,
495
+ // blend toward the marginal anchor based on specific-cell
496
+ // sample richness. w_s = (n_s + 1) / (n_s + 1 + λ). At n_s=0
497
+ // → mostly marginal; at n_s=large → mostly specific.
498
+ const p = priorsById[bucket][a.id];
499
+ const nSpecific = p.alpha + p.beta - 2;
500
+ if (shrinkageLambda > 0 && marginalFound) {
501
+ const wSpecific = (nSpecific + 1) / (nSpecific + 1 + shrinkageLambda);
502
+ alpha = wSpecific * p.alpha + (1 - wSpecific) * alphaMarginal;
503
+ beta = wSpecific * p.beta + (1 - wSpecific) * betaMarginal;
504
+ }
505
+ else {
506
+ alpha = p.alpha;
507
+ beta = p.beta;
508
+ }
509
+ }
510
+ else if (marginalFound) {
511
+ // Marginal fallback (iter 14) — no bucket-specific prior at all.
512
+ alpha = alphaMarginal;
513
+ beta = betaMarginal;
514
+ }
515
+ else {
516
+ return a;
517
+ }
518
+ const samples = alpha + beta;
519
+ if (samples <= 2)
520
+ return a; // hard floor — uniform prior has no signal
521
+ // iter 52 — continuous warmup curve. Replaces iter 14's binary
522
+ // density guard. Two curves available:
523
+ // default (iter 52): blendFactor = 0.5 * min(1, (s-2)/WARMUP)
524
+ // Capped at 0.5 → matches iter 14's
525
+ // 50/50 baseline at saturation.
526
+ // iter 53 opt-in: blendFactor = (s-2) / (s + WARMUP)
527
+ // Asymptotes to 1.0 as samples → ∞.
528
+ // At s=1000 → bandit ~99% influence.
529
+ // Use when you have lots of bandit data
530
+ // and want it to dominate the neural prior.
531
+ // Gate: CLAUDE_FLOW_ROUTER_BANDIT_FULL_INFLUENCE=1.
532
+ const warmupRange = Math.max(1, parseFloat(process.env.CLAUDE_FLOW_ROUTER_BANDIT_WARMUP_RANGE ?? '8') || 8);
533
+ const fullInfluence = process.env.CLAUDE_FLOW_ROUTER_BANDIT_FULL_INFLUENCE === '1';
534
+ const banditScore = sampleBeta(alpha, beta);
535
+ const blendFactor = fullInfluence
536
+ ? (samples - 2) / (samples + warmupRange)
537
+ : 0.5 * Math.min(1, (samples - 2) / warmupRange);
538
+ return { ...a, predictedQuality: (1 - blendFactor) * a.predictedQuality + blendFactor * banditScore };
539
+ });
540
+ }
541
+ }
542
+ catch { /* best-effort */ }
543
+ }
544
+ // ADR-149 iter 12 — latency-aware filtering. When CLAUDE_FLOW_ROUTER_
545
+ // LATENCY_BUDGET_MS is set, drop candidates whose measured latency
546
+ // exceeds the budget BEFORE the cost-optimal pick. The unfiltered
547
+ // alternatives stay on the result for observability — only the chosen
548
+ // `modelId` is constrained.
549
+ let main = activeRouter.route(embedding);
550
+ // Re-derive `main` from the post-Thompson `all` list when per-modelId
551
+ // is on (otherwise the bandit adjustment is invisible to the selector).
552
+ if (process.env.CLAUDE_FLOW_ROUTER_BANDIT_PER_MODEL === '1') {
553
+ const clearing = all.filter(a => a.predictedQuality >= cfg.qualityBar)
554
+ .sort((a, b) => a.costPerMTok - b.costPerMTok);
555
+ const pick = clearing[0] ?? [...all].sort((a, b) => b.predictedQuality - a.predictedQuality)[0];
556
+ if (pick) {
557
+ main = { id: pick.id, predictedQuality: pick.predictedQuality, costPerMTok: pick.costPerMTok, metBar: pick.predictedQuality >= cfg.qualityBar };
558
+ }
559
+ }
560
+ if (cfg.latencyBudgetMs > 0) {
561
+ const latency = await loadLatencyMap();
562
+ const eligible = all.filter(a => {
563
+ const lat = latency[a.id];
564
+ return lat === undefined || lat <= cfg.latencyBudgetMs;
565
+ });
566
+ if (eligible.length > 0) {
567
+ // Re-pick cheapest-clearing-bar among eligible
568
+ const clearing = eligible.filter(a => a.predictedQuality >= cfg.qualityBar)
569
+ .sort((a, b) => a.costPerMTok - b.costPerMTok);
570
+ const pick = clearing[0] ?? [...eligible].sort((a, b) => b.predictedQuality - a.predictedQuality)[0];
571
+ main = { id: pick.id, predictedQuality: pick.predictedQuality, costPerMTok: pick.costPerMTok, metBar: pick.predictedQuality >= cfg.qualityBar };
572
+ }
573
+ // else: every candidate exceeds the budget → fall back to the original pick
574
+ // (better to return a slow answer than no answer)
575
+ }
576
+ // ADR-149 iter 29 — quality-best-under-budget mode. When the cost
577
+ // ceiling is set, OVERRIDE the cost-optimal-above-bar pick:
578
+ // 1. filter candidates by costPerMTok ≤ ceiling
579
+ // 2. sort by predictedQuality DESC
580
+ // 3. pick the top one
581
+ // qualityBar still informs `metBar` on the result for observability,
582
+ // but does NOT filter the selection — the operator's hard constraint
583
+ // is cost, and they want the best quality they can get under it.
584
+ // If no candidate fits the ceiling, fall through to the existing pick
585
+ // (better to return something than nothing — same policy as the
586
+ // latency-budget fallback above).
587
+ if (cfg.costCeilingPerMTok > 0) {
588
+ const affordable = all.filter(a => a.costPerMTok <= cfg.costCeilingPerMTok);
589
+ if (affordable.length > 0) {
590
+ const pick = [...affordable].sort((a, b) => b.predictedQuality - a.predictedQuality)[0];
591
+ main = {
592
+ id: pick.id,
593
+ predictedQuality: pick.predictedQuality,
594
+ costPerMTok: pick.costPerMTok,
595
+ metBar: pick.predictedQuality >= cfg.qualityBar,
596
+ };
597
+ }
598
+ }
599
+ // ADR-149 iter 44+45 — ensemble disagreement diagnostic + uncertainty-
600
+ // aware fallback. When both the unified router and a bucket specialist
601
+ // are loaded AND a bucket was supplied, compute the picked model's
602
+ // prediction disagreement across both backends. ALWAYS surface it on
603
+ // the result (iter 45 — observable signal for tuning the threshold).
604
+ // If iter 44's threshold is > 0 AND the disagreement exceeds it, return
605
+ // null so the caller falls back to the pure bandit path (same as 429/5xx
606
+ // — uncertainty is a kind of soft failure).
607
+ let ensembleDisagreement;
608
+ if (opts?.complexityBucket
609
+ && backend.routerByBucket?.[opts.complexityBucket]
610
+ && backend.router
611
+ && activeRouter !== backend.router // we used the specialist; cross-check vs unified
612
+ ) {
613
+ const unifiedAlts = backend.router.predictAll(embedding);
614
+ const specialistQ = main.predictedQuality;
615
+ const unifiedQ = unifiedAlts.find(a => a.id === main.id)?.predictedQuality ?? specialistQ;
616
+ ensembleDisagreement = Math.abs(unifiedQ - specialistQ);
617
+ if (cfg.ensembleUncertaintyThreshold > 0 && ensembleDisagreement > cfg.ensembleUncertaintyThreshold) {
618
+ return null;
619
+ }
620
+ }
621
+ const t1 = Number(process.hrtime.bigint() / 1000n);
622
+ return {
623
+ model: tierLabelForModelId(main.id),
624
+ modelId: main.id,
625
+ predictedQuality: main.predictedQuality,
626
+ metBar: main.metBar,
627
+ alternatives: all.map(a => ({
628
+ model: tierLabelForModelId(a.id),
629
+ modelId: a.id,
630
+ predictedQuality: a.predictedQuality,
631
+ costPerMTok: a.costPerMTok,
116
632
  })),
633
+ routedBy: backend.routedBy,
634
+ inferenceTimeUs: t1 - t0,
635
+ ...(ensembleDisagreement !== undefined ? { ensembleDisagreement } : {}),
636
+ };
637
+ }
638
+ catch {
639
+ // Any runtime failure is silently swallowed → caller's bandit-fallback engages.
640
+ return null;
641
+ }
642
+ }
643
+ /**
644
+ * Batch counterpart to `tryCostOptimalRoute`. Routes a list of embeddings
645
+ * in one go, sharing backend resolution + (for the pure-TS paths)
646
+ * candidate-view setup across the batch. The native FastGRNN path still
647
+ * dispatches per-call (xenova's worker doesn't support array inputs for
648
+ * tiny-dancer's Router.route).
649
+ *
650
+ * Order of the output array matches the input order. Each slot is either
651
+ * a NeuralRouteResult or null (gate closed, backend unavailable, etc. —
652
+ * mirrors the single-call contract).
653
+ *
654
+ * For harness-style callers (batch evals, GAIA runs, parallel agent
655
+ * dispatch) this amortizes backend init across N queries — first-call
656
+ * cold-load (~10 ms) becomes a fixed cost regardless of batch size.
657
+ */
658
+ export async function tryCostOptimalRouteBatch(embeddings) {
659
+ const out = new Array(embeddings.length).fill(null);
660
+ const cfg = getConfig();
661
+ if (!cfg.enabled)
662
+ return out;
663
+ const backend = await getBackend();
664
+ if (!backend.available || backend.routedBy === null)
665
+ return out;
666
+ // FastGRNN path: per-call native dispatch. The native router doesn't
667
+ // expose a batch entry point. We still share the loaded NativeRouter
668
+ // instance + candidate embeddings, which is most of the per-call cost.
669
+ if (backend.routedBy === 'fastgrnn') {
670
+ if (!backend.native)
671
+ return out;
672
+ const native = backend.native;
673
+ const tasks = embeddings.map(async (e, i) => {
674
+ if (!Array.isArray(e) || e.length === 0)
675
+ return null;
676
+ try {
677
+ const t0 = Number(process.hrtime.bigint() / 1000n);
678
+ const res = await native.router.route(e, native.candidates);
679
+ const modelId = res.id;
680
+ const t1 = Number(process.hrtime.bigint() / 1000n);
681
+ const result = {
682
+ model: tierLabelForModelId(modelId),
683
+ modelId,
684
+ predictedQuality: 1 - res.uncertainty,
685
+ metBar: !res.useLightweight && res.confidence >= cfg.qualityBar,
686
+ alternatives: native.candidates.map(c => ({
687
+ model: tierLabelForModelId(c.id),
688
+ modelId: c.id,
689
+ predictedQuality: c.id === modelId ? res.confidence : 0,
690
+ costPerMTok: c.costPerMTok,
691
+ })),
692
+ routedBy: 'fastgrnn',
693
+ inferenceTimeUs: t1 - t0,
694
+ };
695
+ out[i] = result;
696
+ }
697
+ catch {
698
+ out[i] = null;
699
+ }
700
+ return null;
117
701
  });
118
- const best = response.decisions?.[0];
119
- if (!best || !TIER_ORDER.includes(best.candidateId))
702
+ await Promise.all(tasks);
703
+ return out;
704
+ }
705
+ // Pure-TS path (k-NN or KRR): same shared router + candidate views,
706
+ // tight loop. predictAll is cheap (already-pre-built per-candidate
707
+ // examples) so we avoid re-allocating per call.
708
+ if (!backend.router)
709
+ return out;
710
+ for (let i = 0; i < embeddings.length; i++) {
711
+ const e = embeddings[i];
712
+ if (!Array.isArray(e) || e.length === 0) {
713
+ out[i] = null;
714
+ continue;
715
+ }
716
+ try {
717
+ const t0 = Number(process.hrtime.bigint() / 1000n);
718
+ const main = backend.router.route(e);
719
+ const all = backend.router.predictAll(e);
720
+ const t1 = Number(process.hrtime.bigint() / 1000n);
721
+ out[i] = {
722
+ model: tierLabelForModelId(main.id),
723
+ modelId: main.id,
724
+ predictedQuality: main.predictedQuality,
725
+ metBar: main.metBar,
726
+ alternatives: all.map(a => ({
727
+ model: tierLabelForModelId(a.id),
728
+ modelId: a.id,
729
+ predictedQuality: a.predictedQuality,
730
+ costPerMTok: a.costPerMTok,
731
+ })),
732
+ routedBy: backend.routedBy,
733
+ inferenceTimeUs: t1 - t0,
734
+ };
735
+ }
736
+ catch {
737
+ out[i] = null;
738
+ }
739
+ }
740
+ return out;
741
+ }
742
+ /**
743
+ * Diagnostic surface — returns the active backend without performing a route.
744
+ * Used by the bench and by `claude-flow neural router status` (future CLI).
745
+ */
746
+ export async function neuralRouterStatus() {
747
+ const cfg = getConfig();
748
+ if (!cfg.enabled)
749
+ return { enabled: false, available: false, routedBy: null, reason: 'CLAUDE_FLOW_ROUTER_NEURAL!=1', config: cfg };
750
+ const backend = await getBackend();
751
+ return { enabled: true, available: backend.available, routedBy: backend.routedBy, reason: backend.reason, config: cfg };
752
+ }
753
+ /**
754
+ * ADR-149 iter 7 — fallback selector for retry-on-failure. Returns the
755
+ * cheapest candidate predicted to clear the quality bar (or the best-
756
+ * predicted if none do) that is NOT in `excludeModelIds`. Used by
757
+ * `executeAgentTask` to retry with a different model after a 429/5xx.
758
+ *
759
+ * Returns `null` when:
760
+ * - the gate is closed (mirrors tryCostOptimalRoute)
761
+ * - the embedding is missing or empty
762
+ * - the backend isn't loadable
763
+ * - every candidate is excluded (all retries exhausted)
764
+ *
765
+ * Selection is per-candidate via predictAll, then filter by exclude,
766
+ * then cheapest-clearing-bar (falling back to best-predicted).
767
+ */
768
+ export async function nextCostOptimalAlternative(embedding, excludeModelIds) {
769
+ const cfg = getConfig();
770
+ if (!cfg.enabled)
771
+ return null;
772
+ if (!Array.isArray(embedding) || embedding.length === 0)
773
+ return null;
774
+ const backend = await getBackend();
775
+ if (!backend.available || backend.router === null || backend.routedBy === null)
776
+ return null;
777
+ const exclude = new Set(excludeModelIds);
778
+ const t0 = Number(process.hrtime.bigint() / 1000n);
779
+ // Pure-TS path only — fallback retries are too rare to be worth threading
780
+ // through the FastGRNN candidate-embedding rebuild dance.
781
+ try {
782
+ const all = backend.router.predictAll(embedding);
783
+ const remaining = all.filter(a => !exclude.has(a.id));
784
+ if (remaining.length === 0)
120
785
  return null;
786
+ // Cheapest predicted to clear qualityBar, else best-predicted among
787
+ // remaining. Mirrors @metaharness/router's qualityBar semantics.
788
+ const clearing = remaining.filter(a => a.predictedQuality >= cfg.qualityBar)
789
+ .sort((a, b) => a.costPerMTok - b.costPerMTok);
790
+ const pick = clearing[0] ?? [...remaining].sort((a, b) => b.predictedQuality - a.predictedQuality)[0];
791
+ const t1 = Number(process.hrtime.bigint() / 1000n);
121
792
  return {
122
- model: best.candidateId,
123
- confidence: best.confidence,
124
- uncertainty: best.uncertainty,
125
- inferenceTimeUs: response.inferenceTimeUs,
793
+ model: tierLabelForModelId(pick.id),
794
+ modelId: pick.id,
795
+ predictedQuality: pick.predictedQuality,
796
+ metBar: pick.predictedQuality >= cfg.qualityBar,
797
+ alternatives: remaining.map(a => ({
798
+ model: tierLabelForModelId(a.id),
799
+ modelId: a.id,
800
+ predictedQuality: a.predictedQuality,
801
+ costPerMTok: a.costPerMTok,
802
+ })),
803
+ routedBy: backend.routedBy,
804
+ inferenceTimeUs: t1 - t0,
126
805
  };
127
806
  }
128
807
  catch {
129
808
  return null;
130
809
  }
131
810
  }
811
+ /**
812
+ * Test seam — reset module-level caches so unit tests can simulate cold init.
813
+ * Not exported from the package's barrel.
814
+ */
815
+ export function __resetNeuralRouterForTests() {
816
+ _config = null;
817
+ _backend = null;
818
+ _initPromise = null;
819
+ _latencyMapPromise = null;
820
+ }
132
821
  //# sourceMappingURL=neural-router.js.map