@caupulican/pi-adaptative 0.80.86 → 0.80.89

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 (353) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/dist/core/agent-session.d.ts +412 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +2053 -41
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/approval-gate.d.ts +4 -0
  7. package/dist/core/autonomy/approval-gate.d.ts.map +1 -0
  8. package/dist/core/autonomy/approval-gate.js +27 -0
  9. package/dist/core/autonomy/approval-gate.js.map +1 -0
  10. package/dist/core/autonomy/bounded-completion.d.ts +27 -0
  11. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -0
  12. package/dist/core/autonomy/bounded-completion.js +44 -0
  13. package/dist/core/autonomy/bounded-completion.js.map +1 -0
  14. package/dist/core/autonomy/contracts.d.ts +129 -0
  15. package/dist/core/autonomy/contracts.d.ts.map +1 -0
  16. package/dist/core/autonomy/contracts.js +2 -0
  17. package/dist/core/autonomy/contracts.js.map +1 -0
  18. package/dist/core/autonomy/gates.d.ts +15 -0
  19. package/dist/core/autonomy/gates.d.ts.map +1 -0
  20. package/dist/core/autonomy/gates.js +205 -0
  21. package/dist/core/autonomy/gates.js.map +1 -0
  22. package/dist/core/autonomy/lane-tracker.d.ts +48 -0
  23. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -0
  24. package/dist/core/autonomy/lane-tracker.js +125 -0
  25. package/dist/core/autonomy/lane-tracker.js.map +1 -0
  26. package/dist/core/autonomy/path-scope.d.ts +9 -0
  27. package/dist/core/autonomy/path-scope.d.ts.map +1 -0
  28. package/dist/core/autonomy/path-scope.js +122 -0
  29. package/dist/core/autonomy/path-scope.js.map +1 -0
  30. package/dist/core/autonomy/risk-assessment.d.ts +3 -0
  31. package/dist/core/autonomy/risk-assessment.d.ts.map +1 -0
  32. package/dist/core/autonomy/risk-assessment.js +122 -0
  33. package/dist/core/autonomy/risk-assessment.js.map +1 -0
  34. package/dist/core/autonomy/session-lane-record.d.ts +10 -0
  35. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -0
  36. package/dist/core/autonomy/session-lane-record.js +36 -0
  37. package/dist/core/autonomy/session-lane-record.js.map +1 -0
  38. package/dist/core/autonomy/status.d.ts +40 -0
  39. package/dist/core/autonomy/status.d.ts.map +1 -0
  40. package/dist/core/autonomy/status.js +107 -0
  41. package/dist/core/autonomy/status.js.map +1 -0
  42. package/dist/core/autonomy/subagent-prompt.d.ts +21 -0
  43. package/dist/core/autonomy/subagent-prompt.d.ts.map +1 -0
  44. package/dist/core/autonomy/subagent-prompt.js +28 -0
  45. package/dist/core/autonomy/subagent-prompt.js.map +1 -0
  46. package/dist/core/autonomy/telemetry-events.d.ts +18 -0
  47. package/dist/core/autonomy/telemetry-events.d.ts.map +1 -0
  48. package/dist/core/autonomy/telemetry-events.js +60 -0
  49. package/dist/core/autonomy/telemetry-events.js.map +1 -0
  50. package/dist/core/context/artifact-retrieval.d.ts +49 -0
  51. package/dist/core/context/artifact-retrieval.d.ts.map +1 -0
  52. package/dist/core/context/artifact-retrieval.js +49 -0
  53. package/dist/core/context/artifact-retrieval.js.map +1 -0
  54. package/dist/core/context/brain-curator.d.ts +88 -0
  55. package/dist/core/context/brain-curator.d.ts.map +1 -0
  56. package/dist/core/context/brain-curator.js +192 -0
  57. package/dist/core/context/brain-curator.js.map +1 -0
  58. package/dist/core/context/context-artifacts.d.ts +94 -0
  59. package/dist/core/context/context-artifacts.d.ts.map +1 -0
  60. package/dist/core/context/context-artifacts.js +307 -0
  61. package/dist/core/context/context-artifacts.js.map +1 -0
  62. package/dist/core/context/context-audit.d.ts +66 -0
  63. package/dist/core/context/context-audit.d.ts.map +1 -0
  64. package/dist/core/context/context-audit.js +173 -0
  65. package/dist/core/context/context-audit.js.map +1 -0
  66. package/dist/core/context/context-composition.d.ts +122 -0
  67. package/dist/core/context/context-composition.d.ts.map +1 -0
  68. package/dist/core/context/context-composition.js +163 -0
  69. package/dist/core/context/context-composition.js.map +1 -0
  70. package/dist/core/context/context-item.d.ts +117 -0
  71. package/dist/core/context/context-item.d.ts.map +1 -0
  72. package/dist/core/context/context-item.js +36 -0
  73. package/dist/core/context/context-item.js.map +1 -0
  74. package/dist/core/context/context-prompt-enforcement.d.ts +86 -0
  75. package/dist/core/context/context-prompt-enforcement.d.ts.map +1 -0
  76. package/dist/core/context/context-prompt-enforcement.js +168 -0
  77. package/dist/core/context/context-prompt-enforcement.js.map +1 -0
  78. package/dist/core/context/context-prompt-policy.d.ts +90 -0
  79. package/dist/core/context/context-prompt-policy.d.ts.map +1 -0
  80. package/dist/core/context/context-prompt-policy.js +73 -0
  81. package/dist/core/context/context-prompt-policy.js.map +1 -0
  82. package/dist/core/context/context-retention.d.ts +36 -0
  83. package/dist/core/context/context-retention.d.ts.map +1 -0
  84. package/dist/core/context/context-retention.js +108 -0
  85. package/dist/core/context/context-retention.js.map +1 -0
  86. package/dist/core/context/context-store.d.ts +37 -0
  87. package/dist/core/context/context-store.d.ts.map +1 -0
  88. package/dist/core/context/context-store.js +45 -0
  89. package/dist/core/context/context-store.js.map +1 -0
  90. package/dist/core/context/memory-diagnostics.d.ts +50 -0
  91. package/dist/core/context/memory-diagnostics.d.ts.map +1 -0
  92. package/dist/core/context/memory-diagnostics.js +43 -0
  93. package/dist/core/context/memory-diagnostics.js.map +1 -0
  94. package/dist/core/context/memory-index-store.d.ts +28 -0
  95. package/dist/core/context/memory-index-store.d.ts.map +1 -0
  96. package/dist/core/context/memory-index-store.js +38 -0
  97. package/dist/core/context/memory-index-store.js.map +1 -0
  98. package/dist/core/context/memory-prompt-block.d.ts +34 -0
  99. package/dist/core/context/memory-prompt-block.d.ts.map +1 -0
  100. package/dist/core/context/memory-prompt-block.js +58 -0
  101. package/dist/core/context/memory-prompt-block.js.map +1 -0
  102. package/dist/core/context/memory-provider-contract.d.ts +114 -0
  103. package/dist/core/context/memory-provider-contract.d.ts.map +1 -0
  104. package/dist/core/context/memory-provider-contract.js +121 -0
  105. package/dist/core/context/memory-provider-contract.js.map +1 -0
  106. package/dist/core/context/memory-retrieval.d.ts +27 -0
  107. package/dist/core/context/memory-retrieval.d.ts.map +1 -0
  108. package/dist/core/context/memory-retrieval.js +91 -0
  109. package/dist/core/context/memory-retrieval.js.map +1 -0
  110. package/dist/core/context/okf-memory-provider.d.ts +26 -0
  111. package/dist/core/context/okf-memory-provider.d.ts.map +1 -0
  112. package/dist/core/context/okf-memory-provider.js +154 -0
  113. package/dist/core/context/okf-memory-provider.js.map +1 -0
  114. package/dist/core/context/okf-memory.d.ts +42 -0
  115. package/dist/core/context/okf-memory.d.ts.map +1 -0
  116. package/dist/core/context/okf-memory.js +175 -0
  117. package/dist/core/context/okf-memory.js.map +1 -0
  118. package/dist/core/context/policy-engine.d.ts +66 -0
  119. package/dist/core/context/policy-engine.d.ts.map +1 -0
  120. package/dist/core/context/policy-engine.js +171 -0
  121. package/dist/core/context/policy-engine.js.map +1 -0
  122. package/dist/core/context/policy-types.d.ts +102 -0
  123. package/dist/core/context/policy-types.d.ts.map +1 -0
  124. package/dist/core/context/policy-types.js +7 -0
  125. package/dist/core/context/policy-types.js.map +1 -0
  126. package/dist/core/context/sqlite-runtime-index.d.ts +19 -0
  127. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -0
  128. package/dist/core/context/sqlite-runtime-index.js +344 -0
  129. package/dist/core/context/sqlite-runtime-index.js.map +1 -0
  130. package/dist/core/context/storage-authority.d.ts +20 -0
  131. package/dist/core/context/storage-authority.d.ts.map +1 -0
  132. package/dist/core/context/storage-authority.js +51 -0
  133. package/dist/core/context/storage-authority.js.map +1 -0
  134. package/dist/core/context/tool-output-packer.d.ts +75 -0
  135. package/dist/core/context/tool-output-packer.d.ts.map +1 -0
  136. package/dist/core/context/tool-output-packer.js +77 -0
  137. package/dist/core/context/tool-output-packer.js.map +1 -0
  138. package/dist/core/context-gc.d.ts +13 -0
  139. package/dist/core/context-gc.d.ts.map +1 -1
  140. package/dist/core/context-gc.js +6 -0
  141. package/dist/core/context-gc.js.map +1 -1
  142. package/dist/core/cost/session-usage.d.ts +20 -0
  143. package/dist/core/cost/session-usage.d.ts.map +1 -0
  144. package/dist/core/cost/session-usage.js +164 -0
  145. package/dist/core/cost/session-usage.js.map +1 -0
  146. package/dist/core/delegation/session-worker-result.d.ts +10 -0
  147. package/dist/core/delegation/session-worker-result.d.ts.map +1 -0
  148. package/dist/core/delegation/session-worker-result.js +36 -0
  149. package/dist/core/delegation/session-worker-result.js.map +1 -0
  150. package/dist/core/delegation/worker-result.d.ts +9 -0
  151. package/dist/core/delegation/worker-result.d.ts.map +1 -0
  152. package/dist/core/delegation/worker-result.js +152 -0
  153. package/dist/core/delegation/worker-result.js.map +1 -0
  154. package/dist/core/delegation/worker-runner.d.ts +58 -0
  155. package/dist/core/delegation/worker-runner.d.ts.map +1 -0
  156. package/dist/core/delegation/worker-runner.js +188 -0
  157. package/dist/core/delegation/worker-runner.js.map +1 -0
  158. package/dist/core/extensions/builtin.d.ts +5 -1
  159. package/dist/core/extensions/builtin.d.ts.map +1 -1
  160. package/dist/core/extensions/builtin.js +23 -1
  161. package/dist/core/extensions/builtin.js.map +1 -1
  162. package/dist/core/footer-data-provider.d.ts +5 -1
  163. package/dist/core/footer-data-provider.d.ts.map +1 -1
  164. package/dist/core/footer-data-provider.js +13 -0
  165. package/dist/core/footer-data-provider.js.map +1 -1
  166. package/dist/core/goals/goal-continuation-controller.d.ts +22 -0
  167. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -0
  168. package/dist/core/goals/goal-continuation-controller.js +88 -0
  169. package/dist/core/goals/goal-continuation-controller.js.map +1 -0
  170. package/dist/core/goals/goal-continuation-defaults.d.ts +10 -0
  171. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -0
  172. package/dist/core/goals/goal-continuation-defaults.js +10 -0
  173. package/dist/core/goals/goal-continuation-defaults.js.map +1 -0
  174. package/dist/core/goals/goal-continuation-prompt.d.ts +18 -0
  175. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -0
  176. package/dist/core/goals/goal-continuation-prompt.js +141 -0
  177. package/dist/core/goals/goal-continuation-prompt.js.map +1 -0
  178. package/dist/core/goals/goal-runtime-snapshot.d.ts +19 -0
  179. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -0
  180. package/dist/core/goals/goal-runtime-snapshot.js +23 -0
  181. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -0
  182. package/dist/core/goals/goal-state.d.ts +87 -0
  183. package/dist/core/goals/goal-state.d.ts.map +1 -0
  184. package/dist/core/goals/goal-state.js +259 -0
  185. package/dist/core/goals/goal-state.js.map +1 -0
  186. package/dist/core/goals/goal-tool-core.d.ts +66 -0
  187. package/dist/core/goals/goal-tool-core.d.ts.map +1 -0
  188. package/dist/core/goals/goal-tool-core.js +146 -0
  189. package/dist/core/goals/goal-tool-core.js.map +1 -0
  190. package/dist/core/goals/session-goal-state.d.ts +10 -0
  191. package/dist/core/goals/session-goal-state.d.ts.map +1 -0
  192. package/dist/core/goals/session-goal-state.js +35 -0
  193. package/dist/core/goals/session-goal-state.js.map +1 -0
  194. package/dist/core/learning/learning-audit.d.ts +45 -0
  195. package/dist/core/learning/learning-audit.d.ts.map +1 -0
  196. package/dist/core/learning/learning-audit.js +139 -0
  197. package/dist/core/learning/learning-audit.js.map +1 -0
  198. package/dist/core/learning/learning-gate.d.ts +29 -0
  199. package/dist/core/learning/learning-gate.d.ts.map +1 -0
  200. package/dist/core/learning/learning-gate.js +150 -0
  201. package/dist/core/learning/learning-gate.js.map +1 -0
  202. package/dist/core/learning/session-learning-decision.d.ts +10 -0
  203. package/dist/core/learning/session-learning-decision.d.ts.map +1 -0
  204. package/dist/core/learning/session-learning-decision.js +36 -0
  205. package/dist/core/learning/session-learning-decision.js.map +1 -0
  206. package/dist/core/model-capability.d.ts +41 -0
  207. package/dist/core/model-capability.d.ts.map +1 -0
  208. package/dist/core/model-capability.js +101 -0
  209. package/dist/core/model-capability.js.map +1 -0
  210. package/dist/core/model-router/config-diagnostics.d.ts.map +1 -1
  211. package/dist/core/model-router/config-diagnostics.js +1 -0
  212. package/dist/core/model-router/config-diagnostics.js.map +1 -1
  213. package/dist/core/model-router/intent-classifier.d.ts +2 -0
  214. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  215. package/dist/core/model-router/intent-classifier.js +154 -9
  216. package/dist/core/model-router/intent-classifier.js.map +1 -1
  217. package/dist/core/model-router/route-judge.d.ts +54 -0
  218. package/dist/core/model-router/route-judge.d.ts.map +1 -0
  219. package/dist/core/model-router/route-judge.js +128 -0
  220. package/dist/core/model-router/route-judge.js.map +1 -0
  221. package/dist/core/model-router/status.d.ts +4 -1
  222. package/dist/core/model-router/status.d.ts.map +1 -1
  223. package/dist/core/model-router/status.js +30 -6
  224. package/dist/core/model-router/status.js.map +1 -1
  225. package/dist/core/model-router/tool-escalation.d.ts +4 -6
  226. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  227. package/dist/core/model-router/tool-escalation.js +1 -1
  228. package/dist/core/model-router/tool-escalation.js.map +1 -1
  229. package/dist/core/models/fitness-store.d.ts +40 -0
  230. package/dist/core/models/fitness-store.d.ts.map +1 -0
  231. package/dist/core/models/fitness-store.js +61 -0
  232. package/dist/core/models/fitness-store.js.map +1 -0
  233. package/dist/core/profile-registry.d.ts.map +1 -1
  234. package/dist/core/profile-registry.js +1 -1
  235. package/dist/core/profile-registry.js.map +1 -1
  236. package/dist/core/prompt-templates.d.ts +2 -0
  237. package/dist/core/prompt-templates.d.ts.map +1 -1
  238. package/dist/core/prompt-templates.js +12 -4
  239. package/dist/core/prompt-templates.js.map +1 -1
  240. package/dist/core/research/automata-provider.d.ts +5 -0
  241. package/dist/core/research/automata-provider.d.ts.map +1 -0
  242. package/dist/core/research/automata-provider.js +15 -0
  243. package/dist/core/research/automata-provider.js.map +1 -0
  244. package/dist/core/research/evidence-bundle.d.ts +10 -0
  245. package/dist/core/research/evidence-bundle.d.ts.map +1 -0
  246. package/dist/core/research/evidence-bundle.js +116 -0
  247. package/dist/core/research/evidence-bundle.js.map +1 -0
  248. package/dist/core/research/model-fitness.d.ts +82 -0
  249. package/dist/core/research/model-fitness.d.ts.map +1 -0
  250. package/dist/core/research/model-fitness.js +308 -0
  251. package/dist/core/research/model-fitness.js.map +1 -0
  252. package/dist/core/research/research-gate.d.ts +11 -0
  253. package/dist/core/research/research-gate.d.ts.map +1 -0
  254. package/dist/core/research/research-gate.js +82 -0
  255. package/dist/core/research/research-gate.js.map +1 -0
  256. package/dist/core/research/research-runner.d.ts +59 -0
  257. package/dist/core/research/research-runner.d.ts.map +1 -0
  258. package/dist/core/research/research-runner.js +155 -0
  259. package/dist/core/research/research-runner.js.map +1 -0
  260. package/dist/core/research/session-evidence-bundle.d.ts +11 -0
  261. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -0
  262. package/dist/core/research/session-evidence-bundle.js +55 -0
  263. package/dist/core/research/session-evidence-bundle.js.map +1 -0
  264. package/dist/core/resource-loader.d.ts.map +1 -1
  265. package/dist/core/resource-loader.js +4 -0
  266. package/dist/core/resource-loader.js.map +1 -1
  267. package/dist/core/settings-manager.d.ts +160 -4
  268. package/dist/core/settings-manager.d.ts.map +1 -1
  269. package/dist/core/settings-manager.js +304 -9
  270. package/dist/core/settings-manager.js.map +1 -1
  271. package/dist/core/skills.d.ts +4 -0
  272. package/dist/core/skills.d.ts.map +1 -1
  273. package/dist/core/skills.js +18 -6
  274. package/dist/core/skills.js.map +1 -1
  275. package/dist/core/slash-commands.d.ts.map +1 -1
  276. package/dist/core/slash-commands.js +10 -1
  277. package/dist/core/slash-commands.js.map +1 -1
  278. package/dist/core/toolkit/script-registry.d.ts +34 -0
  279. package/dist/core/toolkit/script-registry.d.ts.map +1 -0
  280. package/dist/core/toolkit/script-registry.js +71 -0
  281. package/dist/core/toolkit/script-registry.js.map +1 -0
  282. package/dist/core/toolkit/script-runner.d.ts +28 -0
  283. package/dist/core/toolkit/script-runner.d.ts.map +1 -0
  284. package/dist/core/toolkit/script-runner.js +48 -0
  285. package/dist/core/toolkit/script-runner.js.map +1 -0
  286. package/dist/core/tools/artifact-retrieve.d.ts +23 -0
  287. package/dist/core/tools/artifact-retrieve.d.ts.map +1 -0
  288. package/dist/core/tools/artifact-retrieve.js +110 -0
  289. package/dist/core/tools/artifact-retrieve.js.map +1 -0
  290. package/dist/core/tools/delegate.d.ts +32 -0
  291. package/dist/core/tools/delegate.d.ts.map +1 -0
  292. package/dist/core/tools/delegate.js +60 -0
  293. package/dist/core/tools/delegate.js.map +1 -0
  294. package/dist/core/tools/fff-search-backend.d.ts +103 -0
  295. package/dist/core/tools/fff-search-backend.d.ts.map +1 -0
  296. package/dist/core/tools/fff-search-backend.js +151 -0
  297. package/dist/core/tools/fff-search-backend.js.map +1 -0
  298. package/dist/core/tools/find.d.ts +21 -1
  299. package/dist/core/tools/find.d.ts.map +1 -1
  300. package/dist/core/tools/find.js +183 -10
  301. package/dist/core/tools/find.js.map +1 -1
  302. package/dist/core/tools/goal.d.ts +35 -0
  303. package/dist/core/tools/goal.d.ts.map +1 -0
  304. package/dist/core/tools/goal.js +122 -0
  305. package/dist/core/tools/goal.js.map +1 -0
  306. package/dist/core/tools/grep.d.ts +21 -1
  307. package/dist/core/tools/grep.d.ts.map +1 -1
  308. package/dist/core/tools/grep.js +272 -27
  309. package/dist/core/tools/grep.js.map +1 -1
  310. package/dist/core/tools/index.d.ts +4 -1
  311. package/dist/core/tools/index.d.ts.map +1 -1
  312. package/dist/core/tools/index.js +9 -0
  313. package/dist/core/tools/index.js.map +1 -1
  314. package/dist/core/tools/model-fitness.d.ts +30 -0
  315. package/dist/core/tools/model-fitness.d.ts.map +1 -0
  316. package/dist/core/tools/model-fitness.js +38 -0
  317. package/dist/core/tools/model-fitness.js.map +1 -0
  318. package/dist/core/tools/run-toolkit-script.d.ts +24 -0
  319. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -0
  320. package/dist/core/tools/run-toolkit-script.js +103 -0
  321. package/dist/core/tools/run-toolkit-script.js.map +1 -0
  322. package/dist/core/tools/search-router.d.ts +75 -0
  323. package/dist/core/tools/search-router.d.ts.map +1 -0
  324. package/dist/core/tools/search-router.js +85 -0
  325. package/dist/core/tools/search-router.js.map +1 -0
  326. package/dist/modes/interactive/components/fitness-role-selector.d.ts +13 -0
  327. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -0
  328. package/dist/modes/interactive/components/fitness-role-selector.js +65 -0
  329. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -0
  330. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  331. package/dist/modes/interactive/components/footer.js +18 -16
  332. package/dist/modes/interactive/components/footer.js.map +1 -1
  333. package/dist/modes/interactive/components/settings-selector.d.ts +16 -1
  334. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  335. package/dist/modes/interactive/components/settings-selector.js +555 -11
  336. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  337. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  338. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  339. package/dist/modes/interactive/interactive-mode.js +308 -39
  340. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  341. package/dist/utils/tools-manager.d.ts +2 -0
  342. package/dist/utils/tools-manager.d.ts.map +1 -1
  343. package/dist/utils/tools-manager.js +154 -2
  344. package/dist/utils/tools-manager.js.map +1 -1
  345. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  346. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  347. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  348. package/examples/extensions/sandbox/package-lock.json +2 -2
  349. package/examples/extensions/sandbox/package.json +1 -1
  350. package/examples/extensions/with-deps/package-lock.json +2 -2
  351. package/examples/extensions/with-deps/package.json +1 -1
  352. package/npm-shrinkwrap.json +368 -12
  353. package/package.json +5 -4
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Model capability auto-detection: derive what the harness may load onto a model FROM the model's
3
+ * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)
4
+ * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.
5
+ *
6
+ * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero
7
+ * window keeps today's full behavior rather than guessing). Detection can be disabled or forced
8
+ * per class via the `modelCapability.mode` setting.
9
+ */
10
+ export type ModelCapabilityClass = "full" | "lean" | "minimal" | "chat";
11
+ export type ModelCapabilityMode = "auto" | "off" | ModelCapabilityClass;
12
+ export interface ModelCapabilityProfile {
13
+ class: ModelCapabilityClass;
14
+ contextWindow?: number;
15
+ reasonCode: string;
16
+ /** Allow-list; undefined = no allow-list restriction. */
17
+ allowedToolNames?: readonly string[];
18
+ /** Block-list applied after the allow-list; undefined = nothing blocked. */
19
+ blockedToolNames?: readonly string[];
20
+ /** Whether idle background lanes (goal auto-continue, research) may run on this model. */
21
+ backgroundLanesEnabled: boolean;
22
+ /** Output-token cap for lane isolated completions, scaled to the window. */
23
+ laneMaxOutputTokens: number;
24
+ }
25
+ /** Windows at or above this keep the full harness surface. */
26
+ export declare const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32768;
27
+ /** Windows at or above this keep core tools but shed background-autonomy extras. */
28
+ export declare const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16384;
29
+ /** Windows at or above this get the minimal coding set; below is chat-only. */
30
+ export declare const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8192;
31
+ export declare const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS: readonly string[];
32
+ export declare const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS: readonly string[];
33
+ export declare const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS: readonly string[];
34
+ export declare const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;
35
+ export declare function deriveModelCapabilityProfile(args: {
36
+ contextWindow?: number;
37
+ mode?: ModelCapabilityMode;
38
+ }): ModelCapabilityProfile;
39
+ /** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */
40
+ export declare function filterToolNamesForCapability(toolNames: readonly string[], profile: ModelCapabilityProfile): string[];
41
+ //# sourceMappingURL=model-capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-capability.d.ts","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,oBAAoB,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACtC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,0FAA0F;IAC1F,sBAAsB,EAAE,OAAO,CAAC;IAChC,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,CAAC;CAC5B;AAED,8DAA8D;AAC9D,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,oFAAoF;AACpF,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,+EAA+E;AAC/E,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAE1D,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAAkC,CAAC;AACpG,eAAO,MAAM,sCAAsC,EAAE,SAAS,MAAM,EAOnE,CAAC;AACF,eAAO,MAAM,mCAAmC,EAAE,SAAS,MAAM,EAAO,CAAC;AAEzE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AA0CnD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC3B,GAAG,sBAAsB,CA2BzB;AAED,6FAA6F;AAC7F,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,sBAAsB,GAAG,MAAM,EAAE,CAWpH","sourcesContent":["/**\n * Model capability auto-detection: derive what the harness may load onto a model FROM the model's\n * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)\n * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.\n *\n * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero\n * window keeps today's full behavior rather than guessing). Detection can be disabled or forced\n * per class via the `modelCapability.mode` setting.\n */\n\nexport type ModelCapabilityClass = \"full\" | \"lean\" | \"minimal\" | \"chat\";\n\nexport type ModelCapabilityMode = \"auto\" | \"off\" | ModelCapabilityClass;\n\nexport interface ModelCapabilityProfile {\n\tclass: ModelCapabilityClass;\n\tcontextWindow?: number;\n\treasonCode: string;\n\t/** Allow-list; undefined = no allow-list restriction. */\n\tallowedToolNames?: readonly string[];\n\t/** Block-list applied after the allow-list; undefined = nothing blocked. */\n\tblockedToolNames?: readonly string[];\n\t/** Whether idle background lanes (goal auto-continue, research) may run on this model. */\n\tbackgroundLanesEnabled: boolean;\n\t/** Output-token cap for lane isolated completions, scaled to the window. */\n\tlaneMaxOutputTokens: number;\n}\n\n/** Windows at or above this keep the full harness surface. */\nexport const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32_768;\n/** Windows at or above this keep core tools but shed background-autonomy extras. */\nexport const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;\n/** Windows at or above this get the minimal coding set; below is chat-only. */\nexport const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;\n\nexport const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS: readonly string[] = [\"delegate\", \"context_audit\"];\nexport const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS: readonly string[] = [\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t// The executor tool: minimal-class models ARE the daily-ops executors, and its schema is tiny.\n\t\"run_toolkit_script\",\n];\nexport const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS: readonly string[] = [];\n\nexport const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;\nconst MIN_LANE_MAX_OUTPUT_TOKENS = 256;\n\nfunction laneOutputTokensForWindow(contextWindow: number | undefined): number {\n\tif (contextWindow === undefined || contextWindow <= 0) return DEFAULT_LANE_MAX_OUTPUT_TOKENS;\n\t// A lane completion may use at most an eighth of the window for output, floored so tiny\n\t// windows still produce something parseable.\n\treturn Math.min(DEFAULT_LANE_MAX_OUTPUT_TOKENS, Math.max(MIN_LANE_MAX_OUTPUT_TOKENS, Math.floor(contextWindow / 8)));\n}\n\nfunction profileForClass(\n\tcapabilityClass: ModelCapabilityClass,\n\treasonCode: string,\n\tcontextWindow: number | undefined,\n): ModelCapabilityProfile {\n\tconst base = {\n\t\tclass: capabilityClass,\n\t\treasonCode,\n\t\tbackgroundLanesEnabled: true,\n\t\tlaneMaxOutputTokens: laneOutputTokensForWindow(contextWindow),\n\t\t...(contextWindow !== undefined && contextWindow > 0 ? { contextWindow } : {}),\n\t};\n\tswitch (capabilityClass) {\n\t\tcase \"full\":\n\t\t\treturn base;\n\t\tcase \"lean\":\n\t\t\treturn { ...base, blockedToolNames: MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS };\n\t\tcase \"minimal\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t\tcase \"chat\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t}\n}\n\nexport function deriveModelCapabilityProfile(args: {\n\tcontextWindow?: number;\n\tmode?: ModelCapabilityMode;\n}): ModelCapabilityProfile {\n\tconst mode = args.mode ?? \"auto\";\n\tconst contextWindow =\n\t\targs.contextWindow !== undefined && Number.isFinite(args.contextWindow) && args.contextWindow > 0\n\t\t\t? args.contextWindow\n\t\t\t: undefined;\n\tif (mode === \"off\") {\n\t\treturn profileForClass(\"full\", \"detection_disabled\", contextWindow);\n\t}\n\tif (mode !== \"auto\") {\n\t\treturn profileForClass(mode, \"forced_by_setting\", contextWindow);\n\t}\n\n\tif (contextWindow === undefined) {\n\t\t// Metadata missing: defaults, never guesses.\n\t\treturn profileForClass(\"full\", \"unknown_context_window_defaults\", undefined);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_FULL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"full\", \"large_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_LEAN_MIN_CONTEXT) {\n\t\treturn profileForClass(\"lean\", \"lean_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"minimal\", \"minimal_context_window\", contextWindow);\n\t}\n\treturn profileForClass(\"chat\", \"chat_only_context_window\", contextWindow);\n}\n\n/** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */\nexport function filterToolNamesForCapability(toolNames: readonly string[], profile: ModelCapabilityProfile): string[] {\n\tlet filtered = [...toolNames];\n\tif (profile.allowedToolNames !== undefined) {\n\t\tconst allowed = new Set(profile.allowedToolNames);\n\t\tfiltered = filtered.filter((name) => allowed.has(name));\n\t}\n\tif (profile.blockedToolNames !== undefined) {\n\t\tconst blocked = new Set(profile.blockedToolNames);\n\t\tfiltered = filtered.filter((name) => !blocked.has(name));\n\t}\n\treturn filtered;\n}\n"]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Model capability auto-detection: derive what the harness may load onto a model FROM the model's
3
+ * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)
4
+ * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.
5
+ *
6
+ * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero
7
+ * window keeps today's full behavior rather than guessing). Detection can be disabled or forced
8
+ * per class via the `modelCapability.mode` setting.
9
+ */
10
+ /** Windows at or above this keep the full harness surface. */
11
+ export const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32_768;
12
+ /** Windows at or above this keep core tools but shed background-autonomy extras. */
13
+ export const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;
14
+ /** Windows at or above this get the minimal coding set; below is chat-only. */
15
+ export const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;
16
+ export const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS = ["delegate", "context_audit"];
17
+ export const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS = [
18
+ "read",
19
+ "bash",
20
+ "edit",
21
+ "write",
22
+ // The executor tool: minimal-class models ARE the daily-ops executors, and its schema is tiny.
23
+ "run_toolkit_script",
24
+ ];
25
+ export const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS = [];
26
+ export const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;
27
+ const MIN_LANE_MAX_OUTPUT_TOKENS = 256;
28
+ function laneOutputTokensForWindow(contextWindow) {
29
+ if (contextWindow === undefined || contextWindow <= 0)
30
+ return DEFAULT_LANE_MAX_OUTPUT_TOKENS;
31
+ // A lane completion may use at most an eighth of the window for output, floored so tiny
32
+ // windows still produce something parseable.
33
+ return Math.min(DEFAULT_LANE_MAX_OUTPUT_TOKENS, Math.max(MIN_LANE_MAX_OUTPUT_TOKENS, Math.floor(contextWindow / 8)));
34
+ }
35
+ function profileForClass(capabilityClass, reasonCode, contextWindow) {
36
+ const base = {
37
+ class: capabilityClass,
38
+ reasonCode,
39
+ backgroundLanesEnabled: true,
40
+ laneMaxOutputTokens: laneOutputTokensForWindow(contextWindow),
41
+ ...(contextWindow !== undefined && contextWindow > 0 ? { contextWindow } : {}),
42
+ };
43
+ switch (capabilityClass) {
44
+ case "full":
45
+ return base;
46
+ case "lean":
47
+ return { ...base, blockedToolNames: MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS };
48
+ case "minimal":
49
+ return {
50
+ ...base,
51
+ allowedToolNames: MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS,
52
+ backgroundLanesEnabled: false,
53
+ };
54
+ case "chat":
55
+ return {
56
+ ...base,
57
+ allowedToolNames: MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS,
58
+ backgroundLanesEnabled: false,
59
+ };
60
+ }
61
+ }
62
+ export function deriveModelCapabilityProfile(args) {
63
+ const mode = args.mode ?? "auto";
64
+ const contextWindow = args.contextWindow !== undefined && Number.isFinite(args.contextWindow) && args.contextWindow > 0
65
+ ? args.contextWindow
66
+ : undefined;
67
+ if (mode === "off") {
68
+ return profileForClass("full", "detection_disabled", contextWindow);
69
+ }
70
+ if (mode !== "auto") {
71
+ return profileForClass(mode, "forced_by_setting", contextWindow);
72
+ }
73
+ if (contextWindow === undefined) {
74
+ // Metadata missing: defaults, never guesses.
75
+ return profileForClass("full", "unknown_context_window_defaults", undefined);
76
+ }
77
+ if (contextWindow >= MODEL_CAPABILITY_FULL_MIN_CONTEXT) {
78
+ return profileForClass("full", "large_context_window", contextWindow);
79
+ }
80
+ if (contextWindow >= MODEL_CAPABILITY_LEAN_MIN_CONTEXT) {
81
+ return profileForClass("lean", "lean_context_window", contextWindow);
82
+ }
83
+ if (contextWindow >= MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT) {
84
+ return profileForClass("minimal", "minimal_context_window", contextWindow);
85
+ }
86
+ return profileForClass("chat", "chat_only_context_window", contextWindow);
87
+ }
88
+ /** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */
89
+ export function filterToolNamesForCapability(toolNames, profile) {
90
+ let filtered = [...toolNames];
91
+ if (profile.allowedToolNames !== undefined) {
92
+ const allowed = new Set(profile.allowedToolNames);
93
+ filtered = filtered.filter((name) => allowed.has(name));
94
+ }
95
+ if (profile.blockedToolNames !== undefined) {
96
+ const blocked = new Set(profile.blockedToolNames);
97
+ filtered = filtered.filter((name) => !blocked.has(name));
98
+ }
99
+ return filtered;
100
+ }
101
+ //# sourceMappingURL=model-capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-capability.js","sourceRoot":"","sources":["../../src/core/model-capability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model capability auto-detection: derive what the harness may load onto a model FROM the model's\n * own metadata (`Model.contextWindow`), so small open models (4k/8k/16k windows, sub-1B params)\n * can still hold a usable chat instead of drowning in tool schemas and background-lane prompts.\n *\n * Derivation is metadata-first; defaults apply only when the metadata is missing (unknown/zero\n * window keeps today's full behavior rather than guessing). Detection can be disabled or forced\n * per class via the `modelCapability.mode` setting.\n */\n\nexport type ModelCapabilityClass = \"full\" | \"lean\" | \"minimal\" | \"chat\";\n\nexport type ModelCapabilityMode = \"auto\" | \"off\" | ModelCapabilityClass;\n\nexport interface ModelCapabilityProfile {\n\tclass: ModelCapabilityClass;\n\tcontextWindow?: number;\n\treasonCode: string;\n\t/** Allow-list; undefined = no allow-list restriction. */\n\tallowedToolNames?: readonly string[];\n\t/** Block-list applied after the allow-list; undefined = nothing blocked. */\n\tblockedToolNames?: readonly string[];\n\t/** Whether idle background lanes (goal auto-continue, research) may run on this model. */\n\tbackgroundLanesEnabled: boolean;\n\t/** Output-token cap for lane isolated completions, scaled to the window. */\n\tlaneMaxOutputTokens: number;\n}\n\n/** Windows at or above this keep the full harness surface. */\nexport const MODEL_CAPABILITY_FULL_MIN_CONTEXT = 32_768;\n/** Windows at or above this keep core tools but shed background-autonomy extras. */\nexport const MODEL_CAPABILITY_LEAN_MIN_CONTEXT = 16_384;\n/** Windows at or above this get the minimal coding set; below is chat-only. */\nexport const MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT = 8_192;\n\nexport const MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS: readonly string[] = [\"delegate\", \"context_audit\"];\nexport const MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS: readonly string[] = [\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t// The executor tool: minimal-class models ARE the daily-ops executors, and its schema is tiny.\n\t\"run_toolkit_script\",\n];\nexport const MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS: readonly string[] = [];\n\nexport const DEFAULT_LANE_MAX_OUTPUT_TOKENS = 2048;\nconst MIN_LANE_MAX_OUTPUT_TOKENS = 256;\n\nfunction laneOutputTokensForWindow(contextWindow: number | undefined): number {\n\tif (contextWindow === undefined || contextWindow <= 0) return DEFAULT_LANE_MAX_OUTPUT_TOKENS;\n\t// A lane completion may use at most an eighth of the window for output, floored so tiny\n\t// windows still produce something parseable.\n\treturn Math.min(DEFAULT_LANE_MAX_OUTPUT_TOKENS, Math.max(MIN_LANE_MAX_OUTPUT_TOKENS, Math.floor(contextWindow / 8)));\n}\n\nfunction profileForClass(\n\tcapabilityClass: ModelCapabilityClass,\n\treasonCode: string,\n\tcontextWindow: number | undefined,\n): ModelCapabilityProfile {\n\tconst base = {\n\t\tclass: capabilityClass,\n\t\treasonCode,\n\t\tbackgroundLanesEnabled: true,\n\t\tlaneMaxOutputTokens: laneOutputTokensForWindow(contextWindow),\n\t\t...(contextWindow !== undefined && contextWindow > 0 ? { contextWindow } : {}),\n\t};\n\tswitch (capabilityClass) {\n\t\tcase \"full\":\n\t\t\treturn base;\n\t\tcase \"lean\":\n\t\t\treturn { ...base, blockedToolNames: MODEL_CAPABILITY_LEAN_BLOCKED_TOOLS };\n\t\tcase \"minimal\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_MINIMAL_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t\tcase \"chat\":\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tallowedToolNames: MODEL_CAPABILITY_CHAT_ALLOWED_TOOLS,\n\t\t\t\tbackgroundLanesEnabled: false,\n\t\t\t};\n\t}\n}\n\nexport function deriveModelCapabilityProfile(args: {\n\tcontextWindow?: number;\n\tmode?: ModelCapabilityMode;\n}): ModelCapabilityProfile {\n\tconst mode = args.mode ?? \"auto\";\n\tconst contextWindow =\n\t\targs.contextWindow !== undefined && Number.isFinite(args.contextWindow) && args.contextWindow > 0\n\t\t\t? args.contextWindow\n\t\t\t: undefined;\n\tif (mode === \"off\") {\n\t\treturn profileForClass(\"full\", \"detection_disabled\", contextWindow);\n\t}\n\tif (mode !== \"auto\") {\n\t\treturn profileForClass(mode, \"forced_by_setting\", contextWindow);\n\t}\n\n\tif (contextWindow === undefined) {\n\t\t// Metadata missing: defaults, never guesses.\n\t\treturn profileForClass(\"full\", \"unknown_context_window_defaults\", undefined);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_FULL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"full\", \"large_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_LEAN_MIN_CONTEXT) {\n\t\treturn profileForClass(\"lean\", \"lean_context_window\", contextWindow);\n\t}\n\tif (contextWindow >= MODEL_CAPABILITY_MINIMAL_MIN_CONTEXT) {\n\t\treturn profileForClass(\"minimal\", \"minimal_context_window\", contextWindow);\n\t}\n\treturn profileForClass(\"chat\", \"chat_only_context_window\", contextWindow);\n}\n\n/** Apply the profile's allow/block lists to a requested tool-name list, preserving order. */\nexport function filterToolNamesForCapability(toolNames: readonly string[], profile: ModelCapabilityProfile): string[] {\n\tlet filtered = [...toolNames];\n\tif (profile.allowedToolNames !== undefined) {\n\t\tconst allowed = new Set(profile.allowedToolNames);\n\t\tfiltered = filtered.filter((name) => allowed.has(name));\n\t}\n\tif (profile.blockedToolNames !== undefined) {\n\t\tconst blocked = new Set(profile.blockedToolNames);\n\t\tfiltered = filtered.filter((name) => !blocked.has(name));\n\t}\n\treturn filtered;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"config-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAyB7D,wBAAgB,mCAAmC,CAClD,QAAQ,EAAE,yBAAyB,EACnC,aAAa,EAAE,aAAa,GAC1B,MAAM,EAAE,CAgBV","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
1
+ {"version":3,"file":"config-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAyB7D,wBAAgB,mCAAmC,CAClD,QAAQ,EAAE,yBAAyB,EACnC,aAAa,EAAE,aAAa,GAC1B,MAAM,EAAE,CAsBV","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
@@ -20,6 +20,7 @@ export function collectModelRouterConfigDiagnostics(settings, modelRegistry) {
20
20
  return [];
21
21
  return [
22
22
  ...collectModelRouterModelDiagnostics("cheap model", "modelRouter.cheapModel", settings.cheapModel, modelRegistry),
23
+ ...collectModelRouterModelDiagnostics("medium model", "modelRouter.mediumModel", settings.mediumModel, modelRegistry),
23
24
  ...collectModelRouterModelDiagnostics("expensive model", "modelRouter.expensiveModel", settings.expensiveModel, modelRegistry),
24
25
  ];
25
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config-diagnostics.js","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,SAAS,WAAW,CAAC,KAAiB,EAAU;IAC/C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;AAAA,CACvC;AAED,SAAS,kCAAkC,CAC1C,KAAwC,EACxC,UAAmE,EACnE,YAAgC,EAChC,aAA4B,EACjB;IACX,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,CAAC,gBAAgB,KAAK,wBAAwB,UAAU,kCAAkC,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,gBAAgB,KAAK,mBAAmB,YAAY,GAAG,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,gBAAgB,KAAK,qBAAqB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,CAAC;AAAA,CACV;AAED,MAAM,UAAU,mCAAmC,CAClD,QAAmC,EACnC,aAA4B,EACjB;IACX,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO;QACN,GAAG,kCAAkC,CACpC,aAAa,EACb,wBAAwB,EACxB,QAAQ,CAAC,UAAU,EACnB,aAAa,CACb;QACD,GAAG,kCAAkC,CACpC,iBAAiB,EACjB,4BAA4B,EAC5B,QAAQ,CAAC,cAAc,EACvB,aAAa,CACb;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
1
+ {"version":3,"file":"config-diagnostics.js","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,SAAS,WAAW,CAAC,KAAiB,EAAU;IAC/C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;AAAA,CACvC;AAED,SAAS,kCAAkC,CAC1C,KAAyD,EACzD,UAA+F,EAC/F,YAAgC,EAChC,aAA4B,EACjB;IACX,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,CAAC,gBAAgB,KAAK,wBAAwB,UAAU,kCAAkC,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,gBAAgB,KAAK,mBAAmB,YAAY,GAAG,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,gBAAgB,KAAK,qBAAqB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,CAAC;AAAA,CACV;AAED,MAAM,UAAU,mCAAmC,CAClD,QAAmC,EACnC,aAA4B,EACjB;IACX,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO;QACN,GAAG,kCAAkC,CACpC,aAAa,EACb,wBAAwB,EACxB,QAAQ,CAAC,UAAU,EACnB,aAAa,CACb;QACD,GAAG,kCAAkC,CACpC,cAAc,EACd,yBAAyB,EACzB,QAAQ,CAAC,WAAW,EACpB,aAAa,CACb;QACD,GAAG,kCAAkC,CACpC,iBAAiB,EACjB,4BAA4B,EAC5B,QAAQ,CAAC,cAAc,EACvB,aAAa,CACb;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
@@ -1,3 +1,5 @@
1
+ import type { RouteDecision } from "../autonomy/contracts.ts";
1
2
  export type ModelRouterIntent = "research" | "modify";
3
+ export declare function classifyModelRouterRoute(prompt: string): RouteDecision;
2
4
  export declare function classifyModelRouterIntent(prompt: string): ModelRouterIntent;
3
5
  //# sourceMappingURL=intent-classifier.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"intent-classifier.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/intent-classifier.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAUtD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAK3E","sourcesContent":["export type ModelRouterIntent = \"research\" | \"modify\";\n\nconst MODIFY_INTENT_RE =\n\t/\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\\b|\\b(npm|pnpm|yarn|bun|git|gh|pytest|vitest|tsc|biome)\\b|[;&|]\\s*\\w+/i;\n\nconst EXPLICIT_MODIFY_REQUEST_RE =\n\t/^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+.*\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\\b/i;\n\nconst READ_ONLY_QUESTION_RE = /^(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show)\\b/i;\n\nexport function classifyModelRouterIntent(prompt: string): ModelRouterIntent {\n\tconst text = prompt.trim();\n\tif (EXPLICIT_MODIFY_REQUEST_RE.test(text)) return \"modify\";\n\tif (READ_ONLY_QUESTION_RE.test(text)) return \"research\";\n\treturn MODIFY_INTENT_RE.test(text) ? \"modify\" : \"research\";\n}\n"]}
1
+ {"version":3,"file":"intent-classifier.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/intent-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAiCtD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CA0ItE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAG3E","sourcesContent":["import type { RouteDecision } from \"../autonomy/contracts.ts\";\n\nexport type ModelRouterIntent = \"research\" | \"modify\";\n\nconst EXPLICIT_MODIFY_REQUEST_RE =\n\t/^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+.*\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write|publish|release|push|deploy|tag|reset|clean|rewrite)\\b/i;\n\nconst READ_ONLY_QUESTION_RE =\n\t/^(?:(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+)?(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show|search|find|view|read|locate)\\b/i;\n\nconst RELEASE_PUBLISH_RE = /\\b(publish|release|push|deploy|tag)\\b/i;\nconst SECURITY_AUTH_RE = /\\b(auth|token|credential|credentials|secret|api[-_]key)\\b/i;\nconst DESTRUCTIVE_RE = /\\b(delete|reset|rm\\s+-rf|clean)\\b/i;\n\nconst SELF_MOD_MUTATE_RE =\n\t/\\b(modify|change|write|update|edit|delete|add|remove)\\s+.*\\b(skills|prompts|settings|tools|behavior)\\b|self[-_]modification/i;\nconst ARCHITECTURE_MUTATE_RE = /\\b(rewrite|redesign|change|modify|rearchitect)\\s+.*\\b(architecture|architect)\\b/i;\n\n// Planning floor: plans steer all downstream work, so planning never routes cheap by default.\n// Only the route judge may downgrade a planning prompt back to cheap (explicit trivial verdict).\n// Core terms are always planning; design/architecture words count only with prospective phrasing,\n// so lookups like \"show me the architecture\" stay cheap.\nconst PLANNING_CORE_RE = /\\b(plan|planning|roadmap|strategy)\\b/i;\nconst PLANNING_DESIGN_WORD_RE = /\\b(design|architect\\w*|structure|approach)\\b/i;\nconst PLANNING_PROSPECTIVE_RE =\n\t/\\b(how (?:should|would|do we|can we)|what(?:'s| is) the best|propose|draft|come up with|figure out|decide (?:on|how))\\b/i;\n\nfunction isPlanningPrompt(text: string): boolean {\n\treturn PLANNING_CORE_RE.test(text) || (PLANNING_DESIGN_WORD_RE.test(text) && PLANNING_PROSPECTIVE_RE.test(text));\n}\n\nconst REFACTOR_RE = /\\b(refactor|refactoring)\\b/i;\nconst TEST_VALIDATION_RE = /\\b(test|testing|validation|lint|vitest|jest|run)\\b/i;\nconst IMPLEMENT_RE = /\\b(implement|fix|apply|change|update|create|write|generate|modify|edit|patch|add)\\b/i;\n\nexport function classifyModelRouterRoute(prompt: string): RouteDecision {\n\tconst text = prompt.trim();\n\n\tif (text.length === 0) {\n\t\treturn {\n\t\t\ttier: \"cheap\",\n\t\t\trisk: \"read-only\",\n\t\t\tconfidence: 0.1,\n\t\t\treasonCode: \"empty_prompt\",\n\t\t\treasons: [\"Empty or whitespace prompt\"],\n\t\t};\n\t}\n\n\t// 1. Explicit read-only questions/lookups dominate (unless prefixed by explicit mutation verb).\n\t// Planning-shaped questions are the exception: a plan steers expensive downstream work, so the\n\t// floor is medium even when phrased as a question.\n\tif (READ_ONLY_QUESTION_RE.test(text) && !EXPLICIT_MODIFY_REQUEST_RE.test(text)) {\n\t\tif (isPlanningPrompt(text)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"read-only\",\n\t\t\t\tconfidence: 0.75,\n\t\t\t\treasonCode: \"planning_min_medium\",\n\t\t\t\treasons: [\"Planning/design prompts never route cheap by default; a judge may deem them trivial\"],\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttier: \"cheap\",\n\t\t\trisk: \"read-only\",\n\t\t\tconfidence: 0.9,\n\t\t\treasonCode: \"read_only_question\",\n\t\t\treasons: [\"Prompt asks a question or requests an explanation, search, or lookup\"],\n\t\t};\n\t}\n\n\t// Helper function to match patterns and return appropriate decision\n\tfunction matchKeywords(input: string): RouteDecision | null {\n\t\t// A. High-risk / approval-required/expensive signals\n\t\tif (RELEASE_PUBLISH_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"release_or_publish\",\n\t\t\t\treasons: [\"Prompt mentions publishing, releasing, pushing, or deploying\"],\n\t\t\t};\n\t\t}\n\t\tif (SECURITY_AUTH_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"high-impact\",\n\t\t\t\tconfidence: 0.95,\n\t\t\t\treasonCode: \"security_or_auth\",\n\t\t\t\treasons: [\"Prompt mentions credentials, authentication, tokens, or secrets\"],\n\t\t\t};\n\t\t}\n\t\tif (DESTRUCTIVE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.85,\n\t\t\t\treasonCode: \"destructive_or_git_history\",\n\t\t\t\treasons: [\"Prompt mentions deleting, resetting, cleaning, or destructive operations\"],\n\t\t\t};\n\t\t}\n\t\tif (SELF_MOD_MUTATE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"settings_or_self_modification\",\n\t\t\t\treasons: [\"Prompt mentions modifying skills, prompts, settings, tools, or self-modification\"],\n\t\t\t};\n\t\t}\n\t\tif (ARCHITECTURE_MUTATE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"high-impact\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"architecture_or_ambiguous\",\n\t\t\t\treasons: [\"Prompt mentions core architecture or rewrite\"],\n\t\t\t};\n\t\t}\n\n\t\t// B. Explicit implementation/scoped-write signals route medium\n\t\tif (isPlanningPrompt(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"read-only\",\n\t\t\t\tconfidence: 0.75,\n\t\t\t\treasonCode: \"planning_min_medium\",\n\t\t\t\treasons: [\"Planning/design prompts never route cheap by default; a judge may deem them trivial\"],\n\t\t\t};\n\t\t}\n\t\tif (REFACTOR_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.8,\n\t\t\t\treasonCode: \"mechanical_refactor\",\n\t\t\t\treasons: [\"Prompt mentions refactoring code structure\"],\n\t\t\t};\n\t\t}\n\t\tif (TEST_VALIDATION_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.8,\n\t\t\t\treasonCode: \"test_or_validation\",\n\t\t\t\treasons: [\"Prompt mentions testing, validation, or linting\"],\n\t\t\t};\n\t\t}\n\t\tif (IMPLEMENT_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.85,\n\t\t\t\treasonCode: \"normal_implementation\",\n\t\t\t\treasons: [\"Prompt mentions implementing, updating, creating, or modifying code\"],\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tconst match = matchKeywords(text);\n\tif (match) {\n\t\treturn match;\n\t}\n\n\t// 4. Default fallbacks\n\treturn {\n\t\ttier: \"cheap\",\n\t\trisk: \"read-only\",\n\t\tconfidence: 0.5,\n\t\treasonCode: \"default_read_only\",\n\t\treasons: [\"No explicit implementation, destructive, or release patterns detected\"],\n\t};\n}\n\nexport function classifyModelRouterIntent(prompt: string): ModelRouterIntent {\n\tconst decision = classifyModelRouterRoute(prompt);\n\treturn decision.tier === \"cheap\" ? \"research\" : \"modify\";\n}\n"]}
@@ -1,12 +1,157 @@
1
- const MODIFY_INTENT_RE = /\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\b|\b(npm|pnpm|yarn|bun|git|gh|pytest|vitest|tsc|biome)\b|[;&|]\s*\w+/i;
2
- const EXPLICIT_MODIFY_REQUEST_RE = /^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\s+.*\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\b/i;
3
- const READ_ONLY_QUESTION_RE = /^(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show)\b/i;
4
- export function classifyModelRouterIntent(prompt) {
1
+ const EXPLICIT_MODIFY_REQUEST_RE = /^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\s+.*\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write|publish|release|push|deploy|tag|reset|clean|rewrite)\b/i;
2
+ const READ_ONLY_QUESTION_RE = /^(?:(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\s+)?(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show|search|find|view|read|locate)\b/i;
3
+ const RELEASE_PUBLISH_RE = /\b(publish|release|push|deploy|tag)\b/i;
4
+ const SECURITY_AUTH_RE = /\b(auth|token|credential|credentials|secret|api[-_]key)\b/i;
5
+ const DESTRUCTIVE_RE = /\b(delete|reset|rm\s+-rf|clean)\b/i;
6
+ const SELF_MOD_MUTATE_RE = /\b(modify|change|write|update|edit|delete|add|remove)\s+.*\b(skills|prompts|settings|tools|behavior)\b|self[-_]modification/i;
7
+ const ARCHITECTURE_MUTATE_RE = /\b(rewrite|redesign|change|modify|rearchitect)\s+.*\b(architecture|architect)\b/i;
8
+ // Planning floor: plans steer all downstream work, so planning never routes cheap by default.
9
+ // Only the route judge may downgrade a planning prompt back to cheap (explicit trivial verdict).
10
+ // Core terms are always planning; design/architecture words count only with prospective phrasing,
11
+ // so lookups like "show me the architecture" stay cheap.
12
+ const PLANNING_CORE_RE = /\b(plan|planning|roadmap|strategy)\b/i;
13
+ const PLANNING_DESIGN_WORD_RE = /\b(design|architect\w*|structure|approach)\b/i;
14
+ const PLANNING_PROSPECTIVE_RE = /\b(how (?:should|would|do we|can we)|what(?:'s| is) the best|propose|draft|come up with|figure out|decide (?:on|how))\b/i;
15
+ function isPlanningPrompt(text) {
16
+ return PLANNING_CORE_RE.test(text) || (PLANNING_DESIGN_WORD_RE.test(text) && PLANNING_PROSPECTIVE_RE.test(text));
17
+ }
18
+ const REFACTOR_RE = /\b(refactor|refactoring)\b/i;
19
+ const TEST_VALIDATION_RE = /\b(test|testing|validation|lint|vitest|jest|run)\b/i;
20
+ const IMPLEMENT_RE = /\b(implement|fix|apply|change|update|create|write|generate|modify|edit|patch|add)\b/i;
21
+ export function classifyModelRouterRoute(prompt) {
5
22
  const text = prompt.trim();
6
- if (EXPLICIT_MODIFY_REQUEST_RE.test(text))
7
- return "modify";
8
- if (READ_ONLY_QUESTION_RE.test(text))
9
- return "research";
10
- return MODIFY_INTENT_RE.test(text) ? "modify" : "research";
23
+ if (text.length === 0) {
24
+ return {
25
+ tier: "cheap",
26
+ risk: "read-only",
27
+ confidence: 0.1,
28
+ reasonCode: "empty_prompt",
29
+ reasons: ["Empty or whitespace prompt"],
30
+ };
31
+ }
32
+ // 1. Explicit read-only questions/lookups dominate (unless prefixed by explicit mutation verb).
33
+ // Planning-shaped questions are the exception: a plan steers expensive downstream work, so the
34
+ // floor is medium even when phrased as a question.
35
+ if (READ_ONLY_QUESTION_RE.test(text) && !EXPLICIT_MODIFY_REQUEST_RE.test(text)) {
36
+ if (isPlanningPrompt(text)) {
37
+ return {
38
+ tier: "medium",
39
+ risk: "read-only",
40
+ confidence: 0.75,
41
+ reasonCode: "planning_min_medium",
42
+ reasons: ["Planning/design prompts never route cheap by default; a judge may deem them trivial"],
43
+ };
44
+ }
45
+ return {
46
+ tier: "cheap",
47
+ risk: "read-only",
48
+ confidence: 0.9,
49
+ reasonCode: "read_only_question",
50
+ reasons: ["Prompt asks a question or requests an explanation, search, or lookup"],
51
+ };
52
+ }
53
+ // Helper function to match patterns and return appropriate decision
54
+ function matchKeywords(input) {
55
+ // A. High-risk / approval-required/expensive signals
56
+ if (RELEASE_PUBLISH_RE.test(input)) {
57
+ return {
58
+ tier: "expensive",
59
+ risk: "approval-required",
60
+ confidence: 0.9,
61
+ reasonCode: "release_or_publish",
62
+ reasons: ["Prompt mentions publishing, releasing, pushing, or deploying"],
63
+ };
64
+ }
65
+ if (SECURITY_AUTH_RE.test(input)) {
66
+ return {
67
+ tier: "expensive",
68
+ risk: "high-impact",
69
+ confidence: 0.95,
70
+ reasonCode: "security_or_auth",
71
+ reasons: ["Prompt mentions credentials, authentication, tokens, or secrets"],
72
+ };
73
+ }
74
+ if (DESTRUCTIVE_RE.test(input)) {
75
+ return {
76
+ tier: "expensive",
77
+ risk: "approval-required",
78
+ confidence: 0.85,
79
+ reasonCode: "destructive_or_git_history",
80
+ reasons: ["Prompt mentions deleting, resetting, cleaning, or destructive operations"],
81
+ };
82
+ }
83
+ if (SELF_MOD_MUTATE_RE.test(input)) {
84
+ return {
85
+ tier: "expensive",
86
+ risk: "approval-required",
87
+ confidence: 0.9,
88
+ reasonCode: "settings_or_self_modification",
89
+ reasons: ["Prompt mentions modifying skills, prompts, settings, tools, or self-modification"],
90
+ };
91
+ }
92
+ if (ARCHITECTURE_MUTATE_RE.test(input)) {
93
+ return {
94
+ tier: "expensive",
95
+ risk: "high-impact",
96
+ confidence: 0.9,
97
+ reasonCode: "architecture_or_ambiguous",
98
+ reasons: ["Prompt mentions core architecture or rewrite"],
99
+ };
100
+ }
101
+ // B. Explicit implementation/scoped-write signals route medium
102
+ if (isPlanningPrompt(input)) {
103
+ return {
104
+ tier: "medium",
105
+ risk: "read-only",
106
+ confidence: 0.75,
107
+ reasonCode: "planning_min_medium",
108
+ reasons: ["Planning/design prompts never route cheap by default; a judge may deem them trivial"],
109
+ };
110
+ }
111
+ if (REFACTOR_RE.test(input)) {
112
+ return {
113
+ tier: "medium",
114
+ risk: "scoped-write",
115
+ confidence: 0.8,
116
+ reasonCode: "mechanical_refactor",
117
+ reasons: ["Prompt mentions refactoring code structure"],
118
+ };
119
+ }
120
+ if (TEST_VALIDATION_RE.test(input)) {
121
+ return {
122
+ tier: "medium",
123
+ risk: "scoped-write",
124
+ confidence: 0.8,
125
+ reasonCode: "test_or_validation",
126
+ reasons: ["Prompt mentions testing, validation, or linting"],
127
+ };
128
+ }
129
+ if (IMPLEMENT_RE.test(input)) {
130
+ return {
131
+ tier: "medium",
132
+ risk: "scoped-write",
133
+ confidence: 0.85,
134
+ reasonCode: "normal_implementation",
135
+ reasons: ["Prompt mentions implementing, updating, creating, or modifying code"],
136
+ };
137
+ }
138
+ return null;
139
+ }
140
+ const match = matchKeywords(text);
141
+ if (match) {
142
+ return match;
143
+ }
144
+ // 4. Default fallbacks
145
+ return {
146
+ tier: "cheap",
147
+ risk: "read-only",
148
+ confidence: 0.5,
149
+ reasonCode: "default_read_only",
150
+ reasons: ["No explicit implementation, destructive, or release patterns detected"],
151
+ };
152
+ }
153
+ export function classifyModelRouterIntent(prompt) {
154
+ const decision = classifyModelRouterRoute(prompt);
155
+ return decision.tier === "cheap" ? "research" : "modify";
11
156
  }
12
157
  //# sourceMappingURL=intent-classifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"intent-classifier.js","sourceRoot":"","sources":["../../../src/core/model-router/intent-classifier.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GACrB,6NAA6N,CAAC;AAE/N,MAAM,0BAA0B,GAC/B,wPAAwP,CAAC;AAE1P,MAAM,qBAAqB,GAAG,wFAAwF,CAAC;AAEvH,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAqB;IAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3D,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,UAAU,CAAC;IACxD,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;AAAA,CAC3D","sourcesContent":["export type ModelRouterIntent = \"research\" | \"modify\";\n\nconst MODIFY_INTENT_RE =\n\t/\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\\b|\\b(npm|pnpm|yarn|bun|git|gh|pytest|vitest|tsc|biome)\\b|[;&|]\\s*\\w+/i;\n\nconst EXPLICIT_MODIFY_REQUEST_RE =\n\t/^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+.*\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write)\\b/i;\n\nconst READ_ONLY_QUESTION_RE = /^(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show)\\b/i;\n\nexport function classifyModelRouterIntent(prompt: string): ModelRouterIntent {\n\tconst text = prompt.trim();\n\tif (EXPLICIT_MODIFY_REQUEST_RE.test(text)) return \"modify\";\n\tif (READ_ONLY_QUESTION_RE.test(text)) return \"research\";\n\treturn MODIFY_INTENT_RE.test(text) ? \"modify\" : \"research\";\n}\n"]}
1
+ {"version":3,"file":"intent-classifier.js","sourceRoot":"","sources":["../../../src/core/model-router/intent-classifier.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RouteDecision } from \"../autonomy/contracts.ts\";\n\nexport type ModelRouterIntent = \"research\" | \"modify\";\n\nconst EXPLICIT_MODIFY_REQUEST_RE =\n\t/^(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+.*\\b(add|apply|build|change|commit|create|delete|edit|fix|generate|implement|install|modify|patch|refactor|remove|rename|replace|run|test|update|write|publish|release|push|deploy|tag|reset|clean|rewrite)\\b/i;\n\nconst READ_ONLY_QUESTION_RE =\n\t/^(?:(?:can you|could you|please|pls|go ahead and|let'?s|i need you to|we need to|you should)\\s+)?(?:how|what|why|when|where|which|who|explain|summarize|compare|describe|list|show|search|find|view|read|locate)\\b/i;\n\nconst RELEASE_PUBLISH_RE = /\\b(publish|release|push|deploy|tag)\\b/i;\nconst SECURITY_AUTH_RE = /\\b(auth|token|credential|credentials|secret|api[-_]key)\\b/i;\nconst DESTRUCTIVE_RE = /\\b(delete|reset|rm\\s+-rf|clean)\\b/i;\n\nconst SELF_MOD_MUTATE_RE =\n\t/\\b(modify|change|write|update|edit|delete|add|remove)\\s+.*\\b(skills|prompts|settings|tools|behavior)\\b|self[-_]modification/i;\nconst ARCHITECTURE_MUTATE_RE = /\\b(rewrite|redesign|change|modify|rearchitect)\\s+.*\\b(architecture|architect)\\b/i;\n\n// Planning floor: plans steer all downstream work, so planning never routes cheap by default.\n// Only the route judge may downgrade a planning prompt back to cheap (explicit trivial verdict).\n// Core terms are always planning; design/architecture words count only with prospective phrasing,\n// so lookups like \"show me the architecture\" stay cheap.\nconst PLANNING_CORE_RE = /\\b(plan|planning|roadmap|strategy)\\b/i;\nconst PLANNING_DESIGN_WORD_RE = /\\b(design|architect\\w*|structure|approach)\\b/i;\nconst PLANNING_PROSPECTIVE_RE =\n\t/\\b(how (?:should|would|do we|can we)|what(?:'s| is) the best|propose|draft|come up with|figure out|decide (?:on|how))\\b/i;\n\nfunction isPlanningPrompt(text: string): boolean {\n\treturn PLANNING_CORE_RE.test(text) || (PLANNING_DESIGN_WORD_RE.test(text) && PLANNING_PROSPECTIVE_RE.test(text));\n}\n\nconst REFACTOR_RE = /\\b(refactor|refactoring)\\b/i;\nconst TEST_VALIDATION_RE = /\\b(test|testing|validation|lint|vitest|jest|run)\\b/i;\nconst IMPLEMENT_RE = /\\b(implement|fix|apply|change|update|create|write|generate|modify|edit|patch|add)\\b/i;\n\nexport function classifyModelRouterRoute(prompt: string): RouteDecision {\n\tconst text = prompt.trim();\n\n\tif (text.length === 0) {\n\t\treturn {\n\t\t\ttier: \"cheap\",\n\t\t\trisk: \"read-only\",\n\t\t\tconfidence: 0.1,\n\t\t\treasonCode: \"empty_prompt\",\n\t\t\treasons: [\"Empty or whitespace prompt\"],\n\t\t};\n\t}\n\n\t// 1. Explicit read-only questions/lookups dominate (unless prefixed by explicit mutation verb).\n\t// Planning-shaped questions are the exception: a plan steers expensive downstream work, so the\n\t// floor is medium even when phrased as a question.\n\tif (READ_ONLY_QUESTION_RE.test(text) && !EXPLICIT_MODIFY_REQUEST_RE.test(text)) {\n\t\tif (isPlanningPrompt(text)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"read-only\",\n\t\t\t\tconfidence: 0.75,\n\t\t\t\treasonCode: \"planning_min_medium\",\n\t\t\t\treasons: [\"Planning/design prompts never route cheap by default; a judge may deem them trivial\"],\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttier: \"cheap\",\n\t\t\trisk: \"read-only\",\n\t\t\tconfidence: 0.9,\n\t\t\treasonCode: \"read_only_question\",\n\t\t\treasons: [\"Prompt asks a question or requests an explanation, search, or lookup\"],\n\t\t};\n\t}\n\n\t// Helper function to match patterns and return appropriate decision\n\tfunction matchKeywords(input: string): RouteDecision | null {\n\t\t// A. High-risk / approval-required/expensive signals\n\t\tif (RELEASE_PUBLISH_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"release_or_publish\",\n\t\t\t\treasons: [\"Prompt mentions publishing, releasing, pushing, or deploying\"],\n\t\t\t};\n\t\t}\n\t\tif (SECURITY_AUTH_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"high-impact\",\n\t\t\t\tconfidence: 0.95,\n\t\t\t\treasonCode: \"security_or_auth\",\n\t\t\t\treasons: [\"Prompt mentions credentials, authentication, tokens, or secrets\"],\n\t\t\t};\n\t\t}\n\t\tif (DESTRUCTIVE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.85,\n\t\t\t\treasonCode: \"destructive_or_git_history\",\n\t\t\t\treasons: [\"Prompt mentions deleting, resetting, cleaning, or destructive operations\"],\n\t\t\t};\n\t\t}\n\t\tif (SELF_MOD_MUTATE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"approval-required\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"settings_or_self_modification\",\n\t\t\t\treasons: [\"Prompt mentions modifying skills, prompts, settings, tools, or self-modification\"],\n\t\t\t};\n\t\t}\n\t\tif (ARCHITECTURE_MUTATE_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"expensive\",\n\t\t\t\trisk: \"high-impact\",\n\t\t\t\tconfidence: 0.9,\n\t\t\t\treasonCode: \"architecture_or_ambiguous\",\n\t\t\t\treasons: [\"Prompt mentions core architecture or rewrite\"],\n\t\t\t};\n\t\t}\n\n\t\t// B. Explicit implementation/scoped-write signals route medium\n\t\tif (isPlanningPrompt(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"read-only\",\n\t\t\t\tconfidence: 0.75,\n\t\t\t\treasonCode: \"planning_min_medium\",\n\t\t\t\treasons: [\"Planning/design prompts never route cheap by default; a judge may deem them trivial\"],\n\t\t\t};\n\t\t}\n\t\tif (REFACTOR_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.8,\n\t\t\t\treasonCode: \"mechanical_refactor\",\n\t\t\t\treasons: [\"Prompt mentions refactoring code structure\"],\n\t\t\t};\n\t\t}\n\t\tif (TEST_VALIDATION_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.8,\n\t\t\t\treasonCode: \"test_or_validation\",\n\t\t\t\treasons: [\"Prompt mentions testing, validation, or linting\"],\n\t\t\t};\n\t\t}\n\t\tif (IMPLEMENT_RE.test(input)) {\n\t\t\treturn {\n\t\t\t\ttier: \"medium\",\n\t\t\t\trisk: \"scoped-write\",\n\t\t\t\tconfidence: 0.85,\n\t\t\t\treasonCode: \"normal_implementation\",\n\t\t\t\treasons: [\"Prompt mentions implementing, updating, creating, or modifying code\"],\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tconst match = matchKeywords(text);\n\tif (match) {\n\t\treturn match;\n\t}\n\n\t// 4. Default fallbacks\n\treturn {\n\t\ttier: \"cheap\",\n\t\trisk: \"read-only\",\n\t\tconfidence: 0.5,\n\t\treasonCode: \"default_read_only\",\n\t\treasons: [\"No explicit implementation, destructive, or release patterns detected\"],\n\t};\n}\n\nexport function classifyModelRouterIntent(prompt: string): ModelRouterIntent {\n\tconst decision = classifyModelRouterRoute(prompt);\n\treturn decision.tier === \"cheap\" ? \"research\" : \"modify\";\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import type { RouteDecision } from "../autonomy/contracts.ts";
2
+ /**
3
+ * Routing-only judge: a bounded, tool-less completion (default: the medium model) that decides the
4
+ * final cheap/medium/expensive tier for a user prompt, refining the regex classifier's baseline.
5
+ * Core rule: planning is never cheap unless the judge explicitly deems the task trivial. The judge
6
+ * proposes; the existing pipeline (model resolution, auth, escalation, gates) still decides.
7
+ * Failure is honest: unparseable/timeout/unavailable falls back to the baseline with a visible
8
+ * reasonCode, never silently.
9
+ */
10
+ /** Static across calls — callers pass cacheRetention "short" so only the variable tail is billed. */
11
+ export declare const ROUTE_JUDGE_SYSTEM_PROMPT: string;
12
+ export declare const ROUTE_JUDGE_MAX_OUTPUT_TOKENS = 128;
13
+ export declare const ROUTE_JUDGE_MAX_WALL_CLOCK_MS = 10000;
14
+ export interface RouteJudgeVerdict {
15
+ tier: "cheap" | "medium" | "expensive";
16
+ risk: RouteDecision["risk"];
17
+ trivial: boolean;
18
+ reason: string;
19
+ }
20
+ export declare function buildRouteJudgeUserPrompt(args: {
21
+ prompt: string;
22
+ baseline: RouteDecision;
23
+ }): string;
24
+ export declare function parseRouteJudgeVerdict(text: string): RouteJudgeVerdict | undefined;
25
+ /** Merge a judge verdict into the baseline decision (pure; never returns learning). */
26
+ export declare function applyRouteJudgeVerdict(baseline: RouteDecision, verdict: RouteJudgeVerdict): RouteDecision;
27
+ export interface RouteJudgeRunResult {
28
+ decision: RouteDecision;
29
+ verdict?: RouteJudgeVerdict;
30
+ /** Set when the judge could not decide and the baseline was kept. */
31
+ fallbackReason?: string;
32
+ costUsd: number;
33
+ }
34
+ /**
35
+ * Run the judge over a baseline decision. The completion executor is injected (production:
36
+ * AgentSession.runIsolatedCompletion on the judge model). Never throws; every failure keeps the
37
+ * baseline with a visible fallbackReason.
38
+ */
39
+ export declare function runRouteJudge(args: {
40
+ prompt: string;
41
+ baseline: RouteDecision;
42
+ complete: (input: {
43
+ systemPrompt: string;
44
+ userPrompt: string;
45
+ signal?: AbortSignal;
46
+ }) => Promise<{
47
+ text: string;
48
+ costUsd: number;
49
+ stopReason: string;
50
+ }>;
51
+ signal?: AbortSignal;
52
+ maxWallClockMs?: number;
53
+ }): Promise<RouteJudgeRunResult>;
54
+ //# sourceMappingURL=route-judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-judge.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/route-judge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;GAOG;AAEH,uGAAqG;AACrG,eAAO,MAAM,yBAAyB,QAS1B,CAAC;AAEb,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;IACvC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,CAMnG;AAKD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAiClF;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,GAAG,aAAa,CAczG;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC;QAChG,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAyC/B","sourcesContent":["import { runBoundedCompletion } from \"../autonomy/bounded-completion.ts\";\nimport type { RouteDecision } from \"../autonomy/contracts.ts\";\n\n/**\n * Routing-only judge: a bounded, tool-less completion (default: the medium model) that decides the\n * final cheap/medium/expensive tier for a user prompt, refining the regex classifier's baseline.\n * Core rule: planning is never cheap unless the judge explicitly deems the task trivial. The judge\n * proposes; the existing pipeline (model resolution, auth, escalation, gates) still decides.\n * Failure is honest: unparseable/timeout/unavailable falls back to the baseline with a visible\n * reasonCode, never silently.\n */\n\n/** Static across calls — callers pass cacheRetention \"short\" so only the variable tail is billed. */\nexport const ROUTE_JUDGE_SYSTEM_PROMPT = [\n\t\"You are a routing judge for a coding agent. You only route; you never answer the task.\",\n\t\"Pick which model tier should handle the user prompt:\",\n\t'- \"cheap\": trivial, mechanical, read-only lookups only.',\n\t'- \"medium\": normal implementation, scoped edits, tests, and NON-trivial planning/design.',\n\t'- \"expensive\": architecture, ambiguity, security/auth, destructive or release operations, high-impact changes.',\n\t\"Planning, design, and strategy prompts are NEVER cheap unless the task is genuinely trivial.\",\n\t\"Respond with STRICT JSON only - no prose:\",\n\t'{\"tier\":\"cheap\"|\"medium\"|\"expensive\",\"risk\":\"read-only\"|\"scoped-write\"|\"high-impact\"|\"approval-required\",\"trivial\":true|false,\"reason\":\"<short reason>\"}',\n].join(\"\\n\");\n\nexport const ROUTE_JUDGE_MAX_OUTPUT_TOKENS = 128;\nexport const ROUTE_JUDGE_MAX_WALL_CLOCK_MS = 10_000;\n\nexport interface RouteJudgeVerdict {\n\ttier: \"cheap\" | \"medium\" | \"expensive\";\n\trisk: RouteDecision[\"risk\"];\n\ttrivial: boolean;\n\treason: string;\n}\n\nexport function buildRouteJudgeUserPrompt(args: { prompt: string; baseline: RouteDecision }): string {\n\treturn [\n\t\t`Baseline (regex) verdict: tier=${args.baseline.tier}, risk=${args.baseline.risk}, reason=${args.baseline.reasonCode}.`,\n\t\t\"User prompt:\",\n\t\targs.prompt.slice(0, 4000),\n\t].join(\"\\n\");\n}\n\nconst JUDGE_TIERS: readonly string[] = [\"cheap\", \"medium\", \"expensive\"];\nconst JUDGE_RISKS: readonly string[] = [\"read-only\", \"scoped-write\", \"high-impact\", \"approval-required\"];\n\nexport function parseRouteJudgeVerdict(text: string): RouteJudgeVerdict | undefined {\n\tconst trimmed = text.trim();\n\tconst candidates: string[] = [trimmed];\n\tconst fenced = /```(?:json)?\\s*([\\s\\S]*?)```/.exec(trimmed);\n\tif (fenced?.[1]) candidates.push(fenced[1].trim());\n\tconst start = trimmed.indexOf(\"{\");\n\tconst end = trimmed.lastIndexOf(\"}\");\n\tif (start >= 0 && end > start) candidates.push(trimmed.slice(start, end + 1));\n\n\tfor (const candidate of candidates) {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(candidate);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) continue;\n\t\tconst record = parsed as Record<string, unknown>;\n\t\t// The judge may never select the learning tier or anything outside the three foreground tiers.\n\t\tif (typeof record.tier !== \"string\" || !JUDGE_TIERS.includes(record.tier)) continue;\n\t\tconst risk =\n\t\t\ttypeof record.risk === \"string\" && JUDGE_RISKS.includes(record.risk)\n\t\t\t\t? (record.risk as RouteDecision[\"risk\"])\n\t\t\t\t: undefined;\n\t\tif (!risk) continue;\n\t\treturn {\n\t\t\ttier: record.tier as RouteJudgeVerdict[\"tier\"],\n\t\t\trisk,\n\t\t\ttrivial: record.trivial === true,\n\t\t\treason: typeof record.reason === \"string\" ? record.reason.slice(0, 200) : \"\",\n\t\t};\n\t}\n\treturn undefined;\n}\n\n/** Merge a judge verdict into the baseline decision (pure; never returns learning). */\nexport function applyRouteJudgeVerdict(baseline: RouteDecision, verdict: RouteJudgeVerdict): RouteDecision {\n\t// Enforce the core rule in code, not just in the judge prompt: downgrading a non-cheap\n\t// baseline to cheap requires an EXPLICIT trivial verdict. An untrusted judge saying\n\t// {tier:\"cheap\", trivial:false} for an elevated prompt keeps the baseline tier.\n\tconst tier =\n\t\tverdict.tier === \"cheap\" && baseline.tier !== \"cheap\" && !verdict.trivial ? baseline.tier : verdict.tier;\n\treturn {\n\t\t...baseline,\n\t\ttier,\n\t\trisk: verdict.risk,\n\t\tconfidence: Math.max(baseline.confidence, 0.75),\n\t\treasonCode: `judge_${tier}${verdict.trivial ? \"_trivial\" : \"\"}`,\n\t\treasons: [...baseline.reasons, `Route judge: ${verdict.reason || \"no reason given\"}`],\n\t};\n}\n\nexport interface RouteJudgeRunResult {\n\tdecision: RouteDecision;\n\tverdict?: RouteJudgeVerdict;\n\t/** Set when the judge could not decide and the baseline was kept. */\n\tfallbackReason?: string;\n\tcostUsd: number;\n}\n\n/**\n * Run the judge over a baseline decision. The completion executor is injected (production:\n * AgentSession.runIsolatedCompletion on the judge model). Never throws; every failure keeps the\n * baseline with a visible fallbackReason.\n */\nexport async function runRouteJudge(args: {\n\tprompt: string;\n\tbaseline: RouteDecision;\n\tcomplete: (input: { systemPrompt: string; userPrompt: string; signal?: AbortSignal }) => Promise<{\n\t\ttext: string;\n\t\tcostUsd: number;\n\t\tstopReason: string;\n\t}>;\n\tsignal?: AbortSignal;\n\tmaxWallClockMs?: number;\n}): Promise<RouteJudgeRunResult> {\n\tconst bounded = await runBoundedCompletion({\n\t\tmaxWallClockMs: args.maxWallClockMs ?? ROUTE_JUDGE_MAX_WALL_CLOCK_MS,\n\t\tsignal: args.signal,\n\t\texecute: (signal) =>\n\t\t\targs.complete({\n\t\t\t\tsystemPrompt: ROUTE_JUDGE_SYSTEM_PROMPT,\n\t\t\t\tuserPrompt: buildRouteJudgeUserPrompt({ prompt: args.prompt, baseline: args.baseline }),\n\t\t\t\tsignal,\n\t\t\t}),\n\t});\n\tconst costUsd = bounded.completion?.costUsd ?? 0;\n\n\tif (bounded.failure || !bounded.completion) {\n\t\treturn {\n\t\t\tdecision: {\n\t\t\t\t...args.baseline,\n\t\t\t\treasons: [...args.baseline.reasons, \"Route judge unavailable; baseline kept\"],\n\t\t\t},\n\t\t\tfallbackReason: bounded.failure ? `judge_${bounded.failure.reasonCode}` : \"judge_unavailable_fallback\",\n\t\t\tcostUsd,\n\t\t};\n\t}\n\tif (bounded.completion.stopReason === \"error\" || bounded.completion.stopReason === \"aborted\") {\n\t\treturn {\n\t\t\tdecision: { ...args.baseline, reasons: [...args.baseline.reasons, \"Route judge errored; baseline kept\"] },\n\t\t\tfallbackReason: \"judge_model_error\",\n\t\t\tcostUsd,\n\t\t};\n\t}\n\n\tconst verdict = parseRouteJudgeVerdict(bounded.completion.text);\n\tif (!verdict) {\n\t\treturn {\n\t\t\tdecision: { ...args.baseline, reasons: [...args.baseline.reasons, \"Route judge unparseable; baseline kept\"] },\n\t\t\tfallbackReason: \"judge_unparseable_fallback\",\n\t\t\tcostUsd,\n\t\t};\n\t}\n\n\treturn { decision: applyRouteJudgeVerdict(args.baseline, verdict), verdict, costUsd };\n}\n"]}