@ashx-j/lunr-dev 0.2.19-dev.10.1 → 0.2.21-dev.12.1

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 (307) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +2 -2
  3. package/dist/builtin-extensions/index.d.ts.map +1 -1
  4. package/dist/builtin-extensions/index.js +0 -2
  5. package/dist/builtin-extensions/index.js.map +1 -1
  6. package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -1
  7. package/dist/builtin-extensions/lunr-computer-use.js +15 -14
  8. package/dist/builtin-extensions/lunr-computer-use.js.map +1 -1
  9. package/dist/builtin-extensions/lunr-todos.d.ts +6 -5
  10. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  11. package/dist/builtin-extensions/lunr-todos.js +9 -7
  12. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  13. package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts +6 -4
  14. package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-intercom/broker/spawn.js +114 -61
  16. package/dist/builtin-extensions/pi-intercom/broker/spawn.js.map +1 -1
  17. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-intercom/index.js +360 -351
  19. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js +2 -2
  22. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
  23. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +8 -6
  24. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  25. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +128 -57
  26. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +18 -8
  28. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  29. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +38 -13
  30. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -3
  32. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +24 -14
  34. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  35. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +8 -1
  36. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +247 -18
  38. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts +1 -0
  40. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts.map +1 -1
  41. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js.map +1 -1
  43. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts +29 -0
  44. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts.map +1 -0
  45. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js +179 -0
  46. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js.map +1 -0
  47. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts +89 -0
  48. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts.map +1 -0
  49. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js +541 -0
  50. package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js.map +1 -0
  51. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +11 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +38 -12
  54. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
  56. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +3 -2
  57. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
  58. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts +2 -1
  59. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
  60. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +35 -8
  61. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
  62. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +7 -3
  63. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +26 -4
  65. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  66. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts +1 -0
  67. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  68. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +55 -38
  69. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  70. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +100 -0
  71. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +46 -31
  73. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  74. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +3 -6
  75. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
  76. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js +64 -5
  77. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
  78. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts +15 -2
  79. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts.map +1 -1
  80. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js +14 -4
  81. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js.map +1 -1
  82. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
  83. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +2 -1
  84. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
  85. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  86. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +21 -8
  87. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  88. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  89. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +1 -1
  90. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  91. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +0 -1
  92. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  93. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +71 -25
  94. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  95. package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts +2 -0
  96. package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts.map +1 -1
  97. package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js +75 -18
  98. package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js.map +1 -1
  99. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts +1 -1
  100. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts.map +1 -1
  101. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js +1 -1
  102. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js.map +1 -1
  103. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +13 -1
  104. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  105. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +67 -11
  106. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  107. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
  108. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  109. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  110. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts +3 -0
  111. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
  112. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +7 -2
  113. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
  114. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  115. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +13 -3
  116. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  117. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts +2 -2
  118. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts.map +1 -1
  119. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js +5 -10
  120. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js.map +1 -1
  121. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts +2 -0
  122. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts.map +1 -0
  123. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js +3 -0
  124. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js.map +1 -0
  125. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +3 -0
  126. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
  127. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +68 -11
  128. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
  129. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +12 -1
  130. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
  131. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +21 -2
  132. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
  133. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +21 -4
  134. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  135. package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
  136. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  137. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
  138. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
  139. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +7 -0
  140. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  141. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +11 -7
  142. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  143. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +6 -0
  144. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  145. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +46 -29
  146. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  147. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +46 -4
  148. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  149. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +407 -450
  150. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  151. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts.map +1 -1
  152. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js +1 -1
  153. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js.map +1 -1
  154. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts +2 -13
  155. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts.map +1 -1
  156. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js +5 -91
  157. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js.map +1 -1
  158. package/dist/core/computer-use/image.d.ts +26 -0
  159. package/dist/core/computer-use/image.d.ts.map +1 -0
  160. package/dist/core/computer-use/image.js +64 -0
  161. package/dist/core/computer-use/image.js.map +1 -0
  162. package/dist/core/computer-use/policy.d.ts.map +1 -1
  163. package/dist/core/computer-use/policy.js +2 -6
  164. package/dist/core/computer-use/policy.js.map +1 -1
  165. package/dist/core/computer-use/schemas.d.ts +13 -10
  166. package/dist/core/computer-use/schemas.d.ts.map +1 -1
  167. package/dist/core/computer-use/schemas.js +26 -35
  168. package/dist/core/computer-use/schemas.js.map +1 -1
  169. package/dist/core/computer-use/workflow.d.ts +16 -8
  170. package/dist/core/computer-use/workflow.d.ts.map +1 -1
  171. package/dist/core/computer-use/workflow.js +271 -260
  172. package/dist/core/computer-use/workflow.js.map +1 -1
  173. package/dist/core/customize.d.ts +2 -0
  174. package/dist/core/customize.d.ts.map +1 -1
  175. package/dist/core/customize.js +3 -0
  176. package/dist/core/customize.js.map +1 -1
  177. package/dist/core/permissions.d.ts +1 -0
  178. package/dist/core/permissions.d.ts.map +1 -1
  179. package/dist/core/permissions.js +8 -4
  180. package/dist/core/permissions.js.map +1 -1
  181. package/dist/core/plan-mode.d.ts.map +1 -1
  182. package/dist/core/plan-mode.js +0 -5
  183. package/dist/core/plan-mode.js.map +1 -1
  184. package/dist/core/runtime-bridges.d.ts.map +1 -1
  185. package/dist/core/runtime-bridges.js +0 -2
  186. package/dist/core/runtime-bridges.js.map +1 -1
  187. package/dist/core/settings-manager.d.ts +4 -0
  188. package/dist/core/settings-manager.d.ts.map +1 -1
  189. package/dist/core/settings-manager.js +11 -0
  190. package/dist/core/settings-manager.js.map +1 -1
  191. package/dist/core/subagent-cancellation.d.ts +21 -0
  192. package/dist/core/subagent-cancellation.d.ts.map +1 -0
  193. package/dist/core/subagent-cancellation.js +53 -0
  194. package/dist/core/subagent-cancellation.js.map +1 -0
  195. package/dist/core/subagent-spinner-setting.d.ts +6 -0
  196. package/dist/core/subagent-spinner-setting.d.ts.map +1 -0
  197. package/dist/core/subagent-spinner-setting.js +12 -0
  198. package/dist/core/subagent-spinner-setting.js.map +1 -0
  199. package/dist/core/subagent-spinner.d.ts +4 -0
  200. package/dist/core/subagent-spinner.d.ts.map +1 -0
  201. package/dist/core/subagent-spinner.js +6 -0
  202. package/dist/core/subagent-spinner.js.map +1 -0
  203. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/assistant-message.js +12 -18
  205. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  206. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  207. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  208. package/dist/modes/interactive/components/settings-selector.js +73 -1
  209. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  210. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  211. package/dist/modes/interactive/components/tool-execution.js +12 -0
  212. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  213. package/dist/modes/interactive/extension-widget-order.d.ts +8 -0
  214. package/dist/modes/interactive/extension-widget-order.d.ts.map +1 -0
  215. package/dist/modes/interactive/extension-widget-order.js +37 -0
  216. package/dist/modes/interactive/extension-widget-order.js.map +1 -0
  217. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  218. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  219. package/dist/modes/interactive/interactive-mode.js +44 -22
  220. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  221. package/dist/node-runtime/{agent-session-services-RSGP5GDN.js → agent-session-services-QANFGM2V.js} +14 -13
  222. package/dist/node-runtime/{builtin-extensions-7STLFW3A.js → builtin-extensions-SRCYQPZ6.js} +4 -3
  223. package/dist/node-runtime/{chunk-5OLJGVCB.js → chunk-3ONQTOMR.js} +2 -2
  224. package/dist/node-runtime/chunk-5ZH3ZFIZ.js +308 -0
  225. package/dist/node-runtime/{chunk-4SJWFTUP.js → chunk-7NBZ5P7I.js} +1 -1
  226. package/dist/node-runtime/chunk-A3KMVBZV.js +60 -0
  227. package/dist/node-runtime/{chunk-FLYMHKCS.js → chunk-BJ6H7DPI.js} +25 -25
  228. package/dist/node-runtime/{chunk-T5GM5BFH.js → chunk-BYQ2MOFI.js} +6 -6
  229. package/dist/node-runtime/{chunk-W7S2REEG.js → chunk-CZUBYPFP.js} +157 -74
  230. package/dist/node-runtime/{chunk-ZRDIMBEQ.js → chunk-D466UAID.js} +9 -6
  231. package/dist/node-runtime/{chunk-K72NVGUB.js → chunk-GT5F3OKM.js} +1 -1
  232. package/dist/node-runtime/chunk-H2BI7G7T.js +73 -0
  233. package/dist/node-runtime/{chunk-ISQDQIZN.js → chunk-HXJS2PJM.js} +2 -2
  234. package/dist/node-runtime/chunk-LISUTZS3.js +19 -0
  235. package/dist/node-runtime/{chunk-BKAZ6BKX.js → chunk-LOYUC2ID.js} +7 -7
  236. package/dist/node-runtime/{chunk-OTT6JMPU.js → chunk-LTYWTR3V.js} +1 -369
  237. package/dist/node-runtime/{chunk-5Q45AVQG.js → chunk-MW2XJZIK.js} +1 -1
  238. package/dist/node-runtime/{chunk-MUMMMTW4.js → chunk-N3PNL74S.js} +536 -265
  239. package/dist/node-runtime/{chunk-HQCKFM2O.js → chunk-VGYZ3APQ.js} +3 -0
  240. package/dist/node-runtime/{chunk-5NWQCSKR.js → chunk-VQ4UBWSA.js} +11 -1
  241. package/dist/node-runtime/{chunk-6SPPH4FY.js → chunk-VTGEKB7R.js} +4 -4
  242. package/dist/node-runtime/chunk-W3SILANP.js +24 -0
  243. package/dist/node-runtime/{chunk-J7QGQD45.js → chunk-WJVFOIB5.js} +1 -1
  244. package/dist/node-runtime/{chunk-JT6YCS5B.js → chunk-XAUQOKYK.js} +408 -45
  245. package/dist/node-runtime/{chunk-LCACDWHJ.js → chunk-XSRCAQK2.js} +13 -13
  246. package/dist/node-runtime/{chunk-TAP7QTUT.js → chunk-ZOIIMEN6.js} +24 -425
  247. package/dist/node-runtime/{chunk-MGSB2C3M.js → chunk-ZVSORYRM.js} +1 -1
  248. package/dist/node-runtime/{command-6UXQ6XZN.js → command-ZURZJDXF.js} +1 -1
  249. package/dist/node-runtime/{commands-LGR5VT5X.js → commands-BZGSLIP7.js} +3 -3
  250. package/dist/node-runtime/{customize-XEXL2DGQ.js → customize-LF5VST47.js} +1 -1
  251. package/dist/node-runtime/{direct-tool-executor-TDJS7TBD.js → direct-tool-executor-BSZDHIYR.js} +4 -4
  252. package/dist/node-runtime/{extract-IUCVZCWC.js → extract-XLQSJN4V.js} +2 -2
  253. package/dist/node-runtime/{gateway-3OLC4GQN.js → gateway-FWHRIBK5.js} +9 -9
  254. package/dist/node-runtime/{gemini-search-NCIDF7NE.js → gemini-search-U7SUMPQA.js} +1 -1
  255. package/dist/node-runtime/{gemini-url-context-OKIN7AIP.js → gemini-url-context-PXWCBQHK.js} +2 -2
  256. package/dist/node-runtime/index.js +34 -31
  257. package/dist/node-runtime/{init-EKE277AG.js → init-DVJUAOUO.js} +3 -3
  258. package/dist/node-runtime/{interactive-mode-S5M26CQ7.js → interactive-mode-BBASNWHN.js} +18 -15
  259. package/dist/node-runtime/lunr-computer-use-TJNDAMWM.js +135 -0
  260. package/dist/node-runtime/{lunr-cron-KKBOGAEF.js → lunr-cron-J64I72C6.js} +5 -5
  261. package/dist/node-runtime/main.js +22 -20
  262. package/dist/node-runtime/{package-manager-cli-C4EJOO5Q.js → package-manager-cli-VMYX4Y2S.js} +8 -7
  263. package/dist/node-runtime/{pi-intercom-EEMBGQID.js → pi-intercom-NV2LW6X7.js} +108 -87
  264. package/dist/node-runtime/{pi-mcp-adapter-DPTJGAAL.js → pi-mcp-adapter-JLB5YL2F.js} +6 -6
  265. package/dist/node-runtime/{pi-subagents-VKCLYIBY.js → pi-subagents-WEHV3W7H.js} +1267 -604
  266. package/dist/node-runtime/{pi-web-access-BV6DSZOP.js → pi-web-access-FN5YC5T6.js} +5 -5
  267. package/dist/node-runtime/{proxy-modes-R64LU6WZ.js → proxy-modes-OR7ISBXK.js} +7 -7
  268. package/dist/node-runtime/rpc-entry.js +22 -20
  269. package/dist/node-runtime/{runtime-bridges-UJ5WJJLN.js → runtime-bridges-2VQTLPOZ.js} +3 -5
  270. package/dist/node-runtime/{settings-manager-K6IPIQTI.js → settings-manager-3CCU326G.js} +3 -2
  271. package/dist/node-runtime/{subagent-executor-EFLTO32A.js → subagent-executor-CXKXQDHH.js} +186 -299
  272. package/dist/node-runtime/{update-cli-I6OVDRPL.js → update-cli-NM7B4CKL.js} +3 -2
  273. package/dist/node-runtime/{video-extract-ETA5RA7U.js → video-extract-OYT5WKS5.js} +2 -2
  274. package/dist/node-runtime/workflow-C26QCUTW.js +468 -0
  275. package/dist/node-runtime/{youtube-extract-SRVGNN26.js → youtube-extract-TQYKGFGS.js} +2 -2
  276. package/docs/computer-use.md +233 -268
  277. package/docs/features.md +22 -4
  278. package/docs/keybindings.md +3 -1
  279. package/docs/settings.md +3 -2
  280. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  281. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  282. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  283. package/examples/extensions/gondolin/package-lock.json +2 -2
  284. package/examples/extensions/gondolin/package.json +1 -1
  285. package/examples/extensions/sandbox/package-lock.json +2 -2
  286. package/examples/extensions/sandbox/package.json +1 -1
  287. package/examples/extensions/with-deps/package-lock.json +2 -2
  288. package/examples/extensions/with-deps/package.json +1 -1
  289. package/install-lock/package-lock.json +24 -24
  290. package/install-lock/package.json +2 -2
  291. package/npm-shrinkwrap.json +21 -21
  292. package/package.json +7 -7
  293. package/dist/builtin-extensions/lunr-settings-tools.d.ts +0 -4
  294. package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +0 -1
  295. package/dist/builtin-extensions/lunr-settings-tools.js +0 -120
  296. package/dist/builtin-extensions/lunr-settings-tools.js.map +0 -1
  297. package/dist/core/settings-tools-bridge.d.ts +0 -35
  298. package/dist/core/settings-tools-bridge.d.ts.map +0 -1
  299. package/dist/core/settings-tools-bridge.js +0 -56
  300. package/dist/core/settings-tools-bridge.js.map +0 -1
  301. package/dist/node-runtime/chunk-EH2LJQPM.js +0 -64
  302. package/dist/node-runtime/image-resize-6ZPBFMWW.js +0 -11
  303. package/dist/node-runtime/lunr-computer-use-4X3KUNF4.js +0 -206
  304. package/dist/node-runtime/workflow-SA34S4VX.js +0 -325
  305. package/dist/node-runtime/{chunk-EHMBW7FA.js → chunk-VSELYKWU.js} +3 -3
  306. package/dist/node-runtime/{chunk-ZSEZY3II.js → chunk-VTPM5V35.js} +0 -0
  307. package/dist/node-runtime/{src-GPFAU3N6.js → src-BPQFJR76.js} +3 -3
