@selesai/code 0.8.3 → 0.8.4

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 (493) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/extensions/handoff-new.ts +1 -1
  3. package/dist/extensions/inline-skills.test.ts +1 -0
  4. package/dist/extensions/inline-skills.ts +1 -1
  5. package/dist/extensions/pi-intercom/CHANGELOG.md +25 -3
  6. package/dist/extensions/pi-intercom/README.md +34 -15
  7. package/dist/extensions/pi-intercom/broker/broker.ts +38 -141
  8. package/dist/extensions/pi-intercom/broker/client-liveness.test.ts +173 -0
  9. package/dist/extensions/pi-intercom/broker/client.ts +78 -150
  10. package/dist/extensions/pi-intercom/broker/extension-state.ts +18 -7
  11. package/dist/extensions/pi-intercom/broker/framing.ts +1 -1
  12. package/dist/extensions/pi-intercom/broker/protocol.ts +182 -0
  13. package/dist/extensions/pi-intercom/broker/spawn.test.ts +106 -7
  14. package/dist/extensions/pi-intercom/broker/spawn.ts +37 -11
  15. package/dist/extensions/pi-intercom/config.test.ts +8 -10
  16. package/dist/extensions/pi-intercom/config.ts +3 -3
  17. package/dist/extensions/pi-intercom/index.ts +218 -147
  18. package/dist/extensions/pi-intercom/intercom.integration.test.ts +891 -103
  19. package/dist/extensions/pi-intercom/package.json +3 -2
  20. package/dist/extensions/pi-intercom/project-agent.test.ts +145 -0
  21. package/dist/extensions/pi-intercom/project-agent.ts +324 -0
  22. package/dist/extensions/pi-intercom/reply-tracker.test.ts +86 -1
  23. package/dist/extensions/pi-intercom/reply-tracker.ts +41 -9
  24. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +49 -9
  25. package/dist/extensions/pi-intercom/test/inline-message.test.ts +9 -9
  26. package/dist/extensions/pi-intercom/types.ts +3 -1
  27. package/dist/extensions/pi-intercom/ui/inline-message.ts +7 -7
  28. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +2 -2
  29. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +14 -1
  30. package/dist/extensions/pi-powerline-footer/README.md +20 -37
  31. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +40 -25
  32. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +149 -18
  33. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -0
  34. package/dist/extensions/pi-powerline-footer/editor-performance.ts +99 -0
  35. package/dist/extensions/pi-powerline-footer/git-status.ts +15 -0
  36. package/dist/extensions/pi-powerline-footer/index.ts +394 -317
  37. package/dist/extensions/pi-powerline-footer/package.json +1 -1
  38. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -12
  39. package/dist/extensions/pi-powerline-footer/queue/store.ts +8 -56
  40. package/dist/extensions/pi-powerline-footer/queue/types.ts +1 -2
  41. package/dist/extensions/pi-powerline-footer/segments.ts +0 -3
  42. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +231 -1
  43. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +5 -19
  44. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +43 -0
  45. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +64 -0
  46. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +116 -0
  47. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +19 -149
  48. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +6 -0
  49. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +5 -5
  50. package/dist/extensions/pi-powerline-footer/types.ts +1 -2
  51. package/dist/extensions/pi-subagents/CHANGELOG.md +202 -14
  52. package/dist/extensions/pi-subagents/README.md +65 -1614
  53. package/dist/extensions/pi-subagents/agents/builder.md +0 -1
  54. package/dist/extensions/pi-subagents/agents/delegate.md +14 -0
  55. package/dist/extensions/pi-subagents/agents/oracle.md +74 -0
  56. package/dist/extensions/pi-subagents/agents/reviewer.md +78 -0
  57. package/dist/extensions/pi-subagents/agents/scout.md +50 -0
  58. package/dist/extensions/pi-subagents/agents/worker.md +58 -0
  59. package/dist/extensions/pi-subagents/docs/configuration.md +328 -0
  60. package/dist/extensions/pi-subagents/docs/extension-api.md +308 -0
  61. package/dist/extensions/pi-subagents/docs/missions.md +119 -0
  62. package/dist/extensions/pi-subagents/docs/models.md +192 -0
  63. package/dist/extensions/pi-subagents/docs/observability.md +174 -0
  64. package/dist/extensions/pi-subagents/docs/tool-reference.md +343 -0
  65. package/dist/extensions/pi-subagents/docs/watchdog.md +176 -0
  66. package/dist/extensions/pi-subagents/docs/workflows.md +163 -0
  67. package/dist/extensions/pi-subagents/inspector-runner.mjs +11 -0
  68. package/dist/extensions/pi-subagents/package-lock.json +152 -194
  69. package/dist/extensions/pi-subagents/package.json +9 -10
  70. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  71. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  72. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  73. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  74. package/dist/extensions/pi-subagents/prompts/review-loop.md +12 -12
  75. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -17
  76. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +77 -72
  77. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +177 -69
  78. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +28 -8
  79. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +88 -66
  80. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +3 -3
  81. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +624 -0
  82. package/dist/extensions/pi-subagents/src/agents/agents.ts +29 -16
  83. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +7 -3
  84. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  85. package/dist/extensions/pi-subagents/src/agents/skills.ts +2 -9
  86. package/dist/extensions/pi-subagents/src/api/delegation.ts +1 -2
  87. package/dist/extensions/pi-subagents/src/api/project-panes.ts +30 -0
  88. package/dist/extensions/pi-subagents/src/extension/config.ts +56 -8
  89. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +2 -2
  90. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +51 -26
  91. package/dist/extensions/pi-subagents/src/extension/index.ts +119 -50
  92. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +72 -0
  93. package/dist/extensions/pi-subagents/src/extension/rpc.ts +15 -15
  94. package/dist/extensions/pi-subagents/src/extension/schemas.ts +58 -21
  95. package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +39 -0
  96. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +47 -25
  97. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +3 -3
  98. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +460 -64
  99. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +102 -4
  100. package/dist/extensions/pi-subagents/src/missions/actions.ts +77 -16
  101. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +162 -0
  102. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +65 -14
  103. package/dist/extensions/pi-subagents/src/missions/store.ts +147 -5
  104. package/dist/extensions/pi-subagents/src/missions/types.ts +58 -3
  105. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +256 -0
  106. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -8
  107. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +42 -0
  108. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +41 -7
  109. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +294 -134
  110. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +19 -3
  111. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +39 -6
  112. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +267 -0
  113. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +145 -60
  114. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +114 -6
  115. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +33 -0
  116. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +68 -0
  117. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +71 -0
  118. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +21 -2
  119. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +42 -14
  120. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +12 -24
  121. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +7 -5
  122. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +152 -68
  123. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -4
  124. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +146 -0
  125. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +21 -5
  126. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +85 -0
  127. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +43 -18
  128. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +20 -19
  129. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +3 -0
  130. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +55 -26
  131. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +137 -0
  132. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +798 -112
  133. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +187 -0
  134. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +165 -22
  135. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  136. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
  137. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +9 -5
  138. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +12 -2
  139. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +1 -0
  140. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +465 -116
  141. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +10 -10
  142. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +189 -32
  143. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -2
  144. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +3 -3
  145. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +128 -3
  146. package/dist/extensions/pi-subagents/src/shared/display-text.ts +100 -0
  147. package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +9 -0
  148. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +13 -0
  149. package/dist/extensions/pi-subagents/src/shared/formatters.ts +4 -6
  150. package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +51 -0
  151. package/dist/extensions/pi-subagents/src/shared/settings.ts +41 -11
  152. package/dist/extensions/pi-subagents/src/shared/types.ts +100 -3
  153. package/dist/extensions/pi-subagents/src/shared/utf8.ts +11 -0
  154. package/dist/extensions/pi-subagents/src/shared/utils.ts +43 -33
  155. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -14
  156. package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +1 -1
  157. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +14 -12
  158. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +176 -21
  159. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +12 -6
  160. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +52 -18
  161. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +36 -5
  162. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +30 -16
  163. package/dist/extensions/pi-subagents/src/tui/fleet.ts +151 -31
  164. package/dist/extensions/pi-subagents/src/tui/render.ts +201 -68
  165. package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +0 -6
  166. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +5 -4
  167. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +0 -1
  168. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +313 -25
  169. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +15 -15
  170. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
  171. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
  172. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
  173. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
  174. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
  175. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
  176. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
  177. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
  178. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
  179. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
  180. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
  181. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
  182. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
  183. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
  184. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
  185. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
  186. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
  187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
  188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
  189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
  190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
  191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
  192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
  193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
  194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
  195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
  196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
  197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
  198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
  199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
  200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
  236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
  237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
  238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
  239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
  240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
  241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
  242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
  243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
  244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
  245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
  246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
  247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
  248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
  249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
  250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
  252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
  253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
  254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
  255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
  256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
  257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
  258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
  259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
  260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
  261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
  262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
  263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
  264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
  265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
  266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
  267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
  272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
  276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
  278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
  281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
  282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
  285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
  286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
  289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
  290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
  293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
  294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
  301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
  304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
  306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
  307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
  308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
  309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
  310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
  311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
  312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
  313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
  314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
  315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
  316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
  317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
  318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
  319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
  320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
  321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
  322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
  323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
  324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
  325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
  326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
  327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
  328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
  329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
  330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
  331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
  332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
  333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
  334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
  335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
  336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
  337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
  338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
  339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
  340. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
  341. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
  342. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
  343. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
  344. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
  345. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +84 -0
  346. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/package.json +14 -0
  347. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +15 -15
  348. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +411 -300
  349. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +309 -123
  350. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +99 -56
  351. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +62 -16
  352. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +159 -108
  353. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -1
  354. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +4 -2
  355. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -56
  356. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +114 -45
  357. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +57 -19
  358. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +106 -106
  359. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +416 -106
  360. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +287 -17
  361. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +809 -87
  362. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +107 -20
  363. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
  364. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +26 -6
  365. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
  366. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  367. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +1 -1
  368. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +0 -8
  369. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +163 -58
  370. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  371. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  372. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +334 -248
  373. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +52 -52
  374. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
  375. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +99 -99
  376. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +160 -0
  377. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
  378. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +43 -1
  379. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +36 -10
  380. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +387 -30
  381. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +9 -9
  382. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +2 -2
  383. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +94 -40
  384. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +5 -3
  385. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +7 -7
  386. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +19 -19
  387. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +19 -19
  388. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
  389. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
  390. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
  391. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +35 -5
  392. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
  393. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +52 -52
  394. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +187 -0
  395. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +125 -71
  396. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +18 -3
  397. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +12 -12
  398. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +17 -23
  399. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  400. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
  401. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
  402. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +15 -0
  403. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +62 -44
  404. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +329 -37
  405. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
  406. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +37 -0
  407. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +69 -0
  408. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +79 -2
  409. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +102 -49
  410. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +98 -32
  411. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +5 -5
  412. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +116 -0
  413. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +55 -7
  414. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +289 -13
  415. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +28 -1
  416. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +2 -2
  417. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +11 -2
  418. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +84 -10
  419. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +122 -8
  420. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +8 -8
  421. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +24 -24
  422. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +16 -2
  423. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +7 -7
  424. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
  425. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
  426. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +26 -17
  427. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +405 -0
  428. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +357 -92
  429. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +22 -6
  430. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +6 -6
  431. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +43 -43
  432. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
  433. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +4 -4
  434. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +19 -19
  435. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +243 -0
  436. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +68 -168
  437. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +58 -15
  438. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +40 -0
  439. package/dist/extensions/pi-subagents/test/unit/reads-resolution.test.ts +66 -0
  440. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +47 -28
  441. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +12 -12
  442. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +57 -0
  443. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +107 -27
  444. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
  445. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +56 -50
  446. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +69 -28
  447. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
  448. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +59 -44
  449. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +424 -49
  450. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +23 -1
  451. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +32 -5
  452. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
  453. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +120 -91
  454. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
  455. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +85 -0
  456. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
  457. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +24 -0
  458. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +312 -22
  459. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +150 -15
  460. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -26
  461. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +73 -0
  462. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -25
  463. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +5 -5
  464. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +55 -6
  465. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
  466. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
  467. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
  468. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
  469. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +6 -6
  470. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +138 -11
  471. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
  472. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +113 -0
  473. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +7 -7
  474. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +6 -6
  475. package/dist/extensions/workflow/extension.ts +21 -19
  476. package/dist/extensions/workflow/modes.ts +65 -0
  477. package/dist/skills/pi-subagents/SKILL.md +20 -15
  478. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +77 -83
  479. package/dist/skills/pi-subagents/references/execution-controls.md +215 -106
  480. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +29 -9
  481. package/dist/skills/pi-subagents/references/prompting-and-roles.md +58 -43
  482. package/docs/plans/workflow-autoloop-reference.md +192 -0
  483. package/docs/plans/workflow-subagent-consolidation-planb.md +374 -0
  484. package/docs/plans/workflow-subagent-scripted-merge.md +406 -0
  485. package/package.json +1 -1
  486. package/dist/extensions/workflow/adapter.ts +0 -949
  487. package/dist/extensions/workflow/modes/loop.ts +0 -74
  488. package/dist/extensions/workflow/modes/prototype.ts +0 -170
  489. package/dist/extensions/workflow/modes/quicktype.ts +0 -150
  490. package/dist/extensions/workflow/modes/task.ts +0 -105
  491. package/dist/extensions/workflow/run-state.ts +0 -124
  492. package/dist/extensions/workflow/state-machine.ts +0 -484
  493. package/dist/extensions/workflow/validators.ts +0 -42
