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.
Files changed (458) hide show
  1. package/README.md +67 -1
  2. package/dist/agents/agents/planner.d.ts.map +1 -1
  3. package/dist/agents/agents/planner.js +9 -0
  4. package/dist/agents/agents/planner.js.map +1 -1
  5. package/dist/agents/agents/reasoner.d.ts.map +1 -1
  6. package/dist/agents/agents/reasoner.js +13 -0
  7. package/dist/agents/agents/reasoner.js.map +1 -1
  8. package/dist/agents/orchestrator.d.ts +31 -0
  9. package/dist/agents/orchestrator.d.ts.map +1 -1
  10. package/dist/agents/orchestrator.js +103 -135
  11. package/dist/agents/orchestrator.js.map +1 -1
  12. package/dist/cli/chat.d.ts +89 -0
  13. package/dist/cli/chat.d.ts.map +1 -1
  14. package/dist/cli/chat.js +798 -39
  15. package/dist/cli/chat.js.map +1 -1
  16. package/dist/cli/cli-program.d.ts.map +1 -1
  17. package/dist/cli/cli-program.js +20 -0
  18. package/dist/cli/cli-program.js.map +1 -1
  19. package/dist/cli/config.d.ts +12 -0
  20. package/dist/cli/config.d.ts.map +1 -1
  21. package/dist/cli/config.js +107 -3
  22. package/dist/cli/config.js.map +1 -1
  23. package/dist/cli/decisions.d.ts +28 -0
  24. package/dist/cli/decisions.d.ts.map +1 -0
  25. package/dist/cli/decisions.js +140 -0
  26. package/dist/cli/decisions.js.map +1 -0
  27. package/dist/cli/doctor.d.ts +6 -0
  28. package/dist/cli/doctor.d.ts.map +1 -1
  29. package/dist/cli/doctor.js +46 -0
  30. package/dist/cli/doctor.js.map +1 -1
  31. package/dist/cli/execute.d.ts.map +1 -1
  32. package/dist/cli/execute.js +6 -0
  33. package/dist/cli/execute.js.map +1 -1
  34. package/dist/cli/failover-runner.d.ts.map +1 -1
  35. package/dist/cli/failover-runner.js +19 -1
  36. package/dist/cli/failover-runner.js.map +1 -1
  37. package/dist/cli/feedback.d.ts.map +1 -1
  38. package/dist/cli/feedback.js +5 -1
  39. package/dist/cli/feedback.js.map +1 -1
  40. package/dist/cli/hooks.d.ts +30 -0
  41. package/dist/cli/hooks.d.ts.map +1 -0
  42. package/dist/cli/hooks.js +224 -0
  43. package/dist/cli/hooks.js.map +1 -0
  44. package/dist/cli/knowledge.d.ts +31 -0
  45. package/dist/cli/knowledge.d.ts.map +1 -0
  46. package/dist/cli/knowledge.js +223 -0
  47. package/dist/cli/knowledge.js.map +1 -0
  48. package/dist/cli/loop-executor.d.ts.map +1 -1
  49. package/dist/cli/loop-executor.js +92 -18
  50. package/dist/cli/loop-executor.js.map +1 -1
  51. package/dist/cli/model.d.ts +20 -0
  52. package/dist/cli/model.d.ts.map +1 -1
  53. package/dist/cli/model.js +294 -14
  54. package/dist/cli/model.js.map +1 -1
  55. package/dist/cli/omniroute-control.d.ts +88 -0
  56. package/dist/cli/omniroute-control.d.ts.map +1 -0
  57. package/dist/cli/omniroute-control.js +179 -0
  58. package/dist/cli/omniroute-control.js.map +1 -0
  59. package/dist/cli/omniroute.d.ts +21 -0
  60. package/dist/cli/omniroute.d.ts.map +1 -0
  61. package/dist/cli/omniroute.js +67 -0
  62. package/dist/cli/omniroute.js.map +1 -0
  63. package/dist/cli/rate.d.ts +78 -0
  64. package/dist/cli/rate.d.ts.map +1 -0
  65. package/dist/cli/rate.js +294 -0
  66. package/dist/cli/rate.js.map +1 -0
  67. package/dist/config/process-env.d.ts.map +1 -1
  68. package/dist/config/process-env.js +10 -0
  69. package/dist/config/process-env.js.map +1 -1
  70. package/dist/config/types.d.ts +8 -0
  71. package/dist/config/types.d.ts.map +1 -1
  72. package/dist/context/history.d.ts +21 -0
  73. package/dist/context/history.d.ts.map +1 -1
  74. package/dist/context/history.js +50 -0
  75. package/dist/context/history.js.map +1 -1
  76. package/dist/enterprise/vault.d.ts.map +1 -1
  77. package/dist/enterprise/vault.js +5 -21
  78. package/dist/enterprise/vault.js.map +1 -1
  79. package/dist/gateway/hook-contract.d.ts +206 -0
  80. package/dist/gateway/hook-contract.d.ts.map +1 -0
  81. package/dist/gateway/hook-contract.js +449 -0
  82. package/dist/gateway/hook-contract.js.map +1 -0
  83. package/dist/gateway/hooks.d.ts.map +1 -1
  84. package/dist/gateway/hooks.js +14 -0
  85. package/dist/gateway/hooks.js.map +1 -1
  86. package/dist/inference/gemini-adapter.d.ts.map +1 -1
  87. package/dist/inference/gemini-adapter.js +4 -0
  88. package/dist/inference/gemini-adapter.js.map +1 -1
  89. package/dist/inference/interface.d.ts +54 -0
  90. package/dist/inference/interface.d.ts.map +1 -1
  91. package/dist/inference/model-id-validation.d.ts +57 -0
  92. package/dist/inference/model-id-validation.d.ts.map +1 -0
  93. package/dist/inference/model-id-validation.js +127 -0
  94. package/dist/inference/model-id-validation.js.map +1 -0
  95. package/dist/inference/native-tools.d.ts +16 -1
  96. package/dist/inference/native-tools.d.ts.map +1 -1
  97. package/dist/inference/native-tools.js +34 -10
  98. package/dist/inference/native-tools.js.map +1 -1
  99. package/dist/inference/openai-compat-adapter.d.ts.map +1 -1
  100. package/dist/inference/openai-compat-adapter.js +12 -1
  101. package/dist/inference/openai-compat-adapter.js.map +1 -1
  102. package/dist/inference/provider-catalog.d.ts +11 -0
  103. package/dist/inference/provider-catalog.d.ts.map +1 -1
  104. package/dist/inference/provider-catalog.js +80 -0
  105. package/dist/inference/provider-catalog.js.map +1 -1
  106. package/dist/inference/route-resolver.d.ts +32 -8
  107. package/dist/inference/route-resolver.d.ts.map +1 -1
  108. package/dist/inference/route-resolver.js +175 -1
  109. package/dist/inference/route-resolver.js.map +1 -1
  110. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  111. package/dist/inference/tool-call-utils.js +10 -8
  112. package/dist/inference/tool-call-utils.js.map +1 -1
  113. package/dist/inference/tools.d.ts +17 -1
  114. package/dist/inference/tools.d.ts.map +1 -1
  115. package/dist/inference/tools.js +69 -19
  116. package/dist/inference/tools.js.map +1 -1
  117. package/dist/learning/acceptance-model.d.ts +247 -0
  118. package/dist/learning/acceptance-model.d.ts.map +1 -0
  119. package/dist/learning/acceptance-model.js +708 -0
  120. package/dist/learning/acceptance-model.js.map +1 -0
  121. package/dist/learning/auto-router.d.ts +121 -24
  122. package/dist/learning/auto-router.d.ts.map +1 -1
  123. package/dist/learning/auto-router.js +374 -96
  124. package/dist/learning/auto-router.js.map +1 -1
  125. package/dist/learning/autonomy-policy.d.ts +12 -16
  126. package/dist/learning/autonomy-policy.d.ts.map +1 -1
  127. package/dist/learning/autonomy-policy.js +50 -48
  128. package/dist/learning/autonomy-policy.js.map +1 -1
  129. package/dist/learning/behavioural-labels.d.ts +86 -0
  130. package/dist/learning/behavioural-labels.d.ts.map +1 -0
  131. package/dist/learning/behavioural-labels.js +166 -0
  132. package/dist/learning/behavioural-labels.js.map +1 -0
  133. package/dist/learning/capability-evidence.d.ts +167 -0
  134. package/dist/learning/capability-evidence.d.ts.map +1 -0
  135. package/dist/learning/capability-evidence.js +210 -0
  136. package/dist/learning/capability-evidence.js.map +1 -0
  137. package/dist/learning/capability-readiness.d.ts +64 -0
  138. package/dist/learning/capability-readiness.d.ts.map +1 -0
  139. package/dist/learning/capability-readiness.js +74 -0
  140. package/dist/learning/capability-readiness.js.map +1 -0
  141. package/dist/learning/capability-types.d.ts +169 -0
  142. package/dist/learning/capability-types.d.ts.map +1 -0
  143. package/dist/learning/capability-types.js +20 -0
  144. package/dist/learning/capability-types.js.map +1 -0
  145. package/dist/learning/catalog-feed.d.ts +89 -0
  146. package/dist/learning/catalog-feed.d.ts.map +1 -0
  147. package/dist/learning/catalog-feed.js +211 -0
  148. package/dist/learning/catalog-feed.js.map +1 -0
  149. package/dist/learning/cli-intent-effects.d.ts +60 -0
  150. package/dist/learning/cli-intent-effects.d.ts.map +1 -0
  151. package/dist/learning/cli-intent-effects.js +153 -0
  152. package/dist/learning/cli-intent-effects.js.map +1 -0
  153. package/dist/learning/command-adaptation.d.ts +41 -0
  154. package/dist/learning/command-adaptation.d.ts.map +1 -0
  155. package/dist/learning/command-adaptation.js +122 -0
  156. package/dist/learning/command-adaptation.js.map +1 -0
  157. package/dist/learning/consent-picture.d.ts +42 -0
  158. package/dist/learning/consent-picture.d.ts.map +1 -0
  159. package/dist/learning/consent-picture.js +53 -0
  160. package/dist/learning/consent-picture.js.map +1 -0
  161. package/dist/learning/context-budget.d.ts +37 -6
  162. package/dist/learning/context-budget.d.ts.map +1 -1
  163. package/dist/learning/context-budget.js +38 -9
  164. package/dist/learning/context-budget.js.map +1 -1
  165. package/dist/learning/decision-log.d.ts +114 -0
  166. package/dist/learning/decision-log.d.ts.map +1 -0
  167. package/dist/learning/decision-log.js +296 -0
  168. package/dist/learning/decision-log.js.map +1 -0
  169. package/dist/learning/deliverable-class.d.ts.map +1 -1
  170. package/dist/learning/deliverable-class.js +19 -0
  171. package/dist/learning/deliverable-class.js.map +1 -1
  172. package/dist/learning/deliverable-corpus.d.ts +117 -0
  173. package/dist/learning/deliverable-corpus.d.ts.map +1 -0
  174. package/dist/learning/deliverable-corpus.js +176 -0
  175. package/dist/learning/deliverable-corpus.js.map +1 -0
  176. package/dist/learning/error-repair.d.ts +45 -2
  177. package/dist/learning/error-repair.d.ts.map +1 -1
  178. package/dist/learning/error-repair.js +95 -0
  179. package/dist/learning/error-repair.js.map +1 -1
  180. package/dist/learning/exhaustion-report.d.ts +136 -0
  181. package/dist/learning/exhaustion-report.d.ts.map +1 -0
  182. package/dist/learning/exhaustion-report.js +239 -0
  183. package/dist/learning/exhaustion-report.js.map +1 -0
  184. package/dist/learning/failure-bookkeeping.d.ts +16 -0
  185. package/dist/learning/failure-bookkeeping.d.ts.map +1 -1
  186. package/dist/learning/failure-bookkeeping.js +51 -5
  187. package/dist/learning/failure-bookkeeping.js.map +1 -1
  188. package/dist/learning/gate-friction.d.ts +39 -0
  189. package/dist/learning/gate-friction.d.ts.map +1 -0
  190. package/dist/learning/gate-friction.js +91 -0
  191. package/dist/learning/gate-friction.js.map +1 -0
  192. package/dist/learning/harness-narration.d.ts +34 -0
  193. package/dist/learning/harness-narration.d.ts.map +1 -0
  194. package/dist/learning/harness-narration.js +58 -0
  195. package/dist/learning/harness-narration.js.map +1 -0
  196. package/dist/learning/hub-skill-catalog.d.ts.map +1 -1
  197. package/dist/learning/hub-skill-catalog.js +1 -2
  198. package/dist/learning/hub-skill-catalog.js.map +1 -1
  199. package/dist/learning/hybrid-router.d.ts +65 -0
  200. package/dist/learning/hybrid-router.d.ts.map +1 -1
  201. package/dist/learning/hybrid-router.js +186 -0
  202. package/dist/learning/hybrid-router.js.map +1 -1
  203. package/dist/learning/knowledge-base.d.ts +350 -0
  204. package/dist/learning/knowledge-base.d.ts.map +1 -0
  205. package/dist/learning/knowledge-base.js +906 -0
  206. package/dist/learning/knowledge-base.js.map +1 -0
  207. package/dist/learning/knowledge-eval.d.ts +171 -0
  208. package/dist/learning/knowledge-eval.d.ts.map +1 -0
  209. package/dist/learning/knowledge-eval.js +196 -0
  210. package/dist/learning/knowledge-eval.js.map +1 -0
  211. package/dist/learning/knowledge-turn.d.ts +92 -0
  212. package/dist/learning/knowledge-turn.d.ts.map +1 -0
  213. package/dist/learning/knowledge-turn.js +190 -0
  214. package/dist/learning/knowledge-turn.js.map +1 -0
  215. package/dist/learning/learned-commands.d.ts +64 -0
  216. package/dist/learning/learned-commands.d.ts.map +1 -0
  217. package/dist/learning/learned-commands.js +108 -0
  218. package/dist/learning/learned-commands.js.map +1 -0
  219. package/dist/learning/lexical-search.d.ts +50 -0
  220. package/dist/learning/lexical-search.d.ts.map +1 -0
  221. package/dist/learning/lexical-search.js +111 -0
  222. package/dist/learning/lexical-search.js.map +1 -0
  223. package/dist/learning/machine-facts.d.ts +71 -0
  224. package/dist/learning/machine-facts.d.ts.map +1 -0
  225. package/dist/learning/machine-facts.js +164 -0
  226. package/dist/learning/machine-facts.js.map +1 -0
  227. package/dist/learning/markdown-outline.d.ts +73 -0
  228. package/dist/learning/markdown-outline.d.ts.map +1 -0
  229. package/dist/learning/markdown-outline.js +136 -0
  230. package/dist/learning/markdown-outline.js.map +1 -0
  231. package/dist/learning/model-first-router.d.ts +33 -1
  232. package/dist/learning/model-first-router.d.ts.map +1 -1
  233. package/dist/learning/model-first-router.js +123 -9
  234. package/dist/learning/model-first-router.js.map +1 -1
  235. package/dist/learning/model-identity.d.ts +80 -0
  236. package/dist/learning/model-identity.d.ts.map +1 -0
  237. package/dist/learning/model-identity.js +115 -0
  238. package/dist/learning/model-identity.js.map +1 -0
  239. package/dist/learning/model-registry.d.ts +141 -2
  240. package/dist/learning/model-registry.d.ts.map +1 -1
  241. package/dist/learning/model-registry.js +324 -38
  242. package/dist/learning/model-registry.js.map +1 -1
  243. package/dist/learning/model-scoring.d.ts.map +1 -1
  244. package/dist/learning/model-scoring.js +8 -1
  245. package/dist/learning/model-scoring.js.map +1 -1
  246. package/dist/learning/model-selection.d.ts +2 -1
  247. package/dist/learning/model-selection.d.ts.map +1 -1
  248. package/dist/learning/model-selection.js +76 -2
  249. package/dist/learning/model-selection.js.map +1 -1
  250. package/dist/learning/outcome-observation.d.ts +49 -0
  251. package/dist/learning/outcome-observation.d.ts.map +1 -0
  252. package/dist/learning/outcome-observation.js +95 -0
  253. package/dist/learning/outcome-observation.js.map +1 -0
  254. package/dist/learning/pair-entitlement.d.ts +184 -0
  255. package/dist/learning/pair-entitlement.d.ts.map +1 -0
  256. package/dist/learning/pair-entitlement.js +214 -0
  257. package/dist/learning/pair-entitlement.js.map +1 -0
  258. package/dist/learning/project-docs.d.ts +83 -0
  259. package/dist/learning/project-docs.d.ts.map +1 -0
  260. package/dist/learning/project-docs.js +132 -0
  261. package/dist/learning/project-docs.js.map +1 -0
  262. package/dist/learning/provider-fallback.d.ts +23 -1
  263. package/dist/learning/provider-fallback.d.ts.map +1 -1
  264. package/dist/learning/provider-fallback.js +24 -0
  265. package/dist/learning/provider-fallback.js.map +1 -1
  266. package/dist/learning/quota-ledger.d.ts +13 -0
  267. package/dist/learning/quota-ledger.d.ts.map +1 -1
  268. package/dist/learning/quota-ledger.js +28 -0
  269. package/dist/learning/quota-ledger.js.map +1 -1
  270. package/dist/learning/reasoning-trace.d.ts +65 -1
  271. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  272. package/dist/learning/reasoning-trace.js +123 -1
  273. package/dist/learning/reasoning-trace.js.map +1 -1
  274. package/dist/learning/requirement-probe.d.ts +55 -0
  275. package/dist/learning/requirement-probe.d.ts.map +1 -0
  276. package/dist/learning/requirement-probe.js +77 -0
  277. package/dist/learning/requirement-probe.js.map +1 -0
  278. package/dist/learning/resilient-call.d.ts +20 -1
  279. package/dist/learning/resilient-call.d.ts.map +1 -1
  280. package/dist/learning/resilient-call.js +42 -4
  281. package/dist/learning/resilient-call.js.map +1 -1
  282. package/dist/learning/response-usability.d.ts +85 -0
  283. package/dist/learning/response-usability.d.ts.map +1 -0
  284. package/dist/learning/response-usability.js +135 -0
  285. package/dist/learning/response-usability.js.map +1 -0
  286. package/dist/learning/router-bandit.d.ts +132 -0
  287. package/dist/learning/router-bandit.d.ts.map +1 -1
  288. package/dist/learning/router-bandit.js +253 -0
  289. package/dist/learning/router-bandit.js.map +1 -1
  290. package/dist/learning/routing-history.d.ts +41 -2
  291. package/dist/learning/routing-history.d.ts.map +1 -1
  292. package/dist/learning/routing-history.js.map +1 -1
  293. package/dist/learning/run-trace.d.ts +21 -0
  294. package/dist/learning/run-trace.d.ts.map +1 -1
  295. package/dist/learning/run-trace.js +38 -0
  296. package/dist/learning/run-trace.js.map +1 -1
  297. package/dist/learning/seeded-benchmark.d.ts +14 -0
  298. package/dist/learning/seeded-benchmark.d.ts.map +1 -1
  299. package/dist/learning/seeded-benchmark.js +43 -4
  300. package/dist/learning/seeded-benchmark.js.map +1 -1
  301. package/dist/learning/session-grant.d.ts +75 -0
  302. package/dist/learning/session-grant.d.ts.map +1 -0
  303. package/dist/learning/session-grant.js +112 -0
  304. package/dist/learning/session-grant.js.map +1 -0
  305. package/dist/learning/task-model-continuity.d.ts +73 -0
  306. package/dist/learning/task-model-continuity.d.ts.map +1 -0
  307. package/dist/learning/task-model-continuity.js +87 -0
  308. package/dist/learning/task-model-continuity.js.map +1 -0
  309. package/dist/learning/turn-feedback.d.ts +93 -0
  310. package/dist/learning/turn-feedback.d.ts.map +1 -0
  311. package/dist/learning/turn-feedback.js +127 -0
  312. package/dist/learning/turn-feedback.js.map +1 -0
  313. package/dist/learning/turn-report.d.ts +104 -2
  314. package/dist/learning/turn-report.d.ts.map +1 -1
  315. package/dist/learning/turn-report.js +161 -6
  316. package/dist/learning/turn-report.js.map +1 -1
  317. package/dist/learning/verdict-routing.d.ts +51 -0
  318. package/dist/learning/verdict-routing.d.ts.map +1 -0
  319. package/dist/learning/verdict-routing.js +122 -0
  320. package/dist/learning/verdict-routing.js.map +1 -0
  321. package/dist/mcp/client.d.ts.map +1 -1
  322. package/dist/mcp/client.js +5 -0
  323. package/dist/mcp/client.js.map +1 -1
  324. package/dist/mcp/manager.d.ts +10 -0
  325. package/dist/mcp/manager.d.ts.map +1 -1
  326. package/dist/mcp/manager.js +20 -0
  327. package/dist/mcp/manager.js.map +1 -1
  328. package/dist/mcp/mcp-discovery.d.ts +34 -0
  329. package/dist/mcp/mcp-discovery.d.ts.map +1 -0
  330. package/dist/mcp/mcp-discovery.js +71 -0
  331. package/dist/mcp/mcp-discovery.js.map +1 -0
  332. package/dist/mcp/mcp-schema-cache.d.ts +17 -0
  333. package/dist/mcp/mcp-schema-cache.d.ts.map +1 -1
  334. package/dist/mcp/mcp-schema-cache.js +47 -3
  335. package/dist/mcp/mcp-schema-cache.js.map +1 -1
  336. package/dist/mcp/types.d.ts +28 -0
  337. package/dist/mcp/types.d.ts.map +1 -1
  338. package/dist/mcp/types.js.map +1 -1
  339. package/dist/security/secret-scan.d.ts +156 -0
  340. package/dist/security/secret-scan.d.ts.map +1 -0
  341. package/dist/security/secret-scan.js +525 -0
  342. package/dist/security/secret-scan.js.map +1 -0
  343. package/dist/tools/ask-user.d.ts.map +1 -1
  344. package/dist/tools/ask-user.js +4 -0
  345. package/dist/tools/ask-user.js.map +1 -1
  346. package/dist/tools/capability-registry.d.ts +150 -0
  347. package/dist/tools/capability-registry.d.ts.map +1 -0
  348. package/dist/tools/capability-registry.js +400 -0
  349. package/dist/tools/capability-registry.js.map +1 -0
  350. package/dist/tools/child-agent-runtime.d.ts.map +1 -1
  351. package/dist/tools/child-agent-runtime.js +19 -5
  352. package/dist/tools/child-agent-runtime.js.map +1 -1
  353. package/dist/tools/coding-tools.d.ts +17 -5
  354. package/dist/tools/coding-tools.d.ts.map +1 -1
  355. package/dist/tools/coding-tools.js +80 -11
  356. package/dist/tools/coding-tools.js.map +1 -1
  357. package/dist/tools/command-memo.d.ts +116 -0
  358. package/dist/tools/command-memo.d.ts.map +1 -0
  359. package/dist/tools/command-memo.js +202 -0
  360. package/dist/tools/command-memo.js.map +1 -0
  361. package/dist/tools/git-tool.d.ts.map +1 -1
  362. package/dist/tools/git-tool.js +13 -3
  363. package/dist/tools/git-tool.js.map +1 -1
  364. package/dist/tools/harness-directive.d.ts +42 -0
  365. package/dist/tools/harness-directive.d.ts.map +1 -0
  366. package/dist/tools/harness-directive.js +45 -0
  367. package/dist/tools/harness-directive.js.map +1 -0
  368. package/dist/tools/loop-skill-hint.d.ts +74 -174
  369. package/dist/tools/loop-skill-hint.d.ts.map +1 -1
  370. package/dist/tools/loop-skill-hint.js +110 -536
  371. package/dist/tools/loop-skill-hint.js.map +1 -1
  372. package/dist/tools/mcp-client-tool.d.ts +2 -0
  373. package/dist/tools/mcp-client-tool.d.ts.map +1 -1
  374. package/dist/tools/mcp-client-tool.js +3 -0
  375. package/dist/tools/mcp-client-tool.js.map +1 -1
  376. package/dist/tools/modality/shared.d.ts +1 -4
  377. package/dist/tools/modality/shared.d.ts.map +1 -1
  378. package/dist/tools/modality/shared.js +7 -22
  379. package/dist/tools/modality/shared.js.map +1 -1
  380. package/dist/tools/plan-store.d.ts +44 -4
  381. package/dist/tools/plan-store.d.ts.map +1 -1
  382. package/dist/tools/plan-store.js +93 -9
  383. package/dist/tools/plan-store.js.map +1 -1
  384. package/dist/tools/registry.d.ts +76 -3
  385. package/dist/tools/registry.d.ts.map +1 -1
  386. package/dist/tools/registry.js +623 -15
  387. package/dist/tools/registry.js.map +1 -1
  388. package/dist/tools/remediation-ladder.d.ts +91 -0
  389. package/dist/tools/remediation-ladder.d.ts.map +1 -0
  390. package/dist/tools/remediation-ladder.js +316 -0
  391. package/dist/tools/remediation-ladder.js.map +1 -0
  392. package/dist/tools/run-cli.d.ts.map +1 -1
  393. package/dist/tools/run-cli.js +29 -4
  394. package/dist/tools/run-cli.js.map +1 -1
  395. package/dist/tools/run-terminal.d.ts +11 -0
  396. package/dist/tools/run-terminal.d.ts.map +1 -1
  397. package/dist/tools/run-terminal.js +289 -10
  398. package/dist/tools/run-terminal.js.map +1 -1
  399. package/dist/tools/skills-hub.d.ts +7 -0
  400. package/dist/tools/skills-hub.d.ts.map +1 -1
  401. package/dist/tools/skills-hub.js +11 -2
  402. package/dist/tools/skills-hub.js.map +1 -1
  403. package/dist/tools/step-artifact.d.ts +35 -0
  404. package/dist/tools/step-artifact.d.ts.map +1 -0
  405. package/dist/tools/step-artifact.js +154 -0
  406. package/dist/tools/step-artifact.js.map +1 -0
  407. package/dist/tools/tool-loop.d.ts +327 -13
  408. package/dist/tools/tool-loop.d.ts.map +1 -1
  409. package/dist/tools/tool-loop.js +891 -66
  410. package/dist/tools/tool-loop.js.map +1 -1
  411. package/dist/tools/toolsets.js +4 -4
  412. package/dist/tools/toolsets.js.map +1 -1
  413. package/dist/tools/worktree.d.ts +11 -2
  414. package/dist/tools/worktree.d.ts.map +1 -1
  415. package/dist/tools/worktree.js +11 -2
  416. package/dist/tools/worktree.js.map +1 -1
  417. package/dist/utils/atomic-store.d.ts +53 -0
  418. package/dist/utils/atomic-store.d.ts.map +1 -0
  419. package/dist/utils/atomic-store.js +178 -0
  420. package/dist/utils/atomic-store.js.map +1 -0
  421. package/dist/utils/binary-probe.d.ts +25 -0
  422. package/dist/utils/binary-probe.d.ts.map +1 -0
  423. package/dist/utils/binary-probe.js +48 -0
  424. package/dist/utils/binary-probe.js.map +1 -0
  425. package/dist/utils/workspace-path.d.ts +50 -0
  426. package/dist/utils/workspace-path.d.ts.map +1 -0
  427. package/dist/utils/workspace-path.js +86 -0
  428. package/dist/utils/workspace-path.js.map +1 -0
  429. package/dist/web-dashboard/chat-console.d.ts +24 -0
  430. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  431. package/dist/web-dashboard/chat-console.js +38 -0
  432. package/dist/web-dashboard/chat-console.js.map +1 -1
  433. package/dist/web-dashboard/project-context.d.ts +27 -2
  434. package/dist/web-dashboard/project-context.d.ts.map +1 -1
  435. package/dist/web-dashboard/project-context.js +32 -2
  436. package/dist/web-dashboard/project-context.js.map +1 -1
  437. package/dist/web-dashboard/server.d.ts +40 -0
  438. package/dist/web-dashboard/server.d.ts.map +1 -1
  439. package/dist/web-dashboard/server.js +733 -15
  440. package/dist/web-dashboard/server.js.map +1 -1
  441. package/dist/web-dashboard/src/types.d.ts +236 -1
  442. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  443. package/dist/web-dashboard/workspace-guard.d.ts +18 -2
  444. package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
  445. package/dist/web-dashboard/workspace-guard.js +94 -12
  446. package/dist/web-dashboard/workspace-guard.js.map +1 -1
  447. package/dist/web-dashboard/workspace-resolution.d.ts +87 -0
  448. package/dist/web-dashboard/workspace-resolution.d.ts.map +1 -0
  449. package/dist/web-dashboard/workspace-resolution.js +105 -0
  450. package/dist/web-dashboard/workspace-resolution.js.map +1 -0
  451. package/package.json +1 -1
  452. package/src/web-dashboard/public/assets/index-tK8Y2qHB.css +1 -0
  453. package/src/web-dashboard/public/assets/index-zjbdJnO-.js +210 -0
  454. package/src/web-dashboard/public/assets/index-zjbdJnO-.js.map +1 -0
  455. package/src/web-dashboard/public/index.html +2 -2
  456. package/src/web-dashboard/public/assets/index-BrZcYhy6.js +0 -207
  457. package/src/web-dashboard/public/assets/index-BrZcYhy6.js.map +0 -1
  458. 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