@@ -6,7 +6,6 @@ import {
6
6
  ASYNC_DIR,
7
7
  DEFAULT_ARTIFACT_CONFIG,
8
8
  INTERCOM_DETACH_REQUEST_EVENT,
9
- MAX_WIDGET_JOBS,
10
9
  POLL_INTERVAL_MS,
11
10
  RESULTS_DIR,
12
11
  SLASH_RESULT_TYPE,
@@ -17,6 +16,7 @@ import {
17
16
  SLASH_SUBAGENT_UPDATE_EVENT,
18
17
  SLASH_TEXT_RESULT_TYPE,
19
18
  SUBAGENT_ASYNC_COMPLETE_EVENT,
19
+ SUBAGENT_ASYNC_GUIDANCE,
20
20
  SUBAGENT_ASYNC_STARTED_EVENT,
21
21
  SUBAGENT_CHILD_ENV,
22
22
  SUBAGENT_CONTROL_EVENT,
@@ -29,10 +29,10 @@ import {
29
29
  SUBAGENT_STEERING_NOTICE_EVENT,
30
30
  TEMP_ROOT_DIR,
31
31
  WIDGET_KEY,
32
- aggregateStepStatus,
33
32
  assertJsonSchemaObject,
34
33
  attachNestedChildrenToResultChildren,
35
34
  buildSubagentResultIntercomPayload,
35
+ childRowLabel,
36
36
  cleanupAllArtifactDirs,
37
37
  cleanupOldArtifacts,
38
38
  cleanupOldChainDirs,
@@ -40,16 +40,16 @@ import {
40
40
  compactNestedResultChildren,
41
41
  deliverStopRequest,
42
42
  deliverSubagentResultIntercomEvent,
43
- flatToLogicalStepIndex,
44
43
  formatActivityLabel,
45
44
  formatAgentRunningLabel,
46
45
  formatAsyncRunProgressLabel,
47
46
  formatAsyncRunTranscript,
47
+ formatCompactModelBadge,
48
48
  formatControlNoticeMessage,
49
49
  formatDuration,
50
50
  formatModelSelection,
51
51
  formatNestedAggregate,
52
- formatParallelOutcome,
52
+ formatNestedResultLines,
53
53
  formatTokens,
54
54
  formatToolCall,
55
55
  formatUsage,
@@ -60,40 +60,54 @@ import {
60
60
  getSingleResultOutput,
61
61
  handleSubagentControlNotice,
62
62
  hasLiveNestedDescendants,
63
+ isAsyncSubagentExecution,
63
64
  listAsyncRuns,
65
+ normalizeAsyncLaunchConfig,
64
66
  normalizeParallelGroups,
65
67
  projectNestedRegistryForRoot,
66
68
  readStatus,
69
+ readSteerCapability,
67
70
  reconcileAsyncRun,
68
71
  reconcileNestedAsyncDescendants,
69
72
  registerMainWatchdog,
73
+ requestAsyncSteer,
70
74
  resolveAsyncRunLocation,
71
75
  resolveCurrentSessionId,
76
+ resolveSubagentRequestParams,
72
77
  resolveSubagentResultStatus,
78
+ resolveSubagentRunId,
73
79
  resolveTurnBudgetConfig,
74
80
  resolveWaitToolConfig,
75
81
  resolveWatchPath,
76
82
  sanitizeChildDescription,
77
83
  sanitizeSummary,
78
84
  shortenPath,
85
+ subagentLaunchRunsAsync,
79
86
  summarizeAsyncStatus,
80
87
  updateAsyncJobNestedProjection,
81
88
  validateAcceptanceInput,
82
89
  validateExecutionAcceptance,
83
90
  validateToolBudgetConfig,
84
91
  writeAtomicJson
85
- } from "./chunk-MUMMMTW4.js";
86
- import "./chunk-MGSB2C3M.js";
87
- import "./chunk-K72NVGUB.js";
92
+ } from "./chunk-N3PNL74S.js";
93
+ import {
94
+ createSubagentCancellation,
95
+ getSubagentSpinnerDefinition,
96
+ registerSubagentCancellation
97
+ } from "./chunk-A3KMVBZV.js";
98
+ import "./chunk-5ZH3ZFIZ.js";
99
+ import "./chunk-ZVSORYRM.js";
100
+ import "./chunk-GT5F3OKM.js";
88
101
  import "./chunk-53YJI7JU.js";
89
102
  import "./chunk-GZQHMEWL.js";
90
103
  import "./chunk-ZCHMJ34K.js";
91
- import "./chunk-5Q45AVQG.js";
92
- import "./chunk-ZRDIMBEQ.js";
93
- import "./chunk-M3LZLQW2.js";
94
- import "./chunk-5OLJGVCB.js";
104
+ import "./chunk-LISUTZS3.js";
95
105
  import "./chunk-FVDOXRUD.js";
96
- import "./chunk-ZSEZY3II.js";
106
+ import "./chunk-MW2XJZIK.js";
107
+ import "./chunk-D466UAID.js";
108
+ import "./chunk-M3LZLQW2.js";
109
+ import "./chunk-VTPM5V35.js";
110
+ import "./chunk-3ONQTOMR.js";
97
111
  import "./chunk-RQ2Q5IYR.js";
98
112
  import {
99
113
  keyText
@@ -106,10 +120,10 @@ import "./chunk-Q4Q6D4IY.js";
106
120
  import "./chunk-BRJX2PRC.js";
107
121
 
108
122
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
109
- import { randomUUID as randomUUID3 } from "node:crypto";
110
- import * as fs9 from "node:fs";
123
+ import { randomUUID as randomUUID4 } from "node:crypto";
124
+ import * as fs10 from "node:fs";
111
125
  import * as os from "node:os";
112
- import * as path11 from "node:path";
126
+ import * as path12 from "node:path";
113
127
  import { Box, Container as Container2, Spacer as Spacer2, Text as Text2, truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth3, wrapTextWithAnsi as wrapTextWithAnsi2 } from "@ashx-j/lunr-tui";
114
128
 
115
129
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/tui/render.js
@@ -172,7 +186,11 @@ function wrapPlainText(text, maxWidth) {
172
186
  }
173
187
  return lines;
174
188
  }
175
- var RUNNING_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], STATIC_RUNNING_GLYPH = "\u25CF";
189
+ var STATIC_RUNNING_GLYPH = "\u25CF", SUBAGENT_PAINT_INTERVAL_MS = 80;
190
+ function selectedSubagentSpinner() {
191
+ let bridge = globalThis[/* @__PURE__ */ Symbol.for("@lunr/customize")];
192
+ return getSubagentSpinnerDefinition(bridge?.getSubagentSpinner?.() ?? "braille");
193
+ }
176
194
  function runningSeed(...values) {
177
195
  let seed;
178
196
  for (let value of values)
@@ -180,7 +198,14 @@ function runningSeed(...values) {
180
198
  return seed;
181
199
  }
182
200
  function runningGlyph(seed) {
183
- return seed === void 0 ? STATIC_RUNNING_GLYPH : RUNNING_FRAMES[Math.abs(seed) % RUNNING_FRAMES.length];
201
+ if (seed === void 0)
202
+ return STATIC_RUNNING_GLYPH;
203
+ let frames = selectedSubagentSpinner().frames;
204
+ return frames[Math.abs(seed) % frames.length];
205
+ }
206
+ function foregroundSpinnerFrame(timerFrame) {
207
+ let { interval } = selectedSubagentSpinner();
208
+ return Math.floor(Math.max(0, timerFrame) * SUBAGENT_PAINT_INTERVAL_MS / interval);
184
209
  }
185
210
  function progressRunningSeed(progress) {
186
211
  if (progress)
@@ -240,6 +265,16 @@ function statJoin(theme, parts) {
240
265
  function formatTokenStat(tokens) {
241
266
  return `${formatTokens(tokens)} token`;
242
267
  }
268
+ function usageTokenTotal(usage) {
269
+ return usage ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : 0;
270
+ }
271
+ function compactResultTokens(result2) {
272
+ let progress = result2.progress || result2.progressSummary;
273
+ if (progress && progress.tokens > 0)
274
+ return progress.tokens;
275
+ let fromUsage = usageTokenTotal(result2.usage);
276
+ return fromUsage > 0 ? fromUsage : progress?.tokens ?? 0;
277
+ }
243
278
  function formatToolUseStat(count) {
244
279
  return `${count} tool use${count === 1 ? "" : "s"}`;
245
280
  }
@@ -250,7 +285,10 @@ function formatTotalCostStat(totalCost) {
250
285
  return totalCost.inputTokens && parts.push(`in:${formatTokens(totalCost.inputTokens)}`), totalCost.outputTokens && parts.push(`out:${formatTokens(totalCost.outputTokens)}`), totalCost.costUsd && parts.push(`$${totalCost.costUsd.toFixed(4)}`), parts.join(" ");
251
286
  }
252
287
  function liveDurationMs(progress, running, now) {
253
- return progress ? !running || progress.lastActivityAt === void 0 ? progress.durationMs : progress.durationMs + Math.max(0, now - progress.lastActivityAt) : 0;
288
+ if (!progress)
289
+ return 0;
290
+ let durationMs = progress.durationMs ?? 0;
291
+ return !running || progress.lastActivityAt === void 0 ? durationMs : durationMs + Math.max(0, now - progress.lastActivityAt);
254
292
  }
255
293
  function formatProgressStats(theme, progress, includeDuration = !0, options = {}) {
256
294
  if (!progress)
@@ -290,13 +328,17 @@ function resultStatusLine(result2, output) {
290
328
  function resultGlyph(result2, output, theme, running = result2.progress?.status === "running", seed = progressRunningSeed(result2.progress ?? result2.progressSummary), frame) {
291
329
  return running ? frame !== void 0 ? theme.fg("accent", runningGlyph((seed ?? 0) + frame)) : theme.fg("accent", runningGlyph(seed)) : result2.detached || result2.stopped || result2.interrupted ? theme.fg("warning", "\u25A0") : result2.exitCode !== 0 ? theme.fg("error", "\u2717") : hasEmptyTextOutputWithoutOutputTarget(result2.task, output) ? theme.fg("warning", "\u2713") : theme.fg("success", "\u2713");
292
330
  }
293
- function formatCompactStatsHangLine(progress, width, paint = (s) => s, prefix = " \u23BF ", now = Date.now(), running = !1) {
294
- let toolCount = progress?.toolCount ?? 0, tokens = progress?.tokens ?? 0, durationMs = liveDurationMs(progress, running, now), body = [
295
- formatToolUseStat(toolCount),
296
- formatTokenStat(tokens),
297
- durationMs > 0 ? formatDuration(durationMs) : "0ms"
298
- ].join(" \xB7 ");
299
- return truncLine(paint(`${prefix}${body}`), width);
331
+ function formatCompactRowStats(tokens, durationMs) {
332
+ return `${formatTokenStat(tokens)} \xB7 ${durationMs > 0 ? formatDuration(durationMs) : "0ms"}`;
333
+ }
334
+ function formatCompactSubagentRow(theme, width, parts) {
335
+ let stats = formatCompactRowStats(parts.tokens, parts.durationMs), body = [
336
+ `${parts.prefix ?? ""}${parts.glyph} ${themeBold(theme, parts.description)}`,
337
+ parts.modelBadge ? theme.fg("dim", parts.modelBadge) : "",
338
+ theme.fg("dim", stats),
339
+ parts.terminalStatus ? theme.fg("dim", parts.terminalStatus) : ""
340
+ ].filter(Boolean).join(` ${theme.fg("dim", "\xB7")} `);
341
+ return truncLine(body, width);
300
342
  }
301
343
  function widgetRenderKey(job) {
302
344
  return JSON.stringify({
@@ -350,14 +392,9 @@ function widgetStepsRunningSeed(steps) {
350
392
  function widgetJobRunningSeed(job) {
351
393
  return runningSeed(job.currentStep, job.runningSteps, widgetStepsRunningSeed(job.steps));
352
394
  }
353
- function widgetJobsRunningSeed(jobs) {
354
- let seed;
355
- for (let job of jobs)
356
- seed = runningSeed(seed, widgetJobRunningSeed(job));
357
- return seed;
358
- }
359
395
  function widgetAnimFrame(now = Date.now()) {
360
- return Math.floor(now / 80) % RUNNING_FRAMES.length;
396
+ let definition = selectedSubagentSpinner();
397
+ return Math.floor(now / definition.interval) % definition.frames.length;
361
398
  }
362
399
  function widgetStatusGlyph(job, theme, now = Date.now()) {
363
400
  return job.status === "running" ? theme.fg("accent", runningGlyph((widgetJobRunningSeed(job) ?? 0) + widgetAnimFrame(now))) : job.status === "queued" ? theme.fg("muted", "\u25E6") : job.status === "complete" ? theme.fg("success", "\u2713") : job.status === "paused" || job.status === "stopped" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717");
@@ -368,48 +405,6 @@ function widgetStepGlyph(status, theme, seed) {
368
405
  function widgetStepStatus(status, theme) {
369
406
  return status === "running" ? theme.fg("accent", "running") : status === "complete" || status === "completed" ? theme.fg("success", "complete") : status === "failed" ? theme.fg("error", "failed") : status === "paused" ? theme.fg("warning", "paused") : status === "stopped" ? theme.fg("warning", "stopped") : theme.fg("dim", status);
370
407
  }
371
- function widgetStepActivity(step, snapshotNow) {
372
- let facts = [];
373
- step.currentTool && step.currentToolStartedAt !== void 0 && snapshotNow !== void 0 ? facts.push(`${step.currentTool} ${formatDuration(Math.max(0, snapshotNow - step.currentToolStartedAt))}`) : step.currentTool && facts.push(step.currentTool), step.currentPath && facts.push(shortenPath(step.currentPath)), step.turnCount !== void 0 && facts.push(`${step.turnCount} turns`), step.toolCount !== void 0 && facts.push(`${step.toolCount} tools`), step.tokens?.total && facts.push(formatTokenStat(step.tokens.total));
374
- let activity = buildLiveStatusLine(step, snapshotNow);
375
- return activity && facts.length ? `${activity} \xB7 ${facts.join(" \xB7 ")}` : activity || facts.join(" \xB7 ");
376
- }
377
- function widgetChainDetails(job, theme, expanded = !1, width = getTermWidth()) {
378
- if (!job.steps?.length)
379
- return [];
380
- let total = job.chainStepCount ?? job.steps.length, lines = [];
381
- for (let span of buildAsyncChainStepSpans(total, job.steps.length, job.parallelGroups)) {
382
- let steps = job.steps.slice(span.start, span.start + span.count);
383
- if (span.isParallel) {
384
- let status = aggregateStepStatus(steps);
385
- lines.push(` ${widgetStepGlyph(status, theme, widgetStepsRunningSeed(steps))} Step ${span.stepIndex + 1}/${total}: ${themeBold(theme, "parallel group")} ${theme.fg("dim", "\xB7")} ${theme.fg("dim", formatParallelOutcome(steps, span.count))}`);
386
- continue;
387
- }
388
- let step = steps[0];
389
- if (!step) {
390
- lines.push(` ${theme.fg("dim", `\u25E6 Step ${span.stepIndex + 1}/${total}: pending`)}`);
391
- continue;
392
- }
393
- lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width));
394
- }
395
- return lines;
396
- }
397
- function widgetParallelAgentDetails(job, theme, expanded = !1, width = getTermWidth()) {
398
- if (!job.steps?.length)
399
- return [];
400
- if (job.mode !== "parallel" && job.mode !== "chain")
401
- return [];
402
- if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length)
403
- return widgetChainDetails(job, theme, expanded, width);
404
- let total = job.stepsTotal ?? job.steps.length, lines = [];
405
- for (let [index, step] of job.steps.entries()) {
406
- let marker = index === job.steps.length - 1 ? "\u2514" : "\u251C", activity = widgetStepActivity(step, job.updatedAt), itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step", modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.modelSelection);
407
- lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${step.agent} \xB7 ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` \xB7 ${activity}` : ""}`)}`);
408
- for (let nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 1))
409
- lines.push(` ${nestedLine}`);
410
- }
411
- return lines;
412
- }
413
408
  function parseParallelGroupAgentCount(label) {
414
409
  if (!label || !label.startsWith("[") || !label.endsWith("]"))
415
410
  return;
@@ -445,24 +440,16 @@ function buildChainStepSpans(details) {
445
440
  function isChainParallelGroupActive(details) {
446
441
  return details.mode !== "chain" || details.currentStepIndex === void 0 ? !1 : buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
447
442
  }
448
- function buildAsyncChainStepSpans(total, stepCount, parallelGroups = []) {
449
- let spans = [], flatIndex = 0;
450
- for (let stepIndex = 0; stepIndex < total; stepIndex++) {
451
- let group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
452
- if (group) {
453
- spans.push({ stepIndex, start: group.start, count: group.count, isParallel: !0 }), flatIndex = Math.max(flatIndex, group.start + group.count);
454
- continue;
455
- }
456
- spans.push({ stepIndex, start: flatIndex, count: flatIndex < stepCount ? 1 : 0, isParallel: !1 }), flatIndex++;
457
- }
458
- return spans;
459
- }
460
443
  function isDoneResult(result2) {
461
444
  let status = result2.progress?.status;
462
445
  return status === "completed" ? !0 : status === "running" || status === "pending" || result2.interrupted || result2.detached ? !1 : result2.exitCode === 0;
463
446
  }
464
447
  function workflowGraphHasStatus(details, statuses) {
465
- return details.workflowGraph?.nodes.some((node) => statuses.includes(node.status)) ?? !1;
448
+ let matches = (nodes) => nodes.some((node) => statuses.includes(node.status) || matches(node.children ?? []));
449
+ return matches(details.workflowGraph?.nodes ?? []);
450
+ }
451
+ function resultAtFlatIndex(details, flatIndex) {
452
+ return details.results.find((result2, arrayIndex) => (result2.progress?.index ?? result2.progressSummary?.index ?? arrayIndex) === flatIndex);
466
453
  }
467
454
  function buildChainRenderEntries(details, label) {
468
455
  if (details.mode !== "chain" || !label.hasParallelInChain || label.showActiveGroupOnly)
@@ -480,13 +467,17 @@ function buildChainRenderEntries(details, label) {
480
467
  });
481
468
  continue;
482
469
  }
483
- for (let index = span.start; index < span.start + span.count; index++)
470
+ for (let index = span.start; index < span.start + span.count; index++) {
471
+ let workflowStep = details.workflowGraph?.nodes.find((node) => node.stepIndex === span.stepIndex), workflowNode = workflowStep?.children?.find((node) => node.flatIndex === index) ?? (workflowStep?.flatIndex === index ? workflowStep : void 0), result2 = resultAtFlatIndex(details, index);
484
472
  entries.push({
485
473
  kind: "result",
486
474
  resultIndex: index,
487
475
  rowNumber: index + 1,
488
- agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`
476
+ agentName: result2?.agent ?? workflowNode?.label ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
477
+ status: workflowNode?.status ?? span.status,
478
+ error: workflowNode?.error ?? span.error
489
479
  });
480
+ }
490
481
  }
491
482
  return entries;
492
483
  }
@@ -551,38 +542,6 @@ function resultRowLabel(details, label, resultIndex, stepNumber) {
551
542
  }
552
543
  return label.itemTitle === "Agent" ? `Agent ${label.activeParallelGroup ? Math.max(1, stepNumber - label.groupStartIndex) : stepNumber}/${label.totalCount}` : `Step ${stepNumber}`;
553
544
  }
554
- function widgetStats(job, theme, now = Date.now()) {
555
- let parts = [], stepsTotal = job.stepsTotal ?? job.agents?.length ?? 1;
556
- if (job.activeParallelGroup) {
557
- let running = job.runningSteps ?? (job.status === "running" ? 1 : 0), done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
558
- if (job.mode === "parallel")
559
- job.status === "running" && running > 0 && parts.push(formatAgentRunningLabel(running)), stepsTotal > 0 && parts.push(`${done}/${stepsTotal} done`);
560
- else {
561
- let logicalStep = (job.currentStep !== void 0 ? job.parallelGroups?.find((group) => job.currentStep >= group.start && job.currentStep < group.start + group.count) : job.parallelGroups?.find((group) => group.start === 0))?.stepIndex ?? job.currentStep ?? 0, total = job.chainStepCount ?? stepsTotal, groupParts = [`${done}/${stepsTotal} done`];
562
- job.status === "running" && running > 0 && groupParts.unshift(formatAgentRunningLabel(running)), parts.push(`step ${logicalStep + 1}/${total} \xB7 parallel group: ${groupParts.join(" \xB7 ")}`);
563
- }
564
- } else if (job.currentStep !== void 0)
565
- if (job.mode === "chain" && job.parallelGroups?.length) {
566
- let total = job.chainStepCount ?? stepsTotal;
567
- parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
568
- } else
569
- parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
570
- else stepsTotal > 1 && parts.push(`steps ${stepsTotal}`);
571
- if (job.toolCount !== void 0 && parts.push(formatToolUseStat(job.toolCount)), job.totalTokens?.total && parts.push(formatTokenStat(job.totalTokens.total)), job.startedAt !== void 0) {
572
- let end = job.status === "running" ? now : job.updatedAt;
573
- end !== void 0 && parts.push(`${job.status === "running" ? "" : "ran for "}${formatDuration(Math.max(0, end - job.startedAt))}`);
574
- }
575
- return statJoin(theme, parts);
576
- }
577
- function widgetStepStats(theme, step, now = Date.now()) {
578
- let running = step.status === "running", durationMs = step.durationMs !== void 0 ? step.durationMs + (running && step.lastActivityAt !== void 0 ? Math.max(0, now - step.lastActivityAt) : 0) : step.startedAt !== void 0 ? Math.max(0, (running ? now : step.endedAt ?? step.lastActivityAt ?? now) - step.startedAt) : void 0;
579
- return statJoin(theme, [
580
- step.turnCount !== void 0 ? `${step.turnCount} turns` : "",
581
- step.toolCount !== void 0 ? formatToolUseStat(step.toolCount) : "",
582
- step.tokens?.total ? formatTokenStat(step.tokens.total) : "",
583
- durationMs !== void 0 ? `${running ? "" : "ran for "}${formatDuration(durationMs)}` : ""
584
- ]);
585
- }
586
545
  function modelThinkingBadge(theme, model, thinking, selection) {
587
546
  let label = formatModelSelection(selection, model, thinking);
588
547
  return label ? theme.fg("dim", ` (${label})`) : "";
@@ -647,30 +606,28 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
647
606
  };
648
607
  return append(children, 0, ""), lines.map((line) => truncLine(line, width));
649
608
  }
650
- function foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index, total, expanded, width) {
651
- let status = widgetStepStatus(step.status, theme), stats = widgetStepStats(theme, step), modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.modelSelection), lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1))} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "\xB7")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`], activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
652
- activity && lines.push(` ${theme.fg("dim", `\u23BF ${activity}`)}`);
653
- for (let nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt))
654
- lines.push(` ${nestedLine}`);
655
- if (step.status === "running" && (expanded || lines.push(` ${theme.fg("accent", liveDetailHintText())}`), expanded)) {
656
- let liveStatus = buildLiveStatusLine(step, job.updatedAt);
657
- liveStatus && liveStatus !== activity && lines.push(` ${theme.fg("accent", liveStatus)}`);
658
- for (let tool of step.recentTools?.slice(-3) ?? []) {
659
- let maxArgsLen = Math.max(40, width - 30), argsPreview = tool.args.length <= maxArgsLen ? tool.args : `${tool.args.slice(0, maxArgsLen)}...`;
660
- lines.push(` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
661
- }
662
- for (let line of step.recentOutput?.slice(-5) ?? [])
663
- lines.push(` ${theme.fg("dim", line)}`);
664
- }
665
- return lines;
609
+ function compactTerminalStatus(status) {
610
+ if (status === "failed")
611
+ return "failed";
612
+ if (status === "paused")
613
+ return "paused";
614
+ if (status === "stopped")
615
+ return "stopped";
616
+ if (status === "detached")
617
+ return "detached";
666
618
  }
667
619
  function compactJobProgress(job, step, now = Date.now()) {
668
- let running = (step?.status ?? job.status) === "running", startedAt = step?.startedAt ?? job.startedAt, endedAt = running ? now : step?.endedAt ?? step?.lastActivityAt ?? job.updatedAt ?? now, durationMs = step?.durationMs !== void 0 ? step.durationMs : startedAt !== void 0 ? Math.max(0, endedAt - startedAt) : 0;
669
- return {
670
- toolCount: step?.toolCount ?? job.toolCount ?? 0,
671
- tokens: step?.tokens?.total ?? job.totalTokens?.total ?? 0,
672
- durationMs
673
- };
620
+ let running = (step?.status ?? job.status) === "running", tokens = step?.tokens?.total ?? job.totalTokens?.total ?? 0;
621
+ if (step?.durationMs !== void 0)
622
+ return {
623
+ tokens,
624
+ durationMs: liveDurationMs({ durationMs: step.durationMs, lastActivityAt: step.lastActivityAt }, running, now)
625
+ };
626
+ let startedAt = step?.startedAt ?? job.startedAt;
627
+ if (startedAt === void 0)
628
+ return { tokens, durationMs: 0 };
629
+ let endedAt = running ? now : step?.endedAt ?? step?.lastActivityAt ?? job.updatedAt ?? now;
630
+ return { tokens, durationMs: Math.max(0, endedAt - startedAt) };
674
631
  }
675
632
  function compactJobLead(job, step) {
676
633
  return compactRowLead({
@@ -679,18 +636,15 @@ function compactJobLead(job, step) {
679
636
  }) || widgetJobName(job);
680
637
  }
681
638
  function compactJobLines(job, step, theme, width, now = Date.now()) {
682
- let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge = formatModelSelection(step?.modelSelection, step?.model, step?.thinking), permissionBadge = step?.permissions ? theme.fg("dim", step.permissions) : "", glyph = step ? widgetStepGlyph(step.status, theme, running ? (widgetStepRunningSeed(step, step.index) ?? 0) + widgetAnimFrame(now) : widgetStepRunningSeed(step, step.index)) : widgetStatusGlyph(job, theme, now), hang = formatCompactStatsHangLine(compactJobProgress(job, step, now), width, (s) => theme.fg("dim", s), " \u23BF ", now, running);
683
- return [
684
- truncLine(`${glyph} ${themeBold(theme, lead)}${modelBadge ? ` ${theme.fg("dim", "\xB7")} ${theme.fg("dim", modelBadge)}` : ""}${permissionBadge ? ` ${theme.fg("dim", "\xB7")} ${permissionBadge}` : ""}`, width),
685
- hang
686
- ];
687
- }
688
- function buildSingleWidgetLines(job, theme, width, _expanded) {
689
- let now = Date.now(), steps = job.steps ?? [];
690
- return steps.length <= 1 ? compactJobLines(job, steps[0], theme, width, now) : steps.flatMap((step) => compactJobLines(job, step, theme, width, now));
691
- }
692
- function compactSingleWidgetLines(job, theme, width) {
693
- return buildSingleWidgetLines(job, theme, width, !1);
639
+ let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge = formatCompactModelBadge(step?.modelSelection, step?.model), glyph = step ? widgetStepGlyph(step.status, theme, running ? (widgetStepRunningSeed(step, step.index) ?? 0) + widgetAnimFrame(now) : widgetStepRunningSeed(step, step.index)) : widgetStatusGlyph(job, theme, now), progress = compactJobProgress(job, step, now);
640
+ return [formatCompactSubagentRow(theme, width, {
641
+ glyph,
642
+ description: lead,
643
+ modelBadge,
644
+ tokens: progress.tokens,
645
+ durationMs: progress.durationMs,
646
+ terminalStatus: compactTerminalStatus(step?.status ?? job.status)
647
+ })];
694
648
  }
695
649
  var RESERVED_NON_WIDGET_ROWS = 19, widgetLayoutSession;
696
650
  function resetWidgetLayoutSession() {
@@ -709,20 +663,6 @@ function currentTerminalColumns() {
709
663
  function widgetSessionMatches(expanded) {
710
664
  return widgetLayoutSession?.expanded === expanded && widgetLayoutSession.rows === currentTerminalRows() && widgetLayoutSession.columns === currentTerminalColumns();
711
665
  }
712
- function widgetHeaderCounts(jobs) {
713
- return {
714
- running: jobs.filter((job) => job.status === "running"),
715
- queued: jobs.filter((job) => job.status === "queued"),
716
- complete: jobs.filter((job) => job.status === "complete"),
717
- failed: jobs.filter((job) => job.status === "failed"),
718
- paused: jobs.filter((job) => job.status === "paused"),
719
- stopped: jobs.filter((job) => job.status === "stopped")
720
- };
721
- }
722
- function buildSingleLineWidgetLines(jobs, theme, width) {
723
- let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
724
- return counts.running.length > 0 && parts.push(`${counts.running.length}/${jobs.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), !hasActive && counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`), [truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "subagents")} (${parts.join(", ") || `${jobs.length} total`})`, width)];
725
- }
726
666
  function orderedWidgetJobs(jobs) {
727
667
  return [
728
668
  ...jobs.filter((job) => job.status === "running"),
@@ -730,73 +670,6 @@ function orderedWidgetJobs(jobs) {
730
670
  ...jobs.filter((job) => job.status !== "running" && job.status !== "queued")
731
671
  ];
732
672
  }
733
- function progressiveJobKey(job) {
734
- return job.asyncId;
735
- }
736
- function isProgressiveActiveJob(job) {
737
- return job?.status === "running" || job?.status === "queued";
738
- }
739
- function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
740
- if (bodyRows <= 0)
741
- return [];
742
- let jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job])), selected = [], append = (key) => {
743
- selected.includes(key) || !jobsByKey.has(key) || selected.push(key);
744
- };
745
- for (let key of previousKeys)
746
- if (isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
747
- return selected;
748
- for (let job of orderedWidgetJobs(jobs)) {
749
- if (!isProgressiveActiveJob(job))
750
- continue;
751
- let key = progressiveJobKey(job);
752
- if (append(key), selected.length >= bodyRows)
753
- break;
754
- }
755
- if (selected.length >= bodyRows)
756
- return selected;
757
- for (let key of previousKeys)
758
- if (!isProgressiveActiveJob(jobsByKey.get(key)) && (append(key), selected.length >= bodyRows))
759
- return selected;
760
- for (let job of orderedWidgetJobs(jobs)) {
761
- let key = progressiveJobKey(job);
762
- if (append(key), selected.length >= bodyRows)
763
- break;
764
- }
765
- return selected;
766
- }
767
- function progressiveHeaderLine(jobs, theme, width) {
768
- let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
769
- return counts.running.length > 0 && parts.push(formatAgentRunningLabel(counts.running.length)), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), hasActive || (counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`)), truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7")} ${theme.fg("dim", parts.join(", ") || `${jobs.length} total`)}`, width);
770
- }
771
- function progressiveJobLine(job, theme, width) {
772
- let stats = widgetStats(job, theme), activity = widgetActivity(job), status = job.status === "complete" ? "done" : job.status, parts = [
773
- themeBold(theme, widgetJobName(job)),
774
- theme.fg("dim", status),
775
- stats,
776
- activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : ""
777
- ].filter(Boolean);
778
- return truncLine(` ${widgetStatusGlyph(job, theme)} ${parts.join(` ${theme.fg("dim", "\xB7")} `)}`, width);
779
- }
780
- function progressiveHiddenLine(hiddenJobs, theme, width) {
781
- let counts = widgetHeaderCounts(hiddenJobs), parts = [];
782
- counts.running.length > 0 && parts.push(`${counts.running.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`);
783
- let finished = counts.complete.length + counts.failed.length + counts.paused.length + counts.stopped.length;
784
- return finished > 0 && parts.push(`${finished} finished`), truncLine(theme.fg("dim", ` +${hiddenJobs.length} more${parts.length ? ` (${parts.join(", ")})` : ""}`), width);
785
- }
786
- function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys) {
787
- let rowCount = Math.max(1, lockedRows);
788
- if (rowCount === 1)
789
- return { lines: buildSingleLineWidgetLines(jobs, theme, width), visibleJobKeys: [] };
790
- let bodyRows = rowCount - 1, visibleJobKeys = selectProgressiveJobKeys(jobs, previousKeys, bodyRows), jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job])), visibleJobs = visibleJobKeys.map((key) => jobsByKey.get(key)).filter((job) => !!job), hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job)));
791
- hiddenJobs.length > 0 && visibleJobs.length >= bodyRows && bodyRows > 0 && (visibleJobs = visibleJobs.slice(0, bodyRows - 1), visibleJobKeys = visibleJobs.map(progressiveJobKey), hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job))));
792
- let lines = [
793
- progressiveHeaderLine(jobs, theme, width),
794
- ...visibleJobs.map((job) => progressiveJobLine(job, theme, width))
795
- ];
796
- for (hiddenJobs.length > 0 && lines.length < rowCount && lines.push(progressiveHiddenLine(hiddenJobs, theme, width)); lines.length < rowCount; )
797
- lines.push(" ");
798
- return { lines: lines.slice(0, rowCount), visibleJobKeys };
799
- }
800
673
  function collapsedWidgetLineBudget(rows) {
801
674
  return Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
802
675
  }
@@ -807,150 +680,185 @@ function fitWidgetLineBudget(lines, theme, width, expanded) {
807
680
  let visibleLines = Math.max(1, budget - 1), hiddenCount = lines.length - visibleLines, hint = expanded ? `\u2026 ${hiddenCount} live-detail lines hidden` : `\u2026 ${hiddenCount} lines hidden \xB7 ${liveDetailKeyText()} expands`;
808
681
  return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
809
682
  }
810
- function fitAdaptiveWidgetLines(jobs, lines, theme, width, expanded) {
683
+ function fitAdaptiveWidgetLines(_jobs, lines, theme, width, expanded) {
811
684
  if (expanded)
812
685
  return resetWidgetLayoutSession(), fitWidgetLineBudget(lines, theme, width, !0);
813
- let hasMatchingSession = widgetSessionMatches(expanded), rows = currentTerminalRows(), columns = currentTerminalColumns(), availableRows = estimateAvailableWidgetRows();
814
- if (hasMatchingSession && widgetLayoutSession?.tier === "single-line")
815
- return buildSingleLineWidgetLines(jobs, theme, width);
816
- if (hasMatchingSession && widgetLayoutSession?.tier === "progressive" && widgetLayoutSession.lockedRows !== void 0) {
817
- let rendered2 = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys);
818
- return widgetLayoutSession.visibleJobKeys = rendered2.visibleJobKeys, rendered2.lines;
819
- }
686
+ let rows = currentTerminalRows(), columns = currentTerminalColumns(), availableRows = estimateAvailableWidgetRows();
820
687
  if (lines.length <= availableRows)
821
- return widgetLayoutSession = { expanded, rows, columns, tier: "full", visibleJobKeys: [] }, fitWidgetLineBudget(lines, theme, width, !1);
822
- if (availableRows <= 2)
823
- return widgetLayoutSession = { expanded, rows, columns, tier: "single-line", visibleJobKeys: [] }, buildSingleLineWidgetLines(jobs, theme, width);
824
- let lockedRows = Math.min(availableRows, collapsedWidgetLineBudget(rows)), rendered = buildProgressiveWidgetLines(jobs, theme, width, lockedRows, []);
825
- return widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows, visibleJobKeys: rendered.visibleJobKeys }, rendered.lines;
826
- }
827
- function buildWidgetComponent(jobs, expanded) {
828
- return (_tui, theme) => {
829
- let width = getTermWidth(), lines = expanded ? buildWidgetLines(jobs, theme, width, !0) : jobs.length === 1 ? compactSingleWidgetLines(jobs[0], theme, width) : buildWidgetLines(jobs, theme, width, !1), container = new Container();
830
- for (let line of fitAdaptiveWidgetLines(jobs, lines, theme, width, expanded))
831
- container.addChild(new Text(line, 1, 0));
832
- return container;
833
- };
688
+ return widgetLayoutSession = { expanded, rows, columns, tier: "full" }, fitWidgetLineBudget(lines, theme, width, !1);
689
+ let lockedRows = widgetSessionMatches(expanded) && widgetLayoutSession?.tier === "progressive" ? widgetLayoutSession.lockedRows ?? 1 : Math.max(1, Math.min(availableRows, collapsedWidgetLineBudget(rows)));
690
+ if (widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows }, lockedRows === 1)
691
+ return lines.slice(0, 1);
692
+ let visibleCount = Math.max(1, lockedRows - 1), visible = lines.slice(0, visibleCount), hidden = lines.length - visible.length;
693
+ for (hidden > 0 && visible.push(truncLine(theme.fg("dim", `+${hidden} more`), width)); visible.length < lockedRows; )
694
+ visible.push(" ");
695
+ return visible.slice(0, lockedRows);
696
+ }
697
+ function widgetExpandedDiagnostics(job, step, index, theme, width, now) {
698
+ let lines = [], status = step?.status ?? job.status, runLabel = step ? `${job.asyncId}:${step.index ?? index}` : job.asyncId;
699
+ lines.push(truncLine(theme.fg("dim", ` \u23BF run ${runLabel} \xB7 ${status}`), width));
700
+ let error = step?.error;
701
+ error && lines.push(truncLine(theme.fg("error", ` ${error}`), width));
702
+ let snapshotNow = status === "running" ? now : step?.endedAt ?? job.updatedAt, activity = step ? widgetStepActivityLine(step, Math.max(1, width - 4), !0, snapshotNow) : widgetActivity(job, now);
703
+ activity && lines.push(truncLine(theme.fg("dim", ` ${activity}`), width));
704
+ for (let tool of step?.recentTools?.slice(-3) ?? [])
705
+ lines.push(truncLine(theme.fg("dim", ` ${tool.tool}${tool.args ? `: ${tool.args}` : ""}`), width));
706
+ for (let output of step?.recentOutput?.slice(-5) ?? [])
707
+ lines.push(truncLine(theme.fg("dim", ` ${output}`), width));
708
+ for (let nestedLine of formatNestedWidgetLines(step?.children ?? job.nestedChildren, theme, width, !0, snapshotNow))
709
+ lines.push(truncLine(` ${nestedLine}`, width));
710
+ return lines;
834
711
  }
