@bastani/atomic 0.8.13-0 → 0.8.14-0

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 (355) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/host-html-template.ts +1 -1
  4. package/dist/builtin/mcp/init.ts +15 -2
  5. package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
  6. package/dist/builtin/mcp/package.json +1 -1
  7. package/dist/builtin/mcp/ui-session.ts +9 -6
  8. package/dist/builtin/subagents/CHANGELOG.md +8 -1
  9. package/dist/builtin/subagents/README.md +39 -32
  10. package/dist/builtin/subagents/package.json +1 -1
  11. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  12. package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
  13. package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
  14. package/dist/builtin/subagents/src/agents/agents.ts +44 -19
  15. package/dist/builtin/subagents/src/extension/config.ts +16 -0
  16. package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
  17. package/dist/builtin/subagents/src/extension/index.ts +466 -603
  18. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
  19. package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
  21. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
  22. package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
  24. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
  25. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
  26. package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
  27. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
  28. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
  30. package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
  31. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
  32. package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
  33. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
  34. package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
  35. package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
  36. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
  37. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
  38. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
  39. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
  40. package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
  41. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
  42. package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
  43. package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
  44. package/dist/builtin/subagents/src/shared/types.ts +96 -1
  45. package/dist/builtin/subagents/src/shared/utils.ts +10 -2
  46. package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
  47. package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
  48. package/dist/builtin/web-access/package.json +1 -1
  49. package/dist/builtin/workflows/CHANGELOG.md +24 -0
  50. package/dist/builtin/workflows/README.md +28 -11
  51. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
  52. package/dist/builtin/workflows/builtin/ralph.ts +362 -176
  53. package/dist/builtin/workflows/package.json +2 -5
  54. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  55. package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
  56. package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
  57. package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
  58. package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
  59. package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
  60. package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
  61. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  62. package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  63. package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
  64. package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
  65. package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  66. package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
  67. package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
  68. package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
  69. package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
  70. package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
  71. package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
  72. package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
  73. package/dist/builtin/workflows/src/extension/index.ts +869 -93
  74. package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
  75. package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
  76. package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
  77. package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
  78. package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
  79. package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
  80. package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
  81. package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
  82. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
  83. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
  84. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
  85. package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
  86. package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
  87. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
  88. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
  89. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
  90. package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
  91. package/dist/builtin/workflows/src/shared/store.ts +145 -17
  92. package/dist/builtin/workflows/src/shared/timing.ts +6 -2
  93. package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
  94. package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
  95. package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
  96. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
  97. package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
  98. package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
  99. package/dist/builtin/workflows/src/tui/header.ts +38 -0
  100. package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
  101. package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
  102. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
  103. package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
  104. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
  105. package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
  106. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
  107. package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
  108. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
  109. package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
  110. package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
  111. package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
  112. package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
  113. package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
  114. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
  115. package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
  116. package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
  117. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
  118. package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
  119. package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
  120. package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
  121. package/dist/builtin/workflows/src/tui/widget.ts +90 -68
  122. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
  123. package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
  124. package/dist/cli/file-processor.d.ts.map +1 -1
  125. package/dist/cli/file-processor.js +2 -3
  126. package/dist/cli/file-processor.js.map +1 -1
  127. package/dist/config.d.ts.map +1 -1
  128. package/dist/config.js +3 -10
  129. package/dist/config.js.map +1 -1
  130. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  131. package/dist/core/agent-session-runtime.js +2 -1
  132. package/dist/core/agent-session-runtime.js.map +1 -1
  133. package/dist/core/agent-session-services.d.ts.map +1 -1
  134. package/dist/core/agent-session-services.js +3 -2
  135. package/dist/core/agent-session-services.js.map +1 -1
  136. package/dist/core/agent-session.d.ts +6 -0
  137. package/dist/core/agent-session.d.ts.map +1 -1
  138. package/dist/core/agent-session.js +16 -2
  139. package/dist/core/agent-session.js.map +1 -1
  140. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  141. package/dist/core/atomic-guide-command.js +8 -9
  142. package/dist/core/atomic-guide-command.js.map +1 -1
  143. package/dist/core/auth-storage.d.ts.map +1 -1
  144. package/dist/core/auth-storage.js +3 -2
  145. package/dist/core/auth-storage.js.map +1 -1
  146. package/dist/core/bash-executor.d.ts.map +1 -1
  147. package/dist/core/bash-executor.js +2 -1
  148. package/dist/core/bash-executor.js.map +1 -1
  149. package/dist/core/export-html/index.d.ts.map +1 -1
  150. package/dist/core/export-html/index.js +8 -6
  151. package/dist/core/export-html/index.js.map +1 -1
  152. package/dist/core/export-html/template.js +6 -3
  153. package/dist/core/extensions/loader.d.ts.map +1 -1
  154. package/dist/core/extensions/loader.js +12 -29
  155. package/dist/core/extensions/loader.js.map +1 -1
  156. package/dist/core/model-registry.d.ts.map +1 -1
  157. package/dist/core/model-registry.js +5 -1
  158. package/dist/core/model-registry.js.map +1 -1
  159. package/dist/core/package-manager.d.ts +8 -0
  160. package/dist/core/package-manager.d.ts.map +1 -1
  161. package/dist/core/package-manager.js +145 -58
  162. package/dist/core/package-manager.js.map +1 -1
  163. package/dist/core/prompt-templates.d.ts.map +1 -1
  164. package/dist/core/prompt-templates.js +6 -20
  165. package/dist/core/prompt-templates.js.map +1 -1
  166. package/dist/core/resource-loader.d.ts.map +1 -1
  167. package/dist/core/resource-loader.js +38 -31
  168. package/dist/core/resource-loader.js.map +1 -1
  169. package/dist/core/sdk.d.ts.map +1 -1
  170. package/dist/core/sdk.js +9 -4
  171. package/dist/core/sdk.js.map +1 -1
  172. package/dist/core/session-manager.d.ts.map +1 -1
  173. package/dist/core/session-manager.js +32 -24
  174. package/dist/core/session-manager.js.map +1 -1
  175. package/dist/core/settings-manager.d.ts.map +1 -1
  176. package/dist/core/settings-manager.js +8 -15
  177. package/dist/core/settings-manager.js.map +1 -1
  178. package/dist/core/skills.d.ts.map +1 -1
  179. package/dist/core/skills.js +8 -22
  180. package/dist/core/skills.js.map +1 -1
  181. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
  182. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  183. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
  184. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  185. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
  186. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  187. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
  188. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  189. package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
  190. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
  191. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
  192. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  193. package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
  194. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  195. package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
  196. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  197. package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
  198. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
  199. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  200. package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
  201. package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
  202. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
  203. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  204. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
  205. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  206. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  207. package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
  208. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  209. package/dist/core/tools/bash.d.ts.map +1 -1
  210. package/dist/core/tools/bash.js +56 -53
  211. package/dist/core/tools/bash.js.map +1 -1
  212. package/dist/core/tools/edit-diff.d.ts +3 -1
  213. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  214. package/dist/core/tools/edit-diff.js +8 -1
  215. package/dist/core/tools/edit-diff.js.map +1 -1
  216. package/dist/core/tools/edit.d.ts +3 -1
  217. package/dist/core/tools/edit.d.ts.map +1 -1
  218. package/dist/core/tools/edit.js +44 -81
  219. package/dist/core/tools/edit.js.map +1 -1
  220. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  221. package/dist/core/tools/file-mutation-queue.js +27 -12
  222. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  223. package/dist/core/tools/find.d.ts.map +1 -1
  224. package/dist/core/tools/find.js +2 -3
  225. package/dist/core/tools/find.js.map +1 -1
  226. package/dist/core/tools/grep.d.ts.map +1 -1
  227. package/dist/core/tools/grep.js +3 -3
  228. package/dist/core/tools/grep.js.map +1 -1
  229. package/dist/core/tools/ls.d.ts.map +1 -1
  230. package/dist/core/tools/ls.js +5 -5
  231. package/dist/core/tools/ls.js.map +1 -1
  232. package/dist/core/tools/output-accumulator.d.ts +2 -0
  233. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  234. package/dist/core/tools/output-accumulator.js +11 -4
  235. package/dist/core/tools/output-accumulator.js.map +1 -1
  236. package/dist/core/tools/path-utils.d.ts +2 -0
  237. package/dist/core/tools/path-utils.d.ts.map +1 -1
  238. package/dist/core/tools/path-utils.js +39 -21
  239. package/dist/core/tools/path-utils.js.map +1 -1
  240. package/dist/core/tools/read.d.ts.map +1 -1
  241. package/dist/core/tools/read.js +9 -8
  242. package/dist/core/tools/read.js.map +1 -1
  243. package/dist/core/tools/truncate.d.ts.map +1 -1
  244. package/dist/core/tools/truncate.js +12 -2
  245. package/dist/core/tools/truncate.js.map +1 -1
  246. package/dist/core/tools/write.d.ts.map +1 -1
  247. package/dist/core/tools/write.js +20 -35
  248. package/dist/core/tools/write.js.map +1 -1
  249. package/dist/index.d.ts +2 -1
  250. package/dist/index.d.ts.map +1 -1
  251. package/dist/index.js +4 -1
  252. package/dist/index.js.map +1 -1
  253. package/dist/main.d.ts.map +1 -1
  254. package/dist/main.js +5 -6
  255. package/dist/main.js.map +1 -1
  256. package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
  257. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
  258. package/dist/modes/interactive/chat-input-actions.js +179 -0
  259. package/dist/modes/interactive/chat-input-actions.js.map +1 -0
  260. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  261. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
  263. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  264. package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
  265. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
  266. package/dist/modes/interactive/components/chat-session-host.js +1007 -0
  267. package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
  268. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/config-selector.js +1 -1
  270. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  271. package/dist/modes/interactive/components/footer.d.ts +1 -0
  272. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/footer.js +14 -5
  274. package/dist/modes/interactive/components/footer.js.map +1 -1
  275. package/dist/modes/interactive/components/index.d.ts +1 -0
  276. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/index.js +1 -0
  278. package/dist/modes/interactive/components/index.js.map +1 -1
  279. package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
  280. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/login-dialog.js +29 -4
  282. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  283. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  284. package/dist/modes/interactive/interactive-mode.js +18 -67
  285. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  286. package/dist/utils/child-process.d.ts +1 -0
  287. package/dist/utils/child-process.d.ts.map +1 -1
  288. package/dist/utils/child-process.js +8 -0
  289. package/dist/utils/child-process.js.map +1 -1
  290. package/dist/utils/clipboard-native.d.ts +3 -1
  291. package/dist/utils/clipboard-native.d.ts.map +1 -1
  292. package/dist/utils/clipboard-native.js +14 -8
  293. package/dist/utils/clipboard-native.js.map +1 -1
  294. package/dist/utils/image-resize-core.d.ts +30 -0
  295. package/dist/utils/image-resize-core.d.ts.map +1 -0
  296. package/dist/utils/image-resize-core.js +124 -0
  297. package/dist/utils/image-resize-core.js.map +1 -0
  298. package/dist/utils/image-resize-worker.d.ts +2 -0
  299. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  300. package/dist/utils/image-resize-worker.js +31 -0
  301. package/dist/utils/image-resize-worker.js.map +1 -0
  302. package/dist/utils/image-resize.d.ts +7 -27
  303. package/dist/utils/image-resize.d.ts.map +1 -1
  304. package/dist/utils/image-resize.js +75 -115
  305. package/dist/utils/image-resize.js.map +1 -1
  306. package/dist/utils/paths.d.ts +16 -1
  307. package/dist/utils/paths.d.ts.map +1 -1
  308. package/dist/utils/paths.js +49 -7
  309. package/dist/utils/paths.js.map +1 -1
  310. package/docs/changelog.mdx +29 -0
  311. package/docs/compaction.md +1 -1
  312. package/docs/custom-provider.md +2 -2
  313. package/docs/development.md +1 -1
  314. package/docs/docs.json +98 -143
  315. package/docs/extensions.md +29 -16
  316. package/docs/favicon.svg +29 -0
  317. package/docs/images/interactive-mode.png +0 -0
  318. package/docs/images/tree-view.png +0 -0
  319. package/docs/images/workflow-command.png +0 -0
  320. package/docs/images/workflow-graph.png +0 -0
  321. package/docs/images/workflow-input-picker.png +0 -0
  322. package/docs/images/workflow-list.png +0 -0
  323. package/docs/index.md +10 -1
  324. package/docs/logo.svg +59 -0
  325. package/docs/packages.md +3 -3
  326. package/docs/providers.md +1 -1
  327. package/docs/quickstart.md +98 -2
  328. package/docs/rpc.md +8 -8
  329. package/docs/sdk.md +23 -12
  330. package/docs/sessions.md +1 -1
  331. package/docs/skills.md +15 -1
  332. package/docs/termux.md +11 -1
  333. package/docs/themes.md +6 -6
  334. package/docs/tui.md +18 -18
  335. package/docs/usage.md +1 -1
  336. package/docs/workflows.md +172 -2
  337. package/examples/extensions/subagent/index.ts +2 -1
  338. package/package.json +6 -6
  339. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
  340. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
  341. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
  342. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
  343. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
  344. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
  345. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
  346. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
  347. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
  348. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
  349. /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
  350. /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
  351. /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
  352. /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
  353. /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
  354. /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
  355. /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  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
