@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
@@ -0,0 +1,52 @@
1
+ import * as path from "node:path";
2
+
3
+ const MAX_NESTED_ID_LENGTH = 128;
4
+ export const MAX_NESTED_PATH_ENTRIES = 4;
5
+
6
+ export type NestedPathEntry = { runId: string; stepIndex?: number; agent?: string };
7
+
8
+ export function isSafeNestedPathId(value: unknown): value is string {
9
+ return typeof value === "string"
10
+ && value.length > 0
11
+ && value.length <= MAX_NESTED_ID_LENGTH
12
+ && !path.isAbsolute(value)
13
+ && !value.includes("/")
14
+ && !value.includes("\\")
15
+ && !value.includes("..");
16
+ }
17
+
18
+ function finiteNumber(value: unknown): number | undefined {
19
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
20
+ }
21
+
22
+ function nonEmptyString(value: unknown, max: number): string | undefined {
23
+ return typeof value === "string" && value.length > 0 ? value.slice(0, max) : undefined;
24
+ }
25
+
26
+ export function sanitizeNestedPath(value: unknown): NestedPathEntry[] {
27
+ if (!Array.isArray(value)) return [];
28
+ return value.map((part) => {
29
+ if (!part || typeof part !== "object") return undefined;
30
+ const record = part as Record<string, unknown>;
31
+ if (!isSafeNestedPathId(record.runId)) return undefined;
32
+ return {
33
+ runId: record.runId,
34
+ ...(finiteNumber(record.stepIndex) !== undefined ? { stepIndex: finiteNumber(record.stepIndex) } : {}),
35
+ ...(nonEmptyString(record.agent, 128) ? { agent: nonEmptyString(record.agent, 128) } : {}),
36
+ };
37
+ }).filter((part): part is NestedPathEntry => Boolean(part)).slice(0, MAX_NESTED_PATH_ENTRIES);
38
+ }
39
+
40
+ export function parseNestedPathEnv(value: string | undefined): NestedPathEntry[] {
41
+ if (!value) return [];
42
+ try {
43
+ return sanitizeNestedPath(JSON.parse(value) as unknown);
44
+ } catch {
45
+ return [];
46
+ }
47
+ }
48
+
49
+ export function encodeNestedPathEnv(value: NestedPathEntry[]): string {
50
+ const sanitized = sanitizeNestedPath(value);
51
+ return sanitized.length ? JSON.stringify(sanitized) : "";
52
+ }
@@ -0,0 +1,115 @@
1
+ import { formatDuration, formatTokens, shortenPath } from "../../shared/formatters.ts";
2
+ import { formatActivityLabel } from "../../shared/status-format.ts";
3
+ import type { ActivityState, NestedRunSummary, NestedStepSummary } from "../../shared/types.ts";
4
+
5
+ export interface NestedRunCounts {
6
+ total: number;
7
+ running: number;
8
+ paused: number;
9
+ complete: number;
10
+ failed: number;
11
+ queued: number;
12
+ }
13
+
14
+ export function countNestedRuns(children: NestedRunSummary[] | undefined): NestedRunCounts {
15
+ const counts: NestedRunCounts = { total: 0, running: 0, paused: 0, complete: 0, failed: 0, queued: 0 };
16
+ for (const child of children ?? []) {
17
+ counts.total++;
18
+ counts[child.state]++;
19
+ const nested = countNestedRuns([...(child.children ?? []), ...(child.steps?.flatMap((step) => step.children ?? []) ?? [])]);
20
+ counts.total += nested.total;
21
+ counts.running += nested.running;
22
+ counts.paused += nested.paused;
23
+ counts.complete += nested.complete;
24
+ counts.failed += nested.failed;
25
+ counts.queued += nested.queued;
26
+ }
27
+ return counts;
28
+ }
29
+
30
+ export function formatNestedAggregate(children: NestedRunSummary[] | undefined): string | undefined {
31
+ const counts = countNestedRuns(children);
32
+ if (counts.total === 0) return undefined;
33
+ const parts = [
34
+ counts.running > 0 ? `${counts.running} running` : "",
35
+ counts.paused > 0 ? `${counts.paused} paused` : "",
36
+ counts.failed > 0 ? `${counts.failed} failed` : "",
37
+ counts.complete > 0 ? `${counts.complete} complete` : "",
38
+ counts.queued > 0 ? `${counts.queued} queued` : "",
39
+ ].filter(Boolean);
40
+ return `+${counts.total} nested run${counts.total === 1 ? "" : "s"}${parts.length ? ` (${parts.join(", ")})` : ""}`;
41
+ }
42
+
43
+ function nestedRunLabel(run: NestedRunSummary): string {
44
+ if (run.agent) return run.agent;
45
+ if (run.agents?.length) return run.agents.length === 1 ? run.agents[0]! : `${run.agents.slice(0, 2).join(", ")}${run.agents.length > 2 ? ` +${run.agents.length - 2}` : ""}`;
46
+ return run.id;
47
+ }
48
+
49
+ function formatNestedActivity(input: {
50
+ activityState?: ActivityState;
51
+ lastActivityAt?: number;
52
+ currentTool?: string;
53
+ currentToolStartedAt?: number;
54
+ currentPath?: string;
55
+ turnCount?: number;
56
+ toolCount?: number;
57
+ totalTokens?: NestedRunSummary["totalTokens"];
58
+ }): string | undefined {
59
+ const facts: string[] = [];
60
+ if (input.currentTool && input.currentToolStartedAt !== undefined) facts.push(`tool ${input.currentTool} ${formatDuration(Math.max(0, Date.now() - input.currentToolStartedAt))}`);
61
+ else if (input.currentTool) facts.push(`tool ${input.currentTool}`);
62
+ if (input.currentPath) facts.push(shortenPath(input.currentPath));
63
+ if (input.turnCount !== undefined) facts.push(`${input.turnCount} turns`);
64
+ if (input.toolCount !== undefined) facts.push(`${input.toolCount} tools`);
65
+ if (input.totalTokens) facts.push(`${formatTokens(input.totalTokens.total)} tok`);
66
+ const activity = formatActivityLabel(input.lastActivityAt, input.activityState as ActivityState | undefined);
67
+ return activity || facts.length ? [activity, ...facts].filter(Boolean).join(" | ") : undefined;
68
+ }
69
+
70
+ function formatNestedRunLines(children: NestedRunSummary[] | undefined, options: { indent: string; maxDepth: number; maxLines: number; commandHints?: boolean }): string[] {
71
+ const lines: string[] = [];
72
+ const append = (items: NestedRunSummary[] | undefined, depth: number, indent: string): void => {
73
+ if (!items?.length || lines.length >= options.maxLines) return;
74
+ if (depth > options.maxDepth) {
75
+ const aggregate = formatNestedAggregate(items);
76
+ if (aggregate && lines.length < options.maxLines) lines.push(`${indent}↳ ${aggregate}`);
77
+ return;
78
+ }
79
+ for (let index = 0; index < items.length; index++) {
80
+ const child = items[index]!;
81
+ if (lines.length >= options.maxLines) {
82
+ const aggregate = formatNestedAggregate(items.slice(index));
83
+ if (aggregate) lines[lines.length - 1] = `${indent}↳ ${aggregate}`;
84
+ return;
85
+ }
86
+ const activity = child.state === "running" ? formatNestedActivity(child) : undefined;
87
+ const error = child.error ? ` | error: ${child.error}` : "";
88
+ lines.push(`${indent}↳ ${nestedRunLabel(child)} [${child.id}] ${child.state}${activity ? ` | ${activity}` : ""}${error}`);
89
+ if (options.commandHints && lines.length < options.maxLines) lines.push(`${indent} Status: subagent({ action: "status", id: "${child.id}" })`);
90
+ if (depth === options.maxDepth) {
91
+ const aggregate = formatNestedAggregate([...(child.steps?.flatMap((step) => step.children ?? []) ?? []), ...(child.children ?? [])]);
92
+ if (aggregate && lines.length < options.maxLines) lines.push(`${indent} ↳ ${aggregate}`);
93
+ continue;
94
+ }
95
+ for (const [stepIndex, step] of (child.steps ?? []).entries()) {
96
+ if (lines.length >= options.maxLines) return;
97
+ const stepActivity = step.status === "running" ? formatNestedActivity(step) : undefined;
98
+ lines.push(`${indent} ${stepIndex + 1}. ${step.agent} ${step.status}${stepActivity ? ` | ${stepActivity}` : ""}${step.error ? ` | error: ${step.error}` : ""}`);
99
+ append(step.children, depth + 1, `${indent} `);
100
+ }
101
+ append(child.children, depth + 1, `${indent} `);
102
+ }
103
+ };
104
+ append(children, 0, options.indent);
105
+ return lines;
106
+ }
107
+
108
+ export function formatNestedRunStatusLines(children: NestedRunSummary[] | undefined, options: { indent?: string; maxDepth?: number; maxLines?: number; commandHints?: boolean } = {}): string[] {
109
+ return formatNestedRunLines(children, {
110
+ indent: options.indent ?? " ",
111
+ maxDepth: options.maxDepth ?? 2,
112
+ maxLines: options.maxLines ?? 40,
113
+ commandHints: options.commandHints ?? false,
114
+ });
115
+ }
@@ -8,6 +8,7 @@ export interface RunnerSubagentStep {
8
8
  tools?: string[];
9
9
  extensions?: string[];
10
10
  mcpDirectTools?: string[];
11
+ completionGuard?: boolean;
11
12
  systemPrompt?: string | null;
12
13
  systemPromptMode?: "append" | "replace";
13
14
  inheritProjectContext: boolean;
@@ -2,17 +2,30 @@ import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { APP_NAME } from "@bastani/atomic";
5
+ import { APP_NAME, getEnvValue } from "@bastani/atomic";
6
+ import { encodeNestedPathEnv, parseNestedPathEnv, type NestedPathEntry } from "./nested-path.ts";
7
+ import { resolveMcpDirectToolNames } from "./mcp-direct-tool-allowlist.ts";
6
8
 
7
9
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
8
10
  const TASK_ARG_LIMIT = 8000;
9
- const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
11
+ export const SUBAGENT_PARENT_MAX_DEPTH = 3;
12
+ export const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
10
13
  const ENV_PREFIX = APP_NAME.toUpperCase();
14
+ export const FANOUT_CHILD_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "extension", "fanout-child.ts");
11
15
  export const SUBAGENT_CHILD_ENV = `${ENV_PREFIX}_SUBAGENT_CHILD`;