835
- function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
836
- if (jobs.length === 0)
837
- return [];
838
- if (jobs.length === 1)
839
- return buildSingleWidgetLines(jobs[0], theme, width, expanded);
840
- let running = jobs.filter((job) => job.status === "running"), queued = jobs.filter((job) => job.status === "queued"), finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued"), lines = [], hasActive = running.length > 0 || queued.length > 0, headerGlyph = running.length > 0 ? runningGlyph(widgetJobsRunningSeed(running)) : hasActive ? "\u25CF" : "\u25CB";
841
- lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7 background")}`, width));
842
- let items = [], hiddenRunning = 0, hiddenFinished = 0, queuedSummaryShown = !1, slots = MAX_WIDGET_JOBS;
843
- for (let job of running) {
844
- if (slots <= 0) {
845
- hiddenRunning++;
846
- continue;
847
- }
848
- let stats = widgetStats(job, theme);
849
- items.push([
850
- `${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
851
- ` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
852
- ...widgetParallelAgentDetails(job, theme, expanded, width)
853
- ]), slots--;
854
- }
855
- queued.length > 0 && slots > 0 && (items.push([`${theme.fg("muted", "\u25E6")} ${theme.fg("dim", `${queued.length} queued`)}`]), queuedSummaryShown = !0, slots--);
856
- for (let job of finished) {
857
- if (slots <= 0) {
858
- hiddenFinished++;
859
- continue;
860
- }
861
- let stats = widgetStats(job, theme);
862
- items.push([
863
- `${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
864
- ` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
865
- ...widgetParallelAgentDetails(job, theme, expanded, width)
866
- ]), slots--;
867
- }
868
- let hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0, hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
869
- if (hiddenTotal > 0) {
870
- let parts = [];
871
- hiddenRunning > 0 && parts.push(`${hiddenRunning} running`), hiddenQueued > 0 && parts.push(`${hiddenQueued} queued`), hiddenFinished > 0 && parts.push(`${hiddenFinished} finished`), items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
872
- }
873
- for (let i = 0; i < items.length; i++) {
874
- let item = items[i], last = i === items.length - 1, branch = last ? "\u2514\u2500" : "\u251C\u2500", continuation = last ? " " : "\u2502 ";
875
- lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
876
- for (let detail of item.slice(1))
877
- lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
712
+ function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1, now = Date.now()) {
713
+ let lines = [];
714
+ for (let job of orderedWidgetJobs(jobs)) {
715
+ let steps = job.steps?.length ? job.steps : job.agents?.length ? job.agents.map((agent, index) => ({ agent, description: agent, status: job.status, index })) : [void 0];
716
+ for (let [index, step] of steps.entries())
717
+ lines.push(...compactJobLines(job, step, theme, width, now)), expanded && lines.push(...widgetExpandedDiagnostics(job, step, index, theme, width, now));
878
718
  }
879
719
  return lines;
880
720
  }
881
- var widgetPaintTimer, widgetPaintCtx;
882
- function clearWidgetPaintTimer() {
883
- widgetPaintTimer && (clearInterval(widgetPaintTimer), widgetPaintTimer = void 0), widgetPaintCtx = void 0;
884
- }
885
- function ensureWidgetPaintTimer(ctx, running) {
886
- if (widgetPaintCtx = ctx, !running || !ctx.hasUI) {
887
- clearWidgetPaintTimer();
888
- return;
721
+ var activeWidget, SubagentAsyncWidget = class {
722
+ jobs;
723
+ expanded;
724
+ theme;
725
+ requestRender;
726
+ nowFn;
727
+ timer;
728
+ disposed = !1;
729
+ cachedWidth;
730
+ cachedLines;
731
+ constructor(jobs, theme, options) {
732
+ this.jobs = jobs, this.theme = theme, this.expanded = options?.expanded ?? !1, this.requestRender = options?.requestRender, this.nowFn = options?.now ?? Date.now, this.syncTimer();
733
+ }
734
+ setTheme(theme) {
735
+ this.theme = theme, this.invalidate();
736
+ }
737
+ setJobs(jobs) {
738
+ this.jobs = jobs, this.invalidate(), this.syncTimer();
739
+ }
740
+ setExpanded(expanded) {
741
+ this.expanded !== expanded && (this.expanded = expanded, this.invalidate());
742
+ }
743
+ setRequestRender(requestRender) {
744
+ this.requestRender = requestRender;
745
+ }
746
+ hasRunning() {
747
+ return this.jobs.some((job) => job.status === "running");
748
+ }
749
+ syncTimer() {
750
+ this.disposed || (this.hasRunning() ? this.startTimer() : this.stopTimer());
751
+ }
752
+ startTimer() {
753
+ this.timer || (this.timer = setInterval(() => {
754
+ this.disposed || (this.invalidate(), this.requestRender?.());
755
+ }, SUBAGENT_PAINT_INTERVAL_MS), this.timer.unref?.());
756
+ }
757
+ stopTimer() {
758
+ this.timer && (clearInterval(this.timer), this.timer = void 0);
889
759
  }
890
- widgetPaintTimer || (widgetPaintTimer = setInterval(() => {
891
- widgetPaintCtx?.ui.requestRender?.();
892
- }, 80), widgetPaintTimer.unref?.());
760
+ invalidate() {
761
+ this.cachedWidth = void 0, this.cachedLines = void 0;
762
+ }
763
+ dispose() {
764
+ this.disposed = !0, this.stopTimer(), activeWidget === this && (activeWidget = void 0);
765
+ }
766
+ render(width) {
767
+ let theme = this.theme;
768
+ if (!theme)
769
+ return [];
770
+ if (this.cachedLines && this.cachedWidth === width && !this.hasRunning())
771
+ return this.cachedLines;
772
+ let now = this.nowFn(), innerWidth = Math.max(1, width - 2), lines = buildWidgetLines(this.jobs, theme, innerWidth, this.expanded, now), padded = fitAdaptiveWidgetLines(this.jobs, lines, theme, innerWidth, this.expanded).map((line) => {
773
+ let withPad = ` ${line}`, vis = visibleWidth(withPad);
774
+ return withPad + " ".repeat(Math.max(0, width - vis));
775
+ });
776
+ return this.cachedWidth = width, this.cachedLines = padded, padded;
777
+ }
778
+ };
779
+ function disposeSubagentWidget() {
780
+ activeWidget?.dispose(), activeWidget = void 0;
893
781
  }
894
782
  function renderWidget(ctx, jobs) {
895
783
  if (jobs.length === 0) {
896
- resetWidgetLayoutSession(), clearWidgetPaintTimer(), ctx.hasUI && ctx.ui.setWidget(WIDGET_KEY, void 0);
784
+ resetWidgetLayoutSession(), disposeSubagentWidget(), ctx.hasUI && ctx.ui.setWidget(WIDGET_KEY, void 0);
785
+ return;
786
+ }
787
+ if (!ctx.hasUI)
788
+ return;
789
+ let expanded = ctx.ui.getToolsExpanded?.() ?? !1, requestRender = () => ctx.ui.requestRender?.();
790
+ if (activeWidget) {
791
+ activeWidget.setJobs(jobs), activeWidget.setExpanded(expanded), activeWidget.setRequestRender(requestRender), ctx.ui.requestRender?.();
897
792
  return;
898
793
  }
899
- ctx.hasUI && (ensureWidgetPaintTimer(ctx, jobs.some((job) => job.status === "running")), ctx.ui.setWidget(WIDGET_KEY, buildWidgetComponent(jobs, ctx.ui.getToolsExpanded?.() ?? !1)));
794
+ let widget = new SubagentAsyncWidget(jobs, void 0, { expanded, requestRender });
795
+ activeWidget = widget, ctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => (widget.setTheme(theme), widget));
796
+ }
797
+ function compactResultTerminalStatus(result2) {
798
+ if (result2.stopped)
799
+ return "stopped";
800
+ if (result2.detached)
801
+ return "detached";
802
+ if (result2.interrupted)
803
+ return "paused";
804
+ if (result2.exitCode !== 0)
805
+ return "failed";
900
806
  }
901
807
  function renderSingleCompact(d, r, theme, frame) {
902
- let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running", contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "", c = new Container(), width = getTermWidth() - 4, lead = compactRowLead({ description: r.description ?? r.agent, task: r.task }), modelBadge = formatModelSelection(r.modelSelection ?? progress?.modelSelection, r.model ?? progress?.model, r.thinking ?? progress?.thinking), permissionBadge = r.permissions ? theme.fg("dim", r.permissions) : "";
903
- if (c.addChild(animatedLine((f, now) => {
904
- let stats = formatProgressStats(theme, progress, !0, { running: isRunning, terminal: !isRunning, now });
905
- return truncLine(`${resultGlyph(r, output, theme, isRunning, void 0, f)} ${themeBold(theme, lead)}${modelBadge ? ` ${theme.fg("dim", "\xB7")} ${theme.fg("dim", modelBadge)}` : ""}${permissionBadge ? ` ${theme.fg("dim", "\xB7")} ${permissionBadge}` : ""}${contextBadge}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`, width);
906
- }, frame, isRunning)), isRunning && r.progress)
907
- return c.addChild(animatedLine((_f, now) => formatCompactStatsHangLine(r.progress, width, (s) => theme.fg("dim", s), " \u23BF ", now, !0), frame, !0)), c;
908
- c.addChild(new Text(truncLine(theme.fg("dim", ` \u23BF ${resultStatusLine(r, output)}`), width), 0, 0));
909
- let preview = firstOutputLine(output);
910
- return preview && r.exitCode === 0 && !hasEmptyTextOutputWithoutOutputTarget(r.task, output) && c.addChild(new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0)), r.sessionFile && c.addChild(new Text(truncLine(theme.fg("dim", ` session: ${shortenPath(r.sessionFile)}`), width), 0, 0)), c;
808
+ let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running", width = getTermWidth() - 4, lead = compactRowLead({ description: r.description ?? r.agent, task: r.task }), modelBadge = formatCompactModelBadge(r.modelSelection ?? progress?.modelSelection, r.model ?? progress?.model);
809
+ return animatedLine((animationTick, now) => formatCompactSubagentRow(theme, width, {
810
+ glyph: resultGlyph(r, output, theme, isRunning, void 0, foregroundSpinnerFrame(animationTick)),
811
+ description: lead,
812
+ modelBadge,
813
+ tokens: compactResultTokens(r),
814
+ durationMs: liveDurationMs(progress, isRunning, now),
815
+ terminalStatus: compactResultTerminalStatus(r)
816
+ }), frame, isRunning);
817
+ }
818
+ function progressStatusGlyph(progress, theme, frame) {
819
+ return !progress || progress.status === "pending" ? theme.fg("muted", "\u25E6") : progress.status === "running" ? theme.fg("accent", runningGlyph((progressRunningSeed(progress) ?? 0) + (frame ?? 0))) : progress.status === "completed" ? theme.fg("success", "\u2713") : progress.status === "failed" ? theme.fg("error", "\u2717") : theme.fg("warning", "\u25A0");
820
+ }
821
+ function flattenCompactWorkflowNodes(nodes) {
822
+ let rows = [];
823
+ for (let node of nodes)
824
+ node.children?.length ? rows.push(...flattenCompactWorkflowNodes(node.children)) : rows.push(node);
825
+ return rows;
826
+ }
827
+ function compactForegroundEntries(d) {
828
+ let workflowNodes = flattenCompactWorkflowNodes(d.workflowGraph?.nodes ?? []);
829
+ if (workflowNodes.length === 0) {
830
+ let progressCount2 = d.progress?.reduce((count2, entry) => Math.max(count2, entry.index + 1), 0) ?? 0, chainCount = d.mode === "chain" ? buildChainStepSpans(d).reduce((count2, span) => Math.max(count2, span.start + span.count), 0) : 0, count = Math.max(d.results.length, progressCount2, d.chainChildren?.length ?? 0, d.chainAgents?.length ?? 0, chainCount);
831
+ return Array.from({ length: count }, (_, flatIndex) => ({ flatIndex }));
832
+ }
833
+ let entries = workflowNodes.map((node) => ({ flatIndex: node.flatIndex, node })), seen = new Set(entries.flatMap((entry) => entry.flatIndex === void 0 ? [] : [entry.flatIndex])), progressCount = d.progress?.reduce((count, entry) => Math.max(count, entry.index + 1), 0) ?? 0, dataCount = Math.max(d.results.length, progressCount, d.chainChildren?.length ?? 0);
834
+ for (let flatIndex = 0; flatIndex < dataCount; flatIndex++)
835
+ seen.has(flatIndex) || entries.push({ flatIndex });
836
+ return entries;
911
837
  }
912
838
  function renderMultiCompact(d, theme, frame) {
913
- let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), stopped = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), failed = d.results.some((r) => !r.stopped && r.exitCode !== 0 && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["failed"]), paused = d.results.some((r) => (r.interrupted || r.detached) && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["paused", "detached"]), totalSummary = d.progressSummary;
914
- if (!totalSummary) {
915
- let sawProgress = !1, summary = { toolCount: 0, tokens: 0, durationMs: 0, lastActivityAt: void 0 };
916
- for (let r of d.results) {
917
- let prog = r.progress || r.progressSummary;
918
- prog && (sawProgress = !0, summary.toolCount += prog.toolCount, summary.tokens += prog.tokens, summary.durationMs = d.mode === "chain" ? summary.durationMs + prog.durationMs : Math.max(summary.durationMs, prog.durationMs), prog.status === "running" && prog.lastActivityAt !== void 0 && (summary.lastActivityAt = Math.max(summary.lastActivityAt ?? 0, prog.lastActivityAt)));
919
- }
920
- sawProgress && (totalSummary = summary);
921
- }
922
- let multiLabel = buildMultiProgressLabel(d, hasRunning), itemTitle = multiLabel.itemTitle, glyph = (f) => hasRunning ? theme.fg("accent", runningGlyph(f !== void 0 ? (runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex) ?? 0) + f : runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex))) : stopped ? theme.fg("warning", "\u25A0") : failed ? theme.fg("error", "\u2717") : paused ? theme.fg("warning", "\u25A0") : theme.fg("success", "\u2713"), contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "", c = new Container(), width = getTermWidth() - 4;
923
- c.addChild(animatedLine((f, now) => {
924
- let stats = statJoin(theme, [multiLabel.headerLabel, formatProgressStats(theme, totalSummary, !0, { running: hasRunning, terminal: !hasRunning, now }), formatTotalCostStat(d.totalCost)]);
925
- return truncLine(`${glyph(f)} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`, width);
926
- }, frame, hasRunning));
927
- let useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length, displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0, displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : useResultsDirectly ? d.results.length : d.chainAgents.length, chainEntries = buildChainRenderEntries(d, multiLabel), renderEntries = chainEntries ?? Array.from({ length: displayEnd - displayStart }, (_, offset) => {
928
- let i = displayStart + offset, r = d.results[i], fallbackLabel = itemTitle.toLowerCase(), rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
929
- return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? r?.agent || `${fallbackLabel}-${rowNumber}` : d.chainAgents[i] || r?.agent || `${fallbackLabel}-${rowNumber}` };
930
- });
931
- for (let entry of renderEntries) {
932
- if (entry.kind === "placeholder") {
933
- let glyph2 = widgetStepGlyph(entry.status, theme), statusLabel = widgetStepStatus(entry.status, theme);
934
- c.addChild(new Text(truncLine(` ${glyph2} ${entry.stepLabel}: ${themeBold(theme, entry.agentName)} ${theme.fg("dim", "\xB7")} ${statusLabel}`, width), 0, 0)), entry.error && c.addChild(new Text(truncLine(theme.fg("error", ` \u23BF Error: ${entry.error}`), width), 0, 0));
935
- continue;
936
- }
937
- let i = entry.resultIndex, r = d.results[i], rowNumber = entry.rowNumber, agentName = entry.agentName;
938
- if (!r) {
939
- let pendingLabel2 = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
940
- c.addChild(new Text(truncLine(theme.fg("dim", ` \u25E6 ${pendingLabel2}: ${agentName} \xB7 pending`), width), 0, 0));
941
- continue;
942
- }
943
- let output = getSingleResultOutput(r), progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg && "status" in rProg && rProg.status === "running", rPending = rProg && "status" in rProg && rProg.status === "pending", pendingLabel = rPending ? ` ${theme.fg("dim", "\xB7 pending")}` : "", lead = compactRowLead({ description: r.description ?? agentName, task: r.task }), permissionBadge = r.permissions ? theme.fg("dim", r.permissions) : "", modelBadge = formatModelSelection(r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : void 0), r.model ?? (rProg && "model" in rProg ? rProg.model : void 0), r.thinking ?? (rProg && "thinking" in rProg ? rProg.thinking : void 0));
944
- c.addChild(animatedLine((f, now) => {
945
- let glyph2 = rPending ? theme.fg("dim", "\u25E6") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), f), stepStats = formatProgressStats(theme, rProg, !0, { running: !!rRunning, terminal: !rRunning && !rPending, now });
946
- return truncLine(` ${glyph2} ${themeBold(theme, lead)}${modelBadge ? ` ${theme.fg("dim", "\xB7")} ${theme.fg("dim", modelBadge)}` : ""}${permissionBadge ? ` ${theme.fg("dim", "\xB7")} ${permissionBadge}` : ""}${stepStats ? ` ${theme.fg("dim", "\xB7")} ${stepStats}` : ""}${pendingLabel}`, width);
947
- }, frame, !!rRunning)), rRunning && rProg && "status" in rProg ? c.addChild(animatedLine((_f, now) => formatCompactStatsHangLine(rProg, width, (s) => theme.fg("dim", s), " \u23BF ", now, !0), frame, !0)) : !rPending && (r.exitCode !== 0 || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output)) && c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` \u23BF ${resultStatusLine(r, output)}`), width), 0, 0));
839
+ let c = new Container(), width = getTermWidth() - 4, entries = compactForegroundEntries(d);
840
+ for (let [rowIndex, entry] of entries.entries()) {
841
+ let flatIndex = entry.flatIndex, result2 = flatIndex === void 0 ? void 0 : resultAtFlatIndex(d, flatIndex), progress = result2?.progress ?? (flatIndex === void 0 ? void 0 : d.progress?.find((candidate) => candidate.index === flatIndex)) ?? result2?.progressSummary, status = progress?.status ?? entry.node?.status ?? "pending", description = compactRowLead({
842
+ description: result2?.description ?? progress?.description ?? entry.node?.label ?? (flatIndex === void 0 ? void 0 : d.chainChildren?.[flatIndex]?.description) ?? (flatIndex === void 0 ? void 0 : d.chainAgents?.[flatIndex]) ?? result2?.agent ?? progress?.agent,
843
+ task: result2?.task ?? progress?.task
844
+ }) || `child ${rowIndex + 1}`, running = status === "running", output = result2 ? getSingleResultOutput(result2) : "", modelBadge = formatCompactModelBadge(result2?.modelSelection ?? progress?.modelSelection, result2?.model ?? progress?.model);
845
+ c.addChild(animatedLine((animationTick, now) => {
846
+ let animationFrame = foregroundSpinnerFrame(animationTick);
847
+ return formatCompactSubagentRow(theme, width, {
848
+ glyph: result2 ? resultGlyph(result2, output, theme, running, progressRunningSeed(progress), animationFrame) : progressStatusGlyph({ ...progress, status, index: flatIndex }, theme, animationFrame),
849
+ description,
850
+ modelBadge,
851
+ tokens: result2 ? compactResultTokens(result2) : progress?.tokens ?? 0,
852
+ durationMs: liveDurationMs(progress, running, now),
853
+ terminalStatus: result2 ? compactResultTerminalStatus(result2) : compactTerminalStatus(status)
854
+ });
855
+ }, frame, running));
948
856
  }
949
857
  return c;
950
858
  }
951
859
  function renderSubagentResult(result2, options, theme, frame) {
952
- let d = result2.details;
953
- if (!d || !d.results.length) {
860
+ let d = result2.details, hasWorkflowRows = d?.mode !== "management" && !!(d?.workflowGraph?.nodes.length || d?.progress?.length || d?.chainChildren?.length);
861
+ if (!d || !d.results.length && !hasWorkflowRows) {
954
862
  let t = result2.content[0], text = t?.type === "text" ? t.text : "(no output)", contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "", width = getTermWidth() - 4;
955
863
  if (!text.includes(`
956
864
  `))
@@ -971,7 +879,7 @@ function renderSubagentResult(result2, options, theme, frame) {
971
879
  return fit3(`${icon2} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge2}${stats ? ` | ${stats}` : ""}`);
972
880
  }, frame, isRunning)), c2.addChild(new Spacer(1));
973
881
  let taskMaxLen = Math.max(20, w2 - 8), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
974
- if (c2.addChild(new Text(fit3(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0)), c2.addChild(new Spacer(1)), isRunning && r.progress) {
882
+ if (c2.addChild(new Text(fit3(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0)), !isRunning && compactResultTerminalStatus(r) && c2.addChild(new Text(fit3(theme.fg(r.exitCode !== 0 ? "error" : "warning", resultStatusLine(r, output))), 0, 0)), c2.addChild(new Spacer(1)), isRunning && r.progress) {
975
883
  let progressSnapshotNow = snapshotNowForProgress(r.progress), toolLine = formatCurrentToolLine(r.progress, w2, expanded, progressSnapshotNow);
976
884
  toolLine && c2.addChild(new Text(fit3(theme.fg("warning", `> ${toolLine}`)), 0, 0));
977
885
  let liveStatusLine = buildLiveStatusLine(r.progress, progressSnapshotNow);
@@ -993,7 +901,7 @@ function renderSubagentResult(result2, options, theme, frame) {
993
901
  }
994
902
  if (!expanded)
995
903
  return renderMultiCompact(d, theme, frame);
996
- let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), ok = d.results.filter((r) => r.progress?.status === "completed" || r.exitCode === 0 && r.progress?.status !== "running").length, hasEmptyWithoutTarget = d.results.some((r) => r.exitCode === 0 && r.progress?.status !== "running" && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r))), hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]), hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), hasWorkflowPause = workflowGraphHasStatus(d, ["paused", "detached"]), icon = hasRunning ? theme.fg("warning", "running") : hasEmptyWithoutTarget ? theme.fg("warning", "warning") : hasWorkflowFailure ? theme.fg("error", "failed") : hasWorkflowStop ? theme.fg("warning", "stopped") : hasWorkflowPause ? theme.fg("warning", "paused") : ok === d.results.length ? theme.fg("success", "ok") : theme.fg("error", "failed"), totalSummary = d.progressSummary || d.results.reduce((acc, r) => {
904
+ let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), ok = d.results.filter((r) => r.progress?.status === "completed" || r.exitCode === 0 && r.progress?.status !== "running").length, hasEmptyWithoutTarget = d.results.some((r) => r.exitCode === 0 && r.progress?.status !== "running" && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r))), hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]), hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), hasWorkflowPause = workflowGraphHasStatus(d, ["paused", "detached"]), hasWorkflowPending = workflowGraphHasStatus(d, ["pending"]), icon = hasRunning ? theme.fg("warning", "running") : hasEmptyWithoutTarget ? theme.fg("warning", "warning") : hasWorkflowFailure ? theme.fg("error", "failed") : hasWorkflowStop ? theme.fg("warning", "stopped") : hasWorkflowPause ? theme.fg("warning", "paused") : hasWorkflowPending ? theme.fg("dim", "pending") : ok === d.results.length ? theme.fg("success", "ok") : theme.fg("error", "failed"), totalSummary = d.progressSummary || d.results.reduce((acc, r) => {
997
905
  let prog = r.progress || r.progressSummary;
998
906
  return prog && (acc.toolCount += prog.toolCount, acc.tokens += prog.tokens, acc.durationMs = d.mode === "chain" ? acc.durationMs + prog.durationMs : Math.max(acc.durationMs, prog.durationMs)), acc;
999
907
  }, { toolCount: 0, tokens: 0, durationMs: 0 }), modeLabel = d.mode, contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "", multiLabel = buildMultiProgressLabel(d, hasRunning), itemTitle = multiLabel.itemTitle, chainVis = d.chainAgents?.length && !multiLabel.hasParallelInChain ? d.chainAgents.map((agent, i) => {
@@ -1007,10 +915,26 @@ function renderSubagentResult(result2, options, theme, frame) {
1007
915
  ].filter(Boolean), summaryStr = summaryParts.length ? ` | ${summaryParts.join(", ")}` : "";
1008
916
  return fit2(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} \xB7 ${multiLabel.headerLabel}${summaryStr}`);
1009
917
  }, frame, hasRunning)), chainVis && c.addChild(new Text(fit2(` ${chainVis}`), 0, 0));
1010
- let useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length, displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0, displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : useResultsDirectly ? d.results.length : d.chainAgents.length, chainEntries = buildChainRenderEntries(d, multiLabel), renderEntries = chainEntries ?? Array.from({ length: displayEnd - displayStart }, (_, offset) => {
1011
- let i = displayStart + offset, r = d.results[i], rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
1012
- return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? r?.agent || `step-${rowNumber}` : d.chainAgents[i] || r?.agent || `step-${rowNumber}` };
1013
- });
918
+ let chainEntries = buildChainRenderEntries(d, multiLabel), fallbackEntries = compactForegroundEntries(d).filter((entry) => !multiLabel.showActiveGroupOnly || entry.flatIndex === void 0 || entry.flatIndex >= multiLabel.groupStartIndex && entry.flatIndex < multiLabel.groupEndIndex).map((entry, rowIndex) => {
919
+ if (entry.flatIndex === void 0)
920
+ return {
921
+ kind: "placeholder",
922
+ rowNumber: entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1,
923
+ stepLabel: `Step ${entry.node?.stepIndex !== void 0 ? entry.node.stepIndex + 1 : rowIndex + 1}`,
924
+ agentName: entry.node?.label ?? `step-${rowIndex + 1}`,
925
+ status: entry.node?.status ?? "pending",
926
+ error: entry.node?.error
927
+ };
928
+ let result3 = resultAtFlatIndex(d, entry.flatIndex), rowNumber = multiLabel.showActiveGroupOnly ? entry.flatIndex - multiLabel.groupStartIndex + 1 : entry.flatIndex + 1;
929
+ return {
930
+ kind: "result",
931
+ resultIndex: entry.flatIndex,
932
+ rowNumber,
933
+ agentName: result3?.agent ?? entry.node?.label ?? d.chainChildren?.[entry.flatIndex]?.description ?? d.chainAgents?.[entry.flatIndex] ?? `step-${rowNumber}`,
934
+ status: entry.node?.status,
935
+ error: entry.node?.error
936
+ };
937
+ }), renderEntries = chainEntries ?? fallbackEntries;
1014
938
  c.addChild(new Spacer(1));
1015
939
  for (let entry of renderEntries) {
1016
940
  if (entry.kind === "placeholder") {
@@ -1018,10 +942,10 @@ function renderSubagentResult(result2, options, theme, frame) {
1018
942
  c.addChild(new Text(fit2(` ${statusLabel} ${entry.stepLabel}: ${theme.bold(entry.agentName)}`), 0, 0)), c.addChild(new Text(theme.fg(entry.status === "failed" ? "error" : "dim", ` status: ${entry.status}`), 0, 0)), entry.error && c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0)), c.addChild(new Spacer(1));
1019
943
  continue;
1020
944
  }
1021
- let i = entry.resultIndex, r = d.results[i], rowNumber = entry.rowNumber, agentName = entry.agentName;
945
+ let i = entry.resultIndex, r = resultAtFlatIndex(d, i), rowNumber = entry.rowNumber, agentName = entry.agentName;
1022
946
  if (!r) {
1023
- let pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1024
- c.addChild(new Text(fit2(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0)), c.addChild(new Text(theme.fg("dim", " status: pending"), 0, 0)), c.addChild(new Spacer(1));
947
+ let rowLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`, status = entry.status ?? "pending";
948
+ c.addChild(new Text(fit2(theme.fg(status === "failed" ? "error" : "dim", ` ${rowLabel}: ${agentName}`)), 0, 0)), c.addChild(new Text(theme.fg(status === "failed" ? "error" : "dim", ` status: ${status}`), 0, 0)), entry.error && c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0)), c.addChild(new Spacer(1));
1025
949
  continue;
