agent-nuvira 3.3.12 → 3.3.14
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/README.md +67 -1
- package/dist/agents/agents/planner.d.ts.map +1 -1
- package/dist/agents/agents/planner.js +9 -0
- package/dist/agents/agents/planner.js.map +1 -1
- package/dist/agents/agents/reasoner.d.ts.map +1 -1
- package/dist/agents/agents/reasoner.js +13 -0
- package/dist/agents/agents/reasoner.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +31 -0
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +103 -135
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/cli/chat.d.ts +89 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +798 -39
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/cli-program.d.ts.map +1 -1
- package/dist/cli/cli-program.js +20 -0
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/config.d.ts +12 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +107 -3
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/decisions.d.ts +28 -0
- package/dist/cli/decisions.d.ts.map +1 -0
- package/dist/cli/decisions.js +140 -0
- package/dist/cli/decisions.js.map +1 -0
- package/dist/cli/doctor.d.ts +6 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +46 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +6 -0
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/failover-runner.d.ts.map +1 -1
- package/dist/cli/failover-runner.js +19 -1
- package/dist/cli/failover-runner.js.map +1 -1
- package/dist/cli/feedback.d.ts.map +1 -1
- package/dist/cli/feedback.js +5 -1
- package/dist/cli/feedback.js.map +1 -1
- package/dist/cli/hooks.d.ts +30 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +224 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/knowledge.d.ts +31 -0
- package/dist/cli/knowledge.d.ts.map +1 -0
- package/dist/cli/knowledge.js +223 -0
- package/dist/cli/knowledge.js.map +1 -0
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +92 -18
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/model.d.ts +20 -0
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +294 -14
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/omniroute-control.d.ts +88 -0
- package/dist/cli/omniroute-control.d.ts.map +1 -0
- package/dist/cli/omniroute-control.js +179 -0
- package/dist/cli/omniroute-control.js.map +1 -0
- package/dist/cli/omniroute.d.ts +21 -0
- package/dist/cli/omniroute.d.ts.map +1 -0
- package/dist/cli/omniroute.js +67 -0
- package/dist/cli/omniroute.js.map +1 -0
- package/dist/cli/rate.d.ts +78 -0
- package/dist/cli/rate.d.ts.map +1 -0
- package/dist/cli/rate.js +294 -0
- package/dist/cli/rate.js.map +1 -0
- package/dist/config/process-env.d.ts.map +1 -1
- package/dist/config/process-env.js +10 -0
- package/dist/config/process-env.js.map +1 -1
- package/dist/config/types.d.ts +8 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/context/history.d.ts +21 -0
- package/dist/context/history.d.ts.map +1 -1
- package/dist/context/history.js +50 -0
- package/dist/context/history.js.map +1 -1
- package/dist/enterprise/vault.d.ts.map +1 -1
- package/dist/enterprise/vault.js +5 -21
- package/dist/enterprise/vault.js.map +1 -1
- package/dist/gateway/hook-contract.d.ts +206 -0
- package/dist/gateway/hook-contract.d.ts.map +1 -0
- package/dist/gateway/hook-contract.js +449 -0
- package/dist/gateway/hook-contract.js.map +1 -0
- package/dist/gateway/hooks.d.ts.map +1 -1
- package/dist/gateway/hooks.js +14 -0
- package/dist/gateway/hooks.js.map +1 -1
- package/dist/inference/gemini-adapter.d.ts.map +1 -1
- package/dist/inference/gemini-adapter.js +4 -0
- package/dist/inference/gemini-adapter.js.map +1 -1
- package/dist/inference/interface.d.ts +54 -0
- package/dist/inference/interface.d.ts.map +1 -1
- package/dist/inference/model-id-validation.d.ts +57 -0
- package/dist/inference/model-id-validation.d.ts.map +1 -0
- package/dist/inference/model-id-validation.js +127 -0
- package/dist/inference/model-id-validation.js.map +1 -0
- package/dist/inference/native-tools.d.ts +16 -1
- package/dist/inference/native-tools.d.ts.map +1 -1
- package/dist/inference/native-tools.js +34 -10
- package/dist/inference/native-tools.js.map +1 -1
- package/dist/inference/openai-compat-adapter.d.ts.map +1 -1
- package/dist/inference/openai-compat-adapter.js +12 -1
- package/dist/inference/openai-compat-adapter.js.map +1 -1
- package/dist/inference/provider-catalog.d.ts +11 -0
- package/dist/inference/provider-catalog.d.ts.map +1 -1
- package/dist/inference/provider-catalog.js +80 -0
- package/dist/inference/provider-catalog.js.map +1 -1
- package/dist/inference/route-resolver.d.ts +32 -8
- package/dist/inference/route-resolver.d.ts.map +1 -1
- package/dist/inference/route-resolver.js +175 -1
- package/dist/inference/route-resolver.js.map +1 -1
- package/dist/inference/tool-call-utils.d.ts.map +1 -1
- package/dist/inference/tool-call-utils.js +10 -8
- package/dist/inference/tool-call-utils.js.map +1 -1
- package/dist/inference/tools.d.ts +17 -1
- package/dist/inference/tools.d.ts.map +1 -1
- package/dist/inference/tools.js +69 -19
- package/dist/inference/tools.js.map +1 -1
- package/dist/learning/acceptance-model.d.ts +247 -0
- package/dist/learning/acceptance-model.d.ts.map +1 -0
- package/dist/learning/acceptance-model.js +708 -0
- package/dist/learning/acceptance-model.js.map +1 -0
- package/dist/learning/auto-router.d.ts +121 -24
- package/dist/learning/auto-router.d.ts.map +1 -1
- package/dist/learning/auto-router.js +374 -96
- package/dist/learning/auto-router.js.map +1 -1
- package/dist/learning/autonomy-policy.d.ts +12 -16
- package/dist/learning/autonomy-policy.d.ts.map +1 -1
- package/dist/learning/autonomy-policy.js +50 -48
- package/dist/learning/autonomy-policy.js.map +1 -1
- package/dist/learning/behavioural-labels.d.ts +86 -0
- package/dist/learning/behavioural-labels.d.ts.map +1 -0
- package/dist/learning/behavioural-labels.js +166 -0
- package/dist/learning/behavioural-labels.js.map +1 -0
- package/dist/learning/capability-evidence.d.ts +167 -0
- package/dist/learning/capability-evidence.d.ts.map +1 -0
- package/dist/learning/capability-evidence.js +210 -0
- package/dist/learning/capability-evidence.js.map +1 -0
- package/dist/learning/capability-readiness.d.ts +64 -0
- package/dist/learning/capability-readiness.d.ts.map +1 -0
- package/dist/learning/capability-readiness.js +74 -0
- package/dist/learning/capability-readiness.js.map +1 -0
- package/dist/learning/capability-types.d.ts +169 -0
- package/dist/learning/capability-types.d.ts.map +1 -0
- package/dist/learning/capability-types.js +20 -0
- package/dist/learning/capability-types.js.map +1 -0
- package/dist/learning/catalog-feed.d.ts +89 -0
- package/dist/learning/catalog-feed.d.ts.map +1 -0
- package/dist/learning/catalog-feed.js +211 -0
- package/dist/learning/catalog-feed.js.map +1 -0
- package/dist/learning/cli-intent-effects.d.ts +60 -0
- package/dist/learning/cli-intent-effects.d.ts.map +1 -0
- package/dist/learning/cli-intent-effects.js +153 -0
- package/dist/learning/cli-intent-effects.js.map +1 -0
- package/dist/learning/command-adaptation.d.ts +41 -0
- package/dist/learning/command-adaptation.d.ts.map +1 -0
- package/dist/learning/command-adaptation.js +122 -0
- package/dist/learning/command-adaptation.js.map +1 -0
- package/dist/learning/consent-picture.d.ts +42 -0
- package/dist/learning/consent-picture.d.ts.map +1 -0
- package/dist/learning/consent-picture.js +53 -0
- package/dist/learning/consent-picture.js.map +1 -0
- package/dist/learning/context-budget.d.ts +37 -6
- package/dist/learning/context-budget.d.ts.map +1 -1
- package/dist/learning/context-budget.js +38 -9
- package/dist/learning/context-budget.js.map +1 -1
- package/dist/learning/decision-log.d.ts +114 -0
- package/dist/learning/decision-log.d.ts.map +1 -0
- package/dist/learning/decision-log.js +296 -0
- package/dist/learning/decision-log.js.map +1 -0
- package/dist/learning/deliverable-class.d.ts.map +1 -1
- package/dist/learning/deliverable-class.js +19 -0
- package/dist/learning/deliverable-class.js.map +1 -1
- package/dist/learning/deliverable-corpus.d.ts +117 -0
- package/dist/learning/deliverable-corpus.d.ts.map +1 -0
- package/dist/learning/deliverable-corpus.js +176 -0
- package/dist/learning/deliverable-corpus.js.map +1 -0
- package/dist/learning/error-repair.d.ts +45 -2
- package/dist/learning/error-repair.d.ts.map +1 -1
- package/dist/learning/error-repair.js +95 -0
- package/dist/learning/error-repair.js.map +1 -1
- package/dist/learning/exhaustion-report.d.ts +136 -0
- package/dist/learning/exhaustion-report.d.ts.map +1 -0
- package/dist/learning/exhaustion-report.js +239 -0
- package/dist/learning/exhaustion-report.js.map +1 -0
- package/dist/learning/failure-bookkeeping.d.ts +16 -0
- package/dist/learning/failure-bookkeeping.d.ts.map +1 -1
- package/dist/learning/failure-bookkeeping.js +51 -5
- package/dist/learning/failure-bookkeeping.js.map +1 -1
- package/dist/learning/gate-friction.d.ts +39 -0
- package/dist/learning/gate-friction.d.ts.map +1 -0
- package/dist/learning/gate-friction.js +91 -0
- package/dist/learning/gate-friction.js.map +1 -0
- package/dist/learning/harness-narration.d.ts +34 -0
- package/dist/learning/harness-narration.d.ts.map +1 -0
- package/dist/learning/harness-narration.js +58 -0
- package/dist/learning/harness-narration.js.map +1 -0
- package/dist/learning/hub-skill-catalog.d.ts.map +1 -1
- package/dist/learning/hub-skill-catalog.js +1 -2
- package/dist/learning/hub-skill-catalog.js.map +1 -1
- package/dist/learning/hybrid-router.d.ts +65 -0
- package/dist/learning/hybrid-router.d.ts.map +1 -1
- package/dist/learning/hybrid-router.js +186 -0
- package/dist/learning/hybrid-router.js.map +1 -1
- package/dist/learning/knowledge-base.d.ts +350 -0
- package/dist/learning/knowledge-base.d.ts.map +1 -0
- package/dist/learning/knowledge-base.js +906 -0
- package/dist/learning/knowledge-base.js.map +1 -0
- package/dist/learning/knowledge-eval.d.ts +171 -0
- package/dist/learning/knowledge-eval.d.ts.map +1 -0
- package/dist/learning/knowledge-eval.js +196 -0
- package/dist/learning/knowledge-eval.js.map +1 -0
- package/dist/learning/knowledge-turn.d.ts +92 -0
- package/dist/learning/knowledge-turn.d.ts.map +1 -0
- package/dist/learning/knowledge-turn.js +190 -0
- package/dist/learning/knowledge-turn.js.map +1 -0
- package/dist/learning/learned-commands.d.ts +64 -0
- package/dist/learning/learned-commands.d.ts.map +1 -0
- package/dist/learning/learned-commands.js +108 -0
- package/dist/learning/learned-commands.js.map +1 -0
- package/dist/learning/lexical-search.d.ts +50 -0
- package/dist/learning/lexical-search.d.ts.map +1 -0
- package/dist/learning/lexical-search.js +111 -0
- package/dist/learning/lexical-search.js.map +1 -0
- package/dist/learning/machine-facts.d.ts +71 -0
- package/dist/learning/machine-facts.d.ts.map +1 -0
- package/dist/learning/machine-facts.js +164 -0
- package/dist/learning/machine-facts.js.map +1 -0
- package/dist/learning/markdown-outline.d.ts +73 -0
- package/dist/learning/markdown-outline.d.ts.map +1 -0
- package/dist/learning/markdown-outline.js +136 -0
- package/dist/learning/markdown-outline.js.map +1 -0
- package/dist/learning/model-first-router.d.ts +33 -1
- package/dist/learning/model-first-router.d.ts.map +1 -1
- package/dist/learning/model-first-router.js +123 -9
- package/dist/learning/model-first-router.js.map +1 -1
- package/dist/learning/model-identity.d.ts +80 -0
- package/dist/learning/model-identity.d.ts.map +1 -0
- package/dist/learning/model-identity.js +115 -0
- package/dist/learning/model-identity.js.map +1 -0
- package/dist/learning/model-registry.d.ts +141 -2
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +324 -38
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/model-scoring.d.ts.map +1 -1
- package/dist/learning/model-scoring.js +8 -1
- package/dist/learning/model-scoring.js.map +1 -1
- package/dist/learning/model-selection.d.ts +2 -1
- package/dist/learning/model-selection.d.ts.map +1 -1
- package/dist/learning/model-selection.js +76 -2
- package/dist/learning/model-selection.js.map +1 -1
- package/dist/learning/outcome-observation.d.ts +49 -0
- package/dist/learning/outcome-observation.d.ts.map +1 -0
- package/dist/learning/outcome-observation.js +95 -0
- package/dist/learning/outcome-observation.js.map +1 -0
- package/dist/learning/pair-entitlement.d.ts +184 -0
- package/dist/learning/pair-entitlement.d.ts.map +1 -0
- package/dist/learning/pair-entitlement.js +214 -0
- package/dist/learning/pair-entitlement.js.map +1 -0
- package/dist/learning/project-docs.d.ts +83 -0
- package/dist/learning/project-docs.d.ts.map +1 -0
- package/dist/learning/project-docs.js +132 -0
- package/dist/learning/project-docs.js.map +1 -0
- package/dist/learning/provider-fallback.d.ts +23 -1
- package/dist/learning/provider-fallback.d.ts.map +1 -1
- package/dist/learning/provider-fallback.js +24 -0
- package/dist/learning/provider-fallback.js.map +1 -1
- package/dist/learning/quota-ledger.d.ts +13 -0
- package/dist/learning/quota-ledger.d.ts.map +1 -1
- package/dist/learning/quota-ledger.js +28 -0
- package/dist/learning/quota-ledger.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +65 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +123 -1
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/requirement-probe.d.ts +55 -0
- package/dist/learning/requirement-probe.d.ts.map +1 -0
- package/dist/learning/requirement-probe.js +77 -0
- package/dist/learning/requirement-probe.js.map +1 -0
- package/dist/learning/resilient-call.d.ts +20 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +42 -4
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/response-usability.d.ts +85 -0
- package/dist/learning/response-usability.d.ts.map +1 -0
- package/dist/learning/response-usability.js +135 -0
- package/dist/learning/response-usability.js.map +1 -0
- package/dist/learning/router-bandit.d.ts +132 -0
- package/dist/learning/router-bandit.d.ts.map +1 -1
- package/dist/learning/router-bandit.js +253 -0
- package/dist/learning/router-bandit.js.map +1 -1
- package/dist/learning/routing-history.d.ts +41 -2
- package/dist/learning/routing-history.d.ts.map +1 -1
- package/dist/learning/routing-history.js.map +1 -1
- package/dist/learning/run-trace.d.ts +21 -0
- package/dist/learning/run-trace.d.ts.map +1 -1
- package/dist/learning/run-trace.js +38 -0
- package/dist/learning/run-trace.js.map +1 -1
- package/dist/learning/seeded-benchmark.d.ts +14 -0
- package/dist/learning/seeded-benchmark.d.ts.map +1 -1
- package/dist/learning/seeded-benchmark.js +43 -4
- package/dist/learning/seeded-benchmark.js.map +1 -1
- package/dist/learning/session-grant.d.ts +75 -0
- package/dist/learning/session-grant.d.ts.map +1 -0
- package/dist/learning/session-grant.js +112 -0
- package/dist/learning/session-grant.js.map +1 -0
- package/dist/learning/task-model-continuity.d.ts +73 -0
- package/dist/learning/task-model-continuity.d.ts.map +1 -0
- package/dist/learning/task-model-continuity.js +87 -0
- package/dist/learning/task-model-continuity.js.map +1 -0
- package/dist/learning/turn-feedback.d.ts +93 -0
- package/dist/learning/turn-feedback.d.ts.map +1 -0
- package/dist/learning/turn-feedback.js +127 -0
- package/dist/learning/turn-feedback.js.map +1 -0
- package/dist/learning/turn-report.d.ts +104 -2
- package/dist/learning/turn-report.d.ts.map +1 -1
- package/dist/learning/turn-report.js +161 -6
- package/dist/learning/turn-report.js.map +1 -1
- package/dist/learning/verdict-routing.d.ts +51 -0
- package/dist/learning/verdict-routing.d.ts.map +1 -0
- package/dist/learning/verdict-routing.js +122 -0
- package/dist/learning/verdict-routing.js.map +1 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +5 -0
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/manager.d.ts +10 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +20 -0
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/mcp-discovery.d.ts +34 -0
- package/dist/mcp/mcp-discovery.d.ts.map +1 -0
- package/dist/mcp/mcp-discovery.js +71 -0
- package/dist/mcp/mcp-discovery.js.map +1 -0
- package/dist/mcp/mcp-schema-cache.d.ts +17 -0
- package/dist/mcp/mcp-schema-cache.d.ts.map +1 -1
- package/dist/mcp/mcp-schema-cache.js +47 -3
- package/dist/mcp/mcp-schema-cache.js.map +1 -1
- package/dist/mcp/types.d.ts +28 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/security/secret-scan.d.ts +156 -0
- package/dist/security/secret-scan.d.ts.map +1 -0
- package/dist/security/secret-scan.js +525 -0
- package/dist/security/secret-scan.js.map +1 -0
- package/dist/tools/ask-user.d.ts.map +1 -1
- package/dist/tools/ask-user.js +4 -0
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/capability-registry.d.ts +150 -0
- package/dist/tools/capability-registry.d.ts.map +1 -0
- package/dist/tools/capability-registry.js +400 -0
- package/dist/tools/capability-registry.js.map +1 -0
- package/dist/tools/child-agent-runtime.d.ts.map +1 -1
- package/dist/tools/child-agent-runtime.js +19 -5
- package/dist/tools/child-agent-runtime.js.map +1 -1
- package/dist/tools/coding-tools.d.ts +17 -5
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +80 -11
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/command-memo.d.ts +116 -0
- package/dist/tools/command-memo.d.ts.map +1 -0
- package/dist/tools/command-memo.js +202 -0
- package/dist/tools/command-memo.js.map +1 -0
- package/dist/tools/git-tool.d.ts.map +1 -1
- package/dist/tools/git-tool.js +13 -3
- package/dist/tools/git-tool.js.map +1 -1
- package/dist/tools/harness-directive.d.ts +42 -0
- package/dist/tools/harness-directive.d.ts.map +1 -0
- package/dist/tools/harness-directive.js +45 -0
- package/dist/tools/harness-directive.js.map +1 -0
- package/dist/tools/loop-skill-hint.d.ts +74 -174
- package/dist/tools/loop-skill-hint.d.ts.map +1 -1
- package/dist/tools/loop-skill-hint.js +110 -536
- package/dist/tools/loop-skill-hint.js.map +1 -1
- package/dist/tools/mcp-client-tool.d.ts +2 -0
- package/dist/tools/mcp-client-tool.d.ts.map +1 -1
- package/dist/tools/mcp-client-tool.js +3 -0
- package/dist/tools/mcp-client-tool.js.map +1 -1
- package/dist/tools/modality/shared.d.ts +1 -4
- package/dist/tools/modality/shared.d.ts.map +1 -1
- package/dist/tools/modality/shared.js +7 -22
- package/dist/tools/modality/shared.js.map +1 -1
- package/dist/tools/plan-store.d.ts +44 -4
- package/dist/tools/plan-store.d.ts.map +1 -1
- package/dist/tools/plan-store.js +93 -9
- package/dist/tools/plan-store.js.map +1 -1
- package/dist/tools/registry.d.ts +76 -3
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +623 -15
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/remediation-ladder.d.ts +91 -0
- package/dist/tools/remediation-ladder.d.ts.map +1 -0
- package/dist/tools/remediation-ladder.js +316 -0
- package/dist/tools/remediation-ladder.js.map +1 -0
- package/dist/tools/run-cli.d.ts.map +1 -1
- package/dist/tools/run-cli.js +29 -4
- package/dist/tools/run-cli.js.map +1 -1
- package/dist/tools/run-terminal.d.ts +11 -0
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +289 -10
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/skills-hub.d.ts +7 -0
- package/dist/tools/skills-hub.d.ts.map +1 -1
- package/dist/tools/skills-hub.js +11 -2
- package/dist/tools/skills-hub.js.map +1 -1
- package/dist/tools/step-artifact.d.ts +35 -0
- package/dist/tools/step-artifact.d.ts.map +1 -0
- package/dist/tools/step-artifact.js +154 -0
- package/dist/tools/step-artifact.js.map +1 -0
- package/dist/tools/tool-loop.d.ts +327 -13
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +891 -66
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/toolsets.js +4 -4
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/tools/worktree.d.ts +11 -2
- package/dist/tools/worktree.d.ts.map +1 -1
- package/dist/tools/worktree.js +11 -2
- package/dist/tools/worktree.js.map +1 -1
- package/dist/utils/atomic-store.d.ts +53 -0
- package/dist/utils/atomic-store.d.ts.map +1 -0
- package/dist/utils/atomic-store.js +178 -0
- package/dist/utils/atomic-store.js.map +1 -0
- package/dist/utils/binary-probe.d.ts +25 -0
- package/dist/utils/binary-probe.d.ts.map +1 -0
- package/dist/utils/binary-probe.js +48 -0
- package/dist/utils/binary-probe.js.map +1 -0
- package/dist/utils/workspace-path.d.ts +50 -0
- package/dist/utils/workspace-path.d.ts.map +1 -0
- package/dist/utils/workspace-path.js +86 -0
- package/dist/utils/workspace-path.js.map +1 -0
- package/dist/web-dashboard/chat-console.d.ts +24 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +38 -0
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/project-context.d.ts +27 -2
- package/dist/web-dashboard/project-context.d.ts.map +1 -1
- package/dist/web-dashboard/project-context.js +32 -2
- package/dist/web-dashboard/project-context.js.map +1 -1
- package/dist/web-dashboard/server.d.ts +40 -0
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +733 -15
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +236 -1
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-guard.d.ts +18 -2
- package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-guard.js +94 -12
- package/dist/web-dashboard/workspace-guard.js.map +1 -1
- package/dist/web-dashboard/workspace-resolution.d.ts +87 -0
- package/dist/web-dashboard/workspace-resolution.d.ts.map +1 -0
- package/dist/web-dashboard/workspace-resolution.js +105 -0
- package/dist/web-dashboard/workspace-resolution.js.map +1 -0
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/index-tK8Y2qHB.css +1 -0
- package/src/web-dashboard/public/assets/index-zjbdJnO-.js +210 -0
- package/src/web-dashboard/public/assets/index-zjbdJnO-.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/src/web-dashboard/public/assets/index-BrZcYhy6.js +0 -207
- package/src/web-dashboard/public/assets/index-BrZcYhy6.js.map +0 -1
- package/src/web-dashboard/public/assets/index-DsWczTa6.css +0 -1
|
@@ -40,12 +40,14 @@ import { analyzeComplexity } from './hybrid-router.js';
|
|
|
40
40
|
import { getTaskType } from './model-router.js';
|
|
41
41
|
import { getBenchmarkRuns } from './benchmark.js';
|
|
42
42
|
import { getAgentStats } from './agent-stats.js';
|
|
43
|
-
import { getRouterBandit, DEFAULT_MIN_SAMPLES } from './router-bandit.js';
|
|
43
|
+
import { getRouterBandit, DEFAULT_MIN_SAMPLES, } from './router-bandit.js';
|
|
44
44
|
import { getRouterPromotion, DEFAULT_MIN_PROMOTION_DECISIONS } from './router-promotion.js';
|
|
45
45
|
import { getMlRouter, DEFAULT_ML_K, DEFAULT_ML_MIN_SAMPLES, DEFAULT_ML_STRENGTH } from './ml-router.js';
|
|
46
46
|
import { buildModelCandidates } from './model-first-router.js';
|
|
47
47
|
import { pickBestModel, topModelCandidates } from './model-scoring.js';
|
|
48
48
|
import { getModelRegistry } from './model-registry.js';
|
|
49
|
+
import { areTwins, classifyPairEntitlement } from './pair-entitlement.js';
|
|
50
|
+
import { effectiveParameter } from './capability-evidence.js';
|
|
49
51
|
// R4: the agentic capability floor needs the same "too small to hold a tool
|
|
50
52
|
// loop" judgement the harness uses. model-harness.ts imports nothing, so this
|
|
51
53
|
// adds no cycle risk to the router.
|
|
@@ -62,6 +64,7 @@ import { CATALOG_PROVIDER_IDS, getCatalogProvider, getDefaultModel, isCatalogKey
|
|
|
62
64
|
import { isContentArtifactAsk } from '../nlu/intent.js';
|
|
63
65
|
import { ProviderFactory } from '../inference/factory.js';
|
|
64
66
|
import { logger } from '../utils/logger.js';
|
|
67
|
+
import { getQuotaLedger } from './quota-ledger.js';
|
|
65
68
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
66
69
|
/** The special model value that triggers automatic per-task routing. */
|
|
67
70
|
export const AUTO_MODEL = 'auto';
|
|
@@ -242,6 +245,48 @@ export function governanceVerdict(configManager, provider, opts = {}) {
|
|
|
242
245
|
}
|
|
243
246
|
return { allowed: true };
|
|
244
247
|
}
|
|
248
|
+
export function adminBudgetVerdict(configManager, provider, opts = {}) {
|
|
249
|
+
if (!configManager)
|
|
250
|
+
return { allowed: true };
|
|
251
|
+
// 1. Declared window budget (tokens / requests).
|
|
252
|
+
try {
|
|
253
|
+
if (getQuotaLedger().isOverConfiguredLimit(configManager, provider)) {
|
|
254
|
+
return {
|
|
255
|
+
allowed: false,
|
|
256
|
+
control: 'quota',
|
|
257
|
+
reason: `'${provider}' is over the daily budget you set in ` +
|
|
258
|
+
`routing.quota.${provider} (tokens or requests for the current window). ` +
|
|
259
|
+
'Raise the limit in the dashboard 💰 Daily Budget panel (or `nuvira model quota set`), ' +
|
|
260
|
+
'or wait for the window to reset.',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Best-effort — an unreadable ledger must never block a run.
|
|
266
|
+
}
|
|
267
|
+
// 2. Admin cost cap — strictest of the routing + governance caps.
|
|
268
|
+
try {
|
|
269
|
+
const routing = configManager.getAll?.()?.routing;
|
|
270
|
+
const caps = [routing?.maxCostUsd, routing?.governance?.maxCostUsd].filter((c) => c !== undefined);
|
|
271
|
+
if (caps.length > 0) {
|
|
272
|
+
const cap = Math.min(...caps);
|
|
273
|
+
const typical = estimateCallCostUsd(provider);
|
|
274
|
+
if (typical > cap) {
|
|
275
|
+
return {
|
|
276
|
+
allowed: false,
|
|
277
|
+
control: 'cost',
|
|
278
|
+
reason: `'${provider}' costs ~$${typical}/call, over the admin cap of $${cap} per call ` +
|
|
279
|
+
'(routing.maxCostUsd / routing.governance.maxCostUsd). ' +
|
|
280
|
+
'Raise the cap in the dashboard Budget panel, or pick a cheaper provider.',
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// Best-effort.
|
|
287
|
+
}
|
|
288
|
+
return { allowed: true };
|
|
289
|
+
}
|
|
245
290
|
// ─── Provider Capability Profiles ───────────────────────────────────────────
|
|
246
291
|
//
|
|
247
292
|
// Static baseline profiles for the built-in providers. These encode the
|
|
@@ -556,6 +601,36 @@ export function isAutoModel(model) {
|
|
|
556
601
|
export function isAutoProvider(provider) {
|
|
557
602
|
return provider === AUTO_PROVIDER;
|
|
558
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* A CONCRETE provider pinned with the `auto` model sentinel means THAT
|
|
606
|
+
* provider's OWN auto routing — an OmniRoute gateway combo, say — not
|
|
607
|
+
* agent-nuvira's provider/model selection.
|
|
608
|
+
*
|
|
609
|
+
* This is the distinction behind the `-p omniroute -m auto` collision: the
|
|
610
|
+
* sentinel was read as the agent's auto-route EVERYWHERE, so the request was
|
|
611
|
+
* re-routed by our router and never reached the gateway that was named. The
|
|
612
|
+
* sentinel is provider-owned ONLY when the provider is named and is not itself
|
|
613
|
+
* the `auto` directive; `-m auto` with no provider (or `-p auto`) stays our
|
|
614
|
+
* auto-route exactly as before.
|
|
615
|
+
*/
|
|
616
|
+
export function isProviderOwnAuto(provider, model) {
|
|
617
|
+
if (!provider || isAutoProvider(provider) || !isAutoModel(model))
|
|
618
|
+
return false;
|
|
619
|
+
// …and the provider must ACTUALLY expose its own `auto`. The first cut returned
|
|
620
|
+
// true for ANY concrete provider (`-p groq -m auto`, `-p local -m auto`, …),
|
|
621
|
+
// which left the sentinel unresolved and handed the literal string `auto` to a
|
|
622
|
+
// provider that has no such model — a live 400 waiting to happen, and the cause
|
|
623
|
+
// of a verify-probe + real call (the same request paid for twice). Only a
|
|
624
|
+
// gateway whose catalog default IS `auto` (OmniRoute, and anything configured
|
|
625
|
+
// the same way) owns the sentinel; every other provider gets a real model id.
|
|
626
|
+
return getDefaultModel(provider) === 'auto';
|
|
627
|
+
}
|
|
628
|
+
/** Agent-nuvira's OWN auto routing: the `auto` directive on either axis, minus
|
|
629
|
+
* the provider-owned case — use this where a decision would otherwise
|
|
630
|
+
* re-route a concretely pinned provider. */
|
|
631
|
+
export function isAgentAutoRoute(provider, model) {
|
|
632
|
+
return !isProviderOwnAuto(provider, model) && (isAutoProvider(provider) || isAutoModel(model));
|
|
633
|
+
}
|
|
559
634
|
/**
|
|
560
635
|
* Compute the effective dimension weights for a task.
|
|
561
636
|
* Combines complexity baseline + preference-mode adjustments + user overrides,
|
|
@@ -630,7 +705,23 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
630
705
|
notes: ['content (non-code) task detected'],
|
|
631
706
|
};
|
|
632
707
|
}
|
|
633
|
-
|
|
708
|
+
// ── THE INTENT RULES MATCH WHOLE WORDS (B2 sweep) ───────────────────────
|
|
709
|
+
//
|
|
710
|
+
// These were unanchored alternations, so a FRAGMENT of an ordinary word
|
|
711
|
+
// decided the intent. Demonstrated against the built `dist`: "add a **prefix**
|
|
712
|
+
// constant to the config" and "create a **fixture** file for the tests" both
|
|
713
|
+
// classified as `debugging` (because `fix` is inside them), earning a
|
|
714
|
+
// verification pass and an openrouter escalation; "the provider **ecosystem**"
|
|
715
|
+
// classified as `architecture` (`system` inside it) with a gemini escalation;
|
|
716
|
+
// "**plant** a tree" classified as `planning`. It is the same class of defect
|
|
717
|
+
// as B2's `desc.includes('hi')` — a substring is not a word.
|
|
718
|
+
//
|
|
719
|
+
// The fix is a boundary PLUS the inflections the rule actually wants, because a
|
|
720
|
+
// bare `\b` does not help when the fragment sits at the START of a word:
|
|
721
|
+
// `\bfix\w*\b` still matches "fixture". So `fix` is spelled out
|
|
722
|
+
// (`fix|fixes|fixed|fixing|fixer|fixup`) and `plan` likewise (so `plant` is not
|
|
723
|
+
// a plan). Nothing else changed — same rules, same ORDER, same intents.
|
|
724
|
+
if (/\bmigrat(?:e|ion)\w*\b|\bupgrade\w*\b|\brefactor\w*\b|\bpipeline\w*\b|\bdeployment\w*\b/.test(text)) {
|
|
634
725
|
return {
|
|
635
726
|
intent: 'migration',
|
|
636
727
|
requiresVerification: true,
|
|
@@ -638,7 +729,8 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
638
729
|
notes: ['migration-related task detected'],
|
|
639
730
|
};
|
|
640
731
|
}
|
|
641
|
-
if (
|
|
732
|
+
if (/\barchitect\w*\b|\bdesign\w*\b|\bsystem\w*\b|\bmicroservice\w*\b|\bplatform\w*\b/.test(text) &&
|
|
733
|
+
!/\b(?:outline|plan|roadmap|strategy)\w*\b/.test(text)) {
|
|
642
734
|
return {
|
|
643
735
|
intent: 'architecture',
|
|
644
736
|
requiresVerification: true,
|
|
@@ -646,7 +738,15 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
646
738
|
notes: ['architecture-focused task detected'],
|
|
647
739
|
};
|
|
648
740
|
}
|
|
649
|
-
|
|
741
|
+
// `verif(?:y|ies|ying|ication|ications)` — the ASK forms — and deliberately NOT
|
|
742
|
+
// `verified`. Bounding this rule surfaced why: a bare `\bverif\w*\b` would also
|
|
743
|
+
// match the past participle, and "the build was verified" is a STATUS REPORT,
|
|
744
|
+
// not a verification task, so it must not buy a reviewer pass and an
|
|
745
|
+
// openrouter escalation. Measured: that narrower spelling changes the intent of
|
|
746
|
+
// ZERO of the 161 tasks in the local routing history, where `\bverif\w*\b`
|
|
747
|
+
// changed one (a past-tense status message) — so the conservative form is the
|
|
748
|
+
// one that fixes the fragment bug without moving any real decision.
|
|
749
|
+
if (/\bverif(?:y|ies|ying|ication|ications)\b|\bvalidat\w*\b|\brollout\w*\b|\bdeploy\w*\b|\bproduction\w*\b|\blaunch\w*\b|\brelease\w*\b/.test(text)) {
|
|
650
750
|
return {
|
|
651
751
|
intent: 'verification',
|
|
652
752
|
requiresVerification: true,
|
|
@@ -654,7 +754,7 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
654
754
|
notes: ['verification-related task detected'],
|
|
655
755
|
};
|
|
656
756
|
}
|
|
657
|
-
if (
|
|
757
|
+
if (/\bsecurit\w*\b|\baudit\w*\b|\bvulnerab\w*\b|\bthreat\w*\b|\bexploit\w*\b/.test(text)) {
|
|
658
758
|
return {
|
|
659
759
|
intent: 'security',
|
|
660
760
|
requiresVerification: true,
|
|
@@ -662,7 +762,7 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
662
762
|
notes: ['security-sensitive task detected'],
|
|
663
763
|
};
|
|
664
764
|
}
|
|
665
|
-
if (
|
|
765
|
+
if (/\bdebug\w*\b|\bbug\w*\b|\berror\w*\b|\bfix(?:es|ed|ing|er|up)?\b|\btrace\w*\b/.test(text)) {
|
|
666
766
|
return {
|
|
667
767
|
intent: 'debugging',
|
|
668
768
|
// C4 — a debug/build-fix task is not done until the fix is OBSERVED to
|
|
@@ -676,7 +776,7 @@ export function analyzeTaskProfile(taskDescription) {
|
|
|
676
776
|
notes: ['debugging task detected'],
|
|
677
777
|
};
|
|
678
778
|
}
|
|
679
|
-
if (
|
|
779
|
+
if (/\bplan(?:s|ned|ning|ner)?\b|\boutline\w*\b|\broadmap\w*\b|\bstrateg\w*\b/.test(text)) {
|
|
680
780
|
return {
|
|
681
781
|
intent: 'planning',
|
|
682
782
|
requiresVerification: false,
|
|
@@ -723,6 +823,14 @@ export function scoreProvider(provider, capabilities, weights) {
|
|
|
723
823
|
*/
|
|
724
824
|
export class AutoModelRouter {
|
|
725
825
|
profiles;
|
|
826
|
+
/**
|
|
827
|
+
* The attribution of the LAST recorded outcome, kept so a user correction that
|
|
828
|
+
* arrives with the next message ("still broken") can be applied to the turn it
|
|
829
|
+
* is actually about. The bandit's own `getLastProvider`/`getLastModel` are
|
|
830
|
+
* overwritten by the next resolve(), so the pair must be captured HERE, at
|
|
831
|
+
* record time — see `recordUserRejection`.
|
|
832
|
+
*/
|
|
833
|
+
pendingOutcome = null;
|
|
726
834
|
constructor(profiles) {
|
|
727
835
|
this.profiles = profiles ? { ...DEFAULT_PROFILES, ...profiles } : { ...DEFAULT_PROFILES };
|
|
728
836
|
}
|
|
@@ -754,97 +862,57 @@ export class AutoModelRouter {
|
|
|
754
862
|
/**
|
|
755
863
|
* MODEL-LEVEL capability refinement (model-first-router parity): the
|
|
756
864
|
* provider's capability profile is a BASELINE — the model actually served
|
|
757
|
-
* may be much stronger or weaker. Refines the provider caps with
|
|
758
|
-
*
|
|
865
|
+
* may be much stronger or weaker. Refines the provider caps with MEASURED
|
|
866
|
+
* evidence about the model the router will serve for THIS task (resolveModel:
|
|
759
867
|
* the configured pin, or the task-resolved best model via pickBestModel).
|
|
760
868
|
*
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
772
|
-
*
|
|
869
|
+
* B1/B4 (Bundle 3b, 2026-10-07) — THE NAME NO LONGER CONTRIBUTES ANYTHING.
|
|
870
|
+
*
|
|
871
|
+
* What used to be here: a fast-tier word list (`mini`/`tiny`/`small`/`nano`/
|
|
872
|
+
* `lite`/`instant`/`flash`/`turbo`/`haiku`) SUBTRACTING 0.1 per word, a
|
|
873
|
+
* slow-tier list (`large`/`max`/`opus`/`pro`/`ultra`/`frontier`) adding 0.15, a
|
|
874
|
+
* parameter-size table (`70b` +0.45 … `≤4b` −0.2), and frontier-family
|
|
875
|
+
* keywords (+0.2). All of it is REMOVED, not capped: a capped name hint is
|
|
876
|
+
* still a name judgement, and the programme's own investigation fell into the
|
|
877
|
+
* trap — it called DeepSeek V4.1 Flash weak *because of the word "flash"*
|
|
878
|
+
* while that model served an 82-step build. It also made max mode's reasoning
|
|
879
|
+
* floor a spelling test, so `gemini-3.1-flash-lite` was excluded for its name
|
|
880
|
+
* and a 70B id on a weak provider was admitted for its name.
|
|
881
|
+
*
|
|
882
|
+
* What replaces it: the MEASURED scorecard (`learning/capability-evidence.ts`),
|
|
883
|
+
* stored per provider × model in the registry. `accuracy` replaces the id
|
|
884
|
+
* reasoning hints and `performance` replaces the id speed hints, both blended
|
|
885
|
+
* against this provider's declared baseline as the cold-start PRIOR:
|
|
886
|
+
*
|
|
887
|
+
* - 0 samples → the provider baseline, byte-for-byte. Nothing that has never
|
|
888
|
+
* been measured changes behaviour, and two ids differing only by a tier
|
|
889
|
+
* word score IDENTICALLY (B1's acceptance) because the prior is
|
|
890
|
+
* provider-level and cannot see the id;
|
|
891
|
+
* - ≥ MIN_SAMPLES → the measured value, with the prior's weight decaying to
|
|
892
|
+
* zero by `PRIOR_FULL_SAMPLES`.
|
|
773
893
|
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
* (the gate then behaves exactly as before — no behavior change for
|
|
777
|
-
* unresolvable evidence). Deterministic: same inputs → same caps.
|
|
894
|
+
* B4 follows from that: max mode's floor is now applied to measured accuracy,
|
|
895
|
+
* so a pair is excluded for what it has DONE, not for what it is called.
|
|
778
896
|
*/
|
|
779
897
|
getModelCapabilities(provider, model) {
|
|
780
898
|
const base = { ...this.getCapabilities(provider) };
|
|
781
899
|
if (!model || model === 'default')
|
|
782
900
|
return base;
|
|
783
|
-
const m = model.toLowerCase();
|
|
784
|
-
let reasoningAdj = 0;
|
|
785
|
-
let speedAdj = 0;
|
|
786
|
-
// Parameter-size evidence. The >=60B boost is deliberately large enough to
|
|
787
|
-
// lift a genuine 70B model on a WEAK provider baseline (local 0.30 + 0.45 =
|
|
788
|
-
// 0.75) over the C4 verification reasoning floor — otherwise a capable local
|
|
789
|
-
// 70B model would be dropped for a build-debug ask while a cloud fast-lite
|
|
790
|
-
// model slips through. It never affects SCORING (this method feeds the
|
|
791
|
-
// constraint gate only), just who is eligible.
|
|
792
|
-
const params = /(\d+(?:\.\d+)?)b(?:\b|-|$)/.exec(m);
|
|
793
|
-
if (params) {
|
|
794
|
-
const b = parseFloat(params[1]);
|
|
795
|
-
if (b >= 60)
|
|
796
|
-
reasoningAdj += 0.45;
|
|
797
|
-
else if (b >= 30)
|
|
798
|
-
reasoningAdj += 0.15;
|
|
799
|
-
else if (b >= 12)
|
|
800
|
-
reasoningAdj += 0.05;
|
|
801
|
-
else if (b <= 4) {
|
|
802
|
-
reasoningAdj -= 0.2;
|
|
803
|
-
speedAdj += 0.15; // tiny models are fast
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
// Tier-word evidence. A fast-tier word LOWERS reasoning and RAISES speed;
|
|
807
|
-
// STACKED fast-tier markers are penalized PER WORD, so `gemini-3.1-flash-lite`
|
|
808
|
-
// (flash + lite) lands below the C4 verification reasoning floor while a
|
|
809
|
-
// single `-flash` id stays above it. This stacked-token penalty is what makes
|
|
810
|
-
// "don't serve a flash-lite model for a build-debug ask" expressible as a
|
|
811
|
-
// reasoning floor rather than a brittle id denylist.
|
|
812
|
-
const SLOW_TIER = /\b(large|max|opus|pro|ultra|frontier)\b/;
|
|
813
|
-
const FAST_TIER = /\b(mini|tiny|small|nano|lite|instant|flash|turbo|haiku)\b/g;
|
|
814
|
-
if (SLOW_TIER.test(m))
|
|
815
|
-
reasoningAdj += 0.15;
|
|
816
|
-
const fastTierHits = m.match(FAST_TIER)?.length ?? 0;
|
|
817
|
-
if (fastTierHits > 0) {
|
|
818
|
-
reasoningAdj -= 0.1 * fastTierHits;
|
|
819
|
-
speedAdj += 0.15;
|
|
820
|
-
}
|
|
821
|
-
// Frontier-family evidence (keyword match on the id).
|
|
822
|
-
if (/\b(gpt-[45]|o[134](?:-|$)|claude-[34]|gemini-2|deepseek-r1|qwen3|qwen-3)\b/.test(m) ||
|
|
823
|
-
/gpt-4|gpt-5|claude-3|claude-4|gemini-2/.test(m)) {
|
|
824
|
-
reasoningAdj += 0.2;
|
|
825
|
-
}
|
|
826
|
-
// Registry latency evidence: measured latency far from the provider
|
|
827
|
-
// baseline's implied speed. 0.5s ≈ fast tier, 10s+ ≈ heavyweight.
|
|
828
901
|
try {
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
}
|
|
838
|
-
}
|
|
902
|
+
const record = getModelRegistry().getCapability(provider, model);
|
|
903
|
+
const accuracy = effectiveParameter(record, 'accuracy', base.reasoning);
|
|
904
|
+
const performance = effectiveParameter(record, 'performance', base.speed);
|
|
905
|
+
return {
|
|
906
|
+
...base,
|
|
907
|
+
reasoning: Math.min(1, Math.max(0, accuracy.value)),
|
|
908
|
+
speed: Math.min(1, Math.max(0, performance.value)),
|
|
909
|
+
};
|
|
839
910
|
}
|
|
840
911
|
catch {
|
|
841
|
-
// Registry unavailable —
|
|
912
|
+
// Registry unavailable — the provider baseline stands (never a race for a
|
|
913
|
+
// capability read: the gate must behave exactly as it did before).
|
|
914
|
+
return base;
|
|
842
915
|
}
|
|
843
|
-
return {
|
|
844
|
-
...base,
|
|
845
|
-
reasoning: Math.min(1, Math.max(0, base.reasoning + reasoningAdj)),
|
|
846
|
-
speed: Math.min(1, Math.max(0, base.speed + speedAdj)),
|
|
847
|
-
};
|
|
848
916
|
}
|
|
849
917
|
/**
|
|
850
918
|
* Default candidate providers — DYNAMIC (Issue 001): every provider the user
|
|
@@ -1548,6 +1616,17 @@ export class AutoModelRouter {
|
|
|
1548
1616
|
let routedBy = 'heuristic';
|
|
1549
1617
|
let banditEscalation = false;
|
|
1550
1618
|
let escalatedProvider;
|
|
1619
|
+
/**
|
|
1620
|
+
* P6 (fix_model_routing) — did the bandit's data actually inform this pick?
|
|
1621
|
+
*
|
|
1622
|
+
* `routedBy = 'bandit'` is set the moment `useBandit` is on, and the
|
|
1623
|
+
* explanation then says `bandit-learned` — even when the arm has no samples
|
|
1624
|
+
* at all (a cold-start prior, whose constant 0.5 multiplier cannot reorder
|
|
1625
|
+
* anything) and even when the whole store is stale. The label is a claim
|
|
1626
|
+
* about evidence, so it is only made when there IS evidence: samples for the
|
|
1627
|
+
* winning arm, or a real uncertainty-driven escalation.
|
|
1628
|
+
*/
|
|
1629
|
+
let banditInformed = false;
|
|
1551
1630
|
if (options.useBandit) {
|
|
1552
1631
|
const bandit = getRouterBandit();
|
|
1553
1632
|
// v3 — the learning bucket is task-INTENT-aware. Derive the intent from
|
|
@@ -1578,6 +1657,13 @@ export class AutoModelRouter {
|
|
|
1578
1657
|
const minSamples = options.escalationMinSamples ?? DEFAULT_MIN_SAMPLES;
|
|
1579
1658
|
const winner = scored.find((s) => !s.inCooldown) || scored[0];
|
|
1580
1659
|
const winnerPrior = bandit.getPrior(winner.provider, complexity, analyzedProfile.intent);
|
|
1660
|
+
// The concrete model is resolved LATER in this method (after the model-first
|
|
1661
|
+
// override), so the verdict is made on the provider arm — the arm every
|
|
1662
|
+
// provider-level sample lands in, and the one the pre-model-first pick is
|
|
1663
|
+
// actually driven by.
|
|
1664
|
+
banditInformed =
|
|
1665
|
+
!bandit.isStale() &&
|
|
1666
|
+
bandit.hasLearnedData(winner.provider, complexity, analyzedProfile.intent);
|
|
1581
1667
|
if (winnerPrior.alpha + winnerPrior.beta < minSamples) {
|
|
1582
1668
|
// S5: never escalate DOWNWARD in capability. The bandit's learned
|
|
1583
1669
|
// priors come from past sessions (often coding-heavy local usage), so
|
|
@@ -1597,6 +1683,9 @@ export class AutoModelRouter {
|
|
|
1597
1683
|
})());
|
|
1598
1684
|
if (learnedAlternative) {
|
|
1599
1685
|
banditEscalation = true;
|
|
1686
|
+
// An escalation IS learned behaviour by construction — it fired
|
|
1687
|
+
// BECAUSE the alternative has accumulated data.
|
|
1688
|
+
banditInformed = !bandit.isStale();
|
|
1600
1689
|
escalatedProvider = learnedAlternative.provider;
|
|
1601
1690
|
if (options.verbose) {
|
|
1602
1691
|
logger.info(` 🎲 Bandit uncertainty: ${winner.provider} has no learning data (α+β=${winnerPrior.alpha + winnerPrior.beta}) — escalating to learned ${learnedAlternative.provider} (win-rate ≥ ${ESCALATION_WIN_RATE_FLOOR})`);
|
|
@@ -1733,6 +1822,78 @@ export class AutoModelRouter {
|
|
|
1733
1822
|
catch {
|
|
1734
1823
|
// Best-effort — model-first must never break routing
|
|
1735
1824
|
}
|
|
1825
|
+
// ── PAIR ENTITLEMENT: is the pick ALLOWED to be called? (D7) ────────────
|
|
1826
|
+
// The user's rule, verbatim: "suppose we have 4 providers and everyone
|
|
1827
|
+
// provides deepseek — only one provider's key actually provides deepseek,
|
|
1828
|
+
// where as the user has not purchased model access on the other providers or
|
|
1829
|
+
// there is no free tokens offered by those providers… the rank of a model can
|
|
1830
|
+
// be the same, but the agent should only pick, or only be allowed to access,
|
|
1831
|
+
// the one which has a genuine token budget available, rather than going for
|
|
1832
|
+
// rounds unnecessarily."
|
|
1833
|
+
//
|
|
1834
|
+
// Ranking cannot express that, and it should not try: rank is a property of
|
|
1835
|
+
// the MODEL, while access is a property of the ACCOUNT serving it. So the
|
|
1836
|
+
// pick made above is re-checked against the registry's OWN evidence for
|
|
1837
|
+
// exactly this pair, and when that evidence is a refusal the router looks for
|
|
1838
|
+
// the same model on a provider that can actually serve it. Two ordered
|
|
1839
|
+
// attempts, then out:
|
|
1840
|
+
// 1. a FUNDED TWIN of the same model (the reported case: identical rank,
|
|
1841
|
+
// one funded account) — chosen through `getAllUsablePairs()`, which is
|
|
1842
|
+
// built on the SAME `isUsable()` gate routing uses, so this can never
|
|
1843
|
+
// name a pair the router would itself reject;
|
|
1844
|
+
// 2. otherwise the top-ranked provider whose served pair is not refused, so
|
|
1845
|
+
// a dead pin on one provider does not cost a round trip before the
|
|
1846
|
+
// fallback walk starts.
|
|
1847
|
+
// If neither exists the original pick stands — a refusal we cannot route
|
|
1848
|
+
// around must not become "return no route at all".
|
|
1849
|
+
let entitlementRescue;
|
|
1850
|
+
try {
|
|
1851
|
+
const registry = getModelRegistry();
|
|
1852
|
+
if (classifyPairEntitlement(registry.getEntry(provider, model)) === 'refused') {
|
|
1853
|
+
const from = `${provider}/${model}`;
|
|
1854
|
+
const fundedTwin = registry
|
|
1855
|
+
.getAllUsablePairs()
|
|
1856
|
+
.find((p) => p.provider !== provider && areTwins(p.model, model));
|
|
1857
|
+
if (fundedTwin) {
|
|
1858
|
+
provider = fundedTwin.provider;
|
|
1859
|
+
model = fundedTwin.model;
|
|
1860
|
+
entitlementRescue = {
|
|
1861
|
+
from,
|
|
1862
|
+
to: `${provider}/${model}`,
|
|
1863
|
+
why: 'the pinned pair is refused by its account; the same model is funded here',
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
else {
|
|
1867
|
+
const alternative = scored.find((s) => {
|
|
1868
|
+
if (s.inCooldown)
|
|
1869
|
+
return false;
|
|
1870
|
+
if (s.provider === provider)
|
|
1871
|
+
return false;
|
|
1872
|
+
try {
|
|
1873
|
+
return (classifyPairEntitlement(registry.getEntry(s.provider, this.resolveModel(s.provider, agentType, configManager, taskDescription))) !== 'refused');
|
|
1874
|
+
}
|
|
1875
|
+
catch {
|
|
1876
|
+
return false;
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
if (alternative) {
|
|
1880
|
+
provider = alternative.provider;
|
|
1881
|
+
model = this.resolveModel(provider, agentType, configManager, taskDescription);
|
|
1882
|
+
entitlementRescue = {
|
|
1883
|
+
from,
|
|
1884
|
+
to: `${provider}/${model}`,
|
|
1885
|
+
why: 'the selected pair is refused by its account; this provider is not',
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (entitlementRescue && options.verbose) {
|
|
1890
|
+
logger.warn(` ⛔ Pair entitlement: ${entitlementRescue.from} cannot serve this model — using ${entitlementRescue.to} (${entitlementRescue.why})`);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
catch {
|
|
1895
|
+
// Best-effort — the entitlement gate must never break routing.
|
|
1896
|
+
}
|
|
1736
1897
|
// Note the decision so outcome recording (recordOutcome) can reward the
|
|
1737
1898
|
// provider that actually served the task.
|
|
1738
1899
|
if (options.useBandit) {
|
|
@@ -1766,22 +1927,76 @@ export class AutoModelRouter {
|
|
|
1766
1927
|
// burning through all of its fallbacks.
|
|
1767
1928
|
const fallbackChain = [];
|
|
1768
1929
|
const seenFallback = new Set();
|
|
1930
|
+
/**
|
|
1931
|
+
* D6 — THE OFFERED CHAIN OBEYS THE SAME GATE THE PICK DOES.
|
|
1932
|
+
*
|
|
1933
|
+
* "Every provider contributes MULTIPLE models" is right, but the models it
|
|
1934
|
+
* contributed were never asked whether they could work. Measured on a live
|
|
1935
|
+
* `model explain`: the chain offered `openrouter/~deepseek/deepseek-pro-latest`
|
|
1936
|
+
* and `openrouter/~deepseek/deepseek-flash-latest` — both `unverified` alias
|
|
1937
|
+
* ids — with the same confident `Fallback (alternate model on openrouter)`
|
|
1938
|
+
* label as a proven model. A pair the registry has already proven dead
|
|
1939
|
+
* (`unavailable` — a 404, a dead key, an unfunded account) was offered the
|
|
1940
|
+
* same way, and `model list`/`models explain` are where an operator decides
|
|
1941
|
+
* what to pin next.
|
|
1942
|
+
*
|
|
1943
|
+
* Two different verdicts, two different treatments, and the distinction is
|
|
1944
|
+
* the product's own doctrine (`model-reachability.ts`):
|
|
1945
|
+
* - `unavailable` is a NO. It is not offered at all — recommending a pair a
|
|
1946
|
+
* real call already established cannot work is telling the operator to
|
|
1947
|
+
* walk into a wall.
|
|
1948
|
+
* - `unverified` is an UNKNOWN, not a failure. It stays (a weak candidate
|
|
1949
|
+
* beats no candidate, and "nothing tried yet" is exactly what a reserve
|
|
1950
|
+
* pool is for) but it is LABELLED as unproven, so the confidence in the
|
|
1951
|
+
* label matches the evidence behind it.
|
|
1952
|
+
*
|
|
1953
|
+
* The reserve passes below already label themselves this way; passes 1–3 did
|
|
1954
|
+
* not, which is where the unproven ids leaked in.
|
|
1955
|
+
*
|
|
1956
|
+
* D7 (2026-10-07) — THE PAIR'S RIGHT TO BE CALLED, not just its status.
|
|
1957
|
+
* The gate above reads `status`, and `status` cannot see the case the user
|
|
1958
|
+
* reported: four providers offer the same model, one key has access to it,
|
|
1959
|
+
* and the other three have no model access and no free tokens. A refusal like
|
|
1960
|
+
* that does NOT demote a `verified` entry (the registry keeps the latch
|
|
1961
|
+
* because a top-up repairs it), so such a pair reads `verified` with a `402`
|
|
1962
|
+
* in `lastError` — offerable by the old check. `classifyPairEntitlement`
|
|
1963
|
+
* decides from the EVIDENCE (this pair's own row) instead, and a refused pair
|
|
1964
|
+
* is not offered at all while anything else can be. Read it per PAIR: twins
|
|
1965
|
+
* on different providers are different ACCOUNTS with opposite verdicts, and
|
|
1966
|
+
* neither inherits the other's.
|
|
1967
|
+
*/
|
|
1769
1968
|
const pushFallback = (s, model, alternate) => {
|
|
1770
1969
|
const key = `${s.provider}|${model}`;
|
|
1771
1970
|
if (seenFallback.has(key))
|
|
1772
1971
|
return;
|
|
1972
|
+
let entry;
|
|
1973
|
+
try {
|
|
1974
|
+
entry = getModelRegistry().getEntry(s.provider, model);
|
|
1975
|
+
}
|
|
1976
|
+
catch {
|
|
1977
|
+
// Best-effort — the reachability gate must never break routing.
|
|
1978
|
+
}
|
|
1979
|
+
const entitlement = classifyPairEntitlement(entry);
|
|
1980
|
+
// A refusal is a NO (D7); an `unavailable` entry for any other reason is
|
|
1981
|
+
// the reachability NO the D6 gate already encoded.
|
|
1982
|
+
if (entitlement === 'refused' || entry?.status === 'unavailable')
|
|
1983
|
+
return;
|
|
1773
1984
|
seenFallback.add(key);
|
|
1985
|
+
const unproven = entitlement === 'unknown';
|
|
1986
|
+
const stalledNote = entitlement === 'stalled' ? ' (stalled — proven once, not servable right now)' : '';
|
|
1774
1987
|
fallbackChain.push({
|
|
1775
1988
|
provider: s.provider,
|
|
1776
1989
|
model,
|
|
1777
1990
|
estimatedCost: 0,
|
|
1778
1991
|
qualityScore: s.score,
|
|
1779
1992
|
contextWindowTokens: s.contextWindowTokens,
|
|
1780
|
-
reason:
|
|
1781
|
-
? `Fallback (alternate model on ${s.provider})`
|
|
1782
|
-
:
|
|
1783
|
-
? `Fallback (
|
|
1784
|
-
:
|
|
1993
|
+
reason: unproven
|
|
1994
|
+
? `Fallback (unverified — not yet proven${alternate ? `, alternate model on ${s.provider}` : `: ${s.provider}`})`
|
|
1995
|
+
: alternate
|
|
1996
|
+
? `Fallback (alternate model on ${s.provider})${stalledNote}`
|
|
1997
|
+
: s.inCooldown
|
|
1998
|
+
? `Fallback (in cooldown): ${s.provider}${stalledNote}`
|
|
1999
|
+
: `Fallback: ${s.provider}${stalledNote}`,
|
|
1785
2000
|
});
|
|
1786
2001
|
};
|
|
1787
2002
|
const modelsFor = (s) => this.fallbackModelsFor(s.provider, agentType, configManager, taskDescription, complexity);
|
|
@@ -1880,7 +2095,19 @@ export class AutoModelRouter {
|
|
|
1880
2095
|
}
|
|
1881
2096
|
const explanation = this.buildExplanation(agentType, complexity, taskType, provider, decisionScore, mode, model, weights, taskProfile) +
|
|
1882
2097
|
(weakModelForAgenticAsk ? ' | ⚠ weak-model fallback (no capable provider free)' : '') +
|
|
1883
|
-
(
|
|
2098
|
+
(entitlementRescue
|
|
2099
|
+
? ` | ⛔ ${entitlementRescue.from} is refused by its account — served by ${entitlementRescue.to}`
|
|
2100
|
+
: '') +
|
|
2101
|
+
// P6 — say what actually happened. A cold-start or stale store gets NO
|
|
2102
|
+
// claim of learning: the deterministic ranking stands, and the user is told
|
|
2103
|
+
// that instead of being told "learned" about a prior nobody has ever
|
|
2104
|
+
// updated. Silent-about-nothing would be worse still — the absence of the
|
|
2105
|
+
// old label is only honest if the reason is named.
|
|
2106
|
+
(routedBy === 'bandit'
|
|
2107
|
+
? banditInformed
|
|
2108
|
+
? ' | bandit-learned'
|
|
2109
|
+
: ' | bandit cold-start (no learned samples for this task bucket — the deterministic ranking stands)'
|
|
2110
|
+
: '') +
|
|
1884
2111
|
(banditEscalation ? ' | escalated: winner unlearned' : '') +
|
|
1885
2112
|
// ISSUE-002 explanation transparency: cite the registry data that
|
|
1886
2113
|
// excluded providers from the candidate pool, so auto routing proves
|
|
@@ -1934,6 +2161,8 @@ export class AutoModelRouter {
|
|
|
1934
2161
|
agenticCapable: isAgenticCapableModel(model, provider),
|
|
1935
2162
|
overrideReason,
|
|
1936
2163
|
banditEscalation,
|
|
2164
|
+
// P6 — whether the bandit's data informed this pick (see `banditInformed`).
|
|
2165
|
+
banditInformed,
|
|
1937
2166
|
governanceBlocked,
|
|
1938
2167
|
registryExcluded,
|
|
1939
2168
|
contextPreflight,
|
|
@@ -2004,6 +2233,15 @@ export class AutoModelRouter {
|
|
|
2004
2233
|
* @param outcome success | failure | escalated
|
|
2005
2234
|
* @param configManager Optional — used to resolve per-provider pricing
|
|
2006
2235
|
* overrides when computing the cost-adjusted reward
|
|
2236
|
+
* @param outcomeData The MEASURED outcome telemetry for the reward model —
|
|
2237
|
+
* `testPassed` / `userAccepted` / `verificationPassed` /
|
|
2238
|
+
* `qualityScore`. Typed as the bandit's own
|
|
2239
|
+
* `Partial<BanditOutcomeData>` so the harness cannot
|
|
2240
|
+
* collect a signal this path has no parameter for: the
|
|
2241
|
+
* three honesty fields previously had nowhere to go, so
|
|
2242
|
+
* every caller passed `undefined` and the reward model
|
|
2243
|
+
* they feed — the ONLY measured-quality input the router
|
|
2244
|
+
* has — was dead on the real path.
|
|
2007
2245
|
*/
|
|
2008
2246
|
recordOutcome(agentType, taskDescription, outcome, configManager, outcomeData, complexityHint) {
|
|
2009
2247
|
const bandit = getRouterBandit();
|
|
@@ -2017,20 +2255,20 @@ export class AutoModelRouter {
|
|
|
2017
2255
|
// versa. Mirrors the select-time derivation exactly.
|
|
2018
2256
|
const learnIntent = analyzeTaskProfile(taskDescription).intent;
|
|
2019
2257
|
if (complexityHint) {
|
|
2020
|
-
bandit.recordOutcomeWithComplexity(provider, complexityHint, outcome, costScore,
|
|
2258
|
+
bandit.recordOutcomeWithComplexity(provider, complexityHint, outcome, costScore, outcomeData, learnIntent);
|
|
2021
2259
|
}
|
|
2022
2260
|
else {
|
|
2023
|
-
bandit.recordOutcome(provider, taskDescription, outcome, costScore,
|
|
2261
|
+
bandit.recordOutcome(provider, taskDescription, outcome, costScore, outcomeData, learnIntent);
|
|
2024
2262
|
}
|
|
2025
2263
|
// Per-modelId learning: attribute the same outcome to the concrete model
|
|
2026
2264
|
// that served the task (ruflo ADR-149 mirror) so the model choice learns.
|
|
2027
2265
|
const model = bandit.getLastModel(agentType);
|
|
2028
2266
|
if (model) {
|
|
2029
2267
|
if (complexityHint) {
|
|
2030
|
-
bandit.recordModelOutcomeWithComplexity(model, complexityHint, outcome, costScore,
|
|
2268
|
+
bandit.recordModelOutcomeWithComplexity(model, complexityHint, outcome, costScore, outcomeData, learnIntent);
|
|
2031
2269
|
}
|
|
2032
2270
|
else {
|
|
2033
|
-
bandit.recordModelOutcome(model, taskDescription, outcome, costScore,
|
|
2271
|
+
bandit.recordModelOutcome(model, taskDescription, outcome, costScore, outcomeData, learnIntent);
|
|
2034
2272
|
}
|
|
2035
2273
|
}
|
|
2036
2274
|
// Promotion gate: finalize the parallel A/B decision with the real outcome
|
|
@@ -2053,6 +2291,46 @@ export class AutoModelRouter {
|
|
|
2053
2291
|
catch {
|
|
2054
2292
|
// Best-effort — ML recording must never break outcome handling.
|
|
2055
2293
|
}
|
|
2294
|
+
// Remember WHAT this outcome was attributed to, so a correction arriving with
|
|
2295
|
+
// the NEXT user message lands on the turn it is about rather than on whoever
|
|
2296
|
+
// serves the next turn. Overwritten by the next recordOutcome, which is exactly
|
|
2297
|
+
// right: a correction speaks about the most recent judged turn.
|
|
2298
|
+
this.pendingOutcome = {
|
|
2299
|
+
agentType,
|
|
2300
|
+
provider,
|
|
2301
|
+
model: bandit.getLastModel(agentType),
|
|
2302
|
+
complexity: complexityHint ?? analyzeComplexity(taskDescription),
|
|
2303
|
+
intent: analyzeTaskProfile(taskDescription).intent,
|
|
2304
|
+
outcome,
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Apply the user's verdict on the PREVIOUS turn.
|
|
2309
|
+
*
|
|
2310
|
+
* A regression report in the user's next message ("still broken", "no change",
|
|
2311
|
+
* "doesn't work") IS `userAccepted: false` for the turn that just ran — the one
|
|
2312
|
+
* measured-quality input the reward model has that nothing on the real path ever
|
|
2313
|
+
* filled in. It cannot be part of that turn's own observation (it does not exist
|
|
2314
|
+
* until the user speaks again), so it is applied here, to the pair captured at
|
|
2315
|
+
* record time, as the exact deferred delta (see `RouterBandit.recordUserRejection`).
|
|
2316
|
+
*
|
|
2317
|
+
* @returns True when a correction was applied to a still-pending outcome.
|
|
2318
|
+
*/
|
|
2319
|
+
recordUserRejection(agentType) {
|
|
2320
|
+
const pending = this.pendingOutcome;
|
|
2321
|
+
if (!pending || pending.agentType !== agentType)
|
|
2322
|
+
return false;
|
|
2323
|
+
// Clear FIRST: one correction must never be applied twice, even if the bandit
|
|
2324
|
+
// write throws.
|
|
2325
|
+
this.pendingOutcome = null;
|
|
2326
|
+
try {
|
|
2327
|
+
getRouterBandit().recordUserRejection(pending.provider, pending.complexity, pending.outcome, pending.intent, pending.model);
|
|
2328
|
+
}
|
|
2329
|
+
catch {
|
|
2330
|
+
// Learning is best-effort — never break a turn on a bandit error.
|
|
2331
|
+
return false;
|
|
2332
|
+
}
|
|
2333
|
+
return true;
|
|
2056
2334
|
}
|
|
2057
2335
|
/**
|
|
2058
2336
|
* Choose the concrete model within the selected provider using per-model
|