@@ -4,7 +4,7 @@ import * as path from "node:path";
4
4
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
5
5
  import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
6
6
  import { resolveAgentName, type AgentConfig, type AgentScope } from "../../agents/agents.ts";
7
- import { getArtifactsDir, getChainRunsDir } from "../../shared/artifacts.ts";
7
+ import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
8
8
  import { writeAtomicJson } from "../../shared/atomic-json.ts";
9
9
  import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
10
10
  import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
@@ -17,13 +17,18 @@ import {
17
17
  settleForegroundSchedulingOwner,
18
18
  updateForegroundChild,
19
19
  } from "./foreground-control.ts";
20
+ import { persistForegroundRunHistory, MAX_REMEMBERED_FOREGROUND_RUNS } from "./foreground-history.ts";
20
21
  import { resolveExecutionAgentScope } from "../../agents/agent-scope.ts";
21
22
  import { handleManagementAction } from "../../agents/agent-management.ts";
23
+ import { handleRefinementAction } from "../../agents/agent-refinements.ts";
22
24
  import { buildDoctorReport } from "../../extension/doctor.ts";
25
+ import { readSubagentGuide } from "../../extension/subagent-guide.ts";
26
+ import { normalizePublicSubagentExecution } from "../../extension/public-execution.ts";
23
27
  import { runSync } from "./execution.ts";
24
28
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
25
29
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
26
30
  import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelCandidate, type ParentModel } from "../shared/model-fallback.ts";
31
+ import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
27
32
  import type { ModelScopeConfig } from "../shared/model-scope.ts";
28
33
  import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
29
34
  import { recordRun } from "../shared/run-history.ts";
@@ -33,6 +38,8 @@ import {
33
38
  getStepAgents,
34
39
  isParallelStep,
35
40
  isDynamicParallelStep,
41
+ resolveChainPath,
42
+ resolveExistingReadPaths,
36
43
  resolveStepBehavior,
37
44
  suppressProgressForReadOnlyTask,
38
45
  taskDisallowsFileUpdates,
@@ -45,11 +52,12 @@ import {
45
52
  type StepOverrides,
46
53
  } from "../../shared/settings.ts";
47
54
  import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
48
- import { buildAsyncRunnerSteps, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable } from "../background/async-execution.ts";
49
- import type { ScheduledRunAction } from "../background/scheduled-runs.ts";
55
+ import { buildAsyncRunnerSteps, DEFAULT_ASYNC_TIMEOUT_MS, executeAsyncChain, executeAsyncSingle, formatAsyncStartedMessage, isAsyncAvailable, workflowAwaitedAsyncResultPath } from "../background/async-execution.ts";
56
+ import { updateActiveRunIndex } from "../background/active-run-index.ts";
57
+ import { isScheduledRunAction, type ScheduledRunAction } from "../background/scheduled-runs.ts";
50
58
  import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
51
59
  import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
52
- import { validateExecutionAcceptance } from "../shared/acceptance.ts";
60
+ import { normalizeGateAcceptance, validateExecutionAcceptance } from "../shared/acceptance.ts";
53
61
  import { createForkContextResolver, forkedChildRequiresThinkingOff } from "../../shared/fork-context.ts";
54
62
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
55
63
  import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
@@ -75,23 +83,35 @@ import {
75
83
  stripDetailsOutputsForIntercomReceipt,
76
84
  } from "../../intercom/result-intercom.ts";
77
85
  import { applySteeringRecoveryAgentConfig, buildRevivedAsyncTask, resolveAsyncResumeTarget, resolveAsyncRunLocation } from "../background/async-resume.ts";
78
- import { deliverCheckpointDecisionRequest, deliverInterruptRequest, requestAsyncSteer } from "../background/control-channel.ts";
79
- import { waitForSteeringAction } from "../background/steering.ts";
86
+ import { deliverCheckpointDecisionRequest, deliverInterruptRequest, readRevivalBriefs, requestAsyncSteer, type SteerDeliveryMode } from "../background/control-channel.ts";
87
+ import { updateSteeringTarget, waitForSteeringAction } from "../background/steering.ts";
80
88
  import { steerAsyncRun } from "./async-steering-action.ts";
89
+ import {
90
+ removeWorkflowForegroundSteeringRoute,
91
+ resolveWorkflowForegroundSteeringTarget,
92
+ steerWorkflowForegroundTarget,
93
+ workflowForegroundSteeringDir,
94
+ workflowForegroundSteeringLaunchOptions,
95
+ } from "./workflow-foreground-steering.ts";
81
96
  import { stopAsyncRun } from "./async-stop-action.ts";
97
+ import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
82
98
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
83
- import { resolveAsyncRootResultPath } from "../background/chain-root-attachment.ts";
99
+ import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
84
100
  import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
85
101
  import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
86
102
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
87
103
  import { inspectSubagentStatus } from "../background/run-status.ts";
88
104
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
89
105
  import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
90
- import { attachMissionToLaunchResult, prepareMissionLaunch, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
106
+ import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
107
+ import { updateMission } from "../../missions/store.ts";
108
+ import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
109
+ import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
91
110
  import { resolveAuthorityDecision } from "../../policy/authority.ts";
92
111
  import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
93
112
  import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
94
- import { runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
113
+ import { previewSimpleWorkflowRun, runWorkflowScript, WorkflowScriptError, type WorkflowScriptChildResult } from "../../workflows/scripted-workflow.ts";
114
+ import { renderWorkflowPrompt } from "../../shared/prompt-resources.ts";
95
115
  import { resolveWorkflowChatProgress, type WorkflowChatProgressProjection } from "../../workflows/chat-progress.ts";
96
116
  import {
97
117
  cleanupWorktrees,
@@ -144,7 +164,48 @@ import {
144
164
  wrapForkTask,
145
165
  } from "../../shared/types.ts";
146
166
 
147
- const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
167
+ const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete", "eject", "disable", "enable", "reset", "grant-spawn-budget", "watchdog.configure", "mission.create", "mission.update", "mission.resolve-decision", "mission.attach-run", "mission.close", "inspector.open", "inspector.close", "project.open", "project.close", "worktree.discard", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
168
+ const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "reject-checkpoint", "schedule.delete"]);
169
+
170
+ function editDistance(left: string, right: string): number {
171
+ const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
172
+ for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
173
+ let diagonal = previous[0]!;
174
+ previous[0] = leftIndex;
175
+ for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
176
+ const above = previous[rightIndex]!;
177
+ previous[rightIndex] = left[leftIndex - 1] === right[rightIndex - 1]
178
+ ? diagonal
179
+ : Math.min(diagonal, above, previous[rightIndex - 1]!) + 1;
180
+ diagonal = above;
181
+ }
182
+ }
183
+ return previous[right.length]!;
184
+ }
185
+
186
+ function hasSingleAdjacentTransposition(left: string, right: string): boolean {
187
+ if (left.length !== right.length) return false;
188
+ const mismatch = [...left].findIndex((character, index) => character !== right[index]);
189
+ return mismatch >= 0
190
+ && left[mismatch] === right[mismatch + 1]
191
+ && left[mismatch + 1] === right[mismatch]
192
+ && left.slice(mismatch + 2) === right.slice(mismatch + 2);
193
+ }
194
+
195
+ export function unknownSubagentActionMessage(action: string): string {
196
+ const requested = action.toLowerCase();
197
+ const suggestion = SUBAGENT_ACTIONS.find((candidate) => {
198
+ const distance = editDistance(requested, candidate);
199
+ const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
200
+ if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate)) return distance === 1 && requested.length >= candidate.length - 1;
201
+ return closeMatch;
202
+ });
203
+ const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
204
+ const validActions = `Valid: ${SUBAGENT_ACTIONS.join(", ")}.`;
205
+ return suggestion
206
+ ? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
207
+ : `Unknown action: ${action}. ${nextStep} ${validActions}`;
208
+ }
148
209
 