1026
950
  }
1027
951
  let progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg?.status === "running", stepNumber = typeof rProg?.index == "number" ? rProg.index + 1 : i + 1, resultOutput = getSingleResultOutput(r), statusIcon = rRunning ? theme.fg("warning", "running") : r.exitCode !== 0 ? theme.fg("error", "failed") : hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput) ? theme.fg("warning", "warning") : theme.fg("success", "done"), modelDisplay = modelThinkingBadge(theme, r.model, r.thinking ?? (rProg && "thinking" in rProg ? rProg.thinking : void 0), r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : void 0)), stepLabel = resultRowLabel(d, multiLabel, i, stepNumber), toolCallLines = getToolCallLines(r, expanded);
@@ -1030,7 +954,7 @@ function renderSubagentResult(result2, options, theme, frame) {
1030
954
  return fit2(rRunning ? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats ? ` | ${stats}` : ""}` : `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats ? ` | ${stats}` : ""}`);
1031
955
  }, frame, rRunning));
1032
956
  let taskMaxLen = Math.max(20, w - 12), taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
1033
- if (c.addChild(new Text(fit2(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0)), r.skills?.length && c.addChild(new Text(fit2(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0)), r.skillsWarning && c.addChild(new Text(fit2(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0)), r.attemptedModels && r.attemptedModels.length > 1 && c.addChild(new Text(fit2(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" \u2192 ")}`)), 0, 0)), rRunning && rProg) {
957
+ if (c.addChild(new Text(fit2(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0)), !rRunning && compactResultTerminalStatus(r) && c.addChild(new Text(fit2(theme.fg(r.exitCode !== 0 ? "error" : "warning", ` ${resultStatusLine(r, resultOutput)}`)), 0, 0)), r.skills?.length && c.addChild(new Text(fit2(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0)), r.skillsWarning && c.addChild(new Text(fit2(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0)), r.attemptedModels && r.attemptedModels.length > 1 && c.addChild(new Text(fit2(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" \u2192 ")}`)), 0, 0)), rRunning && rProg) {
1034
958
  rProg.skills?.length && c.addChild(new Text(fit2(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)), 0, 0));
1035
959
  let progressSnapshotNow = snapshotNowForProgress(rProg), toolLine = formatCurrentToolLine(rProg, w, expanded, progressSnapshotNow);
1036
960
  toolLine && c.addChild(new Text(fit2(theme.fg("warning", ` > ${toolLine}`)), 0, 0));
@@ -1059,15 +983,15 @@ import { Type } from "typebox";
1059
983
  function keepTopLevelParameterDescriptions(schema) {
1060
984
  return pruneNestedDescriptions(schema, []);
1061
985
  }
1062
- function pruneNestedDescriptions(value, path12) {
986
+ function pruneNestedDescriptions(value, path13) {
1063
987
  if (!value || typeof value != "object")
1064
988
  return value;
1065
989
  let result2 = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
1066
990
  for (let key of Reflect.ownKeys(value)) {
1067
991
  let descriptor = Object.getOwnPropertyDescriptor(value, key);
1068
- if (descriptor && !(key === "description" && !isPropertiesMap(path12) && !isTopLevelParameterHelpText(path12))) {
992
+ if (descriptor && !(key === "description" && !isPropertiesMap(path13) && !isTopLevelParameterHelpText(path13))) {
1069
993
  if ("value" in descriptor) {
1070
- let nextPath = typeof key == "string" ? [...path12, key] : path12;
994
+ let nextPath = typeof key == "string" ? [...path13, key] : path13;
1071
995
  descriptor.value = pruneNestedDescriptions(descriptor.value, nextPath);
1072
996
  }
1073
997
  Object.defineProperty(result2, key, descriptor);
@@ -1075,11 +999,11 @@ function pruneNestedDescriptions(value, path12) {
1075
999
  }
1076
1000
  return result2;
1077
1001
  }
1078
- function isPropertiesMap(path12) {
1079
- return path12[path12.length - 1] === "properties";
1002
+ function isPropertiesMap(path13) {
1003
+ return path13[path13.length - 1] === "properties";
1080
1004
  }
1081
- function isTopLevelParameterHelpText(path12) {
1082
- return path12.length === 2 && path12[0] === "properties";
1005
+ function isTopLevelParameterHelpText(path13) {
1006
+ return path13.length === 2 && path13[0] === "properties";
1083
1007
  }
1084
1008
  var SkillOverride = Type.Unsafe({
1085
1009
  anyOf: [
@@ -1097,10 +1021,16 @@ var SkillOverride = Type.Unsafe({
1097
1021
  }), OutputModeOverride = Type.String({
1098
1022
  enum: ["inline", "file-only"],
1099
1023
  description: "Return saved output inline (default) or only a concise file reference. file-only requires output to be a path."
1100
- }), TierOverride = Type.String({
1024
+ }), TIER_SELECTION_DESCRIPTION = "Choose the lowest model tier that can reliably complete the task: 'light' for quick checks, fact gathering, and small code inspections; 'standard' for everyday coding, clearly described bugs, and focused reviews; 'heavy' for complex implementation, difficult debugging, architectural decisions, and large or high-risk reviews.", TierOverride = Type.Optional(Type.String({
1101
1025
  enum: ["light", "standard", "heavy"],
1102
- description: "Required model tier: 'light' for simple/fast work, 'standard' for typical coding, or 'heavy' for complex reasoning. The configured tier model must be available and authenticated."
1103
- }), ReadsOverride = Type.Unsafe({
1026
+ description: `Required by default. ${TIER_SELECTION_DESCRIPTION} Do not combine with model. The configured tier model must be available and authenticated.`
1027
+ })), ModelOverride = Type.Optional(Type.String({
1028
+ minLength: 1,
1029
+ description: "Explicit provider/model id only when the user names a model. Do not combine with tier. Optional thinking belongs in the thinking field or as a known :level suffix."
1030
+ })), ThinkingOverride = Type.Optional(Type.String({
1031
+ enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
1032
+ description: "Thinking level for an explicit model launch. Omit for tier launches (they use configured tier thinking)."
1033
+ })), ReadsOverride = Type.Unsafe({
1104
1034
  anyOf: [
1105
1035
  { type: "array", items: { type: "string" } },
1106
1036
  { type: "boolean", enum: [!1] }
@@ -1147,6 +1077,8 @@ var SkillOverride = Type.Unsafe({
1147
1077
  reads: Type.Optional(ReadsOverride),
1148
1078
  progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for this task" })),
1149
1079
  tier: TierOverride,
1080
+ model: ModelOverride,
1081
+ thinking: ThinkingOverride,
1150
1082
  skill: Type.Optional(SkillOverride),
1151
1083
  toolBudget: Type.Optional(ToolBudgetOverride),
1152
1084
  acceptance: Type.Optional(AcceptanceOverride)
@@ -1166,6 +1098,8 @@ var SkillOverride = Type.Unsafe({
1166
1098
  progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
1167
1099
  skill: Type.Optional(SkillOverride),
1168
1100
  tier: TierOverride,
1101
+ model: ModelOverride,
1102
+ thinking: ThinkingOverride,
1169
1103
  toolBudget: Type.Optional(ToolBudgetOverride),
1170
1104
  acceptance: Type.Optional(AcceptanceOverride)
1171
1105
  }), DynamicExpandSchema = Type.Object({
@@ -1195,6 +1129,8 @@ var SkillOverride = Type.Unsafe({
1195
1129
  progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
1196
1130
  skill: Type.Optional(SkillOverride),
1197
1131
  tier: TierOverride,
1132
+ model: ModelOverride,
1133
+ thinking: ThinkingOverride,
1198
1134
  toolBudget: Type.Optional(ToolBudgetOverride),
1199
1135
  acceptance: Type.Optional(AcceptanceOverride)
1200
1136
  }, { additionalProperties: !1 }), DynamicCollectSchema = Type.Object({
@@ -1217,6 +1153,8 @@ var SkillOverride = Type.Unsafe({
1217
1153
  progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
1218
1154
  skill: Type.Optional(SkillOverride),
1219
1155
  tier: TierOverride,
1156
+ model: ModelOverride,
1157
+ thinking: ThinkingOverride,
1220
1158
  toolBudget: Type.Optional(ToolBudgetOverride),
1221
1159
  acceptance: Type.Optional(AcceptanceOverride),
1222
1160
  parallel: Type.Optional(Type.Unsafe({
@@ -1234,7 +1172,7 @@ var SkillOverride = Type.Unsafe({
1234
1172
  description: "Create isolated git worktrees for each parallel task."
1235
1173
  }))
1236
1174
  }, {
1237
- description: "Chain step: use {task, description, tier, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child requires tier.",
1175
+ description: "Chain step: use {task, description, tier|model, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child needs exactly one of tier or model.",
1238
1176
  additionalProperties: !1
1239
1177
  }), ControlOverrides = Type.Object({
1240
1178
  enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
@@ -1273,12 +1211,9 @@ var SkillOverride = Type.Unsafe({
1273
1211
  })),
1274
1212
  lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 500, description: "Maximum transcript lines for action='status', view='transcript'. Defaults to 80." })),
1275
1213
  message: Type.Optional(Type.String({ description: "Follow-up message for action='resume' or non-terminal guidance for action='steer'. Use index to choose a child from multi-child runs." })),
1276
- scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session to avoid persistent settings writes unless user/project is explicit." })),
1277
- target: Type.Optional(Type.String({ enum: ["main", "children"], description: "Target for action='watchdog.configure'. Defaults to main." })),
1278
- thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [!1] }], description: "Thinking level for action='watchdog.configure' (off/minimal/low/medium/high/xhigh/max, inherit, or false for off)." })),
1279
1214
  schedule: Type.Optional(Type.String({ description: "Explicit one-shot schedule for action='schedule'. Only honored when scheduledRuns.enabled is true. Use '+10m' or a future ISO timestamp with timezone; scheduled runs always launch async with fresh context." })),
1280
1215
  scheduleName: Type.Optional(Type.String({ description: "Optional display name for action='schedule'." })),
1281
- tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
1216
+ tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier|model, thinking?, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
1282
1217
  concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or unlimited." })),
1283
1218
  worktree: Type.Optional(Type.Boolean({
1284
1219
  description: "Create isolated git worktrees for parallel tasks; requires clean git state."
@@ -1289,7 +1224,7 @@ var SkillOverride = Type.Unsafe({
1289
1224
  description: "Children always start with a fresh session. Fork context is not available."
1290
1225
  })),
1291
1226
  chainDir: Type.Optional(Type.String({ description: "Persistent chain artifact directory; defaults to user-scoped temp storage." })),
1292
- async: Type.Optional(Type.Boolean({ description: "Run in background (default: false, or per config)" })),
1227
+ async: Type.Optional(Type.Boolean({ description: "Run async in the background. Defaults to true when omitted; set false for immediate foreground results." })),
1293
1228
  timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Optional run-level timeout in ms for foreground and async/background runs. Alias of maxRuntimeMs." })),
1294
1229
  maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias of timeoutMs for optional run-level timeout in foreground and async/background runs." })),
1295
1230
  turnBudget: Type.Optional(TurnBudgetOverride),
@@ -1300,7 +1235,7 @@ var SkillOverride = Type.Unsafe({
1300
1235
  share: Type.Optional(Type.Boolean({ description: "Upload session to GitHub Gist for sharing (default: false)" })),
1301
1236
  sessionDir: Type.Optional(Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" })),
1302
1237
  // Clarification TUI
1303
- clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution. Explicit clarify: true keeps the run foreground for the clarify UI; omitted clarify can still run in the background when async: true is set." })),
1238
+ clarify: Type.Optional(Type.Boolean({ description: "Show the interactive TUI preview/editor before execution. clarify:true keeps the run foreground even when async is omitted or true." })),
1304
1239
  control: Type.Optional(ControlOverrides),
1305
1240
  output: Type.Optional(Type.Unsafe({
1306
1241
  anyOf: [
@@ -1313,15 +1248,26 @@ var SkillOverride = Type.Unsafe({
1313
1248
  skill: Type.Optional(SkillOverride),
1314
1249
  tier: Type.Optional(Type.String({
1315
1250
  enum: ["light", "standard", "heavy"],
1316
- description: "Required for SINGLE execution. Omit for control actions; PARALLEL and CHAIN children carry their own required tier."
1251
+ description: `Preferred for SINGLE execution. Required by default. Omit for control actions. Do not combine with model; PARALLEL and CHAIN children carry their own tier or model. ${TIER_SELECTION_DESCRIPTION}`
1252
+ })),
1253
+ model: Type.Optional(Type.String({
1254
+ minLength: 1,
1255
+ description: "Explicit provider/model for SINGLE execution only when the user names a model. Do not combine with tier."
1256
+ })),
1257
+ thinking: Type.Optional(Type.String({
1258
+ enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
1259
+ description: "Thinking level for an explicit model launch. Tier launches use configured tier thinking instead."
1317
1260
  })),
1318
1261
  acceptance: Type.Optional(AcceptanceOverride)
1319
1262
  }), SubagentParams = keepTopLevelParameterDescriptions(SubagentParamsSchema), SubagentWaitParamsSchema = Type.Object({
1320
1263
  id: Type.Optional(Type.String({
1321
1264
  description: "Async run or remembered detached foreground run id/prefix to wait for one specific run. Omit to wait across every active async run started in this session."
1322
1265
  })),
1266
+ questionId: Type.Optional(Type.String({
1267
+ description: "Wait for one supervisor question id from subagent_supervisor action='ask'. Returns when that question is answered, expired, or cancelled. Separate from final async run results."
1268
+ })),
1323
1269
  all: Type.Optional(Type.Boolean({
1324
- description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id targets a single run."
1270
+ description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id or questionId targets one item."
1325
1271
  })),
1326
1272
  timeoutMs: Type.Optional(Type.Integer({
1327
1273
  minimum: 1,
@@ -1346,28 +1292,28 @@ function typeName(value) {
1346
1292
  function quoteList(keys) {
1347
1293
  return keys.map((key) => `"${key}"`).join(", ");
1348
1294
  }
1349
- function disallowedMessage(path12, keys, allowed, example) {
1295
+ function disallowedMessage(path13, keys, allowed, example) {
1350
1296
  let subject = keys.length === 1 ? `property "${keys[0]}" is not allowed` : `properties ${quoteList(keys)} are not allowed`, examplePart = example ? `
1351
1297
 
1352
1298
  Example: ${example}` : "";
1353
- return `subagent ${path12}: ${subject}.
1299
+ return `subagent ${path13}: ${subject}.
1354
1300
  Allowed properties: ${allowed.join(", ")}.${examplePart}`;
1355
1301
  }
1356
- function expectObject(value, path12, allowed, example) {
1302
+ function expectObject(value, path13, allowed, example) {
1357
1303
  if (!isPlainObject(value)) {
1358
1304
  let examplePart = example ? `
1359
1305
 
1360
1306
  Example: ${example}` : "";
1361
- throw new Error(`subagent ${path12}: expected an object, received ${typeName(value)}.
1307
+ throw new Error(`subagent ${path13}: expected an object, received ${typeName(value)}.
1362
1308
  Allowed properties: ${allowed.join(", ")}.${examplePart}`);
1363
1309
  }
1364
1310
  }
1365
- function checkNoExtraKeys(value, path12, schema, allowed, example) {
1311
+ function checkNoExtraKeys(value, path13, schema, allowed, example) {
1366
1312
  if (schema.additionalProperties !== !1)
1367
1313
  return;
1368
1314
  let extra = disallowedKeys(value, allowed);
1369
1315
  if (extra.length > 0)
1370
- throw new Error(disallowedMessage(path12, extra, allowed, example));
1316
+ throw new Error(disallowedMessage(path13, extra, allowed, example));
1371
1317
  }
1372
1318
  function validateChainInput(args) {
1373
1319
  if (!isPlainObject(args))
@@ -1525,7 +1471,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
1525
1471
  if (parsed.type !== "subagent.control")
1526
1472
  return;
1527
1473
  let record = parsed;
1528
- if (!record.event || !Array.isArray(record.channels))
1474
+ if (!record.event || !Array.isArray(record.channels) || record.event.reason === "completion_guard")
1529
1475
  return;
1530
1476
  let payload = {
1531
1477
  event: record.event,
@@ -1620,8 +1566,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
1620
1566
  }
1621
1567
  widgetRenderKey(job) !== widgetStateBefore && (widgetChanged = !0);
1622
1568
  }
1623
- let timerNeedsPaint = [...state.asyncJobs.values()].some((job) => job.status === "running");
1624
- (widgetChanged || timerNeedsPaint) && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
1569
+ widgetChanged && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
1625
1570
  }, pollIntervalMs), state.poller.unref?.());
1626
1571
  };
1627
1572
  return { ensurePoller, refreshWidget, handleStarted: (data) => {
@@ -1761,15 +1706,15 @@ function createFileCoalescer(handler, defaultDelayMs, timerApi = defaultTimerApi
1761
1706
 
1762
1707
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js
1763
1708
  var WATCHER_RESTART_DELAY_MS = 3e3, POLL_INTERVAL_MS2 = 3e3;
1764
- function sanitizeNestedResultChildren(value, resultPath, label) {
1709
+ function sanitizeNestedResultChildren(value, resultPath, label, report) {
1765
1710
  if (value === void 0)
1766
1711
  return;
1767
1712
  if (!Array.isArray(value)) {
1768
- console.error(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
1713
+ report(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
1769
1714
  return;
1770
1715
  }
1771
1716
  let children = value.map((child) => sanitizeSummary(child)).filter((child) => !!child);
1772
- return children.length !== value.length && console.error(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`), children.length ? children : void 0;
1717
+ return children.length !== value.length && report(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`), children.length ? children : void 0;
1773
1718
  }
1774
1719
  function getErrorCode(error) {
1775
1720
  return typeof error == "object" && error !== null && "code" in error ? error.code : void 0;
@@ -1782,27 +1727,27 @@ function shouldFallBackToPolling(error) {
1782
1727
  return code === "EMFILE" || code === "ENOSPC";
1783
1728
  }
1784
1729
  function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {}) {
1785
- let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval }, handleResult = async (file) => {
1786
- let resultPath = path2.join(resultsDir, file);
1787
- if (fsApi.existsSync(resultPath))
1730
+ let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval }, generation = 0, inFlight = /* @__PURE__ */ new Set(), attempts = /* @__PURE__ */ new Map(), report = (message, error) => {
1731
+ pi.appendEntry?.("subagent_result_delivery", { message, error: error instanceof Error ? error.message : error });
1732
+ }, handleResult = async (file) => {
1733
+ if (inFlight.has(file) || (attempts.get(file) ?? 0) >= 3)
1734
+ return;
1735
+ let currentGeneration = generation, sessionId = state.currentSessionId, isCurrent = () => generation === currentGeneration && state.currentSessionId === sessionId, resultPath = path2.join(resultsDir, file);
1736
+ if (fsApi.existsSync(resultPath)) {
1737
+ inFlight.add(file);
1788
1738
  try {
1789
1739
  let data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8"));
1790
1740
  if (typeof data.sessionId != "string" || data.sessionId !== state.currentSessionId)
1791
1741
  return;
1792
- let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren"));
1742
+ let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren", report));
1793
1743
  if (!nestedChildren?.length && !hasExplicitNestedChildren)
1794
1744
  try {
1795
1745
  nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
1796
1746
  } catch (error) {
1797
- console.error(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
1747
+ report(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
1798
1748
  return;
1799
1749
  }
1800
- let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`);
1801
- if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
1802
- fsApi.unlinkSync(resultPath);
1803
- return;
1804
- }
1805
- let hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
1750
+ let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`), hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
1806
1751
  agent: data.agent,
1807
1752
  output: data.summary,
1808
1753
  success: data.success
@@ -1810,7 +1755,7 @@ function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {})
1810
1755
  let baseOutput = result2.output ?? data.summary, hasRealOutput = typeof baseOutput == "string" && baseOutput.trim().length > 0, output = hasRealOutput ? baseOutput : "(no output)", summary = result2.success === !1 && result2.error ? `${result2.error}${hasRealOutput ? `
1811
1756
 
1812
1757
  Output:
1813
- ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children`), childState = result2.state === "paused" || result2.state === "stopped" ? result2.state : result2.stopped === !0 ? "stopped" : data.state === "paused" || !hasResultChildren && (data.state === "stopped" || typeof result2.success != "boolean") ? data.state : void 0;
1758
+ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children`, report), childState = result2.state === "paused" || result2.state === "stopped" ? result2.state : result2.stopped === !0 ? "stopped" : data.state === "paused" || !hasResultChildren && (data.state === "stopped" || typeof result2.success != "boolean") ? data.state : void 0;
1814
1759
  return {
1815
1760
  agent: result2.agent ?? data.agent ?? `step-${index + 1}`,
1816
1761
  status: resolveSubagentResultStatus({
@@ -1824,7 +1769,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
1824
1769
  ...result2.intercomTarget ? { intercomTarget: result2.intercomTarget } : {},
1825
1770
  ...childNestedChildren ? { children: childNestedChildren } : {}
1826
1771
  };
1827
- }), nestedChildren), intercomTarget = data.intercomTarget?.trim();
1772
+ }), nestedChildren);
1773
+ if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs) || pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
1774
+ ...data,
1775
+ runId,
1776
+ ...nestedChildren?.length ? { nestedChildren } : {},
1777
+ ...Array.isArray(data.results) ? { results: hasResultChildren ? normalizedChildren.map((child, index) => ({
1778
+ ...data.results[index],
1779
+ agent: child.agent,
1780
+ status: child.status,
1781
+ summary: child.summary,
1782
+ index: child.index,
1783
+ artifactPath: child.artifactPath,
1784
+ sessionPath: child.sessionPath,
1785
+ children: child.children
1786
+ })) : [] } : {}
1787
+ }), !isCurrent())
1788
+ return;
1789
+ let intercomTarget = data.state === "stopped" ? void 0 : data.intercomTarget?.trim();
1828
1790
  if (intercomTarget) {
1829
1791
  let mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain" ? data.mode : resultChildren.length > 1 ? "chain" : "single", payload = buildSubagentResultIntercomPayload({
1830
1792
  to: intercomTarget,
@@ -1835,30 +1797,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
1835
1797
  asyncId: data.id,
1836
1798
  asyncDir: data.asyncDir
1837
1799
  });
1838
- await deliverSubagentResultIntercomEvent(pi.events, payload) || console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
1800
+ payload.requestId = `async-result:${sessionId}:${completionKey}`, payload.ownerNotificationSessionId = sessionId, attempts.set(file, (attempts.get(file) ?? 0) + 1);
1801
+ let delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
1802
+ if (!isCurrent())
1803
+ return;
1804
+ if (!delivered) {
1805
+ report(`Async result delivery pending for '${runId}'; output retained.`, resultPath), attempts.get(file) < 3 && state.resultFileCoalescer.schedule(file, 1e3);
1806
+ return;
1807
+ }
1839
1808
  }
1840
- pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
1841
- ...data,
1842
- runId,
1843
- ...nestedChildren?.length ? { nestedChildren } : {},
1844
- ...Array.isArray(data.results) ? {
1845
- results: hasResultChildren ? normalizedChildren.map((child, index) => ({
1846
- ...data.results[index],
1847
- agent: child.agent,
1848
- status: child.status,
1849
- summary: child.summary,
1850
- index: child.index,
1851
- artifactPath: child.artifactPath,
1852
- sessionPath: child.sessionPath,
1853
- children: child.children
1854
- })) : []
1855
- } : {}
1856
- }), fsApi.unlinkSync(resultPath);
1809
+ isCurrent() && fsApi.unlinkSync(resultPath);
1857
1810
  } catch (error) {
1858
1811
  if (isNotFoundError(error))
1859
1812
  return;
1860
- console.error(`Failed to process subagent result file '${resultPath}':`, error);
1813
+ report(`Failed to process subagent result file '${resultPath}'.`, error);
1814
+ } finally {
1815
+ inFlight.delete(file);
1861
1816
  }
1817
+ }
1862
1818
  };
1863
1819
  state.resultFileCoalescer = createFileCoalescer((file) => {
1864
1820
  handleResult(file);
@@ -1869,10 +1825,10 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
1869
1825
  } catch (error) {
1870
1826
  if (isNotFoundError(error))
1871
1827
  return;
1872
- console.error(`Failed to scan subagent result directory '${resultsDir}':`, error);
1828
+ report(`Failed to scan subagent result directory '${resultsDir}':`, error);
1873
1829
  }
1874
1830
  }, startPollingFallback = (reason) => {
1875
- state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (console.error(`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`), primeExistingResults(), state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS2), state.watcherRestartTimer.unref?.());
1831
+ state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (report(`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`), primeExistingResults(), state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS2), state.watcherRestartTimer.unref?.());
1876
1832
  }, scheduleRestart = () => {
1877
1833
  state.watcherRestartTimer || (state.watcherRestartTimer = timers.setTimeout(() => {
1878
1834
  state.watcherRestartTimer = null;
@@ -1883,7 +1839,7 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
1883
1839
  startPollingFallback(error);
1884
1840
  return;
1885
1841
  }
1886
- console.error(`Failed to restart subagent result watcher for '${resultsDir}':`, error), scheduleRestart();
1842
+ report(`Failed to restart subagent result watcher for '${resultsDir}':`, error), scheduleRestart();
1887
1843
  }
1888
1844
  }, WATCHER_RESTART_DELAY_MS), state.watcherRestartTimer.unref?.());
1889
1845
  }, startResultWatcher = () => {
@@ -1901,19 +1857,19 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
1901
1857
  startPollingFallback(error);
1902
1858
  return;
1903
1859
  }
1904
- console.error(`Subagent result watcher failed for '${resultsDir}':`, error), state.watcher?.close(), state.watcher = null, scheduleRestart();
1860
+ report(`Subagent result watcher failed for '${resultsDir}':`, error), state.watcher?.close(), state.watcher = null, scheduleRestart();
1905
1861
  }), state.watcher.unref?.();
1906
1862
  } catch (error) {
1907
1863
  if (shouldFallBackToPolling(error)) {
1908
1864
  startPollingFallback(error);
1909
1865
  return;
1910
1866
  }
1911
- console.error(`Failed to start subagent result watcher for '${resultsDir}':`, error), state.watcher = null, scheduleRestart();
1867
+ report(`Failed to start subagent result watcher for '${resultsDir}':`, error), state.watcher = null, scheduleRestart();
1912
1868
  }
1913
1869
  }
1914
1870
  };
1915
1871
  return { startResultWatcher, primeExistingResults, stopResultWatcher: () => {
1916
- state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
1872
+ generation++, attempts.clear(), state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
1917
1873
  } };
1918
1874
  }
1919
1875
 
@@ -2446,7 +2402,7 @@ function substituteArgs(template, args) {
2446
2402
  return template.replace(/\$ARGUMENTS/g, all).replace(/\$@/g, all).replace(/\$\{(\d+):-([^}]*)\}/g, (_match, index, fallback) => args[Number(index) - 1] || fallback).replace(/\$(\d+)/g, (_match, index) => args[Number(index) - 1] ?? "");
2447
2403
  }
2448
2404
  function parseRuntimeOptions(words) {
2449
- let args = [], descriptionOverride, permissions, worktree = !1, bg = !1;
2405
+ let args = [], descriptionOverride, permissions, worktree = !1, async;
2450
2406
  for (let i = 0; i < words.length; i++) {
2451
2407
  let word = words[i];
2452
2408
  if (word === "--read-only") {
@@ -2462,7 +2418,11 @@ function parseRuntimeOptions(words) {
2462
2418
  continue;
2463
2419
  }
2464
2420
  if (word === "--bg" || word === "--async") {
2465
- bg = !0;
2421
+ async = !0;
2422
+ continue;
2423
+ }
2424
+ if (word === "--fg" || word === "--foreground") {
2425
+ async = !1;
2466
2426
  continue;
2467
2427
  }
2468
2428
  if (word === "--description") {
@@ -2476,7 +2436,7 @@ function parseRuntimeOptions(words) {
2476
2436
  }
2477
2437
  args.push(word);
2478
2438
  }
2479
- return { args, descriptionOverride, permissions, worktree, bg };
2439
+ return { args, descriptionOverride, permissions, worktree, async };
2480
2440
  }
2481
2441
  function splitChainDeclaration(input) {
2482
2442
  let delimiter = input.indexOf(" -- ");
@@ -2495,7 +2455,7 @@ function workflowParams(workflow, args, runtime) {
2495
2455
  ...workflow.skill !== void 0 ? { skill: workflow.skill } : {},
2496
2456
  ...workflow.cwd ? { cwd: workflow.cwd } : {},
2497
2457
  ...runtime.worktree || workflow.worktree ? { worktree: !0 } : {},
2498
- ...runtime.bg ? { async: !0 } : {}
2458
+ ...runtime.async !== void 0 ? { async: runtime.async } : {}
2499
2459
  };
2500
2460
  }
2501
2461
  function workflowChainStep(workflow, args, runtime) {
@@ -2543,7 +2503,7 @@ function registerPromptWorkflowCommands(input) {
2543
2503
  throw new Error(`Unknown prompt workflow in chain '${workflow.name}': ${stepName}`);
2544
2504
  return workflowChainStep(step, runtime.args, runtime);
2545
2505
  });
2546
- await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.bg ? { async: !0 } : {} }, ctx);
2506
+ await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
2547
2507
  return;
2548
2508
  }
2549
2509
  await run(workflowParams(workflow, runtime.args, runtime), ctx);
@@ -2571,7 +2531,7 @@ function registerPromptWorkflowCommands(input) {
2571
2531
  throw new Error(`Unknown prompt workflow: ${name}`);
2572
2532
  return workflowChainStep(workflow, runtime.args, runtime);
2573
2533
  });
2574
- await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.bg ? { async: !0 } : {} }, ctx);
2534
+ await run({ chain, task: runtime.args.join(" "), clarify: !1, ...runtime.async !== void 0 ? { async: runtime.async } : {} }, ctx);
2575
2535
  } catch (error) {
2576
2536
  ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
2577
2537
  }
@@ -3102,6 +3062,12 @@ var parseInlineConfig = (raw) => {
3102
3062
  case "tier":
3103
3063
  (val === "light" || val === "standard" || val === "heavy") && (config.tier = val);
3104
3064
  break;
3065
+ case "model":
3066
+ val && (config.model = val);
3067
+ break;
3068
+ case "thinking":
3069
+ ["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(val) && (config.thinking = val);
3070
+ break;
3105
3071
  case "permissions":
3106
3072
  (val === "full" || val === "read-only") && (config.permissions = val);
3107
3073
  break;
@@ -3145,15 +3111,14 @@ var parseInlineConfig = (raw) => {
3145
3111
  let end = token.lastIndexOf("]");
3146
3112
  return { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : void 0)) };
3147
3113
  }, extractExecutionFlags = (rawArgs) => {
3148
- let args = rawArgs.trim(), bg = !1;
3114
+ let args = rawArgs.trim(), async;
3149
3115
  for (; ; ) {
3150
- if (args.endsWith(" --bg") || args === "--bg") {
3151
- bg = !0, args = args === "--bg" ? "" : args.slice(0, -5).trim();
3152
- continue;
3153
- }
3154
- break;
3116
+ let match = args.match(/(?:^|\s)(--bg|--async|--fg|--foreground)$/);
3117
+ if (!match)
3118
+ break;
3119
+ async ??= match[1] === "--bg" || match[1] === "--async", args = args.slice(0, match.index).trim();
3155
3120
  }
3156
- return { args, bg };
3121
+ return { args, async };
3157
3122
  };
3158
3123
  function sendSlashText(pi, text) {
3159
3124
  pi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: !0 });
@@ -3651,12 +3616,16 @@ function loadInlineOutputSchema(baseCwd, agent, value) {
3651
3616
  }
3652
3617
  var mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
3653
3618
  let { name, config, task: stepTask } = step;
3654
- if (!config.tier)
3655
- throw new SlashParseError(`Step '${name}' requires tier=light, tier=standard, or tier=heavy.`);
3619
+ if (!config.tier && !config.model)
3620
+ throw new SlashParseError(`Step '${name}' requires tier=light|standard|heavy or model=provider/id.`);
3621
+ if (config.tier && config.model)
3622
+ throw new SlashParseError(`Step '${name}' must choose exactly one of tier or model.`);
3656
3623
  return config.acceptance !== void 0 && validateInlineAcceptanceInput(config.acceptance, name), {
3657
3624
  description: name,
3658
3625
  permissions: config.permissions ?? "full",
3659
- tier: config.tier,
3626
+ ...config.tier ? { tier: config.tier } : {},
3627
+ ...config.model ? { model: config.model } : {},
3628
+ ...config.thinking ? { thinking: config.thinking } : {},
3660
3629
  ...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
3661
3630
  ...config.output !== void 0 ? { output: config.output } : {},
3662
3631
  ...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
@@ -3730,16 +3699,16 @@ function registerSlashCommands(pi, state) {
3730
3699
  }
3731
3700
  };
3732
3701
  pi.registerCommand("run", {
3733
- description: "Run a prompt-defined child: /run description[tier=standard] task [--bg]",
3702
+ description: "Run an async prompt-defined child; add --foreground to wait inline",
3734
3703
  handler: async (args, ctx) => {
3735
- let { args: cleanedArgs, bg } = extractExecutionFlags(args), input = cleanedArgs.trim(), firstSpace = input.indexOf(" ");
3704
+ let { args: cleanedArgs, async } = extractExecutionFlags(args), input = cleanedArgs.trim(), firstSpace = input.indexOf(" ");
3736
3705
  if (!input) {
3737
- ctx.ui.notify("Usage: /run <description> <task> [--bg]", "error");
3706
+ ctx.ui.notify("Usage: /run <description> <task> [--foreground]", "error");
3738
3707
  return;
3739
3708
  }
3740
3709
  let { name: description, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace)), task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
3741
3710
  if (!description || !task || !inline.tier) {
3742
- ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--bg]", "error");
3711
+ ctx.ui.notify("Usage: /run <description>[tier=light|standard|heavy] <task> [--foreground]", "error");
3743
3712
  return;
3744
3713
  }
3745
3714
  let finalTask = task;
@@ -3747,27 +3716,29 @@ function registerSlashCommands(pi, state) {
3747
3716
 
3748
3717
  ${finalTask}`);
3749
3718
  let params = { description, permissions: inline.permissions ?? "full", tier: inline.tier, task: finalTask, clarify: !1 };
3750
- inline.output !== void 0 && (params.output = inline.output), inline.outputMode !== void 0 && (params.outputMode = inline.outputMode), inline.skill !== void 0 && (params.skill = inline.skill), bg && (params.async = !0), await runSlashSubagent(pi, ctx, params);
3719
+ inline.output !== void 0 && (params.output = inline.output), inline.outputMode !== void 0 && (params.outputMode = inline.outputMode), inline.skill !== void 0 && (params.skill = inline.skill), async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
3751
3720
  }
3752
3721
  }), pi.registerCommand("chain", {
3753
- description: 'Run prompt-defined children in sequence: /chain inspect "task" -> implement [--bg]',
3722
+ description: "Run prompt-defined children as an async sequence; add --foreground to wait inline",
3754
3723
  handler: async (args, ctx) => {
3755
- let { args: cleanedArgs, bg } = extractExecutionFlags(args), built = buildChainExpressionSteps(state, cleanedArgs, ctx);
3724
+ let { args: cleanedArgs, async } = extractExecutionFlags(args), built = buildChainExpressionSteps(state, cleanedArgs, ctx);
3756
3725
  if (!built)
3757
3726
  return;
3758
3727
  let params = { chain: built.chain, task: built.task, clarify: !1 };
3759
- bg && (params.async = !0), await runSlashSubagent(pi, ctx, params);
3728
+ async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
3760
3729
  }
3761
3730
  }), pi.registerCommand("parallel", {
3762
- description: 'Run prompt-defined children in parallel: /parallel inspect "task1" -> review "task2" [--bg]',
3731
+ description: "Run prompt-defined children async in parallel; add --foreground to wait inline",
3763
3732
  handler: async (args, ctx) => {
3764
- let { args: cleanedArgs, bg } = extractExecutionFlags(args), parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
3733
+ let { args: cleanedArgs, async } = extractExecutionFlags(args), parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
3765
3734
  if (!parsed)
3766
3735
  return;
3767
3736
  let tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
3768
3737
  description: name,
3769
3738
  permissions: config.permissions ?? "full",
3770
- tier: config.tier,
3739
+ ...config.tier ? { tier: config.tier } : {},
3740
+ ...config.model ? { model: config.model } : {},
3741
+ ...config.thinking ? { thinking: config.thinking } : {},
3771
3742
  task: stepTask ?? parsed.task,
3772
3743
  ...config.output !== void 0 ? { output: config.output } : {},
3773
3744
  ...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
@@ -3775,12 +3746,16 @@ ${finalTask}`);
3775
3746
  ...config.skill !== void 0 ? { skill: config.skill } : {},
3776
3747
  ...config.progress !== void 0 ? { progress: config.progress } : {}
3777
3748
  }));
3778
- if (tasks.some((task) => !task.tier)) {
3779
- ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy].", "error");
3749
+ if (tasks.some((task) => !task.tier && !task.model)) {
3750
+ ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy] or [model=provider/id].", "error");
3751
+ return;
3752
+ }
3753
+ if (tasks.some((task) => task.tier && task.model)) {
3754
+ ctx.ui.notify("Each parallel child must choose exactly one of tier or model.", "error");
3780
3755
  return;
3781
3756
  }
3782
3757
  let params = { tasks, clarify: !1 };
3783
- bg && (params.async = !0), await runSlashSubagent(pi, ctx, params);
3758
+ async !== void 0 && (params.async = async), await runSlashSubagent(pi, ctx, params);
3784
3759
  }
3785
3760
  }), pi.registerCommand("subagent-cost", {
3786
3761
  description: "Show parent and subagent child usage cost for this session",
@@ -4404,28 +4379,506 @@ function registerSlashSubagentBridge(options) {
4404
4379
  }
4405
4380
 
4406
4381
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
4382
+ import * as fs7 from "node:fs";
4383
+ import * as path8 from "node:path";
4384
+ import { Type as Type2 } from "typebox";
4385
+
4386
+ // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js
4387
+ import { randomUUID as randomUUID3 } from "node:crypto";
4407
4388
  import * as fs6 from "node:fs";
4408
4389
  import * as path7 from "node:path";
4409
- import { Type as Type2 } from "typebox";
4410
- var SUPERVISOR_CHANNEL_ROOT = path7.join(TEMP_ROOT_DIR, "supervisor-channels"), REQUESTS_DIR = "requests", REPLIES_DIR = "replies", NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor", MAX_MESSAGE_BYTES = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS = 600 * 1e3, CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500), STALE_EMPTY_CHANNEL_AGE_MS = 60 * 1e3, STALE_EMPTY_CHANNEL_CLEANUP_INTERVAL_MS = 60 * 1e3, ContactSupervisorParamsSchema = Type2.Object({
4411
- reason: Type2.String({ enum: ["need_decision", "interview_request", "progress_update"] }),
4390
+ var QUESTIONS_DIR = "questions", SUPERVISOR_CHANNEL_ROOT = path7.join(TEMP_ROOT_DIR, "supervisor-channels"), SUBAGENT_SUPERVISOR_ANSWER_TYPE = "subagent_supervisor_answer", SUBAGENT_QUESTION_EVENT = "subagent:supervisor-question", MAX_MESSAGE_BYTES = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS = 600 * 1e3, activeQuestionWaits = /* @__PURE__ */ new Set();
4391
+ function askTimeoutMs() {
4392
+ let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
4393
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
4394
+ }
4395
+ function safeSegment(value) {
4396
+ return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
4397
+ }
4398
+ function resolveSupervisorChannelDir(runId, agent, childIndex) {
4399
+ return path7.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
4400
+ }
4401
+ function questionsDir(channelDir) {
4402
+ return path7.join(channelDir, QUESTIONS_DIR);
4403
+ }
4404
+ function questionFile(channelDir, questionId) {
4405
+ return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.json`);
4406
+ }
4407
+ function deliveredFile(channelDir, questionId) {
4408
+ return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.delivered.json`);
4409
+ }
4410
+ function terminalFile(channelDir, questionId) {
4411
+ return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.terminal.json`);
4412
+ }
4413
+ function notifiedFile(channelDir, questionId) {
4414
+ return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.notified.json`);
4415
+ }
4416
+ function lateFile(channelDir, questionId) {
4417
+ return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.late.json`);
4418
+ }
4419
+ function writeExclusiveJson(filePath, payload) {
4420
+ fs6.mkdirSync(path7.dirname(filePath), { recursive: !0, mode: 448 });
4421
+ let temporary = `${filePath}.${randomUUID3()}.tmp`;
4422
+ try {
4423
+ return fs6.writeFileSync(temporary, JSON.stringify(payload), { encoding: "utf-8", flag: "wx", mode: 384 }), fs6.linkSync(temporary, filePath), !0;
4424
+ } catch (error) {
4425
+ if (error.code === "EEXIST")
4426
+ return !1;
4427
+ throw error;
4428
+ } finally {
4429
+ fs6.rmSync(temporary, { force: !0 });
4430
+ }
4431
+ }
4432
+ function readJsonFile(filePath) {
4433
+ return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
4434
+ }
4435
+ function tryReadJson(filePath) {
4436
+ try {
4437
+ return readJsonFile(filePath);
4438
+ } catch (error) {
4439
+ return error.code === "ENOENT", void 0;
4440
+ }
4441
+ }
4442
+ function parseIncarnation(raw) {
4443
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4444
+ return;
4445
+ let input = raw, pid = input.pid, readyAt = input.readyAt;
4446
+ if (!(!Number.isInteger(pid) || pid === void 0 || pid <= 0) && !(typeof readyAt != "number" || !Number.isFinite(readyAt) || readyAt <= 0))
4447
+ return { pid, readyAt };
4448
+ }
4449
+ function parseQuestionRecord(raw) {
4450
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4451
+ return;
4452
+ let input = raw;
4453
+ if (input.type !== "subagent.supervisor.question" || typeof input.id != "string" || !input.id.trim() || typeof input.createdAt != "number" || !Number.isFinite(input.createdAt) || typeof input.expiresAt != "number" || !Number.isFinite(input.expiresAt) || typeof input.reason != "string" || !input.reason.trim() || typeof input.message != "string" || !input.message.trim() || typeof input.runId != "string" || !input.runId.trim())
4454
+ return;
4455
+ let childIndex = input.childIndex;
4456
+ if (!Number.isInteger(childIndex) || childIndex === void 0 || childIndex < 0 || typeof input.childId != "string" || !input.childId.trim() || typeof input.parentSessionId != "string" || !input.parentSessionId.trim())
4457
+ return;
4458
+ let parentGeneration = input.parentGeneration;
4459
+ if (!Number.isInteger(parentGeneration) || parentGeneration === void 0 || parentGeneration < 0)
4460
+ return;
4461
+ let incarnation = parseIncarnation(input.childIncarnation);
4462
+ return {
4463
+ type: "subagent.supervisor.question",
4464
+ id: input.id.trim(),
4465
+ createdAt: input.createdAt,
4466
+ expiresAt: input.expiresAt,
4467
+ reason: input.reason.trim(),
4468
+ message: input.message.trim(),
4469
+ runId: input.runId.trim(),
4470
+ childIndex,
4471
+ childId: input.childId.trim(),
4472
+ parentSessionId: input.parentSessionId.trim(),
4473
+ parentGeneration,
4474
+ ...incarnation ? { childIncarnation: incarnation } : {}
4475
+ };
4476
+ }
4477
+ function parseDeliveredRecord(raw) {
4478
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4479
+ return;
4480
+ let input = raw;
4481
+ if (input.type !== "subagent.supervisor.delivered" || typeof input.id != "string" || !input.id.trim() || typeof input.at != "number" || !Number.isFinite(input.at))
4482
+ return;
4483
+ let incarnation = parseIncarnation(input);
4484
+ if (incarnation)
4485
+ return { type: "subagent.supervisor.delivered", id: input.id.trim(), at: input.at, pid: incarnation.pid, readyAt: incarnation.readyAt };
4486
+ }
4487
+ function parseTerminalRecord(raw) {
4488
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4489
+ return;
4490
+ let input = raw;
4491
+ if (!(input.type !== "subagent.supervisor.terminal" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at))) {
4492
+ if (input.state === "answered")
4493
+ return typeof input.message != "string" || !input.message.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "answered", at: input.at, message: input.message };
4494
+ if (input.state === "expired")
4495
+ return { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "expired", at: input.at };
4496
+ if (input.state === "cancelled")
4497
+ return typeof input.reason != "string" || !input.reason.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "cancelled", at: input.at, reason: input.reason.trim() };
4498
+ }
4499
+ }
4500
+ function parseNotifiedRecord(raw) {
4501
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4502
+ return;
4503
+ let input = raw;
4504
+ if (!(input.type !== "subagent.supervisor.notified" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at)))
4505
+ return { type: "subagent.supervisor.notified", id: input.id.trim(), at: input.at };
4506
+ }
4507
+ function parseLateRecord(raw) {
4508
+ if (!raw || typeof raw != "object" || Array.isArray(raw))
4509
+ return;
4510
+ let input = raw;
4511
+ if (!(input.type !== "subagent.supervisor.late-answer" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at) || typeof input.message != "string"))
4512
+ return { type: "subagent.supervisor.late-answer", id: input.id.trim(), at: input.at, message: input.message };
4513
+ }
4514
+ function composeQuestion(record, channelDir) {
4515
+ let delivered = parseDeliveredRecord(tryReadJson(deliveredFile(channelDir, record.id))), terminal = parseTerminalRecord(tryReadJson(terminalFile(channelDir, record.id))), notified = parseNotifiedRecord(tryReadJson(notifiedFile(channelDir, record.id))), late = parseLateRecord(tryReadJson(lateFile(channelDir, record.id)));
4516
+ return {
4517
+ ...record,
4518
+ ...delivered ? { deliveredAt: delivered.at, childIncarnation: record.childIncarnation ?? { pid: delivered.pid, readyAt: delivered.readyAt } } : {},
4519
+ ...terminal?.state === "answered" ? { answeredAt: terminal.at, answer: terminal.message } : {},
4520
+ ...terminal?.state === "expired" ? { expiredAt: terminal.at } : {},
4521
+ ...terminal?.state === "cancelled" ? { cancelledAt: terminal.at, cancelReason: terminal.reason } : {},
4522
+ ...notified ? { notifiedAt: notified.at } : {},
4523
+ ...late ? { lateAnswerAt: late.at, ...terminal?.state === "answered" ? {} : { answer: late.message } } : {}
4524
+ };
4525
+ }
4526
+ function assertMessageSize(value, label) {
4527
+ if (Buffer.byteLength(value, "utf-8") > MAX_MESSAGE_BYTES)
4528
+ throw new Error(`${label} is too large.`);
4529
+ }
4530
+ function ensureQuestionsDir(channelDir) {
4531
+ fs6.mkdirSync(questionsDir(channelDir), { recursive: !0, mode: 448 });
4532
+ }
4533
+ function supervisorQuestionPublicState(question) {
4534
+ return question.answeredAt ? "answered" : question.expiredAt ? "expired" : question.cancelledAt ? "cancelled" : "pending";
4535
+ }
4536
+ function supervisorQuestionIsTerminal(question) {
4537
+ return supervisorQuestionPublicState(question) !== "pending";
4538
+ }
4539
+ function readSupervisorQuestion(channelDir, questionId) {
4540
+ let record = parseQuestionRecord(tryReadJson(questionFile(channelDir, questionId)));
4541
+ if (!(!record || record.id !== questionId))
4542
+ return composeQuestion(record, channelDir);
4543
+ }
4544
+ function listSupervisorQuestions(channelDir) {
4545
+ let dir = questionsDir(channelDir), entries;
4546
+ try {
4547
+ entries = fs6.readdirSync(dir).filter((name) => name.endsWith(".json") && !name.endsWith(".delivered.json") && !name.endsWith(".terminal.json") && !name.endsWith(".notified.json") && !name.endsWith(".late.json"));
4548
+ } catch (error) {
4549
+ if (error.code === "ENOENT")
4550
+ return [];
4551
+ throw error;
4552
+ }
4553
+ let questions = [];
4554
+ for (let entry of entries.sort()) {
4555
+ let record = parseQuestionRecord(tryReadJson(path7.join(dir, entry)));
4556
+ record && questions.push(composeQuestion(record, channelDir));
4557
+ }
4558
+ return questions;
4559
+ }
4560
+ function writeTerminal(channelDir, record) {
4561
+ return writeExclusiveJson(terminalFile(channelDir, record.id), record);
4562
+ }
4563
+ function createSupervisorQuestion(input) {
4564
+ let reason = input.reason.trim(), message = input.message.trim();
4565
+ if (!reason)
4566
+ throw new Error("reason is required and must name the concrete decision the child's answer will change.");
4567
+ if (!message)
4568
+ throw new Error("message is required for supervisor questions.");
4569
+ assertMessageSize(reason, "reason"), assertMessageSize(message, "message");
4570
+ let createdAt = input.now ?? Date.now(), timeoutMs = input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : askTimeoutMs(), record = {
4571
+ type: "subagent.supervisor.question",
4572
+ id: input.id ?? randomUUID3(),
4573
+ createdAt,
4574
+ expiresAt: createdAt + timeoutMs,
4575
+ reason,
4576
+ message,
4577
+ runId: input.runId,
4578
+ childIndex: input.childIndex,
4579
+ childId: input.childId,
4580
+ parentSessionId: input.parentSessionId,
4581
+ parentGeneration: input.parentGeneration,
4582
+ ...input.childIncarnation ? { childIncarnation: input.childIncarnation } : {}
4583
+ };
4584
+ if (assertMessageSize(JSON.stringify(record), "Supervisor question"), ensureQuestionsDir(input.channelDir), !writeExclusiveJson(questionFile(input.channelDir, record.id), record))
4585
+ throw new Error(`Supervisor question '${record.id}' already exists.`);
4586
+ return composeQuestion(record, input.channelDir);
4587
+ }
4588
+ function outstandingQuestionsForChild(channelDir, childIndex, childId) {
4589
+ return refreshSupervisorQuestionLifecycle(channelDir), listSupervisorQuestions(channelDir).filter((question) => !(supervisorQuestionIsTerminal(question) || question.childIndex !== childIndex || childId && question.childId !== childId));
4590
+ }
4591
+ function hasBlockingSupervisorRequest(channelDir, runId, childIndex) {
4592
+ let directory = path7.join(channelDir, "requests");
4593
+ if (!fs6.existsSync(directory))
4594
+ return !1;
4595
+ for (let name of fs6.readdirSync(directory)) {
4596
+ if (!name.endsWith(".json"))
4597
+ continue;
4598
+ let raw = tryReadJson(path7.join(directory, name));
4599
+ if (!raw || typeof raw != "object")
4600
+ continue;
4601
+ let request = raw;
4602
+ if (!(request.type !== "subagent.supervisor.request" || request.expectsReply !== !0) && !(request.runId !== runId || request.childIndex !== childIndex || typeof request.id != "string") && !(typeof request.expiresAt == "number" && request.expiresAt <= Date.now()) && !fs6.existsSync(path7.join(channelDir, "replies", `${safeSegment(request.id)}.json`)))
4603
+ return !0;
4604
+ }
4605
+ return !1;
4606
+ }
4607
+ function hasOutstandingQuestionForChild(channelDir, childIndex, childId) {
4608
+ return outstandingQuestionsForChild(channelDir, childIndex, childId).length > 0;
4609
+ }
4610
+ function incarnationMatches(expected, actual) {
4611
+ return expected ? expected.pid === actual.pid && expected.readyAt === actual.readyAt : !0;
4612
+ }
4613
+ function expireSupervisorQuestion(channelDir, questionId, now = Date.now()) {
4614
+ let current = readSupervisorQuestion(channelDir, questionId);
4615
+ return !current || supervisorQuestionIsTerminal(current) || now <= current.expiresAt ? current && supervisorQuestionIsTerminal(current) ? current : void 0 : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "expired", at: now }), readSupervisorQuestion(channelDir, questionId));
4616
+ }
4617
+ function cancelSupervisorQuestion(channelDir, questionId, reason, now = Date.now()) {
4618
+ let current = readSupervisorQuestion(channelDir, questionId);
4619
+ return !current || supervisorQuestionIsTerminal(current) ? current : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "cancelled", at: now, reason: reason.trim() || "cancelled" }), readSupervisorQuestion(channelDir, questionId));
4620
+ }
4621
+ function refreshSupervisorQuestionLifecycle(channelDir, now = Date.now()) {
4622
+ let changed = [];
4623
+ for (let question of listSupervisorQuestions(channelDir))
4624
+ if (!supervisorQuestionIsTerminal(question) && now > question.expiresAt) {
4625
+ let expired = expireSupervisorQuestion(channelDir, question.id, now);
4626
+ expired && expired.expiredAt && changed.push(expired);
4627
+ }
4628
+ return changed;
4629
+ }
4630
+ function formatParentQuestionForChild(question) {
4631
+ return [
4632
+ `Parent question (${question.id})`,
4633
+ `Decision needed: ${question.reason}`,
4634
+ "",
4635
+ question.message,
4636
+ "",
4637
+ `Reply with: contact_supervisor({ action: "reply", replyTo: "${question.id}", message: "..." })`,
4638
+ "Answer from current findings and uncertainty. Do not silently start extra investigation. Continue the assigned task after replying."
4639
+ ].join(`
4640
+ `);
4641
+ }
4642
+ function formatSupervisorQuestionAnswer(question) {
4643
+ let state = supervisorQuestionPublicState(question), lines = [
4644
+ state === "answered" ? `Subagent answer (${question.id})` : `Subagent question ${state} (${question.id})`,
4645
+ `Run: ${question.runId}`,
4646
+ `Child: ${question.childId} [#${question.childIndex}]`,
4647
+ `Decision: ${question.reason}`
4648
+ ];
4649
+ return state === "answered" && question.answer?.trim() ? lines.push("", question.answer.trim()) : question.cancelReason && lines.push("", question.cancelReason), lines.join(`
4650
+ `);
4651
+ }
4652
+ function registerQuestionWait(questionIds) {
4653
+ let ids = [...questionIds];
4654
+ for (let id of ids)
4655
+ activeQuestionWaits.add(id);
4656
+ return () => {
4657
+ for (let id of ids)
4658
+ activeQuestionWaits.delete(id);
4659
+ };
4660
+ }
4661
+ function isQuestionWaitActive(questionId) {
4662
+ return activeQuestionWaits.has(questionId);
4663
+ }
4664
+ function claimQuestionNotification(channelDir, questionId, now = Date.now()) {
4665
+ return writeExclusiveJson(notifiedFile(channelDir, questionId), {
4666
+ type: "subagent.supervisor.notified",
4667
+ id: questionId,
4668
+ at: now
4669
+ });
4670
+ }
4671
+ function listSupervisorChannelDirs() {
4672
+ let entries;
4673
+ try {
4674
+ entries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
4675
+ } catch (error) {
4676
+ if (error.code === "ENOENT")
4677
+ return [];
4678
+ throw error;
4679
+ }
4680
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name));
4681
+ }
4682
+ function findSupervisorQuestion(questionId) {
4683
+ let id = questionId.trim();
4684
+ if (id)
4685
+ for (let channelDir of listSupervisorChannelDirs()) {
4686
+ let question = readSupervisorQuestion(channelDir, id);
4687
+ if (question)
4688
+ return { channelDir, question };
4689
+ }
4690
+ }
4691
+ function cancelPendingSupervisorQuestionsForOwner(input) {
4692
+ if (!input.parentSessionId?.trim())
4693
+ return [];
4694
+ let now = input.now ?? Date.now(), cancelled = [];
4695
+ for (let channelDir of listSupervisorChannelDirs())
4696
+ for (let question of listSupervisorQuestions(channelDir)) {
4697
+ if (supervisorQuestionIsTerminal(question) || question.parentSessionId !== input.parentSessionId || input.parentGeneration !== void 0 && question.parentGeneration !== input.parentGeneration || input.runId && question.runId !== input.runId)
4698
+ continue;
4699
+ let next = cancelSupervisorQuestion(channelDir, question.id, input.reason, now);
4700
+ next?.cancelledAt && cancelled.push(next);
4701
+ }
4702
+ return cancelled;
4703
+ }
4704
+
4705
+ // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js
4706
+ function runningSteps(status) {
4707
+ return (status.steps ?? []).map((step, index) => ({
4708
+ index,
4709
+ childId: step.childId ?? step.agent,
4710
+ agent: step.agent,
4711
+ status: step.status
4712
+ })).filter((step) => step.status === "running").map((step) => ({ index: step.index, childId: step.childId, agent: step.agent }));
4713
+ }
4714
+ function resolveAskChild(status, params) {
4715
+ if (params.index !== void 0 && (!Number.isInteger(params.index) || params.index < 0))
4716
+ throw new Error("index must be a non-negative integer.");
4717
+ let running = runningSteps(status);
4718
+ if (params.childId?.trim()) {
4719
+ let childId = params.childId.trim(), matches = (status.steps ?? []).map((step, index) => ({ index, childId: step.childId ?? step.agent, agent: step.agent, status: step.status })).filter((step) => step.childId === childId);
4720
+ if (matches.length === 0)
4721
+ throw new Error(`Async run '${status.runId}' has no child '${childId}'.`);
4722
+ if (matches.length > 1)
4723
+ throw new Error(`Async run '${status.runId}' has multiple children named '${childId}'. Pass index.`);
4724
+ let match = matches[0];
4725
+ if (params.index !== void 0 && params.index !== match.index)
4726
+ throw new Error(`Child '${childId}' is index ${match.index}, not ${params.index}.`);
4727
+ if (match.status !== "running")
4728
+ throw new Error(`Async run '${status.runId}' child ${match.index} is ${match.status} and cannot be asked.`);
4729
+ return { index: match.index, childId: match.childId, agent: match.agent };
4730
+ }
4731
+ if (params.index !== void 0) {
4732
+ let steps = status.steps ?? [];
4733
+ if (params.index >= steps.length)
4734
+ throw new Error(`Async run '${status.runId}' has ${steps.length} children. Index ${params.index} is out of range.`);
4735
+ let step = steps[params.index];
4736
+ if (step.status !== "running")
4737
+ throw new Error(`Async run '${status.runId}' child ${params.index} is ${step.status} and cannot be asked.`);
4738
+ return { index: params.index, childId: step.childId ?? step.agent, agent: step.agent };
4739
+ }
4740
+ if (running.length === 1)
4741
+ return running[0];
4742
+ throw running.length === 0 ? new Error(`Async run '${status.runId}' has no running child to ask.`) : new Error(`Async run '${status.runId}' has ${running.length} running children. Pass index or childId; broadcast asks are not supported.`);
4743
+ }
4744
+ function requestMatchesChild(request, runId, child) {
4745
+ if (!request.expectsReply || typeof request.runId != "string" || request.runId !== runId)
4746
+ return !1;
4747
+ let indexMatches = Number.isInteger(request.childIndex) && request.childIndex === child.index, idMatches = request.agent === child.childId || request.agent === child.agent || request.childTarget === child.childId;
4748
+ return indexMatches && idMatches;
4749
+ }
4750
+ function reconcileAsyncQuestion(channelDir, question, state) {
4751
+ expireSupervisorQuestion(channelDir, question.id);
4752
+ let current = readSupervisorQuestion(channelDir, question.id) ?? question;
4753
+ if (supervisorQuestionIsTerminal(current))
4754
+ return current;
4755
+ let resolved = resolveSubagentRunId(current.runId, { state });
4756
+ if (resolved?.kind !== "async" || !resolved.location.asyncDir)
4757
+ return current;
4758
+ let status = readStatus(resolved.location.asyncDir);
4759
+ if (!status)
4760
+ return current;
4761
+ let step = status.steps?.[current.childIndex], capability = readSteerCapability(resolved.location.asyncDir, current.childIndex), reason;
4762
+ status.sessionId !== current.parentSessionId ? reason = "async run ownership changed" : status.state !== "running" || !step || step.status !== "running" ? reason = "target child is no longer running" : (step.childId ?? step.agent) !== current.childId ? reason = "target child identity changed" : capability && !incarnationMatches(current.childIncarnation, capability) ? reason = "target child process changed" : capability && !capability.supported && (reason = "target child no longer accepts questions");
4763
+ let delivery = status.steering?.recent.find((request) => request.id === current.id)?.targets.find((target) => target.index === current.childIndex);
4764
+ return (delivery?.state === "failed" || delivery?.state === "late") && (reason = delivery.reason ?? "question delivery failed"), reason ? cancelSupervisorQuestion(channelDir, current.id, reason) ?? current : current;
4765
+ }
4766
+ function askRunningAsyncChild(input) {
4767
+ let runId = input.params.id?.trim();
4768
+ if (!runId)
4769
+ throw new Error("action='ask' requires id for the async run.");
4770
+ let reason = input.params.reason?.trim(), message = input.params.message?.trim();
4771
+ if (!reason)
4772
+ throw new Error("action='ask' requires reason naming the concrete decision the child's answer will change.");
4773
+ if (!message)
4774
+ throw new Error("action='ask' requires message.");
4775
+ let sessionId = input.state.currentSessionId;
4776
+ if (!sessionId)
4777
+ throw new Error("action='ask' requires an active session identity.");
4778
+ let resolved = resolveSubagentRunId(runId, { state: input.state });
4779
+ if (!resolved)
4780
+ throw new Error(`No async run matched '${runId}'.`);
4781
+ if (resolved.kind !== "async")
4782
+ throw new Error("action='ask' targets a running async child only. It does not revive, reassign, or broadcast.");
4783
+ if (!resolved.location.asyncDir)
4784
+ throw new Error(`Async run '${resolved.id}' has no live run directory.`);
4785
+ let status = readStatus(resolved.location.asyncDir);
4786
+ if (!status)
4787
+ throw new Error(`Async run '${resolved.id}' has no status.`);
4788
+ if (!status.sessionId || status.sessionId !== sessionId)
4789
+ throw new Error(`Async run '${resolved.id}' was not found in the active session.`);
4790
+ if (status.state !== "running")
4791
+ throw new Error(`Async run '${resolved.id}' is ${status.state} and cannot be asked.`);
4792
+ let child = resolveAskChild(status, input.params), channelDir = resolveSupervisorChannelDir(status.runId, child.childId, child.index);
4793
+ if (hasOutstandingQuestionForChild(channelDir, child.index, child.childId))
4794
+ throw new Error(`A question is already outstanding for async run '${status.runId}' child ${child.index}. Wait for that answer or expiry before asking again.`);
4795
+ for (let request of input.childRequests ?? [])
4796
+ if (requestMatchesChild(request, status.runId, child))
4797
+ throw new Error(`Child ${child.index} is waiting for a supervisor reply. Asking now would deadlock. Reply to the child request first.`);
4798
+ if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
4799
+ throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
4800
+ let capability = readSteerCapability(resolved.location.asyncDir, child.index);
4801
+ if (!capability?.supported)
4802
+ throw new Error("This child is not ready to receive questions. Continue other work until it is ready; no question was queued.");
4803
+ let incarnation = { pid: capability.pid, readyAt: capability.readyAt }, question = createSupervisorQuestion({
4804
+ channelDir,
4805
+ reason,
4806
+ message,
4807
+ runId: status.runId,
4808
+ childIndex: child.index,
4809
+ childId: child.childId,
4810
+ parentSessionId: sessionId,
4811
+ parentGeneration: input.state.sessionGeneration ?? 0,
4812
+ childIncarnation: incarnation
4813
+ });
4814
+ try {
4815
+ if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
4816
+ throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
4817
+ requestAsyncSteer(resolved.location.asyncDir, {
4818
+ message: formatParentQuestionForChild(question),
4819
+ targetIndex: child.index,
4820
+ source: "supervisor-question",
4821
+ id: question.id
4822
+ });
4823
+ } catch (error) {
4824
+ throw cancelSupervisorQuestion(channelDir, question.id, "failed to queue delivery to child"), error instanceof Error ? error : new Error(String(error));
4825
+ }
4826
+ return {
4827
+ content: [{ type: "text", text: `Question ${question.id} queued for async run ${status.runId} child ${child.index}. The child's answer is delivered separately from the run result.` }],
4828
+ details: {
4829
+ questionId: question.id,
4830
+ state: "pending",
4831
+ runId: status.runId,
4832
+ childIndex: child.index,
4833
+ childId: child.childId,
4834
+ delivered: !1,
4835
+ answered: !1
4836
+ }
4837
+ };
4838
+ }
4839
+
4840
+ // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
4841
+ var REQUESTS_DIR = "requests", REPLIES_DIR = "replies", NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
4842
+ var MAX_MESSAGE_BYTES2 = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS2 = 600 * 1e3, CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500), STALE_EMPTY_CHANNEL_AGE_MS = 60 * 1e3, STALE_EMPTY_CHANNEL_CLEANUP_INTERVAL_MS = 60 * 1e3, PARENT_ASK_DESCRIPTION = [
4843
+ "Ask a specific running async child, or reply to a child supervisor request.",
4844
+ "Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks a block or rework.",
4845
+ "Use available results first, batch related questions, and keep working while awaiting the answer. Do not use ask for status checks, polling, duplicate queries, or step-by-step supervision. Follow up only when the answer leaves the original decision unresolved.",
4846
+ "Returns a question id immediately. The child's explicit answer is delivered separately from the final run result.",
4847
+ 'Ask: { action: "ask", id: "<runId>", index?: 0, childId?: "...", reason: "<concrete decision>", message: "..." }',
4848
+ "Use pending or list to find child requests awaiting your reply; status reports channel availability. Free-form send is unsupported. Asking never revives a child or changes its assignment.",
4849
+ 'Reply: { action: "reply", replyTo: "<requestId>", message: "..." }'
4850
+ ].join(" "), CHILD_CONTACT_DESCRIPTION = [
4851
+ "Contact the parent/supervisor session for a blocking decision, structured interview, progress update, or reply to a parent question.",
4852
+ "For parent questions use action='reply' with replyTo set to the question id. Answer from current findings and uncertainty; do not start extra investigation just to reply. Continue the assigned task after replying.",
4853
+ "Do not open a blocking need_decision while a parent question is already outstanding for this child."
4854
+ ].join(" "), ContactSupervisorParamsSchema = Type2.Object({
4855
+ reason: Type2.Optional(Type2.String({ enum: ["need_decision", "interview_request", "progress_update"] })),
4412
4856
  message: Type2.Optional(Type2.String()),
4413
- interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 }))
4857
+ interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 })),
4858
+ action: Type2.Optional(Type2.String({ enum: ["reply"] })),
4859
+ replyTo: Type2.Optional(Type2.String())
4414
4860
  }, { additionalProperties: !1 }), IntercomParamsSchema = Type2.Object({
4415
4861
  action: Type2.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
4416
4862
  to: Type2.Optional(Type2.String()),
4417
4863
  message: Type2.Optional(Type2.String()),
4418
- replyTo: Type2.Optional(Type2.String())
4864
+ replyTo: Type2.Optional(Type2.String()),
4865
+ id: Type2.Optional(Type2.String({ description: "Async run id for action='ask'." })),
4866
+ index: Type2.Optional(Type2.Integer({ minimum: 0, description: "Zero-based child index for action='ask'. Select index or childId when more than one child is running." })),
4867
+ childId: Type2.Optional(Type2.String({ description: "Private child identity for action='ask'." })),
4868
+ reason: Type2.Optional(Type2.String({ description: "Concrete decision the child's answer will change. Required for action='ask'." }))
4869
+ }, { additionalProperties: !1 }), ParentSupervisorParamsSchema = Type2.Object({
4870
+ ...IntercomParamsSchema.properties,
4871
+ action: Type2.String({ enum: ["list", "ask", "reply", "pending", "status"] })
4419
4872
  }, { additionalProperties: !1 });
