@selesai/code 0.13.40 → 0.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1360) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/bun/runtime-setup.js +4 -1
  3. package/dist/cli/args.d.ts +3 -1
  4. package/dist/cli/args.js +76 -33
  5. package/dist/cli/auth-check.d.ts +18 -0
  6. package/dist/cli/auth-check.js +49 -0
  7. package/dist/cli/credential-print.d.ts +5 -15
  8. package/dist/cli/credential-print.js +42 -82
  9. package/dist/cli/file-processor.d.ts +1 -1
  10. package/dist/cli/file-processor.js +2 -1
  11. package/dist/cli/list-models.d.ts +1 -1
  12. package/dist/cli/list-models.js +2 -2
  13. package/dist/cli/startup-ui.js +38 -30
  14. package/dist/config.d.ts +45 -7
  15. package/dist/config.js +134 -18
  16. package/dist/core/agent-session-custom-tools.test.js +64 -0
  17. package/dist/core/agent-session-length-continuation.test.js +7 -3
  18. package/dist/core/agent-session-runtime.js +2 -2
  19. package/dist/core/agent-session-services.d.ts +1 -0
  20. package/dist/core/agent-session-services.js +14 -0
  21. package/dist/core/agent-session.d.ts +180 -40
  22. package/dist/core/agent-session.js +1061 -485
  23. package/dist/core/auth-storage.d.ts +25 -15
  24. package/dist/core/auth-storage.js +242 -66
  25. package/dist/core/bash-executor.js +42 -52
  26. package/dist/core/bug-report.js +1 -1
  27. package/dist/core/cache-warmer.d.ts +1 -0
  28. package/dist/core/cache-warmer.js +15 -1
  29. package/dist/core/compaction/branch-summarization.js +3 -0
  30. package/dist/core/compaction/compaction.d.ts +3 -1
  31. package/dist/core/compaction/compaction.js +156 -79
  32. package/dist/core/compaction/utils.d.ts +2 -1
  33. package/dist/core/compaction/utils.js +25 -18
  34. package/dist/core/crash-log.d.ts +5 -0
  35. package/dist/core/crash-log.js +69 -0
  36. package/dist/core/defaults.d.ts +1 -1
  37. package/dist/core/defaults.js +1 -1
  38. package/dist/core/export-html/template.css +10 -0
  39. package/dist/core/export-html/template.js +72 -18
  40. package/dist/core/export-html/tool-renderer.d.ts +3 -3
  41. package/dist/core/export-html/tool-renderer.js +5 -3
  42. package/dist/core/extensions/index.d.ts +2 -2
  43. package/dist/core/extensions/index.js +1 -1
  44. package/dist/core/extensions/loader.js +138 -55
  45. package/dist/core/extensions/runner.d.ts +38 -5
  46. package/dist/core/extensions/runner.js +220 -7
  47. package/dist/core/extensions/types.d.ts +438 -51
  48. package/dist/core/extensions/types.js +3 -0
  49. package/dist/core/extensions/virtual-modules.js +6 -0
  50. package/dist/core/extensions/wrapper.d.ts +2 -2
  51. package/dist/core/extensions/wrapper.js +3 -3
  52. package/dist/core/footer-data-provider.d.ts +1 -1
  53. package/dist/core/footer-data-provider.js +1 -1
  54. package/dist/core/handoff.d.ts +0 -1
  55. package/dist/core/handoff.js +0 -1
  56. package/dist/core/http-dispatcher.js +5 -0
  57. package/dist/core/index.d.ts +1 -1
  58. package/dist/core/keybindings.d.ts +25 -24
  59. package/dist/core/keybindings.js +27 -6
  60. package/dist/core/mcp-servers.d.ts +134 -0
  61. package/dist/core/mcp-servers.js +223 -0
  62. package/dist/core/model-config.d.ts +122 -5
  63. package/dist/core/model-config.js +35 -1
  64. package/dist/core/model-registry.d.ts +23 -15
  65. package/dist/core/model-registry.js +46 -22
  66. package/dist/core/model-resolver.d.ts +6 -5
  67. package/dist/core/model-resolver.js +51 -21
  68. package/dist/core/model-runtime.d.ts +73 -14
  69. package/dist/core/model-runtime.js +398 -104
  70. package/dist/core/models-store.d.ts +12 -7
  71. package/dist/core/models-store.js +85 -15
  72. package/dist/core/nested-tool-calls.d.ts +102 -0
  73. package/dist/core/nested-tool-calls.js +178 -0
  74. package/dist/core/package-manager.d.ts +8 -1
  75. package/dist/core/package-manager.js +144 -61
  76. package/dist/core/package-manager.test.js +5 -5
  77. package/dist/core/pi-manifest.d.ts +9 -0
  78. package/dist/core/pi-manifest.js +25 -0
  79. package/dist/core/prompt-templates.d.ts +6 -1
  80. package/dist/core/prompt-templates.js +61 -35
  81. package/dist/core/provider-composer.d.ts +44 -19
  82. package/dist/core/provider-composer.js +166 -64
  83. package/dist/core/radius.d.ts +2 -0
  84. package/dist/core/radius.js +2 -0
  85. package/dist/core/remote-catalog-provider.d.ts +8 -1
  86. package/dist/core/remote-catalog-provider.js +25 -13
  87. package/dist/core/resource-loader.d.ts +9 -1
  88. package/dist/core/resource-loader.js +179 -45
  89. package/dist/core/runtime-credentials.d.ts +5 -5
  90. package/dist/core/runtime-credentials.js +11 -8
  91. package/dist/core/sdk.d.ts +16 -7
  92. package/dist/core/sdk.js +57 -24
  93. package/dist/core/session-manager.d.ts +45 -9
  94. package/dist/core/session-manager.js +124 -31
  95. package/dist/core/settings-diagnostics.d.ts +8 -0
  96. package/dist/core/settings-diagnostics.js +20 -0
  97. package/dist/core/settings-manager.d.ts +62 -32
  98. package/dist/core/settings-manager.js +147 -69
  99. package/dist/core/skills.d.ts +5 -2
  100. package/dist/core/skills.js +58 -35
  101. package/dist/core/slash-commands.js +2 -1
  102. package/dist/core/source-info.d.ts +8 -0
  103. package/dist/core/source-info.js +16 -0
  104. package/dist/core/system-prompt.d.ts +9 -3
  105. package/dist/core/system-prompt.js +9 -12
  106. package/dist/core/system-prompt.test.js +2 -7
  107. package/dist/core/tools/bash.d.ts +12 -0
  108. package/dist/core/tools/bash.js +33 -2
  109. package/dist/core/tools/edit-diff.d.ts +0 -5
  110. package/dist/core/tools/edit-diff.js +2 -5
  111. package/dist/core/tools/edit.d.ts +4 -0
  112. package/dist/core/tools/edit.js +31 -11
  113. package/dist/core/tools/find.d.ts +7 -0
  114. package/dist/core/tools/find.js +16 -19
  115. package/dist/core/tools/grep.d.ts +4 -0
  116. package/dist/core/tools/grep.js +5 -1
  117. package/dist/core/tools/index.d.ts +1 -1
  118. package/dist/core/tools/index.js +11 -2
  119. package/dist/core/tools/ls.d.ts +4 -0
  120. package/dist/core/tools/ls.js +5 -1
  121. package/dist/core/tools/output-accumulator.d.ts +11 -0
  122. package/dist/core/tools/output-accumulator.js +40 -10
  123. package/dist/core/tools/read.d.ts +17 -2
  124. package/dist/core/tools/read.js +23 -2
  125. package/dist/core/tools/render-utils.d.ts +6 -0
  126. package/dist/core/tools/render-utils.js +26 -0
  127. package/dist/core/tools/renderers/bash.js +19 -35
  128. package/dist/core/tools/renderers/edit.js +5 -4
  129. package/dist/core/tools/renderers/index.d.ts +2 -2
  130. package/dist/core/tools/renderers/read.js +3 -2
  131. package/dist/core/tools/tool-definition-wrapper.d.ts +5 -3
  132. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  133. package/dist/core/tools/truncate.d.ts +14 -0
  134. package/dist/core/tools/truncate.js +29 -0
  135. package/dist/core/tools/write.d.ts +4 -0
  136. package/dist/core/tools/write.js +6 -2
  137. package/dist/core/trust-manager.js +3 -1
  138. package/dist/core/usage-totals.d.ts +2 -0
  139. package/dist/core/usage-totals.js +23 -0
  140. package/dist/core/virtual-models.d.ts +116 -0
  141. package/dist/core/virtual-models.js +123 -0
  142. package/dist/defaults/models.json +32 -0
  143. package/dist/defaults/settings.json +1 -9
  144. package/dist/extensions/codemode/execute.d.ts +15 -0
  145. package/dist/extensions/codemode/execute.js +645 -0
  146. package/dist/extensions/codemode/execute.lazy.d.ts +2 -0
  147. package/dist/extensions/codemode/execute.lazy.js +2 -0
  148. package/dist/extensions/codemode/index.d.ts +20 -0
  149. package/dist/extensions/codemode/index.js +31 -0
  150. package/dist/extensions/codemode/renderer.d.ts +11 -0
  151. package/dist/extensions/codemode/renderer.js +136 -0
  152. package/dist/extensions/codemode/tool.d.ts +129 -0
  153. package/dist/extensions/codemode/tool.js +275 -0
  154. package/dist/extensions/context-compaction-reminder.ts +1 -1
  155. package/dist/extensions/grep-app/index.ts +2 -10
  156. package/dist/extensions/herdr-agent-state.ts +1 -1
  157. package/dist/extensions/{pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts → index.d.ts} +0 -1
  158. package/dist/extensions/index.js +12 -0
  159. package/dist/extensions/jev/classifier-context.ts +54 -0
  160. package/dist/extensions/jev/classifier.test.ts +150 -0
  161. package/dist/extensions/jev/classifier.ts +191 -0
  162. package/dist/extensions/jev/decisions.test.ts +56 -47
  163. package/dist/extensions/jev/decisions.ts +94 -109
  164. package/dist/extensions/jev/test-support.ts +33 -38
  165. package/dist/extensions/jev-advisory-lifecycle.test.ts +12 -12
  166. package/dist/extensions/jev-advisory-memory.test.ts +9 -9
  167. package/dist/extensions/jev-advisory-recommendations.test.ts +8 -8
  168. package/dist/extensions/jev-ask-tool.test.ts +41 -45
  169. package/dist/extensions/jev-ask-tool.ts +11 -39
  170. package/dist/extensions/jev-find-source.test.ts +26 -0
  171. package/dist/extensions/jev-find-source.ts +58 -19
  172. package/dist/extensions/jev-tool-ranker.test.ts +185 -0
  173. package/dist/extensions/jev-tool-ranker.ts +169 -0
  174. package/dist/{core → extensions}/llama/client.d.ts +2 -0
  175. package/dist/{core → extensions}/llama/client.js +7 -3
  176. package/dist/{core → extensions}/llama/index.js +13 -2
  177. package/dist/{core → extensions}/llama/provider.d.ts +1 -1
  178. package/dist/extensions/llama/provider.js +235 -0
  179. package/dist/extensions/mcp/cli.d.ts +20 -0
  180. package/dist/extensions/mcp/cli.js +496 -0
  181. package/dist/extensions/mcp/cli.lazy.d.ts +2 -0
  182. package/dist/extensions/mcp/cli.lazy.js +2 -0
  183. package/dist/extensions/mcp/config.d.ts +83 -0
  184. package/dist/extensions/mcp/config.js +193 -0
  185. package/dist/extensions/mcp/index.d.ts +78 -0
  186. package/dist/extensions/mcp/index.js +1201 -0
  187. package/dist/extensions/mcp/log.d.ts +15 -0
  188. package/dist/extensions/mcp/log.js +65 -0
  189. package/dist/extensions/mcp/oauth.d.ts +97 -0
  190. package/dist/extensions/mcp/oauth.js +401 -0
  191. package/dist/extensions/mcp/resources.d.ts +41 -0
  192. package/dist/extensions/mcp/resources.js +260 -0
  193. package/dist/extensions/mcp/runtime.d.ts +96 -0
  194. package/dist/extensions/mcp/runtime.js +412 -0
  195. package/dist/extensions/mcp/runtime.lazy.d.ts +2 -0
  196. package/dist/extensions/mcp/runtime.lazy.js +2 -0
  197. package/dist/extensions/mcp/tools.d.ts +86 -0
  198. package/dist/extensions/mcp/tools.js +252 -0
  199. package/dist/extensions/mcp/ui.d.ts +59 -0
  200. package/dist/extensions/mcp/ui.js +185 -0
  201. package/dist/extensions/package.json +2 -1
  202. package/dist/extensions/pi-graft/index.ts +4 -19
  203. package/dist/extensions/pi-graft/lifecycle.test.ts +32 -25
  204. package/dist/extensions/pi-graft/tools.ts +6 -0
  205. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +25 -0
  206. package/dist/extensions/pi-hermes-memory/README.md +140 -4
  207. package/dist/extensions/pi-hermes-memory/package-lock.json +624 -2720
  208. package/dist/extensions/pi-hermes-memory/package.json +9 -7
  209. package/dist/extensions/pi-hermes-memory/scripts/benchmark-memory-startup.mjs +72 -0
  210. package/dist/extensions/pi-hermes-memory/scripts/check-production-install.mjs +107 -0
  211. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +2 -1
  212. package/dist/extensions/pi-hermes-memory/src/config.ts +35 -0
  213. package/dist/extensions/pi-hermes-memory/src/constants.ts +62 -15
  214. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +374 -50
  215. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +7 -4
  216. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +19 -14
  217. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +75 -1
  218. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +322 -22
  219. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +8 -0
  220. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +193 -54
  221. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +6 -2
  222. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +33 -5
  223. package/dist/extensions/pi-hermes-memory/src/index.ts +116 -47
  224. package/dist/extensions/pi-hermes-memory/src/memory-initialization.ts +101 -0
  225. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +10 -2
  226. package/dist/extensions/pi-hermes-memory/src/store/db.ts +34 -9
  227. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +44 -8
  228. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +3 -1
  229. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +141 -0
  230. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +17 -6
  231. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +20 -10
  232. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +1 -17
  233. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +2 -10
  234. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +4 -8
  235. package/dist/extensions/pi-hermes-memory/src/types.ts +48 -0
  236. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +108 -0
  237. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +1185 -42
  238. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +57 -1
  239. package/dist/extensions/pi-hermes-memory/tests/handlers/lazy-memory-hooks.test.ts +128 -0
  240. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +844 -1
  241. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +16 -0
  242. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +512 -3
  243. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +38 -0
  244. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +183 -2
  245. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +1 -3
  246. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +17 -0
  247. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +21 -0
  248. package/dist/extensions/pi-hermes-memory/tests/lazy-startup.test.ts +313 -0
  249. package/dist/extensions/pi-hermes-memory/tests/memory-initialization.test.ts +182 -0
  250. package/dist/extensions/pi-hermes-memory/tests/prompt-injection.test.ts +184 -0
  251. package/dist/extensions/pi-hermes-memory/tests/skill-discovery-contract.test.ts +60 -0
  252. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-lazy-probe.test.ts +80 -0
  253. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +2 -0
  254. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +79 -0
  255. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +268 -0
  256. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +29 -0
  257. package/dist/extensions/pi-hermes-memory/tests/store/usage-tracking.test.ts +170 -0
  258. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +80 -1
  259. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +51 -0
  260. package/dist/extensions/pi-intercom/CHANGELOG.md +66 -0
  261. package/dist/extensions/pi-intercom/README.md +100 -12
  262. package/dist/extensions/pi-intercom/broker/broker.ts +28 -12
  263. package/dist/extensions/pi-intercom/broker/client.ts +3 -0
  264. package/dist/extensions/pi-intercom/broker/cross-machine-provenance.test.ts +63 -0
  265. package/dist/extensions/pi-intercom/broker/protocol.ts +64 -0
  266. package/dist/extensions/pi-intercom/broker/spawn.test.ts +58 -167
  267. package/dist/extensions/pi-intercom/broker/spawn.ts +40 -87
  268. package/dist/extensions/pi-intercom/cli.mjs +6 -0
  269. package/dist/extensions/pi-intercom/cli.test.ts +428 -0
  270. package/dist/extensions/pi-intercom/cli.ts +343 -0
  271. package/dist/extensions/pi-intercom/config.test.ts +61 -1
  272. package/dist/extensions/pi-intercom/config.ts +53 -2
  273. package/dist/extensions/pi-intercom/cross-machine-discovery.test.ts +218 -0
  274. package/dist/extensions/pi-intercom/cross-machine-discovery.ts +140 -0
  275. package/dist/extensions/pi-intercom/cross-machine-envelope.test.ts +106 -0
  276. package/dist/extensions/pi-intercom/cross-machine-envelope.ts +92 -0
  277. package/dist/extensions/pi-intercom/cross-machine-integration.test.ts +25 -0
  278. package/dist/extensions/pi-intercom/cross-machine-transport.test.ts +106 -0
  279. package/dist/extensions/pi-intercom/cross-machine-transport.ts +110 -0
  280. package/dist/extensions/pi-intercom/extension-api.ts +14 -0
  281. package/dist/extensions/pi-intercom/handover.test.ts +88 -0
  282. package/dist/extensions/pi-intercom/handover.ts +115 -0
  283. package/dist/extensions/pi-intercom/herdr-location.test.ts +173 -0
  284. package/dist/extensions/pi-intercom/herdr-location.ts +137 -0
  285. package/dist/extensions/pi-intercom/human-priority.pi.test.ts +489 -0
  286. package/dist/extensions/pi-intercom/index.ts +582 -164
  287. package/dist/extensions/pi-intercom/intercom.integration.test.ts +590 -22
  288. package/dist/extensions/pi-intercom/package.json +12 -2
  289. package/dist/extensions/pi-intercom/project-agent.ts +1 -1
  290. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +26 -2
  291. package/dist/extensions/pi-intercom/test/handover-picker.test.ts +134 -0
  292. package/dist/extensions/pi-intercom/test/inline-message.test.ts +43 -0
  293. package/dist/extensions/pi-intercom/types.d.ts +247 -0
  294. package/dist/extensions/pi-intercom/types.js +2 -0
  295. package/dist/extensions/pi-intercom/types.ts +39 -1
  296. package/dist/extensions/pi-intercom/ui/handover-picker.ts +318 -0
  297. package/dist/extensions/pi-intercom/ui/inline-message.ts +10 -3
  298. package/dist/extensions/pi-intercom/ui/session-list.ts +28 -15
  299. package/dist/extensions/pi-subagents/.github/workflows/release.yml +5 -1
  300. package/dist/extensions/pi-subagents/.github/workflows/test.yml +188 -18
  301. package/dist/extensions/pi-subagents/CHANGELOG.md +514 -1
  302. package/dist/extensions/pi-subagents/README.md +7 -5
  303. package/dist/extensions/pi-subagents/VISION.md +6 -0
  304. package/dist/extensions/pi-subagents/agents/reviewer.md +7 -5
  305. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  306. package/dist/extensions/pi-subagents/docs/agents.md +75 -35
  307. package/dist/extensions/pi-subagents/docs/configuration.md +156 -25
  308. package/dist/extensions/pi-subagents/docs/extension-api.md +113 -29
  309. package/dist/extensions/pi-subagents/docs/missions.md +46 -14
  310. package/dist/extensions/pi-subagents/docs/models.md +22 -84
  311. package/dist/extensions/pi-subagents/docs/observability.md +49 -24
  312. package/dist/extensions/pi-subagents/docs/standalone-background.md +59 -0
  313. package/dist/extensions/pi-subagents/docs/tool-reference.md +167 -116
  314. package/dist/extensions/pi-subagents/docs/watchdog.md +41 -10
  315. package/dist/extensions/pi-subagents/docs/workflows.md +239 -163
  316. package/dist/extensions/pi-subagents/examples/typed-gate/.pi/agents/classifier.md +14 -0
  317. package/dist/extensions/pi-subagents/examples/typed-gate/README.md +42 -0
  318. package/dist/extensions/pi-subagents/examples/typed-gate/classify +38 -0
  319. package/dist/extensions/pi-subagents/examples/typed-gate/workflow.js +32 -0
  320. package/dist/extensions/pi-subagents/index.ts +11 -3
  321. package/dist/extensions/pi-subagents/inspector-runner.mjs +3 -3
  322. package/dist/extensions/pi-subagents/package-lock.json +517 -1129
  323. package/dist/extensions/pi-subagents/package.json +22 -11
  324. package/dist/extensions/pi-subagents/prompts/council.md +7 -4
  325. package/dist/extensions/pi-subagents/prompts/parallel-review.md +2 -2
  326. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  327. package/dist/extensions/pi-subagents/runner-peer-loader.mjs +21 -0
  328. package/dist/extensions/pi-subagents/runner-peer-preload.mjs +30 -0
  329. package/dist/extensions/pi-subagents/scripts/build-package.mjs +79 -0
  330. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +2 -2
  331. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +46 -27
  332. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +11 -7
  333. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +78 -73
  334. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +4 -5
  335. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +10 -2
  336. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +69 -67
  337. package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +3 -3
  338. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +21 -10
  339. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +113 -113
  340. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +91 -4
  341. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +15 -9
  342. package/dist/extensions/pi-subagents/src/agents/agents.ts +432 -193
  343. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +6 -1
  344. package/dist/extensions/pi-subagents/src/agents/global-npm-root.ts +58 -0
  345. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +5 -2
  346. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +22 -18
  347. package/dist/extensions/pi-subagents/src/agents/skills.ts +43 -10
  348. package/dist/extensions/pi-subagents/src/api/delegation.ts +20 -1
  349. package/dist/extensions/pi-subagents/src/api/inspectors.ts +27 -0
  350. package/dist/extensions/pi-subagents/src/api/preflight.ts +140 -62
  351. package/dist/extensions/pi-subagents/src/api/required-child-extensions.ts +6 -0
  352. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
  353. package/dist/extensions/pi-subagents/src/extension/config.ts +24 -40
  354. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +4 -1
  355. package/dist/extensions/pi-subagents/src/extension/doctor.ts +18 -15
  356. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +87 -13
  357. package/dist/extensions/pi-subagents/src/extension/herdr-pi-bridge.ts +162 -0
  358. package/dist/extensions/pi-subagents/src/extension/index.ts +280 -268
  359. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +76 -37
  360. package/dist/extensions/pi-subagents/src/extension/reply-workflow-script.ts +67 -0
  361. package/dist/extensions/pi-subagents/src/extension/rpc.ts +34 -11
  362. package/dist/extensions/pi-subagents/src/extension/schemas.ts +116 -205
  363. package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +13 -5
  364. package/dist/extensions/pi-subagents/src/extension/tool-activation.ts +146 -0
  365. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +77 -90
  366. package/dist/extensions/pi-subagents/src/extension/upgrade-notice.ts +40 -0
  367. package/dist/extensions/pi-subagents/src/inspectors/actions.ts +148 -0
  368. package/dist/extensions/pi-subagents/src/inspectors/ghostty/actions.ts +74 -0
  369. package/dist/extensions/pi-subagents/src/inspectors/ghostty/plugin.ts +29 -0
  370. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +100 -180
  371. package/dist/extensions/pi-subagents/src/inspectors/herdr/plugin.ts +20 -0
  372. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -2
  373. package/dist/extensions/pi-subagents/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +13 -13
  374. package/dist/extensions/pi-subagents/src/inspectors/plugins.ts +75 -0
  375. package/dist/extensions/pi-subagents/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +4 -15
  376. package/dist/extensions/pi-subagents/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +1 -1
  377. package/dist/extensions/pi-subagents/src/inspectors/types.ts +51 -0
  378. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +38 -53
  379. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +53 -9
  380. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +69 -55
  381. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +7 -2
  382. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +45 -32
  383. package/dist/extensions/pi-subagents/src/missions/store.ts +11 -2
  384. package/dist/extensions/pi-subagents/src/policy/authority.ts +5 -1
  385. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +30 -18
  386. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +59 -9
  387. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +18 -2
  388. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +463 -248
  389. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +106 -66
  390. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +33 -20
  391. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +47 -17
  392. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -5
  393. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +24 -11
  394. package/dist/extensions/pi-subagents/src/runs/background/await-async-run.ts +78 -0
  395. package/dist/extensions/pi-subagents/src/runs/background/binary-bootstrap.ts +66 -0
  396. package/dist/extensions/pi-subagents/src/runs/background/calendar-schedule.ts +90 -0
  397. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -2
  398. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +19 -38
  399. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +62 -3
  400. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +82 -10
  401. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +259 -21
  402. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +30 -3
  403. package/dist/extensions/pi-subagents/src/runs/background/pid-namespace.ts +18 -0
  404. package/dist/extensions/pi-subagents/src/runs/background/process-terminal-candidate.ts +98 -0
  405. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +18 -97
  406. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +14 -5
  407. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +66 -12
  408. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +4 -3
  409. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +126 -45
  410. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +11 -6
  411. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +18 -7
  412. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +43 -51
  413. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +8 -1
  414. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +5 -4
  415. package/dist/extensions/pi-subagents/src/runs/background/runner-http-dispatcher.ts +119 -0
  416. package/dist/extensions/pi-subagents/src/runs/background/runner-startup-failure.ts +56 -0
  417. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +233 -69
  418. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +67 -16
  419. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +21 -3
  420. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner-bootstrap.ts +276 -0
  421. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +592 -573
  422. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +177 -29
  423. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +77 -28
  424. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +10 -4
  425. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +7 -13
  426. package/dist/extensions/pi-subagents/src/runs/background/workflow-terminal-proof.ts +41 -23
  427. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +19 -8
  428. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +130 -7
  429. package/dist/extensions/pi-subagents/src/runs/foreground/command-action.ts +41 -0
  430. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +267 -385
  431. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +4 -2
  432. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +6 -10
  433. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1158 -413
  434. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +3 -1
  435. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +2 -1
  436. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +200 -74
  437. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +133 -34
  438. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +6 -1
  439. package/dist/extensions/pi-subagents/src/runs/shared/child-commands.ts +228 -0
  440. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +18 -18
  441. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +16 -4
  442. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +55 -12
  443. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +6 -3
  444. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +11 -1
  445. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +367 -74
  446. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +50 -86
  447. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +288 -0
  448. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  449. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  450. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +5 -5
  451. package/dist/extensions/pi-subagents/src/runs/shared/effective-system-prompt.ts +33 -0
  452. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +49 -10
  453. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +61 -9
  454. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +16 -9
  455. package/dist/extensions/pi-subagents/src/runs/shared/external-job-bridge.ts +55 -1
  456. package/dist/extensions/pi-subagents/src/runs/shared/git-environment.ts +1 -1
  457. package/dist/extensions/pi-subagents/src/runs/shared/herdr-connection.ts +134 -0
  458. package/dist/extensions/pi-subagents/src/runs/shared/herdr-external-adapters.ts +169 -0
  459. package/dist/extensions/pi-subagents/src/runs/shared/herdr-machine.ts +279 -0
  460. package/dist/extensions/pi-subagents/src/runs/shared/herdr-pi-protocol.ts +59 -0
  461. package/dist/extensions/pi-subagents/src/runs/shared/herdr-placed-run.ts +263 -0
  462. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +6 -2
  463. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +108 -13
  464. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +23 -7
  465. package/dist/extensions/pi-subagents/src/runs/shared/model-resolution-diagnostic.ts +76 -0
  466. package/dist/extensions/pi-subagents/src/runs/shared/{model-fallback.ts → model-resolution.ts} +65 -245
  467. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +30 -11
  468. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +24 -3
  469. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +5 -4
  470. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +55 -7
  471. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +14 -4
  472. package/dist/extensions/pi-subagents/src/runs/shared/partial-output.ts +48 -0
  473. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +12 -4
  474. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +88 -1
  475. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +12 -3
  476. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +160 -9
  477. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +35 -34
  478. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +58 -19
  479. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +2 -2
  480. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +21 -0
  481. package/dist/extensions/pi-subagents/src/runs/shared/usage-reconciliation.ts +73 -0
  482. package/dist/extensions/pi-subagents/src/runs/shared/workflow-async-child-guidance.ts +18 -0
  483. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-apply.ts +160 -0
  484. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +42 -20
  485. package/dist/extensions/pi-subagents/src/runs/shared/worktree-lock.ts +29 -0
  486. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +27 -4
  487. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +94 -26
  488. package/dist/extensions/pi-subagents/src/shared/child-cache-retention.ts +43 -0
  489. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +5 -3
  490. package/dist/extensions/pi-subagents/src/shared/disabled-features.ts +123 -0
  491. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +14 -1
  492. package/dist/extensions/pi-subagents/src/shared/file-lease.ts +74 -0
  493. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +27 -77
  494. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +70 -10
  495. package/dist/extensions/pi-subagents/src/shared/model-info.ts +10 -1
  496. package/dist/extensions/pi-subagents/src/shared/opencode-session-headers.ts +30 -0
  497. package/dist/extensions/pi-subagents/src/shared/package-version.ts +17 -0
  498. package/dist/extensions/pi-subagents/src/shared/parent-wake.ts +66 -0
  499. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +71 -58
  500. package/dist/extensions/pi-subagents/src/shared/required-child-extensions.ts +115 -0
  501. package/dist/extensions/pi-subagents/src/shared/settings-file-lease.ts +36 -0
  502. package/dist/extensions/pi-subagents/src/shared/settings.ts +7 -4
  503. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +1 -5
  504. package/dist/extensions/pi-subagents/src/shared/types.ts +146 -48
  505. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +28 -2
  506. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +15 -1
  507. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +19 -4
  508. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +13 -2
  509. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +15 -15
  510. package/dist/extensions/pi-subagents/src/slash/subagent-cost.ts +46 -9
  511. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +29 -16
  512. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +201 -26
  513. package/dist/extensions/pi-subagents/src/tui/fleet.ts +33 -16
  514. package/dist/extensions/pi-subagents/src/tui/render.ts +334 -103
  515. package/dist/extensions/pi-subagents/src/tui/running-tone.ts +28 -0
  516. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +27 -28
  517. package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +47 -12
  518. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +2 -2
  519. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +22 -2
  520. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +17 -17
  521. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +24 -8
  522. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +43 -39
  523. package/dist/extensions/pi-subagents/src/watchdog/render.ts +2 -2
  524. package/dist/extensions/pi-subagents/src/watchdog/review.ts +127 -78
  525. package/dist/extensions/pi-subagents/src/watchdog/rules.ts +1 -1
  526. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +107 -31
  527. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +1 -1
  528. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +22 -14
  529. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +14 -13
  530. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +35 -6
  531. package/dist/extensions/pi-subagents/src/watchdog/types.ts +6 -4
  532. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +2 -2
  533. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  534. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +500 -65
  535. package/dist/extensions/pi-subagents/src/workflows/structured-workflow-scripts.ts +177 -0
  536. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +8 -2
  537. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +5 -3
  538. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +70 -10
  539. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +57 -18
  540. package/dist/extensions/pi-subagents/src/workflows/workflow-reuse.ts +120 -0
  541. package/dist/extensions/pi-subagents/src/workflows/workflow-revival.ts +123 -0
  542. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +5 -2
  543. package/dist/extensions/pi-subagents/test/fixtures/cwd-workflow-child.ts +37 -0
  544. package/dist/extensions/pi-subagents/test/fixtures/herdr-allocation-lock-child.ts +16 -0
  545. package/dist/extensions/pi-subagents/test/fixtures/herdr-provision-child.ts +15 -0
  546. package/dist/extensions/pi-subagents/test/fixtures/native-nested-wait-provider.ts +76 -0
  547. package/dist/extensions/pi-subagents/test/fixtures/runner-bootstrap-harness.ts +36 -0
  548. package/dist/extensions/pi-subagents/test/fixtures/schedule-history-writer.mjs +48 -0
  549. package/dist/extensions/pi-subagents/test/fixtures/settings-race-writer.mjs +57 -0
  550. package/dist/extensions/pi-subagents/test/fixtures/workflow-cwd-access-failure.ts +33 -0
  551. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +40 -7
  552. package/dist/extensions/pi-subagents/test/integration/async-attention-clearing.test.ts +9 -8
  553. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +435 -276
  554. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +435 -397
  555. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +918 -497
  556. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +264 -17
  557. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +185 -22
  558. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +3 -2
  559. package/dist/extensions/pi-subagents/test/integration/claude-code-dispatch.test.ts +127 -0
  560. package/dist/extensions/pi-subagents/test/integration/deadline-checkpoint.test.ts +126 -0
  561. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +29 -1
  562. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +88 -0
  563. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +450 -13
  564. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +10 -6
  565. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +105 -86
  566. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +403 -18
  567. package/dist/extensions/pi-subagents/test/integration/inline-workflow-visibility.test.ts +351 -0
  568. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +195 -10
  569. package/dist/extensions/pi-subagents/test/integration/model-resolution-diagnostic.test.ts +108 -0
  570. package/dist/extensions/pi-subagents/test/integration/nested-async-wait.test.ts +61 -0
  571. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +5 -4
  572. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +1 -1
  573. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +6 -4
  574. package/dist/extensions/pi-subagents/test/integration/render-widget-mouse.test.ts +84 -0
  575. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +242 -16
  576. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +2 -2
  577. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +158 -4
  578. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +1435 -248
  579. package/dist/extensions/pi-subagents/test/integration/single-execution.part-2.test.ts +454 -515
  580. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +140 -21
  581. package/dist/extensions/pi-subagents/test/integration/structured-workflows.test.ts +105 -0
  582. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +90 -11
  583. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +18 -15
  584. package/dist/extensions/pi-subagents/test/integration/workflow-worktree-admission.test.ts +100 -0
  585. package/dist/extensions/pi-subagents/test/smoke/clean-install.mjs +106 -0
  586. package/dist/extensions/pi-subagents/test/smoke/npm-background.mjs +43 -0
  587. package/dist/extensions/pi-subagents/test/smoke/packed-runner-selection.mjs +42 -0
  588. package/dist/extensions/pi-subagents/test/smoke/runner-loader-capability.mjs +50 -0
  589. package/dist/extensions/pi-subagents/test/smoke/sdk-child.ts +57 -0
  590. package/dist/extensions/pi-subagents/test/smoke/{pi085-extension.ts → sdk-extension.ts} +2 -2
  591. package/dist/extensions/pi-subagents/test/smoke/standalone-background.mjs +140 -0
  592. package/dist/extensions/pi-subagents/test/smoke/standalone-fault.ts +43 -0
  593. package/dist/extensions/pi-subagents/test/smoke/standalone-matrix.mjs +65 -0
  594. package/dist/extensions/pi-subagents/test/smoke/standalone-observer.ts +8 -0
  595. package/dist/extensions/pi-subagents/test/smoke/standalone-parent.ts +220 -0
  596. package/dist/extensions/pi-subagents/test/smoke/standalone-provider.ts +58 -0
  597. package/dist/extensions/pi-subagents/test/smoke/standalone-release.json +8 -0
  598. package/dist/extensions/pi-subagents/test/smoke/standalone-revival.ts +109 -0
  599. package/dist/extensions/pi-subagents/test/smoke/standalone-shared.ts +79 -0
  600. package/dist/extensions/pi-subagents/test/smoke/tool-activation.test.ts +128 -0
  601. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +6 -25
  602. package/dist/extensions/pi-subagents/test/support/async-result-timeout-evidence.ts +61 -0
  603. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +122 -9
  604. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +15 -7
  605. package/dist/extensions/pi-subagents/test/support/helpers.ts +11 -14
  606. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +21 -3
  607. package/dist/extensions/pi-subagents/test/support/native-peer-loader.mjs +15 -0
  608. package/dist/extensions/pi-subagents/test/support/node-command.ts +29 -6
  609. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +5 -3
  610. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +7 -15
  611. package/dist/extensions/pi-subagents/test/support/watchdog-stable-host-loader.mjs +23 -0
  612. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +3 -3
  613. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +158 -155
  614. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +52 -1
  615. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +21 -17
  616. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +105 -36
  617. package/dist/extensions/pi-subagents/test/unit/advertisement-barrier.test.ts +151 -0
  618. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -2
  619. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +33 -3
  620. package/dist/extensions/pi-subagents/test/unit/agent-end-auto-drain.test.ts +149 -0
  621. package/dist/extensions/pi-subagents/test/unit/agent-exclude-dirs.test.ts +246 -0
  622. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +62 -87
  623. package/dist/extensions/pi-subagents/test/unit/agent-global-root-override.test.ts +86 -0
  624. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +398 -48
  625. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +126 -1
  626. package/dist/extensions/pi-subagents/test/unit/agent-name-resolution.test.ts +75 -0
  627. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +94 -42
  628. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +73 -3
  629. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +428 -2
  630. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +4 -4
  631. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +112 -1
  632. package/dist/extensions/pi-subagents/test/unit/async-result-timeout-evidence.test.ts +53 -0
  633. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +92 -4
  634. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +58 -6
  635. package/dist/extensions/pi-subagents/test/unit/async-run-history.test.ts +123 -0
  636. package/dist/extensions/pi-subagents/test/unit/async-spawn-package-dir.test.ts +203 -0
  637. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +26 -35
  638. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +185 -0
  639. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +9 -1
  640. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +106 -1
  641. package/dist/extensions/pi-subagents/test/unit/await-async-run.test.ts +202 -0
  642. package/dist/extensions/pi-subagents/test/unit/binary-async-spawn.test.ts +115 -0
  643. package/dist/extensions/pi-subagents/test/unit/binary-pi-spawn.test.ts +27 -0
  644. package/dist/extensions/pi-subagents/test/unit/calendar-schedule.test.ts +70 -0
  645. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +38 -0
  646. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +10 -2
  647. package/dist/extensions/pi-subagents/test/unit/changelog-format.test.ts +11 -0
  648. package/dist/extensions/pi-subagents/test/unit/child-cache-retention.test.ts +87 -0
  649. package/dist/extensions/pi-subagents/test/unit/child-commands.test.ts +243 -0
  650. package/dist/extensions/pi-subagents/test/unit/child-external-job-bridge.test.ts +126 -0
  651. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +12 -1
  652. package/dist/extensions/pi-subagents/test/unit/child-lifecycle.test.ts +36 -0
  653. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +24 -0
  654. package/dist/extensions/pi-subagents/test/unit/child-session-commands.test.ts +162 -0
  655. package/dist/extensions/pi-subagents/test/unit/child-session-host-sdk.test.ts +226 -0
  656. package/dist/extensions/pi-subagents/test/unit/child-session-parent-providers.test.ts +200 -0
  657. package/dist/extensions/pi-subagents/test/unit/child-session-queued.test.ts +260 -0
  658. package/dist/extensions/pi-subagents/test/unit/child-tool-plan-permission-system.test.ts +3 -98
  659. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +222 -1
  660. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +314 -0
  661. package/dist/extensions/pi-subagents/test/unit/claude-code-dispatch.test.ts +182 -0
  662. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +157 -0
  663. package/dist/extensions/pi-subagents/test/unit/command-action.test.ts +50 -0
  664. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +25 -11
  665. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +3 -1
  666. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +18 -0
  667. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +7 -1
  668. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +220 -0
  669. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +104 -32
  670. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +138 -1
  671. package/dist/extensions/pi-subagents/test/unit/disabled-features.test.ts +227 -0
  672. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +48 -9
  673. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +7 -4
  674. package/dist/extensions/pi-subagents/test/unit/extension-lazy-loading.test.ts +21 -0
  675. package/dist/extensions/pi-subagents/test/unit/extension-maintenance-lifecycle.test.ts +66 -0
  676. package/dist/extensions/pi-subagents/test/unit/extension-module-preload.test.ts +49 -0
  677. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +104 -3
  678. package/dist/extensions/pi-subagents/test/unit/file-lease.test.ts +17 -0
  679. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +316 -0
  680. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +58 -7
  681. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +25 -96
  682. package/dist/extensions/pi-subagents/test/unit/ghostty-inspector.test.ts +92 -0
  683. package/dist/extensions/pi-subagents/test/unit/global-npm-root.test.ts +108 -0
  684. package/dist/extensions/pi-subagents/test/unit/herdr-connection.test.ts +43 -0
  685. package/dist/extensions/pi-subagents/test/unit/herdr-external-adapters.test.ts +89 -0
  686. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +11 -20
  687. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +113 -19
  688. package/dist/extensions/pi-subagents/test/unit/herdr-machine.test.ts +161 -0
  689. package/dist/extensions/pi-subagents/test/unit/herdr-native-placement.test.ts +187 -0
  690. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +7 -12
  691. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  692. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +182 -87
  693. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +68 -49
  694. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +295 -135
  695. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +3 -3
  696. package/dist/extensions/pi-subagents/test/unit/inspector-authority-policy.test.ts +144 -0
  697. package/dist/extensions/pi-subagents/test/unit/inspector-registration.test.ts +128 -0
  698. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +49 -1
  699. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +74 -1
  700. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-resolution.test.ts +112 -0
  701. package/dist/extensions/pi-subagents/test/unit/mission-global-projection.test.ts +215 -0
  702. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +65 -1
  703. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +2 -1
  704. package/dist/extensions/pi-subagents/test/unit/model-resolution-diagnostic.test.ts +78 -0
  705. package/dist/extensions/pi-subagents/test/unit/model-resolution.test.ts +153 -0
  706. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +43 -1
  707. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +1 -21
  708. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +279 -2
  709. package/dist/extensions/pi-subagents/test/unit/nested-control-fanout.test.ts +73 -0
  710. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +297 -3
  711. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +1 -1
  712. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +431 -23
  713. package/dist/extensions/pi-subagents/test/unit/opencode-session-headers.test.ts +32 -0
  714. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +89 -21
  715. package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +31 -1
  716. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +18 -24
  717. package/dist/extensions/pi-subagents/test/unit/parent-wake.test.ts +206 -0
  718. package/dist/extensions/pi-subagents/test/unit/partial-output.test.ts +53 -0
  719. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +216 -32
  720. package/dist/extensions/pi-subagents/test/unit/pi-spawn-host-root.test.ts +82 -0
  721. package/dist/extensions/pi-subagents/test/unit/pi-subagents-policy-guidance.test.ts +28 -0
  722. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +193 -104
  723. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +16 -1
  724. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +17 -1
  725. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +89 -9
  726. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +6 -0
  727. package/dist/extensions/pi-subagents/test/unit/prompt-audit-provider-routing.test.ts +69 -0
  728. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +2 -2
  729. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +23 -1
  730. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +117 -3
  731. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +35 -14
  732. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +3 -2
  733. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +261 -9
  734. package/dist/extensions/pi-subagents/test/unit/reply-workflow-script.test.ts +37 -0
  735. package/dist/extensions/pi-subagents/test/unit/required-child-extensions.test.ts +114 -0
  736. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +11 -1
  737. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +22 -2
  738. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +125 -18
  739. package/dist/extensions/pi-subagents/test/unit/run-child-session.test.ts +230 -0
  740. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +50 -1
  741. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +301 -4
  742. package/dist/extensions/pi-subagents/test/unit/runner-bootstrap.test.ts +182 -0
  743. package/dist/extensions/pi-subagents/test/unit/runner-http-dispatcher.test.ts +136 -0
  744. package/dist/extensions/pi-subagents/test/unit/runner-peer-loader.test.ts +68 -0
  745. package/dist/extensions/pi-subagents/test/unit/runner-peer-symlinks.test.ts +50 -0
  746. package/dist/extensions/pi-subagents/test/unit/running-tone.test.ts +13 -0
  747. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +155 -4
  748. package/dist/extensions/pi-subagents/test/unit/schedule-claim-initialization.test.ts +71 -0
  749. package/dist/extensions/pi-subagents/test/unit/schedule-claim-preservation.test.ts +98 -0
  750. package/dist/extensions/pi-subagents/test/unit/schedule-history-transactions.test.ts +201 -0
  751. package/dist/extensions/pi-subagents/test/unit/scheduled-mission.test.ts +317 -0
  752. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +412 -11
  753. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +2 -8
  754. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +70 -71
  755. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +748 -5
  756. package/dist/extensions/pi-subagents/test/unit/session-liveness-delivery.test.ts +337 -0
  757. package/dist/extensions/pi-subagents/test/unit/settings-atomic-save.test.ts +419 -0
  758. package/dist/extensions/pi-subagents/test/unit/settings-transactions.test.ts +88 -0
  759. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +13 -1
  760. package/dist/extensions/pi-subagents/test/unit/skills-disable-model-invocation.test.ts +164 -0
  761. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +35 -2
  762. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +2 -2
  763. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +334 -9
  764. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +170 -0
  765. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +34 -5
  766. package/dist/extensions/pi-subagents/test/unit/structured-output-validation.test.ts +199 -0
  767. package/dist/extensions/pi-subagents/test/unit/structured-workflow-resources.test.ts +241 -0
  768. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +47 -38
  769. package/dist/extensions/pi-subagents/test/unit/subagent-cost.test.ts +97 -0
  770. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +10 -0
  771. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +343 -44
  772. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +410 -11
  773. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +459 -22
  774. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +43 -3
  775. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +11 -1
  776. package/dist/extensions/pi-subagents/test/unit/tool-activation-stale-peer.test.ts +32 -0
  777. package/dist/extensions/pi-subagents/test/unit/tool-activation.test.ts +313 -0
  778. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +14 -0
  779. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -89
  780. package/dist/extensions/pi-subagents/test/unit/typed-gate-output.test.ts +141 -0
  781. package/dist/extensions/pi-subagents/test/unit/upgrade-notice.test.ts +31 -0
  782. package/dist/extensions/pi-subagents/test/unit/usage-reconciliation-owners.test.ts +66 -0
  783. package/dist/extensions/pi-subagents/test/unit/usage-reconciliation.test.ts +23 -0
  784. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +195 -1
  785. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +3 -1
  786. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +48 -45
  787. package/dist/extensions/pi-subagents/test/unit/watchdog-compaction-scope.test.ts +94 -0
  788. package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +48 -3
  789. package/dist/extensions/pi-subagents/test/unit/watchdog-native-scheduling.test.ts +107 -0
  790. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +95 -7
  791. package/dist/extensions/pi-subagents/test/unit/watchdog-register-child.test.ts +79 -0
  792. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -4
  793. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +111 -24
  794. package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +1 -1
  795. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +283 -2
  796. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +3 -0
  797. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +12 -1
  798. package/dist/extensions/pi-subagents/test/unit/watchdog-stable-host-layout.test.ts +23 -0
  799. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +30 -0
  800. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +39 -2
  801. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +18 -1
  802. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +8 -4
  803. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +10 -0
  804. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +6 -3
  805. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +44 -1
  806. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +28 -1
  807. package/dist/extensions/pi-subagents/test/unit/workflow-revival.test.ts +85 -0
  808. package/dist/extensions/pi-subagents/test/unit/workflow-scripts-feature.test.ts +177 -0
  809. package/dist/extensions/pi-subagents/test/unit/workflow-terminal-proof.test.ts +8 -0
  810. package/dist/extensions/pi-subagents/test/unit/workflow-worktree-admission.test.ts +29 -0
  811. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +343 -4
  812. package/dist/extensions/pi-subagents/test/unit/worktree-lock.test.ts +123 -0
  813. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +252 -22
  814. package/dist/extensions/pi-subagents/tsconfig.build.json +13 -0
  815. package/dist/extensions/pi-tool-display/tests/custom-tool-overrides.test.ts +1 -1
  816. package/dist/extensions/pi-tool-display/tests/tool-overrides-config.test.ts +1 -1
  817. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +24 -1
  818. package/dist/extensions/pi-tool-display/tests/tool-overrides-registration.test.ts +6 -2
  819. package/dist/extensions/pi-web-agent/src/extension.ts +1 -5
  820. package/dist/extensions/pi-zentui/.github/workflows/ci.yml +67 -4
  821. package/dist/extensions/pi-zentui/AGENTS.md +3 -2
  822. package/dist/extensions/pi-zentui/README.md +60 -32
  823. package/dist/extensions/pi-zentui/docs/configuration.md +662 -51
  824. package/dist/extensions/pi-zentui/docs/footer-format.md +174 -5
  825. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +15 -4
  826. package/dist/extensions/pi-zentui/extensions/zentui/average-token-rate.ts +146 -0
  827. package/dist/extensions/pi-zentui/extensions/zentui/codex-quota-display.ts +34 -0
  828. package/dist/extensions/pi-zentui/extensions/zentui/codex-quota.ts +298 -0
  829. package/dist/extensions/pi-zentui/extensions/zentui/component-colors.ts +172 -0
  830. package/dist/extensions/pi-zentui/extensions/zentui/component-settings.ts +243 -0
  831. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +851 -114
  832. package/dist/extensions/pi-zentui/extensions/zentui/custom-value-colors.ts +93 -0
  833. package/dist/extensions/pi-zentui/extensions/zentui/custom-variable-demand.ts +61 -0
  834. package/dist/extensions/pi-zentui/extensions/zentui/custom-variable-format.ts +80 -0
  835. package/dist/extensions/pi-zentui/extensions/zentui/custom-variables.ts +152 -0
  836. package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +138 -23
  837. package/dist/extensions/pi-zentui/extensions/zentui/editor-mouse.ts +89 -0
  838. package/dist/extensions/pi-zentui/extensions/zentui/extension-status-controller.ts +148 -0
  839. package/dist/extensions/pi-zentui/extensions/zentui/extension-status-settings.ts +40 -0
  840. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +109 -1
  841. package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +203 -23
  842. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +80 -3
  843. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +1 -1
  844. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +432 -125
  845. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +92 -4
  846. package/dist/extensions/pi-zentui/extensions/zentui/git.ts +47 -30
  847. package/dist/extensions/pi-zentui/extensions/zentui/github-status.ts +575 -0
  848. package/dist/extensions/pi-zentui/extensions/zentui/host-project-changes.ts +25 -0
  849. package/dist/extensions/pi-zentui/extensions/zentui/host-template-values.ts +29 -0
  850. package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +53 -13
  851. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +778 -258
  852. package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +70 -5
  853. package/dist/extensions/pi-zentui/extensions/zentui/layered-editor.ts +55 -0
  854. package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +6 -1
  855. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata-demand.ts +72 -0
  856. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata-display.ts +21 -0
  857. package/dist/extensions/pi-zentui/extensions/zentui/live-metadata.ts +255 -0
  858. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +476 -36
  859. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-settings.ts +118 -0
  860. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-template.ts +177 -0
  861. package/dist/extensions/pi-zentui/extensions/zentui/omp-editor-frame.ts +148 -0
  862. package/dist/extensions/pi-zentui/extensions/zentui/omp-statusline.ts +569 -0
  863. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-lookups.ts +344 -0
  864. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-metrics.ts +91 -0
  865. package/dist/extensions/pi-zentui/extensions/zentui/omp-template-modes.ts +323 -0
  866. package/dist/extensions/pi-zentui/extensions/zentui/omp-ui.ts +210 -0
  867. package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +30 -12
  868. package/dist/extensions/pi-zentui/extensions/zentui/project-demand.ts +46 -0
  869. package/dist/extensions/pi-zentui/extensions/zentui/project-discovery.ts +58 -0
  870. package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +10 -3
  871. package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +37 -0
  872. package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +1 -0
  873. package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +119 -43
  874. package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +40 -29
  875. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1268 -722
  876. package/dist/extensions/pi-zentui/extensions/zentui/settings-keys.ts +59 -0
  877. package/dist/extensions/pi-zentui/extensions/zentui/settings-list-selection.ts +13 -0
  878. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +11 -4
  879. package/dist/extensions/pi-zentui/extensions/zentui/state.ts +24 -11
  880. package/dist/extensions/pi-zentui/extensions/zentui/style.ts +6 -0
  881. package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +113 -11
  882. package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +234 -46
  883. package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +20 -5
  884. package/dist/extensions/pi-zentui/extensions/zentui/token-rate.ts +313 -0
  885. package/dist/extensions/pi-zentui/extensions/zentui/turn-summary-format.ts +59 -0
  886. package/dist/extensions/pi-zentui/extensions/zentui/turn-summary-rate.ts +52 -0
  887. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +640 -172
  888. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +72 -34
  889. package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +219 -34
  890. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +495 -119
  891. package/dist/extensions/pi-zentui/omp.ts +157 -0
  892. package/dist/extensions/pi-zentui/package-lock.json +3762 -1507
  893. package/dist/extensions/pi-zentui/package.json +50 -12
  894. package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +13 -2
  895. package/dist/extensions/pi-zentui/test/average-token-rate.test.ts +297 -0
  896. package/dist/extensions/pi-zentui/test/behavior-compatibility.mjs +89 -0
  897. package/dist/extensions/pi-zentui/test/codex-quota-lifecycle.test.ts +284 -0
  898. package/dist/extensions/pi-zentui/test/codex-quota-rendering.test.ts +466 -0
  899. package/dist/extensions/pi-zentui/test/codex-quota.test.ts +440 -0
  900. package/dist/extensions/pi-zentui/test/component-colors.test.ts +554 -0
  901. package/dist/extensions/pi-zentui/test/component-settings.test.ts +364 -0
  902. package/dist/extensions/pi-zentui/test/config-component-independence.test.ts +402 -0
  903. package/dist/extensions/pi-zentui/test/config-extension-status-choice.test.ts +233 -0
  904. package/dist/extensions/pi-zentui/test/config-hidden-extension-status-colors.test.ts +118 -0
  905. package/dist/extensions/pi-zentui/test/config-hidden-extension-status-placement.test.ts +142 -0
  906. package/dist/extensions/pi-zentui/test/config-minimalist-templates.test.ts +240 -0
  907. package/dist/extensions/pi-zentui/test/config-template-variables.test.ts +85 -0
  908. package/dist/extensions/pi-zentui/test/config.test.ts +80 -17
  909. package/dist/extensions/pi-zentui/test/custom-value-colors-integration.test.ts +453 -0
  910. package/dist/extensions/pi-zentui/test/custom-value-colors.test.ts +399 -0
  911. package/dist/extensions/pi-zentui/test/custom-variable-alias-prototypes.test.ts +177 -0
  912. package/dist/extensions/pi-zentui/test/custom-variable-consumers.test.ts +194 -0
  913. package/dist/extensions/pi-zentui/test/custom-variable-demand.test.ts +169 -0
  914. package/dist/extensions/pi-zentui/test/custom-variable-probe-reservation.test.ts +141 -0
  915. package/dist/extensions/pi-zentui/test/custom-variables-lifecycle.integration.test.ts +365 -0
  916. package/dist/extensions/pi-zentui/test/custom-variables.test.ts +330 -0
  917. package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +121 -0
  918. package/dist/extensions/pi-zentui/test/editor-mouse.test.ts +507 -0
  919. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +247 -20
  920. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +204 -260
  921. package/dist/extensions/pi-zentui/test/extension-status-controller.test.ts +323 -0
  922. package/dist/extensions/pi-zentui/test/extension-status-lifecycle.integration.test.ts +466 -0
  923. package/dist/extensions/pi-zentui/test/extension-status-settings.test.ts +79 -0
  924. package/dist/extensions/pi-zentui/test/footer-alignment.test.ts +135 -0
  925. package/dist/extensions/pi-zentui/test/footer-authority.test.ts +69 -0
  926. package/dist/extensions/pi-zentui/test/footer-auto-compaction.test.ts +148 -0
  927. package/dist/extensions/pi-zentui/test/footer-format-cache.test.ts +186 -0
  928. package/dist/extensions/pi-zentui/test/footer-format.test.ts +154 -4
  929. package/dist/extensions/pi-zentui/test/footer-hidden.test.ts +312 -0
  930. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +22 -0
  931. package/dist/extensions/pi-zentui/test/footer-layout.test.ts +80 -0
  932. package/dist/extensions/pi-zentui/test/footer-thinking-level.test.ts +123 -0
  933. package/dist/extensions/pi-zentui/test/format.test.ts +75 -1
  934. package/dist/extensions/pi-zentui/test/git-probes.test.ts +182 -0
  935. package/dist/extensions/pi-zentui/test/github-status.test.ts +931 -0
  936. package/dist/extensions/pi-zentui/test/host-template-surfaces.test.ts +287 -0
  937. package/dist/extensions/pi-zentui/test/icons.test.ts +86 -16
  938. package/dist/extensions/pi-zentui/test/layered-editor.test.ts +53 -0
  939. package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +154 -7
  940. package/dist/extensions/pi-zentui/test/live-context.test.ts +45 -0
  941. package/dist/extensions/pi-zentui/test/live-metadata-lifecycle.integration.test.ts +963 -0
  942. package/dist/extensions/pi-zentui/test/live-metadata.test.ts +767 -0
  943. package/dist/extensions/pi-zentui/test/minimalist-cache-hit-colors.test.ts +183 -0
  944. package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +222 -4
  945. package/dist/extensions/pi-zentui/test/minimalist-settings.test.ts +103 -0
  946. package/dist/extensions/pi-zentui/test/minimalist-templates.test.ts +489 -0
  947. package/dist/extensions/pi-zentui/test/omp-editor-composers.test.ts +324 -0
  948. package/dist/extensions/pi-zentui/test/omp-statusline.test.ts +579 -0
  949. package/dist/extensions/pi-zentui/test/omp-template-lookups.test.ts +630 -0
  950. package/dist/extensions/pi-zentui/test/omp-template-metrics.test.ts +72 -0
  951. package/dist/extensions/pi-zentui/test/omp-template-modes.test.ts +557 -0
  952. package/dist/extensions/pi-zentui/test/omp-ui.test.ts +93 -0
  953. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  954. package/dist/extensions/pi-zentui/test/pi-version-policy.test.ts +37 -0
  955. package/dist/extensions/pi-zentui/test/project-demand-integration.test.ts +542 -0
  956. package/dist/extensions/pi-zentui/test/project-discovery.test.ts +412 -0
  957. package/dist/extensions/pi-zentui/test/project-refresh.test.ts +44 -0
  958. package/dist/extensions/pi-zentui/test/project-state.test.ts +38 -1
  959. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +7 -7
  960. package/dist/extensions/pi-zentui/test/runtime.test.ts +127 -2
  961. package/dist/extensions/pi-zentui/test/selesai-v030-gate.json +939 -0
  962. package/dist/extensions/pi-zentui/test/settings-command.test.ts +1330 -82
  963. package/dist/extensions/pi-zentui/test/settings-keys.test.ts +41 -0
  964. package/dist/extensions/pi-zentui/test/settings-list-selection.test.ts +88 -0
  965. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +4 -1
  966. package/dist/extensions/pi-zentui/test/state.test.ts +15 -1
  967. package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +214 -12
  968. package/dist/extensions/pi-zentui/test/telemetry-snapshot.test.ts +246 -0
  969. package/dist/extensions/pi-zentui/test/thinking-experimental-cache.test.ts +246 -0
  970. package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +22 -0
  971. package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +113 -51
  972. package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +576 -76
  973. package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +23 -0
  974. package/dist/extensions/pi-zentui/test/token-rate.test.ts +450 -0
  975. package/dist/extensions/pi-zentui/test/turn-summary-format.test.ts +207 -0
  976. package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +72 -5
  977. package/dist/extensions/pi-zentui/test/turn-summary-rate.test.ts +158 -0
  978. package/dist/extensions/pi-zentui/test/usage-lifecycle-integration.test.ts +421 -0
  979. package/dist/extensions/pi-zentui/test/usage-snapshot.test.ts +154 -0
  980. package/dist/extensions/pi-zentui/test/user-message-cache.test.ts +239 -0
  981. package/dist/extensions/pi-zentui/test/user-message-native-reduced-capabilities.test.ts +61 -0
  982. package/dist/extensions/pi-zentui/test/user-message-native.test.ts +447 -0
  983. package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +2 -2
  984. package/dist/extensions/pi-zentui/test/working-line-editor-placement.test.ts +317 -0
  985. package/dist/extensions/pi-zentui/test/working-line-generation-cache.test.ts +238 -0
  986. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +674 -179
  987. package/dist/extensions/pi-zentui/test/working-line-message-ownership.test.ts +239 -0
  988. package/dist/extensions/pi-zentui/test/working-line-metric-animation.test.ts +284 -0
  989. package/dist/extensions/pi-zentui/test/working-line-placement.test.ts +298 -0
  990. package/dist/extensions/pi-zentui/test/working-line.test.ts +386 -18
  991. package/dist/extensions/pi-zentui/test/wrapped-editor-consumers.integration.test.ts +579 -0
  992. package/dist/extensions/pi-zentui/test/wrapped-editor-usage.integration.test.ts +298 -0
  993. package/dist/extensions/pi-zentui/tsconfig.json +1 -1
  994. package/dist/extensions/pi-zentui/vitest.host.config.ts +33 -0
  995. package/dist/extensions/question/index.ts +1 -5
  996. package/dist/extensions/rtk.test.ts +28 -15
  997. package/dist/extensions/rtk.ts +22 -22
  998. package/dist/extensions/skills-index/index.test.ts +99 -0
  999. package/dist/extensions/skills-index/index.ts +105 -0
  1000. package/dist/extensions/skills-index/package.json +9 -0
  1001. package/dist/extensions/tokenin-onboarding.ts +48 -208
  1002. package/dist/extensions/tool-search/index.d.ts +11 -0
  1003. package/dist/extensions/tool-search/index.js +14 -0
  1004. package/dist/extensions/tool-search/tool.d.ts +91 -0
  1005. package/dist/extensions/tool-search/tool.js +214 -0
  1006. package/dist/index.d.ts +17 -9
  1007. package/dist/index.js +11 -5
  1008. package/dist/main.d.ts +4 -0
  1009. package/dist/main.js +106 -42
  1010. package/dist/migrations.js +4 -3
  1011. package/dist/modes/index.d.ts +1 -0
  1012. package/dist/modes/interactive/bug-report.js +4 -0
  1013. package/dist/modes/interactive/components/armin.d.ts +3 -0
  1014. package/dist/modes/interactive/components/armin.js +7 -5
  1015. package/dist/modes/interactive/components/assistant-message.js +1 -1
  1016. package/dist/modes/interactive/components/auth-url.d.ts +13 -0
  1017. package/dist/modes/interactive/components/auth-url.js +36 -0
  1018. package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
  1019. package/dist/modes/interactive/components/bash-execution.js +17 -14
  1020. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
  1021. package/dist/modes/interactive/components/branch-summary-message.js +5 -2
  1022. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
  1023. package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
  1024. package/dist/modes/interactive/components/config-selector.js +13 -3
  1025. package/dist/modes/interactive/components/custom-editor.js +7 -0
  1026. package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
  1027. package/dist/modes/interactive/components/custom-entry.js +8 -2
  1028. package/dist/modes/interactive/components/custom-message.js +1 -0
  1029. package/dist/modes/interactive/components/first-time-setup.d.ts +4 -4
  1030. package/dist/modes/interactive/components/first-time-setup.js +8 -2
  1031. package/dist/modes/interactive/components/footer.d.ts +7 -0
  1032. package/dist/modes/interactive/components/footer.js +44 -4
  1033. package/dist/modes/interactive/components/index.d.ts +0 -1
  1034. package/dist/modes/interactive/components/index.js +0 -1
  1035. package/dist/modes/interactive/components/login-dialog.d.ts +2 -0
  1036. package/dist/modes/interactive/components/login-dialog.js +11 -5
  1037. package/dist/modes/interactive/components/mermaid.d.ts +10 -0
  1038. package/dist/modes/interactive/components/mermaid.js +74 -0
  1039. package/dist/modes/interactive/components/model-selector.d.ts +14 -9
  1040. package/dist/modes/interactive/components/model-selector.js +79 -31
  1041. package/dist/modes/interactive/components/oauth-selector.d.ts +8 -2
  1042. package/dist/modes/interactive/components/oauth-selector.js +24 -21
  1043. package/dist/modes/interactive/components/pi-logo.d.ts +18 -0
  1044. package/dist/modes/interactive/components/pi-logo.js +36 -0
  1045. package/dist/modes/interactive/components/radius-login-selector.d.ts +14 -0
  1046. package/dist/modes/interactive/components/radius-login-selector.js +84 -0
  1047. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  1048. package/dist/modes/interactive/components/scoped-models-selector.js +26 -0
  1049. package/dist/modes/interactive/components/settings-selector.d.ts +8 -11
  1050. package/dist/modes/interactive/components/settings-selector.js +60 -102
  1051. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
  1052. package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
  1053. package/dist/modes/interactive/components/themed-text.d.ts +16 -0
  1054. package/dist/modes/interactive/components/themed-text.js +28 -0
  1055. package/dist/modes/interactive/components/tool-execution.d.ts +11 -18
  1056. package/dist/modes/interactive/components/tool-execution.js +34 -47
  1057. package/dist/modes/interactive/components/tree-selector.js +7 -0
  1058. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  1059. package/dist/modes/interactive/components/user-message.js +14 -6
  1060. package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
  1061. package/dist/modes/interactive/components/visual-truncate.js +39 -6
  1062. package/dist/modes/interactive/external-editor.js +2 -1
  1063. package/dist/modes/interactive/interactive-mode.d.ts +41 -8
  1064. package/dist/modes/interactive/interactive-mode.js +854 -492
  1065. package/dist/modes/interactive/program-status-reporter.d.ts +33 -0
  1066. package/dist/modes/interactive/program-status-reporter.js +109 -0
  1067. package/dist/modes/interactive/theme/dark.json +59 -72
  1068. package/dist/modes/interactive/theme/light.json +57 -69
  1069. package/dist/modes/interactive/theme/system-theme.d.ts +54 -0
  1070. package/dist/modes/interactive/theme/system-theme.js +545 -0
  1071. package/dist/modes/interactive/theme/theme-controller.d.ts +46 -6
  1072. package/dist/modes/interactive/theme/theme-controller.js +127 -47
  1073. package/dist/modes/interactive/theme/theme-json.d.ts +1 -0
  1074. package/dist/modes/interactive/theme/theme-json.js +3 -1
  1075. package/dist/modes/interactive/theme/theme-schema.json +7 -2
  1076. package/dist/modes/interactive/theme/theme.d.ts +66 -39
  1077. package/dist/modes/interactive/theme/theme.js +243 -320
  1078. package/dist/modes/interactive/tui-renderer.d.ts +2 -1
  1079. package/dist/modes/interactive/tui-renderer.js +1 -0
  1080. package/dist/modes/json-event.d.ts +33 -0
  1081. package/dist/modes/json-event.js +28 -0
  1082. package/dist/modes/print-mode.js +12 -2
  1083. package/dist/modes/rpc/rpc-client.d.ts +11 -19
  1084. package/dist/modes/rpc/rpc-client.js +52 -35
  1085. package/dist/modes/rpc/rpc-mode.js +11 -26
  1086. package/dist/modes/rpc/rpc-types.d.ts +12 -23
  1087. package/dist/package-manager-cli.js +16 -5
  1088. package/dist/rpc-entry.js +1 -0
  1089. package/dist/skills/pi-subagents/SKILL.md +109 -35
  1090. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +38 -238
  1091. package/dist/skills/pi-subagents/references/execution-controls.md +181 -83
  1092. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +9 -9
  1093. package/dist/skills/pi-subagents/references/multi-lane-orchestration.md +24 -2
  1094. package/dist/skills/pi-subagents/references/prompting-and-roles.md +121 -91
  1095. package/dist/skills/pi-subagents/references/review-and-validation.md +73 -0
  1096. package/dist/utils/ansi.d.ts +8 -0
  1097. package/dist/utils/ansi.js +30 -11
  1098. package/dist/utils/clipboard-image.js +14 -17
  1099. package/dist/utils/clipboard.d.ts +2 -0
  1100. package/dist/utils/clipboard.js +17 -11
  1101. package/dist/utils/frontmatter.js +2 -1
  1102. package/dist/utils/image-convert.d.ts +12 -0
  1103. package/dist/utils/image-convert.js +45 -6
  1104. package/dist/utils/image-resize-core.d.ts +12 -0
  1105. package/dist/utils/image-resize-core.js +5 -0
  1106. package/dist/utils/image-resize-worker.js +3 -2
  1107. package/dist/utils/image-resize.js +9 -10
  1108. package/dist/utils/mime.js +1 -1
  1109. package/dist/utils/output-files.d.ts +14 -0
  1110. package/dist/utils/output-files.js +29 -0
  1111. package/dist/utils/paths.d.ts +6 -3
  1112. package/dist/utils/paths.js +28 -5
  1113. package/dist/utils/shell.d.ts +1 -3
  1114. package/dist/utils/shell.js +3 -30
  1115. package/dist/utils/syntax-highlight.d.ts +1 -0
  1116. package/dist/utils/syntax-highlight.js +68 -2
  1117. package/dist/utils/tool-result-images.d.ts +20 -0
  1118. package/dist/utils/tool-result-images.js +47 -0
  1119. package/dist/utils/tools-manager.d.ts +5 -0
  1120. package/dist/utils/tools-manager.js +6 -6
  1121. package/dist/utils/version-check.d.ts +4 -0
  1122. package/dist/utils/version-check.js +20 -2
  1123. package/docs/cli-integration.md +104 -0
  1124. package/docs/cli.md +333 -0
  1125. package/docs/codemode.md +230 -0
  1126. package/docs/configuration.md +47 -0
  1127. package/docs/environment-variables.md +5 -0
  1128. package/docs/extensions.md +107 -30
  1129. package/docs/json.md +8 -4
  1130. package/docs/keybindings.md +10 -10
  1131. package/docs/mcp.md +262 -0
  1132. package/docs/models.md +104 -3
  1133. package/docs/providers.md +20 -2
  1134. package/docs/rpc.md +13 -12
  1135. package/docs/sdk.md +41 -33
  1136. package/docs/security.md +21 -20
  1137. package/docs/settings.md +18 -46
  1138. package/docs/terminal-setup.md +14 -0
  1139. package/docs/virtual-models.md +114 -0
  1140. package/examples/extensions/jev-router.ts +113 -0
  1141. package/examples/extensions/tokenin-usage-widget.ts +124 -0
  1142. package/examples/rpc-client.ts +35 -0
  1143. package/examples/sdk/14-codemode-mcp.ts +67 -0
  1144. package/package.json +15 -8
  1145. package/dist/core/agent-session-auto-handoff.test.js +0 -195
  1146. package/dist/core/built-in-extensions.d.ts +0 -2
  1147. package/dist/core/built-in-extensions.js +0 -2
  1148. package/dist/core/llama/provider.js +0 -143
  1149. package/dist/core/settings-manager-auto-handoff.test.d.ts +0 -1
  1150. package/dist/core/settings-manager-auto-handoff.test.js +0 -53
  1151. package/dist/extensions/capability-gateway/catalog.ts +0 -177
  1152. package/dist/extensions/capability-gateway/index.test.ts +0 -226
  1153. package/dist/extensions/capability-gateway/index.ts +0 -669
  1154. package/dist/extensions/capability-gateway/integration.test.ts +0 -803
  1155. package/dist/extensions/capability-gateway/package.json +0 -16
  1156. package/dist/extensions/capability-gateway/routing.test.ts +0 -574
  1157. package/dist/extensions/capability-gateway/routing.ts +0 -403
  1158. package/dist/extensions/pi-subagents/runner-server-preload.mjs +0 -13
  1159. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +0 -89
  1160. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +0 -259
  1161. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +0 -312
  1162. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +0 -374
  1163. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +0 -69
  1164. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +0 -307
  1165. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +0 -211
  1166. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +0 -3
  1167. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +0 -2
  1168. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +0 -17
  1169. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +0 -57
  1170. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +0 -16
  1171. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +0 -15
  1172. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +0 -18
  1173. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +0 -9
  1174. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +0 -10
  1175. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +0 -10
  1176. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +0 -20
  1177. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +0 -3
  1178. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +0 -96
  1179. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +0 -119
  1180. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +0 -85
  1181. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +0 -620
  1182. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +0 -5
  1183. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +0 -56
  1184. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +0 -32
  1185. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +0 -49
  1186. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +0 -93
  1187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +0 -128
  1188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +0 -7
  1189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +0 -38
  1190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +0 -3
  1191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +0 -15
  1192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +0 -9
  1193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +0 -29
  1194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +0 -2
  1195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +0 -22
  1196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +0 -37
  1197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +0 -34
  1198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +0 -12
  1199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +0 -23
  1200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +0 -171
  1201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +0 -1261
  1202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +0 -20
  1203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +0 -54
  1204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +0 -22
  1205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +0 -13
  1206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +0 -358
  1207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +0 -77
  1208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +0 -512
  1209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +0 -43
  1210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +0 -121
  1211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +0 -82
  1212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +0 -17
  1213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +0 -7
  1214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +0 -210
  1215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +0 -15
  1216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +0 -54
  1217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +0 -4
  1218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +0 -55
  1219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +0 -2
  1220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +0 -5
  1221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +0 -30
  1222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +0 -206
  1223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +0 -15
  1224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +0 -106
  1225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +0 -19
  1226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +0 -356
  1227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +0 -297
  1228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +0 -60
  1229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +0 -15
  1230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +0 -18
  1231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +0 -28
  1232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +0 -3
  1233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +0 -10
  1234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +0 -68
  1235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +0 -106
  1236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +0 -51
  1237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +0 -6
  1238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +0 -35
  1239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +0 -37
  1240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +0 -40
  1241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +0 -37
  1242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +0 -52
  1243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +0 -10
  1244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +0 -35
  1245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +0 -24
  1246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +0 -14
  1247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +0 -70
  1248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +0 -26
  1249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +0 -36
  1250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +0 -19
  1251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +0 -61
  1252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +0 -23
  1253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +0 -3
  1254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +0 -10
  1255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +0 -42
  1256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +0 -35
  1257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +0 -12
  1258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +0 -33
  1259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +0 -9
  1260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +0 -34
  1261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +0 -22
  1262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +0 -34
  1263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
  1264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
  1265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
  1266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +0 -102
  1267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
  1268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +0 -21
  1269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +0 -19
  1270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +0 -20
  1271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  1272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +0 -12
  1273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
  1274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  1275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +0 -22
  1276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +0 -23
  1277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +0 -26
  1278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +0 -25
  1279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +0 -32
  1280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +0 -34
  1281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +0 -13
  1282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +0 -52
  1283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +0 -54
  1284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +0 -33
  1285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
  1286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
  1287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +0 -95
  1288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +0 -77
  1289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
  1290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
  1291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +0 -28
  1292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +0 -11
  1293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
  1294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +0 -63
  1295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +0 -94
  1296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +0 -23
  1297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
  1298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +0 -14
  1299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +0 -24
  1300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +0 -394
  1301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +0 -12
  1302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +0 -29
  1303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +0 -120
  1304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +0 -28
  1305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +0 -17
  1306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +0 -246
  1307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +0 -20
  1308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +0 -457
  1309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +0 -8
  1310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +0 -3
  1311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +0 -2
  1312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +0 -22
  1313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +0 -18
  1314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +0 -11
  1315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +0 -11
  1316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +0 -4
  1317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +0 -4
  1318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +0 -5
  1319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +0 -8
  1320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +0 -5
  1321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +0 -26
  1322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +0 -7
  1323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +0 -10
  1324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +0 -18
  1325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +0 -30
  1326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +0 -2
  1327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +0 -16
  1328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +0 -3
  1329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +0 -3
  1330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +0 -9
  1331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +0 -31
  1332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +0 -21
  1333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +0 -2
  1334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +0 -31
  1335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +0 -5
  1336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +0 -12
  1337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +0 -3
  1338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +0 -15
  1339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +0 -3
  1340. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +0 -31
  1341. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +0 -74
  1342. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +0 -150
  1343. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +0 -766
  1344. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +0 -327
  1345. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +0 -309
  1346. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +0 -1093
  1347. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +0 -94
  1348. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +0 -1329
  1349. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +0 -153
  1350. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +0 -457
  1351. package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +0 -669
  1352. package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +0 -51
  1353. package/dist/modes/interactive/components/daxnuts.d.ts +0 -22
  1354. package/dist/modes/interactive/components/daxnuts.js +0 -139
  1355. /package/dist/core/{agent-session-auto-handoff.test.d.ts → agent-session-custom-tools.test.d.ts} +0 -0
  1356. /package/dist/{core → extensions}/llama/huggingface.d.ts +0 -0
  1357. /package/dist/{core → extensions}/llama/huggingface.js +0 -0
  1358. /package/dist/{core → extensions}/llama/index.d.ts +0 -0
  1359. /package/dist/{core → extensions}/llama/ui.d.ts +0 -0
  1360. /package/dist/{core → extensions}/llama/ui.js +0 -0