149
210
  type UndefinedOmitted<T extends object> = {
150
211
  [K in keyof T as undefined extends T[K] ? never : K]: T[K];
@@ -203,20 +264,25 @@ export interface SubagentParamsLike {
203
264
  index?: number;
204
265
  view?: "fleet" | "transcript";
205
266
  lines?: number;
267
+ topic?: string;
206
268
  chainName?: string;
207
269
  config?: unknown;
208
270
  name?: string;
209
271
  type?: string;
210
272
  agent?: string;
211
273
  task?: string;
274
+ /** Retained async child run id. Valid only on workflow runs.run items. */
275
+ resume?: string;
212
276
  message?: string;
213
277
  steeringRecovery?: boolean;
278
+ mode?: SteerDeliveryMode;
214
279
  workflowScript?: string;
215
280
  chatProgress?: "auto" | "off" | "terminal" | "milestones" | "live-card";
216
281
  step?: ChainStep;
217
282
  /** Internal workflow ownership metadata; not part of the public schema. */
218
283
  workflowParentRunId?: string;
219
284
  workflowKey?: string;
285
+ workflowChildAsyncId?: string;
220
286
  suppressRoutineResultIntercom?: boolean;
221
287
  /** Internal durable-run compatibility fields. Public callers must use workflowScript. */
222
288
  chain?: ChainStep[];
@@ -248,11 +314,14 @@ export interface SubagentParamsLike {
248
314
  focus?: boolean;
249
315
  skill?: string | string[] | boolean;
250
316
  output?: string | boolean;
317
+ /** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
318
+ reads?: string[] | false;
251
319
  outputMode?: "inline" | "file-only";
252
320
  outputSchema?: JsonSchemaObject;
253
321
  agentScope?: unknown;
254
322
  chainDir?: string;
255
323
  acceptance?: AcceptanceInput;
324
+ gate?: string;
256
325
  agentContract?: AgentContract;
257
326
  schedule?: string;
258
327
  scheduleName?: string;
@@ -344,6 +413,7 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
344
413
  function removeForegroundControlIfIdle(state: SubagentState, runId: string): boolean {
345
414
  const control = state.foregroundControls.get(runId);
346
415
  if (control && (!foregroundSchedulingSettled(control) || (control.activeChildren?.size ?? 0) > 0)) return false;
416
+ if (control) removeWorkflowForegroundSteeringRoute(control);
347
417
  state.foregroundControls.delete(runId);
348
418
  if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
349
419
  return true;
@@ -461,7 +531,7 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
461
531
 
462
532
  function trimRememberedForegroundRuns(state: SubagentState): void {
463
533
  if (!state.foregroundRuns) return;
464
- while (state.foregroundRuns.size > 50) {
534
+ while (state.foregroundRuns.size > MAX_REMEMBERED_FOREGROUND_RUNS) {
465
535
  const oldestTerminal = [...state.foregroundRuns.values()]
466
536
  .filter((run) => !run.children.some((child) => child.status === "detached"))
467
537
  .sort((left, right) => left.updatedAt - right.updatedAt)[0];
@@ -470,6 +540,14 @@ function trimRememberedForegroundRuns(state: SubagentState): void {
470
540
  }
471
541
  }
472
542
 
543
+ function persistRememberedForegroundRuns(state: SubagentState): void {
544
+ try {
545
+ persistForegroundRunHistory(state);
546
+ } catch (error) {
547
+ console.error("Failed to persist foreground run history:", error);
548
+ }
549
+ }
550
+
473
551
  function foregroundChildActivityFromProgress(progress: SingleResult["progress"] | undefined) {
474
552
  return {
475
553
  ...(progress?.activityState ? { activityState: progress.activityState } : {}),
@@ -536,6 +614,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
536
614
  }),
537
615
  });
538
616
  trimRememberedForegroundRuns(state);
617
+ persistRememberedForegroundRuns(state);
539
618
  }
540
619
 
541
620
  function applyControlEventToRememberedForegroundRun(state: SubagentState, event: ControlEvent): void {
@@ -611,6 +690,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
611
690
  ...(input.result.capabilityAudit ? { capabilityAudit: input.result.capabilityAudit } : {}),
612
691
  });
613
692
  trimRememberedForegroundRuns(state);
693
+ persistRememberedForegroundRuns(state);
614
694
  const output = getSingleResultOutput(input.result).trim();
615
695
  const success = terminalStatus === "completed";
616
696
  const summary = !success && input.result.error
@@ -790,6 +870,20 @@ function getAsyncInterruptTarget(
790
870
  return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : undefined;
791
871
  }
792
872
 
873
+ function isStaleExtensionContextError(error: unknown): boolean {
874
+ if (!(error instanceof Error)) return false;
875
+ return /extension ctx is stale|stale after session replacement or reload/i.test(error.message);
876
+ }
877
+
878
+ function emitAdvisoryControlEvent(pi: ExtensionAPI, channel: string, payload: unknown): void {
879
+ try {
880
+ pi.events.emit(channel, payload);
881
+ } catch (error) {
882
+ if (isStaleExtensionContextError(error)) return;
883
+ throw error;
884
+ }
885
+ }
886
+
793
887
  function emitControlNotification(input: {
794
888
  pi: ExtensionAPI;
795
889
  controlConfig: ResolvedControlConfig;
@@ -807,10 +901,10 @@ function emitControlNotification(input: {
807
901
  noticeText: formatControlNoticeMessage(input.event, childIntercomTarget),
808
902
  };
809
903
  if (input.controlConfig.notifyChannels.includes("event")) {
810
- input.pi.events.emit(SUBAGENT_CONTROL_EVENT, payload);
904
+ emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_EVENT, payload);
811
905
  }
812
906
  if (input.event.type !== "active_long_running" && input.controlConfig.notifyChannels.includes("intercom") && input.intercomBridge.active && input.intercomBridge.orchestratorTarget) {
813
- input.pi.events.emit(SUBAGENT_CONTROL_INTERCOM_EVENT, {
907
+ emitAdvisoryControlEvent(input.pi, SUBAGENT_CONTROL_INTERCOM_EVENT, {
814
908
  ...payload,
815
909
  to: input.intercomBridge.orchestratorTarget,
816
910
  message: formatControlIntercomMessage(input.event, childIntercomTarget),
@@ -842,8 +936,15 @@ function interruptAsyncRun(
842
936
  details: { mode: "management", results: [] },
843
937
  };
844
938
  }
939
+ if (status.mode === "workflow") {
940
+ return {
941
+ content: [{ type: "text", text: `Interrupt is unsupported for async workflow ${target.asyncId}; use stop instead.` }],
942
+ isError: true,
943
+ details: { mode: "management", results: [] },
944
+ };
945
+ }
845
946
  try {
846
- deliverInterruptRequest(omitUndefinedProperties({ asyncDir: target.asyncDir, pid: status.pid, kill, source: "interrupt-action" }));
947
+ deliverInterruptRequest({ asyncDir: target.asyncDir, source: "interrupt-action" });
847
948
  const tracked = state.asyncJobs.get(target.asyncId);
848
949
  if (tracked) {
849
950
  delete tracked.activityState;
@@ -1152,7 +1253,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
1152
1253
  const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
1153
1254
  if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return undefined;
1154
1255
  try {
1155
- deliverInterruptRequest({ asyncDir, pid, source: "nested-interrupt" });
1256
+ deliverInterruptRequest({ asyncDir, source: "nested-interrupt" });
1156
1257
  return { content: [{ type: "text", text: `Interrupt requested for nested async run ${run.id}.` }], details: { mode: "management", results: [] } };
1157
1258
  } catch (error) {
1158
1259
  const message = error instanceof Error ? error.message : String(error);
@@ -1160,7 +1261,7 @@ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nes
1160
1261
  }
1161
1262
  }
1162
1263
 
1163
- async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
1264
+ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details> | undefined> {
1164
1265
  const run = input.target.match.run;
1165
1266
  const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
1166
1267
  if (!asyncDir) return undefined;
@@ -1182,6 +1283,7 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
1182
1283
  try {
1183
1284
  requestAsyncSteer(asyncDir, {
1184
1285
  message: input.message,
1286
+ mode: input.mode,
1185
1287
  ...(effectiveTargetIndex !== undefined ? { targetIndex: effectiveTargetIndex } : { targetIndexes }),
1186
1288
  source: "nested-steer",
1187
1289
  id: requestId,
@@ -1191,12 +1293,12 @@ async function directNestedAsyncSteer(input: { target: ResolvedSubagentRunId & {
1191
1293
  }
1192
1294
  const targets = targetIndexes.map((index) => ({ index, state: steps[index]?.status === "pending" ? "scheduled" as const : "pending" as const }));
1193
1295
  if (targets.every((target) => target.state === "scheduled")) {
1194
- const scheduled = { requestId, state: "scheduled" as const, sourceRunId: run.id, targets };
1296
+ const scheduled = { requestId, state: "scheduled" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
1195
1297
  return { content: [{ type: "text", text: `Steering scheduled for nested async run ${run.id} (request ${requestId}).` }], details: { mode: "management", results: [], steering: scheduled } };
1196
1298
  }
1197
1299
  const waited = await waitForSteeringAction(omitUndefinedProperties({ asyncDir, sourceRunId: run.id, requestId, timeoutMs: 3_000, signal: input.signal }));
1198
- const result = waited ?? { requestId, state: "pending" as const, sourceRunId: run.id, targets };
1199
- const stateText = result.state === "delivered" ? "delivered" : result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : "pending";
1300
+ const result = waited ?? { requestId, state: "pending" as const, deliveryStatus: "queued" as const, sourceRunId: run.id, targets };
1301
+ const stateText = result.state === "failed" ? "failed" : result.state === "partial" ? "partial" : result.deliveryStatus === "queued" ? "queued" : result.state === "delivered" ? "delivered" : "pending";
1200
1302
  return { content: [{ type: "text", text: `Steering ${stateText} for nested async run ${run.id} (request ${requestId}).` }], ...(result.state === "failed" || result.state === "partial" ? { isError: true } : {}), details: { mode: "management", results: [], steering: result } };
1201
1303
  }
1202
1304
 
@@ -1219,7 +1321,7 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
1219
1321
  return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
1220
1322
  }
1221
1323
 
1222
- async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
1324
+ async function steerNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string; mode?: SteerDeliveryMode; index?: number; signal?: AbortSignal }): Promise<AgentToolResult<Details>> {
1223
1325
  const run = input.target.match.run;
1224
1326
  if (run.state !== "running" && run.state !== "queued") return { content: [{ type: "text", text: `Nested run ${run.id} is ${run.state} and cannot be steered.` }], isError: true, details: { mode: "management", results: [] } };
1225
1327
  const direct = await directNestedAsyncSteer(input);
@@ -1243,6 +1345,7 @@ async function resumeAsyncRun(input: {
1243
1345
  deps: ExecutorDeps;
1244
1346
  parentModel?: ParentModel;
1245
1347
  absoluteDeadlineAt?: number;
1348
+ signal?: AbortSignal;
1246
1349
  }): Promise<AgentToolResult<Details>> {
1247
1350
  const followUp = (input.params.message ?? input.params.task ?? "").trim();
1248
1351
  const attachChain = (input.params.chain?.length ?? 0) > 0 ? input.params.chain as ChainStep[] : undefined;
@@ -1358,7 +1461,7 @@ async function resumeAsyncRun(input: {
1358
1461
  inheritProjectContext: recoveryDescriptor.inheritProjectContext,
1359
1462
  inheritSkills: recoveryDescriptor.inheritSkills,
1360
1463
  source: "project",
1361
- filePath: recoveryDescriptor.agentFilePath ?? path.join(recoveryDescriptor.cwd, ".pi-subagents-recovery-agent"),
1464
+ filePath: recoveryDescriptor.agentFilePath ?? path.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
1362
1465
  } : undefined);
1363
1466
  if (!agentConfig) {
1364
1467
  return {
@@ -1449,6 +1552,9 @@ async function resumeAsyncRun(input: {
1449
1552
  return { content: [{ type: "text", text: formatAsyncStartedMessage(lines.join("\n"), input.ctx.hasUI) }], details: result.details };
1450
1553
  }
1451
1554
 
1555
+ const sourceAsyncDir = target.source === "async" ? target.asyncDir : undefined;
1556
+ const queuedBriefs = sourceAsyncDir ? readRevivalBriefs(sourceAsyncDir) : [];
1557
+ const effectiveFollowUp = [...queuedBriefs.map(({ request }) => request.message), followUp].filter(Boolean).join("\n\n");
1452
1558
  const revivalSessionFile = target.sessionFile;
1453
1559
  if (!revivalSessionFile) {
1454
1560
  return { content: [{ type: "text", text: `Async child '${target.runId}' has no persisted session file to resume.` }], isError: true, details: { mode: "management", results: [] } };
@@ -1461,8 +1567,8 @@ async function resumeAsyncRun(input: {
1461
1567
  const parentModel = input.parentModel;
1462
1568
  const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1463
1569
  agent: target.agent,
1464
- task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], followUp),
1465
- goal: followUp,
1570
+ task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
1571
+ goal: effectiveFollowUp,
1466
1572
  agentConfig: recoveryAgentConfig,
1467
1573
  ctx: compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
1468
1574
  pi: input.deps.pi,
@@ -1512,10 +1618,72 @@ async function resumeAsyncRun(input: {
1512
1618
  ...(input.params.turnBudget !== undefined ? { turnBudget: input.params.turnBudget } : {}),
1513
1619
  ...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
1514
1620
  capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
1621
+ parentWorkflowRunId: input.params.workflowParentRunId,
1622
+ workflowKey: input.params.workflowKey,
1515
1623
  }));
1516
1624
  if (result.isError) return result;
1625
+ for (const brief of queuedBriefs) fs.rmSync(brief.path, { force: true });
1626
+ if (queuedBriefs.length > 0 && sourceAsyncDir) {
1627
+ const sourceStatus = readStatus(sourceAsyncDir);
1628
+ if (sourceStatus?.steering) {
1629
+ for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
1630
+ writeAtomicJson(path.join(sourceAsyncDir, "status.json"), sourceStatus);
1631
+ }
1632
+ }
1517
1633
 
1518
1634
  const revivedId = result.details.asyncId ?? runId;
1635
+ if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
1636
+ const asyncDir = result.details.asyncDir;
1637
+ const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
1638
+ const stopOnAbort = () => { stopAsyncRun(input.deps.state, revivedId, input.deps.kill, { asyncDir, resolvedId: revivedId }); };
1639
+ if (input.signal?.aborted) stopOnAbort();
1640
+ else input.signal?.addEventListener("abort", stopOnAbort, { once: true });
1641
+ let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
1642
+ try {
1643
+ completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 });
1644
+ } finally {
1645
+ input.signal?.removeEventListener("abort", stopOnAbort);
1646
+ }
1647
+ fs.rmSync(resultPath, { force: true });
1648
+ const totalCost = completed.totalCost;
1649
+ const childResult: SingleResult = {
1650
+ index: 0,
1651
+ agent: completed.agent,
1652
+ task: effectiveFollowUp,
1653
+ exitCode: completed.exitCode,
1654
+ usage: {
1655
+ input: totalCost?.inputTokens ?? 0,
1656
+ output: totalCost?.outputTokens ?? 0,
1657
+ cacheRead: 0,
1658
+ cacheWrite: 0,
1659
+ cost: totalCost?.costUsd ?? 0,
1660
+ turns: 0,
1661
+ },
1662
+ finalOutput: completed.output,
1663
+ outputState: completed.output.trim() ? "present" : "absent",
1664
+ ...(completed.error ? { error: completed.error } : {}),
1665
+ ...(completed.timedOut ? { timedOut: true } : {}),
1666
+ ...(completed.stopped ? { stopped: true } : {}),
1667
+ ...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
1668
+ ...(completed.model ? { model: completed.model } : {}),
1669
+ ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
1670
+ ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
1671
+ ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
1672
+ ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
1673
+ ...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
1674
+ ...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
1675
+ };
1676
+ return {
1677
+ content: [{ type: "text", text: completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
1678
+ ...(completed.success ? {} : { isError: true }),
1679
+ details: {
1680
+ ...result.details,
1681
+ runId: revivedId,
1682
+ results: [childResult],
1683
+ ...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
1684
+ },
1685
+ };
1686
+ }
1519
1687
  const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
1520
1688
  const sourceLabel = target.source;
1521
1689
  const lines = [
@@ -1631,6 +1799,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
1631
1799
  mode: SubagentRunMode;
1632
1800
  details: Details;
1633
1801
  nestedChildren?: NestedRunSummary[];
1802
+ preserveDetailsOutputs?: boolean;
1634
1803
  }): Promise<{ text: string; details: Details } | null> {
1635
1804
  const payload = await emitForegroundResultIntercom({
1636
1805
  pi: input.pi,
@@ -1645,7 +1814,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
1645
1814
  if (!payload) return null;
1646
1815
  return {
1647
1816
  text: formatSubagentResultReceipt({ mode: input.mode, runId: input.runId, payload }),
1648
- details: stripDetailsOutputsForIntercomReceipt(input.details),
1817
+ details: input.preserveDetailsOutputs ? input.details : stripDetailsOutputsForIntercomReceipt(input.details),
1649
1818
  };
1650
1819
  }
1651
1820
 
@@ -1842,6 +2011,19 @@ function getRequestedModeLabel(params: SubagentParamsLike): Details["mode"] {
1842
2011
  return "single";
1843
2012
  }
1844
2013
 
2014
+ function formatStatusTargetLabel(params: Pick<SubagentParamsLike, "dir" | "index" | "view">, targetRunId: string | undefined): string {
2015
+ let target: string;
2016
+ if (targetRunId) {
2017
+ target = `run ${targetRunId}`;
2018
+ } else if (params.dir) {
2019
+ target = `dir ${params.dir}`;
2020
+ } else {
2021
+ target = params.view === "transcript" ? "active run" : "active runs";
2022
+ }
2023
+ if (params.view !== "transcript") return `Status target: ${target}`;
2024
+ return `Transcript target: ${target}${params.index !== undefined ? ` · child ${params.index}` : ""}`;
2025
+ }
2026
+
1845
2027
  interface AgentDefaultContextPolicy {
1846
2028
  params: SubagentParamsLike;
1847
2029
  contextForAgent(agentName: string): ContextMode;
@@ -1925,7 +2107,17 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
1925
2107
 
1926
2108
  export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
1927
2109
 
1928
- function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
2110
+ // Async single-agent runs also need a wall-clock backstop: a child whose bash
2111
+ // tool blocks forever (e.g. a background process inheriting the terminal with
2112
+ // no bash `timeout` arg) would otherwise hang the parent indefinitely with
2113
+ // zero signal. Same generous default as foreground; explicit timeoutMs/
2114
+ // maxRuntimeMs and agent-level defaultTimeoutMs remain authoritative.
2115
+ //
2116
+ // Deliberately NOT applied at the workflow level: async scripted workflows
2117
+ // stay unbounded as a whole, while each runner child has its own deadline.
2118
+ export { DEFAULT_ASYNC_TIMEOUT_MS };
2119
+
2120
+ export function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?: number): { timeoutMs?: number; error?: string } {
1929
2121
  const rawTimeout = params.timeoutMs;
1930
2122
  const rawMaxRuntime = params.maxRuntimeMs;
1931
2123
  if (rawTimeout === undefined && rawMaxRuntime === undefined) {
@@ -1944,6 +2136,21 @@ function resolveForegroundTimeout(params: SubagentParamsLike, defaultTimeoutMs?:
1944
2136
  return timeoutMs === undefined ? {} : { timeoutMs };
1945
2137
  }
1946
2138
 
2139
+ /**
2140
+ * Resolve the effective launch timeout for a single-agent run, applying the
2141
+ * async/foreground default when neither the caller nor the agent set one.
2142
+ *
2143
+ * The async default is deliberately applied only to plain single-agent
2144
+ * launches. Composite launches keep their top-level execution unbounded when
2145
+ * no timeout is set; their runner children resolve separate deadlines.
2146
+ * Exported so the executor wiring is directly testable.
2147
+ */
2148
+ export function resolveSingleAgentLaunchTimeout(params: SubagentParamsLike, async: boolean): { timeoutMs?: number; error?: string } {
2149
+ const isComposite = (params.chain?.length ?? 0) > 0 || (params.tasks?.length ?? 0) > 0 || params.workflowScript !== undefined;
2150
+ const defaultTimeoutMs = !async ? DEFAULT_FOREGROUND_TIMEOUT_MS : isComposite ? undefined : DEFAULT_ASYNC_TIMEOUT_MS;
2151
+ return resolveForegroundTimeout(params, defaultTimeoutMs);
2152
+ }
2153
+
1947
2154
  function resolveToolBudget(
1948
2155
  raw: unknown,
1949
2156
  label = "toolBudget",
@@ -2353,7 +2560,8 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2353
2560
  details: { mode: "single" as const, results: [] },
2354
2561
  };
2355
2562
  }
2356
- const id = randomUUID();
2563
+ const requestedWorkflowChildAsyncId = typeof params.workflowChildAsyncId === "string" ? params.workflowChildAsyncId.trim() : "";
2564
+ const id = requestedWorkflowChildAsyncId && path.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID();
2357
2565
  const parentModel = data.parentModel;
2358
2566
  const asyncCtx = compactOptional<Parameters<typeof executeAsyncSingle>[1]["ctx"]>({
2359
2567
  pi: deps.pi,
@@ -2511,6 +2719,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
2511
2719
  skills,
2512
2720
  output: effectiveOutput,
2513
2721
  outputMode: effectiveOutputMode,
2722
+ ...(params.reads !== undefined ? { reads: params.reads } : {}),
2514
2723
  outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
2515
2724
  modelOverride,
2516
2725
  thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
@@ -2982,6 +3191,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
2982
3191
  const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskText, compactOptional<Parameters<typeof runSync>[4]>({
2983
3192
  permissions: input.permissions,
2984
3193
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
3194
+ ...workflowForegroundSteeringLaunchOptions(input.foregroundControl, index),
2985
3195
  context: input.contextPolicy.contextForAgent(task.agent),
2986
3196
  cwd: taskCwd,
2987
3197
  signal: input.signal,
@@ -3300,10 +3510,11 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3300
3510
  );
3301
3511
  if (errorResult) return errorResult;
3302
3512
 
3303
- let worktreeFinalized = false;
3513
+ let worktreeCleanupHandled = false;
3514
+ let pendingHandoff: Details["parallelHandoff"];
3304
3515
  try {
3305
3516
  if (worktreeSetup) {
3306
- writePendingParallelHandoff({
3517
+ pendingHandoff = writePendingParallelHandoff({
3307
3518
  manifestPath: parallelHandoffPath(artifactsDir, runId),
3308
3519
  runId,
3309
3520
  mode: "parallel",
@@ -3406,10 +3617,13 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3406
3617
  updateForegroundNestedProjection(foregroundControl);
3407
3618
  attachRootChildrenToSteps(runId, results, foregroundControl.nestedChildren);
3408
3619
  }
3620
+ const detached = results.find((result) => result.detached);
3409
3621
  let handoff: ReturnType<typeof finalizeParallelWorktreeHandoff> | undefined;
3410
3622
  if (worktreeSetup) {
3411
- worktreeFinalized = true;
3412
- handoff = finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
3623
+ worktreeCleanupHandled = true;
3624
+ handoff = detached
3625
+ ? { suffix: pendingHandoff ? formatParallelHandoffReference(pendingHandoff) : "", reference: pendingHandoff }
3626
+ : finalizeParallelWorktreeHandoff({ worktreeSetup, artifactsDir, runId, cwd: effectiveCwd, tasks, results });
3413
3627
  }
3414
3628
  const interrupted = results.find((result) => result.interrupted);
3415
3629
  const totalCost = sumResultsCost(results);
@@ -3432,11 +3646,10 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3432
3646
  details,
3433
3647
  };
3434
3648
  }
3435
- const detachedIndex = results.findIndex((result) => result.detached);
3436
- const detached = detachedIndex >= 0 ? results[detachedIndex] : undefined;
3437
3649
  if (detached) {
3650
+ const handoffSuffix = handoff?.suffix ? `\n\n${handoff.suffix}` : "";
3438
3651
  return {
3439
- content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.` }],
3652
+ content: [{ type: "text", text: `Parallel run detached for intercom coordination (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.${handoffSuffix}` }],
3440
3653
  details,
3441
3654
  };
3442
3655
  }
@@ -3450,6 +3663,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3450
3663
  runId,
3451
3664
  mode: "parallel",
3452
3665
  details,
3666
+ ...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
3453
3667
  ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
3454
3668
  });
3455
3669
  if (intercomReceipt) {
@@ -3484,7 +3698,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
3484
3698
  details,
3485
3699
  };
3486
3700
  } finally {
3487
- if (worktreeSetup && !worktreeFinalized) cleanupWorktrees(worktreeSetup);
3701
+ if (worktreeSetup && !worktreeCleanupHandled) cleanupWorktrees(worktreeSetup);
3488
3702
  }
3489
3703
  }
3490
3704
 
@@ -3535,6 +3749,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3535
3749
  data.modelScope === undefined ? {} : { scope: data.modelScope },
3536
3750
  );
3537
3751
  let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
3752
+ let readsOverride: string[] | false | undefined = params.reads;
3538
3753
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
3539
3754
  let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
3540
3755
  const effectiveOutputMode = params.outputMode ?? "inline";
@@ -3572,6 +3787,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3572
3787
  if (override?.model !== undefined) modelOverride = resolveEffectiveSubagentModel(override.model, agentConfig.model, parentModel, availableModels, currentProvider, data.modelScope === undefined ? {} : { scope: data.modelScope });
3573
3788
  if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
3574
3789
  if (override?.skills !== undefined) skillOverride = override.skills;
3790
+ if (override?.reads !== undefined) readsOverride = override.reads;
3575
3791
 
3576
3792
  if (result.runInBackground) {
3577
3793
  if (!isAsyncAvailable()) {
@@ -3611,6 +3827,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3611
3827
  skills: skillOverride === false ? [] : skillOverride,
3612
3828
  output: effectiveOutput,
3613
3829
  outputMode: effectiveOutputMode,
3830
+ ...(readsOverride !== undefined ? { reads: readsOverride } : {}),
3614
3831
  outputBaseDir: resolveSingleRunOutputBaseDir(deps, artifactsDir, id),
3615
3832
  modelOverride,
3616
3833
  thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride),
@@ -3647,6 +3864,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3647
3864
  const structuredRuntime = params.outputSchema
3648
3865
  ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path.join(artifactsDir, "structured-output", runId) : undefined)
3649
3866
  : undefined;
3867
+ // Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
3868
+ // absolute paths pass through; relative paths resolve against the child cwd.
3869
+ const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
3870
+ const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, effectiveCwd) : [];
3871
+ const readsInstruction = readPaths.length > 0
3872
+ ? `[Read from: ${readPaths.join(", ")}]\n\n`
3873
+ : "";
3874
+ task = readsInstruction + task;
3650
3875
  task = injectSingleOutputInstruction(task, outputPath, agentConfig);
3651
3876
 
3652
3877
  let effectiveSkills: string[] | undefined;
@@ -3688,6 +3913,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3688
3913
  r = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
3689
3914
  permissions: deps.config.permissions,
3690
3915
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3916
+ ...workflowForegroundSteeringLaunchOptions(foregroundControl, 0),
3691
3917
  context: data.contextPolicy.contextForAgent(params.agent!),
3692
3918
  cwd: effectiveCwd,
3693
3919
  signal,
@@ -3810,6 +4036,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3810
4036
  runId,
3811
4037
  mode: "single",
3812
4038
  details,
4039
+ ...(params.workflowParentRunId !== undefined ? { preserveDetailsOutputs: true } : {}),
3813
4040
  ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
3814
4041
  });
3815
4042
  if (intercomReceipt) {
@@ -3865,8 +4092,70 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
3865
4092
  };
3866
4093
  }
3867
4094
 
4095
+ const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionFile?: string }) => void>();
4096
+
4097
+ function recordMissionWorkflowChild(
4098
+ binding: MissionLaunchBinding | undefined,
4099
+ workflowRunId: string,
4100
+ key: string,
4101
+ update: Omit<MissionWorkflowChildUpdate, "workflowRunId" | "key">,
4102
+ ): void {
4103
+ if (!binding) return;
4104
+ try {
4105
+ updateMission(binding.location, binding.missionId, { upsertWorkflowChildren: [{ workflowRunId, key, ...update }] });
4106
+ } catch (error) {
4107
+ console.warn(`[pi-subagents] Failed to record mission workflow child '${key}': ${error instanceof Error ? error.message : String(error)}`);
4108
+ }
4109
+ }
4110
+
4111
+ export function missionWorkflowChildStatus(result: AgentToolResult<Details>): string {
4112
+ const childResults = result.details.results;
4113
+ if (childResults.some((child) => child.detached || child.interrupted)) return "paused";
4114
+ if (result.isError === true || childResults.some((child) => child.exitCode !== 0)) return "failed";
4115
+ if (childResults.length === 0 && (result.details.asyncId || result.details.asyncDir)) return "running";
4116
+ return "completed";
4117
+ }
4118
+
4119
+ export async function runMissionWorkflowChild(
4120
+ binding: MissionLaunchBinding | undefined,
4121
+ workflowRunId: string,
4122
+ key: string,
4123
+ phase: string | undefined,
4124
+ run: () => Promise<AgentToolResult<Details>>,
4125
+ ): Promise<AgentToolResult<Details>> {
4126
+ try {
4127
+ return await run();
4128
+ } catch (error) {
4129
+ recordMissionWorkflowChild(binding, workflowRunId, key, {
4130
+ status: "failed",
4131
+ completedAt: new Date().toISOString(),
4132
+ heartbeat: { status: "failed", ...(phase ? { phase } : {}), message: error instanceof Error ? error.message : String(error) },
4133
+ });
4134
+ throw error;
4135
+ }
4136
+ }
4137
+
4138
+ export function bindMissionWorkflowChildAsyncLaunch(
4139
+ params: SubagentParamsLike,
4140
+ binding: MissionLaunchBinding | undefined,
4141
+ asyncByDefault: boolean,
4142
+ asyncId: string = randomUUID(),
4143
+ ): SubagentParamsLike {
4144
+ const requestedAsync = params.async ?? asyncByDefault;
4145
+ if (!binding || !requestedAsync || params.clarify === true) return params;
4146
+ const id = asyncId.trim();
4147
+ if (!id || path.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
4148
+ writeMissionAsyncBinding(path.join(DIRS.async, id), binding);
4149
+ return { ...params, workflowChildAsyncId: id };
4150
+ }
4151
+
3868
4152
  function workflowChildResult(key: string, result: AgentToolResult<Details>): WorkflowScriptChildResult {
3869
- const output = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
4153
+ const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
4154
+ const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
4155
+ ? result.details.results[0].finalOutput
4156
+ : receiptOutput;
4157
+ const detached = result.details.results.some((child) => child.detached);
4158
+ const ok = result.isError !== true && !detached;
3870
4159
  const artifactPaths = new Set<string>();
3871
4160
  if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
3872
4161
  if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
@@ -3876,18 +4165,75 @@ function workflowChildResult(key: string, result: AgentToolResult<Details>): Wor
3876
4165
  if (child.sessionFile) artifactPaths.add(child.sessionFile);
3877
4166
  }
3878
4167
  const structured = result.details.results.map((child) => child.structuredOutput).filter((value) => value !== undefined);
4168
+ const resolvedAgents = [...new Set(result.details.results.map((child) => child.agent).filter((agent): agent is string => Boolean(agent)))];
3879
4169
  return {
3880
4170
  key,
3881
- ok: result.isError !== true,
4171
+ ok,
4172
+ ...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
3882
4173
  ...(result.details.runId || result.details.asyncId ? { runId: result.details.runId ?? result.details.asyncId } : {}),
3883
4174
  output,
3884
- ...(result.isError === true ? { error: output || "Child run failed." } : {}),
4175
+ ...(!ok ? { error: receiptOutput || output || "Child run failed." } : {}),
3885
4176
  ...(structured.length === 1 ? { structuredOutput: structured[0] } : structured.length > 1 ? { structuredOutput: structured } : {}),
3886
4177
  artifactPaths: [...artifactPaths],
3887
4178
  results: result.details.results,
3888
4179
  };
3889
4180
  }
3890
4181
 
4182
+ export function prepareWorkflowLaunchParams(
4183
+ workflowDefaults: SubagentParamsLike,
4184
+ childParams: Record<string, unknown>,
4185
+ parentWorkflowRunId: string,
4186
+ workflowKey: string,
4187
+ options: { missionDetached?: boolean; suppressRoutineResultIntercom?: boolean } = {},
4188
+ ): SubagentParamsLike {
4189
+ if (typeof childParams.resume === "string") {
4190
+ if (childParams.gate !== undefined || workflowDefaults.gate !== undefined) {
4191
+ throw new Error("gate is not supported with retained resume; resume uses the retained child contract.");
4192
+ }
4193
+ const timeoutMs = childParams.timeoutMs ?? childParams.maxRuntimeMs ?? workflowDefaults.timeoutMs ?? workflowDefaults.maxRuntimeMs;
4194
+ const turnBudget = childParams.turnBudget ?? workflowDefaults.turnBudget;
4195
+ const toolBudget = childParams.toolBudget ?? workflowDefaults.toolBudget;
4196
+ return {
4197
+ action: "resume",
4198
+ id: childParams.resume.trim(),
4199
+ message: typeof childParams.task === "string" ? childParams.task.trim() : "",
4200
+ workflowParentRunId: parentWorkflowRunId,
4201
+ workflowKey,
4202
+ ...(options.missionDetached ? { mission: false } : {}),
4203
+ ...(timeoutMs !== undefined ? { timeoutMs: timeoutMs as number } : {}),
4204
+ ...(turnBudget !== undefined ? { turnBudget: turnBudget as TurnBudgetConfig } : {}),
4205
+ ...(toolBudget !== undefined ? { toolBudget: toolBudget as ToolBudgetConfig } : {}),
4206
+ };
4207
+ }
4208
+ const launchParams = {
4209
+ ...workflowDefaults,
4210
+ async: false,
4211
+ ...childParams,
4212
+ ...(options.missionDetached ? { mission: false } : {}),
4213
+ workflowParentRunId: parentWorkflowRunId,
4214
+ workflowKey,
4215
+ ...(options.suppressRoutineResultIntercom ? { suppressRoutineResultIntercom: true } : {}),
4216
+ } as SubagentParamsLike;
4217
+ const normalizedGate = normalizeGateParams(launchParams);
4218
+ if (!normalizedGate.ok) throw new Error(normalizedGate.error);
4219
+ return prepareWorkflowChildParams(normalizedGate.params);
4220
+ }
4221
+
4222
+ type GateParamsNormalizationResult =
4223
+ | { ok: true; params: SubagentParamsLike }
4224
+ | { ok: false; error: string };
4225
+
4226
+ function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizationResult {
4227
+ if (params.gate !== undefined && params.action === "resume") {
4228
+ return { ok: false, error: "gate is not supported with action='resume'; resume uses the retained child contract." };
4229
+ }
4230
+ const normalized = normalizeGateAcceptance(params.gate, params.acceptance);
4231
+ if (!normalized.ok) return { ok: false, error: normalized.error };
4232
+ if (params.gate === undefined) return { ok: true, params };
4233
+ const { gate: _gate, ...rest } = params;
4234
+ return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
4235
+ }
4236
+
3891
4237
  function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsLike {
3892
4238
  if (params.worktree !== true || !params.agent) return params;
3893
4239
  const {
@@ -3901,6 +4247,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
3901
4247
  acceptance,
3902
4248
  agentContract,
3903
4249
  toolBudget,
4250
+ reads,
3904
4251
  ...runParams
3905
4252
  } = params;
3906
4253
  return {
@@ -3912,6 +4259,7 @@ function prepareWorkflowChildParams(params: SubagentParamsLike): SubagentParamsL
3912
4259
  ...(model !== undefined ? { model } : {}),
3913
4260
  ...(skill !== undefined ? { skill } : {}),
3914
4261
  ...(output !== undefined ? { output } : {}),
4262
+ ...(reads !== undefined ? { reads } : {}),
3915
4263
  ...(outputMode !== undefined ? { outputMode } : {}),
3916
4264
  ...(outputSchema !== undefined ? { outputSchema } : {}),
3917
4265
  ...(acceptance !== undefined ? { acceptance } : {}),
@@ -3943,21 +4291,6 @@ function workflowChatProgressUpdate(
3943
4291
  };
3944
4292
  }
3945
4293
 
3946
- function omitExecutionModeActionAlias(params: SubagentParamsLike): SubagentParamsLike {
3947
- const action = params.action?.toLowerCase();
3948
- if (action === "single" && (params.agent !== undefined || params.task !== undefined)) {
3949
- const rest = { ...params };
3950
- delete rest.action;
3951
- return rest;
3952
- }
3953
- if ((action === "parallel" || action === "tasks") && (params.tasks?.length ?? 0) > 0) {
3954
- const rest = { ...params };
3955
- delete rest.action;
3956
- return rest;
3957
- }
3958
- return params;
3959
- }
3960
-
3961
4294
  function createScheduledOwnerState(source: SubagentState, ownerSessionId: string, ctx: ExtensionContext): SubagentState {
3962
4295
  const ownerSpawns = source.subagentSpawns?.sessionId === ownerSessionId
3963
4296
  ? {
@@ -3996,6 +4329,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
3996
4329
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
3997
4330
  ctx: ExtensionContext,
3998
4331
  ) => Promise<AgentToolResult<Details>>;
4332
+ /** Public/model-facing execution boundary. Internal direct launch primitives use execute or executeDelegated. */
4333
+ executePublic: (
4334
+ id: string,
4335
+ params: SubagentParamsLike,
4336
+ signal: AbortSignal,
4337
+ onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
4338
+ ctx: ExtensionContext,
4339
+ ) => Promise<AgentToolResult<Details>>;
3999
4340
  /**
4000
4341
  * Correlated extension-to-extension delegation owns its request IDs and
4001
4342
  * cancellation controllers, so independent requests may execute concurrently.
@@ -4018,6 +4359,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4018
4359
  } {
4019
4360
  const delegatedThinkingOverrides = new WeakMap<object, AgentConfig["thinking"]>();
4020
4361
  const delegatedZeroToolBudgets = new WeakSet<object>();
4362
+ const warnedArtifactPackageDirs = new Set<string>();
4021
4363
  const scheduledOwnerExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
4022
4364
  const execute = async (
4023
4365
  _id: string,
@@ -4027,30 +4369,77 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4027
4369
  ctx: ExtensionContext,
4028
4370
  preserveActiveSession = false,
4029
4371
  ): Promise<AgentToolResult<Details>> => {
4372
+ const workflowLaunchObserver = workflowLaunchObservers.get(params);
4030
4373
  const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
4031
4374
  const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
4032
4375
  if (!preserveActiveSession) deps.state.baseCwd = ctx.cwd;
4033
4376
  deps.state.foregroundRuns ??= new Map();
4034
4377
  deps.state.foregroundControls ??= new Map();
4035
4378
  deps.state.lastForegroundControlId ??= null;
4036
- const requestParams = omitExecutionModeActionAlias(params);
4037
- if (requestParams.workflowScript !== undefined) {
4038
- const invalidMode = requestParams.action !== undefined || requestParams.agent !== undefined || requestParams.step !== undefined || requestParams.tasks !== undefined || requestParams.chain !== undefined;
4039
- if (invalidMode) {
4040
- return { content: [{ type: "text", text: "workflowScript is its own execution mode; do not combine it with action, agent, step, tasks, or chain." }], isError: true, details: { mode: "workflow", results: [] } };
4041
- }
4042
- if (requestParams.clarify === true) {
4043
- return { content: [{ type: "text", text: "workflowScript does not support clarify UI." }], isError: true, details: { mode: "workflow", results: [] } };
4044
- }
4045
- const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? DEFAULT_FOREGROUND_TIMEOUT_MS;
4379
+ const normalizedGate = normalizeGateParams(params);
4380
+ if (!normalizedGate.ok) return buildRequestedModeError(params, normalizedGate.error);
4381
+ const requestParams = normalizedGate.params;
4382
+ const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
4383
+ if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
4384
+ const parentCwd = ctx.cwd;
4385
+ const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
4046
4386
  const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
4047
4387
  if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
4048
- const workflowCwd = resolveRequestedCwd(ctx.cwd, requestParams.cwd);
4049
- const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd: ctx.cwd, workflowCwd, background: requestParams.async !== false });
4388
+ const workflowCwd = resolveRequestedCwd(parentCwd, requestParams.cwd);
4389
+ const workflowPrompts = { render: (ref: string, vars?: unknown) => renderWorkflowPrompt(ref, vars, workflowCwd) };
4390
+ const chatProgressResult = resolveWorkflowChatProgress({ requested: requestParams.chatProgress, parentCwd, workflowCwd, background: requestParams.async !== false });
4050
4391
  if (chatProgressResult.error) return { content: [{ type: "text", text: chatProgressResult.error }], isError: true, details: { mode: "workflow", results: [] } };
4051
4392
  const chatProgress = chatProgressResult.projection!;
4393
+ const explicitMission = requestParams.missionId !== undefined || requestParams.mission !== undefined;
4394
+ const autoMission = !explicitMission;
4395
+ const workflowPreview = autoMission ? previewSimpleWorkflowRun(requestParams.workflowScript) : undefined;
4396
+ const previewTask = workflowPreview?.task?.trim() || undefined;
4397
+ const previewAgent = workflowPreview?.agent?.trim() || undefined;
4398
+ const scriptFirstLine = requestParams.workflowScript.split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "Workflow";
4399
+ const boundedScriptPreview = scriptFirstLine.length > 100 ? `${scriptFirstLine.slice(0, 97)}...` : scriptFirstLine;
4400
+ const derivedObjective = previewTask || (previewAgent ? `Workflow: ${previewAgent}` : boundedScriptPreview);
4401
+ let missionBinding: MissionLaunchBinding | undefined;
4402
+ let missionWarning: string | undefined;
4403
+ try {
4404
+ missionBinding = prepareMissionLaunch({
4405
+ params: autoMission ? { ...requestParams, task: derivedObjective } : requestParams,
4406
+ projectRoot: workflowCwd,
4407
+ ...(deps.config.missions ? { config: deps.config.missions } : {}),
4408
+ ownerSessionId: resolveCurrentSessionId(ctx.sessionManager),
4409
+ });
4410
+ } catch (error) {
4411
+ if (explicitMission) return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "workflow", results: [] } };
4412
+ missionWarning = `Mission tracking unavailable: ${error instanceof Error ? error.message : String(error)}`;
4413
+ }
4414
+ let shouldPatchMissionObjective = autoMission && previewTask === undefined && missionBinding !== undefined;
4415
+ const patchMissionObjective = (task: unknown): void => {
4416
+ if (!shouldPatchMissionObjective || !missionBinding || typeof task !== "string" || !task.trim()) return;
4417
+ shouldPatchMissionObjective = false;
4418
+ const objective = task.trim();
4419
+ const firstLine = objective.split(/\r?\n/, 1)[0]?.trim() || objective;
4420
+ const title = firstLine.length > 100 ? `${firstLine.slice(0, 97)}...` : firstLine;
4421
+ try {
4422
+ updateMission(missionBinding.location, missionBinding.missionId, { title, objective });
4423
+ } catch (error) {
4424
+ console.warn(`[pi-subagents] Failed to update automatic mission objective: ${error instanceof Error ? error.message : String(error)}`);
4425
+ }
4426
+ };
4427
+ const detachWorkflowChildMissions = autoMission || missionBinding !== undefined || requestParams.mission === false;
4428
+ const workflowState = missionBinding ? createMissionWorkflowState(missionBinding.location, missionBinding.missionId) : undefined;
4429
+ const attachWorkflowMission = (result: AgentToolResult<Details>): AgentToolResult<Details> => {
4430
+ if (!missionBinding) return missionWarning ? { ...result, details: { ...result.details, missionWarning } } : result;
4431
+ try {
4432
+ return attachMissionToLaunchResult({ binding: missionBinding, result });
4433
+ } catch (error) {
4434
+ const warning = `Mission tracking unavailable after launch: ${error instanceof Error ? error.message : String(error)}`;
4435
+ return explicitMission
4436
+ ? { ...result, isError: true, content: [...result.content, { type: "text", text: warning }], details: { ...result.details, missionWarning: warning } }
4437
+ : { ...result, details: { ...result.details, missionWarning: warning } };
4438
+ }
4439
+ };
4052
4440
  if (requestParams.async !== false) {
4053
- const workflowRunId = _id;
4441
+ const toolCallId = _id;
4442
+ const workflowRunId = randomUUID();
4054
4443
  const asyncDir = path.join(DIRS.async, workflowRunId);
4055
4444
  const resultPath = path.join(DIRS.results, `${workflowRunId}.json`);
4056
4445
  const statusPath = path.join(asyncDir, "status.json");
@@ -4064,26 +4453,39 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4064
4453
  deps.state.workflowControllers.set(workflowRunId, controller);
4065
4454
  let status: AsyncStatus = {
4066
4455
  runId: workflowRunId,
4456
+ toolCallId,
4067
4457
  sessionId: currentSessionId ?? undefined,
4068
4458
  mode: "workflow",
4069
4459
  state: "running",
4070
4460
  startedAt,
4071
4461
  lastUpdate: startedAt,
4072
- deadlineAt: startedAt + timeout,
4073
- timeoutMs: timeout,
4074
- cwd: ctx.cwd,
4462
+ ...(timeout !== undefined ? { deadlineAt: startedAt + timeout, timeoutMs: timeout } : {}),
4463
+ cwd: workflowCwd,
4075
4464
  pid: process.pid,
4076
4465
  steps: [],
4077
4466
  workflow: { trace: [], emits: [], console: [] },
4078
4467
  };
4079
4468
  const appendWorkflowEvent = (event: Record<string, unknown>) => fs.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}\n`, "utf-8");
4469
+ let indexedState: AsyncStatus["state"] | undefined;
4080
4470
  const persist = () => {
4081
4471
  status.lastUpdate = Date.now();
4082
4472
  writeAtomicJson(statusPath, status);
4473
+ if (indexedState !== status.state) {
4474
+ updateActiveRunIndex(asyncDir, status.state);
4475
+ indexedState = status.state;
4476
+ }
4083
4477
  const job = deps.state.asyncJobs.get(workflowRunId);
4084
4478
  if (job) {
4085
4479
  job.status = status.state;
4086
4480
  job.updatedAt = status.lastUpdate;
4481
+ job.activityState = status.activityState;
4482
+ job.lastActivityAt = status.lastActivityAt;
4483
+ job.currentTool = status.currentTool;
4484
+ job.currentToolStartedAt = status.currentToolStartedAt;
4485
+ job.currentPath = status.currentPath;
4486
+ job.turnCount = status.turnCount;
4487
+ job.toolCount = status.toolCount;
4488
+ job.currentStep = status.currentStep;
4087
4489
  if (status.steps) {
4088
4490
  job.steps = status.steps.map((step, index) => ({ ...step, index }));
4089
4491
  job.agents = status.steps.map((step) => step.agent);
@@ -4094,7 +4496,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4094
4496
  job.workflow = status.workflow;
4095
4497
  }
4096
4498
  };
4097
- const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: ctx.cwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, timeoutMs: timeout, deadlineAt: startedAt + timeout, workflow: status.workflow };
4499
+ const projectWorkflowActivity = () => {
4500
+ const steps = status.steps ?? [];
4501
+ const runningSteps = steps.filter((step) => step.status === "running");
4502
+ const lastActivityAt = runningSteps.reduce<number | undefined>((latest, step) => step.lastActivityAt === undefined ? latest : Math.max(latest ?? step.lastActivityAt, step.lastActivityAt), undefined);
4503
+ const activeToolStep = runningSteps
4504
+ .filter((step) => step.currentTool)
4505
+ .sort((left, right) => (left.lastActivityAt ?? 0) - (right.lastActivityAt ?? 0))
4506
+ .at(-1);
4507
+ status.activityState = runningSteps.some((step) => step.activityState === "needs_attention")
4508
+ ? "needs_attention"
4509
+ : runningSteps.some((step) => step.activityState === "active_long_running") ? "active_long_running" : undefined;
4510
+ status.lastActivityAt = lastActivityAt;
4511
+ status.currentTool = activeToolStep?.currentTool;
4512
+ status.currentToolStartedAt = activeToolStep?.currentToolStartedAt;
4513
+ status.currentPath = activeToolStep?.currentPath;
4514
+ const turnCounts = steps.flatMap((step) => step.turnCount === undefined ? [] : [step.turnCount]);
4515
+ const toolCounts = steps.flatMap((step) => step.toolCount === undefined ? [] : [step.toolCount]);
4516
+ status.turnCount = turnCounts.length > 0 ? turnCounts.reduce((total, count) => total + count, 0) : undefined;
4517
+ status.toolCount = toolCounts.length > 0 ? toolCounts.reduce((total, count) => total + count, 0) : undefined;
4518
+ status.currentStep = runningSteps.length === 1 ? steps.indexOf(runningSteps[0]!) : undefined;
4519
+ };
4520
+ const workflowJob: AsyncJobState = { asyncId: workflowRunId, asyncDir, cwd: workflowCwd, status: "running", sessionId: currentSessionId ?? undefined, mode: "workflow", agents: [], steps: [], startedAt, updatedAt: startedAt, ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}), workflow: status.workflow };
4098
4521
  deps.state.asyncJobs.set(workflowRunId, workflowJob);
4099
4522
  deps.state.fleetJobs ??= new Map();
4100
4523
  deps.state.fleetJobs.set(workflowRunId, workflowJob);
@@ -4103,23 +4526,43 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4103
4526
  const { workflowScript, async: _workflowAsync, chatProgress: _chatProgress, ...workflowRequest } = requestParams;
4104
4527
  void Promise.resolve().then(async () => {
4105
4528
  const workflowResults: SingleResult[] = [];
4106
- const { action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, ...workflowChildDefaults } = workflowRequest;
4529
+ const { action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = workflowRequest;
4530
+ const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
4531
+ let projectedTraceLength = 0;
4532
+ let projectedTraceTail: NonNullable<Details["workflow"]>["trace"][number] | undefined;
4107
4533
  const updateTrace = (trace: NonNullable<Details["workflow"]>["trace"]) => {
4108
4534
  status.workflow = { ...(status.workflow ?? { emits: [], console: [] }), trace };
4109
- for (const entry of trace.filter((candidate) => candidate.operation === "run")) {
4110
- const existing = status.steps?.find((step) => step.workflowKey === entry.key);
4535
+ const rebuild = trace.length < projectedTraceLength
4536
+ || (projectedTraceLength > 0 && trace[projectedTraceLength - 1] !== projectedTraceTail);
4537
+ if (rebuild) {
4538
+ workflowSteps.clear();
4539
+ for (const step of status.steps ?? []) {
4540
+ if (step.workflowKey) workflowSteps.set(step.workflowKey, step);
4541
+ }
4542
+ projectedTraceLength = 0;
4543
+ }
4544
+ for (let index = projectedTraceLength; index < trace.length; index += 1) {
4545
+ const entry = trace[index]!;
4546
+ if (entry.operation !== "run") continue;
4547
+ const existing = workflowSteps.get(entry.key);
4111
4548
  if (entry.state === "reused" && existing) continue;
4112
4549
  const mapped = entry.state === "started" || entry.state === "reused" ? "running" : entry.state === "completed" ? "completed" : "failed";
4113
4550
  if (existing) {
4114
4551
  existing.status = mapped;
4552
+ if (entry.agent) existing.agent = entry.agent;
4115
4553
  if (entry.error === undefined) delete existing.error;
4116
4554
  else existing.error = entry.error;
4117
4555
  if (entry.durationMs === undefined) delete existing.durationMs;
4118
4556
  else existing.durationMs = entry.durationMs;
4119
4557
  } else {
4120
- status.steps?.push({ agent: entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped });
4558
+ const step: NonNullable<AsyncStatus["steps"]>[number] = { agent: entry.agent ?? entry.key, label: entry.key, workflowKey: entry.key, parentWorkflowRunId: workflowRunId, status: mapped, startedAt: Date.now() };
4559
+ status.steps?.push(step);
4560
+ workflowSteps.set(entry.key, step);
4121
4561
  }
4122
4562
  }
4563
+ projectedTraceLength = trace.length;
4564
+ projectedTraceTail = trace.at(-1);
4565
+ projectWorkflowActivity();
4123
4566
  persist();
4124
4567
  appendWorkflowEvent({ type: "subagent.workflow.trace", trace });
4125
4568
  };
@@ -4128,6 +4571,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4128
4571
  script: workflowScript,
4129
4572
  timeoutMs: timeout,
4130
4573
  signal: controller.signal,
4574
+ prompts: workflowPrompts,
4575
+ ...(workflowState ? { state: workflowState } : {}),
4131
4576
  onTrace: updateTrace,
4132
4577
  onEmit: (emits) => {
4133
4578
  // Each emit is validated at the host boundary in runWorkflowScript before onEmit fires.
@@ -4139,10 +4584,71 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4139
4584
  if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4140
4585
  const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4141
4586
  if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4142
- const childRequest = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: workflowRunId, workflowKey: key } as SubagentParamsLike);
4143
- const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
4587
+ patchMissionObjective(childParams.task);
4588
+ const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
4589
+ const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
4590
+ recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
4591
+ status: "running",
4592
+ ...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
4593
+ ...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
4594
+ ...(childLabel ? { label: childLabel } : {}),
4595
+ ...(childPhase ? { phase: childPhase } : {}),
4596
+ heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
4597
+ });
4598
+ const result = await runMissionWorkflowChild(missionBinding, workflowRunId, key, childPhase, () => {
4599
+ const childRequest = bindMissionWorkflowChildAsyncLaunch(
4600
+ prepareWorkflowLaunchParams(workflowChildDefaults, childParams, workflowRunId, key, { missionDetached: detachWorkflowChildMissions }),
4601
+ missionBinding,
4602
+ deps.asyncByDefault,
4603
+ );
4604
+ workflowLaunchObservers.set(childRequest, (launch) => {
4605
+ const step = status.steps?.find((candidate) => candidate.workflowKey === key);
4606
+ if (step) {
4607
+ step.agent = launch.agent;
4608
+ step.sessionFile = launch.sessionFile;
4609
+ persist();
4610
+ }
4611
+ recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
4612
+ });
4613
+ return execute(randomUUID(), childRequest, workflowSignal, (update) => {
4614
+ const progress = update.details.progress?.[0];
4615
+ const step = status.steps?.find((candidate) => candidate.workflowKey === key);
4616
+ if (!progress || !step) return;
4617
+ step.status = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
4618
+ step.activityState = progress.activityState;
4619
+ step.lastActivityAt = progress.lastActivityAt;
4620
+ step.currentTool = progress.currentTool;
4621
+ step.currentToolArgs = progress.currentToolArgs;
4622
+ step.currentToolStartedAt = progress.currentToolStartedAt;
4623
+ step.currentPath = progress.currentPath;
4624
+ step.recentTools = progress.recentTools.map((tool) => ({ ...tool }));
4625
+ step.recentOutput = [...progress.recentOutput];
4626
+ step.turnCount = progress.turnCount;
4627
+ step.toolCount = progress.toolCount;
4628
+ step.model = progress.model;
4629
+ step.thinking = progress.thinking;
4630
+ step.error = progress.error;
4631
+ projectWorkflowActivity();
4632
+ persist();
4633
+ recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
4634
+ status: step.status,
4635
+ heartbeat: { status: step.status, ...(childPhase ? { phase: childPhase } : {}) },
4636
+ });
4637
+ }, ctx, preserveActiveSession);
4638
+ });
4144
4639
  workflowResults.push(...result.details.results);
4640
+ if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
4145
4641
  const child = workflowChildResult(key, result);
4642
+ const childStatus = missionWorkflowChildStatus(result);
4643
+ recordMissionWorkflowChild(missionBinding, workflowRunId, key, {
4644
+ status: childStatus,
4645
+ ...(child.runId ? { runId: child.runId } : {}),
4646
+ ...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
4647
+ ...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
4648
+ artifactPaths: child.artifactPaths,
4649
+ ...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
4650
+ heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
4651
+ });
4146
4652
  if (result.details.asyncId) {
4147
4653
  const childJob = deps.state.asyncJobs.get(result.details.asyncId);
4148
4654
  if (childJob) { childJob.parentWorkflowRunId = workflowRunId; childJob.workflowKey = key; }
@@ -4156,26 +4662,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4156
4662
  const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).`;
4157
4663
  const workflowUsage = sumResultsUsage(workflowResults);
4158
4664
  status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
4665
+ writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4159
4666
  persist();
4160
4667
  appendWorkflowEvent({ type: "subagent.workflow.completed", state: "complete" });
4161
- writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary, output: summary, results: workflow.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4162
4668
  } catch (error) {
4163
4669
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
4164
4670
  const stopped = controller.signal.aborted;
4165
4671
  status = compactOptional<AsyncStatus>({ ...status, state: stopped ? "stopped" : "failed", stopped: stopped || undefined, error: error instanceof Error ? error.message : String(error), endedAt: Date.now(), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console } });
4672
+ writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: workflowCwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4166
4673
  persist();
4167
4674
  appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, error: status.error });
4168
- writeAtomicJson(resultPath, { id: workflowRunId, runId: workflowRunId, agent: "workflow", mode: "workflow", success: false, state: status.state, summary: status.error, error: status.error, stopped: status.stopped, results: partial.children.map((child) => ({ agent: child.key, output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, asyncDir, cwd: ctx.cwd, sessionId: currentSessionId, timestamp: Date.now(), durationMs: Date.now() - startedAt });
4169
4675
  } finally {
4170
4676
  deps.state.workflowControllers?.delete(workflowRunId);
4171
4677
  }
4172
4678
  });
4173
- return {
4679
+ return attachWorkflowMission({
4174
4680
  content: [{ type: "text", text: formatAsyncStartedMessage(`Async workflow [${workflowRunId}]`, ctx.hasUI === true) }],
4175
- details: { mode: "workflow", runId: workflowRunId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
4176
- };
4681
+ details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], chatProgress },
4682
+ });
4177
4683
  }
4178
- const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, ...workflowChildDefaults } = requestParams;
4684
+ const { workflowScript: _workflowScript, action: _action, agent: _agent, task: _task, resume: _resume, tasks: _tasks, chain: _chain, concurrency: _concurrency, async: _async, foregroundOnly: _foregroundOnly, clarify: _clarify, timeoutMs: _timeoutMs, maxRuntimeMs: _maxRuntimeMs, usageBudget: _usageBudget, chatProgress: _chatProgress, missionId: _missionId, mission: _mission, ...workflowChildDefaults } = requestParams;
4179
4685
  const workflowResults: SingleResult[] = [];
4180
4686
  let liveWorkflow: NonNullable<Details["workflow"]> = { trace: [], emits: [], console: [] };
4181
4687
  const sendWorkflowProgress = () => {
@@ -4187,6 +4693,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4187
4693
  script: requestParams.workflowScript,
4188
4694
  timeoutMs: timeout,
4189
4695
  signal,
4696
+ prompts: workflowPrompts,
4697
+ ...(workflowState ? { state: workflowState } : {}),
4190
4698
  onTrace: (trace) => {
4191
4699
  liveWorkflow = { ...liveWorkflow, trace };
4192
4700
  sendWorkflowProgress();
@@ -4199,10 +4707,52 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4199
4707
  if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."));
4200
4708
  const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
4201
4709
  if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)));
4202
- const childRequest = prepareWorkflowChildParams({ ...workflowChildDefaults, ...childParams, workflowParentRunId: _id, workflowKey: key, suppressRoutineResultIntercom: chatProgress.mode === "live-card" } as SubagentParamsLike);
4203
- const result = await execute(randomUUID(), childRequest, workflowSignal, undefined, ctx, preserveActiveSession);
4710
+ patchMissionObjective(childParams.task);
4711
+ const childPhase = typeof childParams.phase === "string" && childParams.phase.trim() ? childParams.phase.trim() : undefined;
4712
+ const childLabel = typeof childParams.label === "string" && childParams.label.trim() ? childParams.label.trim() : undefined;
4713
+ recordMissionWorkflowChild(missionBinding, _id, key, {
4714
+ status: "running",
4715
+ ...(typeof childParams.agent === "string" && childParams.agent.trim() ? { agent: childParams.agent.trim() } : {}),
4716
+ ...(typeof childParams.task === "string" && childParams.task.trim() ? { task: childParams.task.trim() } : {}),
4717
+ ...(childLabel ? { label: childLabel } : {}),
4718
+ ...(childPhase ? { phase: childPhase } : {}),
4719
+ heartbeat: { status: "running", ...(childPhase ? { phase: childPhase } : {}) },
4720
+ });
4721
+ const result = await runMissionWorkflowChild(missionBinding, _id, key, childPhase, () => {
4722
+ const childRequest = bindMissionWorkflowChildAsyncLaunch(
4723
+ prepareWorkflowLaunchParams(workflowChildDefaults, childParams, _id, key, { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card" }),
4724
+ missionBinding,
4725
+ deps.asyncByDefault,
4726
+ );
4727
+ workflowLaunchObservers.set(childRequest, (launch) => recordMissionWorkflowChild(missionBinding, _id, key, {
4728
+ status: "running",
4729
+ agent: launch.agent,
4730
+ ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}),
4731
+ }));
4732
+ return execute(randomUUID(), childRequest, workflowSignal, (update) => {
4733
+ const progress = update.details.progress?.[0];
4734
+ if (!progress) return;
4735
+ const progressStatus = progress.status === "completed" ? "completed" : progress.status === "failed" ? "failed" : "running";
4736
+ recordMissionWorkflowChild(missionBinding, _id, key, {
4737
+ status: progressStatus,
4738
+ heartbeat: { status: progressStatus, ...(childPhase ? { phase: childPhase } : {}) },
4739
+ });
4740
+ }, ctx, preserveActiveSession);
4741
+ });
4204
4742
  workflowResults.push(...result.details.results);
4205
- return workflowChildResult(key, result);
4743
+ if (result.details.asyncDir && missionBinding) writeMissionAsyncBinding(result.details.asyncDir, missionBinding);
4744
+ const child = workflowChildResult(key, result);
4745
+ const childStatus = missionWorkflowChildStatus(result);
4746
+ recordMissionWorkflowChild(missionBinding, _id, key, {
4747
+ status: childStatus,
4748
+ ...(child.runId ? { runId: child.runId } : {}),
4749
+ ...(result.details.results[0]?.agent ? { agent: result.details.results[0].agent } : {}),
4750
+ ...(result.details.results[0]?.sessionFile ? { sessionPath: result.details.results[0].sessionFile } : {}),
4751
+ artifactPaths: child.artifactPaths,
4752
+ ...(["completed", "failed"].includes(childStatus) ? { completedAt: new Date().toISOString() } : {}),
4753
+ heartbeat: { status: childStatus, ...(childPhase ? { phase: childPhase } : {}) },
4754
+ });
4755
+ return child;
4206
4756
  },
4207
4757
  status: async (keyOrRunId, workflowSignal) => workflowChildResult(keyOrRunId, await execute(randomUUID(), { action: "status", id: keyOrRunId }, workflowSignal, undefined, ctx, preserveActiveSession)),
4208
4758
  });
@@ -4211,10 +4761,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4211
4761
  if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
4212
4762
  if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
4213
4763
  if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
4214
- return {
4764
+ return attachWorkflowMission({
4215
4765
  content: [{ type: "text", text: sections.join("\n\n") }],
4216
4766
  details: compactOptional<Details>({ mode: "workflow", runId: _id, results: workflow.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: workflow.trace, emits: workflow.emits, console: workflow.console }, chatProgress }),
4217
- };
4767
+ });
4218
4768
  } catch (error) {
4219
4769
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
4220
4770
  const text = error instanceof Error ? error.message : String(error);
@@ -4223,11 +4773,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4223
4773
  if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
4224
4774
  if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
4225
4775
  if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
4226
- return {
4776
+ return attachWorkflowMission({
4227
4777
  content: [{ type: "text", text: sections.join("\n\n") }],
4228
4778
  isError: true,
4229
4779
  details: compactOptional<Details>({ mode: "workflow", runId: _id, results: partial.children.flatMap((child) => (child.results ?? []) as SingleResult[]), totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: partial.trace, emits: partial.emits, console: partial.console }, chatProgress }),
4230
- };
4780
+ });
4231
4781
  }
4232
4782
  }
4233
4783
  const directParams = prepareWorkflowChildParams(requestParams);
@@ -4244,7 +4794,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4244
4794
  ? normalizeParentModel(ctx.model)
4245
4795
  : rememberParentModel(deps.state, requestSessionId, ctx.model);
4246
4796
  } catch (error) {
4247
- if (action?.toLowerCase() !== "doctor") throw error;
4797
+ if (action?.toLowerCase() !== "doctor" && action?.toLowerCase() !== "guide") throw error;
4248
4798
  requestParentModel = normalizeParentModel(ctx.model);
4249
4799
  }
4250
4800
  if (action) {
@@ -4331,6 +4881,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4331
4881
  }
4332
4882
  return handleWatchdogToolAction(action, paramsWithResolvedCwd, ctx, deps.watchdog);
4333
4883
  }
4884
+ if (action === "refine" || action === "refine.show" || action === "refine.rollback") {
4885
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4886
+ return {
4887
+ content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
4888
+ isError: true,
4889
+ details: { mode: "management", results: [] },
4890
+ };
4891
+ }
4892
+ return handleRefinementAction(action, paramsWithResolvedCwd, {
4893
+ cwd: requestCwd,
4894
+ state: deps.state,
4895
+ signal,
4896
+ launchProposalChild: (task, outputSchema, proposalSignal) => execute(randomUUID(), {
4897
+ agent: "reviewer",
4898
+ task,
4899
+ context: "fresh",
4900
+ async: false,
4901
+ artifacts: false,
4902
+ outputSchema,
4903
+ toolBudget: { hard: 1, block: ["write", "edit", "bash"] },
4904
+ }, proposalSignal, undefined, ctx, true),
4905
+ });
4906
+ }
4334
4907
  if (action === "grant-spawn-budget") {
4335
4908
  if (deps.allowMutatingManagementActions === false || !ctx.hasUI) {
4336
4909
  return {
@@ -4403,6 +4976,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4403
4976
  details: { mode: "management", results: [], spawnBudget: granted.snapshot },
4404
4977
  };
4405
4978
  }
4979
+ if (action === "guide") {
4980
+ try {
4981
+ return {
4982
+ content: [{ type: "text", text: readSubagentGuide(paramsWithResolvedCwd.topic) }],
4983
+ details: { mode: "management", results: [] },
4984
+ };
4985
+ } catch (error) {
4986
+ return {
4987
+ content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
4988
+ isError: true,
4989
+ details: { mode: "management", results: [] },
4990
+ };
4991
+ }
4992
+ }
4993
+ if (action === "children.list") {
4994
+ deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
4995
+ const children = listRetainedChildren(DIRS.async, deps.state.currentSessionId);
4996
+ return {
4997
+ content: [{ type: "text", text: formatRetainedChildren(children) }],
4998
+ details: { mode: "management", results: [] },
4999
+ };
5000
+ }
4406
5001
  if (action === "doctor") {
4407
5002
  let currentSessionFile: string | null = null;
4408
5003
  let currentSessionId = deps.state.currentSessionId;
@@ -4441,13 +5036,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4441
5036
  }
4442
5037
  if (action === "status") {
4443
5038
  if (!preserveActiveSession) deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
4444
- const withBudget = (result: AgentToolResult<Details>) => withSpawnBudgetStatus(
4445
- result,
4446
- deps.state,
4447
- deps.config,
4448
- deps.state.currentSessionId,
4449
- );
4450
5039
  const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
5040
+ const hasDirectoryTarget = Boolean(paramsWithResolvedCwd.dir);
5041
+ const targetLabel = formatStatusTargetLabel(paramsWithResolvedCwd, targetRunId);
5042
+ const withBudget = (result: AgentToolResult<Details>) => {
5043
+ const budgeted = withSpawnBudgetStatus(result, deps.state, deps.config, deps.state.currentSessionId);
5044
+ return {
5045
+ ...budgeted,
5046
+ content: budgeted.content.map((item, index) => index === 0 && item.type === "text"
5047
+ ? { ...item, text: `${targetLabel}\n${item.text}` }
5048
+ : item),
5049
+ };
5050
+ };
4451
5051
  const nestedScope = nestedResolutionScopeForExecutor(deps);
4452
5052
  const sessionRoots = trustedSessionRootsForStatus(ctx, deps);
4453
5053
  if (paramsWithResolvedCwd.view === "fleet") {
@@ -4472,7 +5072,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4472
5072
  const message = error instanceof Error ? error.message : String(error);
4473
5073
  return withBudget({ content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } });
4474
5074
  }
4475
- } else {
5075
+ } else if (!hasDirectoryTarget) {
4476
5076
  const foreground = getForegroundControl(deps.state, undefined);
4477
5077
  if (foreground && paramsWithResolvedCwd.view !== "transcript") return withBudget(foregroundStatusResult(foreground));
4478
5078
  if (foreground && paramsWithResolvedCwd.view === "transcript") {
@@ -4540,7 +5140,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4540
5140
  }
4541
5141
  }
4542
5142
  if (action === "resume") {
4543
- return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
5143
+ return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
4544
5144
  }
4545
5145
  if (action === "steer") {
4546
5146
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
@@ -4551,6 +5151,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4551
5151
  try {
4552
5152
  const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
4553
5153
  const runId = location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
5154
+ const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
5155
+ if (directoryStatus?.mode === "workflow") {
5156
+ const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: directoryStatus.runId || runId, asyncDirRoot: DIRS.async });
5157
+ if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
5158
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
5159
+ }
4554
5160
  if (location.asyncDir) {
4555
5161
  const unsupported = externalRunnerControlError(location.asyncDir, "steer");
4556
5162
  if (unsupported) return unsupported;
@@ -4559,6 +5165,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4559
5165
  state: deps.state,
4560
5166
  runId,
4561
5167
  message,
5168
+ mode: paramsWithResolvedCwd.mode,
4562
5169
  index: paramsWithResolvedCwd.index,
4563
5170
  kill: deps.kill,
4564
5171
  location,
@@ -4584,9 +5191,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4584
5191
  const text = error instanceof Error ? error.message : String(error);
4585
5192
  return { content: [{ type: "text", text }], isError: true, details: { mode: "management", results: [] } };
4586
5193
  }
4587
- if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, index: paramsWithResolvedCwd.index, signal }));
4588
- if (resolved?.kind === "foreground") return { content: [{ type: "text", text: "action='steer' currently supports live async Pi child sessions only; use action='interrupt' or action='resume' for foreground runs." }], isError: true, details: { mode: "management", results: [] } };
5194
+ if (resolved?.kind === "nested") return steerNestedRun(omitUndefinedProperties({ target: resolved, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal }));
5195
+ if (resolved?.kind === "foreground") {
5196
+ const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, childRunId: resolved.id, asyncDirRoot: DIRS.async });
5197
+ if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
5198
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
5199
+ }
4589
5200
  if (resolved?.kind !== "async") return { content: [{ type: "text", text: `No async run found for '${targetRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
5201
+ const resolvedStatus = resolved.location.asyncDir ? readStatus(resolved.location.asyncDir) : null;
5202
+ if (resolvedStatus?.mode === "workflow") {
5203
+ const route = resolveWorkflowForegroundSteeringTarget({ state: deps.state, workflowRunId: resolvedStatus.runId || resolved.id, asyncDirRoot: DIRS.async });
5204
+ if (!route.ok) return { content: [{ type: "text", text: route.message }], isError: true, details: { mode: "management", results: [] } };
5205
+ return steerWorkflowForegroundTarget({ target: route.target, message, mode: paramsWithResolvedCwd.mode, index: paramsWithResolvedCwd.index, signal });
5206
+ }
4590
5207
  if (resolved.location.asyncDir) {
4591
5208
  const unsupported = externalRunnerControlError(resolved.location.asyncDir, "steer");
4592
5209
  if (unsupported) return unsupported;
@@ -4595,6 +5212,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4595
5212
  state: deps.state,
4596
5213
  runId: resolved.id,
4597
5214
  message,
5215
+ mode: paramsWithResolvedCwd.mode,
4598
5216
  index: paramsWithResolvedCwd.index,
4599
5217
  kill: deps.kill,
4600
5218
  location: resolved.location,
@@ -4619,6 +5237,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4619
5237
  return appendStepToAsyncChain(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel }));
4620
5238
  }
4621
5239
  if (action.startsWith("schedule.")) {
5240
+ if (!isScheduledRunAction(action)) {
5241
+ return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
5242
+ }
4622
5243
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4623
5244
  return {
4624
5245
  content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
@@ -4635,6 +5256,28 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4635
5256
  }
4636
5257
  return deps.handleScheduledRunAction(paramsWithResolvedCwd, ctx);
4637
5258
  }
5259
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
5260
+ return {
5261
+ content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
5262
+ isError: true,
5263
+ details: { mode: "management" as const, results: [] },
5264
+ };
5265
+ }
5266
+ if (action === "dismiss") {
5267
+ const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
5268
+ if (!targetRunId) return { content: [{ type: "text", text: "action='dismiss' requires id." }], isError: true, details: { mode: "management", results: [] } };
5269
+ let resolved: ResolvedSubagentRunId | undefined;
5270
+ try {
5271
+ resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
5272
+ } catch (error) {
5273
+ const message = error instanceof Error ? error.message : String(error);
5274
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
5275
+ }
5276
+ if (resolved?.kind !== "async") {
5277
+ return { content: [{ type: "text", text: `Run '${targetRunId}' is not a recovered workflow.` }], isError: true, details: { mode: "management", results: [] } };
5278
+ }
5279
+ return dismissRecoveredWorkflow(deps.state, resolved.location);
5280
+ }
4638
5281
  if (action === "stop") {
4639
5282
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
4640
5283
  const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
@@ -4646,6 +5289,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4646
5289
  if (paramsWithResolvedCwd.dir) {
4647
5290
  try {
4648
5291
  const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
5292
+ const existingStatus = readStatus(location.asyncDir ?? "");
5293
+ if (existingStatus?.mode === "workflow" && existingStatus.state === "running") {
5294
+ return { content: [{ type: "text", text: `Workflow ${existingStatus.runId} is not controlled by this extension runtime; reload recovery cannot stop it safely.` }], isError: true, details: { mode: "management", results: [] } };
5295
+ }
4649
5296
  const stopResult = stopAsyncRun(deps.state, location.resolvedId ?? targetRunId ?? path.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps.kill, location);
4650
5297
  return stopResult ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
4651
5298
  } catch (error) {
@@ -4725,14 +5372,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4725
5372
  }
4726
5373
  if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
4727
5374
  return {
4728
- content: [{ type: "text", text: `Unknown action: ${action}. Valid: ${SUBAGENT_ACTIONS.join(", ")}` }],
4729
- isError: true,
4730
- details: { mode: "management" as const, results: [] },
4731
- };
4732
- }
4733
- if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
4734
- return {
4735
- content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }],
5375
+ content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
4736
5376
  isError: true,
4737
5377
  details: { mode: "management" as const, results: [] },
4738
5378
  };
@@ -4889,9 +5529,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4889
5529
  if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
4890
5530
  return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='external-cli', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
4891
5531
  }
4892
- const foregroundTimeout = resolveForegroundTimeout(
5532
+ const foregroundTimeout = resolveSingleAgentLaunchTimeout(
4893
5533
  effectiveParams,
4894
- effectiveAsync ? undefined : DEFAULT_FOREGROUND_TIMEOUT_MS,
5534
+ effectiveAsync,
4895
5535
  );
4896
5536
  if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
4897
5537
  const controlConfig = resolveControlConfig(deps.config.control, effectiveParams.control);
@@ -4902,6 +5542,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4902
5542
  dir: deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
4903
5543
  });
4904
5544
  const artifactsDir = getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
5545
+ if (artifactConfig.dir === "project" && !warnedArtifactPackageDirs.has(effectiveCwd)) {
5546
+ warnedArtifactPackageDirs.add(effectiveCwd);
5547
+ const warning = getProjectArtifactPackagingWarning(effectiveCwd);
5548
+ if (warning) console.warn(`[pi-subagents] ${warning}`);
5549
+ }
4905
5550
 
4906
5551
  let sessionRoot: string;
4907
5552
  if (effectiveParams.sessionDir) {
@@ -5036,12 +5681,26 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5036
5681
  const foregroundDescription = effectiveParams.task?.trim()
5037
5682
  || effectiveParams.tasks?.[0]?.task?.trim()
5038
5683
  || (effectiveParams.chain ? firstRawChainTask(effectiveParams.chain)?.trim() : undefined);
5684
+ const parentWorkflowStatus = effectiveParams.workflowParentRunId
5685
+ ? readStatus(path.join(DIRS.async, effectiveParams.workflowParentRunId))
5686
+ : null;
5687
+ const workflowSteeringDir = effectiveParams.workflowParentRunId
5688
+ && requestSessionId
5689
+ && deps.state.workflowControllers?.has(effectiveParams.workflowParentRunId)
5690
+ && parentWorkflowStatus?.mode === "workflow"
5691
+ && (parentWorkflowStatus.state === "running" || parentWorkflowStatus.state === "queued")
5692
+ && parentWorkflowStatus.sessionId === requestSessionId
5693
+ ? workflowForegroundSteeringDir(DIRS.async, effectiveParams.workflowParentRunId, runId)
5694
+ : undefined;
5039
5695
  const foregroundControl: ForegroundRunControl | undefined = effectiveAsync
5040
5696
  ? undefined
5041
5697
  : compactOptional<ForegroundRunControl>({
5042
5698
  runId,
5043
5699
  sessionId: requestSessionId,
5044
5700
  mode: foregroundMode,
5701
+ ...(effectiveParams.workflowParentRunId ? { parentWorkflowRunId: effectiveParams.workflowParentRunId } : {}),
5702
+ ...(effectiveParams.workflowKey ? { workflowKey: effectiveParams.workflowKey } : {}),
5703
+ workflowSteeringDir,
5045
5704
  startedAt: Date.now(),
5046
5705
  updatedAt: Date.now(),
5047
5706
  cwd: effectiveCwd,
@@ -5147,6 +5806,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5147
5806
 
5148
5807
  let nestedForegroundStarted = false;
5149
5808
  try {
5809
+ if (workflowLaunchObserver) {
5810
+ const singleTask = hasTasks && effectiveParams.tasks?.length === 1 ? effectiveParams.tasks[0] : undefined;
5811
+ const launch = hasSingle
5812
+ ? { agent: effectiveParams.agent!, sessionFile: childSessionFileForTask(effectiveParams.agent!, 0, effectiveParams.model) }
5813
+ : singleTask
5814
+ ? { agent: singleTask.agent, sessionFile: childSessionFileForTask(singleTask.agent, 0, singleTask.model) }
5815
+ : undefined;
5816
+ if (launch) {
5817
+ workflowLaunchObservers.delete(params);
5818
+ workflowLaunchObserver(launch);
5819
+ }
5820
+ }
5150
5821
  const asyncResult = runAsyncPath(execData, deps);
5151
5822
  if (asyncResult) return attachMission(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary));
5152
5823
  if (foregroundControl) {
@@ -5193,8 +5864,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5193
5864
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
5194
5865
  ctx: ExtensionContext,
5195
5866
  ): Promise<AgentToolResult<Details>> => {
5196
- const requestParams = omitExecutionModeActionAlias(params);
5197
- if (requestParams.action) return execute(id, requestParams, signal, onUpdate, ctx);
5867
+ const normalizedAction = typeof params.action === "string" ? params.action.trim() : params.action;
5868
+ const requestParams = normalizedAction ? { ...params, action: normalizedAction } : params;
5869
+ if (normalizedAction) return execute(id, requestParams, signal, onUpdate, ctx);
5198
5870
  const { depth } = checkSubagentDepth(deps.config.maxSubagentDepth);
5199
5871
  const dispatchParams = applyForceTopLevelAsyncOverride(requestParams, depth, deps.config.forceTopLevelAsync === true);
5200
5872
  const runsForeground = dispatchParams.clarify === true || (dispatchParams.async ?? deps.asyncByDefault) !== true;
@@ -5208,6 +5880,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5208
5880
  }
5209
5881
  };
5210
5882
 
5883
+ const executePublic = (
5884
+ id: string,
5885
+ params: SubagentParamsLike,
5886
+ signal: AbortSignal,
5887
+ onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
5888
+ ctx: ExtensionContext,
5889
+ ): Promise<AgentToolResult<Details>> => {
5890
+ const normalized = normalizePublicSubagentExecution(params);
5891
+ if (!normalized.ok) {
5892
+ return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
5893
+ }
5894
+ return executeWithSingleDispatchGuard(id, normalized.params, signal, onUpdate, ctx);
5895
+ };
5896
+
5211
5897
  const executeDelegated = async (
5212
5898
  id: string,
5213
5899
  params: SubagentParamsLike,
@@ -5247,5 +5933,5 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5247
5933
  return ownerExecutor.execute(id, params, signal, undefined, ctx);
5248
5934
  };
5249
5935
 
5250
- return { execute: executeWithSingleDispatchGuard, executeDelegated, executeScheduled };
5936
+ return { execute: executeWithSingleDispatchGuard, executePublic, executeDelegated, executeScheduled };
5251
5937
  }