4420
- function safeSegment(value) {
4873
+ function safeSegment2(value) {
4421
4874
  return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
4422
4875
  }
4423
4876
  function replyPath(channelDir, requestId) {
4424
- return path7.join(channelDir, REPLIES_DIR, `${safeSegment(requestId)}.json`);
4877
+ return path8.join(channelDir, REPLIES_DIR, `${safeSegment2(requestId)}.json`);
4425
4878
  }
4426
- function askTimeoutMs() {
4879
+ function askTimeoutMs2() {
4427
4880
  let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
4428
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
4881
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS2;
4429
4882
  }
4430
4883
  function hasTool(pi, name) {
4431
4884
  try {
@@ -4436,16 +4889,26 @@ function hasTool(pi, name) {
4436
4889
  }
4437
4890
  function parseRequestFile(file, channelDir) {
4438
4891
  try {
4439
- let parsed = JSON.parse(fs6.readFileSync(file, "utf-8"));
4440
- return parsed.type !== "subagent.supervisor.request" || typeof parsed.id != "string" || !parsed.id || parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update" || typeof parsed.message != "string" || !parsed.message || typeof parsed.runId != "string" || typeof parsed.agent != "string" || typeof parsed.childIndex != "number" ? void 0 : { ...parsed, channelDir, requestFile: file };
4892
+ let parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
4893
+ return parsed.type !== "subagent.supervisor.request" || parsed.supervisorSessionId !== void 0 && (typeof parsed.supervisorSessionId != "string" || !parsed.supervisorSessionId.trim()) || typeof parsed.id != "string" || !parsed.id || parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update" || typeof parsed.message != "string" || !parsed.message || typeof parsed.runId != "string" || typeof parsed.agent != "string" || typeof parsed.childIndex != "number" ? void 0 : { ...parsed, channelDir, requestFile: file };
4441
4894
  } catch {
4442
4895
  return;
4443
4896
  }
4444
4897
  }
4898
+ function hasPendingBlockingSupervisorRequest(runId, sessionId, now = Date.now()) {
4899
+ if (!runId)
4900
+ return !1;
4901
+ for (let { channelDir, file } of listRequestFiles()) {
4902
+ let request = parseRequestFile(file, channelDir);
4903
+ if (request?.expectsReply && request.runId === runId && !(sessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) !== sessionId) && !fs7.existsSync(replyPath(request.channelDir, request.id)) && !(now > requestExpiresAt(request, now)))
4904
+ return !0;
4905
+ }
4906
+ return !1;
4907
+ }
4445
4908
  function listRequestFiles() {
4446
4909
  let channelEntries;
4447
4910
  try {
4448
- channelEntries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
4911
+ channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
4449
4912
  } catch (error) {
4450
4913
  if (error.code === "ENOENT")
4451
4914
  return [];
@@ -4455,34 +4918,34 @@ function listRequestFiles() {
4455
4918
  for (let entry of channelEntries) {
4456
4919
  if (!entry.isDirectory())
4457
4920
  continue;
4458
- let channelDir = path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name), requestsDir = path7.join(channelDir, REQUESTS_DIR), requestEntries;
4921
+ let channelDir = path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), requestsDir = path8.join(channelDir, REQUESTS_DIR), requestEntries;
4459
4922
  try {
4460
- requestEntries = fs6.readdirSync(requestsDir, { withFileTypes: !0 });
4923
+ requestEntries = fs7.readdirSync(requestsDir, { withFileTypes: !0 });
4461
4924
  } catch {
4462
4925
  continue;
4463
4926
  }
4464
4927
  for (let requestEntry of requestEntries)
4465
- requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file: path7.join(requestsDir, requestEntry.name) });
4928
+ requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file: path8.join(requestsDir, requestEntry.name) });
4466
4929
  }
