@ashx-j/lunr-dev 0.2.16-dev.9.1 → 0.2.21-dev.12.1

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