12
16
  export const SUBAGENT_ORCHESTRATOR_TARGET_ENV = `${ENV_PREFIX}_SUBAGENT_ORCHESTRATOR_TARGET`;
13
17
  export const SUBAGENT_RUN_ID_ENV = `${ENV_PREFIX}_SUBAGENT_RUN_ID`;
14
18
  export const SUBAGENT_CHILD_AGENT_ENV = `${ENV_PREFIX}_SUBAGENT_CHILD_AGENT`;
15
19
  export const SUBAGENT_CHILD_INDEX_ENV = `${ENV_PREFIX}_SUBAGENT_CHILD_INDEX`;
20
+ export const SUBAGENT_FANOUT_CHILD_ENV = `${ENV_PREFIX}_SUBAGENT_FANOUT_CHILD`;
21
+ export const SUBAGENT_PARENT_EVENT_SINK_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_EVENT_SINK`;
22
+ export const SUBAGENT_PARENT_CONTROL_INBOX_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_CONTROL_INBOX`;
23
+ export const SUBAGENT_PARENT_ROOT_RUN_ID_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_ROOT_RUN_ID`;
24
+ export const SUBAGENT_PARENT_RUN_ID_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_RUN_ID`;
25
+ export const SUBAGENT_PARENT_CHILD_INDEX_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_CHILD_INDEX`;
26
+ export const SUBAGENT_PARENT_DEPTH_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_DEPTH`;
27
+ export const SUBAGENT_PARENT_PATH_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_PATH`;
28
+ export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV = `${ENV_PREFIX}_SUBAGENT_PARENT_CAPABILITY_TOKEN`;
16
29
  export const SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV = `${ENV_PREFIX}_SUBAGENT_INHERIT_PROJECT_CONTEXT`;
17
30
  export const SUBAGENT_INHERIT_SKILLS_ENV = `${ENV_PREFIX}_SUBAGENT_INHERIT_SKILLS`;
18
31
  export const SUBAGENT_INTERCOM_SESSION_NAME_ENV = `${ENV_PREFIX}_SUBAGENT_INTERCOM_SESSION_NAME`;
@@ -32,12 +45,21 @@ interface BuildPiArgsInput {
32
45
  extensions?: string[];
33
46
  systemPrompt?: string | null;
34
47
  mcpDirectTools?: string[];
48
+ cwd?: string;
35
49
  promptFileStem?: string;
36
50
  intercomSessionName?: string;
37
51
  orchestratorIntercomTarget?: string;
38
52
  runId?: string;
39
53
  childAgentName?: string;
40
54
  childIndex?: number;
55
+ parentEventSink?: string;
56
+ parentControlInbox?: string;
57
+ parentRootRunId?: string;
58
+ parentRunId?: string;
59
+ parentChildIndex?: number;
60
+ parentDepth?: number;
61
+ parentPath?: NestedPathEntry[];
62
+ parentCapabilityToken?: string;
41
63
  }
42
64
 
43
65
  interface BuildPiArgsResult {
@@ -74,22 +96,27 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
74
96
  args.push("--model", modelArg);
75
97
  }
76
98
 
99
+ const declaredBuiltinTools = input.tools?.filter((tool) => !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) ?? [];
100
+ const fanoutAuthorized = declaredBuiltinTools.includes("subagent");
77
101
  const toolExtensionPaths: string[] = [];
78
102
  if (input.tools?.length) {
79
- const builtinTools: string[] = [];
103
+ const builtinTools = [...declaredBuiltinTools];
80
104
  for (const tool of input.tools) {
81
- if (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")) {
105
+ if (!declaredBuiltinTools.includes(tool) && (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) {
82
106
  toolExtensionPaths.push(tool);
83
- } else {
84
- builtinTools.push(tool);
85
107
  }
86
108
  }
87
109
  if (builtinTools.length > 0) {
110
+ if (input.mcpDirectTools?.length) {
111
+ builtinTools.push(...resolveMcpDirectToolNames(input.mcpDirectTools, input.cwd));
112
+ }
88
113
  args.push("--tools", builtinTools.join(","));
89
114
  }
90
115
  }
91
116
 
92
- const runtimeExtensions = [PROMPT_RUNTIME_EXTENSION_PATH];
117
+ const runtimeExtensions = fanoutAuthorized
118
+ ? [PROMPT_RUNTIME_EXTENSION_PATH, FANOUT_CHILD_EXTENSION_PATH]
119
+ : [PROMPT_RUNTIME_EXTENSION_PATH];
93
120
  if (input.extensions !== undefined) {
94
121
  args.push("--no-extensions");
95
122
  for (const extPath of [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...input.extensions])]) {
@@ -107,7 +134,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
107
134
 
108
135
  let tempDir: string | undefined;
109
136
  if (input.systemPrompt !== undefined && input.systemPrompt !== null) {
110
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
137
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${APP_NAME}-subagent-`));
111
138
  const stem = (input.promptFileStem ?? "prompt").replace(/[^\w.-]/g, "_");
