@selesai/code 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +4 -2
  3. package/dist/cli/args.d.ts +2 -0
  4. package/dist/cli/args.js +18 -0
  5. package/dist/cli/config-selector.js +2 -2
  6. package/dist/cli/startup-ui.d.ts +1 -1
  7. package/dist/cli/startup-ui.js +2 -2
  8. package/dist/core/keybindings.d.ts +44 -4
  9. package/dist/core/llama/provider.js +22 -7
  10. package/dist/core/model-resolver.js +2 -0
  11. package/dist/core/model-runtime.d.ts +4 -2
  12. package/dist/core/model-runtime.js +23 -2
  13. package/dist/core/remote-catalog-provider.js +72 -67
  14. package/dist/core/settings-manager.d.ts +8 -0
  15. package/dist/core/settings-manager.js +18 -0
  16. package/dist/extensions/handoff-new.ts +1 -1
  17. package/dist/extensions/inline-skills.test.ts +1 -0
  18. package/dist/extensions/inline-skills.ts +1 -1
  19. package/dist/extensions/pi-intercom/CHANGELOG.md +25 -3
  20. package/dist/extensions/pi-intercom/README.md +34 -15
  21. package/dist/extensions/pi-intercom/broker/broker.ts +38 -141
  22. package/dist/extensions/pi-intercom/broker/client-liveness.test.ts +173 -0
  23. package/dist/extensions/pi-intercom/broker/client.ts +78 -150
  24. package/dist/extensions/pi-intercom/broker/extension-state.ts +18 -7
  25. package/dist/extensions/pi-intercom/broker/framing.ts +1 -1
  26. package/dist/extensions/pi-intercom/broker/protocol.ts +182 -0
  27. package/dist/extensions/pi-intercom/broker/spawn.test.ts +106 -7
  28. package/dist/extensions/pi-intercom/broker/spawn.ts +37 -11
  29. package/dist/extensions/pi-intercom/config.test.ts +8 -10
  30. package/dist/extensions/pi-intercom/config.ts +3 -3
  31. package/dist/extensions/pi-intercom/index.ts +218 -147
  32. package/dist/extensions/pi-intercom/intercom.integration.test.ts +891 -103
  33. package/dist/extensions/pi-intercom/package.json +3 -2
  34. package/dist/extensions/pi-intercom/project-agent.test.ts +145 -0
  35. package/dist/extensions/pi-intercom/project-agent.ts +324 -0
  36. package/dist/extensions/pi-intercom/reply-tracker.test.ts +86 -1
  37. package/dist/extensions/pi-intercom/reply-tracker.ts +41 -9
  38. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +49 -9
  39. package/dist/extensions/pi-intercom/test/inline-message.test.ts +9 -9
  40. package/dist/extensions/pi-intercom/types.ts +3 -1
  41. package/dist/extensions/pi-intercom/ui/inline-message.ts +7 -7
  42. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +2 -2
  43. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +15 -1
  44. package/dist/extensions/pi-powerline-footer/README.md +20 -37
  45. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +40 -25
  46. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +149 -18
  47. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -0
  48. package/dist/extensions/pi-powerline-footer/editor-performance.ts +99 -0
  49. package/dist/extensions/pi-powerline-footer/git-status.ts +15 -0
  50. package/dist/extensions/pi-powerline-footer/index.ts +394 -586
  51. package/dist/extensions/pi-powerline-footer/package.json +5 -6
  52. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -18
  53. package/dist/extensions/pi-powerline-footer/queue/store.ts +8 -56
  54. package/dist/extensions/pi-powerline-footer/queue/types.ts +1 -2
  55. package/dist/extensions/pi-powerline-footer/segments.ts +0 -3
  56. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +231 -1
  57. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +5 -19
  58. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +43 -0
  59. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +2 -2
  60. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +64 -0
  61. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +1 -1
  62. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +86 -19
  63. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +19 -149
  64. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +6 -0
  65. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +5 -5
  66. package/dist/extensions/pi-powerline-footer/tsconfig.json +1 -1
  67. package/dist/extensions/pi-powerline-footer/types.ts +1 -2
  68. package/dist/extensions/pi-subagents/CHANGELOG.md +202 -14
  69. package/dist/extensions/pi-subagents/README.md +65 -1614
  70. package/dist/extensions/pi-subagents/agents/builder.md +0 -1
  71. package/dist/extensions/pi-subagents/agents/delegate.md +14 -0
  72. package/dist/extensions/pi-subagents/agents/oracle.md +74 -0
  73. package/dist/extensions/pi-subagents/agents/reviewer.md +78 -0
  74. package/dist/extensions/pi-subagents/agents/scout.md +50 -0
  75. package/dist/extensions/pi-subagents/agents/worker.md +58 -0
  76. package/dist/extensions/pi-subagents/docs/configuration.md +328 -0
  77. package/dist/extensions/pi-subagents/docs/extension-api.md +308 -0
  78. package/dist/extensions/pi-subagents/docs/missions.md +119 -0
  79. package/dist/extensions/pi-subagents/docs/models.md +192 -0
  80. package/dist/extensions/pi-subagents/docs/observability.md +174 -0
  81. package/dist/extensions/pi-subagents/docs/tool-reference.md +343 -0
  82. package/dist/extensions/pi-subagents/docs/watchdog.md +176 -0
  83. package/dist/extensions/pi-subagents/docs/workflows.md +163 -0
  84. package/dist/extensions/pi-subagents/inspector-runner.mjs +11 -0
  85. package/dist/extensions/pi-subagents/package-lock.json +152 -194
  86. package/dist/extensions/pi-subagents/package.json +9 -10
  87. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  88. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  89. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  90. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  91. package/dist/extensions/pi-subagents/prompts/review-loop.md +12 -12
  92. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -17
  93. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +77 -72
  94. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +177 -69
  95. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +28 -8
  96. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +88 -66
  97. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +3 -3
  98. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +624 -0
  99. package/dist/extensions/pi-subagents/src/agents/agents.ts +29 -16
  100. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +7 -3
  101. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  102. package/dist/extensions/pi-subagents/src/agents/skills.ts +2 -9
  103. package/dist/extensions/pi-subagents/src/api/delegation.ts +1 -2
  104. package/dist/extensions/pi-subagents/src/api/project-panes.ts +30 -0
  105. package/dist/extensions/pi-subagents/src/extension/config.ts +56 -8
  106. package/dist/extensions/pi-subagents/src/extension/control-notices.ts +2 -2
  107. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +51 -26
  108. package/dist/extensions/pi-subagents/src/extension/index.ts +119 -50
  109. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +72 -0
  110. package/dist/extensions/pi-subagents/src/extension/rpc.ts +15 -15
  111. package/dist/extensions/pi-subagents/src/extension/schemas.ts +58 -21
  112. package/dist/extensions/pi-subagents/src/extension/subagent-guide.ts +39 -0
  113. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +47 -25
  114. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +3 -3
  115. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +460 -64
  116. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +102 -4
  117. package/dist/extensions/pi-subagents/src/missions/actions.ts +77 -16
  118. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +162 -0
  119. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +65 -14
  120. package/dist/extensions/pi-subagents/src/missions/store.ts +147 -5
  121. package/dist/extensions/pi-subagents/src/missions/types.ts +58 -3
  122. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +256 -0
  123. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -8
  124. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +42 -0
  125. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +41 -7
  126. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +294 -134
  127. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +19 -3
  128. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +39 -6
  129. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +267 -0
  130. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +145 -60
  131. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +114 -6
  132. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +33 -0
  133. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +68 -0
  134. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +71 -0
  135. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +21 -2
  136. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +42 -14
  137. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +12 -24
  138. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +7 -5
  139. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +152 -68
  140. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -4
  141. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +146 -0
  142. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +21 -5
  143. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +85 -0
  144. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +43 -18
  145. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +20 -19
  146. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +3 -0
  147. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +55 -26
  148. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +137 -0
  149. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +798 -112
  150. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +187 -0
  151. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +165 -22
  152. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  153. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
  154. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +9 -5
  155. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +12 -2
  156. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +1 -0
  157. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +465 -116
  158. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +10 -10
  159. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +189 -32
  160. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +2 -2
  161. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +3 -3
  162. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +128 -3
  163. package/dist/extensions/pi-subagents/src/shared/display-text.ts +100 -0
  164. package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +9 -0
  165. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +13 -0
  166. package/dist/extensions/pi-subagents/src/shared/formatters.ts +4 -6
  167. package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +51 -0
  168. package/dist/extensions/pi-subagents/src/shared/settings.ts +41 -11
  169. package/dist/extensions/pi-subagents/src/shared/types.ts +100 -3
  170. package/dist/extensions/pi-subagents/src/shared/utf8.ts +11 -0
  171. package/dist/extensions/pi-subagents/src/shared/utils.ts +43 -33
  172. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -14
  173. package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +1 -1
  174. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +14 -12
  175. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +176 -21
  176. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +12 -6
  177. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +52 -18
  178. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +36 -5
  179. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +30 -16
  180. package/dist/extensions/pi-subagents/src/tui/fleet.ts +151 -31
  181. package/dist/extensions/pi-subagents/src/tui/render.ts +201 -68
  182. package/dist/extensions/pi-subagents/src/types/pi-runtime-compat.d.ts +0 -6
  183. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +5 -4
  184. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +0 -1
  185. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +313 -25
  186. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +15 -15
  187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
  188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
  189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
  190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
  191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
  192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
  193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
  194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
  195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
  196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
  197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
  198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
  199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
  200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
  236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
  237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
  238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
  239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
  240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
  241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
  242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
  243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
  244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
  245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
  246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
  247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
  248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
  249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
  250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
  251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
  252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
  253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
  254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
  255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
  256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
  257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
  258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
  259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
  260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
  261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
  262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
  263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
  264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
  265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
  266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
  267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
  269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
  270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
  271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
  272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
  273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
  274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
  275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
  276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
  277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
  278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
  279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
  280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
  281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
  282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
  283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
  284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
  289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
  293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
  295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
  298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
  299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
  302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
  303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
  306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
  307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
  310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
  311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
  318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
  321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
  323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
  324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
  325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
  326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
  327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
  328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
  329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
  330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
  331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
  332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
  333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
  334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
  335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
  336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
  337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
  338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
  339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
  340. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
  341. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
  342. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
  343. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
  344. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
  345. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
  346. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
  347. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
  348. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
  349. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
  350. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
  351. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
  352. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
  353. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
  354. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
  355. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
  356. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
  357. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
  358. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
  359. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
  360. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
  361. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
  362. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +84 -0
  363. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/package.json +14 -0
  364. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +15 -15
  365. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +411 -300
  366. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +309 -123
  367. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +99 -56
  368. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +62 -16
  369. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +159 -108
  370. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +1 -1
  371. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +4 -2
  372. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +56 -56
  373. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +114 -45
  374. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +57 -19
  375. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +106 -106
  376. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +416 -106
  377. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +287 -17
  378. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +809 -87
  379. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +107 -20
  380. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +9 -9
  381. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +26 -6
  382. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +2 -2
  383. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  384. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +1 -1
  385. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +0 -8
  386. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +163 -58
  387. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  388. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  389. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +334 -248
  390. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +52 -52
  391. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +26 -26
  392. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +99 -99
  393. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +160 -0
  394. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +9 -9
  395. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +43 -1
  396. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +36 -10
  397. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +387 -30
  398. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +9 -9
  399. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +2 -2
  400. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +94 -40
  401. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +5 -3
  402. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +7 -7
  403. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +19 -19
  404. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +19 -19
  405. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +9 -9
  406. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +20 -20
  407. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +9 -9
  408. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +35 -5
  409. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +2 -2
  410. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +52 -52
  411. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +187 -0
  412. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +125 -71
  413. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +18 -3
  414. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +12 -12
  415. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +17 -23
  416. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  417. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +25 -25
  418. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +3 -3
  419. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +15 -0
  420. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +62 -44
  421. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +329 -37
  422. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +4 -4
  423. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +37 -0
  424. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +69 -0
  425. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +79 -2
  426. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +102 -49
  427. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +98 -32
  428. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +5 -5
  429. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +116 -0
  430. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +55 -7
  431. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +289 -13
  432. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +28 -1
  433. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +2 -2
  434. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +11 -2
  435. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +84 -10
  436. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +122 -8
  437. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +8 -8
  438. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +24 -24
  439. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +16 -2
  440. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +7 -7
  441. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +10 -10
  442. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +3 -3
  443. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +26 -17
  444. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +405 -0
  445. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +357 -92
  446. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +22 -6
  447. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +6 -6
  448. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +43 -43
  449. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +13 -13
  450. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +4 -4
  451. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +19 -19
  452. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +243 -0
  453. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +68 -168
  454. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +58 -15
  455. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +40 -0
  456. package/dist/extensions/pi-subagents/test/unit/reads-resolution.test.ts +66 -0
  457. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +47 -28
  458. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +12 -12
  459. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +57 -0
  460. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +107 -27
  461. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +1 -1
  462. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +56 -50
  463. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +69 -28
  464. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +67 -0
  465. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +59 -44
  466. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +424 -49
  467. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +23 -1
  468. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +32 -5
  469. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +13 -13
  470. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +120 -91
  471. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +2 -2
  472. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +85 -0
  473. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +35 -35
  474. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +24 -0
  475. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +312 -22
  476. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +150 -15
  477. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -26
  478. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +73 -0
  479. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +76 -25
  480. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +5 -5
  481. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +55 -6
  482. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +13 -13
  483. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +1 -1
  484. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +1 -1
  485. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +8 -8
  486. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +6 -6
  487. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +138 -11
  488. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +8 -8
  489. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +113 -0
  490. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +7 -7
  491. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +6 -6
  492. package/dist/extensions/workflow/extension.ts +21 -19
  493. package/dist/extensions/workflow/modes.ts +65 -0
  494. package/dist/index.d.ts +1 -1
  495. package/dist/main.js +1 -0
  496. package/dist/modes/interactive/components/settings-selector.d.ts +6 -2
  497. package/dist/modes/interactive/components/settings-selector.js +20 -0
  498. package/dist/modes/interactive/interactive-mode.d.ts +28 -0
  499. package/dist/modes/interactive/interactive-mode.js +214 -28
  500. package/dist/modes/interactive/theme/dark.json +1 -0
  501. package/dist/modes/interactive/theme/light.json +1 -0
  502. package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
  503. package/dist/modes/interactive/theme/theme-controller.js +10 -1
  504. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  505. package/dist/modes/interactive/theme/theme.js +24 -6
  506. package/dist/skills/pi-subagents/SKILL.md +20 -15
  507. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +77 -83
  508. package/dist/skills/pi-subagents/references/execution-controls.md +215 -106
  509. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +29 -9
  510. package/dist/skills/pi-subagents/references/prompting-and-roles.md +58 -43
  511. package/docs/keybindings.md +35 -7
  512. package/docs/plans/workflow-autoloop-reference.md +192 -0
  513. package/docs/plans/workflow-subagent-consolidation-planb.md +374 -0
  514. package/docs/plans/workflow-subagent-scripted-merge.md +406 -0
  515. package/docs/settings.md +16 -14
  516. package/docs/usage.md +5 -0
  517. package/package.json +5 -5
  518. package/dist/extensions/pi-powerline-footer/fixed-editor/cluster.ts +0 -113
  519. package/dist/extensions/pi-powerline-footer/fixed-editor/terminal-split.ts +0 -1080
  520. package/dist/extensions/pi-powerline-footer/tests/fixed-editor.test.ts +0 -1452
  521. package/dist/extensions/workflow/adapter.ts +0 -949
  522. package/dist/extensions/workflow/modes/loop.ts +0 -74
  523. package/dist/extensions/workflow/modes/prototype.ts +0 -170
  524. package/dist/extensions/workflow/modes/quicktype.ts +0 -150
  525. package/dist/extensions/workflow/modes/task.ts +0 -105
  526. package/dist/extensions/workflow/run-state.ts +0 -124
  527. package/dist/extensions/workflow/state-machine.ts +0 -484
  528. package/dist/extensions/workflow/validators.ts +0 -42