- import type { ExtensionAPI, ExtensionContext } from "@bastani/atomic";
5
+ import { APP_NAME, getEnvValue, type ExtensionAPI, type ExtensionContext } from "@bastani/atomic";
6
6
  import { type AgentConfig, type AgentScope } from "../../agents/agents.ts";
7
7
  import { getArtifactsDir } from "../../shared/artifacts.ts";
8
8
  import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
@@ -14,6 +14,7 @@ import { buildDoctorReport } from "../../extension/doctor.ts";
14
14
  import { clearPendingForegroundControlNotices } from "../../extension/control-notices.ts";
15
15
  import { runSync } from "./execution.ts";
16
16
  import { currentModelFullId, resolveModelCandidate } from "../shared/model-fallback.ts";
17
+ import { SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "../shared/pi-args.ts";
17
18
  import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
18
19
  import { recordRun } from "../shared/run-history.ts";
19
20
  import {
@@ -35,9 +36,10 @@ import { createForkContextResolver } from "../../shared/fork-context.ts";
35
36
  import { resolveCurrentSessionId } from "../../shared/session-identity.ts";
36
37
  import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBridge, resolveIntercomSessionTarget, resolveSubagentIntercomTarget, type IntercomBridgeState } from "../../intercom/intercom-bridge.ts";
37
38
  import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "../shared/subagent-control.ts";
38
- import { finalizeSingleOutput, injectSingleOutputInstruction, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
39
+ import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
39
40
  import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd } from "../../shared/utils.ts";
40
41
  import {
42
+ attachNestedChildrenToResultChildren,
41
43
  buildSubagentResultIntercomPayload,
42
44
  deliverSubagentIntercomMessageEvent,
43
45
  deliverSubagentResultIntercomEvent,
@@ -46,6 +48,9 @@ import {
46
48
  stripDetailsOutputsForIntercomReceipt,
47
49
  } from "../../intercom/result-intercom.ts";
48
50
  import { buildRevivedAsyncTask, resolveAsyncResumeTarget } from "../background/async-resume.ts";
51
+ import { createNestedRoute, readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
52
+ import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
53
+ import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
49
54
  import { inspectSubagentStatus } from "../background/run-status.ts";
50
55
  import { applyForceTopLevelAsyncOverride } from "../background/top-level-async.ts";
51
56
  import {
@@ -67,6 +72,8 @@ import {
67
72
  type ExtensionConfig,
68
73
  type IntercomEventBus,
69
74
  type MaxOutputConfig,
75
+ type NestedRouteInfo,
76
+ type NestedRunSummary,
70
77
  type ResolvedControlConfig,
71
78
  type SingleResult,
72
79
  type SubagentRunMode,
@@ -84,6 +91,7 @@ import {
84
91
  } from "../../shared/types.ts";
85
92
 
86
93
  const ASYNC_INTERRUPT_SIGNAL: NodeJS.Signals = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
94
+ const MUTATING_MANAGEMENT_ACTIONS = new Set(["create", "update", "delete"]);
87
95
 
88
96
  interface TaskParam {
89
97
  agent: string;
@@ -138,6 +146,7 @@ interface ExecutorDeps {
138
146
  getSubagentSessionRoot: (parentSessionFile: string | null) => string;
139
147
  expandTilde: (p: string) => string;
140
148
  discoverAgents: (cwd: string, scope: AgentScope) => { agents: AgentConfig[] };
149
+ allowMutatingManagementActions?: boolean;
141
150
  }
142
151
 
143
152
  interface ExecutionContextData {
@@ -158,6 +167,7 @@ interface ExecutionContextData {
158
167
  effectiveAsync: boolean;
159
168
  controlConfig: ResolvedControlConfig;
160
169
  intercomBridge: IntercomBridgeState;
170
+ nestedRoute?: NestedRouteInfo;
161
171
  }
162
172
 
163
173
  function resolveRequestedCwd(runtimeCwd: string, requestedCwd: string | undefined): string {
@@ -196,7 +206,23 @@ function formatForegroundActivity(control: SubagentState["foregroundControls"] e
196
206
  return [`active ${seconds}s ago`, ...facts].join(" | ");
197
207
  }
198
208
 
209
+ function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResolutionScope | undefined {
210
+ if (deps.allowMutatingManagementActions !== false) return undefined;
211
+ const route = resolveInheritedNestedRouteFromEnv();
212
+ const address = route ? resolveNestedParentAddressFromEnv() : undefined;
213
+ return {
214
+ routes: route ? [route] : [],
215
+ ...(address ? { descendantOf: { parentRunId: address.parentRunId, ...(address.parentStepIndex !== undefined ? { parentStepIndex: address.parentStepIndex } : {}) } } : {}),
216
+ };
217
+ }
218
+
199
219
  function foregroundStatusResult(control: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never): AgentToolResult<Details> {
220
+ let nestedWarning: string | undefined;
221
+ try {
222
+ updateForegroundNestedProjection(control);
223
+ } catch (error) {
224
+ nestedWarning = `Nested status unavailable: ${error instanceof Error ? error.message : String(error)}`;
225
+ }
200
226
  const activity = formatForegroundActivity(control);
201
227
  const lines = [
202
228
  `Run: ${control.runId}`,
@@ -205,6 +231,8 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
205
231
  control.currentAgent ? `Current: ${control.currentAgent}${control.currentIndex !== undefined ? ` step ${control.currentIndex + 1}` : ""}` : undefined,
206
232
  activity ? `Activity: ${activity}` : undefined,
207
233
  ].filter((line): line is string => Boolean(line));
234
+ lines.push(...formatNestedRunStatusLines(control.nestedChildren, { indent: "", commandHints: true, maxLines: 20 }));
235
+ if (nestedWarning) lines.push(`Warning: ${nestedWarning}`);
208
236
  return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
209
237
  }
210
238
 
@@ -252,7 +280,18 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
252
280
 
253
281
  type AsyncResumeSourceTarget = ReturnType<typeof resolveAsyncResumeTarget> & { source: "async" };
254
282
  type ForegroundResumeSourceTarget = NonNullable<ReturnType<typeof resolveForegroundResumeTarget>> & { kind: "revive"; source: "foreground" };
255
- type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget;
283
+ type NestedResumeSourceTarget = {
284
+ kind: "revive";
285
+ source: "nested";
286
+ runId: string;
287
+ state: "complete" | "failed" | "paused";
288
+ agent: string;
289
+ index: number;
290
+ intercomTarget: string;
291
+ cwd?: string;
292
+ sessionFile: string;
293
+ };
294
+ type ResumeSourceTarget = AsyncResumeSourceTarget | ForegroundResumeSourceTarget | NestedResumeSourceTarget;
256
295
 
257
296
  function isAsyncRunNotFound(error: unknown): boolean {
258
297
  return error instanceof Error && error.message.startsWith("Async run not found.");
@@ -392,6 +431,119 @@ function interruptAsyncRun(state: SubagentState, runId: string | undefined): Age
392
431
  }
393
432
  }
394
433
 
434
+ function nestedRunSessionFile(run: NestedRunSummary): string | undefined {
435
+ return run.sessionFile ?? (run.steps?.length === 1 ? run.steps[0]?.sessionFile : undefined);
436
+ }
437
+
438
+ function nestedRunAgent(run: NestedRunSummary): string | undefined {
439
+ return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : undefined);
440
+ }
441
+
442
+ function pathWithin(base: string, candidate: string): boolean {
443
+ const resolvedBase = path.resolve(base);
444
+ const resolvedCandidate = path.resolve(candidate);
445
+ return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
446
+ }
447
+
448
+ function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: string[]): string {
449
+ const sessionFile = nestedRunSessionFile(run);
450
+ if (!sessionFile) throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
451
+ if (path.extname(sessionFile) !== ".jsonl") throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
452
+ const resolved = path.resolve(sessionFile);
453
+ if (!path.isAbsolute(sessionFile)) throw new Error(`Nested run '${run.id}' session file must be absolute: ${sessionFile}`);
454
+ if (!fs.existsSync(resolved)) throw new Error(`Nested run '${run.id}' session file does not exist: ${sessionFile}`);
455
+ const stat = fs.lstatSync(resolved);
456
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
457
+ const realSessionFile = fs.realpathSync(resolved);
458
+ const trustedRoots = trustedSessionRoots
459
+ .filter((root) => fs.existsSync(root))
460
+ .map((root) => fs.realpathSync(root));
461
+ if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
462
+ throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
463
+ }
464
+ if (!realSessionFile.split(path.sep).includes(run.id)) {
465
+ throw new Error(`Nested run '${run.id}' session file is not under that nested run's session directory: ${sessionFile}`);
466
+ }
467
+ return realSessionFile;
468
+ }
469
+
470
+ function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, trustedSessionRoots: string[]): NestedResumeSourceTarget {
471
+ const run = match.match.run;
472
+ if (run.state === "running" || run.state === "queued") throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
473
+ const agent = nestedRunAgent(run);
474
+ if (!agent) throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
475
+ const state = run.state === "complete" || run.state === "failed" || run.state === "paused" ? run.state : "failed";
476
+ const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
477
+ return {
478
+ kind: "revive",
479
+ source: "nested",
480
+ runId: run.id,
481
+ state,
482
+ agent,
483
+ index: 0,
484
+ intercomTarget: resolveSubagentIntercomTarget(run.id, agent, 0),
485
+ cwd: asyncDir ? path.dirname(asyncDir) : undefined,
486
+ sessionFile: validateNestedSessionFile(run, trustedSessionRoots),
487
+ };
488
+ }
489
+
490
+ async function waitForNestedControlResult(target: ResolvedSubagentRunId & { kind: "nested" }, requestId: string, timeoutMs = 1_000) {
491
+ const deadline = Date.now() + timeoutMs;
492
+ while (Date.now() < deadline) {
493
+ const result = readNestedControlResults(target.match.route).find((candidate) => candidate.requestId === requestId && candidate.targetRunId === target.match.run.id);
494
+ if (result) return result;
495
+ await new Promise((resolve) => setTimeout(resolve, 50));
496
+ }
497
+ return undefined;
498
+ }
499
+
500
+ async function sendNestedControlRequest(target: ResolvedSubagentRunId & { kind: "nested" }, action: "interrupt" | "resume", message?: string) {
501
+ const requestId = randomUUID();
502
+ writeNestedControlRequest(target.match.route, {
503
+ ts: Date.now(),
504
+ requestId,
505
+ targetRunId: target.match.run.id,
506
+ action,
507
+ ...(message ? { message } : {}),
508
+ });
509
+ return waitForNestedControlResult(target, requestId);
510
+ }
511
+
512
+ function directNestedAsyncInterrupt(target: ResolvedSubagentRunId & { kind: "nested" }): AgentToolResult<Details> | undefined {
513
+ const run = target.match.run;
514
+ const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
515
+ if (!asyncDir) return undefined;
516
+ const status = readStatus(asyncDir);
517
+ const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
518
+ if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return undefined;
519
+ try {
520
+ process.kill(pid, ASYNC_INTERRUPT_SIGNAL);
521
+ return { content: [{ type: "text", text: `Interrupt requested for nested async run ${run.id}.` }], details: { mode: "management", results: [] } };
522
+ } catch (error) {
523
+ const message = error instanceof Error ? error.message : String(error);
524
+ return { content: [{ type: "text", text: `Failed to interrupt nested async run ${run.id}: ${message}` }], isError: true, details: { mode: "management", results: [] } };
525
+ }
526
+ }
527
+
528
+ async function interruptNestedRun(target: ResolvedSubagentRunId & { kind: "nested" }): Promise<AgentToolResult<Details>> {
529
+ const run = target.match.run;
530
+ if (run.state === "complete") return { content: [{ type: "text", text: `Nested run ${run.id} is already complete and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
531
+ if (run.state === "failed") return { content: [{ type: "text", text: `Nested run ${run.id} has failed and cannot be interrupted.` }], isError: true, details: { mode: "management", results: [] } };
532
+ if (run.state === "paused") return { content: [{ type: "text", text: `Nested run ${run.id} is already paused.` }], isError: true, details: { mode: "management", results: [] } };
533
+ const result = await sendNestedControlRequest(target, "interrupt");
534
+ if (result) return { content: [{ type: "text", text: result.message }], isError: result.ok ? undefined : true, details: { mode: "management", results: [] } };
535
+ const direct = directNestedAsyncInterrupt(target);
536
+ if (direct) return direct;
537
+ return { content: [{ type: "text", text: `Nested run ${run.id} owner is not reachable and no safe direct async interrupt fallback is available.` }], isError: true, details: { mode: "management", results: [] } };
538
+ }
539
+
540
+ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { kind: "nested" }; message: string }): Promise<AgentToolResult<Details>> {
541
+ const run = input.target.match.run;
542
+ const result = await sendNestedControlRequest(input.target, "resume", input.message);
543
+ if (result) return { content: [{ type: "text", text: result.message }], isError: result.ok ? undefined : true, details: { mode: "management", results: [] } };
544
+ return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
545
+ }
546
+
395
547
  async function resumeAsyncRun(input: {
396
548
  params: SubagentParamsLike;
397
549
  requestCwd: string;
@@ -408,8 +560,22 @@ async function resumeAsyncRun(input: {
408
560
  }
409
561
 
410
562
  let target: ResumeSourceTarget;
563
+ const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
411
564
  try {
412
- target = resolveResumeTarget(input.params, input.deps.state);
565
+ const requestedId = input.params.id ?? input.params.runId;
566
+ const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
567
+ if (resolved?.kind === "nested") {
568
+ if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
569
+ return resumeLiveNestedRun({ target: resolved, message: followUp });
570
+ }
571
+ const trustedSessionRoots = [
572
+ ...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
573
+ ...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
574
+ ];
575
+ target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
576
+ } else {
577
+ target = resolveResumeTarget(input.params, input.deps.state);
578
+ }
413
579
  } catch (error) {
414
580
  const message = error instanceof Error ? error.message : String(error);
415
581
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
@@ -445,7 +611,6 @@ async function resumeAsyncRun(input: {
445
611
  };
446
612
  }
447
613
 
448
- const parentSessionFile = input.ctx.sessionManager.getSessionFile() ?? null;
449
614
  input.deps.state.currentSessionId = resolveCurrentSessionId(input.ctx.sessionManager);
450
615
  const effectiveCwd = target.cwd ?? input.requestCwd;
451
616
  const scope: AgentScope = resolveExecutionAgentScope(input.params.agentScope);
@@ -502,7 +667,7 @@ async function resumeAsyncRun(input: {
502
667
 
503
668
  const revivedId = result.details.asyncId ?? runId;
504
669
  const revivedTarget = intercomBridge.active ? resolveSubagentIntercomTarget(revivedId, target.agent, 0) : undefined;
505
- const sourceLabel = target.source === "foreground" ? "foreground" : "async";
670
+ const sourceLabel = target.source;
506
671
  const lines = [
507
672
  `Revived ${sourceLabel} subagent from ${target.runId}.`,
508
673
  `Revived run: ${revivedId}`,
@@ -539,6 +704,7 @@ async function emitForegroundResultIntercom(input: {
539
704
  mode: SubagentRunMode;
540
705
  results: SingleResult[];
541
706
  chainSteps?: number;
707
+ nestedChildren?: NestedRunSummary[];
542
708
  }): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
543
709
  if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
544
710
  const children = input.results.flatMap((result, index) => result.detached ? [] : [{
@@ -560,7 +726,7 @@ async function emitForegroundResultIntercom(input: {
560
726
  runId: input.runId,
561
727
  mode: input.mode,
562
728
  source: "foreground",
563
- children,
729
+ children: attachNestedChildrenToResultChildren(input.runId, children, input.nestedChildren),
564
730
  ...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {}),
565
731
  });
566
732
  const delivered = await deliverSubagentResultIntercomEvent(input.pi.events, payload);
@@ -574,6 +740,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
574
740
  runId: string;
575
741
  mode: SubagentRunMode;
576
742
  details: Details;
743
+ nestedChildren?: NestedRunSummary[];
577
744
  }): Promise<{ text: string; details: Details } | null> {
578
745
  const payload = await emitForegroundResultIntercom({
579
746
  pi: input.pi,
@@ -582,6 +749,7 @@ async function maybeBuildForegroundIntercomReceipt(input: {
582
749
  mode: input.mode,
583
750
  results: input.details.results,
584
751
  ...(typeof input.details.totalSteps === "number" ? { chainSteps: input.details.totalSteps } : {}),
752
+ ...(input.nestedChildren?.length ? { nestedChildren: input.nestedChildren } : {}),
585
753
  });
586
754
  if (!payload) return null;
587
755
  return {
@@ -851,6 +1019,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
851
1019
  effectiveAsync,
852
1020
  controlConfig,
853
1021
  intercomBridge,
1022
+ nestedRoute,
854
1023
  } = data;
855
1024
  const hasChain = (params.chain?.length ?? 0) > 0;
856
1025
  const hasTasks = (params.tasks?.length ?? 0) > 0;
@@ -881,7 +1050,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
881
1050
 
882
1051
  if (!isAsyncAvailable()) {
883
1052
  return {
884
- content: [{ type: "text", text: "Async mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1053
+ content: [{ type: "text", text: `Async mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the ${APP_NAME}-subagents package dependencies are installed.` }],
885
1054
  isError: true,
886
1055
  details: { mode: "single" as const, results: [] },
887
1056
  };
@@ -941,6 +1110,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
941
1110
  controlConfig,
942
1111
  controlIntercomTarget,
943
1112
  childIntercomTarget,
1113
+ nestedRoute,
944
1114
  });
945
1115
  }
946
1116
 
@@ -968,6 +1138,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
968
1138
  controlConfig,
969
1139
  controlIntercomTarget,
970
1140
  childIntercomTarget,
1141
+ nestedRoute,
971
1142
  });
972
1143
  }
973
1144
 
@@ -981,7 +1152,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
981
1152
  };
982
1153
  }
983
1154
  const rawOutput = params.output !== undefined ? params.output : a.output;
984
- const effectiveOutput: string | false | undefined = rawOutput === true ? a.output : (rawOutput as string | false | undefined);
1155
+ const effectiveOutput = normalizeSingleOutputOverride(rawOutput, a.output);
985
1156
  const effectiveOutputMode = params.outputMode ?? "inline";
986
1157
  const normalizedSkills = normalizeSkillInput(params.skill);
987
1158
  const skills = normalizedSkills === false ? [] : normalizedSkills;
@@ -1010,6 +1181,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
1010
1181
  controlConfig,
1011
1182
  controlIntercomTarget,
1012
1183
  childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
1184
+ nestedRoute,
1013
1185
  });
1014
1186
  }
1015
1187
 
@@ -1062,6 +1234,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1062
1234
  childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(runId, agent, index) : undefined,
1063
1235
  orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1064
1236
  foregroundControl,
1237
+ nestedRoute: foregroundControl?.nestedRoute,
1065
1238
  chainSkills,
1066
1239
  chainDir: params.chainDir,
1067
1240
  maxSubagentDepth: currentMaxSubagentDepth,
@@ -1072,7 +1245,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1072
1245
  if (chainResult.requestedAsync) {
1073
1246
  if (!isAsyncAvailable()) {
1074
1247
  return {
1075
- content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1248
+ content: [{ type: "text", text: `Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the ${APP_NAME}-subagents package dependencies are installed.` }],
1076
1249
  isError: true,
1077
1250
  details: { mode: "chain" as const, results: [] },
1078
1251
  };
@@ -1106,10 +1279,12 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1106
1279
  controlConfig,
1107
1280
  controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
1108
1281
  childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
1282
+ nestedRoute: data.nestedRoute,
1109
1283
  });
1110
1284
  }
1111
1285
 
1112
1286
  const chainDetails = chainResult.details ? compactForegroundDetails({ ...chainResult.details, runId }) : undefined;
1287
+ if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
1113
1288
  if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
1114
1289
  const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted || result.detached)
1115
1290
  ? await maybeBuildForegroundIntercomReceipt({
@@ -1118,6 +1293,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1118
1293
  runId,
1119
1294
  mode: "chain",
1120
1295
  details: chainDetails,
1296
+ ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
1121
1297
  })
1122
1298
  : null;
1123
1299
  if (intercomReceipt) {
@@ -1314,6 +1490,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
1314
1490
  onControlEvent: input.onControlEvent,
1315
1491
  intercomSessionName: input.childIntercomTarget?.(task.agent, index),
1316
1492
  orchestratorIntercomTarget: input.orchestratorIntercomTarget,
1493
+ nestedRoute: input.foregroundControl?.nestedRoute,
1317
1494
  modelOverride: input.modelOverrides[index],
1318
1495
  availableModels: input.availableModels,
1319
1496
  preferredModelProvider: input.ctx.model?.provider,
@@ -1483,7 +1660,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1483
1660
  if (result.runInBackground) {
1484
1661
  if (!isAsyncAvailable()) {
1485
1662
  return {
1486
- content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1663
+ content: [{ type: "text", text: `Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the ${APP_NAME}-subagents package dependencies are installed.` }],
1487
1664
  isError: true,
1488
1665
  details: { mode: "parallel" as const, results: [] },
1489
1666
  };
@@ -1640,12 +1817,14 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1640
1817
  };
1641
1818
  }
1642
1819
 
1820
+ if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
1643
1821
  const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1644
1822
  pi: deps.pi,
1645
1823
  intercomBridge: data.intercomBridge,
1646
1824
  runId,
1647
1825
  mode: "parallel",
1648
1826
  details,
1827
+ ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
1649
1828
  });
1650
1829
  if (intercomReceipt) {
1651
1830
  return {
@@ -1721,7 +1900,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1721
1900
  );
1722
1901
  let skillOverride: string[] | false | undefined = normalizeSkillInput(params.skill);
1723
1902
  const rawOutput = params.output !== undefined ? params.output : agentConfig.output;
1724
- let effectiveOutput: string | false | undefined = rawOutput === true ? agentConfig.output : (rawOutput as string | false | undefined);
1903
+ let effectiveOutput = normalizeSingleOutputOverride(rawOutput, agentConfig.output);
1725
1904
  const effectiveOutputMode = params.outputMode ?? "inline";
1726
1905
  const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
1727
1906
  const maxSubagentDepth = resolveChildMaxSubagentDepth(currentMaxSubagentDepth, agentConfig.maxSubagentDepth);
@@ -1755,13 +1934,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1755
1934
  task = result.templates[0]!;
1756
1935
  const override = result.behaviorOverrides[0];
1757
1936
  if (override?.model) modelOverride = override.model;
1758
- if (override?.output !== undefined) effectiveOutput = override.output;
1937
+ if (override?.output !== undefined) effectiveOutput = normalizeSingleOutputOverride(override.output, agentConfig.output);
1759
1938
  if (override?.skills !== undefined) skillOverride = override.skills;
1760
1939
 
1761
1940
  if (result.runInBackground) {
1762
1941
  if (!isAsyncAvailable()) {
1763
1942
  return {
1764
- content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1943
+ content: [{ type: "text", text: `Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the ${APP_NAME}-subagents package dependencies are installed.` }],
1765
1944
  isError: true,
1766
1945
  details: { mode: "single" as const, results: [] },
1767
1946
  };
@@ -1875,6 +2054,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1875
2054
  onControlEvent,
1876
2055
  intercomSessionName: childIntercomTarget,
1877
2056
  orchestratorIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
2057
+ nestedRoute: foregroundControl?.nestedRoute,
1878
2058
  index: 0,
1879
2059
  modelOverride,
1880
2060
  availableModels,
@@ -1921,12 +2101,14 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1921
2101
  rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, results: details.results });
1922
2102
 
1923
2103
  if (!r.detached && !r.interrupted) {
2104
+ if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
1924
2105
  const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1925
2106
  pi: deps.pi,
1926
2107
  intercomBridge: data.intercomBridge,
1927
2108
  runId,
1928
2109
  mode: "single",
1929
2110
  details,
2111
+ ...(foregroundControl?.nestedChildren?.length ? { nestedChildren: foregroundControl.nestedChildren } : {}),
1930
2112
  });
1931
2113
  if (intercomReceipt) {
1932
2114
  return {
@@ -2020,16 +2202,41 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2020
2202
  };
2021
2203
  }
2022
2204
  if (params.action === "status") {
2023
- const foreground = getForegroundControl(deps.state, paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId);
2024
- if (foreground) return foregroundStatusResult(foreground);
2025
- return inspectSubagentStatus(paramsWithResolvedCwd);
2205
+ const targetRunId = paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId;
2206
+ if (targetRunId) {
2207
+ try {
2208
+ const nestedScope = nestedResolutionScopeForExecutor(deps);
2209
+ const resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedScope });
2210
+ if (resolved?.kind === "foreground") {
2211
+ const foreground = getForegroundControl(deps.state, resolved.id);
2212
+ if (foreground) return foregroundStatusResult(foreground);
2213
+ }
2214
+ } catch (error) {
2215
+ const message = error instanceof Error ? error.message : String(error);
2216
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
2217
+ }
2218
+ } else {
2219
+ const foreground = getForegroundControl(deps.state, undefined);
2220
+ if (foreground) return foregroundStatusResult(foreground);
2221
+ }
2222
+ return inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
2026
2223
  }
2027
2224
  if (params.action === "resume") {
2028
2225
  return resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
2029
2226
  }
2030
2227
  if (params.action === "interrupt") {
2031
2228
  const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
2032
- const foreground = getForegroundControl(deps.state, targetRunId);
2229
+ let resolved: ResolvedSubagentRunId | undefined;
2230
+ if (targetRunId) {
2231
+ try {
2232
+ resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
2233
+ } catch (error) {
2234
+ const message = error instanceof Error ? error.message : String(error);
2235
+ return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
2236
+ }
2237
+ }
2238
+ if (resolved?.kind === "nested") return interruptNestedRun(resolved);
2239
+ const foreground = getForegroundControl(deps.state, resolved?.kind === "foreground" ? resolved.id : targetRunId);
2033
2240
  if (foreground?.interrupt) {
2034
2241
  const interrupted = foreground.interrupt();
2035
2242
  if (interrupted) {
@@ -2046,7 +2253,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2046
2253
  details: { mode: "management", results: [] },
2047
2254
  };
2048
2255
  }
2049
- const asyncInterruptResult = interruptAsyncRun(deps.state, targetRunId);
2256
+ const asyncInterruptResult = interruptAsyncRun(deps.state, resolved?.kind === "async" ? resolved.id : targetRunId);
2050
2257
  if (asyncInterruptResult) return asyncInterruptResult;
2051
2258
  return {
2052
2259
  content: [{ type: "text", text: "No interrupt-capable run found in this session." }],
@@ -2061,6 +2268,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2061
2268
  details: { mode: "management" as const, results: [] },
2062
2269
  };
2063
2270
  }
2271
+ if (deps.allowMutatingManagementActions === false && MUTATING_MANAGEMENT_ACTIONS.has(params.action)) {
2272
+ return {
2273
+ content: [{ type: "text", text: `Action '${params.action}' is not available from child-safe subagent fanout mode.` }],
2274
+ isError: true,
2275
+ details: { mode: "management" as const, results: [] },
2276
+ };
2277
+ }
2064
2278
  return handleManagementAction(params.action, paramsWithResolvedCwd, { ...ctx, cwd: requestCwd });
2065
2279
  }
2066
2280
 
@@ -2108,6 +2322,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2108
2322
  ? discoveredAgents.map((agent) => applyIntercomBridgeToAgent(agent, intercomBridge))
2109
2323
  : discoveredAgents;
2110
2324
  const runId = randomUUID().slice(0, 8);
2325
+ const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
2326
+ const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
2327
+ const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
2111
2328
  const shareEnabled = effectiveParams.share === true;
2112
2329
  const hasChain = (effectiveParams.chain?.length ?? 0) > 0;
2113
2330
  const hasTasks = (effectiveParams.tasks?.length ?? 0) > 0;
@@ -2189,6 +2406,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2189
2406
  effectiveAsync,
2190
2407
  controlConfig,
2191
2408
  intercomBridge,
2409
+ nestedRoute,
2192
2410
  };
2193
2411
 
2194
2412
  const foregroundMode: "single" | "parallel" | "chain" = hasChain ? "chain" : hasTasks ? "parallel" : "single";
@@ -2202,6 +2420,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2202
2420
  currentAgent: undefined,
2203
2421
  currentIndex: undefined,
2204
2422
  currentActivityState: undefined,
2423
+ nestedRoute,
2205
2424
  interrupt: undefined,
2206
2425
  };
2207
2426
  if (foregroundControl) {
@@ -2209,14 +2428,92 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
2209
2428
  deps.state.lastForegroundControlId = runId;
2210
2429
  }
2211
2430
 
2431
+ const writeNestedForegroundEvent = (type: "subagent.nested.started" | "subagent.nested.completed", result?: AgentToolResult<Details>): void => {
2432
+ if (!inheritedNestedRoute || !nestedParentAddress) return;
2433
+ const now = Date.now();
2434
+ const details = result?.details;
2435
+ const state = type === "subagent.nested.started"
2436
+ ? "running"
2437
+ : result?.isError || details?.results.some((child) => child.exitCode !== 0)
2438
+ ? "failed"
2439
+ : details?.results.some((child) => child.interrupted)
2440
+ ? "paused"
2441
+ : "complete";
2442
+ const errorText = result?.isError
2443
+ ? result.content.find((item) => item.type === "text")?.text
2444
+ : undefined;
2445
+ const agentsForSummary = hasTasks && effectiveParams.tasks
2446
+ ? effectiveParams.tasks.map((task) => task.agent)
2447
+ : hasChain && effectiveParams.chain
2448
+ ? effectiveParams.chain.flatMap((step) => isParallelStep(step) ? step.parallel.map((task) => task.agent) : [(step as SequentialStep).agent])
2449
+ : effectiveParams.agent ? [effectiveParams.agent] : [];
2450
+ const leafIntercomTarget = intercomBridge.active && agentsForSummary[0]
2451
+ ? resolveSubagentIntercomTarget(runId, agentsForSummary[0], 0)
2452
+ : undefined;
2453
+ try {
2454
+ writeNestedEvent(inheritedNestedRoute, {
2455
+ type,
2456
+ ts: now,
2457
+ parentRunId: nestedParentAddress.parentRunId,
2458
+ parentStepIndex: nestedParentAddress.parentStepIndex,
2459
+ child: {
2460
+ id: runId,
2461
+ parentRunId: nestedParentAddress.parentRunId,
2462
+ parentStepIndex: nestedParentAddress.parentStepIndex,
2463
+ depth: nestedParentAddress.depth,
2464
+ path: nestedParentAddress.path,
2465
+ ownerIntercomTarget: getEnvValue(SUBAGENT_INTERCOM_SESSION_NAME_ENV),
2466
+ leafIntercomTarget,
2467
+ intercomTarget: leafIntercomTarget,
2468
+ ownerState: state === "running" ? "live" : "gone",
2469
+ mode: foregroundMode,
2470
+ state,
2471
+ agent: agentsForSummary[0],
2472
+ agents: agentsForSummary,
2473
+ startedAt: foregroundControl?.startedAt ?? now,
2474
+ ...(state !== "running" ? { endedAt: now } : {}),
2475
+ lastUpdate: now,
2476
+ ...(errorText ? { error: errorText } : {}),
2477
+ ...(details?.results.length ? { steps: details.results.map((child) => ({
2478
+ agent: child.agent,
2479
+ status: child.interrupted ? "paused" : child.exitCode === 0 ? "complete" : "failed",
2480
+ ...(child.sessionFile ? { sessionFile: child.sessionFile } : {}),
2481
+ ...(child.error ? { error: child.error } : {}),
2482
+ })) } : {}),
2483
+ },
2484
+ });
2485
+ } catch (error) {
2486
+ console.error("Failed to emit nested foreground status event:", error);
2487
+ }
2488
+ };
2489
+
2490
+ let nestedForegroundStarted = false;
2212
2491
  try {
2213
2492
  const asyncResult = runAsyncPath(execData, deps);
2214
2493
  if (asyncResult) return withForkContext(asyncResult, effectiveParams.context);
2215
- if (hasChain && effectiveParams.chain) return withForkContext(await runChainPath(execData, deps), effectiveParams.context);
2216
- if (hasTasks && effectiveParams.tasks) return withForkContext(await runParallelPath(execData, deps), effectiveParams.context);
2217
- if (hasSingle) return withForkContext(await runSinglePath(execData, deps), effectiveParams.context);
2494
+ if (foregroundControl) {
2495
+ writeNestedForegroundEvent("subagent.nested.started");
2496
+ nestedForegroundStarted = true;
2497
+ }
2498
+ if (hasChain && effectiveParams.chain) {
2499
+ const result = await runChainPath(execData, deps);
2500
+ writeNestedForegroundEvent("subagent.nested.completed", result);
2501
+ return withForkContext(result, effectiveParams.context);
2502
+ }
2503
+ if (hasTasks && effectiveParams.tasks) {
2504
+ const result = await runParallelPath(execData, deps);
2505
+ writeNestedForegroundEvent("subagent.nested.completed", result);
2506
+ return withForkContext(result, effectiveParams.context);
2507
+ }
2508
+ if (hasSingle) {
2509
+ const result = await runSinglePath(execData, deps);
2510
+ writeNestedForegroundEvent("subagent.nested.completed", result);
2511
+ return withForkContext(result, effectiveParams.context);
2512
+ }
2218
2513
  } catch (error) {
2219
- return toExecutionErrorResult(effectiveParams, error);
2514
+ const errorResult = toExecutionErrorResult(effectiveParams, error);
2515
+ if (nestedForegroundStarted) writeNestedForegroundEvent("subagent.nested.completed", errorResult);
2516
+ return errorResult;
2220
2517
  } finally {
2221
2518
  if (foregroundControl) {
2222
2519
  clearPendingForegroundControlNotices(deps.state, runId);