4467
4930
  return files;
4468
4931
  }
4469
4932
  function readDirectoryEntries(dir) {
4470
4933
  try {
4471
- return fs6.readdirSync(dir, { withFileTypes: !0 });
4934
+ return fs7.readdirSync(dir, { withFileTypes: !0 });
4472
4935
  } catch (error) {
4473
4936
  return error.code === "ENOENT" ? [] : void 0;
4474
4937
  }
4475
4938
  }
4476
4939
  function directoryMtimeMs(dir) {
4477
4940
  try {
4478
- return fs6.statSync(dir).mtimeMs;
4941
+ return fs7.statSync(dir).mtimeMs;
4479
4942
  } catch {
4480
4943
  return 0;
4481
4944
  }
4482
4945
  }
4483
4946
  function removeEmptyDirectory(dir) {
4484
4947
  try {
4485
- return fs6.rmdirSync(dir), !0;
4948
+ return fs7.rmdirSync(dir), !0;
4486
4949
  } catch (error) {
4487
4950
  let code = error.code;
4488
4951
  if (code === "ENOENT")
@@ -4493,19 +4956,22 @@ function removeEmptyDirectory(dir) {
4493
4956
  }
4494
4957
  }
4495
4958
  function removeStaleEmptySupervisorChannel(channelDir, nowMs) {
4496
- let requestsDir = path7.join(channelDir, REQUESTS_DIR), repliesDir = path7.join(channelDir, REPLIES_DIR), newestKnownMtimeMs = Math.max(directoryMtimeMs(channelDir), directoryMtimeMs(requestsDir), directoryMtimeMs(repliesDir));
4959
+ let requestsDir = path8.join(channelDir, REQUESTS_DIR), repliesDir = path8.join(channelDir, REPLIES_DIR), questionsPath = path8.join(channelDir, QUESTIONS_DIR), newestKnownMtimeMs = Math.max(directoryMtimeMs(channelDir), directoryMtimeMs(requestsDir), directoryMtimeMs(repliesDir), directoryMtimeMs(questionsPath));
4497
4960
  if (nowMs - newestKnownMtimeMs < STALE_EMPTY_CHANNEL_AGE_MS)
4498
4961
  return !1;
4499
4962
  let requestEntries = readDirectoryEntries(requestsDir);
4500
4963
  if (!requestEntries || requestEntries.length > 0)
4501
4964
  return !1;
4502
4965
  let replyEntries = readDirectoryEntries(repliesDir);
4503
- return !(!replyEntries || replyEntries.length > 0 || !removeEmptyDirectory(requestsDir) || !removeEmptyDirectory(repliesDir) || !removeEmptyDirectory(channelDir));
4966
+ if (!replyEntries || replyEntries.length > 0)
4967
+ return !1;
4968
+ let questionEntries = readDirectoryEntries(questionsPath);
4969
+ return !(!questionEntries || questionEntries.length > 0 || !removeEmptyDirectory(requestsDir) || !removeEmptyDirectory(repliesDir) || !removeEmptyDirectory(questionsPath) || !removeEmptyDirectory(channelDir));
4504
4970
  }
4505
4971
  function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
4506
4972
  let channelEntries;
4507
4973
  try {
4508
- channelEntries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
4974
+ channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
4509
4975
  } catch (error) {
4510
4976
  if (error.code === "ENOENT")
4511
4977
  return 0;
@@ -4515,7 +4981,7 @@ function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
4515
4981
  for (let entry of channelEntries)
4516
4982
  if (entry.isDirectory())
4517
4983
  try {
4518
- removeStaleEmptySupervisorChannel(path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name), nowMs) && removed++;
4984
+ removeStaleEmptySupervisorChannel(path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), nowMs) && removed++;
4519
4985
  } catch {
4520
4986
  }
4521
4987
  return removed;
@@ -4529,19 +4995,26 @@ function currentContextSessionId(state, ctx) {
4529
4995
  }
4530
4996
  return state.currentSessionId ?? void 0;
4531
4997
  }
4998
+ function currentSupervisorSessionId(state, ctx) {
4999
+ try {
5000
+ return ctx.sessionManager.getSessionFile?.() ?? currentContextSessionId(state, ctx);
5001
+ } catch {
5002
+ return state.currentSessionId ?? void 0;
5003
+ }
5004
+ }
4532
5005
  function requestMatchesContext(request, state, ctx) {
4533
- let currentSessionId = currentContextSessionId(state, ctx);
4534
- return !!(currentSessionId && request.orchestratorSessionId === currentSessionId);
5006
+ let currentSessionId = request.supervisorSessionId !== void 0 ? currentSupervisorSessionId(state, ctx) : currentContextSessionId(state, ctx);
5007
+ return !!(currentSessionId && (request.supervisorSessionId ?? request.orchestratorSessionId) === currentSessionId);
4535
5008
  }
4536
5009
  function removeRequestFile(file) {
4537
5010
  try {
4538
- fs6.rmSync(file, { force: !0 });
5011
+ fs7.rmSync(file, { force: !0 });
4539
5012
  } catch {
4540
5013
  }
4541
5014
  }
4542
5015
  function requestExpiresAt(request, now) {
4543
5016
  let expiresAt = request.expiresAt;
4544
- return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt + askTimeoutMs() : now;
5017
+ return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt + askTimeoutMs2() : now;
4545
5018
  }
4546
5019
  function requestRunInactive(request, state) {
4547
5020
  if (state.foregroundControls.has(request.runId))
@@ -4558,7 +5031,7 @@ function requestRunInactive(request, state) {
4558
5031
  return stepStatus === "complete" || stepStatus === "completed" || stepStatus === "failed" || stepStatus === "paused";
4559
5032
  }
4560
5033
  function requestLifecycle(request, state, ctx, now) {
4561
- return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" : fs6.existsSync(request.requestFile) ? request.expectsReply && fs6.existsSync(replyPath(request.channelDir, request.id)) ? "resolved" : request.expectsReply && now > requestExpiresAt(request, now) ? "expired" : request.expectsReply && requestRunInactive(request, state) ? "inactive" : "pending" : "missing";
5034
+ return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" : fs7.existsSync(request.requestFile) ? request.expectsReply && fs7.existsSync(replyPath(request.channelDir, request.id)) ? "resolved" : request.expectsReply && now > requestExpiresAt(request, now) ? "expired" : request.expectsReply && requestRunInactive(request, state) ? "inactive" : "pending" : "missing";
4562
5035
  }
4563
5036
  function cleanupRequestLifecycle(request, lifecycle) {
4564
5037
  (lifecycle === "resolved" || lifecycle === "expired" || lifecycle === "inactive") && removeRequestFile(request.requestFile);
@@ -4623,8 +5096,8 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
4623
5096
  return {
4624
5097
  name,
4625
5098
  label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
4626
- description: name === "intercom" ? "Native pi-subagents supervisor channel. Use reply/pending/status to answer child subagent requests." : "Native pi-subagents supervisor channel. Use reply/pending/status to answer child subagent requests without overriding pi-intercom.",
4627
- parameters: IntercomParamsSchema,
5099
+ description: PARENT_ASK_DESCRIPTION,
5100
+ parameters: ParentSupervisorParamsSchema,
4628
5101
  async execute(_id, params) {
4629
5102
  refreshPendingRequests(pending, state, state.lastUiContext ?? void 0);
4630
5103
  let input = params;
@@ -4639,10 +5112,59 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
4639
5112
  let request = resolvePendingRequest(pending, input);
4640
5113
  return writeReply(request, input.message ?? ""), pending.delete(request.id), { content: [{ type: "text", text: `Replied to supervisor request ${request.id}.` }], details: { replyTo: request.id, runId: request.runId, agent: request.agent } };
4641
5114
  }
4642
- throw input.action === "send" || input.action === "ask" ? new Error("Native pi-subagents intercom currently handles supervisor replies. Child agents initiate asks with contact_supervisor.") : new Error(`Unsupported intercom action: ${input.action}`);
5115
+ if (input.action === "ask")
5116
+ return askRunningAsyncChild({
5117
+ params: {
5118
+ id: input.id,
5119
+ index: input.index,
5120
+ childId: input.childId,
5121
+ reason: input.reason,
5122
+ message: input.message
5123
+ },
5124
+ state,
5125
+ childRequests: pending.values()
5126
+ });
5127
+ throw input.action === "send" ? new Error("Native pi-subagents intercom does not support free-form send. Use action='ask' for a running async child or action='reply' for a pending child request.") : new Error(`Unsupported intercom action: ${input.action}`);
4643
5128
  }
4644
5129
  };
4645
5130
  }
