@ashx-j/lunr 0.2.19 → 0.2.21

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