- if (/migrat(e|ion)|upgrade|refactor|pipeline|deployment/.test(text)) {
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 (/architect|architecture|design|system|microservice|platform/.test(text) && !/outline|plan|roadmap|strategy/.test(text)) {
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
- if (/verify|verification|validate|rollout|deploy|production|launch|release/.test(text)) {
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 (/security|audit|vulnerab|threat|exploit/.test(text)) {
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 (/debug|bug|error|fix|trace/.test(text)) {
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 (/plan|outline|roadmap|strategy/.test(text)) {
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 evidence
758
- * from the model id the router will serve for THIS task (resolveModel:
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
- * Evidence signals (all derived from the model id + registry measurements,
762
- * no network):
763
- * - parameter-size hints: larger parameter counts (70b > 13b > 8b > 3b/1b)
764
- * raise reasoning; tiny param counts lower it;
765
- * - tier words: 'large'/'max'/'opus'/'70b'/… raise; 'mini'/'tiny'/'small'/
766
- * 'instant'/'flash'/'nano'/'lite' lower reasoning (flash/instant also
767
- * RAISE speed — they are speed-optimized models);
768
- * - frontier-keyword families (gpt-4/5-class, claude-3/4-class,
769
- * gemini-2-class, deepseek-r1, llama-70b) raise reasoning;
770
- * - registry latency: a model measured much slower than its provider
771
- * baseline suggests a heavyweight (raises reasoning, lowers speed) and
772
- * vice versa.
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
- * Deliberately CONSERVATIVE: adjustments clamp to ±0.35 and never cross the
775
- * 0..1 bounds; an unknown model id returns the provider baseline unchanged
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 entry = getModelRegistry().getEntry(provider, model);
830
- if (entry?.latencyMs) {
831
- if (entry.latencyMs >= 10_000) {
832
- reasoningAdj += 0.1;
833
- speedAdj -= 0.1;
834
- }
835
- else if (entry.latencyMs <= 800) {
836
- speedAdj += 0.1;
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 — id evidence only.
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: alternate
1781
- ? `Fallback (alternate model on ${s.provider})`
1782
- : s.inCooldown
1783
- ? `Fallback (in cooldown): ${s.provider}`
1784
- : `Fallback: ${s.provider}`,
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
- (routedBy === 'bandit' ? ' | bandit-learned' : '') +
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, undefined, learnIntent);
2258
+ bandit.recordOutcomeWithComplexity(provider, complexityHint, outcome, costScore, outcomeData, learnIntent);
2021
2259
  }
2022
2260
  else {
2023
- bandit.recordOutcome(provider, taskDescription, outcome, costScore, undefined, learnIntent);
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, undefined, learnIntent);
2268
+ bandit.recordModelOutcomeWithComplexity(model, complexityHint, outcome, costScore, outcomeData, learnIntent);
2031
2269
  }
2032
2270
  else {
2033
- bandit.recordModelOutcome(model, taskDescription, outcome, costScore, undefined, learnIntent);
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