@wrongstack/cli 0.295.1 → 0.296.3

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 (398) hide show
  1. package/data/providers.json +0 -24
  2. package/dist/acp-A5OSIU2P.js +824 -0
  3. package/dist/acp-A5OSIU2P.js.map +7 -0
  4. package/dist/acp-server-agent.d.ts.map +1 -1
  5. package/dist/arg-parser.d.ts.map +1 -1
  6. package/dist/audit-2ZSFGNIF.js +102 -0
  7. package/dist/audit-2ZSFGNIF.js.map +7 -0
  8. package/dist/auth-CJWQJVRH.js +504 -0
  9. package/dist/auth-CJWQJVRH.js.map +7 -0
  10. package/dist/auth-menu/auth-menu-audit.d.ts +0 -4
  11. package/dist/auth-menu/auth-menu-audit.d.ts.map +1 -1
  12. package/dist/bench-7ZDWSNEF.js +278 -0
  13. package/dist/bench-7ZDWSNEF.js.map +7 -0
  14. package/dist/boot/brain-decision-log.d.ts +7 -5
  15. package/dist/boot/brain-decision-log.d.ts.map +1 -1
  16. package/dist/boot/dispatch-webui.d.ts +5 -8
  17. package/dist/boot/dispatch-webui.d.ts.map +1 -1
  18. package/dist/boot/system-prompt-builder.d.ts +3 -0
  19. package/dist/boot/system-prompt-builder.d.ts.map +1 -1
  20. package/dist/boot/tui-coordinator-callbacks.d.ts +45 -0
  21. package/dist/boot/tui-coordinator-callbacks.d.ts.map +1 -0
  22. package/dist/boot/tui-project-switch.d.ts +2 -3
  23. package/dist/boot/tui-project-switch.d.ts.map +1 -1
  24. package/dist/boot/tui-runtime-state.d.ts +5 -6
  25. package/dist/boot/tui-runtime-state.d.ts.map +1 -1
  26. package/dist/boot/tui-session-resume.d.ts.map +1 -1
  27. package/dist/boot/tui-settings-adapter.d.ts +2 -3
  28. package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
  29. package/dist/boot.d.ts.map +1 -1
  30. package/dist/brain-menu/panel-service.d.ts.map +1 -1
  31. package/dist/chimera-review-task.d.ts +6 -0
  32. package/dist/chimera-review-task.d.ts.map +1 -0
  33. package/dist/chimera-reviewer-policy.d.ts +34 -0
  34. package/dist/chimera-reviewer-policy.d.ts.map +1 -0
  35. package/dist/chronicle-QQEQFSSE.js +256 -0
  36. package/dist/chronicle-QQEQFSSE.js.map +7 -0
  37. package/dist/chunk-5N34EWJR.js +418 -0
  38. package/dist/chunk-5N34EWJR.js.map +7 -0
  39. package/dist/chunk-66T43Q4Y.js +90 -0
  40. package/dist/chunk-66T43Q4Y.js.map +7 -0
  41. package/dist/chunk-7OCVIDC7.js +11 -0
  42. package/dist/chunk-7OCVIDC7.js.map +7 -0
  43. package/dist/chunk-ABXK4VVY.js +84 -0
  44. package/dist/chunk-ABXK4VVY.js.map +7 -0
  45. package/dist/chunk-AXVZMEWG.js +306 -0
  46. package/dist/chunk-AXVZMEWG.js.map +7 -0
  47. package/dist/chunk-AZESNNIC.js +216 -0
  48. package/dist/chunk-AZESNNIC.js.map +7 -0
  49. package/dist/chunk-BGFM3PD7.js +41 -0
  50. package/dist/chunk-BGFM3PD7.js.map +7 -0
  51. package/dist/chunk-BQCAJ7FI.js +425 -0
  52. package/dist/chunk-BQCAJ7FI.js.map +7 -0
  53. package/dist/chunk-DPJZQCVP.js +13 -0
  54. package/dist/chunk-DPJZQCVP.js.map +7 -0
  55. package/dist/chunk-F4PEGAJZ.js +176 -0
  56. package/dist/chunk-F4PEGAJZ.js.map +7 -0
  57. package/dist/chunk-IHBYBYGA.js +443 -0
  58. package/dist/chunk-IHBYBYGA.js.map +7 -0
  59. package/dist/chunk-JBSQ7ONN.js +1023 -0
  60. package/dist/chunk-JBSQ7ONN.js.map +7 -0
  61. package/dist/chunk-JBTFWWKV.js +1325 -0
  62. package/dist/chunk-JBTFWWKV.js.map +7 -0
  63. package/dist/chunk-JUHRI6OV.js +100 -0
  64. package/dist/chunk-JUHRI6OV.js.map +7 -0
  65. package/dist/chunk-OI7GWJDS.js +3303 -0
  66. package/dist/chunk-OI7GWJDS.js.map +7 -0
  67. package/dist/chunk-OP47B3IX.js +142 -0
  68. package/dist/chunk-OP47B3IX.js.map +7 -0
  69. package/dist/chunk-PJHET45A.js +158 -0
  70. package/dist/chunk-PJHET45A.js.map +7 -0
  71. package/dist/chunk-Q5GTM25S.js +17 -0
  72. package/dist/chunk-Q5GTM25S.js.map +7 -0
  73. package/dist/chunk-QD544J2B.js +19 -0
  74. package/dist/chunk-QD544J2B.js.map +7 -0
  75. package/dist/chunk-QUNHS6O3.js +258 -0
  76. package/dist/chunk-QUNHS6O3.js.map +7 -0
  77. package/dist/chunk-TADVX644.js +480 -0
  78. package/dist/chunk-TADVX644.js.map +7 -0
  79. package/dist/chunk-TGDHN4LM.js +81 -0
  80. package/dist/chunk-TGDHN4LM.js.map +7 -0
  81. package/dist/chunk-TYO2OVAD.js +50 -0
  82. package/dist/chunk-TYO2OVAD.js.map +7 -0
  83. package/dist/chunk-VCYZ3WZV.js +14 -0
  84. package/dist/chunk-VCYZ3WZV.js.map +7 -0
  85. package/dist/chunk-VJUEZZYB.js +29 -0
  86. package/dist/chunk-VJUEZZYB.js.map +7 -0
  87. package/dist/chunk-WHOIR577.js +1 -0
  88. package/dist/chunk-WHOIR577.js.map +7 -0
  89. package/dist/chunk-WY7KKOUK.js +460 -0
  90. package/dist/chunk-WY7KKOUK.js.map +7 -0
  91. package/dist/chunk-XJXDOF63.js +28 -0
  92. package/dist/chunk-XJXDOF63.js.map +7 -0
  93. package/dist/chunk-XWEGWYDU.js +560 -0
  94. package/dist/chunk-XWEGWYDU.js.map +7 -0
  95. package/dist/chunk-YWUPHRGB.js +346 -0
  96. package/dist/chunk-YWUPHRGB.js.map +7 -0
  97. package/dist/chunk-Z7YGASQP.js +313 -0
  98. package/dist/chunk-Z7YGASQP.js.map +7 -0
  99. package/dist/cli-entry-main.d.ts +2 -0
  100. package/dist/cli-entry-main.d.ts.map +1 -0
  101. package/dist/cli-entry-point.d.ts +3 -3
  102. package/dist/cli-entry-point.d.ts.map +1 -1
  103. package/dist/cli-main-RTY35OPD.js +29268 -0
  104. package/dist/cli-main-RTY35OPD.js.map +7 -0
  105. package/dist/cli-main-helpers.d.ts +21 -0
  106. package/dist/cli-main-helpers.d.ts.map +1 -0
  107. package/dist/cli-main.d.ts +25 -1
  108. package/dist/cli-main.d.ts.map +1 -1
  109. package/dist/context-limit.d.ts +48 -0
  110. package/dist/context-limit.d.ts.map +1 -1
  111. package/dist/diag-doctor-OQ3AA63O.js +170 -0
  112. package/dist/diag-doctor-OQ3AA63O.js.map +7 -0
  113. package/dist/execute-deps.d.ts +11 -12
  114. package/dist/execute-deps.d.ts.map +1 -1
  115. package/dist/execution-O7I5DYE6.js +4682 -0
  116. package/dist/execution-O7I5DYE6.js.map +7 -0
  117. package/dist/execution-chimera-ask.d.ts +10 -0
  118. package/dist/execution-chimera-ask.d.ts.map +1 -0
  119. package/dist/execution-chimera-cascade.d.ts +15 -0
  120. package/dist/execution-chimera-cascade.d.ts.map +1 -0
  121. package/dist/execution-cleanup.d.ts +23 -0
  122. package/dist/execution-cleanup.d.ts.map +1 -0
  123. package/dist/execution-kanban-dispatch.d.ts +10 -0
  124. package/dist/execution-kanban-dispatch.d.ts.map +1 -0
  125. package/dist/execution-repl-fleet-callbacks.d.ts +11 -0
  126. package/dist/execution-repl-fleet-callbacks.d.ts.map +1 -0
  127. package/dist/execution-storage-observability.d.ts +3 -0
  128. package/dist/execution-storage-observability.d.ts.map +1 -0
  129. package/dist/execution-tui-next-step-callbacks.d.ts +20 -0
  130. package/dist/execution-tui-next-step-callbacks.d.ts.map +1 -0
  131. package/dist/execution.d.ts +2 -139
  132. package/dist/execution.d.ts.map +1 -1
  133. package/dist/export-TPORYVRZ.js +64 -0
  134. package/dist/export-TPORYVRZ.js.map +7 -0
  135. package/dist/fleet/host-acp-runner-cache.d.ts +9 -0
  136. package/dist/fleet/host-acp-runner-cache.d.ts.map +1 -0
  137. package/dist/fleet/host-acp.d.ts +3 -0
  138. package/dist/fleet/host-acp.d.ts.map +1 -0
  139. package/dist/fleet/host-concurrency.d.ts +2 -0
  140. package/dist/fleet/host-concurrency.d.ts.map +1 -0
  141. package/dist/fleet/host-context.d.ts +5 -0
  142. package/dist/fleet/host-context.d.ts.map +1 -0
  143. package/dist/fleet/host-director-builder.d.ts +24 -0
  144. package/dist/fleet/host-director-builder.d.ts.map +1 -0
  145. package/dist/fleet/host-director-event-bridges.d.ts +37 -0
  146. package/dist/fleet/host-director-event-bridges.d.ts.map +1 -0
  147. package/dist/fleet/host-director-services.d.ts +20 -0
  148. package/dist/fleet/host-director-services.d.ts.map +1 -0
  149. package/dist/fleet/host-event-bridge.d.ts +17 -0
  150. package/dist/fleet/host-event-bridge.d.ts.map +1 -0
  151. package/dist/fleet/host-helpers.d.ts +22 -0
  152. package/dist/fleet/host-helpers.d.ts.map +1 -0
  153. package/dist/fleet/host-learning-tracker.d.ts +11 -0
  154. package/dist/fleet/host-learning-tracker.d.ts.map +1 -0
  155. package/dist/fleet/host-learning.d.ts +4 -0
  156. package/dist/fleet/host-learning.d.ts.map +1 -0
  157. package/dist/fleet/host-lifecycle-events.d.ts +4 -0
  158. package/dist/fleet/host-lifecycle-events.d.ts.map +1 -0
  159. package/dist/fleet/host-paths.d.ts +3 -0
  160. package/dist/fleet/host-paths.d.ts.map +1 -0
  161. package/dist/fleet/host-provider.d.ts +22 -0
  162. package/dist/fleet/host-provider.d.ts.map +1 -0
  163. package/dist/fleet/host-session-writer.d.ts +3 -0
  164. package/dist/fleet/host-session-writer.d.ts.map +1 -0
  165. package/dist/fleet/host-shadow-pass.d.ts +21 -0
  166. package/dist/fleet/host-shadow-pass.d.ts.map +1 -0
  167. package/dist/fleet/host-spawn-types.d.ts +25 -0
  168. package/dist/fleet/host-spawn-types.d.ts.map +1 -0
  169. package/dist/fleet/host-status.d.ts +48 -0
  170. package/dist/fleet/host-status.d.ts.map +1 -0
  171. package/dist/fleet/host-subagent-factory.d.ts +16 -0
  172. package/dist/fleet/host-subagent-factory.d.ts.map +1 -0
  173. package/dist/fleet/host-supervisor.d.ts +16 -0
  174. package/dist/fleet/host-supervisor.d.ts.map +1 -0
  175. package/dist/fleet/host-task-result-report.d.ts +11 -0
  176. package/dist/fleet/host-task-result-report.d.ts.map +1 -0
  177. package/dist/fleet/host-types.d.ts +89 -0
  178. package/dist/fleet/host-types.d.ts.map +1 -0
  179. package/dist/fleet/host.d.ts +10 -357
  180. package/dist/fleet/host.d.ts.map +1 -1
  181. package/dist/hq-JUYKUNJX.js +547 -0
  182. package/dist/hq-JUYKUNJX.js.map +7 -0
  183. package/dist/hq-command-controller.d.ts +2 -2
  184. package/dist/hq-command-controller.d.ts.map +1 -1
  185. package/dist/hq-server/auth.d.ts +15 -4
  186. package/dist/hq-server/auth.d.ts.map +1 -1
  187. package/dist/hq-server/project-root.d.ts +5 -0
  188. package/dist/hq-server/project-root.d.ts.map +1 -0
  189. package/dist/hq-server/routes/auth-handlers.d.ts +25 -0
  190. package/dist/hq-server/routes/auth-handlers.d.ts.map +1 -0
  191. package/dist/hq-server/routes/session-handlers.d.ts +8 -0
  192. package/dist/hq-server/routes/session-handlers.d.ts.map +1 -0
  193. package/dist/hq-server/routes.d.ts +8 -6
  194. package/dist/hq-server/routes.d.ts.map +1 -1
  195. package/dist/hq-server/snapshot.d.ts +44 -0
  196. package/dist/hq-server/snapshot.d.ts.map +1 -1
  197. package/dist/hq-server/types.d.ts +22 -4
  198. package/dist/hq-server/types.d.ts.map +1 -1
  199. package/dist/hq-server/utils.d.ts +8 -0
  200. package/dist/hq-server/utils.d.ts.map +1 -1
  201. package/dist/hq-server/ws.d.ts +1 -1
  202. package/dist/hq-server/ws.d.ts.map +1 -1
  203. package/dist/hq-server-6AQ3BXZA.js +39 -0
  204. package/dist/hq-server-6AQ3BXZA.js.map +7 -0
  205. package/dist/hq-server.d.ts +3 -3
  206. package/dist/hq-server.d.ts.map +1 -1
  207. package/dist/hq-ttl-U2FAYFAT.js +8 -0
  208. package/dist/hq-ttl-U2FAYFAT.js.map +7 -0
  209. package/dist/hq-tunnel-RFKGC4HI.js +114 -0
  210. package/dist/hq-tunnel-RFKGC4HI.js.map +7 -0
  211. package/dist/hq-tunnel.d.ts +1 -1
  212. package/dist/hq-tunnel.d.ts.map +1 -1
  213. package/dist/index.d.ts +2 -1
  214. package/dist/index.d.ts.map +1 -1
  215. package/dist/index.js +2503 -53026
  216. package/dist/index.js.map +4 -4
  217. package/dist/init-E2NDDOHI.js +27 -0
  218. package/dist/init-E2NDDOHI.js.map +7 -0
  219. package/dist/kanban-dispatch-route.d.ts +14 -0
  220. package/dist/kanban-dispatch-route.d.ts.map +1 -0
  221. package/dist/kanban-hq-sync.d.ts +2 -0
  222. package/dist/kanban-hq-sync.d.ts.map +1 -1
  223. package/dist/kanban-run-mirror-EKVNGXWW.js +12 -0
  224. package/dist/kanban-run-mirror-EKVNGXWW.js.map +7 -0
  225. package/dist/live-settings-input.d.ts +69 -0
  226. package/dist/live-settings-input.d.ts.map +1 -0
  227. package/dist/mailbox-serve-2UWHEN3M.js +302 -0
  228. package/dist/mailbox-serve-2UWHEN3M.js.map +7 -0
  229. package/dist/mcp-CYLPUEHC.js +413 -0
  230. package/dist/mcp-CYLPUEHC.js.map +7 -0
  231. package/dist/modeldiag-HENYJ4N7.js +1290 -0
  232. package/dist/modeldiag-HENYJ4N7.js.map +7 -0
  233. package/dist/permissions-FNYLZIL4.js +115 -0
  234. package/dist/permissions-FNYLZIL4.js.map +7 -0
  235. package/dist/picker-key-state.d.ts +7 -0
  236. package/dist/picker-key-state.d.ts.map +1 -0
  237. package/dist/picker-model-picker.d.ts +6 -0
  238. package/dist/picker-model-picker.d.ts.map +1 -0
  239. package/dist/picker-ui.d.ts +17 -0
  240. package/dist/picker-ui.d.ts.map +1 -0
  241. package/dist/picker.d.ts +4 -37
  242. package/dist/picker.d.ts.map +1 -1
  243. package/dist/plugin-usage-OU6X6LM3.js +40 -0
  244. package/dist/plugin-usage-OU6X6LM3.js.map +7 -0
  245. package/dist/plugins-4LJ5IUBI.js +24 -0
  246. package/dist/plugins-4LJ5IUBI.js.map +7 -0
  247. package/dist/pre-launch.d.ts +0 -1
  248. package/dist/pre-launch.d.ts.map +1 -1
  249. package/dist/project-BLQ3ALVL.js +76 -0
  250. package/dist/project-BLQ3ALVL.js.map +7 -0
  251. package/dist/project-manifest-GE25PW4S.js +18 -0
  252. package/dist/project-manifest-GE25PW4S.js.map +7 -0
  253. package/dist/project-picker-BOCWHBXP.js +17 -0
  254. package/dist/project-picker-BOCWHBXP.js.map +7 -0
  255. package/dist/projects-XE76NFNZ.js +38 -0
  256. package/dist/projects-XE76NFNZ.js.map +7 -0
  257. package/dist/provider-helpers.d.ts +22 -6
  258. package/dist/provider-helpers.d.ts.map +1 -1
  259. package/dist/providers-models-GDELBHZK.js +589 -0
  260. package/dist/providers-models-GDELBHZK.js.map +7 -0
  261. package/dist/quick-VIO2BEHS.js +10 -0
  262. package/dist/quick-VIO2BEHS.js.map +7 -0
  263. package/dist/repl-auto-proceed.d.ts +19 -0
  264. package/dist/repl-auto-proceed.d.ts.map +1 -0
  265. package/dist/repl-client-registration.d.ts +7 -0
  266. package/dist/repl-client-registration.d.ts.map +1 -0
  267. package/dist/repl-input.d.ts +11 -0
  268. package/dist/repl-input.d.ts.map +1 -0
  269. package/dist/repl-options.d.ts +54 -0
  270. package/dist/repl-options.d.ts.map +1 -0
  271. package/dist/repl-rendering.d.ts +4 -0
  272. package/dist/repl-rendering.d.ts.map +1 -0
  273. package/dist/repl-suggestions.d.ts +21 -0
  274. package/dist/repl-suggestions.d.ts.map +1 -0
  275. package/dist/repl.d.ts +3 -162
  276. package/dist/repl.d.ts.map +1 -1
  277. package/dist/replay-KCTXNMZQ.js +75 -0
  278. package/dist/replay-KCTXNMZQ.js.map +7 -0
  279. package/dist/rewind-EJMVFVLS.js +185 -0
  280. package/dist/rewind-EJMVFVLS.js.map +7 -0
  281. package/dist/sdd-runtime-JG4EEKL6.js +54 -0
  282. package/dist/sdd-runtime-JG4EEKL6.js.map +7 -0
  283. package/dist/services/sdd/project-context.d.ts +1 -1
  284. package/dist/services/sdd/project-context.d.ts.map +1 -1
  285. package/dist/services/sdd/task-manager.d.ts.map +1 -1
  286. package/dist/services/statusline-config.d.ts +1 -1
  287. package/dist/services/statusline-config.d.ts.map +1 -1
  288. package/dist/session-event-wiring.d.ts +2 -2
  289. package/dist/session-event-wiring.d.ts.map +1 -1
  290. package/dist/sessions-config-NLCMK3BY.js +409 -0
  291. package/dist/sessions-config-NLCMK3BY.js.map +7 -0
  292. package/dist/settings-menu.d.ts +3 -18
  293. package/dist/settings-menu.d.ts.map +1 -1
  294. package/dist/simpleui-dist-JIFGUJO7.js +93 -0
  295. package/dist/simpleui-dist-JIFGUJO7.js.map +7 -0
  296. package/dist/slash-commands/brain.d.ts.map +1 -1
  297. package/dist/slash-commands/clear.d.ts.map +1 -1
  298. package/dist/slash-commands/fallback.d.ts.map +1 -1
  299. package/dist/slash-commands/kanban-agent-helpers.d.ts +18 -0
  300. package/dist/slash-commands/kanban-agent-helpers.d.ts.map +1 -0
  301. package/dist/slash-commands/kanban-format.d.ts +16 -0
  302. package/dist/slash-commands/kanban-format.d.ts.map +1 -0
  303. package/dist/slash-commands/kanban-help.d.ts +2 -0
  304. package/dist/slash-commands/kanban-help.d.ts.map +1 -0
  305. package/dist/slash-commands/kanban.d.ts.map +1 -1
  306. package/dist/slash-commands/mailbox-demo.d.ts.map +1 -1
  307. package/dist/slash-commands/mailbox.d.ts +2 -1
  308. package/dist/slash-commands/mailbox.d.ts.map +1 -1
  309. package/dist/slash-commands/memory-compact.d.ts +5 -0
  310. package/dist/slash-commands/memory-compact.d.ts.map +1 -0
  311. package/dist/slash-commands/memory-flags.d.ts +36 -0
  312. package/dist/slash-commands/memory-flags.d.ts.map +1 -0
  313. package/dist/slash-commands/memory-formatters.d.ts +16 -0
  314. package/dist/slash-commands/memory-formatters.d.ts.map +1 -0
  315. package/dist/slash-commands/memory.d.ts.map +1 -1
  316. package/dist/slash-commands/mouse.d.ts +2 -2
  317. package/dist/slash-commands/review.d.ts.map +1 -1
  318. package/dist/slash-commands/sdd-command-helpers.d.ts +23 -0
  319. package/dist/slash-commands/sdd-command-helpers.d.ts.map +1 -0
  320. package/dist/slash-commands/sdd-format.d.ts +32 -0
  321. package/dist/slash-commands/sdd-format.d.ts.map +1 -0
  322. package/dist/slash-commands/sdd.d.ts.map +1 -1
  323. package/dist/slash-commands/settings.d.ts.map +1 -1
  324. package/dist/subcommands/handlers/chronicle.d.ts.map +1 -1
  325. package/dist/subcommands/handlers/mailbox-serve.d.ts.map +1 -1
  326. package/dist/subcommands/handlers/model-smoke-test.d.ts +51 -0
  327. package/dist/subcommands/handlers/model-smoke-test.d.ts.map +1 -0
  328. package/dist/subcommands/handlers/modeldiag-profiles.d.ts +64 -0
  329. package/dist/subcommands/handlers/modeldiag-profiles.d.ts.map +1 -0
  330. package/dist/subcommands/handlers/modeldiag.d.ts.map +1 -1
  331. package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts +3 -0
  332. package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts.map +1 -0
  333. package/dist/subcommands/handlers/per-subcommand-help-data.d.ts +3 -0
  334. package/dist/subcommands/handlers/per-subcommand-help-data.d.ts.map +1 -0
  335. package/dist/subcommands/handlers/per-subcommand-help-table.d.ts +36 -0
  336. package/dist/subcommands/handlers/per-subcommand-help-table.d.ts.map +1 -0
  337. package/dist/subcommands/handlers/per-subcommand-help-types.d.ts +18 -0
  338. package/dist/subcommands/handlers/per-subcommand-help-types.d.ts.map +1 -0
  339. package/dist/subcommands/handlers/per-subcommand-help.d.ts +2 -80
  340. package/dist/subcommands/handlers/per-subcommand-help.d.ts.map +1 -1
  341. package/dist/subcommands/handlers/rewind.d.ts.map +1 -1
  342. package/dist/subcommands/index.d.ts +7 -0
  343. package/dist/subcommands/index.d.ts.map +1 -1
  344. package/dist/tools-skills-UNKLOB7M.js +29 -0
  345. package/dist/tools-skills-UNKLOB7M.js.map +7 -0
  346. package/dist/update-YC4TYW2Y.js +16 -0
  347. package/dist/update-YC4TYW2Y.js.map +7 -0
  348. package/dist/update-check-UMYTOH7Z.js +18 -0
  349. package/dist/update-check-UMYTOH7Z.js.map +7 -0
  350. package/dist/version-help-DROHZRW6.js +11 -0
  351. package/dist/version-help-DROHZRW6.js.map +7 -0
  352. package/dist/webui-server/connection-handler.d.ts.map +1 -1
  353. package/dist/webui-server/kanban-host-adapter.d.ts +5 -1
  354. package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
  355. package/dist/webui-server/listen-helpers.d.ts +18 -0
  356. package/dist/webui-server/listen-helpers.d.ts.map +1 -0
  357. package/dist/webui-server/node-pty-loader.d.ts +6 -0
  358. package/dist/webui-server/node-pty-loader.d.ts.map +1 -0
  359. package/dist/webui-server-ZF5KVGC5.js +1460 -0
  360. package/dist/webui-server-ZF5KVGC5.js.map +7 -0
  361. package/dist/webui-server-options.d.ts +191 -0
  362. package/dist/webui-server-options.d.ts.map +1 -0
  363. package/dist/webui-server.d.ts +2 -178
  364. package/dist/webui-server.d.ts.map +1 -1
  365. package/dist/wiring/brain-and-orchestration.d.ts.map +1 -1
  366. package/dist/wiring/codebase-index.d.ts.map +1 -1
  367. package/dist/wiring/command-host-state.d.ts +4 -4
  368. package/dist/wiring/dep-watcher-bridge.d.ts +23 -0
  369. package/dist/wiring/dep-watcher-bridge.d.ts.map +1 -0
  370. package/dist/wiring/dep-watcher.d.ts.map +1 -1
  371. package/dist/wiring/hq-telemetry.d.ts +2 -2
  372. package/dist/wiring/lifecycle-plugins.d.ts +4 -3
  373. package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
  374. package/dist/wiring/mailbox-bridge-bootstrap.d.ts +1 -1
  375. package/dist/wiring/management-tools.d.ts +15 -0
  376. package/dist/wiring/management-tools.d.ts.map +1 -0
  377. package/dist/wiring/pipeline.d.ts +2 -1
  378. package/dist/wiring/pipeline.d.ts.map +1 -1
  379. package/dist/wiring/plugins.d.ts +1 -1
  380. package/dist/wiring/plugins.d.ts.map +1 -1
  381. package/dist/wiring/provider-runtime-setup.d.ts +14 -1
  382. package/dist/wiring/provider-runtime-setup.d.ts.map +1 -1
  383. package/dist/wiring/provider.d.ts.map +1 -1
  384. package/dist/wiring/runtime-controller-deps.d.ts +1 -1
  385. package/dist/wiring/runtime-controller-deps.d.ts.map +1 -1
  386. package/dist/wiring/sage.d.ts.map +1 -1
  387. package/dist/wiring/session-registry.d.ts +38 -0
  388. package/dist/wiring/session-registry.d.ts.map +1 -0
  389. package/dist/wiring/session.d.ts +10 -6
  390. package/dist/wiring/session.d.ts.map +1 -1
  391. package/dist/wiring/tools.d.ts.map +1 -1
  392. package/package.json +22 -22
  393. package/dist/cli-recovery-prompt.d.ts +0 -19
  394. package/dist/cli-recovery-prompt.d.ts.map +0 -1
  395. package/dist/first-run-auth-gate.d.ts +0 -20
  396. package/dist/first-run-auth-gate.d.ts.map +0 -1
  397. package/dist/pre-launch/indexing-question.d.ts +0 -28
  398. package/dist/pre-launch/indexing-question.d.ts.map +0 -1