@@ -3,8 +3,8 @@ import * as fs from "node:fs";
3
3
  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
- import { discoverAgents, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope, type UnknownAgentDiagnosticContext } from "../../agents/agents.ts";
7
- import { getArtifactsDir, getChainRunsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
6
+ import { discoverAgents, discoverAgentsAll, findBlockingAgentDiagnostic, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig, type AgentDiscoveryDiagnostic, type AgentScope, type UnknownAgentDiagnosticContext } from "../../agents/agents.ts";
7
+ import { getArtifactsDir, getProjectArtifactPackagingWarning, getProjectSubagentsDir } from "../../shared/artifacts.ts";
8
8
  import { writeAtomicJson } from "../../shared/atomic-json.ts";
9
9
  import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilient-json.ts";
10
10
  import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
@@ -24,12 +24,17 @@ import { handleManagementAction } from "../../agents/agent-management.ts";
24
24
  import { handleRefinementAction } from "../../agents/agent-refinements.ts";
25
25
  import { buildDoctorReport } from "../../extension/doctor.ts";
26
26
  import { readSubagentGuide } from "../../extension/subagent-guide.ts";
27
- import { normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } from "../../extension/public-execution.ts";
27
+ import { isWorkflowScriptPath, normalizePublicSubagentExecution, validateWorkflowCapacityOverrides } from "../../extension/public-execution.ts";
28
+ import { readReplyWorkflowScript } from "../../extension/reply-workflow-script.ts";
29
+ import { disabledFeatureNotice, disabledFeatureUseError, resolveDisabledFeatureSurface, type DisabledFeatureSurface } from "../../shared/disabled-features.ts";
28
30
  import { runSync } from "./execution.ts";
31
+ import { commandAction } from "./command-action.ts";
29
32
  import { handleWatchdogToolAction, WATCHDOG_TOOL_ACTIONS } from "../../watchdog/tool-actions.ts";
30
33
  import type { MainWatchdogRuntime } from "../../watchdog/runtime.ts";
31
34
  import { applyWatchdogLaunchRules } from "../../watchdog/rules.ts";
32
- import { buildModelCandidates, normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, type ModelOrigin, type ParentModel } from "../shared/model-fallback.ts";
35
+ import { childWatchdogProgressForModel } from "../../watchdog/child-status.ts";
36
+ import { normalizeParentModel, resolveEffectiveSubagentModel, resolveModelOrigin, resolveModelSelection, scopedModelIdsFromContext, type ModelOrigin, type ParentModel } from "../shared/model-resolution.ts";
37
+ import { projectChainOutputSchemas, resolveEffectiveOutputSchema } from "../shared/child-launch-plan.ts";
33
38
  import { formatRetainedChildren, listRetainedChildren } from "../background/retained-children.ts";
34
39
  import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
35
40
  import { recordRun } from "../shared/run-history.ts";
@@ -53,27 +58,29 @@ import { isScheduledRunAction } from "../background/scheduled-runs.ts";
53
58
  import { encodeIndexSegment } from "../background/index-segment.ts";
54
59
  import { enqueueChainAppendRequest, readPendingChainAppendRequests, runnerStepOutputNames } from "../background/chain-append.ts";
55
60
  import { ChainOutputValidationError, validateChainOutputBindingsWithContext } from "../shared/chain-outputs.ts";
56
- import { normalizeGateAcceptance, resolveAcceptanceReportMode, validateAcceptanceInput, validateExecutionAcceptance } from "../shared/acceptance.ts";
57
- import { canPreferFork, createForkContextResolver, forkedChildRequiresThinkingOff, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
61
+ import { acceptanceHasTypedVerify, normalizeGateAcceptance, resolveAcceptanceReportMode, TYPED_VERIFY_OUTPUT_SCHEMA_CONFLICT, validateAcceptanceInput, validateExecutionAcceptance, validateExecutionAcceptancePolicy } from "../shared/acceptance.ts";
62
+ import { canPreferFork, createForkContextResolver, resolveSubagentLaunchContext } from "../../shared/fork-context.ts";
58
63
  import { createPrunedForkSessionWriter } from "../../shared/pruned-fork.ts";
59
64
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
60
65
  import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
66
+ import { SUBAGENT_ASYNC_COMPLETE_EVENT } from "../../shared/types.ts";
61
67
  import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
62
68
  import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
63
69
  import { formatSpawnBudget, getSpawnBudgetSnapshot, grantSpawnBudget, preflightSpawnBudget, preflightSpawnBudgetGrant, reserveSpawnBudget } from "../shared/spawn-budget.ts";
64
70
  import { claimRunFanoutBatch, claimRunFanoutBatchWithCommit, createRunFanoutBudget, formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetDescriptor, RunFanoutLimitError, writeRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
65
71
  import { retainLiveForegroundNestedRoute } from "../../integrations/pi-web-session-liveness.ts";
72
+ import { HERDR_FOREGROUND_CONTROL_CHANGED_EVENT } from "../../integrations/herdr-status.ts";
66
73
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
67
74
  import { usageBudgetExceededMessage, usageBudgetState, validateUsageBudgetConfig } from "../shared/usage-budget.ts";
68
- import { intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
75
+ import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
69
76
  import { describeSubagentRouting, emitSubagentRoutingTelemetry, readSubagentRoutingSettings, routeSubagentLaunch, warnJevUnavailableOnce } from "../shared/jev-subagent-routing.ts";
70
77
  import { isAgentContract } from "../shared/agent-contract.ts";
71
78
  import { normalizeExtensionBindings, type ExtensionBindings } from "../shared/extension-bindings.ts";
79
+ import { assertRequiredChildExtensionsAdmitted, hasMandatoryRequiredChildExtensions, readRetainedRequiredChildExtensions, resolveRequiredChildExtensions, type RequiredChildExtensionSnapshot } from "../../shared/required-child-extensions.ts";
72
80
  import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, outputPathMappingFromTask, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
73
81
  import { assertJsonSchemaObject, cleanupStructuredOutputRuntime, createStructuredOutputRuntime } from "../shared/structured-output.ts";
74
- import { compactForegroundDetails, getSingleResultOutput, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
75
- import { createTaskMutationArbiter } from "../shared/llm-intent-arbiter.ts";
76
- import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.ts";
82
+ import { compactForegroundDetails, getSingleResultOutput, PROMPT_REDACTED, readStatus, resolveChildCwd, sumResultsCost, sumResultsUsage, toAgentToolUsage } from "../../shared/utils.ts";
83
+ import { discardPreservedWorktrees, formatParallelHandoffError, formatParallelHandoffReference, protectRetainedWorktreeForResume, formatStoredParallelHandoffCleanup, parallelHandoffPath, readParallelHandoffManifest, recordParallelHandoffMerge, recordParallelHandoffSupersession, writeParallelHandoffGroup, writeWorktreeSetupHandoff } from "../shared/parallel-handoff.ts";
77
84
  import { summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
78
85
  import {
79
86
  attachNestedChildrenToResultChildren,
@@ -97,16 +104,18 @@ import { dismissRecoveredWorkflow } from "./async-dismiss-action.ts";
97
104
  import { promotePausedWorkflowIfSettled, reconcileDetachedWorkflowChildCompletion } from "./workflow-detach-reconcile.ts";
98
105
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
99
106
  import { resolveAsyncRootResultPath, waitForImportedAsyncRoot } from "../background/chain-root-attachment.ts";
100
- import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
101
- import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, resolveNestedAsyncDir, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
107
+ import { awaitExistingAsyncRun, claimWorkflowAwaitedResult } from "../background/await-async-run.ts";
108
+ import { fallbackResultPayloadPathForSessionRun, removeResultIndex, resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
109
+ import { attachRootChildrenToSteps, createNestedRoute, findNestedControlResult, inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedRunScope, resolveNestedAsyncDir, retainNestedLookupRoute, snapshotNestedEventFiles, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedParentAddress, type NestedRoute, type NestedRunResolutionScope } from "../shared/nested-events.ts";
102
110
  import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
103
111
  import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
104
112
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
105
- import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, stopStoppableAsyncStatusChildren } from "../shared/child-identity.ts";
113
+ import { isStoppableAsyncStatusStep, resolveAsyncStatusChild } from "../shared/child-identity.ts";
106
114
  import { inspectSubagentStatus } from "../background/run-status.ts";
107
115
  import { getExternalJobProvider } from "../../api/external-job-provider.ts";
108
116
  import { externalJobFollowUpRequestDigest, externalJobFollowUpRequestId, externalJobFollowUpRunId, externalJobPromptDigest, externalJobStableJson } from "../shared/external-job-runner.ts";
109
117
  import { externalCliReceiptMetadata, normalizeExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
118
+ import { formatHerdrMachineRunnerUnsupported, resolveHerdrMachinePlacement } from "../shared/herdr-machine.ts";
110
119
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
111
120
  import { handleMissionAction, MISSION_ACTIONS } from "../../missions/actions.ts";
112
121
  import { attachMissionToLaunchResult, prepareMissionLaunch, writeMissionAsyncBinding, type MissionLaunchBinding } from "../../missions/lifecycle.ts";
@@ -114,11 +123,15 @@ import { MissionNotFoundError, updateMission } from "../../missions/store.ts";
114
123
  import type { MissionWorkflowChildUpdate } from "../../missions/types.ts";
115
124
  import { createMissionWorkflowState } from "../../missions/workflow-state.ts";
116
125
  import { resolveAuthorityDecision } from "../../policy/authority.ts";
117
- import { handleHerdrInspectorAction, HERDR_INSPECTOR_ACTIONS } from "../../inspectors/herdr/actions.ts";
126
+ import { handleInspectorAction, INSPECTOR_ACTIONS } from "../../inspectors/actions.ts";
127
+ import { getInspectorPlugins } from "../../inspectors/plugins.ts";
118
128
  import { handleHerdrProjectPaneAction, HERDR_PROJECT_PANE_ACTIONS } from "../../inspectors/herdr/project-panes.ts";
119
- import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
129
+ import { previewSimpleWorkflowRun, runWorkflowScript, validateWorkflowScript, WorkflowScriptError, type WorkflowChildSettledNotification, type WorkflowLanePlan, type WorkflowReceiptResumeReference, type WorkflowScriptChildResult, type WorkflowScriptTraceEntry, type WorkflowSteerOptions, type WorkflowSteerResult } from "../../workflows/scripted-workflow.ts";
130
+ import { formatIncrementalChildCompletion, incrementalChildCompletionTriggersTurn } from "../background/notify.ts";
131
+ import { appendWorkflowChildJournal, findWorkflowReuseSource, matchWorkflowReuse, workflowChildFingerprint, workflowScriptDigest, workflowStopCause, WORKFLOW_RUNTIME_REPLACED_RELAUNCH_NOTICE } from "../../workflows/workflow-reuse.ts";
120
132
  import { executeWorkflowHostCommand, resolveWorkflowHostOutputClaimPath, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "../../workflows/host-command.ts";
121
133
  import { buildWorkflowReceipt, readWorkflowReceipt, workflowReceiptPath, resolveWorkflowReceiptResumeEntry, writeWorkflowReceipt, type WorkflowReceipt, type WorkflowReceiptState } from "../../workflows/workflow-receipt.ts";
134
+ import { formatWorkflowKeyRevival, recordWorkflowRevival, withWorkflowRevivals } from "../../workflows/workflow-revival.ts";
122
135
  import { upsertHostStep, validHostStepNodes } from "../shared/host-step-status.ts";
123
136
  import { assertWorkflowLaneKey, normalizeWorkflowLaneMetadata } from "../shared/lane-metadata.ts";
124
137
  import { parseWorkflowChildSummary, workflowChildProgress, workflowChildSummary } from "../../workflows/workflow-child-summary.ts";
@@ -134,16 +147,19 @@ import {
134
147
  type WorkflowResourceAuthority,
135
148
  type WorkflowResourcePermit,
136
149
  } from "../../shared/workflow-child-permit.ts";
137
- import { resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
150
+ import { deepFreezeWorkflowArgs, normalizeWorkflowArgs, resolveStructuredWorkflowResource, resolveWorkflowResource } from "../../workflows/workflow-resources.ts";
151
+ import { stableJsonDigest } from "../../shared/launch-contract.ts";
138
152
  import {
139
153
  cleanupWorktrees,
140
154
  createWorktrees,
155
+ preflightWorktreeSource,
141
156
  withWorktreeTransaction,
142
157
  type WorktreeSetupProgress,
143
158
  diffWorktrees,
144
159
  formatWorktreeDiffSummary,
145
160
  type WorktreeSetup,
146
161
  } from "../shared/worktree.ts";
162
+ import { applyReviewedCleanupPlan, formatCleanupReceipt, loadReviewedCleanupPlan } from "../shared/worktree-cleanup-apply.ts";
147
163
  import { createWorktreeCleanupPlan, formatWorktreeCleanupPlan } from "../shared/worktree-cleanup-plan.ts";
148
164
  import {
149
165
  type AgentProgress,
@@ -188,23 +204,28 @@ import {
188
204
  type SubagentState,
189
205
  DIRS,
190
206
  DEFAULT_ARTIFACT_CONFIG,
207
+ DEFAULT_MAX_OUTPUT,
191
208
  DEFAULT_FORK_PREAMBLE,
192
209
  SUBAGENT_ACTIONS,
210
+ SUBAGENT_ASYNC_STARTED_EVENT,
211
+ SUBAGENT_CHILD_STATUS_EVENT,
193
212
  SUBAGENT_CONTROL_EVENT,
194
213
  SUBAGENT_CONTROL_INTERCOM_EVENT,
195
214
  SUBAGENT_FOREGROUND_COMPLETE_EVENT,
215
+ SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
196
216
  checkSubagentDepth,
197
217
  resolveChildMaxSubagentDepth,
198
218
  resolveCurrentMaxSubagentDepth,
199
219
  resolveMaxSubagentSpawnsPerRun,
220
+ truncateOutput,
200
221
  wrapForkTask,
201
222
  type ScheduleOrigin,
202
223
  type SteeringTargetState,
203
224
  } from "../../shared/types.ts";
204
225
  import { deriveChildSessionName } from "../../shared/child-session-name.ts";
205
226
 
206
- 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", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
207
- const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
227
+ const MUTATING_MANAGEMENT_ACTIONS = new Set(["command.yield", "command.cancel", "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", "worktree.cleanup", "lane.recordMerge", "lane.recordSupersession", "refine", "refine.rollback", "dismiss", "schedule.create", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
228
+ const DESTRUCTIVE_MANAGEMENT_ACTIONS = new Set(["command.cancel", "delete", "eject", "disable", "reset", "mission.close", "worktree.discard", "refine.rollback", "inspector.close", "project.close", "stop", "interrupt", "schedule.delete"]);
208
229
 
209
230
  function resolveSteerDeliveryMode(mode: SubagentParamsLike["mode"]): SteerDeliveryMode | undefined {
210
231
  return mode === "steer" || mode === "follow_up" || mode === "auto" ? mode : undefined;
@@ -235,16 +256,17 @@ function hasSingleAdjacentTransposition(left: string, right: string): boolean {
235
256
  && left.slice(mismatch + 2) === right.slice(mismatch + 2);
236
257
  }
237
258
 
238
- export function unknownSubagentActionMessage(action: string): string {
259
+ export function unknownSubagentActionMessage(action: string, disabled?: DisabledFeatureSurface): string {
239
260
  const requested = action.toLowerCase();
240
- const suggestion = SUBAGENT_ACTIONS.find((candidate) => {
261
+ const enabledActions = SUBAGENT_ACTIONS.filter((candidate) => !disabled?.actions.has(candidate));
262
+ const suggestion = enabledActions.find((candidate) => {
241
263
  const distance = editDistance(requested, candidate);
242
264
  const closeMatch = distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate);
243
265
  if (DESTRUCTIVE_MANAGEMENT_ACTIONS.has(candidate)) return distance === 1 && requested.length >= candidate.length - 1;
244
266
  return closeMatch;
245
267
  });
246
268
  const nextStep = 'Use subagent({ action: "status" }) to inspect runs or subagent({ action: "list" }) to inspect agents.';
247
- const validActions = `Valid: ${SUBAGENT_ACTIONS.join(", ")}.`;
269
+ const validActions = `Valid: ${enabledActions.join(", ")}.`;
248
270
  return suggestion
249
271
  ? `Unknown action: ${action}. Did you mean ${suggestion}? ${nextStep} ${validActions}`
250
272
  : `Unknown action: ${action}. ${nextStep} ${validActions}`;
@@ -285,6 +307,7 @@ interface TaskParam {
285
307
  agent: string;
286
308
  task: string;
287
309
  cwd?: string;
310
+ machine?: string;
288
311
  count?: number;
289
312
  output?: string | boolean;
290
313
  outputMode?: "inline" | "file-only";
@@ -293,7 +316,7 @@ interface TaskParam {
293
316
  model?: string;
294
317
  fast?: boolean;
295
318
  skill?: string | string[] | boolean;
296
- outputSchema?: JsonSchemaObject;
319
+ outputSchema?: JsonSchemaObject | false;
297
320
  acceptance?: AcceptanceInput;
298
321
  agentContract?: AgentContract;
299
322
  toolBudget?: ToolBudgetConfig;
@@ -309,6 +332,7 @@ export interface SubagentParamsLike {
309
332
  merge?: unknown;
310
333
  supersession?: unknown;
311
334
  index?: number;
335
+ toolCallId?: string;
312
336
  childId?: string;
313
337
  view?: "fleet" | "transcript";
314
338
  lines?: number;
@@ -328,8 +352,8 @@ export interface SubagentParamsLike {
328
352
  mode?: SteerDeliveryMode | "plan" | "apply";
329
353
  repo?: string;
330
354
  planId?: string;
355
+ /** Internal script carrier (slash, prompt-workflow, scheduled, RPC, named resources). The model-facing tool rejects it. */
331
356
  workflowScript?: string;
332
- workflowScriptPath?: string;
333
357
  globalConcurrencyLimit?: number;
334
358
  maxSubagentSpawnsPerRun?: number;
335
359
  preflight?: import("../../shared/types.ts").WorkflowPreflight;
@@ -355,7 +379,7 @@ export interface SubagentParamsLike {
355
379
  runFanoutAdmitted?: boolean;
356
380
  /** Internal inherited tool/agent ceiling for delegated child launches. */
357
381
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
358
- /** Internal durable-run compatibility fields. Public callers must use workflowScript. */
382
+ /** Internal durable-run compatibility fields. Public callers must use workflow. */
359
383
  chain?: ChainStep[];
360
384
  tasks?: TaskParam[];
361
385
  concurrency?: number;
@@ -369,6 +393,8 @@ export interface SubagentParamsLike {
369
393
  foregroundOnly?: boolean;
370
394
  timeoutMs?: number;
371
395
  maxRuntimeMs?: number;
396
+ /** Async runs only: steer the child to checkpoint and stop this many ms before the run deadline. */
397
+ checkpointBeforeDeadlineMs?: number;
372
398
  /** Optional hard per-tool-call timeout (ms). Known-fast tools also have a default. */
373
399
  toolTimeoutMs?: number;
374
400
  toolBudget?: ToolBudgetConfig;
@@ -378,6 +404,9 @@ export interface SubagentParamsLike {
378
404
  control?: ControlConfig;
379
405
  sessionDir?: string;
380
406
  cwd?: string;
407
+ machine?: string;
408
+ /** Internal: the typed cwd when a machine is set, kept as a remote path and never resolved locally. */
409
+ machineCwd?: string;
381
410
  maxOutput?: MaxOutputConfig;
382
411
  artifacts?: boolean;
383
412
  includeProgress?: boolean;
@@ -386,8 +415,8 @@ export interface SubagentParamsLike {
386
415
  modelOrigin?: ModelOrigin;
387
416
  fast?: boolean;
388
417
  thinking?: string | false;
389
- /** Public named workflow resource. Resolved before entering the workflow sandbox. */
390
- workflow?: string;
418
+ /** true = the ```js workflow block in the calling reply; a string containing "/" or "\" = script path; otherwise a named workflow resource. */
419
+ workflow?: string | true;
391
420
  args?: Record<string, unknown>;
392
421
  scope?: string;
393
422
  target?: string;
@@ -397,7 +426,7 @@ export interface SubagentParamsLike {
397
426
  /** Internal-only; not part of the public tool schema. Wired for single-run reads (chain steps use their own field). */
398
427
  reads?: string[] | false;
399
428
  outputMode?: "inline" | "file-only";
400
- outputSchema?: JsonSchemaObject;
429
+ outputSchema?: JsonSchemaObject | false;
401
430
  agentScope?: unknown;
402
431
  chainDir?: string;
403
432
  acceptance?: AcceptanceInput;
@@ -406,7 +435,8 @@ export interface SubagentParamsLike {
406
435
  at?: string;
407
436
  every?: string;
408
437
  sessionOnly?: boolean;
409
- on?: string | number;
438
+ quiet?: boolean;
439
+ on?: string[];
410
440
  timezone?: string;
411
441
  overlap?: "skip";
412
442
  catchUp?: "none" | "latest";
@@ -432,6 +462,7 @@ function rememberParentModel(state: { currentSessionId?: string | null; lastPare
432
462
 
433
463
  interface ExecutorDeps {
434
464
  pi: ExtensionAPI;
465
+ parentWake?: Pick<ExtensionAPI, "sendMessage">;
435
466
  state: SubagentState;
436
467
  config: ExtensionConfig;
437
468
  asyncByDefault: boolean;
@@ -443,6 +474,7 @@ interface ExecutorDeps {
443
474
  getSubagentSessionRoot: (parentSessionFile: string | null) => string;
444
475
  expandTilde: (p: string) => string;
445
476
  discoverAgents: (cwd: string, scope: AgentScope, preferredModelProvider?: string) => { agents: AgentConfig[]; agentDiagnostics?: AgentDiscoveryDiagnostic[]; modelScope?: ModelScopeConfig; maxThinking?: AgentConfig["maxThinking"]; cwd?: string; scope?: AgentScope; directories?: UnknownAgentDiagnosticContext["directories"] };
477
+ discoverAgentsAll?: typeof discoverAgentsAll;
446
478
  onAgentsChanged?: () => void;
447
479
  allowMutatingManagementActions?: boolean;
448
480
  activateSupervisorTransport?: () => void;
@@ -468,7 +500,7 @@ function inheritedRunFanoutBudget(deps: Pick<ExecutorDeps, "childRuntime">): Run
468
500
 
469
501
  type ForkSessionFileForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => string | undefined;
470
502
  type PrepareForkSessionForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => Promise<void>;
471
- type ForkThinkingOverrideForTask = (agentName: string, idx?: number, modelOverride?: string, modelOverrideFromParent?: boolean, modelOrigin?: ModelOrigin) => AgentConfig["thinking"] | undefined;
503
+ type ThinkingOverrideForTask = () => AgentConfig["thinking"] | undefined;
472
504
 
473
505
  interface ExecutionContextData {
474
506
  params: SubagentParamsLike;
@@ -488,7 +520,7 @@ interface ExecutionContextData {
488
520
  sessionDirForIndex: (idx?: number) => string;
489
521
  sessionFileForIndex: (idx?: number) => string | undefined;
490
522
  sessionFileForTask: ForkSessionFileForTask;
491
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
523
+ thinkingOverrideForTask: ThinkingOverrideForTask;
492
524
  artifactConfig: ArtifactConfig;
493
525
  artifactsDir: string;
494
526
  backgroundRequestedWhileClarifying: boolean;
@@ -507,11 +539,14 @@ interface ExecutionContextData {
507
539
  usageBudget?: UsageBudgetConfig;
508
540
  /** Parent workflow owns this allowance; presence vetoes unverifiable child continuation. */
509
541
  inheritedUsageBudget?: UsageBudgetConfig;
542
+ /** Mandatory host extensions retained by a parent workflow; they outlive a later registration disposal. */
543
+ requiredExtensions?: RequiredChildExtensionSnapshot;
510
544
  allowZeroToolBudget?: boolean;
511
545
  configToolBudget?: ResolvedToolBudget;
512
546
  contextPolicy: AgentDefaultContextPolicy;
513
547
  modelScope?: ModelScopeConfig;
514
548
  parentModel?: ParentModel;
549
+ scopedModelIds?: string[];
515
550
  parentSessionId: string | null;
516
551
  parentPiSessionId?: string;
517
552
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
@@ -525,18 +560,16 @@ function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefine
525
560
  return requestedCwd ? path.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
526
561
  }
527
562
 
528
- function loadWorkflowScriptPath(params: SubagentParamsLike, runtimeCwd: string): { params?: SubagentParamsLike; error?: string } {
529
- if (params.workflowScriptPath === undefined) return { params };
530
- const scriptPath = path.resolve(resolveRequestedCwd(runtimeCwd, params.cwd), params.workflowScriptPath);
531
- let workflowScript: string;
563
+ function readWorkflowScriptFile(requestedPath: string, requestedCwd: string | undefined, runtimeCwd: string): { script: string } | { error: string } {
564
+ const scriptPath = path.resolve(resolveRequestedCwd(runtimeCwd, requestedCwd), requestedPath);
565
+ let script: string;
532
566
  try {
533
- workflowScript = fs.readFileSync(scriptPath, "utf8");
567
+ script = fs.readFileSync(scriptPath, "utf8");
534
568
  } catch (error) {
535
- return { error: `Failed to read workflowScriptPath '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
569
+ return { error: `Failed to read workflow script '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
536
570
  }
537
- if (!workflowScript.trim()) return { error: `workflowScriptPath file '${scriptPath}' is empty.` };
538
- const { workflowScriptPath: _workflowScriptPath, ...rest } = params;
539
- return { params: { ...rest, workflowScript } };
571
+ if (!script.trim()) return { error: `Workflow script file '${scriptPath}' is empty.` };
572
+ return { script };
540
573
  }
541
574
 
542
575
  export function removeForegroundControlIfIdle(state: SubagentState, runId: string, trackRetainedNestedRoute?: (rootRunId: string) => void): boolean {
@@ -549,6 +582,7 @@ export function removeForegroundControlIfIdle(state: SubagentState, runId: strin
549
582
  console.error(`Failed to retain live nested descendants for foreground run '${runId}':`, error);
550
583
  }
551
584
  }
585
+ retainNestedLookupRoute(state, control?.nestedRoute, control?.sessionId);
552
586
  state.foregroundControls.delete(runId);
553
587
  if (state.lastForegroundControlId === runId) state.lastForegroundControlId = null;
554
588
  return true;
@@ -616,6 +650,11 @@ function trustedSessionRootsForStatus(ctx: ExtensionContext, deps: ExecutorDeps)
616
650
  return [...new Set(roots)];
617
651
  }
618
652
 
653
+ /** Children follow the launching session's trust; hosts older than Pi's trust concept keep Pi's default. */
654
+ function sessionProjectTrust(ctx: ExtensionContext): boolean | undefined {
655
+ return typeof ctx.isProjectTrusted === "function" ? ctx.isProjectTrusted() : undefined;
656
+ }
657
+
619
658
  function spawnBudgetErrorResult(message: string, mode: "single" | "parallel" | "chain"): AgentToolResult<Details> {
620
659
  return {
621
660
  content: [{ type: "text", text: message }],
@@ -743,7 +782,7 @@ function foregroundChildActivityFromProgress(progress: SingleResult["progress"]
743
782
  };
744
783
  }
745
784
 
746
- function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings }): void {
785
+ function rememberForegroundRun(state: SubagentState, input: { modelResponseAliases?: Record<string, string[]>; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; sessionId: string | null; results: SingleResult[]; params: SubagentParamsLike; effectiveOutput?: string | boolean; effectiveOutputMode: OutputMode; extensionBindings?: ExtensionBindings; requiredExtensions?: SteeringRecoveryDescriptor["requiredExtensions"] }): void {
747
786
  state.foregroundRuns ??= new Map();
748
787
  const previous = state.foregroundRuns.get(input.runId);
749
788
  const updatedAt = Date.now();
@@ -782,6 +821,7 @@ function rememberForegroundRun(state: SubagentState, input: { modelResponseAlias
782
821
  ...(result.error ? { error: result.error } : {}),
783
822
  ...(result.finalOutput ? { finalOutput: result.finalOutput } : {}),
784
823
  ...(result.outputState ? { outputState: result.outputState } : {}),
824
+ ...(result.outputPartial ? { outputPartial: true } : {}),
785
825
  ...(result.outputMode ? { outputMode: result.outputMode } : {}),
786
826
  ...(result.savedOutputPath ? { savedOutputPath: result.savedOutputPath } : {}),
787
827
  ...(result.outputSaveError ? { outputSaveError: result.outputSaveError } : {}),
@@ -796,6 +836,7 @@ function rememberForegroundRun(state: SubagentState, input: { modelResponseAlias
796
836
  ...(Object.keys(resumeContract).length ? { resumeContract } : {}),
797
837
  ...(result.launchContractDigest ? { launchContractDigest: result.launchContractDigest } : {}),
798
838
  ...(input.extensionBindings ? { extensionBindings: input.extensionBindings } : {}),
839
+ ...(input.requiredExtensions?.length ? { requiredExtensions: input.requiredExtensions } : {}),
799
840
  ...(result.launchResolvedExtensions ? { launchResolvedExtensions: result.launchResolvedExtensions } : {}),
800
841
  ...(result.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: result.runtimeAcknowledgedExtensions } : {}),
801
842
  ...(result.capabilityCeiling ? { capabilityCeiling: result.capabilityCeiling } : {}),
@@ -864,6 +905,7 @@ function updateRememberedForegroundChild(state: SubagentState, input: { runId: s
864
905
  ...(input.result.error ? { error: input.result.error } : {}),
865
906
  ...(input.result.finalOutput ? { finalOutput: input.result.finalOutput } : {}),
866
907
  outputState: input.result.outputState,
908
+ outputPartial: input.result.outputPartial,
867
909
  outputMode: input.result.outputMode,
868
910
  savedOutputPath: input.result.savedOutputPath,
869
911
  outputSaveError: input.result.outputSaveError,
@@ -947,6 +989,7 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
947
989
  ...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
948
990
  ...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
949
991
  ...(child.extensionBindings ? { extensionBindings: normalizeExtensionBindings(child.extensionBindings)!.value } : {}),
992
+ ...(child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {}),
950
993
  ...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
951
994
  };
952
995
  }
@@ -1237,10 +1280,14 @@ function appendStepToAsyncChain(input: {
1237
1280
  };
1238
1281
  }
1239
1282
  const chain = [input.params.step];
1240
- const acceptanceErrors = validateExecutionAcceptance({ ...input.params, chain } as Parameters<typeof validateExecutionAcceptance>[0]);
1241
- if (acceptanceErrors.length > 0) {
1283
+ const validationParams = { ...input.params, chain } as SubagentParamsLike;
1284
+ const acceptancePolicyErrors = validateExecutionAcceptancePolicy(validationParams);
1285
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(validationParams);
1286
+ const earlyErrors = [...acceptancePolicyErrors];
1287
+ if (outputSchemaError) earlyErrors.push(outputSchemaError);
1288
+ if (earlyErrors.length > 0) {
1242
1289
  return {
1243
- content: [{ type: "text", text: `Cannot append step: ${acceptanceErrors.join(" ")}` }],
1290
+ content: [{ type: "text", text: `Cannot append step: ${earlyErrors.join(" ")}` }],
1244
1291
  isError: true,
1245
1292
  details: { mode: "management", results: [] },
1246
1293
  };
@@ -1299,6 +1346,16 @@ function appendStepToAsyncChain(input: {
1299
1346
  };
1300
1347
  }
1301
1348
 
1349
+ let retainedRequiredExtensions: RequiredChildExtensionSnapshot | undefined;
1350
+ try {
1351
+ retainedRequiredExtensions = readRetainedRequiredChildExtensions(resolved.location.asyncDir);
1352
+ } catch (error) {
1353
+ return {
1354
+ content: [{ type: "text", text: `Cannot append step to run '${resolved.id}': ${error instanceof Error ? error.message : String(error)}` }],
1355
+ isError: true,
1356
+ details: { mode: "management", results: [] },
1357
+ };
1358
+ }
1302
1359
  const pendingAppendRequests = readPendingChainAppendRequests(resolved.location.asyncDir);
1303
1360
  const reservedOutputNames = new Set<string>([
1304
1361
  ...Object.keys(status.outputs ?? {}),
@@ -1322,6 +1379,14 @@ function appendStepToAsyncChain(input: {
1322
1379
  const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
1323
1380
  const discoveredForAppend = input.deps.discoverAgents(input.requestCwd, scope, input.parentModel?.provider);
1324
1381
  const agents = discoveredForAppend.agents;
1382
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(validationParams, agents));
1383
+ if (acceptanceErrors.length > 0) {
1384
+ return {
1385
+ content: [{ type: "text", text: `Cannot append step: ${acceptanceErrors.join(" ")}` }],
1386
+ isError: true,
1387
+ details: { mode: "management", results: [] },
1388
+ };
1389
+ }
1325
1390
  const contextPolicy = resolveExplicitContextPolicy(input.params);
1326
1391
  const chainSkillInput = normalizeSkillInput(input.params.skill);
1327
1392
  const chainSkills = chainSkillInput === false ? [] : (chainSkillInput ?? []);
@@ -1333,6 +1398,7 @@ function appendStepToAsyncChain(input: {
1333
1398
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1334
1399
  currentModelProvider: parentModel?.provider,
1335
1400
  currentModel: parentModel,
1401
+ scopedModelIds: scopedModelIdsFromContext(input.ctx),
1336
1402
  modelScope: discoveredForAppend.modelScope,
1337
1403
  modelResponseAliases: input.deps.config.modelResponseAliases,
1338
1404
  interactive: input.ctx.hasUI,
@@ -1360,6 +1426,7 @@ function appendStepToAsyncChain(input: {
1360
1426
  asyncDir: resolved.location.asyncDir,
1361
1427
  validateOutputBindings: false,
1362
1428
  capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.currentSessionId)),
1429
+ requiredExtensions: retainedRequiredExtensions,
1363
1430
  }));
1364
1431
  if ("error" in built) {
1365
1432
  return {
@@ -1490,6 +1557,51 @@ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "neste
1490
1557
  });
1491
1558
  }
1492
1559
 
1560
+ function resolveNestedExternalJobResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, params: SubagentParamsLike, deps: ExecutorDeps): AsyncResumeSourceTarget | undefined {
1561
+ const run = match.match.run;
1562
+ if (run.state === "stopped") return undefined;
1563
+ const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
1564
+ if (!asyncDir) return undefined;
1565
+ const status = readNestedRunStatus(asyncDir, run);
1566
+ const steps = status?.steps ?? [];
1567
+ const step = steps[params.index ?? 0];
1568
+ if (!step) {
1569
+ // An index outside an external-job run gets the async resolver's index error instead of a Pi revive.
1570
+ if (params.index === undefined || !steps.some((candidate) => candidate.runner?.type === "external-job")) return undefined;
1571
+ } else if (!step.runner) {
1572
+ // Pi steps record no runner, and neither does a launch status before the runner starts.
1573
+ // A session file means a Pi run; otherwise the agent definition says what the step runs.
1574
+ if (nestedRunSessionFile(run)) return undefined;
1575
+ const agent = deps.discoverAgents(status?.cwd ?? deps.state.baseCwd, resolveExecutionAgentScope(params.agentScope)).agents.find((candidate) => candidate.name === step.agent);
1576
+ if (agent?.runner?.type === "external-job") throw new Error(externalJobWithoutMetadataMessage(run.id));
1577
+ return undefined;
1578
+ } else if (step.runner.type !== "external-job") {
1579
+ return undefined;
1580
+ }
1581
+ // External-job runs have no Pi session, so resume them as a provider follow-up from their own run directory.
1582
+ const target = resolveAsyncResumeTarget(compactOptional<Parameters<typeof resolveAsyncResumeTarget>[0]>({ dir: asyncDir, index: params.index }), nestedRunScope(match.match.rootRunId), { requireSessionFile: false });
1583
+ if (target.kind === "live") throw new Error(externalJobStillRunningMessage(target.runId));
1584
+ return { source: "async", ...target };
1585
+ }
1586
+
1587
+ function readNestedRunStatus(asyncDir: string, run: NestedRunSummary): AsyncStatus | null {
1588
+ try {
1589
+ return readStatus(asyncDir);
1590
+ } catch (error) {
1591
+ // A nested Pi run revives from its session file, so its unreadable status file must not block it.
1592
+ if (nestedRunSessionFile(run)) return null;
1593
+ throw error;
1594
+ }
1595
+ }
1596
+
1597
+ function externalJobStillRunningMessage(runId: string): string {
1598
+ return `External-job run '${runId}' is still running. Wait for completion, then use subagent({ action: "resume", id: "${runId}", message: "..." }).`;
1599
+ }
1600
+
1601
+ function externalJobWithoutMetadataMessage(runId: string): string {
1602
+ return `External-job run '${runId}' has no persisted provider metadata. Cannot follow up without the parent provider job id.`;
1603
+ }
1604
+
1493
1605
  async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, ignoredFiles: ReadonlySet<string>, timeoutMs = 1_000) {
1494
1606
  const deadline = Date.now() + timeoutMs;
1495
1607
  while (Date.now() < deadline) {
@@ -1652,12 +1764,12 @@ async function resumeExternalJobFollowUp(input: {
1652
1764
  absoluteDeadlineAt?: number;
1653
1765
  }): Promise<AgentToolResult<Details>> {
1654
1766
  if (input.target.kind === "live" || input.target.state === "running" || input.target.state === "queued") {
1655
- return { content: [{ type: "text", text: `External-job run '${input.target.runId}' is still running. Wait for completion, then use subagent({ action: "resume", id: "${input.target.runId}", message: "..." }).` }], isError: true, details: { mode: "management", results: [] } };
1767
+ return { content: [{ type: "text", text: externalJobStillRunningMessage(input.target.runId) }], isError: true, details: { mode: "management", results: [] } };
1656
1768
  }
1657
1769
  const runner = input.target.runner;
1658
1770
  const externalJob = input.target.externalJob;
1659
1771
  if (runner?.type !== "external-job") return { content: [{ type: "text", text: "Internal error: external-job follow-up was requested for a non-external-job runner." }], isError: true, details: { mode: "management", results: [] } };
1660
- if (!externalJob) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has no persisted provider metadata. Cannot follow up without the parent provider job id.` }], isError: true, details: { mode: "management", results: [] } };
1772
+ if (!externalJob) return { content: [{ type: "text", text: externalJobWithoutMetadataMessage(input.target.runId) }], isError: true, details: { mode: "management", results: [] } };
1661
1773
  if (externalJob.provider !== runner.provider) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has mismatched provider metadata. Refusing to follow up.` }], isError: true, details: { mode: "management", results: [] } };
1662
1774
  if (!externalJobOptionsEqual(externalJob.options, runner.options)) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has mismatched provider options. Refusing to follow up.` }], isError: true, details: { mode: "management", results: [] } };
1663
1775
  if (!externalJob.providerJobId) return { content: [{ type: "text", text: `External-job run '${input.target.runId}' has no parent provider job id. Cannot follow up without reopening or redispatching, so this fails closed.` }], isError: true, details: { mode: "management", results: [] } };
@@ -1669,10 +1781,13 @@ async function resumeExternalJobFollowUp(input: {
1669
1781
  const requestDigest = externalJobFollowUpRequestDigest({ provider: runner.provider, parentProviderJobId: externalJob.providerJobId, promptDigest, options: runner.options });
1670
1782
  const requestId = externalJobFollowUpRequestId(requestDigest);
1671
1783
  const runId = externalJobFollowUpRunId(requestDigest);
1672
- const asyncDir = path.join(DIRS.async, runId);
1784
+ // A child launches the follow-up under its nested route, so look for an earlier one where it would be.
1785
+ const nestedRoute = inheritedNestedRoute(input.deps);
1786
+ const runRoots = nestedRoute ? nestedRunScope(nestedRoute.rootRunId) : { asyncDirRoot: DIRS.async, resultsDir: DIRS.results };
1787
+ const asyncDir = path.join(runRoots.asyncDirRoot, runId);
1673
1788
  const currentSessionId = input.deps.state.currentSessionId;
1674
1789
  if (!currentSessionId) return { content: [{ type: "text", text: "External-job follow-up requires an active parent session." }], isError: true, details: { mode: "management", results: [] } };
1675
- if (fs.existsSync(asyncDir) || fs.existsSync(resultFilePath(DIRS.results, runId))) {
1790
+ if (fs.existsSync(asyncDir) || fs.existsSync(resultFilePath(runRoots.resultsDir, runId))) {
1676
1791
  return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
1677
1792
  }
1678
1793
 
@@ -1702,7 +1817,7 @@ async function resumeExternalJobFollowUp(input: {
1702
1817
  ...input.baseAgentConfig,
1703
1818
  runner: { type: "external-job", provider: runner.provider, options: runner.options },
1704
1819
  };
1705
- const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1820
+ const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
1706
1821
  agent: input.target.agent,
1707
1822
  task: input.followUp,
1708
1823
  goal: input.followUp,
@@ -1715,11 +1830,13 @@ async function resumeExternalJobFollowUp(input: {
1715
1830
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1716
1831
  currentModelProvider: parentModel?.provider,
1717
1832
  currentModel: parentModel,
1833
+ scopedModelIds: scopedModelIdsFromContext(input.ctx),
1718
1834
  modelScope: input.modelScope,
1719
1835
  modelResponseAliases: input.deps.config.modelResponseAliases,
1720
1836
  interactive: input.ctx.hasUI,
1721
1837
  permissions: input.deps.config.permissions,
1722
1838
  childRuntime: input.deps.childRuntime,
1839
+ projectTrusted: sessionProjectTrust(input.ctx),
1723
1840
  }),
1724
1841
  cwd: input.effectiveCwd,
1725
1842
  artifactsDir,
@@ -1765,6 +1882,8 @@ function resolveRequestedResumeTarget(params: SubagentParamsLike, deps: Executor
1765
1882
  if (resolved?.kind === "nested") {
1766
1883
  if (params.chain?.length) throw new Error("Attaching a running subagent as a chain root is currently available for top-level async runs only.");
1767
1884
  if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") return { kind: "live-nested", target: resolved };
1885
+ const externalJobTarget = resolveNestedExternalJobResumeTarget(resolved, params, deps);
1886
+ if (externalJobTarget) return externalJobTarget;
1768
1887
  const trustedSessionRoots = [
1769
1888
  ...(deps.config.defaultSessionDir ? [path.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : []),
1770
1889
  ...(parentSessionFile ? [deps.getSubagentSessionRoot(parentSessionFile)] : []),
@@ -1782,6 +1901,7 @@ async function resumeAsyncRun(input: {
1782
1901
  parentModel?: ParentModel;
1783
1902
  absoluteDeadlineAt?: number;
1784
1903
  signal?: AbortSignal;
1904
+ onLaunch?: (launch: { agent: string; sessionName?: string; sessionFile?: string; async: boolean; runId?: string }) => void;
1785
1905
  }): Promise<AgentToolResult<Details>> {
1786
1906
  const followUp = (input.params.message ?? input.params.task ?? "").trim();
1787
1907
  const attachChain = (input.params.chain?.length ?? 0) > 0 ? input.params.chain as ChainStep[] : undefined;
@@ -1792,21 +1912,29 @@ async function resumeAsyncRun(input: {
1792
1912
  details: { mode: "management", results: [] },
1793
1913
  };
1794
1914
  }
1795
- if (input.params.model !== undefined) {
1915
+ const timeoutOverflowError = timerDelayOverflowError("timeoutMs", input.params.timeoutMs);
1916
+ if (timeoutOverflowError) {
1796
1917
  return {
1797
- content: [{ type: "text", text: "action='resume' reuses the persisted child model and does not accept a model override." }],
1918
+ content: [{ type: "text", text: timeoutOverflowError }],
1798
1919
  isError: true,
1799
1920
  details: { mode: "management", results: [] },
1800
1921
  };
1801
1922
  }
1802
- const acceptanceErrors = validateExecutionAcceptance(input.params as Parameters<typeof validateExecutionAcceptance>[0]);
1803
- if (acceptanceErrors.length > 0) {
1923
+ if (input.params.model !== undefined) {
1804
1924
  return {
1805
- content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }],
1925
+ content: [{ type: "text", text: "action='resume' reuses the persisted child model and does not accept a model override." }],
1806
1926
  isError: true,
1807
1927
  details: { mode: "management", results: [] },
1808
1928
  };
1809
1929
  }
1930
+ const acceptancePolicyErrors = validateExecutionAcceptancePolicy(input.params);
1931
+ if (acceptancePolicyErrors.length > 0) {
1932
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptancePolicyErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
1933
+ }
1934
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(input.params);
1935
+ if (outputSchemaError) {
1936
+ return { content: [{ type: "text", text: `Cannot resume: ${outputSchemaError}` }], isError: true, details: { mode: "management", results: [] } };
1937
+ }
1810
1938
  input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
1811
1939
 
1812
1940
  let target: ResumeSourceTarget;
@@ -1850,7 +1978,7 @@ async function resumeAsyncRun(input: {
1850
1978
  const discoveredAgents = discovered.agents;
1851
1979
  const unknownAgentDiagnosticContext = diagnosticContextFromDiscovery(discovered, effectiveCwd, scope);
1852
1980
  const modelScope = discovered.modelScope;
1853
- const sessionName = resolveIntercomSessionTarget(input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
1981
+ const sessionName = resolveIntercomSessionTarget(input.deps.childRuntime?.intercomSessionName ?? input.deps.pi.getSessionName(), input.ctx.sessionManager.getSessionId());
1854
1982
  const recoveryDescriptor = "recoveryDescriptor" in target ? target.recoveryDescriptor : undefined;
1855
1983
  const recoveryContext = recoveryDescriptor?.context ?? (input.params.context === "profile" ? undefined : input.params.context);
1856
1984
  const intercomBridge = resolveIntercomBridge({
@@ -1889,9 +2017,21 @@ async function resumeAsyncRun(input: {
1889
2017
  : "External runners do not persist Pi sessions and cannot be resumed.";
1890
2018
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
1891
2019
  }
2020
+ if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
2021
+ return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
2022
+ }
2023
+ if (target.source === "async" && target.managedWorktree && target.asyncDir) {
2024
+ try {
2025
+ const protectedCwd = await protectRetainedWorktreeForResume(parallelHandoffPath(target.asyncDir), target.runId, target.index, input.signal);
2026
+ if (path.resolve(protectedCwd) !== path.resolve(effectiveCwd)) throw new Error("Retained worktree cwd changed during resume admission.");
2027
+ } catch (error) {
2028
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
2029
+ }
2030
+ }
2031
+
1892
2032
  if (target.source === "async" && target.runner?.type === "external-job") {
1893
2033
  if (attachChain) return { content: [{ type: "text", text: "External-job follow-up does not support chain attachment. Use action='resume' with message instead." }], isError: true, details: { mode: "management", results: [] } };
1894
- return resumeExternalJobFollowUp({
2034
+ const resumed = await resumeExternalJobFollowUp({
1895
2035
  target,
1896
2036
  followUp,
1897
2037
  baseAgentConfig,
@@ -1905,6 +2045,9 @@ async function resumeAsyncRun(input: {
1905
2045
  parentSessionFile,
1906
2046
  absoluteDeadlineAt: input.absoluteDeadlineAt,
1907
2047
  });
2048
+ const resumedRunId = resumed.details.runId ?? resumed.details.asyncId;
2049
+ if (!resumed.isError && resumedRunId) input.onLaunch?.({ agent: target.agent, async: true, runId: resumedRunId });
2050
+ return resumed;
1908
2051
  }
1909
2052
 
1910
2053
  if (attachChain) {
@@ -1922,6 +2065,10 @@ async function resumeAsyncRun(input: {
1922
2065
  details: { mode: "chain", results: [] },
1923
2066
  };
1924
2067
  }
2068
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(input.params, agents));
2069
+ if (acceptanceErrors.length > 0) {
2070
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "chain", results: [] } };
2071
+ }
1925
2072
  const runId = randomUUID();
1926
2073
  const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
1927
2074
  let activeAsyncCapacity: ActiveAsyncCapacityHandle | undefined;
@@ -1966,11 +2113,13 @@ async function resumeAsyncRun(input: {
1966
2113
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
1967
2114
  currentModelProvider: parentModel?.provider,
1968
2115
  currentModel: parentModel,
2116
+ scopedModelIds: scopedModelIdsFromContext(input.ctx),
1969
2117
  modelScope,
1970
2118
  modelResponseAliases: input.deps.config.modelResponseAliases,
1971
2119
  interactive: input.ctx.hasUI,
1972
2120
  permissions: input.deps.config.permissions,
1973
2121
  childRuntime: input.deps.childRuntime,
2122
+ projectTrusted: sessionProjectTrust(input.ctx),
1974
2123
  }),
1975
2124
  availableModels,
1976
2125
  cwd: effectiveCwd,
@@ -1990,6 +2139,7 @@ async function resumeAsyncRun(input: {
1990
2139
  worktreeSetupHookTimeoutMs: input.deps.config.worktreeSetupHookTimeoutMs,
1991
2140
  worktreeBaseDir: input.deps.config.worktreeBaseDir,
1992
2141
  baseRef: input.params.baseRef ?? target.recoveryDescriptor?.baseRef,
2142
+ ...(recoveryDescriptor?.requiredExtensions ? { requiredExtensions: recoveryDescriptor.requiredExtensions } : {}),
1993
2143
  worktreeProvider: input.deps.config.worktreeProvider,
1994
2144
  worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
1995
2145
  controlConfig: resolveControlConfig(input.deps.config.control, input.params.control),
@@ -2006,6 +2156,7 @@ async function resumeAsyncRun(input: {
2006
2156
  return result;
2007
2157
  }
2008
2158
  const attachedId = result.details.asyncId ?? runId;
2159
+ input.onLaunch?.({ agent: target.agent, ...(target.sessionFile ? { sessionFile: target.sessionFile } : {}), async: true, runId: attachedId });
2009
2160
  const lines = [
2010
2161
  `Attached async subagent ${target.runId} as the first step of a new chain.`,
2011
2162
  `Chain run: ${attachedId}`,
@@ -2026,8 +2177,25 @@ async function resumeAsyncRun(input: {
2026
2177
  if (target.source === "async" && asyncReviveRequiresRecoveryDescriptor(target)) {
2027
2178
  return { content: [{ type: "text", text: `Async child '${target.runId}' is missing its required run fan-out recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
2028
2179
  }
2029
- if (input.params.baseRef !== undefined && "managedWorktree" in target && target.managedWorktree === true) {
2030
- return { content: [{ type: "text", text: "Cannot resume with baseRef: retained managed-worktree children continue in their existing worktree. Start a new worktree run from that base ref instead." }], isError: true, details: { mode: "management", results: [] } };
2180
+ if (target.source === "nested" && !target.recoveryDescriptor) {
2181
+ return { content: [{ type: "text", text: `Nested child '${target.runId}' is missing its required recovery identity. Start a new run instead.` }], isError: true, details: { mode: "management", results: [] } };
2182
+ }
2183
+ const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
2184
+ const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
2185
+ const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
2186
+ const outputSchema = Object.hasOwn(input.params, "outputSchema")
2187
+ ? input.params.outputSchema
2188
+ : Object.hasOwn(foregroundContract ?? {}, "outputSchema")
2189
+ ? foregroundContract?.outputSchema
2190
+ : recoveryDescriptor?.structuredOutputSchema;
2191
+ const acceptance = input.params.acceptance !== undefined
2192
+ ? input.params.acceptance
2193
+ : foregroundContract?.acceptance !== undefined
2194
+ ? foregroundContract.acceptance
2195
+ : recoveryDescriptor?.acceptance;
2196
+ const acceptanceErrors = validateExecutionAcceptance({ ...input.params, acceptance, outputSchema });
2197
+ if (acceptanceErrors.length > 0) {
2198
+ return { content: [{ type: "text", text: `Cannot resume: ${acceptanceErrors.join(" ")}` }], isError: true, details: { mode: "management", results: [] } };
2031
2199
  }
2032
2200
  const runId = randomUUID();
2033
2201
  const topLevelResume = depth === 0 && !inheritedNestedRoute(input.deps) && !input.params.workflowParentRunId;
@@ -2052,17 +2220,13 @@ async function resumeAsyncRun(input: {
2052
2220
  if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
2053
2221
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "single", results: [] } };
2054
2222
  }
2055
- const recoveryAgentConfig = recoveryDescriptor ? applySteeringRecoveryAgentConfig(baseAgentConfig, recoveryDescriptor) : baseAgentConfig;
2056
- const agentConfig = intercomBridge.active ? applyIntercomBridgeToAgent(recoveryAgentConfig, intercomBridge) : recoveryAgentConfig;
2057
- const foregroundContract = target.source === "foreground" ? target.resumeContract : undefined;
2058
- const outputSchema = input.params.outputSchema ?? foregroundContract?.outputSchema ?? recoveryDescriptor?.structuredOutputSchema;
2059
2223
  const agentContract = input.params.agentContract ?? foregroundContract?.agentContract ?? recoveryDescriptor?.agentContract;
2060
2224
  const artifactConfig: ArtifactConfig = recoveryDescriptor?.artifactConfig ?? omitUndefinedProperties({ ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false, dir: input.deps.config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir });
2061
2225
  const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
2062
2226
  const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
2063
2227
  const parentModel = input.parentModel;
2064
2228
  const revivalAsyncDir = path.join(DIRS.async, runId);
2065
- const result = executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2229
+ const result = await executeAsyncSingle(runId, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
2066
2230
  agent: target.agent,
2067
2231
  task: buildRevivedAsyncTask(target as Parameters<typeof buildRevivedAsyncTask>[0], effectiveFollowUp),
2068
2232
  goal: effectiveFollowUp,
@@ -2075,12 +2239,14 @@ async function resumeAsyncRun(input: {
2075
2239
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
2076
2240
  currentModelProvider: parentModel?.provider,
2077
2241
  currentModel: parentModel,
2242
+ scopedModelIds: scopedModelIdsFromContext(input.ctx),
2078
2243
  modelScope,
2079
2244
  // Absence in the retained contract is meaningful; never acquire current aliases.
2080
2245
  modelResponseAliases: recoveryDescriptor ? recoveryDescriptor.modelResponseAliases : foregroundContract?.modelResponseAliases,
2081
2246
  interactive: input.ctx.hasUI,
2082
2247
  permissions: input.deps.config.permissions,
2083
2248
  childRuntime: input.deps.childRuntime,
2249
+ projectTrusted: sessionProjectTrust(input.ctx),
2084
2250
  }),
2085
2251
  cwd: effectiveCwd,
2086
2252
  maxOutput: input.params.maxOutput ?? recoveryDescriptor?.maxOutput,
@@ -2104,6 +2270,7 @@ async function resumeAsyncRun(input: {
2104
2270
  thinkingOverride: recoveryDescriptor?.thinking ?? target.thinking,
2105
2271
  thinkingCeiling: recoveryDescriptor?.thinkingCeiling ?? ("thinkingCeiling" in target ? target.thinkingCeiling : undefined),
2106
2272
  extensionBindings: recoveryDescriptor?.extensionBindings ?? ("extensionBindings" in target ? target.extensionBindings : undefined),
2273
+ requiredExtensions: recoveryDescriptor?.requiredExtensions ?? (target as { requiredExtensions?: SteeringRecoveryDescriptor["requiredExtensions"] }).requiredExtensions,
2107
2274
  outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
2108
2275
  maxSubagentDepth: recoveryDescriptor?.maxSubagentDepth ?? resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime),
2109
2276
  waitToolEnabled: input.deps.waitToolEnabled,
@@ -2129,19 +2296,23 @@ async function resumeAsyncRun(input: {
2129
2296
  ...(agentContract ? { agentContract } : {}),
2130
2297
  ...(outputSchema ? { structuredOutputSchema: outputSchema } : {}),
2131
2298
  ...(recoveryDescriptor?.skills ? { skills: [...recoveryDescriptor.skills] } : {}),
2132
- ...(input.params.acceptance !== undefined ? { acceptance: input.params.acceptance } : foregroundContract?.acceptance !== undefined ? { acceptance: foregroundContract.acceptance } : recoveryDescriptor?.acceptance !== undefined ? { acceptance: recoveryDescriptor.acceptance } : {}),
2299
+ ...(acceptance !== undefined ? { acceptance } : {}),
2133
2300
  ...(input.params.timeoutMs !== undefined ? { timeoutMs: input.params.timeoutMs } : {}),
2134
2301
  ...(input.absoluteDeadlineAt !== undefined ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {}),
2135
2302
  ...(input.params.toolBudget !== undefined ? { toolBudget: input.params.toolBudget } : {}),
2136
- capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : undefined, recoveryDescriptor?.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
2303
+ // Recovery descriptors, remembered foreground runs, and current workflow roots
2304
+ // preserve parent authority before the selected agent's descendant ceiling.
2305
+ capabilityCeiling: intersectSubagentCapabilityCeilings(recoveryDescriptor?.capabilityCeiling ?? (target.source === "foreground" || (target.source === "async" && target.mode === "workflow") ? target.capabilityCeiling : undefined), resolveCurrentSubagentCapabilityCeiling(input.deps.state.currentSessionId)),
2137
2306
  runFanoutBudget: input.params.runFanoutBudget ?? recoveryDescriptor?.runFanoutBudget ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
2138
2307
  parentWorkflowRunId: input.params.workflowParentRunId,
2139
2308
  workflowKey: input.params.workflowKey,
2140
2309
  activeAsyncCapacity,
2141
2310
  }));
2311
+ const notifyLaunch = (): void => input.onLaunch?.({ agent: target.agent, sessionFile: revivalSessionFile, async: true, runId });
2142
2312
  if (result.isError) {
2143
2313
  const startedStatus = readStatus(revivalAsyncDir);
2144
2314
  if (input.params.workflowParentRunId !== undefined && startedStatus?.runId === runId && startedStatus.processTerminal?.runnerProcessInstanceId) {
2315
+ notifyLaunch();
2145
2316
  return {
2146
2317
  ...result,
2147
2318
  details: {
@@ -2156,6 +2327,7 @@ async function resumeAsyncRun(input: {
2156
2327
  activeAsyncCapacity?.rollback();
2157
2328
  return result;
2158
2329
  }
2330
+ notifyLaunch();
2159
2331
  for (const brief of queuedBriefs) fs.rmSync(brief.path, { force: true });
2160
2332
  if (queuedBriefs.length > 0 && sourceAsyncDir) {
2161
2333
  const sourceStatus = readStatus(sourceAsyncDir);
@@ -2169,63 +2341,31 @@ async function resumeAsyncRun(input: {
2169
2341
  if (input.params.workflowParentRunId !== undefined && result.details.asyncDir) {
2170
2342
  const asyncDir = result.details.asyncDir;
2171
2343
  const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
2172
- const stopOnAbort = () => { stopAsyncRun(input.deps.state, revivedId, input.deps.kill, { asyncDir, resolvedId: revivedId }); };
2173
- if (input.signal?.aborted) stopOnAbort();
2174
- else input.signal?.addEventListener("abort", stopOnAbort, { once: true });
2175
- let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
2176
- try {
2177
- completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 });
2178
- } finally {
2179
- input.signal?.removeEventListener("abort", stopOnAbort);
2180
- }
2181
- fs.rmSync(resultPath, { force: true });
2182
- const usage = importedAsyncRootUsage(completed);
2183
- const childResult: SingleResult = {
2184
- index: 0,
2185
- agent: completed.agent,
2186
- ...(completed.sessionName ? { sessionName: completed.sessionName } : {}),
2187
- task: effectiveFollowUp,
2188
- exitCode: completed.exitCode,
2189
- usage,
2190
- finalOutput: completed.output,
2191
- outputState: completed.output.trim() ? "present" : "absent",
2192
- ...(completed.error ? { error: completed.error } : {}),
2193
- ...(completed.timedOut ? { timedOut: true } : {}),
2194
- ...(completed.stopped ? { stopped: true } : {}),
2195
- ...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
2196
- ...(completed.model ? { model: completed.model } : {}),
2197
- ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
2198
- ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
2199
- ...(completed.contextOverflow ? { contextOverflow: true } : {}),
2200
- ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
2201
- ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
2202
- ...(completed.structuredOutputSchemaPath ? { structuredOutputSchemaPath: completed.structuredOutputSchemaPath } : {}),
2203
- ...(completed.acceptance ? { acceptance: completed.acceptance } : {}),
2204
- ...(completed.artifactPaths ? { artifactPaths: completed.artifactPaths } : {}),
2205
- ...(completed.savedOutputPath ? { savedOutputPath: completed.savedOutputPath } : {}),
2206
- ...(completed.outputSaveError ? { outputSaveError: completed.outputSaveError } : {}),
2207
- ...(completed.transcriptPath ? { transcriptPath: completed.transcriptPath } : {}),
2208
- ...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
2209
- };
2210
- return {
2211
- content: [{ type: "text", text: completed.success ? completed.output || completed.error || `Revived ${target.source} subagent ${revivedId} completed without output.` : completed.error || completed.output || `Revived ${target.source} subagent ${revivedId} completed without output.` }],
2212
- ...(completed.success ? {} : { isError: true }),
2213
- details: {
2214
- ...result.details,
2215
- runId: revivedId,
2216
- results: [childResult],
2217
- ...(target.launchContractDigest ? { sourceLaunchContractDigest: target.launchContractDigest } : {}),
2218
- },
2219
- };
2344
+ const stopListener = stopAwaitedAsyncChildOnAbort(input.signal, input.deps.state, revivedId, asyncDir, input.deps.kill);
2345
+ const completed = await waitForImportedAsyncRoot({ runId: revivedId, asyncDir, resultPath, index: 0 }, {
2346
+ shouldAbort: () => input.signal?.aborted === true,
2347
+ timeoutMessage: "Workflow stopped before async child completed.",
2348
+ abortedAsStopped: true,
2349
+ }).finally(stopListener.remove);
2350
+ const details: Details = { ...result.details };
2351
+ if (target.launchContractDigest) details.sourceLaunchContractDigest = target.launchContractDigest;
2352
+ return importWorkflowAwaitedChildResult(completed, { runId: revivedId, asyncDir, resultPath, task: effectiveFollowUp, parentWorkflowRunId: input.params.workflowParentRunId, details, emptyOutputText: `Revived ${target.source} subagent ${revivedId} completed without output.`, state: input.deps.state, pi: input.deps.pi });
2220
2353
  }
2221
2354
  const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
2222
2355
  const sourceLabel = target.source;
2356
+ let workflowRevivalWarning: string | undefined;
2357
+ try {
2358
+ if (target.source === "async") recordWorkflowRevival(DIRS.async, target.runId, revivedId);
2359
+ } catch (error) {
2360
+ workflowRevivalWarning = `Warning: workflow key link not recorded: ${error instanceof Error ? error.message : String(error)}`;
2361
+ }
2223
2362
  const lines = [
2224
2363
  `Revived ${sourceLabel} subagent from ${target.runId}.`,
2225
2364
  `Revived run: ${revivedId}`,
2226
2365
  `Agent: ${target.agent}`,
2227
2366
  `Session: ${target.sessionFile}`,
2228
2367
  result.details.asyncDir ? `Async dir: ${result.details.asyncDir}` : undefined,
2368
+ workflowRevivalWarning,
2229
2369
  revivedTarget ? `Intercom target: ${revivedTarget} (if registered)` : undefined,
2230
2370
  `Status if needed: subagent({ action: "status", id: "${revivedId}" })`,
2231
2371
  ].filter((line): line is string => Boolean(line));
@@ -2327,6 +2467,7 @@ async function emitForegroundResultIntercom(input: {
2327
2467
  ...(result.sessionName ? { sessionName: result.sessionName } : {}),
2328
2468
  status: foregroundResultIntercomStatus(result),
2329
2469
  outputState: result.outputState ?? "unknown",
2470
+ ...(result.outputPartial ? { outputPartial: true } : {}),
2330
2471
  summary: resultSummaryForIntercom(result),
2331
2472
  index,
2332
2473
  artifactPath: result.artifactPaths?.outputPath,
@@ -2393,6 +2534,28 @@ function canonicalizeAgentName(name: string, agents: AgentConfig[], diagnostics:
2393
2534
  return { name: resolved.agent.name };
2394
2535
  }
2395
2536
 
2537
+ // Mirrors child launch: workflow children without their own cwd/agentScope discover agents at the workflow cwd and scope.
2538
+ function workflowValidationOptions(deps: ExecutorDeps, params: SubagentParamsLike, runtimeCwd: string, parentModelProvider: () => string | undefined): Parameters<typeof validateWorkflowScript>[1] {
2539
+ const cwd = resolveRequestedCwd(runtimeCwd, params.cwd);
2540
+ const scope = resolveExecutionAgentScope(params.agentScope);
2541
+ let discovered: ReturnType<ExecutorDeps["discoverAgents"]> | undefined;
2542
+ return {
2543
+ maxSubagentSpawnsPerRun: params.maxSubagentSpawnsPerRun ?? resolveMaxSubagentSpawnsPerRun(deps.config.maxSubagentSpawnsPerRun),
2544
+ agentNameError: (name) => {
2545
+ discovered ??= deps.discoverAgents(cwd, scope, parentModelProvider());
2546
+ const { agents } = discovered;
2547
+ const resolved = resolveAgentName(name, agents);
2548
+ if (resolved.agent || resolved.error) return canonicalizeAgentName(name, agents, discovered.agentDiagnostics, diagnosticContextFromDiscovery(discovered, cwd, scope)).error;
2549
+ const requested = name.trim().toLowerCase();
2550
+ const suggestion = [...new Set(agents.flatMap((agent) => [agent.name, ...(agent.localName ? [agent.localName] : []), ...(agent.aliases ?? [])]))]
2551
+ .map((candidate) => ({ candidate, distance: editDistance(requested, candidate.toLowerCase()) }))
2552
+ .filter(({ candidate, distance }) => distance <= Math.max(1, Math.floor(candidate.length / 4)) || hasSingleAdjacentTransposition(requested, candidate.toLowerCase()))
2553
+ .sort((left, right) => left.distance - right.distance || left.candidate.localeCompare(right.candidate))[0]?.candidate;
2554
+ return `Unknown agent '${name}'.${suggestion ? ` Did you mean '${suggestion}'?` : ""} Use subagent({ action: "list" }) to inspect agents.`;
2555
+ },
2556
+ };
2557
+ }
2558
+
2396
2559
  function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentConfig[], diagnostics: AgentDiscoveryDiagnostic[] | undefined, context: UnknownAgentDiagnosticContext): { params?: SubagentParamsLike; error?: string } {
2397
2560
  const resolve = (name: string, location?: string): { name?: string; error?: string } => {
2398
2561
  const result = canonicalizeAgentName(name, agents, diagnostics, context);
@@ -2404,6 +2567,8 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
2404
2567
  params = omitUndefinedProperties({ ...params, agent: result.name });
2405
2568
  const agent = agents.find((candidate) => candidate.name === result.name);
2406
2569
  if (params.extensionBindings !== undefined && (agent?.runner?.type === "external-cli" || agent?.runner?.type === "external-job")) return { error: `extensionBindings is not supported for runner.type='${agent.runner.type}'.` };
2570
+ const machineError = agent ? formatHerdrMachineRunnerUnsupported({ machine: params.machine ?? agent.machine, agentName: agent.name, runnerType: agent.runner?.type, adapter: agent.runner?.type === "external-cli" ? agent.runner.adapter : undefined, worktree: params.worktree }) : undefined;
2571
+ if (machineError) return { error: machineError };
2407
2572
  }
2408
2573
  if (params.extensionBindings !== undefined) {
2409
2574
  try {
@@ -2456,6 +2621,18 @@ function canonicalizeExecutionParams(params: SubagentParamsLike, agents: AgentCo
2456
2621
  return { params };
2457
2622
  }
2458
2623
 
2624
+ function projectEffectiveAcceptanceSchemas(params: SubagentParamsLike, agents: AgentConfig[]): SubagentParamsLike {
2625
+ const withEffectiveSchema = <T extends { agent: string; outputSchema?: JsonSchemaObject | false }>(step: T): T => {
2626
+ const agent = agents.find((candidate) => candidate.name === step.agent);
2627
+ return agent ? { ...step, outputSchema: resolveEffectiveOutputSchema(agent, step.outputSchema) } : step;
2628
+ };
2629
+ return {
2630
+ ...params,
2631
+ ...(params.tasks ? { tasks: params.tasks.map(withEffectiveSchema) } : {}),
2632
+ ...(params.chain ? { chain: projectChainOutputSchemas(params.chain, agents) as ChainStep[] } : {}),
2633
+ };
2634
+ }
2635
+
2459
2636
  function validateExecutionInput(
2460
2637
  params: SubagentParamsLike,
2461
2638
  agents: AgentConfig[],
@@ -2466,11 +2643,15 @@ function validateExecutionInput(
2466
2643
  context: UnknownAgentDiagnosticContext,
2467
2644
  ): AgentToolResult<Details> | null {
2468
2645
  if (Number(hasChain) + Number(hasTasks) + Number(hasSingle) !== 1) {
2646
+ const agentList = agents.map((a) => a.name).join(", ") || "none";
2647
+ const noMode = !hasChain && !hasTasks && !hasSingle;
2469
2648
  return {
2470
2649
  content: [
2471
2650
  {
2472
2651
  type: "text",
2473
- text: `Provide exactly one mode. Agents: ${agents.map((a) => a.name).join(", ") || "none"}`,
2652
+ text: noMode && typeof params.workflowKey === "string"
2653
+ ? `Workflow child '${params.workflowKey}' has no agent. Pass { key, agent, task }. Agents: ${agentList}`
2654
+ : `Provide exactly one mode. Agents: ${agentList}`,
2474
2655
  },
2475
2656
  ],
2476
2657
  isError: true,
@@ -2478,7 +2659,7 @@ function validateExecutionInput(
2478
2659
  };
2479
2660
  }
2480
2661
 
2481
- const acceptanceErrors = validateExecutionAcceptance(params as Parameters<typeof validateExecutionAcceptance>[0]);
2662
+ const acceptanceErrors = validateExecutionAcceptance(projectEffectiveAcceptanceSchemas(params, agents));
2482
2663
  if (acceptanceErrors.length > 0) {
2483
2664
  return {
2484
2665
  content: [{ type: "text", text: acceptanceErrors.join(" ") }],
@@ -2723,6 +2904,19 @@ function buildRequestedModeError(params: SubagentParamsLike, message: string): A
2723
2904
  );
2724
2905
  }
2725
2906
 
2907
+ function buildWorkflowValidationResult(
2908
+ validation: ReturnType<typeof validateWorkflowScript>,
2909
+ mode: "management" | "workflow",
2910
+ preflight?: import("../../shared/types.ts").WorkflowPreflight,
2911
+ ): AgentToolResult<Details> {
2912
+ const payload = preflight ? { ...validation, preflight } : validation;
2913
+ return {
2914
+ content: [{ type: "text", text: JSON.stringify(payload) }],
2915
+ ...(validation.ok ? {} : { isError: true }),
2916
+ details: { mode, results: [] },
2917
+ };
2918
+ }
2919
+
2726
2920
  function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: AgentConfig[]): SubagentParamsLike {
2727
2921
  if ((params.chain?.length ?? 0) > 0 || (params.tasks?.length ?? 0) > 0 || !params.agent) return params;
2728
2922
  const agent = agents.find((candidate) => candidate.name === params.agent);
@@ -2730,6 +2924,7 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
2730
2924
  const parentTimeoutMs = params.timeoutMs === undefined && params.maxRuntimeMs === undefined && agent.defaultTimeoutMs === undefined && params.workflowParentDeadlineAt !== undefined
2731
2925
  ? Math.max(1, params.workflowParentDeadlineAt - Date.now())
2732
2926
  : undefined;
2927
+ const outputSchema = params.outputSchema === false ? false : resolveEffectiveOutputSchema(agent, params.outputSchema);
2733
2928
  return {
2734
2929
  ...params,
2735
2930
  ...(params.async === undefined && agent.defaultAsync !== undefined ? { async: agent.defaultAsync } : {}),
@@ -2740,9 +2935,28 @@ function applySingleAgentLaunchDefaults(params: SubagentParamsLike, agents: Agen
2740
2935
  ...(params.acceptance === undefined && agent.defaultAcceptance !== undefined
2741
2936
  ? { acceptance: agent.defaultAcceptance }
2742
2937
  : {}),
2938
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
2743
2939
  };
2744
2940
  }
2745
2941
 
2942
+ function validateLaunchOutputSchemaOverrides(params: SubagentParamsLike): string | undefined {
2943
+ const values: unknown[] = [params.outputSchema, ...(params.tasks ?? []).map((task) => task.outputSchema)];
2944
+ for (const step of params.chain ?? []) {
2945
+ if (isParallelStep(step)) values.push(...step.parallel.map((task) => task.outputSchema));
2946
+ else if (isDynamicParallelStep(step)) values.push(step.parallel.outputSchema);
2947
+ else values.push(step.outputSchema);
2948
+ }
2949
+ for (const value of values) {
2950
+ if (value === undefined || value === false) continue;
2951
+ try {
2952
+ assertJsonSchemaObject(value, "outputSchema");
2953
+ } catch (error) {
2954
+ return error instanceof Error ? error.message : String(error);
2955
+ }
2956
+ }
2957
+ return undefined;
2958
+ }
2959
+
2746
2960
  export const DEFAULT_FOREGROUND_TIMEOUT_MS = 30 * 60 * 1000;
2747
2961
 
2748
2962
  // Async single-agent runs also need a wall-clock backstop: a child whose bash
@@ -2764,6 +2978,12 @@ export { DEFAULT_ASYNC_TIMEOUT_MS };
2764
2978
  */
2765
2979
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
2766
2980
 
2981
+ function timerDelayOverflowError(name: string, value: unknown): string | undefined {
2982
+ return typeof value === "number" && value > MAX_TIMER_DELAY_MS
2983
+ ? `${name} must be a positive integer no larger than ${MAX_TIMER_DELAY_MS}.`
2984
+ : undefined;
2985
+ }
2986
+
2767
2987
  /**
2768
2988
  * Resolve the optional global default runtime deadline from extension config
2769
2989
  * (`config.timeoutMs`). Returns undefined for unset or invalid values so callers
@@ -2787,6 +3007,8 @@ export function resolveForegroundTimeout(params: SubagentParamsLike, defaultTime
2787
3007
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
2788
3008
  return { error: `${name} must be a positive integer.` };
2789
3009
  }
3010
+ const overflowError = timerDelayOverflowError(name, value);
3011
+ if (overflowError) return { error: overflowError };
2790
3012
  }
2791
3013
  if (rawTimeout !== undefined && rawMaxRuntime !== undefined && rawTimeout !== rawMaxRuntime) {
2792
3014
  return { error: "timeoutMs and maxRuntimeMs are aliases; provide only one value or use the same value for both." };
@@ -2906,24 +3128,20 @@ function withResolvedContext(
2906
3128
  }
2907
3129
 
2908
3130
  function withAggregatedToolUsage(result: AgentToolResult<Details>): AgentToolResult<Details> {
2909
- if (result.details.results.length === 0) return result;
2910
- const usage = sumResultsUsage(result.details.results);
3131
+ const highWarnings = result.details.results.flatMap((child) => child.watchdog?.warnings?.filter((warning) => warning.importance === "high") ?? []);
3132
+ const projected: AgentToolResult<Details> = {
3133
+ ...result,
3134
+ ...(highWarnings.length ? { content: [...result.content, { type: "text", text: ["High-importance watchdog findings:", ...highWarnings.map((warning) => `- ${warning.severity}: ${warning.summary}\n Evidence: ${warning.evidence}\n Recommended action: ${warning.recommendedAction}`)].join("\n") }] } : {}),
3135
+ details: {
3136
+ ...result.details,
3137
+ results: result.details.results.map((child) => ({ ...child, ...(child.watchdog ? { watchdog: childWatchdogProgressForModel(child.watchdog) } : {}) })),
3138
+ },
3139
+ };
3140
+ if (projected.details.results.length === 0) return projected;
3141
+ const usage = sumResultsUsage(projected.details.results);
2911
3142
  return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
2912
- ? { ...result, usage: toAgentToolUsage(usage) }
2913
- : result;
2914
- }
2915
-
2916
- function withForkThinkingNotes(
2917
- result: AgentToolResult<Details>,
2918
- downgrades: Map<number, string>,
2919
- ): AgentToolResult<Details> {
2920
- if (downgrades.size === 0) return result;
2921
- const children = [...downgrades.entries()]
2922
- .sort(([a], [b]) => a - b)
2923
- .map(([index, agent]) => `${agent} (child ${index})`)
2924
- .join(", ");
2925
- const note = `Note: fork context forced thinking off for ${children}. The forked transcript contained signed Anthropic thinking blocks that were sanitized, and Anthropic children cannot resume such a transcript with thinking enabled. Use context: "fresh" when an Anthropic child needs thinking.`;
2926
- return { ...result, content: [...result.content, { type: "text", text: note }] };
3143
+ ? { ...projected, usage: toAgentToolUsage(usage) }
3144
+ : projected;
2927
3145
  }
2928
3146
 
2929
3147
  function toExecutionErrorResult(params: SubagentParamsLike, error: unknown, contextSummary?: ContextSummary): AgentToolResult<Details> {
@@ -2946,14 +3164,15 @@ function resolveStaticLaunchSummary(input: {
2946
3164
  explicitModel?: string;
2947
3165
  agents: AgentConfig[];
2948
3166
  parentModel?: ParentModel;
3167
+ scopedModelIds?: string[];
2949
3168
  availableModels: ModelInfo[];
2950
3169
  currentProvider?: string;
2951
3170
  modelScope?: ModelScopeConfig;
2952
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
3171
+ thinkingOverrideForTask: ThinkingOverrideForTask;
2953
3172
  }): StaticLaunchSummary {
2954
3173
  const agentConfig = input.agents.find((agent) => agent.name === input.agent);
2955
3174
  const externalRunner = agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job";
2956
- const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel);
3175
+ const modelScopes = resolveModelScopesForAgent(input.modelScope, input.agent, input.parentModel, input.scopedModelIds);
2957
3176
  const model = externalRunner
2958
3177
  ? undefined
2959
3178
  : resolveEffectiveSubagentModel(
@@ -2964,7 +3183,7 @@ function resolveStaticLaunchSummary(input: {
2964
3183
  agentConfig?.modelProvider ?? input.currentProvider,
2965
3184
  modelScopes.length === 0 ? {} : { scope: modelScopes },
2966
3185
  );
2967
- const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask(input.agent, input.index, model);
3186
+ const thinkingOverride = externalRunner ? undefined : input.thinkingOverrideForTask();
2968
3187
  const thinking = externalRunner ? undefined : resolveEffectiveThinking(model, thinkingOverride ?? agentConfig?.thinking);
2969
3188
  return {
2970
3189
  agent: input.agent,
@@ -2977,10 +3196,11 @@ function collectStaticLaunchSummaries(input: {
2977
3196
  params: SubagentParamsLike;
2978
3197
  agents: AgentConfig[];
2979
3198
  parentModel?: ParentModel;
3199
+ scopedModelIds?: string[];
2980
3200
  availableModels: ModelInfo[];
2981
3201
  currentProvider?: string;
2982
3202
  modelScope?: ModelScopeConfig;
2983
- thinkingOverrideForTask: ForkThinkingOverrideForTask;
3203
+ thinkingOverrideForTask: ThinkingOverrideForTask;
2984
3204
  dynamicFanoutMaxItems?: number;
2985
3205
  }): StaticLaunchSummary[] {
2986
3206
  const summary = (agent: string, index: number, explicitModel?: string) => resolveStaticLaunchSummary({
@@ -2989,6 +3209,7 @@ function collectStaticLaunchSummaries(input: {
2989
3209
  explicitModel,
2990
3210
  agents: input.agents,
2991
3211
  parentModel: input.parentModel,
3212
+ scopedModelIds: input.scopedModelIds,
2992
3213
  availableModels: input.availableModels,
2993
3214
  currentProvider: input.currentProvider,
2994
3215
  modelScope: input.modelScope,
@@ -3134,27 +3355,53 @@ function importedAsyncRootUsage(completed: Awaited<ReturnType<typeof waitForImpo
3134
3355
  };
3135
3356
  }
3136
3357
 
3137
- async function waitForWorkflowAsyncSingleResult(
3138
- params: SubagentParamsLike,
3139
- launchResult: AgentToolResult<Details>,
3140
- options: { runId: string; task: string; signal?: AbortSignal; state: SubagentState; kill?: ExecutorDeps["kill"] },
3141
- ): Promise<AgentToolResult<Details>> {
3142
- if (params.workflowAwaitAsync !== true || !launchResult.details.asyncDir) return launchResult;
3143
- const asyncDir = launchResult.details.asyncDir;
3144
- const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
3145
- const stopOnAbort = () => { stopAsyncRun(options.state, options.runId, options.kill, { asyncDir, resolvedId: options.runId }); };
3146
- if (options.signal?.aborted) stopOnAbort();
3147
- else options.signal?.addEventListener("abort", stopOnAbort, { once: true });
3148
- let completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>;
3358
+ function removeWorkflowAwaitedResult(asyncDir: string, resultPath: string, runId: string, publication: NonNullable<Awaited<ReturnType<typeof waitForImportedAsyncRoot>>["importedPublication"]>): void {
3149
3359
  try {
3150
- completed = await waitForImportedAsyncRoot({ runId: options.runId, asyncDir, resultPath, index: 0 }, omitUndefinedProperties({
3151
- shouldAbort: () => options.signal?.aborted === true,
3152
- timeoutMessage: "Workflow stopped before async child completed.",
3153
- }));
3154
- } finally {
3155
- options.signal?.removeEventListener("abort", stopOnAbort);
3360
+ fs.rmSync(resultPath, { force: true });
3361
+ } catch {
3362
+ // Payload cleanup must not replace an already imported delivery.
3156
3363
  }
3157
- fs.rmSync(resultPath, { force: true });
3364
+ removeResultIndex(asyncDir, publication.sessionId, runId, publication.toolCallId);
3365
+ }
3366
+
3367
+ function emitWorkflowAwaitedChildComplete(
3368
+ pi: ExtensionAPI,
3369
+ state: SubagentState,
3370
+ runId: string,
3371
+ asyncDir: string,
3372
+ parentWorkflowRunId: string | undefined,
3373
+ completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>,
3374
+ ): void {
3375
+ let status: ReturnType<typeof readStatus> | undefined;
3376
+ try {
3377
+ status = readStatus(asyncDir);
3378
+ } catch (error) {
3379
+ console.error(`Failed to read awaited workflow child status for ${runId}; emitting completion with fallback metadata:`, error);
3380
+ }
3381
+ pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
3382
+ id: runId,
3383
+ runId,
3384
+ sessionId: status?.sessionId ?? completed.importedPublication?.sessionId ?? state.currentSessionId,
3385
+ completionOwnerId: status?.completionOwnerId ?? state.completionOwnerId ?? currentCompletionOwnerId(),
3386
+ asyncDir,
3387
+ agent: completed.agent,
3388
+ mode: "single",
3389
+ state: completed.stopped ? "stopped" : completed.success ? "complete" : "failed",
3390
+ success: completed.success,
3391
+ timestamp: Date.now(),
3392
+ triggerTurn: false,
3393
+ awaitedByWorkflow: true,
3394
+ parentWorkflowRunId,
3395
+ });
3396
+ }
3397
+
3398
+ /** Consumes an awaited workflow child's published result: clears the payload, announces completion, and builds the tool result. */
3399
+ function importWorkflowAwaitedChildResult(
3400
+ completed: Awaited<ReturnType<typeof waitForImportedAsyncRoot>>,
3401
+ options: { runId: string; asyncDir: string; resultPath: string; task: string; parentWorkflowRunId: string | undefined; details: Details; emptyOutputText?: string; state: SubagentState; pi: ExtensionAPI },
3402
+ ): AgentToolResult<Details> {
3403
+ if (completed.importedPublication) removeWorkflowAwaitedResult(options.asyncDir, options.resultPath, options.runId, completed.importedPublication);
3404
+ emitWorkflowAwaitedChildComplete(options.pi, options.state, options.runId, options.asyncDir, options.parentWorkflowRunId, completed);
3158
3405
  const usage = importedAsyncRootUsage(completed);
3159
3406
  const childResult: SingleResult = omitUndefinedProperties({
3160
3407
  index: 0,
@@ -3170,8 +3417,7 @@ async function waitForWorkflowAsyncSingleResult(
3170
3417
  ...(completed.stopped ? { stopped: true } : {}),
3171
3418
  ...(completed.sessionFile ? { sessionFile: completed.sessionFile } : {}),
3172
3419
  ...(completed.model ? { model: completed.model } : {}),
3173
- ...(completed.attemptedModels ? { attemptedModels: completed.attemptedModels } : {}),
3174
- ...(completed.modelAttempts ? { modelAttempts: completed.modelAttempts } : {}),
3420
+ ...(completed.requestedModel ? { requestedModel: completed.requestedModel } : {}),
3175
3421
  ...(completed.contextOverflow ? { contextOverflow: true } : {}),
3176
3422
  ...(completed.structuredOutput !== undefined ? { structuredOutput: completed.structuredOutput } : {}),
3177
3423
  ...(completed.structuredOutputPath ? { structuredOutputPath: completed.structuredOutputPath } : {}),
@@ -3184,16 +3430,55 @@ async function waitForWorkflowAsyncSingleResult(
3184
3430
  ...(completed.transcriptError ? { transcriptError: completed.transcriptError } : {}),
3185
3431
  });
3186
3432
  return {
3187
- content: [{ type: "text", text: completed.success ? completed.output || completed.error || `Async workflow child ${options.runId} completed without output.` : completed.error || completed.output || `Async workflow child ${options.runId} completed without output.` }],
3433
+ content: [{ type: "text", text: (completed.success ? completed.output || completed.error : completed.error || completed.output) || (options.emptyOutputText ?? `Async workflow child ${options.runId} completed without output.`) }],
3188
3434
  ...(completed.success ? {} : { isError: true }),
3189
3435
  details: {
3190
- ...launchResult.details,
3436
+ ...options.details,
3191
3437
  runId: options.runId,
3192
3438
  results: [childResult],
3193
3439
  },
3194
3440
  };
3195
3441
  }
3196
3442
 
3443
+ // A replaced runtime leaves the child running so a relaunch of the workflow can re-attach to it.
3444
+ function stopAwaitedAsyncChildOnAbort(signal: AbortSignal | undefined, state: SubagentState, runId: string, asyncDir: string, kill?: ExecutorDeps["kill"]): { remove: () => void } {
3445
+ const stopOnAbort = () => { if (!workflowStopCause(signal?.reason)) stopAsyncRun(state, runId, kill, { asyncDir, resolvedId: runId }); };
3446
+ if (signal?.aborted) stopOnAbort();
3447
+ else signal?.addEventListener("abort", stopOnAbort, { once: true });
3448
+ return { remove: () => signal?.removeEventListener("abort", stopOnAbort) };
3449
+ }
3450
+
3451
+ // Live workflow controls are keyed by the full run id; a stop id may be a prefix or the launch tool-call id.
3452
+ function resolveLiveWorkflowRunId(deps: ExecutorDeps, id: string): string | undefined {
3453
+ const controllers = deps.state.workflowControllers;
3454
+ if (!controllers?.size) return undefined;
3455
+ if (controllers.has(id)) return id;
3456
+ try {
3457
+ const resolved = resolveSubagentRunId(id, omitUndefinedProperties({ state: deps.state, nested: nestedResolutionScopeForExecutor(deps) }));
3458
+ return resolved?.kind === "async" && controllers.has(resolved.id) ? resolved.id : undefined;
3459
+ } catch {
3460
+ // The generic stop path resolves the id again and reports the error.
3461
+ return undefined;
3462
+ }
3463
+ }
3464
+
3465
+ async function waitForWorkflowAsyncSingleResult(
3466
+ params: SubagentParamsLike,
3467
+ launchResult: AgentToolResult<Details>,
3468
+ options: { runId: string; task: string; signal?: AbortSignal; state: SubagentState; pi: ExtensionAPI; kill?: ExecutorDeps["kill"] },
3469
+ ): Promise<AgentToolResult<Details>> {
3470
+ if (params.workflowAwaitAsync !== true || !launchResult.details.asyncDir) return launchResult;
3471
+ const asyncDir = launchResult.details.asyncDir;
3472
+ const resultPath = workflowAwaitedAsyncResultPath(asyncDir);
3473
+ const stopListener = stopAwaitedAsyncChildOnAbort(options.signal, options.state, options.runId, asyncDir, options.kill);
3474
+ const completed = await waitForImportedAsyncRoot({ runId: options.runId, asyncDir, resultPath, index: 0 }, omitUndefinedProperties({
3475
+ shouldAbort: () => options.signal?.aborted === true,
3476
+ timeoutMessage: "Workflow stopped before async child completed.",
3477
+ abortedAsStopped: true,
3478
+ })).finally(stopListener.remove);
3479
+ return importWorkflowAwaitedChildResult(completed, { runId: options.runId, asyncDir, resultPath, task: options.task, parentWorkflowRunId: params.workflowParentRunId, details: launchResult.details, state: options.state, pi: options.pi });
3480
+ }
3481
+
3197
3482
  async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Promise<AgentToolResult<Details> | null> {
3198
3483
  const {
3199
3484
  params,
@@ -3235,11 +3520,13 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3235
3520
  parentSessionId: data.parentPiSessionId,
3236
3521
  currentModelProvider: parentModel?.provider,
3237
3522
  currentModel: parentModel,
3523
+ scopedModelIds: data.scopedModelIds,
3238
3524
  modelScope: data.modelScope,
3239
3525
  modelResponseAliases: deps.config.modelResponseAliases,
3240
3526
  interactive: ctx.hasUI,
3241
3527
  permissions: deps.config.permissions,
3242
3528
  childRuntime: deps.childRuntime,
3529
+ projectTrusted: sessionProjectTrust(ctx),
3243
3530
  });
3244
3531
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
3245
3532
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth, deps.childRuntime);
@@ -3269,7 +3556,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3269
3556
  if ((a.runner?.type === "external-cli" || a.runner?.type === "external-job") && (params.fast ?? a.fast) === true) {
3270
3557
  return buildRequestedModeError(params, `Agent '${a.name}' uses runner.type='${a.runner.type}' and does not support fast mode.`);
3271
3558
  }
3272
- const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel);
3559
+ const modelScopes = resolveModelScopesForAgent(data.modelScope, a.name, parentModel, data.scopedModelIds);
3273
3560
  const modelOrigin = resolveModelOrigin({
3274
3561
  storedOrigin: params.modelOrigin as ModelOrigin | undefined,
3275
3562
  explicitModel: params.model as string | undefined,
@@ -3285,16 +3572,19 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3285
3572
  const modelOverrideFromParent = modelOrigin === "inherited";
3286
3573
  const launchRuleError = applyWatchdogLaunchRules({ cwd: effectiveCwd, agent: a.name, model: modelOverride ?? (parentModel && `${parentModel.provider}/${parentModel.id}`), warn: (violation) => deps.watchdog?.displayRuleWarning(violation) });
3287
3574
  if (launchRuleError) return toExecutionErrorResult(params, new Error(launchRuleError), data.contextPolicy.contextSummary);
3288
- const asyncResult = executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3575
+ const asyncResult = await executeAsyncSingle(id, compactOptional<Parameters<typeof executeAsyncSingle>[1]>({
3289
3576
  agent: params.agent!,
3290
3577
  task: shouldForkAgent(contextPolicy, params.agent!) ? wrapForkTask(params.task ?? "") : (params.task ?? ""),
3291
3578
  goal: params.task ?? "",
3292
3579
  agentConfig: a,
3293
3580
  recoveryAgentConfig: data.recoveryAgents.find((agent) => agent.name === params.agent),
3581
+ requiredExtensions: data.requiredExtensions,
3294
3582
  ctx: asyncCtx,
3295
3583
  availableModels,
3296
3584
  cwd: effectiveCwd,
3297
3585
  requestedCwd: data.requestedCwd,
3586
+ machine: params.machine,
3587
+ machineCwd: params.machineCwd,
3298
3588
  maxOutput: params.maxOutput,
3299
3589
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
3300
3590
  artifactConfig,
@@ -3313,7 +3603,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3313
3603
  fast: params.fast,
3314
3604
  modelOverrideFromParent,
3315
3605
  modelOrigin,
3316
- thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
3606
+ thinkingOverride: externalRunnerWithoutExplicitModel ? undefined : thinkingOverrideForTask(),
3317
3607
  thinkingCeiling: a.maxThinking,
3318
3608
  maxSubagentDepth,
3319
3609
  waitToolEnabled: deps.waitToolEnabled,
@@ -3331,7 +3621,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3331
3621
  childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
3332
3622
  nestedRoute,
3333
3623
  agentContract: params.agentContract,
3334
- structuredOutputSchema: params.outputSchema,
3624
+ structuredOutputSchema: params.outputSchema || undefined,
3335
3625
  extensionBindings: params.extensionBindings,
3336
3626
  acceptance: params.acceptance,
3337
3627
  timeoutMs: data.timeoutMs,
@@ -3339,6 +3629,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3339
3629
  usageBudget: data.usageBudget,
3340
3630
  configToolBudget: data.configToolBudget,
3341
3631
  toolTimeoutMs: data.params?.toolTimeoutMs,
3632
+ checkpointBeforeDeadlineMs: data.params?.checkpointBeforeDeadlineMs ?? deps.config.checkpointBeforeDeadlineMs,
3342
3633
  configToolTimeoutMs: data.configToolTimeoutMs,
3343
3634
  capabilityCeiling: data.capabilityCeiling,
3344
3635
  runFanoutBudget: data.runFanoutBudget,
@@ -3347,7 +3638,7 @@ async function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
3347
3638
  lane: params.lane,
3348
3639
  workflowAwaitAsync: params.workflowAwaitAsync,
3349
3640
  }));
3350
- return waitForWorkflowAsyncSingleResult(params, asyncResult, { runId: id, task: params.task ?? "", signal: data.signal, state: deps.state, kill: deps.kill });
3641
+ return waitForWorkflowAsyncSingleResult(params, asyncResult, { runId: id, task: params.task ?? "", signal: data.signal, state: deps.state, pi: deps.pi, kill: deps.kill });
3351
3642
  }
3352
3643
 
3353
3644
  return null;
@@ -3457,6 +3748,62 @@ function appendWorkflowOutputWarning(text: string, warning: string | undefined):
3457
3748
  return warning ? `${text}\n\n${warning}` : text;
3458
3749
  }
3459
3750
 
3751
+ const WORKFLOW_TRACE_ERROR_MAX_CHARS = 500;
3752
+
3753
+ // Return, emits, console, and trace errors (which carry child text) have no size limit of their
3754
+ // own, so only they are cut; other host sections stay whole. The saved full result is uncut.
3755
+ function formatWorkflowResultText(input: {
3756
+ head: string[];
3757
+ script: string[];
3758
+ trace: Array<{ line: string; error?: string }>;
3759
+ tail: string[];
3760
+ maxOutput: MaxOutputConfig | undefined;
3761
+ aggregateOutputPath: string | undefined;
3762
+ fullResultPath: string;
3763
+ producedChildOutputPaths: ReadonlySet<string>;
3764
+ }): string {
3765
+ const traceSection = (error: (text: string) => string) => input.trace.length > 0
3766
+ ? [`Call trace:\n${input.trace.map((entry) => `${entry.line}${entry.error ? ` — ${error(entry.error)}` : ""}`).join("\n")}`]
3767
+ : [];
3768
+ const fullText = [...input.head, ...input.script, ...traceSection((error) => error), ...input.tail].join("\n\n");
3769
+ const outputWarning = writeWorkflowAggregateOutput(input.aggregateOutputPath, fullText, input.producedChildOutputPaths);
3770
+ const scriptText = input.script.join("\n\n");
3771
+ const config = { ...DEFAULT_MAX_OUTPUT, ...input.maxOutput };
3772
+ const scriptCut = truncateOutput(scriptText, config).truncated;
3773
+ const traceCut = input.trace.some((entry) => (entry.error?.length ?? 0) > WORKFLOW_TRACE_ERROR_MAX_CHARS);
3774
+ let shownScriptText = scriptText;
3775
+ let saveWarning: string | undefined;
3776
+ let savedPath: string | undefined;
3777
+ if (scriptCut || traceCut) {
3778
+ const aggregateHoldsFullText = input.aggregateOutputPath !== undefined && !outputWarning
3779
+ && !input.producedChildOutputPaths.has(resolveWorkflowHostOutputClaimPath(input.aggregateOutputPath));
3780
+ if (!aggregateHoldsFullText) saveWarning = writeWorkflowAggregateOutput(input.fullResultPath, fullText, new Set());
3781
+ savedPath = aggregateHoldsFullText ? input.aggregateOutputPath : saveWarning ? undefined : input.fullResultPath;
3782
+ if (scriptCut) shownScriptText = truncateOutput(scriptText, config, savedPath).text;
3783
+ }
3784
+ const shownTrace = traceSection((error) => error.length > WORKFLOW_TRACE_ERROR_MAX_CHARS ? `${error.slice(0, WORKFLOW_TRACE_ERROR_MAX_CHARS)}… (+${error.length - WORKFLOW_TRACE_ERROR_MAX_CHARS} chars)` : error);
3785
+ const traceNote = traceCut && !scriptCut && savedPath ? [`[TRUNCATED: trace errors shortened - full output at ${savedPath}]`] : [];
3786
+ const text = [...input.head, ...(shownScriptText ? [shownScriptText] : []), ...shownTrace, ...input.tail, ...traceNote].join("\n\n");
3787
+ return appendWorkflowOutputWarning(appendWorkflowOutputWarning(text, outputWarning), saveWarning);
3788
+ }
3789
+
3790
+ export function resolveWorkflowChildLocalCwd(input: {
3791
+ workflowCwd: string;
3792
+ discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
3793
+ agents: AgentConfig[];
3794
+ workflowAgentScope?: unknown;
3795
+ params: Record<string, unknown>;
3796
+ }): string {
3797
+ if (input.params.machine !== undefined) return input.workflowCwd;
3798
+ if (typeof input.params.agent === "string") {
3799
+ const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
3800
+ const workflowAgents = input.discoverAgents(input.workflowCwd, agentScope).agents;
3801
+ const agent = resolveAgentName(input.params.agent, workflowAgents).agent ?? resolveAgentName(input.params.agent, input.agents).agent;
3802
+ if (agent?.machine !== undefined) return input.workflowCwd;
3803
+ }
3804
+ return typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
3805
+ }
3806
+
3460
3807
  function resolveWorkflowChildOutputPath(input: {
3461
3808
  ctxCwd: string;
3462
3809
  workflowCwd: string;
@@ -3482,7 +3829,7 @@ function resolveWorkflowChildOutputPath(input: {
3482
3829
  }, input.state);
3483
3830
  return { path: "recoveryDescriptor" in target ? target.recoveryDescriptor?.outputPath : undefined, inherited: false };
3484
3831
  }
3485
- const childCwd = typeof input.params.cwd === "string" ? resolveChildCwd(input.workflowCwd, input.params.cwd) : input.workflowCwd;
3832
+ const childCwd = resolveWorkflowChildLocalCwd(input);
3486
3833
  let agentOutput: string | undefined;
3487
3834
  if (rawOutput === true || rawOutput === "true" || (!hasExplicitOutput && !input.aggregateOutputPath)) {
3488
3835
  const agentScope = resolveExecutionAgentScope(input.params.agentScope ?? input.workflowAgentScope);
@@ -3584,7 +3931,7 @@ function prepareWorkflowChildLaunchParams(input: {
3584
3931
  const resolvedOutput = resolveWorkflowChildOutputPath({ ctxCwd: input.ctxCwd, workflowCwd: input.workflowCwd, artifactsDir: input.artifactsDir, workflowRunId: input.parentWorkflowRunId, aggregateOutputPath: input.aggregateOutputPath, configuredOutputBaseDir: input.configuredOutputBaseDir, discoverAgents: input.discoverAgents, agents: input.agents, workflowAgentScope: input.workflowAgentScope, key: input.workflowKey, params: input.childParams });
3585
3932
  if (resolvedOutput.path) childParams = { ...input.childParams, output: resolvedOutput.path };
3586
3933
  }
3587
- const childCwd = typeof childParams.cwd === "string" ? resolveChildCwd(input.workflowCwd, childParams.cwd) : input.workflowCwd;
3934
+ const childCwd = resolveWorkflowChildLocalCwd({ ...input, params: childParams });
3588
3935
  const agentScope = resolveExecutionAgentScope(childParams.agentScope ?? input.workflowAgentScope);
3589
3936
  const discoveredAgents = input.discoverAgents(childCwd, agentScope).agents;
3590
3937
  const agent = typeof childParams.agent === "string"
@@ -3716,13 +4063,23 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3716
4063
  details: { mode: "single", results: [] },
3717
4064
  };
3718
4065
  }
4066
+ let foregroundMachine: import("../../shared/types.ts").HerdrMachineReference | undefined;
4067
+ const requestedMachine = params.machine ?? agentConfig.machine;
4068
+ const requiredExtensions = data.requiredExtensions ?? deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(data.parentPiSessionId);
4069
+ if (requestedMachine) {
4070
+ try {
4071
+ assertRequiredChildExtensionsAdmitted([requiredExtensions], { agent: agentConfig.name, runnerType: agentConfig.runner?.type, machine: requestedMachine });
4072
+ foregroundMachine = resolveHerdrMachinePlacement({ machine: requestedMachine, cwd: ctx.cwd, stepCwd: params.machineCwd }).machine;
4073
+ }
4074
+ catch (error) { return toExecutionErrorResult(params, error instanceof Error ? error : new Error(String(error)), data.contextPolicy.contextSummary); }
4075
+ }
3719
4076
  const effectiveToolBudget = resolveEffectiveToolBudget(omitUndefinedProperties({ runBudget: data.toolBudget, agentBudget: agentConfig.toolBudget, configBudget: data.configToolBudget }));
3720
4077
  if (effectiveToolBudget.error) return toExecutionErrorResult(params, new Error(effectiveToolBudget.error), data.contextPolicy.contextSummary);
3721
4078
 
3722
4079
  const parentModel = data.parentModel;
3723
4080
  const currentProvider = parentModel?.provider;
3724
4081
  const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
3725
- const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel);
4082
+ const modelScopes = resolveModelScopesForAgent(data.modelScope, agentConfig.name, parentModel, data.scopedModelIds);
3726
4083
  let task = typeof params.task === "string" ? params.task : "";
3727
4084
  const modelOrigin = resolveModelOrigin({
3728
4085
  storedOrigin: params.modelOrigin as ModelOrigin | undefined,
@@ -3753,7 +4110,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3753
4110
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
3754
4111
 
3755
4112
 
3756
- const sourceCwd = effectiveCwd;
4113
+ const sourceCwd = foregroundMachine?.cwd ?? effectiveCwd;
3757
4114
  let pendingHandoff: Details["parallelHandoff"];
3758
4115
  const { setup: worktreeSetup, errorResult: worktreeSetupError } = params.worktree ? await createSingleWorktreeSetup(
3759
4116
  sourceCwd,
@@ -3830,7 +4187,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3830
4187
  // Reads: caller override > agent defaultReads > none. `~`/`~/` expand to home;
3831
4188
  // absolute paths pass through; relative paths resolve against the child cwd.
3832
4189
  const reads = readsOverride !== undefined ? readsOverride : agentConfig.defaultReads ?? false;
3833
- const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
4190
+ const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
3834
4191
  const readsInstruction = readPaths.length > 0
3835
4192
  ? `[Read from: ${readPaths.join(", ")}]\n\n`
3836
4193
  : "";
@@ -3847,8 +4204,22 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3847
4204
  let detachForeground: ((reason?: string) => boolean) | undefined;
3848
4205
  let childSessionControls: ForegroundChildSessionControls | undefined;
3849
4206
  const foregroundControl = deps.state.foregroundControls.get(runId);
4207
+ const syncHerdrForegroundChild = () => {
4208
+ if (!foregroundControl?.parentWorkflowRunId) return;
4209
+ try {
4210
+ deps.pi.events.emit(HERDR_FOREGROUND_CONTROL_CHANGED_EVENT, { runId: foregroundControl.parentWorkflowRunId });
4211
+ } catch (error) {
4212
+ console.error("Failed to sync Herdr foreground child:", error);
4213
+ }
4214
+ };
4215
+ const finishTrackedForegroundChild = () => {
4216
+ if (!foregroundControl) return;
4217
+ const wasActive = foregroundControl.activeChildren?.has(0) === true;
4218
+ finishForegroundChild(foregroundControl, 0);
4219
+ if (wasActive) syncHerdrForegroundChild();
4220
+ };
3850
4221
  if (foregroundControl) {
3851
- const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin));
4222
+ const thinking = resolveEffectiveThinking(modelOverride, thinkingOverrideForTask());
3852
4223
  beginForegroundChild(foregroundControl, omitUndefinedProperties({
3853
4224
  index: 0,
3854
4225
  agent: params.agent!,
@@ -3880,6 +4251,9 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3880
4251
  }
3881
4252
  },
3882
4253
  }));
4254
+ // Capture the owned mailbox before a child can start and finish between polling ticks.
4255
+ if (deps.childRuntime?.fanoutChild) deps.activateSupervisorTransport?.();
4256
+ syncHerdrForegroundChild();
3883
4257
  }
3884
4258
 
3885
4259
  const modelResponseAliases = deps.config.modelResponseAliases === undefined ? undefined : structuredClone(deps.config.modelResponseAliases);
@@ -3898,10 +4272,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3898
4272
  : undefined;
3899
4273
  try {
3900
4274
  const launched = await runSync(ctx.cwd, agents, params.agent!, task, compactOptional<Parameters<typeof runSync>[4]>({
4275
+ machine: foregroundMachine,
4276
+ parentProviderRegistry: ctx.modelRegistry,
4277
+ projectTrusted: sessionProjectTrust(ctx),
4278
+ remoteReads: foregroundMachine ? readsOverride : undefined,
3901
4279
  permissions: deps.config.permissions,
3902
4280
  runtimeSnapshotHost: deps.pi,
3903
4281
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
3904
- llmIntentArbiter: createTaskMutationArbiter(ctx),
4282
+ requiredExtensions,
3905
4283
  childRuntime: deps.childRuntime,
3906
4284
  onChildSession: (controls) => { childSessionControls = controls; },
3907
4285
  context: data.contextPolicy.contextForAgent(params.agent!),
@@ -3910,6 +4288,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3910
4288
  cwd: singleCwd,
3911
4289
  requestedCwd: data.requestedCwd,
3912
4290
  signal,
4291
+ abortedAsStopped: params.workflowParentRunId !== undefined,
3913
4292
  interruptSignal: interruptController.signal,
3914
4293
  allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
3915
4294
  intercomEvents: deps.pi.events,
@@ -3938,7 +4317,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3938
4317
  fast: params.fast,
3939
4318
  modelOverrideFromParent,
3940
4319
  modelOrigin,
3941
- thinkingOverride: thinkingOverrideForTask(params.agent!, 0, modelOverride, modelOverrideFromParent, modelOrigin),
4320
+ thinkingOverride: thinkingOverrideForTask(),
3942
4321
  thinkingCeiling: agentConfig.maxThinking,
3943
4322
  extensionBindings: params.extensionBindings,
3944
4323
  availableModels,
@@ -3985,7 +4364,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
3985
4364
  if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
3986
4365
  } finally {
3987
4366
  try {
3988
- if (foregroundControl) finishForegroundChild(foregroundControl, 0);
4367
+ finishTrackedForegroundChild();
3989
4368
  } finally {
3990
4369
  removeForegroundControlIfIdle(deps.state, runId, deps.trackRetainedNestedRoute);
3991
4370
  }
@@ -4012,7 +4391,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
4012
4391
  // authoritative completion remains live.
4013
4392
  if (!r?.detached) {
4014
4393
  if (!artifactConfig.enabled) cleanupStructuredOutputRuntime(structuredRuntime);
4015
- if (foregroundControl) finishForegroundChild(foregroundControl, 0);
4394
+ finishTrackedForegroundChild();
4016
4395
  }
4017
4396
  }
4018
4397
  if (!r.detached) {
@@ -4061,7 +4440,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
4061
4440
  usageBudget: usageBudgetState(data.usageBudget, totalCost),
4062
4441
  ...(worktreeHandoff?.reference ? { parallelHandoff: worktreeHandoff.reference } : {}),
4063
4442
  }));
4064
- rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings });
4443
+ rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: details.results, params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
4065
4444
 
4066
4445
  const suppressRoutineResultIntercom = shouldSuppressRoutineResultIntercom({ suppressRoutineResultIntercom: params.suppressRoutineResultIntercom, results: [r] });
4067
4446
  if (!r.detached && !r.interrupted && !suppressRoutineResultIntercom) {
@@ -4119,7 +4498,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
4119
4498
  }
4120
4499
 
4121
4500
  function inferExecutionMode(params: SubagentParamsLike): Details["mode"] {
4122
- if (params.workflowScript !== undefined) return "workflow";
4501
+ if (params.workflowScript !== undefined || params.workflow !== undefined) return "workflow";
4123
4502
  if ((params.chain?.length ?? 0) > 0) return "chain";
4124
4503
  if ((params.tasks?.length ?? 0) > 0) return "parallel";
4125
4504
  return "single";
@@ -4138,6 +4517,7 @@ function duplicateSubagentCallResult(params: SubagentParamsLike): AgentToolResul
4138
4517
 
4139
4518
  const workflowLaunchObservers = new WeakMap<object, (launch: { agent: string; sessionName?: string; sessionFile?: string; async: boolean; runId?: string }) => void>();
4140
4519
  const workflowOwnedUsageBudgets = new WeakMap<object, UsageBudgetConfig>();
4520
+ const workflowRetainedRequiredExtensions = new WeakMap<object, RequiredChildExtensionSnapshot>();
4141
4521
 
4142
4522
  /**
4143
4523
  * Terminal-mission retention can remove a mission while its children still
@@ -4197,12 +4577,13 @@ export function bindMissionWorkflowChildAsyncLaunch(
4197
4577
  binding: MissionLaunchBinding | undefined,
4198
4578
  asyncByDefault: boolean,
4199
4579
  asyncId: string = randomUUID(),
4580
+ nestedRootRunId?: string,
4200
4581
  ): SubagentParamsLike {
4201
4582
  const requestedAsync = params.async ?? asyncByDefault;
4202
4583
  if (!binding || !requestedAsync || params.clarify === true) return params;
4203
4584
  const id = asyncId.trim();
4204
4585
  if (!id || path.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
4205
- writeMissionAsyncBinding(path.join(DIRS.async, id), binding);
4586
+ writeMissionAsyncBinding(path.join(nestedRootRunId ? nestedRunScope(nestedRootRunId).asyncDirRoot : DIRS.async, id), binding);
4206
4587
  return { ...params, workflowChildAsyncId: id };
4207
4588
  }
4208
4589
 
@@ -4213,21 +4594,25 @@ function workflowChildResult(
4213
4594
  resumeState?: SubagentState,
4214
4595
  forcedTerminalOutcome?: WorkflowTerminalOutcome,
4215
4596
  ): WorkflowScriptChildResult {
4597
+ const running = missionWorkflowChildStatus(result) === "running";
4216
4598
  const receiptOutput = result.content.map((part) => part.type === "text" ? part.text : "").filter(Boolean).join("\n");
4217
- const output = result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
4599
+ const output = running ? "" : result.details.results.length === 1 && result.details.results[0]?.finalOutput !== undefined
4218
4600
  ? result.details.results[0].finalOutput
4219
4601
  : receiptOutput;
4220
4602
  const childError = result.details.results.map((child) => child.error).find((error): error is string => Boolean(error));
4221
- const failureErrorBase = childError && receiptOutput
4222
- ? receiptOutput.includes(childError) ? receiptOutput : `${childError}\n\n${receiptOutput}`
4223
- : childError || receiptOutput || output || "Child run failed.";
4603
+ const stopped = result.details.results.some((child) => child.stopped);
4604
+ // A stopped child's receipt text carries the run fan-out annotation; report the structured stop reason instead.
4605
+ const failureErrorBase = stopped && childError
4606
+ ? childError
4607
+ : childError && receiptOutput
4608
+ ? receiptOutput.includes(childError) ? receiptOutput : `${childError}\n\n${receiptOutput}`
4609
+ : childError || receiptOutput || output || "Child run failed.";
4224
4610
  const savedOutputEvidence = [...new Set(result.details.results.map((child) => child.savedOutputPath).filter((value): value is string => Boolean(value)))]
4225
4611
  .filter((savedOutputPath) => !failureErrorBase.includes(savedOutputPath))
4226
4612
  .map((savedOutputPath) => `Saved output: ${savedOutputPath}`);
4227
4613
  const failureError = [failureErrorBase, ...savedOutputEvidence].join("\n");
4228
4614
  const detached = result.details.results.some((child) => child.detached);
4229
4615
  const interrupted = result.details.results.some((child) => child.interrupted);
4230
- const stopped = result.details.results.some((child) => child.stopped);
4231
4616
  const terminalOutcome = forcedTerminalOutcome
4232
4617
  ?? (result.details.results.some((child) => child.timedOut)
4233
4618
  ? { state: "partial" as const, reason: "timeout" as const }
@@ -4235,7 +4620,7 @@ function workflowChildResult(
4235
4620
  ? { state: "partial" as const, reason: "budget_exhausted" as const }
4236
4621
  : undefined);
4237
4622
  const acceptanceRecovery = result.details.results.find((child) => child.acceptance?.recovery)?.acceptance?.recovery;
4238
- const ok = result.isError !== true && !detached && !interrupted && !stopped && acceptanceRecovery === undefined;
4623
+ const ok = result.isError !== true && !running && !detached && !interrupted && !stopped && acceptanceRecovery === undefined;
4239
4624
  const artifactPaths = new Set<string>();
4240
4625
  if (result.details.asyncDir) artifactPaths.add(result.details.asyncDir);
4241
4626
  if (result.details.parallelHandoff?.path) artifactPaths.add(result.details.parallelHandoff.path);
@@ -4265,6 +4650,8 @@ function workflowChildResult(
4265
4650
  const resolvedContext = result.details.context ?? (resolvedContexts.length === 1 ? resolvedContexts[0] : resolvedContexts.length > 1 ? "mixed" : undefined);
4266
4651
  const outputReference = result.details.results.find((child) => child.savedOutputPath)?.savedOutputPath
4267
4652
  ?? result.details.results.find((child) => child.outputReference?.path)?.outputReference?.path;
4653
+ const outputArtifactPath = running ? undefined : result.details.parallelHandoff?.path
4654
+ ?? result.details.results.find((child) => child.artifactPaths?.outputPath)?.artifactPaths?.outputPath;
4268
4655
  const outputPathMapping = typeof childParams.task === "string" ? outputPathMappingFromTask(childParams.task, outputReference) : undefined;
4269
4656
  const externalResult = result.details.results.length === 1 && result.details.results[0]?.runner?.type === "external-cli" ? result.details.results[0] : undefined;
4270
4657
  const externalStatus = result.details.asyncDir ? readStatus(result.details.asyncDir) : undefined;
@@ -4278,12 +4665,14 @@ function workflowChildResult(
4278
4665
  return {
4279
4666
  key,
4280
4667
  ok,
4668
+ ...(running ? { state: "running" as const } : {}),
4669
+ ...(result.details.asyncDir ? { asyncDir: result.details.asyncDir } : {}),
4281
4670
  ...(lane ? { lane } : {}),
4282
4671
  ...(terminalOutcome ? { terminalOutcome } : {}),
4283
4672
  ...(resolvedAgents.length === 1 ? { agent: resolvedAgents[0] } : {}),
4284
4673
  ...(runId ? { runId } : {}),
4285
4674
  output,
4286
- ...(!ok ? { error: failureError } : {}),
4675
+ ...(!ok && !running ? { error: failureError } : {}),
4287
4676
  ...(detached ? { detached: true } : {}),
4288
4677
  ...(interrupted ? { interrupted: true } : {}),
4289
4678
  ...(stopped ? { stopped: true } : {}),
@@ -4291,6 +4680,7 @@ function workflowChildResult(
4291
4680
  ...(requestedContext ? { requestedContext } : {}),
4292
4681
  ...(resolvedContext ? { resolvedContext } : {}),
4293
4682
  ...(outputReference ? { outputReference } : {}),
4683
+ ...(outputArtifactPath ? { outputArtifactPath } : {}),
4294
4684
  ...(acceptanceRecovery ? { recovery: acceptanceRecovery } : {}),
4295
4685
  ...(outputPathMapping ? { outputPathMapping } : {}),
4296
4686
  ...(externalAdapter ? { externalAdapter } : {}),
@@ -4312,6 +4702,18 @@ function workflowChildAccountingFields(child: WorkflowScriptChildResult): { usag
4312
4702
  };
4313
4703
  }
4314
4704
 
4705
+ function workflowRunningChildrenSummary(children: WorkflowScriptChildResult[]): string {
4706
+ const running = children.filter((child) => child.state === "running");
4707
+ return running.length ? `${running.length} child ${running.length === 1 ? "run remains" : "runs remain"} running or uncollected: ${running.map((child) => `${child.key}=${child.runId}`).join(", ")}. These launch receipts contain no final child results.` : "";
4708
+ }
4709
+
4710
+ function workflowResultChildren(children: WorkflowScriptChildResult[], status: AsyncStatus, includeFailureFields: boolean) {
4711
+ return withWorkflowRevivals(DIRS.async, status.runId, children).map((child) => {
4712
+ const sessionName = status.steps?.find((step) => step.workflowKey === child.key)?.sessionName;
4713
+ return { workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(child.revival ? { revival: formatWorkflowKeyRevival(child.revival) } : {}), ...(sessionName ? { sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, ...(child.state === "running" ? { state: "running" } : { success: child.ok }), ...(child.asyncDir ? { asyncDir: child.asyncDir } : {}), ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(includeFailureFields && child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(includeFailureFields && child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.outputArtifactPath || child.outputReference ? { artifactPaths: { outputPath: child.outputArtifactPath ?? child.outputReference } } : {}) };
4714
+ });
4715
+ }
4716
+
4315
4717
  function workflowOutputPathMappingSummary(children: WorkflowScriptChildResult[]): string {
4316
4718
  const mappings = children.flatMap((child) => child.outputPathMapping
4317
4719
  ? [`'${child.key}': requested ${child.outputPathMapping.requestedPath} -> saved ${child.outputPathMapping.savedPath}`]
@@ -4320,7 +4722,11 @@ function workflowOutputPathMappingSummary(children: WorkflowScriptChildResult[])
4320
4722
  }
4321
4723
 
4322
4724
  function workflowDetailsResults(children: WorkflowScriptChildResult[]): SingleResult[] {
4323
- return children.flatMap((child) => (child.results ?? []).map((result) => result.workflowKey ? result : { ...result, workflowKey: child.key }));
4725
+ return children.flatMap((child) => (child.results ?? []).map((result) => ({
4726
+ ...result,
4727
+ workflowKey: result.workflowKey ?? child.key,
4728
+ ...(child.runId && !result.runId ? { runId: child.runId } : {}),
4729
+ })));
4324
4730
  }
4325
4731
 
4326
4732
  function workflowSteerReceipt(key: string, result: AgentToolResult<Details>): WorkflowSteerResult {
@@ -4463,8 +4869,9 @@ function terminalWorkflowReceipt(
4463
4869
  terminalOutcome?: WorkflowTerminalOutcome,
4464
4870
  hostSteps?: WorkflowReceipt["hostSteps"],
4465
4871
  resource?: WorkflowReceipt["resource"],
4872
+ argsDigest?: string,
4466
4873
  ): WorkflowReceipt {
4467
- return buildWorkflowReceipt({ workflowRunId, state, children, workflowChildren, terminalOutcome, hostSteps, resource });
4874
+ return buildWorkflowReceipt({ workflowRunId, state, children: withWorkflowRevivals(DIRS.async, workflowRunId, children), workflowChildren, terminalOutcome, hostSteps, resource, ...(argsDigest ? { argsDigest } : {}) });
4468
4875
  }
4469
4876
 
4470
4877
  function workflowFailureTerminalOutcome(error: unknown, _children: WorkflowScriptChildResult[], usageBudget: ReturnType<typeof usageBudgetState>): WorkflowTerminalOutcome | undefined {
@@ -4478,7 +4885,7 @@ function workflowFailureMessage(error: unknown, workflowRunId: string, children:
4478
4885
  if (children.length === 0 && text.includes(validationPrefix)) {
4479
4886
  return `Workflow '${workflowRunId}' validation failed before child launch; no children launched.${text.slice(text.indexOf(validationPrefix) + validationPrefix.length)}`;
4480
4887
  }
4481
- return text;
4888
+ return [text, workflowRunningChildrenSummary(children)].filter(Boolean).join(" ");
4482
4889
  }
4483
4890
 
4484
4891
  export async function steerWorkflowChildByKey(input: {
@@ -4547,6 +4954,35 @@ export async function steerWorkflowChildByKey(input: {
4547
4954
  }
4548
4955
  }
4549
4956
 
4957
+ export async function preflightWorkflowWorktrees(input: {
4958
+ workflowDefaults: SubagentParamsLike;
4959
+ defaultWorktree?: boolean;
4960
+ calls: Array<{ key: string; params: Record<string, unknown> }>;
4961
+ ctxCwd: string;
4962
+ signal: AbortSignal;
4963
+ deadlineAt?: number;
4964
+ }): Promise<void> {
4965
+ const sources = new Map<string, string[]>();
4966
+ for (const { key, params } of input.calls) {
4967
+ // The workflow validates retained IDs/receipt references before admission; resolution follows it.
4968
+ if (params.resume !== undefined) continue;
4969
+ const effective = prepareWorkflowLaunchParams(input.workflowDefaults, params, "preflight", key);
4970
+ if ((effective.worktree ?? input.defaultWorktree) !== true) continue;
4971
+ // Match recursive execute's resolution, including relative default/child cwd.
4972
+ const cwd = resolveRequestedCwd(input.ctxCwd, effective.cwd);
4973
+ const keys = sources.get(cwd) ?? [];
4974
+ keys.push(key);
4975
+ sources.set(cwd, keys);
4976
+ }
4977
+ for (const [cwd, keys] of sources) {
4978
+ try { await preflightWorktreeSource(cwd, { signal: input.signal, deadlineAt: input.deadlineAt }); }
4979
+ catch (error) {
4980
+ throw new Error(`Worktree admission failed for ${keys.map((key) => `'${key}'`).join(", ")} at ${cwd}: ${error instanceof Error ? error.message : String(error)} Select the correct cwd or arrange an operator-approved commit/stash.`, { cause: error });
4981
+ }
4982
+ }
4983
+ input.signal.throwIfAborted();
4984
+ }
4985
+
4550
4986
  export function prepareWorkflowLaunchParams(
4551
4987
  workflowDefaults: SubagentParamsLike,
4552
4988
  childParams: Record<string, unknown>,
@@ -4581,7 +5017,7 @@ export function prepareWorkflowLaunchParams(
4581
5017
  const worktree = childParams.worktree ?? workflowDefaults.worktree;
4582
5018
  const baseRef = Object.hasOwn(childParams, "baseRef") ? childParams.baseRef : undefined;
4583
5019
  const outputSchema = Object.hasOwn(childParams, "outputSchema") ? childParams.outputSchema : workflowDefaults.outputSchema;
4584
- if (outputSchema !== undefined) assertJsonSchemaObject(outputSchema, "outputSchema");
5020
+ if (outputSchema !== undefined && outputSchema !== false) assertJsonSchemaObject(outputSchema, "outputSchema");
4585
5021
  const agentContract = Object.hasOwn(childParams, "agentContract") ? childParams.agentContract : workflowDefaults.agentContract;
4586
5022
  if (agentContract !== undefined && !isAgentContract(agentContract as AgentContract)) throw new Error("agentContract must be { version: 1 }.");
4587
5023
  const acceptance = Object.hasOwn(childParams, "acceptance") ? childParams.acceptance : workflowDefaults.acceptance;
@@ -4601,7 +5037,7 @@ export function prepareWorkflowLaunchParams(
4601
5037
  ...(lane ? { lane } : {}),
4602
5038
  ...(worktree !== undefined ? { worktree: worktree as boolean } : {}),
4603
5039
  ...(baseRef !== undefined ? { baseRef: baseRef as string } : {}),
4604
- ...(outputSchema !== undefined ? { outputSchema: outputSchema as JsonSchemaObject } : {}),
5040
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
4605
5041
  ...(agentContract !== undefined ? { agentContract: agentContract as AgentContract } : {}),
4606
5042
  ...(acceptance !== undefined ? { acceptance: acceptance as AcceptanceInput } : {}),
4607
5043
  ...(output !== undefined ? { output: output as string | boolean } : {}),
@@ -4666,6 +5102,15 @@ function normalizeGateParams(params: SubagentParamsLike): GateParamsNormalizatio
4666
5102
  return { ok: true, params: { ...rest, ...(normalized.acceptance !== undefined ? { acceptance: normalized.acceptance } : {}) } };
4667
5103
  }
4668
5104
 
5105
+ function describeTypedVerifyOutputSchemaConflict(effective: SubagentParamsLike, requested: SubagentParamsLike): string | undefined {
5106
+ if (!acceptanceHasTypedVerify(effective.acceptance)) return undefined;
5107
+ if (effective.outputSchema === undefined || effective.outputSchema === false) return undefined;
5108
+ const agent = effective.agent ?? "?";
5109
+ const schemaSource = requested.outputSchema !== undefined && requested.outputSchema !== false ? "outputSchema" : `agent '${agent}' outputSchema`;
5110
+ const verifySource = requested.gate !== undefined ? "gate.output" : requested.acceptance !== undefined ? "acceptance.verify" : `agent '${agent}' defaultAcceptance`;
5111
+ return `${verifySource}: ${TYPED_VERIFY_OUTPUT_SCHEMA_CONFLICT.replace("with outputSchema", `with ${schemaSource}`)}`;
5112
+ }
5113
+
4669
5114
  function formatWorkflowValue(value: unknown): string {
4670
5115
  if (value === undefined) return "(undefined)";
4671
5116
  if (typeof value === "string") return value;
@@ -4854,6 +5299,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4854
5299
  const workflowPermitContexts = new WeakMap<object, { root: WorkflowChildPermit } | { child: WorkflowChildPermitContext }>();
4855
5300
  const warnedArtifactPackageDirs = new Set<string>();
4856
5301
  const scheduledOwnerExecutors = new Map<string | null, Map<string, { state: SubagentState; executor: ReturnType<typeof createSubagentExecutor> }>>();
5302
+ const disabledFeatures = resolveDisabledFeatureSurface(deps.config);
5303
+ const disabledFeatureResult = (params: SubagentParamsLike): AgentToolResult<Details> | undefined => {
5304
+ const error = disabledFeatureUseError(params, disabledFeatures);
5305
+ if (!error) return undefined;
5306
+ return { content: [{ type: "text", text: error }], isError: true, details: { mode: params.action === undefined ? inferExecutionMode(params) : "management", results: [] } };
5307
+ };
5308
+ const admitEnabledWorkflowChildren = (calls: Array<{ key: string; params: Record<string, unknown> }>): void => {
5309
+ for (const call of calls) {
5310
+ const error = disabledFeatureUseError(call.params, disabledFeatures, `workflow child '${call.key}'`);
5311
+ if (error) throw new Error(error);
5312
+ }
5313
+ };
4857
5314
  const execute = async (
4858
5315
  _id: string,
4859
5316
  params: SubagentParamsLike,
@@ -4865,6 +5322,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4865
5322
  ): Promise<AgentToolResult<Details>> => {
4866
5323
  const workflowLaunchObserver = workflowLaunchObservers.get(params);
4867
5324
  const inheritedUsageBudget = workflowOwnedUsageBudgets.get(params);
5325
+ const retainedRequiredExtensions = workflowRetainedRequiredExtensions.get(params);
4868
5326
  const delegatedThinkingOverride = delegatedThinkingOverrides.get(params);
4869
5327
  const allowZeroToolBudget = delegatedZeroToolBudgets.has(params);
4870
5328
  const delegatedExecution = delegatedExecutions.has(params);
@@ -4883,16 +5341,31 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4883
5341
  const capacityOverrideError = validateWorkflowCapacityOverrides(requestParams);
4884
5342
  if (capacityOverrideError) return buildRequestedModeError(requestParams, capacityOverrideError);
4885
5343
  let workflowPreflight: import("../../shared/types.ts").WorkflowPreflight | undefined;
5344
+ // Only workflow requests read the session here; other actions (for example doctor) must not.
5345
+ const resolveWorkflowParentModel = () => parentModelOverride !== undefined
5346
+ ? parentModelOverride
5347
+ : (() => {
5348
+ const currentParentModel = normalizeParentModel(ctx.model);
5349
+ return (preserveActiveSession
5350
+ ? currentParentModel
5351
+ : rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), currentParentModel)) ?? null;
5352
+ })();
5353
+ // validate checks args with the same normalization launch uses, reported beside script errors.
5354
+ const validateWorkflowRequest = () => {
5355
+ const validation = validateWorkflowScript(requestParams.workflowScript ?? "", workflowValidationOptions(deps, requestParams, ctx.cwd, () => resolveWorkflowParentModel()?.provider));
5356
+ const normalizedArgs = normalizeWorkflowArgs(requestParams.args);
5357
+ return "error" in normalizedArgs ? { ...validation, ok: false, errors: [...validation.errors, { message: normalizedArgs.error }] } : validation;
5358
+ };
4886
5359
  try {
4887
- if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined && requestParams.workflowScriptPath === undefined) {
4888
- throw new Error("preflight requires workflowScript or workflowScriptPath.");
5360
+ if (requestParams.preflight !== undefined && requestParams.workflowScript === undefined) {
5361
+ throw new Error("preflight requires workflow: true or a workflow script path.");
4889
5362
  }
4890
5363
  workflowPreflight = normalizeWorkflowPreflight(requestParams.preflight);
4891
5364
  if (workflowPreflight) requestParams = { ...requestParams, preflight: workflowPreflight };
4892
5365
  } catch (error) {
4893
5366
  const message = error instanceof Error ? error.message : String(error);
4894
5367
  if (requestParams.action?.trim() === "validate") {
4895
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
5368
+ const validation = validateWorkflowRequest();
4896
5369
  const invalidValidation = { ...validation, ok: false, errors: [...validation.errors, { message }] };
4897
5370
  return {
4898
5371
  content: [{ type: "text", text: JSON.stringify(invalidValidation) }],
@@ -4903,13 +5376,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4903
5376
  return buildRequestedModeError(requestParams, message);
4904
5377
  }
4905
5378
  if (requestParams.action?.trim() === "validate") {
4906
- const validation = validateWorkflowScript(requestParams.workflowScript ?? "");
4907
- const payload = workflowPreflight ? { ...validation, preflight: workflowPreflight } : validation;
4908
- return {
4909
- content: [{ type: "text", text: JSON.stringify(payload) }],
4910
- ...(validation.ok ? {} : { isError: true }),
4911
- details: { mode: "management", results: [] },
4912
- };
5379
+ return buildWorkflowValidationResult(validateWorkflowRequest(), "management", workflowPreflight);
4913
5380
  }
4914
5381
  const normalizedAction = typeof requestParams.action === "string" ? requestParams.action.trim() : requestParams.action;
4915
5382
  if (normalizedAction === "resume" && requestParams.extensionBindings !== undefined) return buildRequestedModeError(requestParams, "extensionBindings is not supported with action='resume'; resume uses the original retained child binding.");
@@ -4921,22 +5388,32 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4921
5388
  workflowResource = { permit: workflowResourcePermit, ...consumed };
4922
5389
  }
4923
5390
  if (requestParams.workflowScript !== undefined && normalizedAction === undefined) {
5391
+ const workflowArgsEvidence = requestParams.args === undefined ? undefined : { args: requestParams.args, argsDigest: stableJsonDigest(requestParams.args) };
5392
+ const workflowArgs = workflowArgsEvidence?.args;
5393
+ const workflowArgsDigest = workflowArgsEvidence?.argsDigest;
5394
+ const workflowParentModel = resolveWorkflowParentModel();
5395
+ const workflowValidation = validateWorkflowScript(requestParams.workflowScript, workflowValidationOptions(deps, requestParams, ctx.cwd, () => workflowParentModel?.provider));
5396
+ if (!workflowValidation.ok && publicExecution) return buildWorkflowValidationResult(workflowValidation, "workflow", workflowPreflight);
5397
+ const launchBlockingErrors = workflowValidation.errors.filter((error) => error.kind === "spawn-budget" || error.kind === "agent");
5398
+ if (launchBlockingErrors.length > 0) {
5399
+ return buildRequestedModeError(requestParams, `Workflow '${_id}' validation failed before child launch; no children launched. ${launchBlockingErrors.map((error) => error.message).join(" ")}`);
5400
+ }
5401
+ // A package chain's per-step stop check defeats static launch counting; runtime fan-out enforcement still applies.
5402
+ const structuredResource = workflowResource?.provenance.name === "chain" || workflowResource?.provenance.name === "tasks";
5403
+ for (const warning of workflowValidation.warnings ?? []) {
5404
+ if (!(structuredResource && warning.kind === "dynamic-spawn-count")) console.warn(`[pi-subagents] ${warning.message}`);
5405
+ }
4924
5406
  const acceptanceErrors = validateAcceptanceInput(requestParams.acceptance);
4925
5407
  if (acceptanceErrors.length > 0) return buildRequestedModeError(requestParams, acceptanceErrors.join(" "));
5408
+ // Capture mandatory host extensions at admission: script children may launch after the host disposes its registration.
5409
+ const admittedRequiredExtensions = retainedRequiredExtensions ?? deps.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(ctx.sessionManager.getSessionId() ?? undefined);
5410
+ const workflowRequiredExtensions = hasMandatoryRequiredChildExtensions(admittedRequiredExtensions) ? admittedRequiredExtensions : undefined;
4926
5411
  const foregroundWorkflowRunId = encodeIndexSegment(_id);
4927
5412
  if (delegatedWorkflowPermit) {
4928
5413
  const permitError = validateWorkflowChildPermitRoot(delegatedWorkflowPermit, foregroundWorkflowRunId);
4929
5414
  if (permitError) return buildRequestedModeError(requestParams, permitError);
4930
5415
  if (requestParams.async !== false) return buildRequestedModeError(requestParams, "Workflow child permit supports foreground workflow roots only; set async:false.");
4931
5416
  }
4932
- const workflowParentModel = parentModelOverride !== undefined
4933
- ? parentModelOverride
4934
- : (() => {
4935
- const currentParentModel = normalizeParentModel(ctx.model);
4936
- return (preserveActiveSession
4937
- ? currentParentModel
4938
- : rememberParentModel(deps.state, resolveCurrentSessionId(ctx.sessionManager), currentParentModel)) ?? null;
4939
- })();
4940
5417
  if (requestParams.extensionBindings !== undefined) {
4941
5418
  try {
4942
5419
  requestParams.extensionBindings = normalizeExtensionBindings(requestParams.extensionBindings)!.value;
@@ -4945,6 +5422,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
4945
5422
  }
4946
5423
  }
4947
5424
  const parentCwd = ctx.cwd;
5425
+ for (const [name, value] of [["timeoutMs", requestParams.timeoutMs], ["maxRuntimeMs", requestParams.maxRuntimeMs]] as const) {
5426
+ if (value === undefined) continue;
5427
+ const overflowError = timerDelayOverflowError(name, value);
5428
+ if (overflowError) return buildRequestedModeError(requestParams, overflowError);
5429
+ }
4948
5430
  const timeout = requestParams.timeoutMs ?? requestParams.maxRuntimeMs ?? (requestParams.async === false ? resolveConfigDefaultTimeoutMs(deps.config.timeoutMs) ?? DEFAULT_FOREGROUND_TIMEOUT_MS : undefined);
4949
5431
  const workflowUsageBudget = validateUsageBudgetConfig(requestParams.usageBudget ?? deps.config.usageBudget, requestParams.usageBudget ? "usageBudget" : "config.usageBudget");
4950
5432
  if (workflowUsageBudget.error) return buildRequestedModeError(requestParams, workflowUsageBudget.error);
@@ -5050,6 +5532,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5050
5532
  const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(currentSessionId));
5051
5533
  const completionOwnerId = deps.state.completionOwnerId ?? currentCompletionOwnerId();
5052
5534
  deps.state.completionOwnerId = completionOwnerId;
5535
+ const workflowScriptSourceDigest = workflowScriptDigest(requestParams.workflowScript);
5536
+ const workflowReuseSource = findWorkflowReuseSource(DIRS.async, currentSessionId, workflowScriptSourceDigest, workflowArgsDigest);
5537
+ const workflowRunEvidence: Pick<NonNullable<AsyncStatus["workflow"]>, "args" | "argsDigest" | "scriptDigest" | "reusedFrom" | "stopCause"> = { ...workflowArgsEvidence, scriptDigest: workflowScriptSourceDigest };
5538
+ if (workflowReuseSource) workflowRunEvidence.reusedFrom = workflowReuseSource.runId;
5539
+ const workflowJournalFingerprints = new Map<string, string>();
5053
5540
  try {
5054
5541
  fs.mkdirSync(asyncDir, { recursive: true });
5055
5542
  writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
@@ -5078,9 +5565,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5078
5565
  ...(workflowSessionRoot ? { sessionRoot: workflowSessionRoot } : {}),
5079
5566
  ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}),
5080
5567
  pid: process.pid,
5568
+ ...(workflowCapabilityCeiling ? { admissionCapabilityCeiling: workflowCapabilityCeiling } : {}),
5081
5569
  steps: [],
5082
5570
  ...(workflowPreflight ? { preflight: workflowPreflight } : {}),
5083
- workflow: { trace: [], emits: [], console: [], ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
5571
+ workflow: { trace: [], emits: [], console: [], ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}) },
5084
5572
  workflowChildren: workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "running", inventoryComplete: false }),
5085
5573
  runFanoutBudget: getRunFanoutBudgetSnapshot(workflowFanoutBudget),
5086
5574
  };
@@ -5110,19 +5598,51 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5110
5598
  };
5111
5599
  let pendingResultPublication: Promise<boolean> | undefined;
5112
5600
  let settleResultPublication: ((published: boolean) => void) | undefined;
5113
- const reportResultWriteFailure = (error: unknown): void => {
5601
+ let resultWriteFailureWakeDelivered = false;
5602
+ const reportResultWriteFailure = (error: unknown): boolean => {
5114
5603
  const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
5115
5604
  console.error(message, error);
5605
+ try {
5606
+ if (currentSessionId && fallbackResultPayloadPathForSessionRun(path.dirname(resultPath), currentSessionId, workflowRunId)) return true;
5607
+ } catch (pendingError) {
5608
+ console.error(`Failed to verify pending async workflow result '${workflowRunId}':`, pendingError);
5609
+ }
5116
5610
  appendWorkflowEvent({ type: "subagent.workflow.result_write_failed", error: message });
5611
+ // A missing result file means the result watcher will never deliver the
5612
+ // terminal completion wake. Intermediate child notices are context-only
5613
+ // once their turns are suppressed, so surface the failure as its own
5614
+ // actionable wake instead of leaving the parent asleep.
5615
+ if (resultWriteFailureWakeDelivered) return false;
5616
+ if (deps.state.currentSessionId !== currentSessionId || deps.state.completionOwnerId !== completionOwnerId) return false;
5617
+ try {
5618
+ (deps.parentWake ?? deps.pi).sendMessage(
5619
+ {
5620
+ customType: "subagent-workflow-result-write-failed",
5621
+ content: message,
5622
+ display: true,
5623
+ },
5624
+ { triggerTurn: true },
5625
+ );
5626
+ resultWriteFailureWakeDelivered = true;
5627
+ } catch (sendError) {
5628
+ console.error(`Failed to send workflow result write failure notification for '${workflowRunId}':`, sendError);
5629
+ }
5630
+ deps.refreshResultDelivery?.();
5631
+ return false;
5117
5632
  };
5633
+ const pendingWorkflowChildAnnouncements = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
5634
+ let announcePersistedWorkflowChildren = (): void => {};
5118
5635
  const runPersistence = createCapacityResilientJsonWriter({
5119
5636
  keepAlive: true,
5120
5637
  onSuccess: (filePath, payload) => {
5121
- if (filePath === statusPath) queueActiveRunIndex(payload as AsyncStatus);
5638
+ if (filePath === statusPath) {
5639
+ announcePersistedWorkflowChildren();
5640
+ queueActiveRunIndex(payload as AsyncStatus);
5641
+ }
5122
5642
  if (filePath === resultPath) settleResultPublication?.(true);
5123
5643
  },
5124
5644
  onError: (error, filePath) => {
5125
- if (filePath === resultPath) { reportResultWriteFailure(error); settleResultPublication?.(false); }
5645
+ if (filePath === resultPath) settleResultPublication?.(reportResultWriteFailure(error));
5126
5646
  else console.error(`Failed to persist async workflow state '${filePath}':`, error);
5127
5647
  },
5128
5648
  write: (filePath, payload) => filePath === resultPath
@@ -5139,7 +5659,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5139
5659
  // terminal writes stay fail-fast on purpose: no child work is at risk by then, and
5140
5660
  // silently dropping a terminal write would leave status.json and the active-run
5141
5661
  // index pinned at "running" after the result already says complete.
5142
- const persist = (options: { tolerateStatusWriteFailure?: boolean } = {}) => {
5662
+ const persist = (options: { tolerateStatusWriteFailure?: boolean } = {}): void => {
5143
5663
  if (persistClosed) return;
5144
5664
  const liveJob = deps.state.asyncJobs.get(workflowRunId);
5145
5665
  if (liveJob && (liveJob.status === "complete" || liveJob.status === "failed") && status.state !== "complete" && status.state !== "failed") return;
@@ -5153,7 +5673,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5153
5673
  } else if (options.tolerateStatusWriteFailure) {
5154
5674
  try {
5155
5675
  runPersistence.write(statusPath, { ...status });
5156
- statusPersistenceDegraded = false;
5676
+ if (runPersistence.pendingCount() === 0) statusPersistenceDegraded = false;
5157
5677
  } catch (error) {
5158
5678
  const message = `Failed to persist async workflow state ${statusPath}: ${error instanceof Error ? error.message : String(error)}`;
5159
5679
  console.error(message, error);
@@ -5203,8 +5723,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5203
5723
  if (!resultPublished) pendingResultPublication = new Promise<boolean>((resolve) => { settleResultPublication = resolve; });
5204
5724
  return true;
5205
5725
  } catch (error) {
5206
- reportResultWriteFailure(error);
5207
- return false;
5726
+ return reportResultWriteFailure(error);
5208
5727
  }
5209
5728
  };
5210
5729
  const projectWorkflowActivity = () => {
@@ -5242,7 +5761,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5242
5761
  indexPersistence.dispose();
5243
5762
  return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
5244
5763
  }
5245
- appendWorkflowEvent({ type: "subagent.workflow.started" });
5764
+ appendWorkflowEvent({ type: "subagent.workflow.started", ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}) });
5765
+ try {
5766
+ deps.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
5767
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
5768
+ id: workflowRunId,
5769
+ asyncDir,
5770
+ cwd: workflowCwd,
5771
+ ...(workflowSessionRoot ? { sessionRoot: workflowSessionRoot } : {}),
5772
+ pid: process.pid,
5773
+ sessionId: currentSessionId ?? undefined,
5774
+ completionOwnerId,
5775
+ mode: "workflow",
5776
+ agent: "workflow",
5777
+ goal: derivedObjective.trim() ? PROMPT_REDACTED : undefined,
5778
+ ...(timeout !== undefined ? { timeoutMs: timeout, deadlineAt: startedAt + timeout } : {}),
5779
+ });
5780
+ } catch (error) {
5781
+ console.error("Failed to emit async workflow start event:", error);
5782
+ }
5246
5783
  // The runner does not execute workflow scripts: this active owner must consume its own
5247
5784
  // durable steer inbox, deliver through in-process child controls, and persist receipts.
5248
5785
  const emitWorkflowSteerEvent = (type: string, requestId: string, index?: number, extra: Record<string, unknown> = {}): void => {
@@ -5389,7 +5926,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5389
5926
  const workflowDeadlineAt = timeout === undefined ? undefined : Date.now() + timeout;
5390
5927
  const workflowResults: SingleResult[] = [];
5391
5928
  const workflowChildRunIds = new Map<string, string>();
5392
- 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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
5929
+ const { args: _args, 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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = workflowRequest;
5393
5930
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5394
5931
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5395
5932
  const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, parentCwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, workflowRunId));
@@ -5398,6 +5935,41 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5398
5935
  const childOutputClaimPaths = new Map<string, string>();
5399
5936
  const producedChildOutputPaths = new Set<string>();
5400
5937
  const workflowSteps = new Map<string, NonNullable<AsyncStatus["steps"]>[number]>();
5938
+ const announcedWorkflowChildren = new Set<string>();
5939
+ const announceWorkflowChild = (key: string, step: NonNullable<AsyncStatus["steps"]>[number]): void => {
5940
+ if (announcedWorkflowChildren.has(key)) return;
5941
+ announcedWorkflowChildren.add(key);
5942
+ const stepIndex = status.steps?.indexOf(step);
5943
+ const childEvent: SubagentChildStatusEvent = {
5944
+ type: "subagent.child-status",
5945
+ version: 1,
5946
+ runId: workflowRunId,
5947
+ childId: key,
5948
+ status: "started",
5949
+ ts: Date.now(),
5950
+ source: "async",
5951
+ asyncDir,
5952
+ ...(stepIndex !== undefined ? { stepIndex } : {}),
5953
+ agent: step.agent,
5954
+ ...(step.runId ? { childRunId: step.runId } : {}),
5955
+ workflowKey: key,
5956
+ ...(step.phase ? { phase: step.phase } : {}),
5957
+ ...(step.label ? { label: step.label } : {}),
5958
+ };
5959
+ appendWorkflowEvent({ ...childEvent });
5960
+ try {
5961
+ deps.pi.events.emit(SUBAGENT_CHILD_STATUS_EVENT, childEvent);
5962
+ } catch (error) {
5963
+ console.error("Failed to emit workflow child status event:", error);
5964
+ }
5965
+ };
5966
+ announcePersistedWorkflowChildren = () => {
5967
+ for (const [key, step] of pendingWorkflowChildAnnouncements) {
5968
+ if (!step.runId) continue;
5969
+ announceWorkflowChild(key, step);
5970
+ pendingWorkflowChildAnnouncements.delete(key);
5971
+ }
5972
+ };
5401
5973
  const runHostCommand = workflowHostCommandRunner({
5402
5974
  workflowCwd,
5403
5975
  artifactsDir: workflowArtifactsDir,
@@ -5488,6 +6060,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5488
6060
  workflowSteps.set(entry.key, step);
5489
6061
  }
5490
6062
  const projectedStep = workflowSteps.get(entry.key);
6063
+ if (entry.reused && projectedStep) projectedStep.reused = true;
5491
6064
  if (entry.state === "stopped" && projectedStep) {
5492
6065
  appendWorkflowEvent({
5493
6066
  type: "subagent.child-status",
@@ -5514,11 +6087,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5514
6087
  try {
5515
6088
  const workflow = await runWorkflowScript({
5516
6089
  script: workflowScript,
6090
+ ...(workflowArgs ? { args: workflowArgs } : {}),
6091
+ workflowRunId,
6092
+ processCwd: ctx.cwd,
5517
6093
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5518
6094
  timeoutMs: timeout,
5519
6095
  signal: controller.signal,
5520
6096
  continueAfterAbortWhenChildrenSettled: (abortError) => {
5521
- if (abortError.message !== "Workflow stopped because the extension session was replaced or reloaded.") return false;
6097
+ if (!workflowStopCause(abortError)) return false;
5522
6098
  const activeAsyncChild = [...(deps.state.asyncJobs?.values() ?? [])].some((job) => job.parentWorkflowRunId === workflowRunId && (job.status === "queued" || job.status === "running"));
5523
6099
  const activeForegroundChild = [...deps.state.foregroundControls.values()].some((control) => control.parentWorkflowRunId === workflowRunId && (control.activeChildren?.size ?? 0) > 0);
5524
6100
  return !activeAsyncChild && !activeForegroundChild;
@@ -5529,6 +6105,30 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5529
6105
  },
5530
6106
  ...(workflowState ? { state: workflowState } : {}),
5531
6107
  onTrace: updateTrace,
6108
+ onChildSettled: (notification) => {
6109
+ const journalFingerprint = workflowJournalFingerprints.get(notification.childKey);
6110
+ if (journalFingerprint) appendWorkflowChildJournal(asyncDir, { type: "settle", key: notification.childKey, fingerprint: journalFingerprint, result: notification.result });
6111
+ appendWorkflowEvent({
6112
+ type: "subagent.workflow.child_settled",
6113
+ childKey: notification.childKey,
6114
+ ...(notification.childRunId ? { childRunId: notification.childRunId } : {}),
6115
+ outcome: notification.outcome,
6116
+ workflowRunning: notification.workflowRunning,
6117
+ });
6118
+ try {
6119
+ (deps.parentWake ?? deps.pi).sendMessage(
6120
+ {
6121
+ customType: "subagent-incremental-child-notify",
6122
+ content: formatIncrementalChildCompletion(notification),
6123
+ display: notification.outcome !== "completed",
6124
+ },
6125
+ { triggerTurn: incrementalChildCompletionTriggersTurn(notification, requestParams.scheduleOrigin) },
6126
+ );
6127
+ } catch (sendError) {
6128
+ console.error(`Failed to send incremental child completion notification for '${notification.childKey}':`, sendError);
6129
+ }
6130
+ deps.refreshResultDelivery?.();
6131
+ },
5532
6132
  onLanePlan: (lanes) => {
5533
6133
  status.workflowGraph = buildWorkflowLaneGraph(workflowRunId, lanes, status.workflowGraph);
5534
6134
  applyWorkflowLaneTrace(status.workflowGraph, status.workflow?.trace ?? []);
@@ -5541,7 +6141,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5541
6141
  persist({ tolerateStatusWriteFailure: true });
5542
6142
  } });
5543
6143
  },
5544
- admit: (calls) => {
6144
+ admit: async (calls, admissionSignal) => {
6145
+ admitEnabledWorkflowChildren(calls);
6146
+ await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: parentCwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
5545
6147
  const outputClaims = workflowChildOutputClaims({ ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5546
6148
  if (outputClaims.error) throw new Error(outputClaims.error);
5547
6149
  status.runFanoutBudget = claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
@@ -5557,6 +6159,32 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5557
6159
  appendWorkflowEvent({ type: "subagent.workflow.emit", value: emits.at(-1) });
5558
6160
  },
5559
6161
  launch: async (key, childParams, workflowSignal, admission) => {
6162
+ const journalFingerprint = workflowJournalFingerprints.get(key) ?? workflowChildFingerprint(childParams);
6163
+ workflowJournalFingerprints.set(key, journalFingerprint);
6164
+ const priorChild = workflowReuseSource ? matchWorkflowReuse(workflowReuseSource, key, journalFingerprint) : undefined;
6165
+ if (priorChild?.kind === "settled") {
6166
+ if (priorChild.result.runId) workflowChildRunIds.set(key, priorChild.result.runId);
6167
+ return { ...priorChild.result, reused: true };
6168
+ }
6169
+ if (priorChild?.kind === "started") {
6170
+ // The previous run's awaited async child may still be running: wait for that exact run
6171
+ // instead of launching again. Journal it first so another reload can re-attach too.
6172
+ const priorRunId = priorChild.runId;
6173
+ const priorAsyncDir = path.join(DIRS.async, priorRunId);
6174
+ appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
6175
+ const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps.state, priorRunId, priorAsyncDir, deps.kill);
6176
+ const reattached = await awaitExistingAsyncRun(priorAsyncDir, priorRunId, workflowSignal).finally(stopListener.remove);
6177
+ const claimedPath = reattached.status === "settled" ? claimWorkflowAwaitedResult(priorAsyncDir, workflowRunId) : undefined;
6178
+ if (reattached.status === "settled" && claimedPath) {
6179
+ const imported = importWorkflowAwaitedChildResult(reattached.result, { runId: priorRunId, asyncDir: priorAsyncDir, resultPath: claimedPath, task: typeof childParams.task === "string" ? childParams.task : "", parentWorkflowRunId: workflowReuseSource?.runId, details: { mode: "single", asyncId: priorRunId, asyncDir: priorAsyncDir, results: [] }, state: deps.state, pi: deps.pi });
6180
+ const reattachedResult = workflowChildResult(key, imported, childParams, deps.state);
6181
+ // Like a settled journal match: only a successful child is reused; a failed or stopped one launches fresh.
6182
+ if (reattachedResult.ok) {
6183
+ workflowChildRunIds.set(key, priorRunId);
6184
+ return { ...reattachedResult, reused: true };
6185
+ }
6186
+ }
6187
+ }
5560
6188
  if (workflowUsageBudget.budget && childParams.async === true) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, "workflow usageBudget does not support async runs.run launches."), childParams, deps.state);
5561
6189
  const budgetState = usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults));
5562
6190
  if (budgetState?.exhausted) return workflowChildResult(key, buildRequestedModeError(childParams as SubagentParamsLike, usageBudgetExceededMessage(budgetState)), childParams, deps.state, { state: "partial", reason: "budget_exhausted" });
@@ -5575,10 +6203,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5575
6203
  { ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams, parentWorkflowRunId: workflowRunId, workflowKey: key, ctxCwd: parentCwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, awaitDetachedChild: true, runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
5576
6204
  missionBinding,
5577
6205
  deps.asyncByDefault,
6206
+ undefined,
6207
+ inheritedNestedRoute(deps)?.rootRunId,
5578
6208
  );
5579
6209
  preparedChildParams = childRequest;
5580
6210
  if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
6211
+ if (workflowRequiredExtensions) workflowRetainedRequiredExtensions.set(childRequest, workflowRequiredExtensions);
5581
6212
  workflowLaunchObservers.set(childRequest, (launch) => {
6213
+ if (launch.runId) appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: launch.runId });
5582
6214
  const step = status.steps?.find((candidate) => candidate.workflowKey === key);
5583
6215
  if (step) {
5584
6216
  step.agent = launch.agent;
@@ -5587,6 +6219,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5587
6219
  step.async = launch.async;
5588
6220
  if (launch.runId) step.runId = launch.runId;
5589
6221
  if (childRequest.lane) step.lane = childRequest.lane;
6222
+ if (step.runId) pendingWorkflowChildAnnouncements.set(key, step);
5590
6223
  persist({ tolerateStatusWriteFailure: true });
5591
6224
  }
5592
6225
  recordMissionWorkflowChild(missionBinding, workflowRunId, key, { status: "running", agent: launch.agent, ...(launch.sessionFile ? { sessionPath: launch.sessionFile } : {}) });
@@ -5653,16 +6286,20 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5653
6286
  });
5654
6287
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
5655
6288
  const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
5656
- const returnPreview = formatWorkflowValue(workflow.value).slice(0, 1_000);
5657
- const emitPreview = workflow.emits.length > 0 ? ` Emitted: ${workflow.emits.map(formatWorkflowValue).join(", ").slice(0, 1_000)}` : "";
5658
- const summary = `Workflow completed with ${workflow.children.length} child run(s). Return: ${returnPreview}${emitPreview} Trace: ${workflow.trace.length} event(s).${workflowOutputPathMappingSummary(workflow.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
6289
+ const returnText = formatWorkflowValue(workflow.value);
6290
+ const emitText = workflow.emits.map(formatWorkflowValue).join(", ");
6291
+ const returnPreview = returnText.length > 1_000 ? `${returnText.slice(0, 1_000)}…` : returnText;
6292
+ const emitPreview = workflow.emits.length > 0 ? ` Emitted: ${emitText.length > 1_000 ? `${emitText.slice(0, 1_000)}…` : emitText}` : "";
6293
+ const previewNote = returnText.length > 1_000 || emitText.length > 1_000 ? ` (truncated; full return value and emits: ${statusPath} (workflow.value, workflow.emits))` : "";
6294
+ const runningSummary = workflowRunningChildrenSummary(workflow.children);
6295
+ const summary = `${runningSummary ? `Workflow dispatch completed; ${runningSummary}` : `Workflow completed with ${workflow.children.length} child run(s).`} Return: ${returnPreview}${emitPreview}${previewNote} Trace: ${workflow.trace.length} event(s).${workflowOutputPathMappingSummary(workflow.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
5659
6296
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, summary, producedChildOutputPaths);
5660
6297
  const resultSummary = appendWorkflowOutputWarning(summary, outputWarning);
5661
6298
  const workflowUsage = sumResultsUsage(workflowResults);
5662
6299
  settleWorkflowSteerInbox("complete");
5663
6300
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children, steps: status.steps });
5664
- status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
5665
- const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
6301
+ status = { ...status, state: "complete", endedAt: Date.now(), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren, totalTokens: { input: workflowUsage.input, output: workflowUsage.output, total: workflowUsage.input + workflowUsage.output }, totalCost: sumResultsCost(workflowResults) };
6302
+ const receipt = terminalWorkflowReceipt(workflowRunId, "complete", workflow.children, workflowChildren, undefined, validHostStepNodes(status.workflowGraph), workflowResource?.provenance, workflowArgsDigest);
5666
6303
  delete status.workflowReceiptPath;
5667
6304
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5668
6305
  try {
@@ -5671,17 +6308,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5671
6308
  } catch (receiptError) {
5672
6309
  appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
5673
6310
  }
5674
- if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, workflowChildren, results: workflow.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
6311
+ if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: true, state: "complete", summary: resultSummary, output: resultSummary, workflowChildren, results: workflowResultChildren(workflow.children, status, false), workflow: status.workflow, ...(workflowCapabilityCeiling ? { admissionCapabilityCeiling: workflowCapabilityCeiling } : {}), ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
5675
6312
  if (pendingResultPublication && !await pendingResultPublication) return;
5676
6313
  persist();
5677
6314
  deps.refreshResultDelivery?.();
5678
6315
  persistClosed = true;
5679
- appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(status.error ? { error: status.error } : {}) });
6316
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}), ...(status.error ? { error: status.error } : {}) });
5680
6317
  } catch (error) {
5681
6318
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
5682
6319
  const stopped = controller.signal.aborted;
6320
+ const stopCause = stopped ? workflowStopCause(controller.signal.reason) : undefined;
6321
+ if (stopCause) workflowRunEvidence.stopCause = stopCause;
5683
6322
  const detachedChildKeys = new Set(partial.children.filter((child) => child.detached).map((child) => child.key));
5684
- const hasRealFailedChild = partial.children.some((child) => !child.ok && !child.detached);
6323
+ const hasRealFailedChild = partial.children.some((child) => !child.ok && child.state !== "running" && !child.detached);
5685
6324
  const pauseForDetached = !stopped && error instanceof WorkflowScriptError && error.errorKind === "detached-child" && detachedChildKeys.size > 0 && !hasRealFailedChild;
5686
6325
  const state = stopped ? "stopped" : pauseForDetached ? "paused" : "failed";
5687
6326
  for (const step of status.steps ?? []) {
@@ -5700,19 +6339,21 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5700
6339
  const workflowChildren = workflowChildSummary({ parentToolCallId: toolCallId, workflowRunId, workflowState, inventoryComplete: true, trace: partial.trace, children: partial.children, steps: status.steps });
5701
6340
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
5702
6341
  const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
5703
- status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
6342
+ status = compactOptional<AsyncStatus>({ ...status, state, stopped: stopped || undefined, activityState: pauseForDetached || status.activityState === "needs_attention" ? "needs_attention" : undefined, error: workflowFailureMessage(error, workflowRunId, partial.children), endedAt: Date.now(), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(error instanceof WorkflowScriptError && error.errorKind ? { failureKind: error.errorKind } : {}), ...workflowRunEvidence, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}) }, workflowChildren });
5704
6343
  if (pauseForDetached) {
5705
6344
  const promoted = promotePausedWorkflowIfSettled(status);
5706
6345
  if (promoted) status = promoted;
5707
6346
  }
6347
+ // A script can throw an error of any size; status.json keeps the full text.
6348
+ const errorPreview = status.error && status.error.length > 1_000 ? `${status.error.slice(0, 1_000)}… (truncated; full error: ${statusPath} (error))` : status.error;
5708
6349
  const terminalSummary = `${status.state === "complete"
5709
6350
  ? "Workflow completed after detached child finished."
5710
- : status.error ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
6351
+ : errorPreview ?? (pauseForDetached ? "Workflow paused." : "Workflow failed.")}${stopCause ? ` ${WORKFLOW_RUNTIME_REPLACED_RELAUNCH_NOTICE}` : ""}${workflowOutputPathMappingSummary(partial.children)}${finalPreflightWarnings.length ? ` ${finalPreflightWarnings.join(" ")}` : ""}`;
5711
6352
  const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, terminalSummary, producedChildOutputPaths);
5712
6353
  const resultSummary = appendWorkflowOutputWarning(terminalSummary, outputWarning);
5713
6354
  const receiptState: WorkflowReceiptState = status.state === "complete" ? "complete" : status.state === "paused" ? "paused" : status.state === "stopped" ? "stopped" : "failed";
5714
6355
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5715
- const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance);
6356
+ const receipt = terminalWorkflowReceipt(workflowRunId, receiptState, partial.children, workflowChildren, terminalOutcome, validHostStepNodes(status.workflowGraph), workflowResource?.provenance, workflowArgsDigest);
5716
6357
  delete status.workflowReceiptPath;
5717
6358
  let workflowReceipt: { path: string; receipt: WorkflowReceipt } | undefined;
5718
6359
  try {
@@ -5721,12 +6362,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5721
6362
  } catch (receiptError) {
5722
6363
  appendWorkflowEvent({ type: "subagent.workflow.receipt_write_failed", error: `Failed to persist async workflow receipt: ${receiptError instanceof Error ? receiptError.message : String(receiptError)}` });
5723
6364
  }
5724
- if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, workflowChildren, ...(terminalOutcome ? { terminalOutcome } : {}), results: partial.children.map((child) => ({ workflowKey: child.key, ...(child.agent ? { agent: child.agent } : {}), ...(child.runId ? { runId: child.runId } : {}), ...(status.steps?.find((step) => step.workflowKey === child.key)?.sessionName ? { sessionName: status.steps?.find((step) => step.workflowKey === child.key)?.sessionName } : {}), ...workflowChildAccountingFields(child), output: child.output, outputState: child.output.trim() || child.structuredOutput !== undefined ? "present" : "absent", structuredOutput: child.structuredOutput, success: child.ok, ...(child.outputReference ? { outputReference: child.outputReference } : {}), ...(child.terminalOutcome ? { terminalOutcome: child.terminalOutcome } : {}), ...(child.outputPathMapping ? { outputPathMapping: child.outputPathMapping } : {}), ...(child.stopped ? { stopped: true } : {}), ...(child.interrupted ? { interrupted: true } : {}), ...(child.detached && status.state !== "complete" ? { detached: true } : {}), ...(child.artifactPaths[0] ? { artifactPaths: { outputPath: child.artifactPaths[0] } } : {}) })), workflow: status.workflow, ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
6365
+ if (!writeWorkflowResult({ id: workflowRunId, runId: workflowRunId, toolCallId, agent: "workflow", mode: "workflow", success: status.state === "complete", state: status.state, summary: resultSummary, error: status.state === "complete" ? undefined : status.error, stopped: status.stopped, activityState: status.activityState, workflowChildren, ...(terminalOutcome ? { terminalOutcome } : {}), results: workflowResultChildren(partial.children, status, true), workflow: status.workflow, ...(workflowCapabilityCeiling ? { admissionCapabilityCeiling: workflowCapabilityCeiling } : {}), ...(workflowReceipt ? { workflowReceipt } : {}), asyncDir, cwd: workflowCwd, sessionId: currentSessionId, completionOwnerId, ...(requestParams.scheduleOrigin ? { scheduleOrigin: requestParams.scheduleOrigin } : {}), timestamp: Date.now(), durationMs: Date.now() - startedAt })) return;
5725
6366
  if (pendingResultPublication && !await pendingResultPublication) return;
5726
6367
  persist();
5727
6368
  deps.refreshResultDelivery?.();
5728
6369
  persistClosed = true;
5729
- appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
6370
+ appendWorkflowEvent({ type: "subagent.workflow.completed", state: status.state, ...(workflowArgsDigest ? { argsDigest: workflowArgsDigest } : {}), ...(terminalOutcome ? { terminalOutcome } : {}), ...(status.error ? { error: status.error } : {}), ...(status.activityState ? { activityState: status.activityState } : {}) });
5730
6371
  } finally {
5731
6372
  // Idempotent cleanup only: a failed result/index write must not authorize terminal status.
5732
6373
  try {
@@ -5746,10 +6387,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5746
6387
  details: { mode: "workflow", runId: workflowRunId, toolCallId, asyncId: workflowRunId, asyncDir, results: [], ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflow: status.workflow, workflowChildren: status.workflowChildren, chatProgress, ...(deps.state.activeAsyncCapacity ? { activeAsyncCapacity: deps.state.activeAsyncCapacity } : {}) },
5747
6388
  }, workflowFanoutBudget));
5748
6389
  }
5749
- 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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
6390
+ const { workflowScript: _workflowScript, args: _args, 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, preflight: _preflight, globalConcurrencyLimit: _globalConcurrencyLimit, maxSubagentSpawnsPerRun: _maxSubagentSpawnsPerRun, ...workflowChildDefaults } = requestParams;
5750
6391
  const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : undefined;
5751
6392
  const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps);
5752
6393
  const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps, workflowArtifactsDir, foregroundWorkflowRunId));
6394
+ // A top-level artifact file: outside outputs/, where children save their reports, and
6395
+ // within artifact retention, which only removes top-level files.
6396
+ const workflowFullResultPath = path.join(workflowArtifactsDir, `${sanitizeRunPathSegment(foregroundWorkflowRunId)}_workflow-result.md`);
5753
6397
  const claimedOutputPaths = new Map<string, string>();
5754
6398
  const childOutputOverrides = new Map<string, string>();
5755
6399
  const childOutputClaimPaths = new Map<string, string>();
@@ -5788,6 +6432,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5788
6432
  try {
5789
6433
  const workflow = await runWorkflowScript({
5790
6434
  script: requestParams.workflowScript,
6435
+ ...(workflowArgs ? { args: workflowArgs } : {}),
6436
+ workflowRunId: foregroundWorkflowRunId,
6437
+ processCwd: ctx.cwd,
5791
6438
  ...(delegatedWorkflowPermit ? { oneUsePermit: { claim: (key: string) => claimWorkflowChildPermit(delegatedWorkflowPermit, foregroundWorkflowRunId, key) } } : {}),
5792
6439
  globalConcurrencyLimit: requestParams.globalConcurrencyLimit ?? deps.config.globalConcurrencyLimit,
5793
6440
  timeoutMs: timeout,
@@ -5806,7 +6453,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5806
6453
  }
5807
6454
  sendWorkflowProgress();
5808
6455
  },
5809
- admit: (calls) => {
6456
+ admit: async (calls, admissionSignal) => {
6457
+ admitEnabledWorkflowChildren(calls);
6458
+ await preflightWorkflowWorktrees({ workflowDefaults: workflowChildDefaults, defaultWorktree: deps.config.worktree, calls, ctxCwd: ctx.cwd, signal: admissionSignal, deadlineAt: workflowDeadlineAt });
5810
6459
  const outputClaims = workflowChildOutputClaims({ ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, workflowRunId: foregroundWorkflowRunId, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, state: deps.state, claimedOutputPaths, entries: calls });
5811
6460
  if (outputClaims.error) throw new Error(outputClaims.error);
5812
6461
  claimRunFanoutBatch(workflowFanoutBudget, calls.map(({ key }) => `workflow[${key}]`));
@@ -5840,9 +6489,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5840
6489
  { ...prepareWorkflowChildLaunchParams({ workflowDefaults: workflowChildDefaults, childParams: delegatedWorkflowPermit ? { ...childParams, async: false } : childParams, parentWorkflowRunId: foregroundWorkflowRunId, workflowKey: key, ctxCwd: ctx.cwd, workflowCwd, artifactsDir: workflowArtifactsDir, aggregateOutputPath: workflowAggregateOutputPath, configuredOutputBaseDir, discoverAgents: discoverWorkflowAgents, agents: workflowAgents, workflowAgentScope: workflowChildDefaults.agentScope, outputOverride: childOutputOverrides.get(key), outputClaimPath: childOutputClaimPaths.get(key), options: { missionDetached: detachWorkflowChildMissions, suppressRoutineResultIntercom: chatProgress.mode === "live-card", runFanoutBudget: workflowFanoutBudget, parentDeadlineAt: workflowDeadlineAt, capabilityCeiling: workflowCapabilityCeiling } }), runFanoutAdmitted: admission.admitted },
5841
6490
  missionBinding,
5842
6491
  deps.asyncByDefault,
6492
+ undefined,
6493
+ inheritedNestedRoute(deps)?.rootRunId,
5843
6494
  );
5844
6495
  preparedChildParams = childRequest;
5845
6496
  if (workflowUsageBudget.budget) workflowOwnedUsageBudgets.set(childRequest, workflowUsageBudget.budget);
6497
+ if (workflowRequiredExtensions) workflowRetainedRequiredExtensions.set(childRequest, workflowRequiredExtensions);
5846
6498
  if (delegatedWorkflowPermit) {
5847
6499
  if (childRequest.async !== false) throw new Error("Workflow child permit supports one foreground child only.");
5848
6500
  const childCwd = resolveRequestedCwd(workflowCwd, childRequest.cwd);
@@ -5902,52 +6554,62 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5902
6554
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, workflow.trace, { settled: true });
5903
6555
  const finalPreflightTrace = annotateWorkflowPreflightTrace(workflow.trace, workflowPreflight);
5904
6556
  const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "completed", inventoryComplete: true, trace: workflow.trace, children: workflow.children });
5905
- const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance);
5906
- const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
5907
- const sections = [
5908
- ...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
5909
- "Workflow completed.",
5910
- `Return:\n${formatWorkflowValue(workflow.value)}`,
5911
- ];
5912
- if (workflow.emits.length > 0) sections.push(`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`);
5913
- if (workflow.console.length > 0) sections.push(`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
5914
- if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
5915
- if (finalPreflightWarnings.length > 0) sections.push(formatWorkflowPreflightWarnings(finalPreflightWarnings));
6557
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "complete", workflow.children, workflowChildren, undefined, [...workflowHostSteps.values()], workflowResource?.provenance, workflowArgsDigest);
6558
+ const trace = finalPreflightTrace.map((entry) => ({ line: `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.durationMs !== undefined ? ` in ${entry.durationMs}ms` : ""}${entry.warning ? ` — ${entry.warning}` : ""}`, error: entry.error }));
6559
+ const runningSummary = workflowRunningChildrenSummary(workflow.children);
5916
6560
  const outputMappings = workflowOutputPathMappingSummary(workflow.children).trim();
5917
- if (outputMappings) sections.push(outputMappings);
5918
- const workflowText = sections.join("\n\n");
5919
- const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
5920
- const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
6561
+ const displayText = formatWorkflowResultText({
6562
+ head: [...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []), runningSummary ? `Workflow dispatch completed; ${runningSummary}` : "Workflow completed."],
6563
+ script: [
6564
+ `Return:\n${formatWorkflowValue(workflow.value)}`,
6565
+ ...(workflow.emits.length > 0 ? [`Emitted:\n${workflow.emits.map(formatWorkflowValue).join("\n")}`] : []),
6566
+ ...(workflow.console.length > 0 ? [`Console:\n${workflow.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`] : []),
6567
+ ],
6568
+ trace,
6569
+ tail: [
6570
+ ...(finalPreflightWarnings.length > 0 ? [formatWorkflowPreflightWarnings(finalPreflightWarnings)] : []),
6571
+ ...(outputMappings ? [outputMappings] : []),
6572
+ ],
6573
+ maxOutput: requestParams.maxOutput,
6574
+ aggregateOutputPath: workflowAggregateOutputPath,
6575
+ fullResultPath: workflowFullResultPath,
6576
+ producedChildOutputPaths,
6577
+ });
5921
6578
  return attachWorkflowMission(withRunFanoutBudget({
5922
6579
  content: [{ type: "text", text: displayText }],
5923
- details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
6580
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(workflow.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { value: workflow.value, trace: finalPreflightTrace, emits: workflow.emits, console: workflow.console, ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5924
6581
  }, workflowFanoutBudget));
5925
6582
  } catch (error) {
5926
6583
  const partial = error instanceof WorkflowScriptError ? error.partial : { trace: [], emits: [], console: [], children: [] };
5927
6584
  const text = workflowFailureMessage(error, foregroundWorkflowRunId, partial.children);
5928
6585
  const finalPreflightWarnings = workflowPreflightWarnings(workflowPreflight, partial.trace, { settled: true });
5929
6586
  const finalPreflightTrace = annotateWorkflowPreflightTrace(partial.trace, workflowPreflight);
5930
- const traceLines = finalPreflightTrace.map((entry) => `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.warning ? ` — ${entry.warning}` : ""}${entry.error ? ` — ${entry.error}` : ""}`);
5931
- const sections = [
5932
- ...(workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : []),
5933
- `Workflow failed: ${text}`,
5934
- ];
5935
- if (partial.emits.length > 0) sections.push(`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`);
5936
- if (partial.console.length > 0) sections.push(`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`);
5937
- if (traceLines.length > 0) sections.push(`Call trace:\n${traceLines.join("\n")}`);
5938
- if (finalPreflightWarnings.length > 0) sections.push(formatWorkflowPreflightWarnings(finalPreflightWarnings));
6587
+ const trace = finalPreflightTrace.map((entry) => ({ line: `- ${entry.operation} ${entry.key}: ${entry.state}${entry.runId ? ` (${entry.runId})` : ""}${entry.warning ? ` — ${entry.warning}` : ""}`, error: entry.error }));
5939
6588
  const outputMappings = workflowOutputPathMappingSummary(partial.children).trim();
5940
- if (outputMappings) sections.push(outputMappings);
5941
- const workflowText = sections.join("\n\n");
5942
- const outputWarning = writeWorkflowAggregateOutput(workflowAggregateOutputPath, workflowText, producedChildOutputPaths);
5943
- const displayText = appendWorkflowOutputWarning(workflowText, outputWarning);
6589
+ const displayText = formatWorkflowResultText({
6590
+ head: workflowPreflight ? [formatWorkflowPreflight(workflowPreflight)] : [],
6591
+ script: [
6592
+ `Workflow failed: ${text}`,
6593
+ ...(partial.emits.length > 0 ? [`Emitted:\n${partial.emits.map(formatWorkflowValue).join("\n")}`] : []),
6594
+ ...(partial.console.length > 0 ? [`Console:\n${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`] : []),
6595
+ ],
6596
+ trace,
6597
+ tail: [
6598
+ ...(finalPreflightWarnings.length > 0 ? [formatWorkflowPreflightWarnings(finalPreflightWarnings)] : []),
6599
+ ...(outputMappings ? [outputMappings] : []),
6600
+ ],
6601
+ maxOutput: requestParams.maxOutput,
6602
+ aggregateOutputPath: workflowAggregateOutputPath,
6603
+ fullResultPath: workflowFullResultPath,
6604
+ producedChildOutputPaths,
6605
+ });
5944
6606
  const workflowChildren = workflowChildSummary({ parentToolCallId: _id, workflowRunId: foregroundWorkflowRunId, workflowState: "failed", inventoryComplete: true, trace: partial.trace, children: partial.children });
5945
6607
  const terminalOutcome = workflowFailureTerminalOutcome(error, partial.children, usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)));
5946
- const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance);
6608
+ const receipt = terminalWorkflowReceipt(foregroundWorkflowRunId, "failed", partial.children, workflowChildren, terminalOutcome, [...workflowHostSteps.values()], workflowResource?.provenance, workflowArgsDigest);
5947
6609
  return attachWorkflowMission(withRunFanoutBudget({
5948
6610
  content: [{ type: "text", text: displayText }],
5949
6611
  isError: true,
5950
- details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
6612
+ details: compactOptional<Details>({ mode: "workflow", runId: foregroundWorkflowRunId, results: workflowDetailsResults(partial.children), ...(workflowPreflight ? { preflight: workflowPreflight } : {}), workflowChildren, totalChildUsage: sumResultsUsage(workflowResults), totalCost: sumResultsCost(workflowResults), usageBudget: usageBudgetState(workflowUsageBudget.budget, sumResultsCost(workflowResults)), workflow: { trace: finalPreflightTrace, emits: partial.emits, console: partial.console, ...(error instanceof WorkflowScriptError && error.errorKind ? { failureKind: error.errorKind } : {}), ...(workflowArgsEvidence ?? {}), ...(workflowResource ? { resource: workflowResource.provenance } : {}), ...(finalPreflightWarnings.length ? { preflightWarnings: finalPreflightWarnings } : {}), receipt }, chatProgress }),
5951
6613
  }, workflowFanoutBudget));
5952
6614
  } finally {
5953
6615
  workflowProgressClosed = true;
@@ -5956,9 +6618,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5956
6618
  }
5957
6619
  }
5958
6620
  const directParams = requestParams;
5959
- const requestedCwd = params.cwd;
5960
- const requestCwd = resolveRequestedCwd(ctx.cwd, directParams.cwd);
5961
- const paramsWithResolvedCwd = directParams.cwd === undefined ? directParams : { ...directParams, cwd: requestCwd };
6621
+ // With a machine, cwd names a directory on that machine: keep it out of every local path resolution.
6622
+ // Placement can also come from the agent's frontmatter or settings override, so the agent is resolved first (discovery is fingerprint-cached).
6623
+ const placedByAgent = (): boolean => {
6624
+ if (directParams.machine !== undefined || directParams.cwd === undefined || typeof directParams.agent !== "string") return false;
6625
+ try {
6626
+ return resolveAgentName(directParams.agent, deps.discoverAgents(ctx.cwd, resolveExecutionAgentScope(directParams.agentScope)).agents).agent?.machine !== undefined;
6627
+ } catch {
6628
+ return false; // Discovery errors surface from the launch path itself.
6629
+ }
6630
+ };
6631
+ const remotePlacement = directParams.action === undefined && (directParams.machine !== undefined || placedByAgent());
6632
+ const requestedCwd = remotePlacement ? undefined : directParams.cwd;
6633
+ const requestCwd = remotePlacement ? ctx.cwd : resolveRequestedCwd(ctx.cwd, directParams.cwd);
6634
+ const paramsWithResolvedCwd = directParams.cwd === undefined
6635
+ ? directParams
6636
+ : remotePlacement
6637
+ ? omitUndefinedProperties({ ...directParams, cwd: undefined, machineCwd: directParams.cwd })
6638
+ : { ...directParams, cwd: requestCwd };
5962
6639
  const action = paramsWithResolvedCwd.action;
5963
6640
  let requestSessionId = "";
5964
6641
  let requestPiSessionId: string | undefined;
@@ -5980,11 +6657,30 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
5980
6657
  if (deps.allowMutatingManagementActions === false) {
5981
6658
  return { content: [{ type: "text", text: "Action 'worktree.cleanup' is not available from child-safe subagent fanout mode." }], isError: true, details: { mode: "management", results: [] } };
5982
6659
  }
6660
+ if (paramsWithResolvedCwd.mode === "apply") {
6661
+ try {
6662
+ if (!paramsWithResolvedCwd.repo?.trim() || !paramsWithResolvedCwd.planId?.trim() || paramsWithResolvedCwd.handoffPath !== undefined) throw new Error("worktree.cleanup apply requires explicit repo and planId, without handoffPath.");
6663
+ const repo = path.resolve(requestCwd, paramsWithResolvedCwd.repo);
6664
+ const plan = loadReviewedCleanupPlan(repo, paramsWithResolvedCwd.planId);
6665
+ const decision = resolveAuthorityDecision({ action: "discardWorktree", ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6666
+ if (decision === "forbid") throw new Error("Authority policy forbids worktree cleanup.");
6667
+ if (decision === "confirm") {
6668
+ if (!ctx.hasUI) throw new Error("Authority policy requires interactive confirmation for worktree cleanup.");
6669
+ if (!await ctx.ui.confirm("Apply reviewed worktree cleanup?", `Repository: ${plan.repoRoot}\nPlan: ${plan.planId}\nRemove up to ${plan.entries.filter((entry) => entry.decision === "remove").length} eligible worktrees. All local branches are retained.`)) return { content: [{ type: "text", text: "Worktree cleanup canceled." }], details: { mode: "management", results: [] } };
6670
+ }
6671
+ const applied = await applyReviewedCleanupPlan({ repo: plan.repoRoot, planId: plan.planId, authorized: true, signal,
6672
+ foregroundRunOwnership: (runId) => deps.state.foregroundControls.has(runId) ? "active" : (deps.state.foregroundRuns?.get(runId)?.children.length && deps.state.foregroundRuns.get(runId)!.children.every((child) => child.status !== "detached")) ? "terminal" : "unknown",
6673
+ });
6674
+ return { content: [{ type: "text", text: formatCleanupReceipt(applied) }], details: { mode: "management", results: [] } };
6675
+ } catch (error) {
6676
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6677
+ }
6678
+ }
5983
6679
  if (paramsWithResolvedCwd.mode !== "plan") {
5984
- return { content: [{ type: "text", text: "worktree.cleanup currently supports mode='plan' only; apply/removal is not available yet." }], isError: true, details: { mode: "management", results: [] } };
6680
+ return { content: [{ type: "text", text: "worktree.cleanup requires mode='plan' or mode='apply'." }], isError: true, details: { mode: "management", results: [] } };
5985
6681
  }
5986
6682
  if (paramsWithResolvedCwd.planId !== undefined) {
5987
- return { content: [{ type: "text", text: "worktree.cleanup plan mode does not accept planId; apply is not available yet." }], isError: true, details: { mode: "management", results: [] } };
6683
+ return { content: [{ type: "text", text: "worktree.cleanup plan mode does not accept planId." }], isError: true, details: { mode: "management", results: [] } };
5988
6684
  }
5989
6685
  try {
5990
6686
  const created = createWorktreeCleanupPlan({
@@ -6057,6 +6753,24 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6057
6753
  return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6058
6754
  }
6059
6755
  }
6756
+ const policyAction = action === "stop" || action === "command.cancel" ? "stopRun" : action === "steer" || action === "command.yield" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : action === "inspector.open" ? "inspectorOpen" : action === "project.open" ? "projectOpen" : undefined;
6757
+ if (policyAction) {
6758
+ // Child-safe mode is a hard capability boundary; the policy is an operator
6759
+ // preference. Refuse first, so the gate never prompts for an action that is
6760
+ // going to be rejected anyway and never masks the more specific reason.
6761
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6762
+ return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
6763
+ }
6764
+ const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6765
+ if (decision === "forbid") {
6766
+ return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
6767
+ }
6768
+ if (decision === "confirm") {
6769
+ if (!ctx.hasUI) return { content: [{ type: "text", text: `Authority policy requires user confirmation for action '${action}', but this session has no interactive UI.` }], isError: true, details: { mode: "management", results: [] } };
6770
+ const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
6771
+ if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
6772
+ }
6773
+ }
6060
6774
  if ((HERDR_PROJECT_PANE_ACTIONS as readonly string[]).includes(action)) {
6061
6775
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6062
6776
  return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
@@ -6064,16 +6778,17 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6064
6778
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6065
6779
  return handleHerdrProjectPaneAction(action as (typeof HERDR_PROJECT_PANE_ACTIONS)[number], paramsWithResolvedCwd, { cwd: requestCwd, state: deps.state, signal });
6066
6780
  }
6067
- if ((HERDR_INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
6781
+ if ((INSPECTOR_ACTIONS as readonly string[]).includes(action)) {
6068
6782
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6069
6783
  return { content: [{ type: "text", text: `Action '${action}' is not available from child-safe subagent fanout mode.` }], isError: true, details: { mode: "management", results: [] } };
6070
6784
  }
6071
6785
  deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6072
- return handleHerdrInspectorAction(action as (typeof HERDR_INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
6786
+ return handleInspectorAction(action as (typeof INSPECTOR_ACTIONS)[number], paramsWithResolvedCwd, {
6073
6787
  state: deps.state,
6074
6788
  cwd: requestCwd,
6075
6789
  ...(deps.config.missions ? { missions: deps.config.missions } : {}),
6076
6790
  ...(deps.config.authorityPolicy ? { authorityPolicy: deps.config.authorityPolicy } : {}),
6791
+ plugins: getInspectorPlugins(deps.pi),
6077
6792
  signal,
6078
6793
  });
6079
6794
  }
@@ -6092,18 +6807,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6092
6807
  ...(currentSessionId ? { currentSessionId } : {}),
6093
6808
  });
6094
6809
  }
6095
- const policyAction = action === "stop" ? "stopRun" : action === "steer" ? "steerRun" : action === "schedule.create" ? "scheduleCreate" : undefined;
6096
- if (policyAction) {
6097
- const decision = resolveAuthorityDecision({ action: policyAction, ...(deps.config.authorityPolicy === undefined ? {} : { policy: deps.config.authorityPolicy }) });
6098
- if (decision === "forbid") {
6099
- return { content: [{ type: "text", text: `Authority policy forbids action '${action}'.` }], isError: true, details: { mode: "management", results: [] } };
6100
- }
6101
- if (decision === "confirm") {
6102
- if (!ctx.hasUI) return { content: [{ type: "text", text: `Authority policy requires user confirmation for action '${action}', but this session has no interactive UI.` }], isError: true, details: { mode: "management", results: [] } };
6103
- const confirmed = await ctx.ui.confirm(`Authorize subagent ${action}?`, `Authority policy requires confirmation before '${action}'.`);
6104
- if (!confirmed) return { content: [{ type: "text", text: `Action '${action}' canceled; authority was not granted.` }], details: { mode: "management", results: [] } };
6105
- }
6106
- }
6107
6810
  if ((WATCHDOG_TOOL_ACTIONS as readonly string[]).includes(action)) {
6108
6811
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6109
6812
  return {
@@ -6211,8 +6914,10 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6211
6914
  }
6212
6915
  if (action === "guide") {
6213
6916
  try {
6917
+ const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
6918
+ const notice = paramsWithResolvedCwd.topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : undefined;
6214
6919
  return {
6215
- content: [{ type: "text", text: readSubagentGuide(paramsWithResolvedCwd.topic) }],
6920
+ content: [{ type: "text", text: notice ? `${notice}\n\n${guide}` : guide }],
6216
6921
  details: { mode: "management", results: [] },
6217
6922
  };
6218
6923
  } catch (error) {
@@ -6234,22 +6939,25 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6234
6939
  if (action === "doctor") {
6235
6940
  let currentSessionFile: string | null = null;
6236
6941
  let currentSessionId = deps.state.currentSessionId;
6942
+ let budgetSessionId = deps.state.currentSessionId;
6237
6943
  let sessionError: string | undefined;
6238
6944
  try {
6239
6945
  currentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
6240
6946
  currentSessionId = ctx.sessionManager.getSessionId();
6947
+ // Launch admission's key (resolveCurrentSessionId) without its throw for an unbound session.
6948
+ budgetSessionId = currentSessionFile ?? currentSessionId;
6241
6949
  } catch (error) {
6242
6950
  sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
6243
6951
  }
6244
6952
  let orchestratorTarget: string | undefined;
6245
6953
  try {
6246
- orchestratorTarget = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
6954
+ orchestratorTarget = resolveIntercomSessionTarget(deps.childRuntime?.intercomSessionName ?? deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
6247
6955
  } catch (error) {
6248
6956
  if (!sessionError) sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
6249
6957
  }
6250
- const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config, currentSessionId);
6251
- const activeAsyncCapacity = currentSessionId
6252
- ? getActiveAsyncCapacitySnapshot(currentSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })
6958
+ const spawnBudget = getSpawnBudgetSnapshot(deps.state, deps.config, budgetSessionId);
6959
+ const activeAsyncCapacity = budgetSessionId
6960
+ ? getActiveAsyncCapacitySnapshot(budgetSessionId, resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession), { liveWorkflowRunIds: new Set(deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })
6253
6961
  : { used: 0, limit: resolveMaxActiveAsyncRunsPerSession(deps.config.maxActiveAsyncRunsPerSession) ?? 0 };
6254
6962
  deps.state.activeAsyncCapacity = activeAsyncCapacity;
6255
6963
  return {
@@ -6263,6 +6971,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6263
6971
  requestedSessionDir: paramsWithResolvedCwd.sessionDir,
6264
6972
  currentSessionFile,
6265
6973
  currentSessionId,
6974
+ spawnBudget,
6975
+ activeAsyncCapacity,
6266
6976
  orchestratorTarget,
6267
6977
  sessionError,
6268
6978
  expandTilde: deps.expandTilde,
@@ -6271,6 +6981,19 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6271
6981
  details: { mode: "management", results: [], spawnBudget, activeAsyncCapacity },
6272
6982
  };
6273
6983
  }
6984
+ if (["command.status", "command.yield", "command.cancel"].includes(action)) {
6985
+ try {
6986
+ if (deps.allowMutatingManagementActions === false && action !== "command.status") throw new Error(`Action '${action}' is not available from child-safe subagent fanout mode.`);
6987
+ const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
6988
+ if (!targetRunId || paramsWithResolvedCwd.dir) throw new Error(`${action} requires id or runId; directory targets are not supported.`);
6989
+ deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6990
+ const target = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
6991
+ if (!target) throw new Error(`No run found for '${targetRunId}'.`);
6992
+ return await commandAction({ state: deps.state, target, operation: action.slice("command.".length) as "status" | "yield" | "cancel", index: paramsWithResolvedCwd.index, toolCallId: paramsWithResolvedCwd.toolCallId, signal });
6993
+ } catch (error) {
6994
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
6995
+ }
6996
+ }
6274
6997
  if (action === "status" || action === "debug.run") {
6275
6998
  if (!preserveActiveSession) deps.state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
6276
6999
  const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
@@ -6319,7 +7042,18 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6319
7042
  return withBudget(inspectSubagentStatus(paramsWithResolvedCwd, omitUndefinedProperties({ state: deps.state, nested: nestedScope, sessionRoots, abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps.config.capacity?.abandonedSlotReleaseAfterMs) })));
6320
7043
  }
6321
7044
  if (action === "resume") {
6322
- return resumeAsyncRun(omitUndefinedProperties({ params: paramsWithResolvedCwd, requestCwd, ctx, deps, parentModel: requestParentModel, signal }));
7045
+ return resumeAsyncRun(omitUndefinedProperties({
7046
+ params: paramsWithResolvedCwd,
7047
+ requestCwd,
7048
+ ctx,
7049
+ deps,
7050
+ parentModel: requestParentModel,
7051
+ signal,
7052
+ ...(workflowLaunchObserver ? { onLaunch: (launch: Parameters<NonNullable<typeof workflowLaunchObserver>>[0]) => {
7053
+ workflowLaunchObservers.delete(params);
7054
+ workflowLaunchObserver(launch);
7055
+ } } : {}),
7056
+ }));
6323
7057
  }
6324
7058
  if (action === "steer") {
6325
7059
  if (paramsWithResolvedCwd.mode !== undefined && resolveSteerDeliveryMode(paramsWithResolvedCwd.mode) === undefined) {
@@ -6418,7 +7152,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6418
7152
  }
6419
7153
  if (action.startsWith("schedule.")) {
6420
7154
  if (!isScheduledRunAction(action)) {
6421
- return { content: [{ type: "text", text: unknownSubagentActionMessage(action) }], isError: true, details: { mode: "management", results: [] } };
7155
+ return { content: [{ type: "text", text: unknownSubagentActionMessage(action, disabledFeatures) }], isError: true, details: { mode: "management", results: [] } };
6422
7156
  }
6423
7157
  if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(action)) {
6424
7158
  return {
@@ -6460,47 +7194,40 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6460
7194
  }
6461
7195
  if (action === "stop") {
6462
7196
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
6463
- const workflowController = targetRunId ? deps.state.workflowControllers?.get(targetRunId) : undefined;
6464
- if (workflowController && targetRunId) {
6465
- const stopChild = deps.state.workflowChildStops?.get(targetRunId);
6466
- if (paramsWithResolvedCwd.childId !== undefined) {
6467
- const workflowRunId = targetRunId;
6468
- const asyncJob = deps.state.asyncJobs.get(workflowRunId);
6469
- if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6470
- const status = readStatus(asyncJob.asyncDir);
6471
- if (!status) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
6472
- const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
6473
- if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
6474
- if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${targetRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
6475
- if (!stopChild) return { content: [{ type: "text", text: `Workflow ${targetRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
6476
- if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
6477
- try {
6478
- fs.appendFileSync(path.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
6479
- type: "subagent.child-status",
6480
- version: 1,
6481
- runId: workflowRunId,
6482
- childId: resolution.child.id,
6483
- status: "stopping",
6484
- ts: Date.now(),
6485
- reason: "subagent-action",
6486
- source: "async",
6487
- stepIndex: resolution.child.index,
6488
- agent: resolution.child.step.agent,
6489
- ...(resolution.child.step.runId ? { childRunId: resolution.child.step.runId } : {}),
6490
- ...(resolution.child.step.workflowKey ? { workflowKey: resolution.child.step.workflowKey } : {}),
6491
- ...(resolution.child.step.phase ? { phase: resolution.child.step.phase } : {}),
6492
- ...(resolution.child.step.label ? { label: resolution.child.step.label } : {}),
6493
- } satisfies SubagentChildStatusEvent)}\n`, "utf-8");
6494
- } catch (error) {
6495
- console.error(`Failed to append child status event for workflow ${workflowRunId}:`, error);
6496
- }
6497
- return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
7197
+ // Whole-workflow stops reach stopAsyncRun below, which stops a live workflow in-process.
7198
+ const workflowRunId = targetRunId && paramsWithResolvedCwd.childId !== undefined ? resolveLiveWorkflowRunId(deps, targetRunId) : undefined;
7199
+ if (workflowRunId && paramsWithResolvedCwd.childId !== undefined) {
7200
+ const stopChild = deps.state.workflowChildStops?.get(workflowRunId);
7201
+ const asyncJob = deps.state.asyncJobs.get(workflowRunId);
7202
+ if (!asyncJob?.asyncDir) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
7203
+ const status = readStatus(asyncJob.asyncDir);
7204
+ if (!status) return { content: [{ type: "text", text: `Status file not found for async workflow '${workflowRunId}'.` }], isError: true, details: { mode: "management", results: [] } };
7205
+ const resolution = resolveAsyncStatusChild(status, paramsWithResolvedCwd.childId);
7206
+ if (!resolution.ok) return { content: [{ type: "text", text: resolution.message }], isError: true, details: { mode: "management", results: [] } };
7207
+ if (!isStoppableAsyncStatusStep(resolution.child.step)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in async run '${workflowRunId}' is ${resolution.child.step.status}; stop only supports pending or running children.` }], isError: true, details: { mode: "management", results: [] } };
7208
+ if (!stopChild) return { content: [{ type: "text", text: `Workflow ${workflowRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
7209
+ if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
7210
+ try {
7211
+ fs.appendFileSync(path.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
7212
+ type: "subagent.child-status",
7213
+ version: 1,
7214
+ runId: workflowRunId,
7215
+ childId: resolution.child.id,
7216
+ status: "stopping",
7217
+ ts: Date.now(),
7218
+ reason: "subagent-action",
7219
+ source: "async",
7220
+ stepIndex: resolution.child.index,
7221
+ agent: resolution.child.step.agent,
7222
+ ...(resolution.child.step.runId ? { childRunId: resolution.child.step.runId } : {}),
7223
+ ...(resolution.child.step.workflowKey ? { workflowKey: resolution.child.step.workflowKey } : {}),
7224
+ ...(resolution.child.step.phase ? { phase: resolution.child.step.phase } : {}),
7225
+ ...(resolution.child.step.label ? { label: resolution.child.step.label } : {}),
7226
+ } satisfies SubagentChildStatusEvent)}\n`, "utf-8");
7227
+ } catch (error) {
7228
+ console.error(`Failed to append child status event for workflow ${workflowRunId}:`, error);
6498
7229
  }
6499
- const asyncJob = deps.state.asyncJobs.get(targetRunId);
6500
- const status = asyncJob?.asyncDir ? readStatus(asyncJob.asyncDir) : undefined;
6501
- if (status) stopStoppableAsyncStatusChildren(status, stopChild, "Workflow stopped.");
6502
- workflowController.abort(new Error("Workflow stopped."));
6503
- return { content: [{ type: "text", text: `Stop requested for async workflow ${targetRunId}.` }], details: { mode: "management", results: [] } };
7230
+ return { content: [{ type: "text", text: `Stop requested for child ${resolution.child.id} in async workflow ${workflowRunId}.` }], details: { mode: "management", results: [] } };
6504
7231
  }
6505
7232
  let resolved: ResolvedSubagentRunId | undefined;
6506
7233
  if (paramsWithResolvedCwd.dir) {
@@ -6580,7 +7307,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6580
7307
  }
6581
7308
  if (!(SUBAGENT_ACTIONS as readonly string[]).includes(action)) {
6582
7309
  return {
6583
- content: [{ type: "text", text: unknownSubagentActionMessage(action) }],
7310
+ content: [{ type: "text", text: unknownSubagentActionMessage(action, disabledFeatures) }],
6584
7311
  isError: true,
6585
7312
  details: { mode: "management" as const, results: [] },
6586
7313
  };
@@ -6590,6 +7317,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6590
7317
  cwd: requestCwd,
6591
7318
  config: deps.config,
6592
7319
  currentSessionId: deps.state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined,
7320
+ discoverAgentsAll: deps.discoverAgentsAll,
6593
7321
  runtimeAgentOwner: deps.pi,
6594
7322
  onAgentsChanged: deps.onAgentsChanged,
6595
7323
  });
@@ -6615,6 +7343,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6615
7343
  const normalized = normalizeRepeatedParallelCounts(paramsWithResolvedCwd);
6616
7344
  if (normalized.error) return normalized.error;
6617
7345
  const normalizedParams = normalized.params!;
7346
+ const outputSchemaError = validateLaunchOutputSchemaOverrides(normalizedParams);
7347
+ if (outputSchemaError) return buildRequestedModeError(normalizedParams, outputSchemaError);
6618
7348
 
6619
7349
  let effectiveParams = applyForceTopLevelAsyncOverride(
6620
7350
  normalizedParams,
@@ -6672,6 +7402,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6672
7402
  }
6673
7403
  const modelScope = discovered.modelScope;
6674
7404
  effectiveParams = applySingleAgentLaunchDefaults(effectiveParams, discoveredAgents);
7405
+ // The gate shorthand, an explicit acceptance.verify list, and an agent's
7406
+ // defaultAcceptance all normalize to verify commands, and the agent's
7407
+ // frontmatter outputSchema has been merged by now, so this one check keeps
7408
+ // a run to a single structured-output source regardless of spelling.
7409
+ const typedVerifyConflict = describeTypedVerifyOutputSchemaConflict(effectiveParams, params);
7410
+ if (typedVerifyConflict) return buildRequestedModeError(effectiveParams, typedVerifyConflict);
6675
7411
  // An agent-level defaultContext is a preference, unlike an explicit request.
6676
7412
  // Prefer fork only when the parent session is persisted and has a current leaf;
6677
7413
  // otherwise use fresh immediately instead of launching a guaranteed-to-fail fork.
@@ -6685,7 +7421,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6685
7421
  if ("error" in contextPolicyResult) return buildRequestedModeError(effectiveParams, contextPolicyResult.error);
6686
7422
  const contextPolicy = contextPolicyResult;
6687
7423
  effectiveParams = contextPolicy.params;
6688
- const sessionName = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
7424
+ const sessionName = resolveIntercomSessionTarget(deps.childRuntime?.intercomSessionName ?? deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
6689
7425
  const intercomBridge = resolveIntercomBridge({
6690
7426
  config: deps.config.intercomBridge,
6691
7427
  override: effectiveParams.intercomBridge,
@@ -6695,7 +7431,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6695
7431
  orchestratorTarget: sessionName,
6696
7432
  });
6697
7433
  const agents = applyScopedIntercomBridgeToAgents(discoveredAgents, intercomBridge, contextPolicy);
6698
- const runId = randomUUID();
6699
7434
  const inheritedNestedRouteValue = inheritedNestedRoute(deps);
6700
7435
  const nestedParentAddress = inheritedNestedRouteValue ? inheritedNestedParentAddress(deps) : undefined;
6701
7436
  const shareEnabled = effectiveParams.share === true;
@@ -6730,65 +7465,40 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6730
7465
 
6731
7466
  let forkSessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
6732
7467
  let prepareForkSessionForIndex: (idx?: number) => Promise<void> = async () => {};
6733
- let forkThinkingOverrideForIndex: (idx?: number) => AgentConfig["thinking"] | undefined = () => undefined;
6734
- let prepareForkThinking = (_agentName: string, _index: number, _modelOverride?: string, _modelOverrideFromParent?: boolean, _modelOrigin?: ModelOrigin): void => {};
6735
- const forkThinkingRequirements = new Map<number, boolean>();
6736
- const forkThinkingDowngrades = new Map<number, string>();
7468
+ // Check before pruner/session preparation, then recheck immediately before each fork
7469
+ // because the session capability ceiling can tighten while preparation awaits.
7470
+ const launchCapabilityCeiling = () => intersectSubagentCapabilityCeilings(
7471
+ intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestSessionId)),
7472
+ deps.childRuntime?.capabilityCeiling,
7473
+ );
7474
+ const assertLaunchableAgentsAllowedBeforeFork = (): void => {
7475
+ if (!contextPolicy.usesFork) return;
7476
+ const ceiling = launchCapabilityCeiling();
7477
+ const launchable = hasSingle
7478
+ ? [effectiveParams.agent!]
7479
+ : hasTasks
7480
+ ? (effectiveParams.tasks ?? []).map((task) => task.agent)
7481
+ : (effectiveParams.chain ?? []).flatMap((step) => isDynamicParallelStep(step)
7482
+ && (step.expand.maxItems ?? deps.config.chain?.dynamicFanout?.maxItems ?? 0) === 0
7483
+ ? []
7484
+ : getStepAgents(step));
7485
+ for (const agent of launchable) assertAgentAllowedByCapabilityCeiling(agent, ceiling);
7486
+ };
7487
+ // Forked children keep their requested thinking level. Signed Anthropic thinking
7488
+ // blocks are stripped from the inherited transcript by the resolver (they are bound
7489
+ // to the parent session), which is not a reason to disable the child's own reasoning.
6737
7490
  try {
6738
- const forkAvailableModels = contextPolicy.usesFork ? ctx.modelRegistry.getAvailable().map(toModelInfo) : [];
6739
- const parentModel = requestParentModel;
6740
- prepareForkThinking = (agentName, index, modelOverride, modelOverrideFromParent, storedOrigin) => {
6741
- const agentConfig = agents.find((agent) => agent.name === agentName);
6742
- if (agentConfig?.runner?.type === "external-cli" || agentConfig?.runner?.type === "external-job") {
6743
- forkThinkingRequirements.set(index, true);
6744
- return;
6745
- }
6746
- const effectiveStoredOrigin = storedOrigin === "configured" && modelOverride === undefined && agentConfig?.model === undefined
6747
- ? undefined
6748
- : storedOrigin;
6749
- const origin = resolveModelOrigin({
6750
- fromParent: modelOverrideFromParent,
6751
- storedOrigin: effectiveStoredOrigin,
6752
- explicitModel: modelOverrideFromParent || effectiveStoredOrigin === "configured" ? undefined : modelOverride,
6753
- agentModel: agentConfig?.model,
6754
- parentModel,
6755
- });
6756
- const primaryModel = modelOverrideFromParent
6757
- ? modelOverride
6758
- : resolveEffectiveSubagentModel(
6759
- effectiveStoredOrigin === "configured" ? undefined : modelOverride,
6760
- agentConfig?.model,
6761
- parentModel,
6762
- forkAvailableModels,
6763
- agentConfig?.modelProvider ?? parentModel?.provider,
6764
- { source: origin === "explicit" ? "explicit" : "inherited" },
6765
- );
6766
- const candidates = buildModelCandidates(
6767
- primaryModel,
6768
- agentConfig?.fallbackModels,
6769
- forkAvailableModels,
6770
- agentConfig?.modelProvider ?? parentModel?.provider,
6771
- {
6772
- primaryModelFromParent: origin === "inherited",
6773
- origin,
6774
- },
6775
- );
6776
- forkThinkingRequirements.set(
6777
- index,
6778
- candidates.length === 0
6779
- || candidates.some((candidate) => forkedChildRequiresThinkingOff(candidate, forkAvailableModels, parentModel?.provider)),
6780
- );
6781
- };
7491
+ assertLaunchableAgentsAllowedBeforeFork();
6782
7492
  const pruneSession = contextPolicy.usesFork && deps.config.forkContext?.mode === "pruned"
6783
7493
  ? await createPrunedForkSessionWriter(ctx, deps.config.forkContext, signal)
6784
7494
  : undefined;
6785
- const forkContextResolver = createForkContextResolver(ctx.sessionManager, contextPolicy.usesFork ? "fork" : undefined, {
6786
- forceThinkingOffForIndex: (index) => forkThinkingRequirements.get(index) ?? true,
6787
- ...(pruneSession ? { pruneSession } : {}),
6788
- });
7495
+ const forkContextResolver = createForkContextResolver(
7496
+ ctx.sessionManager,
7497
+ contextPolicy.usesFork ? "fork" : undefined,
7498
+ pruneSession ? { pruneSession } : {},
7499
+ );
6789
7500
  prepareForkSessionForIndex = forkContextResolver.prepareSessionForIndex;
6790
7501
  forkSessionFileForIndex = forkContextResolver.sessionFileForIndex;
6791
- forkThinkingOverrideForIndex = forkContextResolver.thinkingOverrideForIndex;
6792
7502
  } catch (error) {
6793
7503
  return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
6794
7504
  }
@@ -6807,6 +7517,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6807
7517
  if (externalAgent && (!effectiveAsync || effectiveParams.foregroundOnly === true)) {
6808
7518
  return buildRequestedModeError(effectiveParams, `Agent '${externalAgent.name}' uses runner.type='${externalAgent.runner?.type}', which currently supports async/background execution only. Omit async or pass async:true; clarify and foregroundOnly are unsupported.`);
6809
7519
  }
7520
+ if (effectiveAsync && hasSingle && effectiveParams.resume === undefined && effectiveParams.worktree === true) {
7521
+ try {
7522
+ await preflightWorktreeSource(effectiveCwd, { signal });
7523
+ } catch (error) {
7524
+ return toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
7525
+ }
7526
+ }
7527
+ const runId = randomUUID();
6810
7528
  const foregroundTimeout = resolveSingleAgentLaunchTimeout(
6811
7529
  effectiveParams,
6812
7530
  effectiveAsync,
@@ -6891,30 +7609,29 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6891
7609
  }
6892
7610
  const sessionDirForIndex = (idx?: number) =>
6893
7611
  path.join(sessionRoot, `run-${idx ?? 0}`);
6894
- const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
7612
+ const forkSessionFileForTask: ForkSessionFileForTask = (agentName, idx = 0) => {
6895
7613
  if (!shouldForkAgent(contextPolicy, agentName)) return undefined;
6896
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6897
7614
  return forkSessionFileForIndex(idx);
6898
7615
  };
6899
- const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
7616
+ const prepareForkSessionForTask: PrepareForkSessionForTask = async (agentName, idx = 0) => {
6900
7617
  if (!shouldForkAgent(contextPolicy, agentName)) return;
6901
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6902
7618
  await prepareForkSessionForIndex(idx);
6903
7619
  };
6904
- const forkThinkingOverrideForTask: ForkThinkingOverrideForTask = (agentName, idx = 0, modelOverride, modelOverrideFromParent, modelOrigin) => {
6905
- if (!shouldForkAgent(contextPolicy, agentName)) return delegatedThinkingOverride;
6906
- prepareForkThinking(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin);
6907
- const override = forkThinkingOverrideForIndex(idx);
6908
- if (override === "off") forkThinkingDowngrades.set(idx, agentName);
6909
- return override ?? delegatedThinkingOverride;
6910
- };
7620
+ const thinkingOverrideForTask: ThinkingOverrideForTask = () => delegatedThinkingOverride;
6911
7621
  const childSessionFileForTask: ForkSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) =>
6912
7622
  forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
6913
7623
  const childSessionFileForIndex = (idx?: number) =>
6914
7624
  path.join(sessionDirForIndex(idx), "session.jsonl");
6915
7625
  try {
7626
+ // The ceiling can change while the pruned writer is built; clarify skips preflight but
7627
+ // still branches the parent session at launch.
7628
+ assertLaunchableAgentsAllowedBeforeFork();
6916
7629
  if (!(effectiveParams.clarify === true && ctx.hasUI) || deps.config.forkContext?.mode === "pruned") {
6917
- await preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, prepareForkSessionForTask, deps.config.chain?.dynamicFanout?.maxItems);
7630
+ const prepareAllowedForkSession: PrepareForkSessionForTask = async (agent, ...rest) => {
7631
+ assertAgentAllowedByCapabilityCeiling(agent, launchCapabilityCeiling());
7632
+ await prepareForkSessionForTask(agent, ...rest);
7633
+ };
7634
+ await preflightForkSessionsForStaticTasks(effectiveParams, contextPolicy, prepareAllowedForkSession, deps.config.chain?.dynamicFanout?.maxItems);
6918
7635
  }
6919
7636
  } catch (error) {
6920
7637
  activeAsyncCapacity?.rollback();
@@ -6996,7 +7713,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
6996
7713
  sessionDirForIndex,
6997
7714
  sessionFileForIndex: childSessionFileForIndex,
6998
7715
  sessionFileForTask: childSessionFileForTask,
6999
- thinkingOverrideForTask: forkThinkingOverrideForTask,
7716
+ thinkingOverrideForTask,
7000
7717
  artifactConfig,
7001
7718
  artifactsDir,
7002
7719
  backgroundRequestedWhileClarifying,
@@ -7010,12 +7727,14 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7010
7727
  toolBudget: runToolBudget.toolBudget,
7011
7728
  usageBudget: usageBudget.budget,
7012
7729
  inheritedUsageBudget,
7730
+ requiredExtensions: retainedRequiredExtensions,
7013
7731
  allowZeroToolBudget,
7014
7732
  configToolBudget: configToolBudget.toolBudget,
7015
7733
  configToolTimeoutMs: deps.config.toolTimeoutMs,
7016
7734
  contextPolicy,
7017
7735
  modelScope,
7018
7736
  parentModel: requestParentModel,
7737
+ scopedModelIds: scopedModelIdsFromContext(ctx),
7019
7738
  parentSessionId: requestSessionId,
7020
7739
  parentPiSessionId: requestPiSessionId,
7021
7740
  capabilityCeiling: intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestSessionId)),
@@ -7107,10 +7826,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7107
7826
  params: effectiveParams,
7108
7827
  agents,
7109
7828
  parentModel: requestParentModel,
7829
+ scopedModelIds: scopedModelIdsFromContext(ctx),
7110
7830
  availableModels: ctx.modelRegistry.getAvailable().map(toModelInfo),
7111
7831
  currentProvider: requestParentModel?.provider,
7112
7832
  modelScope,
7113
- thinkingOverrideForTask: forkThinkingOverrideForTask,
7833
+ thinkingOverrideForTask,
7114
7834
  dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
7115
7835
  });
7116
7836
  } catch (error) {
@@ -7201,7 +7921,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7201
7921
  const asyncResult = await runAsyncPath(execData, deps);
7202
7922
  if (asyncResult) {
7203
7923
  asyncLaunchFailed = asyncResult.isError === true;
7204
- return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(asyncResult, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget));
7924
+ return attachMission(withRunFanoutBudget(withResolvedContext(asyncResult, contextPolicy.contextSummary), runFanoutBudget));
7205
7925
  }
7206
7926
  if (foregroundControl) {
7207
7927
  writeNestedForegroundEvent("subagent.nested.started");
@@ -7211,11 +7931,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7211
7931
  if (hasSingle) {
7212
7932
  const result = await runSinglePath(execData, deps);
7213
7933
  writeNestedForegroundEvent("subagent.nested.completed", result);
7214
- return attachMission(withRunFanoutBudget(withResolvedContext(withForkThinkingNotes(result, forkThinkingDowngrades), contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
7934
+ return attachMission(withRunFanoutBudget(withResolvedContext(result, contextPolicy.contextSummary), runFanoutBudget, { annotateContent: runFanoutAnnotateContent }));
7215
7935
  }
7216
7936
  } catch (error) {
7217
7937
  asyncLaunchFailed = effectiveAsync;
7218
- const errorResult = withForkThinkingNotes(toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary), forkThinkingDowngrades);
7938
+ const errorResult = toExecutionErrorResult(effectiveParams, error, contextPolicy.contextSummary);
7219
7939
  if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
7220
7940
  return attachMission(errorResult);
7221
7941
  } finally {
@@ -7264,24 +7984,47 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7264
7984
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
7265
7985
  ctx: ExtensionContext,
7266
7986
  ): Promise<AgentToolResult<Details>> => {
7267
- const normalized = normalizePublicSubagentExecution(params);
7987
+ const disabledFeatureError = disabledFeatureResult(params);
7988
+ if (disabledFeatureError) return Promise.resolve(disabledFeatureError);
7989
+ const normalized = normalizePublicSubagentExecution(params, { structuredWorkflows: disabledFeatures.features.has("workflow-scripts") });
7268
7990
  if (!normalized.ok) {
7269
7991
  return Promise.resolve({ content: [{ type: "text", text: normalized.error }], isError: true, details: { mode: normalized.mode, results: [] } });
7270
7992
  }
7271
7993
  let publicParams = normalized.params as SubagentParamsLike;
7272
- if (publicParams.workflow !== undefined) {
7273
- const resolved = resolveWorkflowResource(publicParams.workflow, publicParams.args, ctx.sessionManager.getSessionId() ?? undefined);
7274
- if (!resolved.ok) return Promise.resolve({ content: [{ type: "text", text: resolved.error }], isError: true, details: { mode: "workflow", results: [] } });
7994
+ const workflow = publicParams.workflow;
7995
+ const errorResult = (text: string): Promise<AgentToolResult<Details>> => Promise.resolve({ content: [{ type: "text", text }], isError: true, details: { mode: publicParams.action ? "management" : "workflow", results: [] } });
7996
+ // Models tend to put script text in the workflow string; say how to pass it instead.
7997
+ const scriptTextHint = " A workflow string is a named workflow resource or a script file path. To run script text, write it in one ```js workflow block in the same reply and call subagent({ workflow: true }).";
7998
+ if (publicParams.tasks !== undefined || publicParams.chain !== undefined) {
7999
+ // Normalization admits chain/tasks only with workflow scripts disabled; the original input was checked above.
8000
+ const kind = publicParams.tasks !== undefined ? "tasks" : "chain";
8001
+ const resolved = resolveStructuredWorkflowResource({ kind, steps: publicParams[kind], task: publicParams.task });
8002
+ if (!resolved.ok) return errorResult(resolved.error);
8003
+ const { tasks: _tasks, chain: _chain, task: _task, ...withoutStructuredInput } = publicParams;
8004
+ publicParams = { ...withoutStructuredInput, workflowScript: resolved.resource.script };
8005
+ workflowResourcePermits.set(publicParams, resolved.resource.permit);
8006
+ } else if (typeof workflow === "string" && !isWorkflowScriptPath(workflow)) {
8007
+ const resolved = resolveWorkflowResource(workflow, publicParams.args, ctx.sessionManager.getSessionId() ?? undefined);
8008
+ if (!resolved.ok) return Promise.resolve({ content: [{ type: "text", text: resolved.error + scriptTextHint }], isError: true, details: { mode: "workflow", results: [] } });
7275
8009
  const { workflow: _workflow, args: _args, ...withoutResourceInput } = publicParams;
7276
8010
  publicParams = { ...withoutResourceInput, workflowScript: resolved.resource.script };
7277
8011
  workflowResourcePermits.set(publicParams, resolved.resource.permit);
8012
+ } else if (workflow !== undefined || publicParams.workflowScript !== undefined) {
8013
+ const normalizedArgs = normalizeWorkflowArgs(publicParams.args);
8014
+ // validate reports args errors with its script errors; launch rejects them here.
8015
+ const validateOnly = publicParams.action === "validate";
8016
+ if ("error" in normalizedArgs && !validateOnly) return errorResult(normalizedArgs.error);
8017
+ let workflowScript = publicParams.workflowScript;
8018
+ if (workflow !== undefined) {
8019
+ const source = workflow === true ? readReplyWorkflowScript(ctx.sessionManager, id) : readWorkflowScriptFile(workflow, publicParams.cwd, ctx.cwd);
8020
+ if ("error" in source) return errorResult(workflow === true ? source.error : source.error + scriptTextHint);
8021
+ workflowScript = source.script;
8022
+ }
8023
+ const { workflow: _workflow, ...withoutWorkflowSource } = publicParams;
8024
+ publicParams = { ...withoutWorkflowSource, workflowScript, args: "error" in normalizedArgs ? publicParams.args : deepFreezeWorkflowArgs(normalizedArgs.args) };
7278
8025
  }
7279
- const loaded = loadWorkflowScriptPath(publicParams, ctx.cwd);
7280
- if (loaded.error) {
7281
- return Promise.resolve({ content: [{ type: "text", text: loaded.error }], isError: true, details: { mode: publicParams.action ? "management" : "workflow", results: [] } });
7282
- }
7283
- publicExecutions.add(loaded.params!);
7284
- return executeWithSingleDispatchGuard(id, loaded.params!, signal, onUpdate, ctx);
8026
+ publicExecutions.add(publicParams);
8027
+ return executeWithSingleDispatchGuard(id, publicParams, signal, onUpdate, ctx);
7285
8028
  };
7286
8029
 
7287
8030
  const executeDelegated = async (
@@ -7291,6 +8034,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
7291
8034
  onUpdate: ((r: AgentToolResult<Details>) => void) | undefined,
7292
8035
  ctx: ExtensionContext,
7293
8036
  ): Promise<AgentToolResult<Details>> => {
8037
+ const disabledFeatureError = disabledFeatureResult(params);
8038
+ if (disabledFeatureError) return disabledFeatureError;
7294
8039
  const delegatedParams = { ...params };
7295
8040
  const privateParams = delegatedParams as SubagentParamsLike & {
7296
8041
  delegatedThinkingOverride?: AgentConfig["thinking"];