@selesai/code 0.9.11 → 0.9.13

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 (580) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/config.d.ts +4 -6
  3. package/dist/config.js +10 -14
  4. package/dist/core/agent-session.d.ts +0 -1
  5. package/dist/core/agent-session.js +3 -49
  6. package/dist/core/extensions/types.d.ts +0 -2
  7. package/dist/core/settings-manager.d.ts +0 -2
  8. package/dist/core/settings-manager.js +0 -3
  9. package/dist/core/tools/index.d.ts +0 -1
  10. package/dist/core/tools/index.js +0 -1
  11. package/dist/defaults/settings.json +2 -3
  12. package/dist/extensions/model-prompt-injector/index.test.ts +0 -10
  13. package/dist/extensions/model-prompt-injector/index.ts +0 -3
  14. package/dist/extensions/pi-intercom/CHANGELOG.md +53 -16
  15. package/dist/extensions/pi-intercom/README.md +41 -8
  16. package/dist/extensions/pi-intercom/banner.png +0 -0
  17. package/dist/extensions/pi-intercom/broker/broker.ts +498 -215
  18. package/dist/extensions/pi-intercom/broker/client.ts +39 -12
  19. package/dist/extensions/pi-intercom/broker/extension.test.ts +102 -3
  20. package/dist/extensions/pi-intercom/broker/paths.test.ts +2 -2
  21. package/dist/extensions/pi-intercom/broker/protocol.ts +26 -0
  22. package/dist/extensions/pi-intercom/broker/spawn.test.ts +5 -20
  23. package/dist/extensions/pi-intercom/config.test.ts +41 -0
  24. package/dist/extensions/pi-intercom/config.ts +21 -0
  25. package/dist/extensions/pi-intercom/extension-api.ts +40 -0
  26. package/dist/extensions/pi-intercom/index.ts +362 -26
  27. package/dist/extensions/pi-intercom/intercom.integration.test.ts +819 -13
  28. package/dist/extensions/pi-intercom/package.json +1 -1
  29. package/dist/extensions/pi-intercom/project-agent.ts +2 -2
  30. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +8 -110
  31. package/dist/extensions/pi-intercom/types.ts +32 -5
  32. package/dist/extensions/pi-intercom/ui/inline-message.ts +6 -0
  33. package/dist/extensions/pi-subagents/.github/assets/subagent-fleet-inspector.png +0 -0
  34. package/dist/extensions/pi-subagents/.github/workflows/release.yml +28 -0
  35. package/dist/extensions/pi-subagents/.github/workflows/test.yml +51 -0
  36. package/dist/extensions/pi-subagents/.greptile/config.json +33 -0
  37. package/dist/extensions/pi-subagents/.greptile/files.json +8 -0
  38. package/dist/extensions/pi-subagents/.greptile/rules.md +16 -0
  39. package/dist/extensions/pi-subagents/CHANGELOG.md +309 -87
  40. package/dist/extensions/pi-subagents/README.md +15 -17
  41. package/dist/extensions/pi-subagents/agents/oracle.md +3 -1
  42. package/dist/extensions/pi-subagents/agents/researcher.md +32 -13
  43. package/dist/extensions/pi-subagents/agents/reviewer.md +13 -2
  44. package/dist/extensions/pi-subagents/agents/scout.md +2 -2
  45. package/dist/extensions/pi-subagents/agents/worker.md +2 -1
  46. package/dist/extensions/pi-subagents/async-retention-discovery-worker.mjs +180 -0
  47. package/dist/extensions/pi-subagents/docs/configuration.md +106 -27
  48. package/dist/extensions/pi-subagents/docs/extension-api.md +85 -21
  49. package/dist/extensions/pi-subagents/docs/missions.md +41 -4
  50. package/dist/extensions/pi-subagents/docs/models.md +52 -9
  51. package/dist/extensions/pi-subagents/docs/observability.md +103 -16
  52. package/dist/extensions/pi-subagents/docs/tool-reference.md +60 -45
  53. package/dist/extensions/pi-subagents/docs/watchdog.md +60 -3
  54. package/dist/extensions/pi-subagents/docs/workflows.md +181 -7
  55. package/dist/extensions/pi-subagents/index.ts +10 -1
  56. package/dist/extensions/pi-subagents/install.mjs +4 -4
  57. package/dist/extensions/pi-subagents/package-lock.json +1483 -1470
  58. package/dist/extensions/pi-subagents/package.json +6 -3
  59. package/dist/extensions/pi-subagents/prompts/council.md +60 -0
  60. package/dist/extensions/pi-subagents/prompts/parallel-review.md +5 -1
  61. package/dist/extensions/pi-subagents/prompts/review-loop.md +9 -5
  62. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +254 -0
  63. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +18 -17
  64. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
  65. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +107 -140
  66. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
  67. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
  68. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +44 -92
  69. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +261 -368
  70. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +6 -2
  71. package/dist/extensions/pi-subagents/src/agents/agents.ts +388 -85
  72. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +14 -0
  73. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +418 -0
  74. package/dist/extensions/pi-subagents/src/agents/skills.ts +1 -1
  75. package/dist/extensions/pi-subagents/src/api/agents.ts +7 -0
  76. package/dist/extensions/pi-subagents/src/api/external-job-provider.ts +195 -0
  77. package/dist/extensions/pi-subagents/src/api/preflight.ts +88 -27
  78. package/dist/extensions/pi-subagents/src/api/project-panes.ts +2 -0
  79. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -0
  80. package/dist/extensions/pi-subagents/src/extension/config.ts +20 -11
  81. package/dist/extensions/pi-subagents/src/extension/doctor.ts +14 -6
  82. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -4
  83. package/dist/extensions/pi-subagents/src/extension/index.ts +270 -187
  84. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +65 -28
  85. package/dist/extensions/pi-subagents/src/extension/rpc.ts +120 -11
  86. package/dist/extensions/pi-subagents/src/extension/schemas.ts +46 -64
  87. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +51 -37
  88. package/dist/extensions/pi-subagents/src/extension/tool-result.ts +19 -0
  89. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -2
  90. package/dist/extensions/pi-subagents/src/inspectors/herdr/client.ts +3 -3
  91. package/dist/extensions/pi-subagents/src/inspectors/herdr/focus.ts +55 -0
  92. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -1
  93. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +230 -46
  94. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +26 -4
  95. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +32 -10
  96. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +6 -1
  97. package/dist/extensions/pi-subagents/src/missions/store.ts +4 -9
  98. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +2 -2
  99. package/dist/extensions/pi-subagents/src/policy/authority.ts +2 -0
  100. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +3 -1
  101. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +31 -8
  102. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +210 -52
  103. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +63 -33
  104. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +71 -17
  105. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +886 -0
  106. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +14 -5
  107. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +49 -56
  108. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +1 -0
  109. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +3 -33
  110. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +5 -0
  111. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +5 -1
  112. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +22 -12
  113. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +105 -71
  114. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +68 -19
  115. package/dist/extensions/pi-subagents/src/runs/background/index-segment.ts +59 -0
  116. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +443 -0
  117. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +94 -10
  118. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +158 -90
  119. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +141 -53
  120. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +8 -5
  121. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +14 -4
  122. package/dist/extensions/pi-subagents/src/runs/background/run-id-query.ts +7 -0
  123. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +13 -14
  124. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +46 -25
  125. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +823 -0
  126. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +9 -3
  127. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +11 -1
  128. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +594 -216
  129. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +17 -13
  130. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +138 -0
  131. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +23 -4
  132. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +82 -4
  133. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +5 -3
  134. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +21 -14
  135. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +23 -2
  136. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +153 -58
  137. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -9
  138. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1283 -1657
  139. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +274 -0
  140. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +6 -5
  141. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +59 -10
  142. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  143. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +1 -3
  144. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +36 -0
  145. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +52 -2
  146. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  147. package/dist/extensions/pi-subagents/src/runs/shared/extension-bindings.ts +78 -0
  148. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +2 -0
  149. package/dist/extensions/pi-subagents/src/runs/shared/external-job-bridge.ts +460 -0
  150. package/dist/extensions/pi-subagents/src/runs/shared/external-job-runner.ts +389 -0
  151. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +10 -0
  152. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
  153. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +243 -0
  154. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +201 -85
  155. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +106 -39
  156. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +145 -0
  157. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +150 -27
  158. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +46 -4
  159. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +19 -15
  160. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +5 -1
  161. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +86 -2
  162. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +9 -9
  163. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +0 -6
  164. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +18 -4
  165. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +51 -7
  166. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +62 -21
  167. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +1 -3
  168. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -3
  169. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +1 -23
  170. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +14 -2
  171. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +2 -2
  172. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +22 -2
  173. package/dist/extensions/pi-subagents/src/shared/capacity-resilient-json.ts +102 -0
  174. package/dist/extensions/pi-subagents/src/shared/completion-owner.ts +14 -0
  175. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +49 -1
  176. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +59 -1
  177. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +12 -5
  178. package/dist/extensions/pi-subagents/src/shared/model-info.ts +20 -0
  179. package/dist/extensions/pi-subagents/src/shared/prompt-resources.ts +0 -40
  180. package/dist/extensions/pi-subagents/src/shared/settings.ts +11 -30
  181. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +52 -0
  182. package/dist/extensions/pi-subagents/src/shared/types.ts +228 -49
  183. package/dist/extensions/pi-subagents/src/shared/utils.ts +11 -3
  184. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +10 -0
  185. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +0 -162
  186. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +2 -5
  187. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +105 -13
  188. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +6 -36
  189. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +829 -0
  190. package/dist/extensions/pi-subagents/src/tui/fleet.ts +1415 -0
  191. package/dist/extensions/pi-subagents/src/tui/render.ts +78 -7
  192. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +1 -0
  194. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +59 -51
  195. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +8 -2
  196. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +2 -1
  197. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +4 -4
  198. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +471 -86
  199. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +140 -0
  200. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +3 -0
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +2 -0
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +17 -0
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +57 -0
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +16 -0
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +15 -0
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +18 -0
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +9 -0
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +10 -0
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +10 -0
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +20 -0
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +3 -0
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +96 -0
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +119 -0
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +85 -0
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +620 -0
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +5 -0
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +56 -0
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +32 -0
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +49 -0
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +93 -0
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +128 -0
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +7 -0
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +38 -0
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +3 -0
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +15 -0
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +9 -0
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +29 -0
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +2 -0
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +22 -0
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +37 -0
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +34 -0
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +12 -0
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +23 -0
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +171 -0
  236. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +1261 -0
  237. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +20 -0
  238. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +54 -0
  239. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +22 -0
  240. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +13 -0
  241. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +358 -0
  242. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +77 -0
  243. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +512 -0
  244. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +43 -0
  245. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +121 -0
  246. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +82 -0
  247. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +17 -0
  248. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +7 -0
  249. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +210 -0
  250. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +15 -0
  251. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +54 -0
  252. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +4 -0
  253. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +55 -0
  254. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +2 -0
  255. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +5 -0
  256. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +30 -0
  257. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +206 -0
  258. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +15 -0
  259. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +106 -0
  260. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +19 -0
  261. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +356 -0
  262. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +297 -0
  263. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +60 -0
  264. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +15 -0
  265. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +18 -0
  266. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +28 -0
  267. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +3 -0
  268. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +10 -0
  269. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +68 -0
  270. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +106 -0
  271. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +51 -0
  272. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +6 -0
  273. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +35 -0
  274. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +37 -0
  275. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +40 -0
  276. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +37 -0
  277. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +52 -0
  278. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +10 -0
  279. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +35 -0
  280. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +24 -0
  281. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  282. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +70 -0
  283. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +26 -0
  284. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +36 -0
  285. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +19 -0
  286. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +3 -0
  287. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +61 -0
  288. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +23 -0
  289. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +3 -0
  290. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +10 -0
  291. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +42 -0
  292. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +35 -0
  293. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +12 -0
  294. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +33 -0
  295. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +9 -0
  296. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +34 -0
  297. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +22 -0
  298. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  299. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  300. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  301. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  302. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +102 -0
  303. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  304. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  305. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  306. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +20 -0
  307. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  308. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +12 -0
  309. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  310. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  311. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +22 -0
  312. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +23 -0
  313. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  314. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  315. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +32 -0
  316. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +34 -0
  317. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  318. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  319. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +54 -0
  320. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
  321. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  322. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  323. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +95 -0
  324. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +77 -0
  325. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  326. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  327. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  328. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  329. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  330. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  331. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +94 -0
  332. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  333. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  334. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +14 -0
  335. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  336. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +394 -0
  337. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +12 -0
  338. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
  339. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +120 -0
  340. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +28 -0
  341. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +17 -0
  342. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +246 -0
  343. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +20 -0
  344. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +457 -0
  345. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +8 -0
  346. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +3 -0
  347. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +2 -0
  348. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +22 -0
  349. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +18 -0
  350. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +11 -0
  351. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +11 -0
  352. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +4 -0
  353. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +4 -0
  354. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +5 -0
  355. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +8 -0
  356. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +5 -0
  357. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +26 -0
  358. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +7 -0
  359. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +10 -0
  360. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +18 -0
  361. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +30 -0
  362. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +2 -0
  363. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +16 -0
  364. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +3 -0
  365. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +3 -0
  366. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +9 -0
  367. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +31 -0
  368. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +21 -0
  369. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +2 -0
  370. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +31 -0
  371. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +5 -0
  372. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +12 -0
  373. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +3 -0
  374. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +15 -0
  375. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +3 -0
  376. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/index.mjs +1 -1
  377. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +0 -54
  378. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1053 -414
  379. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +194 -4
  380. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +59 -4
  381. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +0 -77
  382. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +107 -6
  383. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +211 -897
  384. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +265 -375
  385. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +77 -7
  386. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +1 -1
  387. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +10 -10
  388. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +547 -30
  389. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +2112 -310
  390. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +274 -34
  391. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +1 -1
  392. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +20 -3
  393. package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +9 -0
  394. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +30 -5
  395. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  396. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +4 -2
  397. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +1 -0
  398. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +197 -0
  399. package/dist/extensions/pi-subagents/test/unit/agent-contract.test.ts +14 -0
  400. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
  401. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +204 -27
  402. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +444 -145
  403. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +256 -4
  404. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +9 -7
  405. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +152 -22
  406. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +29 -0
  407. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +205 -23
  408. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +579 -0
  409. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +24 -0
  410. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +1 -0
  411. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +3 -3
  412. package/dist/extensions/pi-subagents/test/unit/capacity-resilient-json.test.ts +155 -0
  413. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
  414. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +7 -0
  415. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +123 -0
  416. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +81 -3
  417. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +109 -7
  418. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
  419. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +5 -2
  420. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -2
  421. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +79 -0
  422. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +22 -0
  423. package/dist/extensions/pi-subagents/test/unit/external-job-runner.test.ts +961 -0
  424. package/dist/extensions/pi-subagents/test/unit/file-system-retry.test.ts +64 -0
  425. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +1110 -0
  426. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +1664 -0
  427. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +81 -1
  428. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +138 -11
  429. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +41 -2
  430. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +210 -79
  431. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +95 -0
  432. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +596 -0
  433. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  434. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +3 -5
  435. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +167 -0
  436. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +319 -18
  437. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +78 -0
  438. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +19 -5
  439. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +44 -0
  440. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +129 -0
  441. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +231 -2
  442. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +28 -1
  443. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +106 -2
  444. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +204 -7
  445. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +59 -4
  446. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +2 -6
  447. package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +34 -2
  448. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -6
  449. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +12 -3
  450. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +56 -61
  451. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +416 -1
  452. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +9 -2
  453. package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +42 -0
  454. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +21 -30
  455. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +86 -11
  456. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +49 -1
  457. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +233 -5
  458. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +28 -5
  459. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +323 -20
  460. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +66 -10
  461. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -5
  462. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +174 -0
  463. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +679 -0
  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 +55 -44
  466. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +399 -84
  467. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +13 -0
  468. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  469. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +15 -10
  470. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +11 -7
  471. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -0
  472. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +40 -1
  473. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +10 -2
  474. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +38 -3
  475. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +110 -12
  476. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +204 -19
  477. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +25 -0
  478. package/dist/extensions/pi-subagents/test/unit/thinking-ceiling.test.ts +73 -0
  479. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +90 -50
  480. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +1 -2
  481. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +234 -7
  482. package/dist/extensions/pi-subagents/test/unit/watch-strategy.test.ts +24 -0
  483. package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +36 -0
  484. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +37 -9
  485. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +302 -0
  486. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +167 -7
  487. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +113 -0
  488. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +6 -6
  489. package/dist/extensions/ponytail/index.js +31 -25
  490. package/dist/extensions/ponytail/ponytail-config.cjs +34 -7
  491. package/dist/extensions/ponytail/ponytail-instructions.cjs +7 -9
  492. package/dist/extensions/ponytail/test/extension.test.js +7 -8
  493. package/dist/extensions/ponytail/test/helpers.test.js +1 -1
  494. package/dist/skills/brandkit/SKILL.md +799 -0
  495. package/dist/skills/design-taste-frontend/SKILL.md +1207 -0
  496. package/dist/skills/design-taste-frontend-v1/SKILL.md +227 -0
  497. package/dist/skills/full-output-enforcement/SKILL.md +50 -0
  498. package/dist/skills/gpt-taste/SKILL.md +75 -0
  499. package/dist/skills/high-end-visual-design/SKILL.md +99 -0
  500. package/dist/skills/image-to-code/SKILL.md +1229 -0
  501. package/dist/skills/imagegen-frontend-mobile/SKILL.md +1466 -0
  502. package/dist/skills/imagegen-frontend-web/SKILL.md +988 -0
  503. package/dist/skills/industrial-brutalist-ui/SKILL.md +93 -0
  504. package/dist/skills/minimalist-ui/SKILL.md +86 -0
  505. package/dist/skills/pi-subagents/SKILL.md +18 -17
  506. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +31 -9
  507. package/dist/skills/pi-subagents/references/execution-controls.md +95 -143
  508. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +9 -12
  509. package/dist/skills/pi-subagents/references/multi-lane-orchestration.md +39 -0
  510. package/dist/skills/pi-subagents/references/prompting-and-roles.md +45 -94
  511. package/dist/skills/ponytail/SKILL.md +12 -8
  512. package/dist/skills/ponytail-audit/SKILL.md +0 -1
  513. package/dist/skills/ponytail-debt/SKILL.md +0 -1
  514. package/dist/skills/ponytail-gain/SKILL.md +0 -1
  515. package/dist/skills/ponytail-help/SKILL.md +4 -3
  516. package/dist/skills/ponytail-review/SKILL.md +0 -1
  517. package/dist/skills/redesign-existing-projects/SKILL.md +179 -0
  518. package/dist/skills/stitch-design-taste/SKILL.md +185 -0
  519. package/dist/skills/unlazy/CHANGELOG.md +100 -0
  520. package/dist/skills/unlazy/CONTRIBUTING.md +66 -0
  521. package/dist/skills/unlazy/LICENSE +21 -0
  522. package/dist/skills/unlazy/README.md +242 -0
  523. package/dist/skills/unlazy/SECURITY.md +70 -0
  524. package/dist/skills/unlazy/SKILL.md +100 -0
  525. package/dist/skills/unlazy/package.json +13 -0
  526. package/dist/skills/unlazy/references/dispatch.md +82 -0
  527. package/dist/skills/unlazy/references/gates.md +132 -0
  528. package/dist/skills/unlazy/references/method.md +49 -0
  529. package/dist/skills/unlazy/references/orchestration.md +99 -0
  530. package/dist/skills/unlazy/references/parallel.md +123 -0
  531. package/dist/skills/unlazy/references/token-economy.md +39 -0
  532. package/dist/skills/unlazy/research/validation-protocol.md +81 -0
  533. package/dist/skills/unlazy/scripts/dispatch-check.mjs +108 -0
  534. package/dist/skills/unlazy/scripts/gate-check.mjs +894 -0
  535. package/dist/skills/unlazy/scripts/gate-lint.mjs +178 -0
  536. package/dist/skills/unlazy/scripts/install-hooks.mjs +206 -0
  537. package/dist/skills/unlazy/scripts/lib/check-supervisor.mjs +46 -0
  538. package/dist/skills/unlazy/scripts/lib/dispatch.mjs +283 -0
  539. package/dist/skills/unlazy/scripts/lib/gates.mjs +629 -0
  540. package/dist/skills/unlazy/scripts/lib/process-tree.mjs +161 -0
  541. package/dist/skills/unlazy/scripts/lib/regex-worker.mjs +9 -0
  542. package/dist/skills/unlazy/scripts/stop-hook.mjs +174 -0
  543. package/dist/skills/unlazy/templates/PLAN.md +65 -0
  544. package/dist/skills/unlazy/templates/gates-leaf.md +51 -0
  545. package/dist/skills/unlazy/templates/gates-node.md +50 -0
  546. package/dist/skills/unlazy/tests/contract-tests.mjs +127 -0
  547. package/dist/skills/unlazy/tests/dispatch-tests.mjs +481 -0
  548. package/dist/skills/unlazy/tests/hardening-tests.mjs +903 -0
  549. package/dist/skills/unlazy/tests/lint-tests.mjs +242 -0
  550. package/dist/skills/unlazy/tests/run-tests.mjs +554 -0
  551. package/dist/skills/unlazy/tests/self-check.mjs +165 -0
  552. package/dist/skills/unlazy/tests/stress-tests.mjs +356 -0
  553. package/dist/skills/web-design-guidelines/SKILL.md +40 -0
  554. package/docs/settings.md +0 -5
  555. package/package.json +1 -1
  556. package/dist/core/tools/schema-prune.d.ts +0 -16
  557. package/dist/core/tools/schema-prune.js +0 -39
  558. package/dist/core/tools/schema-prune.test.d.ts +0 -1
  559. package/dist/core/tools/schema-prune.test.js +0 -111
  560. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +0 -172
  561. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +0 -55
  562. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +0 -61
  563. package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +0 -132
  564. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +0 -181
  565. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +0 -1354
  566. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +0 -1581
  567. package/dist/extensions/pi-subagents/src/shared/env.ts +0 -16
  568. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +0 -71
  569. package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +0 -136
  570. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +0 -41
  571. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +0 -466
  572. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +0 -1859
  573. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +0 -733
  574. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +0 -63
  575. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +0 -326
  576. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +0 -103
  577. package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +0 -127
  578. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +0 -73
  579. package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +0 -213
  580. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +0 -65