112
139
  const promptPath = path.join(tempDir, `${stem}.md`);
113
140
  fs.writeFileSync(promptPath, input.systemPrompt, { mode: 0o600 });
@@ -116,7 +143,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
116
143
 
117
144
  if (input.task.length > TASK_ARG_LIMIT) {
118
145
  if (!tempDir) {
119
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
146
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${APP_NAME}-subagent-`));
120
147
  }
121
148
  const taskFilePath = path.join(tempDir, "task.md");
122
149
  fs.writeFileSync(taskFilePath, `Task: ${input.task}`, { mode: 0o600 });
@@ -127,6 +154,49 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
127
154
 
128
155
  const env: Record<string, string | undefined> = {};
129
156
  env[SUBAGENT_CHILD_ENV] = "1";
157
+ env[SUBAGENT_FANOUT_CHILD_ENV] = fanoutAuthorized ? "1" : "0";
158
+ const parentEventSinkEnv = getEnvValue(SUBAGENT_PARENT_EVENT_SINK_ENV);
159
+ const parentControlInboxEnv = getEnvValue(SUBAGENT_PARENT_CONTROL_INBOX_ENV);
160
+ const parentRootRunIdEnv = getEnvValue(SUBAGENT_PARENT_ROOT_RUN_ID_ENV);
161
+ const parentRunIdEnv = getEnvValue(SUBAGENT_PARENT_RUN_ID_ENV);
162
+ const parentChildIndexEnv = getEnvValue(SUBAGENT_PARENT_CHILD_INDEX_ENV);
163
+ const parentDepthEnv = getEnvValue(SUBAGENT_PARENT_DEPTH_ENV);
164
+ const parentPathEnv = getEnvValue(SUBAGENT_PARENT_PATH_ENV);
165
+ const parentCapabilityTokenEnv = getEnvValue(SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV);
166
+ const inheritedNestedRoute = Boolean(parentEventSinkEnv && parentRootRunIdEnv && parentCapabilityTokenEnv);
167
+ const parentRunId = input.parentRunId ?? input.runId ?? (inheritedNestedRoute ? getEnvValue(SUBAGENT_RUN_ID_ENV) : undefined) ?? parentRunIdEnv ?? "";
168
+ const parentChildIndex = input.parentChildIndex !== undefined
169
+ ? String(input.parentChildIndex)
170
+ : input.childIndex !== undefined
171
+ ? String(input.childIndex)
172
+ : parentChildIndexEnv ?? "";
173
+ const inheritedDepth = Number(parentDepthEnv);
174
+ const unclampedParentDepth = input.parentDepth ?? (inheritedNestedRoute && Number.isFinite(inheritedDepth) ? inheritedDepth + 1 : 1);
175
+ const parentDepth = Math.min(Math.max(1, unclampedParentDepth), SUBAGENT_PARENT_MAX_DEPTH);
176
+ const parentPath = input.parentPath ?? [
177
+ ...parseNestedPathEnv(parentPathEnv),
178
+ ...(parentRunId ? [{
179
+ runId: parentRunId,
180
+ ...(parentChildIndex && /^\d+$/.test(parentChildIndex) ? { stepIndex: Number(parentChildIndex) } : {}),
181
+ ...(input.childAgentName ? { agent: input.childAgentName } : {}),
182
+ }] : []),
183
+ ];
184
+ env[SUBAGENT_PARENT_EVENT_SINK_ENV] = fanoutAuthorized
185
+ ? input.parentEventSink ?? parentEventSinkEnv ?? ""
186
+ : "";
187
+ env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] = fanoutAuthorized
188
+ ? input.parentControlInbox ?? parentControlInboxEnv ?? ""
189
+ : "";
190
+ env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = fanoutAuthorized
191
+ ? input.parentRootRunId ?? parentRootRunIdEnv ?? input.runId ?? ""
192
+ : "";
193
+ env[SUBAGENT_PARENT_RUN_ID_ENV] = fanoutAuthorized ? parentRunId : "";
194
+ env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = fanoutAuthorized ? parentChildIndex : "";
195
+ env[SUBAGENT_PARENT_DEPTH_ENV] = fanoutAuthorized ? String(parentDepth) : "";
196
+ env[SUBAGENT_PARENT_PATH_ENV] = fanoutAuthorized ? encodeNestedPathEnv(parentPath) : "";
197
+ env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = fanoutAuthorized
198
+ ? input.parentCapabilityToken ?? parentCapabilityTokenEnv ?? ""
199
+ : "";
130
200
  env[SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV] = input.inheritProjectContext ? "1" : "0";
131
201
  env[SUBAGENT_INHERIT_SKILLS_ENV] = input.inheritSkills ? "1" : "0";
132
202
  if (input.intercomSessionName) {
@@ -144,6 +214,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
144
214
  if (input.childIndex !== undefined) {
145
215
  env[SUBAGENT_CHILD_INDEX_ENV] = String(input.childIndex);
146
216
  }
217
+ // Bare MCP_DIRECT_TOOLS is the MCP adapter contract: unset means config defaults, __none__ forces no direct tools.
147
218
  if (input.mcpDirectTools?.length) {
148
219
  env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
149
220
  } else {
@@ -153,6 +224,8 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
153
224
  return { args, env, tempDir };
154
225
  }
155
226
 
227
+ export const parseParentPathEnv = parseNestedPathEnv;
228
+
156
229
  export function cleanupTempDir(tempDir: string | null | undefined): void {
157
230
  if (!tempDir) return;
158
231
  try {
@@ -94,7 +94,7 @@ export function resolvePiCliScript(deps: PiSpawnDeps = {}): string | undefined {
94
94
  return candidate;
95
95
  }
96
96
  } catch {
97
- // CLI script resolution is optional; falling back to the app command lets PATH handle execution.
97
+ // Windows CLI resolution is optional; falling back to the app command lets PATH handle execution.
98
98
  return undefined;
99
99
  }
100
100
 
@@ -8,12 +8,22 @@ export interface SingleOutputSnapshot {
8
8
  size?: number;
9
9
  }
10
10
 
11
+ export function normalizeSingleOutputOverride(
12
+ output: string | boolean | undefined,
13
+ defaultOutput: string | undefined,
14
+ ): string | false | undefined {
15
+ if (output === false || output === "false") return false;
16
+ if (output === true || output === "true") return defaultOutput;
17
+ if (typeof output === "string" && output.length > 0) return output;
18
+ return undefined;
19
+ }
20
+
11
21
  export function resolveSingleOutputPath(
12
- output: string | false | undefined,
22
+ output: string | boolean | undefined,
13
23
  runtimeCwd: string,
14
24
  requestedCwd?: string,
15
25
  ): string | undefined {
16
- if (typeof output !== "string" || !output) return undefined;
26
+ if (typeof output !== "string" || !output || output === "false" || output === "true") return undefined;
17
27
  if (path.isAbsolute(output)) return output;
18
28
  const baseCwd = requestedCwd
19
29
  ? (path.isAbsolute(requestedCwd) ? requestedCwd : path.resolve(runtimeCwd, requestedCwd))
@@ -1,10 +1,13 @@
1
1
  import type { ExtensionAPI } from "@bastani/atomic";
2
- import { APP_NAME, getEnvValue } from "@bastani/atomic";
2
+ import { getEnvValue } from "@bastani/atomic";
3
+ import {
4
+ SUBAGENT_FANOUT_CHILD_ENV,
5
+ SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV,
6
+ SUBAGENT_INHERIT_SKILLS_ENV,
7
+ SUBAGENT_INTERCOM_SESSION_NAME_ENV,
8
+ } from "./pi-args.ts";
3
9
 
4
- const ENV_PREFIX = APP_NAME.toUpperCase();
5
- const SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV = `${ENV_PREFIX}_SUBAGENT_INHERIT_PROJECT_CONTEXT`;
6
- const SUBAGENT_INHERIT_SKILLS_ENV = `${ENV_PREFIX}_SUBAGENT_INHERIT_SKILLS`;
7
- export const SUBAGENT_INTERCOM_SESSION_NAME_ENV = `${ENV_PREFIX}_SUBAGENT_INTERCOM_SESSION_NAME`;
10
+ export { SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "./pi-args.ts";
8
11
 
9
12
  export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
10
13
  "You are a child subagent, not the parent orchestrator.",
@@ -14,6 +17,15 @@ export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
14
17
  "If you need to edit files, call the actual edit/write tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
15
18
  ].join("\n");
16
19
 
20
+ export const CHILD_FANOUT_BOUNDARY_INSTRUCTIONS = [
21
+ "You are a child subagent with explicit fanout responsibility for this assigned task.",
22
+ "The parent session owns final orchestration, acceptance, and follow-up implementation launches.",
23
+ "You may use the `subagent` tool only for the fanout work explicitly requested in this task.",
24
+ "Do not broaden yourself into general parent orchestration. Do not launch follow-up workers unless the task explicitly asks for that.",
25
+ "The maxSubagentDepth cap still applies and may block further fanout.",
26
+ "If you need to edit files, call the actual edit/write tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
27
+ ].join("\n");
28
+
17
29
  const PARENT_ONLY_CUSTOM_MESSAGE_TYPES = new Set([
18
30
  "subagent-orchestration-instructions",
19
31
  "subagent-slash-result",
@@ -64,9 +76,17 @@ export function stripSubagentOrchestrationSkill(prompt: string): string {
64
76
  .replace(/[ \t]*<skill>\s*[\s\S]*?<\/skill>\s*/g, (block) => SUBAGENT_ORCHESTRATION_SKILL_NAME_PATTERN.test(block) ? "" : block);
65
77
  }
66
78
 
79
+ function stripChildBoundaryInstructions(prompt: string): string {
80
+ let rewritten = prompt;
81
+ for (const boundary of [CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS, CHILD_FANOUT_BOUNDARY_INSTRUCTIONS]) {
82
+ rewritten = rewritten.split(boundary).join("");
83
+ }
84
+ return rewritten.replace(/^(?:[ \t]*\r?\n)+/, "");
85
+ }
86
+
67
87
  export function rewriteSubagentPrompt(
68
88
  prompt: string,
69
- options: { inheritProjectContext: boolean; inheritSkills: boolean },
89
+ options: { inheritProjectContext: boolean; inheritSkills: boolean; fanoutChild?: boolean },
70
90
  ): string {
71
91
  let rewritten = prompt;
72
92
  if (!options.inheritProjectContext) {
@@ -76,9 +96,9 @@ export function rewriteSubagentPrompt(
76
96
  rewritten = stripInheritedSkills(rewritten);
77
97
  }
78
98
  rewritten = stripSubagentOrchestrationSkill(rewritten);
79
- return rewritten.includes(CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS)
80
- ? rewritten
81
- : `${CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS}\n\n${rewritten}`;
99
+ rewritten = stripChildBoundaryInstructions(rewritten);
100
+ const boundary = options.fanoutChild ? CHILD_FANOUT_BOUNDARY_INSTRUCTIONS : CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS;
101
+ return `${boundary}\n\n${rewritten}`;
82
102
  }
83
103
 
84
104
  function isParentOnlySubagentMessage(message: unknown): boolean {
@@ -141,10 +161,12 @@ export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
141
161
 
142
162
  const inheritProjectContext = readBooleanEnv(SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV);
143
163
  const inheritSkills = readBooleanEnv(SUBAGENT_INHERIT_SKILLS_ENV);
144
- if (inheritProjectContext === undefined && inheritSkills === undefined) return;
164
+ const fanoutChild = readBooleanEnv(SUBAGENT_FANOUT_CHILD_ENV);
165
+ if (inheritProjectContext === undefined && inheritSkills === undefined && fanoutChild === undefined) return;
145
166
  const rewritten = rewriteSubagentPrompt(event.systemPrompt, {
146
167
  inheritProjectContext: inheritProjectContext ?? true,
147
168
  inheritSkills: inheritSkills ?? true,
169
+ fanoutChild: fanoutChild === true,
148
170
  });
149
171
  if (rewritten === event.systemPrompt) return;
150
172
  return { systemPrompt: rewritten };
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
+ import { APP_NAME } from "@bastani/atomic";
5
6
 
6
7
  export interface WorktreeSetup {
7
8
  cwd: string;
@@ -149,11 +150,11 @@ function safePatchAgentName(agent: string): string {
149
150
  }
150
151
 
151
152
  function buildWorktreeBranch(runId: string, index: number): string {
152
- return `pi-parallel-${runId}-${index}`;
153
+ return `${APP_NAME}-parallel-${runId}-${index}`;
153
154
  }
154
155
 
155
156
  function buildWorktreePath(runId: string, index: number): string {
156
- return path.join(os.tmpdir(), `pi-worktree-${runId}-${index}`);
157
+ return path.join(os.tmpdir(), `${APP_NAME}-worktree-${runId}-${index}`);
157
158
  }
158
159
 
159
160
  function resolveRepoCwdRelative(cwd: string): string {
@@ -1,5 +1,4 @@
1
1
  import * as fs from "node:fs";
2
- import * as os from "node:os";
3
2
  import * as path from "node:path";
4
3
  import { getAgentConfigPaths } from "@bastani/atomic";
5
4
  import { TEMP_ARTIFACTS_DIR, type ArtifactPaths } from "./types.ts";
@@ -88,6 +88,8 @@ export interface ControlEvent {
88
88
  agent: string;
89
89
  index?: number;
90
90
  runId: string;
91
+ nestedRunId?: string;
92
+ nestingPath?: NestedRunAddress["path"];
91
93
  message: string;
92
94
  reason?: "idle" | "completion_guard" | "active_long_running" | "tool_failures" | "time_threshold" | "turn_threshold" | "token_threshold";
93
95
  turns?: number;
@@ -103,6 +105,21 @@ export interface ControlEvent {
103
105
  export type SubagentResultStatus = "completed" | "failed" | "paused" | "detached";
104
106
  export type SubagentRunMode = "single" | "parallel" | "chain";
105
107
 
108
+ export type PublicNestedStepSummary = Pick<
109
+ NestedStepSummary,
110
+ "agent" | "status" | "sessionFile" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "startedAt" | "endedAt" | "error"
111
+ > & {
112
+ children?: PublicNestedRunSummary[];
113
+ };
114
+
115
+ export type PublicNestedRunSummary = Pick<
116
+ NestedRunSummary,
117
+ "id" | "parentRunId" | "parentStepIndex" | "parentAgent" | "depth" | "path" | "asyncDir" | "sessionId" | "sessionFile" | "intercomTarget" | "ownerIntercomTarget" | "leafIntercomTarget" | "ownerState" | "mode" | "state" | "agent" | "agents" | "currentStep" | "chainStepCount" | "parallelGroups" | "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount" | "totalTokens" | "startedAt" | "endedAt" | "lastUpdate" | "error"
118
+ > & {
119
+ steps?: PublicNestedStepSummary[];
120
+ children?: PublicNestedRunSummary[];
121
+ };
122
+
106
123
  export interface SubagentResultIntercomChild {
107
124
  agent: string;
108
125
  status: SubagentResultStatus;
@@ -111,6 +128,7 @@ export interface SubagentResultIntercomChild {
111
128
  artifactPath?: string;
112
129
  sessionPath?: string;
113
130
  intercomTarget?: string;
131
+ children?: PublicNestedRunSummary[];
114
132
  }
115
133
 
116
134
  export interface SubagentResultIntercomPayload {
@@ -266,6 +284,76 @@ export interface AsyncParallelGroupStatus {
266
284
  stepIndex: number;
267
285
  }
268
286
 
287
+ export type NestedRunState = "queued" | "running" | "complete" | "failed" | "paused";
288
+ export type NestedOwnerState = "live" | "gone" | "unknown";
289
+
290
+ export interface NestedRunAddress {
291
+ id: string;
292
+ parentRunId: string;
293
+ parentStepIndex?: number;
294
+ parentAgent?: string;
295
+ depth: number;
296
+ path: Array<{ runId: string; stepIndex?: number; agent?: string }>;
297
+ }
298
+
299
+ export interface NestedStepSummary {
300
+ agent: string;
301
+ status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
302
+ sessionFile?: string;
303
+ activityState?: ActivityState;
304
+ lastActivityAt?: number;
305
+ currentTool?: string;
306
+ currentToolStartedAt?: number;
307
+ currentPath?: string;
308
+ turnCount?: number;
309
+ toolCount?: number;
310
+ startedAt?: number;
311
+ endedAt?: number;
312
+ error?: string;
313
+ children?: NestedRunSummary[];
314
+ }
315
+
316
+ export interface NestedRunSummary extends NestedRunAddress {
317
+ asyncDir?: string;
318
+ pid?: number;
319
+ sessionId?: string;
320
+ sessionFile?: string;
321
+ intercomTarget?: string;
322
+ ownerIntercomTarget?: string;
323
+ leafIntercomTarget?: string;
324
+ ownerState?: NestedOwnerState;
325
+ controlInbox?: string;
326
+ capabilityToken?: string;
327
+ mode?: SubagentRunMode;
328
+ state: NestedRunState;
329
+ agent?: string;
330
+ agents?: string[];
331
+ currentStep?: number;
332
+ chainStepCount?: number;
333
+ parallelGroups?: AsyncParallelGroupStatus[];
334
+ steps?: NestedStepSummary[];
335
+ children?: NestedRunSummary[];
336
+ activityState?: ActivityState;
337
+ lastActivityAt?: number;
338
+ currentTool?: string;
339
+ currentToolStartedAt?: number;
340
+ currentPath?: string;
341
+ turnCount?: number;
342
+ toolCount?: number;
343
+ totalTokens?: TokenUsage;
344
+ startedAt?: number;
345
+ endedAt?: number;
346
+ lastUpdate?: number;
347
+ error?: string;
348
+ }
349
+
350
+ export interface NestedRouteInfo {
351
+ rootRunId: string;
352
+ eventSink: string;
353
+ controlInbox: string;
354
+ capabilityToken: string;
355
+ }
356
+
269
357
  export interface AsyncStartedEvent {
270
358
  id?: string;
271
359
  asyncDir?: string;
@@ -277,6 +365,7 @@ export interface AsyncStartedEvent {
277
365
  chain?: string[];
278
366
  chainStepCount?: number;
279
367
  parallelGroups?: AsyncParallelGroupStatus[];
368
+ nestedRoute?: NestedRouteInfo;
280
369
  }
281
370
 
282
371
  export interface AsyncStatus {
@@ -302,6 +391,7 @@ export interface AsyncStatus {
302
391
  steps?: Array<{
303
392
  agent: string;
304
393
  status: "pending" | "running" | "complete" | "completed" | "failed" | "paused";
394
+ children?: NestedRunSummary[];
305
395
  sessionFile?: string;
306
396
  activityState?: ActivityState;
307
397
  lastActivityAt?: number;
@@ -366,6 +456,8 @@ export interface AsyncJobState {
366
456
  totalTokens?: TokenUsage;
367
457
  sessionFile?: string;
368
458
  controlEventCursor?: number;
459
+ nestedRoute?: NestedRouteInfo;
460
+ nestedChildren?: NestedRunSummary[];
369
461
  }
370
462
 
371
463
  export interface ForegroundResumeChild {
@@ -403,6 +495,8 @@ export interface SubagentState {
403
495
  turnCount?: number;
404
496
  tokens?: number;
405
497
  toolCount?: number;
498
+ nestedRoute?: NestedRouteInfo;
499
+ nestedChildren?: NestedRunSummary[];
406
500
  interrupt?: () => boolean;
407
501
  }>;
408
502
  lastForegroundControlId: string | null;
@@ -478,6 +572,7 @@ export interface RunSyncOptions {
478
572
  outputPath?: string;
479
573
  outputMode?: OutputMode;
480
574
  maxSubagentDepth?: number;
575
+ nestedRoute?: NestedRouteInfo;
481
576
  /** Override the agent's default model (format: "provider/id" or just "id") */
482
577
  modelOverride?: string;
483
578
  /** Registry models available for heuristic bare-model resolution */
@@ -587,7 +682,7 @@ export function resolveTempScopeId(options?: {
587
682
 
588
683
  const MAX_PARALLEL = 8;
589
684
  export const MAX_CONCURRENCY = 4;
590
- export const TEMP_ROOT_DIR = path.join(os.tmpdir(), `pi-subagents-${resolveTempScopeId()}`);
685
+ export const TEMP_ROOT_DIR = path.join(os.tmpdir(), `${APP_NAME}-subagents-${resolveTempScopeId()}`);
591
686
  export const RESULTS_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-results");
592
687
  export const ASYNC_DIR = path.join(TEMP_ROOT_DIR, "async-subagent-runs");
593
688
  export const CHAIN_RUNS_DIR = path.join(TEMP_ROOT_DIR, "chain-runs");