@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,10 +2,12 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
4
4
  import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
5
- import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type SubagentRunMode, type TokenUsage } from "../../shared/types.ts";
5
+ import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type NestedRunSummary, type SubagentRunMode, type TokenUsage } from "../../shared/types.ts";
6
6
  import { readStatus } from "../../shared/utils.ts";
7
+ import { attachRootChildrenToSteps, findNestedRouteForRootId, projectNestedRegistryForRoot } from "../shared/nested-events.ts";
8
+ import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
7
9
  import { flatToLogicalStepIndex, normalizeParallelGroups } from "./parallel-groups.ts";
8
- import { reconcileAsyncRun } from "./stale-run-reconciler.ts";
10
+ import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
9
11
 
10
12
  interface AsyncRunStepSummary {
11
13
  index: number;
@@ -28,6 +30,7 @@ interface AsyncRunStepSummary {
28
30
  thinking?: string;
29
31
  attemptedModels?: string[];
30
32
  error?: string;
33
+ children?: NestedRunSummary[];
31
34
  }
32
35
 
33
36
  export interface AsyncRunSummary {
@@ -55,6 +58,8 @@ export interface AsyncRunSummary {
55
58
  outputFile?: string;
56
59
  totalTokens?: TokenUsage;
57
60
  sessionFile?: string;
61
+ nestedChildren?: NestedRunSummary[];
62
+ nestedWarnings?: string[];
58
63
  }
59
64
 
60
65
  interface AsyncRunListOptions {
@@ -112,7 +117,7 @@ function deriveAsyncActivityState(asyncDir: string, status: AsyncStatus): { acti
112
117
  };
113
118
  }
114
119
 
115
- function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string }): AsyncRunSummary {
120
+ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string }, nestedWarnings: string[] = []): AsyncRunSummary {
116
121
  if (status.sessionId !== undefined && typeof status.sessionId !== "string") {
117
122
  throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': sessionId must be a string.`);
118
123
  }
@@ -120,6 +125,42 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
120
125
  const steps = status.steps ?? [];
121
126
  const chainStepCount = status.chainStepCount ?? steps.length;
122
127
  const parallelGroups = normalizeParallelGroups(status.parallelGroups, steps.length, chainStepCount);
128
+ let nestedChildren: NestedRunSummary[] = [];
129
+ if (nestedWarnings.length === 0) {
130
+ try {
131
+ nestedChildren = projectNestedRegistryForRoot(status.runId || path.basename(asyncDir))?.children ?? [];
132
+ } catch (error) {
133
+ nestedWarnings.push(`Nested status unavailable: ${getErrorMessage(error)}`);
134
+ }
135
+ }
136
+ const summarizedSteps = steps.map((step, index) => {
137
+ const stepActivityState = step.activityState;
138
+ const stepLastActivityAt = step.lastActivityAt;
139
+ return {
140
+ index,
141
+ agent: step.agent,
142
+ status: step.status,
143
+ ...(stepActivityState ? { activityState: stepActivityState } : {}),
144
+ ...(stepLastActivityAt ? { lastActivityAt: stepLastActivityAt } : {}),
145
+ ...(step.currentTool ? { currentTool: step.currentTool } : {}),
146
+ ...(step.currentToolArgs ? { currentToolArgs: step.currentToolArgs } : {}),
147
+ ...(step.currentToolStartedAt ? { currentToolStartedAt: step.currentToolStartedAt } : {}),
148
+ ...(step.currentPath ? { currentPath: step.currentPath } : {}),
149
+ ...(step.recentTools ? { recentTools: step.recentTools.map((tool) => ({ ...tool })) } : {}),
150
+ ...(step.recentOutput ? { recentOutput: [...step.recentOutput] } : {}),
151
+ ...(step.turnCount !== undefined ? { turnCount: step.turnCount } : {}),
152
+ ...(step.toolCount !== undefined ? { toolCount: step.toolCount } : {}),
153
+ ...(step.durationMs !== undefined ? { durationMs: step.durationMs } : {}),
154
+ ...(step.tokens ? { tokens: step.tokens } : {}),
155
+ ...(step.skills ? { skills: step.skills } : {}),
156
+ ...(step.model ? { model: step.model } : {}),
157
+ ...(step.thinking ? { thinking: step.thinking } : {}),
158
+ ...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
159
+ ...(step.error ? { error: step.error } : {}),
160
+ ...(step.children?.length ? { children: step.children } : {}),
161
+ };
162
+ });
163
+ attachRootChildrenToSteps(status.runId || path.basename(asyncDir), summarizedSteps, nestedChildren);
123
164
  return {
124
165
  id: status.runId || path.basename(asyncDir),
125
166
  asyncDir,
@@ -140,32 +181,9 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
140
181
  currentStep: status.currentStep,
141
182
  ...(status.chainStepCount !== undefined ? { chainStepCount: status.chainStepCount } : {}),
142
183
  ...(parallelGroups.length ? { parallelGroups } : {}),
143
- steps: steps.map((step, index) => {
144
- const stepActivityState = step.activityState;
145
- const stepLastActivityAt = step.lastActivityAt;
146
- return {
147
- index,
148
- agent: step.agent,
149
- status: step.status,
150
- ...(stepActivityState ? { activityState: stepActivityState } : {}),
151
- ...(stepLastActivityAt ? { lastActivityAt: stepLastActivityAt } : {}),
152
- ...(step.currentTool ? { currentTool: step.currentTool } : {}),
153
- ...(step.currentToolArgs ? { currentToolArgs: step.currentToolArgs } : {}),
154
- ...(step.currentToolStartedAt ? { currentToolStartedAt: step.currentToolStartedAt } : {}),
155
- ...(step.currentPath ? { currentPath: step.currentPath } : {}),
156
- ...(step.recentTools ? { recentTools: step.recentTools.map((tool) => ({ ...tool })) } : {}),
157
- ...(step.recentOutput ? { recentOutput: [...step.recentOutput] } : {}),
158
- ...(step.turnCount !== undefined ? { turnCount: step.turnCount } : {}),
159
- ...(step.toolCount !== undefined ? { toolCount: step.toolCount } : {}),
160
- ...(step.durationMs !== undefined ? { durationMs: step.durationMs } : {}),
161
- ...(step.tokens ? { tokens: step.tokens } : {}),
162
- ...(step.skills ? { skills: step.skills } : {}),
163
- ...(step.model ? { model: step.model } : {}),
164
- ...(step.thinking ? { thinking: step.thinking } : {}),
165
- ...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
166
- ...(step.error ? { error: step.error } : {}),
167
- };
168
- }),
184
+ steps: summarizedSteps,
185
+ ...(nestedChildren.length ? { nestedChildren } : {}),
186
+ ...(nestedWarnings.length ? { nestedWarnings } : {}),
169
187
  ...(status.sessionDir ? { sessionDir: status.sessionDir } : {}),
170
188
  ...(status.outputFile ? { outputFile: status.outputFile } : {}),
171
189
  ...(status.totalTokens ? { totalTokens: status.totalTokens } : {}),
@@ -212,7 +230,14 @@ export function listAsyncRuns(asyncDirRoot: string, options: AsyncRunListOptions
212
230
  : reconcileAsyncRun(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now });
213
231
  const status = (reconciliation?.status ?? readStatus(asyncDir)) as (AsyncStatus & { cwd?: string }) | null;
214
232
  if (!status) continue;
215
- const summary = statusToSummary(asyncDir, status);
233
+ const nestedWarnings: string[] = [];
234
+ try {
235
+ const nestedRoute = findNestedRouteForRootId(status.runId || path.basename(asyncDir));
236
+ if (nestedRoute) reconcileNestedAsyncDescendants(nestedRoute, { resultsDir: options.resultsDir, kill: options.kill, now: options.now });
237
+ } catch (error) {
238
+ nestedWarnings.push(`Nested status unavailable: ${getErrorMessage(error)}`);
239
+ }
240
+ const summary = statusToSummary(asyncDir, status, nestedWarnings);
216
241
  if (allowedStates && !allowedStates.has(summary.state)) continue;
217
242
  if (options.sessionId && summary.sessionId !== options.sessionId) continue;
218
243
  runs.push(summary);
@@ -285,7 +310,12 @@ export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active as
285
310
  lines.push(`- ${formatRunHeader(run)}`);
286
311
  for (const step of run.steps) {
287
312
  lines.push(` ${formatStepLine(step)}`);
313
+ lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", maxLines: 12 }));
288
314
  }
315
+ const attached = new Set(run.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
316
+ const unattached = run.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
317
+ lines.push(...formatNestedRunStatusLines(unattached, { indent: " ", maxLines: 12 }));
318
+ for (const warning of run.nestedWarnings ?? []) lines.push(` Warning: ${warning}`);
289
319
  const outputPath = formatAsyncRunOutputPath(run);
290
320
  if (outputPath) lines.push(` output: ${shortenPath(outputPath)}`);
291
321
  if (run.sessionFile) lines.push(` session: ${shortenPath(run.sessionFile)}`);
@@ -5,13 +5,18 @@ import { createFileCoalescer } from "../../shared/file-coalescer.ts";
5
5
  import {
6
6
  SUBAGENT_ASYNC_COMPLETE_EVENT,
7
7
  type IntercomEventBus,
8
+ type NestedRunSummary,
9
+ type SubagentResultIntercomChild,
8
10
  type SubagentState,
9
11
  } from "../../shared/types.ts";
10
12
  import {
13
+ attachNestedChildrenToResultChildren,
11
14
  buildSubagentResultIntercomPayload,
15
+ compactNestedResultChildren,
12
16
  deliverSubagentResultIntercomEvent,
13
17
  resolveSubagentResultStatus,
14
18
  } from "../../intercom/result-intercom.ts";
19
+ import { projectNestedRegistryForRoot, sanitizeSummary } from "../shared/nested-events.ts";
15
20
 
16
21
  const WATCHER_RESTART_DELAY_MS = 3000;
17
22
  const POLL_INTERVAL_MS = 3000;
@@ -30,6 +35,47 @@ type ResultWatcherDeps = {
30
35
  timers?: ResultWatcherTimers;
31
36
  };
32
37
 
38
+ type ResultFileChild = {
39
+ agent?: string;
40
+ output?: string;
41
+ error?: string;
42
+ success?: boolean;
43
+ sessionFile?: string;
44
+ artifactPaths?: { outputPath?: string };
45
+ intercomTarget?: string;
46
+ children?: unknown;
47
+ };
48
+
49
+ type ResultFileData = {
50
+ id?: string;
51
+ runId?: string;
52
+ agent?: string;
53
+ success?: boolean;
54
+ state?: string;
55
+ mode?: string;
56
+ summary?: string;
57
+ results?: ResultFileChild[];
58
+ nestedChildren?: unknown;
59
+ sessionId?: string;
60
+ cwd?: string;
61
+ sessionFile?: string;
62
+ asyncDir?: string;
63
+ intercomTarget?: string;
64
+ };
65
+
66
+ function sanitizeNestedResultChildren(value: unknown, resultPath: string, label: string): NestedRunSummary[] | undefined {
67
+ if (value === undefined) return undefined;
68
+ if (!Array.isArray(value)) {
69
+ console.error(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
70
+ return undefined;
71
+ }
72
+ const children = value.map((child) => sanitizeSummary(child)).filter((child): child is NestedRunSummary => Boolean(child));
73
+ if (children.length !== value.length) {
74
+ console.error(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`);
75
+ }
76
+ return children.length ? children : undefined;
77
+ }
78
+
33
79
  function getErrorCode(error: unknown): string | undefined {
34
80
  return typeof error === "object" && error !== null && "code" in error
35
81
  ? (error as NodeJS.ErrnoException).code
@@ -63,32 +109,21 @@ export function createResultWatcher(
63
109
  const resultPath = path.join(resultsDir, file);
64
110
  if (!fsApi.existsSync(resultPath)) return;
65
111
  try {
66
- const data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8")) as {
67
- id?: string;
68
- runId?: string;
69
- agent?: string;
70
- success?: boolean;
71
- state?: string;
72
- mode?: string;
73
- summary?: string;
74
- results?: Array<{
75
- agent?: string;
76
- output?: string;
77
- error?: string;
78
- success?: boolean;
79
- sessionFile?: string;
80
- artifactPaths?: { outputPath?: string };
81
- intercomTarget?: string;
82
- }>;
83
- sessionId?: string;
84
- cwd?: string;
85
- sessionFile?: string;
86
- asyncDir?: string;
87
- intercomTarget?: string;
88
- };
112
+ const data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8")) as ResultFileData;
89
113
  if (data.sessionId && data.sessionId !== state.currentSessionId) return;
90
114
  if (!data.sessionId && data.cwd && (!state.baseCwd || data.cwd !== state.baseCwd)) return;
91
115
 
116
+ const runId = data.runId ?? data.id ?? file.replace(/\.json$/i, "");
117
+ const hasExplicitNestedChildren = data.nestedChildren !== undefined;
118
+ let nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren"));
119
+ if (!nestedChildren?.length && !hasExplicitNestedChildren) {
120
+ try {
121
+ nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
122
+ } catch (error) {
123
+ console.error(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
124
+ return;
125
+ }
126
+ }
92
127
  const now = Date.now();
93
128
  const completionKey = buildCompletionKey(data, `result:${file}`);
94
129
  if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
@@ -96,45 +131,49 @@ export function createResultWatcher(
96
131
  return;
97
132
  }
98
133
 
134
+ const hasResultChildren = Array.isArray(data.results) && data.results.length > 0;
135
+ const resultChildren = hasResultChildren
136
+ ? data.results!
137
+ : [{
138
+ agent: data.agent,
139
+ output: data.summary,
140
+ success: data.success,
141
+ }];
142
+ const normalizedChildren = attachNestedChildrenToResultChildren(runId, resultChildren.map((result = {}, index): SubagentResultIntercomChild => {
143
+ const baseOutput = result.output ?? data.summary;
144
+ const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
145
+ const output = hasRealOutput ? baseOutput : "(no output)";
146
+ const summary = result.success === false && result.error
147
+ ? `${result.error}${hasRealOutput ? `\n\nOutput:\n${baseOutput}` : ""}`
148
+ : output;
149
+ const sessionPath = result.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : undefined);
150
+ const childNestedChildren = sanitizeNestedResultChildren(result.children, resultPath, `results[${index}].children`);
151
+ return {
152
+ agent: result.agent ?? data.agent ?? `step-${index + 1}`,
153
+ status: resolveSubagentResultStatus({
154
+ success: result.success,
155
+ state: data.state === "paused" || typeof result.success !== "boolean" ? data.state : undefined,
156
+ }),
157
+ summary,
158
+ index,
159
+ artifactPath: result.artifactPaths?.outputPath,
160
+ ...(typeof sessionPath === "string" && fsApi.existsSync(sessionPath) ? { sessionPath } : {}),
161
+ ...(result.intercomTarget ? { intercomTarget: result.intercomTarget } : {}),
162
+ ...(childNestedChildren ? { children: childNestedChildren } : {}),
163
+ };
164
+ }), nestedChildren);
165
+
99
166
  const intercomTarget = data.intercomTarget?.trim();
100
167
  if (intercomTarget) {
101
- const childResults = Array.isArray(data.results) && data.results.length > 0
102
- ? data.results
103
- : [{
104
- agent: data.agent,
105
- output: data.summary,
106
- success: data.success,
107
- }];
108
- const runId = data.runId ?? data.id ?? file.replace(/\.json$/i, "");
109
168
  const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
110
169
  ? data.mode
111
- : childResults.length > 1 ? "chain" : "single";
170
+ : resultChildren.length > 1 ? "chain" : "single";
112
171
  const payload = buildSubagentResultIntercomPayload({
113
172
  to: intercomTarget,
114
173
  runId,
115
174
  mode,
116
175
  source: "async",
117
- children: childResults.map((result = {}, index) => {
118
- const baseOutput = result.output ?? data.summary;
119
- const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
120
- const output = hasRealOutput ? baseOutput : "(no output)";
121
- const summary = result.success === false && result.error
122
- ? `${result.error}${hasRealOutput ? `\n\nOutput:\n${baseOutput}` : ""}`
123
- : output;
124
- const sessionPath = result.sessionFile ?? (childResults.length === 1 ? data.sessionFile : undefined);
125
- return {
126
- agent: result.agent ?? data.agent ?? `step-${index + 1}`,
127
- status: resolveSubagentResultStatus({
128
- success: result.success,
129
- state: data.state === "paused" || typeof result.success !== "boolean" ? data.state : undefined,
130
- }),
131
- summary,
132
- index,
133
- artifactPath: result.artifactPaths?.outputPath,
134
- ...(typeof sessionPath === "string" && fsApi.existsSync(sessionPath) ? { sessionPath } : {}),
135
- intercomTarget: result.intercomTarget,
136
- };
137
- }),
176
+ children: normalizedChildren,
138
177
  asyncId: data.id,
139
178
  asyncDir: data.asyncDir,
140
179
  });
@@ -144,7 +183,25 @@ export function createResultWatcher(
144
183
  }
145
184
  }
146
185
 
147
- pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, data);
186
+ pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
187
+ ...data,
188
+ runId,
189
+ ...(nestedChildren?.length ? { nestedChildren } : {}),
190
+ ...(Array.isArray(data.results) ? {
191
+ results: hasResultChildren
192
+ ? normalizedChildren.map((child, index) => ({
193
+ ...data.results![index],
194
+ agent: child.agent,
195
+ status: child.status,
196
+ summary: child.summary,
197
+ index: child.index,
198
+ artifactPath: child.artifactPath,
199
+ sessionPath: child.sessionPath,
200
+ children: child.children,
201
+ }))
202
+ : [],
203
+ } : {}),
204
+ });
148
205
  fsApi.unlinkSync(resultPath);
149
206
  } catch (error) {
150
207
  if (isNotFoundError(error)) return;
@@ -0,0 +1,83 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { ASYNC_DIR, RESULTS_DIR, type SubagentState } from "../../shared/types.ts";
4
+ import { findAsyncRunPrefixMatches, type AsyncRunLocation } from "./async-resume.ts";
5
+ import { assertSafeNestedId, findNestedRunMatchesById, type NestedRoute, type NestedRunMatch, type NestedRunResolutionScope } from "../shared/nested-events.ts";
6
+
7
+ export type ResolvedSubagentRunId =
8
+ | { kind: "foreground"; id: string }
9
+ | { kind: "async"; id: string; location: AsyncRunLocation }
10
+ | { kind: "nested"; id: string; match: NestedRunMatch };
11
+
12
+ export interface ResolveSubagentRunIdDeps {
13
+ state?: SubagentState;
14
+ asyncDirRoot?: string;
15
+ resultsDir?: string;
16
+ nested?: NestedRunResolutionScope;
17
+ }
18
+
19
+ function exactAsyncLocation(id: string, asyncDirRoot: string, resultsDir: string): AsyncRunLocation | undefined {
20
+ const asyncDir = path.join(asyncDirRoot, id);
21
+ const resultPath = path.join(resultsDir, `${id}.json`);
22
+ if (!fs.existsSync(asyncDir) && !fs.existsSync(resultPath)) return undefined;
23
+ return {
24
+ asyncDir: fs.existsSync(asyncDir) ? asyncDir : null,
25
+ resultPath: fs.existsSync(resultPath) ? resultPath : null,
26
+ resolvedId: id,
27
+ };
28
+ }
29
+
30
+ function foregroundIds(state: SubagentState | undefined): string[] {
31
+ return state ? [...state.foregroundControls.keys()] : [];
32
+ }
33
+
34
+ export function nestedScopeFromState(state: SubagentState | undefined): NestedRunResolutionScope | undefined {
35
+ if (!state) return undefined;
36
+ const routes: NestedRoute[] = [];
37
+ const seen = new Set<string>();
38
+ const add = (route: NestedRoute | undefined) => {
39
+ if (!route) return;
40
+ const key = `${route.rootRunId}:${route.eventSink}:${route.controlInbox}`;
41
+ if (seen.has(key)) return;
42
+ seen.add(key);
43
+ routes.push(route);
44
+ };
45
+ for (const control of state.foregroundControls.values()) add(control.nestedRoute as NestedRoute | undefined);
46
+ for (const job of state.asyncJobs.values()) add(job.nestedRoute as NestedRoute | undefined);
47
+ return { routes };
48
+ }
49
+
50
+ function asyncPrefixMatches(prefix: string, asyncDirRoot: string, resultsDir: string): Array<{ id: string; location: AsyncRunLocation }> {
51
+ return findAsyncRunPrefixMatches(prefix, asyncDirRoot, resultsDir);
52
+ }
53
+
54
+ export function resolveSubagentRunId(id: string, deps: ResolveSubagentRunIdDeps = {}): ResolvedSubagentRunId | undefined {
55
+ assertSafeNestedId("id", id);
56
+ const asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR;
57
+ const resultsDir = deps.resultsDir ?? RESULTS_DIR;
58
+
59
+ const nestedScope = deps.nested ?? nestedScopeFromState(deps.state);
60
+ if (deps.state?.foregroundControls.has(id)) return { kind: "foreground", id };
61
+ const exactAsync = exactAsyncLocation(id, asyncDirRoot, resultsDir);
62
+ if (exactAsync) return { kind: "async", id, location: exactAsync };
63
+ const exactNested = findNestedRunMatchesById(id, nestedScope ? { scope: nestedScope } : {});
64
+ if (exactNested.length > 1) throw new Error(`Nested run id '${id}' is ambiguous across authorized registries. Provide a more specific authorized run id.`);
65
+ if (exactNested[0]) return { kind: "nested", id, match: exactNested[0] };
66
+
67
+ const matches: ResolvedSubagentRunId[] = [];
68
+ for (const foregroundId of foregroundIds(deps.state).filter((candidate) => candidate.startsWith(id))) {
69
+ matches.push({ kind: "foreground", id: foregroundId });
70
+ }
71
+ for (const match of asyncPrefixMatches(id, asyncDirRoot, resultsDir)) {
72
+ matches.push({ kind: "async", id: match.id, location: match.location });
73
+ }
74
+ for (const match of findNestedRunMatchesById(id, nestedScope ? { prefix: true, scope: nestedScope } : { prefix: true })) {
75
+ matches.push({ kind: "nested", id: match.run.id, match });
76
+ }
77
+ const unique = new Map(matches.map((match) => [`${match.kind}:${match.id}`, match]));
78
+ const values = [...unique.values()];
79
+ if (values.length > 1) {
80
+ throw new Error(`Ambiguous subagent run id prefix '${id}' matched: ${values.map((match) => `${match.kind}:${match.id}`).join(", ")}. Provide a longer id.`);
81
+ }
82
+ return values[0];
83
+ }
@@ -2,13 +2,16 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
4
  import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.ts";
5
+ import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
5
6
  import { formatModelThinking } from "../../shared/formatters.ts";
6
7
  import { formatActivityLabel } from "../../shared/status-format.ts";
7
- import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details } from "../../shared/types.ts";
8
+ import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details, type NestedRunSummary, type SubagentState } from "../../shared/types.ts";
8
9
  import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
9
10
  import { resolveAsyncRunLocation } from "./async-resume.ts";
11
+ import { resolveSubagentRunId } from "./run-id-resolver.ts";
10
12
  import { flatToLogicalStepIndex, normalizeParallelGroups } from "./parallel-groups.ts";
11
- import { reconcileAsyncRun } from "./stale-run-reconciler.ts";
13
+ import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
14
+ import { attachRootChildrenToSteps, findNestedRouteForRootId, projectNestedRegistryForRoot, type NestedRunResolutionScope } from "../shared/nested-events.ts";
12
15
 
13
16
  interface RunStatusParams {
14
17
  action?: "status";
@@ -22,6 +25,8 @@ interface RunStatusDeps {
22
25
  resultsDir?: string;
23
26
  kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
24
27
  now?: () => number;
28
+ state?: SubagentState;
29
+ nested?: NestedRunResolutionScope;
25
30
  }
26
31
 
27
32
  function hasExistingSessionFile(value: unknown): value is string {
@@ -57,10 +62,53 @@ function stepLineLabel(status: AsyncStatus, index: number): string {
57
62
  return `Step ${index + 1}`;
58
63
  }
59
64
 
65
+ function nestedRunDisplayName(run: NestedRunSummary): string {
66
+ if (run.agent) return run.agent;
67
+ if (run.agents?.length) return run.agents.join(", ");
68
+ return run.id;
69
+ }
70
+
71
+ function formatNestedExactStatus(rootRunId: string, run: NestedRunSummary): string {
72
+ const lines = [
73
+ `Nested run: ${run.id}`,
74
+ `Root: ${rootRunId}`,
75
+ `Parent: ${run.parentRunId}${run.parentStepIndex !== undefined ? ` step ${run.parentStepIndex + 1}` : ""}`,
76
+ `State: ${run.state}`,
77
+ run.activityState || run.lastActivityAt ? `Activity: ${formatActivityLabel(run.lastActivityAt, run.activityState)}` : undefined,
78
+ run.mode ? `Mode: ${run.mode}` : undefined,
79
+ `Agent: ${nestedRunDisplayName(run)}`,
80
+ run.currentStep !== undefined ? `Progress: step ${run.currentStep + 1}/${run.chainStepCount ?? run.steps?.length ?? 1}` : undefined,
81
+ run.asyncDir ? `Dir: ${run.asyncDir}` : undefined,
82
+ run.sessionFile ? `Session: ${run.sessionFile}` : undefined,
83
+ run.error ? `Error: ${run.error}` : undefined,
84
+ ].filter((line): line is string => Boolean(line));
85
+ if (run.path.length) {
86
+ lines.push(`Path: ${run.path.map((part) => `${part.runId}${part.stepIndex !== undefined ? `:${part.stepIndex + 1}` : ""}${part.agent ? `:${part.agent}` : ""}`).join(" > ")} > ${run.id}`);
87
+ }
88
+ if (run.steps?.length) {
89
+ lines.push("Steps:");
90
+ for (const [index, step] of run.steps.entries()) {
91
+ const activity = step.status === "running" ? formatActivityLabel(step.lastActivityAt, step.activityState) : undefined;
92
+ lines.push(` ${index + 1}. ${step.agent} ${step.status}${activity ? `, ${activity}` : ""}${step.error ? `, error: ${step.error}` : ""}`);
93
+ lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true }));
94
+ }
95
+ }
96
+ lines.push(...formatNestedRunStatusLines(run.children, { indent: " ", commandHints: true }));
97
+ lines.push("Commands:", ` Status: subagent({ action: "status", id: "${run.id}" })`, ` Interrupt: subagent({ action: "interrupt", id: "${run.id}" })`, ` Resume: subagent({ action: "resume", id: "${run.id}", message: "..." })`, ` Root status: subagent({ action: "status", id: "${rootRunId}" })`);
98
+ return lines.join("\n");
99
+ }
100
+
60
101
  export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDeps = {}): AgentToolResult<Details> {
61
102
  const asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR;
62
103
  const resultsDir = deps.resultsDir ?? RESULTS_DIR;
63
104
  if (!params.id && !params.runId && !params.dir) {
105
+ if (deps.nested) {
106
+ return {
107
+ content: [{ type: "text", text: "Child-safe subagent status requires an id when no foreground run is active." }],
108
+ isError: true,
109
+ details: { mode: "single", results: [] },
110
+ };
111
+ }
64
112
  try {
65
113
  const runs = listAsyncRuns(asyncDirRoot, { states: ["queued", "running"], resultsDir, kill: deps.kill, now: deps.now });
66
114
  return {
@@ -79,7 +127,20 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
79
127
 
80
128
  let location;
81
129
  try {
82
- location = resolveAsyncRunLocation(params, asyncDirRoot, resultsDir);
130
+ const requestedId = params.id ?? params.runId;
131
+ if (!params.dir && requestedId) {
132
+ const resolved = resolveSubagentRunId(requestedId, { asyncDirRoot, resultsDir, state: deps.state, nested: deps.nested });
133
+ if (resolved?.kind === "nested") {
134
+ reconcileNestedAsyncDescendants(resolved.match.route, { resultsDir, kill: deps.kill, now: deps.now });
135
+ const refreshed = resolveSubagentRunId(requestedId, { asyncDirRoot, resultsDir, state: deps.state, nested: deps.nested });
136
+ const nested = refreshed?.kind === "nested" ? refreshed : resolved;
137
+ return { content: [{ type: "text", text: formatNestedExactStatus(nested.match.rootRunId, nested.match.run) }], details: { mode: "single", results: [] } };
138
+ }
139
+ if (resolved?.kind === "async") location = resolved.location;
140
+ else location = { asyncDir: null, resultPath: null, resolvedId: requestedId };
141
+ } else {
142
+ location = resolveAsyncRunLocation(params, asyncDirRoot, resultsDir);
143
+ }
83
144
  } catch (error) {
84
145
  const message = error instanceof Error ? error.message : String(error);
85
146
  return {
@@ -115,6 +176,16 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
115
176
  const logPath = path.join(asyncDir, `subagent-log-${effectiveRunId}.md`);
116
177
  const eventsPath = path.join(asyncDir, "events.jsonl");
117
178
  if (status) {
179
+ let nestedChildren: NestedRunSummary[] = [];
180
+ let nestedWarning: string | undefined;
181
+ try {
182
+ const nestedRoute = findNestedRouteForRootId(status.runId);
183
+ if (nestedRoute) reconcileNestedAsyncDescendants(nestedRoute, { resultsDir, kill: deps.kill, now: deps.now });
184
+ nestedChildren = projectNestedRegistryForRoot(status.runId)?.children ?? [];
185
+ attachRootChildrenToSteps(status.runId, status.steps, nestedChildren);
186
+ } catch (error) {
187
+ nestedWarning = `Nested status unavailable: ${error instanceof Error ? error.message : String(error)}`;
188
+ }
118
189
  const outputPath = formatAsyncRunOutputPath({ asyncDir, outputFile: status.outputFile });
119
190
  const progressLabel = formatAsyncRunProgressLabel({
120
191
  mode: status.mode,
@@ -147,12 +218,17 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
147
218
  const modelText = modelThinking ? ` (${modelThinking})` : "";
148
219
  const errorText = step.error ? `, error: ${step.error}` : "";
149
220
  lines.push(`${stepLineLabel(status, index)}: ${step.agent} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${errorText}`);
221
+ lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
150
222
  const stepOutputPath = path.join(asyncDir, `output-${index}.log`);
151
223
  if (stepOutputPath !== outputPath && fs.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
152
224
  if (step.status === "running") {
153
225
  lines.push(` Intercom target: ${resolveSubagentIntercomTarget(status.runId, step.agent, index)} (if registered)`);
154
226
  }
155
227
  }
228
+ const attached = new Set((status.steps ?? []).flatMap((step) => step.children?.map((child) => child.id) ?? []));
229
+ const unattached = nestedChildren.filter((child) => !attached.has(child.id));
230
+ lines.push(...formatNestedRunStatusLines(unattached, { indent: "", commandHints: true, maxLines: 20 }));
231
+ if (nestedWarning) lines.push(`Warning: ${nestedWarning}`);
156
232
  if (status.sessionFile) lines.push(`Session: ${status.sessionFile}`);
157
233
  if (status.state !== "running") {
158
234
  lines.push(formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile));
@@ -1,8 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { writeAtomicJson } from "../../shared/atomic-json.ts";
4
- import { RESULTS_DIR, type AsyncParallelGroupStatus, type AsyncStatus, type SubagentRunMode } from "../../shared/types.ts";
4
+ import { RESULTS_DIR, type AsyncParallelGroupStatus, type AsyncStatus, type NestedRunSummary, type SubagentRunMode } from "../../shared/types.ts";
5
5
  import { normalizeParallelGroups } from "./parallel-groups.ts";
6
+ import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent, type NestedRoute } from "../shared/nested-events.ts";
6
7
 
7
8
  export type PidLiveness = "alive" | "dead" | "unknown";
8
9
 
@@ -233,6 +234,50 @@ function writeFailedRepair(asyncDir: string, status: AsyncStatus, resultPath: st
233
234
  return { status: repair.status, repaired: true, resultPath, message: repair.message };
234
235
  }
235
236
 
237
+ function terminal(state: AsyncStatus["state"]): boolean {
238
+ return state === "complete" || state === "failed" || state === "paused";
239
+ }
240
+
241
+ function* nestedRuns(children: NestedRunSummary[] | undefined): Generator<NestedRunSummary> {
242
+ for (const child of children ?? []) {
243
+ yield child;
244
+ yield* nestedRuns(child.children);
245
+ yield* nestedRuns(child.steps?.flatMap((step) => step.children ?? []));
246
+ }
247
+ }
248
+
249
+ export function reconcileNestedAsyncDescendants(route: NestedRoute, options: ReconcileAsyncRunOptions = {}): void {
250
+ const registry = projectNestedEvents(route);
251
+ for (const run of nestedRuns(registry.children)) {
252
+ if (run.state !== "running" && run.state !== "queued") continue;
253
+ const asyncDir = resolveNestedAsyncDir(route.rootRunId, run);
254
+ if (!asyncDir) continue;
255
+ const result = reconcileAsyncRun(asyncDir, {
256
+ ...options,
257
+ resultsDir: path.join(options.resultsDir ?? RESULTS_DIR, "nested", route.rootRunId),
258
+ });
259
+ const status = result.status;
260
+ if (!status) continue;
261
+ if (!result.repaired && !terminal(status.state)) continue;
262
+ const ts = options.now?.() ?? Date.now();
263
+ writeNestedEvent(route, {
264
+ type: terminal(status.state) ? "subagent.nested.completed" : "subagent.nested.updated",
265
+ ts,
266
+ parentRunId: run.parentRunId,
267
+ parentStepIndex: run.parentStepIndex,
268
+ child: nestedSummaryFromAsyncStatus(status, asyncDir, {
269
+ id: run.id,
270
+ parentRunId: run.parentRunId,
271
+ parentStepIndex: run.parentStepIndex,
272
+ depth: run.depth,
273
+ path: run.path,
274
+ mode: run.mode,
275
+ ts,
276
+ }),
277
+ });
278
+ }
279
+ }
280
+
236
281
  export function checkPidLiveness(pid: number, kill: KillFn = process.kill): PidLiveness {
237
282
  try {
238
283
  kill(pid, 0);