@@ -1,1859 +0,0 @@
1
- /**
2
- * Integration tests for chain execution (sequential and parallel steps).
3
- *
4
- * Uses the local createMockPi() harness to simulate subagent processes.
5
- * Tests the full chain pipeline: template resolution → spawn → output capture
6
- * → {previous} passing.
7
- *
8
- * Requires pi packages to be importable. Skips gracefully if unavailable.
9
- */
10
-
11
- import { describe, it, before, after, beforeEach, afterEach } from "node:test";
12
- import assert from "node:assert/strict";
13
- import { spawnSync } from "node:child_process";
14
- import * as fs from "node:fs";
15
- import * as path from "node:path";
16
- import type { MockPi } from "../support/helpers.ts";
17
- import {
18
- createMockPi,
19
- createTempDir,
20
- createEventBus,
21
- removeTempDir,
22
- makeAgent,
23
- makeMinimalCtx,
24
- tryImport,
25
- events,
26
- } from "../support/helpers.ts";
27
- import { INTERCOM_DETACH_REQUEST_EVENT } from "../../src/shared/types.ts";
28
- import { PERMISSION_AUDIT_PATH_ENV, PERMISSION_POLICY_ENV } from "../../src/runs/shared/permissions.ts";
29
-
30
- interface TestSequentialStep {
31
- agent: string;
32
- task?: string;
33
- phase?: string;
34
- label?: string;
35
- as?: string;
36
- outputSchema?: Record<string, unknown>;
37
- model?: string;
38
- output?: string | false;
39
- outputMode?: "inline" | "file-only";
40
- reads?: string[] | false;
41
- skill?: string | string[] | false;
42
- progress?: boolean;
43
- cwd?: string;
44
- acceptance?: unknown;
45
- agentContract?: { version: 1 };
46
- gateOn?: "execution" | "acceptance";
47
- }
48
-
49
- interface TestParallelTask {
50
- agent: string;
51
- task?: string;
52
- phase?: string;
53
- label?: string;
54
- as?: string;
55
- outputSchema?: Record<string, unknown>;
56
- model?: string;
57
- output?: string | false;
58
- outputMode?: "inline" | "file-only";
59
- reads?: string[] | false;
60
- skill?: string | string[] | false;
61
- progress?: boolean;
62
- cwd?: string;
63
- acceptance?: unknown;
64
- agentContract?: { version: 1 };
65
- gateOn?: "execution" | "acceptance";
66
- }
67
-
68
- type TestChainStep = { checkpoint: string; message?: string; label?: string; phase?: string } | TestSequentialStep | {
69
- parallel: TestParallelTask[];
70
- concurrency?: number;
71
- failFast?: boolean;
72
- worktree?: boolean;
73
- cwd?: string;
74
- agentContract?: { version: 1 };
75
- gateOn?: "execution" | "acceptance";
76
- } | {
77
- expand: {
78
- from: { output: string; path: string };
79
- item?: string;
80
- key?: string;
81
- maxItems?: number;
82
- onEmpty?: "skip" | "fail";
83
- };
84
- parallel: TestParallelTask;
85
- collect: { as: string; outputSchema?: Record<string, unknown> };
86
- concurrency?: number;
87
- failFast?: boolean;
88
- label?: string;
89
- acceptance?: unknown;
90
- agentContract?: { version: 1 };
91
- gateOn?: "execution" | "acceptance";
92
- };
93
-
94
- interface ChainResultItem {
95
- index: number;
96
- agent: string;
97
- exitCode: number;
98
- finalOutput?: string;
99
- structuredOutput?: unknown;
100
- task?: string;
101
- detached?: boolean;
102
- timedOut?: boolean;
103
- error?: string;
104
- attemptedModels?: string[];
105
- skills?: string[];
106
- acceptance?: { status?: string; verifyRuns?: Array<{ status?: string }>; childReport?: unknown; runtimeChecks?: Array<{ status?: string; id?: string }> };
107
- }
108
-
109
- interface ChainExecutionResult {
110
- isError?: boolean;
111
- content: Array<{ text: string }>;
112
- details: {
113
- results: ChainResultItem[];
114
- chainAgents?: string[];
115
- totalSteps?: number;
116
- totalCost?: { inputTokens: number; outputTokens: number; costUsd: number };
117
- workflowGraph?: {
118
- nodes: Array<{ kind?: string; agent?: string; flatIndex?: number; outputName?: string; status?: string; error?: string; acceptanceStatus?: string; children?: Array<{ itemKey?: string; label?: string; status?: string; acceptanceStatus?: string }> }>;
119
- };
120
- currentStepIndex?: number;
121
- outputs?: Record<string, { text: string; structured?: unknown }>;
122
- parallelHandoff?: { version: number; path: string; groupCount: number; childCount: number; cleanupState: string };
123
- checkpoint?: { name?: string; status?: string; message?: string };
124
- };
125
- }
126
-
127
- interface ChainExecutionModule {
128
- executeChain(params: Record<string, unknown>): Promise<ChainExecutionResult>;
129
- }
130
-
131
- const chainMod = await tryImport<ChainExecutionModule>("./src/runs/foreground/chain-execution.ts");
132
- const available = !!chainMod;
133
- const executeChain = chainMod?.executeChain;
134
-
135
- describe("chain execution — sequential", { skip: !available ? "pi packages not available" : undefined }, () => {
136
- let tempDir: string;
137
- let artifactsDir: string;
138
- let mockPi: MockPi;
139
-
140
- before(() => {
141
- mockPi = createMockPi();
142
- mockPi.install();
143
- });
144
-
145
- after(() => {
146
- mockPi.uninstall();
147
- });
148
-
149
- beforeEach(() => {
150
- tempDir = createTempDir();
151
- artifactsDir = path.join(tempDir, "artifacts");
152
- mockPi.reset();
153
- });
154
-
155
- afterEach(() => {
156
- removeTempDir(tempDir);
157
- });
158
-
159
- function makeChainParams(
160
- chain: TestChainStep[],
161
- agents: ReturnType<typeof makeAgent>[],
162
- overrides: Record<string, unknown> = {},
163
- ) {
164
- return {
165
- chain,
166
- agents,
167
- ctx: makeMinimalCtx(tempDir),
168
- runId: `test-${Date.now().toString(36)}`,
169
- shareEnabled: false,
170
- sessionDirForIndex: () => undefined,
171
- artifactsDir,
172
- artifactConfig: { enabled: false },
173
- clarify: false,
174
- ...overrides,
175
- };
176
- }
177
-
178
- function readCallArgs(index: number): string[] {
179
- const callFiles = fs.readdirSync(mockPi.dir)
180
- .filter((name) => name.startsWith("call-") && name.endsWith(".json"))
181
- .sort();
182
- const callFile = callFiles[index];
183
- assert.ok(callFile, `expected call ${index}`);
184
- return JSON.parse(fs.readFileSync(path.join(mockPi.dir, callFile), "utf-8")).args as string[];
185
- }
186
-
187
- function acceptanceReport(overrides: Record<string, unknown> = {}): string {
188
- return [
189
- "done",
190
- "```acceptance-report",
191
- JSON.stringify({
192
- criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "integration test evidence" }],
193
- changedFiles: ["src/a.ts"],
194
- testsAddedOrUpdated: ["test/a.test.ts"],
195
- commandsRun: [{ command: "npm test", result: "passed", summary: "passed" }],
196
- validationOutput: ["validation passed"],
197
- residualRisks: [],
198
- noStagedFiles: true,
199
- notes: "complete",
200
- ...overrides,
201
- }),
202
- "```",
203
- ].join("\n");
204
- }
205
-
206
- function writePackageSkill(packageRoot: string, skillName: string): void {
207
- const skillDir = path.join(packageRoot, "skills", skillName);
208
- fs.mkdirSync(skillDir, { recursive: true });
209
- fs.writeFileSync(
210
- path.join(packageRoot, "package.json"),
211
- JSON.stringify({ name: `${skillName}-pkg`, version: "1.0.0", pi: { skills: [`./skills/${skillName}`] } }, null, 2),
212
- "utf-8",
213
- );
214
- fs.writeFileSync(
215
- path.join(skillDir, "SKILL.md"),
216
- `---\nname: ${skillName}\ndescription: test skill\n---\nbody\n`,
217
- "utf-8",
218
- );
219
- }
220
-
221
- it("pauses foreground chains at approval checkpoints without launching the next child", async () => {
222
- mockPi.onCall({ output: "analysis done" });
223
- mockPi.onCall({ output: "should not run" });
224
- const agents = [makeAgent("analyst"), makeAgent("worker")];
225
-
226
- const result = await executeChain!(makeChainParams([
227
- { agent: "analyst", task: "Analyze" },
228
- { checkpoint: "review", message: "Approve implementation?" },
229
- { agent: "worker", task: "Implement" },
230
- ], agents));
231
-
232
- assert.equal(result.isError, undefined);
233
- assert.match(result.content[0]!.text, /Chain paused at checkpoint 'review'/);
234
- assert.equal(result.details.results.length, 1);
235
- assert.equal(result.details.checkpoint?.name, "review");
236
- assert.equal(result.details.checkpoint?.status, "pending");
237
- assert.equal(result.details.workflowGraph?.nodes[1]?.kind, "checkpoint");
238
- assert.equal(result.details.workflowGraph?.nodes[1]?.status, "paused");
239
- assert.equal(mockPi.callCount(), 1);
240
- });
241
-
242
- it("applies global permissions to foreground chain children", async () => {
243
- mockPi.onCall({ echoEnv: [PERMISSION_POLICY_ENV, PERMISSION_AUDIT_PATH_ENV] });
244
- const agents = [makeAgent("worker")];
245
-
246
- const result = await executeChain!(makeChainParams(
247
- [{ agent: "worker", task: "Work" }],
248
- agents,
249
- { permissions: { rules: { write: "ask" } } },
250
- ));
251
-
252
- assert.equal(result.isError, undefined);
253
- const env = JSON.parse(result.details.results[0]?.finalOutput ?? "{}") as Record<string, string | null>;
254
- assert.equal(env[PERMISSION_POLICY_ENV], JSON.stringify({ write: "ask" }));
255
- assert.match(env[PERMISSION_AUDIT_PATH_ENV] ?? "", /permission-audit\.jsonl$/);
256
- });
257
-
258
- it("runs a 2-step chain", async () => {
259
- mockPi.onCall({ output: "Analysis complete: found 3 issues" });
260
- const agents = [makeAgent("analyst"), makeAgent("reporter")];
261
-
262
- const result = await executeChain(
263
- makeChainParams(
264
- [{ agent: "analyst", task: "Analyze the code" }, { agent: "reporter" }],
265
- agents,
266
- ),
267
- );
268
-
269
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
270
- assert.equal(result.details.results.length, 2);
271
- assert.equal(result.details.results[0].agent, "analyst");
272
- assert.equal(result.details.results[1].agent, "reporter");
273
- assert.deepEqual(result.details.totalCost, { inputTokens: 200, outputTokens: 100, costUsd: 0.002 });
274
- });
275
-
276
- it("runs a foreground sequential chain without clarify UI when clarify is omitted", async () => {
277
- mockPi.onCall({ output: "Analysis complete" });
278
- const agents = [makeAgent("analyst"), makeAgent("reporter")];
279
- let customCalls = 0;
280
- const ctx = {
281
- ...makeMinimalCtx(tempDir),
282
- hasUI: true,
283
- ui: {
284
- custom: async () => {
285
- customCalls += 1;
286
- return undefined;
287
- },
288
- },
289
- };
290
-
291
- const result = await executeChain(
292
- makeChainParams(
293
- [{ agent: "analyst", task: "Analyze the code" }, { agent: "reporter" }],
294
- agents,
295
- { ctx, clarify: undefined },
296
- ),
297
- );
298
-
299
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
300
- assert.doesNotMatch(result.content[0]?.text ?? "", /Chain cancelled/);
301
- assert.equal(result.details.results.length, 2);
302
- assert.equal(mockPi.callCount(), 2);
303
- assert.equal(customCalls, 0);
304
- });
305
-
306
- it("uses clarify UI for a foreground sequential chain when clarify is true", async () => {
307
- mockPi.onCall({ output: "Analysis complete" });
308
- const agents = [makeAgent("analyst"), makeAgent("reporter")];
309
- let customCalls = 0;
310
- const ctx = {
311
- ...makeMinimalCtx(tempDir),
312
- hasUI: true,
313
- ui: {
314
- custom: async () => {
315
- customCalls += 1;
316
- return {
317
- confirmed: true,
318
- templates: ["Clarified analysis", "Report on {previous}"],
319
- behaviorOverrides: [],
320
- };
321
- },
322
- },
323
- };
324
-
325
- const result = await executeChain(
326
- makeChainParams(
327
- [{ agent: "analyst", task: "Analyze the code" }, { agent: "reporter" }],
328
- agents,
329
- { ctx, clarify: true },
330
- ),
331
- );
332
-
333
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
334
- assert.equal(customCalls, 1);
335
- assert.equal(mockPi.callCount(), 2);
336
- assert.match(readCallArgs(0).at(-1) ?? "", /Clarified analysis/);
337
- });
338
-
339
- it("preserves completed chain results and marks the timed-out current step", async () => {
340
- mockPi.onCall({ matchArgIncludes: "Quick first step", output: "first done" });
341
- mockPi.onCall({ matchArgIncludes: "Slow second step", delay: 10000 });
342
- const agents = [makeAgent("analyst"), makeAgent("reporter")];
343
-
344
- const start = Date.now();
345
- const result = await executeChain(
346
- makeChainParams(
347
- [{ agent: "analyst", task: "Quick first step" }, { agent: "reporter", task: "Slow second step" }],
348
- agents,
349
- { timeoutMs: 300 },
350
- ),
351
- );
352
- const elapsed = Date.now() - start;
353
-
354
- assert.ok(elapsed < 5000, `should time out early, took ${elapsed}ms`);
355
- assert.equal(result.isError, true);
356
- assert.equal(result.details.results.length, 2);
357
- assert.equal(result.details.results[0]?.exitCode, 0);
358
- assert.equal(result.details.results[0]?.finalOutput, "first done");
359
- assert.equal(result.details.results[1]?.timedOut, true);
360
- assert.equal(result.details.results[1]?.error, "Subagent timed out after 300ms.");
361
- assert.match(result.content[0]?.text ?? "", /Subagent timed out after 300ms\./);
362
- });
363
-
364
- it("passes file-only saved-output references through {previous}", async () => {
365
- mockPi.onCall({ output: "full chain output\nwith details" });
366
- const agents = [makeAgent("analyst", { tools: ["read", "grep", "find", "ls"] }), makeAgent("reporter")];
367
-
368
- const result = await executeChain(
369
- makeChainParams(
370
- [
371
- { agent: "analyst", task: "Analyze", output: "analysis.md", outputMode: "file-only" },
372
- { agent: "reporter" },
373
- ],
374
- agents,
375
- { chainDir: tempDir },
376
- ),
377
- );
378
-
379
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
380
- const firstTaskArg = readCallArgs(0).at(-1) ?? "";
381
- assert.match(firstTaskArg, /Return the complete artifact in your final response\./);
382
- assert.match(firstTaskArg, /runtime will persist it to exactly this path: .*analysis\.md/);
383
- assert.match(firstTaskArg, /Do not call contact_supervisor merely because no write-capable tool is available\./);
384
- assert.doesNotMatch(firstTaskArg, /\[Write to:|Write your findings to exactly this path/);
385
- assert.ok(result.details.results[0]?.savedOutputPath);
386
- assert.equal(fs.readFileSync(result.details.results[0].savedOutputPath, "utf-8"), "full chain output\nwith details");
387
- assert.match(result.details.results[0]?.finalOutput ?? "", /Output saved to:/);
388
- assert.doesNotMatch(result.details.results[0]?.finalOutput ?? "", /full chain output/);
389
- const secondTaskArg = readCallArgs(1).at(-1) ?? "";
390
- assert.match(secondTaskArg, /Output saved to:/);
391
- assert.match(secondTaskArg, /2 lines/);
392
- assert.doesNotMatch(secondTaskArg, /full chain output/);
393
- });
394
-
395
- it("persists explicit checked acceptance and rejects missing evidence", async () => {
396
- mockPi.onCall({
397
- output: [
398
- "implemented",
399
- "```acceptance-report",
400
- JSON.stringify({
401
- criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "patched" }],
402
- changedFiles: ["src/file.ts"],
403
- testsAddedOrUpdated: ["test/file.test.ts"],
404
- commandsRun: [{ command: "npm test", result: "passed", summary: "passed" }],
405
- validationOutput: ["passed"],
406
- residualRisks: [],
407
- noStagedFiles: true,
408
- notes: "done",
409
- }),
410
- "```",
411
- ].join("\n"),
412
- });
413
- const agents = [makeAgent("worker", { completionGuard: false })];
414
-
415
- const result = await executeChain(
416
- makeChainParams(
417
- [{ agent: "worker", task: "Implement fix", output: "accepted.md", outputMode: "file-only", acceptance: { level: "checked", criteria: ["Patch bug"] } }],
418
- agents,
419
- { chainDir: tempDir },
420
- ),
421
- );
422
-
423
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
424
- assert.match(result.details.results[0]?.finalOutput ?? "", /Output saved to:/);
425
- assert.equal(result.details.results[0]?.acceptance?.status, "checked");
426
- assert.ok(result.details.results[0]?.acceptance?.childReport);
427
-
428
- mockPi.onCall({
429
- output: [
430
- "implemented",
431
- "```acceptance-report",
432
- JSON.stringify({
433
- criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "patched" }],
434
- changedFiles: ["src/file.ts"],
435
- commandsRun: [{ command: "npm test", result: "passed", summary: "passed" }],
436
- residualRisks: [],
437
- noStagedFiles: true,
438
- }),
439
- "```",
440
- ].join("\n"),
441
- });
442
-
443
- const failed = await executeChain(
444
- makeChainParams(
445
- [{ agent: "worker", task: "Implement fix", acceptance: { level: "checked" } }],
446
- agents,
447
- ),
448
- );
449
- assert.equal(failed.isError, true);
450
- assert.equal(failed.details.results[0]?.acceptance?.status, "rejected");
451
- assert.match(failed.details.results[0]?.error ?? "", /tests-added evidence missing/);
452
- });
453
-
454
- it("runs explicit verified acceptance commands and does not trust child command claims as verification", async () => {
455
- const acceptanceReport = [
456
- "implemented",
457
- "```acceptance-report",
458
- JSON.stringify({
459
- criteriaSatisfied: [{ id: "criterion-1", status: "satisfied", evidence: "patched" }],
460
- changedFiles: ["src/file.ts"],
461
- testsAddedOrUpdated: ["test/file.test.ts"],
462
- commandsRun: [{ command: "npm test", result: "passed", summary: "child claimed pass" }],
463
- validationOutput: ["child output"],
464
- residualRisks: [],
465
- noStagedFiles: true,
466
- }),
467
- "```",
468
- ].join("\n");
469
- mockPi.onCall({ output: acceptanceReport });
470
- const agents = [makeAgent("worker", { completionGuard: false })];
471
-
472
- const result = await executeChain(
473
- makeChainParams(
474
- [{ agent: "worker", task: "Implement fix", acceptance: { level: "verified", verify: [{ id: "runtime-pass", command: "node -e \"process.exit(0)\"" }] } }],
475
- agents,
476
- ),
477
- );
478
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
479
- assert.equal(result.details.results[0]?.acceptance?.status, "verified");
480
- assert.equal(result.details.results[0]?.acceptance?.verifyRuns?.[0]?.status, "passed");
481
-
482
- mockPi.onCall({ output: acceptanceReport });
483
- const failed = await executeChain(
484
- makeChainParams(
485
- [{ agent: "worker", task: "Implement fix", acceptance: { level: "verified", verify: [{ id: "runtime-fail", command: "node -e \"process.exit(5)\"" }] } }],
486
- agents,
487
- ),
488
- );
489
- assert.equal(failed.isError, true);
490
- assert.equal(failed.details.results[0]?.acceptance?.status, "rejected");
491
- assert.equal(failed.details.results[0]?.acceptance?.verifyRuns?.[0]?.status, "failed");
492
- assert.match(failed.details.results[0]?.error ?? "", /runtime-fail/);
493
- });
494
-
495
- it("retries chain steps with fallback models on retryable provider failures", async () => {
496
- mockPi.onCall({
497
- jsonl: [{
498
- type: "message_end",
499
- message: {
500
- role: "assistant",
501
- content: [{ type: "text", text: "primary failed" }],
502
- model: "openai/gpt-5-mini",
503
- errorMessage: "provider unavailable",
504
- usage: { input: 10, output: 5, cacheRead: 0, cacheWrite: 0, cost: { total: 0.01 } },
505
- },
506
- }],
507
- exitCode: 1,
508
- });
509
- mockPi.onCall({ output: "Step 1 recovered" });
510
- mockPi.onCall({ output: "Step 2 ran" });
511
- const agents = [
512
- makeAgent("step1", { model: "openai/gpt-5-mini", fallbackModels: ["anthropic/claude-sonnet-4"] }),
513
- makeAgent("step2"),
514
- ];
515
-
516
- const result = await executeChain(
517
- makeChainParams(
518
- [{ agent: "step1", task: "Do step 1" }, { agent: "step2" }],
519
- agents,
520
- ),
521
- );
522
-
523
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
524
- assert.equal(result.details.results.length, 2);
525
- assert.deepEqual(result.details.results[0].attemptedModels, ["openai/gpt-5-mini", "anthropic/claude-sonnet-4"]);
526
- assert.equal(mockPi.callCount(), 3);
527
- });
528
-
529
- it("prefers the parent session provider for ambiguous bare chain step models", async () => {
530
- mockPi.onCall({ output: "Step 1 ran" });
531
- mockPi.onCall({ output: "Step 2 ran" });
532
- const agents = [makeAgent("step1", { model: "gpt-5-mini" }), makeAgent("step2")];
533
-
534
- const result = await executeChain(
535
- makeChainParams(
536
- [{ agent: "step1", task: "Do step 1" }, { agent: "step2" }],
537
- agents,
538
- {
539
- ctx: {
540
- ...makeMinimalCtx(tempDir),
541
- model: { provider: "github-copilot", id: "gpt-5-mini" },
542
- modelRegistry: {
543
- getAvailable: () => [
544
- { provider: "openai", id: "gpt-5-mini" },
545
- { provider: "github-copilot", id: "gpt-5-mini" },
546
- ],
547
- },
548
- },
549
- },
550
- ),
551
- );
552
-
553
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
554
- assert.equal(result.details.results[0].model, "github-copilot/gpt-5-mini");
555
- assert.deepEqual(result.details.results[0].attemptedModels, ["github-copilot/gpt-5-mini"]);
556
- });
557
-
558
- it("foreground chains inherit the parent session model when no step or agent model is set", async () => {
559
- mockPi.onCall({ output: "Step ran" });
560
-
561
- const result = await executeChain(
562
- makeChainParams(
563
- [{ agent: "worker", task: "Do work" }],
564
- [makeAgent("worker")],
565
- {
566
- ctx: {
567
- ...makeMinimalCtx(tempDir),
568
- model: { provider: "deepseek", id: "deepseek-v4-flash" },
569
- },
570
- },
571
- ),
572
- );
573
-
574
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
575
- const args = readCallArgs(0);
576
- assert.equal(args[args.indexOf("--model") + 1], "deepseek/deepseek-v4-flash");
577
- assert.equal(result.details.results[0].model, "deepseek/deepseek-v4-flash");
578
- });
579
-
580
- it("foreground chains treat the inherit model sentinel as the parent session model", async () => {
581
- mockPi.onCall({ output: "Step ran" });
582
-
583
- const result = await executeChain(
584
- makeChainParams(
585
- [{ agent: "worker", task: "Do work", model: "inherit" }],
586
- [makeAgent("worker")],
587
- {
588
- ctx: {
589
- ...makeMinimalCtx(tempDir),
590
- model: { provider: "deepseek", id: "deepseek-v4-flash" },
591
- },
592
- },
593
- ),
594
- );
595
-
596
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
597
- const args = readCallArgs(0);
598
- assert.equal(args[args.indexOf("--model") + 1], "deepseek/deepseek-v4-flash");
599
- assert.equal(result.details.results[0].model, "deepseek/deepseek-v4-flash");
600
- });
601
-
602
- it("suppresses progress for {task} chain templates when the top-level task is review-only", async () => {
603
- mockPi.onCall({ output: "Review done" });
604
- const agents = [makeAgent("reviewer", { defaultProgress: true })];
605
-
606
- await executeChain(
607
- makeChainParams(
608
- [{ agent: "reviewer" }],
609
- agents,
610
- { task: "Review-only. Do not edit files. Return findings." },
611
- ),
612
- );
613
-
614
- const taskArg = readCallArgs(0).at(-1) ?? "";
615
- assert.doesNotMatch(taskArg, /progress\.md/);
616
- assert.equal(fs.existsSync(path.join(tempDir, "progress.md")), false);
617
- });
618
-
619
- it("foreground chains resolve explicit reads inside the chain directory", async () => {
620
- mockPi.onCall({ output: "Review done" });
621
- const chainDir = path.join(tempDir, "chain-reads");
622
- const runId = "chain-reads-run";
623
- const runDir = path.join(chainDir, runId);
624
- fs.mkdirSync(runDir, { recursive: true });
625
- fs.writeFileSync(path.join(runDir, "plan.md"), "chain plan");
626
- fs.writeFileSync(path.join(runDir, "progress.md"), "chain progress");
627
-
628
- await executeChain!(makeChainParams(
629
- [{ agent: "reviewer", task: "Review the chain artifacts.", reads: ["plan.md", "progress.md"] }],
630
- [makeAgent("reviewer")],
631
- { chainDir, runId },
632
- ));
633
-
634
- const taskArg = readCallArgs(0).at(-1) ?? "";
635
- assert.ok(taskArg.includes("[Read from: "));
636
- assert.ok(taskArg.includes(path.join(runDir, "plan.md")));
637
- assert.ok(taskArg.includes(path.join(runDir, "progress.md")));
638
- });
639
-
640
- it("foreground chains still resolve defaultProgress inside the chain directory", async () => {
641
- mockPi.onCall({ output: "Progress done" });
642
- const agents = [makeAgent("reviewer", { defaultProgress: true })];
643
- const chainDir = path.join(tempDir, "chain-progress");
644
- const runId = "chain-progress-run";
645
-
646
- await executeChain(
647
- makeChainParams(
648
- [{ agent: "reviewer", task: "Track chain work" }],
649
- agents,
650
- { chainDir, runId },
651
- ),
652
- );
653
-
654
- const taskArg = readCallArgs(0).at(-1) ?? "";
655
- assert.ok(taskArg.includes(`Create and maintain progress at: ${path.join(chainDir, runId, "progress.md")}`), taskArg);
656
- });
657
-
658
- it("passes {previous} between steps (step 2 receives step 1 output)", async () => {
659
- mockPi.onCall({ output: "Step 1 unique output: MARKER_ABC_123" });
660
- const agents = [makeAgent("step1"), makeAgent("step2")];
661
-
662
- const result = await executeChain(
663
- makeChainParams(
664
- [{ agent: "step1", task: "Produce output" }, { agent: "step2" }],
665
- agents,
666
- ),
667
- );
668
-
669
- assert.ok(!result.isError);
670
- assert.equal(result.details.results[1].task, "[prompt redacted]");
671
- const step2Task = readCallArgs(1).at(-1) ?? "";
672
- assert.ok(
673
- step2Task.includes("MARKER_ABC_123"),
674
- `step 2 invocation should contain step 1 output via {previous}: ${step2Task.slice(0, 200)}`,
675
- );
676
- });
677
-
678
- it("passes named sequential outputs through {outputs.name}", async () => {
679
- mockPi.onCall({ output: "Context marker: CTX_123" });
680
- mockPi.onCall({ output: "Done" });
681
- const agents = [makeAgent("context"), makeAgent("writer")];
682
-
683
- const result = await executeChain(
684
- makeChainParams(
685
- [
686
- { agent: "context", task: "Gather context", as: "contextOutput" },
687
- { agent: "writer", task: "Use {outputs.contextOutput}" },
688
- ],
689
- agents,
690
- ),
691
- );
692
-
693
- assert.ok(!result.isError);
694
- assert.match(readCallArgs(1).at(-1) ?? "", /CTX_123/);
695
- assert.equal(result.details.workflowGraph?.nodes[0]?.outputName, "contextOutput");
696
- });
697
-
698
- it("expands structured named output into dynamic parallel children and collects results", async () => {
699
- mockPi.onCall({
700
- output: "targets",
701
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
702
- });
703
- mockPi.onCall({ output: "review-a", structuredOutput: { ok: "a" } });
704
- mockPi.onCall({ output: "review-b", structuredOutput: { ok: "b" } });
705
- mockPi.onCall({ output: "synthesized" });
706
- const agents = [makeAgent("scout"), makeAgent("reviewer"), makeAgent("writer")];
707
-
708
- const result = await executeChain(
709
- makeChainParams(
710
- [
711
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
712
- {
713
- expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 4 },
714
- parallel: {
715
- agent: "reviewer",
716
- task: "Review {target.path}",
717
- label: "Review {target.path}",
718
- outputSchema: { type: "object" },
719
- },
720
- collect: { as: "reviews" },
721
- concurrency: 1,
722
- },
723
- { agent: "writer", task: "Use {outputs.reviews}" },
724
- ],
725
- agents,
726
- ),
727
- );
728
-
729
- assert.ok(!result.isError);
730
- assert.equal(mockPi.callCount(), 4);
731
- assert.match(readCallArgs(1).at(-1) ?? "", /Review src\/a\.ts/);
732
- assert.match(readCallArgs(2).at(-1) ?? "", /Review src\/b\.ts/);
733
- assert.match(readCallArgs(3).at(-1) ?? "", /"key":"src\/a\.ts"/);
734
- const collected = result.details.outputs?.reviews?.structured as Array<{ key: string; structured: unknown }>;
735
- assert.deepEqual(collected.map((item) => item.key), ["src/a.ts", "src/b.ts"]);
736
- assert.deepEqual(collected.map((item) => item.structured), [{ ok: "a" }, { ok: "b" }]);
737
- const dynamicNode = result.details.workflowGraph?.nodes[1];
738
- assert.equal(dynamicNode?.kind, "dynamic-parallel-group");
739
- assert.deepEqual(dynamicNode?.children?.map((child) => child.itemKey), ["src/a.ts", "src/b.ts"]);
740
- });
741
-
742
- it("applies global permissions to materialized dynamic parallel children", async () => {
743
- mockPi.onCall({
744
- output: "targets",
745
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
746
- });
747
- mockPi.onCall({ echoEnv: [PERMISSION_POLICY_ENV, PERMISSION_AUDIT_PATH_ENV], structuredOutput: { ok: "a" } });
748
- mockPi.onCall({ echoEnv: [PERMISSION_POLICY_ENV, PERMISSION_AUDIT_PATH_ENV], structuredOutput: { ok: "b" } });
749
- const agents = [makeAgent("scout"), makeAgent("reviewer")];
750
-
751
- const result = await executeChain!(makeChainParams(
752
- [
753
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
754
- {
755
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
756
- parallel: { agent: "reviewer", task: "Review {item.path}", outputSchema: { type: "object" } },
757
- collect: { as: "reviews" },
758
- concurrency: 1,
759
- },
760
- ],
761
- agents,
762
- { permissions: { rules: { write: "ask" } } },
763
- ));
764
-
765
- assert.equal(result.isError, undefined);
766
- const reviewerResults = result.details.results.filter((entry) => entry.agent === "reviewer");
767
- assert.equal(reviewerResults.length, 2);
768
- for (const child of reviewerResults) {
769
- const env = JSON.parse(child.finalOutput ?? "{}") as Record<string, string | null>;
770
- assert.equal(env[PERMISSION_POLICY_ENV], JSON.stringify({ write: "ask" }));
771
- assert.match(env[PERMISSION_AUDIT_PATH_ENV] ?? "", /permission-audit\.jsonl$/);
772
- }
773
- });
774
-
775
- it("persists checked child evidence and pending aggregate review for dynamic fanout", async () => {
776
- mockPi.onCall({
777
- output: "targets",
778
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
779
- });
780
- mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/a.ts"] }), structuredOutput: { ok: "a" } });
781
- mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/b.ts"] }), structuredOutput: { ok: "b" } });
782
- const agents = [makeAgent("scout"), makeAgent("reviewer", { completionGuard: false })];
783
-
784
- const result = await executeChain(
785
- makeChainParams(
786
- [
787
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
788
- {
789
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
790
- parallel: { agent: "reviewer", task: "Review {item.path}", outputSchema: { type: "object" }, acceptance: { level: "checked" } },
791
- collect: { as: "reviews" },
792
- acceptance: { level: "checked" },
793
- concurrency: 1,
794
- },
795
- ],
796
- agents,
797
- ),
798
- );
799
-
800
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
801
- const dynamicNode = result.details.workflowGraph?.nodes[1];
802
- assert.equal(dynamicNode?.acceptanceStatus, "review-required");
803
- assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["checked", "checked"]);
804
- assert.deepEqual(result.details.results.filter((child) => child.agent === "reviewer").map((child) => child.acceptance?.evidenceStatus), ["checked", "checked"]);
805
- });
806
-
807
- it("applies read-only acceptance roles to dynamic children and their aggregate group", async () => {
808
- mockPi.onCall({
809
- output: "targets",
810
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
811
- });
812
- const readOnlyReport = acceptanceReport({
813
- changedFiles: [],
814
- testsAddedOrUpdated: [],
815
- commandsRun: [],
816
- validationOutput: [],
817
- reviewFindings: ["No blocking findings"],
818
- });
819
- mockPi.onCall({ output: readOnlyReport, structuredOutput: { ok: "a" } });
820
- mockPi.onCall({ output: readOnlyReport, structuredOutput: { ok: "b" } });
821
- const agents = [makeAgent("scout"), makeAgent("explorer", { acceptanceRole: "read-only" })];
822
-
823
- const result = await executeChain(
824
- makeChainParams(
825
- [
826
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
827
- {
828
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
829
- parallel: { agent: "explorer", task: "Explore {item.path}", outputSchema: { type: "object" } },
830
- collect: { as: "reviews" },
831
- concurrency: 1,
832
- },
833
- ],
834
- agents,
835
- ),
836
- );
837
-
838
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
839
- const explorerResults = result.details.results.filter((child) => child.agent === "explorer");
840
- assert.deepEqual(explorerResults.map((child) => child.acceptance?.effectiveAcceptance.level), ["attested", "attested"]);
841
- const dynamicNode = result.details.workflowGraph?.nodes[1];
842
- assert.equal(dynamicNode?.acceptanceStatus, "attested");
843
- assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["attested", "attested"]);
844
- });
845
-
846
- it("infers foreground dynamic acceptance after materializing item templates", async () => {
847
- mockPi.onCall({
848
- output: "targets",
849
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
850
- });
851
- mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/a.ts"] }), structuredOutput: { ok: "a" } });
852
- mockPi.onCall({ output: acceptanceReport({ changedFiles: ["src/b.ts"] }), structuredOutput: { ok: "b" } });
853
- const agents = [makeAgent("scout"), makeAgent("explorer", { acceptanceRole: "read-only" })];
854
-
855
- const result = await executeChain(
856
- makeChainParams(
857
- [
858
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
859
- {
860
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
861
- parallel: { agent: "explorer", task: "Patch {item.path}", outputSchema: { type: "object" } },
862
- collect: { as: "reviews" },
863
- concurrency: 1,
864
- },
865
- ],
866
- agents,
867
- ),
868
- );
869
-
870
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
871
- const explorerResults = result.details.results.filter((child) => child.agent === "explorer");
872
- assert.deepEqual(explorerResults.map((child) => child.acceptance?.effectiveAcceptance.level), ["checked", "checked"]);
873
- const dynamicNode = result.details.workflowGraph?.nodes[1];
874
- assert.equal(dynamicNode?.acceptanceStatus, "rejected");
875
- assert.deepEqual(dynamicNode?.children?.map((child) => child.acceptanceStatus), ["rejected", "rejected"]);
876
- });
877
-
878
- it("does not expose collected dynamic output when a child fails", async () => {
879
- mockPi.onCall({
880
- output: "targets",
881
- structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] },
882
- });
883
- mockPi.onCall({ output: "review-a", structuredOutput: { ok: "a" } });
884
- mockPi.onCall({ exitCode: 1, stderr: "review-b failed" });
885
- const agents = [makeAgent("scout"), makeAgent("reviewer")];
886
-
887
- const result = await executeChain(
888
- makeChainParams(
889
- [
890
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
891
- {
892
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
893
- parallel: { agent: "reviewer", task: "Review {item.path}", outputSchema: { type: "object" } },
894
- collect: { as: "reviews" },
895
- concurrency: 1,
896
- },
897
- ],
898
- agents,
899
- ),
900
- );
901
-
902
- assert.equal(result.isError, true);
903
- assert.equal(mockPi.callCount(), 3);
904
- assert.equal(result.details.outputs?.reviews, undefined);
905
- assert.equal(result.details.results.some((entry) => entry.exitCode === 1), true);
906
- });
907
-
908
- it("fails dynamic fanout before spawning children for invalid source arrays", async () => {
909
- mockPi.onCall({ output: "targets", structuredOutput: { items: [{ path: "a" }, { path: "b" }] } });
910
- const agents = [makeAgent("scout"), makeAgent("reviewer")];
911
-
912
- const result = await executeChain(
913
- makeChainParams(
914
- [
915
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
916
- {
917
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 1 },
918
- parallel: { agent: "reviewer", task: "Review {item.path}" },
919
- collect: { as: "reviews" },
920
- },
921
- ],
922
- agents,
923
- ),
924
- );
925
-
926
- assert.equal(result.isError, true);
927
- assert.match(result.content[0]?.text ?? "", /exceeding maxItems 1/);
928
- assert.equal(mockPi.callCount(), 1);
929
- assert.equal(result.details.workflowGraph?.nodes[1]?.status, "failed");
930
- assert.match(result.details.workflowGraph?.nodes[1]?.error ?? "", /exceeding maxItems 1/);
931
- });
932
-
933
- it("marks dynamic file-only validation failures as failed graph groups before spawning children", async () => {
934
- mockPi.onCall({ output: "targets", structuredOutput: { items: [{ path: "src/a.ts" }] } });
935
- const agents = [makeAgent("scout"), makeAgent("reviewer")];
936
-
937
- const result = await executeChain(
938
- makeChainParams(
939
- [
940
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
941
- {
942
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
943
- parallel: { agent: "reviewer", task: "Review {item.path}", outputMode: "file-only" },
944
- collect: { as: "reviews" },
945
- },
946
- ],
947
- agents,
948
- { chainDir: tempDir },
949
- ),
950
- );
951
-
952
- assert.equal(result.isError, true);
953
- assert.match(result.content[0]?.text ?? "", /outputMode: "file-only"/);
954
- assert.equal(mockPi.callCount(), 1);
955
- assert.equal(result.details.workflowGraph?.nodes[1]?.status, "failed");
956
- assert.match(result.details.workflowGraph?.nodes[1]?.error ?? "", /outputMode: "file-only"/);
957
- });
958
-
959
- it("marks empty dynamic fanout skip as a completed graph group", async () => {
960
- mockPi.onCall({ output: "targets", structuredOutput: { items: [] } });
961
- mockPi.onCall({ output: "used empty reviews" });
962
- const agents = [makeAgent("scout"), makeAgent("reviewer"), makeAgent("writer")];
963
-
964
- const result = await executeChain(
965
- makeChainParams(
966
- [
967
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
968
- {
969
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4, onEmpty: "skip" },
970
- parallel: { agent: "reviewer", task: "Review {item.path}" },
971
- collect: { as: "reviews" },
972
- },
973
- { agent: "writer", task: "Use {outputs.reviews}" },
974
- ],
975
- agents,
976
- ),
977
- );
978
-
979
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
980
- assert.equal(mockPi.callCount(), 2);
981
- assert.deepEqual(result.details.outputs?.reviews?.structured, []);
982
- assert.equal(result.details.workflowGraph?.nodes[1]?.status, "completed");
983
- assert.deepEqual(result.details.workflowGraph?.nodes[1]?.children, []);
984
- });
985
-
986
- it("marks dynamic collect schema failures as failed graph groups", async () => {
987
- mockPi.onCall({ output: "targets", structuredOutput: { items: [{ path: "src/a.ts" }] } });
988
- mockPi.onCall({ output: "review-a", structuredOutput: { ok: "a" } });
989
- const agents = [makeAgent("scout"), makeAgent("reviewer")];
990
-
991
- const result = await executeChain(
992
- makeChainParams(
993
- [
994
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
995
- {
996
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
997
- parallel: { agent: "reviewer", task: "Review {item.path}", outputSchema: { type: "object" } },
998
- collect: { as: "reviews", outputSchema: { type: "object" } },
999
- },
1000
- ],
1001
- agents,
1002
- ),
1003
- );
1004
-
1005
- assert.equal(result.isError, true);
1006
- assert.match(result.content[0]?.text ?? "", /Collected output validation failed/);
1007
- assert.equal(result.details.outputs?.reviews, undefined);
1008
- assert.equal(result.details.workflowGraph?.nodes[1]?.status, "failed");
1009
- assert.match(result.details.workflowGraph?.nodes[1]?.error ?? "", /Collected output validation failed/);
1010
- assert.equal(result.details.workflowGraph?.nodes[1]?.children?.[0]?.status, "completed");
1011
- });
1012
-
1013
- it("keeps materialized dynamic children in live graph updates for later sequential steps", async () => {
1014
- mockPi.onCall({ output: "targets", structuredOutput: { items: [{ path: "src/a.ts" }, { path: "src/b.ts" }] } });
1015
- mockPi.onCall({ output: "review-a", structuredOutput: { ok: "a" } });
1016
- mockPi.onCall({ output: "review-b", structuredOutput: { ok: "b" } });
1017
- mockPi.onCall({ steps: [{ jsonl: [events.assistantMessage("writer started")] }] });
1018
- const agents = [makeAgent("scout"), makeAgent("reviewer"), makeAgent("writer")];
1019
- let writerUpdateChildren: Array<{ itemKey?: string; status?: string }> | undefined;
1020
-
1021
- const result = await executeChain(
1022
- makeChainParams(
1023
- [
1024
- { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
1025
- {
1026
- expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
1027
- parallel: { agent: "reviewer", task: "Review {item.path}", outputSchema: { type: "object" } },
1028
- collect: { as: "reviews" },
1029
- concurrency: 1,
1030
- },
1031
- { agent: "writer", task: "Use {outputs.reviews}" },
1032
- ],
1033
- agents,
1034
- {
1035
- onUpdate(update: { details?: ChainExecutionResult["details"] }) {
1036
- if (update.details?.currentStepIndex !== 2) return;
1037
- writerUpdateChildren = update.details.workflowGraph?.nodes[1]?.children;
1038
- },
1039
- },
1040
- ),
1041
- );
1042
-
1043
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
1044
- assert.deepEqual(writerUpdateChildren?.map((child) => child.itemKey), ["src/a.ts", "src/b.ts"]);
1045
- });
1046
-
1047
- it("fails duplicate and unknown named outputs before spawning children", async () => {
1048
- const agents = [makeAgent("a"), makeAgent("b")];
1049
-
1050
- const duplicate = await executeChain(
1051
- makeChainParams(
1052
- [{ agent: "a", task: "A", as: "same" }, { agent: "b", task: "B", as: "same" }],
1053
- agents,
1054
- ),
1055
- );
1056
- assert.equal(duplicate.isError, true);
1057
- assert.match(duplicate.content[0]?.text ?? "", /Duplicate chain output name 'same'/);
1058
- assert.equal(mockPi.callCount(), 0);
1059
-
1060
- const unknown = await executeChain(
1061
- makeChainParams(
1062
- [{ agent: "b", task: "Use {outputs.missing}" }],
1063
- agents,
1064
- ),
1065
- );
1066
- assert.equal(unknown.isError, true);
1067
- assert.match(unknown.content[0]?.text ?? "", /Unknown chain output reference/);
1068
- assert.equal(mockPi.callCount(), 0);
1069
-
1070
- const malformed = await executeChain(
1071
- makeChainParams(
1072
- [{ agent: "b", task: "Use {outputs.bad-name}" }],
1073
- agents,
1074
- ),
1075
- );
1076
- assert.equal(malformed.isError, true);
1077
- assert.match(malformed.content[0]?.text ?? "", /Invalid chain output reference '\{outputs\.bad-name\}'/);
1078
- assert.equal(mockPi.callCount(), 0);
1079
- });
1080
-
1081
- it("requires schema-valid structured_output when outputSchema is set", async () => {
1082
- const schema = {
1083
- type: "object",
1084
- required: ["ok"],
1085
- properties: { ok: { type: "boolean" }, note: { type: "string" } },
1086
- };
1087
- mockPi.onCall({ output: "prose", structuredOutput: { ok: true, note: "captured" } });
1088
- const agents = [makeAgent("worker")];
1089
-
1090
- const result = await executeChain(
1091
- makeChainParams([{ agent: "worker", task: "Return structured", outputSchema: schema }], agents),
1092
- );
1093
-
1094
- assert.ok(!result.isError);
1095
- assert.deepEqual(result.details.results[0]?.structuredOutput, { ok: true, note: "captured" });
1096
-
1097
- mockPi.reset();
1098
- mockPi.onCall({ structuredOutput: { ok: true, note: "tool-only" } });
1099
- const structuredOnly = await executeChain(
1100
- makeChainParams([{ agent: "worker", task: "Return structured", outputSchema: schema }], agents),
1101
- );
1102
- assert.ok(!structuredOnly.isError);
1103
- assert.deepEqual(structuredOnly.details.results[0]?.structuredOutput, { ok: true, note: "tool-only" });
1104
-
1105
- mockPi.reset();
1106
- mockPi.onCall({ output: "prose only" });
1107
- const missing = await executeChain(
1108
- makeChainParams([{ agent: "worker", task: "Return structured", outputSchema: schema }], agents),
1109
- );
1110
- assert.equal(missing.isError, true);
1111
- assert.match(missing.details.results[0]?.error ?? "", /Missing structured_output call/);
1112
-
1113
- mockPi.reset();
1114
- mockPi.onCall({ output: "prose only" });
1115
- const missingImplementation = await executeChain(
1116
- makeChainParams([{ agent: "worker", task: "Implement the fix and return structured data", outputSchema: schema }], agents),
1117
- );
1118
- assert.equal(missingImplementation.isError, true);
1119
- assert.match(missingImplementation.details.results[0]?.error ?? "", /Missing structured_output call/);
1120
-
1121
- mockPi.reset();
1122
- mockPi.onCall({ output: "invalid", structuredOutput: { ok: "yes" } });
1123
- const invalid = await executeChain(
1124
- makeChainParams([{ agent: "worker", task: "Return structured", outputSchema: schema, phase: "Validate", label: "Structured worker", as: "result" }], agents),
1125
- );
1126
- assert.equal(invalid.isError, true);
1127
- assert.match(invalid.details.results[0]?.error ?? "", /Structured output validation failed/);
1128
- assert.equal(invalid.details.workflowGraph?.nodes[0]?.status, "failed");
1129
- assert.equal(invalid.details.workflowGraph?.nodes[0]?.outputName, "result");
1130
- assert.match(invalid.details.workflowGraph?.nodes[0]?.error ?? "", /Structured output validation failed/);
1131
- });
1132
-
1133
- it("accepts recovered tool errors before valid structured output", async () => {
1134
- mockPi.onCall({
1135
- jsonl: [{
1136
- type: "tool_result_end",
1137
- message: { role: "toolResult", toolName: "read", isError: true, content: [{ type: "text", text: "EISDIR" }] },
1138
- }],
1139
- structuredOutput: { ok: true },
1140
- });
1141
- const agents = [makeAgent("worker")];
1142
-
1143
- const result = await executeChain(
1144
- makeChainParams([{
1145
- agent: "worker",
1146
- task: "Recover and return structured data",
1147
- outputSchema: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" } } },
1148
- }], agents),
1149
- );
1150
-
1151
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
1152
- assert.deepEqual(result.details.results[0]?.structuredOutput, { ok: true });
1153
- });
1154
-
1155
- it("substitutes {task} in templates", async () => {
1156
- mockPi.onCall({ output: "Done" });
1157
- const agents = [makeAgent("worker")];
1158
-
1159
- const result = await executeChain(
1160
- makeChainParams(
1161
- [{ agent: "worker", task: "Review {task} carefully" }],
1162
- agents,
1163
- { task: "the authentication module" },
1164
- ),
1165
- );
1166
-
1167
- assert.ok(!result.isError);
1168
- assert.equal(result.details.results[0].task, "[prompt redacted]");
1169
- const workerTask = readCallArgs(0).at(-1) ?? "";
1170
- assert.ok(
1171
- workerTask.includes("the authentication module"),
1172
- `should substitute {task} in the child invocation: ${workerTask.slice(0, 200)}`,
1173
- );
1174
- });
1175
-
1176
- it("creates and uses chain_dir", async () => {
1177
- mockPi.onCall({ output: "Done" });
1178
- const agents = [makeAgent("worker")];
1179
-
1180
- const result = await executeChain(
1181
- makeChainParams(
1182
- [{ agent: "worker", task: "Write to {chain_dir}" }],
1183
- agents,
1184
- ),
1185
- );
1186
-
1187
- assert.ok(!result.isError);
1188
- const summary = result.content[0].text;
1189
- assert.ok(summary.includes("✅ Chain completed:"), `missing completion marker: ${summary}`);
1190
- assert.ok(summary.includes("📁 Artifacts:"), `missing artifacts marker: ${summary}`);
1191
- });
1192
-
1193
- it("stops chain on step failure", async () => {
1194
- mockPi.onCall({ exitCode: 1, stderr: "Agent crashed" });
1195
- const agents = [makeAgent("step1"), makeAgent("step2")];
1196
-
1197
- const result = await executeChain(
1198
- makeChainParams(
1199
- [{ agent: "step1", task: "Do first thing" }, { agent: "step2" }],
1200
- agents,
1201
- ),
1202
- );
1203
-
1204
- assert.ok(result.isError, "chain should fail");
1205
- assert.equal(result.details.results.length, 1, "only step1 should have run");
1206
- assert.equal(result.details.results[0].exitCode, 1);
1207
- });
1208
-
1209
- it("agent contract v1 chain defaults to execution gating after acceptance rejection", async () => {
1210
- mockPi.onCall({ output: "Step 1 done\n```acceptance-report\n{\"criteriaSatisfied\":[{\"id\":\"criterion-1\",\"status\":\"not-satisfied\",\"evidence\":\"no proof\"}]}\n```" });
1211
- mockPi.onCall({ output: "Step 2 ran" });
1212
- const agents = [makeAgent("step1", { completionGuard: false }), makeAgent("step2", { completionGuard: false })];
1213
-
1214
- const result = await executeChain(
1215
- makeChainParams(
1216
- [
1217
- { agent: "step1", task: "First", acceptance: { level: "checked", criteria: ["Return required proof"] } },
1218
- { agent: "step2", task: "Second receives {previous}" },
1219
- ],
1220
- agents,
1221
- { task: "Original", agentContract: { version: 1 } },
1222
- ),
1223
- );
1224
-
1225
- assert.equal(result.isError, undefined);
1226
- assert.equal(result.details.results.length, 2);
1227
- assert.equal(result.details.results[0]?.acceptance?.status, "rejected");
1228
- assert.equal(result.details.results[0]?.exitCode, 0);
1229
- assert.match(result.details.results[1]?.finalOutput ?? "", /Step 2 ran/);
1230
- });
1231
-
1232
- it("agent contract v1 chain can gate progression on acceptance", async () => {
1233
- mockPi.onCall({ output: "Step 1 done\n```acceptance-report\n{\"criteriaSatisfied\":[{\"id\":\"criterion-1\",\"status\":\"not-satisfied\",\"evidence\":\"no proof\"}]}\n```" });
1234
- const agents = [makeAgent("step1", { completionGuard: false }), makeAgent("step2", { completionGuard: false })];
1235
-
1236
- const result = await executeChain(
1237
- makeChainParams(
1238
- [
1239
- { agent: "step1", task: "First", acceptance: { level: "checked", criteria: ["Return required proof"] }, gateOn: "acceptance" },
1240
- { agent: "step2", task: "Should not run" },
1241
- ],
1242
- agents,
1243
- { task: "Original", agentContract: { version: 1 } },
1244
- ),
1245
- );
1246
-
1247
- assert.equal(result.isError, true);
1248
- assert.equal(result.details.results.length, 1);
1249
- assert.equal(result.details.results[0]?.acceptance?.status, "rejected");
1250
- assert.equal(mockPi.callCount(), 1);
1251
- });
1252
-
1253
- it("runs a 3-step chain end-to-end", async () => {
1254
- mockPi.onCall({ output: "Step output" });
1255
- const agents = [makeAgent("scout"), makeAgent("planner"), makeAgent("executor")];
1256
-
1257
- const result = await executeChain(
1258
- makeChainParams(
1259
- [
1260
- { agent: "scout", task: "Survey the codebase" },
1261
- { agent: "planner" },
1262
- { agent: "executor" },
1263
- ],
1264
- agents,
1265
- ),
1266
- );
1267
-
1268
- assert.ok(!result.isError);
1269
- assert.equal(result.details.results.length, 3);
1270
- assert.ok(result.details.results.every((r) => r.exitCode === 0));
1271
- });
1272
-
1273
- it("runs a 40-step alternating worker and reviewer chain", async () => {
1274
- const chainLength = 40;
1275
- for (let i = 0; i < chainLength; i++) {
1276
- mockPi.onCall({ output: `step-${i}-output` });
1277
- }
1278
- const chain = Array.from({ length: chainLength }, (_, i): TestSequentialStep => ({
1279
- agent: i % 2 === 0 ? "worker" : "reviewer",
1280
- ...(i === 0 ? { task: "Start long worker/reviewer chain" } : {}),
1281
- }));
1282
- const agents = [makeAgent("worker"), makeAgent("reviewer")];
1283
-
1284
- const result = await executeChain(makeChainParams(chain, agents));
1285
-
1286
- assert.ok(!result.isError, `long chain should succeed: ${JSON.stringify(result.content)}`);
1287
- assert.equal(mockPi.callCount(), chainLength);
1288
- assert.equal(result.details.results.length, chainLength);
1289
- assert.equal(result.details.totalSteps, chainLength);
1290
- assert.equal(result.details.chainAgents?.length, chainLength);
1291
- assert.equal(result.details.workflowGraph?.nodes.length, chainLength);
1292
- assert.equal(result.details.workflowGraph?.nodes.at(-1)?.agent, "reviewer");
1293
- assert.equal(result.details.workflowGraph?.nodes.at(-1)?.flatIndex, chainLength - 1);
1294
- assert.ok(result.details.results.every((r) => r.exitCode === 0));
1295
- assert.deepEqual(
1296
- result.details.results.map((r) => r.agent),
1297
- chain.map((step) => step.agent),
1298
- );
1299
-
1300
- const finalTaskArg = readCallArgs(chainLength - 1).at(-1) ?? "";
1301
- assert.match(finalTaskArg, /step-38-output/);
1302
- assert.doesNotMatch(finalTaskArg, /step-37-output/);
1303
- assert.match(result.content[0]?.text ?? "", /40 steps/);
1304
- });
1305
-
1306
- it("returns error for unknown agent in chain", async () => {
1307
- const agents = [makeAgent("scout")];
1308
-
1309
- const result = await executeChain(
1310
- makeChainParams(
1311
- [{ agent: "scout", task: "Start" }, { agent: "nonexistent" }],
1312
- agents,
1313
- ),
1314
- );
1315
-
1316
- assert.ok(result.isError);
1317
- assert.ok(result.content[0].text.includes("Unknown agent"));
1318
- });
1319
-
1320
- it("resolves relative step cwd values against the chain cwd for skills", async () => {
1321
- mockPi.onCall({ output: "ok" });
1322
- const chainCwd = path.join(tempDir, "worktree");
1323
- const stepPackageDir = path.join(chainCwd, "packages", "app");
1324
- writePackageSkill(stepPackageDir, "chain-step-skill");
1325
- const agents = [makeAgent("analyst", { skills: ["chain-step-skill"] })];
1326
-
1327
- const result = await executeChain(
1328
- makeChainParams(
1329
- [{ agent: "analyst", task: "Analyze", cwd: "packages/app" }],
1330
- agents,
1331
- { cwd: chainCwd },
1332
- ),
1333
- );
1334
-
1335
- assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
1336
- assert.deepEqual(result.details.results[0]?.skills, ["chain-step-skill"]);
1337
- });
1338
-
1339
- it("tracks chain metadata (chainAgents, totalSteps)", async () => {
1340
- mockPi.onCall({ output: "Done" });
1341
- const agents = [makeAgent("a"), makeAgent("b")];
1342
-
1343
- const result = await executeChain(
1344
- makeChainParams(
1345
- [{ agent: "a", task: "Start" }, { agent: "b" }],
1346
- agents,
1347
- ),
1348
- );
1349
-
1350
- assert.ok(!result.isError);
1351
- assert.deepEqual(result.details.chainAgents, ["a", "b"]);
1352
- assert.equal(result.details.totalSteps, 2);
1353
- });
1354
-
1355
- it("uses custom chainDir when provided", async () => {
1356
- mockPi.onCall({ output: "Done" });
1357
- const agents = [makeAgent("worker")];
1358
- const customChainDir = path.join(tempDir, "my-chain");
1359
-
1360
- const result = await executeChain(
1361
- makeChainParams(
1362
- [{ agent: "worker", task: "Use {chain_dir}" }],
1363
- agents,
1364
- { chainDir: customChainDir },
1365
- ),
1366
- );
1367
-
1368
- assert.ok(!result.isError);
1369
- assert.ok(fs.existsSync(customChainDir), "custom chainDir should exist");
1370
- });
1371
-
1372
- it("tightens child recursion depth per agent without relaxing the inherited chain max", async () => {
1373
- const originalDepth = process.env.SELESAI_SUBAGENT_DEPTH;
1374
- const originalMaxDepth = process.env.SELESAI_SUBAGENT_MAX_DEPTH;
1375
- delete process.env.SELESAI_SUBAGENT_DEPTH;
1376
- delete process.env.SELESAI_SUBAGENT_MAX_DEPTH;
1377
- try {
1378
- mockPi.onCall({ echoEnv: ["SELESAI_SUBAGENT_DEPTH", "SELESAI_SUBAGENT_MAX_DEPTH"] });
1379
- const agents = [makeAgent("worker", { maxSubagentDepth: 1 })];
1380
-
1381
- const result = await executeChain(
1382
- makeChainParams(
1383
- [{ agent: "worker", task: "Inspect env" }],
1384
- agents,
1385
- { maxSubagentDepth: 3 },
1386
- ),
1387
- );
1388
-
1389
- assert.ok(!result.isError);
1390
- assert.deepEqual(JSON.parse(result.details.results[0].finalOutput ?? "{}"), {
1391
- SELESAI_SUBAGENT_DEPTH: "1",
1392
- SELESAI_SUBAGENT_MAX_DEPTH: "1",
1393
- });
1394
- } finally {
1395
- if (originalDepth === undefined) delete process.env.SELESAI_SUBAGENT_DEPTH;
1396
- else process.env.SELESAI_SUBAGENT_DEPTH = originalDepth;
1397
- if (originalMaxDepth === undefined) delete process.env.SELESAI_SUBAGENT_MAX_DEPTH;
1398
- else process.env.SELESAI_SUBAGENT_MAX_DEPTH = originalMaxDepth;
1399
- }
1400
- });
1401
- });
1402
-
1403
- describe("chain execution — parallel steps", { skip: !available ? "pi packages not available" : undefined }, () => {
1404
- let tempDir: string;
1405
- let mockPi: MockPi;
1406
-
1407
- before(() => {
1408
- mockPi = createMockPi();
1409
- mockPi.install();
1410
- });
1411
-
1412
- after(() => {
1413
- mockPi.uninstall();
1414
- });
1415
-
1416
- beforeEach(() => {
1417
- tempDir = createTempDir();
1418
- mockPi.reset();
1419
- });
1420
-
1421
- afterEach(() => {
1422
- removeTempDir(tempDir);
1423
- });
1424
-
1425
- function makeChainParams(
1426
- chain: TestChainStep[],
1427
- agents: ReturnType<typeof makeAgent>[],
1428
- overrides: Record<string, unknown> = {},
1429
- ) {
1430
- return {
1431
- chain,
1432
- agents,
1433
- ctx: makeMinimalCtx(tempDir),
1434
- runId: `test-${Date.now().toString(36)}`,
1435
- shareEnabled: false,
1436
- sessionDirForIndex: () => undefined,
1437
- artifactsDir: path.join(tempDir, "artifacts"),
1438
- artifactConfig: { enabled: false },
1439
- clarify: false,
1440
- ...overrides,
1441
- };
1442
- }
1443
-
1444
- function git(args: string[]): string {
1445
- const result = spawnSync("git", args, { cwd: tempDir, encoding: "utf-8" });
1446
- assert.equal(result.status, 0, result.stderr || result.stdout);
1447
- return result.stdout.trim();
1448
- }
1449
-
1450
- function readCallArgs(index: number): string[] {
1451
- const callFiles = fs.readdirSync(mockPi.dir)
1452
- .filter((name) => name.startsWith("call-") && name.endsWith(".json"))
1453
- .sort();
1454
- const callFile = callFiles[index];
1455
- assert.ok(callFile, `expected call ${index}`);
1456
- return JSON.parse(fs.readFileSync(path.join(mockPi.dir, callFile), "utf-8")).args as string[];
1457
- }
1458
-
1459
- function readCallArgsMatching(text: string): string[] {
1460
- const callFiles = fs.readdirSync(mockPi.dir)
1461
- .filter((name) => name.startsWith("call-") && name.endsWith(".json"))
1462
- .sort();
1463
- for (const callFile of callFiles) {
1464
- const args = JSON.parse(fs.readFileSync(path.join(mockPi.dir, callFile), "utf-8")).args as string[];
1465
- if (args.join("\n").includes(text)) return args;
1466
- }
1467
- assert.fail(`expected recorded call containing ${text}`);
1468
- }
1469
-
1470
- it("runs parallel tasks within a chain step", async () => {
1471
- mockPi.onCall({ output: "Parallel task done" });
1472
- const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b")];
1473
-
1474
- const result = await executeChain(
1475
- makeChainParams(
1476
- [
1477
- {
1478
- parallel: [
1479
- { agent: "reviewer-a", task: "Review auth module" },
1480
- { agent: "reviewer-b", task: "Review data layer" },
1481
- ],
1482
- },
1483
- ],
1484
- agents,
1485
- ),
1486
- );
1487
-
1488
- assert.ok(!result.isError, `should succeed: ${JSON.stringify(result.content)}`);
1489
- assert.equal(result.details.results.length, 2);
1490
- assert.deepEqual(result.details.results.map((row) => row.index), [0, 1]);
1491
- });
1492
-
1493
- it("applies global permissions to foreground static parallel children", async () => {
1494
- mockPi.onCall({ echoEnv: [PERMISSION_POLICY_ENV, PERMISSION_AUDIT_PATH_ENV] });
1495
- mockPi.onCall({ echoEnv: [PERMISSION_POLICY_ENV, PERMISSION_AUDIT_PATH_ENV] });
1496
- const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b")];
1497
-
1498
- const result = await executeChain!(makeChainParams(
1499
- [{
1500
- parallel: [
1501
- { agent: "reviewer-a", task: "Review A" },
1502
- { agent: "reviewer-b", task: "Review B" },
1503
- ],
1504
- }],
1505
- agents,
1506
- { permissions: { rules: { write: "ask" } } },
1507
- ));
1508
-
1509
- assert.equal(result.isError, undefined);
1510
- assert.equal(result.details.results.length, 2);
1511
- for (const child of result.details.results) {
1512
- const env = JSON.parse(child.finalOutput ?? "{}") as Record<string, string | null>;
1513
- assert.equal(env[PERMISSION_POLICY_ENV], JSON.stringify({ write: "ask" }));
1514
- assert.match(env[PERMISSION_AUDIT_PATH_ENV] ?? "", /permission-audit\.jsonl$/);
1515
- }
1516
- });
1517
-
1518
- it("aggregates worktree handoffs across foreground chain groups", { skip: process.platform === "win32" ? "worktree paths differ on Windows" : undefined }, async () => {
1519
- git(["init"]);
1520
- git(["config", "user.email", "test@example.com"]);
1521
- git(["config", "user.name", "Test User"]);
1522
- fs.writeFileSync(path.join(tempDir, "tracked.txt"), "base\n", "utf-8");
1523
- git(["add", "tracked.txt"]);
1524
- git(["commit", "-m", "initial"]);
1525
- mockPi.onCall({ output: "Parallel task done" });
1526
- const runId = "foreground-chain-handoff";
1527
- const artifactsDir = path.join(tempDir, "artifacts");
1528
- const result = await executeChain(
1529
- makeChainParams(
1530
- [{ parallel: [{ agent: "reviewer-a", task: "Review in isolation" }], worktree: true }],
1531
- [makeAgent("reviewer-a")],
1532
- { runId, artifactsDir },
1533
- ),
1534
- );
1535
-
1536
- assert.equal(result.isError, undefined);
1537
- assert.equal(result.details.parallelHandoff?.version, 1);
1538
- assert.equal(result.details.parallelHandoff?.childCount, 1);
1539
- assert.equal(result.details.parallelHandoff?.cleanupState, "complete");
1540
- const handoff = JSON.parse(fs.readFileSync(result.details.parallelHandoff!.path, "utf-8")) as {
1541
- groups: Array<{ stepIndex: number; children: Array<{ agent: string; patch: { path: string } }>; cleanup: { state: string } }>;
1542
- };
1543
- assert.equal(handoff.groups[0]!.stepIndex, 0);
1544
- assert.equal(handoff.groups[0]!.children[0]!.agent, "reviewer-a");
1545
- assert.equal(handoff.groups[0]!.cleanup.state, "complete");
1546
- assert.equal(fs.existsSync(handoff.groups[0]!.children[0]!.patch.path), true);
1547
- assert.match(handoff.groups[0]!.children[0]!.patch.path, /worktree-diffs\/foreground-chain-handoff\/step-0\//);
1548
- });
1549
-
1550
- it("aggregates parallel outputs for next sequential step", async () => {
1551
- mockPi.onCall({ output: "Review findings here" });
1552
- const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b"), makeAgent("synthesizer")];
1553
-
1554
- const result = await executeChain(
1555
- makeChainParams(
1556
- [
1557
- {
1558
- parallel: [
1559
- { agent: "reviewer-a", task: "Review security" },
1560
- { agent: "reviewer-b", task: "Review performance" },
1561
- ],
1562
- },
1563
- { agent: "synthesizer" },
1564
- ],
1565
- agents,
1566
- ),
1567
- );
1568
-
1569
- assert.ok(!result.isError);
1570
- assert.equal(result.details.results.length, 3);
1571
- assert.equal(result.details.results[2].task, "[prompt redacted]");
1572
- const synthTask = readCallArgs(2).at(-1) ?? "";
1573
- assert.ok(
1574
- synthTask.includes("=== Parallel Task 1 (reviewer-a) ==="),
1575
- "synthesizer should include reviewer-a output block",
1576
- );
1577
- assert.ok(
1578
- synthTask.includes("=== Parallel Task 2 (reviewer-b) ==="),
1579
- "synthesizer should include reviewer-b output block",
1580
- );
1581
- });
1582
-
1583
- it("passes completed parallel task outputs to later {outputs.name} references", async () => {
1584
- mockPi.onCall({ matchArgIncludes: "Alpha", output: "Alpha named output" });
1585
- mockPi.onCall({ matchArgIncludes: "Beta", output: "Beta named output" });
1586
- mockPi.onCall({ output: "Final" });
1587
- const agents = [makeAgent("alpha"), makeAgent("beta"), makeAgent("writer")];
1588
-
1589
- const result = await executeChain(
1590
- makeChainParams(
1591
- [
1592
- {
1593
- parallel: [
1594
- { agent: "alpha", task: "Alpha", as: "alphaOutput" },
1595
- { agent: "beta", task: "Beta", as: "betaOutput" },
1596
- ],
1597
- },
1598
- { agent: "writer", task: "Use {outputs.alphaOutput} and {outputs.betaOutput}" },
1599
- ],
1600
- agents,
1601
- ),
1602
- );
1603
-
1604
- assert.ok(!result.isError);
1605
- const finalTask = readCallArgs(2).at(-1) ?? "";
1606
- assert.match(finalTask, /Alpha named output/);
1607
- assert.match(finalTask, /Beta named output/);
1608
- });
1609
-
1610
- it("funnels an initial parallel step through one agent, then fans the funnel output back out", async () => {
1611
- mockPi.onCall({ matchArgIncludes: "Scout API", output: "Scout A findings" });
1612
- mockPi.onCall({ matchArgIncludes: "Scout UI", output: "Scout B findings" });
1613
- mockPi.onCall({ matchArgIncludes: "Synthesize:", output: "Funnel synthesis" });
1614
- mockPi.onCall({ matchArgIncludes: "Review funnel A:", output: "Reviewer A done" });
1615
- mockPi.onCall({ matchArgIncludes: "Review funnel B:", output: "Reviewer B done" });
1616
- const agents = [makeAgent("scout-a"), makeAgent("scout-b"), makeAgent("synthesizer"), makeAgent("review-a"), makeAgent("review-b")];
1617
-
1618
- const result = await executeChain(
1619
- makeChainParams(
1620
- [
1621
- {
1622
- parallel: [
1623
- { agent: "scout-a", task: "Scout API" },
1624
- { agent: "scout-b", task: "Scout UI" },
1625
- ],
1626
- },
1627
- { agent: "synthesizer", task: "Synthesize:\n{previous}" },
1628
- {
1629
- parallel: [
1630
- { agent: "review-a", task: "Review funnel A:\n{previous}" },
1631
- { agent: "review-b", task: "Review funnel B:\n{previous}" },
1632
- ],
1633
- },
1634
- ],
1635
- agents,
1636
- ),
1637
- );
1638
-
1639
- assert.ok(!result.isError, `should succeed: ${JSON.stringify(result.content)}`);
1640
- assert.deepEqual(result.details.results.map((entry) => entry.agent), ["scout-a", "scout-b", "synthesizer", "review-a", "review-b"]);
1641
- assert.equal(result.details.totalSteps, 3);
1642
- const funnelTask = readCallArgsMatching("Synthesize:").at(-1) ?? "";
1643
- assert.match(funnelTask, /=== Parallel Task 1 \(scout-a\) ===/);
1644
- assert.match(funnelTask, /Scout A findings/);
1645
- assert.match(funnelTask, /=== Parallel Task 2 \(scout-b\) ===/);
1646
- assert.match(funnelTask, /Scout B findings/);
1647
- const fanoutTaskA = readCallArgsMatching("Review funnel A:").at(-1) ?? "";
1648
- const fanoutTaskB = readCallArgsMatching("Review funnel B:").at(-1) ?? "";
1649
- assert.match(fanoutTaskA, /Review funnel A:\nFunnel synthesis/);
1650
- assert.match(fanoutTaskB, /Review funnel B:\nFunnel synthesis/);
1651
- assert.equal(result.details.workflowGraph?.nodes[0]?.kind, "parallel-group");
1652
- assert.equal(result.details.workflowGraph?.nodes[1]?.kind, "step");
1653
- assert.equal(result.details.workflowGraph?.nodes[2]?.kind, "parallel-group");
1654
- });
1655
-
1656
- it("aggregates file-only parallel outputs as file references for the next step", async () => {
1657
- mockPi.onCall({ output: "full parallel chain output\nwith details" });
1658
- const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b"), makeAgent("synthesizer")];
1659
-
1660
- const result = await executeChain(
1661
- makeChainParams(
1662
- [
1663
- {
1664
- parallel: [
1665
- { agent: "reviewer-a", task: "Review A", output: "a.md", outputMode: "file-only" },
1666
- { agent: "reviewer-b", task: "Review B", output: "b.md", outputMode: "file-only" },
1667
- ],
1668
- },
1669
- { agent: "synthesizer" },
1670
- ],
1671
- agents,
1672
- { chainDir: tempDir },
1673
- ),
1674
- );
1675
-
1676
- assert.ok(!result.isError, `should succeed: ${JSON.stringify(result.content)}`);
1677
- assert.doesNotMatch(result.details.results[0]?.finalOutput ?? "", /full parallel chain output/);
1678
- assert.doesNotMatch(result.details.results[1]?.finalOutput ?? "", /full parallel chain output/);
1679
- const synthTaskArg = readCallArgs(2).at(-1) ?? "";
1680
- assert.match(synthTaskArg, /Output saved to:/);
1681
- assert.match(synthTaskArg, /2 lines/);
1682
- assert.doesNotMatch(synthTaskArg, /full parallel chain output/);
1683
- });
1684
-
1685
- it("rejects chain parallel file-only output without spawning siblings", async () => {
1686
- const agents = [makeAgent("reviewer-a"), makeAgent("reviewer-b")];
1687
-
1688
- const result = await executeChain(
1689
- makeChainParams(
1690
- [{
1691
- parallel: [
1692
- { agent: "reviewer-a", task: "Review A", outputMode: "file-only" },
1693
- { agent: "reviewer-b", task: "Review B", output: "b.md" },
1694
- ],
1695
- }],
1696
- agents,
1697
- { chainDir: tempDir },
1698
- ),
1699
- );
1700
-
1701
- assert.equal(result.isError, true);
1702
- assert.match(result.content[0]?.text ?? "", /outputMode: "file-only"/);
1703
- assert.equal(mockPi.callCount(), 0);
1704
- });
1705
-
1706
- it("detaches parallel chain children cleanly on intercom handoff", async () => {
1707
- mockPi.onCall({
1708
- steps: [
1709
- { jsonl: [events.toolStart("intercom", { action: "send", to: "orchestrator" })] },
1710
- { delay: 25, jsonl: [events.assistantMessage("after handoff")] },
1711
- ],
1712
- });
1713
- mockPi.onCall({ delay: 150, output: "Other task done" });
1714
- const agents = [
1715
- makeAgent("a", { systemPrompt: "Intercom orchestration channel:" }),
1716
- makeAgent("b", { systemPrompt: "Intercom orchestration channel:" }),
1717
- ];
1718
- const intercomEvents = createEventBus();
1719
- let detachEmitted = false;
1720
-
1721
- const result = await executeChain(
1722
- makeChainParams(
1723
- [
1724
- {
1725
- parallel: [
1726
- { agent: "a", task: "Send handoff" },
1727
- { agent: "b", task: "Keep working" },
1728
- ],
1729
- },
1730
- ],
1731
- agents,
1732
- {
1733
- intercomEvents,
1734
- onUpdate(update: { details?: { progress?: Array<{ currentTool?: string }> } }) {
1735
- if (detachEmitted) return;
1736
- if (!update.details?.progress?.some((entry) => entry.currentTool === "intercom")) return;
1737
- detachEmitted = true;
1738
- intercomEvents.emit(INTERCOM_DETACH_REQUEST_EVENT, { requestId: "chain-parallel-detach" });
1739
- },
1740
- },
1741
- ),
1742
- );
1743
-
1744
- assert.equal(result.isError, undefined);
1745
- assert.match(result.content[0]?.text ?? "", /Chain detached for intercom coordination/);
1746
- assert.match(result.content[0]?.text ?? "", /do not resume or launch a replacement/);
1747
- assert.equal(detachEmitted, true);
1748
- assert.equal(result.details.results.some((entry) => entry.detached === true && entry.exitCode === -2), true);
1749
- });
1750
-
1751
- it("stops a sequential chain when a child detaches for intercom coordination", async () => {
1752
- mockPi.onCall({
1753
- steps: [
1754
- { jsonl: [events.toolStart("contact_supervisor", { reason: "need_decision", message: "Need a decision" })] },
1755
- { delay: 1000, jsonl: [events.assistantMessage("after reply")] },
1756
- ],
1757
- });
1758
- const agents = [
1759
- makeAgent("a", { systemPrompt: "Intercom orchestration channel:" }),
1760
- makeAgent("b"),
1761
- ];
1762
- const intercomEvents = createEventBus();
1763
- let detachEmitted = false;
1764
-
1765
- const result = await executeChain(
1766
- makeChainParams(
1767
- [
1768
- { agent: "a", task: "Ask supervisor" },
1769
- { agent: "b", task: "Must not run yet" },
1770
- ],
1771
- agents,
1772
- {
1773
- intercomEvents,
1774
- onUpdate(update: { details?: { progress?: Array<{ currentTool?: string }> } }) {
1775
- if (detachEmitted) return;
1776
- if (!update.details?.progress?.some((entry) => entry.currentTool === "contact_supervisor")) return;
1777
- detachEmitted = true;
1778
- intercomEvents.emit(INTERCOM_DETACH_REQUEST_EVENT, { requestId: "chain-sequential-detach" });
1779
- },
1780
- },
1781
- ),
1782
- );
1783
-
1784
- assert.equal(result.isError, undefined);
1785
- assert.match(result.content[0]?.text ?? "", /Chain detached for intercom coordination/);
1786
- assert.match(result.content[0]?.text ?? "", /do not resume or launch a replacement/);
1787
- assert.equal(detachEmitted, true);
1788
- assert.equal(mockPi.callCount(), 1);
1789
- });
1790
-
1791
- it("fails chain on parallel step failure", async () => {
1792
- mockPi.onCall({ exitCode: 1, stderr: "Parallel task failed" });
1793
- const agents = [makeAgent("a"), makeAgent("b")];
1794
-
1795
- const result = await executeChain(
1796
- makeChainParams(
1797
- [
1798
- {
1799
- parallel: [
1800
- { agent: "a", task: "Task A" },
1801
- { agent: "b", task: "Task B" },
1802
- ],
1803
- },
1804
- ],
1805
- agents,
1806
- ),
1807
- );
1808
-
1809
- assert.ok(result.isError, "chain should fail when parallel step fails");
1810
- });
1811
-
1812
- it("rejects worktree parallel steps that set a different task cwd", async () => {
1813
- const agents = [makeAgent("a"), makeAgent("b")];
1814
- const result = await executeChain(
1815
- makeChainParams(
1816
- [
1817
- {
1818
- parallel: [
1819
- { agent: "a", task: "Task A" },
1820
- { agent: "b", task: "Task B", cwd: path.join(tempDir, "other") },
1821
- ],
1822
- worktree: true,
1823
- },
1824
- ],
1825
- agents,
1826
- ),
1827
- );
1828
-
1829
- assert.ok(result.isError, "chain should reject conflicting task cwd under worktree");
1830
- assert.match(result.content[0]?.text ?? "", /worktree isolation uses the shared cwd/i);
1831
- assert.match(result.content[0]?.text ?? "", /task 2 \(b\) sets cwd/i);
1832
- });
1833
-
1834
- it("sequential → parallel → sequential (mixed chain)", async () => {
1835
- mockPi.onCall({ output: "Step complete" });
1836
- const agents = [makeAgent("scout"), makeAgent("rev-a"), makeAgent("rev-b"), makeAgent("writer")];
1837
-
1838
- const result = await executeChain(
1839
- makeChainParams(
1840
- [
1841
- { agent: "scout", task: "Initial scan" },
1842
- {
1843
- parallel: [
1844
- { agent: "rev-a", task: "Deep review A" },
1845
- { agent: "rev-b", task: "Deep review B" },
1846
- ],
1847
- },
1848
- { agent: "writer" },
1849
- ],
1850
- agents,
1851
- ),
1852
- );
1853
-
1854
- assert.ok(!result.isError);
1855
- assert.equal(result.details.results.length, 4);
1856
- assert.deepEqual(result.details.results.map((row) => row.index), [0, 1, 2, 3]);
1857
- assert.equal(result.details.totalSteps, 3);
1858
- });
1859
- });