5131
+ function questionMatchesParentContext(question, state, ctx) {
5132
+ let sessionId = currentSupervisorSessionId(state, ctx);
5133
+ return !(!sessionId || question.parentSessionId !== sessionId || (state.sessionGeneration ?? 0) !== question.parentGeneration);
5134
+ }
5135
+ function notifyQuestionTerminal(pi, question, channelDir) {
5136
+ if (!supervisorQuestionIsTerminal(question) || question.notifiedAt || (pi.events?.emit(SUBAGENT_QUESTION_EVENT, {
5137
+ questionId: question.id,
5138
+ state: supervisorQuestionPublicState(question),
5139
+ runId: question.runId
5140
+ }), isQuestionWaitActive(question.id)) || !claimQuestionNotification(channelDir, question.id))
5141
+ return;
5142
+ let state = supervisorQuestionPublicState(question);
5143
+ pi.sendMessage({
5144
+ customType: SUBAGENT_SUPERVISOR_ANSWER_TYPE,
5145
+ content: formatSupervisorQuestionAnswer(question),
5146
+ display: state === "answered",
5147
+ details: {
5148
+ questionId: question.id,
5149
+ state,
5150
+ runId: question.runId,
5151
+ childIndex: question.childIndex,
5152
+ childId: question.childId,
5153
+ reason: question.reason,
5154
+ answered: state === "answered",
5155
+ delivered: !!question.deliveredAt
5156
+ }
5157
+ }, { triggerTurn: state === "answered" });
5158
+ }
5159
+ function pollSupervisorQuestions(pi, state, ctx) {
5160
+ for (let channelDir of listSupervisorChannelDirs())
5161
+ for (let question of listSupervisorQuestions(channelDir)) {
5162
+ if (!questionMatchesParentContext(question, state, ctx))
5163
+ continue;
5164
+ let current = reconcileAsyncQuestion(channelDir, question, state);
5165
+ notifyQuestionTerminal(pi, current, channelDir);
5166
+ }
5167
+ }
4646
5168
  function createNativeSupervisorChannel(pi, state) {
4647
5169
  let pending = /* @__PURE__ */ new Map(), seenFiles = /* @__PURE__ */ new Set(), poller, lastStaleCleanupAt = 0, registerParentTools = () => {
4648
5170
  hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME) || pi.registerTool(buildParentIntercomTool(pending, state, NATIVE_SUPERVISOR_TOOL_NAME)), hasTool(pi, "intercom") || pi.registerTool(buildParentIntercomTool(pending, state));
@@ -4655,12 +5177,24 @@ function createNativeSupervisorChannel(pi, state) {
4655
5177
  } catch {
4656
5178
  }
4657
5179
  }
5180
+ }, cancelOwnedQuestions = (reason) => {
5181
+ state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
5182
+ parentSessionId: state.currentSessionId,
5183
+ parentGeneration: state.sessionGeneration ?? 0,
5184
+ reason
5185
+ });
5186
+ }, settleRunQuestions = (runId, reason) => {
5187
+ state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
5188
+ parentSessionId: state.currentSessionId,
5189
+ runId,
5190
+ reason
5191
+ });
4658
5192
  }, poll = () => {
4659
5193
  cleanupStaleChannelsIfDue();
4660
5194
  let ctx = state.lastUiContext;
4661
5195
  if (!ctx)
4662
5196
  return;
4663
- refreshPendingRequests(pending, state, ctx);
5197
+ refreshPendingRequests(pending, state, ctx), pollSupervisorQuestions(pi, state, ctx);
4664
5198
  let now = Date.now();
4665
5199
  for (let { channelDir, file } of listRequestFiles()) {
4666
5200
  if (seenFiles.has(file))
@@ -4673,7 +5207,7 @@ function createNativeSupervisorChannel(pi, state) {
4673
5207
  seenFiles.add(file), cleanupRequestLifecycle(request, lifecycle);
4674
5208
  continue;
4675
5209
  }
4676
- seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
5210
+ if (seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
4677
5211
  customType: "subagent_supervisor_request",
4678
5212
  content: requestVisibleText(request),
4679
5213
  display: !0,
@@ -4685,12 +5219,23 @@ function createNativeSupervisorChannel(pi, state) {
4685
5219
  agent: request.agent,
4686
5220
  childIndex: request.childIndex
4687
5221
  }
4688
- }), request.expectsReply && pi.events?.emit(INTERCOM_DETACH_REQUEST_EVENT, {
4689
- requestId: request.id,
4690
- runId: request.runId,
4691
- agent: request.agent,
4692
- childIndex: request.childIndex
4693
- });
5222
+ }), request.expectsReply) {
5223
+ let job = state.asyncJobs.get(request.runId);
5224
+ job && (job.activityState = "needs_attention");
5225
+ let events = pi.events;
5226
+ events?.emit(INTERCOM_DETACH_REQUEST_EVENT, {
5227
+ requestId: request.id,
5228
+ runId: request.runId,
5229
+ agent: request.agent,
5230
+ childIndex: request.childIndex
5231
+ }), events?.emit(SUBAGENT_CONTROL_EVENT, {
5232
+ type: "needs_attention",
5233
+ to: "needs_attention",
5234
+ runId: request.runId,
5235
+ agent: request.agent,
5236
+ index: request.childIndex
5237
+ });
5238
+ }
4694
5239
  }
4695
5240
  };
4696
5241
  return {
@@ -4698,14 +5243,16 @@ function createNativeSupervisorChannel(pi, state) {
4698
5243
  poller || (registerParentTools(), poll(), poller = setInterval(poll, CHANNEL_POLL_MS), poller.unref?.());
4699
5244
  },
4700
5245
  dispose: () => {
4701
- poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
5246
+ cancelOwnedQuestions("parent supervisor channel disposed"), poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
4702
5247
  },
4703
- pending
5248
+ pending,
5249
+ cancelOwnedQuestions,
5250
+ settleRunQuestions
4704
5251
  };
4705
5252
  }
4706
5253
 
4707
5254
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/rpc.js
4708
- import * as path8 from "node:path";
5255
+ import * as path9 from "node:path";
4709
5256
  import { Compile } from "typebox/compile";
4710
5257
  var SUBAGENT_RPC_PROTOCOL_VERSION = 1, SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request", SUBAGENT_RPC_READY_EVENT = "subagents:rpc:v1:ready", SUBAGENT_RPC_REPLY_EVENT_PREFIX = "subagents:rpc:v1:reply:", SUBAGENT_RPC_METHODS = ["ping", "status", "spawn", "interrupt", "stop"], SubagentRpcError = class extends Error {
4711
5258
  code;
@@ -4812,7 +5359,7 @@ function stopAsyncRun(params, options, ctx) {
4812
5359
  }
4813
5360
  if (!location.asyncDir)
4814
5361
  throw new SubagentRpcError("not_found", "Async run not found or already completed; stop requires a live async run directory.");
4815
- let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ?? path8.basename(location.asyncDir);
5362
+ let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ?? path9.basename(location.asyncDir);
4816
5363
  if (!initialStatus)
4817
5364
  throw new SubagentRpcError("not_found", `Status file not found for async run '${initialRunId}'.`);
4818
5365
  if (!currentSessionId || initialStatus.sessionId !== currentSessionId)
@@ -5044,7 +5591,9 @@ var ACTIVE_STATES = ["queued", "running"], DEFAULT_TIMEOUT_MS = 1800 * 1e3, MIN_
5044
5591
  SUBAGENT_FOREGROUND_COMPLETE_EVENT,
5045
5592
  SUBAGENT_CONTROL_EVENT,
5046
5593
  SUBAGENT_CONTROL_INTERCOM_EVENT,
5047
- SUBAGENT_RESULT_INTERCOM_EVENT
5594
+ SUBAGENT_RESULT_INTERCOM_EVENT,
5595
+ INTERCOM_DETACH_REQUEST_EVENT,
5596
+ SUBAGENT_QUESTION_EVENT
5048
5597
  ];
