@claude-flow/cli 3.32.3 → 3.32.5
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/helpers/helpers.manifest.json +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/agenticow/speculative-exploration.d.ts +148 -0
- package/dist/src/agenticow/speculative-exploration.js +218 -0
- package/dist/src/appliance/gguf-engine.d.ts +91 -0
- package/dist/src/appliance/gguf-engine.js +425 -0
- package/dist/src/appliance/ruvllm-bridge.d.ts +102 -0
- package/dist/src/appliance/ruvllm-bridge.js +292 -0
- package/dist/src/appliance/rvfa-builder.d.ts +44 -0
- package/dist/src/appliance/rvfa-builder.js +329 -0
- package/dist/src/appliance/rvfa-distribution.d.ts +97 -0
- package/dist/src/appliance/rvfa-distribution.js +370 -0
- package/dist/src/appliance/rvfa-format.d.ts +111 -0
- package/dist/src/appliance/rvfa-format.js +393 -0
- package/dist/src/appliance/rvfa-runner.d.ts +69 -0
- package/dist/src/appliance/rvfa-runner.js +237 -0
- package/dist/src/appliance/rvfa-signing.d.ts +123 -0
- package/dist/src/appliance/rvfa-signing.js +347 -0
- package/dist/src/auth/client.d.ts +89 -0
- package/dist/src/auth/client.js +242 -0
- package/dist/src/auth/constants.d.ts +7 -0
- package/dist/src/auth/constants.js +7 -0
- package/dist/src/auth/scopes.d.ts +14 -0
- package/dist/src/auth/scopes.js +21 -0
- package/dist/src/auth/security-bridge.d.ts +36 -0
- package/dist/src/auth/security-bridge.js +42 -0
- package/dist/src/auth/session.d.ts +20 -0
- package/dist/src/auth/session.js +32 -0
- package/dist/src/auth/state.d.ts +19 -0
- package/dist/src/auth/state.js +53 -0
- package/dist/src/auth/types.d.ts +27 -0
- package/dist/src/auth/types.js +11 -0
- package/dist/src/autopilot-state.d.ts +77 -0
- package/dist/src/autopilot-state.js +271 -0
- package/dist/src/benchmarks/gaia-agent-planning.smoke.d.ts +18 -0
- package/dist/src/benchmarks/gaia-agent-planning.smoke.js +253 -0
- package/dist/src/benchmarks/gaia-agent.d.ts +198 -0
- package/dist/src/benchmarks/gaia-agent.js +651 -0
- package/dist/src/benchmarks/gaia-causal-memory.d.ts +133 -0
- package/dist/src/benchmarks/gaia-causal-memory.js +281 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.d.ts +22 -0
- package/dist/src/benchmarks/gaia-causal-memory.smoke.js +300 -0
- package/dist/src/benchmarks/gaia-convergence.d.ts +138 -0
- package/dist/src/benchmarks/gaia-convergence.js +260 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.d.ts +19 -0
- package/dist/src/benchmarks/gaia-convergence.smoke.js +246 -0
- package/dist/src/benchmarks/gaia-critic.d.ts +123 -0
- package/dist/src/benchmarks/gaia-critic.js +312 -0
- package/dist/src/benchmarks/gaia-critic.smoke.d.ts +21 -0
- package/dist/src/benchmarks/gaia-critic.smoke.js +327 -0
- package/dist/src/benchmarks/gaia-decomposer.d.ts +125 -0
- package/dist/src/benchmarks/gaia-decomposer.js +350 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.d.ts +21 -0
- package/dist/src/benchmarks/gaia-decomposer.smoke.js +228 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.d.ts +27 -0
- package/dist/src/benchmarks/gaia-e2e-smoke.js +136 -0
- package/dist/src/benchmarks/gaia-extract.smoke.d.ts +45 -0
- package/dist/src/benchmarks/gaia-extract.smoke.js +242 -0
- package/dist/src/benchmarks/gaia-hardness/features.d.ts +46 -0
- package/dist/src/benchmarks/gaia-hardness/features.js +170 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.d.ts +105 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.js +260 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.d.ts +20 -0
- package/dist/src/benchmarks/gaia-hardness/predictor.smoke.js +235 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.d.ts +51 -0
- package/dist/src/benchmarks/gaia-hardness/train-data-loader.js +179 -0
- package/dist/src/benchmarks/gaia-judge.d.ts +88 -0
- package/dist/src/benchmarks/gaia-judge.js +437 -0
- package/dist/src/benchmarks/gaia-loader.d.ts +87 -0
- package/dist/src/benchmarks/gaia-loader.js +326 -0
- package/dist/src/benchmarks/gaia-tools/file_read.d.ts +35 -0
- package/dist/src/benchmarks/gaia-tools/file_read.js +403 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.d.ts +126 -0
- package/dist/src/benchmarks/gaia-tools/grounded_query.js +225 -0
- package/dist/src/benchmarks/gaia-tools/index.d.ts +32 -0
- package/dist/src/benchmarks/gaia-tools/index.js +36 -0
- package/dist/src/benchmarks/gaia-tools/types.d.ts +62 -0
- package/dist/src/benchmarks/gaia-tools/types.js +12 -0
- package/dist/src/benchmarks/gaia-tools/web_search.d.ts +30 -0
- package/dist/src/benchmarks/gaia-tools/web_search.js +210 -0
- package/dist/src/benchmarks/gaia-voting.d.ts +88 -0
- package/dist/src/benchmarks/gaia-voting.js +297 -0
- package/dist/src/benchmarks/gaia-voting.smoke.d.ts +20 -0
- package/dist/src/benchmarks/gaia-voting.smoke.js +332 -0
- package/dist/src/benchmarks/pretrain/index.d.ts +58 -0
- package/dist/src/benchmarks/pretrain/index.js +404 -0
- package/dist/src/business-pods/bbs-budget-tracker.d.ts +139 -0
- package/dist/src/business-pods/bbs-budget-tracker.js +358 -0
- package/dist/src/business-pods/domain-affinity-policy.d.ts +47 -0
- package/dist/src/business-pods/domain-affinity-policy.js +65 -0
- package/dist/src/business-pods/pod-schema.d.ts +96 -0
- package/dist/src/business-pods/pod-schema.js +225 -0
- package/dist/src/commands/advisor.d.ts +15 -0
- package/dist/src/commands/advisor.js +94 -0
- package/dist/src/commands/agent-wasm.d.ts +14 -0
- package/dist/src/commands/agent-wasm.js +333 -0
- package/dist/src/commands/agent.d.ts +8 -0
- package/dist/src/commands/agent.js +927 -0
- package/dist/src/commands/analyze.d.ts +19 -0
- package/dist/src/commands/analyze.js +2047 -0
- package/dist/src/commands/announcements.d.ts +17 -0
- package/dist/src/commands/announcements.js +0 -0
- package/dist/src/commands/appliance-advanced.d.ts +9 -0
- package/dist/src/commands/appliance-advanced.js +215 -0
- package/dist/src/commands/appliance.d.ts +8 -0
- package/dist/src/commands/appliance.js +404 -0
- package/dist/src/commands/auth.d.ts +15 -0
- package/dist/src/commands/auth.js +244 -0
- package/dist/src/commands/autopilot.d.ts +15 -0
- package/dist/src/commands/autopilot.js +407 -0
- package/dist/src/commands/benchmark.d.ts +10 -0
- package/dist/src/commands/benchmark.js +460 -0
- package/dist/src/commands/claims.d.ts +10 -0
- package/dist/src/commands/claims.js +620 -0
- package/dist/src/commands/cleanup.d.ts +13 -0
- package/dist/src/commands/cleanup.js +250 -0
- package/dist/src/commands/completions.d.ts +10 -0
- package/dist/src/commands/completions.js +539 -0
- package/dist/src/commands/config.d.ts +8 -0
- package/dist/src/commands/config.js +428 -0
- package/dist/src/commands/daemon.d.ts +59 -0
- package/dist/src/commands/daemon.js +1733 -0
- package/dist/src/commands/deployment.d.ts +10 -0
- package/dist/src/commands/deployment.js +672 -0
- package/dist/src/commands/doctor.d.ts +10 -0
- package/dist/src/commands/doctor.js +1902 -0
- package/dist/src/commands/eject.d.ts +33 -0
- package/dist/src/commands/eject.js +195 -0
- package/dist/src/commands/embeddings.d.ts +18 -0
- package/dist/src/commands/embeddings.js +1623 -0
- package/dist/src/commands/funnel.d.ts +18 -0
- package/dist/src/commands/funnel.js +302 -0
- package/dist/src/commands/gaia-bench.d.ts +40 -0
- package/dist/src/commands/gaia-bench.js +597 -0
- package/dist/src/commands/guidance.d.ts +8 -0
- package/dist/src/commands/guidance.js +556 -0
- package/dist/src/commands/hive-mind.d.ts +11 -0
- package/dist/src/commands/hive-mind.js +1319 -0
- package/dist/src/commands/hooks.d.ts +8 -0
- package/dist/src/commands/hooks.js +4606 -0
- package/dist/src/commands/index.d.ts +118 -0
- package/dist/src/commands/index.js +366 -0
- package/dist/src/commands/init.d.ts +13 -0
- package/dist/src/commands/init.js +1297 -0
- package/dist/src/commands/issues.d.ts +21 -0
- package/dist/src/commands/issues.js +567 -0
- package/dist/src/commands/mcp.d.ts +11 -0
- package/dist/src/commands/mcp.js +732 -0
- package/dist/src/commands/memory-backup.d.ts +11 -0
- package/dist/src/commands/memory-backup.js +46 -0
- package/dist/src/commands/memory-distill.d.ts +27 -0
- package/dist/src/commands/memory-distill.js +374 -0
- package/dist/src/commands/memory.d.ts +8 -0
- package/dist/src/commands/memory.js +1596 -0
- package/dist/src/commands/metaharness.d.ts +39 -0
- package/dist/src/commands/metaharness.js +215 -0
- package/dist/src/commands/migrate.d.ts +8 -0
- package/dist/src/commands/migrate.js +742 -0
- package/dist/src/commands/neural.d.ts +10 -0
- package/dist/src/commands/neural.js +4331 -0
- package/dist/src/commands/performance.d.ts +10 -0
- package/dist/src/commands/performance.js +583 -0
- package/dist/src/commands/plugins.d.ts +11 -0
- package/dist/src/commands/plugins.js +826 -0
- package/dist/src/commands/process.d.ts +10 -0
- package/dist/src/commands/process.js +694 -0
- package/dist/src/commands/progress.d.ts +11 -0
- package/dist/src/commands/progress.js +259 -0
- package/dist/src/commands/providers.d.ts +10 -0
- package/dist/src/commands/providers.js +502 -0
- package/dist/src/commands/proxy-lifecycle.d.ts +20 -0
- package/dist/src/commands/proxy-lifecycle.js +277 -0
- package/dist/src/commands/proxy.d.ts +21 -0
- package/dist/src/commands/proxy.js +403 -0
- package/dist/src/commands/route.d.ts +16 -0
- package/dist/src/commands/route.js +822 -0
- package/dist/src/commands/ruvector/backup.d.ts +11 -0
- package/dist/src/commands/ruvector/backup.js +747 -0
- package/dist/src/commands/ruvector/benchmark.d.ts +11 -0
- package/dist/src/commands/ruvector/benchmark.js +490 -0
- package/dist/src/commands/ruvector/import.d.ts +18 -0
- package/dist/src/commands/ruvector/import.js +373 -0
- package/dist/src/commands/ruvector/index.d.ts +29 -0
- package/dist/src/commands/ruvector/index.js +129 -0
- package/dist/src/commands/ruvector/init.d.ts +11 -0
- package/dist/src/commands/ruvector/init.js +467 -0
- package/dist/src/commands/ruvector/migrate.d.ts +11 -0
- package/dist/src/commands/ruvector/migrate.js +498 -0
- package/dist/src/commands/ruvector/optimize.d.ts +11 -0
- package/dist/src/commands/ruvector/optimize.js +505 -0
- package/dist/src/commands/ruvector/pg-utils.d.ts +14 -0
- package/dist/src/commands/ruvector/pg-utils.js +41 -0
- package/dist/src/commands/ruvector/setup.d.ts +18 -0
- package/dist/src/commands/ruvector/setup.js +765 -0
- package/dist/src/commands/ruvector/status.d.ts +11 -0
- package/dist/src/commands/ruvector/status.js +479 -0
- package/dist/src/commands/security.d.ts +10 -0
- package/dist/src/commands/security.js +1012 -0
- package/dist/src/commands/session.d.ts +8 -0
- package/dist/src/commands/session.js +757 -0
- package/dist/src/commands/settings.d.ts +19 -0
- package/dist/src/commands/settings.js +180 -0
- package/dist/src/commands/spinner.d.ts +16 -0
- package/dist/src/commands/spinner.js +329 -0
- package/dist/src/commands/start.d.ts +8 -0
- package/dist/src/commands/start.js +418 -0
- package/dist/src/commands/status.d.ts +8 -0
- package/dist/src/commands/status.js +608 -0
- package/dist/src/commands/swarm.d.ts +8 -0
- package/dist/src/commands/swarm.js +891 -0
- package/dist/src/commands/task.d.ts +8 -0
- package/dist/src/commands/task.js +675 -0
- package/dist/src/commands/transfer-store.d.ts +13 -0
- package/dist/src/commands/transfer-store.js +428 -0
- package/dist/src/commands/update.d.ts +8 -0
- package/dist/src/commands/update.js +276 -0
- package/dist/src/commands/verify.d.ts +19 -0
- package/dist/src/commands/verify.js +261 -0
- package/dist/src/commands/version.d.ts +42 -0
- package/dist/src/commands/version.js +106 -0
- package/dist/src/commands/workflow.d.ts +8 -0
- package/dist/src/commands/workflow.js +617 -0
- package/dist/src/config/harness-feedback-applier.d.ts +50 -0
- package/dist/src/config/harness-feedback-applier.js +122 -0
- package/dist/src/config/proven-config-refresh.d.ts +39 -0
- package/dist/src/config/proven-config-refresh.js +154 -0
- package/dist/src/config/proven-config-rvfa.d.ts +23 -0
- package/dist/src/config/proven-config-rvfa.js +73 -0
- package/dist/src/config/proven-config.d.ts +86 -0
- package/dist/src/config/proven-config.js +176 -0
- package/dist/src/config-adapter.d.ts +15 -0
- package/dist/src/config-adapter.js +186 -0
- package/dist/src/encryption/vault.d.ts +94 -0
- package/dist/src/encryption/vault.js +172 -0
- package/dist/src/fs-secure.d.ts +86 -0
- package/dist/src/fs-secure.js +133 -0
- package/dist/src/funnel/advisor-tip.d.ts +58 -0
- package/dist/src/funnel/advisor-tip.js +92 -0
- package/dist/src/funnel/attribution.d.ts +37 -0
- package/dist/src/funnel/attribution.js +101 -0
- package/dist/src/funnel/consent.d.ts +22 -0
- package/dist/src/funnel/consent.js +58 -0
- package/dist/src/funnel/credit-errors.d.ts +31 -0
- package/dist/src/funnel/credit-errors.js +88 -0
- package/dist/src/funnel/credit-notifier.d.ts +44 -0
- package/dist/src/funnel/credit-notifier.js +74 -0
- package/dist/src/funnel/disclosure.d.ts +47 -0
- package/dist/src/funnel/disclosure.js +109 -0
- package/dist/src/funnel/enrollment.d.ts +36 -0
- package/dist/src/funnel/enrollment.js +64 -0
- package/dist/src/funnel/environment.d.ts +17 -0
- package/dist/src/funnel/environment.js +39 -0
- package/dist/src/funnel/event-transport.d.ts +51 -0
- package/dist/src/funnel/event-transport.js +199 -0
- package/dist/src/funnel/events.d.ts +42 -0
- package/dist/src/funnel/events.js +150 -0
- package/dist/src/funnel/index.d.ts +21 -0
- package/dist/src/funnel/index.js +21 -0
- package/dist/src/funnel/insights.d.ts +50 -0
- package/dist/src/funnel/insights.js +120 -0
- package/dist/src/funnel/local-signals.d.ts +20 -0
- package/dist/src/funnel/local-signals.js +77 -0
- package/dist/src/funnel/message-transport.d.ts +51 -0
- package/dist/src/funnel/message-transport.js +149 -0
- package/dist/src/funnel/messages.d.ts +55 -0
- package/dist/src/funnel/messages.js +160 -0
- package/dist/src/funnel/payout.d.ts +40 -0
- package/dist/src/funnel/payout.js +60 -0
- package/dist/src/funnel/power-saver-notifier.d.ts +44 -0
- package/dist/src/funnel/power-saver-notifier.js +92 -0
- package/dist/src/funnel/precedence.d.ts +16 -0
- package/dist/src/funnel/precedence.js +85 -0
- package/dist/src/funnel/promo.d.ts +41 -0
- package/dist/src/funnel/promo.js +144 -0
- package/dist/src/funnel/rate-limit-notifier.d.ts +55 -0
- package/dist/src/funnel/rate-limit-notifier.js +102 -0
- package/dist/src/funnel/rotation.d.ts +19 -0
- package/dist/src/funnel/rotation.js +70 -0
- package/dist/src/funnel/state.d.ts +13 -0
- package/dist/src/funnel/state.js +52 -0
- package/dist/src/funnel/toggle-cooldown.d.ts +17 -0
- package/dist/src/funnel/toggle-cooldown.js +32 -0
- package/dist/src/funnel/types.d.ts +98 -0
- package/dist/src/funnel/types.js +26 -0
- package/dist/src/index.d.ts +81 -0
- package/dist/src/index.js +609 -0
- package/dist/src/infrastructure/in-memory-repositories.d.ts +68 -0
- package/dist/src/infrastructure/in-memory-repositories.js +264 -0
- package/dist/src/init/claudemd-generator.d.ts +16 -0
- package/dist/src/init/claudemd-generator.js +368 -0
- package/dist/src/init/executor.d.ts +41 -0
- package/dist/src/init/executor.js +2142 -0
- package/dist/src/init/helper-refresh.d.ts +79 -0
- package/dist/src/init/helper-refresh.js +347 -0
- package/dist/src/init/helper-signing.d.ts +37 -0
- package/dist/src/init/helper-signing.js +67 -0
- package/dist/src/init/helpers-generator.d.ts +87 -0
- package/dist/src/init/helpers-generator.js +1456 -0
- package/dist/src/init/index.d.ts +13 -0
- package/dist/src/init/index.js +15 -0
- package/dist/src/init/mcp-generator.d.ts +26 -0
- package/dist/src/init/mcp-generator.js +126 -0
- package/dist/src/init/memory-package-resolver.d.ts +53 -0
- package/dist/src/init/memory-package-resolver.js +118 -0
- package/dist/src/init/settings-generator.d.ts +14 -0
- package/dist/src/init/settings-generator.js +462 -0
- package/dist/src/init/statusline-generator.d.ts +28 -0
- package/dist/src/init/statusline-generator.js +177 -0
- package/dist/src/init/types.d.ts +331 -0
- package/dist/src/init/types.js +276 -0
- package/dist/src/log-filters.d.ts +46 -0
- package/dist/src/log-filters.js +107 -0
- package/dist/src/mcp-client.d.ts +92 -0
- package/dist/src/mcp-client.js +397 -0
- package/dist/src/mcp-server.d.ts +163 -0
- package/dist/src/mcp-server.js +751 -0
- package/dist/src/mcp-tools/agent-execute-core.d.ts +115 -0
- package/dist/src/mcp-tools/agent-execute-core.js +587 -0
- package/dist/src/mcp-tools/agent-tools.d.ts +9 -0
- package/dist/src/mcp-tools/agent-tools.js +837 -0
- package/dist/src/mcp-tools/agentbbs-tools.d.ts +28 -0
- package/dist/src/mcp-tools/agentbbs-tools.js +394 -0
- package/dist/src/mcp-tools/agentdb-tools.d.ts +35 -0
- package/dist/src/mcp-tools/agentdb-tools.js +1473 -0
- package/dist/src/mcp-tools/agenticow-loader.d.ts +59 -0
- package/dist/src/mcp-tools/agenticow-loader.js +105 -0
- package/dist/src/mcp-tools/agenticow-speculate-tools.d.ts +24 -0
- package/dist/src/mcp-tools/agenticow-speculate-tools.js +209 -0
- package/dist/src/mcp-tools/agenticow-tools.d.ts +36 -0
- package/dist/src/mcp-tools/agenticow-tools.js +360 -0
- package/dist/src/mcp-tools/analyze-tools.d.ts +38 -0
- package/dist/src/mcp-tools/analyze-tools.js +346 -0
- package/dist/src/mcp-tools/auto-install.d.ts +83 -0
- package/dist/src/mcp-tools/auto-install.js +131 -0
- package/dist/src/mcp-tools/autopilot-tools.d.ts +12 -0
- package/dist/src/mcp-tools/autopilot-tools.js +231 -0
- package/dist/src/mcp-tools/browser-intent-tools.d.ts +162 -0
- package/dist/src/mcp-tools/browser-intent-tools.js +548 -0
- package/dist/src/mcp-tools/browser-session-tools.d.ts +27 -0
- package/dist/src/mcp-tools/browser-session-tools.js +398 -0
- package/dist/src/mcp-tools/browser-tools.d.ts +21 -0
- package/dist/src/mcp-tools/browser-tools.js +760 -0
- package/dist/src/mcp-tools/business-pod-tools.d.ts +20 -0
- package/dist/src/mcp-tools/business-pod-tools.js +169 -0
- package/dist/src/mcp-tools/claims-tools.d.ts +12 -0
- package/dist/src/mcp-tools/claims-tools.js +863 -0
- package/dist/src/mcp-tools/config-tools.d.ts +8 -0
- package/dist/src/mcp-tools/config-tools.js +411 -0
- package/dist/src/mcp-tools/coordination-tools.d.ts +13 -0
- package/dist/src/mcp-tools/coordination-tools.js +729 -0
- package/dist/src/mcp-tools/daa-tools.d.ts +13 -0
- package/dist/src/mcp-tools/daa-tools.js +534 -0
- package/dist/src/mcp-tools/embeddings-tools.d.ts +9 -0
- package/dist/src/mcp-tools/embeddings-tools.js +904 -0
- package/dist/src/mcp-tools/github-tools.d.ts +9 -0
- package/dist/src/mcp-tools/github-tools.js +659 -0
- package/dist/src/mcp-tools/guidance-tools.d.ts +15 -0
- package/dist/src/mcp-tools/guidance-tools.js +639 -0
- package/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
- package/dist/src/mcp-tools/hive-mind-tools.js +953 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +65 -0
- package/dist/src/mcp-tools/hooks-tools.js +4836 -0
- package/dist/src/mcp-tools/http-fetch-tools.d.ts +55 -0
- package/dist/src/mcp-tools/http-fetch-tools.js +329 -0
- package/dist/src/mcp-tools/index.d.ts +34 -0
- package/dist/src/mcp-tools/index.js +40 -0
- package/dist/src/mcp-tools/managed-agent-tools.d.ts +22 -0
- package/dist/src/mcp-tools/managed-agent-tools.js +357 -0
- package/dist/src/mcp-tools/memory-tools.d.ts +14 -0
- package/dist/src/mcp-tools/memory-tools.js +1330 -0
- package/dist/src/mcp-tools/metaharness-tools.d.ts +51 -0
- package/dist/src/mcp-tools/metaharness-tools.js +684 -0
- package/dist/src/mcp-tools/neural-tools.d.ts +54 -0
- package/dist/src/mcp-tools/neural-tools.js +1168 -0
- package/dist/src/mcp-tools/performance-tools.d.ts +16 -0
- package/dist/src/mcp-tools/performance-tools.js +675 -0
- package/dist/src/mcp-tools/progress-tools.d.ts +14 -0
- package/dist/src/mcp-tools/progress-tools.js +348 -0
- package/dist/src/mcp-tools/request-tracker.d.ts +17 -0
- package/dist/src/mcp-tools/request-tracker.js +27 -0
- package/dist/src/mcp-tools/ruvllm-tools.d.ts +9 -0
- package/dist/src/mcp-tools/ruvllm-tools.js +355 -0
- package/dist/src/mcp-tools/security-tools.d.ts +18 -0
- package/dist/src/mcp-tools/security-tools.js +556 -0
- package/dist/src/mcp-tools/session-tools.d.ts +8 -0
- package/dist/src/mcp-tools/session-tools.js +517 -0
- package/dist/src/mcp-tools/swarm-tools.d.ts +37 -0
- package/dist/src/mcp-tools/swarm-tools.js +390 -0
- package/dist/src/mcp-tools/system-tools.d.ts +13 -0
- package/dist/src/mcp-tools/system-tools.js +688 -0
- package/dist/src/mcp-tools/task-tools.d.ts +8 -0
- package/dist/src/mcp-tools/task-tools.js +487 -0
- package/dist/src/mcp-tools/terminal-tools.d.ts +8 -0
- package/dist/src/mcp-tools/terminal-tools.js +306 -0
- package/dist/src/mcp-tools/testgen-tools.d.ts +26 -0
- package/dist/src/mcp-tools/testgen-tools.js +168 -0
- package/dist/src/mcp-tools/tool-loop-guardrail.d.ts +31 -0
- package/dist/src/mcp-tools/tool-loop-guardrail.js +71 -0
- package/dist/src/mcp-tools/transfer-tools.d.ts +14 -0
- package/dist/src/mcp-tools/transfer-tools.js +447 -0
- package/dist/src/mcp-tools/types.d.ts +8 -0
- package/dist/src/mcp-tools/types.js +8 -0
- package/dist/src/mcp-tools/validate-input.d.ts +9 -0
- package/dist/src/mcp-tools/validate-input.js +9 -0
- package/dist/src/mcp-tools/wasm-agent-tools.d.ts +13 -0
- package/dist/src/mcp-tools/wasm-agent-tools.js +840 -0
- package/dist/src/mcp-tools/workflow-tools.d.ts +8 -0
- package/dist/src/mcp-tools/workflow-tools.js +884 -0
- package/dist/src/memory/bge-embedder.d.ts +25 -0
- package/dist/src/memory/bge-embedder.js +121 -0
- package/dist/src/memory/cross-encoder-rerank.d.ts +33 -0
- package/dist/src/memory/cross-encoder-rerank.js +123 -0
- package/dist/src/memory/embedding-policy.d.ts +21 -0
- package/dist/src/memory/embedding-policy.js +30 -0
- package/dist/src/memory/embedding-quantization.d.ts +62 -0
- package/dist/src/memory/embedding-quantization.js +156 -0
- package/dist/src/memory/ewc-consolidation.d.ts +305 -0
- package/dist/src/memory/ewc-consolidation.js +611 -0
- package/dist/src/memory/graph-edge-writer.d.ts +95 -0
- package/dist/src/memory/graph-edge-writer.js +217 -0
- package/dist/src/memory/hybrid-retrieval.d.ts +77 -0
- package/dist/src/memory/hybrid-retrieval.js +192 -0
- package/dist/src/memory/intelligence.d.ts +405 -0
- package/dist/src/memory/intelligence.js +1316 -0
- package/dist/src/memory/lucene-bm25.d.ts +19 -0
- package/dist/src/memory/lucene-bm25.js +308 -0
- package/dist/src/memory/memory-bridge.d.ts +537 -0
- package/dist/src/memory/memory-bridge.js +2460 -0
- package/dist/src/memory/memory-initializer.d.ts +556 -0
- package/dist/src/memory/memory-initializer.js +3021 -0
- package/dist/src/memory/neural-package-bridge.d.ts +48 -0
- package/dist/src/memory/neural-package-bridge.js +87 -0
- package/dist/src/memory/rabitq-index.d.ts +60 -0
- package/dist/src/memory/rabitq-index.js +242 -0
- package/dist/src/memory/sona-optimizer.d.ts +267 -0
- package/dist/src/memory/sona-optimizer.js +779 -0
- package/dist/src/memory/structured-distill.d.ts +48 -0
- package/dist/src/memory/structured-distill.js +125 -0
- package/dist/src/output.d.ts +9 -0
- package/dist/src/output.js +9 -0
- package/dist/src/parser.d.ts +89 -0
- package/dist/src/parser.js +516 -0
- package/dist/src/plugins/manager.d.ts +133 -0
- package/dist/src/plugins/manager.js +415 -0
- package/dist/src/plugins/store/discovery.d.ts +99 -0
- package/dist/src/plugins/store/discovery.js +1224 -0
- package/dist/src/plugins/store/index.d.ts +76 -0
- package/dist/src/plugins/store/index.js +141 -0
- package/dist/src/plugins/store/search.d.ts +46 -0
- package/dist/src/plugins/store/search.js +230 -0
- package/dist/src/plugins/store/types.d.ts +279 -0
- package/dist/src/plugins/store/types.js +7 -0
- package/dist/src/plugins/tests/demo-plugin-store.d.ts +7 -0
- package/dist/src/plugins/tests/demo-plugin-store.js +126 -0
- package/dist/src/plugins/tests/standalone-test.d.ts +12 -0
- package/dist/src/plugins/tests/standalone-test.js +188 -0
- package/dist/src/plugins/tests/test-plugin-store.d.ts +7 -0
- package/dist/src/plugins/tests/test-plugin-store.js +206 -0
- package/dist/src/production/circuit-breaker.d.ts +101 -0
- package/dist/src/production/circuit-breaker.js +241 -0
- package/dist/src/production/error-handler.d.ts +92 -0
- package/dist/src/production/error-handler.js +299 -0
- package/dist/src/production/index.d.ts +23 -0
- package/dist/src/production/index.js +18 -0
- package/dist/src/production/monitoring.d.ts +161 -0
- package/dist/src/production/monitoring.js +356 -0
- package/dist/src/production/rate-limiter.d.ts +80 -0
- package/dist/src/production/rate-limiter.js +201 -0
- package/dist/src/production/retry.d.ts +48 -0
- package/dist/src/production/retry.js +179 -0
- package/dist/src/prompt.d.ts +44 -0
- package/dist/src/prompt.js +501 -0
- package/dist/src/proxy/install.d.ts +29 -0
- package/dist/src/proxy/install.js +135 -0
- package/dist/src/proxy/lifecycle.d.ts +61 -0
- package/dist/src/proxy/lifecycle.js +249 -0
- package/dist/src/proxy/paths.d.ts +34 -0
- package/dist/src/proxy/paths.js +70 -0
- package/dist/src/proxy/release.d.ts +47 -0
- package/dist/src/proxy/release.js +138 -0
- package/dist/src/proxy/token-bridge.d.ts +5 -0
- package/dist/src/proxy/token-bridge.js +61 -0
- package/dist/src/proxy/verify.d.ts +44 -0
- package/dist/src/proxy/verify.js +68 -0
- package/dist/src/runtime/headless.d.ts +60 -0
- package/dist/src/runtime/headless.js +284 -0
- package/dist/src/runtime/parent-death-watchdog.d.ts +42 -0
- package/dist/src/runtime/parent-death-watchdog.js +70 -0
- package/dist/src/ruvector/agent-wasm.d.ts +228 -0
- package/dist/src/ruvector/agent-wasm.js +463 -0
- package/dist/src/ruvector/ast-analyzer.d.ts +67 -0
- package/dist/src/ruvector/ast-analyzer.js +277 -0
- package/dist/src/ruvector/codemods/engine.d.ts +45 -0
- package/dist/src/ruvector/codemods/engine.js +291 -0
- package/dist/src/ruvector/codemods/scope-analysis.d.ts +29 -0
- package/dist/src/ruvector/codemods/scope-analysis.js +162 -0
- package/dist/src/ruvector/coverage-router.d.ts +160 -0
- package/dist/src/ruvector/coverage-router.js +531 -0
- package/dist/src/ruvector/coverage-tools.d.ts +33 -0
- package/dist/src/ruvector/coverage-tools.js +157 -0
- package/dist/src/ruvector/diff-classifier.d.ts +175 -0
- package/dist/src/ruvector/diff-classifier.js +699 -0
- package/dist/src/ruvector/diskann-backend.d.ts +78 -0
- package/dist/src/ruvector/diskann-backend.js +310 -0
- package/dist/src/ruvector/enhanced-model-router.d.ts +172 -0
- package/dist/src/ruvector/enhanced-model-router.js +577 -0
- package/dist/src/ruvector/graph-analyzer.d.ts +187 -0
- package/dist/src/ruvector/graph-analyzer.js +929 -0
- package/dist/src/ruvector/graph-backend.d.ts +79 -0
- package/dist/src/ruvector/graph-backend.js +220 -0
- package/dist/src/ruvector/index.d.ts +38 -0
- package/dist/src/ruvector/index.js +86 -0
- package/dist/src/ruvector/lora-adapter.d.ts +292 -0
- package/dist/src/ruvector/lora-adapter.js +710 -0
- package/dist/src/ruvector/model-prices.d.ts +50 -0
- package/dist/src/ruvector/model-prices.js +72 -0
- package/dist/src/ruvector/model-router.d.ts +408 -0
- package/dist/src/ruvector/model-router.js +1162 -0
- package/dist/src/ruvector/neural-router.d.ts +182 -0
- package/dist/src/ruvector/neural-router.js +825 -0
- package/dist/src/ruvector/output-verifier.d.ts +83 -0
- package/dist/src/ruvector/output-verifier.js +277 -0
- package/dist/src/ruvector/q-learning-router.d.ts +227 -0
- package/dist/src/ruvector/q-learning-router.js +721 -0
- package/dist/src/ruvector/router-calibrator.d.ts +65 -0
- package/dist/src/ruvector/router-calibrator.js +120 -0
- package/dist/src/ruvector/router-parallel-recorder.d.ts +127 -0
- package/dist/src/ruvector/router-parallel-recorder.js +183 -0
- package/dist/src/ruvector/router-trajectory.d.ts +194 -0
- package/dist/src/ruvector/router-trajectory.js +281 -0
- package/dist/src/ruvector/run-transcript-recorder.d.ts +154 -0
- package/dist/src/ruvector/run-transcript-recorder.js +209 -0
- package/dist/src/ruvector/ruvllm-wasm.d.ts +179 -0
- package/dist/src/ruvector/ruvllm-wasm.js +379 -0
- package/dist/src/ruvector/semantic-router.d.ts +77 -0
- package/dist/src/ruvector/semantic-router.js +178 -0
- package/dist/src/ruvector/task-embedder.d.ts +56 -0
- package/dist/src/ruvector/task-embedder.js +237 -0
- package/dist/src/ruvector/trajectory-tree.d.ts +113 -0
- package/dist/src/ruvector/trajectory-tree.js +237 -0
- package/dist/src/ruvector/vector-db.d.ts +73 -0
- package/dist/src/ruvector/vector-db.js +301 -0
- package/dist/src/ruvector/wasm-embedder.d.ts +13 -0
- package/dist/src/ruvector/wasm-embedder.js +143 -0
- package/dist/src/security/builtin-aidefence.d.ts +34 -0
- package/dist/src/security/builtin-aidefence.js +86 -0
- package/dist/src/services/agentic-flow-bridge.d.ts +50 -0
- package/dist/src/services/agentic-flow-bridge.js +95 -0
- package/dist/src/services/ai-job-dedup.d.ts +61 -0
- package/dist/src/services/ai-job-dedup.js +136 -0
- package/dist/src/services/checkpoint-gate.d.ts +140 -0
- package/dist/src/services/checkpoint-gate.js +223 -0
- package/dist/src/services/claim-service.d.ts +204 -0
- package/dist/src/services/claim-service.js +818 -0
- package/dist/src/services/config-file-manager.d.ts +37 -0
- package/dist/src/services/config-file-manager.js +224 -0
- package/dist/src/services/container-worker-pool.d.ts +204 -0
- package/dist/src/services/container-worker-pool.js +589 -0
- package/dist/src/services/daemon-autostart.d.ts +17 -0
- package/dist/src/services/daemon-autostart.js +102 -0
- package/dist/src/services/distill-oracle.d.ts +190 -0
- package/dist/src/services/distill-oracle.js +349 -0
- package/dist/src/services/distill-tuning.d.ts +111 -0
- package/dist/src/services/distill-tuning.js +510 -0
- package/dist/src/services/evolve-proof.d.ts +253 -0
- package/dist/src/services/evolve-proof.js +343 -0
- package/dist/src/services/fable-harness.d.ts +208 -0
- package/dist/src/services/fable-harness.js +388 -0
- package/dist/src/services/git-workspace-identity.d.ts +42 -0
- package/dist/src/services/git-workspace-identity.js +99 -0
- package/dist/src/services/global-ai-budget.d.ts +135 -0
- package/dist/src/services/global-ai-budget.js +415 -0
- package/dist/src/services/harness-benchmark.d.ts +68 -0
- package/dist/src/services/harness-benchmark.js +94 -0
- package/dist/src/services/harness-canary.d.ts +60 -0
- package/dist/src/services/harness-canary.js +69 -0
- package/dist/src/services/harness-corpus-harvester.d.ts +51 -0
- package/dist/src/services/harness-corpus-harvester.js +74 -0
- package/dist/src/services/harness-flywheel-generations.d.ts +115 -0
- package/dist/src/services/harness-flywheel-generations.js +360 -0
- package/dist/src/services/harness-flywheel-runtime.d.ts +25 -0
- package/dist/src/services/harness-flywheel-runtime.js +92 -0
- package/dist/src/services/harness-flywheel.d.ts +48 -0
- package/dist/src/services/harness-flywheel.js +207 -0
- package/dist/src/services/harness-frozen-eval.d.ts +22 -0
- package/dist/src/services/harness-frozen-eval.js +66 -0
- package/dist/src/services/harness-hosts.d.ts +40 -0
- package/dist/src/services/harness-hosts.js +88 -0
- package/dist/src/services/harness-improvement-ledger.d.ts +63 -0
- package/dist/src/services/harness-improvement-ledger.js +101 -0
- package/dist/src/services/harness-loop.d.ts +53 -0
- package/dist/src/services/harness-loop.js +85 -0
- package/dist/src/services/harness-qualification.d.ts +66 -0
- package/dist/src/services/harness-qualification.js +143 -0
- package/dist/src/services/harness-replay.d.ts +37 -0
- package/dist/src/services/harness-replay.js +92 -0
- package/dist/src/services/harness-verify.d.ts +33 -0
- package/dist/src/services/harness-verify.js +26 -0
- package/dist/src/services/harness-worker.d.ts +23 -0
- package/dist/src/services/harness-worker.js +66 -0
- package/dist/src/services/headless-worker-executor.d.ts +358 -0
- package/dist/src/services/headless-worker-executor.js +1269 -0
- package/dist/src/services/index.d.ts +13 -0
- package/dist/src/services/index.js +11 -0
- package/dist/src/services/memory-backup.d.ts +53 -0
- package/dist/src/services/memory-backup.js +198 -0
- package/dist/src/services/memory-distillation.d.ts +41 -0
- package/dist/src/services/memory-distillation.js +277 -0
- package/dist/src/services/native-training.d.ts +68 -0
- package/dist/src/services/native-training.js +141 -0
- package/dist/src/services/registry-api.d.ts +58 -0
- package/dist/src/services/registry-api.js +146 -0
- package/dist/src/services/repo-supervisor.d.ts +70 -0
- package/dist/src/services/repo-supervisor.js +228 -0
- package/dist/src/services/ruvector-training.d.ts +222 -0
- package/dist/src/services/ruvector-training.js +693 -0
- package/dist/src/services/swarm-memory-branches.d.ts +135 -0
- package/dist/src/services/swarm-memory-branches.js +213 -0
- package/dist/src/services/weight-eft.d.ts +305 -0
- package/dist/src/services/weight-eft.js +296 -0
- package/dist/src/services/worker-daemon.d.ts +439 -0
- package/dist/src/services/worker-daemon.js +1865 -0
- package/dist/src/services/worker-queue.d.ts +194 -0
- package/dist/src/services/worker-queue.js +513 -0
- package/dist/src/services/workspace-lease.d.ts +55 -0
- package/dist/src/services/workspace-lease.js +191 -0
- package/dist/src/suggest.d.ts +53 -0
- package/dist/src/suggest.js +200 -0
- package/dist/src/transfer/anonymization/index.d.ts +25 -0
- package/dist/src/transfer/anonymization/index.js +175 -0
- package/dist/src/transfer/deploy-seraphine.d.ts +13 -0
- package/dist/src/transfer/deploy-seraphine.js +205 -0
- package/dist/src/transfer/export.d.ts +25 -0
- package/dist/src/transfer/export.js +113 -0
- package/dist/src/transfer/index.d.ts +12 -0
- package/dist/src/transfer/index.js +31 -0
- package/dist/src/transfer/ipfs/client.d.ts +109 -0
- package/dist/src/transfer/ipfs/client.js +307 -0
- package/dist/src/transfer/ipfs/upload.d.ts +95 -0
- package/dist/src/transfer/ipfs/upload.js +413 -0
- package/dist/src/transfer/models/seraphine.d.ts +72 -0
- package/dist/src/transfer/models/seraphine.js +373 -0
- package/dist/src/transfer/serialization/cfp.d.ts +49 -0
- package/dist/src/transfer/serialization/cfp.js +183 -0
- package/dist/src/transfer/storage/gcs.d.ts +82 -0
- package/dist/src/transfer/storage/gcs.js +272 -0
- package/dist/src/transfer/storage/index.d.ts +6 -0
- package/dist/src/transfer/storage/index.js +6 -0
- package/dist/src/transfer/store/discovery.d.ts +84 -0
- package/dist/src/transfer/store/discovery.js +382 -0
- package/dist/src/transfer/store/download.d.ts +70 -0
- package/dist/src/transfer/store/download.js +334 -0
- package/dist/src/transfer/store/index.d.ts +84 -0
- package/dist/src/transfer/store/index.js +153 -0
- package/dist/src/transfer/store/publish.d.ts +76 -0
- package/dist/src/transfer/store/publish.js +294 -0
- package/dist/src/transfer/store/registry.d.ts +58 -0
- package/dist/src/transfer/store/registry.js +285 -0
- package/dist/src/transfer/store/search.d.ts +54 -0
- package/dist/src/transfer/store/search.js +232 -0
- package/dist/src/transfer/store/tests/standalone-test.d.ts +12 -0
- package/dist/src/transfer/store/tests/standalone-test.js +190 -0
- package/dist/src/transfer/store/types.d.ts +193 -0
- package/dist/src/transfer/store/types.js +6 -0
- package/dist/src/transfer/test-seraphine.d.ts +6 -0
- package/dist/src/transfer/test-seraphine.js +105 -0
- package/dist/src/transfer/tests/test-store.d.ts +7 -0
- package/dist/src/transfer/tests/test-store.js +214 -0
- package/dist/src/transfer/types.d.ts +245 -0
- package/dist/src/transfer/types.js +6 -0
- package/dist/src/types.d.ts +13 -0
- package/dist/src/types.js +13 -0
- package/dist/src/update/checker.d.ts +34 -0
- package/dist/src/update/checker.js +191 -0
- package/dist/src/update/executor.d.ts +33 -0
- package/dist/src/update/executor.js +217 -0
- package/dist/src/update/index.d.ts +33 -0
- package/dist/src/update/index.js +64 -0
- package/dist/src/update/rate-limiter.d.ts +20 -0
- package/dist/src/update/rate-limiter.js +96 -0
- package/dist/src/update/validator.d.ts +17 -0
- package/dist/src/update/validator.js +123 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent Model Router — lexical complexity heuristic + Thompson bandit
|
|
3
|
+
*
|
|
4
|
+
* Dynamically routes requests to the optimal Claude model (haiku/sonnet/opus)
|
|
5
|
+
* based on task complexity, uncertainty, and online-learned routing outcomes.
|
|
6
|
+
*
|
|
7
|
+
* Mechanism (shipped):
|
|
8
|
+
* - Complexity score = blend of lexical, semantic-depth, task-scope, and
|
|
9
|
+
* uncertainty heuristics (see `computeLexicalComplexity` and friends).
|
|
10
|
+
* Pure JS arithmetic — no model load, no tensor math.
|
|
11
|
+
* - Model selection = Thompson-sampling Beta-Bernoulli bandit with
|
|
12
|
+
* complexity-bucketed Beta(α,β) priors, persisted to
|
|
13
|
+
* `.swarm/model-router-state.json` and updated by `recordOutcome` after
|
|
14
|
+
* each routing decision.
|
|
15
|
+
* - Uncertainty quantification + a circuit breaker drive escalation when
|
|
16
|
+
* the bandit's confidence is low or downstream failures are observed.
|
|
17
|
+
*
|
|
18
|
+
* Routing Strategy:
|
|
19
|
+
* - Haiku: high confidence, low complexity (fast, cheap)
|
|
20
|
+
* - Sonnet: medium confidence, moderate complexity (balanced)
|
|
21
|
+
* - Opus: low confidence, high complexity (most capable)
|
|
22
|
+
*
|
|
23
|
+
* Note (#2329): An earlier design (ADR-026 + this file's previous header)
|
|
24
|
+
* described a Tiny-Dancer / FastGRNN neural router with embedding-based
|
|
25
|
+
* complexity scoring. That path was never wired in directly.
|
|
26
|
+
*
|
|
27
|
+
* Note (ADR-148, #2334): The cost-optimal neural router is now wired as an
|
|
28
|
+
* optional, gated addition via `./neural-router.ts` (which uses
|
|
29
|
+
* `@metaharness/router`, optionally accelerated by `@ruvector/tiny-dancer`).
|
|
30
|
+
* It is double-gated on `CLAUDE_FLOW_ROUTER_NEURAL=1` + an embedding being
|
|
31
|
+
* supplied + a corpus/artifact being loadable. When any gate is closed the
|
|
32
|
+
* shipped heuristic + bandit path runs unchanged and the result carries
|
|
33
|
+
* `routedBy: 'heuristic'` (default) or `'bandit-fallback'` (neural enabled
|
|
34
|
+
* but declined). When all gates are open and a backend resolves, the result
|
|
35
|
+
* carries `routedBy: 'metaharness-knn' | 'metaharness-krr' | 'fastgrnn'`.
|
|
36
|
+
*
|
|
37
|
+
* @module model-router
|
|
38
|
+
*/
|
|
39
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
40
|
+
import { dirname, join } from 'path';
|
|
41
|
+
// ----------------------------------------------------------------------------
|
|
42
|
+
// Lazy module loaders — initialised once per process. The dynamic-import calls
|
|
43
|
+
// keep these modules off the critical-path-cold-start, but we want subsequent
|
|
44
|
+
// route() calls to pay only a Map lookup, not a new Promise per call.
|
|
45
|
+
// ----------------------------------------------------------------------------
|
|
46
|
+
let _neuralRouterMod = null;
|
|
47
|
+
function loadNeuralRouter() {
|
|
48
|
+
if (_neuralRouterMod === null)
|
|
49
|
+
_neuralRouterMod = import('./neural-router.js');
|
|
50
|
+
return _neuralRouterMod;
|
|
51
|
+
}
|
|
52
|
+
let _trajectoryMod = null;
|
|
53
|
+
function loadTrajectoryRecorder() {
|
|
54
|
+
if (_trajectoryMod === null)
|
|
55
|
+
_trajectoryMod = import('./router-trajectory.js');
|
|
56
|
+
return _trajectoryMod;
|
|
57
|
+
}
|
|
58
|
+
// ADR-150 iter 11–12 — parallel-decision recorder for the SelfEvolvingRouter
|
|
59
|
+
// promotion gate. Dynamic-imported lazily so the routing hot path never
|
|
60
|
+
// pays the load cost when CLAUDE_FLOW_ROUTER_PARALLEL_LOG is unset.
|
|
61
|
+
let _parallelRecorderMod = null;
|
|
62
|
+
function loadParallelRecorder() {
|
|
63
|
+
if (_parallelRecorderMod === null)
|
|
64
|
+
_parallelRecorderMod = import('./router-parallel-recorder.js');
|
|
65
|
+
return _parallelRecorderMod;
|
|
66
|
+
}
|
|
67
|
+
let _altsCache = null;
|
|
68
|
+
let _altsProbeDone = false;
|
|
69
|
+
function loadOpenRouterAlts() {
|
|
70
|
+
if (_altsProbeDone)
|
|
71
|
+
return _altsCache;
|
|
72
|
+
_altsProbeDone = true;
|
|
73
|
+
try {
|
|
74
|
+
// Probe candidate paths: explicit env override, then asset locations
|
|
75
|
+
// relative to this file (src dev) and the dist build.
|
|
76
|
+
const explicit = process.env.CLAUDE_FLOW_ROUTER_OPENROUTER_ALTS;
|
|
77
|
+
const candidates = [];
|
|
78
|
+
if (explicit)
|
|
79
|
+
candidates.push(explicit);
|
|
80
|
+
// Probe asset dirs without using import.meta.url so this stays compatible
|
|
81
|
+
// with both CJS and ESM consumers of the compiled .js.
|
|
82
|
+
candidates.push(join(process.cwd(), 'v3', '@claude-flow', 'cli', 'assets', 'model-router', 'openrouter-alts.json'));
|
|
83
|
+
candidates.push(join(process.cwd(), 'assets', 'model-router', 'openrouter-alts.json'));
|
|
84
|
+
for (const p of candidates) {
|
|
85
|
+
if (existsSync(p)) {
|
|
86
|
+
_altsCache = JSON.parse(readFileSync(p, 'utf8'));
|
|
87
|
+
return _altsCache;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Silent — alts are optional.
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/** Return the resolved provider + OpenRouter model for the picked tier. */
|
|
97
|
+
function resolveExecutionProvider(model) {
|
|
98
|
+
const explicit = process.env.CLAUDE_FLOW_ROUTER_PROVIDER?.toLowerCase();
|
|
99
|
+
// Default: anthropic unless explicitly set to openrouter, or OPENROUTER_API_KEY
|
|
100
|
+
// is the only credential present (matches agent-execute-core's selection).
|
|
101
|
+
const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
|
|
102
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
|
103
|
+
const wantOR = explicit === 'openrouter' ||
|
|
104
|
+
(!hasAnthropic && hasOpenRouter && explicit !== 'anthropic');
|
|
105
|
+
if (!wantOR)
|
|
106
|
+
return { provider: 'anthropic' };
|
|
107
|
+
const alts = loadOpenRouterAlts();
|
|
108
|
+
if (!alts)
|
|
109
|
+
return { provider: 'openrouter' }; // OR provider but no alt slug
|
|
110
|
+
const entry = alts.tiers[model];
|
|
111
|
+
if (!entry?.openrouter_alt)
|
|
112
|
+
return { provider: 'openrouter' };
|
|
113
|
+
return { provider: 'openrouter', openrouterModel: entry.openrouter_alt };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Model capabilities and characteristics
|
|
117
|
+
*/
|
|
118
|
+
export const MODEL_CAPABILITIES = {
|
|
119
|
+
haiku: {
|
|
120
|
+
maxComplexity: 0.4,
|
|
121
|
+
costMultiplier: 0.04, // ~25x cheaper than Opus
|
|
122
|
+
speedMultiplier: 3.0, // ~3x faster than Sonnet
|
|
123
|
+
description: 'Fast, cost-effective for simple tasks',
|
|
124
|
+
},
|
|
125
|
+
sonnet: {
|
|
126
|
+
maxComplexity: 0.7,
|
|
127
|
+
costMultiplier: 0.2, // ~5x cheaper than Opus
|
|
128
|
+
speedMultiplier: 1.5, // ~1.5x faster than Opus
|
|
129
|
+
description: 'Balanced capability and cost',
|
|
130
|
+
},
|
|
131
|
+
opus: {
|
|
132
|
+
maxComplexity: 1.0,
|
|
133
|
+
costMultiplier: 1.0, // Baseline
|
|
134
|
+
speedMultiplier: 1.0, // Baseline
|
|
135
|
+
description: 'Most capable for complex reasoning',
|
|
136
|
+
},
|
|
137
|
+
inherit: {
|
|
138
|
+
maxComplexity: 1.0,
|
|
139
|
+
costMultiplier: 1.0,
|
|
140
|
+
speedMultiplier: 1.0,
|
|
141
|
+
description: 'Use parent model selection',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Complexity indicators for task classification
|
|
146
|
+
*/
|
|
147
|
+
export const COMPLEXITY_INDICATORS = {
|
|
148
|
+
high: [
|
|
149
|
+
'architect', 'design', 'refactor', 'optimize', 'security', 'audit',
|
|
150
|
+
'complex', 'analyze', 'investigate', 'debug', 'performance', 'scale',
|
|
151
|
+
'distributed', 'concurrent', 'algorithm', 'system', 'integration',
|
|
152
|
+
],
|
|
153
|
+
medium: [
|
|
154
|
+
'implement', 'feature', 'add', 'update', 'modify', 'fix', 'test',
|
|
155
|
+
'review', 'validate', 'check', 'improve', 'enhance', 'extend',
|
|
156
|
+
],
|
|
157
|
+
low: [
|
|
158
|
+
'simple', 'typo', 'comment', 'format', 'rename', 'move', 'copy',
|
|
159
|
+
'delete', 'documentation', 'readme', 'config', 'version', 'bump',
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Cost-adjusted Bernoulli rewards for Thompson sampling updates. Higher
|
|
164
|
+
* reward when the right tier is chosen — Haiku-success > Sonnet-success >
|
|
165
|
+
* Opus-success because Opus-success on a simple task is wasteful even when
|
|
166
|
+
* the answer is correct. Escalations get partial credit at best (Sonnet) or
|
|
167
|
+
* zero (Haiku/Opus) since they signal the initial choice was wrong.
|
|
168
|
+
*/
|
|
169
|
+
const BANDIT_REWARDS = {
|
|
170
|
+
haiku: { success: 1.0, failure: 0.0, escalated: 0.0 },
|
|
171
|
+
sonnet: { success: 0.7, failure: 0.0, escalated: 0.1 },
|
|
172
|
+
opus: { success: 0.4, failure: 0.0, escalated: 0.0 },
|
|
173
|
+
inherit: { success: 0.5, failure: 0.0, escalated: 0.0 },
|
|
174
|
+
};
|
|
175
|
+
function complexityBucket(score) {
|
|
176
|
+
if (score < 0.4)
|
|
177
|
+
return 'low'; // haiku territory
|
|
178
|
+
if (score < 0.7)
|
|
179
|
+
return 'med'; // sonnet territory
|
|
180
|
+
return 'high'; // opus territory
|
|
181
|
+
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Beta Sampling for Thompson Sampling Bandit
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/**
|
|
186
|
+
* Standard normal sample via Box-Muller. Used by Marsaglia-Tsang Gamma.
|
|
187
|
+
* Module-local so the bandit doesn't pull in a heavy stats dep.
|
|
188
|
+
*/
|
|
189
|
+
function sampleStandardNormal() {
|
|
190
|
+
const u1 = Math.random() || 1e-12; // avoid log(0)
|
|
191
|
+
const u2 = Math.random();
|
|
192
|
+
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Sample from Gamma(shape α, scale=1). Marsaglia & Tsang (2000), with the
|
|
196
|
+
* standard "boost α<1 by α+1 then scale by U^(1/α)" trick for shape parameters
|
|
197
|
+
* smaller than 1. O(1) expected, no rejection-loop pathology in practice.
|
|
198
|
+
*/
|
|
199
|
+
function sampleGamma(alpha) {
|
|
200
|
+
if (alpha < 1) {
|
|
201
|
+
const u = Math.random() || 1e-12;
|
|
202
|
+
return sampleGamma(alpha + 1) * Math.pow(u, 1 / alpha);
|
|
203
|
+
}
|
|
204
|
+
const d = alpha - 1 / 3;
|
|
205
|
+
const c = 1 / Math.sqrt(9 * d);
|
|
206
|
+
while (true) {
|
|
207
|
+
let x;
|
|
208
|
+
let v;
|
|
209
|
+
do {
|
|
210
|
+
x = sampleStandardNormal();
|
|
211
|
+
v = 1 + c * x;
|
|
212
|
+
} while (v <= 0);
|
|
213
|
+
v = v * v * v;
|
|
214
|
+
const u = Math.random();
|
|
215
|
+
const xx = x * x;
|
|
216
|
+
if (u < 1 - 0.0331 * xx * xx)
|
|
217
|
+
return d * v;
|
|
218
|
+
if (Math.log(u) < 0.5 * xx + d * (1 - v + Math.log(v)))
|
|
219
|
+
return d * v;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Sample θ ~ Beta(α, β) via the identity Beta(α,β) = X / (X+Y) where
|
|
224
|
+
* X ~ Gamma(α), Y ~ Gamma(β). Returns the mean for degenerate α+β=0
|
|
225
|
+
* (shouldn't happen in practice but defensive).
|
|
226
|
+
*/
|
|
227
|
+
export function sampleBeta(alpha, beta) {
|
|
228
|
+
if (alpha <= 0 || beta <= 0)
|
|
229
|
+
return 0.5;
|
|
230
|
+
const x = sampleGamma(alpha);
|
|
231
|
+
const y = sampleGamma(beta);
|
|
232
|
+
const denom = x + y;
|
|
233
|
+
return denom > 0 ? x / denom : 0.5;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Default uniform priors (no prior knowledge). Beta(1,1) is the standard
|
|
237
|
+
* Bayesian-Bernoulli starting point — uniform over [0,1].
|
|
238
|
+
*/
|
|
239
|
+
function defaultBanditPriors() {
|
|
240
|
+
return {
|
|
241
|
+
haiku: { alpha: 1, beta: 1 },
|
|
242
|
+
sonnet: { alpha: 1, beta: 1 },
|
|
243
|
+
opus: { alpha: 1, beta: 1 },
|
|
244
|
+
inherit: { alpha: 1, beta: 1 },
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/** Uniform priors for every complexity bucket (cold start). */
|
|
248
|
+
function defaultBucketedPriors() {
|
|
249
|
+
return { low: defaultBanditPriors(), med: defaultBanditPriors(), high: defaultBanditPriors() };
|
|
250
|
+
}
|
|
251
|
+
/** ADR-149 — empty per-modelId shadow priors. Each bucket starts as `{}`; entries
|
|
252
|
+
* populate on first `recordOutcomeByModelId(task, modelId, outcome)` call. */
|
|
253
|
+
function defaultBucketedPriorsById() {
|
|
254
|
+
return { low: {}, med: {}, high: {} };
|
|
255
|
+
}
|
|
256
|
+
function clonePriors(p) {
|
|
257
|
+
return { haiku: { ...p.haiku }, sonnet: { ...p.sonnet }, opus: { ...p.opus }, inherit: { ...p.inherit } };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Forward-migrate a persisted `priors` field of any layout to the bucketed
|
|
261
|
+
* shape, never throwing (ADR-142):
|
|
262
|
+
* - missing/garbage → fresh uniform buckets
|
|
263
|
+
* - already bucketed (has `low.haiku`) → kept, backfilling any missing bucket
|
|
264
|
+
* - flat per-model (v1 bandit) → seed ALL buckets from it (lossless: prior
|
|
265
|
+
* learning becomes a shared starting point that then diverges per bucket)
|
|
266
|
+
*/
|
|
267
|
+
function migratePriors(p) {
|
|
268
|
+
if (!p || typeof p !== 'object')
|
|
269
|
+
return defaultBucketedPriors();
|
|
270
|
+
const obj = p;
|
|
271
|
+
if (obj.low && typeof obj.low === 'object' && obj.low.haiku) {
|
|
272
|
+
return {
|
|
273
|
+
low: obj.low,
|
|
274
|
+
med: obj.med ?? clonePriors(obj.low),
|
|
275
|
+
high: obj.high ?? clonePriors(obj.low),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (obj.haiku && typeof obj.haiku.alpha === 'number') {
|
|
279
|
+
const flat = obj;
|
|
280
|
+
return { low: clonePriors(flat), med: clonePriors(flat), high: clonePriors(flat) };
|
|
281
|
+
}
|
|
282
|
+
return defaultBucketedPriors();
|
|
283
|
+
}
|
|
284
|
+
// ============================================================================
|
|
285
|
+
// Default Configuration
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// #2250 — env override for maxUncertainty so callers can suppress the
|
|
288
|
+
// escalation without recompiling. Parsed once at module load; invalid /
|
|
289
|
+
// out-of-range values fall through to the default below.
|
|
290
|
+
function envMaxUncertainty() {
|
|
291
|
+
const raw = process.env.CLAUDE_FLOW_MAX_UNCERTAINTY;
|
|
292
|
+
if (!raw)
|
|
293
|
+
return undefined;
|
|
294
|
+
const n = Number(raw);
|
|
295
|
+
if (!Number.isFinite(n) || n < 0 || n > 1)
|
|
296
|
+
return undefined;
|
|
297
|
+
return n;
|
|
298
|
+
}
|
|
299
|
+
const DEFAULT_CONFIG = {
|
|
300
|
+
confidenceThreshold: 0.85,
|
|
301
|
+
maxUncertainty: envMaxUncertainty() ?? 0.15,
|
|
302
|
+
enableCircuitBreaker: true,
|
|
303
|
+
circuitBreakerThreshold: 5,
|
|
304
|
+
statePath: '.swarm/model-router-state.json',
|
|
305
|
+
autoSaveInterval: 1, // Save after every decision for CLI persistence
|
|
306
|
+
enableCostOptimization: true,
|
|
307
|
+
preferSpeed: true,
|
|
308
|
+
};
|
|
309
|
+
// Posterior mean of a Beta(α,β) prior — used by the #2250 escalation guard
|
|
310
|
+
// to detect when the bandit has *learned* the escalation target is worse.
|
|
311
|
+
function priorMean(p) {
|
|
312
|
+
return p.alpha / (p.alpha + p.beta);
|
|
313
|
+
}
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// Model Router Implementation
|
|
316
|
+
// ============================================================================
|
|
317
|
+
/**
|
|
318
|
+
* Intelligent Model Router using complexity-based routing
|
|
319
|
+
*/
|
|
320
|
+
export class ModelRouter {
|
|
321
|
+
config;
|
|
322
|
+
state;
|
|
323
|
+
decisionCount = 0;
|
|
324
|
+
consecutiveFailures = {
|
|
325
|
+
haiku: 0,
|
|
326
|
+
sonnet: 0,
|
|
327
|
+
opus: 0,
|
|
328
|
+
inherit: 0,
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* ADR-148 — in-memory counters surfaced via `getStats()` and read by the
|
|
332
|
+
* `hooks_intelligence_stats` MCP tool. Process-local, not persisted (these
|
|
333
|
+
* are operational metrics, not authoritative state — see ADR-074/086).
|
|
334
|
+
*/
|
|
335
|
+
routedByCounts = {
|
|
336
|
+
heuristic: 0,
|
|
337
|
+
'bandit-fallback': 0,
|
|
338
|
+
hybrid: 0,
|
|
339
|
+
};
|
|
340
|
+
neuralBackendCounts = {
|
|
341
|
+
'metaharness-knn': 0,
|
|
342
|
+
'metaharness-krr': 0,
|
|
343
|
+
fastgrnn: 0,
|
|
344
|
+
};
|
|
345
|
+
abDisagreements = 0;
|
|
346
|
+
abComparisons = 0;
|
|
347
|
+
constructor(config = {}) {
|
|
348
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
349
|
+
this.state = this.loadState();
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Route a task to the optimal model.
|
|
353
|
+
*
|
|
354
|
+
* When `embedding` is supplied and `CLAUDE_FLOW_ROUTER_NEURAL=1` is set,
|
|
355
|
+
* the cost-optimal neural backend (ADR-148) is consulted first; its
|
|
356
|
+
* decision is used when its `metBar` clears the configured quality bar
|
|
357
|
+
* and `routedBy` reflects which backend produced the decision. Otherwise
|
|
358
|
+
* the shipped heuristic + Thompson bandit path runs (byte-identical to
|
|
359
|
+
* the pre-ADR-148 behavior) and the result carries `routedBy:
|
|
360
|
+
* 'bandit-fallback'` (neural was enabled but declined) or
|
|
361
|
+
* `'heuristic'` (neural was disabled).
|
|
362
|
+
*/
|
|
363
|
+
async route(task, embedding) {
|
|
364
|
+
const startTime = performance.now();
|
|
365
|
+
// Analyze task complexity
|
|
366
|
+
const complexity = this.analyzeComplexity(task, embedding);
|
|
367
|
+
// ADR-148 — optional neural cost-optimal path (gated, opt-in).
|
|
368
|
+
//
|
|
369
|
+
// Hybrid math: we use the neural's per-candidate predicted quality as a
|
|
370
|
+
// weighted prior on the bandit's Beta(α,β) posterior, rather than
|
|
371
|
+
// overriding the bandit's pick. With weight=w (env-tunable, default 5)
|
|
372
|
+
// each candidate's Beta becomes Beta(α + q*w, β + (1-q)*w). Cold start
|
|
373
|
+
// → neural dominates (α+β ≈ 2 + w); many real outcomes → bandit
|
|
374
|
+
// dominates (α+β >> w). The persistent bandit state is unchanged.
|
|
375
|
+
//
|
|
376
|
+
// `bandit-fallback` is reserved for the case where neural was enabled
|
|
377
|
+
// but the backend returned no decision at all (artifact load failed,
|
|
378
|
+
// dim mismatch, etc.). In that case we route via pure bandit.
|
|
379
|
+
let neuralPrior = null;
|
|
380
|
+
let neuralBackend = undefined;
|
|
381
|
+
let neuralModelId = undefined;
|
|
382
|
+
let neuralDeclined = false;
|
|
383
|
+
// iter 46 — capture iter 45's ensemble-disagreement diagnostic so we can
|
|
384
|
+
// persist it to the trajectory below; downstream tuners (future) can
|
|
385
|
+
// analyze the distribution to recommend an iter 44 threshold.
|
|
386
|
+
let neuralEnsembleDisagreement = undefined;
|
|
387
|
+
if (embedding && embedding.length > 0 && process.env.CLAUDE_FLOW_ROUTER_NEURAL === '1') {
|
|
388
|
+
try {
|
|
389
|
+
const { tryCostOptimalRoute } = await loadNeuralRouter();
|
|
390
|
+
// ADR-149 iter 15 — pass the task's complexity bucket through so
|
|
391
|
+
// the neural-router's per-modelId Thompson (when gated on) can
|
|
392
|
+
// use the bucket-specific prior instead of marginalising.
|
|
393
|
+
const nr = await tryCostOptimalRoute(embedding, { complexityBucket: complexityBucket(complexity.score) });
|
|
394
|
+
if (nr) {
|
|
395
|
+
// ADR-149: capture the concrete picked model id (the cost-optimal
|
|
396
|
+
// pick across all candidates, not just within a tier).
|
|
397
|
+
neuralModelId = nr.modelId;
|
|
398
|
+
neuralEnsembleDisagreement = nr.ensembleDisagreement;
|
|
399
|
+
// Build per-tier quality map for the bandit prior. The neural
|
|
400
|
+
// backend returns per-model alternatives (e.g. 7 distinct slugs);
|
|
401
|
+
// for the Beta-prior bump we collapse to tier by taking the MAX
|
|
402
|
+
// predicted quality within each tier — the tier whose best
|
|
403
|
+
// candidate is best overall is the tier the bandit should favor.
|
|
404
|
+
// Then we apply rank-based scaling [1.0, 0.5, 0.2] across the
|
|
405
|
+
// three tiers so the Beta bumps are well-separated (raw KRR
|
|
406
|
+
// outputs are typically in a narrow band — see ADR-148 phase 1).
|
|
407
|
+
const sorted = [...nr.alternatives].sort((a, b) => b.predictedQuality - a.predictedQuality);
|
|
408
|
+
if (!sorted.find(s => s.model === nr.model)) {
|
|
409
|
+
sorted.unshift({ model: nr.model, modelId: nr.modelId, predictedQuality: nr.predictedQuality, costPerMTok: 0 });
|
|
410
|
+
}
|
|
411
|
+
// Collapse per-model alternatives → per-tier MAX quality, preserving
|
|
412
|
+
// first-occurrence order (which is best-quality first thanks to the sort).
|
|
413
|
+
const tierMaxQ = {};
|
|
414
|
+
const tierOrder = [];
|
|
415
|
+
for (const a of sorted) {
|
|
416
|
+
if (tierMaxQ[a.model] === undefined) {
|
|
417
|
+
tierMaxQ[a.model] = a.predictedQuality;
|
|
418
|
+
tierOrder.push(a.model);
|
|
419
|
+
}
|
|
420
|
+
else if (a.predictedQuality > (tierMaxQ[a.model] ?? 0)) {
|
|
421
|
+
tierMaxQ[a.model] = a.predictedQuality;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const rankQ = [1.0, 0.5, 0.2];
|
|
425
|
+
const qualities = {};
|
|
426
|
+
for (let i = 0; i < tierOrder.length && i < rankQ.length; i++) {
|
|
427
|
+
qualities[tierOrder[i]] = rankQ[i];
|
|
428
|
+
}
|
|
429
|
+
const weight = parseFloat(process.env.CLAUDE_FLOW_ROUTER_NEURAL_WEIGHT ?? '5') || 5;
|
|
430
|
+
neuralPrior = { qualities, weight };
|
|
431
|
+
neuralBackend = nr.routedBy;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
neuralDeclined = true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Silent — neural path is best-effort.
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const scores = this.computeModelScores(complexity);
|
|
442
|
+
const adjustedScores = this.applyCircuitBreaker(scores);
|
|
443
|
+
// A/B mode: compute the pure-bandit pick alongside the hybrid pick so
|
|
444
|
+
// we can log disagreement. Both samples are drawn from the same Beta
|
|
445
|
+
// posteriors but with/without the neural prior bump — useful for
|
|
446
|
+
// measuring real lift before flipping defaults.
|
|
447
|
+
//
|
|
448
|
+
// iter 37 — TWO knobs:
|
|
449
|
+
// CLAUDE_FLOW_ROUTER_AB=1 → A/B on every call (orig)
|
|
450
|
+
// CLAUDE_FLOW_ROUTER_AB_SAMPLE_RATE=<0..1> → A/B on a sampled subset,
|
|
451
|
+
// keyed deterministically by task_hash so the same task always falls
|
|
452
|
+
// in or out of the sample. Lets production accumulate ab_pair data
|
|
453
|
+
// passively at low overhead (e.g. 0.05 = 5% of decisions).
|
|
454
|
+
// Both set → SAMPLE_RATE wins (more specific).
|
|
455
|
+
const rateRaw = process.env.CLAUDE_FLOW_ROUTER_AB_SAMPLE_RATE;
|
|
456
|
+
const rate = rateRaw ? Math.max(0, Math.min(1, parseFloat(rateRaw) || 0)) : 0;
|
|
457
|
+
const allOn = process.env.CLAUDE_FLOW_ROUTER_AB === '1';
|
|
458
|
+
let inSample = false;
|
|
459
|
+
if (rate > 0) {
|
|
460
|
+
// Deterministic sample by FNV-1a-32 of the task text. Same task always
|
|
461
|
+
// gets the same A/B decision across re-runs (reproducible tests, stable
|
|
462
|
+
// population over time). Inlined to avoid the async router-trajectory
|
|
463
|
+
// import on the hot path.
|
|
464
|
+
let h = 0x811c9dc5 >>> 0;
|
|
465
|
+
for (let i = 0; i < task.length; i++) {
|
|
466
|
+
h ^= task.charCodeAt(i);
|
|
467
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
468
|
+
}
|
|
469
|
+
inSample = (h % 10000) / 10000 < rate;
|
|
470
|
+
}
|
|
471
|
+
const abEnabled = (rate > 0 ? inSample : allOn) && neuralPrior !== null;
|
|
472
|
+
let abPair;
|
|
473
|
+
if (abEnabled) {
|
|
474
|
+
// Pre-compute the bandit-only pick. Single extra Thompson sample per
|
|
475
|
+
// call — independent draws are noisy but cheap (~3 Beta samples ≈
|
|
476
|
+
// <10 μs). For a per-call disagreement signal one draw is fine; over
|
|
477
|
+
// N decisions the rate stabilises.
|
|
478
|
+
const banditOnly = this.selectModel(adjustedScores, complexity.score, undefined);
|
|
479
|
+
const picked = this.selectModel(adjustedScores, complexity.score, neuralPrior ?? undefined);
|
|
480
|
+
abPair = {
|
|
481
|
+
bandit_pick: banditOnly.model,
|
|
482
|
+
hybrid_pick: picked.model,
|
|
483
|
+
disagree: banditOnly.model !== picked.model,
|
|
484
|
+
};
|
|
485
|
+
this.abComparisons++;
|
|
486
|
+
if (abPair.disagree)
|
|
487
|
+
this.abDisagreements++;
|
|
488
|
+
// ADR-150 iter 12 — opt-in parallel-decision recorder. No-op when
|
|
489
|
+
// CLAUDE_FLOW_ROUTER_PARALLEL_LOG is unset (the default), so this
|
|
490
|
+
// adds zero overhead to the default routing path. Fire-and-forget
|
|
491
|
+
// dynamic-import + recordPair; never blocks the route() return.
|
|
492
|
+
if (process.env.CLAUDE_FLOW_ROUTER_PARALLEL_LOG === '1') {
|
|
493
|
+
loadParallelRecorder().then((mod) => {
|
|
494
|
+
try {
|
|
495
|
+
mod.recordPair({
|
|
496
|
+
task,
|
|
497
|
+
bandit: {
|
|
498
|
+
pick: banditOnly.model,
|
|
499
|
+
predictedQuality: banditOnly.confidence,
|
|
500
|
+
predictedCostUsd: 0, // bandit doesn't price; analyzer uses outcome
|
|
501
|
+
backend: 'thompson-bandit',
|
|
502
|
+
},
|
|
503
|
+
ser: {
|
|
504
|
+
pick: picked.model,
|
|
505
|
+
predictedQuality: picked.confidence,
|
|
506
|
+
predictedCostUsd: 0,
|
|
507
|
+
backend: neuralPrior ? 'metaharness-router-hybrid' : 'bandit-only',
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
// ADR-150 rule #3 — never throw from the routing path.
|
|
513
|
+
}
|
|
514
|
+
}).catch(() => { });
|
|
515
|
+
}
|
|
516
|
+
var pickedForResult = picked; // eslint-disable-line no-var
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
var pickedForResult = this.selectModel(adjustedScores, complexity.score, neuralPrior ?? undefined); // eslint-disable-line no-var
|
|
520
|
+
}
|
|
521
|
+
const model = pickedForResult.model;
|
|
522
|
+
const confidence = pickedForResult.confidence;
|
|
523
|
+
const uncertainty = pickedForResult.uncertainty;
|
|
524
|
+
const routedBy = neuralPrior
|
|
525
|
+
? 'hybrid'
|
|
526
|
+
: neuralDeclined ? 'bandit-fallback' : 'heuristic';
|
|
527
|
+
const inferenceTimeUs = (performance.now() - startTime) * 1000;
|
|
528
|
+
// ADR-148 phase 2 — resolve OpenRouter alt for the picked tier when the
|
|
529
|
+
// execution provider is OpenRouter. Free of side effects on the bandit;
|
|
530
|
+
// purely advisory metadata for downstream agent-execute-core.
|
|
531
|
+
//
|
|
532
|
+
// ADR-149 — when the neural backend returned a concrete `modelId` that
|
|
533
|
+
// is NOT an Anthropic slug, we override the provider to 'openrouter'
|
|
534
|
+
// and use the modelId as the openrouterModel. Otherwise consumers
|
|
535
|
+
// dispatching on `model` would call the Anthropic SDK for a model id
|
|
536
|
+
// the SDK can't reach (e.g. 'inclusionai/ling-2.6-flash'), losing the
|
|
537
|
+
// cost-optimal pick. When the neural picked an Anthropic id we keep
|
|
538
|
+
// the standard provider resolution since the Anthropic SDK can serve it.
|
|
539
|
+
let exec = resolveExecutionProvider(model);
|
|
540
|
+
if (neuralModelId && !neuralModelId.startsWith('anthropic/')) {
|
|
541
|
+
exec = { provider: 'openrouter', openrouterModel: neuralModelId };
|
|
542
|
+
}
|
|
543
|
+
// Build result
|
|
544
|
+
const result = {
|
|
545
|
+
model,
|
|
546
|
+
confidence,
|
|
547
|
+
uncertainty,
|
|
548
|
+
complexity: complexity.score,
|
|
549
|
+
reasoning: this.buildReasoning(model, complexity, confidence),
|
|
550
|
+
alternatives: Object.entries(adjustedScores)
|
|
551
|
+
.filter(([m]) => m !== model)
|
|
552
|
+
.map(([m, score]) => ({ model: m, score }))
|
|
553
|
+
.sort((a, b) => b.score - a.score),
|
|
554
|
+
inferenceTimeUs,
|
|
555
|
+
costMultiplier: MODEL_CAPABILITIES[model].costMultiplier,
|
|
556
|
+
routedBy,
|
|
557
|
+
...(neuralBackend ? { neuralBackend } : {}),
|
|
558
|
+
provider: exec.provider,
|
|
559
|
+
...(exec.openrouterModel ? { openrouterModel: exec.openrouterModel } : {}),
|
|
560
|
+
// ADR-149: surface the concrete neural pick when present. Prefer the
|
|
561
|
+
// explicit OpenRouter alt (resolveExecutionProvider) for execution,
|
|
562
|
+
// but expose the model id the neural backend chose so observers and
|
|
563
|
+
// consumers can see the cost-optimal decision.
|
|
564
|
+
...(neuralModelId ? { modelId: neuralModelId } : {}),
|
|
565
|
+
};
|
|
566
|
+
// Track decision (in-memory bandit state)
|
|
567
|
+
this.trackDecision(task, result);
|
|
568
|
+
// ADR-148 — opt-in DRACO-shaped trajectory collection
|
|
569
|
+
if (process.env.CLAUDE_FLOW_ROUTER_TRAJECTORY === '1') {
|
|
570
|
+
try {
|
|
571
|
+
const { recordDecision } = await loadTrajectoryRecorder();
|
|
572
|
+
recordDecision({
|
|
573
|
+
task, embedding, complexity: complexity.score,
|
|
574
|
+
model, confidence, uncertainty, routedBy,
|
|
575
|
+
neuralBackend, abPair,
|
|
576
|
+
provider: exec.provider,
|
|
577
|
+
openrouterModel: exec.openrouterModel,
|
|
578
|
+
ensembleDisagreement: neuralEnsembleDisagreement,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
// Silent — trajectory recording must never break routing.
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Analyze task complexity
|
|
589
|
+
*/
|
|
590
|
+
analyzeComplexity(task, embedding) {
|
|
591
|
+
const taskLower = task.toLowerCase();
|
|
592
|
+
const words = taskLower.split(/\s+/);
|
|
593
|
+
// Find complexity indicators
|
|
594
|
+
const indicators = {
|
|
595
|
+
high: COMPLEXITY_INDICATORS.high.filter(ind => taskLower.includes(ind)),
|
|
596
|
+
medium: COMPLEXITY_INDICATORS.medium.filter(ind => taskLower.includes(ind)),
|
|
597
|
+
low: COMPLEXITY_INDICATORS.low.filter(ind => taskLower.includes(ind)),
|
|
598
|
+
};
|
|
599
|
+
// Compute feature scores
|
|
600
|
+
const lexicalComplexity = this.computeLexicalComplexity(task);
|
|
601
|
+
const semanticDepth = this.computeSemanticDepth(indicators, embedding);
|
|
602
|
+
const taskScope = this.computeTaskScope(task, words);
|
|
603
|
+
const uncertaintyLevel = this.computeUncertaintyLevel(task);
|
|
604
|
+
// Weighted combination
|
|
605
|
+
const score = Math.min(1, Math.max(0, lexicalComplexity * 0.2 +
|
|
606
|
+
semanticDepth * 0.35 +
|
|
607
|
+
taskScope * 0.25 +
|
|
608
|
+
uncertaintyLevel * 0.2));
|
|
609
|
+
return {
|
|
610
|
+
score,
|
|
611
|
+
indicators,
|
|
612
|
+
features: {
|
|
613
|
+
lexicalComplexity,
|
|
614
|
+
semanticDepth,
|
|
615
|
+
taskScope,
|
|
616
|
+
uncertaintyLevel,
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Compute lexical complexity from text features
|
|
622
|
+
*/
|
|
623
|
+
computeLexicalComplexity(task) {
|
|
624
|
+
const words = task.split(/\s+/);
|
|
625
|
+
const avgWordLength = words.reduce((sum, w) => sum + w.length, 0) / Math.max(1, words.length);
|
|
626
|
+
const sentenceLength = words.length;
|
|
627
|
+
// Normalize: longer sentences with longer words = more complex
|
|
628
|
+
const lengthScore = Math.min(1, sentenceLength / 50);
|
|
629
|
+
const wordScore = Math.min(1, (avgWordLength - 3) / 7); // 3-10 char words
|
|
630
|
+
return lengthScore * 0.4 + wordScore * 0.6;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Compute semantic depth from indicators and embedding
|
|
634
|
+
*/
|
|
635
|
+
computeSemanticDepth(indicators, embedding) {
|
|
636
|
+
// Weight by indicator presence
|
|
637
|
+
const highWeight = indicators.high.length * 0.3;
|
|
638
|
+
const mediumWeight = indicators.medium.length * 0.15;
|
|
639
|
+
const lowWeight = indicators.low.length * -0.1;
|
|
640
|
+
let baseScore = Math.min(1, Math.max(0, 0.3 + highWeight + mediumWeight + lowWeight));
|
|
641
|
+
// Boost with embedding variance if available
|
|
642
|
+
if (embedding && embedding.length > 0) {
|
|
643
|
+
const mean = embedding.reduce((a, b) => a + b, 0) / embedding.length;
|
|
644
|
+
const variance = embedding.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / embedding.length;
|
|
645
|
+
// Higher variance suggests more nuanced semantics
|
|
646
|
+
baseScore = baseScore * 0.7 + Math.min(1, variance * 10) * 0.3;
|
|
647
|
+
}
|
|
648
|
+
return baseScore;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Compute task scope from content analysis
|
|
652
|
+
*/
|
|
653
|
+
computeTaskScope(task, words) {
|
|
654
|
+
// Multi-file indicators
|
|
655
|
+
const multiFilePatterns = [
|
|
656
|
+
/multiple files?/i, /across.*modules?/i, /refactor.*codebase/i,
|
|
657
|
+
/all.*files/i, /entire.*project/i, /system.*wide/i,
|
|
658
|
+
];
|
|
659
|
+
const hasMultiFile = multiFilePatterns.some(p => p.test(task)) ? 0.4 : 0;
|
|
660
|
+
// Code generation indicators
|
|
661
|
+
const codeGenPatterns = [
|
|
662
|
+
/implement/i, /create.*feature/i, /build.*system/i,
|
|
663
|
+
/design.*api/i, /write.*tests/i, /add.*functionality/i,
|
|
664
|
+
];
|
|
665
|
+
const hasCodeGen = codeGenPatterns.some(p => p.test(task)) ? 0.3 : 0;
|
|
666
|
+
// Word count contribution
|
|
667
|
+
const wordCountScore = Math.min(0.3, words.length / 100);
|
|
668
|
+
return hasMultiFile + hasCodeGen + wordCountScore;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Compute uncertainty level from task phrasing
|
|
672
|
+
*/
|
|
673
|
+
computeUncertaintyLevel(task) {
|
|
674
|
+
const uncertainPatterns = [
|
|
675
|
+
/not sure/i, /might/i, /maybe/i, /possibly/i, /investigate/i,
|
|
676
|
+
/figure out/i, /unclear/i, /unknown/i, /debug/i, /strange/i,
|
|
677
|
+
/weird/i, /issue/i, /problem/i, /error/i, /bug/i,
|
|
678
|
+
];
|
|
679
|
+
const matchCount = uncertainPatterns.filter(p => p.test(task)).length;
|
|
680
|
+
return Math.min(1, matchCount * 0.2);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Compute scores for each model
|
|
684
|
+
*/
|
|
685
|
+
computeModelScores(complexity) {
|
|
686
|
+
const { score } = complexity;
|
|
687
|
+
// Base scoring: inverse relationship with complexity
|
|
688
|
+
// Low complexity → haiku scores high
|
|
689
|
+
// High complexity → opus scores high
|
|
690
|
+
return {
|
|
691
|
+
haiku: Math.max(0, 1 - score * 2), // Drops off quickly as complexity rises
|
|
692
|
+
sonnet: 1 - Math.abs(score - 0.5) * 2, // Peaks at medium complexity
|
|
693
|
+
opus: Math.min(1, score * 1.5), // Rises with complexity
|
|
694
|
+
inherit: 0.1, // Low baseline unless explicitly needed
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Apply circuit breaker adjustments
|
|
699
|
+
*/
|
|
700
|
+
applyCircuitBreaker(scores) {
|
|
701
|
+
if (!this.config.enableCircuitBreaker) {
|
|
702
|
+
return scores;
|
|
703
|
+
}
|
|
704
|
+
const adjusted = { ...scores };
|
|
705
|
+
for (const model of Object.keys(adjusted)) {
|
|
706
|
+
if (this.consecutiveFailures[model] >= this.config.circuitBreakerThreshold) {
|
|
707
|
+
// Circuit is open - heavily penalize this model
|
|
708
|
+
adjusted[model] *= 0.1;
|
|
709
|
+
}
|
|
710
|
+
else if (this.consecutiveFailures[model] > 0) {
|
|
711
|
+
// Partial penalty for recent failures
|
|
712
|
+
adjusted[model] *= 1 - (this.consecutiveFailures[model] / this.config.circuitBreakerThreshold) * 0.5;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return adjusted;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Select the best model from scores. Uses Thompson sampling (#1772):
|
|
719
|
+
* each model's deterministic complexity score is multiplied by a draw
|
|
720
|
+
* θ_m ~ Beta(α_m, β_m) from its bandit prior. Models with strong empirical
|
|
721
|
+
* track records get sampled higher; models with poor outcomes get sampled
|
|
722
|
+
* lower; the system auto-corrects against tier overuse without manual
|
|
723
|
+
* threshold tuning. Beta(1,1) = uniform on cold start so behavior matches
|
|
724
|
+
* the prior deterministic router until outcomes accumulate.
|
|
725
|
+
*/
|
|
726
|
+
selectModel(scores, complexityScore,
|
|
727
|
+
/**
|
|
728
|
+
* Optional neural prior (ADR-148 hybrid math). When supplied, each
|
|
729
|
+
* candidate's Beta(α, β) prior is perturbed by `weight` pseudo-counts of
|
|
730
|
+
* the neural's predicted quality before sampling. Cold start → neural
|
|
731
|
+
* dominates; many real outcomes → bandit dominates. The persistent
|
|
732
|
+
* bandit state is NOT modified — this is a per-call posterior bump only.
|
|
733
|
+
*/
|
|
734
|
+
neuralPrior) {
|
|
735
|
+
// Thompson sampling: combine deterministic score with bandit posterior,
|
|
736
|
+
// keyed by complexity bucket (ADR-142) so learning is task-type-local.
|
|
737
|
+
const bucketed = this.state.priors ?? defaultBucketedPriors();
|
|
738
|
+
const priors = bucketed[complexityBucket(complexityScore)] ?? defaultBanditPriors();
|
|
739
|
+
// Apply the optional neural prior: Beta(α + q*w, β + (1-q)*w). Per-call,
|
|
740
|
+
// does not persist. Clamp `q` into [0, 1] so a bogus backend reading
|
|
741
|
+
// cannot push the prior into invalid territory.
|
|
742
|
+
const bump = (a, b, q, w) => {
|
|
743
|
+
if (q === undefined || w <= 0)
|
|
744
|
+
return { alpha: a, beta: b };
|
|
745
|
+
const clamped = Math.min(1, Math.max(0, q));
|
|
746
|
+
return { alpha: a + clamped * w, beta: b + (1 - clamped) * w };
|
|
747
|
+
};
|
|
748
|
+
const w = neuralPrior?.weight ?? 0;
|
|
749
|
+
const ph = bump(priors.haiku.alpha, priors.haiku.beta, neuralPrior?.qualities.haiku, w);
|
|
750
|
+
const ps = bump(priors.sonnet.alpha, priors.sonnet.beta, neuralPrior?.qualities.sonnet, w);
|
|
751
|
+
const po = bump(priors.opus.alpha, priors.opus.beta, neuralPrior?.qualities.opus, w);
|
|
752
|
+
const sampledScores = {
|
|
753
|
+
haiku: scores.haiku * sampleBeta(ph.alpha, ph.beta),
|
|
754
|
+
sonnet: scores.sonnet * sampleBeta(ps.alpha, ps.beta),
|
|
755
|
+
opus: scores.opus * sampleBeta(po.alpha, po.beta),
|
|
756
|
+
inherit: scores.inherit, // not bandit-controlled
|
|
757
|
+
};
|
|
758
|
+
// Get sorted models by sampled score (drops 'inherit' from selection)
|
|
759
|
+
const sorted = Object.entries(sampledScores)
|
|
760
|
+
.filter(([m]) => m !== 'inherit')
|
|
761
|
+
.sort((a, b) => b[1] - a[1]);
|
|
762
|
+
const [bestModel, bestScore] = sorted[0];
|
|
763
|
+
const [, secondScore] = sorted[1] || ['sonnet', 0];
|
|
764
|
+
// Confidence is how much better the best is vs second
|
|
765
|
+
const confidence = bestScore > 0 ? Math.min(1, bestScore / (bestScore + secondScore + 0.01)) : 0.5;
|
|
766
|
+
// Uncertainty based on score spread and complexity
|
|
767
|
+
const scoreSpread = bestScore - secondScore;
|
|
768
|
+
const uncertainty = Math.max(0, 1 - scoreSpread - confidence * 0.5);
|
|
769
|
+
// Escalate if uncertainty is too high.
|
|
770
|
+
//
|
|
771
|
+
// #2250 — `uncertainty` here is structurally ~0.6-0.7 for low-complexity
|
|
772
|
+
// tasks (formula: `1 - scoreSpread - confidence*0.5`, where `scoreSpread`
|
|
773
|
+
// is a raw 0-1 difference between bandit-sampled scores that rarely
|
|
774
|
+
// exceeds 0.1). With `maxUncertainty = 0.15` the gate fires on
|
|
775
|
+
// ~every trivial route, promoting `sonnet→opus` and `haiku→sonnet`
|
|
776
|
+
// even when the Thompson sampler has *already* suppressed the higher
|
|
777
|
+
// tier (e.g. opus `Beta(3.8, 17.2)`, mean ≈ 0.18). The learned
|
|
778
|
+
// suppression is computed and then discarded one line later.
|
|
779
|
+
//
|
|
780
|
+
// Guard: skip the escalation when EITHER (a) the bandit has confidently
|
|
781
|
+
// learned the escalation target performs WORSE than the selected model,
|
|
782
|
+
// OR (b) the bandit has a confident, decent posterior on the selected
|
|
783
|
+
// model — i.e. the Thompson sampler picked this tier on real evidence,
|
|
784
|
+
// not a coin flip. Cold-start priors (Beta(1,1), α+β=2, mean=0.5) fail
|
|
785
|
+
// both checks, so unlearned routers still escalate as before.
|
|
786
|
+
let model = bestModel;
|
|
787
|
+
if (uncertainty > this.config.maxUncertainty && bestModel !== 'opus') {
|
|
788
|
+
const escalateTo = bestModel === 'haiku' ? 'sonnet' : 'opus';
|
|
789
|
+
const selectedMean = priorMean(priors[bestModel]);
|
|
790
|
+
const targetMean = priorMean(priors[escalateTo]);
|
|
791
|
+
const targetWorse = targetMean < selectedMean - 0.10;
|
|
792
|
+
// Treat the selected model as trusted once the bandit has accumulated
|
|
793
|
+
// ~5 effective observations AND its mean is at least 0.45 (neutral-or-
|
|
794
|
+
// better). Both thresholds chosen to keep cold-start behavior identical
|
|
795
|
+
// while honoring any non-trivial learning.
|
|
796
|
+
const selectedSamples = priors[bestModel].alpha + priors[bestModel].beta;
|
|
797
|
+
const selectedTrusted = selectedSamples >= 5 && selectedMean >= 0.45;
|
|
798
|
+
// ADR-148 — additional trust path: when the neural prior agrees with
|
|
799
|
+
// the bandit's pick (i.e. neuralPrior.qualities[bestModel] is the
|
|
800
|
+
// highest), the neural backend's signal is treated as a vote of
|
|
801
|
+
// confidence that lets us skip escalation. Without this, cold-start
|
|
802
|
+
// installations stay stuck in the structurally-high-uncertainty
|
|
803
|
+
// regime (#2250) and the neural's clear preference never reaches
|
|
804
|
+
// the final pick. The neural prior's top is computed as the max
|
|
805
|
+
// quality over the supplied candidates.
|
|
806
|
+
let neuralEndorsesPick = false;
|
|
807
|
+
if (neuralPrior) {
|
|
808
|
+
let bestNeuralQ = -1;
|
|
809
|
+
let neuralTop = null;
|
|
810
|
+
for (const [m, q] of Object.entries(neuralPrior.qualities)) {
|
|
811
|
+
if (q > bestNeuralQ) {
|
|
812
|
+
bestNeuralQ = q;
|
|
813
|
+
neuralTop = m;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (neuralTop === bestModel)
|
|
817
|
+
neuralEndorsesPick = true;
|
|
818
|
+
}
|
|
819
|
+
if (!targetWorse && !selectedTrusted && !neuralEndorsesPick) {
|
|
820
|
+
model = escalateTo;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return { model, confidence, uncertainty };
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Build human-readable reasoning
|
|
827
|
+
*/
|
|
828
|
+
buildReasoning(model, complexity, confidence) {
|
|
829
|
+
const parts = [];
|
|
830
|
+
parts.push(`Complexity: ${(complexity.score * 100).toFixed(0)}%`);
|
|
831
|
+
if (complexity.indicators.high.length > 0) {
|
|
832
|
+
parts.push(`High-complexity indicators: ${complexity.indicators.high.join(', ')}`);
|
|
833
|
+
}
|
|
834
|
+
parts.push(`Confidence: ${(confidence * 100).toFixed(0)}%`);
|
|
835
|
+
parts.push(`Model: ${model} - ${MODEL_CAPABILITIES[model].description}`);
|
|
836
|
+
if (this.config.enableCostOptimization) {
|
|
837
|
+
parts.push(`Cost: ${MODEL_CAPABILITIES[model].costMultiplier}x baseline`);
|
|
838
|
+
}
|
|
839
|
+
return parts.join(' | ');
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Track routing decision for learning
|
|
843
|
+
*/
|
|
844
|
+
trackDecision(task, result) {
|
|
845
|
+
this.decisionCount++;
|
|
846
|
+
this.state.totalDecisions++;
|
|
847
|
+
this.state.modelDistribution[result.model] =
|
|
848
|
+
(this.state.modelDistribution[result.model] || 0) + 1;
|
|
849
|
+
// ADR-148 — operational counters for hooks_intelligence_stats.
|
|
850
|
+
this.routedByCounts[result.routedBy]++;
|
|
851
|
+
if (result.neuralBackend)
|
|
852
|
+
this.neuralBackendCounts[result.neuralBackend]++;
|
|
853
|
+
// Update running averages
|
|
854
|
+
const n = this.state.totalDecisions;
|
|
855
|
+
this.state.avgComplexity =
|
|
856
|
+
(this.state.avgComplexity * (n - 1) + result.complexity) / n;
|
|
857
|
+
this.state.avgConfidence =
|
|
858
|
+
(this.state.avgConfidence * (n - 1) + result.confidence) / n;
|
|
859
|
+
// Auto-save periodically
|
|
860
|
+
if (this.decisionCount % this.config.autoSaveInterval === 0) {
|
|
861
|
+
this.saveState();
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Record outcome for learning
|
|
866
|
+
*/
|
|
867
|
+
recordOutcome(task, model, outcome) {
|
|
868
|
+
// Update circuit breaker state
|
|
869
|
+
if (outcome === 'failure') {
|
|
870
|
+
this.consecutiveFailures[model]++;
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
this.consecutiveFailures[model] = 0;
|
|
874
|
+
}
|
|
875
|
+
// Re-derive this task's complexity bucket from the task string (the MCP
|
|
876
|
+
// outcome payload carries no complexity), using the SAME analyzeComplexity
|
|
877
|
+
// path route() uses so record-time and select-time buckets match.
|
|
878
|
+
const taskScore = this.analyzeComplexity(task).score;
|
|
879
|
+
const bucket = complexityBucket(taskScore);
|
|
880
|
+
// Track in history (record THIS task's score, not the running average)
|
|
881
|
+
this.state.learningHistory.push({
|
|
882
|
+
task: task.slice(0, 100),
|
|
883
|
+
model,
|
|
884
|
+
complexity: taskScore,
|
|
885
|
+
outcome,
|
|
886
|
+
timestamp: new Date().toISOString(),
|
|
887
|
+
});
|
|
888
|
+
// Keep history bounded
|
|
889
|
+
if (this.state.learningHistory.length > 100) {
|
|
890
|
+
this.state.learningHistory = this.state.learningHistory.slice(-100);
|
|
891
|
+
}
|
|
892
|
+
if (outcome === 'failure') {
|
|
893
|
+
this.state.circuitBreakerTrips++;
|
|
894
|
+
}
|
|
895
|
+
// Thompson sampling update (#1772): cost-adjusted Bernoulli reward.
|
|
896
|
+
// Haiku-success > Sonnet-success > Opus-success (Opus on simple tasks
|
|
897
|
+
// is wasteful even when correct). Failure/escalation always β++.
|
|
898
|
+
if (!this.state.priors)
|
|
899
|
+
this.state.priors = defaultBucketedPriors();
|
|
900
|
+
const bp = this.state.priors[bucket] ?? (this.state.priors[bucket] = defaultBanditPriors());
|
|
901
|
+
const reward = BANDIT_REWARDS[model]?.[outcome] ?? 0.5;
|
|
902
|
+
bp[model].alpha += reward;
|
|
903
|
+
bp[model].beta += 1 - reward;
|
|
904
|
+
this.saveState();
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* ADR-149 — record an outcome keyed by the CONCRETE model id (e.g.
|
|
908
|
+
* 'inclusionai/ling-2.6-flash') rather than the tier label. Updates the
|
|
909
|
+
* shadow `priorsById` state without affecting `priors` (tier priors).
|
|
910
|
+
*
|
|
911
|
+
* This is the per-model learning signal the bandit needs to eventually
|
|
912
|
+
* distinguish GPT-4.1 from Sonnet within the 'sonnet' tier. Selection
|
|
913
|
+
* currently still uses tier priors; this state accumulates so a future
|
|
914
|
+
* refactor can switch the selector over once there's enough data.
|
|
915
|
+
*
|
|
916
|
+
* Cost-adjusted reward semantics: cheap models get the highest reward on
|
|
917
|
+
* success (their successes are most cost-efficient). We map modelId to
|
|
918
|
+
* its closest tier for the reward table — the routing math doesn't have
|
|
919
|
+
* a per-modelId reward configuration yet.
|
|
920
|
+
*/
|
|
921
|
+
recordOutcomeByModelId(task, modelId, outcome) {
|
|
922
|
+
if (!modelId || typeof modelId !== 'string')
|
|
923
|
+
return;
|
|
924
|
+
const taskScore = this.analyzeComplexity(task).score;
|
|
925
|
+
const bucket = complexityBucket(taskScore);
|
|
926
|
+
if (!this.state.priorsById)
|
|
927
|
+
this.state.priorsById = defaultBucketedPriorsById();
|
|
928
|
+
let perBucket = this.state.priorsById[bucket];
|
|
929
|
+
if (!perBucket) {
|
|
930
|
+
perBucket = {};
|
|
931
|
+
this.state.priorsById[bucket] = perBucket;
|
|
932
|
+
}
|
|
933
|
+
if (!perBucket[modelId])
|
|
934
|
+
perBucket[modelId] = { alpha: 1, beta: 1 };
|
|
935
|
+
// Reward proxy: derive a tier-equivalent for cost weighting. Substring
|
|
936
|
+
// match keeps it cheap and accurate for the candidates in the registry.
|
|
937
|
+
const id = modelId.toLowerCase();
|
|
938
|
+
const tierProxy = id.includes('haiku') || id.includes('ling-') || id.includes('flash-lite')
|
|
939
|
+
|| id.includes('nemotron-nano') || id.includes('ministral')
|
|
940
|
+
|| id.includes('llama-3.2-3b') || id.includes('llama-3.1-8b')
|
|
941
|
+
? 'haiku'
|
|
942
|
+
: id.includes('opus') ? 'opus' : 'sonnet';
|
|
943
|
+
const reward = BANDIT_REWARDS[tierProxy]?.[outcome] ?? 0.5;
|
|
944
|
+
perBucket[modelId].alpha += reward;
|
|
945
|
+
perBucket[modelId].beta += 1 - reward;
|
|
946
|
+
// Bump the schema version on first per-modelId write so downstream
|
|
947
|
+
// tooling can see v3 was reached (the version field stays at 3 once set).
|
|
948
|
+
if ((this.state.version ?? 0) < 3)
|
|
949
|
+
this.state.version = 3;
|
|
950
|
+
this.saveState();
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Get router statistics
|
|
954
|
+
*/
|
|
955
|
+
getStats() {
|
|
956
|
+
return {
|
|
957
|
+
totalDecisions: this.state.totalDecisions,
|
|
958
|
+
modelDistribution: { ...this.state.modelDistribution },
|
|
959
|
+
avgComplexity: this.state.avgComplexity,
|
|
960
|
+
avgConfidence: this.state.avgConfidence,
|
|
961
|
+
circuitBreakerTrips: this.state.circuitBreakerTrips,
|
|
962
|
+
consecutiveFailures: { ...this.consecutiveFailures },
|
|
963
|
+
routedByCounts: { ...this.routedByCounts },
|
|
964
|
+
neuralBackendCounts: { ...this.neuralBackendCounts },
|
|
965
|
+
ab: {
|
|
966
|
+
comparisons: this.abComparisons,
|
|
967
|
+
disagreements: this.abDisagreements,
|
|
968
|
+
disagreementRate: this.abComparisons > 0 ? this.abDisagreements / this.abComparisons : 0,
|
|
969
|
+
},
|
|
970
|
+
stateVersion: this.state.version ?? 2,
|
|
971
|
+
...(this.state.priorsById ? { priorsById: this.state.priorsById } : {}),
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Load state from disk
|
|
976
|
+
*/
|
|
977
|
+
loadState() {
|
|
978
|
+
const defaultState = {
|
|
979
|
+
totalDecisions: 0,
|
|
980
|
+
modelDistribution: { haiku: 0, sonnet: 0, opus: 0, inherit: 0 },
|
|
981
|
+
avgComplexity: 0.5,
|
|
982
|
+
avgConfidence: 0.8,
|
|
983
|
+
circuitBreakerTrips: 0,
|
|
984
|
+
lastUpdated: new Date().toISOString(),
|
|
985
|
+
learningHistory: [],
|
|
986
|
+
version: 2,
|
|
987
|
+
priors: defaultBucketedPriors(),
|
|
988
|
+
};
|
|
989
|
+
try {
|
|
990
|
+
const fullPath = join(process.cwd(), this.config.statePath);
|
|
991
|
+
if (existsSync(fullPath)) {
|
|
992
|
+
const data = readFileSync(fullPath, 'utf-8');
|
|
993
|
+
const loaded = JSON.parse(data);
|
|
994
|
+
// ADR-142: forward-migrate priors of ANY layout (missing / flat v1 /
|
|
995
|
+
// already-bucketed) to the bucketed shape without data loss or throwing.
|
|
996
|
+
loaded.priors = migratePriors(loaded.priors);
|
|
997
|
+
loaded.version = 2;
|
|
998
|
+
return { ...defaultState, ...loaded };
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
catch {
|
|
1002
|
+
// Ignore load errors
|
|
1003
|
+
}
|
|
1004
|
+
return defaultState;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Save state to disk
|
|
1008
|
+
*/
|
|
1009
|
+
saveState() {
|
|
1010
|
+
try {
|
|
1011
|
+
const fullPath = join(process.cwd(), this.config.statePath);
|
|
1012
|
+
const dir = dirname(fullPath);
|
|
1013
|
+
if (!existsSync(dir)) {
|
|
1014
|
+
mkdirSync(dir, { recursive: true });
|
|
1015
|
+
}
|
|
1016
|
+
this.state.lastUpdated = new Date().toISOString();
|
|
1017
|
+
writeFileSync(fullPath, JSON.stringify(this.state, null, 2));
|
|
1018
|
+
}
|
|
1019
|
+
catch {
|
|
1020
|
+
// Ignore save errors in non-critical scenarios
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Reset router state
|
|
1025
|
+
*/
|
|
1026
|
+
reset() {
|
|
1027
|
+
this.state = {
|
|
1028
|
+
totalDecisions: 0,
|
|
1029
|
+
modelDistribution: { haiku: 0, sonnet: 0, opus: 0, inherit: 0 },
|
|
1030
|
+
avgComplexity: 0.5,
|
|
1031
|
+
avgConfidence: 0.8,
|
|
1032
|
+
circuitBreakerTrips: 0,
|
|
1033
|
+
lastUpdated: new Date().toISOString(),
|
|
1034
|
+
learningHistory: [],
|
|
1035
|
+
version: 2,
|
|
1036
|
+
priors: defaultBucketedPriors(),
|
|
1037
|
+
};
|
|
1038
|
+
this.consecutiveFailures = { haiku: 0, sonnet: 0, opus: 0, inherit: 0 };
|
|
1039
|
+
this.decisionCount = 0;
|
|
1040
|
+
this.saveState();
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Public read-only accessor for the bandit priors. Useful for tests,
|
|
1044
|
+
* dashboards, and the pending hooks_intelligence_stats integration that
|
|
1045
|
+
* surfaces convergence in the dashboard. Returns a copy.
|
|
1046
|
+
*/
|
|
1047
|
+
getBanditPriors(bucket = 'med') {
|
|
1048
|
+
const bucketed = this.state.priors ?? defaultBucketedPriors();
|
|
1049
|
+
const p = bucketed[bucket] ?? defaultBanditPriors();
|
|
1050
|
+
return {
|
|
1051
|
+
haiku: { ...p.haiku },
|
|
1052
|
+
sonnet: { ...p.sonnet },
|
|
1053
|
+
opus: { ...p.opus },
|
|
1054
|
+
inherit: { ...p.inherit },
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
/** All bucketed priors (copy) — for dashboards/tests. */
|
|
1058
|
+
getBucketedPriors() {
|
|
1059
|
+
const b = this.state.priors ?? defaultBucketedPriors();
|
|
1060
|
+
return {
|
|
1061
|
+
low: clonePriors(b.low ?? defaultBanditPriors()),
|
|
1062
|
+
med: clonePriors(b.med ?? defaultBanditPriors()),
|
|
1063
|
+
high: clonePriors(b.high ?? defaultBanditPriors()),
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
// ============================================================================
|
|
1068
|
+
// Singleton & Factory Functions
|
|
1069
|
+
// ============================================================================
|
|
1070
|
+
let modelRouterInstance = null;
|
|
1071
|
+
/**
|
|
1072
|
+
* Get or create the singleton ModelRouter instance
|
|
1073
|
+
*/
|
|
1074
|
+
export function getModelRouter(config) {
|
|
1075
|
+
if (!modelRouterInstance) {
|
|
1076
|
+
modelRouterInstance = new ModelRouter(config);
|
|
1077
|
+
}
|
|
1078
|
+
return modelRouterInstance;
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Reset the singleton instance
|
|
1082
|
+
*/
|
|
1083
|
+
export function resetModelRouter() {
|
|
1084
|
+
modelRouterInstance = null;
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Create a new ModelRouter instance (non-singleton)
|
|
1088
|
+
*/
|
|
1089
|
+
export function createModelRouter(config) {
|
|
1090
|
+
return new ModelRouter(config);
|
|
1091
|
+
}
|
|
1092
|
+
// ============================================================================
|
|
1093
|
+
// Convenience Functions
|
|
1094
|
+
// ============================================================================
|
|
1095
|
+
/**
|
|
1096
|
+
* Quick route function for common use case
|
|
1097
|
+
*/
|
|
1098
|
+
export async function routeToModel(task) {
|
|
1099
|
+
const router = getModelRouter();
|
|
1100
|
+
const result = await router.route(task);
|
|
1101
|
+
return result.model;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Route with full result
|
|
1105
|
+
*/
|
|
1106
|
+
export async function routeToModelFull(task, embedding) {
|
|
1107
|
+
const router = getModelRouter();
|
|
1108
|
+
return router.route(task, embedding);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Analyze task complexity without routing
|
|
1112
|
+
*/
|
|
1113
|
+
export function analyzeTaskComplexity(task) {
|
|
1114
|
+
const router = getModelRouter();
|
|
1115
|
+
return router.analyzeComplexity(task, undefined);
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Get model router statistics
|
|
1119
|
+
*/
|
|
1120
|
+
export function getModelRouterStats() {
|
|
1121
|
+
const router = getModelRouter();
|
|
1122
|
+
return router.getStats();
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Record routing outcome for learning
|
|
1126
|
+
*/
|
|
1127
|
+
export function recordModelOutcome(task, model, outcome) {
|
|
1128
|
+
const router = getModelRouter();
|
|
1129
|
+
router.recordOutcome(task, model, outcome);
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* ADR-149 — record an outcome keyed by the concrete model id rather than
|
|
1133
|
+
* the tier label. Updates the shadow `priorsById` state. Selection logic
|
|
1134
|
+
* still uses tier priors; this data accumulates for a future per-modelId
|
|
1135
|
+
* selector refactor.
|
|
1136
|
+
*
|
|
1137
|
+
* Safe to call alongside `recordModelOutcome` — they update independent
|
|
1138
|
+
* state slices so double-counting is impossible.
|
|
1139
|
+
*/
|
|
1140
|
+
export function recordModelOutcomeByModelId(task, modelId, outcome) {
|
|
1141
|
+
const router = getModelRouter();
|
|
1142
|
+
router.recordOutcomeByModelId(task, modelId, outcome);
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* ADR-149 iter 14 — read-only access to the per-modelId Beta priors. The
|
|
1146
|
+
* neural-router consumes this to apply per-model Thompson sampling on top
|
|
1147
|
+
* of its predicted-quality vector when CLAUDE_FLOW_ROUTER_BANDIT_PER_MODEL=1.
|
|
1148
|
+
* Returns the legacy bucketed priors (`priorsById[bucket][modelId]`) when
|
|
1149
|
+
* present, else null.
|
|
1150
|
+
*/
|
|
1151
|
+
export function getModelRouterPriorsById() {
|
|
1152
|
+
const router = getModelRouter();
|
|
1153
|
+
const stats = router.getStats();
|
|
1154
|
+
return stats.priorsById ?? null;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Re-export the complexity-bucket helper so the neural-router (which gets
|
|
1158
|
+
* the task text via the route() call) can map a complexity score to the
|
|
1159
|
+
* matching bandit bucket.
|
|
1160
|
+
*/
|
|
1161
|
+
export { complexityBucket };
|
|
1162
|
+
//# sourceMappingURL=model-router.js.map
|