@@ -0,0 +1,4682 @@
1
+ import {
2
+ deriveFsAccessPair,
3
+ filterSafeForProject,
4
+ getSuggestions,
5
+ normalizeTuiThinkingWord,
6
+ resolveActualTarget,
7
+ resolvePersistPath,
8
+ setAutoSuggestions,
9
+ setSuggestions
10
+ } from "./chunk-Z7YGASQP.js";
11
+ import {
12
+ startCliHqConnection
13
+ } from "./chunk-WY7KKOUK.js";
14
+ import "./chunk-WHOIR577.js";
15
+ import {
16
+ advanceToNextTask,
17
+ autoDetectTaskCompletion,
18
+ getActiveSDDContext,
19
+ getActiveSDDPhase,
20
+ getCurrentExecutingContext,
21
+ getTaskListText,
22
+ getTaskProgress,
23
+ renderTaskListWithProgress,
24
+ trySaveImplementationPlan,
25
+ trySaveSpecFromAIOutput,
26
+ trySaveTasksFromAIOutput
27
+ } from "./chunk-BQCAJ7FI.js";
28
+ import {
29
+ theme
30
+ } from "./chunk-QD544J2B.js";
31
+ import {
32
+ fmtTok
33
+ } from "./chunk-TYO2OVAD.js";
34
+ import {
35
+ CLI_VERSION
36
+ } from "./chunk-XJXDOF63.js";
37
+ import {
38
+ resolveActiveApiKey
39
+ } from "./chunk-PJHET45A.js";
40
+ import "./chunk-7OCVIDC7.js";
41
+
42
+ // src/execution.ts
43
+ import { randomUUID as randomUUID4 } from "node:crypto";
44
+ import * as path10 from "node:path";
45
+ import { setQueuedMessagesSnapshot as setQueuedMessagesSnapshot2 } from "@wrongstack/core/agent";
46
+ import { isMailboxLeader } from "@wrongstack/core/coordination";
47
+ import {
48
+ CHIMERA_REVIEW_PROMPT
49
+ } from "@wrongstack/core/plugin";
50
+ import { attachTodosCheckpoint as attachTodosCheckpoint2 } from "@wrongstack/core/storage";
51
+ import {
52
+ normalizeTokenSavingTier as normalizeTokenSavingTier2
53
+ } from "@wrongstack/core/types";
54
+ import { mergeCustomModelDefs } from "@wrongstack/core/utils";
55
+ import { capabilitiesFor } from "@wrongstack/providers";
56
+ import { createToolVisionAdapters } from "@wrongstack/runtime/vision";
57
+
58
+ // src/boot/dispatch-singleshot.ts
59
+ import { color, writeOut } from "@wrongstack/core/utils";
60
+
61
+ // src/context-overflow-diagnostic.ts
62
+ import { ERROR_CODES } from "@wrongstack/core/types";
63
+ var CONTEXT_OVERFLOW_RE = /context window|exceeds the context|too many tokens|context.*tokens/i;
64
+ function contextOverflowHint(err) {
65
+ const structured = err.code === ERROR_CODES.PROVIDER_CONTEXT_OVERFLOW || err.code === ERROR_CODES.AGENT_CONTEXT_OVERFLOW;
66
+ const textual = CONTEXT_OVERFLOW_RE.test(`${err.message}
67
+ ${err.describe()}`);
68
+ if (!structured && !textual) return null;
69
+ return [
70
+ "Provider rejected the request as over its effective context window.",
71
+ "If you use a custom baseUrl/proxy, the real limit may be lower than models.dev reports.",
72
+ "Try: /context limit 220k",
73
+ "Then, if needed: /context thresholds 50% 70% 85%",
74
+ "Persistent config: set context.effectiveMaxContext."
75
+ ].join("\n");
76
+ }
77
+
78
+ // src/boot/dispatch-singleshot.ts
79
+ async function runSingleShotDispatch(ctx) {
80
+ const { agent, query, flags, tokenCounter, renderer } = ctx;
81
+ const ctrl = new AbortController();
82
+ const onSigint = () => ctrl.abort();
83
+ process.on("SIGINT", onSigint);
84
+ const startedAt = Date.now();
85
+ const before = tokenCounter.total();
86
+ const costBefore = tokenCounter.estimateCost().total;
87
+ let result;
88
+ try {
89
+ result = await agent.run(query, { signal: ctrl.signal });
90
+ } finally {
91
+ process.off("SIGINT", onSigint);
92
+ const { getProcessRegistry: getProcessRegistry2 } = await import("@wrongstack/tools");
93
+ getProcessRegistry2().killAll({ preserveBackground: true });
94
+ }
95
+ const after = tokenCounter.total();
96
+ const costAfter = tokenCounter.estimateCost().total;
97
+ const usage = {
98
+ input: after.input - before.input,
99
+ output: after.output - before.output,
100
+ iterations: result.iterations,
101
+ cost: costAfter - costBefore,
102
+ elapsedMs: Date.now() - startedAt
103
+ };
104
+ if (flags["output-json"]) {
105
+ const json = JSON.stringify({
106
+ status: result.status,
107
+ finalText: result.finalText ?? null,
108
+ error: result.error ? {
109
+ code: result.error.code,
110
+ subsystem: result.error.subsystem,
111
+ severity: result.error.severity,
112
+ recoverable: result.error.recoverable,
113
+ message: result.error.message,
114
+ context: result.error.context ?? null
115
+ } : null,
116
+ usage
117
+ });
118
+ writeOut(json + "\n");
119
+ return 0;
120
+ }
121
+ let code = 0;
122
+ if (result.status === "failed") {
123
+ code = 1;
124
+ const err = result.error;
125
+ if (err) {
126
+ const tag = err.recoverable ? " (recoverable)" : "";
127
+ renderer.writeError(`Failed [${err.severity}]${tag}: ${err.describe()}`);
128
+ const hint = contextOverflowHint(err);
129
+ if (hint) renderer.writeWarning(hint);
130
+ } else {
131
+ renderer.writeError("Failed.");
132
+ }
133
+ } else if (result.status === "aborted") {
134
+ code = 130;
135
+ renderer.writeWarning("Aborted.");
136
+ } else if (result.status === "max_iterations") {
137
+ code = 1;
138
+ renderer.writeWarning(`Hit max iterations (${result.iterations}).`);
139
+ }
140
+ if (result.finalText) renderer.write("\n" + result.finalText + "\n");
141
+ const r = result;
142
+ renderer.writeDelegateSummaries(r);
143
+ renderer.write(
144
+ "\n" + color.dim(
145
+ `[in: ${fmtTok(usage.input)} out: ${fmtTok(usage.output)} iters: ${usage.iterations} cost: ${usage.cost.toFixed(4)} ${(usage.elapsedMs / 1e3).toFixed(1)}s]`
146
+ ) + "\n"
147
+ );
148
+ return code;
149
+ }
150
+
151
+ // src/boot/dispatch-tui.ts
152
+ async function runTuiDispatch(options) {
153
+ const { runTui } = await import("@wrongstack/tui");
154
+ return runTui(options);
155
+ }
156
+
157
+ // src/boot/dispatch-webui.ts
158
+ import { color as color2 } from "@wrongstack/core/utils";
159
+ async function runWebUIDispatch(ctx) {
160
+ const {
161
+ agent,
162
+ events,
163
+ statusTracker,
164
+ session,
165
+ config,
166
+ flags,
167
+ projectRoot,
168
+ globalConfigPath,
169
+ projectSessionsDir,
170
+ modelsRegistry,
171
+ mcpRegistry,
172
+ brain,
173
+ brainSettings,
174
+ brainRuntime,
175
+ getBrainLog,
176
+ subscribeEternalIteration,
177
+ sessionStore,
178
+ memoryStore,
179
+ skillLoader,
180
+ promptLoader,
181
+ modeStore,
182
+ modeId,
183
+ needsSetup,
184
+ renderer,
185
+ onAutonomy,
186
+ applyLiveSettings,
187
+ onModelContextResolved,
188
+ activateSessionIdentity,
189
+ rebindTodosCheckpoint,
190
+ agentTranscripts,
191
+ sddSubagentFactory,
192
+ onKanbanDispatch
193
+ } = ctx;
194
+ const isSimpleUi = flags["simpleui"] === true;
195
+ agent.disableInteractiveConfirmation();
196
+ renderer.setSilent(true);
197
+ const { runWebUI } = await import("./webui-server-ZF5KVGC5.js");
198
+ const flagValue = (names) => {
199
+ for (const name of names) {
200
+ if (!Object.hasOwn(flags, name)) continue;
201
+ const value = flags[name];
202
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
203
+ throw new Error(`--${name} requires a value`);
204
+ }
205
+ return void 0;
206
+ };
207
+ const flagBoolean = (names) => {
208
+ for (const name of names) {
209
+ if (!Object.hasOwn(flags, name)) continue;
210
+ const value = flags[name];
211
+ if (value === void 0) continue;
212
+ if (typeof value === "boolean") return value;
213
+ const normalized = value.trim().toLowerCase();
214
+ if (["1", "true", "yes", "on"].includes(normalized)) return true;
215
+ if (["0", "false", "no", "off"].includes(normalized)) return false;
216
+ throw new Error(`--${name} must be a boolean value`);
217
+ }
218
+ return void 0;
219
+ };
220
+ const envFlag = (name) => {
221
+ const value = process.env[name]?.trim().toLowerCase();
222
+ return value === "1" || value === "true" || value === "yes" || value === "on";
223
+ };
224
+ const parsePort = (value, fallback, label) => {
225
+ if (value === void 0) return fallback;
226
+ const parsed = Number.parseInt(value, 10);
227
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
228
+ throw new Error(`${label} must be a port between 1 and 65535`);
229
+ }
230
+ return parsed;
231
+ };
232
+ let webuiHost;
233
+ let webuiPort;
234
+ let webuiAccessToken;
235
+ let webuiPublicUrl;
236
+ let webuiPublicWsUrl;
237
+ let webuiRequireToken;
238
+ let frontendDistDir;
239
+ try {
240
+ webuiHost = flagValue(["webui-host", "host"]) ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
241
+ const defaultPort = isSimpleUi ? 3466 : 3456;
242
+ webuiPort = parsePort(
243
+ flagValue(["webui-port", "http-port", "port", "ws-port"]) ?? process.env["WEBUI_PORT"] ?? process.env["PORT"] ?? process.env["WS_PORT"],
244
+ defaultPort,
245
+ "--port"
246
+ );
247
+ webuiAccessToken = flagValue(["webui-token"]) ?? process.env["WEBUI_TOKEN"] ?? process.env["WEBUI_AUTH_TOKEN"];
248
+ webuiPublicUrl = flagValue(["webui-public-url", "public-url"]) ?? process.env["WEBUI_PUBLIC_URL"];
249
+ webuiPublicWsUrl = flagValue(["webui-public-ws-url", "public-ws-url"]) ?? process.env["WEBUI_PUBLIC_WS_URL"];
250
+ webuiRequireToken = flagBoolean(["webui-require-token", "require-token"]) ?? envFlag("WEBUI_REQUIRE_TOKEN");
251
+ if (isSimpleUi) {
252
+ const { ensureSimpleUiDistDir } = await import("./simpleui-dist-JIFGUJO7.js");
253
+ frontendDistDir = await ensureSimpleUiDistDir();
254
+ }
255
+ } catch (err) {
256
+ renderer.setSilent(false);
257
+ renderer.writeInfo(color2.red(` ${err instanceof Error ? err.message : String(err)}`));
258
+ return 1;
259
+ }
260
+ const webuiPromise = runWebUI({
261
+ agent,
262
+ events,
263
+ statusTracker,
264
+ session,
265
+ surface: isSimpleUi ? "simpleui" : "webui",
266
+ host: webuiHost,
267
+ port: webuiPort,
268
+ frontendDistDir,
269
+ accessToken: webuiAccessToken,
270
+ publicUrl: webuiPublicUrl,
271
+ publicWsUrl: webuiPublicWsUrl,
272
+ requireToken: webuiRequireToken,
273
+ projectRoot,
274
+ appConfig: config,
275
+ open: !!flags.open,
276
+ agentTranscripts,
277
+ hqAllowExec: flagBoolean(["hq-allow-exec"]) ?? false,
278
+ modelsRegistry,
279
+ globalConfigPath,
280
+ profileConfigPath: ctx.profileConfigPath,
281
+ mcpRegistry,
282
+ subscribeEternalIteration,
283
+ sessionStore,
284
+ sessionsDir: projectSessionsDir,
285
+ claimSession: activateSessionIdentity ? async (sessionId) => {
286
+ const previousSessionId = agent.ctx.session?.id ?? session.id;
287
+ await activateSessionIdentity(sessionId);
288
+ return async () => activateSessionIdentity(previousSessionId);
289
+ } : void 0,
290
+ brain,
291
+ brainSettings,
292
+ brainRuntime,
293
+ getBrainLog,
294
+ onBeforeSessionTodosReplaced: (sessionId, sessionsDir) => rebindTodosCheckpoint?.(sessionId, sessionsDir),
295
+ onSessionSwapped: async (sessionId, target) => {
296
+ if (target && activateSessionIdentity) {
297
+ await activateSessionIdentity(sessionId, target);
298
+ }
299
+ void import("@wrongstack/tools/session-kanban").then(
300
+ ({ hydrateSessionKanban }) => hydrateSessionKanban(agent.ctx)
301
+ );
302
+ },
303
+ onModelContextResolved,
304
+ memoryStore,
305
+ skillLoader,
306
+ promptLoader,
307
+ modeStore,
308
+ modeId,
309
+ needsSetup,
310
+ sddSubagentFactory,
311
+ onKanbanDispatch,
312
+ // Print the "open this" banner only once the server is actually
313
+ // listening, using the RESOLVED port. Requested ports auto-advance past
314
+ // busy ports inside runWebUI, so a banner printed up-front lies whenever
315
+ // the default when it is taken (a second instance, leftover socket).
316
+ onListening: ({ url }) => {
317
+ const surface = isSimpleUi ? "SimpleUI" : "WebUI";
318
+ renderer.writeInfo(color2.green(` \u2726 ${surface} running \u2192 ${color2.bold(url)}`));
319
+ renderer.writeInfo(
320
+ color2.dim(` Press Ctrl+C in this terminal to stop the ${surface} server.
321
+ `)
322
+ );
323
+ },
324
+ // Make autonomy.switch from the browser flip the CLI's real
325
+ // autonomy mode — context.meta alone never reaches the run loop.
326
+ onAutonomySwitch: (mode) => {
327
+ if (mode === "off" || mode === "suggest" || mode === "auto" || mode === "eternal" || mode === "eternal-parallel") {
328
+ onAutonomy?.(mode);
329
+ }
330
+ },
331
+ onYoloSwitch: (enabled) => {
332
+ applyLiveSettings?.({ yolo: enabled });
333
+ }
334
+ });
335
+ const webuiExit = new Promise((resolve3) => {
336
+ webuiPromise.then(() => {
337
+ renderer.setSilent(false);
338
+ renderer.write("\n");
339
+ renderer.writeInfo(
340
+ color2.yellow(` Shutting down ${isSimpleUi ? "SimpleUI" : "WebUI"} server\u2026`)
341
+ );
342
+ resolve3(0);
343
+ }).catch((err) => {
344
+ renderer.setSilent(false);
345
+ console.debug(`[execution] webui error: ${err}`);
346
+ resolve3(1);
347
+ });
348
+ });
349
+ return webuiExit;
350
+ }
351
+
352
+ // src/boot/execution-mode.ts
353
+ function resolveExecutionMode(positional, flags) {
354
+ const prompt = typeof flags["prompt"] === "string" ? flags["prompt"] : "";
355
+ if (positional.length > 0 || prompt.length > 0) return "single-shot";
356
+ if (flags.tui && flags["no-tui"] !== true && !flags.webui) return "tui";
357
+ if (flags.webui) return "webui";
358
+ return "repl";
359
+ }
360
+
361
+ // src/boot/tui-coordinator-callbacks.ts
362
+ function createTuiCoordinatorCallbacks({
363
+ state,
364
+ context,
365
+ coordinatorEvents,
366
+ ensureAutonomousCoordinator
367
+ }) {
368
+ return {
369
+ getAutonomousCoordinator: () => ensureAutonomousCoordinator(),
370
+ subscribeCoordinatorEvents: (fn) => {
371
+ coordinatorEvents.add(fn);
372
+ return () => {
373
+ coordinatorEvents.delete(fn);
374
+ };
375
+ },
376
+ onCoordinatorStart: (goal) => {
377
+ const coordinator = ensureAutonomousCoordinator();
378
+ if (!coordinator) {
379
+ console.error(
380
+ JSON.stringify({
381
+ level: "error",
382
+ event: "coordinator.not_ready",
383
+ message: "no director available",
384
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
385
+ })
386
+ );
387
+ return;
388
+ }
389
+ if (state.coordinatorRun) return;
390
+ state.coordinatorRun = coordinator.run({ goal: goal ?? "Improve the codebase", runUntilComplete: true }).then(() => void 0).catch((err) => {
391
+ console.error(
392
+ JSON.stringify({
393
+ level: "error",
394
+ event: "coordinator.run_failed",
395
+ message: err instanceof Error ? err.message : String(err),
396
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
397
+ })
398
+ );
399
+ }).finally(() => {
400
+ state.coordinatorRun = null;
401
+ });
402
+ },
403
+ onCoordinatorStop: () => {
404
+ state.autonomousCoordinator?.stop();
405
+ },
406
+ onCoordinatorTasks: async () => {
407
+ const coordinator = ensureAutonomousCoordinator();
408
+ if (!coordinator) return null;
409
+ await coordinator.graph.load();
410
+ return coordinator.auction.getPendingTasks().map((task) => ({
411
+ id: task.id,
412
+ title: task.title,
413
+ priority: task.priority,
414
+ tags: task.tags
415
+ }));
416
+ },
417
+ onCoordinatorClaim: async (taskId) => {
418
+ const coordinator = ensureAutonomousCoordinator();
419
+ if (!coordinator) return "No coordinator is active.";
420
+ await coordinator.graph.load();
421
+ const goal = coordinator.graph.get(taskId);
422
+ if (goal?.type !== "goal") {
423
+ return `Task ${taskId.slice(0, 8)} not found in the coordinator graph.`;
424
+ }
425
+ if (goal.status !== "pending") {
426
+ return `Task ${taskId.slice(0, 8)} is ${goal.status}, not claimable.`;
427
+ }
428
+ const ok = await coordinator.auction.claim(
429
+ taskId,
430
+ `terminal@${context.session.id ?? "unknown"}`,
431
+ "Terminal worker"
432
+ );
433
+ if (!ok) {
434
+ return `Task ${taskId.slice(0, 8)} could not be claimed (status changed?).`;
435
+ }
436
+ return { description: goal.description };
437
+ },
438
+ onCoordinatorComplete: async (taskId, result) => {
439
+ const coordinator = ensureAutonomousCoordinator();
440
+ if (!coordinator) return "No coordinator is active.";
441
+ await coordinator.graph.load();
442
+ const goal = coordinator.graph.get(taskId);
443
+ if (goal?.type !== "goal") {
444
+ return `Task ${taskId.slice(0, 8)} not found in the coordinator graph.`;
445
+ }
446
+ if (goal.status !== "in_progress") {
447
+ return `Task ${taskId.slice(0, 8)} is ${goal.status}, cannot complete.`;
448
+ }
449
+ await coordinator.reportTaskCompletion(
450
+ taskId,
451
+ result ?? "Terminal worker completed the task"
452
+ );
453
+ return null;
454
+ },
455
+ onCoordinatorFail: async (taskId, error) => {
456
+ const coordinator = ensureAutonomousCoordinator();
457
+ if (!coordinator) return "No coordinator is active.";
458
+ await coordinator.graph.load();
459
+ const goal = coordinator.graph.get(taskId);
460
+ if (goal?.type !== "goal") {
461
+ return `Task ${taskId.slice(0, 8)} not found in the coordinator graph.`;
462
+ }
463
+ if (goal.status !== "in_progress") {
464
+ return `Task ${taskId.slice(0, 8)} is ${goal.status}, cannot fail.`;
465
+ }
466
+ await coordinator.reportTaskFailure(taskId, error);
467
+ return null;
468
+ },
469
+ onCoordinatorStatus: async () => {
470
+ const coordinator = ensureAutonomousCoordinator();
471
+ if (!coordinator) return null;
472
+ await coordinator.syncFromGraph();
473
+ const stats = coordinator.getStats();
474
+ return {
475
+ goals: {
476
+ total: stats.goals.total,
477
+ done: stats.goals.done,
478
+ pending: stats.goals.pending,
479
+ failed: stats.goals.failed
480
+ },
481
+ dag: {
482
+ running: stats.dag.running,
483
+ ready: stats.dag.ready,
484
+ done: stats.dag.done,
485
+ failed: stats.dag.failed
486
+ },
487
+ auction: {
488
+ pending: stats.auction.pending,
489
+ inProgress: stats.auction.in_progress
490
+ }
491
+ };
492
+ }
493
+ };
494
+ }
495
+
496
+ // src/boot/tui-coordinator-setup.ts
497
+ import * as path from "node:path";
498
+ import { AutonomousCoordinator } from "@wrongstack/core/coordination";
499
+ function setupAutonomousCoordinator(ctx) {
500
+ const { state, events, context, wpaths, mailbox, director, getDirector, coordinatorController, onCoordinatorStopSetter } = ctx;
501
+ const ensure = () => {
502
+ if (state.autonomousCoordinator) return state.autonomousCoordinator;
503
+ const currentDirector = getDirector?.() ?? director;
504
+ if (!currentDirector) return null;
505
+ const transcript = context.session.transcriptPath;
506
+ const sessionDir = transcript ? path.dirname(transcript) : wpaths.projectDir;
507
+ const llmProvider = {
508
+ decide: async (prompt) => {
509
+ const sysPrompt = [
510
+ {
511
+ type: "text",
512
+ text: 'You are the autonomous brain of a multi-agent coordination system. Pick the best option for the decision described and reply with JSON: {"optionId":"<id>","rationale":"<short why>"}.'
513
+ }
514
+ ];
515
+ const userPrompt = {
516
+ type: "text",
517
+ text: `Decision: ${prompt.question}
518
+
519
+ Context: ${JSON.stringify(prompt.context)}
520
+
521
+ Options:
522
+ ${prompt.options.map((o, i) => ` ${i + 1}. [${o.id}] ${o.label}${o.consequence ? ` \u2014 ${o.consequence}` : ""}`).join("\n")}
523
+
524
+ Risk: ${prompt.risk}
525
+
526
+ Reply with ONLY the JSON object.`
527
+ };
528
+ const resp = await context.provider.complete(
529
+ {
530
+ model: context.model,
531
+ system: sysPrompt,
532
+ messages: [
533
+ {
534
+ role: "user",
535
+ content: [userPrompt]
536
+ }
537
+ ],
538
+ maxTokens: 1024,
539
+ temperature: 0
540
+ },
541
+ { signal: context.signal }
542
+ );
543
+ const text = resp.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim();
544
+ const cleaned = text.replace(/^```(?:json)?\s*/i, "").replace(/```$/, "").trim();
545
+ try {
546
+ const parsed = JSON.parse(cleaned);
547
+ const optId = parsed.optionId ?? prompt.options[0]?.id ?? "";
548
+ return { optionId: optId, rationale: parsed.rationale ?? "" };
549
+ } catch {
550
+ return { optionId: prompt.options[0]?.id ?? "", rationale: text };
551
+ }
552
+ }
553
+ };
554
+ state.autonomousCoordinator = new AutonomousCoordinator({
555
+ sessionDir,
556
+ fleet: currentDirector.fleet,
557
+ fleetManager: currentDirector.fleetManager,
558
+ director: currentDirector,
559
+ mailbox,
560
+ selfAgentId: `leader@${context.session.id ?? "unknown"}`,
561
+ selfAgentName: "Leader",
562
+ llmProvider,
563
+ onCoordinatorEvent: (event) => {
564
+ for (const fn of state.coordinatorEvents) fn(event);
565
+ }
566
+ });
567
+ onCoordinatorStopSetter(() => state.autonomousCoordinator?.dispose());
568
+ if (coordinatorController) {
569
+ coordinatorController["onCoordinatorStart"] = (goal) => {
570
+ const coordinator = state.autonomousCoordinator;
571
+ if (!coordinator) return;
572
+ coordinator.run({ goal: goal ?? "Improve the codebase", runUntilComplete: true }).then(() => void 0).catch(
573
+ (err) => console.error(
574
+ JSON.stringify({
575
+ level: "error",
576
+ event: "coordinator.run_failed",
577
+ message: err instanceof Error ? err.message : String(err),
578
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
579
+ })
580
+ )
581
+ );
582
+ };
583
+ coordinatorController["onCoordinatorStop"] = () => state.autonomousCoordinator?.stop();
584
+ coordinatorController["onCoordinatorTasks"] = async () => {
585
+ if (!state.autonomousCoordinator) return null;
586
+ await state.autonomousCoordinator.graph.load();
587
+ return state.autonomousCoordinator.auction.getPendingTasks().map((task) => ({ id: task.id, title: task.title, priority: task.priority, tags: task.tags }));
588
+ };
589
+ coordinatorController["onCoordinatorClaim"] = async (taskId) => {
590
+ if (!state.autonomousCoordinator) return "No coordinator is active.";
591
+ await state.autonomousCoordinator.graph.load();
592
+ const goal = state.autonomousCoordinator.graph.get(taskId);
593
+ if (goal?.type !== "goal") return `Task ${taskId.slice(0, 8)} not found.`;
594
+ if (goal.status !== "pending") return `Task ${taskId.slice(0, 8)} is ${goal.status}, not claimable.`;
595
+ const ok = await state.autonomousCoordinator.auction.claim(
596
+ taskId,
597
+ `terminal@${context.session.id ?? "unknown"}`,
598
+ "Terminal worker"
599
+ );
600
+ if (!ok) return `Task ${taskId.slice(0, 8)} could not be claimed.`;
601
+ return { description: goal.description };
602
+ };
603
+ coordinatorController["onCoordinatorComplete"] = async (taskId, result) => {
604
+ if (!state.autonomousCoordinator) return "No coordinator is active.";
605
+ await state.autonomousCoordinator.graph.load();
606
+ const goal = state.autonomousCoordinator.graph.get(taskId);
607
+ if (goal?.type !== "goal") return `Task ${taskId.slice(0, 8)} not found.`;
608
+ if (goal.status !== "in_progress") return `Task ${taskId.slice(0, 8)} is ${goal.status}, cannot complete.`;
609
+ await state.autonomousCoordinator.reportTaskCompletion(taskId, result ?? "Terminal worker completed the task");
610
+ return null;
611
+ };
612
+ coordinatorController["onCoordinatorFail"] = async (taskId, error) => {
613
+ if (!state.autonomousCoordinator) return "No coordinator is active.";
614
+ await state.autonomousCoordinator.graph.load();
615
+ const goal = state.autonomousCoordinator.graph.get(taskId);
616
+ if (goal?.type !== "goal") return `Task ${taskId.slice(0, 8)} not found.`;
617
+ if (goal.status !== "in_progress") return `Task ${taskId.slice(0, 8)} is ${goal.status}, cannot fail.`;
618
+ await state.autonomousCoordinator.reportTaskFailure(taskId, error);
619
+ return null;
620
+ };
621
+ coordinatorController["onCoordinatorStatus"] = async () => {
622
+ if (!state.autonomousCoordinator) return null;
623
+ await state.autonomousCoordinator.syncFromGraph();
624
+ const stats = state.autonomousCoordinator.getStats();
625
+ return {
626
+ goals: { total: stats.goals.total, done: stats.goals.done, pending: stats.goals.pending, failed: stats.goals.failed },
627
+ dag: { running: stats.dag.running, ready: stats.dag.ready, done: stats.dag.done, failed: stats.dag.failed },
628
+ auction: { pending: stats.auction.pending, inProgress: stats.auction.in_progress }
629
+ };
630
+ };
631
+ }
632
+ return state.autonomousCoordinator;
633
+ };
634
+ if (director) ensure();
635
+ const offDirectorSpawned = events.onPattern("subagent.spawned", () => {
636
+ if (ensure()) offDirectorSpawned();
637
+ });
638
+ return {
639
+ ensure,
640
+ cleanup: () => offDirectorSpawned()
641
+ };
642
+ }
643
+
644
+ // src/boot/tui-debug-stream.ts
645
+ async function registerDebugStreamCallback(cb) {
646
+ try {
647
+ const { setDebugStreamCallback } = await import("@wrongstack/providers");
648
+ setDebugStreamCallback(cb);
649
+ } catch (err) {
650
+ console.error(
651
+ JSON.stringify({
652
+ level: "error",
653
+ event: "execution.debug_stream_register_failed",
654
+ message: err instanceof Error ? err.message : String(err),
655
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
656
+ })
657
+ );
658
+ }
659
+ }
660
+ async function restoreDebugStreamCallback() {
661
+ try {
662
+ const { setDebugStreamCallback, defaultDebugStreamCallback } = await import("@wrongstack/providers");
663
+ setDebugStreamCallback(defaultDebugStreamCallback);
664
+ } catch (err) {
665
+ console.error(
666
+ JSON.stringify({
667
+ level: "error",
668
+ event: "execution.debug_stream_restore_failed",
669
+ message: err instanceof Error ? err.message : String(err),
670
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
671
+ })
672
+ );
673
+ }
674
+ }
675
+
676
+ // src/boot/tui-goal-wiring.ts
677
+ import { PHASE_EVENT_NAMES } from "@wrongstack/core/goal";
678
+ var GOAL_EVENTS = [
679
+ ...PHASE_EVENT_NAMES,
680
+ // Extra events not in PhaseEventMap (sourced from other subsystems)
681
+ "sdd.board.snapshot",
682
+ "worktree.allocated",
683
+ "worktree.committed",
684
+ "worktree.merged",
685
+ "worktree.conflict",
686
+ "worktree.released",
687
+ "worktree.failed",
688
+ "countdown.tick"
689
+ ];
690
+ function wireGoal(events) {
691
+ const handlers = /* @__PURE__ */ new Map();
692
+ const onUntyped = events.on.bind(events);
693
+ const offUntyped = events.off.bind(events);
694
+ const subscribe = (handler) => {
695
+ const registrations = [];
696
+ for (const ev of GOAL_EVENTS) {
697
+ const h = (p) => handler(ev, p);
698
+ handlers.set(ev, h);
699
+ onUntyped(ev, h);
700
+ registrations.push(() => offUntyped(ev, h));
701
+ }
702
+ return () => {
703
+ for (const unregister of registrations) unregister();
704
+ handlers.clear();
705
+ };
706
+ };
707
+ const cleanup = () => {
708
+ for (const [ev, h] of handlers) {
709
+ offUntyped(ev, h);
710
+ }
711
+ handlers.clear();
712
+ };
713
+ return { subscribe, cleanup };
714
+ }
715
+
716
+ // src/boot/tui-live-sessions.ts
717
+ import * as path2 from "node:path";
718
+ async function getLiveSessions(ctx) {
719
+ const { SessionRegistry } = await import("@wrongstack/core/storage");
720
+ const globalRoot = path2.dirname(ctx.state.wpaths.globalConfig);
721
+ const registry = new SessionRegistry(globalRoot);
722
+ const sessions = await registry.list();
723
+ return sessions.filter((s) => s.status !== "stale").map((s) => ({
724
+ sessionId: s.sessionId,
725
+ projectName: s.projectName,
726
+ projectSlug: s.projectSlug,
727
+ projectRoot: s.projectRoot,
728
+ workingDir: s.workingDir,
729
+ gitBranch: s.gitBranch,
730
+ status: s.status,
731
+ pid: s.pid,
732
+ startedAt: s.startedAt,
733
+ agentCount: s.agentCount,
734
+ agents: s.agents.map((a) => ({
735
+ id: a.id,
736
+ name: a.name,
737
+ status: a.status,
738
+ currentTool: a.currentTool,
739
+ iterations: a.iterations,
740
+ toolCalls: a.toolCalls,
741
+ lastActivityAt: a.lastActivityAt
742
+ }))
743
+ }));
744
+ }
745
+ function onSwitchToSession(ctx, _sessionId, targetRoot, projectName) {
746
+ ctx.state.pendingProjectSwitch = { root: targetRoot, name: projectName };
747
+ }
748
+
749
+ // src/boot/tui-project-picker-callback.ts
750
+ import * as path3 from "node:path";
751
+ import { color as color3 } from "@wrongstack/core/utils";
752
+ async function getProjectPickerItems(ctx) {
753
+ const { buildPickerItems } = await import("./project-picker-BOCWHBXP.js");
754
+ return buildPickerItems({
755
+ globalConfigPath: ctx.state.wpaths.globalConfig,
756
+ currentProjectRoot: ctx.state.projectRoot
757
+ });
758
+ }
759
+ async function onProjectSelect(ctx, slug, kind) {
760
+ const { state, renderer, director, getEternalEngine, getParallelEngine, switchProjectInPlace: switchProjectInPlace2 } = ctx;
761
+ try {
762
+ if (kind === "action") {
763
+ if (slug === "new-session") {
764
+ const name = path3.basename(state.projectRoot) || state.projectRoot;
765
+ const err2 = await switchProjectInPlace2(state.projectRoot, name);
766
+ if (err2) renderer.write(color3.red(`Project switch failed: ${err2}
767
+ `));
768
+ }
769
+ return;
770
+ }
771
+ const { loadManifest, saveManifest } = await import("./project-manifest-GE25PW4S.js");
772
+ const manifest = await loadManifest(state.wpaths.globalConfig);
773
+ const project = manifest.projects.find((p) => p.slug === slug);
774
+ if (!project) return;
775
+ const targetRoot = path3.resolve(project.root);
776
+ if (path3.resolve(state.projectRoot) === targetRoot) return;
777
+ const fleetStatus = director?.status();
778
+ const fleetRunning = fleetStatus?.subagents.filter((a) => a.status === "running").length ?? 0;
779
+ const eternalActive = getEternalEngine?.()?.currentState === "running";
780
+ const parallelActive = getParallelEngine?.()?.currentState === "running";
781
+ const hasActiveAgents = fleetRunning > 0 || eternalActive || parallelActive;
782
+ if (hasActiveAgents) {
783
+ const parts = [
784
+ color3.yellow("\u26A0 Switching project in place; active background work is still tied to the previous project:")
785
+ ];
786
+ if (fleetRunning > 0) parts.push(color3.dim(` \u2022 ${fleetRunning} subagent(s) currently running`));
787
+ if (eternalActive) parts.push(color3.dim(" \u2022 Eternal engine is active"));
788
+ if (parallelActive) parts.push(color3.dim(" \u2022 Parallel engine is active"));
789
+ parts.push("");
790
+ parts.push(color3.dim(` New project: ${project.name}`));
791
+ renderer.write(`
792
+ ${parts.join("\n")}
793
+ `);
794
+ }
795
+ project.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
796
+ await saveManifest(manifest, state.wpaths.globalConfig);
797
+ const err = await switchProjectInPlace2(targetRoot, project.name);
798
+ if (err) renderer.write(color3.red(`Project switch failed: ${err}
799
+ `));
800
+ } catch (err) {
801
+ renderer.write(
802
+ color3.red(
803
+ `Project switch failed: ${err instanceof Error ? err.message : String(err)}
804
+ `
805
+ )
806
+ );
807
+ }
808
+ }
809
+
810
+ // src/boot/tui-project-spawn.ts
811
+ import { spawn } from "node:child_process";
812
+ import { createRequire } from "node:module";
813
+ import * as fs from "node:fs/promises";
814
+ import * as path4 from "node:path";
815
+ import { color as color4 } from "@wrongstack/core/utils";
816
+ async function handleProjectSwitchSpawn(opts) {
817
+ const PROJECT_SWITCH_EXIT_CODE = 42;
818
+ if (opts.code !== PROJECT_SWITCH_EXIT_CODE || !opts.pendingProjectSwitch) {
819
+ return null;
820
+ }
821
+ const { root, name, resumeSessionId } = opts.pendingProjectSwitch;
822
+ process.stdout.write("\x1B[2J\x1B[H");
823
+ let cliPath;
824
+ try {
825
+ const req = createRequire(import.meta.url);
826
+ const pkgPath = req.resolve("@wrongstack/cli/package.json");
827
+ const pkgDir = path4.dirname(pkgPath);
828
+ cliPath = path4.join(pkgDir, "dist", "index.js");
829
+ await fs.access(cliPath);
830
+ } catch {
831
+ cliPath = process.argv[1] ?? "";
832
+ if (!cliPath) {
833
+ console.error(color4.red("Could not locate the CLI entry point.\n"));
834
+ return 1;
835
+ }
836
+ }
837
+ const nodeExe = process.execPath;
838
+ const spawnArgs = [cliPath, "--no-interactive"];
839
+ if (resumeSessionId) spawnArgs.push("--resume", resumeSessionId);
840
+ spawn(nodeExe, spawnArgs, {
841
+ cwd: root,
842
+ stdio: "ignore",
843
+ detached: true
844
+ }).on("error", (err) => {
845
+ console.error(color4.red(`Failed to spawn wstack: ${err.message}`));
846
+ });
847
+ console.log(
848
+ [
849
+ "",
850
+ color4.green(` Switched to ${name}`),
851
+ color4.dim(` Root: ${root}`),
852
+ color4.dim(" (current session stays open \u2014 Ctrl+C to return)"),
853
+ ""
854
+ ].join("\n")
855
+ );
856
+ return 0;
857
+ }
858
+
859
+ // src/boot/tui-project-switch.ts
860
+ import * as fs2 from "node:fs/promises";
861
+ import * as path5 from "node:path";
862
+ import {
863
+ DefaultSystemPromptBuilder,
864
+ setQueuedMessagesSnapshot
865
+ } from "@wrongstack/core/agent";
866
+ import { DefaultSessionStore } from "@wrongstack/core/storage";
867
+ import { resolveWstackPaths, sessionScopedPath } from "@wrongstack/core/utils";
868
+ async function switchProjectInPlace(ctx, targetRoot, displayName) {
869
+ const {
870
+ state,
871
+ context,
872
+ events,
873
+ agent,
874
+ config,
875
+ tokenCounter,
876
+ modeId,
877
+ modeStore,
878
+ memoryStore,
879
+ skillLoader,
880
+ attachTodosCheckpoint: attachTodosCheckpoint3
881
+ } = ctx;
882
+ const resolved = path5.resolve(targetRoot);
883
+ const stat2 = await fs2.stat(resolved).catch(() => null);
884
+ if (!stat2?.isDirectory()) return `Cannot switch: not a directory: ${resolved}`;
885
+ const oldWriter = context.session;
886
+ const oldUsage = tokenCounter.total();
887
+ const oldProjectRoot = state.projectRoot;
888
+ const nextWpaths = resolveWstackPaths({
889
+ projectRoot: resolved,
890
+ globalRoot: state.wpaths.globalRoot
891
+ });
892
+ await fs2.mkdir(nextWpaths.projectSessions, { recursive: true });
893
+ const nextSessionStore = new DefaultSessionStore({
894
+ dir: nextWpaths.projectSessions,
895
+ projectRoot: resolved
896
+ });
897
+ const nextWriter = await nextSessionStore.create({
898
+ id: "",
899
+ title: "",
900
+ model: context.model,
901
+ provider: context.provider.id ?? config.provider
902
+ });
903
+ const previousCwd = process.cwd();
904
+ try {
905
+ process.chdir(resolved);
906
+ process.chdir(previousCwd);
907
+ } catch (err) {
908
+ await nextWriter.close().catch(() => void 0);
909
+ await nextSessionStore.delete(nextWriter.id).catch(() => void 0);
910
+ return `Cannot switch: ${err instanceof Error ? err.message : String(err)}`;
911
+ }
912
+ if (!state.activateSessionIdentity) {
913
+ await nextWriter.close().catch(() => void 0);
914
+ await nextSessionStore.delete(nextWriter.id).catch(() => void 0);
915
+ return "Cannot switch: session ownership registry is unavailable.";
916
+ }
917
+ try {
918
+ await state.activateSessionIdentity(nextWriter.id, {
919
+ projectSlug: nextWpaths.projectSlug,
920
+ projectRoot: resolved,
921
+ projectName: displayName || path5.basename(resolved),
922
+ workingDir: resolved
923
+ });
924
+ } catch (err) {
925
+ await nextWriter.close().catch(() => void 0);
926
+ await nextSessionStore.delete(nextWriter.id).catch(() => void 0);
927
+ return `Cannot switch: ${err instanceof Error ? err.message : String(err)}`;
928
+ }
929
+ await Promise.resolve(state.detachActiveTodosCheckpoint?.()).catch(() => void 0);
930
+ process.chdir(resolved);
931
+ state.projectRoot = resolved;
932
+ state.wpaths = nextWpaths;
933
+ state.activeSessionStore = nextSessionStore;
934
+ context.cwd = resolved;
935
+ context.projectRoot = resolved;
936
+ context.workingDir = resolved;
937
+ context.session = nextWriter;
938
+ context.state.replaceMessages([]);
939
+ context.state.replaceTodos([]);
940
+ context.clearFileTracking();
941
+ context.tokenCounter.reset();
942
+ context.meta["packageTrackerOpts"] = { storageDir: nextWpaths.projectDir, projectRoot: resolved };
943
+ context.state.setMeta(
944
+ "plan.path",
945
+ sessionScopedPath(nextWpaths.projectSessions, nextWriter.id, ".plan.json")
946
+ );
947
+ context.state.setMeta(
948
+ "task.path",
949
+ sessionScopedPath(nextWpaths.projectSessions, nextWriter.id, ".tasks.json")
950
+ );
951
+ try {
952
+ state.detachActiveTodosCheckpoint = attachTodosCheckpoint3(
953
+ context.state,
954
+ sessionScopedPath(nextWpaths.projectSessions, nextWriter.id, ".todos.json"),
955
+ nextWriter.id,
956
+ events,
957
+ context.traceId
958
+ );
959
+ } catch {
960
+ state.detachActiveTodosCheckpoint = void 0;
961
+ }
962
+ setQueuedMessagesSnapshot(context, []);
963
+ try {
964
+ const switchMode = modeId && modeId !== "default" && modeStore ? await modeStore.getMode(modeId) : void 0;
965
+ const switchBuilder = new DefaultSystemPromptBuilder({
966
+ memoryStore: memoryStore ?? void 0,
967
+ // Single injection channel: SAGE turn middleware, not a static section.
968
+ injectMemory: false,
969
+ skillLoader,
970
+ modeStore,
971
+ modeId: modeId ?? "default",
972
+ modePrompt: switchMode?.prompt ?? "",
973
+ instructionPaths: {
974
+ globalDir: nextWpaths.globalInstructions,
975
+ projectDir: nextWpaths.inProjectInstructions,
976
+ systemVariant: config.systemPrompt?.variant
977
+ }
978
+ });
979
+ context.systemPrompt = await switchBuilder.build({
980
+ cwd: resolved,
981
+ projectRoot: resolved,
982
+ tools: agent.tools.list(),
983
+ provider: context.provider.id,
984
+ model: context.model
985
+ });
986
+ } catch (err) {
987
+ console.error(
988
+ JSON.stringify({
989
+ level: "warn",
990
+ event: "execution.project_switch_prompt_rebuild_failed",
991
+ message: err instanceof Error ? err.message : String(err),
992
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
993
+ })
994
+ );
995
+ }
996
+ try {
997
+ await oldWriter.append({ type: "session_end", ts: (/* @__PURE__ */ new Date()).toISOString(), usage: oldUsage });
998
+ await oldWriter.close();
999
+ } catch (err) {
1000
+ console.error(
1001
+ JSON.stringify({
1002
+ level: "warn",
1003
+ event: "execution.project_switch_old_session_close_failed",
1004
+ message: err instanceof Error ? err.message : String(err),
1005
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1006
+ })
1007
+ );
1008
+ }
1009
+ const emitUntyped = events.emit;
1010
+ emitUntyped("project.switched", { from: oldProjectRoot, to: resolved, name: displayName });
1011
+ return null;
1012
+ }
1013
+
1014
+ // src/boot/tui-sdd-callback.ts
1015
+ async function getSDDContext() {
1016
+ const { getActiveSDDContext: getActiveSDDContext2 } = await import("./sdd-runtime-JG4EEKL6.js");
1017
+ return getActiveSDDContext2();
1018
+ }
1019
+ async function onSDDOutput(output) {
1020
+ const {
1021
+ trySaveSpecFromAIOutput: trySaveSpecFromAIOutput2,
1022
+ trySaveImplementationPlan: trySaveImplementationPlan2,
1023
+ trySaveTasksFromAIOutput: trySaveTasksFromAIOutput2,
1024
+ autoDetectTaskCompletion: autoDetectTaskCompletion2,
1025
+ getTaskProgress: getTaskProgress2,
1026
+ getActiveSDDPhase: getActiveSDDPhase2
1027
+ } = await import("./sdd-runtime-JG4EEKL6.js");
1028
+ const messages = [];
1029
+ const specSaved = await trySaveSpecFromAIOutput2(output);
1030
+ if (specSaved)
1031
+ messages.push("\u2713 Spec detected and saved! Use /sdd approve to continue.");
1032
+ const planSaved = trySaveImplementationPlan2(output);
1033
+ if (planSaved) messages.push("\u2713 Implementation plan saved!");
1034
+ const tasksSaved = await trySaveTasksFromAIOutput2(output);
1035
+ if (tasksSaved) {
1036
+ const progress = getTaskProgress2();
1037
+ const count = progress?.total ?? 0;
1038
+ messages.push(`\u2713 ${count} tasks detected and saved! Use /sdd approve to execute.`);
1039
+ }
1040
+ const sddPhase = getActiveSDDPhase2();
1041
+ if (sddPhase === "executing") {
1042
+ const autoCompleted = autoDetectTaskCompletion2(output);
1043
+ if (autoCompleted > 0) {
1044
+ const progress = getTaskProgress2();
1045
+ if (progress) {
1046
+ messages.push(
1047
+ `\u2713 ${autoCompleted} task(s) auto-completed! Progress: ${progress.completed}/${progress.total} (${progress.percentComplete}%)`
1048
+ );
1049
+ }
1050
+ }
1051
+ }
1052
+ return messages;
1053
+ }
1054
+
1055
+ // src/boot/tui-session-resume.ts
1056
+ import * as path6 from "node:path";
1057
+ import { attachTodosCheckpoint, loadTodosCheckpoint } from "@wrongstack/core/storage";
1058
+ import { sessionScopedPath as sessionScopedPath2 } from "@wrongstack/core/utils";
1059
+ async function resumeSession(ctx, sessionId) {
1060
+ const { state, agent, tokenCounter, switchProviderAndModel, events } = ctx;
1061
+ if (!state.activeSessionStore) return null;
1062
+ const { replaySessionMessages } = await import("@wrongstack/tui");
1063
+ let canonicalSessionId = sessionId;
1064
+ try {
1065
+ if (state.activeSessionStore.resolveId) {
1066
+ canonicalSessionId = await state.activeSessionStore.resolveId(sessionId);
1067
+ }
1068
+ } catch (err) {
1069
+ console.error(
1070
+ JSON.stringify({
1071
+ level: "error",
1072
+ event: "execution.resume_id_resolve_failed",
1073
+ sessionId,
1074
+ message: err instanceof Error ? err.message : String(err),
1075
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1076
+ })
1077
+ );
1078
+ return null;
1079
+ }
1080
+ try {
1081
+ const { SessionRegistry } = await import("@wrongstack/core/storage");
1082
+ const registry = new SessionRegistry(path6.dirname(state.wpaths.globalConfig));
1083
+ const live = (await registry.list()).find(
1084
+ (s) => s.sessionId === canonicalSessionId && s.status !== "stale" && s.pid !== process.pid
1085
+ );
1086
+ if (live) {
1087
+ throw new Error(
1088
+ `Session is open in another running wstack (pid ${live.pid}) \u2014 it cannot be resumed here while live.`
1089
+ );
1090
+ }
1091
+ } catch (err) {
1092
+ if (err instanceof Error && err.message.startsWith("Session is open")) throw err;
1093
+ }
1094
+ const previousSessionId = agent.ctx.session?.id;
1095
+ let identityClaimed = false;
1096
+ let writerSwapped = false;
1097
+ let openedWriter;
1098
+ try {
1099
+ if (state.activateSessionIdentity) {
1100
+ await state.activateSessionIdentity(canonicalSessionId);
1101
+ identityClaimed = true;
1102
+ }
1103
+ const resumed = await state.activeSessionStore.resume(canonicalSessionId);
1104
+ openedWriter = resumed.writer;
1105
+ const meta = resumed.data.metadata;
1106
+ const entries = replaySessionMessages(
1107
+ resumed.data.messages,
1108
+ resumed.data.events,
1109
+ /* startId */
1110
+ 1
1111
+ );
1112
+ const sessionsDir = state.wpaths.projectSessions;
1113
+ const resumedTodosPath = sessionScopedPath2(sessionsDir, resumed.writer.id, ".todos.json");
1114
+ const restoredTodos = await loadTodosCheckpoint(
1115
+ resumedTodosPath,
1116
+ events,
1117
+ agent.ctx.traceId,
1118
+ resumed.writer.id
1119
+ ).catch(() => null);
1120
+ const oldWriter = agent.ctx.session;
1121
+ const oldMessages = [...agent.ctx.messages];
1122
+ agent.ctx.session = resumed.writer;
1123
+ try {
1124
+ agent.ctx.state.replaceMessages(resumed.data.messages);
1125
+ } catch (err) {
1126
+ agent.ctx.session = oldWriter;
1127
+ agent.ctx.state.replaceMessages(oldMessages);
1128
+ throw err;
1129
+ }
1130
+ writerSwapped = true;
1131
+ await agent.ctx.flushConversationJournal().catch((err) => {
1132
+ console.error(
1133
+ JSON.stringify({
1134
+ level: "error",
1135
+ event: "execution.resume_journal_flush_failed",
1136
+ message: err instanceof Error ? err.message : String(err),
1137
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1138
+ })
1139
+ );
1140
+ });
1141
+ await Promise.resolve(state.detachActiveTodosCheckpoint?.()).catch(() => void 0);
1142
+ agent.ctx.state.setMeta(
1143
+ "plan.path",
1144
+ sessionScopedPath2(sessionsDir, resumed.writer.id, ".plan.json")
1145
+ );
1146
+ agent.ctx.state.setMeta(
1147
+ "task.path",
1148
+ sessionScopedPath2(sessionsDir, resumed.writer.id, ".tasks.json")
1149
+ );
1150
+ agent.ctx.state.replaceTodos(restoredTodos ?? []);
1151
+ try {
1152
+ state.detachActiveTodosCheckpoint = attachTodosCheckpoint(
1153
+ agent.ctx.state,
1154
+ resumedTodosPath,
1155
+ resumed.writer.id,
1156
+ events,
1157
+ agent.ctx.traceId
1158
+ );
1159
+ } catch {
1160
+ state.detachActiveTodosCheckpoint = void 0;
1161
+ }
1162
+ const currentProviderId = agent.ctx.provider.id;
1163
+ const targetProviderId = typeof meta.provider === "string" && meta.provider.length > 0 ? meta.provider : currentProviderId;
1164
+ const targetModel = typeof meta.model === "string" && meta.model.length > 0 ? meta.model : agent.ctx.model;
1165
+ if (switchProviderAndModel && targetProviderId && (targetProviderId !== currentProviderId || targetModel !== agent.ctx.model)) {
1166
+ await Promise.resolve(switchProviderAndModel(targetProviderId, targetModel)).catch(
1167
+ (err) => {
1168
+ console.error(
1169
+ JSON.stringify({
1170
+ level: "error",
1171
+ event: "execution.resume_model_restore_failed",
1172
+ provider: targetProviderId,
1173
+ model: targetModel,
1174
+ message: err instanceof Error ? err.message : String(err),
1175
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1176
+ })
1177
+ );
1178
+ }
1179
+ );
1180
+ } else if (targetModel !== agent.ctx.model) {
1181
+ agent.ctx.model = targetModel;
1182
+ }
1183
+ if (oldWriter && oldWriter !== resumed.writer) {
1184
+ const endedUsage = tokenCounter.total();
1185
+ void (async () => {
1186
+ let appendOk = false;
1187
+ try {
1188
+ await oldWriter.append({
1189
+ type: "session_end",
1190
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1191
+ usage: endedUsage
1192
+ });
1193
+ appendOk = true;
1194
+ } catch (err) {
1195
+ console.error(
1196
+ JSON.stringify({
1197
+ level: "error",
1198
+ event: "execution.session_end_append_failed",
1199
+ message: err instanceof Error ? err.message : String(err),
1200
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1201
+ })
1202
+ );
1203
+ }
1204
+ if (appendOk) {
1205
+ try {
1206
+ await oldWriter.close();
1207
+ } catch (err) {
1208
+ console.error(
1209
+ JSON.stringify({
1210
+ level: "error",
1211
+ event: "execution.session_close_failed",
1212
+ message: err instanceof Error ? err.message : String(err),
1213
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1214
+ })
1215
+ );
1216
+ }
1217
+ }
1218
+ })();
1219
+ }
1220
+ tokenCounter.reset();
1221
+ tokenCounter.account(resumed.data.usage, targetModel, targetProviderId);
1222
+ return {
1223
+ entries,
1224
+ nextId: entries.length + 1,
1225
+ sessionId: resumed.writer.id
1226
+ };
1227
+ } catch (err) {
1228
+ if (identityClaimed && !writerSwapped && previousSessionId) {
1229
+ await state.activateSessionIdentity?.(previousSessionId).catch((rollbackErr) => {
1230
+ console.error(
1231
+ JSON.stringify({
1232
+ level: "error",
1233
+ event: "execution.resume_identity_rollback_failed",
1234
+ sessionId: previousSessionId,
1235
+ message: rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr),
1236
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1237
+ })
1238
+ );
1239
+ });
1240
+ }
1241
+ if (!writerSwapped) {
1242
+ await openedWriter?.close().catch(() => void 0);
1243
+ }
1244
+ console.error(
1245
+ JSON.stringify({
1246
+ level: "error",
1247
+ event: "execution.resume_session_failed",
1248
+ message: err instanceof Error ? err.message : String(err),
1249
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1250
+ })
1251
+ );
1252
+ return null;
1253
+ }
1254
+ }
1255
+
1256
+ // src/boot/tui-settings-adapter.ts
1257
+ import * as fs3 from "node:fs/promises";
1258
+ import * as path7 from "node:path";
1259
+ import { decryptConfigSecrets, encryptConfigSecrets, noOpVault } from "@wrongstack/core/security";
1260
+ import { normalizeTokenSavingTier, resolveFleetChatVerbosity } from "@wrongstack/core/types";
1261
+ import { atomicWrite } from "@wrongstack/core/utils";
1262
+ import { getProcessRegistry } from "@wrongstack/tools";
1263
+ var ANIMATION_STYLES = ["rainbow", "wave", "pulse", "dots", "breathe", "cycle"];
1264
+ function normalizeAnimationStyle(raw) {
1265
+ return typeof raw === "string" && ANIMATION_STYLES.includes(raw) ? raw : "rainbow";
1266
+ }
1267
+ function createSettingsAdapter(ctx) {
1268
+ const { configStore, wpaths, fleetStreamController, applyLiveSettings } = ctx;
1269
+ const deriveFsAccess = deriveFsAccessPair;
1270
+ function getSettings() {
1271
+ const cfg = configStore.get();
1272
+ const autonomy = cfg.autonomy;
1273
+ const rawMode = autonomy?.defaultMode;
1274
+ const mode = rawMode === "suggest" || rawMode === "auto" ? rawMode : "off";
1275
+ const modelRuntime = cfg.modelRuntime;
1276
+ const contextModeRaw = cfg.context?.mode;
1277
+ const contextMode = contextModeRaw === "frugal" || contextModeRaw === "deep" ? contextModeRaw : "balanced";
1278
+ const reasoningEffortRaw = modelRuntime?.reasoning?.effort;
1279
+ const reasoningEffort = reasoningEffortRaw === "none" || reasoningEffortRaw === "minimal" || reasoningEffortRaw === "low" || reasoningEffortRaw === "medium" || reasoningEffortRaw === "high" || reasoningEffortRaw === "xhigh" || reasoningEffortRaw === "max" ? reasoningEffortRaw : "high";
1280
+ const featuresAllow = cfg.features?.allowOutsideProjectRoot;
1281
+ const toolsRestrict = cfg.tools?.restrictToProjectRoot;
1282
+ const resolvedAllow = featuresAllow !== void 0 ? featuresAllow : toolsRestrict !== void 0 ? !toolsRestrict : true;
1283
+ const resolvedRestrict = !resolvedAllow;
1284
+ return {
1285
+ mode,
1286
+ delayMs: autonomy?.autoProceedDelayMs ?? 45e3,
1287
+ titleAnimation: autonomy?.terminalTitleAnimation !== false,
1288
+ yolo: cfg.yolo ?? autonomy?.yolo ?? false,
1289
+ fleetChatVerbosity: resolveFleetChatVerbosity(cfg.autonomy),
1290
+ chime: autonomy?.chime ?? false,
1291
+ confirmExit: autonomy?.confirmExit !== false,
1292
+ nextPrediction: cfg.nextPrediction ?? false,
1293
+ featureMcp: cfg.features?.mcp !== false,
1294
+ featurePlugins: cfg.features?.plugins !== false,
1295
+ featureMemory: cfg.features?.memory !== false,
1296
+ featureSkills: cfg.features?.skills !== false,
1297
+ featureModelsRegistry: cfg.features?.modelsRegistry !== false,
1298
+ // Preserve the 'auto' sentinel for the picker DISPLAY (normalize would
1299
+ // collapse it to 'off', which would then overwrite 'auto' on save);
1300
+ // everything else normalizes to a concrete tier.
1301
+ featureTokenSaving: cfg.features?.tokenSavingMode === "auto" ? "auto" : normalizeTokenSavingTier(cfg.features?.tokenSavingMode),
1302
+ allowOutsideProjectRoot: resolvedAllow,
1303
+ contextAutoCompact: cfg.context?.autoCompact !== false,
1304
+ contextStrategy: cfg.context?.strategy ?? "hybrid",
1305
+ contextMode,
1306
+ maxConcurrent: cfg.maxConcurrent ?? 4,
1307
+ logLevel: cfg.log?.level ?? "info",
1308
+ auditLevel: cfg.session?.auditLevel ?? "standard",
1309
+ indexOnStart: cfg.indexing?.onSessionStart !== false,
1310
+ maxIterations: cfg.tools?.maxIterations ?? 500,
1311
+ restrictFsToRoot: resolvedRestrict,
1312
+ autoProceedMaxIterations: cfg.autonomy?.autoProceedMaxIterations ?? 50,
1313
+ debugStream: cfg.debugStream ?? false,
1314
+ shellBangWarningDontShowAgain: autonomy?.shellBangWarningDontShowAgain === true,
1315
+ statuslineMode: autonomy?.statuslineMode === "no-color" ? "no-color" : autonomy?.statuslineMode === "minimum" ? "minimum" : "detailed",
1316
+ thinkingWord: normalizeTuiThinkingWord(autonomy?.thinkingWord),
1317
+ animationStyle: normalizeAnimationStyle(autonomy?.animationStyle),
1318
+ configScope: cfg.configScope ?? "global",
1319
+ enhanceDelayMs: cfg.autonomy?.enhanceDelayMs ?? 6e4,
1320
+ enhanceEnabled: cfg.autonomy?.enhance ?? true,
1321
+ enhanceLanguage: cfg.autonomy?.enhanceLanguage === "english" ? "english" : "original",
1322
+ midRunSendPicker: cfg.autonomy?.midRunSendPicker ?? true,
1323
+ mouseMode: autonomy?.mouseMode ?? false,
1324
+ autonomyNextPrompt: cfg.autonomy?.autonomyNextPrompt ?? "auto {{suggestion}}",
1325
+ reasoningMode: modelRuntime?.reasoning?.mode === "on" || modelRuntime?.reasoning?.mode === "off" ? modelRuntime.reasoning.mode : "auto",
1326
+ reasoningEffort,
1327
+ reasoningPreserve: modelRuntime?.reasoning?.preserve === true,
1328
+ cacheTtl: modelRuntime?.cache?.ttl === "5m" || modelRuntime?.cache?.ttl === "1h" ? modelRuntime.cache.ttl : "default",
1329
+ breakerEnabled: cfg.circuitBreaker?.enabled === true,
1330
+ breakerAutoKillResetMs: cfg.circuitBreaker?.autoKillResetMs ?? 6e4,
1331
+ showModelReasoning: autonomy?.showModelReasoning ?? true,
1332
+ showAgentSwarmPanel: autonomy?.showAgentSwarmPanel ?? true
1333
+ };
1334
+ }
1335
+ async function saveSettings(s) {
1336
+ try {
1337
+ if (s.mode !== void 0 || s.delayMs !== void 0 || s.titleAnimation !== void 0 || s.yolo !== void 0 || s.fleetChatVerbosity !== void 0 || s.chime !== void 0 || s.confirmExit !== void 0 || s.mouseMode !== void 0 || s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.contextMode !== void 0 || s.maxConcurrent !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.restrictFsToRoot !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.shellBangWarningDontShowAgain !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0 || s.enhanceEnabled !== void 0 || s.enhanceLanguage !== void 0 || s.midRunSendPicker !== void 0 || s.statuslineMode !== void 0 || s.thinkingWord !== void 0 || s.animationStyle !== void 0 || s.autonomyNextPrompt !== void 0 || s.autoProceedMaxIterations !== void 0 || s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0 || s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0 || s.showModelReasoning !== void 0 || s.showAgentSwarmPanel !== void 0) {
1338
+ const cfg = configStore.get();
1339
+ const activeProfileName = cfg.activeProfile ?? "default";
1340
+ const persistDeps = {
1341
+ configStore,
1342
+ profileConfigPath: wpaths.profileConfig(activeProfileName),
1343
+ inProjectConfigPath: wpaths.inProjectConfig,
1344
+ vault: noOpVault,
1345
+ resolveProfilePath: (name) => wpaths.profileConfig(name)
1346
+ };
1347
+ const targetPath = resolvePersistPath(persistDeps);
1348
+ let raw;
1349
+ try {
1350
+ raw = await fs3.readFile(targetPath, "utf8");
1351
+ } catch (err) {
1352
+ if (err.code !== "ENOENT") {
1353
+ throw new Error(
1354
+ `Failed to read config at ${targetPath}: ${err instanceof Error ? err.message : String(err)}`,
1355
+ { cause: err }
1356
+ );
1357
+ }
1358
+ raw = "{}";
1359
+ }
1360
+ const parsed = JSON.parse(raw);
1361
+ const decrypted = decryptConfigSecrets(parsed, noOpVault);
1362
+ const autonomy = decrypted.autonomy ?? {};
1363
+ if (s.mode !== void 0) autonomy.defaultMode = s.mode;
1364
+ if (s.delayMs !== void 0) autonomy.autoProceedDelayMs = s.delayMs;
1365
+ if (s.titleAnimation !== void 0) autonomy.terminalTitleAnimation = s.titleAnimation;
1366
+ if (s.yolo !== void 0) autonomy.yolo = s.yolo;
1367
+ if (s.fleetChatVerbosity !== void 0) {
1368
+ autonomy.fleetChatVerbosity = s.fleetChatVerbosity;
1369
+ }
1370
+ if (s.chime !== void 0) autonomy.chime = s.chime;
1371
+ if (s.confirmExit !== void 0) autonomy.confirmExit = s.confirmExit;
1372
+ if (s.mouseMode !== void 0) autonomy.mouseMode = s.mouseMode;
1373
+ if (s.enhanceDelayMs !== void 0) autonomy.enhanceDelayMs = s.enhanceDelayMs;
1374
+ if (s.enhanceEnabled !== void 0) autonomy.enhance = s.enhanceEnabled;
1375
+ if (s.enhanceLanguage !== void 0) autonomy.enhanceLanguage = s.enhanceLanguage;
1376
+ if (s.midRunSendPicker !== void 0) autonomy.midRunSendPicker = s.midRunSendPicker;
1377
+ if (s.shellBangWarningDontShowAgain !== void 0)
1378
+ autonomy.shellBangWarningDontShowAgain = s.shellBangWarningDontShowAgain;
1379
+ if (s.statuslineMode !== void 0) autonomy.statuslineMode = s.statuslineMode;
1380
+ if (s.thinkingWord !== void 0)
1381
+ autonomy.thinkingWord = normalizeTuiThinkingWord(s.thinkingWord);
1382
+ if (s.animationStyle !== void 0) autonomy.animationStyle = s.animationStyle;
1383
+ if (s.showModelReasoning !== void 0) autonomy.showModelReasoning = s.showModelReasoning;
1384
+ if (s.showAgentSwarmPanel !== void 0)
1385
+ autonomy.showAgentSwarmPanel = s.showAgentSwarmPanel;
1386
+ if (s.autonomyNextPrompt !== void 0) autonomy.autonomyNextPrompt = s.autonomyNextPrompt;
1387
+ if (s.autoProceedMaxIterations !== void 0)
1388
+ autonomy.autoProceedMaxIterations = s.autoProceedMaxIterations;
1389
+ decrypted.autonomy = autonomy;
1390
+ if (s.nextPrediction !== void 0) decrypted.nextPrediction = s.nextPrediction;
1391
+ if (s.yolo !== void 0) decrypted.yolo = s.yolo;
1392
+ const fsAccess = deriveFsAccess(s);
1393
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || fsAccess !== void 0) {
1394
+ const feats = decrypted.features ?? {};
1395
+ if (s.featureMcp !== void 0) feats.mcp = s.featureMcp;
1396
+ if (s.featurePlugins !== void 0) feats.plugins = s.featurePlugins;
1397
+ if (s.featureMemory !== void 0) feats.memory = s.featureMemory;
1398
+ if (s.featureSkills !== void 0) feats.skills = s.featureSkills;
1399
+ if (s.featureModelsRegistry !== void 0) feats.modelsRegistry = s.featureModelsRegistry;
1400
+ if (s.featureTokenSaving !== void 0) feats.tokenSavingMode = s.featureTokenSaving;
1401
+ if (fsAccess !== void 0)
1402
+ feats.allowOutsideProjectRoot = fsAccess.allowOutsideProjectRoot;
1403
+ decrypted.features = feats;
1404
+ }
1405
+ if (s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.contextMode !== void 0) {
1406
+ const c = decrypted.context ?? {};
1407
+ if (s.contextAutoCompact !== void 0) c.autoCompact = s.contextAutoCompact;
1408
+ if (s.contextStrategy !== void 0) c.strategy = s.contextStrategy;
1409
+ if (s.contextMode !== void 0) c.mode = s.contextMode;
1410
+ decrypted.context = c;
1411
+ }
1412
+ if (s.maxConcurrent !== void 0) decrypted.maxConcurrent = s.maxConcurrent;
1413
+ if (s.logLevel !== void 0) {
1414
+ const log = decrypted.log ?? {};
1415
+ log.level = s.logLevel;
1416
+ decrypted.log = log;
1417
+ }
1418
+ if (s.auditLevel !== void 0) {
1419
+ const sess = decrypted.session ?? {};
1420
+ sess.auditLevel = s.auditLevel;
1421
+ decrypted.session = sess;
1422
+ }
1423
+ if (s.indexOnStart !== void 0) {
1424
+ const idx = decrypted.indexing ?? {};
1425
+ idx.onSessionStart = s.indexOnStart;
1426
+ decrypted.indexing = idx;
1427
+ }
1428
+ if (s.maxIterations !== void 0 || fsAccess !== void 0) {
1429
+ const tools = decrypted.tools ?? {};
1430
+ if (s.maxIterations !== void 0) tools.maxIterations = s.maxIterations;
1431
+ if (fsAccess !== void 0) tools.restrictToProjectRoot = fsAccess.restrictToProjectRoot;
1432
+ decrypted.tools = tools;
1433
+ }
1434
+ if (s.debugStream !== void 0) {
1435
+ decrypted.debugStream = s.debugStream;
1436
+ const { setDebugStreamEnabled } = await import("@wrongstack/providers");
1437
+ setDebugStreamEnabled(s.debugStream);
1438
+ }
1439
+ if (s.configScope !== void 0) decrypted.configScope = s.configScope;
1440
+ if (s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0) {
1441
+ const modelRuntime = decrypted.modelRuntime ?? {};
1442
+ if (s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0) {
1443
+ const reasoning = modelRuntime.reasoning ?? {};
1444
+ if (s.reasoningMode !== void 0) reasoning.mode = s.reasoningMode;
1445
+ if (s.reasoningEffort !== void 0) reasoning.effort = s.reasoningEffort;
1446
+ if (s.reasoningPreserve !== void 0) reasoning.preserve = s.reasoningPreserve;
1447
+ modelRuntime.reasoning = reasoning;
1448
+ }
1449
+ if (s.cacheTtl !== void 0) {
1450
+ const cache = modelRuntime.cache ?? {};
1451
+ if (s.cacheTtl === "default") {
1452
+ delete cache.ttl;
1453
+ } else {
1454
+ cache.ttl = s.cacheTtl;
1455
+ }
1456
+ if (Object.keys(cache).length > 0) modelRuntime.cache = cache;
1457
+ else delete modelRuntime.cache;
1458
+ }
1459
+ decrypted.modelRuntime = modelRuntime;
1460
+ }
1461
+ if (s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0) {
1462
+ const cb = decrypted.circuitBreaker ?? {};
1463
+ if (s.breakerEnabled !== void 0) cb.enabled = s.breakerEnabled;
1464
+ if (s.breakerAutoKillResetMs !== void 0) cb.autoKillResetMs = s.breakerAutoKillResetMs;
1465
+ decrypted.circuitBreaker = cb;
1466
+ }
1467
+ const actualTarget = resolveActualTarget(
1468
+ persistDeps,
1469
+ decrypted,
1470
+ targetPath
1471
+ );
1472
+ const isProjectTarget = actualTarget === wpaths.inProjectConfig;
1473
+ const toWrite = isProjectTarget ? filterSafeForProject(decrypted) : decrypted;
1474
+ const encrypted = encryptConfigSecrets(toWrite, noOpVault);
1475
+ await fs3.mkdir(path7.dirname(actualTarget), { recursive: true });
1476
+ await atomicWrite(actualTarget, JSON.stringify(encrypted, null, 2), { mode: 384 });
1477
+ const currentConfig = configStore.get();
1478
+ const nextModelRuntime = {
1479
+ ...currentConfig.modelRuntime,
1480
+ ...decrypted.modelRuntime ?? {}
1481
+ };
1482
+ if (s.cacheTtl === "default") {
1483
+ delete nextModelRuntime.cache;
1484
+ }
1485
+ configStore.update({
1486
+ ...s.nextPrediction !== void 0 ? { nextPrediction: s.nextPrediction } : {},
1487
+ ...s.yolo !== void 0 ? { yolo: s.yolo } : {},
1488
+ ...s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || fsAccess !== void 0 ? {
1489
+ features: {
1490
+ ...currentConfig.features,
1491
+ ...decrypted.features ?? {}
1492
+ }
1493
+ } : {},
1494
+ ...s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.contextMode !== void 0 ? {
1495
+ context: {
1496
+ ...currentConfig.context,
1497
+ ...decrypted.context ?? {}
1498
+ }
1499
+ } : {},
1500
+ ...s.maxConcurrent !== void 0 ? { maxConcurrent: s.maxConcurrent } : {},
1501
+ ...s.logLevel !== void 0 ? {
1502
+ log: {
1503
+ ...currentConfig.log,
1504
+ ...decrypted.log ?? {}
1505
+ }
1506
+ } : {},
1507
+ ...s.auditLevel !== void 0 ? {
1508
+ session: {
1509
+ ...currentConfig.session,
1510
+ ...decrypted.session ?? {}
1511
+ }
1512
+ } : {},
1513
+ ...s.indexOnStart !== void 0 ? {
1514
+ indexing: {
1515
+ ...currentConfig.indexing,
1516
+ ...decrypted.indexing ?? {}
1517
+ }
1518
+ } : {},
1519
+ ...s.maxIterations !== void 0 || fsAccess !== void 0 ? {
1520
+ tools: {
1521
+ ...currentConfig.tools,
1522
+ ...decrypted.tools ?? {}
1523
+ }
1524
+ } : {},
1525
+ ...s.debugStream !== void 0 ? { debugStream: s.debugStream } : {},
1526
+ ...s.configScope !== void 0 ? { configScope: s.configScope } : {},
1527
+ autonomy: {
1528
+ ...currentConfig.autonomy,
1529
+ ...decrypted.autonomy ?? {}
1530
+ },
1531
+ ...s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0 ? {
1532
+ modelRuntime: nextModelRuntime
1533
+ } : {},
1534
+ ...s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0 ? {
1535
+ circuitBreaker: {
1536
+ ...currentConfig.circuitBreaker,
1537
+ ...decrypted.circuitBreaker ?? {}
1538
+ }
1539
+ } : {}
1540
+ });
1541
+ }
1542
+ if (s.breakerEnabled !== void 0 || s.breakerAutoKillResetMs !== void 0) {
1543
+ getProcessRegistry().setBreakerConfig({
1544
+ ...s.breakerEnabled !== void 0 ? { enabled: s.breakerEnabled } : {},
1545
+ ...s.breakerAutoKillResetMs !== void 0 ? { autoKillResetMs: s.breakerAutoKillResetMs } : {}
1546
+ });
1547
+ }
1548
+ if (s.fleetChatVerbosity !== void 0) {
1549
+ if (fleetStreamController?.setMode) fleetStreamController.setMode(s.fleetChatVerbosity);
1550
+ }
1551
+ applyLiveSettings?.(s);
1552
+ return null;
1553
+ } catch (err) {
1554
+ const message = err instanceof Error ? err.message : String(err);
1555
+ console.debug(
1556
+ JSON.stringify({
1557
+ level: "error",
1558
+ event: "execution.settings_persist_failed",
1559
+ message,
1560
+ errorName: err instanceof Error ? err.name : void 0,
1561
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1562
+ })
1563
+ );
1564
+ return message;
1565
+ }
1566
+ }
1567
+ return { getSettings, saveSettings };
1568
+ }
1569
+
1570
+ // src/brain-menu/panel-service.ts
1571
+ var PERSONA_CYCLE = ["executor", "skeptic", "auditor"];
1572
+ function compactEntry(entry) {
1573
+ return entry.provider ? `${entry.provider}/${entry.model}` : entry.model;
1574
+ }
1575
+ function createBrainPanelHost(deps) {
1576
+ const { brainRuntime } = deps;
1577
+ const apply = async (patch) => {
1578
+ try {
1579
+ const { persisted } = brainRuntime.apply(patch);
1580
+ const result = await persisted;
1581
+ return result.ok ? null : `Applied live but NOT saved: ${result.error ?? "unknown error"}`;
1582
+ } catch (err) {
1583
+ return err instanceof Error ? err.message : String(err);
1584
+ }
1585
+ };
1586
+ const currentPool = () => brainRuntime.getSnapshot().models.map(compactEntry);
1587
+ const currentVoters = () => brainRuntime.getSnapshot().council.voters.map((v) => ({ ...v }));
1588
+ const applyVoters = (voters) => apply({ council: { voters: voters.length > 0 ? voters : null } });
1589
+ return {
1590
+ getSettings() {
1591
+ const snap = brainRuntime.getSnapshot();
1592
+ return {
1593
+ mode: snap.mode,
1594
+ riskLevel: snap.maxAutoRisk,
1595
+ strategy: snap.strategy,
1596
+ decisionTimeoutMs: snap.decisionTimeoutMs,
1597
+ humanTimeoutMs: snap.humanTimeoutMs,
1598
+ pool: snap.models.map(compactEntry),
1599
+ poolResolved: snap.poolLabels,
1600
+ usingSessionModel: snap.usingSessionModel,
1601
+ councilEnabled: snap.council.enabled,
1602
+ councilMinRisk: snap.council.minRisk,
1603
+ voters: snap.council.voters.map((v) => ({
1604
+ label: compactEntry(v),
1605
+ persona: v.persona,
1606
+ veto: v.veto,
1607
+ weight: v.weight
1608
+ })),
1609
+ councilSeats: snap.councilLabels,
1610
+ // The EFFECTIVE judge, not `council.judge`. The configured one is
1611
+ // usually absent, so the panel used to read "auto" and say nothing
1612
+ // about who actually breaks the panel's ties — including when that is
1613
+ // one of the seats that produced the tie.
1614
+ judgeLabel: snap.judgeLabel,
1615
+ judgeConfigured: snap.council.judge !== void 0,
1616
+ judgeIsVoter: snap.judgeIsVoter,
1617
+ ledgerEnabled: snap.ledger.enabled,
1618
+ autoDenyAfterFailures: snap.ledger.autoDenyAfterFailures,
1619
+ terminalPolicy: snap.terminalPolicy,
1620
+ heuristics: {
1621
+ lowRiskAutoAnswer: snap.heuristics.lowRiskAutoAnswer,
1622
+ blockedResolved: snap.heuristics.blockedResolved,
1623
+ deadlockSkip: snap.heuristics.deadlockSkip,
1624
+ retryExhausted: snap.heuristics.retryExhausted,
1625
+ continuePing: snap.heuristics.continuePing,
1626
+ blockedResolvedMarkers: snap.heuristics.blockedResolvedMarkers
1627
+ },
1628
+ llmMaxTokens: snap.llm.maxTokens,
1629
+ llmRejectUncertain: snap.llm.rejectUncertain,
1630
+ llmMinConfidence: snap.llm.minConfidence,
1631
+ llmDenyIsTerminal: snap.llm.denyIsTerminal,
1632
+ cacheEnabled: snap.cache.enabled,
1633
+ cacheTtlMs: snap.cache.ttlMs,
1634
+ cacheMaxEntries: snap.cache.maxEntries,
1635
+ cacheHits: snap.cache.hits,
1636
+ cacheMisses: snap.cache.misses,
1637
+ cacheSize: snap.cache.size,
1638
+ traceEnabled: snap.trace.enabled,
1639
+ traceContent: snap.trace.content,
1640
+ tracePath: snap.trace.path,
1641
+ circuitState: snap.circuit?.state,
1642
+ circuitFailures: snap.circuit?.consecutiveFailures,
1643
+ ruleCount: snap.rules.length,
1644
+ ruleErrors: [...snap.ruleErrors]
1645
+ };
1646
+ },
1647
+ setMode: (mode) => apply({ mode }),
1648
+ setRisk: (level) => apply({ maxAutoRisk: level }),
1649
+ setStrategy: (strategy) => apply({ strategy }),
1650
+ setDecisionTimeout: (ms) => apply({ decisionTimeoutMs: ms ?? null }),
1651
+ setHumanTimeout: (ms) => apply({ humanTimeoutMs: ms ?? null }),
1652
+ addPoolModel: (providerId, model) => apply({ models: [...currentPool(), `${providerId}/${model}`] }),
1653
+ removePoolModel: (index) => {
1654
+ const next = currentPool().filter((_, i) => i !== index);
1655
+ return apply({ models: next.length > 0 ? next : null });
1656
+ },
1657
+ clearPool: () => apply({ models: null }),
1658
+ setCouncilEnabled: (on) => apply({ council: { enabled: on } }),
1659
+ setCouncilMinRisk: (risk) => apply({ council: { minRisk: risk } }),
1660
+ addVoter: (providerId, model) => {
1661
+ const voters = currentVoters();
1662
+ const persona = PERSONA_CYCLE[voters.length % PERSONA_CYCLE.length];
1663
+ return applyVoters([...voters, { provider: providerId, model, persona }]);
1664
+ },
1665
+ removeVoter: (index) => applyVoters(currentVoters().filter((_, i) => i !== index)),
1666
+ cycleVoterPersona: (index) => {
1667
+ const voters = currentVoters();
1668
+ const voter = voters[index];
1669
+ if (!voter) return Promise.resolve("No such voter.");
1670
+ const at = PERSONA_CYCLE.indexOf(voter.persona);
1671
+ voter.persona = PERSONA_CYCLE[(at + 1) % PERSONA_CYCLE.length];
1672
+ return applyVoters(voters);
1673
+ },
1674
+ toggleVoterVeto: (index) => {
1675
+ const voters = currentVoters();
1676
+ const voter = voters[index];
1677
+ if (!voter) return Promise.resolve("No such voter.");
1678
+ voter.veto = !voter.veto;
1679
+ return applyVoters(voters);
1680
+ },
1681
+ setJudge: (providerId, model) => apply({ council: { judge: `${providerId}/${model}` } }),
1682
+ clearJudge: () => apply({ council: { judge: null } }),
1683
+ setLedgerEnabled: (on) => apply({ ledger: { enabled: on } }),
1684
+ setAutoDeny: (count) => apply({ ledger: { autoDenyAfterFailures: count ?? null } }),
1685
+ setTerminalPolicy: (policy) => apply({ terminalPolicy: policy }),
1686
+ // Every sub-block below is MERGED field-by-field by `mergePatch`, so a
1687
+ // single-key patch leaves the operator's other knobs alone.
1688
+ setHeuristic: (key, on) => apply({ heuristics: { [key]: on } }),
1689
+ setLlmMaxTokens: (tokens) => apply({ llm: { maxTokens: tokens } }),
1690
+ setLlmRejectUncertain: (on) => apply({ llm: { rejectUncertain: on } }),
1691
+ setLlmMinConfidence: (value) => apply({ llm: { minConfidence: value } }),
1692
+ setLlmDenyIsTerminal: (mode) => apply({ llm: { denyIsTerminal: mode } }),
1693
+ setCacheEnabled: (on) => apply({ cache: { enabled: on } }),
1694
+ setCacheTtl: (ms) => apply({ cache: { ttlMs: ms } }),
1695
+ setCacheMaxEntries: (count) => apply({ cache: { maxEntries: count } }),
1696
+ setTraceEnabled: (on) => apply({ trace: { enabled: on } }),
1697
+ setTraceContent: (content) => apply({ trace: { content } })
1698
+ };
1699
+ }
1700
+
1701
+ // src/chimera-review-task.ts
1702
+ import { parseReviewSeverity } from "@wrongstack/core/plugin";
1703
+ function truncateAtCodePointBoundary(text, maxCodeUnits) {
1704
+ if (text.length <= maxCodeUnits) return text;
1705
+ let result = "";
1706
+ for (const ch of text) {
1707
+ if (result.length + ch.length > maxCodeUnits) break;
1708
+ result += ch;
1709
+ }
1710
+ return result;
1711
+ }
1712
+ function isChimeraAllClearReview(text) {
1713
+ const severities = parseReviewSeverity(text);
1714
+ if (severities.critical > 0 || severities.high > 0 || severities.medium > 0) {
1715
+ return false;
1716
+ }
1717
+ const normalized = text.trim().toLowerCase();
1718
+ return normalized.startsWith("## \u{1F982} chimera review \u2014 all clear") || normalized.includes("chimera review: all clear");
1719
+ }
1720
+ function buildChimeraReviewTaskDescription(p) {
1721
+ const lines = [];
1722
+ lines.push(`Review the following ${p.files.length} file(s) changed in this session at ${p.cwd}.`);
1723
+ lines.push("");
1724
+ for (const f of p.files) {
1725
+ lines.push(`## [${f.status.toUpperCase()}] ${f.path}`);
1726
+ if (f.diff) {
1727
+ lines.push("");
1728
+ lines.push("```diff");
1729
+ lines.push(f.diff);
1730
+ lines.push("```");
1731
+ } else if (f.status === "added") {
1732
+ lines.push("");
1733
+ lines.push("(New file \u2014 full content provided)");
1734
+ }
1735
+ lines.push("");
1736
+ }
1737
+ if (p.allChangedFiles && p.allChangedFiles.length > p.files.length) {
1738
+ const reviewedPaths = new Set(p.files.map((f) => f.path));
1739
+ const siblings = p.allChangedFiles.filter((s) => !reviewedPaths.has(s.path)).map((s) => ` ${s.path} (${s.status})`);
1740
+ if (siblings.length > 0) {
1741
+ lines.push("---");
1742
+ lines.push("");
1743
+ lines.push(
1744
+ `**Also changed this session (${siblings.length} files \u2014 for context, NOT in your review scope):**`
1745
+ );
1746
+ lines.push(siblings.slice(0, 30).join("\n"));
1747
+ lines.push("");
1748
+ }
1749
+ }
1750
+ if (p.recentCommits && p.recentCommits.length > 0) {
1751
+ lines.push("---");
1752
+ lines.push("");
1753
+ lines.push("**Recent commits (newest first):**");
1754
+ for (const c of p.recentCommits) lines.push(` ${c}`);
1755
+ lines.push("");
1756
+ }
1757
+ if (p.activeTodos && p.activeTodos.length > 0) {
1758
+ lines.push("---");
1759
+ lines.push("");
1760
+ lines.push(`**Active task items (${p.activeTodos.length}):**`);
1761
+ for (const t of p.activeTodos) {
1762
+ lines.push(` [${t.status}] ${t.content}`);
1763
+ }
1764
+ lines.push("");
1765
+ }
1766
+ if (p.kanbanCard) {
1767
+ lines.push("---");
1768
+ lines.push("");
1769
+ lines.push(`**Kanban card: ${p.kanbanCard.title}**`);
1770
+ if (p.kanbanCard.description) {
1771
+ lines.push(` ${p.kanbanCard.description.slice(0, 500)}`);
1772
+ }
1773
+ if (p.kanbanCard.successCriteria && p.kanbanCard.successCriteria.length > 0) {
1774
+ lines.push(" **Success criteria:**");
1775
+ for (const sc of p.kanbanCard.successCriteria) lines.push(` - ${sc}`);
1776
+ }
1777
+ lines.push("");
1778
+ }
1779
+ if (p.fileProvenance && p.fileProvenance.length > 0) {
1780
+ lines.push("---");
1781
+ lines.push("");
1782
+ lines.push("**File provenance (Chronicle):**");
1783
+ for (const fp of p.fileProvenance) {
1784
+ const parts = [];
1785
+ if (fp.agentId) parts.push(`agent: ${fp.agentId}`);
1786
+ if (fp.taskId) parts.push(`task: ${fp.taskId}`);
1787
+ if (fp.eventType) parts.push(fp.eventType);
1788
+ if (fp.observedAt) parts.push(fp.observedAt);
1789
+ lines.push(` ${fp.path} \u2014 ${parts.join(", ")}`);
1790
+ }
1791
+ lines.push("");
1792
+ }
1793
+ lines.push("---");
1794
+ lines.push("");
1795
+ lines.push("Read each file using the read tool. For modified files, focus on the");
1796
+ lines.push("diff above \u2014 do not re-review unchanged pre-existing code.");
1797
+ lines.push("Check for bugs, type issues, security problems, and produce a");
1798
+ lines.push("structured review report.");
1799
+ return lines.join("\n");
1800
+ }
1801
+ function buildChimeraCascadeTaskDescription(agentKind, p) {
1802
+ const fileList = p.bundle.files.map((f) => `- ${f.path}`).join("\n");
1803
+ const reportSlice = truncateAtCodePointBoundary(p.reviewText, 12e3);
1804
+ const severityLine = `Critical: ${p.severities.critical}, High: ${p.severities.high}, Medium: ${p.severities.medium}`;
1805
+ if (agentKind === "security-scanner") {
1806
+ return [
1807
+ `You are a security cascade agent. A Chimera code review flagged security-relevant findings.`,
1808
+ ``,
1809
+ `Repository: ${p.bundle.cwd}`,
1810
+ `Severity summary: ${severityLine}`,
1811
+ ``,
1812
+ `--- Review report ---`,
1813
+ reportSlice,
1814
+ ``,
1815
+ `--- Changed files ---`,
1816
+ fileList,
1817
+ ``,
1818
+ `Investigate the security findings above. Read the flagged files, confirm or refute`,
1819
+ `each finding, and **apply fixes** for confirmed vulnerabilities using the edit tool.`,
1820
+ `Use severity (Critical/High/Medium), file:line citations, and remediation steps.`,
1821
+ `After fixing, run the project's typecheck and linter to verify. If a finding is a`,
1822
+ `false positive, say so and do not modify the file.`
1823
+ ].join("\n");
1824
+ }
1825
+ return [
1826
+ `You are a bug-hunter cascade agent. A Chimera code review flagged correctness defects.`,
1827
+ ``,
1828
+ `Repository: ${p.bundle.cwd}`,
1829
+ `Severity summary: ${severityLine}`,
1830
+ ``,
1831
+ `--- Review report ---`,
1832
+ reportSlice,
1833
+ ``,
1834
+ `--- Changed files ---`,
1835
+ fileList,
1836
+ ``,
1837
+ `Hunt for the bugs flagged above. Read the affected files, trace each finding to its`,
1838
+ `root cause, and **apply minimal fixes** for confirmed bugs using the edit tool.`,
1839
+ `Use severity (Critical/High/Medium), file:line citations. After fixing, run the`,
1840
+ `project's typecheck and linter to verify. If a finding is a false positive, say so`,
1841
+ `and do not modify the file.`
1842
+ ].join("\n");
1843
+ }
1844
+
1845
+ // src/chimera-reviewer-policy.ts
1846
+ import {
1847
+ buildReviewerModelPool,
1848
+ selectRoundRobinReviewerAssignment
1849
+ } from "@wrongstack/core/plugin";
1850
+ function resolveReviewerFallbackModels(reviewFallbackModels, sessionRef) {
1851
+ const base = reviewFallbackModels && reviewFallbackModels.length > 0 ? [...reviewFallbackModels] : [];
1852
+ if (sessionRef && !base.includes(sessionRef)) {
1853
+ base.push(sessionRef);
1854
+ }
1855
+ return base;
1856
+ }
1857
+ var reviewerRoundRobinCursor = 0;
1858
+ function __resetReviewerRoundRobinCursor(value = 0) {
1859
+ reviewerRoundRobinCursor = value;
1860
+ }
1861
+ function assignReviewerModelsRoundRobin(provider, model, fallbackModels) {
1862
+ const pool = buildReviewerModelPool(provider, model, fallbackModels);
1863
+ if (pool.length <= 1) {
1864
+ return {
1865
+ provider,
1866
+ model,
1867
+ fallbackModels: [...fallbackModels]
1868
+ };
1869
+ }
1870
+ const assignment = selectRoundRobinReviewerAssignment(
1871
+ pool,
1872
+ reviewerRoundRobinCursor,
1873
+ provider,
1874
+ model
1875
+ );
1876
+ reviewerRoundRobinCursor = assignment.nextCursor;
1877
+ return {
1878
+ provider: assignment.provider,
1879
+ model: assignment.model,
1880
+ fallbackModels: assignment.fallbackModels
1881
+ };
1882
+ }
1883
+ var CHIMERA_REVIEW_READ_ONLY_TOOLS = Object.freeze([
1884
+ "read",
1885
+ "grep",
1886
+ "glob",
1887
+ "tree",
1888
+ "codebase-search",
1889
+ "codebase-stats"
1890
+ ]);
1891
+ function applyChimeraReviewerReadOnlyPolicy(config) {
1892
+ return {
1893
+ ...config,
1894
+ // A security boundary, not an additive default: discard any inherited
1895
+ // mutation tools/capabilities so Chimera reviewers cannot repair files.
1896
+ tools: [...CHIMERA_REVIEW_READ_ONLY_TOOLS],
1897
+ allowedCapabilities: ["fs.read"],
1898
+ worktree: "off"
1899
+ };
1900
+ }
1901
+
1902
+ // src/execution-cleanup.ts
1903
+ async function finalizeExecutionCleanup(input) {
1904
+ const {
1905
+ offStorageObservability,
1906
+ fleetStatusLine,
1907
+ onCoordinatorStop,
1908
+ stats,
1909
+ renderer,
1910
+ detachTodosCheckpoint,
1911
+ mcpRegistry,
1912
+ agent,
1913
+ session,
1914
+ tokenCounter,
1915
+ events,
1916
+ getPendingChimeraWork,
1917
+ director,
1918
+ reader
1919
+ } = input;
1920
+ offStorageObservability();
1921
+ fleetStatusLine?.stop();
1922
+ onCoordinatorStop?.();
1923
+ try {
1924
+ stats.render(renderer);
1925
+ } catch (_err) {
1926
+ }
1927
+ await Promise.resolve(detachTodosCheckpoint?.()).catch(() => void 0);
1928
+ await mcpRegistry.stopAll().catch((err) => {
1929
+ console.warn(
1930
+ JSON.stringify({
1931
+ level: "warn",
1932
+ event: "shutdown.mcp_stop_failed",
1933
+ message: `MCP registry stopAll failed: ${err instanceof Error ? err.message : String(err)}`,
1934
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1935
+ })
1936
+ );
1937
+ });
1938
+ const activeSession = agent.ctx.session ?? session;
1939
+ const pending = activeSession.pendingToolUses;
1940
+ await activeSession.append({
1941
+ type: "session_end",
1942
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1943
+ usage: tokenCounter.total(),
1944
+ pendingToolUses: pending.length > 0 ? pending : void 0
1945
+ }).catch((err) => {
1946
+ console.warn(
1947
+ JSON.stringify({
1948
+ level: "warn",
1949
+ event: "shutdown.session_end_append_failed",
1950
+ message: `session_end append failed: ${err instanceof Error ? err.message : String(err)}`,
1951
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1952
+ })
1953
+ );
1954
+ });
1955
+ events.emit("session.ended", {
1956
+ id: activeSession.id,
1957
+ sessionId: activeSession.id,
1958
+ usage: tokenCounter.total()
1959
+ });
1960
+ if (getPendingChimeraWork) {
1961
+ const chimeraWork = getPendingChimeraWork();
1962
+ if (chimeraWork) {
1963
+ await chimeraWork.catch((err) => {
1964
+ console.warn(
1965
+ JSON.stringify({
1966
+ level: "warn",
1967
+ event: "shutdown.chimera_work_failed",
1968
+ message: `Pending chimera work failed: ${err instanceof Error ? err.message : String(err)}`,
1969
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1970
+ })
1971
+ );
1972
+ });
1973
+ }
1974
+ }
1975
+ if (director) {
1976
+ try {
1977
+ await director.terminateAll();
1978
+ } catch (termErr) {
1979
+ console.warn(
1980
+ JSON.stringify({
1981
+ level: "warn",
1982
+ event: "shutdown.director_terminate_all_failed",
1983
+ message: termErr instanceof Error ? termErr.message : String(termErr),
1984
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1985
+ })
1986
+ );
1987
+ }
1988
+ }
1989
+ await activeSession.close().catch((err) => {
1990
+ console.warn(
1991
+ JSON.stringify({
1992
+ level: "warn",
1993
+ event: "shutdown.session_close_failed",
1994
+ message: `Session close failed: ${err instanceof Error ? err.message : String(err)}`,
1995
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1996
+ })
1997
+ );
1998
+ });
1999
+ await reader.close().catch((err) => {
2000
+ console.warn(
2001
+ JSON.stringify({
2002
+ level: "warn",
2003
+ event: "shutdown.reader_close_failed",
2004
+ message: `Input reader close failed: ${err instanceof Error ? err.message : String(err)}`,
2005
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2006
+ })
2007
+ );
2008
+ });
2009
+ }
2010
+
2011
+ // src/execution-kanban-dispatch.ts
2012
+ import { randomUUID } from "node:crypto";
2013
+ import { WIDE_SUBAGENT_CAPABILITIES } from "@wrongstack/core/security";
2014
+
2015
+ // src/kanban-dispatch-route.ts
2016
+ import {
2017
+ fallbackProfileChain,
2018
+ parseModelRef
2019
+ } from "@wrongstack/core/agent";
2020
+ function resolveKanbanDispatchRoute(config, spawnOpts) {
2021
+ let provider = spawnOpts?.provider;
2022
+ let model = spawnOpts?.model;
2023
+ let fallbackModels = spawnOpts?.fallbackModels;
2024
+ if (spawnOpts?.fallbackProfile) {
2025
+ const chain = fallbackProfileChain(config, spawnOpts.fallbackProfile);
2026
+ const primary = chain[0] ? parseModelRef(chain[0]) : void 0;
2027
+ if (primary?.model) {
2028
+ provider = primary.provider ?? config.provider;
2029
+ model = primary.model;
2030
+ fallbackModels = spawnOpts.fallbackModels ?? chain.slice(1);
2031
+ }
2032
+ }
2033
+ return { provider, model, fallbackModels };
2034
+ }
2035
+
2036
+ // src/execution-kanban-dispatch.ts
2037
+ function createKanbanDispatchHandler({
2038
+ config,
2039
+ events,
2040
+ skillLoader,
2041
+ sddSubagentFactory
2042
+ }) {
2043
+ if (!sddSubagentFactory) return {};
2044
+ const onKanbanDispatch = async (description, spawnOpts) => {
2045
+ const subagentId = `kanban-${randomUUID().slice(0, 8)}`;
2046
+ const taskId = randomUUID();
2047
+ const name = spawnOpts?.name ?? "kanban-agent";
2048
+ const {
2049
+ provider: resolvedProvider,
2050
+ model: resolvedModel,
2051
+ fallbackModels: resolvedFallbackModels
2052
+ } = resolveKanbanDispatchRoute(config, spawnOpts);
2053
+ let agentDescription = description;
2054
+ if (spawnOpts?.skills?.length && skillLoader) {
2055
+ const loaded = await Promise.all(
2056
+ spawnOpts.skills.map(async (skillName) => {
2057
+ const manifest = await skillLoader.find(skillName);
2058
+ if (!manifest) throw new Error(`Kanban skill not found: ${skillName}`);
2059
+ const body = await skillLoader.readBody(skillName);
2060
+ return `## Required skill: ${skillName}
2061
+
2062
+ ${body}`;
2063
+ })
2064
+ );
2065
+ agentDescription = `${description}
2066
+
2067
+ # Required agentic skill instructions
2068
+
2069
+ ${loaded.join("\n\n")}`;
2070
+ }
2071
+ void (async () => {
2072
+ const built = await sddSubagentFactory({
2073
+ id: subagentId,
2074
+ name,
2075
+ role: "kanban-agent",
2076
+ prompt: agentDescription,
2077
+ allowedCapabilities: spawnOpts?.allowedCapabilities ?? WIDE_SUBAGENT_CAPABILITIES,
2078
+ ...resolvedProvider ? { provider: resolvedProvider } : {},
2079
+ ...resolvedModel ? { model: resolvedModel } : {},
2080
+ ...resolvedFallbackModels ? { fallbackModels: resolvedFallbackModels } : {},
2081
+ ...spawnOpts?.tools ? { tools: spawnOpts.tools } : {}
2082
+ });
2083
+ try {
2084
+ const result = await built.agent.run(agentDescription);
2085
+ await spawnOpts?.onDone?.({
2086
+ status: result.status === "done" ? "completed" : "failed",
2087
+ result: result.finalText,
2088
+ ..."error" in result && result.error?.message ? { error: result.error.message } : {}
2089
+ });
2090
+ } catch (err) {
2091
+ await spawnOpts?.onDone?.({
2092
+ status: "failed",
2093
+ error: err instanceof Error ? err.message : String(err)
2094
+ });
2095
+ throw err;
2096
+ } finally {
2097
+ await built.dispose?.();
2098
+ }
2099
+ })().catch((err) => {
2100
+ events.emit("error", {
2101
+ err: err instanceof Error ? err : new Error(String(err)),
2102
+ phase: "kanban.dispatch"
2103
+ });
2104
+ });
2105
+ const tags = [];
2106
+ if (resolvedProvider) tags.push(resolvedProvider);
2107
+ if (resolvedModel) tags.push(resolvedModel);
2108
+ if (resolvedFallbackModels?.length) {
2109
+ tags.push(`fallback=${resolvedFallbackModels.join(",")}`);
2110
+ }
2111
+ if (spawnOpts?.fallbackProfile) tags.push(`profile=${spawnOpts.fallbackProfile}`);
2112
+ if (spawnOpts?.skills?.length) tags.push(`skills=${spawnOpts.skills.join(",")}`);
2113
+ if (spawnOpts?.name) tags.push(`"${spawnOpts.name}"`);
2114
+ const tag = tags.length > 0 ? ` (${tags.join(" / ")})` : "";
2115
+ return `Spawned subagent ${subagentId}${tag} for task ${taskId}.`;
2116
+ };
2117
+ return { onKanbanDispatch };
2118
+ }
2119
+
2120
+ // src/execution-repl-fleet-callbacks.ts
2121
+ function createReplFleetCallbacks({
2122
+ director,
2123
+ getDirector
2124
+ }) {
2125
+ const resolveDirector = () => getDirector?.() ?? director;
2126
+ return {
2127
+ onInterruptFleet: () => {
2128
+ const dir = resolveDirector();
2129
+ if (!dir) return 0;
2130
+ let killed = 0;
2131
+ for (const sa of dir.status().subagents) {
2132
+ if (sa.status === "running" || sa.status === "idle") {
2133
+ try {
2134
+ void dir.remove(sa.id);
2135
+ killed++;
2136
+ } catch {
2137
+ }
2138
+ }
2139
+ }
2140
+ return killed;
2141
+ },
2142
+ onAgentIterationComplete: (tokens) => {
2143
+ const dir = resolveDirector();
2144
+ dir?.setLeaderContextPressure(tokens);
2145
+ }
2146
+ };
2147
+ }
2148
+
2149
+ // src/fleet-statusline.ts
2150
+ import { color as color5 } from "@wrongstack/core/utils";
2151
+ import { onResize } from "@wrongstack/core/utils";
2152
+ function fmtElapsed(ms) {
2153
+ const s = Math.floor(ms / 1e3);
2154
+ if (s < 60) return `${s}s`;
2155
+ const m = Math.floor(s / 60);
2156
+ const rem = s % 60;
2157
+ if (m < 60) return `${m}m${rem.toString().padStart(2, "0")}s`;
2158
+ const h = Math.floor(m / 60);
2159
+ return `${h}h${(m % 60).toString().padStart(2, "0")}m`;
2160
+ }
2161
+ function renderFleetLine(states, now, columns, version) {
2162
+ const all = [...states.values()];
2163
+ if (all.length === 0) return "";
2164
+ const running = all.filter((a) => a.status === "running");
2165
+ const done = all.filter((a) => a.status === "done").length;
2166
+ const failed = all.filter((a) => a.status === "failed").length;
2167
+ const versionChip = version ? `${color5.bold("WS")}${color5.dim(` v${version}`)} ${color5.dim("\u2502")} ` : "";
2168
+ const counts = versionChip + `${color5.cyan("\u27F3 fleet")} ${color5.yellow(`\u25B6${running.length}`)} ${color5.green(`\u2713${done}`)}` + (failed > 0 ? ` ${color5.red(`\u2717${failed}`)}` : "");
2169
+ const shown = running.sort((a, b) => a.startedAt - b.startedAt).slice(0, 4).map((a) => {
2170
+ const elapsed = fmtElapsed(Math.max(0, now - a.startedAt));
2171
+ const tool = a.lastTool ? ` ${color5.dim(a.lastTool)}` : "";
2172
+ const ext = a.extensions && a.extensions > 0 ? ` ${color5.yellow(`\u26A1${a.extensions}`)}` : "";
2173
+ return `${color5.bold(a.name)} ${color5.yellow("\u25B6")} ${color5.dim(elapsed)} ${color5.dim(`L${a.iterations}`)} ${color5.dim(`${a.toolCalls}t`)}${tool}${ext}`;
2174
+ });
2175
+ let line = shown.length > 0 ? `${counts} ${color5.dim("\u2502")} ${shown.join(color5.dim(" \xB7 "))}` : counts;
2176
+ const max = Math.max(20, columns - 1);
2177
+ const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
2178
+ if (visible.length > max) {
2179
+ let count = 0;
2180
+ const out = [];
2181
+ let i = 0;
2182
+ while (i < line.length && count < max - 1) {
2183
+ if (line[i] === "\x1B") {
2184
+ const end = line.indexOf("m", i);
2185
+ if (end !== -1) {
2186
+ out.push(line.slice(i, end + 1));
2187
+ i = end + 1;
2188
+ continue;
2189
+ }
2190
+ }
2191
+ if (line[i] !== void 0) out.push(line[i]);
2192
+ count++;
2193
+ i++;
2194
+ }
2195
+ line = out.join("") + "\u2026";
2196
+ }
2197
+ return line;
2198
+ }
2199
+ var FleetStatusLine = class {
2200
+ events;
2201
+ out;
2202
+ throttleMs;
2203
+ now;
2204
+ version;
2205
+ states = /* @__PURE__ */ new Map();
2206
+ unsubs = [];
2207
+ active = false;
2208
+ rows = 0;
2209
+ repaintTimer = null;
2210
+ tickTimer = null;
2211
+ resizeCleanup = null;
2212
+ lastPaint = 0;
2213
+ onResize = () => {
2214
+ if (this.active) {
2215
+ this.rows = this.out.rows ?? 24;
2216
+ this.out.write(`\x1B[1;${this.rows - 1}r`);
2217
+ this.paint(true);
2218
+ }
2219
+ };
2220
+ constructor(opts) {
2221
+ this.events = opts.events;
2222
+ this.out = opts.out ?? process.stdout;
2223
+ this.throttleMs = opts.throttleMs ?? 150;
2224
+ this.now = opts.now ?? Date.now;
2225
+ this.version = opts.version;
2226
+ }
2227
+ /** Subscribe to host fleet events. No terminal output until a subagent spawns. */
2228
+ start() {
2229
+ const ensure = (id, name) => {
2230
+ let s = this.states.get(id);
2231
+ if (!s) {
2232
+ s = {
2233
+ id,
2234
+ name: name ?? id,
2235
+ status: "running",
2236
+ iterations: 0,
2237
+ toolCalls: 0,
2238
+ startedAt: this.now()
2239
+ };
2240
+ this.states.set(id, s);
2241
+ } else if (name && s.name === s.id) {
2242
+ s.name = name;
2243
+ }
2244
+ return s;
2245
+ };
2246
+ this.unsubs.push(
2247
+ this.events.on("subagent.spawned", (e) => {
2248
+ ensure(e.subagentId, e.name);
2249
+ this.activate();
2250
+ this.schedulePaint();
2251
+ }),
2252
+ this.events.on("subagent.task_started", (e) => {
2253
+ const s = ensure(e.subagentId);
2254
+ s.status = "running";
2255
+ this.activate();
2256
+ this.schedulePaint();
2257
+ }),
2258
+ this.events.on("subagent.tool_executed", (e) => {
2259
+ const s = ensure(e.subagentId);
2260
+ s.toolCalls++;
2261
+ s.lastTool = e.name;
2262
+ this.activate();
2263
+ this.schedulePaint();
2264
+ }),
2265
+ this.events.on("subagent.iteration_summary", (e) => {
2266
+ const s = ensure(e.subagentId);
2267
+ s.iterations = e.iteration;
2268
+ if (typeof e.toolCalls === "number") s.toolCalls = e.toolCalls;
2269
+ if (e.currentTool) s.lastTool = e.currentTool;
2270
+ this.schedulePaint();
2271
+ }),
2272
+ this.events.on("subagent.budget_extended", (e) => {
2273
+ const s = ensure(e.subagentId);
2274
+ s.extensions = e.totalExtensions;
2275
+ this.activate();
2276
+ this.schedulePaint();
2277
+ }),
2278
+ this.events.on("subagent.task_completed", (e) => {
2279
+ const s = ensure(e.subagentId);
2280
+ s.status = e.status === "success" ? "done" : "failed";
2281
+ s.iterations = e.iterations;
2282
+ s.toolCalls = e.toolCalls;
2283
+ s.endedAt = this.now();
2284
+ this.schedulePaint();
2285
+ if (![...this.states.values()].some((a) => a.status === "running")) {
2286
+ setTimeout(() => {
2287
+ if (![...this.states.values()].some((a) => a.status === "running")) {
2288
+ this.deactivate();
2289
+ }
2290
+ }, 800);
2291
+ }
2292
+ })
2293
+ );
2294
+ }
2295
+ /** Unsubscribe and restore the terminal. Idempotent. */
2296
+ stop() {
2297
+ for (const u of this.unsubs.splice(0)) u();
2298
+ this.deactivate();
2299
+ }
2300
+ isTty() {
2301
+ return !!this.out.isTTY;
2302
+ }
2303
+ activate() {
2304
+ if (this.active || !this.isTty()) return;
2305
+ this.active = true;
2306
+ this.rows = this.out.rows ?? 24;
2307
+ this.out.write("\n");
2308
+ this.out.write(`\x1B[1;${this.rows - 1}r`);
2309
+ this.out.write(`\x1B[${this.rows - 1};1H`);
2310
+ this.resizeCleanup = onResize(() => this.onResize(), this.out);
2311
+ this.tickTimer = setInterval(() => this.paint(true), 1e3);
2312
+ if (this.tickTimer.unref) this.tickTimer.unref();
2313
+ this.paint(true);
2314
+ }
2315
+ deactivate() {
2316
+ if (!this.active) return;
2317
+ this.active = false;
2318
+ if (this.tickTimer) {
2319
+ clearInterval(this.tickTimer);
2320
+ this.tickTimer = null;
2321
+ }
2322
+ if (this.repaintTimer) {
2323
+ clearTimeout(this.repaintTimer);
2324
+ this.repaintTimer = null;
2325
+ }
2326
+ this.resizeCleanup?.();
2327
+ this.resizeCleanup = null;
2328
+ if (this.isTty()) {
2329
+ this.out.write("\x1B7");
2330
+ this.out.write(`\x1B[${this.rows};1H`);
2331
+ this.out.write("\x1B[2K");
2332
+ this.out.write("\x1B8");
2333
+ this.out.write("\x1B[r");
2334
+ }
2335
+ }
2336
+ schedulePaint() {
2337
+ if (!this.active) return;
2338
+ const since = this.now() - this.lastPaint;
2339
+ if (since >= this.throttleMs) {
2340
+ this.paint(false);
2341
+ return;
2342
+ }
2343
+ if (this.repaintTimer) return;
2344
+ this.repaintTimer = setTimeout(() => {
2345
+ this.repaintTimer = null;
2346
+ this.paint(false);
2347
+ }, this.throttleMs - since);
2348
+ if (this.repaintTimer.unref) this.repaintTimer.unref();
2349
+ }
2350
+ paint(force) {
2351
+ if (!this.active || !this.isTty()) return;
2352
+ if (!force) this.lastPaint = this.now();
2353
+ else this.lastPaint = this.now();
2354
+ const line = renderFleetLine(this.states, this.now(), this.out.columns ?? 80, this.version);
2355
+ this.out.write("\x1B7");
2356
+ this.out.write(`\x1B[${this.rows};1H`);
2357
+ this.out.write("\x1B[2K");
2358
+ this.out.write(line);
2359
+ this.out.write("\x1B8");
2360
+ }
2361
+ };
2362
+
2363
+ // src/execution-chimera-ask.ts
2364
+ import { createHash } from "node:crypto";
2365
+ async function waitForChimeraAskApproval(opts) {
2366
+ const rawPoll = opts.meta["chimeraPollIntervalMs"] ?? 2e3;
2367
+ const pollIntervalMs = Number.isFinite(rawPoll) ? Math.max(0, rawPoll) : 2e3;
2368
+ const abortSignal = opts.meta["chimeraAbortSignal"];
2369
+ const askStartTime = Date.now();
2370
+ let leaderApproved = false;
2371
+ let repliesFound = false;
2372
+ let nonLeaderReplyCount = 0;
2373
+ let pollDone = false;
2374
+ const pollNext = () => {
2375
+ if (pollDone) return Promise.resolve();
2376
+ const elapsed = Date.now() - askStartTime;
2377
+ const remaining = opts.askTimeoutMs - elapsed;
2378
+ if (remaining <= 0 || abortSignal?.aborted) {
2379
+ pollDone = true;
2380
+ return Promise.resolve();
2381
+ }
2382
+ return new Promise((r) => setTimeout(r, Math.min(pollIntervalMs, remaining))).then(
2383
+ () => pollForReply()
2384
+ );
2385
+ };
2386
+ const pollForReply = async () => {
2387
+ if (pollDone) return;
2388
+ const replies = await opts.mailbox.query({ replyTo: opts.messageId, limit: 5 });
2389
+ if (replies.length > 0) {
2390
+ repliesFound = true;
2391
+ const firstReply = replies[0];
2392
+ if (!firstReply) return pollNext();
2393
+ const replyFromBase = (firstReply.from ?? "").split("@")[0];
2394
+ if (replyFromBase !== "leader" || firstReply.audience === "leaders") {
2395
+ nonLeaderReplyCount++;
2396
+ if (nonLeaderReplyCount >= 5) {
2397
+ console.warn(
2398
+ JSON.stringify({
2399
+ level: "warn",
2400
+ event: "execution.chimera_ask_non_leader_threshold",
2401
+ message: `${nonLeaderReplyCount} non-leader replies seen on chimera ask \u2014 stopping poll`,
2402
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2403
+ })
2404
+ );
2405
+ pollDone = true;
2406
+ return;
2407
+ }
2408
+ console.warn(
2409
+ JSON.stringify({
2410
+ level: nonLeaderReplyCount <= 1 ? "warn" : "debug",
2411
+ event: "execution.chimera_ask_non_leader_reply",
2412
+ message: `Ignoring non-leader reply #${nonLeaderReplyCount} from ${firstReply.from}`,
2413
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2414
+ })
2415
+ );
2416
+ return pollNext();
2417
+ }
2418
+ await opts.mailbox.ack({
2419
+ messageId: firstReply.id,
2420
+ readerId: "chimera-review",
2421
+ read: true,
2422
+ completed: true
2423
+ }).catch((_ackErr) => {
2424
+ console.warn(
2425
+ JSON.stringify({
2426
+ level: "warn",
2427
+ event: "execution.chimera_ask_ack_failed",
2428
+ message: _ackErr instanceof Error ? _ackErr.message : String(_ackErr),
2429
+ replyMessageId: firstReply.id,
2430
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2431
+ })
2432
+ );
2433
+ });
2434
+ const body = firstReply.body.toLowerCase();
2435
+ if (body.trim().length === 0) {
2436
+ pollDone = true;
2437
+ return;
2438
+ }
2439
+ leaderApproved = /^\s*(?:y(?:es)?|sure|ok(?:ay)?|go ahead|proceed|approve|fix it|please do|do it|yep|yeah)\b/.test(
2440
+ body
2441
+ ) || /^\s*👍/.test(body);
2442
+ if (leaderApproved) {
2443
+ pollDone = true;
2444
+ return;
2445
+ }
2446
+ if (/^\s*(no|don'?t|stop|skip|ignore|cancel|reject|decline)\b/.test(body)) {
2447
+ pollDone = true;
2448
+ return;
2449
+ }
2450
+ leaderApproved = /\b(?:y(?:es)?|sure|approve|proceed|go ahead)\b/i.test(body);
2451
+ if (leaderApproved) {
2452
+ pollDone = true;
2453
+ return;
2454
+ }
2455
+ const hash = createHash("sha256").update(body).digest("hex").slice(0, 12);
2456
+ console.warn(
2457
+ JSON.stringify({
2458
+ level: "debug",
2459
+ event: "execution.chimera_ask_reply_unmatched",
2460
+ replyBodyHash: hash,
2461
+ replyBodyLength: body.length,
2462
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2463
+ })
2464
+ );
2465
+ }
2466
+ return pollNext();
2467
+ };
2468
+ await pollForReply();
2469
+ if (!leaderApproved) {
2470
+ await recordChimeraAskFallback(opts.session, opts.askTimeoutMs, repliesFound);
2471
+ }
2472
+ return leaderApproved;
2473
+ }
2474
+ async function recordChimeraAskFallback(session, askTimeoutMs, repliesFound) {
2475
+ console.warn(
2476
+ JSON.stringify({
2477
+ level: "warn",
2478
+ event: repliesFound ? "execution.chimera_ask_rejected" : "execution.chimera_ask_timeout",
2479
+ message: repliesFound ? "Leader replied to ask but did not approve" : "Ask timeout expired, falling back to off mode (no auto-fix)",
2480
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2481
+ })
2482
+ );
2483
+ await session.append({
2484
+ type: "llm_response",
2485
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2486
+ content: [
2487
+ {
2488
+ type: "text",
2489
+ text: `\u{1F982} Chimera auto-fix request sent to leader \u2014 no approval received within ${askTimeoutMs / 1e3}s timeout. Falling back to manual review mode. Set autoFix via \`/chimera autoFix auto\` to auto-approve future reviews.`
2490
+ }
2491
+ ],
2492
+ stopReason: "end_turn",
2493
+ usage: { input: 0, output: 0 }
2494
+ }).catch((_appendErr) => {
2495
+ console.warn(
2496
+ JSON.stringify({
2497
+ level: "warn",
2498
+ event: "execution.chimera_timeout_append_failed",
2499
+ message: _appendErr instanceof Error ? _appendErr.message : String(_appendErr),
2500
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2501
+ })
2502
+ );
2503
+ });
2504
+ }
2505
+
2506
+ // src/execution-chimera-cascade.ts
2507
+ import { randomUUID as randomUUID2 } from "node:crypto";
2508
+ import * as fsp from "node:fs/promises";
2509
+ import * as path8 from "node:path";
2510
+ import { emitReviewIfChanged } from "@wrongstack/core/plugin";
2511
+ function installChimeraCascadeHandler({
2512
+ events,
2513
+ director,
2514
+ session,
2515
+ getPendingWork,
2516
+ setPendingWork
2517
+ }) {
2518
+ events.onPattern("chimera.cascade_needed", (_event, payload) => {
2519
+ const p = payload;
2520
+ const dir = director;
2521
+ if (!dir) return;
2522
+ if (p.agents.length === 0) return;
2523
+ const previousWork = getPendingWork();
2524
+ const pendingWork = (async () => {
2525
+ try {
2526
+ await previousWork;
2527
+ } catch {
2528
+ }
2529
+ for (const agentKind of p.agents) {
2530
+ try {
2531
+ const taskDesc = buildChimeraCascadeTaskDescription(agentKind, p);
2532
+ const role = agentKind === "security-scanner" ? "security-scanner" : "bug-hunter";
2533
+ const cfg = {
2534
+ name: `chimera-cascade-${agentKind}`,
2535
+ role,
2536
+ maxIterations: 40,
2537
+ maxToolCalls: 200,
2538
+ timeoutMs: 6e5
2539
+ };
2540
+ const subagentId = await dir.spawn(cfg);
2541
+ try {
2542
+ const taskId = randomUUID2();
2543
+ await dir.assign({ id: taskId, description: taskDesc, subagentId });
2544
+ const results = await dir.awaitTasks([taskId]);
2545
+ const result = results[0];
2546
+ if (result?.status === "success") {
2547
+ const resultText = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
2548
+ await session.append({
2549
+ type: "llm_response",
2550
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2551
+ content: [
2552
+ {
2553
+ type: "text",
2554
+ text: `\u{1F982} Chimera cascade (${agentKind}) \u2014 ${resultText}`
2555
+ }
2556
+ ],
2557
+ stopReason: "end_turn",
2558
+ usage: { input: 0, output: 0 }
2559
+ });
2560
+ } else {
2561
+ await session.append({
2562
+ type: "error",
2563
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2564
+ message: `\u{1F982} Chimera cascade (${agentKind}) ${result?.status ?? "unknown"}: ${result?.error?.message ?? "no result"}`,
2565
+ phase: "agent"
2566
+ });
2567
+ }
2568
+ } finally {
2569
+ try {
2570
+ await dir.terminate(subagentId);
2571
+ } catch {
2572
+ }
2573
+ }
2574
+ } catch (err) {
2575
+ await session.append({
2576
+ type: "error",
2577
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2578
+ message: `\u{1F982} Chimera cascade (${agentKind}) failed: ${err instanceof Error ? err.message : String(err)}`,
2579
+ phase: "agent"
2580
+ });
2581
+ }
2582
+ }
2583
+ await maybeReReviewCascade({ events, session, bundle: p.bundle });
2584
+ })();
2585
+ setPendingWork(pendingWork);
2586
+ });
2587
+ }
2588
+ async function maybeReReviewCascade({
2589
+ events,
2590
+ session,
2591
+ bundle
2592
+ }) {
2593
+ const maxDepth = bundle.maxCascadeDepth ?? 0;
2594
+ const currentDepth = bundle.cascadeDepth ?? 0;
2595
+ if (maxDepth > 0 && currentDepth < maxDepth) {
2596
+ try {
2597
+ const reReadFiles = [];
2598
+ for (const f of bundle.files) {
2599
+ try {
2600
+ const absPath = path8.join(bundle.cwd, f.path);
2601
+ const content = await fsp.readFile(absPath, "utf8");
2602
+ reReadFiles.push({ path: f.path, status: "modified", content });
2603
+ } catch {
2604
+ }
2605
+ }
2606
+ if (reReadFiles.length > 0) {
2607
+ const reReviewBundle = {
2608
+ ...bundle,
2609
+ files: reReadFiles,
2610
+ cascadeDepth: currentDepth + 1
2611
+ };
2612
+ await session.append({
2613
+ type: "llm_response",
2614
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2615
+ content: [
2616
+ {
2617
+ type: "text",
2618
+ text: `\u{1F982} Chimera cascade re-review (depth ${currentDepth + 1}/${maxDepth}) \u2014 re-reviewing ${reReadFiles.length} file(s) after fixes`
2619
+ }
2620
+ ],
2621
+ stopReason: "end_turn",
2622
+ usage: { input: 0, output: 0 }
2623
+ });
2624
+ const emitted = emitReviewIfChanged(
2625
+ { events, emitCustom: events.emitCustom.bind(events) },
2626
+ reReviewBundle
2627
+ );
2628
+ if (!emitted) {
2629
+ await session.append({
2630
+ type: "llm_response",
2631
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2632
+ content: [
2633
+ {
2634
+ type: "text",
2635
+ text: `\u{1F982} Chimera cascade re-review skipped \u2014 content unchanged since previous review (depth ${currentDepth + 1}/${maxDepth})`
2636
+ }
2637
+ ],
2638
+ stopReason: "end_turn",
2639
+ usage: { input: 0, output: 0 }
2640
+ });
2641
+ }
2642
+ }
2643
+ } catch (err) {
2644
+ await session.append({
2645
+ type: "error",
2646
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2647
+ message: `\u{1F982} Chimera cascade re-review failed: ${err instanceof Error ? err.message : String(err)}`,
2648
+ phase: "agent"
2649
+ });
2650
+ }
2651
+ } else if (maxDepth > 0 && currentDepth >= maxDepth) {
2652
+ await session.append({
2653
+ type: "llm_response",
2654
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
2655
+ content: [
2656
+ {
2657
+ type: "text",
2658
+ text: `\u{1F982} Chimera cascade stopped at depth limit (${currentDepth}/${maxDepth}) \u2014 manual review recommended if issues persist`
2659
+ }
2660
+ ],
2661
+ stopReason: "end_turn",
2662
+ usage: { input: 0, output: 0 }
2663
+ });
2664
+ }
2665
+ }
2666
+
2667
+ // src/execution-storage-observability.ts
2668
+ function installStorageObservability(events, rootTraceId) {
2669
+ const isStorageNamespace = (event) => event === "storage.read" || event === "storage.write" || event === "storage.error" || event.startsWith("storage.");
2670
+ const storageLog = (event, payload) => {
2671
+ if (isStorageNamespace(event)) {
2672
+ const outcome = payload.outcome;
2673
+ const isErrorEvent = event === "storage.error";
2674
+ const reportsFailure = outcome === "failure" || outcome === "error";
2675
+ const isFailure = isErrorEvent || reportsFailure;
2676
+ if (!isFailure) {
2677
+ return;
2678
+ }
2679
+ const traceId2 = payload.traceId ?? rootTraceId;
2680
+ console.warn(
2681
+ JSON.stringify({
2682
+ ...payload,
2683
+ level: "warning",
2684
+ event,
2685
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2686
+ traceId: traceId2
2687
+ })
2688
+ );
2689
+ return;
2690
+ }
2691
+ const traceId = payload.traceId ?? rootTraceId;
2692
+ console.warn(
2693
+ JSON.stringify({
2694
+ ...payload,
2695
+ level: "info",
2696
+ event,
2697
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2698
+ traceId
2699
+ })
2700
+ );
2701
+ };
2702
+ const onStorageRead = (...args) => storageLog("storage.read", args[0]);
2703
+ const onStorageWrite = (...args) => storageLog("storage.write", args[0]);
2704
+ const onStorageError = (...args) => storageLog("storage.error", args[0]);
2705
+ const offStorageRead = events.on("storage.read", onStorageRead);
2706
+ const offStorageWrite = events.on("storage.write", onStorageWrite);
2707
+ const offStorageError = events.on("storage.error", onStorageError);
2708
+ return () => {
2709
+ offStorageRead();
2710
+ offStorageWrite();
2711
+ offStorageError();
2712
+ };
2713
+ }
2714
+
2715
+ // src/next-task-predictor.ts
2716
+ import { readBundledInstructionText } from "@wrongstack/core/utils";
2717
+ var SYSTEM_PROMPT = readBundledInstructionText("cli/next-task-predictor.md");
2718
+ var MAX_REQUEST_CHARS = 1200;
2719
+ var MAX_SUMMARY_CHARS = 1200;
2720
+ function clamp(text, n) {
2721
+ const t = text.trim();
2722
+ return t.length <= n ? t : `${t.slice(0, n)}\u2026`;
2723
+ }
2724
+ function buildPredictionPrompt(input) {
2725
+ const parts = [];
2726
+ parts.push(`The user asked:
2727
+ ${clamp(input.userRequest, MAX_REQUEST_CHARS) || "(no text)"}`);
2728
+ if (input.assistantSummary.trim()) {
2729
+ parts.push(
2730
+ `The assistant just finished and reported:
2731
+ ${clamp(input.assistantSummary, MAX_SUMMARY_CHARS)}`
2732
+ );
2733
+ }
2734
+ const pending = input.todos.filter((t) => t.status !== "completed");
2735
+ if (pending.length > 0) {
2736
+ const list = pending.slice(0, 8).map((t) => `- [${t.status}] ${t.content}`).join("\n");
2737
+ parts.push(`Open todo items:
2738
+ ${list}`);
2739
+ }
2740
+ parts.push(
2741
+ "Predict the 1-3 most likely exact prompt messages the user could submit back to the agent through the TUI or WebUI. Each must ask the agent to perform the work, never assign a manual chore to the user."
2742
+ );
2743
+ return parts.join("\n\n");
2744
+ }
2745
+ function parsePredictions(raw, max = 3) {
2746
+ const text = raw.trim();
2747
+ if (!text) return [];
2748
+ if (/^none\b/i.test(text) || /no further steps/i.test(text)) return [];
2749
+ const out = [];
2750
+ for (const lineRaw of text.split("\n")) {
2751
+ const line = lineRaw.trim();
2752
+ if (!line) continue;
2753
+ const stripped = line.replace(/^\s*(?:\d+[.)]|[-*•])\s+/, "").trim();
2754
+ const candidate = stripped || line;
2755
+ if (/^none$/i.test(candidate)) continue;
2756
+ if (candidate) out.push(candidate);
2757
+ if (out.length >= max) break;
2758
+ }
2759
+ return out;
2760
+ }
2761
+ function extractText(content) {
2762
+ if (Array.isArray(content)) {
2763
+ return content[0]?.text ?? "";
2764
+ }
2765
+ if (content && typeof content === "object") {
2766
+ return content.text ?? "";
2767
+ }
2768
+ return typeof content === "string" ? content : "";
2769
+ }
2770
+ async function predictNextTasks(input, opts) {
2771
+ const max = opts.maxPredictions ?? 3;
2772
+ const internal = new AbortController();
2773
+ const timeout = setTimeout(() => internal.abort(), 12e3);
2774
+ const onParentAbort = () => internal.abort();
2775
+ if (opts.signal) {
2776
+ if (opts.signal.aborted) internal.abort();
2777
+ else opts.signal.addEventListener("abort", onParentAbort, { once: true });
2778
+ }
2779
+ try {
2780
+ const resp = await opts.provider.complete(
2781
+ {
2782
+ model: opts.model,
2783
+ system: [{ type: "text", text: SYSTEM_PROMPT }],
2784
+ messages: [
2785
+ { role: "user", content: [{ type: "text", text: buildPredictionPrompt(input) }] }
2786
+ ],
2787
+ maxTokens: 160,
2788
+ temperature: 0.3
2789
+ },
2790
+ { signal: internal.signal }
2791
+ );
2792
+ return parsePredictions(extractText(resp.content), max);
2793
+ } catch {
2794
+ return [];
2795
+ } finally {
2796
+ clearTimeout(timeout);
2797
+ if (opts.signal) opts.signal.removeEventListener("abort", onParentAbort);
2798
+ }
2799
+ }
2800
+
2801
+ // src/repl.ts
2802
+ import { toErrorMessage } from "@wrongstack/core/utils";
2803
+ import { color as color9, estimateRequestTokensCalibrated as estimateRequestTokensCalibrated2, expectDefined, hasOpenTodos as hasOpenTodos2 } from "@wrongstack/core/utils";
2804
+ import { detectContinueIntent, InputBuilder, resolveContinuation } from "@wrongstack/core/agent";
2805
+ import { goalFilePath, loadGoal, summarizeUsage } from "@wrongstack/core/goal";
2806
+ import { readClipboardImage, routeImagesForModel } from "@wrongstack/runtime";
2807
+
2808
+ // src/repl-suggestions.ts
2809
+ import { hasOpenTodos } from "@wrongstack/core/utils";
2810
+ import { parseNextSteps } from "@wrongstack/tools/next-steps";
2811
+ function parseSuggestionsFromOutput(finalText, todos) {
2812
+ if (hasOpenTodos(todos)) {
2813
+ setAutoSuggestions([]);
2814
+ return null;
2815
+ }
2816
+ const { texts, autoTexts } = parseNextSteps(finalText, false);
2817
+ if (autoTexts.length > 0) {
2818
+ setAutoSuggestions(autoTexts);
2819
+ }
2820
+ return texts.length > 0 ? texts : null;
2821
+ }
2822
+
2823
+ // src/repl-auto-proceed.ts
2824
+ import { color as color6, estimateRequestTokensCalibrated } from "@wrongstack/core/utils";
2825
+ import { GROUNDED_NO_PROGRESS_STEER } from "@wrongstack/tools/auto-proceed-loop-guard";
2826
+ async function runAutoProceed(opts, suggestion, delayMs, ctrl, loopGuard, grounded = false) {
2827
+ const truncated = suggestion.length > 80 ? `${suggestion.slice(0, 77)}\u2026` : suggestion;
2828
+ console.log(
2829
+ JSON.stringify({
2830
+ level: "info",
2831
+ event: "auto_proceed_started",
2832
+ suggestion: truncated,
2833
+ delayMs
2834
+ })
2835
+ );
2836
+ try {
2837
+ const proceed = await autoProceedCooldown(opts, delayMs, suggestion, ctrl.signal);
2838
+ if (!proceed) {
2839
+ console.log(
2840
+ JSON.stringify({
2841
+ level: "info",
2842
+ event: "auto_proceed_cancelled",
2843
+ suggestion: truncated
2844
+ })
2845
+ );
2846
+ return false;
2847
+ }
2848
+ if ((opts.getAutonomy?.() ?? "off") !== "auto") {
2849
+ console.log(
2850
+ JSON.stringify({
2851
+ level: "info",
2852
+ event: "auto_proceed_cancelled",
2853
+ suggestion: truncated,
2854
+ reason: "autonomy_not_auto"
2855
+ })
2856
+ );
2857
+ return false;
2858
+ }
2859
+ const repetition = grounded ? loopGuard.recordGrounded(suggestion) : loopGuard.record(suggestion);
2860
+ if (repetition.shouldHalt) {
2861
+ setSuggestions([]);
2862
+ setAutoSuggestions([]);
2863
+ opts.onSuggestionsParsed?.(null);
2864
+ opts.renderer.writeWarning(
2865
+ `Auto-proceed halted: the same prompt was fed ${repetition.runLength} times in a row. Autonomy mode is still on, but no automatic next step will run until you provide input. Type anything to continue (resets the guard). Last seen prompt: "${suggestion.length > 100 ? `${suggestion.slice(0, 97)}\u2026` : suggestion}"`
2866
+ );
2867
+ return false;
2868
+ }
2869
+ const steered = "action" in repetition && repetition.action === "steer";
2870
+ if (steered) {
2871
+ opts.renderer.writeWarning(
2872
+ "Todo board unchanged since the last automatic turn \u2014 steering the agent to update the board before continuing."
2873
+ );
2874
+ }
2875
+ const runBlocks = [
2876
+ {
2877
+ type: "text",
2878
+ text: steered ? `${suggestion}
2879
+
2880
+ ${GROUNDED_NO_PROGRESS_STEER}` : suggestion
2881
+ }
2882
+ ];
2883
+ const runResult = await opts.agent.run(runBlocks, { signal: ctrl.signal });
2884
+ console.log(
2885
+ JSON.stringify({
2886
+ level: "info",
2887
+ event: "auto_proceed_completed",
2888
+ suggestion: truncated,
2889
+ status: runResult.status,
2890
+ iterations: runResult.iterations
2891
+ })
2892
+ );
2893
+ opts.onAgentIterationComplete?.(
2894
+ estimateRequestTokensCalibrated(
2895
+ opts.agent.ctx.messages,
2896
+ opts.agent.ctx.systemPrompt,
2897
+ opts.agent.ctx.tools ?? []
2898
+ ).total
2899
+ );
2900
+ if (runResult.status === "done" && opts.onSuggestionsParsed) {
2901
+ const parsed = runResult.finalText ? parseSuggestionsFromOutput(runResult.finalText, opts.agent.ctx.todos) : null;
2902
+ opts.onSuggestionsParsed(parsed);
2903
+ }
2904
+ return true;
2905
+ } finally {
2906
+ }
2907
+ }
2908
+ async function autoProceedCooldown(opts, delayMs, suggestion, signal) {
2909
+ if (delayMs <= 0) return true;
2910
+ const truncated = suggestion.length > 100 ? `${suggestion.slice(0, 97)}\u2026` : suggestion;
2911
+ const sec = Math.ceil(delayMs / 1e3);
2912
+ opts.renderer.write(`
2913
+ ${color6.cyan("\u23F3 Auto")} ${color6.dim("(Ctrl+C to cancel)")}
2914
+ `);
2915
+ opts.renderer.write(`${color6.dim(" \u25B8")} ${color6.dim(truncated)}
2916
+ `);
2917
+ void runContextCompaction(opts, signal);
2918
+ const start = Date.now();
2919
+ let interval;
2920
+ let lastTickedSecond = sec + 1;
2921
+ let onAbort;
2922
+ return new Promise((resolve3) => {
2923
+ onAbort = () => resolve3(false);
2924
+ signal.addEventListener("abort", onAbort, { once: true });
2925
+ interval = setInterval(() => {
2926
+ if (signal.aborted) {
2927
+ resolve3(false);
2928
+ return;
2929
+ }
2930
+ const elapsed = Date.now() - start;
2931
+ const remaining = Math.max(0, Math.ceil((delayMs - elapsed) / 1e3));
2932
+ if (remaining <= 0) {
2933
+ opts.renderer.write(color6.dim(` \u21B3 ${truncated}
2934
+ `));
2935
+ resolve3(true);
2936
+ return;
2937
+ }
2938
+ if (opts.onCountdownTick && remaining !== lastTickedSecond) {
2939
+ lastTickedSecond = remaining;
2940
+ try {
2941
+ const shouldAbort = opts.onCountdownTick(remaining);
2942
+ if (shouldAbort === true) {
2943
+ opts.renderer.write(
2944
+ color6.yellow(" \u21B3 Countdown cancelled \u2014 switching to manual mode\n")
2945
+ );
2946
+ resolve3(false);
2947
+ return;
2948
+ }
2949
+ } catch {
2950
+ }
2951
+ }
2952
+ if (remaining % 5 === 0 || remaining === sec) {
2953
+ opts.renderer.write(color6.dim(` \u23F3 ${remaining}s\u2026
2954
+ `));
2955
+ }
2956
+ }, 1e3);
2957
+ }).finally(() => {
2958
+ if (interval) clearInterval(interval);
2959
+ if (onAbort) signal.removeEventListener("abort", onAbort);
2960
+ try {
2961
+ opts.onCountdownTick?.(0);
2962
+ } catch {
2963
+ }
2964
+ });
2965
+ }
2966
+ async function runContextCompaction(opts, _signal) {
2967
+ try {
2968
+ const ctx = opts.agent.ctx;
2969
+ if (!ctx?.compactor) return;
2970
+ if ((ctx.messages?.length ?? 0) < 50) return;
2971
+ await ctx.compactor.compact(ctx, { aggressive: false });
2972
+ } catch {
2973
+ }
2974
+ }
2975
+
2976
+ // src/repl-input.ts
2977
+ import { color as color7 } from "@wrongstack/core/utils";
2978
+ async function readPossiblyMultiline(opts) {
2979
+ const firstPrompt = theme.primary("\u203A ");
2980
+ const contPrompt = color7.dim("\xB7 ");
2981
+ const first = await opts.reader.readLine(firstPrompt);
2982
+ if (first.trim() === '"""') {
2983
+ const parts = [];
2984
+ try {
2985
+ for (; ; ) {
2986
+ const next = await opts.reader.readLine(contPrompt);
2987
+ if (next.trim() === '"""') break;
2988
+ parts.push(next);
2989
+ }
2990
+ } catch {
2991
+ return parts.join("\n");
2992
+ }
2993
+ return parts.join("\n");
2994
+ }
2995
+ let buf = first;
2996
+ while (buf.endsWith("\\")) {
2997
+ buf = buf.slice(0, -1);
2998
+ const cont = await opts.reader.readLine(contPrompt);
2999
+ buf += "\n" + cont;
3000
+ }
3001
+ return buf;
3002
+ }
3003
+
3004
+ // src/repl-rendering.ts
3005
+ import { color as color8 } from "@wrongstack/core/utils";
3006
+ var FILLED = "\u2588";
3007
+ var EMPTY = "\u2591";
3008
+ function renderContextChip(used, max) {
3009
+ const ratio = Math.max(0, Math.min(1, used / max));
3010
+ const pct = Math.round(ratio * 100);
3011
+ const bar = renderProgress(ratio, 6);
3012
+ return `${bar} ${pct}% (${fmtTok(used)}/${fmtTok(max)})`;
3013
+ }
3014
+ function renderProgress(ratio, width) {
3015
+ const clamped = Math.max(0, Math.min(1, ratio));
3016
+ const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
3017
+ const capped = Math.min(width, filled);
3018
+ return FILLED.repeat(capped) + EMPTY.repeat(width - capped);
3019
+ }
3020
+ function printBanner(renderer, projectName) {
3021
+ const lines = [
3022
+ theme.primary(theme.bold("WrongStack")) + color8.dim(` v${CLI_VERSION}`),
3023
+ color8.dim("Built on the wrong stack. Shipped anyway.")
3024
+ ];
3025
+ if (projectName && projectName.length > 0) {
3026
+ lines.push(color8.dim("Project: ") + theme.bold(projectName));
3027
+ }
3028
+ lines.push(color8.dim("Type /help for commands, /exit or q to quit."), "");
3029
+ renderer.write(`${lines.join("\n")}
3030
+ `);
3031
+ }
3032
+
3033
+ // src/repl-client-registration.ts
3034
+ import * as crypto from "node:crypto";
3035
+ import * as path9 from "node:path";
3036
+ import {
3037
+ getSharedProjectMailbox,
3038
+ resolveProjectDir
3039
+ } from "@wrongstack/core/coordination";
3040
+ import { wstackGlobalRoot } from "@wrongstack/core/utils";
3041
+ function registerReplClient(opts) {
3042
+ const replProjectRoot = opts.projectRoot ?? process.cwd();
3043
+ const projectDir = resolveProjectDir(replProjectRoot, wstackGlobalRoot());
3044
+ const clientId = `repl@${crypto.randomUUID().slice(0, 8)}`;
3045
+ const hqConnection = startCliHqConnection({
3046
+ clientKind: "repl",
3047
+ projectRoot: replProjectRoot,
3048
+ projectName: path9.basename(replProjectRoot),
3049
+ appConfig: opts.appConfig,
3050
+ capabilities: ["telemetry.publish", "mailbox.summary"]
3051
+ });
3052
+ const clientMailbox = getSharedProjectMailbox(
3053
+ projectDir,
3054
+ void 0,
3055
+ () => hqConnection.getPublisher()
3056
+ );
3057
+ let clientHeartbeat;
3058
+ clientMailbox.registerClient({
3059
+ clientId,
3060
+ sessionId: opts.getSessionId?.() ?? replProjectRoot,
3061
+ name: `REPL [${path9.basename(replProjectRoot)}]`,
3062
+ source: "repl",
3063
+ pid: process.pid
3064
+ }).then(() => {
3065
+ clientHeartbeat = setInterval(() => {
3066
+ clientMailbox.clientHeartbeat({ clientId, sessionId: opts.getSessionId?.() ?? replProjectRoot }).catch(() => {
3067
+ });
3068
+ }, 15e3);
3069
+ }).catch(() => {
3070
+ });
3071
+ return {
3072
+ clientId,
3073
+ close() {
3074
+ if (clientHeartbeat) clearInterval(clientHeartbeat);
3075
+ clientMailbox.deregisterClient(clientId).catch(() => void 0);
3076
+ hqConnection.stop();
3077
+ }
3078
+ };
3079
+ }
3080
+
3081
+ // src/repl.ts
3082
+ import { createAutoProceedLoopGuard } from "@wrongstack/tools/auto-proceed-loop-guard";
3083
+ var DEFAULT_MAX_CONSECUTIVE_AUTO_PROCEED = 50;
3084
+ async function runRepl(opts) {
3085
+ if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
3086
+ await renderGoalBanner(opts);
3087
+ let activeCtrl;
3088
+ let interrupts = 0;
3089
+ if (opts.interruptController) {
3090
+ opts.interruptController.abortLeader = () => {
3091
+ if (activeCtrl) {
3092
+ activeCtrl.abort();
3093
+ return true;
3094
+ }
3095
+ return false;
3096
+ };
3097
+ }
3098
+ let autoIterCount = 0;
3099
+ let autoCapWarned = false;
3100
+ const autoProceedLoopGuard = createAutoProceedLoopGuard();
3101
+ let exiting = false;
3102
+ const onSigint = () => {
3103
+ interrupts++;
3104
+ if (interrupts >= 3) {
3105
+ process.exit(130);
3106
+ }
3107
+ if (interrupts >= 2) {
3108
+ opts.renderer.writeWarning("Exiting.");
3109
+ exiting = true;
3110
+ activeCtrl?.abort();
3111
+ const hardExit = setTimeout(() => process.exit(130), 2e3);
3112
+ hardExit.unref?.();
3113
+ return;
3114
+ }
3115
+ if (opts.getAutonomy?.() === "eternal" || opts.getAutonomy?.() === "eternal-parallel") {
3116
+ opts.getEternalEngine?.()?.stop();
3117
+ opts.getParallelEngine?.()?.stop();
3118
+ opts.onAutonomy?.("off");
3119
+ opts.renderer.writeWarning("Engine stop requested. Press Ctrl+C again to exit.");
3120
+ interrupts = 0;
3121
+ return;
3122
+ }
3123
+ const sddRun = opts.getSddRun?.();
3124
+ if (sddRun?.isRunning()) {
3125
+ sddRun.stop();
3126
+ opts.renderer.writeWarning("SDD run stop requested. Press Ctrl+C again to exit.");
3127
+ interrupts = 0;
3128
+ return;
3129
+ }
3130
+ if (activeCtrl) {
3131
+ activeCtrl.abort();
3132
+ const killed = opts.onInterruptFleet?.() ?? 0;
3133
+ opts.renderer.writeWarning(
3134
+ killed > 0 ? `Iteration cancelled \xB7 stopped ${killed} subagent${killed === 1 ? "" : "s"}. Press Ctrl+C again to exit.` : "Iteration cancelled. Press Ctrl+C again to exit."
3135
+ );
3136
+ } else {
3137
+ opts.renderer.writeWarning("Press Ctrl+C again to exit.");
3138
+ }
3139
+ };
3140
+ process.on("SIGINT", onSigint);
3141
+ const replClientRegistration = registerReplClient(opts);
3142
+ const builder = new InputBuilder({ store: opts.attachments });
3143
+ try {
3144
+ for (; ; ) {
3145
+ if (exiting) break;
3146
+ if (opts.getAutonomy?.() === "eternal") {
3147
+ const engine = opts.getEternalEngine?.();
3148
+ if (!engine) {
3149
+ opts.renderer.writeWarning("Eternal mode set but no engine wired \u2014 falling back to off.");
3150
+ } else {
3151
+ const beforeGoal = await loadGoalSafe(opts);
3152
+ const beforeIter = beforeGoal?.iterations ?? 0;
3153
+ opts.renderer.write(color9.dim(`
3154
+ \u21B3 [eternal #${beforeIter + 1}] running iteration\u2026
3155
+ `));
3156
+ interrupts = 0;
3157
+ try {
3158
+ const ok = await engine.runOneIteration();
3159
+ const afterGoal = await loadGoalSafe(opts);
3160
+ const last = afterGoal?.journal[afterGoal.journal.length - 1];
3161
+ if (!ok && !last) {
3162
+ opts.renderer.write(color9.dim(" \u21B3 [eternal] iteration produced no progress.\n"));
3163
+ } else if (last) {
3164
+ const mark = last.status === "success" ? color9.green("\u2713") : last.status === "failure" ? color9.red("\u2717") : color9.amber("\u2298");
3165
+ const tail = last.note ? color9.dim(` \u2014 ${last.note.slice(0, 80)}`) : "";
3166
+ opts.renderer.write(
3167
+ ` ${mark} ${color9.dim(`#${last.iteration}`)} ${color9.dim(`[${last.source}]`)} ${last.task}${tail}
3168
+ `
3169
+ );
3170
+ }
3171
+ if (engine.currentState === "stopped") {
3172
+ const goal = await loadGoalSafe(opts);
3173
+ if (goal?.goalState === "completed") {
3174
+ const u = goal.journal.length > 0 ? summarizeUsage(goal) : null;
3175
+ const costLine = u && u.iterationsWithUsage > 0 ? color9.dim(
3176
+ ` \u2014 ${u.totalCostUsd.toFixed(4)} \xB7 ${u.totalInputTokens} in / ${u.totalOutputTokens} out \xB7 ${goal.iterations} iterations`
3177
+ ) : goal.iterations > 0 ? color9.dim(` \u2014 ${goal.iterations} iterations`) : "";
3178
+ opts.renderer.write(
3179
+ color9.green(`
3180
+ \u{1F3AF} Goal completed!${costLine}
3181
+
3182
+ `) + color9.dim(" Goal cleared. Use /goal set <mission> to create a new goal.\n")
3183
+ );
3184
+ if (opts.projectRoot) {
3185
+ try {
3186
+ const { unlink } = await import("node:fs/promises");
3187
+ await unlink(goalFilePath(opts.projectRoot));
3188
+ } catch {
3189
+ }
3190
+ }
3191
+ }
3192
+ opts.onAutonomy?.("auto");
3193
+ continue;
3194
+ }
3195
+ } catch (err) {
3196
+ opts.renderer.writeError(
3197
+ `[eternal] ${toErrorMessage(err)}`
3198
+ );
3199
+ }
3200
+ await new Promise((resolve3) => setTimeout(resolve3, 250));
3201
+ continue;
3202
+ }
3203
+ } else if (opts.getAutonomy?.() === "eternal-parallel") {
3204
+ const engine = opts.getParallelEngine?.();
3205
+ if (!engine) {
3206
+ opts.renderer.writeWarning(
3207
+ "Parallel mode set but no engine wired \u2014 falling back to off."
3208
+ );
3209
+ } else {
3210
+ const beforeGoal = await loadGoalSafe(opts);
3211
+ const beforeIter = beforeGoal?.iterations ?? 0;
3212
+ const coord = engine.getCoordinator();
3213
+ if (coord) {
3214
+ const stats = coord.getStats();
3215
+ opts.renderer.write(
3216
+ color9.dim(
3217
+ ` \u250C\u2500 Fleet: ${stats.running} running, ${stats.idle} idle, ${stats.pending} pending, ${stats.completed} done`
3218
+ ) + "\n"
3219
+ );
3220
+ }
3221
+ opts.renderer.write(
3222
+ color9.magenta(` \u21B3 [parallel #${beforeIter + 1}] launching fan-out\u2026
3223
+ `)
3224
+ );
3225
+ interrupts = 0;
3226
+ try {
3227
+ await engine.runOneIteration();
3228
+ const afterGoal = await loadGoalSafe(opts);
3229
+ const last = afterGoal?.journal[afterGoal.journal.length - 1];
3230
+ if (coord) {
3231
+ const stats = coord.getStats();
3232
+ opts.renderer.write(
3233
+ color9.dim(
3234
+ ` \u2514\u2500 Fleet: ${stats.running} running, ${stats.idle} idle, ${stats.completed} done
3235
+ `
3236
+ )
3237
+ );
3238
+ }
3239
+ if (last) {
3240
+ const mark = last.status === "success" ? color9.green("\u2713") : last.status === "failure" ? color9.red("\u2717") : color9.amber("\u2298");
3241
+ const tail = last.note ? color9.dim(` \u2014 ${last.note.slice(0, 80)}`) : "";
3242
+ opts.renderer.write(
3243
+ ` ${mark} ${color9.dim(`#${last.iteration}`)} ${color9.dim(`[${last.source}]`)} ${last.task}${tail}
3244
+ `
3245
+ );
3246
+ }
3247
+ if (engine.currentState === "stopped") {
3248
+ const goal = await loadGoalSafe(opts);
3249
+ if (goal?.goalState === "completed") {
3250
+ const u = goal.journal.length > 0 ? summarizeUsage(goal) : null;
3251
+ const costLine = u && u.iterationsWithUsage > 0 ? color9.dim(
3252
+ ` \u2014 ${u.totalCostUsd.toFixed(4)} \xB7 ${u.totalInputTokens} in / ${u.totalOutputTokens} out \xB7 ${goal.iterations} iterations`
3253
+ ) : goal.iterations > 0 ? color9.dim(` \u2014 ${goal.iterations} iterations`) : "";
3254
+ opts.renderer.write(
3255
+ color9.green(`
3256
+ \u{1F3AF} Goal completed!${costLine}
3257
+
3258
+ `) + color9.dim(" Goal cleared. Use /goal set <mission> to create a new goal.\n")
3259
+ );
3260
+ if (opts.projectRoot) {
3261
+ try {
3262
+ const { unlink } = await import("node:fs/promises");
3263
+ await unlink(goalFilePath(opts.projectRoot));
3264
+ } catch {
3265
+ }
3266
+ }
3267
+ }
3268
+ opts.onAutonomy?.("auto");
3269
+ continue;
3270
+ }
3271
+ } catch (err) {
3272
+ opts.renderer.writeError(
3273
+ `[parallel] ${toErrorMessage(err)}`
3274
+ );
3275
+ }
3276
+ await new Promise((resolve3) => setTimeout(resolve3, 250));
3277
+ continue;
3278
+ }
3279
+ }
3280
+ {
3281
+ const mode = opts.getAutonomy?.() ?? "off";
3282
+ const suggestions = opts.getSuggestions?.() ?? [];
3283
+ if (mode === "suggest" && suggestions.length > 0) {
3284
+ const lines = suggestions.map((s, i) => ` ${color9.bold(`${i + 1}.`)} ${color9.dim(s)}`);
3285
+ opts.renderer.write(
3286
+ `
3287
+ ${color9.cyan(" \u{1F4A1} Suggested next steps")} ${color9.dim("(use /next 1, /next 2, or /next 1 2 3)")}
3288
+ ${lines.join("\n")}
3289
+
3290
+ `
3291
+ );
3292
+ }
3293
+ if (mode === "auto") {
3294
+ let top;
3295
+ let groundedTodo = false;
3296
+ if (suggestions.length > 0) {
3297
+ const isYolo = opts.getYolo?.() ?? false;
3298
+ const autoSuggestions = opts.getAutoSuggestions?.() ?? [];
3299
+ const useAutoSuggestions = isYolo && autoSuggestions.length > 0;
3300
+ top = useAutoSuggestions ? autoSuggestions[0] ?? "" : suggestions[0] ?? "";
3301
+ } else {
3302
+ const todos = opts.agent.ctx.todos ?? [];
3303
+ const resolved = resolveContinuation({ todos, suggestions: [] });
3304
+ top = resolved.source === "todo" ? resolved.text : "";
3305
+ groundedTodo = top !== "";
3306
+ }
3307
+ if (!top) {
3308
+ } else {
3309
+ const maxAuto = opts.autoProceedMaxIterations ?? DEFAULT_MAX_CONSECUTIVE_AUTO_PROCEED;
3310
+ if (maxAuto > 0 && autoIterCount >= maxAuto) {
3311
+ if (!autoCapWarned) {
3312
+ autoCapWarned = true;
3313
+ opts.renderer.writeWarning(
3314
+ `Auto-proceed paused after ${maxAuto} consecutive automatic turns \u2014 enter input to continue (resets the counter). Autonomy stays on.`
3315
+ );
3316
+ }
3317
+ } else {
3318
+ const delay = opts.autoProceedDelayMs ?? 1e3;
3319
+ const ctrl = new AbortController();
3320
+ activeCtrl = ctrl;
3321
+ try {
3322
+ const isYolo = opts.getYolo?.() ?? false;
3323
+ const autoSuggestions = opts.getAutoSuggestions?.() ?? [];
3324
+ const useAutoSuggestions = isYolo && autoSuggestions.length > 0;
3325
+ const promptToFeed = useAutoSuggestions && opts.autonomyNextPrompt ? opts.autonomyNextPrompt.replace("{{suggestion}}", top) : top;
3326
+ const submitted = await runAutoProceed(
3327
+ opts,
3328
+ promptToFeed,
3329
+ delay,
3330
+ ctrl,
3331
+ autoProceedLoopGuard,
3332
+ groundedTodo
3333
+ );
3334
+ if (submitted) {
3335
+ autoIterCount++;
3336
+ continue;
3337
+ }
3338
+ } finally {
3339
+ activeCtrl = void 0;
3340
+ }
3341
+ }
3342
+ }
3343
+ }
3344
+ }
3345
+ let raw;
3346
+ try {
3347
+ raw = await readPossiblyMultiline(opts);
3348
+ } catch {
3349
+ break;
3350
+ }
3351
+ const trimmed = raw.trim();
3352
+ if (!trimmed) {
3353
+ interrupts = 0;
3354
+ continue;
3355
+ }
3356
+ interrupts = 0;
3357
+ autoIterCount = 0;
3358
+ autoCapWarned = false;
3359
+ autoProceedLoopGuard.reset();
3360
+ if (trimmed === "q") {
3361
+ opts.renderer.write(color9.dim(" Goodbye!\n"));
3362
+ break;
3363
+ }
3364
+ if (trimmed === "wd" || trimmed.startsWith("cd ")) {
3365
+ const args = trimmed.startsWith("cd ") ? trimmed.slice(3).trim() : "";
3366
+ try {
3367
+ const res = await opts.slashRegistry.dispatch(`/working_dir ${args}`, opts.agent.ctx);
3368
+ if (res?.message) opts.renderer.write(`${res.message}
3369
+ `);
3370
+ } catch (err) {
3371
+ opts.renderer.writeError(toErrorMessage(err));
3372
+ }
3373
+ continue;
3374
+ }
3375
+ if (trimmed === "/image" || trimmed === "/paste-image" || raw === "\x1Bv") {
3376
+ await pasteClipboardImage(builder, opts);
3377
+ continue;
3378
+ }
3379
+ if (trimmed.startsWith("/")) {
3380
+ try {
3381
+ const res = await opts.slashRegistry.dispatch(trimmed, opts.agent.ctx);
3382
+ if (res?.message) opts.renderer.write(`${res.message}
3383
+ `);
3384
+ if (res?.exit) break;
3385
+ if (res?.runText) {
3386
+ const runBlocks = [{ type: "text", text: res.runText }];
3387
+ const runCtrl2 = new AbortController();
3388
+ activeCtrl = runCtrl2;
3389
+ try {
3390
+ const runResult = await opts.agent.run(runBlocks, { signal: runCtrl2.signal });
3391
+ opts.onAgentIterationComplete?.(
3392
+ estimateRequestTokensCalibrated2(
3393
+ opts.agent.ctx.messages,
3394
+ opts.agent.ctx.systemPrompt,
3395
+ opts.agent.ctx.tools ?? []
3396
+ ).total
3397
+ );
3398
+ if (runResult.status === "done" && runResult.finalText) {
3399
+ const specSaved = await trySaveSpecFromAIOutput(runResult.finalText);
3400
+ if (specSaved) {
3401
+ opts.renderer.write(
3402
+ `
3403
+ ${color9.cyan(" \u2713 Spec detected and saved! Use /sdd approve to continue.")}
3404
+ `
3405
+ );
3406
+ }
3407
+ const planSaved = trySaveImplementationPlan(runResult.finalText);
3408
+ if (planSaved) {
3409
+ opts.renderer.write(`
3410
+ ${color9.cyan(" \u2713 Implementation plan saved!")}
3411
+ `);
3412
+ }
3413
+ const tasksSaved = await trySaveTasksFromAIOutput(runResult.finalText);
3414
+ if (tasksSaved) {
3415
+ const progress = getTaskProgress();
3416
+ const count = progress?.total ?? 0;
3417
+ opts.renderer.write(
3418
+ `
3419
+ ${color9.cyan(` \u2713 ${count} tasks detected and saved! Use /sdd approve to execute.`)}
3420
+ `
3421
+ );
3422
+ }
3423
+ const sddPhase2 = getActiveSDDPhase();
3424
+ if (sddPhase2 === "executing") {
3425
+ const autoCompleted = autoDetectTaskCompletion(runResult.finalText);
3426
+ if (autoCompleted > 0) {
3427
+ const progress = getTaskProgress();
3428
+ if (progress) {
3429
+ opts.renderer.write(
3430
+ `
3431
+ ${color9.cyan(` \u2713 ${autoCompleted} task(s) auto-completed! Progress: ${progress.completed}/${progress.total} (${progress.percentComplete}%)`)}
3432
+ `
3433
+ );
3434
+ const taskList2 = renderTaskListWithProgress();
3435
+ if (taskList2) {
3436
+ opts.renderer.write(`
3437
+ ${color9.dim(taskList2)}
3438
+ `);
3439
+ }
3440
+ }
3441
+ } else {
3442
+ const taskList2 = renderTaskListWithProgress();
3443
+ if (taskList2) {
3444
+ opts.renderer.write(`
3445
+ ${color9.dim(taskList2)}
3446
+ `);
3447
+ }
3448
+ }
3449
+ }
3450
+ if (opts.onSuggestionsParsed) {
3451
+ const parsed = parseSuggestionsFromOutput(
3452
+ runResult.finalText,
3453
+ opts.agent.ctx.todos
3454
+ );
3455
+ opts.onSuggestionsParsed(parsed);
3456
+ }
3457
+ }
3458
+ } catch (_runErr) {
3459
+ opts.renderer.writeWarning("AI auto-trigger failed. You can continue manually.");
3460
+ }
3461
+ }
3462
+ } catch (err) {
3463
+ opts.renderer.writeError(toErrorMessage(err));
3464
+ }
3465
+ continue;
3466
+ }
3467
+ if (detectContinueIntent(trimmed)) {
3468
+ const resolved = resolveContinuation({
3469
+ todos: opts.agent.ctx.todos,
3470
+ suggestions: getSuggestions()
3471
+ });
3472
+ if (resolved.source === "open") {
3473
+ opts.renderer.write(
3474
+ ` ${color9.amber("\u26A0")} ${color9.bold("'continue' has no anchor")} ${color9.dim("\u2014 no pending todo or suggestion.")}
3475
+ ` + color9.dim(" If you proceed, I'll choose the next step from context; that can drift.\n")
3476
+ );
3477
+ const ans = (await opts.reader.readLine(color9.dim(" Proceed anyway? [Y/n \xB7 e = type your own] "))).trim().toLowerCase();
3478
+ if (ans === "n" || ans === "no") {
3479
+ opts.renderer.write(color9.dim(" Cancelled \u2014 nothing sent.\n"));
3480
+ continue;
3481
+ }
3482
+ if (ans === "e" || ans === "edit") {
3483
+ opts.renderer.write(color9.dim(" Okay \u2014 type what to continue with.\n"));
3484
+ continue;
3485
+ }
3486
+ } else {
3487
+ opts.renderer.write(color9.dim(` ${resolved.label}
3488
+ `));
3489
+ }
3490
+ builder.appendText(resolved.text);
3491
+ } else {
3492
+ const ph = await builder.appendPaste(raw);
3493
+ if (ph) {
3494
+ const lineCount = raw.split("\n").length;
3495
+ opts.renderer.write(color9.dim(` \u21B3 ${ph} (${lineCount} lines)
3496
+ `));
3497
+ }
3498
+ }
3499
+ const blocks = await builder.submit();
3500
+ const sddContext = getActiveSDDContext();
3501
+ const taskList = getTaskListText();
3502
+ const taskProgress = getTaskProgress();
3503
+ const sddPhase = getActiveSDDPhase();
3504
+ let sddPrefix = "";
3505
+ if (sddContext) {
3506
+ sddPrefix = `[SDD SESSION ACTIVE]
3507
+ ${sddContext}`;
3508
+ if (sddPhase === "executing") {
3509
+ const currentCtx = getCurrentExecutingContext();
3510
+ if (currentCtx) {
3511
+ sddPrefix += `
3512
+
3513
+ ${currentCtx}`;
3514
+ }
3515
+ }
3516
+ if (taskList) {
3517
+ sddPrefix += `
3518
+
3519
+ **Current Task List:**
3520
+ ${taskList}`;
3521
+ }
3522
+ if (taskProgress && taskProgress.total > 0) {
3523
+ sddPrefix += `
3524
+ **Progress:** ${taskProgress.completed}/${taskProgress.total} (${taskProgress.percentComplete}%)`;
3525
+ }
3526
+ if (sddPhase === "executing" && taskProgress && taskProgress.percentComplete === 100) {
3527
+ sddPrefix += "\n\n**All tasks completed! Provide a summary of everything implemented.**";
3528
+ }
3529
+ sddPrefix += "\n\n---\nUser message:\n";
3530
+ }
3531
+ const effectiveBlocks = sddPrefix ? [{ type: "text", text: sddPrefix }, ...blocks] : blocks;
3532
+ const runCtrl = new AbortController();
3533
+ activeCtrl = runCtrl;
3534
+ try {
3535
+ const startedAt = Date.now();
3536
+ const before = opts.tokenCounter?.total();
3537
+ const costBefore = opts.tokenCounter?.estimateCost().total ?? 0;
3538
+ const routed = effectiveBlocks.some((block) => block.type === "image") ? await routeImagesForModel(effectiveBlocks, {
3539
+ supportsVision: opts.supportsVision ? await opts.supportsVision() : opts.agent.ctx.provider.capabilities.vision,
3540
+ adapters: opts.visionAdapters ?? [],
3541
+ ctx: opts.agent.ctx,
3542
+ signal: runCtrl.signal,
3543
+ providerId: opts.agent.ctx.provider.id,
3544
+ model: opts.agent.ctx.model
3545
+ }) : { blocks: effectiveBlocks, route: "none", convertedImages: 0 };
3546
+ if (routed.route === "adapter") {
3547
+ opts.renderer.write(
3548
+ color9.dim(
3549
+ ` \u21B3 image analyzed via ${routed.adapterName ?? "vision adapter"} (${routed.convertedImages} image${routed.convertedImages === 1 ? "" : "s"})
3550
+ `
3551
+ )
3552
+ );
3553
+ }
3554
+ const result = await opts.agent.run(routed.blocks, { signal: runCtrl.signal });
3555
+ opts.onAgentIterationComplete?.(
3556
+ estimateRequestTokensCalibrated2(
3557
+ opts.agent.ctx.messages,
3558
+ opts.agent.ctx.systemPrompt,
3559
+ opts.agent.ctx.tools ?? []
3560
+ ).total
3561
+ );
3562
+ if (result.status === "aborted") {
3563
+ opts.renderer.writeWarning("Aborted.");
3564
+ } else if (result.status === "failed") {
3565
+ const err = result.error;
3566
+ if (err) {
3567
+ const tag = err.recoverable ? " (recoverable)" : "";
3568
+ opts.renderer.writeError(`Failed [${err.severity}]${tag}: ${err.describe()}`);
3569
+ const hint = contextOverflowHint(err);
3570
+ if (hint) opts.renderer.writeWarning(hint);
3571
+ } else {
3572
+ opts.renderer.writeError("Failed.");
3573
+ }
3574
+ } else if (result.status === "max_iterations") {
3575
+ opts.renderer.writeWarning(`Hit max iterations (${result.iterations}).`);
3576
+ }
3577
+ if (result.status === "done" && result.finalText && sddContext) {
3578
+ const specSaved = await trySaveSpecFromAIOutput(result.finalText);
3579
+ if (specSaved) {
3580
+ opts.renderer.write(
3581
+ `
3582
+ ${color9.cyan(" \u2713 Spec detected and saved! Use /sdd approve to continue.")}
3583
+ `
3584
+ );
3585
+ }
3586
+ const planSaved = trySaveImplementationPlan(result.finalText);
3587
+ if (planSaved) {
3588
+ opts.renderer.write(`
3589
+ ${color9.cyan(" \u2713 Implementation plan saved!")}
3590
+ `);
3591
+ }
3592
+ const tasksSaved = await trySaveTasksFromAIOutput(result.finalText);
3593
+ if (tasksSaved) {
3594
+ const progress = getTaskProgress();
3595
+ const count = progress?.total ?? 0;
3596
+ opts.renderer.write(
3597
+ `
3598
+ ${color9.cyan(` \u2713 ${count} tasks detected and saved! Use /sdd approve to execute.`)}
3599
+ `
3600
+ );
3601
+ }
3602
+ const phase = getActiveSDDPhase();
3603
+ if (phase === "executing") {
3604
+ const autoCompleted = autoDetectTaskCompletion(result.finalText);
3605
+ if (autoCompleted > 0) {
3606
+ const progress = getTaskProgress();
3607
+ if (progress) {
3608
+ opts.renderer.write(
3609
+ `
3610
+ ${color9.cyan(` \u2713 ${autoCompleted} task(s) auto-completed! Progress: ${progress.completed}/${progress.total} (${progress.percentComplete}%)`)}
3611
+ `
3612
+ );
3613
+ if (progress.percentComplete === 100) {
3614
+ opts.renderer.write(
3615
+ `
3616
+ ${color9.green(" \u{1F389} All tasks completed! Use /sdd cancel to end the session.")}
3617
+ `
3618
+ );
3619
+ }
3620
+ }
3621
+ advanceToNextTask();
3622
+ const taskList2 = renderTaskListWithProgress();
3623
+ if (taskList2) {
3624
+ opts.renderer.write(`
3625
+ ${color9.dim(taskList2)}
3626
+ `);
3627
+ }
3628
+ } else {
3629
+ const taskList2 = renderTaskListWithProgress();
3630
+ if (taskList2) {
3631
+ opts.renderer.write(`
3632
+ ${color9.dim(taskList2)}
3633
+ `);
3634
+ }
3635
+ }
3636
+ }
3637
+ }
3638
+ if (result.status === "done" && result.finalText && opts.onSuggestionsParsed) {
3639
+ const parsed = parseSuggestionsFromOutput(result.finalText, opts.agent.ctx.todos);
3640
+ opts.onSuggestionsParsed(parsed);
3641
+ }
3642
+ if (opts.tokenCounter && before) {
3643
+ const after = opts.tokenCounter.total();
3644
+ const costAfter = opts.tokenCounter.estimateCost().total;
3645
+ const effectiveMaxContext = opts.getEffectiveMaxContext?.() ?? opts.effectiveMaxContext;
3646
+ const ctxChip = effectiveMaxContext && effectiveMaxContext > 0 ? ` ctx: ${renderContextChip(after.input, effectiveMaxContext)}` : "";
3647
+ opts.renderer.write(
3648
+ `
3649
+ ${color9.dim(
3650
+ `[in: ${fmtTok(after.input - before.input)} out: ${fmtTok(after.output - before.output)} iters: ${result.iterations} cost: ${(costAfter - costBefore).toFixed(4)} ${((Date.now() - startedAt) / 1e3).toFixed(1)}s]${ctxChip}`
3651
+ )}
3652
+ `
3653
+ );
3654
+ }
3655
+ if (result.status === "done" && opts.getAutonomy) {
3656
+ const autonomy = opts.getAutonomy();
3657
+ if (autonomy === "suggest" && !hasOpenTodos2(opts.agent.ctx.todos)) {
3658
+ const suggestPrompt = 'Based on what you just did, suggest 3 exact prompt messages that can be submitted back to you through the TUI or WebUI. Each prompt must ask the agent to perform work; never assign a manual chore to the user. If you include suggestions, wrap them in a balanced <nextsteps>...</nextsteps> block, with one numbered prompt per line and no explanation. If there is nothing meaningful left, say "No further steps needed."';
3659
+ const suggestBlocks = [{ type: "text", text: suggestPrompt }];
3660
+ const suggestCtrl = new AbortController();
3661
+ activeCtrl = suggestCtrl;
3662
+ try {
3663
+ const suggestResult = await opts.agent.run(suggestBlocks, {
3664
+ signal: suggestCtrl.signal
3665
+ });
3666
+ opts.onAgentIterationComplete?.(
3667
+ estimateRequestTokensCalibrated2(
3668
+ opts.agent.ctx.messages,
3669
+ opts.agent.ctx.systemPrompt,
3670
+ opts.agent.ctx.tools ?? []
3671
+ ).total
3672
+ );
3673
+ if (suggestResult.status === "done" && suggestResult.finalText) {
3674
+ opts.renderer.write(
3675
+ `
3676
+ ${color9.cyan(" Suggested next steps:")}
3677
+ ${suggestResult.finalText}
3678
+ `
3679
+ );
3680
+ if (opts.onSuggestionsParsed) {
3681
+ const parsed = parseSuggestionsFromOutput(
3682
+ suggestResult.finalText,
3683
+ opts.agent.ctx.todos
3684
+ );
3685
+ opts.onSuggestionsParsed(parsed);
3686
+ }
3687
+ }
3688
+ } catch {
3689
+ } finally {
3690
+ activeCtrl = void 0;
3691
+ }
3692
+ }
3693
+ }
3694
+ if (result.status === "done" && opts.getNextPredict?.() && !hasOpenTodos2(opts.agent.ctx.todos)) {
3695
+ const autonomy = opts.getAutonomy?.() ?? "off";
3696
+ if (autonomy === "off") {
3697
+ const predictCtrl = new AbortController();
3698
+ activeCtrl = predictCtrl;
3699
+ try {
3700
+ const predictions = await predictNextTasks(
3701
+ {
3702
+ userRequest: trimmed,
3703
+ assistantSummary: result.finalText ?? "",
3704
+ todos: opts.agent.ctx.todos
3705
+ },
3706
+ {
3707
+ provider: opts.agent.ctx.provider,
3708
+ model: opts.agent.ctx.model,
3709
+ signal: predictCtrl.signal
3710
+ }
3711
+ );
3712
+ if (predictions.length > 0) {
3713
+ const lines = predictions.map((p, i) => ` ${i + 1}. ${p}`).join("\n");
3714
+ opts.renderer.write(`
3715
+ ${color9.dim(" \u21B3 likely next:")}
3716
+ ${color9.dim(lines)}
3717
+ `);
3718
+ }
3719
+ } catch {
3720
+ } finally {
3721
+ activeCtrl = void 0;
3722
+ }
3723
+ }
3724
+ }
3725
+ } catch (err) {
3726
+ opts.renderer.writeError(toErrorMessage(err));
3727
+ } finally {
3728
+ activeCtrl = void 0;
3729
+ }
3730
+ }
3731
+ return 0;
3732
+ } finally {
3733
+ process.off("SIGINT", onSigint);
3734
+ await opts.reader.close().catch(() => {
3735
+ });
3736
+ replClientRegistration.close();
3737
+ opts.onDestroy?.();
3738
+ }
3739
+ }
3740
+ async function pasteClipboardImage(builder, opts) {
3741
+ try {
3742
+ const img = await readClipboardImage();
3743
+ if (!img) {
3744
+ opts.renderer.write(color9.dim(" no image on clipboard\n"));
3745
+ return;
3746
+ }
3747
+ const placeholder = await builder.appendImage(img.base64, img.mediaType);
3748
+ const kb = (img.bytes / 1024).toFixed(0);
3749
+ opts.renderer.write(color9.dim(` \u21B3 ${placeholder} (PNG ${kb}KB)
3750
+ `));
3751
+ } catch (err) {
3752
+ opts.renderer.writeError(
3753
+ `Clipboard image error: ${toErrorMessage(err)}`
3754
+ );
3755
+ }
3756
+ }
3757
+ async function loadGoalSafe(opts) {
3758
+ if (!opts.projectRoot) return null;
3759
+ try {
3760
+ return await loadGoal(goalFilePath(opts.projectRoot));
3761
+ } catch {
3762
+ return null;
3763
+ }
3764
+ }
3765
+ async function renderGoalBanner(opts) {
3766
+ const goal = await loadGoalSafe(opts);
3767
+ if (!goal) return;
3768
+ const summary = goal.goal.length > 80 ? `${goal.goal.slice(0, 77)}\u2026` : goal.goal;
3769
+ const stateColor = goal.goalState === "active" ? color9.green : goal.goalState === "paused" ? color9.amber : goal.goalState === "completed" ? color9.green : goal.goalState === "abandoned" ? color9.dim : color9.dim;
3770
+ opts.renderer.write(
3771
+ color9.dim("Goal: ") + stateColor(summary) + color9.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
3772
+ );
3773
+ if (goal.journal.length > 0) {
3774
+ const lastEntry = expectDefined(goal.journal[goal.journal.length - 1]);
3775
+ const statusIcon = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
3776
+ opts.renderer.write(
3777
+ color9.dim(` Last: ${statusIcon} ${lastEntry.task} (${lastEntry.status})`) + "\n"
3778
+ );
3779
+ }
3780
+ if (goal.engineState === "running") {
3781
+ opts.renderer.write(
3782
+ color9.amber(" \u21BA Eternal engine was running when last session ended.") + "\n"
3783
+ );
3784
+ try {
3785
+ const answer = (await opts.reader.readLine(color9.dim(" Resume eternal mode? [y/N] "))).trim().toLowerCase();
3786
+ if (answer === "y" || answer === "yes") {
3787
+ try {
3788
+ await opts.slashRegistry.dispatch("/autonomy eternal", opts.agent.ctx);
3789
+ } catch (err) {
3790
+ opts.renderer.writeError(
3791
+ `Auto-resume failed: ${toErrorMessage(err)}`
3792
+ );
3793
+ }
3794
+ } else {
3795
+ opts.renderer.write(
3796
+ color9.dim(" Not resuming. Use `/autonomy eternal` later to continue.") + "\n"
3797
+ );
3798
+ }
3799
+ } catch {
3800
+ opts.renderer.write(color9.dim(" Use `/autonomy eternal` to resume.") + "\n");
3801
+ }
3802
+ } else if (goal.goalState === "paused") {
3803
+ opts.renderer.write(color9.amber(" \u23F8 Goal is paused. Use `/goal resume` to continue.") + "\n");
3804
+ } else if (goal.goalState === "completed") {
3805
+ opts.renderer.write(
3806
+ color9.green(" \u2713 Goal completed! Use `/goal clear` to set a new goal.") + "\n"
3807
+ );
3808
+ } else if (goal.goalState === "abandoned") {
3809
+ opts.renderer.write(color9.dim(" Use `/goal clear` to set a new goal.") + "\n");
3810
+ }
3811
+ opts.renderer.write("\n");
3812
+ }
3813
+
3814
+ // src/execution-tui-next-step-callbacks.ts
3815
+ function createTuiNextStepCallbacks(input) {
3816
+ const { context, getNextPredict, getAutonomy, getSuggestions: getSuggestions2 } = input;
3817
+ return {
3818
+ async predictNext(request) {
3819
+ if (!getNextPredict?.()) return [];
3820
+ if ((getAutonomy?.() ?? "off") !== "off") return [];
3821
+ return predictNextTasks(
3822
+ { ...request, todos: context.todos ?? [] },
3823
+ {
3824
+ provider: context.provider,
3825
+ model: context.model
3826
+ }
3827
+ );
3828
+ },
3829
+ onSuggestionsParsed(finalText) {
3830
+ const parsed = parseSuggestionsFromOutput(finalText, context.todos);
3831
+ setSuggestions(parsed ?? []);
3832
+ },
3833
+ getSuggestions: () => getSuggestions2?.() ?? [],
3834
+ setSuggestions
3835
+ };
3836
+ }
3837
+
3838
+ // src/execution.ts
3839
+ async function execute(deps) {
3840
+ const {
3841
+ core: {
3842
+ agent,
3843
+ events,
3844
+ config,
3845
+ configStore,
3846
+ wpaths: initialWpaths,
3847
+ projectRoot: initialProjectRoot,
3848
+ flags,
3849
+ positional,
3850
+ slashRegistry,
3851
+ tokenCounter,
3852
+ activateSessionIdentity,
3853
+ updateInfo: initialUpdateInfo
3854
+ },
3855
+ session: {
3856
+ session,
3857
+ context,
3858
+ attachments,
3859
+ queueStore,
3860
+ mcpRegistry,
3861
+ mailbox,
3862
+ sessionStore,
3863
+ memoryStore,
3864
+ modeStore,
3865
+ detachTodosCheckpoint,
3866
+ rebindTodosCheckpoint,
3867
+ restoredMessages,
3868
+ restoredToolCalls,
3869
+ restoredEvents,
3870
+ needsSetup
3871
+ },
3872
+ provider: {
3873
+ modelsRegistry,
3874
+ savedProviderCfg,
3875
+ resolvedProvider,
3876
+ statusTracker,
3877
+ getPickableProviders,
3878
+ switchProviderAndModel,
3879
+ onModelContextResolved,
3880
+ sddSubagentFactory
3881
+ },
3882
+ ui: {
3883
+ renderer,
3884
+ reader,
3885
+ secretInputController,
3886
+ stats,
3887
+ effectiveMaxContext,
3888
+ getEffectiveMaxContext,
3889
+ skillLoader,
3890
+ promptLoader,
3891
+ modeId
3892
+ },
3893
+ fleet: {
3894
+ director,
3895
+ getDirector,
3896
+ coordinatorController,
3897
+ fleetRoster,
3898
+ fleetStreamController,
3899
+ agentsMonitorController,
3900
+ agentTranscripts,
3901
+ authHost,
3902
+ onPanelOpen
3903
+ },
3904
+ controllers: {
3905
+ interruptController,
3906
+ enhanceController,
3907
+ getEnhancerReasoning,
3908
+ buildEnhancerProvider,
3909
+ getEnhanceFallbackRef,
3910
+ getConfiguredRefinerRef,
3911
+ statuslineHiddenItems,
3912
+ setStatuslineHiddenItems,
3913
+ saveStatuslineHiddenItems,
3914
+ getYolo,
3915
+ onYolo,
3916
+ getAutonomy,
3917
+ onAutonomy,
3918
+ getNextPredict,
3919
+ applyLiveSettings,
3920
+ onCountdownTick
3921
+ },
3922
+ picker: {
3923
+ getPluginItems,
3924
+ onPluginToggle,
3925
+ getMcpServers,
3926
+ onMcpToggle,
3927
+ onMcpRestart,
3928
+ getToolsItems,
3929
+ onToolToggle,
3930
+ getBrainData,
3931
+ onBrainRiskLevel,
3932
+ getBrainLog,
3933
+ brain,
3934
+ brainSettings,
3935
+ brainRuntime,
3936
+ getShadowData,
3937
+ onShadowStart,
3938
+ onShadowStop
3939
+ } = {},
3940
+ lifecycles: {
3941
+ getSuggestions: getSuggestions2,
3942
+ getAutoSuggestions,
3943
+ onSuggestionsParsed,
3944
+ autonomyNextPrompt,
3945
+ autoProceedDelayMs,
3946
+ autoProceedMaxIterations,
3947
+ onValidateAutoProceed,
3948
+ getEternalEngine,
3949
+ getParallelEngine,
3950
+ getSddRun,
3951
+ onSddLifecycle,
3952
+ subscribeEternalIteration,
3953
+ subscribeEternalStage,
3954
+ onDestroy,
3955
+ onCoordinatorStop
3956
+ } = {}
3957
+ } = deps;
3958
+ let onCoordinatorStopImpl = onCoordinatorStop;
3959
+ const wpaths = initialWpaths;
3960
+ const projectRoot = initialProjectRoot;
3961
+ const activeSessionStore = sessionStore;
3962
+ const detachActiveTodosCheckpoint = detachTodosCheckpoint;
3963
+ const profileName = config.activeProfile ?? "default";
3964
+ const bootUpdateInfo = initialUpdateInfo;
3965
+ const offStorageObservability = installStorageObservability(events, context.traceId);
3966
+ let pendingChimeraWork;
3967
+ events.onPattern("chimera.review_needed", (_event, payload) => {
3968
+ const p = payload;
3969
+ const dir = director;
3970
+ if (!dir) {
3971
+ return;
3972
+ }
3973
+ if (p.files.length === 0) return;
3974
+ pendingChimeraWork = (async () => {
3975
+ let subagentId;
3976
+ try {
3977
+ const taskDesc = buildChimeraReviewTaskDescription(p);
3978
+ const tProvider = config.provider?.trim() || void 0;
3979
+ const tModel = config.model?.trim() || void 0;
3980
+ const rawProvider = p.reviewFallbackModels ? p.config.provider?.trim() || void 0 : tProvider;
3981
+ const rawModel = p.reviewFallbackModels ? p.config.model?.trim() || void 0 : tModel;
3982
+ const baseProvider = rawProvider || tProvider || config.provider;
3983
+ const baseModel = rawModel || tModel || config.model;
3984
+ const baseFallbacks = p.reviewFallbackModels ? [...p.reviewFallbackModels] : resolveReviewerFallbackModels(void 0);
3985
+ const assigned = assignReviewerModelsRoundRobin(baseProvider, baseModel, baseFallbacks);
3986
+ const cfg = applyChimeraReviewerReadOnlyPolicy({
3987
+ name: "chimera-review",
3988
+ role: "reviewer",
3989
+ systemPromptOverride: CHIMERA_REVIEW_PROMPT,
3990
+ maxIterations: 50,
3991
+ maxToolCalls: 250,
3992
+ timeoutMs: 9e5,
3993
+ provider: assigned.provider,
3994
+ model: assigned.model,
3995
+ fallbackModels: assigned.fallbackModels
3996
+ });
3997
+ subagentId = await dir.spawn(cfg);
3998
+ const taskId = randomUUID4();
3999
+ await dir.assign({
4000
+ id: taskId,
4001
+ description: taskDesc,
4002
+ subagentId
4003
+ });
4004
+ const results = await dir.awaitTasks([taskId]);
4005
+ const result = results[0];
4006
+ if (result?.status !== "success") {
4007
+ try {
4008
+ await session.append({
4009
+ type: "error",
4010
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4011
+ message: `\u{1F982} Chimera review subagent ${result?.status ?? "unknown"}: ${result?.error?.message ?? "no result"}`,
4012
+ phase: "agent"
4013
+ });
4014
+ } catch (err) {
4015
+ console.error(
4016
+ JSON.stringify({
4017
+ level: "error",
4018
+ event: "execution.chimera_append_failed",
4019
+ message: err instanceof Error ? err.message : String(err),
4020
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4021
+ })
4022
+ );
4023
+ }
4024
+ events.emitCustom("chimera.review_complete", {
4025
+ bundle: p,
4026
+ reviewText: "",
4027
+ status: result?.status ?? "unknown",
4028
+ cwd: p.cwd,
4029
+ sessionId: session.id
4030
+ });
4031
+ return;
4032
+ }
4033
+ const reviewText = typeof result.result === "string" ? result.result.trim() : JSON.stringify(result.result);
4034
+ events.emitCustom("chimera.review_complete", {
4035
+ bundle: p,
4036
+ reviewText,
4037
+ status: "success",
4038
+ cwd: p.cwd,
4039
+ sessionId: session.id
4040
+ });
4041
+ if (reviewText) {
4042
+ await session.append({
4043
+ type: "llm_response",
4044
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4045
+ content: [{ type: "text", text: reviewText }],
4046
+ stopReason: "end_turn",
4047
+ usage: { input: 0, output: 0 }
4048
+ });
4049
+ const autoFix = agent.ctx.meta["chimeraAutoFix"] ?? p.config.autoFix ?? "off";
4050
+ const reviewHasFindings = !isChimeraAllClearReview(reviewText);
4051
+ const isAskMode = autoFix === "ask" && reviewHasFindings;
4052
+ const mailboxType = isAskMode ? "ask" : "result";
4053
+ const subject = isAskMode ? `\u{1F982} Chimera review \u2014 ${p.files.length} file(s) changed. Shall I fix the findings?` : `\u{1F982} Chimera review \u2014 ${p.files.length} file(s) changed`;
4054
+ let leaderApproved = false;
4055
+ let spawnedFix = false;
4056
+ let askMailMsgId;
4057
+ let leaderOnline = false;
4058
+ if (isAskMode) {
4059
+ try {
4060
+ const onlineAgents = await mailbox.getOnlineAgents();
4061
+ leaderOnline = onlineAgents.some(
4062
+ (a) => a.online && isMailboxLeader(a.agentId, a.role)
4063
+ );
4064
+ } catch (leaderCheckErr) {
4065
+ console.warn(
4066
+ JSON.stringify({
4067
+ level: "warn",
4068
+ event: "execution.chimera_leader_check_failed",
4069
+ message: leaderCheckErr instanceof Error ? leaderCheckErr.message : String(leaderCheckErr),
4070
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4071
+ })
4072
+ );
4073
+ }
4074
+ if (!leaderOnline) {
4075
+ console.warn(
4076
+ JSON.stringify({
4077
+ level: "warn",
4078
+ event: "execution.chimera_mailbox_leader_offline",
4079
+ message: `Leader not online \u2014 mailbox ${mailboxType} from chimera-review to leader will be delivered when leader reconnects`,
4080
+ fileCount: p.files.length,
4081
+ autoFix,
4082
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4083
+ })
4084
+ );
4085
+ }
4086
+ } else {
4087
+ leaderOnline = true;
4088
+ console.debug(
4089
+ JSON.stringify({
4090
+ level: "debug",
4091
+ event: "execution.chimera_auto_no_presence_check",
4092
+ message: "Result mode: leader presence not checked \u2014 mailbox message delivered regardless",
4093
+ fileCount: p.files.length,
4094
+ autoFix,
4095
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4096
+ })
4097
+ );
4098
+ }
4099
+ try {
4100
+ const askTimeoutRaw = agent.ctx.meta["chimeraAskTimeoutMs"] ?? 3e4;
4101
+ const ASK_TIMEOUT_MS = Number.isFinite(askTimeoutRaw) ? Math.max(0, askTimeoutRaw) : 3e4;
4102
+ const directive = isAskMode ? `LEADER ACTION REQUIRED: The Chimera review below found issues. Reply with "yes" to auto-fix, or "no" to skip (${ASK_TIMEOUT_MS / 1e3}s timeout).` : reviewHasFindings ? "LEADER ACTION REQUIRED: The Chimera review below found issues. Read the report and fix any Critical or High findings automatically using the edit tool. Verify with typecheck and linter." : "Chimera review completed with no findings. Read the report summary below; no fix action is requested.";
4103
+ const reviewBody = reviewText.length > 7500 ? truncateAtCodePointBoundary(reviewText, 7500) + "\n\n\u2026(truncated, full report in session transcript)" : reviewText;
4104
+ const body = `${directive}
4105
+
4106
+ ${reviewBody}`;
4107
+ const mailMsg = await mailbox.send({
4108
+ from: "chimera-review",
4109
+ to: "leader",
4110
+ type: mailboxType,
4111
+ audience: "leaders",
4112
+ subject,
4113
+ body,
4114
+ priority: "normal"
4115
+ });
4116
+ if (!mailMsg?.id) throw new Error("mailbox.send returned no message id");
4117
+ askMailMsgId = mailMsg.id;
4118
+ if (isAskMode) {
4119
+ leaderApproved = await waitForChimeraAskApproval({
4120
+ mailbox,
4121
+ messageId: askMailMsgId,
4122
+ meta: agent.ctx.meta,
4123
+ session: agent.ctx.session ?? session,
4124
+ askTimeoutMs: ASK_TIMEOUT_MS
4125
+ });
4126
+ }
4127
+ } catch (mailErr) {
4128
+ const errMsg = mailErr instanceof Error ? mailErr.message : String(mailErr);
4129
+ console.error(
4130
+ JSON.stringify({
4131
+ level: "error",
4132
+ event: "execution.chimera_mailbox_failed",
4133
+ message: errMsg,
4134
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4135
+ })
4136
+ );
4137
+ await session.append({
4138
+ type: "error",
4139
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4140
+ message: `\u{1F982} Chimera auto-fix skipped \u2014 mailbox unreachable: ${errMsg}. Falling back to manual review mode.`,
4141
+ phase: "agent"
4142
+ });
4143
+ }
4144
+ events.emitCustom("chimera.mailbox_delivered", {
4145
+ subject,
4146
+ autoFixMode: autoFix,
4147
+ fileCount: p.files.length,
4148
+ reviewLength: reviewText.length
4149
+ });
4150
+ if (!leaderOnline) {
4151
+ try {
4152
+ await mailbox.send({
4153
+ from: "chimera-review",
4154
+ to: "leader",
4155
+ type: "note",
4156
+ audience: "leaders",
4157
+ subject: `\u23F0 Chimera review pending \u2014 ${p.files.length} file(s) checked`,
4158
+ body: `The leader was offline when a chimera review completed. A full review result with "LEADER ACTION REQUIRED" directive is waiting in this mailbox from chimera-review. Open it, read the findings, and fix any Critical or High issues.`,
4159
+ priority: "high"
4160
+ });
4161
+ } catch (wakeErr) {
4162
+ console.warn(
4163
+ JSON.stringify({
4164
+ level: "warn",
4165
+ event: "execution.chimera_wakeup_companion_failed",
4166
+ message: wakeErr instanceof Error ? wakeErr.message : String(wakeErr),
4167
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4168
+ })
4169
+ );
4170
+ }
4171
+ }
4172
+ const shouldSpawnFix = !spawnedFix && reviewHasFindings && (autoFix === "auto" || isAskMode && leaderApproved) && reviewText.length > 0;
4173
+ if (shouldSpawnFix) {
4174
+ spawnedFix = true;
4175
+ const fixTaskDesc = [
4176
+ `You are a fix agent. Apply the fixes requested in this review report.`,
4177
+ ``,
4178
+ `Repository: ${p.cwd}`,
4179
+ ``,
4180
+ `--- Review report ---`,
4181
+ truncateAtCodePointBoundary(reviewText, 12e3),
4182
+ ``,
4183
+ `--- Changed files ---`,
4184
+ p.files.map((f) => `- ${f.path}`).join("\n"),
4185
+ ``,
4186
+ `Read each file, understand the issue, apply fixes using the edit tool.`,
4187
+ `After fixing, run the project's typecheck and linter to verify.`,
4188
+ `Do NOT remove or reorder existing code unless the bug requires it.`
4189
+ ].join("\n");
4190
+ try {
4191
+ const fixCfg = {
4192
+ name: "chimera-fix",
4193
+ role: "fixer",
4194
+ maxIterations: 60,
4195
+ maxToolCalls: 350,
4196
+ timeoutMs: 12e5
4197
+ };
4198
+ const fixSubagentId = await dir.spawn(fixCfg);
4199
+ try {
4200
+ const fixTaskId = randomUUID4();
4201
+ await dir.assign({
4202
+ id: fixTaskId,
4203
+ description: fixTaskDesc,
4204
+ subagentId: fixSubagentId
4205
+ });
4206
+ const fixResults = await dir.awaitTasks([fixTaskId]);
4207
+ const fixResult = fixResults[0];
4208
+ if (fixResult?.status === "success") {
4209
+ await session.append({
4210
+ type: "llm_response",
4211
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4212
+ content: [
4213
+ { type: "text", text: `Chimera fix subagent completed: ${fixResult.result}` }
4214
+ ],
4215
+ stopReason: "end_turn",
4216
+ usage: { input: 0, output: 0 }
4217
+ });
4218
+ } else {
4219
+ await session.append({
4220
+ type: "error",
4221
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4222
+ message: `Chimera fix subagent ${fixResult?.status ?? "unknown"}: ${fixResult?.error?.message ?? "no result"}`,
4223
+ phase: "agent"
4224
+ });
4225
+ }
4226
+ } finally {
4227
+ try {
4228
+ await dir.terminate(fixSubagentId);
4229
+ } catch {
4230
+ }
4231
+ }
4232
+ } catch (fixErr) {
4233
+ await session.append({
4234
+ type: "error",
4235
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4236
+ message: `\u{1F982} Chimera auto-fix failed: ${fixErr instanceof Error ? fixErr.message : String(fixErr)}`,
4237
+ phase: "agent"
4238
+ });
4239
+ }
4240
+ }
4241
+ }
4242
+ } catch (err) {
4243
+ try {
4244
+ await session.append({
4245
+ type: "error",
4246
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
4247
+ message: `\u{1F982} Chimera review failed: ${err instanceof Error ? err.message : String(err)}`,
4248
+ phase: "agent"
4249
+ });
4250
+ } catch (appendErr) {
4251
+ console.error(
4252
+ JSON.stringify({
4253
+ level: "error",
4254
+ event: "execution.chimera_review_append_failed",
4255
+ message: appendErr instanceof Error ? appendErr.message : String(appendErr),
4256
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4257
+ })
4258
+ );
4259
+ }
4260
+ } finally {
4261
+ if (subagentId) {
4262
+ try {
4263
+ await dir.terminate(subagentId);
4264
+ } catch {
4265
+ }
4266
+ }
4267
+ }
4268
+ })();
4269
+ });
4270
+ installChimeraCascadeHandler({
4271
+ events,
4272
+ director,
4273
+ session,
4274
+ getPendingWork: () => pendingChimeraWork,
4275
+ setPendingWork: (work) => {
4276
+ pendingChimeraWork = work;
4277
+ }
4278
+ });
4279
+ let code = 0;
4280
+ let fleetStatusLine = null;
4281
+ try {
4282
+ const visionAdapters = () => createToolVisionAdapters(agent.tools);
4283
+ const supportsVision = async () => {
4284
+ try {
4285
+ const providerConfig = config.providers?.[context.provider.id];
4286
+ const mergedModels = mergeCustomModelDefs(providerConfig?.customModels, config.models);
4287
+ const caps = await capabilitiesFor(
4288
+ modelsRegistry,
4289
+ context.provider.id,
4290
+ context.model,
4291
+ mergedModels
4292
+ );
4293
+ return caps.vision;
4294
+ } catch {
4295
+ return context.provider.capabilities.vision;
4296
+ }
4297
+ };
4298
+ const promptFlag = typeof flags["prompt"] === "string" ? flags["prompt"] : void 0;
4299
+ if (promptFlag) {
4300
+ positional.unshift(promptFlag);
4301
+ }
4302
+ const goalFlag = typeof flags["goal"] === "string" ? flags["goal"] : void 0;
4303
+ const askFlag = typeof flags["ask"] === "string" ? flags["ask"] : void 0;
4304
+ if ((goalFlag || askFlag) && positional.length === 0 && !promptFlag) {
4305
+ flags.tui = true;
4306
+ }
4307
+ const executionMode = resolveExecutionMode(positional, flags);
4308
+ const enteringTui = executionMode === "tui";
4309
+ if (!enteringTui) {
4310
+ fleetStatusLine = new FleetStatusLine({ events, version: CLI_VERSION });
4311
+ fleetStatusLine.start();
4312
+ }
4313
+ if (executionMode === "single-shot") {
4314
+ code = await runSingleShotDispatch({
4315
+ agent,
4316
+ query: positional.join(" "),
4317
+ flags,
4318
+ tokenCounter,
4319
+ renderer
4320
+ });
4321
+ } else if (executionMode === "tui") {
4322
+ agent.disableInteractiveConfirmation();
4323
+ const state = {
4324
+ projectRoot,
4325
+ wpaths,
4326
+ activeSessionStore,
4327
+ activateSessionIdentity,
4328
+ detachActiveTodosCheckpoint,
4329
+ pendingProjectSwitch: null,
4330
+ autonomousCoordinator: null,
4331
+ coordinatorRun: null,
4332
+ coordinatorEvents: /* @__PURE__ */ new Set()
4333
+ };
4334
+ const banneredFamily = savedProviderCfg?.family ?? resolvedProvider?.family;
4335
+ const banneredKey = (savedProviderCfg ? resolveActiveApiKey(savedProviderCfg) : void 0) ?? config.apiKey ?? (resolvedProvider?.envVars ?? savedProviderCfg?.envVars ?? []).map((v) => process.env[v]).find((v) => !!v);
4336
+ const banneredKeyTail = banneredKey && banneredKey.length >= 3 ? banneredKey.slice(-3) : void 0;
4337
+ const goalWiring = wireGoal(events);
4338
+ const subscribeGoal = goalWiring.subscribe;
4339
+ const coordinatorEvents = /* @__PURE__ */ new Set();
4340
+ state.coordinatorEvents = coordinatorEvents;
4341
+ const autonomousCoordinationEnabled = config.features.autonomousCoordination !== false;
4342
+ const coordinatorSetup = autonomousCoordinationEnabled ? setupAutonomousCoordinator({
4343
+ state,
4344
+ events,
4345
+ context,
4346
+ wpaths,
4347
+ mailbox,
4348
+ director,
4349
+ getDirector,
4350
+ coordinatorController,
4351
+ onCoordinatorStopSetter: (fn) => {
4352
+ onCoordinatorStopImpl = fn ?? void 0;
4353
+ }
4354
+ }) : {
4355
+ ensure: () => null,
4356
+ cleanup: () => void 0
4357
+ };
4358
+ const ensureAutonomousCoordinator = coordinatorSetup.ensure;
4359
+ const offDirectorSpawned = coordinatorSetup.cleanup;
4360
+ const switchCtx = {
4361
+ state,
4362
+ context,
4363
+ events,
4364
+ agent,
4365
+ config,
4366
+ tokenCounter,
4367
+ modeId,
4368
+ modeStore,
4369
+ memoryStore,
4370
+ skillLoader,
4371
+ attachTodosCheckpoint: attachTodosCheckpoint2
4372
+ };
4373
+ const switchProjectInPlace2 = async (targetRoot, displayName) => {
4374
+ return switchProjectInPlace(switchCtx, targetRoot, displayName);
4375
+ };
4376
+ const pickerCtx = {
4377
+ state,
4378
+ renderer,
4379
+ director,
4380
+ getEternalEngine,
4381
+ getParallelEngine,
4382
+ switchCtx,
4383
+ switchProjectInPlace: switchProjectInPlace2
4384
+ };
4385
+ renderer.setTuiActive(true);
4386
+ try {
4387
+ code = await runTuiDispatch({
4388
+ agent,
4389
+ events,
4390
+ slashRegistry,
4391
+ secretInputController,
4392
+ attachments,
4393
+ tokenCounter,
4394
+ visionAdapters,
4395
+ supportsVision,
4396
+ model: context.model,
4397
+ banner: !flags["no-banner"],
4398
+ queueStore,
4399
+ onQueueChange: (items) => {
4400
+ setQueuedMessagesSnapshot2(context, items);
4401
+ },
4402
+ mouse: flags.mouse ? true : void 0,
4403
+ yolo: !!config.yolo,
4404
+ getYolo,
4405
+ onYolo,
4406
+ getAutonomy,
4407
+ ...createTuiNextStepCallbacks({
4408
+ context,
4409
+ getNextPredict,
4410
+ getAutonomy,
4411
+ getSuggestions: getSuggestions2
4412
+ }),
4413
+ getEternalEngine,
4414
+ getSddRun,
4415
+ onSddLifecycle,
4416
+ subscribeEternalIteration,
4417
+ subscribeEternalStage,
4418
+ subscribeGoal,
4419
+ appVersion: CLI_VERSION,
4420
+ latestVersion: bootUpdateInfo?.latest,
4421
+ updateAvailable: bootUpdateInfo?.outdated,
4422
+ provider: config.provider,
4423
+ family: banneredFamily,
4424
+ keyTail: banneredKeyTail,
4425
+ profile: profileName,
4426
+ profileConfigPath: (() => {
4427
+ const abs = wpaths.profileConfig(profileName);
4428
+ const home = wpaths.homeDir;
4429
+ return home && abs.startsWith(home) ? `~${abs.slice(home.length)}` : abs;
4430
+ })(),
4431
+ getPickableProviders,
4432
+ switchProviderAndModel,
4433
+ switchAutonomy: (mode) => {
4434
+ onAutonomy?.(mode);
4435
+ return null;
4436
+ },
4437
+ ...createSettingsAdapter({
4438
+ configStore,
4439
+ wpaths,
4440
+ fleetStreamController,
4441
+ applyLiveSettings
4442
+ }),
4443
+ effectiveMaxContext,
4444
+ titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
4445
+ chime: config.autonomy?.["chime"] ?? false,
4446
+ confirmExit: config.autonomy?.["confirmExit"] ?? true,
4447
+ director,
4448
+ getDirector,
4449
+ fleetRoster,
4450
+ ...createTuiCoordinatorCallbacks({
4451
+ state,
4452
+ context,
4453
+ coordinatorEvents,
4454
+ ensureAutonomousCoordinator
4455
+ }),
4456
+ onClearHistory: (dispatch) => {
4457
+ dispatch({ type: "clearHistory", model: context.model, provider: context.provider.id });
4458
+ dispatch({ type: "resetContextChip" });
4459
+ dispatch({ type: "streamReset" });
4460
+ dispatch({ type: "toolStreamClear" });
4461
+ },
4462
+ fleetStreamController,
4463
+ interruptController,
4464
+ enhanceController,
4465
+ getEnhancerReasoning,
4466
+ buildEnhancerProvider,
4467
+ getEnhanceFallbackRef,
4468
+ getConfiguredRefinerRef,
4469
+ statuslineHiddenItems,
4470
+ setStatuslineHiddenItems,
4471
+ saveStatuslineHiddenItems,
4472
+ getPluginItems,
4473
+ onPluginToggle,
4474
+ getMcpServers,
4475
+ onMcpToggle,
4476
+ onMcpRestart,
4477
+ getToolsItems,
4478
+ onToolToggle,
4479
+ getBrainData,
4480
+ onBrainRiskLevel,
4481
+ brainPanelHost: brainRuntime ? createBrainPanelHost({ brainRuntime }) : void 0,
4482
+ getShadowData,
4483
+ onShadowStart,
4484
+ onShadowStop,
4485
+ authHost,
4486
+ agentsMonitorController,
4487
+ agentTranscripts,
4488
+ getLiveSessions: () => getLiveSessions({ state }),
4489
+ onSwitchToSession: (_sessionId, targetRoot, projectName) => onSwitchToSession({ state }, _sessionId, targetRoot, projectName),
4490
+ initialGoal: goalFlag,
4491
+ initialAsk: askFlag,
4492
+ projectRoot,
4493
+ appConfig: config,
4494
+ hqTelemetryOwnedExternally: true,
4495
+ getSessionId: () => agent.ctx.session?.id ?? session.id,
4496
+ getSDDContext: () => getSDDContext(),
4497
+ onSDDOutput: (output) => onSDDOutput(output),
4498
+ modeLabel: modeId,
4499
+ getModeLabel: () => {
4500
+ const metaMode = context.meta?.["mode"];
4501
+ return typeof metaMode === "string" ? metaMode : modeId ?? "default";
4502
+ },
4503
+ getModes: modeStore ? async () => {
4504
+ const [modes, active] = await Promise.all([
4505
+ modeStore.listModes(),
4506
+ modeStore.getActiveMode()
4507
+ ]);
4508
+ return { modes, activeId: active?.id ?? null };
4509
+ } : void 0,
4510
+ switchMode: modeStore ? async (id) => {
4511
+ const prev = await modeStore.getActiveMode();
4512
+ await modeStore.setActiveMode(id);
4513
+ const active = await modeStore.getActiveMode();
4514
+ const from = prev?.id ?? "default";
4515
+ if (agent.ctx.session && from !== id) {
4516
+ void agent.ctx.session.append({ type: "mode_changed", ts: (/* @__PURE__ */ new Date()).toISOString(), from, to: id }).catch(() => {
4517
+ });
4518
+ }
4519
+ return active?.name ?? null;
4520
+ } : void 0,
4521
+ registerDebugStreamCallback,
4522
+ restoreDebugStreamCallback,
4523
+ restoredMessages,
4524
+ restoredToolCalls,
4525
+ restoredEvents,
4526
+ listSessions: async (limit = 20) => {
4527
+ if (!activeSessionStore) return [];
4528
+ const summaries = await activeSessionStore.list(limit);
4529
+ const currentId = agent.ctx.session?.id ?? session.id;
4530
+ return summaries.map((s) => ({
4531
+ id: s.id,
4532
+ title: s.title ?? "",
4533
+ name: s.name,
4534
+ lastUserMessage: s.lastUserMessage,
4535
+ messageCount: s.messageCount,
4536
+ lastActivityAt: s.lastActivityAt,
4537
+ startedAt: s.startedAt ?? "",
4538
+ endedAt: s.endedAt,
4539
+ tokenTotal: s.tokenTotal ?? 0,
4540
+ iterationCount: s.iterationCount ?? 0,
4541
+ toolCallCount: s.toolCallCount ?? 0,
4542
+ toolErrorCount: s.toolErrorCount ?? 0,
4543
+ outcome: s.outcome,
4544
+ isCurrent: s.id === currentId
4545
+ }));
4546
+ },
4547
+ onResumeSession: (sessionId) => resumeSession(
4548
+ { state, agent, tokenCounter, switchProviderAndModel, events },
4549
+ sessionId
4550
+ ),
4551
+ getProjectPickerItems: () => getProjectPickerItems(pickerCtx),
4552
+ onProjectSelect: (slug, kind) => onProjectSelect(pickerCtx, slug, kind),
4553
+ initialAgentsMonitorOpen: !!flags.quick,
4554
+ tokenSavingMode: normalizeTokenSavingTier2(config.features.tokenSavingMode),
4555
+ toolCount: agent.tools.list().length,
4556
+ onPanelOpen,
4557
+ memoryStore
4558
+ });
4559
+ const spawnResult = await handleProjectSwitchSpawn({
4560
+ code,
4561
+ pendingProjectSwitch: state.pendingProjectSwitch
4562
+ });
4563
+ if (spawnResult !== null) return spawnResult;
4564
+ } finally {
4565
+ renderer.setTuiActive(false);
4566
+ offDirectorSpawned();
4567
+ }
4568
+ } else if (executionMode === "webui") {
4569
+ code = await runWebUIDispatch({
4570
+ agent,
4571
+ events,
4572
+ session,
4573
+ config,
4574
+ flags,
4575
+ projectRoot,
4576
+ globalConfigPath: wpaths.globalConfig,
4577
+ profileConfigPath: wpaths.profileConfig(profileName),
4578
+ projectSessionsDir: wpaths.projectSessions,
4579
+ modelsRegistry,
4580
+ mcpRegistry,
4581
+ brain,
4582
+ brainSettings,
4583
+ brainRuntime,
4584
+ getBrainLog,
4585
+ subscribeEternalIteration,
4586
+ sessionStore: activeSessionStore,
4587
+ memoryStore,
4588
+ skillLoader,
4589
+ promptLoader,
4590
+ modeStore,
4591
+ modeId,
4592
+ needsSetup,
4593
+ renderer,
4594
+ onAutonomy,
4595
+ applyLiveSettings,
4596
+ activateSessionIdentity,
4597
+ rebindTodosCheckpoint,
4598
+ agentTranscripts,
4599
+ onModelContextResolved,
4600
+ sddSubagentFactory,
4601
+ statusTracker,
4602
+ ...createKanbanDispatchHandler({ config, events, skillLoader, sddSubagentFactory })
4603
+ });
4604
+ } else {
4605
+ const headlessKanbanMirror = projectRoot ? (await import("./kanban-run-mirror-EKVNGXWW.js")).createKanbanRunMirror({
4606
+ projectRoot,
4607
+ events,
4608
+ broadcast: () => {
4609
+ },
4610
+ log: (m) => console.log(m)
4611
+ }) : null;
4612
+ try {
4613
+ code = await runRepl({
4614
+ agent,
4615
+ renderer,
4616
+ reader,
4617
+ slashRegistry,
4618
+ tokenCounter,
4619
+ visionAdapters,
4620
+ supportsVision,
4621
+ attachments,
4622
+ effectiveMaxContext,
4623
+ getEffectiveMaxContext,
4624
+ projectName: path10.basename(projectRoot) || void 0,
4625
+ projectRoot,
4626
+ appConfig: config,
4627
+ getSessionId: () => agent.ctx.session?.id ?? session.id,
4628
+ getAutonomy,
4629
+ onAutonomy,
4630
+ getNextPredict,
4631
+ onSuggestionsParsed,
4632
+ getSuggestions: getSuggestions2,
4633
+ getAutoSuggestions,
4634
+ getYolo,
4635
+ autonomyNextPrompt,
4636
+ autoProceedDelayMs,
4637
+ onValidateAutoProceed,
4638
+ autoProceedMaxIterations,
4639
+ getEternalEngine,
4640
+ getParallelEngine,
4641
+ getSddRun,
4642
+ skillLoader,
4643
+ agentsMonitorController,
4644
+ fleetStreamController,
4645
+ interruptController,
4646
+ ...createReplFleetCallbacks({ director, getDirector }),
4647
+ onCountdownTick,
4648
+ onDestroy
4649
+ });
4650
+ } finally {
4651
+ headlessKanbanMirror?.dispose();
4652
+ }
4653
+ }
4654
+ } finally {
4655
+ await finalizeExecutionCleanup({
4656
+ offStorageObservability,
4657
+ fleetStatusLine,
4658
+ onCoordinatorStop: onCoordinatorStopImpl,
4659
+ stats,
4660
+ renderer,
4661
+ detachTodosCheckpoint,
4662
+ mcpRegistry,
4663
+ agent,
4664
+ session,
4665
+ tokenCounter,
4666
+ events,
4667
+ getPendingChimeraWork: () => pendingChimeraWork,
4668
+ director,
4669
+ reader
4670
+ });
4671
+ }
4672
+ return code;
4673
+ }
4674
+ export {
4675
+ CHIMERA_REVIEW_READ_ONLY_TOOLS,
4676
+ __resetReviewerRoundRobinCursor,
4677
+ applyChimeraReviewerReadOnlyPolicy,
4678
+ assignReviewerModelsRoundRobin,
4679
+ execute,
4680
+ resolveReviewerFallbackModels
4681
+ };
4682
+ //# sourceMappingURL=execution-O7I5DYE6.js.map