@@ -96,6 +96,7 @@ interface AsyncStatusPayload {
96
96
  currentTool?: string;
97
97
  currentPath?: string;
98
98
  state?: string;
99
+ endedAt?: number;
99
100
  launchContractDigest?: string;
100
101
  launchResolvedExtensions?: LaunchResolvedExtensions;
101
102
  runtimeAcknowledgedExtensions?: RuntimeAcknowledgedExtensions;
@@ -191,7 +192,7 @@ function childWatchdogStatus(runId: string, phase: "idle" | "reviewing" | "autof
191
192
  return {
192
193
  type: CHILD_WATCHDOG_STATUS_EVENT,
193
194
  runId,
194
- agent: "builder",
195
+ agent: "worker",
195
196
  childIndex: 0,
196
197
  stepIndex: 0,
197
198
  seq,
@@ -230,6 +231,7 @@ interface AsyncExecutionModule {
230
231
 
231
232
  interface UtilsModule {
232
233
  readStatus(dir: string): { runId: string; state: string; mode: string } | null;
234
+ pruneStatusCacheForAsyncRoot(root: string, runIds: Iterable<string>): number;
233
235
  }
234
236
 
235
237
  interface TypesModule {
@@ -254,6 +256,7 @@ const isAsyncAvailable = asyncMod?.isAsyncAvailable;
254
256
  const executeAsyncSingle = asyncMod?.executeAsyncSingle;
255
257
  const executeAsyncChain = asyncMod?.executeAsyncChain;
256
258
  const readStatus = utils?.readStatus;
259
+ const pruneStatusCacheForAsyncRoot = utils?.pruneStatusCacheForAsyncRoot;
257
260
  const ASYNC_DIR = typesMod?.ASYNC_DIR;
258
261
  const RESULTS_DIR = typesMod?.RESULTS_DIR;
259
262
  const TEMP_ROOT_DIR = typesMod?.TEMP_ROOT_DIR;
@@ -455,9 +458,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
455
458
 
456
459
  function launchProtocolTest(id: string): void {
457
460
  executeAsyncSingle(id, {
458
- agent: "builder",
461
+ agent: "worker",
459
462
  task: "Exercise child protocol",
460
- agentConfig: makeAgent("builder", { completionGuard: false }),
463
+ agentConfig: makeAgent("worker", { completionGuard: false }),
461
464
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
462
465
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
463
466
  shareEnabled: false,
@@ -483,6 +486,16 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
483
486
  assert.equal(payload.results[0]?.output, "你好 from fragmented async JSON");
484
487
  });
485
488
 
489
+ it("persists terminal status before creating the result artifact", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
490
+ mockPi.onCall({ output: "completed output" });
491
+ const id = `async-terminal-status-${Date.now().toString(36)}`;
492
+ launchProtocolTest(id);
493
+ await waitForAsyncResultFile(id);
494
+ const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
495
+ assert.equal(status.state, "complete");
496
+ assert.equal(status.endedAt !== undefined, true);
497
+ });
498
+
486
499
  it("persists absent output provenance when async lifecycle text is synthetic", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
487
500
  mockPi.onCall({ jsonl: [mockAssistantMessage("", "tool_use")], stderr: "mock child failure", exitCode: 1 });
488
501
  const id = `async-output-absent-${Date.now().toString(36)}`;
@@ -504,12 +517,12 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
504
517
  });
505
518
 
506
519
  it("matches preflight launch digest in equivalent foreground and async execution", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
507
- const agentName = `contract-builder-${Date.now().toString(36)}`;
520
+ const agentName = `contract-worker-${Date.now().toString(36)}`;
508
521
  const task = "Compare the resolved launch inputs.";
509
522
  const turnBudget = { maxTurns: 2, graceTurns: 1 } as const;
510
523
  const agentPath = path.join(tempDir, ".selesai", "agents", `${agentName}.md`);
511
524
  fs.mkdirSync(path.dirname(agentPath), { recursive: true });
512
- fs.writeFileSync(agentPath, `---\nname: ${agentName}\ndescription: Contract comparison builder\n---\n`, "utf-8");
525
+ fs.writeFileSync(agentPath, `---\nname: ${agentName}\ndescription: Contract comparison worker\n---\n`, "utf-8");
513
526
  const discovered = discoverAgents(tempDir).agents.find((agent) => agent.name === agentName);
514
527
  assert.ok(discovered, "expected temporary agent definition to be discovered");
515
528
  const preflight = await resolveSubagentLaunchContract({ agent: agentName, cwd: tempDir, task, turnBudget, runId: "contract-preflight" });
@@ -548,9 +561,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
548
561
  const id = `async-launch-digest-${Date.now().toString(36)}`;
549
562
  const privateExtension = path.join(tempDir, "extensions", "private-extension.ts");
550
563
  const launch = executeAsyncSingle(id, {
551
- agent: "builder",
564
+ agent: "worker",
552
565
  task: "Exercise launch digest reporting",
553
- agentConfig: makeAgent("builder", { completionGuard: false, extensions: [privateExtension] }),
566
+ agentConfig: makeAgent("worker", { completionGuard: false, extensions: [privateExtension] }),
554
567
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
555
568
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
556
569
  shareEnabled: false,
@@ -560,7 +573,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
560
573
  });
561
574
  assert.match(launch.details.launchContractDigest ?? "", /^[a-f0-9]{64}$/);
562
575
  const payload = await readAsyncPayload(id);
563
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
576
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete" && candidate.runtimeAcknowledgedExtensions !== undefined);
564
577
  assert.equal(payload.launchContractDigest, launch.details.launchContractDigest);
565
578
  assert.equal(payload.results[0]?.launchContractDigest, launch.details.launchContractDigest);
566
579
  assert.equal(status.launchContractDigest, launch.details.launchContractDigest);
@@ -612,7 +625,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
612
625
 
613
626
  it("foreground checkpoint decisions use current-session control actions", { skip: !createSubagentExecutor ? "executor not available" : undefined }, async () => {
614
627
  mockPi.onCall({ output: "analysis done" });
615
- const executor = makeAsyncExecutor([makeAgent("analyst"), makeAgent("builder")]);
628
+ const executor = makeAsyncExecutor([makeAgent("analyst"), makeAgent("worker")]);
616
629
  const ctx = makeMinimalCtx(tempDir);
617
630
  ctx.sessionManager.getSessionId = () => "session-foreground-checkpoint";
618
631
  const id = `foreground-checkpoint-${Date.now().toString(36)}`;
@@ -621,7 +634,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
621
634
  chain: [
622
635
  { agent: "analyst", task: "Analyze" },
623
636
  { checkpoint: "review", message: "Approve implementation?" },
624
- { agent: "builder", task: "Implement" },
637
+ { agent: "worker", task: "Implement" },
625
638
  ],
626
639
  }, new AbortController().signal, undefined, ctx) as AsyncExecutionResult;
627
640
 
@@ -646,9 +659,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
646
659
  chain: [
647
660
  { agent: "analyst", task: "Analyze" },
648
661
  { checkpoint: "review" },
649
- { agent: "builder", task: "Implement" },
662
+ { agent: "worker", task: "Implement" },
650
663
  ],
651
- agents: [makeAgent("analyst"), makeAgent("builder")],
664
+ agents: [makeAgent("analyst"), makeAgent("worker")],
652
665
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
653
666
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
654
667
  shareEnabled: false,
@@ -717,11 +730,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
717
730
  const sessionFile = path.join(tempDir, "sessions", "parent-session", "session.jsonl");
718
731
  const ctx = makeMinimalCtx(tempDir);
719
732
  ctx.sessionManager.getSessionFile = () => sessionFile;
720
- const executor = makeAsyncExecutor([makeAgent("builder", { completionGuard: false })], { artifactDir: "session" });
733
+ const executor = makeAsyncExecutor([makeAgent("worker", { completionGuard: false })], { artifactDir: "session" });
721
734
 
722
735
  const launch = await executor.execute(
723
736
  "async-session-artifact-dir",
724
- { agent: "builder", task: "Write async session artifacts", async: true, runId: "async-session-artifacts", acceptance: false },
737
+ { agent: "worker", task: "Write async session artifacts", async: true, runId: "async-session-artifacts", acceptance: false },
725
738
  new AbortController().signal,
726
739
  undefined,
727
740
  ctx,
@@ -746,13 +759,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
746
759
  const sessionId = `session-capability-${Date.now().toString(36)}`;
747
760
  const handle = registerSubagentCapabilityCeiling({ sessionId, ceiling: { allowedTools: ["read"], denyExtensions: true }, source: "test" });
748
761
  try {
749
- const executor = makeAsyncExecutor([makeAgent("builder", { tools: ["read", "write"], completionGuard: false })]);
762
+ const executor = makeAsyncExecutor([makeAgent("worker", { tools: ["read", "write"], completionGuard: false })]);
750
763
  const id = `async-capability-${Date.now().toString(36)}`;
751
764
  const ctx = makeMinimalCtx(tempDir);
752
765
  ctx.sessionManager.getSessionId = () => sessionId;
753
766
  const launch = await executor.execute(
754
767
  id,
755
- { agent: "builder", task: "Run with a restricted capability ceiling", async: true, runId: id, acceptance: false, artifacts: true },
768
+ { agent: "worker", task: "Run with a restricted capability ceiling", async: true, runId: id, acceptance: false, artifacts: true },
756
769
  new AbortController().signal,
757
770
  undefined,
758
771
  ctx,
@@ -787,9 +800,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
787
800
  mockPi.onCall({ output: "", stderr: "model unavailable", exitCode: 1 });
788
801
  const id = `async-empty-failure-artifact-${Date.now().toString(36)}`;
789
802
  executeAsyncSingle(id, {
790
- agent: "builder",
803
+ agent: "worker",
791
804
  task: "Fail before output",
792
- agentConfig: makeAgent("builder", { completionGuard: false }),
805
+ agentConfig: makeAgent("worker", { completionGuard: false }),
793
806
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
794
807
  artifactConfig: { enabled: true, includeInput: true, includeOutput: true, includeJsonl: false, includeMetadata: true, cleanupDays: 7 },
795
808
  artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
@@ -814,9 +827,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
814
827
  const id = `async-artifact-dir-recovery-${Date.now().toString(36)}`;
815
828
  const artifactsDir = path.join(tempDir, ".pi-subagents", "artifacts");
816
829
  executeAsyncSingle(id, {
817
- agent: "builder",
830
+ agent: "worker",
818
831
  task: "Complete after generated artifacts are cleaned",
819
- agentConfig: makeAgent("builder", { completionGuard: false }),
832
+ agentConfig: makeAgent("worker", { completionGuard: false }),
820
833
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
821
834
  artifactConfig: { enabled: true, includeInput: true, includeOutput: true, includeJsonl: false, includeMetadata: true, cleanupDays: 7 },
822
835
  artifactsDir,
@@ -899,9 +912,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
899
912
  mockPi.onCall({ output: "OK" });
900
913
  const id = `async-single-literal-output-ref-${Date.now().toString(36)}`;
901
914
  const result = executeAsyncSingle(id, {
902
- agent: "builder",
915
+ agent: "worker",
903
916
  task,
904
- agentConfig: makeAgent("builder"),
917
+ agentConfig: makeAgent("worker"),
905
918
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
906
919
  artifactConfig: {
907
920
  enabled: false,
@@ -931,9 +944,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
931
944
  mockPi.onCall({ output: "non-node exec async done" });
932
945
  const id = `async-non-node-exec-${Date.now().toString(36)}`;
933
946
  const result = executeAsyncSingle(id, {
934
- agent: "builder",
947
+ agent: "worker",
935
948
  task: "Say non-node exec async done. Do not edit files.",
936
- agentConfig: makeAgent("builder"),
949
+ agentConfig: makeAgent("worker"),
937
950
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
938
951
  artifactConfig: {
939
952
  enabled: false,
@@ -965,9 +978,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
965
978
  mockPi.onCall({ output: "stale node exec async done" });
966
979
  const id = `async-stale-node-exec-${Date.now().toString(36)}`;
967
980
  const result = executeAsyncSingle(id, {
968
- agent: "builder",
981
+ agent: "worker",
969
982
  task: "Say stale node exec async done. Do not edit files.",
970
- agentConfig: makeAgent("builder"),
983
+ agentConfig: makeAgent("worker"),
971
984
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
972
985
  artifactConfig: {
973
986
  enabled: false,
@@ -1095,7 +1108,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1095
1108
  await waitForMockPiCall(mockPi, 1, 10_000);
1096
1109
  const resultPath = await waitForAsyncResultFile(id, 8_000);
1097
1110
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1098
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1111
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
1099
1112
  assert.equal(payload.state, "failed");
1100
1113
  assert.equal(payload.success, false);
1101
1114
  assert.equal(payload.exitCode, 1);
@@ -1113,6 +1126,69 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1113
1126
  assert.equal(mockPi.callCount(), 2);
1114
1127
  });
1115
1128
 
1129
+ it("enforces an agent-level timeout on an async serial child without a composite deadline", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
1130
+ mockPi.onCall({ delay: 5_000, output: "too late" });
1131
+ const id = `async-child-timeout-chain-${Date.now().toString(36)}`;
1132
+ executeAsyncChain(id, {
1133
+ chain: [{ agent: "slow", task: "Wait" }],
1134
+ agents: [makeAgent("slow", { defaultTimeoutMs: 150 })],
1135
+ ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1136
+ artifactConfig: {
1137
+ enabled: false,
1138
+ includeInput: false,
1139
+ includeOutput: false,
1140
+ includeJsonl: false,
1141
+ includeMetadata: false,
1142
+ cleanupDays: 7,
1143
+ },
1144
+ shareEnabled: false,
1145
+ maxSubagentDepth: 2,
1146
+ });
1147
+
1148
+ const payload = await readAsyncPayload(id);
1149
+ assert.equal(payload.timeoutMs, undefined, "composite parent must remain unbounded by default");
1150
+ assert.equal(payload.state, "failed");
1151
+ assert.equal(payload.results[0]?.timedOut, true);
1152
+ assert.equal(payload.results[0]?.error, "Subagent timed out after 150ms.");
1153
+ });
1154
+
1155
+ it("enforces child timeouts on async parallel tasks without a composite deadline", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
1156
+ mockPi.onCall({ delay: 5_000, output: "one too late" });
1157
+ mockPi.onCall({ delay: 5_000, output: "two too late" });
1158
+ const id = `async-child-timeout-parallel-${Date.now().toString(36)}`;
1159
+ executeAsyncChain(id, {
1160
+ chain: [{
1161
+ parallel: [
1162
+ { agent: "slow-one", task: "Wait" },
1163
+ { agent: "slow-two", task: "Wait" },
1164
+ ],
1165
+ concurrency: 2,
1166
+ }],
1167
+ resultMode: "parallel",
1168
+ agents: [
1169
+ makeAgent("slow-one", { defaultTimeoutMs: 150 }),
1170
+ makeAgent("slow-two", { defaultTimeoutMs: 200 }),
1171
+ ],
1172
+ ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1173
+ artifactConfig: {
1174
+ enabled: false,
1175
+ includeInput: false,
1176
+ includeOutput: false,
1177
+ includeJsonl: false,
1178
+ includeMetadata: false,
1179
+ cleanupDays: 7,
1180
+ },
1181
+ shareEnabled: false,
1182
+ maxSubagentDepth: 2,
1183
+ });
1184
+
1185
+ const payload = await readAsyncPayload(id);
1186
+ assert.equal(payload.timeoutMs, undefined, "composite parent must remain unbounded by default");
1187
+ assert.equal(payload.state, "failed");
1188
+ assert.deepEqual(payload.results.map((result) => result.timedOut), [true, true]);
1189
+ assert.deepEqual(payload.results.map((result) => result.error), ["Subagent timed out after 150ms.", "Subagent timed out after 200ms."]);
1190
+ });
1191
+
1116
1192
  it("hard-kills async children that ignore timeout SIGTERM", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
1117
1193
  mockPi.onCall({ delay: 60_000, ignoreSigterm: true, output: "too late" });
1118
1194
  const id = `async-timeout-hard-kill-${Date.now().toString(36)}`;
@@ -1140,7 +1216,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1140
1216
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1141
1217
  const elapsedMs = Date.now() - startedAt;
1142
1218
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1143
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1219
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
1144
1220
  assert.equal(payload.state, "failed");
1145
1221
  assert.equal(payload.timedOut, true);
1146
1222
  assert.equal(payload.results[0]?.timedOut, true);
@@ -1151,15 +1227,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1151
1227
  assert.equal(mockPi.callCount(), 1);
1152
1228
  });
1153
1229
 
1154
- it("cancels async acceptance verification when the run times out", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
1230
+ it("cancels async acceptance verification when the run times out", { skip: !isAsyncAvailable() ? "jiti not available" : process.platform === "win32" ? "timeout signal delivery intermittent on Windows CI" : undefined }, async () => {
1155
1231
  mockPi.onCall({ output: "implementation complete" });
1156
1232
  const id = `async-timeout-acceptance-${Date.now().toString(36)}`;
1157
1233
  const timeoutMs = 1_000;
1158
1234
  const startedAt = Date.now();
1159
1235
  executeAsyncSingle(id, {
1160
- agent: "builder",
1236
+ agent: "worker",
1161
1237
  task: "Implement with verified acceptance",
1162
- agentConfig: makeAgent("builder"),
1238
+ agentConfig: makeAgent("worker"),
1163
1239
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1164
1240
  artifactConfig: {
1165
1241
  enabled: true,
@@ -1182,7 +1258,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1182
1258
  const resultPath = await waitForAsyncResultFile(id, 5_000);
1183
1259
  const elapsedMs = Date.now() - startedAt;
1184
1260
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1185
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1261
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
1186
1262
  assert.equal(payload.state, "failed");
1187
1263
  assert.equal(payload.timedOut, true);
1188
1264
  assert.equal(payload.results[0]?.timedOut, true);
@@ -1206,9 +1282,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1206
1282
  });
1207
1283
  const id = `async-turn-budget-soft-${Date.now().toString(36)}`;
1208
1284
  executeAsyncSingle(id, {
1209
- agent: "builder",
1285
+ agent: "worker",
1210
1286
  task: "Use the final grace turn to wrap up.",
1211
- agentConfig: makeAgent("builder"),
1287
+ agentConfig: makeAgent("worker"),
1212
1288
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1213
1289
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1214
1290
  shareEnabled: false,
@@ -1218,7 +1294,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1218
1294
 
1219
1295
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1220
1296
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1221
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1297
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
1222
1298
  assert.equal(payload.success, true);
1223
1299
  assert.equal(payload.state, "complete");
1224
1300
  assert.equal(payload.turnBudgetExceeded, undefined);
@@ -1245,9 +1321,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1245
1321
  });
1246
1322
  const id = `async-turn-budget-hard-${Date.now().toString(36)}`;
1247
1323
  executeAsyncSingle(id, {
1248
- agent: "builder",
1324
+ agent: "worker",
1249
1325
  task: "Exceed the turn budget.",
1250
- agentConfig: makeAgent("builder"),
1326
+ agentConfig: makeAgent("worker"),
1251
1327
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1252
1328
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1253
1329
  shareEnabled: false,
@@ -1257,7 +1333,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1257
1333
 
1258
1334
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1259
1335
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1260
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1336
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
1261
1337
  assert.equal(payload.success, true);
1262
1338
  assert.equal(payload.state, "complete");
1263
1339
  assert.equal(payload.exitCode, 0);
@@ -1295,9 +1371,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1295
1371
  ],
1296
1372
  });
1297
1373
  executeAsyncSingle(id, {
1298
- agent: "builder",
1374
+ agent: "worker",
1299
1375
  task: "Finish active tool work before enforcing the hard limit.",
1300
- agentConfig: makeAgent("builder"),
1376
+ agentConfig: makeAgent("worker"),
1301
1377
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1302
1378
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1303
1379
  shareEnabled: false,
@@ -1319,7 +1395,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1319
1395
 
1320
1396
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1321
1397
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1322
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
1398
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
1323
1399
  assert.equal(payload.state, "complete");
1324
1400
  assert.equal(payload.turnBudgetExceeded, undefined);
1325
1401
  assert.equal(payload.turnBudget?.outcome, "wrap-up-requested");
@@ -1345,9 +1421,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1345
1421
  });
1346
1422
  const id = `async-turn-budget-timeout-${Date.now().toString(36)}`;
1347
1423
  executeAsyncSingle(id, {
1348
- agent: "builder",
1424
+ agent: "worker",
1349
1425
  task: "Timeout while hard termination is deferred.",
1350
- agentConfig: makeAgent("builder"),
1426
+ agentConfig: makeAgent("worker"),
1351
1427
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1352
1428
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1353
1429
  shareEnabled: false,
@@ -1361,7 +1437,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1361
1437
  deliverTimeoutRequest({ asyncDir, pid: pending.pid, source: "test" });
1362
1438
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1363
1439
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1364
- const status = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
1440
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
1365
1441
  assert.equal(payload.state, "failed");
1366
1442
  assert.equal(payload.timedOut, true);
1367
1443
  assert.equal(payload.turnBudgetExceeded, undefined);
@@ -1387,9 +1463,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1387
1463
  });
1388
1464
  const id = `async-turn-budget-stop-${Date.now().toString(36)}`;
1389
1465
  executeAsyncSingle(id, {
1390
- agent: "builder",
1466
+ agent: "worker",
1391
1467
  task: "Stop while hard termination is deferred.",
1392
- agentConfig: makeAgent("builder"),
1468
+ agentConfig: makeAgent("worker"),
1393
1469
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1394
1470
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1395
1471
  shareEnabled: false,
@@ -1402,7 +1478,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1402
1478
  deliverStopRequest({ asyncDir, pid: pending.pid, source: "test" });
1403
1479
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1404
1480
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1405
- const status = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
1481
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "stopped");
1406
1482
  assert.equal(payload.state, "stopped");
1407
1483
  assert.equal(payload.stopped, true);
1408
1484
  assert.equal(payload.turnBudgetExceeded, undefined);
@@ -1445,13 +1521,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1445
1521
  const wrappedTask = `Fork preamble: ${"execution ".repeat(20)}`;
1446
1522
  const rawGoal = `Caller-facing goal: ${"raw ".repeat(40)}`;
1447
1523
  const singleResult = executeAsyncSingle(singleId, {
1448
- agent: "builder",
1524
+ agent: "worker",
1449
1525
  task: wrappedTask,
1450
1526
  goal: rawGoal,
1451
- agentConfig: makeAgent("builder"),
1527
+ agentConfig: makeAgent("worker"),
1452
1528
  ...commonParams,
1453
1529
  });
1454
- assert.match(singleResult.content[0]?.text ?? "", /Async: builder \[/);
1530
+ assert.match(singleResult.content[0]?.text ?? "", /Async: worker \[/);
1455
1531
  assert.match(singleResult.content[0]?.text ?? "", /Do not run sleep timers or polling loops/);
1456
1532
  assert.match(singleResult.content[0]?.text ?? "", /call subagent_wait\(\)/i);
1457
1533
  assert.match(singleResult.content[0]?.text ?? "", /non-interactive run: Pi auto-drains current-session background work at agent_end/);
@@ -1462,9 +1538,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1462
1538
  mockPi.onCall({ output: "interactive done" });
1463
1539
  const interactiveId = `async-handoff-interactive-${Date.now().toString(36)}`;
1464
1540
  const interactiveResult = executeAsyncSingle(interactiveId, {
1465
- agent: "builder",
1541
+ agent: "worker",
1466
1542
  task: "Interactive handoff",
1467
- agentConfig: makeAgent("builder"),
1543
+ agentConfig: makeAgent("worker"),
1468
1544
  ...commonParams,
1469
1545
  ctx: { ...commonParams.ctx, interactive: true },
1470
1546
  });
@@ -1478,9 +1554,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1478
1554
  mockPi.onCall({ output: "parallel two done" });
1479
1555
  const parallelId = `async-handoff-parallel-${Date.now().toString(36)}`;
1480
1556
  const parallelResult = executeAsyncChain(parallelId, {
1481
- chain: [{ parallel: [{ agent: "builder", task: "Do one" }, { agent: "commentator", task: "Do two" }] }],
1557
+ chain: [{ parallel: [{ agent: "worker", task: "Do one" }, { agent: "reviewer", task: "Do two" }] }],
1482
1558
  resultMode: "parallel",
1483
- agents: [makeAgent("builder"), makeAgent("commentator")],
1559
+ agents: [makeAgent("worker"), makeAgent("reviewer")],
1484
1560
  ...commonParams,
1485
1561
  });
1486
1562
  assert.match(parallelResult.content[0]?.text ?? "", /Async parallel:/);
@@ -1490,7 +1566,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1490
1566
  const parallelResultPath = await waitForAsyncResultFile(parallelId, 10_000);
1491
1567
  const parallelPayload = JSON.parse(fs.readFileSync(parallelResultPath, "utf-8")) as { agent?: string; mode?: string };
1492
1568
  assert.equal(parallelPayload.mode, "parallel");
1493
- assert.equal(parallelPayload.agent, "parallel:builder+commentator");
1569
+ assert.equal(parallelPayload.agent, "parallel:worker+reviewer");
1494
1570
 
1495
1571
  mockPi.onCall({ output: "chain done" });
1496
1572
  const chainId = `async-handoff-chain-${Date.now().toString(36)}`;
@@ -1498,8 +1574,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1498
1574
  const chainChildTask = `Do chained work ${"child ".repeat(15)}`;
1499
1575
  const chainResult = executeAsyncChain(chainId, {
1500
1576
  task: chainGoal,
1501
- chain: [{ agent: "builder", task: chainChildTask }],
1502
- agents: [makeAgent("builder")],
1577
+ chain: [{ agent: "worker", task: chainChildTask }],
1578
+ agents: [makeAgent("worker")],
1503
1579
  ...commonParams,
1504
1580
  });
1505
1581
  assert.match(chainResult.content[0]?.text ?? "", /Async chain:/);
@@ -1515,8 +1591,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1515
1591
  const id = `async-missing-extension-tool-${Date.now().toString(36)}`;
1516
1592
 
1517
1593
  executeAsyncChain(id, {
1518
- chain: [{ agent: "extension-builder", task: "Use fixture search" }],
1519
- agents: [makeAgent("extension-builder", { tools: ["read", "fixture_search"] })],
1594
+ chain: [{ agent: "extension-worker", task: "Use fixture search" }],
1595
+ agents: [makeAgent("extension-worker", { tools: ["read", "fixture_search"] })],
1520
1596
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
1521
1597
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1522
1598
  shareEnabled: false,
@@ -1533,11 +1609,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1533
1609
 
1534
1610
  it("applies agent acceptance roles to inferred async acceptance", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1535
1611
  mockPi.onCall({ output: "exploration complete" });
1536
- const executor = makeAsyncExecutor([makeAgent("builder", { acceptanceRole: "read-only" })]);
1612
+ const executor = makeAsyncExecutor([makeAgent("worker", { acceptanceRole: "read-only" })]);
1537
1613
 
1538
1614
  const result = await executor.execute(
1539
1615
  "async-agent-acceptance-role",
1540
- { agent: "builder", task: "Explore the authentication flow", async: true, clarify: false },
1616
+ { agent: "worker", task: "Explore the authentication flow", async: true, clarify: false },
1541
1617
  new AbortController().signal,
1542
1618
  undefined,
1543
1619
  makeMinimalCtx(tempDir),
@@ -1551,11 +1627,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1551
1627
 
1552
1628
  it("applies agent acceptance roles to inferred async parallel acceptance", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1553
1629
  mockPi.onCall({ output: "parallel exploration complete" });
1554
- const executor = makeAsyncExecutor([makeAgent("builder", { acceptanceRole: "read-only" })]);
1630
+ const executor = makeAsyncExecutor([makeAgent("worker", { acceptanceRole: "read-only" })]);
1555
1631
 
1556
1632
  const result = await executor.execute(
1557
1633
  "async-parallel-agent-acceptance-role",
1558
- { tasks: [{ agent: "builder", task: "Explore the authentication flow" }], async: true, clarify: false },
1634
+ { tasks: [{ agent: "worker", task: "Explore the authentication flow" }], async: true, clarify: false },
1559
1635
  new AbortController().signal,
1560
1636
  undefined,
1561
1637
  makeMinimalCtx(tempDir),
@@ -1599,6 +1675,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1599
1675
  });
1600
1676
 
1601
1677
  it("top-level async parallel conversion preserves output, reads, and progress", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1678
+ fs.writeFileSync(path.join(tempDir, "input.md"), "context");
1602
1679
  mockPi.onCall({ output: "Async top-level report" });
1603
1680
  const executor = createSubagentExecutor!({
1604
1681
  pi: { events: createEventBus(), getSessionName: () => undefined },
@@ -1608,13 +1685,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1608
1685
  tempArtifactsDir: tempDir,
1609
1686
  getSubagentSessionRoot: () => tempDir,
1610
1687
  expandTilde: (p: string) => p,
1611
- discoverAgents: () => ({ agents: [makeAgent("builder", { defaultProgress: true })] }),
1688
+ discoverAgents: () => ({ agents: [makeAgent("worker", { defaultProgress: true })] }),
1612
1689
  });
1613
1690
 
1614
1691
  const result = await executor.execute(
1615
1692
  "async-parallel-fields",
1616
1693
  {
1617
- tasks: [{ agent: "builder", task: "Do async work", output: "async-top-output.md", reads: ["input.md"] }],
1694
+ tasks: [{ agent: "worker", task: "Do async work", output: "async-top-output.md", reads: ["input.md"] }],
1618
1695
  async: true,
1619
1696
  clarify: false,
1620
1697
  },
@@ -1667,11 +1744,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1667
1744
  it(`async top-level parallel isolates inherited output (${outputOverride === true ? "output:true" : "omitted output"})`, { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1668
1745
  mockPi.onCall({ matchArgIncludes: "Write the first report", output: "first async report" });
1669
1746
  mockPi.onCall({ matchArgIncludes: "Write the second report", output: "second async report" });
1670
- const agent = makeAgent("builder", { output: "context.md", tools: ["read", "grep", "find", "ls"] });
1747
+ const agent = makeAgent("worker", { output: "context.md", tools: ["read", "grep", "find", "ls"] });
1671
1748
  const executor = makeAsyncExecutor([agent]);
1672
1749
  const tasks = [
1673
- { agent: "builder", task: "Write the first report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
1674
- { agent: "builder", task: "Write the second report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
1750
+ { agent: "worker", task: "Write the first report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
1751
+ { agent: "worker", task: "Write the second report", ...(outputOverride !== undefined ? { output: outputOverride } : {}) },
1675
1752
  ];
1676
1753
  const launch = await executor.execute(
1677
1754
  `async-inherited-output-${outputOverride === true ? "true" : "omitted"}`,
@@ -1688,8 +1765,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1688
1765
  assert.equal(payload.results[1]?.output?.split("\n\nOutput saved to:")[0], "second async report");
1689
1766
  const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", launch.details?.asyncId as string);
1690
1767
  const authoritativePaths = [
1691
- path.join(outputDir, "parallel-0", "0-builder", "context.md"),
1692
- path.join(outputDir, "parallel-0", "1-builder", "context.md"),
1768
+ path.join(outputDir, "parallel-0", "0-worker", "context.md"),
1769
+ path.join(outputDir, "parallel-0", "1-worker", "context.md"),
1693
1770
  ];
1694
1771
  assert.equal(fs.readFileSync(authoritativePaths[0]!, "utf-8"), "first async report");
1695
1772
  assert.equal(fs.readFileSync(authoritativePaths[1]!, "utf-8"), "second async report");
@@ -1702,8 +1779,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1702
1779
  const taskArgs = calls.map((name) => (JSON.parse(fs.readFileSync(path.join(mockPi.dir, name), "utf-8")) as MockPiCallRecord).args?.at(-1) ?? "");
1703
1780
  const firstTask = taskArgs.find((task) => task.includes("Write the first report")) ?? "";
1704
1781
  const secondTask = taskArgs.find((task) => task.includes("Write the second report")) ?? "";
1705
- assert.ok(firstTask.includes(path.join("parallel-0", "0-builder", "context.md")));
1706
- assert.ok(secondTask.includes(path.join("parallel-0", "1-builder", "context.md")));
1782
+ assert.ok(firstTask.includes(path.join("parallel-0", "0-worker", "context.md")));
1783
+ assert.ok(secondTask.includes(path.join("parallel-0", "1-worker", "context.md")));
1707
1784
  for (const taskArg of [firstTask, secondTask]) {
1708
1785
  assert.match(taskArg, /Return the complete artifact in your final response\./);
1709
1786
  assert.match(taskArg, /Do not call contact_supervisor merely because no write-capable tool is available\./);
@@ -1713,13 +1790,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1713
1790
  }
1714
1791
 
1715
1792
  it("async top-level parallel rejects duplicate explicit output paths before spawning", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1716
- const executor = makeAsyncExecutor([makeAgent("builder")]);
1793
+ const executor = makeAsyncExecutor([makeAgent("worker")]);
1717
1794
  const result = await executor.execute(
1718
1795
  "async-duplicate-output",
1719
1796
  {
1720
1797
  tasks: [
1721
- { agent: "builder", task: "Write A", output: "same.md" },
1722
- { agent: "builder", task: "Write B", output: "same.md" },
1798
+ { agent: "worker", task: "Write A", output: "same.md" },
1799
+ { agent: "worker", task: "Write B", output: "same.md" },
1723
1800
  ],
1724
1801
  async: true,
1725
1802
  clarify: false,
@@ -1730,20 +1807,20 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1730
1807
  );
1731
1808
 
1732
1809
  assert.equal(result.isError, true);
1733
- assert.match(result.content[0]?.text ?? "", /Parallel tasks 1 \(builder\) and 2 \(builder\).*same\.md/);
1810
+ assert.match(result.content[0]?.text ?? "", /Parallel tasks 1 \(worker\) and 2 \(worker\).*same\.md/);
1734
1811
  assert.equal(mockPi.callCount(), 0);
1735
1812
  });
1736
1813
 
1737
1814
  it("async top-level parallel preserves distinct explicit output destinations", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : undefined }, async () => {
1738
1815
  mockPi.onCall({ matchArgIncludes: "first.md", output: "first explicit report" });
1739
1816
  mockPi.onCall({ matchArgIncludes: "second.md", output: "second explicit report" });
1740
- const executor = makeAsyncExecutor([makeAgent("builder", { output: "context.md" })]);
1817
+ const executor = makeAsyncExecutor([makeAgent("worker", { output: "context.md" })]);
1741
1818
  const launch = await executor.execute(
1742
1819
  "async-distinct-explicit-output",
1743
1820
  {
1744
1821
  tasks: [
1745
- { agent: "builder", task: "Write A", output: "first.md" },
1746
- { agent: "builder", task: "Write B", output: "second.md" },
1822
+ { agent: "worker", task: "Write A", output: "first.md" },
1823
+ { agent: "worker", task: "Write B", output: "second.md" },
1747
1824
  ],
1748
1825
  async: true,
1749
1826
  clarify: false,
@@ -1758,8 +1835,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1758
1835
  const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", launch.details?.asyncId as string);
1759
1836
  assert.equal(fs.readFileSync(path.join(outputDir, "first.md"), "utf-8"), "first explicit report");
1760
1837
  assert.equal(fs.readFileSync(path.join(outputDir, "second.md"), "utf-8"), "second explicit report");
1761
- assert.ok(payload.results[0]?.artifactPaths?.outputPath?.endsWith("_builder_0_output.md"));
1762
- assert.ok(payload.results[1]?.artifactPaths?.outputPath?.endsWith("_builder_1_output.md"));
1838
+ assert.ok(payload.results[0]?.artifactPaths?.outputPath?.endsWith("_worker_0_output.md"));
1839
+ assert.ok(payload.results[1]?.artifactPaths?.outputPath?.endsWith("_worker_1_output.md"));
1763
1840
  });
1764
1841
 
1765
1842
  it("async chain static parallel namespaces inherited default outputs", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
@@ -1767,8 +1844,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1767
1844
  mockPi.onCall({ matchArgIncludes: "Write second", output: "chain second report" });
1768
1845
  const id = `async-chain-parallel-output-${Date.now().toString(36)}`;
1769
1846
  const launch = executeAsyncChain(id, {
1770
- chain: [{ parallel: [{ agent: "builder", task: "Write first" }, { agent: "builder", task: "Write second" }] }],
1771
- agents: [makeAgent("builder", { output: "context.md" })],
1847
+ chain: [{ parallel: [{ agent: "worker", task: "Write first" }, { agent: "worker", task: "Write second" }] }],
1848
+ agents: [makeAgent("worker", { output: "context.md" })],
1772
1849
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-chain-output" },
1773
1850
  artifactConfig: { enabled: true, includeInput: false, includeOutput: true, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1774
1851
  artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
@@ -1781,8 +1858,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1781
1858
  assert.equal(payload.success, true);
1782
1859
  const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", id);
1783
1860
  const authoritativePaths = [
1784
- path.join(outputDir, "parallel-0", "0-builder", "context.md"),
1785
- path.join(outputDir, "parallel-0", "1-builder", "context.md"),
1861
+ path.join(outputDir, "parallel-0", "0-worker", "context.md"),
1862
+ path.join(outputDir, "parallel-0", "1-worker", "context.md"),
1786
1863
  ];
1787
1864
  assert.equal(fs.readFileSync(authoritativePaths[0]!, "utf-8"), "chain first report");
1788
1865
  assert.equal(fs.readFileSync(authoritativePaths[1]!, "utf-8"), "chain second report");
@@ -1793,8 +1870,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1793
1870
  assert.equal(fs.readFileSync(artifactPaths[1], "utf-8"), "chain second report");
1794
1871
  const calls = fs.readdirSync(mockPi.dir).filter((name) => name.startsWith("call-")).sort();
1795
1872
  const taskArgs = calls.map((name) => (JSON.parse(fs.readFileSync(path.join(mockPi.dir, name), "utf-8")) as MockPiCallRecord).args?.at(-1) ?? "");
1796
- assert.ok(taskArgs.find((task) => task.includes("Write first"))?.includes(path.join("parallel-0", "0-builder", "context.md")));
1797
- assert.ok(taskArgs.find((task) => task.includes("Write second"))?.includes(path.join("parallel-0", "1-builder", "context.md")));
1873
+ assert.ok(taskArgs.find((task) => task.includes("Write first"))?.includes(path.join("parallel-0", "0-worker", "context.md")));
1874
+ assert.ok(taskArgs.find((task) => task.includes("Write second"))?.includes(path.join("parallel-0", "1-worker", "context.md")));
1798
1875
  });
1799
1876
 
1800
1877
  it("async single preserves checked evidence while independent review is pending", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
@@ -1825,14 +1902,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1825
1902
  };
1826
1903
  const id = `async-acceptance-${Date.now().toString(36)}`;
1827
1904
  executeAsyncSingle(id, {
1828
- agent: "builder",
1905
+ agent: "worker",
1829
1906
  task: "Implement acceptance-covered fix",
1830
- agentConfig: makeAgent("builder", { completionGuard: false }),
1907
+ agentConfig: makeAgent("worker", { completionGuard: false }),
1831
1908
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-acceptance" },
1832
1909
  artifactConfig,
1833
1910
  shareEnabled: false,
1834
1911
  maxSubagentDepth: 2,
1835
- acceptance: { level: "checked", criteria: ["Patch bug"], review: { agent: "commentator", required: true } },
1912
+ acceptance: { level: "checked", criteria: ["Patch bug"], review: { agent: "reviewer", required: true } },
1836
1913
  });
1837
1914
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1838
1915
  const result = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
@@ -1856,13 +1933,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1856
1933
  tempArtifactsDir: tempDir,
1857
1934
  getSubagentSessionRoot: () => tempDir,
1858
1935
  expandTilde: (p: string) => p,
1859
- discoverAgents: () => ({ agents: [makeAgent("commentator", { defaultProgress: true })] }),
1936
+ discoverAgents: () => ({ agents: [makeAgent("reviewer", { defaultProgress: true })] }),
1860
1937
  });
1861
1938
 
1862
1939
  const result = await executor.execute(
1863
1940
  "async-chain-read-only-progress",
1864
1941
  {
1865
- chain: [{ agent: "commentator" }],
1942
+ chain: [{ agent: "reviewer" }],
1866
1943
  task: "Review-only. Do not edit files. Return findings.",
1867
1944
  async: true,
1868
1945
  clarify: false,
@@ -1934,7 +2011,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1934
2011
  assert.ok(!result.isError);
1935
2012
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1936
2013
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1937
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
2014
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
1938
2015
  assert.deepEqual(payload.results[0]?.structuredOutput, { value: "Alpha structured" });
1939
2016
  assert.deepEqual(payload.outputs?.data?.structured, { value: "Alpha structured" });
1940
2017
  assert.match(readMockPiArgs(mockPi, 1).at(-1) ?? "", /Alpha structured/);
@@ -1952,15 +2029,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1952
2029
  mockPi.onCall({ matchArgIncludes: "Scout API", output: "Scout A async findings" });
1953
2030
  mockPi.onCall({ matchArgIncludes: "Scout UI", output: "Scout B async findings" });
1954
2031
  mockPi.onCall({ matchArgIncludes: "Synthesize:", output: "Async funnel synthesis" });
1955
- mockPi.onCall({ matchArgIncludes: "Review funnel A:", output: "Async commentator A done" });
1956
- mockPi.onCall({ matchArgIncludes: "Review funnel B:", output: "Async commentator B done" });
2032
+ mockPi.onCall({ matchArgIncludes: "Review funnel A:", output: "Async reviewer A done" });
2033
+ mockPi.onCall({ matchArgIncludes: "Review funnel B:", output: "Async reviewer B done" });
1957
2034
  const id = `async-parallel-funnel-fanout-${Date.now().toString(36)}`;
1958
2035
  const result = executeAsyncChain(id, {
1959
2036
  chain: [
1960
2037
  {
1961
2038
  parallel: [
1962
- { agent: "explorer-a", task: "Scout API" },
1963
- { agent: "explorer-b", task: "Scout UI" },
2039
+ { agent: "scout-a", task: "Scout API" },
2040
+ { agent: "scout-b", task: "Scout UI" },
1964
2041
  ],
1965
2042
  concurrency: 2,
1966
2043
  },
@@ -1973,7 +2050,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1973
2050
  concurrency: 2,
1974
2051
  },
1975
2052
  ],
1976
- agents: [makeAgent("explorer-a"), makeAgent("explorer-b"), makeAgent("synthesizer"), makeAgent("review-a"), makeAgent("review-b")],
2053
+ agents: [makeAgent("scout-a"), makeAgent("scout-b"), makeAgent("synthesizer"), makeAgent("review-a"), makeAgent("review-b")],
1977
2054
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-parallel-funnel-fanout" },
1978
2055
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
1979
2056
  shareEnabled: false,
@@ -1983,14 +2060,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1983
2060
  assert.ok(!result.isError, `should launch: ${JSON.stringify(result.content)}`);
1984
2061
  const resultPath = await waitForAsyncResultFile(id, 10_000);
1985
2062
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
1986
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
2063
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
1987
2064
  assert.equal(payload.success, true);
1988
2065
  assert.deepEqual(payload.results.map((entry) => entry.output), [
1989
2066
  "Scout A async findings",
1990
2067
  "Scout B async findings",
1991
2068
  "Async funnel synthesis",
1992
- "Async commentator A done",
1993
- "Async commentator B done",
2069
+ "Async reviewer A done",
2070
+ "Async reviewer B done",
1994
2071
  ]);
1995
2072
  assert.deepEqual(status.steps?.map((step) => step.status), ["complete", "complete", "complete", "complete", "complete"]);
1996
2073
  assert.deepEqual(status.parallelGroups, [
@@ -1998,9 +2075,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
1998
2075
  { start: 3, count: 2, stepIndex: 2 },
1999
2076
  ]);
2000
2077
  const funnelTask = readMockPiArgsMatching(mockPi, "Synthesize:").at(-1) ?? "";
2001
- assert.match(funnelTask, /=== Parallel Task 1 \(explorer-a\) ===/);
2078
+ assert.match(funnelTask, /=== Parallel Task 1 \(scout-a\) ===/);
2002
2079
  assert.match(funnelTask, /Scout A async findings/);
2003
- assert.match(funnelTask, /=== Parallel Task 2 \(explorer-b\) ===/);
2080
+ assert.match(funnelTask, /=== Parallel Task 2 \(scout-b\) ===/);
2004
2081
  assert.match(funnelTask, /Scout B async findings/);
2005
2082
  assert.match(readMockPiArgsMatching(mockPi, "Review funnel A:").at(-1) ?? "", /Review funnel A:\nAsync funnel synthesis/);
2006
2083
  assert.match(readMockPiArgsMatching(mockPi, "Review funnel B:").at(-1) ?? "", /Review funnel B:\nAsync funnel synthesis/);
@@ -2023,13 +2100,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2023
2100
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2024
2101
  {
2025
2102
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2026
- parallel: { agent: "commentator", task: "Review {target.path}", label: "Review {target.path}", outputSchema: { type: "object" } },
2103
+ parallel: { agent: "reviewer", task: "Review {target.path}", label: "Review {target.path}", outputSchema: { type: "object" } },
2027
2104
  collect: { as: "reviews" },
2028
2105
  concurrency: 1,
2029
2106
  },
2030
2107
  { agent: "consumer", task: "Use {outputs.reviews}" },
2031
2108
  ],
2032
- agents: [makeAgent("producer"), makeAgent("commentator"), makeAgent("consumer")],
2109
+ agents: [makeAgent("producer"), makeAgent("reviewer"), makeAgent("consumer")],
2033
2110
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-placeholder" },
2034
2111
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2035
2112
  shareEnabled: false,
@@ -2045,7 +2122,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2045
2122
  if (fs.existsSync(statusPath)) status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
2046
2123
  else await new Promise((resolve) => setTimeout(resolve, 50));
2047
2124
  }
2048
- assert.deepEqual(status.steps?.map((step) => step.agent), ["producer", "expand:commentator", "consumer"]);
2125
+ assert.deepEqual(status.steps?.map((step) => step.agent), ["producer", "expand:reviewer", "consumer"]);
2049
2126
  assert.equal(status.steps?.[1]?.label, "Review {target.path}");
2050
2127
  assert.equal(status.steps?.[1]?.outputName, "reviews");
2051
2128
  assert.deepEqual(status.parallelGroups, [{ start: 1, count: 1, stepIndex: 1 }]);
@@ -2054,7 +2131,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2054
2131
  const finalStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
2055
2132
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
2056
2133
  assert.equal(payload.success, true);
2057
- assert.deepEqual(finalStatus.steps?.map((step) => step.agent), ["producer", "commentator", "commentator", "consumer"]);
2134
+ assert.deepEqual(finalStatus.steps?.map((step) => step.agent), ["producer", "reviewer", "reviewer", "consumer"]);
2058
2135
  assert.deepEqual(finalStatus.parallelGroups, [{ start: 1, count: 2, stepIndex: 1 }]);
2059
2136
  });
2060
2137
 
@@ -2070,7 +2147,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2070
2147
  {
2071
2148
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2072
2149
  parallel: {
2073
- agent: "commentator",
2150
+ agent: "reviewer",
2074
2151
  task: "Review {target.path}",
2075
2152
  label: "Review {target.path}",
2076
2153
  outputSchema: { type: "object" },
@@ -2080,7 +2157,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2080
2157
  },
2081
2158
  { agent: "consumer", task: "Use {outputs.reviews}" },
2082
2159
  ],
2083
- agents: [makeAgent("producer"), makeAgent("commentator", { output: "context.md" }), makeAgent("consumer")],
2160
+ agents: [makeAgent("producer"), makeAgent("reviewer", { output: "context.md" }), makeAgent("consumer")],
2084
2161
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic" },
2085
2162
  artifactConfig: { enabled: true, includeInput: false, includeOutput: true, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2086
2163
  artifactsDir: path.join(tempDir, ".pi-subagents", "artifacts"),
@@ -2102,8 +2179,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2102
2179
  assert.deepEqual(collected.map((item) => item.structured), [{ ok: "a" }, { ok: "b" }]);
2103
2180
  const outputDir = path.join(tempDir, ".pi-subagents", "artifacts", "outputs", id);
2104
2181
  const dynamicOutputPaths = [
2105
- path.join(outputDir, "dynamic-1", "0-commentator", "context.md"),
2106
- path.join(outputDir, "dynamic-1", "1-commentator", "context.md"),
2182
+ path.join(outputDir, "dynamic-1", "0-reviewer", "context.md"),
2183
+ path.join(outputDir, "dynamic-1", "1-reviewer", "context.md"),
2107
2184
  ];
2108
2185
  assert.equal(fs.readFileSync(dynamicOutputPaths[0]!, "utf-8"), "review-a");
2109
2186
  assert.equal(fs.readFileSync(dynamicOutputPaths[1]!, "utf-8"), "review-b");
@@ -2130,13 +2207,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2130
2207
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2131
2208
  {
2132
2209
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2133
- parallel: { agent: "commentator", task: "Review {target.path}", outputSchema: { type: "object" } },
2210
+ parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
2134
2211
  collect: { as: "reviews" },
2135
2212
  concurrency: 1,
2136
2213
  },
2137
2214
  ],
2138
2215
  usageBudget: { tokens: { hard: 200 } },
2139
- agents: [makeAgent("producer"), makeAgent("commentator")],
2216
+ agents: [makeAgent("producer"), makeAgent("reviewer")],
2140
2217
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-budget" },
2141
2218
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2142
2219
  shareEnabled: false,
@@ -2151,7 +2228,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2151
2228
  assert.equal(status.steps?.[1]?.status, "complete");
2152
2229
  assert.equal(status.steps?.[2]?.status, "failed");
2153
2230
  assert.match(status.steps?.[2]?.error ?? "", /Usage budget exhausted/);
2154
- assert.equal(payload.results.find((result) => result.agent === "commentator" && result.skipped)?.skipped, true);
2231
+ assert.equal(payload.results.find((result) => result.agent === "reviewer" && result.skipped)?.skipped, true);
2155
2232
  });
2156
2233
 
2157
2234
  it("rejects a shared explicit output before dynamic fanout children start", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
@@ -2162,12 +2239,12 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2162
2239
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2163
2240
  {
2164
2241
  expand: { from: { output: "targets", path: "/items" }, item: "target", maxItems: 2 },
2165
- parallel: { agent: "commentator", task: "Review {target.path}", output: "shared.md" },
2242
+ parallel: { agent: "reviewer", task: "Review {target.path}", output: "shared.md" },
2166
2243
  collect: { as: "reviews" },
2167
2244
  concurrency: 2,
2168
2245
  },
2169
2246
  ],
2170
- agents: [makeAgent("producer"), makeAgent("commentator")],
2247
+ agents: [makeAgent("producer"), makeAgent("reviewer")],
2171
2248
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-explicit-output" },
2172
2249
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2173
2250
  shareEnabled: false,
@@ -2196,7 +2273,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2196
2273
  {
2197
2274
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 2 },
2198
2275
  parallel: {
2199
- agent: "commentator",
2276
+ agent: "reviewer",
2200
2277
  task: "Review {target.path}",
2201
2278
  label: "Review {target.path}",
2202
2279
  outputSchema: { type: "object" },
@@ -2205,7 +2282,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2205
2282
  concurrency: 1,
2206
2283
  },
2207
2284
  ],
2208
- agents: [makeAgent("producer"), makeAgent("commentator", { model: "anthropic/claude-sonnet-4-5:high", thinking: "high" })],
2285
+ agents: [makeAgent("producer"), makeAgent("reviewer", { model: "anthropic/claude-sonnet-4-5:high", thinking: "high" })],
2209
2286
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic" },
2210
2287
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2211
2288
  shareEnabled: false,
@@ -2330,7 +2407,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2330
2407
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2331
2408
  {
2332
2409
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2333
- parallel: { agent: "commentator", task: "Review {target.path}", outputSchema: { type: "object" }, acceptance: { level: "checked" } },
2410
+ parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" }, acceptance: { level: "checked" } },
2334
2411
  collect: { as: "reviews" },
2335
2412
  acceptance: {
2336
2413
  level: "verified",
@@ -2338,7 +2415,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2338
2415
  },
2339
2416
  },
2340
2417
  ],
2341
- agents: [makeAgent("producer"), makeAgent("commentator")],
2418
+ agents: [makeAgent("producer"), makeAgent("reviewer")],
2342
2419
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-acceptance-timeout" },
2343
2420
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2344
2421
  shareEnabled: false,
@@ -2349,7 +2426,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2349
2426
  const resultPath = await waitForAsyncResultFile(id, 5_000);
2350
2427
  const elapsedMs = Date.now() - startedAt;
2351
2428
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
2352
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
2429
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
2353
2430
  const dynamicNode = payload.workflowGraph?.nodes?.[1] as { status?: string; error?: string; acceptanceStatus?: string } | undefined;
2354
2431
  assert.equal(payload.state, "failed");
2355
2432
  assert.equal(payload.timedOut, true);
@@ -2373,13 +2450,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2373
2450
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2374
2451
  {
2375
2452
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2376
- parallel: { agent: "commentator", task: "Review {target.path}", outputSchema: { type: "object" } },
2453
+ parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
2377
2454
  collect: { as: "reviews" },
2378
2455
  concurrency: 1,
2379
2456
  },
2380
2457
  { agent: "consumer", task: "Use {outputs.reviews}" },
2381
2458
  ],
2382
- agents: [makeAgent("producer"), makeAgent("commentator"), makeAgent("consumer")],
2459
+ agents: [makeAgent("producer"), makeAgent("reviewer"), makeAgent("consumer")],
2383
2460
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-targets" },
2384
2461
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2385
2462
  shareEnabled: false,
@@ -2405,11 +2482,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2405
2482
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2406
2483
  {
2407
2484
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 1 },
2408
- parallel: { agent: "commentator", task: "Review {target.path}" },
2485
+ parallel: { agent: "reviewer", task: "Review {target.path}" },
2409
2486
  collect: { as: "reviews" },
2410
2487
  },
2411
2488
  ],
2412
- agents: [makeAgent("producer"), makeAgent("commentator")],
2489
+ agents: [makeAgent("producer"), makeAgent("reviewer")],
2413
2490
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-fail" },
2414
2491
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2415
2492
  shareEnabled: false,
@@ -2419,7 +2496,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2419
2496
  assert.ok(!result.isError);
2420
2497
  const resultPath = await waitForAsyncResultFile(id, 10_000);
2421
2498
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
2422
- const status = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload & { workflowGraph?: AsyncResultPayload["workflowGraph"]; error?: string };
2499
+ const status = await waitForAsyncState(id, (candidate) => candidate.state === "failed") as AsyncStatusPayload & { workflowGraph?: AsyncResultPayload["workflowGraph"]; error?: string };
2423
2500
  assert.equal(payload.success, false);
2424
2501
  assert.match(payload.results.at(-1)?.error ?? "", /exceeding maxItems 1/);
2425
2502
  assert.equal(payload.workflowGraph?.nodes?.[1]?.status, "failed");
@@ -2438,11 +2515,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2438
2515
  { agent: "producer", task: "Produce targets", as: "targets", outputSchema: { type: "object" } },
2439
2516
  {
2440
2517
  expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
2441
- parallel: { agent: "commentator", task: "Review {target.path}", outputSchema: { type: "object" } },
2518
+ parallel: { agent: "reviewer", task: "Review {target.path}", outputSchema: { type: "object" } },
2442
2519
  collect: { as: "reviews", outputSchema: { type: "object" } },
2443
2520
  },
2444
2521
  ],
2445
- agents: [makeAgent("producer"), makeAgent("commentator")],
2522
+ agents: [makeAgent("producer"), makeAgent("reviewer")],
2446
2523
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-dynamic-collect-fail" },
2447
2524
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
2448
2525
  shareEnabled: false,
@@ -2459,7 +2536,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2459
2536
  assert.match(payload.workflowGraph?.nodes?.[1]?.error ?? "", /Collected output validation failed/);
2460
2537
  });
2461
2538
 
2462
- it("top-level async worktree parallel resolves reads against the worktree and output under project artifacts", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : process.platform === "win32" ? "worktree path separators unreliable on Windows CI" : undefined }, async () => {
2539
+ it("top-level async worktree parallel keeps existing reads and writes output under project artifacts", { skip: !isAsyncAvailable() || !createSubagentExecutor ? "jiti or executor not available" : process.platform === "win32" ? "worktree path separators unreliable on Windows CI" : undefined }, async () => {
2463
2540
  const repoDir = createRepo("pi-subagent-async-worktree-");
2464
2541
  try {
2465
2542
  mockPi.onCall({ output: "Worktree report" });
@@ -2471,13 +2548,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2471
2548
  tempArtifactsDir: repoDir,
2472
2549
  getSubagentSessionRoot: () => repoDir,
2473
2550
  expandTilde: (p: string) => p,
2474
- discoverAgents: () => ({ agents: [makeAgent("builder")] }),
2551
+ discoverAgents: () => ({ agents: [makeAgent("worker")] }),
2475
2552
  });
2476
2553
 
2477
2554
  const result = await executor.execute(
2478
2555
  "async-parallel-worktree-fields",
2479
2556
  {
2480
- tasks: [{ agent: "builder", task: "Do worktree work", output: "report.md", reads: ["input.md"] }],
2557
+ tasks: [{ agent: "worker", task: "Do worktree work", output: "report.md", reads: ["input.md"] }],
2481
2558
  async: true,
2482
2559
  clarify: false,
2483
2560
  worktree: true,
@@ -2493,11 +2570,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2493
2570
  const worktreeCwd = path.join(os.tmpdir(), `pi-worktree-${asyncId}-s0-0`);
2494
2571
  const args = await waitForMockPiArgs(mockPi, 0);
2495
2572
  const taskArg = args.at(-1) ?? "";
2496
- assert.ok(taskArg.includes(`[Read from: ${path.join(worktreeCwd, "input.md")}]`));
2573
+ assert.match(taskArg, new RegExp(`\\[Read from: ${escapeRegExp(path.join(worktreeCwd, "input.md"))}\\]`));
2497
2574
  assert.ok(taskArg.includes(`Write your findings to exactly this path: ${path.join(repoDir, ".pi-subagents", "artifacts", "outputs", asyncId, "report.md")}`));
2498
2575
  const resultPath = await waitForAsyncResultFile(asyncId, 90_000);
2499
2576
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
2500
- const status = JSON.parse(fs.readFileSync(path.join(result.details!.asyncDir!, "status.json"), "utf-8")) as AsyncStatusPayload;
2577
+ const status = await waitForAsyncState(asyncId, (candidate) => candidate.state === "complete");
2501
2578
  assert.equal(payload.parallelHandoff?.version, 1);
2502
2579
  assert.equal(payload.parallelHandoff?.path, path.join(result.details!.asyncDir!, "handoff.json"));
2503
2580
  assert.deepEqual(status.parallelHandoff, payload.parallelHandoff);
@@ -2509,7 +2586,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2509
2586
  groups: Array<{ children: Array<{ agent: string; status: string; patch: { path: string } }>; cleanup: { state: string } }>;
2510
2587
  };
2511
2588
  assert.equal(handoff.version, 1);
2512
- assert.equal(handoff.groups[0]!.children[0]!.agent, "builder");
2589
+ assert.equal(handoff.groups[0]!.children[0]!.agent, "worker");
2513
2590
  assert.equal(handoff.groups[0]!.children[0]!.status, "completed");
2514
2591
  assert.equal(handoff.groups[0]!.cleanup.state, "complete");
2515
2592
  assert.equal(fs.existsSync(handoff.groups[0]!.children[0]!.patch.path), true, "patch artifact should outlive cleanup");
@@ -2518,33 +2595,49 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2518
2595
  }
2519
2596
  });
2520
2597
 
2521
- it("readStatus caches unchanged files and invalidates same-mtime replacements", () => {
2522
- const dir = createTempDir();
2598
+ it("readStatus caches ordered sweeps above 50 files and invalidates same-mtime replacements", () => {
2599
+ const root = createTempDir();
2523
2600
  try {
2524
- const statusPath = path.join(dir, "status.json");
2525
2601
  const fixedTimestamp = new Date(1_700_000_000_000);
2526
- const statusData = {
2527
- runId: "cache-test",
2528
- state: "running",
2529
- mode: "single",
2530
- startedAt: fixedTimestamp.getTime(),
2531
- };
2532
- fs.writeFileSync(statusPath, JSON.stringify(statusData));
2533
- fs.utimesSync(statusPath, fixedTimestamp, fixedTimestamp);
2602
+ const dirs = Array.from({ length: 51 }, (_, index) => {
2603
+ const dir = path.join(root, `run-${index}`);
2604
+ const statusPath = path.join(dir, "status.json");
2605
+ fs.mkdirSync(dir);
2606
+ fs.writeFileSync(statusPath, JSON.stringify({
2607
+ runId: `cache-test-${index}`,
2608
+ state: "running",
2609
+ mode: "single",
2610
+ startedAt: fixedTimestamp.getTime(),
2611
+ }));
2612
+ fs.utimesSync(statusPath, fixedTimestamp, fixedTimestamp);
2613
+ return dir;
2614
+ });
2534
2615
 
2535
- const cached = readStatus(dir);
2536
- assert.ok(cached);
2537
- assert.strictEqual(readStatus(dir), cached);
2616
+ const cached = dirs.map((dir) => readStatus(dir));
2617
+ cached.forEach((status) => assert.ok(status));
2618
+ dirs.forEach((dir, index) => assert.strictEqual(readStatus(dir), cached[index]));
2538
2619
 
2539
- writeAtomicJson(statusPath, { ...statusData, state: "stopped" });
2620
+ const replacedDir = dirs[25]!;
2621
+ const cachedStatus = cached[25];
2622
+ assert.ok(cachedStatus);
2623
+ const statusPath = path.join(replacedDir, "status.json");
2624
+ writeAtomicJson(statusPath, { ...cachedStatus, state: "stopped" });
2540
2625
  fs.utimesSync(statusPath, fixedTimestamp, fixedTimestamp);
2541
2626
  assert.equal(fs.statSync(statusPath).mtimeMs, fixedTimestamp.getTime());
2542
- const replaced = readStatus(dir);
2627
+ const replaced = readStatus(replacedDir);
2543
2628
  assert.ok(replaced);
2544
2629
  assert.equal(replaced.state, "stopped");
2545
- assert.notStrictEqual(replaced, cached);
2630
+ assert.notStrictEqual(replaced, cachedStatus);
2631
+
2632
+ fs.rmSync(statusPath);
2633
+ assert.equal(readStatus(replacedDir), null);
2634
+
2635
+ const removedDir = dirs[50]!;
2636
+ assert.ok(readStatus(removedDir));
2637
+ fs.rmSync(removedDir, { recursive: true, force: true });
2638
+ assert.equal(pruneStatusCacheForAsyncRoot(root, dirs.slice(0, 50).map((dir) => path.basename(dir))), 1);
2546
2639
  } finally {
2547
- removeTempDir(dir);
2640
+ removeTempDir(root);
2548
2641
  }
2549
2642
  });
2550
2643
 
@@ -2578,10 +2671,11 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2578
2671
  const asyncDir = path.join(ASYNC_DIR, id);
2579
2672
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
2580
2673
  const run = executeAsyncSingle(id, {
2581
- agent: "builder",
2674
+ agent: "worker",
2582
2675
  task: "Do work",
2583
2676
  acceptance: { level: "none", reason: "descriptor persistence coverage" },
2584
- agentConfig: makeAgent("builder", {
2677
+ turnBudget: { maxTurns: 8, graceTurns: 2 },
2678
+ agentConfig: makeAgent("worker", {
2585
2679
  model: "openai/gpt-5-mini:high",
2586
2680
  fallbackModels: ["anthropic/claude-sonnet-4:low"],
2587
2681
  }),
@@ -2616,12 +2710,13 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2616
2710
  const descriptorPath = path.join(asyncDir, "recovery-descriptor.json");
2617
2711
  const descriptor = JSON.parse(fs.readFileSync(descriptorPath, "utf-8"));
2618
2712
  assert.equal(descriptor.sourceRunId, id);
2619
- assert.equal(descriptor.agent, "builder");
2713
+ assert.equal(descriptor.agent, "worker");
2620
2714
  assert.equal(descriptor.model, "openai/gpt-5-mini:high");
2621
2715
  assert.deepEqual(descriptor.fallbackModels, ["anthropic/claude-sonnet-4:low"]);
2622
2716
  assert.equal(descriptor.cwd, tempDir);
2623
2717
  assert.equal(descriptor.sessionDir, path.join(sessionRoot, `async-${id}`));
2624
2718
  assert.deepEqual(descriptor.acceptance, { level: "none", reason: "descriptor persistence coverage" });
2719
+ assert.deepEqual(descriptor.initialTurnBudget, { maxTurns: 8, graceTurns: 2 });
2625
2720
  assert.equal(Object.hasOwn(descriptor.acceptance, "explicit"), false);
2626
2721
  assert.equal(Object.hasOwn(descriptor.acceptance, "inferredReason"), false);
2627
2722
  assert.equal(Object.hasOwn(descriptor, "task"), false);
@@ -2635,7 +2730,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2635
2730
  assert.equal(payload.results[0].modelAttempts.length, 2);
2636
2731
  assert.deepEqual(payload.results[0].totalCost, { inputTokens: 110, outputTokens: 55, costUsd: 0.011 });
2637
2732
  assert.deepEqual(payload.totalCost, { inputTokens: 110, outputTokens: 55, costUsd: 0.011 });
2638
- const statusPayload = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
2733
+ const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete" && candidate.totalCost !== undefined);
2639
2734
  assert.equal(statusPayload.lifecycleArtifactVersion, SUBAGENT_LIFECYCLE_ARTIFACT_VERSION);
2640
2735
  assert.equal(statusPayload.steps[0]?.model, "anthropic/claude-sonnet-4:low");
2641
2736
  assert.equal(statusPayload.steps[0]?.thinking, "low");
@@ -2657,9 +2752,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2657
2752
  mockPi.onCall({ output: "Recovered asynchronously after startup race" });
2658
2753
  const id = `async-startup-retry-${Date.now().toString(36)}`;
2659
2754
  executeAsyncSingle(id, {
2660
- agent: "builder",
2755
+ agent: "worker",
2661
2756
  task: "Do work",
2662
- agentConfig: makeAgent("builder", {
2757
+ agentConfig: makeAgent("worker", {
2663
2758
  model: "openai/gpt-5-mini:high",
2664
2759
  fallbackModels: ["anthropic/claude-sonnet-4:low"],
2665
2760
  }),
@@ -2706,9 +2801,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2706
2801
  mockPi.onCall({ output: "Recovered after stream failure" });
2707
2802
  const id = `async-fallback-stream-${Date.now().toString(36)}`;
2708
2803
  executeAsyncSingle(id, {
2709
- agent: "builder",
2804
+ agent: "worker",
2710
2805
  task: "Do work",
2711
- agentConfig: makeAgent("builder", {
2806
+ agentConfig: makeAgent("worker", {
2712
2807
  model: "openai/gpt-5-mini:high",
2713
2808
  fallbackModels: ["anthropic/claude-sonnet-4:low"],
2714
2809
  }),
@@ -2747,9 +2842,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2747
2842
  mockPi.onCall({ output: "fallback must not run" });
2748
2843
  const id = `async-fallback-toolfail-${Date.now().toString(36)}`;
2749
2844
  executeAsyncSingle(id, {
2750
- agent: "builder",
2845
+ agent: "worker",
2751
2846
  task: "Do work",
2752
- agentConfig: makeAgent("builder", {
2847
+ agentConfig: makeAgent("worker", {
2753
2848
  model: "openai/gpt-5-mini:high",
2754
2849
  fallbackModels: ["anthropic/claude-sonnet-4:low"],
2755
2850
  }),
@@ -2795,9 +2890,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2795
2890
  mockPi.onCall({ output: "Recovered asynchronously" });
2796
2891
  const id = `async-fallback-thinking-off-${Date.now().toString(36)}`;
2797
2892
  const run = executeAsyncSingle(id, {
2798
- agent: "builder",
2893
+ agent: "worker",
2799
2894
  task: "Do work",
2800
- agentConfig: makeAgent("builder", {
2895
+ agentConfig: makeAgent("worker", {
2801
2896
  model: "openai/gpt-5-mini:high",
2802
2897
  fallbackModels: ["anthropic/claude-sonnet-4:low"],
2803
2898
  thinking: "high",
@@ -2850,9 +2945,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2850
2945
  mockPi.onCall({ output: "Recovered asynchronously from empty output" });
2851
2946
  const id = `async-empty-output-fallback-${Date.now().toString(36)}`;
2852
2947
  executeAsyncSingle(id, {
2853
- agent: "builder",
2948
+ agent: "worker",
2854
2949
  task: "Do work",
2855
- agentConfig: makeAgent("builder", {
2950
+ agentConfig: makeAgent("worker", {
2856
2951
  model: "openai/gpt-5-mini",
2857
2952
  fallbackModels: ["anthropic/claude-sonnet-4"],
2858
2953
  }),
@@ -2900,9 +2995,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2900
2995
  mockPi.onCall({ output: "Recovered asynchronously on fallback" });
2901
2996
  const id = `async-empty-output-after-tool-error-${Date.now().toString(36)}`;
2902
2997
  executeAsyncSingle(id, {
2903
- agent: "builder",
2998
+ agent: "worker",
2904
2999
  task: "Do work",
2905
- agentConfig: makeAgent("builder", {
3000
+ agentConfig: makeAgent("worker", {
2906
3001
  model: "openai/gpt-5-mini",
2907
3002
  fallbackModels: ["anthropic/claude-sonnet-4"],
2908
3003
  }),
@@ -2942,11 +3037,10 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2942
3037
  exitCode: 0,
2943
3038
  });
2944
3039
  const id = `async-zero-exit-provider-error-${Date.now().toString(36)}`;
2945
- const asyncDir = path.join(ASYNC_DIR, id);
2946
3040
  executeAsyncSingle(id, {
2947
- agent: "builder",
3041
+ agent: "worker",
2948
3042
  task: "Do work",
2949
- agentConfig: makeAgent("builder", { model: "openai/gpt-5-mini" }),
3043
+ agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
2950
3044
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
2951
3045
  artifactConfig: {
2952
3046
  enabled: false,
@@ -2964,8 +3058,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2964
3058
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
2965
3059
  assert.equal(payload.success, false);
2966
3060
  assert.match(payload.results[0]?.error ?? "", /429 quota exceeded/);
2967
- const statusPayload = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
2968
- assert.equal(statusPayload.state, "failed");
3061
+ const statusPayload = await waitForAsyncState(id, (status) => status.state === "failed");
2969
3062
  assert.match(statusPayload.steps?.[0]?.error ?? "", /429 quota exceeded/);
2970
3063
  });
2971
3064
 
@@ -2990,9 +3083,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
2990
3083
  const id = `async-recovered-child-error-${Date.now().toString(36)}`;
2991
3084
  const asyncDir = path.join(ASYNC_DIR, id);
2992
3085
  executeAsyncSingle(id, {
2993
- agent: "builder",
3086
+ agent: "worker",
2994
3087
  task: "Do work",
2995
- agentConfig: makeAgent("builder", { model: "openai/gpt-5-mini" }),
3088
+ agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
2996
3089
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
2997
3090
  artifactConfig: {
2998
3091
  enabled: false,
@@ -3014,7 +3107,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3014
3107
  assert.equal(payload.results[0]?.success, true);
3015
3108
  assert.equal(payload.results[0]?.error, undefined);
3016
3109
  assert.equal(payload.results[0]?.output, "Recovered asynchronously");
3017
- const statusPayload = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
3110
+ const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
3018
3111
  assert.equal(statusPayload.state, "complete");
3019
3112
  assert.equal(statusPayload.steps?.[0]?.status, "complete");
3020
3113
  assert.equal(statusPayload.steps?.[0]?.exitCode, 0);
@@ -3040,9 +3133,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3040
3133
  const id = `async-provider-error-empty-stop-${Date.now().toString(36)}`;
3041
3134
  const asyncDir = path.join(ASYNC_DIR, id);
3042
3135
  executeAsyncSingle(id, {
3043
- agent: "builder",
3136
+ agent: "worker",
3044
3137
  task: "Do work",
3045
- agentConfig: makeAgent("builder", { model: "openai/gpt-5-mini" }),
3138
+ agentConfig: makeAgent("worker", { model: "openai/gpt-5-mini" }),
3046
3139
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3047
3140
  artifactConfig: {
3048
3141
  enabled: false,
@@ -3064,7 +3157,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3064
3157
  assert.equal(payload.results[0]?.success, false);
3065
3158
  assert.match(payload.results[0]?.error ?? "", /provider transport failed/);
3066
3159
  assert.equal(payload.results[0]?.output, "");
3067
- const statusPayload = JSON.parse(fs.readFileSync(path.join(asyncDir, "status.json"), "utf-8")) as AsyncStatusPayload;
3160
+ const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "failed");
3068
3161
  assert.equal(statusPayload.state, "failed");
3069
3162
  assert.equal(statusPayload.steps?.[0]?.status, "failed");
3070
3163
  assert.equal(statusPayload.steps?.[0]?.exitCode, 1);
@@ -3199,9 +3292,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3199
3292
  mockPi.onCall({ output: "async inline report" });
3200
3293
  const id = `async-string-false-output-${Date.now().toString(36)}`;
3201
3294
  const run = executeAsyncSingle(id, {
3202
- agent: "builder",
3295
+ agent: "worker",
3203
3296
  task: "Do work",
3204
- agentConfig: makeAgent("builder", { output: "default-report.md" }),
3297
+ agentConfig: makeAgent("worker", { output: "default-report.md" }),
3205
3298
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3206
3299
  artifactConfig: {
3207
3300
  enabled: false,
@@ -3238,9 +3331,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3238
3331
  const sessionRoot = path.join(tempDir, "sessions");
3239
3332
 
3240
3333
  executeAsyncSingle(id, {
3241
- agent: "builder",
3334
+ agent: "worker",
3242
3335
  task: "Deploy app",
3243
- agentConfig: makeAgent("builder"),
3336
+ agentConfig: makeAgent("worker"),
3244
3337
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3245
3338
  artifactConfig: {
3246
3339
  enabled: false,
@@ -3277,9 +3370,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3277
3370
  const sessionRoot = path.join(tempDir, "sessions");
3278
3371
 
3279
3372
  executeAsyncSingle(id, {
3280
- agent: "builder",
3373
+ agent: "worker",
3281
3374
  task: "Implement the approved fixes",
3282
- agentConfig: makeAgent("builder"),
3375
+ agentConfig: makeAgent("worker"),
3283
3376
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3284
3377
  artifactConfig: {
3285
3378
  enabled: false,
@@ -3312,7 +3405,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3312
3405
  const eventsPath = path.join(ASYNC_DIR, id, "events.jsonl");
3313
3406
  const eventsText = fs.readFileSync(eventsPath, "utf-8");
3314
3407
  assert.match(eventsText, /"reason":"completion_guard"/);
3315
- assert.match(eventsText, /Subagent failed: builder/);
3408
+ assert.match(eventsText, /Subagent failed: worker/);
3316
3409
  assert.doesNotMatch(eventsText, /Status:/);
3317
3410
  assert.doesNotMatch(eventsText, /Interrupt:/);
3318
3411
  });
@@ -3322,9 +3415,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3322
3415
  const id = `async-v1-separate-${Date.now().toString(36)}`;
3323
3416
 
3324
3417
  executeAsyncSingle(id, {
3325
- agent: "builder",
3418
+ agent: "worker",
3326
3419
  task: "Implement the approved fixes",
3327
- agentConfig: makeAgent("builder", { completionGuard: true }),
3420
+ agentConfig: makeAgent("worker", { completionGuard: true }),
3328
3421
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3329
3422
  artifactConfig: {
3330
3423
  enabled: false,
@@ -3343,7 +3436,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3343
3436
 
3344
3437
  const resultPath = await waitForAsyncResultFile(id, 10_000);
3345
3438
  const payload = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as AsyncResultPayload;
3346
- const statusPayload = JSON.parse(fs.readFileSync(path.join(ASYNC_DIR, id, "status.json"), "utf-8")) as AsyncStatusPayload;
3439
+ const statusPayload = await waitForAsyncState(id, (candidate) => candidate.state === "complete");
3347
3440
 
3348
3441
  assert.equal(payload.success, true);
3349
3442
  assert.equal(payload.state, "complete");
@@ -3364,9 +3457,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3364
3457
  const id = `async-single-schema-${Date.now().toString(36)}`;
3365
3458
 
3366
3459
  executeAsyncSingle(id, {
3367
- agent: "builder",
3460
+ agent: "worker",
3368
3461
  task: "Return structured data",
3369
- agentConfig: makeAgent("builder", { completionGuard: false }),
3462
+ agentConfig: makeAgent("worker", { completionGuard: false }),
3370
3463
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3371
3464
  artifactConfig: {
3372
3465
  enabled: false,
@@ -3432,9 +3525,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3432
3525
  const sessionRoot = path.join(tempDir, "sessions");
3433
3526
 
3434
3527
  executeAsyncSingle(id, {
3435
- agent: "builder",
3528
+ agent: "worker",
3436
3529
  task: "Do work",
3437
- agentConfig: makeAgent("builder", { model: "gpt-5-mini" }),
3530
+ agentConfig: makeAgent("worker", { model: "gpt-5-mini" }),
3438
3531
  ctx: {
3439
3532
  pi: { events: { emit() {} } },
3440
3533
  cwd: tempDir,
@@ -3494,14 +3587,14 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3494
3587
  tempArtifactsDir: tempDir,
3495
3588
  getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
3496
3589
  expandTilde: (p: string) => p,
3497
- discoverAgents: () => ({ agents: [makeAgent("builder")] }),
3590
+ discoverAgents: () => ({ agents: [makeAgent("worker")] }),
3498
3591
  });
3499
3592
  const retainedCtx = makeMinimalCtx(tempDir);
3500
3593
  retainedCtx.sessionManager.getSessionId = () => "session-a";
3501
3594
  retainedCtx.model = { provider: "deepseek", id: "scheduled-model" };
3502
3595
  const launch = await executor.executeScheduled(
3503
3596
  `scheduled-owner-${Date.now().toString(36)}`,
3504
- { agent: "builder", task: "Run retained project timer", async: true, acceptance: false },
3597
+ { agent: "worker", task: "Run retained project timer", async: true, acceptance: false },
3505
3598
  new AbortController().signal,
3506
3599
  retainedCtx,
3507
3600
  ) as AsyncExecutionResult;
@@ -3514,7 +3607,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3514
3607
  assert.deepEqual(state.subagentSpawns, { sessionId: "session-b", count: 1, configuredLimit: 1, granted: 1, grantHistory: [] });
3515
3608
  const blocked = await executor.executeScheduled(
3516
3609
  `scheduled-owner-blocked-${Date.now().toString(36)}`,
3517
- { agent: "builder", task: "Exceed the retained owner budget", async: true, acceptance: false },
3610
+ { agent: "worker", task: "Exceed the retained owner budget", async: true, acceptance: false },
3518
3611
  new AbortController().signal,
3519
3612
  retainedCtx,
3520
3613
  );
@@ -3544,7 +3637,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3544
3637
  tempArtifactsDir: tempDir,
3545
3638
  getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
3546
3639
  expandTilde: (p: string) => p,
3547
- discoverAgents: () => ({ agents: [makeAgent("builder")] }),
3640
+ discoverAgents: () => ({ agents: [makeAgent("worker")] }),
3548
3641
  });
3549
3642
  const retainedCtx = makeMinimalCtx(tempDir);
3550
3643
  retainedCtx.sessionManager.getSessionId = () => "session-a";
@@ -3584,7 +3677,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3584
3677
  tempArtifactsDir: tempDir,
3585
3678
  getSubagentSessionRoot: () => path.join(tempDir, "sessions"),
3586
3679
  expandTilde: (p: string) => p,
3587
- discoverAgents: () => ({ agents: [makeAgent("builder")] }),
3680
+ discoverAgents: () => ({ agents: [makeAgent("worker")] }),
3588
3681
  });
3589
3682
  const initialCtx = makeMinimalCtx(tempDir);
3590
3683
  initialCtx.sessionManager.getSessionId = () => "session-continued";
@@ -3595,7 +3688,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3595
3688
  continuedCtx.sessionManager.getSessionId = () => "session-continued";
3596
3689
  const launch = await executor.execute(
3597
3690
  "continued-async-child",
3598
- { agent: "builder", task: "Do work", async: true, acceptance: false },
3691
+ { agent: "worker", task: "Do work", async: true, acceptance: false },
3599
3692
  new AbortController().signal,
3600
3693
  undefined,
3601
3694
  continuedCtx,
@@ -3612,7 +3705,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3612
3705
 
3613
3706
  it("foreground chains keep the last parent session model after continuation drops ctx.model", { skip: !createSubagentExecutor ? "executor not available" : undefined }, async () => {
3614
3707
  mockPi.onCall({ output: "Done foreground" });
3615
- const executor = makeAsyncExecutor([makeAgent("builder", { completionGuard: false })]);
3708
+ const executor = makeAsyncExecutor([makeAgent("worker", { completionGuard: false })]);
3616
3709
  const initialCtx = makeMinimalCtx(tempDir);
3617
3710
  initialCtx.sessionManager.getSessionId = () => "session-continued";
3618
3711
  initialCtx.model = { provider: "deepseek", id: "deepseek-v4-flash" };
@@ -3622,7 +3715,7 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3622
3715
  continuedCtx.sessionManager.getSessionId = () => "session-continued";
3623
3716
  const result = await executor.execute(
3624
3717
  "continued-foreground-chain",
3625
- { chain: [{ agent: "builder", task: "Do work" }], acceptance: false },
3718
+ { chain: [{ agent: "worker", task: "Do work" }], acceptance: false },
3626
3719
  new AbortController().signal,
3627
3720
  undefined,
3628
3721
  continuedCtx,
@@ -3637,9 +3730,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3637
3730
 
3638
3731
  const id = `async-single-parent-model-${Date.now().toString(36)}`;
3639
3732
  executeAsyncSingle(id, {
3640
- agent: "builder",
3733
+ agent: "worker",
3641
3734
  task: "Do work",
3642
- agentConfig: makeAgent("builder"),
3735
+ agentConfig: makeAgent("worker"),
3643
3736
  ctx: {
3644
3737
  pi: { events: { emit() {} } },
3645
3738
  cwd: tempDir,
@@ -3674,8 +3767,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3674
3767
 
3675
3768
  const id = `async-chain-parent-model-${Date.now().toString(36)}`;
3676
3769
  executeAsyncChain(id, {
3677
- chain: [{ agent: "builder", task: "Do work" }],
3678
- agents: [makeAgent("builder")],
3770
+ chain: [{ agent: "worker", task: "Do work" }],
3771
+ agents: [makeAgent("worker")],
3679
3772
  ctx: {
3680
3773
  pi: { events: { emit() {} } },
3681
3774
  cwd: tempDir,
@@ -3710,8 +3803,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3710
3803
 
3711
3804
  const id = `async-chain-empty-model-${Date.now().toString(36)}`;
3712
3805
  executeAsyncChain(id, {
3713
- chain: [{ agent: "builder", task: "Do work", model: "" }],
3714
- agents: [makeAgent("builder", { model: "anthropic/claude-sonnet-4-5", thinking: "high" })],
3806
+ chain: [{ agent: "worker", task: "Do work", model: "" }],
3807
+ agents: [makeAgent("worker", { model: "anthropic/claude-sonnet-4-5", thinking: "high" })],
3715
3808
  ctx: {
3716
3809
  pi: { events: { emit() {} } },
3717
3810
  cwd: tempDir,
@@ -3754,8 +3847,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3754
3847
  for (const [index, requestedModel] of [undefined, "", "inherit"].entries()) {
3755
3848
  const id = `async-chain-no-parent-${index}-${Date.now().toString(36)}`;
3756
3849
  executeAsyncChain(id, {
3757
- chain: [{ agent: "builder", task: "Do work", ...(requestedModel !== undefined ? { model: requestedModel } : {}) }],
3758
- agents: [makeAgent("builder", { model: "openai/gpt-5-mini", thinking: "high" })],
3850
+ chain: [{ agent: "worker", task: "Do work", ...(requestedModel !== undefined ? { model: requestedModel } : {}) }],
3851
+ agents: [makeAgent("worker", { model: "openai/gpt-5-mini", thinking: "high" })],
3759
3852
  ctx: {
3760
3853
  pi: { events: { emit() {} } },
3761
3854
  cwd: tempDir,
@@ -3804,9 +3897,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3804
3897
  try {
3805
3898
  writePackageSkill(taskCwd, "async-task-cwd-skill");
3806
3899
  executeAsyncSingle(id, {
3807
- agent: "builder",
3900
+ agent: "worker",
3808
3901
  task: "Do work",
3809
- agentConfig: makeAgent("builder", { skills: ["async-task-cwd-skill"] }),
3902
+ agentConfig: makeAgent("worker", { skills: ["async-task-cwd-skill"] }),
3810
3903
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3811
3904
  cwd: taskCwd,
3812
3905
  artifactConfig: {
@@ -3842,15 +3935,15 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3842
3935
  it("injects agent-file-relative local skills into background single child prompts", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
3843
3936
  mockPi.onCall({ output: "Done asynchronously" });
3844
3937
  const id = `async-local-skill-${Date.now().toString(36)}`;
3845
- const agentFile = path.join(tempDir, "agents", "builder", "builder.md");
3938
+ const agentFile = path.join(tempDir, "agents", "worker", "worker.md");
3846
3939
  const skillFile = path.join(path.dirname(agentFile), "skills", "local", "SKILL.md");
3847
3940
  fs.mkdirSync(path.dirname(skillFile), { recursive: true });
3848
3941
  fs.writeFileSync(skillFile, "---\ndescription: async local skill\n---\nLocal skill body\n", "utf-8");
3849
3942
 
3850
3943
  executeAsyncSingle(id, {
3851
- agent: "builder",
3944
+ agent: "worker",
3852
3945
  task: "Do work",
3853
- agentConfig: makeAgent("builder", { filePath: agentFile, skills: ["local"], skillPath: ["./skills"] }),
3946
+ agentConfig: makeAgent("worker", { filePath: agentFile, skills: ["local"], skillPath: ["./skills"] }),
3854
3947
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3855
3948
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
3856
3949
  shareEnabled: false,
@@ -3896,9 +3989,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3896
3989
  it("background single runs report unavailable pi-subagents skill requests", () => {
3897
3990
  const id = `async-pi-subagents-skill-${Date.now().toString(36)}`;
3898
3991
  const result = executeAsyncSingle(id, {
3899
- agent: "builder",
3992
+ agent: "worker",
3900
3993
  task: "Do work",
3901
- agentConfig: makeAgent("builder"),
3994
+ agentConfig: makeAgent("worker"),
3902
3995
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3903
3996
  cwd: tempDir,
3904
3997
  artifactConfig: {
@@ -3922,8 +4015,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3922
4015
  it("background chains report unavailable pi-subagents skill requests", () => {
3923
4016
  const id = `async-chain-pi-subagents-skill-${Date.now().toString(36)}`;
3924
4017
  const result = executeAsyncChain(id, {
3925
- chain: [{ agent: "builder", task: "Do work", skill: ["pi-subagents"] }],
3926
- agents: [makeAgent("builder")],
4018
+ chain: [{ agent: "worker", task: "Do work", skill: ["pi-subagents"] }],
4019
+ agents: [makeAgent("worker")],
3927
4020
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3928
4021
  cwd: tempDir,
3929
4022
  artifactConfig: {
@@ -3954,8 +4047,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
3954
4047
  try {
3955
4048
  writePackageSkill(path.join(chainCwd, "packages", "app"), "async-chain-step-skill");
3956
4049
  executeAsyncChain(id, {
3957
- chain: [{ agent: "builder", task: "Do work", cwd: "packages/app", skill: ["async-chain-step-skill"] }],
3958
- agents: [makeAgent("builder")],
4050
+ chain: [{ agent: "worker", task: "Do work", cwd: "packages/app", skill: ["async-chain-step-skill"] }],
4051
+ agents: [makeAgent("worker")],
3959
4052
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
3960
4053
  cwd: chainCwd,
3961
4054
  artifactConfig: {
@@ -4053,9 +4146,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4053
4146
  fs.mkdirSync(path.join(TEMP_ROOT_DIR, `async-cfg-${id}.json`), { recursive: true });
4054
4147
 
4055
4148
  const result = executeAsyncSingle(id, {
4056
- agent: "builder",
4149
+ agent: "worker",
4057
4150
  task: "Do work",
4058
- agentConfig: makeAgent("builder"),
4151
+ agentConfig: makeAgent("worker"),
4059
4152
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4060
4153
  artifactConfig: {
4061
4154
  enabled: false,
@@ -4080,9 +4173,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4080
4173
  const missingCwd = path.join(tempDir, "missing-cwd");
4081
4174
 
4082
4175
  const singleResult = executeAsyncSingle(id, {
4083
- agent: "builder",
4176
+ agent: "worker",
4084
4177
  task: "Do work",
4085
- agentConfig: makeAgent("builder"),
4178
+ agentConfig: makeAgent("worker"),
4086
4179
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4087
4180
  cwd: missingCwd,
4088
4181
  artifactConfig: {
@@ -4104,8 +4197,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4104
4197
 
4105
4198
  const chainId = `async-missing-cwd-chain-${Date.now().toString(36)}`;
4106
4199
  const chainResult = executeAsyncChain(chainId, {
4107
- chain: [{ agent: "builder", task: "Do work" }],
4108
- agents: [makeAgent("builder")],
4200
+ chain: [{ agent: "worker", task: "Do work" }],
4201
+ agents: [makeAgent("worker")],
4109
4202
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4110
4203
  cwd: missingCwd,
4111
4204
  artifactConfig: {
@@ -4135,9 +4228,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4135
4228
  try {
4136
4229
  const id = `async-spawn-fail-${Date.now().toString(36)}`;
4137
4230
  const result = executeAsyncSingle(id, {
4138
- agent: "builder",
4231
+ agent: "worker",
4139
4232
  task: "Do work",
4140
- agentConfig: makeAgent("builder"),
4233
+ agentConfig: makeAgent("worker"),
4141
4234
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4142
4235
  artifactConfig: {
4143
4236
  enabled: false,
@@ -4172,8 +4265,8 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4172
4265
  fs.mkdirSync(path.join(TEMP_ROOT_DIR, `async-cfg-${id}.json`), { recursive: true });
4173
4266
 
4174
4267
  const result = executeAsyncChain(id, {
4175
- chain: [{ agent: "builder", task: "Do work" }],
4176
- agents: [makeAgent("builder")],
4268
+ chain: [{ agent: "worker", task: "Do work" }],
4269
+ agents: [makeAgent("worker")],
4177
4270
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4178
4271
  artifactConfig: {
4179
4272
  enabled: false,
@@ -4203,9 +4296,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4203
4296
 
4204
4297
  const start = Date.now();
4205
4298
  executeAsyncSingle(id, {
4206
- agent: "builder",
4299
+ agent: "worker",
4207
4300
  task: "Do work",
4208
- agentConfig: makeAgent("builder"),
4301
+ agentConfig: makeAgent("worker"),
4209
4302
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4210
4303
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4211
4304
  shareEnabled: false,
@@ -4237,9 +4330,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4237
4330
 
4238
4331
  const start = Date.now();
4239
4332
  executeAsyncSingle(id, {
4240
- agent: "builder",
4333
+ agent: "worker",
4241
4334
  task: "Do work",
4242
- agentConfig: makeAgent("builder"),
4335
+ agentConfig: makeAgent("worker"),
4243
4336
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4244
4337
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4245
4338
  shareEnabled: false,
@@ -4269,9 +4362,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4269
4362
 
4270
4363
  const start = Date.now();
4271
4364
  executeAsyncSingle(id, {
4272
- agent: "builder",
4365
+ agent: "worker",
4273
4366
  task: "Do work",
4274
- agentConfig: makeAgent("builder"),
4367
+ agentConfig: makeAgent("worker"),
4275
4368
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4276
4369
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4277
4370
  shareEnabled: false,
@@ -4304,9 +4397,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4304
4397
 
4305
4398
  const start = Date.now();
4306
4399
  executeAsyncSingle(id, {
4307
- agent: "builder",
4400
+ agent: "worker",
4308
4401
  task: "Do work",
4309
- agentConfig: makeAgent("builder"),
4402
+ agentConfig: makeAgent("worker"),
4310
4403
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4311
4404
  artifactConfig: {
4312
4405
  enabled: false,
@@ -4349,9 +4442,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4349
4442
 
4350
4443
  const start = Date.now();
4351
4444
  executeAsyncSingle(id, {
4352
- agent: "explorer",
4445
+ agent: "scout",
4353
4446
  task: "Inspect something",
4354
- agentConfig: makeAgent("explorer"),
4447
+ agentConfig: makeAgent("scout"),
4355
4448
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4356
4449
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4357
4450
  shareEnabled: false,
@@ -4394,9 +4487,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4394
4487
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
4395
4488
 
4396
4489
  executeAsyncSingle(id, {
4397
- agent: "builder",
4490
+ agent: "worker",
4398
4491
  task: "Do work",
4399
- agentConfig: makeAgent("builder"),
4492
+ agentConfig: makeAgent("worker"),
4400
4493
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4401
4494
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4402
4495
  shareEnabled: false,
@@ -4431,9 +4524,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4431
4524
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
4432
4525
 
4433
4526
  executeAsyncSingle(id, {
4434
- agent: "explorer",
4527
+ agent: "scout",
4435
4528
  task: "Investigate behavior",
4436
- agentConfig: makeAgent("explorer"),
4529
+ agentConfig: makeAgent("scout"),
4437
4530
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4438
4531
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4439
4532
  shareEnabled: false,
@@ -4499,9 +4592,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4499
4592
  const eventsPath = path.join(asyncDir, "events.jsonl");
4500
4593
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
4501
4594
  executeAsyncSingle(id, {
4502
- agent: "builder",
4595
+ agent: "worker",
4503
4596
  task: "Run the command",
4504
- agentConfig: makeAgent("builder"),
4597
+ agentConfig: makeAgent("worker"),
4505
4598
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4506
4599
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4507
4600
  shareEnabled: false,
@@ -4541,23 +4634,25 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4541
4634
  });
4542
4635
 
4543
4636
  it("subagent_wait wakes when an async child is waiting on contact_supervisor", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
4637
+ const id = `async-supervisor-attention-${Date.now().toString(36)}`;
4638
+ const replyReleasePath = path.join(tempDir, `${id}.reply`);
4639
+ const finalReleasePath = path.join(tempDir, `${id}.final`);
4544
4640
  mockPi.onCall({
4545
4641
  steps: [
4546
4642
  { jsonl: [events.toolStart("contact_supervisor", { reason: "need_decision", message: "Need a decision" })] },
4547
- { delay: 5_000, jsonl: [events.toolEnd("contact_supervisor"), events.toolResult("contact_supervisor", "**Reply from supervisor:**\nProceed")] },
4548
- { delay: 2_500, jsonl: [events.assistantMessage("Done")] },
4643
+ { waitForPath: replyReleasePath, jsonl: [events.toolEnd("contact_supervisor"), events.toolResult("contact_supervisor", "**Reply from supervisor:**\nProceed")] },
4644
+ { waitForPath: finalReleasePath, jsonl: [events.assistantMessage("Done")] },
4549
4645
  ],
4550
4646
  });
4551
4647
 
4552
- const id = `async-supervisor-attention-${Date.now().toString(36)}`;
4553
4648
  const asyncDir = path.join(ASYNC_DIR, id);
4554
4649
  const eventsPath = path.join(asyncDir, "events.jsonl");
4555
4650
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
4556
4651
  const statusPath = path.join(asyncDir, "status.json");
4557
4652
  executeAsyncSingle(id, {
4558
- agent: "builder",
4653
+ agent: "worker",
4559
4654
  task: "Ask the supervisor for a blocking decision",
4560
- agentConfig: makeAgent("builder"),
4655
+ agentConfig: makeAgent("worker"),
4561
4656
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4562
4657
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4563
4658
  shareEnabled: false,
@@ -4573,55 +4668,71 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4573
4668
  },
4574
4669
  });
4575
4670
 
4576
- const attentionDeadline = Date.now() + 10_000;
4577
- let statusDuringAttention: AsyncStatusPayload | undefined;
4578
- while (Date.now() < attentionDeadline && !fs.existsSync(resultPath)) {
4579
- if (fs.existsSync(statusPath)) {
4580
- const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
4581
- if (nextStatus.currentTool === "contact_supervisor" && nextStatus.activityState === "needs_attention") {
4582
- statusDuringAttention = nextStatus;
4583
- break;
4671
+ const releaseMockChild = () => {
4672
+ if (!fs.existsSync(replyReleasePath)) fs.writeFileSync(replyReleasePath, "release", "utf-8");
4673
+ if (!fs.existsSync(finalReleasePath)) fs.writeFileSync(finalReleasePath, "release", "utf-8");
4674
+ };
4675
+ const releaseSupervisorReply = () => {
4676
+ if (!fs.existsSync(replyReleasePath)) fs.writeFileSync(replyReleasePath, "release", "utf-8");
4677
+ };
4678
+ try {
4679
+ const attentionDeadline = Date.now() + 10_000;
4680
+ let statusDuringAttention: AsyncStatusPayload | undefined;
4681
+ while (Date.now() < attentionDeadline && !fs.existsSync(resultPath)) {
4682
+ if (fs.existsSync(statusPath)) {
4683
+ const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
4684
+ if (nextStatus.currentTool === "contact_supervisor" && nextStatus.activityState === "needs_attention") {
4685
+ statusDuringAttention = nextStatus;
4686
+ break;
4687
+ }
4584
4688
  }
4689
+ await new Promise((resolve) => setTimeout(resolve, 100));
4585
4690
  }
4586
- await new Promise((resolve) => setTimeout(resolve, 100));
4587
- }
4588
- assert.ok(statusDuringAttention, "expected status.json to expose the blocking supervisor request");
4691
+ assert.ok(statusDuringAttention, "expected status.json to expose the blocking supervisor request");
4589
4692
 
4590
- const waitResult = await waitForSubagents({ id, timeoutMs: 3_500 }, undefined, {
4591
- state: { currentSessionId: "session-1", foregroundRuns: new Map(), asyncJobs: new Map(), cleanupTimers: new Map(), resultFileCoalescer: new Map() },
4592
- pollIntervalMs: 100,
4593
- events: createEventBus(),
4594
- });
4595
- const waitText = waitResult.content[0]?.text ?? "";
4596
- assert.equal(waitResult.isError, undefined);
4597
- assert.match(waitText, /attention required/i);
4598
- assert.match(waitText, new RegExp(id));
4599
- assert.match(waitText, /intercom\(\{ action: "pending" \}\)/);
4600
- assert.equal(fs.existsSync(resultPath), false, "wait should return before the child completes");
4693
+ try {
4694
+ const waitResult = await waitForSubagents({ id, timeoutMs: 3_500 }, undefined, {
4695
+ state: { currentSessionId: "session-1", foregroundRuns: new Map(), asyncJobs: new Map(), cleanupTimers: new Map(), resultFileCoalescer: new Map() },
4696
+ pollIntervalMs: 100,
4697
+ events: createEventBus(),
4698
+ });
4699
+ const waitText = waitResult.content[0]?.text ?? "";
4700
+ assert.equal(waitResult.isError, undefined);
4701
+ assert.match(waitText, /attention required/i);
4702
+ assert.match(waitText, new RegExp(id));
4703
+ assert.match(waitText, /intercom\(\{ action: "pending" \}\)/);
4704
+ assert.equal(fs.existsSync(resultPath), false, "wait should return before the child completes");
4705
+ } finally {
4706
+ releaseSupervisorReply();
4707
+ }
4601
4708
 
4602
- const eventText = fs.existsSync(eventsPath) ? fs.readFileSync(eventsPath, "utf-8") : "";
4603
- assert.match(eventText, /"type":"needs_attention"/);
4604
- assert.match(eventText, /"reason":"supervisor_request"/);
4605
- assert.equal(statusDuringAttention.activityState, "needs_attention");
4606
- assert.equal(statusDuringAttention.steps?.[0]?.activityState, "needs_attention");
4607
- assert.equal(statusDuringAttention.currentTool, "contact_supervisor");
4608
- assert.equal(statusDuringAttention.steps?.[0]?.currentTool, "contact_supervisor");
4609
-
4610
- const clearDeadline = Date.now() + 10_000;
4611
- let statusAfterReply: AsyncStatusPayload | undefined;
4612
- while (Date.now() < clearDeadline && !fs.existsSync(resultPath)) {
4613
- const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
4614
- if (nextStatus.state === "running" && !nextStatus.currentTool && !nextStatus.steps?.[0]?.currentTool) {
4615
- statusAfterReply = nextStatus;
4616
- break;
4709
+ const eventText = fs.existsSync(eventsPath) ? fs.readFileSync(eventsPath, "utf-8") : "";
4710
+ assert.match(eventText, /"type":"needs_attention"/);
4711
+ assert.match(eventText, /"reason":"supervisor_request"/);
4712
+ assert.equal(statusDuringAttention.activityState, "needs_attention");
4713
+ assert.equal(statusDuringAttention.steps?.[0]?.activityState, "needs_attention");
4714
+ assert.equal(statusDuringAttention.currentTool, "contact_supervisor");
4715
+ assert.equal(statusDuringAttention.steps?.[0]?.currentTool, "contact_supervisor");
4716
+
4717
+ const clearDeadline = Date.now() + 10_000;
4718
+ let statusAfterReply: AsyncStatusPayload | undefined;
4719
+ while (Date.now() < clearDeadline && !fs.existsSync(resultPath)) {
4720
+ const nextStatus = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatusPayload;
4721
+ if (nextStatus.state === "running" && !nextStatus.currentTool && !nextStatus.steps?.[0]?.currentTool) {
4722
+ statusAfterReply = nextStatus;
4723
+ break;
4724
+ }
4725
+ await new Promise((resolve) => setTimeout(resolve, 100));
4617
4726
  }
4618
- await new Promise((resolve) => setTimeout(resolve, 100));
4619
- }
4620
- assert.ok(statusAfterReply, "expected the child to keep running after the supervisor reply");
4621
- assert.equal(statusAfterReply.activityState, undefined);
4622
- assert.equal(statusAfterReply.steps?.[0]?.activityState, undefined);
4727
+ assert.ok(statusAfterReply, "expected the child to keep running after the supervisor reply");
4728
+ assert.equal(statusAfterReply.activityState, undefined);
4729
+ assert.equal(statusAfterReply.steps?.[0]?.activityState, undefined);
4623
4730
 
4624
- await waitForAsyncResultFile(id);
4731
+ fs.writeFileSync(finalReleasePath, "release", "utf-8");
4732
+ await waitForAsyncResultFile(id);
4733
+ } finally {
4734
+ releaseMockChild();
4735
+ }
4625
4736
  });
4626
4737
 
4627
4738
  it("background runs escalate repeated mutating tool failures", { skip: !isAsyncAvailable() ? "jiti not available" : undefined }, async () => {
@@ -4640,9 +4751,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4640
4751
  const resultPath = path.join(RESULTS_DIR, `${id}.json`);
4641
4752
 
4642
4753
  executeAsyncSingle(id, {
4643
- agent: "builder",
4754
+ agent: "worker",
4644
4755
  task: "Implement the approved fixes",
4645
- agentConfig: makeAgent("builder"),
4756
+ agentConfig: makeAgent("worker"),
4646
4757
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4647
4758
  artifactConfig: { enabled: false, includeInput: false, includeOutput: false, includeJsonl: false, includeMetadata: false, cleanupDays: 7 },
4648
4759
  shareEnabled: false,
@@ -4724,9 +4835,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4724
4835
  const sessionRoot = path.join(tempDir, "sessions");
4725
4836
 
4726
4837
  executeAsyncSingle(id, {
4727
- agent: "builder",
4838
+ agent: "worker",
4728
4839
  task: "Stream noisy diagnostics",
4729
- agentConfig: makeAgent("builder"),
4840
+ agentConfig: makeAgent("worker"),
4730
4841
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4731
4842
  artifactConfig: {
4732
4843
  enabled: false,
@@ -4775,9 +4886,9 @@ describe("async execution utilities", { skip: !available ? "pi packages not avai
4775
4886
  const sessionRoot = path.join(tempDir, "sessions");
4776
4887
 
4777
4888
  executeAsyncSingle(id, {
4778
- agent: "builder",
4889
+ agent: "worker",
4779
4890
  task: "Stream detailed progress",
4780
- agentConfig: makeAgent("builder"),
4891
+ agentConfig: makeAgent("worker"),
4781
4892
  ctx: { pi: { events: { emit() {} } }, cwd: tempDir, currentSessionId: "session-1" },
4782
4893
  artifactConfig: {
4783
4894
  enabled: false,