5049
5598
  function defaultSleep(ms, signal) {
5050
5599
  return new Promise((resolve3) => {
@@ -5113,8 +5662,8 @@ function summarizeForegroundChildren(run, indices) {
5113
5662
  !indices.has(child.index) || child.status === "detached" || counts.set(child.status, (counts.get(child.status) ?? 0) + 1);
5114
5663
  return [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(", ");
5115
5664
  }
5116
- function needsAttention(run) {
5117
- return run.activityState === "needs_attention";
5665
+ function needsAttention(run, sessionId) {
5666
+ return run.activityState === "needs_attention" || hasPendingBlockingSupervisorRequest(run.id, sessionId);
5118
5667
  }
5119
5668
  function backgroundWorkIdentity(item) {
5120
5669
  return `${item.provider}\0${item.sessionId}\0${item.id}`;
@@ -5136,7 +5685,7 @@ function activeRunsForSession(params, deps) {
5136
5685
  return params.id ? runs.filter((run) => matchesId(run, params.id)) : runs;
5137
5686
  }
5138
5687
  function attentionRunsForSession(params, deps, initialIds) {
5139
- return activeRunsForSession(params, deps).filter((run) => needsAttention(run) && initialIds.has(run.id));
5688
+ return activeRunsForSession(params, deps).filter((run) => needsAttention(run, deps.state.currentSessionId) && initialIds.has(run.id));
5140
5689
  }
5141
5690
  function allRunsForSession(params, deps) {
5142
5691
  let asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR, resultsDir = deps.resultsDir ?? RESULTS_DIR, runs = listAsyncRuns(asyncDirRoot, {
@@ -5182,12 +5731,57 @@ async function waitForDetachedForegroundRun(run, signal, deps, startedAt, now, p
5182
5731
  await waitForWake(pollIntervalMs, signal, deps);
5183
5732
  }
5184
5733
  }
5734
+ async function waitForSupervisorQuestion(questionId, signal, deps, startedAt, now, pollIntervalMs, timeoutMs) {
5735
+ let sessionId = deps.state.currentSessionId;
5736
+ if (!sessionId)
5737
+ return result("subagent_wait requires an active session identity to scope background work safely.", !0);
5738
+ let releaseWait = registerQuestionWait([questionId]);
5739
+ try {
5740
+ for (; ; ) {
5741
+ let found = findSupervisorQuestion(questionId);
5742
+ if (!found)
5743
+ return result(`No supervisor question matched "${questionId}".`, !0);
5744
+ let { channelDir } = found, { question } = found;
5745
+ if (question.parentSessionId !== sessionId || deps.state.currentSessionId !== sessionId)
5746
+ return result(`Supervisor question "${questionId}" belongs to another session.`, !0);
5747
+ if ((deps.state.sessionGeneration ?? 0) !== question.parentGeneration)
5748
+ return result(`Supervisor question "${questionId}" was cancelled by a session replacement.`, !0);
5749
+ if (question = reconcileAsyncQuestion(channelDir, question, deps.state), supervisorQuestionIsTerminal(question)) {
5750
+ claimQuestionNotification(channelDir, question.id);
5751
+ let state = supervisorQuestionPublicState(question);
5752
+ return {
5753
+ content: [{ type: "text", text: formatSupervisorQuestionAnswer(question) }],
5754
+ ...state === "answered" ? {} : { isError: !0 },
5755
+ details: {
5756
+ mode: "management",
5757
+ results: [],
5758
+ questionId: question.id,
5759
+ state,
5760
+ runId: question.runId,
5761
+ answered: state === "answered",
5762
+ delivered: !!question.deliveredAt
5763
+ }
5764
+ };
5765
+ }
5766
+ if (signal?.aborted)
5767
+ return result(`Wait aborted after ${formatDuration(now() - startedAt)} for supervisor question "${questionId}".`, !0);
5768
+ if (now() - startedAt >= timeoutMs)
5769
+ return result(`Wait timed out after ${formatDuration(timeoutMs)} for supervisor question "${questionId}". The question remains pending until answered, expired, or cancelled.`, !0);
5770
+ await waitForWake(pollIntervalMs, signal, deps);
5771
+ }
5772
+ } finally {
5773
+ releaseWait();
5774
+ }
5775
+ }
5185
5776
  async function waitForSubagents(params, signal, deps) {
5186
5777
  if (deps.enabled === !1)
5187
5778
  return result('subagent_wait is disabled by config.waitTool or PI_SUBAGENT_WAIT_TOOL_ENABLED; returning immediately without blocking background work. Active work keeps going, and you can inspect subagents with subagent({ action: "status" }) or rely on completion notifications.');
5188
5779
  if (!deps.state.currentSessionId)
5189
5780
  return result("subagent_wait requires an active session identity to scope background work safely.", !0);
5190
- let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now(), waitForAll = params.id ? !0 : params.all === !0, active, foreground, providerSnapshot;
5781
+ let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now();
5782
+ if (params.questionId?.trim())
5783
+ return waitForSupervisorQuestion(params.questionId.trim(), signal, deps, startedAt, now, pollIntervalMs, timeoutMs);
5784
+ let waitForAll = params.id ? !0 : params.all === !0, active, foreground, providerSnapshot;
5191
5785
  try {
5192
5786
  active = activeRunsForSession(params, deps), foreground = activeDetachedForegroundRuns(params, deps), providerSnapshot = params.id ? { providers: [], items: [] } : backgroundWorkForSession(deps, startedAt);
5193
5787
  } catch (error) {
@@ -5208,7 +5802,7 @@ async function waitForSubagents(params, signal, deps) {
5208
5802
  let providerActive = providerSnapshot.items;
5209
5803
  if (active.length === 0 && providerActive.length === 0)
5210
5804
  return result(params.id ? `No active run matched "${params.id}". Nothing to wait for.` : "No active async runs or registered provider work in this session. Nothing to wait for.");
5211
- let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run)), isDone = () => {
5805
+ let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run, deps.state.currentSessionId)), isDone = () => {
5212
5806
  if (stopOnAttention && attention.some((run) => initialAsyncIds.has(run.id)))
5213
5807
  return !0;
5214
5808
  let activeAsyncIds = new Set(active.map((run) => run.id)), activeProviderIds2 = new Set(providerActive.map(backgroundWorkIdentity));
@@ -5256,11 +5850,12 @@ function registerWaitTool(pi, state, enabled = resolveWaitToolConfig().enabled,
5256
5850
  label: "Subagent Wait",
5257
5851
  description: `Block until background work owned by this session changes, then return.
5258
5852
 
5259
- In an interactive chat, do not call this merely to wait: return control to the user and let Pi wake the session on completion. Call it when the current request must run to completion in this turn, when a skill cannot return before its work finishes, or in a non-interactive run where there is no next turn.
5853
+ Call this when the current turn or skill must finish after its children. For normal interactive completion, yield and let lunR wake the session. Headless sessions auto-drain current-session work after the agent turn.
5260
5854
 
5261
5855
  \u2022 { } \u2014 return when the first initially active async run or registered provider item finishes, or when a subagent needs attention.
5262
5856
  \u2022 { all: true } \u2014 wait for every async run and provider item that was active when the call began.
5263
5857
  \u2022 { id: "..." } \u2014 wait for one async or remembered detached foreground subagent run (id or prefix).
5858
+ \u2022 { questionId: "..." } \u2014 wait for one supervisor question from subagent_supervisor action='ask' (answered/expired/cancelled). Separate from final run results.
5264
5859
  \u2022 { timeoutMs: 600000 } \u2014 stop waiting after N ms; active work keeps running.
5265
5860
 
5266
5861
  Provider jobs are session-scoped and identified exactly, so replacing one job with another cannot hide a completion. Provider extensions must be explicitly loaded in this process. In a child agent, keep \`subagent_wait\` in the child tool allowlist and load each provider through the agent's extensions or subagentOnlyExtensions; this tool never loads providers or grants tools itself.${enabled ? "" : `
@@ -5395,7 +5990,7 @@ function formatSingleCompletion(details) {
5395
5990
  }
5396
5991
  function parseSubagentNotifyContent(content) {
5397
5992
  let lines = content.split(`
5398
- `), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
5993
+ `), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
5399
5994
  if (!match)
5400
5995
  return;
5401
5996
  let body = lines.slice(2), sessionIndex = -1;
@@ -5439,7 +6034,7 @@ function sendCompletion(pi, details) {
5439
6034
  customType: "subagent-notify",
5440
6035
  content,
5441
6036
  display: !0
5442
- }, { triggerTurn: !0 });
6037
+ }, { triggerTurn: details.some((detail) => detail.status !== "stopped") });
5443
6038
  }
5444
6039
  function completionBatchKey(result2) {
5445
6040
  let sessionId = typeof result2.sessionId == "string" ? result2.sessionId.trim() : "";
@@ -5449,13 +6044,38 @@ function completionBatchKey(result2) {
5449
6044
  return cwd ? `cwd:${cwd}` : "unknown";
5450
6045
  }
5451
6046
  function buildCompletionDetails(result2) {
5452
- let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "", status = !result2.success && (result2.exitCode === 0 || result2.state === "paused" || summary.startsWith("Paused after interrupt.")) ? "paused" : result2.success ? "completed" : "failed", taskInfo = result2.taskIndex !== void 0 && result2.totalTasks !== void 0 ? ` (${result2.taskIndex + 1}/${result2.totalTasks})` : void 0, session = result2.shareUrl ? { label: "Session", value: result2.shareUrl } : result2.shareError ? { label: "Session share error", value: result2.shareError } : result2.sessionFile ? { label: "Session file", value: result2.sessionFile } : void 0;
6047
+ let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "", paused = !result2.success && (result2.exitCode === 0 || result2.state === "paused" || summary.startsWith("Paused after interrupt.")), status = result2.state === "stopped" ? "stopped" : paused ? "paused" : result2.success ? "completed" : "failed", taskInfo = result2.taskIndex !== void 0 && result2.totalTasks !== void 0 ? ` (${result2.taskIndex + 1}/${result2.totalTasks})` : void 0, session = result2.shareUrl ? { label: "Session", value: result2.shareUrl } : result2.shareError ? { label: "Session share error", value: result2.shareError } : result2.sessionFile ? { label: "Session file", value: result2.sessionFile } : void 0;
5453
6048
  return {
5454
6049
  agent,
5455
6050
  status,
5456
6051
  ...result2.source ? { source: result2.source } : {},
5457
6052
  ...taskInfo ? { taskInfo } : {},
5458
- resultPreview: summary,
6053
+ resultPreview: [
6054
+ result2.id ? `Run: ${result2.id}` : void 0,
6055
+ result2.asyncDir ? `Async dir: ${result2.asyncDir}` : void 0,
6056
+ result2.results?.length ? result2.results.map((child, index) => {
6057
+ let output = child.summary ?? (child.error ? `${child.error}
6058
+
6059
+ Output:
6060
+ ${child.output}` : child.output), report = child.acceptance?.childReport, childIndex = child.index ?? index;
6061
+ return [
6062
+ `${childIndex + 1}. ${child.agent} [${child.status ?? resolveSubagentResultStatus({ success: child.success, state: result2.state })}]`,
6063
+ child.artifactPath ? `Output artifact: ${child.artifactPath}` : void 0,
6064
+ child.sessionPath ? `Session: ${child.sessionPath}` : void 0,
6065
+ child.sessionPath && result2.id && result2.source !== "foreground" ? `Revive: subagent({ action: "resume", id: ${JSON.stringify(result2.id)}, index: ${childIndex}, message: "..." })` : void 0,
6066
+ ...formatNestedResultLines(child.children),
6067
+ output,
6068
+ report ? `\`\`\`acceptance-report
6069
+ ${JSON.stringify(report, null, 2)}
6070
+ \`\`\`` : void 0
6071
+ ].filter((line) => line !== void 0).join(`
6072
+ `);
6073
+ }).join(`
6074
+
6075
+ `) : summary
6076
+ ].filter((line) => line !== void 0).join(`
6077
+
6078
+ `),
5459
6079
  ...typeof result2.durationMs == "number" ? { durationMs: result2.durationMs } : {},
5460
6080
  ...session ? { sessionLabel: session.label, sessionValue: session.value } : {}
5461
6081
  };
@@ -5538,15 +6158,15 @@ function handleSubagentSteeringNotice(input) {
5538
6158
  }
5539
6159
 
5540
6160
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/config.js
5541
- import * as fs7 from "node:fs";
5542
- import * as path9 from "node:path";
6161
+ import * as fs8 from "node:fs";
6162
+ import * as path10 from "node:path";
5543
6163
  function getConfigPath() {
5544
- return path9.join(getAgentDir(), "extensions", "subagent", "config.json");
6164
+ return path10.join(getAgentDir(), "extensions", "subagent", "config.json");
5545
6165
  }
5546
6166
  function readConfigForUpdate(configPath = getConfigPath()) {
5547
- if (!fs7.existsSync(configPath))
6167
+ if (!fs8.existsSync(configPath))
5548
6168
  return {};
5549
- let parsed = JSON.parse(fs7.readFileSync(configPath, "utf-8"));
6169
+ let parsed = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
5550
6170
  if (!parsed || typeof parsed != "object" || Array.isArray(parsed))
5551
6171
  throw new Error(`Subagent config at '${configPath}' must be a JSON object`);
5552
6172
  return parsed;
@@ -5562,25 +6182,31 @@ function loadConfig() {
5562
6182
  }
5563
6183
 
5564
6184
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js
5565
- import * as fs8 from "node:fs";
5566
- import * as path10 from "node:path";
5567
- var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024, SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:
6185
+ import * as fs9 from "node:fs";
6186
+ import * as path11 from "node:path";
6187
+ var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024, SUBAGENT_TIER_GUIDANCE = `TIER SELECTION:
6188
+ \u2022 Choose the lowest tier that can reliably complete the task.
6189
+ \u2022 light: Use for simple, straightforward tasks that require little reasoning. Good for quick checks, finding information, collecting facts for research, inspecting a small amount of code, or verifying a specific fact.
6190
+ \u2022 standard: Use for moderately difficult work and everyday coding tasks. Good for writing simple code, fixing a clearly described bug, reviewing a focused change, or completing work with clear requirements. Use heavy when the task requires deep architectural understanding or complex reasoning.
6191
+ \u2022 heavy: Use for complex or ambiguous tasks that require deep reasoning and strong code understanding. Good for implementing complex plans, debugging difficult or poorly understood problems, reviewing large or high-risk changes, and making architectural decisions.`, SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:
5568
6192
  \u2022 Prompt children directly. There is no agent roster. Every execution task needs task (the full child prompt) and description (a concise UI label).
5569
- \u2022 Every executable child also needs tier: "light", "standard", or "heavy". Direct model overrides and parent-model inheritance are not available. A missing, disabled, unconfigured, unavailable, or unauthenticated tier fails before launch.
6193
+ \u2022 Every executable child chooses exactly one of tier: "light"|"standard"|"heavy" (default) or model: "provider/id" when the user names a model. Do not pass both, and do not use inherit. Missing, disabled, unconfigured, unavailable, or unauthenticated selections fail before launch.
6194
+ \u2022 Optional thinking (off/minimal/low/medium/high/xhigh/max) is only valid with an explicit model. Tier launches use the configured tier thinking level.
6195
+ ${SUBAGENT_TIER_GUIDANCE}
5570
6196
  \u2022 permissions is "full" or "read-only". Omitted permissions means full. Plan-mode parents must pass permissions: "read-only"; full or omitted launches are rejected.
5571
- \u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog.*/schedule*.
5572
- \u2022 Async/background runs: launch with async:true only when work can proceed independently. Do not sleep or poll status just to wait. In an interactive session, normally return control and let lunR wake you; use subagent_wait when this request must run to completion in the current turn or skill. Headless sessions auto-drain current-session work.
6197
+ \u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog.status|check|recommend-model/schedule*.
6198
+ \u2022 ${SUBAGENT_ASYNC_GUIDANCE}
5573
6199
  \u2022 Child-safety boundary: ordinary children are not orchestrators and must not run subagents. Only explicitly configured fanout children may use the child-safe subagent tool, still bounded by depth/session limits.
5574
6200
  \u2022 Writing/review safety: keep one full-access writer for the same cwd/worktree. Use fresh-context permissions: "read-only" children for independent review, then have the parent synthesize and apply fixes as the sole writer unless an isolated worktree was intentionally requested.
5575
6201
  \u2022 Artifacts/status essentials: chain outputs live under {chain_dir}; async runs expose asyncId/asyncDir with status.json, events.jsonl, output logs, and status via { action: "status", id }. Include output paths and residual risks when reporting results.`, FULL_SUBAGENT_TOOL_DESCRIPTION = `Delegate work to generic children by prompting them directly. There are no named agent types.
5576
6202
 
5577
6203
  EXECUTION (use exactly ONE mode):
5578
- \u2022 SINGLE: { task, description, tier, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
5579
- \u2022 PARALLEL: { tasks: [{task, description, tier, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
5580
- \u2022 CHAIN: { chain: [{task, description, tier, permissions?}, {parallel:[{task, description, tier, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
6204
+ \u2022 SINGLE: { task, description, tier|model, thinking?, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
6205
+ \u2022 PARALLEL: { tasks: [{task, description, tier|model, thinking?, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
6206
+ \u2022 CHAIN: { chain: [{task, description, tier|model, thinking?, permissions?}, {parallel:[{task, description, tier|model, thinking?, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
5581
6207
  \u2022 description is required, single-line, max 80 characters. It is UI metadata only. task is the complete child prompt.
5582
6208
  \u2022 permissions: "full" (default) or "read-only". Full includes coding tools (read, search, shell, edit, write, web, LSP, MCP) and excludes parent-owned tools (cron, memory, behavior, goals, nested subagents). Plan-mode parents may launch only permissions: "read-only".
5583
- \u2022 Every executable child requires tier: "light", "standard", or "heavy". Direct model overrides and parent-model inheritance are rejected. The configured tier model and credentials must be available.
6209
+ \u2022 Model selection: pass tier by default ("light"|"standard"|"heavy"). Pass model: "provider/id" only when the user specifies a model. Exactly one of tier or model. Optional thinking only with model; tier uses configured thinking. Direct model launches do not require tier mode to be enabled. No inherit and no silent fallback for an explicit model.
5584
6210
  \u2022 Children always start with a fresh session (no inherited parent transcript).
5585
6211
  \u2022 Optional timeout: { timeoutMs } or { maxRuntimeMs } sets a run-level max runtime for foreground and async/background runs
5586
6212
 
@@ -5595,8 +6221,7 @@ CHAIN EXAMPLES:
5595
6221
  \u2022 Mixed: { chain: [{task:"Research {task}", description:"Research request", tier:"standard", permissions:"read-only"}, {parallel: [{task:"Review {previous}", description:"Review findings", tier:"light", permissions:"read-only", count: 2}]}, {task:"Summarize {previous}", description:"Summarize reviews", tier:"standard"}] }
5596
6222
 
5597
6223
  CONTROL (use action field, omit task/chain/tasks):
5598
- \u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation
5599
- \u2022 { action: "watchdog.configure", model: "recommended" | "inherit" | "provider/model[:thinking]", scope?: "session" | "user" | "project", target?: "main" | "children", thinking?: "inherit" | "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" } - configure watchdog model selection; default scope is session, use persistent scopes only when the user asks
6224
+ \u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation (read-only; there is no configure action)
5600
6225
  \u2022 { action: "status", id: "..." } - inspect an async/background run by id or prefix
5601
6226
  \u2022 { action: "status", view: "fleet" } - read-only active foreground/async fleet view with transcript commands
5602
6227
  \u2022 { action: "status", id: "...", view: "transcript", index?: 0, lines?: 80 } - tail a run or child output/session transcript
@@ -5604,10 +6229,12 @@ CONTROL (use action field, omit task/chain/tasks):
5604
6229
  \u2022 { action: "stop", id: "..." } - stop a current-session top-level async run; stopped runs finish with state "stopped"
5605
6230
  \u2022 { action: "resume", id: "...", message: "...", index?: 0 } - interrupt then follow up with a live async child, or revive a completed async/foreground child from its session
5606
6231
  \u2022 { action: "steer", id: "...", message: "...", index?: 0 } - await correlated child input acceptance for up to 3 seconds; returns delivered, scheduled, pending, partial, recovered, or failed with a request id. Only top-level single runs may recover after a further 15-second pause/revival bound; chain, parallel, and nested runs never auto-interrupt.
6232
+ \u2022 subagent_supervisor({ action: "ask", id, reason, message, index?, childId? }) - ask one running async child; returns question id immediately. Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks block/rework. No broadcast, revive, task replacement, status polling, or micromanagement.
6233
+ \u2022 subagent_wait({ questionId }) - wait for that supervisor question answer/expiry/cancel. Answers stay separate from final run results.
5607
6234
  \u2022 { action: "append-step", id: "...", chain: [{task:"Use {previous}", description:"Follow-up step", tier:"standard"}] } - append one step to the tail of a running async chain
5608
6235
 
5609
6236
  SCHEDULE (opt-in; requires { "scheduledRuns": { "enabled": true } } in config.json):
5610
- \u2022 { action: "schedule", task, description, tier, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
6237
+ \u2022 { action: "schedule", task, description, tier|model, thinking?, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
5611
6238
  \u2022 { action: "schedule-list" } - list scheduled runs for this session
5612
6239
  \u2022 { action: "schedule-status", id: "..." } - inspect one scheduled run
5613
6240
  \u2022 { action: "schedule-cancel", id: "..." } - cancel a scheduled run before it fires
@@ -5618,19 +6245,20 @@ DIAGNOSTICS:
5618
6245
  ${SUBAGENT_SAFETY_GUIDANCE}`, COMPACT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to generic children by prompting them. Use exactly one mode per call.
5619
6246
 
5620
6247
  EXECUTE:
5621
- \u2022 SINGLE {task, description, tier, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier,permissions?},{parallel:[...]}]} for sequential work.
6248
+ \u2022 SINGLE {task, description, tier|model, thinking?, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier|model,thinking?,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier|model,thinking?,permissions?},{parallel:[...]}]} for sequential work.
5622
6249
  \u2022 description is required (single-line, max 80 chars, UI only). task is the full child prompt. permissions omitted = full. Plan-mode parents must pass permissions:"read-only".
5623
- \u2022 Every executable child requires tier:"light"|"standard"|"heavy". Direct model overrides and parent-model inheritance are rejected; tier resolution fails closed. Children always start with a fresh session. timeoutMs/maxRuntimeMs apply to foreground and async/background runs.
6250
+ \u2022 Exactly one of tier:"light"|"standard"|"heavy" (default) or model:"provider/id" when the user names a model. thinking only with model. No inherit; fail closed on unavailable selections. Children always start fresh. timeoutMs/maxRuntimeMs apply to foreground and async/background runs.
6251
+ ${SUBAGENT_TIER_GUIDANCE}
5624
6252
  \u2022 Chain templates may use {task}, {previous}, {chain_dir}, and named outputs. Parallel worktree isolation requires a clean git repo.
5625
6253
  \u2022 Chain example: { chain: [{task:"Analyze {task}", description:"Analyze request", tier:"standard"}, {parallel: [{task:"Check {previous}", description:"Check prior result", tier:"light", permissions:"read-only", count: 3}]}] }
5626
6254
 
5627
6255
  CONTROL:
5628
- \u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model, watchdog.configure, status, interrupt, stop, resume, steer, append-step.
6256
+ \u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model, status, interrupt, stop, resume, steer, append-step.
5629
6257
  \u2022 Async control actions: status, interrupt, stop, resume, steer, append-step. Use stop with an id for current-session top-level async runs. Use status view:"fleet" for active-run overview, view:"transcript" to tail child output, and steer for acknowledged live guidance. Steering delivery means lunR accepted the correlated user input, not model compliance; use index for a specific child.
5630
6258
  \u2022 Opt-in schedule actions: schedule, schedule-list, schedule-status, schedule-cancel. Schedule only explicit delayed runs the user asked for.
5631
6259
 
5632
6260
  ASYNC / WAIT:
5633
- \u2022 async:true detaches background work. Do not sleep or poll just to wait. Interactive sessions normally yield for completion notifications; use subagent_wait for run-to-completion turns or skills. Headless sessions auto-drain current-session work.
6261
+ \u2022 ${SUBAGENT_ASYNC_GUIDANCE}
5634
6262
  \u2022 Status and artifacts live under asyncId/asyncDir with status.json, events.jsonl, output logs, session files, and { action:"status", id:"..." }.
5635
6263
 
5636
6264
  SAFETY:
@@ -5649,8 +6277,8 @@ function resolveToolDescriptionMode(config, options) {
5649
6277
  function customDescriptionPaths(options) {
5650
6278
  let cwd = options?.cwd ?? process.cwd(), agentDir = options?.agentDir ?? getAgentDir();
5651
6279
  return [
5652
- path10.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),
5653
- path10.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE)
6280
+ path11.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),
6281
+ path11.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE)
5654
6282
  ];
5655
6283
  }
5656
6284
  function renderCustomTemplate(template, options) {
@@ -5673,7 +6301,7 @@ function loadCustomToolDescription(options) {
5673
6301
  for (let filePath of customDescriptionPaths(options)) {
5674
6302
  let stat;
5675
6303
  try {
5676
- stat = fs8.statSync(filePath);
6304
+ stat = fs9.statSync(filePath);
5677
6305
  } catch (error) {
5678
6306
  if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
5679
6307
  continue;
@@ -5689,7 +6317,7 @@ function loadCustomToolDescription(options) {
5689
6317
  continue;
5690
6318
  }
5691
6319
  try {
5692
- let template = fs8.readFileSync(filePath, "utf-8").trim();
6320
+ let template = fs9.readFileSync(filePath, "utf-8").trim();
5693
6321
  if (!template) {
5694
6322
  warn(options, `Ignoring empty custom tool description '${filePath}'.`);
5695
6323
  continue;
@@ -5728,24 +6356,24 @@ function buildSubagentToolDescription(config = {}, options) {
5728
6356
  // packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
5729
6357
  function getSubagentSessionRoot(parentSessionFile) {
5730
6358
  if (parentSessionFile) {
5731
- let baseName = path11.basename(parentSessionFile, ".jsonl"), sessionsDir = path11.dirname(parentSessionFile);
5732
- return path11.join(sessionsDir, baseName);
6359
+ let baseName = path12.basename(parentSessionFile, ".jsonl"), sessionsDir = path12.dirname(parentSessionFile);
6360
+ return path12.join(sessionsDir, baseName);
5733
6361
  }
5734
- return fs9.mkdtempSync(path11.join(os.tmpdir(), "pi-subagent-session-"));
6362
+ return fs10.mkdtempSync(path12.join(os.tmpdir(), "pi-subagent-session-"));
5735
6363
  }
5736
6364
  function expandTilde(p) {
5737
- return p.startsWith("~/") ? path11.join(os.homedir(), p.slice(2)) : p;
6365
+ return p.startsWith("~/") ? path12.join(os.homedir(), p.slice(2)) : p;
5738
6366
  }
5739
6367
  function ensureAccessibleDir(dirPath) {
5740
- fs9.mkdirSync(dirPath, { recursive: !0 });
6368
+ fs10.mkdirSync(dirPath, { recursive: !0 });
5741
6369
  try {
5742
- fs9.accessSync(dirPath, fs9.constants.R_OK | fs9.constants.W_OK);
6370
+ fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
5743
6371
  } catch {
5744
6372
  try {
5745
- fs9.rmSync(dirPath, { recursive: !0, force: !0 });
6373
+ fs10.rmSync(dirPath, { recursive: !0, force: !0 });
5746
6374
  } catch {
5747
6375
  }
5748
- fs9.mkdirSync(dirPath, { recursive: !0 }), fs9.accessSync(dirPath, fs9.constants.R_OK | fs9.constants.W_OK);
6376
+ fs10.mkdirSync(dirPath, { recursive: !0 }), fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
5749
6377
  }
5750
6378
  }
5751
6379
  function isSlashResultRunning(result2) {
@@ -5760,23 +6388,58 @@ function effectiveParallelTaskCount(tasks) {
5760
6388
  return total + count;
5761
6389
  }, 0);
5762
6390
  }
5763
- function renderSubagentCall(args, theme, context) {
6391
+ function matchingActionJobs(state, requestedId) {
6392
+ let jobs = /* @__PURE__ */ new Map();
6393
+ for (let job of state.asyncJobs.values())
6394
+ jobs.set(job.asyncId, job);
6395
+ for (let job of state.fleetJobs?.values() ?? [])
6396
+ jobs.set(job.asyncId, job);
6397
+ let exact = jobs.get(requestedId);
6398
+ return exact ? [exact] : [...jobs.values()].filter((job) => job.asyncId.startsWith(requestedId));
6399
+ }
6400
+ function resolveSubagentActionDisplayTitle(args, state) {
6401
+ let requestedId = (args.id || args.runId || "").trim();
6402
+ if (!requestedId)
6403
+ return;
6404
+ let matches = matchingActionJobs(state, requestedId);
6405
+ if (matches.length !== 1)
6406
+ return;
6407
+ let job = matches[0], steps = job.steps ?? [];
6408
+ if (Number.isInteger(args.index)) {
6409
+ let step = steps.find((candidate) => candidate.index === args.index) ?? steps[args.index];
6410
+ return step ? childRowLabel(step) : void 0;
6411
+ }
6412
+ if (steps.length === 1)
6413
+ return childRowLabel(steps[0]);
6414
+ if (steps.length > 1)
6415
+ return `${job.mode ?? "subagent"} run`;
6416
+ if (job.agents?.length === 1)
6417
+ return job.agents[0];
6418
+ if (job.agents?.length)
6419
+ return `${job.mode ?? "subagent"} run`;
6420
+ }
6421
+ function renderSubagentCall(rawArgs, theme, context, options) {
6422
+ let args = resolveSubagentRequestParams(rawArgs);
5764
6423
  if (args.action) {
5765
- let target = args.id || args.runId || "";
6424
+ let target = context?.result?.details?.displayTitle || options?.resolveActionTitle?.(args) || args.id || args.runId || "";
5766
6425
  return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`, 0, 0);
5767
6426
  }
5768
- let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks), asyncLabel = args.async === !0 && args.clarify !== !0 ? theme.fg("warning", " [async]") : "";
6427
+ let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks), asyncMark = subagentLaunchRunsAsync(args) ? `${theme.fg("warning", "async")} ` : "";
5769
6428
  if (args.chain?.length)
5770
- return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})${asyncLabel}`, 0, 0);
6429
+ return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}chain (${args.chain.length})`, 0, 0);
5771
6430
  if (isParallel)
5772
- return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})${asyncLabel}`, 0, 0);
5773
- let description = !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "";
5774
- return new Text2(`${theme.fg("toolTitle", theme.bold(description ? "subagent " : "subagent"))}${description}${asyncLabel}`, 0, 0);
6431
+ return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}parallel (${parallelCount})`, 0, 0);
6432
+ let description = !context?.result && !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "", title = theme.fg("toolTitle", theme.bold("subagent")), rest = [asyncMark.trim() ? theme.fg("warning", "async") : "", description].filter(Boolean).join(" ");
6433
+ return new Text2(rest ? `${title} ${rest}` : title, 0, 0);
6434
+ }
6435
+ function renderSubagentNotify(details, theme) {
6436
+ let status = typeof details?.status == "string" ? details.status : "", icon = status === "completed" ? theme.fg("success", "\u2713") : status === "failed" ? theme.fg("error", "\u2717") : theme.fg("warning", "\u25A0"), title = details?.agent || "child";
6437
+ return new Text2(`${icon} ${theme.bold(title)} ${theme.fg("dim", status)}`, 0, 0);
5775
6438
  }
5776
6439
  function ensureSubagentResultAnimation(context) {
5777
6440
  let state = context.state;
5778
6441
  state.subagentResultAnimationTimer || typeof context.invalidate == "function" && (state.frame === void 0 && (state.frame = 0), state.subagentResultAnimationTimer = setInterval(() => {
5779
- state.frame = ((state.frame ?? 0) + 1) % 10;
6442
+ state.frame = (state.frame ?? 0) + 1;
5780
6443
  try {
5781
6444
  let entries = state.animEntries;
5782
6445
  if (entries?.length && typeof context.requestRender == "function") {
@@ -5788,7 +6451,7 @@ function ensureSubagentResultAnimation(context) {
5788
6451
  context.invalidate();
5789
6452
  } catch {
5790
6453
  }
5791
- }, 80));
6454
+ }, SUBAGENT_PAINT_INTERVAL_MS));
5792
6455
  }
5793
6456
  function isSlashResultError(result2) {
5794
6457
  return result2.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || !1;
@@ -5818,11 +6481,12 @@ function registerSubagentExtension(pi) {
5818
6481
  } catch {
5819
6482
  }
5820
6483
  ensureAccessibleDir(RESULTS_DIR), ensureAccessibleDir(ASYNC_DIR), cleanupOldChainDirs();
5821
- let config = loadConfig(), waitToolConfig = resolveWaitToolConfig(config.waitTool), asyncByDefault = config.asyncByDefault === !0, tempArtifactsDir = getArtifactsDir(null);
6484
+ let config = normalizeAsyncLaunchConfig(loadConfig()), waitToolConfig = resolveWaitToolConfig(config.waitTool), tempArtifactsDir = getArtifactsDir(null);
5822
6485
  cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
5823
6486
  let state = {
5824
6487
  baseCwd: "",
5825
6488
  currentSessionId: null,
6489
+ sessionGeneration: 0,
5826
6490
  foregroundSubagentInFlight: 0,
5827
6491
  subagentSpawns: { sessionId: null, count: 0 },
5828
6492
  asyncJobs: /* @__PURE__ */ new Map(),
@@ -5844,15 +6508,19 @@ function registerSubagentExtension(pi) {
5844
6508
  }
5845
6509
  }, supervisorChannel = createNativeSupervisorChannel(pi, state), mainWatchdog = registerMainWatchdog(pi), { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(pi, state, RESULTS_DIR, 600 * 1e3);
5846
6510
  startResultWatcher(), primeExistingResults();
5847
- let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), runtimeCleanup = () => {
5848
- generation++, executorPromise = void 0, pendingLaunches.clear(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), state.poller && (clearInterval(state.poller), state.poller = null);
6511
+ let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), pendingAsyncLaunches = /* @__PURE__ */ new Set(), unregisterCancellation, runtimeCleanup = () => {
6512
+ generation++, supervisorChannel.cancelOwnedQuestions("parent runtime replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), disposeSubagentWidget(), state.poller && (clearInterval(state.poller), state.poller = null);
5849
6513
  };
5850
6514
  globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
5851
- let { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
6515
+ let { ensurePoller, refreshWidget, handleStarted, handleComplete: trackComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
5852
6516
  widgetEnabled: config.asyncWidget !== !1
5853
- }), executorExecute, scheduledRunManager = createScheduledRunManager({
6517
+ }), handleComplete = (data) => {
6518
+ trackComplete(data);
6519
+ let result2 = data;
6520
+ typeof state.currentSessionId == "string" && result2.sessionId !== state.currentSessionId || typeof result2.id == "string" && result2.id.trim() && supervisorChannel.settleRunQuestions(result2.id, "async run settled");
6521
+ }, executorExecute, scheduledRunManager = createScheduledRunManager({
5854
6522
  config,
5855
- launch: (params, ctx, signal) => executorExecute ? executorExecute(randomUUID3(), params, signal, void 0, ctx) : Promise.resolve({
6523
+ launch: (params, ctx, signal) => executorExecute ? executorExecute(randomUUID4(), params, signal, void 0, ctx) : Promise.resolve({
5856
6524
  content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
5857
6525
  isError: !0,
5858
6526
  details: { mode: "management", results: [] }
@@ -5862,20 +6530,19 @@ function registerSubagentExtension(pi) {
5862
6530
  let resolveLaunchReady, launchReady = !1, launchReadyPromise = new Promise((resolve3) => {
5863
6531
  resolveLaunchReady = resolve3;
5864
6532
  }), markLaunchReady = () => {
5865
- launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), resolveLaunchReady());
6533
+ launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), pendingAsyncLaunches.delete(launchReadyPromise), resolveLaunchReady());
5866
6534
  };
5867
- pendingLaunches.add(launchReadyPromise);
6535
+ pendingLaunches.add(launchReadyPromise), isAsyncSubagentExecution(resolveSubagentRequestParams(params)) && pendingAsyncLaunches.add(launchReadyPromise);
5868
6536
  try {
5869
6537
  let currentGeneration = generation;
5870
6538
  if (signal?.throwIfAborted(), !executorPromise) {
5871
- let pending = import("./subagent-executor-EFLTO32A.js").then(({ createSubagentExecutor }) => {
6539
+ let pending = import("./subagent-executor-CXKXQDHH.js").then(({ createSubagentExecutor }) => {
5872
6540
  if (generation !== currentGeneration)
5873
6541
  throw new Error("Subagent session changed before initialization");
5874
6542
  return createSubagentExecutor({
5875
6543
  pi,
5876
6544
  state,
5877
6545
  config,
5878
- asyncByDefault,
5879
6546
  waitToolEnabled: waitToolConfig.enabled,
5880
6547
  handleScheduledRunAction: (params2, ctx2) => scheduledRunManager.handleToolCall(params2, ctx2),
5881
6548
  watchdog: mainWatchdog,
@@ -5906,35 +6573,20 @@ function registerSubagentExtension(pi) {
5906
6573
  let content = typeof message.content == "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
5907
6574
  `);
5908
6575
  return new Text2(content, 0, 0);
5909
- }), pi.registerMessageRenderer("subagent-notify", (message, options, theme) => {
6576
+ }), pi.registerMessageRenderer("subagent-notify", (message, _options, theme) => {
5910
6577
  let content = typeof message.content == "string" ? message.content : "", details = message.details ?? parseSubagentNotifyContent(content);
5911
- if (!details)
5912
- return new Text2(content, 0, 0);
5913
- let icon = details.status === "completed" ? theme.fg("success", "\u2713") : details.status === "paused" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717"), parts = [];
5914
- details.taskInfo && parts.push(details.taskInfo), details.durationMs !== void 0 && parts.push(formatDuration(details.durationMs));
5915
- let notifyLabel = details.taskInfo || details.agent || "child", text = `${icon} ${theme.bold(notifyLabel)} ${theme.fg("dim", details.status)}`;
5916
- parts.length > 0 && (text += ` ${theme.fg("dim", "\xB7")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "\xB7")} `)}`);
5917
- let trimmedPreview = details.resultPreview.trim(), previewLines = options.expanded ? trimmedPreview.split(`
5918
- `).filter((line) => line.trim()) : [trimmedPreview.split(`
5919
- `, 1)[0] ?? ""].filter((line) => line.trim());
5920
- for (let line of previewLines.length > 0 ? previewLines : ["(no output)"])
5921
- text += `
5922
- ${theme.fg("dim", `\u23BF ${line}`)}`;
5923
- if (!options.expanded && trimmedPreview.includes(`
5924
- `)) {
5925
- let expandKey = keyText("app.tools.expand");
5926
- text += `
5927
- ${theme.fg("dim", `${expandKey} full notification`)}`;
5928
- }
5929
- return details.sessionLabel && details.sessionValue && (text += `
5930
- ${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`), new Text2(text, 0, 0);
6578
+ return details ? renderSubagentNotify(details, theme) : new Text2(content, 0, 0);
5931
6579
  }), pi.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
5932
6580
  let details = message.details;
5933
6581
  if (details)
5934
6582
  return new Text2(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
5935
6583
  }), pi.registerMessageRenderer(SUBAGENT_CONTROL_MESSAGE_TYPE, () => {
5936
6584
  });
5937
- let executeSubagentCollapsed = (id, params, signal, onUpdate, ctx) => (ctx.hasUI && ctx.ui.setToolsExpanded(!1), executor.execute(id, params, signal, onUpdate, ctx)), slashBridge = registerSlashSubagentBridge({
6585
+ let executeSubagentCollapsed = async (id, params, signal, onUpdate, ctx) => {
6586
+ ctx.hasUI && ctx.ui.setToolsExpanded(!1);
6587
+ let displayTitle = params.action === "steer" ? resolveSubagentActionDisplayTitle(params, state) : void 0, result2 = await executor.execute(id, params, signal, onUpdate, ctx);
6588
+ return displayTitle && result2.details && (result2.details.displayTitle = displayTitle), result2;
6589
+ }, slashBridge = registerSlashSubagentBridge({
5938
6590
  events: pi.events,
5939
6591
  getContext: () => state.lastUiContext,
5940
6592
  execute: (id, params, signal, onUpdate, ctx) => executeSubagentCollapsed(id, params, signal, onUpdate, ctx)
@@ -5959,7 +6611,9 @@ function registerSubagentExtension(pi) {
5959
6611
  execute(id, params, signal, onUpdate, ctx) {
5960
6612
  return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
5961
6613
  },
5962
- renderCall: renderSubagentCall,
6614
+ renderCall: (args, theme, context) => renderSubagentCall(args, theme, context, {
6615
+ resolveActionTitle: (params) => resolveSubagentActionDisplayTitle(params, state)
6616
+ }),
5963
6617
  renderResult(result2, options, theme, context) {
5964
6618
  subagentResultIsRunning(result2) ? ensureSubagentResultAnimation(context) : clearLegacyResultAnimationTimer(context);
5965
6619
  let state2 = context.state, entries = [];
@@ -6018,16 +6672,25 @@ function registerSubagentExtension(pi) {
6018
6672
  } catch {
6019
6673
  }
6020
6674
  }, resetSessionState = (ctx) => {
6021
- if (generation++, executorPromise = void 0, pendingLaunches.clear(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
6022
- let sessionId = ctx.sessionManager.getSessionId();
6023
- sessionId && (process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId);
6024
- }
6025
- state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx), scheduledRunManager.bindSession(ctx), restoreSlashFinalSnapshots(ctx.sessionManager.getEntries()), primeExistingResults();
6675
+ if (generation++, supervisorChannel.cancelOwnedQuestions("parent session replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
6676
+ let sessionId2 = ctx.sessionManager.getSessionId();
6677
+ sessionId2 && (process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId2);
6678
+ }
6679
+ state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx);
6680
+ let sessionId = state.currentSessionId, sessionGeneration = generation;
6681
+ sessionId && (unregisterCancellation = registerSubagentCancellation(sessionId, createSubagentCancellation({
6682
+ pendingLaunches: pendingAsyncLaunches,
6683
+ isCurrent: () => generation === sessionGeneration && state.currentSessionId === sessionId,
6684
+ getActiveRunIds: () => [...state.asyncJobs.values()].filter((job) => job.sessionId === sessionId && (job.status === "queued" || job.status === "running")).map((job) => job.asyncId),
6685
+ async stopRun(id) {
6686
+ return !(await executor.execute(randomUUID4(), { action: "stop", id }, new AbortController().signal, void 0, ctx)).isError;
6687
+ }
6688
+ }))), scheduledRunManager.bindSession(ctx), restoreSlashFinalSnapshots(ctx.sessionManager.getEntries()), primeExistingResults();
6026
6689
  };
6027
6690
  pi.on("session_start", (_event, ctx) => {
6028
6691
  resetSessionState(ctx), rpcBridge.emitReady(ctx), supervisorChannel.start();
6029
6692
  }), pi.on("session_shutdown", () => {
6030
- generation++, executorPromise = void 0, pendingLaunches.clear(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
6693
+ generation++, supervisorChannel.cancelOwnedQuestions("parent session shutdown"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
6031
6694
  for (let unsubscribe of eventUnsubscribes)
6032
6695
  try {
6033
6696
  unsubscribe();
@@ -6036,7 +6699,7 @@ function registerSubagentExtension(pi) {
6036
6699
  globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes && delete globalStore[eventUnsubscribeStoreKey], stopResultWatcher(), scheduledRunManager.stop(), state.poller && clearInterval(state.poller), state.poller = null, clearPendingForegroundControlNotices(state);
6037
6700
  for (let timer of state.cleanupTimers.values())
6038
6701
  clearTimeout(timer);
6039
- state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey];
6702
+ state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey], disposeSubagentWidget();
6040
6703
  try {
6041
6704
  state.lastUiContext?.hasUI && state.lastUiContext.ui.setWidget(WIDGET_KEY, void 0);
6042
6705
  } catch (error) {