@claude-flow/cli 3.10.46 → 3.12.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.
- package/.claude/agents/analysis/analyze-code-quality.md +178 -178
- package/.claude/agents/analysis/code-analyzer.md +209 -209
- package/.claude/agents/analysis/code-review/analyze-code-quality.md +178 -178
- package/.claude/agents/architecture/arch-system-design.md +156 -156
- package/.claude/agents/architecture/system-design/arch-system-design.md +154 -154
- package/.claude/agents/browser/browser-agent.yaml +182 -182
- package/.claude/agents/consensus/byzantine-coordinator.md +62 -62
- package/.claude/agents/consensus/crdt-synchronizer.md +996 -996
- package/.claude/agents/consensus/gossip-coordinator.md +62 -62
- package/.claude/agents/consensus/performance-benchmarker.md +850 -850
- package/.claude/agents/consensus/quorum-manager.md +822 -822
- package/.claude/agents/consensus/raft-manager.md +62 -62
- package/.claude/agents/consensus/security-manager.md +621 -621
- package/.claude/agents/core/planner.md +374 -374
- package/.claude/agents/custom/test-long-runner.md +44 -44
- package/.claude/agents/data/data-ml-model.md +444 -444
- package/.claude/agents/data/ml/data-ml-model.md +192 -192
- package/.claude/agents/development/backend/dev-backend-api.md +141 -141
- package/.claude/agents/development/dev-backend-api.md +344 -344
- package/.claude/agents/devops/ci-cd/ops-cicd-github.md +163 -163
- package/.claude/agents/devops/ops-cicd-github.md +164 -164
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +173 -173
- package/.claude/agents/documentation/docs-api-openapi.md +354 -354
- package/.claude/agents/flow-nexus/app-store.md +87 -87
- package/.claude/agents/flow-nexus/authentication.md +68 -68
- package/.claude/agents/flow-nexus/challenges.md +80 -80
- package/.claude/agents/flow-nexus/neural-network.md +87 -87
- package/.claude/agents/flow-nexus/payments.md +82 -82
- package/.claude/agents/flow-nexus/sandbox.md +75 -75
- package/.claude/agents/flow-nexus/swarm.md +75 -75
- package/.claude/agents/flow-nexus/user-tools.md +95 -95
- package/.claude/agents/flow-nexus/workflow.md +83 -83
- package/.claude/agents/github/code-review-swarm.md +377 -377
- package/.claude/agents/github/github-modes.md +172 -172
- package/.claude/agents/github/issue-tracker.md +575 -575
- package/.claude/agents/github/multi-repo-swarm.md +552 -552
- package/.claude/agents/github/pr-manager.md +437 -437
- package/.claude/agents/github/project-board-sync.md +508 -508
- package/.claude/agents/github/release-manager.md +604 -604
- package/.claude/agents/github/release-swarm.md +582 -582
- package/.claude/agents/github/repo-architect.md +397 -397
- package/.claude/agents/github/swarm-issue.md +572 -572
- package/.claude/agents/github/swarm-pr.md +427 -427
- package/.claude/agents/github/sync-coordinator.md +451 -451
- package/.claude/agents/github/workflow-automation.md +902 -902
- package/.claude/agents/goal/agent.md +815 -815
- package/.claude/agents/optimization/benchmark-suite.md +664 -664
- package/.claude/agents/optimization/load-balancer.md +430 -430
- package/.claude/agents/optimization/performance-monitor.md +671 -671
- package/.claude/agents/optimization/resource-allocator.md +673 -673
- package/.claude/agents/optimization/topology-optimizer.md +807 -807
- package/.claude/agents/payments/agentic-payments.md +126 -126
- package/.claude/agents/sona/sona-learning-optimizer.md +74 -74
- package/.claude/agents/sparc/architecture.md +698 -698
- package/.claude/agents/sparc/pseudocode.md +519 -519
- package/.claude/agents/sparc/refinement.md +801 -801
- package/.claude/agents/sparc/specification.md +477 -477
- package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +224 -224
- package/.claude/agents/specialized/spec-mobile-react-native.md +226 -226
- package/.claude/agents/sublinear/consensus-coordinator.md +337 -337
- package/.claude/agents/sublinear/matrix-optimizer.md +184 -184
- package/.claude/agents/sublinear/pagerank-analyzer.md +298 -298
- package/.claude/agents/sublinear/performance-optimizer.md +367 -367
- package/.claude/agents/sublinear/trading-predictor.md +245 -245
- package/.claude/agents/swarm/adaptive-coordinator.md +1126 -1126
- package/.claude/agents/swarm/hierarchical-coordinator.md +709 -709
- package/.claude/agents/swarm/mesh-coordinator.md +962 -962
- package/.claude/agents/templates/automation-smart-agent.md +204 -204
- package/.claude/agents/templates/base-template-generator.md +289 -289
- package/.claude/agents/templates/coordinator-swarm-init.md +89 -89
- package/.claude/agents/templates/github-pr-manager.md +176 -176
- package/.claude/agents/templates/implementer-sparc-coder.md +258 -258
- package/.claude/agents/templates/memory-coordinator.md +186 -186
- package/.claude/agents/templates/orchestrator-task.md +138 -138
- package/.claude/agents/templates/performance-analyzer.md +198 -198
- package/.claude/agents/templates/sparc-coordinator.md +513 -513
- package/.claude/agents/testing/production-validator.md +394 -394
- package/.claude/agents/testing/tdd-london-swarm.md +243 -243
- package/.claude/agents/v3/aidefence-guardian.md +282 -282
- package/.claude/agents/v3/claims-authorizer.md +208 -208
- package/.claude/agents/v3/collective-intelligence-coordinator.md +993 -993
- package/.claude/agents/v3/ddd-domain-expert.md +220 -220
- package/.claude/agents/v3/injection-analyst.md +236 -236
- package/.claude/agents/v3/performance-engineer.md +1233 -1233
- package/.claude/agents/v3/pii-detector.md +151 -151
- package/.claude/agents/v3/reasoningbank-learner.md +213 -213
- package/.claude/agents/v3/security-architect-aidefence.md +410 -410
- package/.claude/agents/v3/security-architect.md +867 -867
- package/.claude/agents/v3/swarm-memory-manager.md +157 -157
- package/.claude/agents/v3/v3-integration-architect.md +205 -205
- package/.claude/commands/agents/README.md +50 -50
- package/.claude/commands/agents/agent-capabilities.md +140 -140
- package/.claude/commands/agents/agent-coordination.md +28 -28
- package/.claude/commands/agents/agent-spawning.md +28 -28
- package/.claude/commands/agents/agent-types.md +216 -216
- package/.claude/commands/agents/health.md +139 -139
- package/.claude/commands/agents/list.md +100 -100
- package/.claude/commands/agents/logs.md +130 -130
- package/.claude/commands/agents/metrics.md +122 -122
- package/.claude/commands/agents/pool.md +127 -127
- package/.claude/commands/agents/spawn.md +140 -140
- package/.claude/commands/agents/status.md +115 -115
- package/.claude/commands/agents/stop.md +102 -102
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
- package/.claude/commands/analysis/README.md +9 -9
- package/.claude/commands/analysis/bottleneck-detect.md +162 -162
- package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
- package/.claude/commands/analysis/performance-report.md +25 -25
- package/.claude/commands/analysis/token-efficiency.md +44 -44
- package/.claude/commands/analysis/token-usage.md +25 -25
- package/.claude/commands/automation/README.md +9 -9
- package/.claude/commands/automation/auto-agent.md +122 -122
- package/.claude/commands/automation/self-healing.md +105 -105
- package/.claude/commands/automation/session-memory.md +89 -89
- package/.claude/commands/automation/smart-agents.md +72 -72
- package/.claude/commands/automation/smart-spawn.md +25 -25
- package/.claude/commands/automation/workflow-select.md +25 -25
- package/.claude/commands/claude-flow-help.md +103 -103
- package/.claude/commands/claude-flow-memory.md +107 -107
- package/.claude/commands/claude-flow-swarm.md +205 -205
- package/.claude/commands/coordination/README.md +9 -9
- package/.claude/commands/coordination/agent-spawn.md +25 -25
- package/.claude/commands/coordination/init.md +44 -44
- package/.claude/commands/coordination/orchestrate.md +43 -43
- package/.claude/commands/coordination/spawn.md +45 -45
- package/.claude/commands/coordination/swarm-init.md +85 -85
- package/.claude/commands/coordination/task-orchestrate.md +25 -25
- package/.claude/commands/github/README.md +11 -11
- package/.claude/commands/github/code-review-swarm.md +513 -513
- package/.claude/commands/github/code-review.md +25 -25
- package/.claude/commands/github/github-modes.md +146 -146
- package/.claude/commands/github/github-swarm.md +121 -121
- package/.claude/commands/github/issue-tracker.md +291 -291
- package/.claude/commands/github/issue-triage.md +25 -25
- package/.claude/commands/github/multi-repo-swarm.md +518 -518
- package/.claude/commands/github/pr-enhance.md +26 -26
- package/.claude/commands/github/pr-manager.md +169 -169
- package/.claude/commands/github/project-board-sync.md +470 -470
- package/.claude/commands/github/release-manager.md +339 -339
- package/.claude/commands/github/release-swarm.md +543 -543
- package/.claude/commands/github/repo-analyze.md +25 -25
- package/.claude/commands/github/repo-architect.md +366 -366
- package/.claude/commands/github/swarm-issue.md +484 -484
- package/.claude/commands/github/swarm-pr.md +287 -287
- package/.claude/commands/github/sync-coordinator.md +302 -302
- package/.claude/commands/github/workflow-automation.md +441 -441
- package/.claude/commands/hive-mind/README.md +17 -17
- package/.claude/commands/hive-mind/hive-mind-consensus.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-init.md +18 -18
- package/.claude/commands/hive-mind/hive-mind-memory.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-metrics.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-resume.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-sessions.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-spawn.md +21 -21
- package/.claude/commands/hive-mind/hive-mind-status.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-stop.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-wizard.md +8 -8
- package/.claude/commands/hive-mind/hive-mind.md +27 -27
- package/.claude/commands/hooks/README.md +11 -11
- package/.claude/commands/hooks/overview.md +57 -57
- package/.claude/commands/hooks/post-edit.md +117 -117
- package/.claude/commands/hooks/post-task.md +112 -112
- package/.claude/commands/hooks/pre-edit.md +113 -113
- package/.claude/commands/hooks/pre-task.md +111 -111
- package/.claude/commands/hooks/session-end.md +118 -118
- package/.claude/commands/hooks/setup.md +102 -102
- package/.claude/commands/memory/README.md +9 -9
- package/.claude/commands/memory/memory-persist.md +25 -25
- package/.claude/commands/memory/memory-search.md +25 -25
- package/.claude/commands/memory/memory-usage.md +25 -25
- package/.claude/commands/memory/neural.md +47 -47
- package/.claude/commands/monitoring/README.md +9 -9
- package/.claude/commands/monitoring/agent-metrics.md +25 -25
- package/.claude/commands/monitoring/agents.md +44 -44
- package/.claude/commands/monitoring/real-time-view.md +25 -25
- package/.claude/commands/monitoring/status.md +46 -46
- package/.claude/commands/monitoring/swarm-monitor.md +25 -25
- package/.claude/commands/optimization/README.md +9 -9
- package/.claude/commands/optimization/auto-topology.md +61 -61
- package/.claude/commands/optimization/cache-manage.md +25 -25
- package/.claude/commands/optimization/parallel-execute.md +25 -25
- package/.claude/commands/optimization/parallel-execution.md +49 -49
- package/.claude/commands/optimization/topology-optimize.md +25 -25
- package/.claude/commands/pair/README.md +260 -260
- package/.claude/commands/pair/commands.md +545 -545
- package/.claude/commands/pair/config.md +509 -509
- package/.claude/commands/pair/examples.md +511 -511
- package/.claude/commands/pair/modes.md +347 -347
- package/.claude/commands/pair/session.md +406 -406
- package/.claude/commands/pair/start.md +208 -208
- package/.claude/commands/sparc/analyzer.md +51 -51
- package/.claude/commands/sparc/architect.md +53 -53
- package/.claude/commands/sparc/ask.md +97 -97
- package/.claude/commands/sparc/batch-executor.md +54 -54
- package/.claude/commands/sparc/code.md +89 -89
- package/.claude/commands/sparc/coder.md +54 -54
- package/.claude/commands/sparc/debug.md +83 -83
- package/.claude/commands/sparc/debugger.md +54 -54
- package/.claude/commands/sparc/designer.md +53 -53
- package/.claude/commands/sparc/devops.md +109 -109
- package/.claude/commands/sparc/docs-writer.md +80 -80
- package/.claude/commands/sparc/documenter.md +54 -54
- package/.claude/commands/sparc/innovator.md +54 -54
- package/.claude/commands/sparc/integration.md +83 -83
- package/.claude/commands/sparc/mcp.md +117 -117
- package/.claude/commands/sparc/memory-manager.md +54 -54
- package/.claude/commands/sparc/optimizer.md +54 -54
- package/.claude/commands/sparc/orchestrator.md +131 -131
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
- package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
- package/.claude/commands/sparc/researcher.md +54 -54
- package/.claude/commands/sparc/reviewer.md +54 -54
- package/.claude/commands/sparc/security-review.md +80 -80
- package/.claude/commands/sparc/sparc-modes.md +174 -174
- package/.claude/commands/sparc/sparc.md +111 -111
- package/.claude/commands/sparc/spec-pseudocode.md +80 -80
- package/.claude/commands/sparc/supabase-admin.md +348 -348
- package/.claude/commands/sparc/swarm-coordinator.md +54 -54
- package/.claude/commands/sparc/tdd.md +54 -54
- package/.claude/commands/sparc/tester.md +54 -54
- package/.claude/commands/sparc/tutorial.md +79 -79
- package/.claude/commands/sparc/workflow-manager.md +54 -54
- package/.claude/commands/sparc.md +166 -166
- package/.claude/commands/stream-chain/pipeline.md +120 -120
- package/.claude/commands/stream-chain/run.md +69 -69
- package/.claude/commands/swarm/README.md +15 -15
- package/.claude/commands/swarm/analysis.md +95 -95
- package/.claude/commands/swarm/development.md +96 -96
- package/.claude/commands/swarm/examples.md +168 -168
- package/.claude/commands/swarm/maintenance.md +102 -102
- package/.claude/commands/swarm/optimization.md +117 -117
- package/.claude/commands/swarm/research.md +136 -136
- package/.claude/commands/swarm/swarm-analysis.md +8 -8
- package/.claude/commands/swarm/swarm-background.md +8 -8
- package/.claude/commands/swarm/swarm-init.md +19 -19
- package/.claude/commands/swarm/swarm-modes.md +8 -8
- package/.claude/commands/swarm/swarm-monitor.md +8 -8
- package/.claude/commands/swarm/swarm-spawn.md +19 -19
- package/.claude/commands/swarm/swarm-status.md +8 -8
- package/.claude/commands/swarm/swarm-strategies.md +8 -8
- package/.claude/commands/swarm/swarm.md +87 -87
- package/.claude/commands/swarm/testing.md +131 -131
- package/.claude/commands/training/README.md +9 -9
- package/.claude/commands/training/model-update.md +25 -25
- package/.claude/commands/training/neural-patterns.md +107 -107
- package/.claude/commands/training/neural-train.md +75 -75
- package/.claude/commands/training/pattern-learn.md +25 -25
- package/.claude/commands/training/specialization.md +62 -62
- package/.claude/commands/truth/start.md +142 -142
- package/.claude/commands/verify/check.md +49 -49
- package/.claude/commands/verify/start.md +127 -127
- package/.claude/commands/workflows/README.md +9 -9
- package/.claude/commands/workflows/development.md +77 -77
- package/.claude/commands/workflows/research.md +62 -62
- package/.claude/commands/workflows/workflow-create.md +25 -25
- package/.claude/commands/workflows/workflow-execute.md +25 -25
- package/.claude/commands/workflows/workflow-export.md +25 -25
- package/.claude/helpers/README.md +96 -96
- package/.claude/helpers/adr-compliance.sh +186 -186
- package/.claude/helpers/auto-commit.sh +178 -178
- package/.claude/helpers/auto-memory-hook.mjs +368 -368
- package/.claude/helpers/checkpoint-manager.sh +251 -251
- package/.claude/helpers/daemon-manager.sh +252 -252
- package/.claude/helpers/ddd-tracker.sh +144 -144
- package/.claude/helpers/github-safe.js +156 -156
- package/.claude/helpers/github-setup.sh +45 -45
- package/.claude/helpers/guidance-hook.sh +13 -13
- package/.claude/helpers/guidance-hooks.sh +102 -102
- package/.claude/helpers/health-monitor.sh +108 -108
- package/.claude/helpers/hook-handler.cjs +286 -286
- package/.claude/helpers/intelligence.cjs +1031 -1031
- package/.claude/helpers/learning-hooks.sh +329 -329
- package/.claude/helpers/learning-optimizer.sh +127 -127
- package/.claude/helpers/learning-service.mjs +1144 -1144
- package/.claude/helpers/memory.js +83 -83
- package/.claude/helpers/metrics-db.mjs +488 -488
- package/.claude/helpers/pattern-consolidator.sh +86 -86
- package/.claude/helpers/perf-worker.sh +160 -160
- package/.claude/helpers/post-commit +16 -16
- package/.claude/helpers/pre-commit +26 -26
- package/.claude/helpers/quick-start.sh +19 -19
- package/.claude/helpers/router.js +105 -105
- package/.claude/helpers/security-scanner.sh +127 -127
- package/.claude/helpers/session.js +157 -157
- package/.claude/helpers/setup-mcp.sh +18 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +189 -189
- package/.claude/helpers/statusline-hook.sh +21 -21
- package/.claude/helpers/statusline.cjs +604 -604
- package/.claude/helpers/statusline.js +352 -352
- package/.claude/helpers/swarm-comms.sh +353 -353
- package/.claude/helpers/swarm-hooks.sh +761 -761
- package/.claude/helpers/swarm-monitor.sh +210 -210
- package/.claude/helpers/sync-v3-metrics.sh +245 -245
- package/.claude/helpers/update-v3-progress.sh +165 -165
- package/.claude/helpers/v3-quick-status.sh +57 -57
- package/.claude/helpers/v3.sh +110 -110
- package/.claude/helpers/validate-v3-config.sh +215 -215
- package/.claude/helpers/worker-manager.sh +170 -170
- package/.claude/settings.json +182 -182
- package/.claude/skills/agentdb-advanced/SKILL.md +550 -550
- package/.claude/skills/agentdb-learning/SKILL.md +545 -545
- package/.claude/skills/agentdb-memory-patterns/SKILL.md +339 -339
- package/.claude/skills/agentdb-optimization/SKILL.md +509 -509
- package/.claude/skills/agentdb-vector-search/SKILL.md +339 -339
- package/.claude/skills/browser/SKILL.md +204 -204
- package/.claude/skills/dual-mode/README.md +71 -71
- package/.claude/skills/dual-mode/dual-collect.md +103 -103
- package/.claude/skills/dual-mode/dual-coordinate.md +85 -85
- package/.claude/skills/dual-mode/dual-spawn.md +81 -81
- package/.claude/skills/flow-nexus-neural/SKILL.md +727 -727
- package/.claude/skills/flow-nexus-platform/SKILL.md +1154 -1154
- package/.claude/skills/flow-nexus-swarm/SKILL.md +604 -604
- package/.claude/skills/github-code-review/SKILL.md +1125 -1125
- package/.claude/skills/github-multi-repo/SKILL.md +862 -862
- package/.claude/skills/github-project-management/SKILL.md +1262 -1262
- package/.claude/skills/github-release-management/SKILL.md +1064 -1064
- package/.claude/skills/github-workflow-automation/SKILL.md +1047 -1047
- package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
- package/.claude/skills/pair-programming/SKILL.md +1202 -1202
- package/.claude/skills/reasoningbank-agentdb/SKILL.md +446 -446
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +201 -201
- package/.claude/skills/skill-builder/SKILL.md +910 -910
- package/.claude/skills/sparc-methodology/SKILL.md +1106 -1106
- package/.claude/skills/stream-chain/SKILL.md +560 -560
- package/.claude/skills/swarm-advanced/SKILL.md +970 -970
- package/.claude/skills/swarm-orchestration/SKILL.md +179 -179
- package/.claude/skills/v3-cli-modernization/SKILL.md +871 -871
- package/.claude/skills/v3-core-implementation/SKILL.md +796 -796
- package/.claude/skills/v3-ddd-architecture/SKILL.md +441 -441
- package/.claude/skills/v3-integration-deep/SKILL.md +240 -240
- package/.claude/skills/v3-mcp-optimization/SKILL.md +776 -776
- package/.claude/skills/v3-memory-unification/SKILL.md +173 -173
- package/.claude/skills/v3-performance-optimization/SKILL.md +389 -389
- package/.claude/skills/v3-security-overhaul/SKILL.md +81 -81
- package/.claude/skills/v3-swarm-coordination/SKILL.md +339 -339
- package/.claude/skills/verification-quality/SKILL.md +691 -691
- package/README.md +412 -412
- package/bin/cli.js +314 -314
- package/bin/mcp-server.js +224 -224
- package/bin/preinstall.cjs +2 -2
- package/dist/src/benchmarks/gaia-critic.js +24 -24
- package/dist/src/commands/completions.js +409 -409
- package/dist/src/commands/daemon.js +44 -44
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +175 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/eject.d.ts +33 -0
- package/dist/src/commands/eject.d.ts.map +1 -0
- package/dist/src/commands/eject.js +195 -0
- package/dist/src/commands/eject.js.map +1 -0
- package/dist/src/commands/embeddings.js +26 -26
- package/dist/src/commands/hive-mind.js +97 -97
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +11 -10
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +4 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +3 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/metaharness.d.ts +39 -0
- package/dist/src/commands/metaharness.d.ts.map +1 -0
- package/dist/src/commands/metaharness.js +194 -0
- package/dist/src/commands/metaharness.js.map +1 -0
- package/dist/src/commands/neural.d.ts.map +1 -1
- package/dist/src/commands/neural.js +2351 -1
- package/dist/src/commands/neural.js.map +1 -1
- package/dist/src/commands/ruvector/backup.js +23 -23
- package/dist/src/commands/ruvector/benchmark.js +31 -31
- package/dist/src/commands/ruvector/import.js +14 -14
- package/dist/src/commands/ruvector/init.js +115 -115
- package/dist/src/commands/ruvector/migrate.js +99 -99
- package/dist/src/commands/ruvector/optimize.js +51 -51
- package/dist/src/commands/ruvector/setup.js +624 -624
- package/dist/src/commands/ruvector/status.js +38 -38
- package/dist/src/init/claudemd-generator.js +231 -231
- package/dist/src/init/executor.js +453 -453
- package/dist/src/init/helpers-generator.js +727 -727
- package/dist/src/init/statusline-generator.js +717 -717
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +4 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/agent-execute-core.d.ts +23 -1
- package/dist/src/mcp-tools/agent-execute-core.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-execute-core.js +136 -9
- package/dist/src/mcp-tools/agent-execute-core.js.map +1 -1
- package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-tools.js +48 -4
- package/dist/src/mcp-tools/agent-tools.js.map +1 -1
- package/dist/src/mcp-tools/agentdb-tools.js +15 -15
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +196 -0
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +1 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +2 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/metaharness-tools.d.ts +31 -0
- package/dist/src/mcp-tools/metaharness-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/metaharness-tools.js +372 -0
- package/dist/src/mcp-tools/metaharness-tools.js.map +1 -0
- package/dist/src/memory/graph-edge-writer.js +18 -18
- package/dist/src/memory/memory-bridge.js +84 -84
- package/dist/src/memory/memory-initializer.js +393 -393
- package/dist/src/memory/rabitq-index.js +5 -5
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +3 -0
- package/dist/src/plugins/store/discovery.js.map +1 -1
- package/dist/src/plugins/store/types.d.ts +6 -1
- package/dist/src/plugins/store/types.d.ts.map +1 -1
- package/dist/src/runtime/headless.js +28 -28
- package/dist/src/ruvector/enhanced-model-router.d.ts +5 -0
- package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
- package/dist/src/ruvector/enhanced-model-router.js +18 -1
- package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
- package/dist/src/ruvector/model-prices.d.ts +50 -0
- package/dist/src/ruvector/model-prices.d.ts.map +1 -0
- package/dist/src/ruvector/model-prices.js +72 -0
- package/dist/src/ruvector/model-prices.js.map +1 -0
- package/dist/src/ruvector/model-router.d.ts +147 -8
- package/dist/src/ruvector/model-router.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.js +455 -18
- package/dist/src/ruvector/model-router.js.map +1 -1
- package/dist/src/ruvector/neural-router.d.ts +172 -39
- package/dist/src/ruvector/neural-router.d.ts.map +1 -1
- package/dist/src/ruvector/neural-router.js +780 -91
- package/dist/src/ruvector/neural-router.js.map +1 -1
- package/dist/src/ruvector/router-calibrator.d.ts +65 -0
- package/dist/src/ruvector/router-calibrator.d.ts.map +1 -0
- package/dist/src/ruvector/router-calibrator.js +120 -0
- package/dist/src/ruvector/router-calibrator.js.map +1 -0
- package/dist/src/ruvector/router-parallel-recorder.d.ts +127 -0
- package/dist/src/ruvector/router-parallel-recorder.d.ts.map +1 -0
- package/dist/src/ruvector/router-parallel-recorder.js +183 -0
- package/dist/src/ruvector/router-parallel-recorder.js.map +1 -0
- package/dist/src/ruvector/router-trajectory.d.ts +137 -45
- package/dist/src/ruvector/router-trajectory.d.ts.map +1 -1
- package/dist/src/ruvector/router-trajectory.js +226 -63
- package/dist/src/ruvector/router-trajectory.js.map +1 -1
- package/dist/src/ruvector/task-embedder.d.ts +56 -0
- package/dist/src/ruvector/task-embedder.d.ts.map +1 -0
- package/dist/src/ruvector/task-embedder.js +237 -0
- package/dist/src/ruvector/task-embedder.js.map +1 -0
- package/dist/src/services/headless-worker-executor.js +84 -84
- package/dist/src/transfer/deploy-seraphine.js +23 -23
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +138 -134
- package/scripts/beir-bootstrap-significance.mjs +162 -162
- package/scripts/bench-rvagent.mjs +312 -312
- package/scripts/benchmark-codemods.mjs +127 -127
- package/scripts/benchmark-cross-repo.mjs +206 -206
- package/scripts/benchmark-pretrained-retrieval.mjs +305 -305
- package/scripts/benchmark-self-learning.mjs +241 -241
- package/scripts/benchmark-trajectory-mrr.mjs +191 -191
- package/scripts/deploy-ipfs-node.sh +153 -153
- package/scripts/grid-search-retrieval.mjs +216 -216
- package/scripts/postinstall.cjs +153 -153
- package/scripts/pretrain-from-github.mjs +358 -358
- package/scripts/publish-registry.ts +345 -345
- package/scripts/publish.sh +57 -57
- package/scripts/run-beir-bge.mjs +324 -324
- package/scripts/run-beir-hybrid.mjs +261 -261
- package/scripts/run-beir-lucene-bm25.mjs +154 -154
- package/scripts/run-beir-nfcorpus.mjs +231 -231
- package/scripts/run-beir-rrf-ablation.mjs +221 -221
- package/scripts/setup-ipfs-registry.md +366 -366
- package/dist/src/ruvector/flash-attention.d.ts +0 -195
- package/dist/src/ruvector/flash-attention.d.ts.map +0 -1
- package/dist/src/ruvector/flash-attention.js +0 -643
- package/dist/src/ruvector/flash-attention.js.map +0 -1
- package/dist/src/ruvector/moe-router.d.ts +0 -206
- package/dist/src/ruvector/moe-router.d.ts.map +0 -1
- package/dist/src/ruvector/moe-router.js +0 -626
- package/dist/src/ruvector/moe-router.js.map +0 -1
- package/dist/src/services/event-stream.d.ts +0 -25
- package/dist/src/services/event-stream.js +0 -27
- package/dist/src/services/loop-worker-runner.d.ts +0 -16
- package/dist/src/services/loop-worker-runner.js +0 -34
- package/dist/src/services/runtime-capabilities.d.ts +0 -22
- package/dist/src/services/runtime-capabilities.js +0 -45
|
@@ -1,132 +1,821 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* neural-router.ts — Optional cost-optimal neural routing path (ADR-148).
|
|
3
3
|
*
|
|
4
|
-
* Wires
|
|
5
|
-
*
|
|
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
|
|
8
|
-
*
|
|
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
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* one
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
-
//
|
|
107
|
+
// Config resolution
|
|
31
108
|
// ============================================================================
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
if (
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
66
|
-
return routerInstance;
|
|
179
|
+
return data;
|
|
67
180
|
}
|
|
68
181
|
catch {
|
|
69
|
-
loadFailed = true;
|
|
70
182
|
return null;
|
|
71
183
|
}
|
|
72
184
|
}
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
//
|
|
396
|
+
// Public function
|
|
92
397
|
// ============================================================================
|
|
93
398
|
/**
|
|
94
|
-
*
|
|
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
|
-
*
|
|
97
|
-
*
|
|
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
|
|
102
|
-
|
|
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
|
|
107
|
-
if (!
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
candidates
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|