muonroi-cli 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/LICENSE +17 -5
  2. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  3. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  4. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  5. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.d.ts +64 -0
  7. package/dist/packages/agent-harness-core/src/event-tee.js +104 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +25 -0
  9. package/dist/packages/agent-harness-core/src/mcp-server.js +169 -21
  10. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  11. package/dist/packages/agent-harness-core/src/protocol.d.ts +90 -4
  12. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  13. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  14. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  15. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  16. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  17. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  18. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  19. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  20. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  21. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  22. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  23. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  24. package/dist/src/agent-harness/mock-model.d.ts +38 -0
  25. package/dist/src/agent-harness/mock-model.js +69 -3
  26. package/dist/src/agent-harness/test-spawn.js +31 -0
  27. package/dist/src/chat/chat-keychain.d.ts +7 -12
  28. package/dist/src/chat/chat-keychain.js +19 -86
  29. package/dist/src/cli/config/screen-providers.js +1 -1
  30. package/dist/src/cli/cost-forensics.d.ts +10 -0
  31. package/dist/src/cli/cost-forensics.js +18 -3
  32. package/dist/src/cli/keys-bundle.d.ts +1 -1
  33. package/dist/src/cli/keys-bundle.js +1 -1
  34. package/dist/src/cli/keys.d.ts +10 -47
  35. package/dist/src/cli/keys.js +31 -399
  36. package/dist/src/council/clarifier.d.ts +31 -3
  37. package/dist/src/council/clarifier.js +220 -32
  38. package/dist/src/council/context.js +49 -15
  39. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  40. package/dist/src/council/debate-checkpoint.js +176 -0
  41. package/dist/src/council/debate-planner.js +54 -5
  42. package/dist/src/council/debate-summary.d.ts +25 -0
  43. package/dist/src/council/debate-summary.js +85 -0
  44. package/dist/src/council/debate.d.ts +169 -2
  45. package/dist/src/council/debate.js +1265 -135
  46. package/dist/src/council/index.d.ts +108 -1
  47. package/dist/src/council/index.js +670 -197
  48. package/dist/src/council/leader.d.ts +26 -0
  49. package/dist/src/council/leader.js +150 -9
  50. package/dist/src/council/llm.d.ts +94 -0
  51. package/dist/src/council/llm.js +348 -55
  52. package/dist/src/council/panel-select.d.ts +30 -0
  53. package/dist/src/council/panel-select.js +82 -0
  54. package/dist/src/council/planner.js +40 -0
  55. package/dist/src/council/preflight.d.ts +17 -0
  56. package/dist/src/council/preflight.js +50 -2
  57. package/dist/src/council/prompts.d.ts +39 -4
  58. package/dist/src/council/prompts.js +256 -69
  59. package/dist/src/council/stance-recall.d.ts +42 -0
  60. package/dist/src/council/stance-recall.js +57 -0
  61. package/dist/src/council/strip-think.d.ts +17 -0
  62. package/dist/src/council/strip-think.js +33 -0
  63. package/dist/src/council/types.d.ts +138 -0
  64. package/dist/src/ee/artifact-cache.d.ts +16 -0
  65. package/dist/src/ee/artifact-cache.js +32 -0
  66. package/dist/src/ee/auth.d.ts +20 -0
  67. package/dist/src/ee/auth.js +54 -2
  68. package/dist/src/ee/bridge.d.ts +10 -0
  69. package/dist/src/ee/bridge.js +58 -0
  70. package/dist/src/ee/client.js +109 -21
  71. package/dist/src/ee/ee-onboarding.js +6 -26
  72. package/dist/src/ee/export-transcripts.d.ts +1 -0
  73. package/dist/src/ee/export-transcripts.js +8 -10
  74. package/dist/src/ee/extract-session.js +29 -0
  75. package/dist/src/ee/extract-style.d.ts +58 -0
  76. package/dist/src/ee/extract-style.js +270 -0
  77. package/dist/src/ee/recall-ledger.d.ts +9 -0
  78. package/dist/src/ee/recall-ledger.js +3 -0
  79. package/dist/src/ee/scope.d.ts +1 -0
  80. package/dist/src/ee/scope.js +26 -1
  81. package/dist/src/ee/search.d.ts +7 -0
  82. package/dist/src/ee/search.js +24 -0
  83. package/dist/src/ee/transcript-emit.js +2 -0
  84. package/dist/src/ee/types.d.ts +22 -0
  85. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  86. package/dist/src/ee/who-am-i-brain.js +220 -0
  87. package/dist/src/ee/who-am-i.d.ts +10 -3
  88. package/dist/src/ee/who-am-i.js +12 -0
  89. package/dist/src/ee/workflow-event.d.ts +48 -0
  90. package/dist/src/ee/workflow-event.js +81 -0
  91. package/dist/src/flow/compaction/compress.d.ts +3 -3
  92. package/dist/src/flow/compaction/compress.js +58 -8
  93. package/dist/src/flow/compaction/extract.d.ts +4 -7
  94. package/dist/src/flow/compaction/extract.js +50 -10
  95. package/dist/src/flow/compaction/index.d.ts +14 -1
  96. package/dist/src/flow/compaction/index.js +96 -3
  97. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  98. package/dist/src/flow/compaction/input-guard.js +43 -0
  99. package/dist/src/flow/compaction/progress.d.ts +35 -0
  100. package/dist/src/flow/compaction/progress.js +35 -0
  101. package/dist/src/flow/fold-planning.d.ts +36 -0
  102. package/dist/src/flow/fold-planning.js +83 -0
  103. package/dist/src/flow/hierarchy.d.ts +146 -0
  104. package/dist/src/flow/hierarchy.js +427 -0
  105. package/dist/src/flow/index.d.ts +1 -0
  106. package/dist/src/flow/index.js +2 -0
  107. package/dist/src/flow/run-artifacts.d.ts +102 -0
  108. package/dist/src/flow/run-artifacts.js +208 -0
  109. package/dist/src/generated/version.d.ts +1 -1
  110. package/dist/src/generated/version.js +1 -1
  111. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  112. package/dist/src/gsd/assessment-schema.js +134 -0
  113. package/dist/src/gsd/capability-registry.d.ts +45 -0
  114. package/dist/src/gsd/capability-registry.js +337 -0
  115. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  116. package/dist/src/gsd/complexity-assessor.js +152 -0
  117. package/dist/src/gsd/config-bridge.d.ts +7 -0
  118. package/dist/src/gsd/config-bridge.js +114 -0
  119. package/dist/src/gsd/config-loader.d.ts +27 -0
  120. package/dist/src/gsd/config-loader.js +50 -0
  121. package/dist/src/gsd/council-context.d.ts +44 -0
  122. package/dist/src/gsd/council-context.js +114 -0
  123. package/dist/src/gsd/ee-closure.d.ts +28 -0
  124. package/dist/src/gsd/ee-closure.js +49 -0
  125. package/dist/src/gsd/flags.d.ts +66 -0
  126. package/dist/src/gsd/flags.js +102 -0
  127. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  128. package/dist/src/gsd/gsd-dispatch.js +131 -0
  129. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  130. package/dist/src/gsd/gsd-runtime.js +37 -0
  131. package/dist/src/gsd/host-adapter.d.ts +11 -0
  132. package/dist/src/gsd/host-adapter.js +29 -0
  133. package/dist/src/gsd/index.d.ts +24 -1
  134. package/dist/src/gsd/index.js +27 -0
  135. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  136. package/dist/src/gsd/loop-host-contract.js +39 -0
  137. package/dist/src/gsd/loop-host.d.ts +69 -0
  138. package/dist/src/gsd/loop-host.js +245 -0
  139. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  140. package/dist/src/gsd/loop-resolver.js +79 -0
  141. package/dist/src/gsd/model-tier.d.ts +13 -0
  142. package/dist/src/gsd/model-tier.js +45 -0
  143. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  144. package/dist/src/gsd/mutation-gate.js +41 -0
  145. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  146. package/dist/src/gsd/native-roadmap.js +343 -0
  147. package/dist/src/gsd/native-state.d.ts +47 -0
  148. package/dist/src/gsd/native-state.js +220 -0
  149. package/dist/src/gsd/paths.d.ts +23 -0
  150. package/dist/src/gsd/paths.js +66 -0
  151. package/dist/src/gsd/phase-dag.d.ts +12 -0
  152. package/dist/src/gsd/phase-dag.js +94 -0
  153. package/dist/src/gsd/phase-sync.d.ts +42 -0
  154. package/dist/src/gsd/phase-sync.js +321 -0
  155. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  156. package/dist/src/gsd/pil-gate-context.js +64 -0
  157. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  158. package/dist/src/gsd/pil-gate-critic.js +74 -0
  159. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  160. package/dist/src/gsd/plan-council-prompts.js +79 -0
  161. package/dist/src/gsd/plan-council.d.ts +44 -0
  162. package/dist/src/gsd/plan-council.js +283 -0
  163. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  164. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  165. package/dist/src/gsd/product-workspace.d.ts +13 -0
  166. package/dist/src/gsd/product-workspace.js +124 -0
  167. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  168. package/dist/src/gsd/ship-bridge.js +65 -0
  169. package/dist/src/gsd/state-document.d.ts +40 -0
  170. package/dist/src/gsd/state-document.js +163 -0
  171. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  172. package/dist/src/gsd/verdict-schema.js +144 -0
  173. package/dist/src/gsd/verify-context.d.ts +22 -0
  174. package/dist/src/gsd/verify-context.js +27 -0
  175. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  176. package/dist/src/gsd/verify-council-prompts.js +85 -0
  177. package/dist/src/gsd/verify-council.d.ts +25 -0
  178. package/dist/src/gsd/verify-council.js +119 -0
  179. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  180. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  181. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  182. package/dist/src/gsd/workflow-engine.js +207 -0
  183. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  184. package/dist/src/gsd/workflow-tools.js +277 -0
  185. package/dist/src/headless/council-answers.js +4 -0
  186. package/dist/src/hooks/index.js +1 -1
  187. package/dist/src/index.js +172 -270
  188. package/dist/src/lsp/builtins.js +3 -1
  189. package/dist/src/lsp/manager.d.ts +5 -1
  190. package/dist/src/lsp/manager.js +249 -3
  191. package/dist/src/lsp/npm-cache.d.ts +11 -1
  192. package/dist/src/lsp/npm-cache.js +17 -1
  193. package/dist/src/lsp/runtime.d.ts +6 -1
  194. package/dist/src/lsp/runtime.js +17 -1
  195. package/dist/src/lsp/types.d.ts +83 -1
  196. package/dist/src/lsp/types.js +10 -0
  197. package/dist/src/maintain/pr-builder.js +23 -13
  198. package/dist/src/mcp/auto-setup.js +57 -32
  199. package/dist/src/mcp/client-pool.js +44 -16
  200. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  201. package/dist/src/mcp/lsp-tools.js +93 -2
  202. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  203. package/dist/src/mcp/mcp-keychain.js +9 -49
  204. package/dist/src/mcp/research-onboarding.js +8 -7
  205. package/dist/src/mcp/runtime.js +34 -2
  206. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  207. package/dist/src/mcp/setup-guide-text.js +22 -2
  208. package/dist/src/mcp/tools-server.d.ts +10 -0
  209. package/dist/src/mcp/tools-server.js +10 -2
  210. package/dist/src/models/catalog-client.d.ts +87 -0
  211. package/dist/src/models/catalog-client.js +105 -38
  212. package/dist/src/models/catalog.json +528 -265
  213. package/dist/src/models/registry.d.ts +22 -7
  214. package/dist/src/models/registry.js +73 -10
  215. package/dist/src/ops/doctor.js +1 -1
  216. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  217. package/dist/src/orchestrator/ask-user.js +65 -0
  218. package/dist/src/orchestrator/auto-commit.js +1 -1
  219. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  220. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  221. package/dist/src/orchestrator/cache-prefix.js +83 -0
  222. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  223. package/dist/src/orchestrator/compact-request.js +41 -0
  224. package/dist/src/orchestrator/compaction.d.ts +12 -3
  225. package/dist/src/orchestrator/compaction.js +35 -15
  226. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  227. package/dist/src/orchestrator/council-manager.js +74 -32
  228. package/dist/src/orchestrator/council-request.d.ts +49 -0
  229. package/dist/src/orchestrator/council-request.js +62 -0
  230. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  231. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  232. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  233. package/dist/src/orchestrator/error-utils.js +132 -24
  234. package/dist/src/orchestrator/grounding-check.js +39 -1
  235. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  236. package/dist/src/orchestrator/interactive-pause.js +36 -0
  237. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  238. package/dist/src/orchestrator/message-processor.js +268 -41
  239. package/dist/src/orchestrator/orchestrator.d.ts +64 -3
  240. package/dist/src/orchestrator/orchestrator.js +823 -120
  241. package/dist/src/orchestrator/preprocessor.js +3 -3
  242. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  243. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  244. package/dist/src/orchestrator/prompts.js +17 -17
  245. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  246. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  247. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  248. package/dist/src/orchestrator/retry-classifier.js +46 -2
  249. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  250. package/dist/src/orchestrator/safety-askcard.js +5 -2
  251. package/dist/src/orchestrator/safety-intercept.d.ts +50 -0
  252. package/dist/src/orchestrator/safety-intercept.js +62 -0
  253. package/dist/src/orchestrator/scope-reminder.js +1 -1
  254. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  255. package/dist/src/orchestrator/session-experience.js +2 -1
  256. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  257. package/dist/src/orchestrator/should-run-gate.js +18 -0
  258. package/dist/src/orchestrator/stall-watchdog.d.ts +31 -3
  259. package/dist/src/orchestrator/stall-watchdog.js +65 -10
  260. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  261. package/dist/src/orchestrator/stream-runner.js +115 -49
  262. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  263. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  264. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  265. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  266. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  267. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  268. package/dist/src/orchestrator/tool-engine.d.ts +41 -0
  269. package/dist/src/orchestrator/tool-engine.js +846 -66
  270. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  271. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  272. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  273. package/dist/src/orchestrator/turn-watchdog.d.ts +44 -0
  274. package/dist/src/orchestrator/turn-watchdog.js +84 -0
  275. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  276. package/dist/src/pil/agent-operating-contract.js +6 -4
  277. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  278. package/dist/src/pil/cheap-model-playbook.js +5 -1
  279. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  280. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  281. package/dist/src/pil/discovery-types.d.ts +1 -0
  282. package/dist/src/pil/discovery.d.ts +1 -1
  283. package/dist/src/pil/discovery.js +18 -13
  284. package/dist/src/pil/layer1-intent.d.ts +18 -6
  285. package/dist/src/pil/layer1-intent.js +66 -757
  286. package/dist/src/pil/layer15-context-scan.js +15 -1
  287. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  288. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  289. package/dist/src/pil/layer3-ee-injection.js +23 -8
  290. package/dist/src/pil/layer4-gsd.js +69 -16
  291. package/dist/src/pil/layer5-context.js +7 -3
  292. package/dist/src/pil/layer6-output.d.ts +23 -0
  293. package/dist/src/pil/layer6-output.js +5 -1
  294. package/dist/src/pil/llm-classify.d.ts +111 -5
  295. package/dist/src/pil/llm-classify.js +421 -189
  296. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  297. package/dist/src/pil/native-capabilities-workbook.js +8 -0
  298. package/dist/src/pil/pipeline.js +36 -2
  299. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  300. package/dist/src/pil/repo-grounding-probe.js +136 -0
  301. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  302. package/dist/src/pil/repo-structure-hints.js +45 -0
  303. package/dist/src/pil/response-tools.js +5 -3
  304. package/dist/src/pil/schema.d.ts +1 -0
  305. package/dist/src/pil/schema.js +2 -0
  306. package/dist/src/pil/types.d.ts +18 -0
  307. package/dist/src/playbook/directives.d.ts +4 -0
  308. package/dist/src/playbook/directives.js +17 -5
  309. package/dist/src/product-loop/artifact-io.js +4 -0
  310. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  311. package/dist/src/product-loop/backlog-builder.js +30 -6
  312. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  313. package/dist/src/product-loop/criteria-seed.js +200 -0
  314. package/dist/src/product-loop/discovery-context-format.js +3 -1
  315. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  316. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  317. package/dist/src/product-loop/discovery-interview.js +60 -12
  318. package/dist/src/product-loop/discovery-recommender.js +2 -1
  319. package/dist/src/product-loop/discovery-schema.js +19 -2
  320. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  321. package/dist/src/product-loop/discovery-triage.js +109 -0
  322. package/dist/src/product-loop/gather.js +150 -2
  323. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  324. package/dist/src/product-loop/ideal-trace.js +64 -0
  325. package/dist/src/product-loop/index.d.ts +13 -1
  326. package/dist/src/product-loop/index.js +340 -52
  327. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  328. package/dist/src/product-loop/loop-driver.js +330 -106
  329. package/dist/src/product-loop/phase-plan.d.ts +21 -0
  330. package/dist/src/product-loop/phase-plan.js +81 -6
  331. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  332. package/dist/src/product-loop/phase-rituals.js +8 -3
  333. package/dist/src/product-loop/phase-runner.js +39 -12
  334. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  335. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  336. package/dist/src/product-loop/sprint-runner.d.ts +173 -0
  337. package/dist/src/product-loop/sprint-runner.js +863 -19
  338. package/dist/src/product-loop/types.d.ts +61 -5
  339. package/dist/src/providers/adapter.d.ts +1 -1
  340. package/dist/src/providers/adapter.js +3 -4
  341. package/dist/src/providers/anthropic.d.ts +9 -8
  342. package/dist/src/providers/anthropic.js +13 -47
  343. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  344. package/dist/src/providers/auth/browser-flow.js +1 -1
  345. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  346. package/dist/src/providers/auth/grok-oauth.js +30 -5
  347. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  348. package/dist/src/providers/auth/openai-oauth.js +15 -1
  349. package/dist/src/providers/auth/registry.js +0 -34
  350. package/dist/src/providers/auth/token-store.d.ts +9 -9
  351. package/dist/src/providers/auth/token-store.js +8 -67
  352. package/dist/src/providers/auth/types.d.ts +9 -1
  353. package/dist/src/providers/auth/types.js +1 -1
  354. package/dist/src/providers/capabilities.d.ts +24 -5
  355. package/dist/src/providers/capabilities.js +42 -24
  356. package/dist/src/providers/endpoints.d.ts +2 -2
  357. package/dist/src/providers/endpoints.js +11 -10
  358. package/dist/src/providers/env-store.d.ts +17 -0
  359. package/dist/src/providers/env-store.js +228 -0
  360. package/dist/src/providers/keychain.d.ts +22 -18
  361. package/dist/src/providers/keychain.js +127 -140
  362. package/dist/src/providers/mcp-vision-bridge.js +56 -146
  363. package/dist/src/providers/openai-compatible.js +8 -1
  364. package/dist/src/providers/pricing.d.ts +2 -2
  365. package/dist/src/providers/pricing.js +3 -13
  366. package/dist/src/providers/runtime.d.ts +43 -3
  367. package/dist/src/providers/runtime.js +88 -14
  368. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  369. package/dist/src/providers/strategies/base.strategy.js +24 -1
  370. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  371. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  372. package/dist/src/providers/strategies/registry.js +4 -4
  373. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  374. package/dist/src/providers/strategies/thinking-mode.js +288 -1
  375. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  376. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  377. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  378. package/dist/src/providers/types.d.ts +5 -6
  379. package/dist/src/providers/types.js +2 -2
  380. package/dist/src/providers/vision-backend.d.ts +47 -0
  381. package/dist/src/providers/vision-backend.js +258 -0
  382. package/dist/src/providers/vision-proxy.d.ts +22 -9
  383. package/dist/src/providers/vision-proxy.js +63 -132
  384. package/dist/src/providers/warm.d.ts +65 -0
  385. package/dist/src/providers/warm.js +145 -0
  386. package/dist/src/providers/wire-debug.js +95 -0
  387. package/dist/src/router/decide.d.ts +13 -0
  388. package/dist/src/router/decide.js +138 -36
  389. package/dist/src/router/peak-hour.d.ts +38 -0
  390. package/dist/src/router/peak-hour.js +107 -0
  391. package/dist/src/router/step-router.js +3 -2
  392. package/dist/src/router/warm.js +4 -5
  393. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  394. package/dist/src/scaffold/continuation-prompt.js +26 -0
  395. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  396. package/dist/src/scaffold/point-to-existing.js +25 -0
  397. package/dist/src/self-qa/agentic-loop.js +6 -5
  398. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  399. package/dist/src/{ui/state → state}/active-run.js +21 -0
  400. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  401. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  402. package/dist/src/state/turn-trace.d.ts +43 -0
  403. package/dist/src/state/turn-trace.js +32 -0
  404. package/dist/src/storage/db.js +2 -1
  405. package/dist/src/storage/index.d.ts +1 -1
  406. package/dist/src/storage/index.js +1 -1
  407. package/dist/src/storage/interaction-log.d.ts +1 -1
  408. package/dist/src/storage/migrations.js +71 -1
  409. package/dist/src/storage/sessions.d.ts +28 -10
  410. package/dist/src/storage/sessions.js +78 -21
  411. package/dist/src/storage/transcript-view.js +1 -1
  412. package/dist/src/storage/transcript.d.ts +51 -0
  413. package/dist/src/storage/transcript.js +340 -15
  414. package/dist/src/tools/file.d.ts +15 -0
  415. package/dist/src/tools/file.js +32 -0
  416. package/dist/src/tools/git-safety.d.ts +19 -0
  417. package/dist/src/tools/git-safety.js +168 -0
  418. package/dist/src/tools/native-tools.d.ts +1 -1
  419. package/dist/src/tools/native-tools.js +81 -1
  420. package/dist/src/tools/registry.d.ts +20 -0
  421. package/dist/src/tools/registry.js +576 -23
  422. package/dist/src/tools/research.d.ts +29 -0
  423. package/dist/src/tools/research.js +233 -0
  424. package/dist/src/types/index.d.ts +147 -4
  425. package/dist/src/ui/app.js +0 -0
  426. package/dist/src/ui/cards/product-status-card.js +1 -1
  427. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  428. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  429. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  430. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  431. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  432. package/dist/src/ui/components/compact-progress-card.js +42 -0
  433. package/dist/src/ui/components/context-rail.d.ts +26 -0
  434. package/dist/src/ui/components/context-rail.js +33 -0
  435. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  436. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  437. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  438. package/dist/src/ui/components/council-debate-pill.js +34 -0
  439. package/dist/src/ui/components/council-info-card.js +2 -2
  440. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  441. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  442. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  443. package/dist/src/ui/components/council-message-bubble.js +16 -15
  444. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  445. package/dist/src/ui/components/council-phase-timeline.js +66 -17
  446. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  447. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  448. package/dist/src/ui/components/council-question-card.js +13 -12
  449. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  450. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  451. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  452. package/dist/src/ui/components/council-round-group.js +88 -0
  453. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  454. package/dist/src/ui/components/council-status-list.js +36 -24
  455. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  456. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  457. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  458. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  459. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  460. package/dist/src/ui/components/message-view.d.ts +15 -0
  461. package/dist/src/ui/components/message-view.js +50 -1
  462. package/dist/src/ui/components/prompt-box.js +18 -16
  463. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  464. package/dist/src/ui/components/session-tree-card.js +46 -0
  465. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  466. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  467. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  468. package/dist/src/ui/components/task-list-panel.js +22 -2
  469. package/dist/src/ui/components/tool-group.d.ts +15 -3
  470. package/dist/src/ui/components/tool-group.js +69 -11
  471. package/dist/src/ui/containers/modals-layer.d.ts +4 -2
  472. package/dist/src/ui/containers/modals-layer.js +2 -2
  473. package/dist/src/ui/council-harness-event.d.ts +57 -0
  474. package/dist/src/ui/council-harness-event.js +46 -0
  475. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  476. package/dist/src/ui/heartbeat-debug.js +45 -0
  477. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  478. package/dist/src/ui/mcp-modal.js +2 -4
  479. package/dist/src/ui/modals/api-key-modal.js +1 -1
  480. package/dist/src/ui/modals/connect-modal.js +4 -3
  481. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  482. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  483. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  484. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  485. package/dist/src/ui/picker-providers.d.ts +1 -1
  486. package/dist/src/ui/picker-providers.js +1 -1
  487. package/dist/src/ui/primitives/index.d.ts +1 -0
  488. package/dist/src/ui/primitives/index.js +2 -0
  489. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  490. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  491. package/dist/src/ui/slash/compact.js +5 -7
  492. package/dist/src/ui/slash/cost.js +1 -1
  493. package/dist/src/ui/slash/council.js +19 -1
  494. package/dist/src/ui/slash/debug.d.ts +3 -31
  495. package/dist/src/ui/slash/debug.js +9 -20
  496. package/dist/src/ui/slash/ee.js +81 -0
  497. package/dist/src/ui/slash/ideal.d.ts +6 -2
  498. package/dist/src/ui/slash/ideal.js +97 -7
  499. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  500. package/dist/src/ui/slash/menu-items.js +23 -20
  501. package/dist/src/ui/slash/registry.d.ts +2 -0
  502. package/dist/src/ui/slash/registry.js +4 -0
  503. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  504. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  505. package/dist/src/ui/status-bar/index.d.ts +1 -1
  506. package/dist/src/ui/status-bar/index.js +7 -3
  507. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  508. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  509. package/dist/src/ui/theme.d.ts +1 -0
  510. package/dist/src/ui/theme.js +2 -0
  511. package/dist/src/ui/types.d.ts +7 -0
  512. package/dist/src/ui/use-app-logic.js +0 -0
  513. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  514. package/dist/src/ui/utils/agent-activities.js +96 -0
  515. package/dist/src/ui/utils/format.d.ts +14 -0
  516. package/dist/src/ui/utils/format.js +23 -3
  517. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  518. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  519. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  520. package/dist/src/ui/utils/tool-summary.js +91 -0
  521. package/dist/src/usage/downgrade.js +2 -2
  522. package/dist/src/usage/product-ledger.js +2 -2
  523. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  524. package/dist/src/utils/event-loop-monitor.js +107 -0
  525. package/dist/src/utils/install-manager.js +2 -1
  526. package/dist/src/utils/llm-deadline.d.ts +14 -0
  527. package/dist/src/utils/llm-deadline.js +19 -0
  528. package/dist/src/utils/logger.js +2 -2
  529. package/dist/src/utils/loop-profiler.d.ts +102 -0
  530. package/dist/src/utils/loop-profiler.js +202 -0
  531. package/dist/src/utils/permission-mode.js +5 -3
  532. package/dist/src/utils/redactor.js +1 -1
  533. package/dist/src/utils/settings.d.ts +180 -5
  534. package/dist/src/utils/settings.js +271 -31
  535. package/dist/src/utils/side-question.d.ts +1 -2
  536. package/dist/src/utils/side-question.js +2 -2
  537. package/dist/src/utils/visible-retry.d.ts +11 -0
  538. package/dist/src/utils/visible-retry.js +10 -1
  539. package/dist/src/verify/entrypoint.d.ts +1 -1
  540. package/dist/src/verify/entrypoint.js +52 -17
  541. package/dist/src/verify/orchestrator.d.ts +1 -1
  542. package/dist/src/verify/orchestrator.js +20 -3
  543. package/dist/src/verify/recipes.d.ts +13 -0
  544. package/dist/src/verify/recipes.js +15 -0
  545. package/package.json +134 -132
  546. package/dist/src/cli/bw-vault.d.ts +0 -55
  547. package/dist/src/cli/bw-vault.js +0 -133
  548. package/dist/src/mcp/ee-tools.d.ts +0 -46
  549. package/dist/src/mcp/ee-tools.js +0 -193
  550. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  551. package/dist/src/providers/auth/gcloud.js +0 -102
  552. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  553. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  554. package/dist/src/providers/gemini.d.ts +0 -11
  555. package/dist/src/providers/gemini.js +0 -45
  556. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  557. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  558. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  559. package/dist/src/providers/strategies/google.strategy.js +0 -174
  560. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  561. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  562. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -70,36 +70,84 @@ export function shouldContinueAfterMidLoopStall(s) {
70
70
  export function stallRepromptBackoffMs(attempt) {
71
71
  return Math.min(500 * 2 ** (Math.max(1, attempt) - 1), 4_000);
72
72
  }
73
- export function createStallWatchdog(timeoutMs, onFire) {
73
+ export function createStallWatchdog(timeoutMs, onFire, progressOpts,
74
+ /**
75
+ * Optional gate consulted the instant a timer would fire. When it returns
76
+ * true the watchdog RE-ARMS instead of aborting — used to hold the stream
77
+ * open while a blocking interactive card (`ask_user`) awaits a human, without
78
+ * counting that wait as a provider stall.
79
+ */
80
+ shouldSuppressFire) {
74
81
  const controller = new AbortController();
75
82
  let firedFlag = false;
76
83
  let timer = null;
77
84
  const enabled = Number.isFinite(timeoutMs) && timeoutMs > 0;
85
+ const progressTimeoutMs = progressOpts?.progressTimeoutMs ?? 0;
86
+ const progressEnabled = Number.isFinite(progressTimeoutMs) && progressTimeoutMs > 0;
87
+ let progressTimer = null;
88
+ const fire = (onSpecificFire) => {
89
+ if (firedFlag)
90
+ return;
91
+ firedFlag = true;
92
+ // Stop the OTHER timer so it can't also fire after the abort (e.g. the
93
+ // any-activity stall timer that was armed just before the progress timer
94
+ // tripped — otherwise both onFire callbacks would run).
95
+ clearBoth();
96
+ // DOMException(TimeoutError) mirrors AbortSignal.timeout() semantics so
97
+ // downstream isAbortError-style checks treat it as an abort.
98
+ controller.abort(new DOMException(STALL_ABORT_REASON, "TimeoutError"));
99
+ try {
100
+ onSpecificFire?.();
101
+ }
102
+ catch {
103
+ /* callback must not break the watchdog */
104
+ }
105
+ };
78
106
  const arm = () => {
79
107
  if (!enabled)
80
108
  return;
81
109
  timer = setTimeout(() => {
82
- firedFlag = true;
83
- // DOMException(TimeoutError) mirrors AbortSignal.timeout() semantics so
84
- // downstream isAbortError-style checks treat it as an abort.
85
- controller.abort(new DOMException(STALL_ABORT_REASON, "TimeoutError"));
86
- try {
87
- onFire?.();
88
- }
89
- catch {
90
- /* callback must not break the watchdog */
110
+ // Hold open (re-arm) while an interactive card blocks the turn — the
111
+ // human is thinking, not a stalled provider.
112
+ if (shouldSuppressFire?.()) {
113
+ arm();
114
+ return;
91
115
  }
116
+ fire(onFire);
92
117
  }, timeoutMs);
93
118
  // Don't keep the event loop alive solely for the watchdog (Node).
94
119
  timer.unref?.();
95
120
  };
121
+ const armProgress = () => {
122
+ if (!progressEnabled)
123
+ return;
124
+ progressTimer = setTimeout(() => {
125
+ if (shouldSuppressFire?.()) {
126
+ armProgress();
127
+ return;
128
+ }
129
+ fire(progressOpts?.onProgressFire);
130
+ }, progressTimeoutMs);
131
+ progressTimer.unref?.();
132
+ };
96
133
  const clear = () => {
97
134
  if (timer) {
98
135
  clearTimeout(timer);
99
136
  timer = null;
100
137
  }
101
138
  };
139
+ const clearProgress = () => {
140
+ if (progressTimer) {
141
+ clearTimeout(progressTimer);
142
+ progressTimer = null;
143
+ }
144
+ };
145
+ function clearBoth() {
146
+ clear();
147
+ clearProgress();
148
+ }
102
149
  arm();
150
+ armProgress();
103
151
  return {
104
152
  signal: controller.signal,
105
153
  pet() {
@@ -108,8 +156,15 @@ export function createStallWatchdog(timeoutMs, onFire) {
108
156
  clear();
109
157
  arm();
110
158
  },
159
+ petProgress() {
160
+ if (!progressEnabled || firedFlag)
161
+ return;
162
+ clearProgress();
163
+ armProgress();
164
+ },
111
165
  dispose() {
112
166
  clear();
167
+ clearProgress();
113
168
  },
114
169
  fired() {
115
170
  return firedFlag;
@@ -3,7 +3,6 @@ import { type ResolvedModelRuntime } from "../providers/runtime.js";
3
3
  import type { ProviderId } from "../providers/types.js";
4
4
  import { BashTool } from "../tools/bash.js";
5
5
  import type { AgentMode, TaskRequest, ToolResult } from "../types/index.js";
6
- import type { LegacyProvider } from "./agent-options.js";
7
6
  import type { CrossTurnDedup } from "./cross-turn-dedup.js";
8
7
  import type { ReadPathBudget } from "./read-path-budget.js";
9
8
  /**
@@ -11,8 +10,6 @@ import type { ReadPathBudget } from "./read-path-budget.js";
11
10
  * without holding a circular reference. Mirrors the CouncilManager DI pattern.
12
11
  */
13
12
  export interface StreamRunnerDeps {
14
- /** Current top-level provider instance (already validated). */
15
- getProvider(): LegacyProvider;
16
13
  /** Resolve a specific task tier's model id (uses Agent's role config). */
17
14
  resolveModelForTask(task: "compact" | "explore" | "general" | "title" | "verify"): string;
18
15
  /** Top-level model id (for routed_from telemetry). */
@@ -108,6 +105,19 @@ export type SetupOutcome = {
108
105
  kind: "prepared";
109
106
  prepared: PreparedSubAgentCall;
110
107
  } | SetupShortCircuit;
108
+ /**
109
+ * Emit one sub-agent diagnostic line (MUONROI_DEBUG_SUBAGENT=1).
110
+ *
111
+ * Writes to the file named by MUONROI_SUBAGENT_DEBUG_LOG when set, else stderr.
112
+ * The file sink exists because stderr is a dead end for the case this
113
+ * diagnostic was written for: under the MCP harness the TUI runs as a child
114
+ * whose stderr nobody reads (opentui-spawn.ts consumes only the fd3/named-pipe
115
+ * stream), so every line went into a pipe no one drains. G1 — "Task failed: No
116
+ * output generated" — was named, given this flag, and never diagnosed, because
117
+ * the diagnostic could not reach whoever turned it on. Mirrors the working
118
+ * MUONROI_COUNCIL_DEBUG_LOG sink in council/llm.ts.
119
+ */
120
+ export declare function writeSubagentDebug(enabled: boolean, line: string): void;
111
121
  /**
112
122
  * StreamRunner — extracted sub-agent stream lifecycle.
113
123
  *
@@ -25,7 +25,8 @@
25
25
  // - C1 (DeepSeek cache split read) — onFinish normalization
26
26
  // - C3 (cross-turn dedup wrap) — wrapToolSetWithDedup
27
27
  // - F1 (sub-agent cumulative cap) — wrapToolSetWithCap
28
- // - siliconflow reasoning-strip — taskCaps.sanitizeHistory
28
+ // - reasoning-strip (provider quirk) — taskCaps.sanitizeHistory
29
+ import { appendFileSync } from "node:fs";
29
30
  import { stepCountIs, streamText } from "ai";
30
31
  import { recordArtifact } from "../ee/artifact-cache.js";
31
32
  import { getDefaultEEClient } from "../ee/intercept.js";
@@ -36,14 +37,14 @@ import { injectCheapModelWorkbook, shouldInjectCheapModelWorkbook, subagentTaskT
36
37
  import { getProviderCapabilities } from "../providers/capabilities.js";
37
38
  import { getVisionGuidanceForTextOnly } from "../providers/mcp-vision-bridge.js";
38
39
  import { captureToolSchemas } from "../providers/patch-zod-schema.js";
39
- import { buildTurnProviderOptions, requireRuntimeProvider, resolveModelRuntime, shouldDropParam, } from "../providers/runtime.js";
40
+ import { buildTurnProviderOptions, factoryForModel, requireRuntimeProvider, resolveModelRuntime, resolveTemperatureParam, shouldDropParam, } from "../providers/runtime.js";
40
41
  import { needsVisionProxy } from "../providers/vision-proxy.js";
41
42
  import { wireDebug } from "../providers/wire-debug.js";
43
+ import { statusBarStore } from "../state/status-bar-store.js";
42
44
  import { BashTool } from "../tools/bash.js";
43
45
  import { createBuiltinTools } from "../tools/registry.js";
44
- import { statusBarStore } from "../ui/status-bar/store.js";
45
46
  import { openUrl } from "../utils/open-url.js";
46
- import { getCurrentShellSettings, getProviderStallTimeoutMs, getSubAgentBudgetChars, getSubAgentCompactKeepLast, getSubAgentCompactThresholdChars, loadMcpServers, loadValidSubAgents, } from "../utils/settings.js";
47
+ import { getCurrentShellSettings, getProviderProgressTimeoutMs, getProviderStallTimeoutMs, getSubAgentBudgetChars, getSubAgentCompactKeepLast, getSubAgentCompactThresholdChars, loadMcpServers, loadValidSubAgents, } from "../utils/settings.js";
47
48
  import { resolveShell } from "../utils/shell.js";
48
49
  import { prepareVerifySandbox } from "../verify/entrypoint.js";
49
50
  import { asNumber } from "./batch-utils.js";
@@ -58,8 +59,37 @@ import { attachReminderToMessages, buildScopeReminder, cadenceForSize, shouldInj
58
59
  import { recordCompaction, recordElision } from "./session-experience.js";
59
60
  import { createStallWatchdog, STALL_ERROR_MESSAGE } from "./stall-watchdog.js";
60
61
  import { wrapToolSetWithCap } from "./sub-agent-cap.js";
61
- import { compactSubAgentMessages, applyAnthropicPromptCaching } from "./subagent-compactor.js";
62
+ import { applyAnthropicPromptCaching, compactSubAgentMessages } from "./subagent-compactor.js";
62
63
  import { combineAbortSignals, firstLine, formatSubagentActivity } from "./tool-utils.js";
64
+ /**
65
+ * Emit one sub-agent diagnostic line (MUONROI_DEBUG_SUBAGENT=1).
66
+ *
67
+ * Writes to the file named by MUONROI_SUBAGENT_DEBUG_LOG when set, else stderr.
68
+ * The file sink exists because stderr is a dead end for the case this
69
+ * diagnostic was written for: under the MCP harness the TUI runs as a child
70
+ * whose stderr nobody reads (opentui-spawn.ts consumes only the fd3/named-pipe
71
+ * stream), so every line went into a pipe no one drains. G1 — "Task failed: No
72
+ * output generated" — was named, given this flag, and never diagnosed, because
73
+ * the diagnostic could not reach whoever turned it on. Mirrors the working
74
+ * MUONROI_COUNCIL_DEBUG_LOG sink in council/llm.ts.
75
+ */
76
+ export function writeSubagentDebug(enabled, line) {
77
+ if (!enabled)
78
+ return;
79
+ const text = `[subagent] ${line}\n`;
80
+ const path = process.env.MUONROI_SUBAGENT_DEBUG_LOG?.trim();
81
+ if (!path) {
82
+ process.stderr.write(text);
83
+ return;
84
+ }
85
+ try {
86
+ appendFileSync(path, text);
87
+ }
88
+ catch (err) {
89
+ // Never let diagnostics become the failure. Fall back to stderr and say why.
90
+ process.stderr.write(`[subagent] debug-log append failed (path=${path}): ${err instanceof Error ? err.message : String(err)}\n${text}`);
91
+ }
92
+ }
63
93
  /**
64
94
  * StreamRunner — extracted sub-agent stream lifecycle.
65
95
  *
@@ -81,7 +111,6 @@ export class StreamRunner {
81
111
  * Returns either a prepared call or a short-circuit ToolResult.
82
112
  */
83
113
  async setup(request, onActivity, signal) {
84
- const provider = this.deps.getProvider();
85
114
  const agentKey = String(request.agent);
86
115
  const isExplore = agentKey === "explore";
87
116
  const isGeneral = agentKey === "general";
@@ -137,23 +166,36 @@ export class StreamRunner {
137
166
  });
138
167
  // Resolve child model early so we can pass modelId to createBuiltinTools
139
168
  // (needed for vision-proxy tools: analyze_image / ask_vision_proxy).
140
- const childModelId = normalizeModelId(isVision
141
- ? VISION_MODEL
142
- : isComputer
143
- ? COMPUTER_MODEL
144
- : custom
145
- ? custom.model
146
- : this.deps.resolveModelForTask(isExplore ? "explore" : isVerify || isVerifyDetect || isVerifyManifest ? "verify" : "general"));
169
+ const childModelId = normalizeModelId(request.modelId
170
+ ? request.modelId
171
+ : isVision
172
+ ? VISION_MODEL
173
+ : isComputer
174
+ ? COMPUTER_MODEL
175
+ : custom
176
+ ? custom.model
177
+ : this.deps.resolveModelForTask(isExplore ? "explore" : isVerify || isVerifyDetect || isVerifyManifest ? "verify" : "general"));
147
178
  // Mirror the file-local `createTools` wrapper from orchestrator.ts —
148
179
  // pass modelId so registry can inject analyze_image/ask_vision_proxy for
149
180
  // text-only child models (needsVisionProxy).
150
- const childBaseToolsRaw = createBuiltinTools(childBash, childMode, { modelId: childModelId });
181
+ const childBaseToolsRaw = createBuiltinTools(childBash, childMode, {
182
+ modelId: childModelId,
183
+ sessionId: this.deps.getSessionId(),
184
+ });
151
185
  // Wrap with the cumulative cap so the sub-agent's tool loop cannot
152
186
  // accumulate unbounded tool_result tokens. See sub-agent-cap.ts for the
153
187
  // tiered compression schedule. The cap is per-invocation; each sub-agent
154
188
  // gets a fresh budget.
155
189
  const subAgentCapBudget = getSubAgentBudgetChars();
156
- const subAgentCap = wrapToolSetWithCap(childBaseToolsRaw, { maxCumulativeChars: subAgentCapBudget });
190
+ const subAgentCap = wrapToolSetWithCap(childBaseToolsRaw, {
191
+ maxCumulativeChars: subAgentCapBudget,
192
+ // Opt-out toggle (default ON). The cap dedups identical tool outputs by
193
+ // content hash; set MUONROI_SUBAGENT_CAP_DEDUP=0 to disable. Used by the
194
+ // B3 compaction E2E, where the harness re-processes each tool result and
195
+ // the self-dedup would otherwise collapse distinct reads into pointer
196
+ // stubs before cumulative history can grow enough to trigger compaction.
197
+ dedupRepeatOutputs: process.env.MUONROI_SUBAGENT_CAP_DEDUP !== "0",
198
+ });
157
199
  // Phase C3: layer cross-turn dedup ON TOP of the per-invocation cap. The
158
200
  // cap sees raw output (for accurate cumulative accounting); dedup sees
159
201
  // the already-trimmed output that will actually reach the model.
@@ -176,12 +218,18 @@ export class StreamRunner {
176
218
  if (childModelId !== topModelId) {
177
219
  statusBarStore.setState({ routed_from: topModelId, model: childModelId });
178
220
  }
221
+ // The vision branch must build its model from the CHILD model's own factory.
222
+ // It previously called the PARENT session's factory directly, so a sub-agent
223
+ // routed to another provider had its id POSTed to the parent's endpoint.
179
224
  const childRuntime = isVision
180
- ? {
181
- ...resolveModelRuntime(provider, childModelId),
182
- model: provider.responses?.(childModelId) ?? provider(childModelId),
183
- }
184
- : resolveModelRuntime(provider, childModelId);
225
+ ? (() => {
226
+ const childFactory = factoryForModel(childModelId);
227
+ return {
228
+ ...resolveModelRuntime(childModelId),
229
+ model: childFactory.responses?.(childModelId) ?? childFactory(childModelId),
230
+ };
231
+ })()
232
+ : resolveModelRuntime(childModelId);
185
233
  const taskCaps = getProviderCapabilities(requireRuntimeProvider(childRuntime));
186
234
  if (isComputer && !taskCaps.supportsClientTools(childRuntime.modelInfo)) {
187
235
  return {
@@ -255,7 +303,7 @@ export class StreamRunner {
255
303
  void signal;
256
304
  const childMessages = [{ role: "user", content: childPrompt }];
257
305
  // The main agent manages its sub-agents, so don't apply an arbitrary hard limit.
258
- const maxSteps = request.maxToolRounds ?? (this.deps.getMaxToolRounds() * 2);
306
+ const maxSteps = request.maxToolRounds ?? this.deps.getMaxToolRounds() * 2;
259
307
  // F1 parity — derive per-turn providerOptions so the sub-agent OpenAI calls
260
308
  // carry a stable session-derived promptCacheKey (every tool round routes to
261
309
  // the same cache node, keeping the unchanging prefix cached). The top-level
@@ -308,10 +356,7 @@ export class StreamRunner {
308
356
  // diagnose "No output generated" / silent task failures (e.g. with
309
357
  // gpt-5.4 reasoning models). Disabled by default — zero cost.
310
358
  const debugSubagent = process.env.MUONROI_DEBUG_SUBAGENT === "1";
311
- const debugLog = (line) => {
312
- if (debugSubagent)
313
- process.stderr.write(`[subagent] ${line}\n`);
314
- };
359
+ const debugLog = writeSubagentDebug.bind(null, debugSubagent);
315
360
  if (debugSubagent) {
316
361
  const mi = childRuntime.modelInfo;
317
362
  debugLog(`start: model=${childRuntime.modelId} provider=${mi?.provider} reasoning=${mi?.reasoning} thinkingType=${mi?.thinkingType} supportsClientTools=${mi?.supportsClientTools} supportsMaxOutputTokens=${mi?.supportsMaxOutputTokens} agent=${prepared.request.agent}`);
@@ -328,7 +373,6 @@ export class StreamRunner {
328
373
  // already strips it via `dropParam`, the sub-agent path was missing
329
374
  // the same guard, causing G1: "Task failed: No output generated.").
330
375
  const childDropMaxOutput = shouldDropParam(childRuntime, "maxOutputTokens");
331
- const childDropTemperature = shouldDropParam(childRuntime, "temperature");
332
376
  // Phase B3: compact older tool_results out of the running message history
333
377
  // before each AI SDK step. First step (stepNumber === 0) has no history
334
378
  // worth compacting; later steps are where cumulative input balloons.
@@ -346,8 +390,8 @@ export class StreamRunner {
346
390
  providerOptions: childProviderOptions,
347
391
  });
348
392
  }
349
- // SiliconFlow DeepSeek thinking-mode rejects assistant history with
350
- // `reasoning` parts (HTTP 400 code 20015). The siliconflow capability
393
+ // Some DeepSeek thinking-mode endpoints reject assistant history with
394
+ // `reasoning` parts (HTTP 400 code 20015). A provider capability
351
395
  // override strips them; every other provider's capability is identity.
352
396
  const subMessagesForCall = taskCaps.sanitizeHistory(childMessages);
353
397
  // Phase 4 Plan 04 (4B) — mirror top-level scope-ceiling integration.
@@ -377,8 +421,26 @@ export class StreamRunner {
377
421
  // and is re-armed by stall.pet() on every chunk. Cheap models (which run
378
422
  // mostly as sub-agents via SAMR) hit this most.
379
423
  let stallTriggered = false;
424
+ // Second timer (progressTimeoutMs) is the no-forward-progress guard: the
425
+ // stall timer re-arms on EVERY chunk — including a reasoning model's
426
+ // `reasoning-delta` chunks — so a sub-agent stuck in an endless
427
+ // chain-of-thought keeps petting it and it never fires (observed live
428
+ // 2026-07-10: a deepseek-v4-flash sprint sub-agent churned reasoning for
429
+ // 30+ min, 1.4M input tokens, ZERO text/tool output; the 2-min stall
430
+ // watchdog never tripped). The progress timer is reset ONLY by
431
+ // stall.petProgress() on real output (text-delta / tool-call), so a runaway
432
+ // reasoning loop is aborted while a legitimately long reasoning burst that
433
+ // DOES eventually emit output survives. Both timers abort the same signal
434
+ // and set stallTriggered, so the existing surface-and-return path handles it.
380
435
  const stall = createStallWatchdog(getProviderStallTimeoutMs(), () => {
381
436
  stallTriggered = true;
437
+ }, {
438
+ progressTimeoutMs: getProviderProgressTimeoutMs(),
439
+ onProgressFire: () => {
440
+ stallTriggered = true;
441
+ console.error(`[stream-runner] sub-agent aborted: no text/tool output for ${getProviderProgressTimeoutMs()}ms ` +
442
+ `(runaway reasoning / no forward progress) model=${childRuntime.modelId}`);
443
+ },
382
444
  });
383
445
  const result = streamText({
384
446
  model: childRuntime.model,
@@ -396,11 +458,11 @@ export class StreamRunner {
396
458
  prepareStep: ({ messages, stepNumber }) => {
397
459
  if (stepNumber < 1)
398
460
  return undefined;
399
- // SiliconFlow internal multi-step loop: AI-SDK accumulates streamed
400
- // reasoning parts into in-flight assistant history and re-POSTs them
401
- // on the next step within the same streamText call — orchestrator-
402
- // level strip at call setup never sees this. Strip per step too via
403
- // the capability hook (identity for non-siliconflow providers).
461
+ // Internal multi-step loop: AI-SDK accumulates streamed reasoning
462
+ // parts into in-flight assistant history and re-POSTs them on the
463
+ // next step within the same streamText call — orchestrator-level
464
+ // strip at call setup never sees this. Strip per step too via the
465
+ // capability hook (identity for providers without the quirk).
404
466
  const stripped = taskCaps.sanitizeHistory(messages);
405
467
  // G1 + G2 — pass the sub-agent's model context window so the
406
468
  // compactor can use a token-aware threshold and shrink the keep
@@ -428,17 +490,16 @@ export class StreamRunner {
428
490
  break;
429
491
  }
430
492
  }
431
- // Idea 4 persist for sub-agent elisions (best-effort; may lack full session but EE can still index the artifact content).
432
- const persistSubArtifact = (toolCallId, toolName, fullContent, reason) => {
493
+ const persistSubArtifact = (toolCallId, toolName, fullContent, reason, summary) => {
433
494
  // Local-first durable cache so ee_query rehydrates even when EE is down.
434
495
  recordArtifact(toolCallId, toolName, fullContent);
435
- recordElision(toolCallId, toolName, fullContent.length, stepNumber);
496
+ recordElision(toolCallId, toolName, fullContent.length, stepNumber, summary);
436
497
  try {
437
498
  getDefaultEEClient()
438
499
  .extract({
439
500
  transcript: fullContent.slice(0, 4000),
440
501
  projectPath: process.cwd(),
441
- meta: { source: "tool-artifact", toolCallId, toolName, reason },
502
+ meta: { source: "tool-artifact", toolCallId, toolName, reason, summary },
442
503
  }, AbortSignal.timeout(600))
443
504
  .catch(() => { });
444
505
  }
@@ -494,7 +555,7 @@ export class StreamRunner {
494
555
  return undefined;
495
556
  return { messages: finalMessages };
496
557
  },
497
- ...(childDropTemperature ? {} : { temperature: isExplore ? 0.2 : 0.5 }),
558
+ ...resolveTemperatureParam(childRuntime, isExplore ? 0.2 : 0.5),
498
559
  ...(childDropMaxOutput ? {} : { maxOutputTokens: Math.min(this.deps.getMaxTokens(), 8_192) }),
499
560
  ...(childProviderOptions ? { providerOptions: childProviderOptions } : {}),
500
561
  onFinish: ({ totalUsage, finishReason }) => {
@@ -557,6 +618,7 @@ export class StreamRunner {
557
618
  });
558
619
  }
559
620
  if (part.type === "text-delta") {
621
+ stall.petProgress(); // real forward progress — reset the no-progress guard
560
622
  textDeltaCount++;
561
623
  assistantText += part.text;
562
624
  // Task 2.6b — emit llm-token (agent-mode only; high-volume, default-off per Phase 4).
@@ -576,6 +638,7 @@ export class StreamRunner {
576
638
  continue;
577
639
  }
578
640
  if (part.type === "tool-call") {
641
+ stall.petProgress(); // real forward progress — reset the no-progress guard
579
642
  toolCallCount++;
580
643
  lastActivity = formatSubagentActivity(part.toolName, part.input);
581
644
  onActivity?.(lastActivity);
@@ -653,7 +716,9 @@ export class StreamRunner {
653
716
  ...priorMessages,
654
717
  {
655
718
  role: "user",
656
- content: "You've reached your investigation budget and have not written your findings yet. Stop now — do NOT call any more tools. Write your final synthesis FOR THE PARENT AGENT: lead with the answer to the delegated task, cite the concrete file:line behind each claim, then note any gaps or the recommended next step. Be concise; the parent only ingests this message.",
719
+ content: "You've reached your tool execution budget (max steps) for this turn and have not written your final answer yet. Stop now — do NOT call any more tools.\n\n" +
720
+ "Option A: If you have enough findings, write your final synthesis FOR THE PARENT AGENT: lead with the answer to the delegated task, cite the concrete file:line behind each claim, then note any gaps or the recommended next step. Be concise; the parent only ingests this message.\n\n" +
721
+ "Option B: If you need to continue working but are blocked by this limit, you can request the system to compact the context and start a fresh turn. To do this, reply with EXACTLY this format and nothing else:\n/compact <instructions on what to focus on after compaction>",
657
722
  },
658
723
  ],
659
724
  tools: {},
@@ -756,30 +821,31 @@ export class StreamRunner {
756
821
  // MUONROI_DEBUG_SUBAGENT=1 so we can see whether `msg` is "No output
757
822
  // generated" (AI SDK validation failure on empty assistant response)
758
823
  // or a deeper provider error.
759
- if (process.env.MUONROI_DEBUG_SUBAGENT === "1") {
824
+ const debugCatch = process.env.MUONROI_DEBUG_SUBAGENT === "1";
825
+ if (debugCatch) {
760
826
  const e = err;
761
- process.stderr.write(`[subagent] catch: name=${e.name ?? "?"} statusCode=${e.statusCode ?? "?"} message=${(e.message ?? "").slice(0, 400)}\n`);
827
+ writeSubagentDebug(true, `catch: name=${e.name ?? "?"} statusCode=${e.statusCode ?? "?"} message=${(e.message ?? "").slice(0, 400)}`);
762
828
  if (e.cause !== undefined) {
763
829
  try {
764
- process.stderr.write(`[subagent] catch.cause=${JSON.stringify(e.cause, Object.getOwnPropertyNames(e.cause)).slice(0, 600)}\n`);
830
+ writeSubagentDebug(true, `catch.cause=${JSON.stringify(e.cause, Object.getOwnPropertyNames(e.cause)).slice(0, 600)}`);
765
831
  }
766
- catch {
767
- process.stderr.write(`[subagent] catch.cause(string)=${String(e.cause).slice(0, 400)}\n`);
832
+ catch (jsonErr) {
833
+ writeSubagentDebug(true, `catch.cause(string)=${String(e.cause).slice(0, 400)} [stringify failed: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}]`);
768
834
  }
769
835
  }
770
836
  if (e.responseBody !== undefined) {
771
- process.stderr.write(`[subagent] catch.responseBody=${String(e.responseBody).slice(0, 600)}\n`);
837
+ writeSubagentDebug(true, `catch.responseBody=${String(e.responseBody).slice(0, 600)}`);
772
838
  }
773
839
  if (e.data !== undefined) {
774
840
  try {
775
- process.stderr.write(`[subagent] catch.data=${JSON.stringify(e.data).slice(0, 400)}\n`);
841
+ writeSubagentDebug(true, `catch.data=${JSON.stringify(e.data).slice(0, 400)}`);
776
842
  }
777
- catch {
778
- /* non-serializable */
843
+ catch (jsonErr) {
844
+ writeSubagentDebug(true, `catch.data(unserializable): ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`);
779
845
  }
780
846
  }
781
847
  if (e.stack)
782
- process.stderr.write(`[subagent] stack: ${e.stack.split("\n").slice(0, 6).join(" | ")}\n`);
848
+ writeSubagentDebug(true, `stack: ${e.stack.split("\n").slice(0, 6).join(" | ")}`);
783
849
  }
784
850
  const output = `Task failed: ${msg}`;
785
851
  return {
@@ -18,7 +18,19 @@
18
18
  * returns a fresh wrapper with its own counters. Don't share across
19
19
  * sub-agent runs.
20
20
  *
21
- * Tiers (default 120_000 chars cumulative ~30k tokens):
21
+ * BUDGET SOURCE — do not read the 120_000 below as "the sub-agent budget".
22
+ * DEFAULT_MAX_CUMULATIVE_CHARS (120_000) is only the fallback used when
23
+ * wrapToolSetWithCap() is called WITHOUT maxCumulativeChars. The wired
24
+ * budgets come from settings, not this constant:
25
+ * - sub-agent (`task`) turns: getSubAgentBudgetChars() → default 240_000
26
+ * (settings.ts), passed at stream-runner.ts.
27
+ * - top-level orchestrator turn: getTopLevelToolBudgetChars() → default
28
+ * 400_000, wired at tool-engine.ts with looser 0.5/0.8 tier ratios.
29
+ * See docs/agent-harness/CONTEXT-CONTROL-LAYERS.md for how this cap relates
30
+ * to the other four context-control layers (compactor, rotation, reactive
31
+ * sub-session, top-level cap).
32
+ *
33
+ * Tiers (percentages of whichever budget is in effect, NOT of 120_000):
22
34
  * < 30% → pass through (A1's 32KB per-call cap already applied)
23
35
  * 30-70% → truncate each new result to 8_000 chars head/tail
24
36
  * 70-100% → truncate to 2_000 chars head + "[budget low, finalize work]" note
@@ -18,13 +18,28 @@
18
18
  * returns a fresh wrapper with its own counters. Don't share across
19
19
  * sub-agent runs.
20
20
  *
21
- * Tiers (default 120_000 chars cumulative ~30k tokens):
21
+ * BUDGET SOURCE — do not read the 120_000 below as "the sub-agent budget".
22
+ * DEFAULT_MAX_CUMULATIVE_CHARS (120_000) is only the fallback used when
23
+ * wrapToolSetWithCap() is called WITHOUT maxCumulativeChars. The wired
24
+ * budgets come from settings, not this constant:
25
+ * - sub-agent (`task`) turns: getSubAgentBudgetChars() → default 240_000
26
+ * (settings.ts), passed at stream-runner.ts.
27
+ * - top-level orchestrator turn: getTopLevelToolBudgetChars() → default
28
+ * 400_000, wired at tool-engine.ts with looser 0.5/0.8 tier ratios.
29
+ * See docs/agent-harness/CONTEXT-CONTROL-LAYERS.md for how this cap relates
30
+ * to the other four context-control layers (compactor, rotation, reactive
31
+ * sub-session, top-level cap).
32
+ *
33
+ * Tiers (percentages of whichever budget is in effect, NOT of 120_000):
22
34
  * < 30% → pass through (A1's 32KB per-call cap already applied)
23
35
  * 30-70% → truncate each new result to 8_000 chars head/tail
24
36
  * 70-100% → truncate to 2_000 chars head + "[budget low, finalize work]" note
25
37
  * ≥ 100% → return error stub that signals the agent to stop expanding scope
26
38
  */
27
39
  import { createHash } from "node:crypto";
40
+ // Fallback ONLY — used when wrapToolSetWithCap gets no maxCumulativeChars.
41
+ // Real wired budgets are 240_000 (sub-agent) / 400_000 (top-level) from
42
+ // settings; see the header note. Keep this in sync only as the no-arg floor.
28
43
  const DEFAULT_MAX_CUMULATIVE_CHARS = 120_000;
29
44
  const DEFAULT_DEDUP_MIN_CHARS = 500;
30
45
  const DEFAULT_MID_TIER_RATIO = 0.3;
@@ -9,5 +9,17 @@ export type TierLookup = (tier: "fast" | "balanced" | "premium", preferProvider?
9
9
  * Resolve the model id for a task kind on a given provider. Walks the tier
10
10
  * preference list, accepting only same-provider matches, and falls back to
11
11
  * `fallbackModelId` (the parent model) when nothing matches.
12
+ *
13
+ * `opts.parentTier` (the top-level / parent model's tier) acts as a ceiling:
14
+ * a delegated sub-task never runs on a HIGHER tier than the parent. Without
15
+ * this cap, a `verify` sub-agent (prefs `["premium","balanced"]`) spawned from
16
+ * a flash (fast) parent silently promotes to premium — e.g. on a DeepSeek-only
17
+ * setup (no balanced model) every verify sub-agent lands on deepseek-v4-pro.
18
+ * The parent already did the hard thinking; the sub-task should not exceed it.
19
+ *
20
+ * The cap is skipped when `parentTier` is omitted (preserves legacy behavior
21
+ * for callers that have not been threaded yet).
12
22
  */
13
- export declare function resolveModelForTask(task: ModelTaskKind, providerId: string, fallbackModelId: string, lookup?: TierLookup): string;
23
+ export declare function resolveModelForTask(task: ModelTaskKind, providerId: string, fallbackModelId: string, lookup?: TierLookup, opts?: {
24
+ parentTier?: "fast" | "balanced" | "premium";
25
+ }): string;
@@ -16,7 +16,7 @@
16
16
  * - verify : validation/verification → premium first; rigor matters
17
17
  * more than cost here, so we do NOT downgrade by default.
18
18
  */
19
- import { getModelByTier } from "../models/registry.js";
19
+ import { getRoutedModelByTier } from "../router/peak-hour.js";
20
20
  export const TASK_TIER_PREFS = {
21
21
  compact: ["fast", "balanced"],
22
22
  title: ["fast", "balanced"],
@@ -29,13 +29,27 @@ export const TASK_TIER_PREFS = {
29
29
  // exists to perform. Mirrors council `verify` role preference.
30
30
  verify: ["premium", "balanced"],
31
31
  };
32
+ const TIER_RANK = { fast: 0, balanced: 1, premium: 2 };
32
33
  /**
33
34
  * Resolve the model id for a task kind on a given provider. Walks the tier
34
35
  * preference list, accepting only same-provider matches, and falls back to
35
36
  * `fallbackModelId` (the parent model) when nothing matches.
37
+ *
38
+ * `opts.parentTier` (the top-level / parent model's tier) acts as a ceiling:
39
+ * a delegated sub-task never runs on a HIGHER tier than the parent. Without
40
+ * this cap, a `verify` sub-agent (prefs `["premium","balanced"]`) spawned from
41
+ * a flash (fast) parent silently promotes to premium — e.g. on a DeepSeek-only
42
+ * setup (no balanced model) every verify sub-agent lands on deepseek-v4-pro.
43
+ * The parent already did the hard thinking; the sub-task should not exceed it.
44
+ *
45
+ * The cap is skipped when `parentTier` is omitted (preserves legacy behavior
46
+ * for callers that have not been threaded yet).
36
47
  */
37
- export function resolveModelForTask(task, providerId, fallbackModelId, lookup = getModelByTier) {
48
+ export function resolveModelForTask(task, providerId, fallbackModelId, lookup = getRoutedModelByTier, opts) {
49
+ const ceilingRank = opts?.parentTier ? TIER_RANK[opts.parentTier] : undefined;
38
50
  for (const tier of TASK_TIER_PREFS[task] ?? ["balanced"]) {
51
+ if (ceilingRank !== undefined && TIER_RANK[tier] > ceilingRank)
52
+ continue;
39
53
  const m = lookup(tier, providerId);
40
54
  if (m?.provider === providerId)
41
55
  return m.id;