muonroi-cli 1.8.3 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -1,11 +1,34 @@
1
1
  import { APICallError } from "@ai-sdk/provider";
2
+ import { isProviderThinkingDegraded, markProviderThinkingDegrade } from "../providers/strategies/thinking-mode.js";
2
3
  import { STALL_ABORT_REASON } from "./stall-watchdog.js";
4
+ /**
5
+ * Detect the generic, spec-undocumented param rejection from the z.ai GLM
6
+ * coding endpoint (HTTP 400 code 1210 "Invalid API parameter") and the
7
+ * opencode-go Console Go proxy (HTTP 400 invalid_request "Upstream request
8
+ * failed"). z.ai does NOT publish the exact constraint (verified 2026-07-02 vs
9
+ * docs.z.ai/api-reference/api-code — 1210 is an intentionally generic bucket),
10
+ * so no client transform can guarantee prevention. These are matched narrowly
11
+ * (400 + specific phrasing) so ordinary 400s stay non-transient.
12
+ */
13
+ function isProviderParamReject(err) {
14
+ const status = APICallError.isInstance(err)
15
+ ? err.statusCode
16
+ : (err?.statusCode ??
17
+ err?.status);
18
+ if (status !== 400)
19
+ return false;
20
+ const message = err instanceof Error ? err.message : String(err);
21
+ const body = APICallError.isInstance(err) && typeof err.responseBody === "string" ? err.responseBody : "";
22
+ const hay = `${message}\n${body}`;
23
+ return /invalid api parameter|upstream request failed|unexpected end of JSON input|code"?\s*:?\s*1210/i.test(hay);
24
+ }
3
25
  /**
4
26
  * Classifies a stream error as transient (safe to retry) or non-transient.
5
27
  *
6
28
  * Transient:
7
29
  * - Network-level errors: ECONNREFUSED, ETIMEDOUT, ECONNRESET, EAI_AGAIN,
8
- * "fetch failed", "Unable to connect", "socket hang up", "network"
30
+ * "fetch failed", "Unable to connect", "socket hang up", "network",
31
+ * "socket connection was closed unexpectedly" (Bun stream drop)
9
32
  * - HTTP 408 (request timeout), 425 (too early), 429 (rate limit), 5xx
10
33
  * - TypeError with "fetch failed" message (browser/bun/node fetch layer)
11
34
  * - AbortSignal.timeout firing (err.name === "TimeoutError")
@@ -38,6 +61,19 @@ export function classifyStreamError(err, depth = 0) {
38
61
  if (e.name === "TimeoutError") {
39
62
  return { transient: true, reason: "timeout-error" };
40
63
  }
64
+ // z.ai / opencode-go generic param reject (1210 / "Upstream request failed"):
65
+ // give EXACTLY ONE retry with a degraded-but-valid body. The first sighting
66
+ // latches thinking OFF (markProviderThinkingDegrade) so the rebuilt request
67
+ // (factory re-invoked by withStreamRetry) sends the validator-safe shape;
68
+ // parallel tool_calls are already split by the transform. If it STILL rejects
69
+ // after we've degraded, stop retrying — the cause is beyond our client fix.
70
+ if (isProviderParamReject(err)) {
71
+ if (isProviderThinkingDegraded()) {
72
+ return { transient: false, reason: "provider-param-reject-after-degrade" };
73
+ }
74
+ markProviderThinkingDegrade();
75
+ return { transient: true, reason: "provider-param-reject-degrade-retry" };
76
+ }
41
77
  // AI SDK APICallError with statusCode
42
78
  if (APICallError.isInstance(err)) {
43
79
  const status = err.statusCode;
@@ -61,6 +97,10 @@ export function classifyStreamError(err, depth = 0) {
61
97
  }
62
98
  }
63
99
  const message = typeof e.message === "string" ? e.message : "";
100
+ // Rate limits (including wrapped messages from proxies like "Console Go")
101
+ if (/rate limit|rate-limited|429|too many requests/i.test(message)) {
102
+ return { transient: true, reason: "rate-limit-message" };
103
+ }
64
104
  // Malformed function/tool name errors — non-transient (own handler elsewhere)
65
105
  if (/invalid.*function.*name|function.*name.*invalid|malformed.*tool|NoSuchTool/i.test(message)) {
66
106
  return { transient: false, reason: "malformed-tool-name" };
@@ -86,7 +126,11 @@ function isTransientStatusCode(code) {
86
126
  return code === 408 || code === 425 || code === 429 || (code >= 500 && code <= 599);
87
127
  }
88
128
  function isTransientMessage(message) {
89
- return /ECONNREFUSED|ETIMEDOUT|ECONNRESET|EAI_AGAIN|fetch failed|Unable to connect|network|socket hang up/i.test(message);
129
+ // "socket connection was closed unexpectedly" is Bun's fetch phrasing when a
130
+ // provider drops a streaming response mid-flight — a transient network drop,
131
+ // not a client error. Left unclassified it escapes as an unhandledRejection,
132
+ // which OpenTUI's handleError turns into an un-dismissable console overlay.
133
+ return /ECONNREFUSED|ETIMEDOUT|ECONNRESET|EAI_AGAIN|fetch failed|Unable to connect|network|socket hang up|socket connection was closed|socket.*closed unexpectedly/i.test(message);
90
134
  }
91
135
  /**
92
136
  * Parse Retry-After header value to milliseconds.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * safety-intercept.ts — pure decision helpers for the tool-engine safety-block
3
+ * interceptor (tool-engine.ts). Extracted so the block-parse and the
4
+ * permission-mode policy are unit-testable in isolation from the giant
5
+ * executeToolEngine generator.
6
+ *
7
+ * Flow context: bash.execute / registry precheck emit a tool result whose text
8
+ * starts with `BLOCKED (<kind>): <reason>`. The tool-engine joins output+error,
9
+ * parses it here, and decides whether to auto-block, auto-allow (yolo), or show
10
+ * the safety-override askcard via deps.askSafetyOverride.
11
+ */
12
+ import type { PermissionMode } from "../utils/permission-mode.js";
13
+ import type { SafetyBlockKind } from "./safety-askcard.js";
14
+ export interface ParsedSafetyBlock {
15
+ kind: SafetyBlockKind;
16
+ reason: string;
17
+ }
18
+ /**
19
+ * Parse a joined tool-result text into a safety block, or null when the text
20
+ * is not a BLOCKED marker.
21
+ *
22
+ * Tolerant of leading whitespace: when bash.execute returns `{error: "BLOCKED
23
+ * (...)"}` with no `output`, the joined text is the raw marker — but a result
24
+ * shape that carries an empty-string `output` produces a leading "\n" before
25
+ * the marker, which an anchored `/^BLOCKED/` would miss and silently drop the
26
+ * askcard (hard-stop with no prompt). Stripping leading whitespace first makes
27
+ * the parse robust to both shapes while still requiring the marker at the head
28
+ * of the (trimmed) text — so a command whose normal output merely mentions
29
+ * "BLOCKED (...)" mid-stream is not mistaken for a real block.
30
+ */
31
+ export declare function parseSafetyBlock(outputText: string): ParsedSafetyBlock | null;
32
+ /**
33
+ * Whether a parsed block should be auto-allowed (allow-once) without showing
34
+ * the askcard, given the active permission mode.
35
+ *
36
+ * Policy (confirmed with the user):
37
+ * - `yolo` auto-allows lower-severity blocks (`git-safety`, `dangerous`) so
38
+ * the "don't ask me" mode actually stops asking for routine guardrails.
39
+ * - `catastrophic` ALWAYS shows the askcard, even in yolo — an irreversible
40
+ * destroyer (rm -rf /dev, mkfs, sudo …) must never run unattended.
41
+ * - `empty-bash` is handled separately (auto-block, no card) and never
42
+ * reaches this policy.
43
+ * - `safe` / `auto-edit` always show the card for any real block.
44
+ */
45
+ export declare function shouldAutoAllowYolo(kind: SafetyBlockKind, mode: PermissionMode): boolean;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * safety-intercept.ts — pure decision helpers for the tool-engine safety-block
3
+ * interceptor (tool-engine.ts). Extracted so the block-parse and the
4
+ * permission-mode policy are unit-testable in isolation from the giant
5
+ * executeToolEngine generator.
6
+ *
7
+ * Flow context: bash.execute / registry precheck emit a tool result whose text
8
+ * starts with `BLOCKED (<kind>): <reason>`. The tool-engine joins output+error,
9
+ * parses it here, and decides whether to auto-block, auto-allow (yolo), or show
10
+ * the safety-override askcard via deps.askSafetyOverride.
11
+ */
12
+ /**
13
+ * Parse a joined tool-result text into a safety block, or null when the text
14
+ * is not a BLOCKED marker.
15
+ *
16
+ * Tolerant of leading whitespace: when bash.execute returns `{error: "BLOCKED
17
+ * (...)"}` with no `output`, the joined text is the raw marker — but a result
18
+ * shape that carries an empty-string `output` produces a leading "\n" before
19
+ * the marker, which an anchored `/^BLOCKED/` would miss and silently drop the
20
+ * askcard (hard-stop with no prompt). Stripping leading whitespace first makes
21
+ * the parse robust to both shapes while still requiring the marker at the head
22
+ * of the (trimmed) text — so a command whose normal output merely mentions
23
+ * "BLOCKED (...)" mid-stream is not mistaken for a real block.
24
+ */
25
+ export function parseSafetyBlock(outputText) {
26
+ if (typeof outputText !== "string")
27
+ return null;
28
+ const match = outputText.replace(/^\s+/, "").match(/^BLOCKED \(([^)]+)\):\s*(.*)/);
29
+ if (!match)
30
+ return null;
31
+ return { kind: match[1], reason: match[2] ?? "" };
32
+ }
33
+ /**
34
+ * Whether a parsed block should be auto-allowed (allow-once) without showing
35
+ * the askcard, given the active permission mode.
36
+ *
37
+ * Policy (confirmed with the user):
38
+ * - `yolo` auto-allows lower-severity blocks (`git-safety`, `dangerous`) so
39
+ * the "don't ask me" mode actually stops asking for routine guardrails.
40
+ * - `catastrophic` ALWAYS shows the askcard, even in yolo — an irreversible
41
+ * destroyer (rm -rf /dev, mkfs, sudo …) must never run unattended.
42
+ * - `empty-bash` is handled separately (auto-block, no card) and never
43
+ * reaches this policy.
44
+ * - `safe` / `auto-edit` always show the card for any real block.
45
+ */
46
+ export function shouldAutoAllowYolo(kind, mode) {
47
+ if (mode !== "yolo")
48
+ return false;
49
+ if (kind === "catastrophic")
50
+ return false;
51
+ if (kind === "empty-bash")
52
+ return false;
53
+ return true;
54
+ }
55
+ //# sourceMappingURL=safety-intercept.js.map
@@ -229,7 +229,7 @@ export function buildCheckpointReminder(iteration, hasEECheckpoint) {
229
229
  * compared `stripped.length` (a message count, ~tens) against a char-scaled
230
230
  * threshold (~156000), so the warning could never fire — session 2b7a10219499.
231
231
  */
232
- export function shouldPreWarnCompaction(promptChars, thresholdChars, ratio = 0.78) {
232
+ export function shouldPreWarnCompaction(promptChars, thresholdChars, ratio = 0.6) {
233
233
  if (thresholdChars <= 0 || promptChars <= 0)
234
234
  return false;
235
235
  return promptChars >= Math.floor(thresholdChars * ratio);
@@ -27,6 +27,7 @@ export interface ElisionRecord {
27
27
  chars: number;
28
28
  /** prepareStep step number at which it was elided. */
29
29
  step: number;
30
+ summary?: string;
30
31
  }
31
32
  export interface SessionExperience {
32
33
  compactions: number;
@@ -40,7 +41,7 @@ export interface SessionExperience {
40
41
  /** Record that B3/B4 compaction actually elided something at `step`. */
41
42
  export declare function recordCompaction(step: number): void;
42
43
  /** Record a single tool output the compactor rewrote into a stub. */
43
- export declare function recordElision(toolCallId: string, toolName: string, chars: number, step: number): void;
44
+ export declare function recordElision(toolCallId: string, toolName: string, chars: number, step: number, summary?: string): void;
44
45
  /**
45
46
  * Record an ee_query rehydrate of an elided artifact, tagged by where it came
46
47
  * from. `unavailable` means the agent asked for an artifact that was neither in
@@ -38,7 +38,7 @@ export function recordCompaction(step) {
38
38
  state.lastCompactionStep = Number.isFinite(step) ? step : state.lastCompactionStep;
39
39
  }
40
40
  /** Record a single tool output the compactor rewrote into a stub. */
41
- export function recordElision(toolCallId, toolName, chars, step) {
41
+ export function recordElision(toolCallId, toolName, chars, step, summary) {
42
42
  if (!toolCallId)
43
43
  return;
44
44
  state.elisions.push({
@@ -46,6 +46,7 @@ export function recordElision(toolCallId, toolName, chars, step) {
46
46
  toolName: toolName || "",
47
47
  chars: Number.isFinite(chars) && chars > 0 ? Math.floor(chars) : 0,
48
48
  step: Number.isFinite(step) ? step : 0,
49
+ summary,
49
50
  });
50
51
  // FIFO trim — keep the most recent MAX_ELISIONS.
51
52
  if (state.elisions.length > MAX_ELISIONS) {
@@ -0,0 +1,5 @@
1
+ export declare function shouldRunGate(pilCtx: {
2
+ intentKind?: string | null;
3
+ resumeDigest?: string | null;
4
+ activeRunId?: string | null;
5
+ }, readPhase: () => string | null): boolean;
@@ -0,0 +1,18 @@
1
+ // Keep the gate off pure chitchat, but ON for a resumed heavy task the classifier
2
+ // mislabels as chitchat (continuation phrases — preprocessor.ts:118-134). Reading
3
+ // STATE.md phase is the resume signal (execute phase = an active run).
4
+ export function shouldRunGate(pilCtx, readPhase) {
5
+ if (pilCtx.intentKind !== "chitchat")
6
+ return true;
7
+ if (pilCtx.resumeDigest || pilCtx.activeRunId)
8
+ return true;
9
+ try {
10
+ return readPhase() === "execute";
11
+ }
12
+ catch (err) {
13
+ // Missing/corrupt .planning is the normal "no active run" case, not an error — treat as no run.
14
+ console.error(`[pil-gate] shouldRunGate readPhase failed (treating as no active run): ${err.message}`);
15
+ return false;
16
+ }
17
+ }
18
+ //# sourceMappingURL=should-run-gate.js.map
@@ -21,13 +21,34 @@
21
21
  export interface StallWatchdog {
22
22
  /** Combine this into the streamText abortSignal. */
23
23
  readonly signal: AbortSignal;
24
- /** Call on every received stream chunk to reset the stall timer. */
24
+ /** Call on every received stream chunk to reset the any-activity stall timer. */
25
25
  pet(): void;
26
- /** Stop the timer (call when the stream completes or errors). Idempotent. */
26
+ /**
27
+ * Call ONLY on real forward-progress chunks (a text-delta or a tool-call) to
28
+ * reset the no-forward-progress timer. No-op when the watchdog was created
29
+ * without a progressTimeoutMs. This is what makes the guard catch a reasoning
30
+ * model stuck in an endless chain-of-thought: `pet()` (called on EVERY chunk,
31
+ * including reasoning-delta) keeps the any-activity timer alive, but the
32
+ * progress timer only survives if actual output flows.
33
+ */
34
+ petProgress(): void;
35
+ /** Stop the timers (call when the stream completes or errors). Idempotent. */
27
36
  dispose(): void;
28
37
  /** True iff the watchdog aborted the stream because of a stall. */
29
38
  fired(): boolean;
30
39
  }
40
+ /** Options for the second (no-forward-progress) timer of a stall watchdog. */
41
+ export interface StallWatchdogProgressOpts {
42
+ /**
43
+ * If > 0, arm a SECOND timer that is reset only by petProgress() (real
44
+ * output), not by pet() (any chunk). Aborts the same signal when no forward
45
+ * progress happens for this long — catching runaway reasoning that keeps the
46
+ * any-activity timer alive with reasoning-delta chunks. <= 0 disables it.
47
+ */
48
+ progressTimeoutMs: number;
49
+ /** Called when the no-forward-progress timer fires (before abort). */
50
+ onProgressFire?: () => void;
51
+ }
31
52
  export declare const STALL_ABORT_REASON = "provider-stall";
32
53
  /** User-facing message surfaced when the stall watchdog fires. */
33
54
  export declare const STALL_ERROR_MESSAGE: string;
@@ -109,4 +130,4 @@ export declare function shouldContinueAfterMidLoopStall(s: MidLoopStallState): b
109
130
  * (1-based): 500 → 1000 → 2000 → 4000 → 4000.
110
131
  */
111
132
  export declare function stallRepromptBackoffMs(attempt: number): number;
112
- export declare function createStallWatchdog(timeoutMs: number, onFire?: () => void): StallWatchdog;
133
+ export declare function createStallWatchdog(timeoutMs: number, onFire?: () => void, progressOpts?: StallWatchdogProgressOpts): StallWatchdog;
@@ -70,36 +70,63 @@ 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
74
  const controller = new AbortController();
75
75
  let firedFlag = false;
76
76
  let timer = null;
77
77
  const enabled = Number.isFinite(timeoutMs) && timeoutMs > 0;
78
+ const progressTimeoutMs = progressOpts?.progressTimeoutMs ?? 0;
79
+ const progressEnabled = Number.isFinite(progressTimeoutMs) && progressTimeoutMs > 0;
80
+ let progressTimer = null;
81
+ const fire = (onSpecificFire) => {
82
+ if (firedFlag)
83
+ return;
84
+ firedFlag = true;
85
+ // Stop the OTHER timer so it can't also fire after the abort (e.g. the
86
+ // any-activity stall timer that was armed just before the progress timer
87
+ // tripped — otherwise both onFire callbacks would run).
88
+ clearBoth();
89
+ // DOMException(TimeoutError) mirrors AbortSignal.timeout() semantics so
90
+ // downstream isAbortError-style checks treat it as an abort.
91
+ controller.abort(new DOMException(STALL_ABORT_REASON, "TimeoutError"));
92
+ try {
93
+ onSpecificFire?.();
94
+ }
95
+ catch {
96
+ /* callback must not break the watchdog */
97
+ }
98
+ };
78
99
  const arm = () => {
79
100
  if (!enabled)
80
101
  return;
81
- 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 */
91
- }
92
- }, timeoutMs);
102
+ timer = setTimeout(() => fire(onFire), timeoutMs);
93
103
  // Don't keep the event loop alive solely for the watchdog (Node).
94
104
  timer.unref?.();
95
105
  };
106
+ const armProgress = () => {
107
+ if (!progressEnabled)
108
+ return;
109
+ progressTimer = setTimeout(() => fire(progressOpts?.onProgressFire), progressTimeoutMs);
110
+ progressTimer.unref?.();
111
+ };
96
112
  const clear = () => {
97
113
  if (timer) {
98
114
  clearTimeout(timer);
99
115
  timer = null;
100
116
  }
101
117
  };
118
+ const clearProgress = () => {
119
+ if (progressTimer) {
120
+ clearTimeout(progressTimer);
121
+ progressTimer = null;
122
+ }
123
+ };
124
+ function clearBoth() {
125
+ clear();
126
+ clearProgress();
127
+ }
102
128
  arm();
129
+ armProgress();
103
130
  return {
104
131
  signal: controller.signal,
105
132
  pet() {
@@ -108,8 +135,15 @@ export function createStallWatchdog(timeoutMs, onFire) {
108
135
  clear();
109
136
  arm();
110
137
  },
138
+ petProgress() {
139
+ if (!progressEnabled || firedFlag)
140
+ return;
141
+ clearProgress();
142
+ armProgress();
143
+ },
111
144
  dispose() {
112
145
  clear();
146
+ clearProgress();
113
147
  },
114
148
  fired() {
115
149
  return firedFlag;
@@ -25,7 +25,7 @@
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
29
  import { stepCountIs, streamText } from "ai";
30
30
  import { recordArtifact } from "../ee/artifact-cache.js";
31
31
  import { getDefaultEEClient } from "../ee/intercept.js";
@@ -36,14 +36,14 @@ import { injectCheapModelWorkbook, shouldInjectCheapModelWorkbook, subagentTaskT
36
36
  import { getProviderCapabilities } from "../providers/capabilities.js";
37
37
  import { getVisionGuidanceForTextOnly } from "../providers/mcp-vision-bridge.js";
38
38
  import { captureToolSchemas } from "../providers/patch-zod-schema.js";
39
- import { buildTurnProviderOptions, requireRuntimeProvider, resolveModelRuntime, shouldDropParam, } from "../providers/runtime.js";
39
+ import { buildTurnProviderOptions, requireRuntimeProvider, resolveModelRuntime, resolveTemperatureParam, shouldDropParam, } from "../providers/runtime.js";
40
40
  import { needsVisionProxy } from "../providers/vision-proxy.js";
41
41
  import { wireDebug } from "../providers/wire-debug.js";
42
+ import { statusBarStore } from "../state/status-bar-store.js";
42
43
  import { BashTool } from "../tools/bash.js";
43
44
  import { createBuiltinTools } from "../tools/registry.js";
44
- import { statusBarStore } from "../ui/status-bar/store.js";
45
45
  import { openUrl } from "../utils/open-url.js";
46
- import { getCurrentShellSettings, getProviderStallTimeoutMs, getSubAgentBudgetChars, getSubAgentCompactKeepLast, getSubAgentCompactThresholdChars, loadMcpServers, loadValidSubAgents, } from "../utils/settings.js";
46
+ import { getCurrentShellSettings, getProviderProgressTimeoutMs, getProviderStallTimeoutMs, getSubAgentBudgetChars, getSubAgentCompactKeepLast, getSubAgentCompactThresholdChars, loadMcpServers, loadValidSubAgents, } from "../utils/settings.js";
47
47
  import { resolveShell } from "../utils/shell.js";
48
48
  import { prepareVerifySandbox } from "../verify/entrypoint.js";
49
49
  import { asNumber } from "./batch-utils.js";
@@ -58,7 +58,7 @@ import { attachReminderToMessages, buildScopeReminder, cadenceForSize, shouldInj
58
58
  import { recordCompaction, recordElision } from "./session-experience.js";
59
59
  import { createStallWatchdog, STALL_ERROR_MESSAGE } from "./stall-watchdog.js";
60
60
  import { wrapToolSetWithCap } from "./sub-agent-cap.js";
61
- import { compactSubAgentMessages, applyAnthropicPromptCaching } from "./subagent-compactor.js";
61
+ import { applyAnthropicPromptCaching, compactSubAgentMessages } from "./subagent-compactor.js";
62
62
  import { combineAbortSignals, firstLine, formatSubagentActivity } from "./tool-utils.js";
63
63
  /**
64
64
  * StreamRunner — extracted sub-agent stream lifecycle.
@@ -137,23 +137,36 @@ export class StreamRunner {
137
137
  });
138
138
  // Resolve child model early so we can pass modelId to createBuiltinTools
139
139
  // (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"));
140
+ const childModelId = normalizeModelId(request.modelId
141
+ ? request.modelId
142
+ : isVision
143
+ ? VISION_MODEL
144
+ : isComputer
145
+ ? COMPUTER_MODEL
146
+ : custom
147
+ ? custom.model
148
+ : this.deps.resolveModelForTask(isExplore ? "explore" : isVerify || isVerifyDetect || isVerifyManifest ? "verify" : "general"));
147
149
  // Mirror the file-local `createTools` wrapper from orchestrator.ts —
148
150
  // pass modelId so registry can inject analyze_image/ask_vision_proxy for
149
151
  // text-only child models (needsVisionProxy).
150
- const childBaseToolsRaw = createBuiltinTools(childBash, childMode, { modelId: childModelId });
152
+ const childBaseToolsRaw = createBuiltinTools(childBash, childMode, {
153
+ modelId: childModelId,
154
+ sessionId: this.deps.getSessionId(),
155
+ });
151
156
  // Wrap with the cumulative cap so the sub-agent's tool loop cannot
152
157
  // accumulate unbounded tool_result tokens. See sub-agent-cap.ts for the
153
158
  // tiered compression schedule. The cap is per-invocation; each sub-agent
154
159
  // gets a fresh budget.
155
160
  const subAgentCapBudget = getSubAgentBudgetChars();
156
- const subAgentCap = wrapToolSetWithCap(childBaseToolsRaw, { maxCumulativeChars: subAgentCapBudget });
161
+ const subAgentCap = wrapToolSetWithCap(childBaseToolsRaw, {
162
+ maxCumulativeChars: subAgentCapBudget,
163
+ // Opt-out toggle (default ON). The cap dedups identical tool outputs by
164
+ // content hash; set MUONROI_SUBAGENT_CAP_DEDUP=0 to disable. Used by the
165
+ // B3 compaction E2E, where the harness re-processes each tool result and
166
+ // the self-dedup would otherwise collapse distinct reads into pointer
167
+ // stubs before cumulative history can grow enough to trigger compaction.
168
+ dedupRepeatOutputs: process.env.MUONROI_SUBAGENT_CAP_DEDUP !== "0",
169
+ });
157
170
  // Phase C3: layer cross-turn dedup ON TOP of the per-invocation cap. The
158
171
  // cap sees raw output (for accurate cumulative accounting); dedup sees
159
172
  // the already-trimmed output that will actually reach the model.
@@ -255,7 +268,7 @@ export class StreamRunner {
255
268
  void signal;
256
269
  const childMessages = [{ role: "user", content: childPrompt }];
257
270
  // The main agent manages its sub-agents, so don't apply an arbitrary hard limit.
258
- const maxSteps = request.maxToolRounds ?? (this.deps.getMaxToolRounds() * 2);
271
+ const maxSteps = request.maxToolRounds ?? this.deps.getMaxToolRounds() * 2;
259
272
  // F1 parity — derive per-turn providerOptions so the sub-agent OpenAI calls
260
273
  // carry a stable session-derived promptCacheKey (every tool round routes to
261
274
  // the same cache node, keeping the unchanging prefix cached). The top-level
@@ -328,7 +341,6 @@ export class StreamRunner {
328
341
  // already strips it via `dropParam`, the sub-agent path was missing
329
342
  // the same guard, causing G1: "Task failed: No output generated.").
330
343
  const childDropMaxOutput = shouldDropParam(childRuntime, "maxOutputTokens");
331
- const childDropTemperature = shouldDropParam(childRuntime, "temperature");
332
344
  // Phase B3: compact older tool_results out of the running message history
333
345
  // before each AI SDK step. First step (stepNumber === 0) has no history
334
346
  // worth compacting; later steps are where cumulative input balloons.
@@ -346,8 +358,8 @@ export class StreamRunner {
346
358
  providerOptions: childProviderOptions,
347
359
  });
348
360
  }
349
- // SiliconFlow DeepSeek thinking-mode rejects assistant history with
350
- // `reasoning` parts (HTTP 400 code 20015). The siliconflow capability
361
+ // Some DeepSeek thinking-mode endpoints reject assistant history with
362
+ // `reasoning` parts (HTTP 400 code 20015). A provider capability
351
363
  // override strips them; every other provider's capability is identity.
352
364
  const subMessagesForCall = taskCaps.sanitizeHistory(childMessages);
353
365
  // Phase 4 Plan 04 (4B) — mirror top-level scope-ceiling integration.
@@ -377,8 +389,26 @@ export class StreamRunner {
377
389
  // and is re-armed by stall.pet() on every chunk. Cheap models (which run
378
390
  // mostly as sub-agents via SAMR) hit this most.
379
391
  let stallTriggered = false;
392
+ // Second timer (progressTimeoutMs) is the no-forward-progress guard: the
393
+ // stall timer re-arms on EVERY chunk — including a reasoning model's
394
+ // `reasoning-delta` chunks — so a sub-agent stuck in an endless
395
+ // chain-of-thought keeps petting it and it never fires (observed live
396
+ // 2026-07-10: a deepseek-v4-flash sprint sub-agent churned reasoning for
397
+ // 30+ min, 1.4M input tokens, ZERO text/tool output; the 2-min stall
398
+ // watchdog never tripped). The progress timer is reset ONLY by
399
+ // stall.petProgress() on real output (text-delta / tool-call), so a runaway
400
+ // reasoning loop is aborted while a legitimately long reasoning burst that
401
+ // DOES eventually emit output survives. Both timers abort the same signal
402
+ // and set stallTriggered, so the existing surface-and-return path handles it.
380
403
  const stall = createStallWatchdog(getProviderStallTimeoutMs(), () => {
381
404
  stallTriggered = true;
405
+ }, {
406
+ progressTimeoutMs: getProviderProgressTimeoutMs(),
407
+ onProgressFire: () => {
408
+ stallTriggered = true;
409
+ console.error(`[stream-runner] sub-agent aborted: no text/tool output for ${getProviderProgressTimeoutMs()}ms ` +
410
+ `(runaway reasoning / no forward progress) model=${childRuntime.modelId}`);
411
+ },
382
412
  });
383
413
  const result = streamText({
384
414
  model: childRuntime.model,
@@ -396,11 +426,11 @@ export class StreamRunner {
396
426
  prepareStep: ({ messages, stepNumber }) => {
397
427
  if (stepNumber < 1)
398
428
  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).
429
+ // Internal multi-step loop: AI-SDK accumulates streamed reasoning
430
+ // parts into in-flight assistant history and re-POSTs them on the
431
+ // next step within the same streamText call — orchestrator-level
432
+ // strip at call setup never sees this. Strip per step too via the
433
+ // capability hook (identity for providers without the quirk).
404
434
  const stripped = taskCaps.sanitizeHistory(messages);
405
435
  // G1 + G2 — pass the sub-agent's model context window so the
406
436
  // compactor can use a token-aware threshold and shrink the keep
@@ -428,17 +458,16 @@ export class StreamRunner {
428
458
  break;
429
459
  }
430
460
  }
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) => {
461
+ const persistSubArtifact = (toolCallId, toolName, fullContent, reason, summary) => {
433
462
  // Local-first durable cache so ee_query rehydrates even when EE is down.
434
463
  recordArtifact(toolCallId, toolName, fullContent);
435
- recordElision(toolCallId, toolName, fullContent.length, stepNumber);
464
+ recordElision(toolCallId, toolName, fullContent.length, stepNumber, summary);
436
465
  try {
437
466
  getDefaultEEClient()
438
467
  .extract({
439
468
  transcript: fullContent.slice(0, 4000),
440
469
  projectPath: process.cwd(),
441
- meta: { source: "tool-artifact", toolCallId, toolName, reason },
470
+ meta: { source: "tool-artifact", toolCallId, toolName, reason, summary },
442
471
  }, AbortSignal.timeout(600))
443
472
  .catch(() => { });
444
473
  }
@@ -494,7 +523,7 @@ export class StreamRunner {
494
523
  return undefined;
495
524
  return { messages: finalMessages };
496
525
  },
497
- ...(childDropTemperature ? {} : { temperature: isExplore ? 0.2 : 0.5 }),
526
+ ...resolveTemperatureParam(childRuntime, isExplore ? 0.2 : 0.5),
498
527
  ...(childDropMaxOutput ? {} : { maxOutputTokens: Math.min(this.deps.getMaxTokens(), 8_192) }),
499
528
  ...(childProviderOptions ? { providerOptions: childProviderOptions } : {}),
500
529
  onFinish: ({ totalUsage, finishReason }) => {
@@ -557,6 +586,7 @@ export class StreamRunner {
557
586
  });
558
587
  }
559
588
  if (part.type === "text-delta") {
589
+ stall.petProgress(); // real forward progress — reset the no-progress guard
560
590
  textDeltaCount++;
561
591
  assistantText += part.text;
562
592
  // Task 2.6b — emit llm-token (agent-mode only; high-volume, default-off per Phase 4).
@@ -576,6 +606,7 @@ export class StreamRunner {
576
606
  continue;
577
607
  }
578
608
  if (part.type === "tool-call") {
609
+ stall.petProgress(); // real forward progress — reset the no-progress guard
579
610
  toolCallCount++;
580
611
  lastActivity = formatSubagentActivity(part.toolName, part.input);
581
612
  onActivity?.(lastActivity);
@@ -653,7 +684,9 @@ export class StreamRunner {
653
684
  ...priorMessages,
654
685
  {
655
686
  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.",
687
+ 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" +
688
+ "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" +
689
+ "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
690
  },
658
691
  ],
659
692
  tools: {},
@@ -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