@selesai/code 0.5.20 → 0.5.21

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 (263) hide show
  1. package/dist/extensions/handoff-new.test.ts +8 -9
  2. package/dist/extensions/handoff-new.ts +6 -8
  3. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  4. package/dist/extensions/pi-subagents/README.md +602 -220
  5. package/dist/extensions/pi-subagents/banner.png +0 -0
  6. package/dist/extensions/pi-subagents/index.ts +1 -0
  7. package/dist/extensions/pi-subagents/install.mjs +3 -3
  8. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  9. package/dist/extensions/pi-subagents/package.json +26 -14
  10. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  11. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  12. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  13. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  14. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  15. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  16. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  18. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  19. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  20. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  21. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  22. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  23. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  24. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  25. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  26. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  27. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  28. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  29. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  30. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  31. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  32. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  33. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  34. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  35. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  36. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  37. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  38. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  39. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  40. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  41. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  42. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  43. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  44. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  45. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  46. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  48. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  49. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  50. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  51. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  53. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  54. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  55. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  56. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  57. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  58. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  59. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  60. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  61. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  62. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  63. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  65. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  66. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  67. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  68. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  69. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  70. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  71. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  72. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  73. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  74. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  75. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  76. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  77. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  78. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  79. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  80. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  81. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  82. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  83. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  87. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  88. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  89. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  90. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  92. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  94. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  97. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  98. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  99. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  100. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  101. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  102. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  103. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  104. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  105. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  106. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  107. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  108. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  109. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  110. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  111. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  112. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  113. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  114. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  115. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  116. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  117. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  120. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  121. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  122. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  123. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  124. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  125. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  126. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  127. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  128. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  129. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  130. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  131. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  132. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  133. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  134. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  135. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  136. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  137. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  138. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  139. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  140. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  141. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  142. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  143. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  144. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  145. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  146. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  147. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  148. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  149. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  150. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  151. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  152. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  153. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  154. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  155. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  156. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  157. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  158. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  159. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  160. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  161. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  162. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  163. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  164. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  165. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  166. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  167. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  168. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  169. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  170. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  171. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  172. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  173. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  174. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  175. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  176. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  177. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  178. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  179. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  180. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  181. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  182. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  183. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  184. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  185. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  186. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  187. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  188. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  189. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  190. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  191. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  192. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  194. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  195. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  196. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  197. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  198. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  199. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  200. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  201. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  202. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  203. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  204. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  205. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  206. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  207. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  208. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  209. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  210. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  211. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  212. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  213. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  214. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  215. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  216. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  217. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  218. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  219. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  220. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  221. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  222. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  223. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  224. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  225. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  226. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  227. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  228. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  229. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  230. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  231. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  232. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  233. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  234. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  235. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  236. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  237. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  238. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  239. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  240. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  241. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  245. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  246. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  247. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  248. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  249. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  250. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  251. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  252. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  253. package/dist/extensions/question/batch.ts +39 -0
  254. package/dist/extensions/question/dialog-adapter.ts +23 -1
  255. package/dist/extensions/question/index.ts +237 -7
  256. package/dist/extensions/question/schemas.ts +28 -15
  257. package/dist/extensions/question/tests/batch.test.ts +26 -0
  258. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  259. package/dist/extensions/question/tui-adapter.ts +2 -3
  260. package/dist/extensions/question/types.ts +37 -0
  261. package/dist/extensions/question/ui-protocol.ts +3 -3
  262. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  263. package/package.json +1 -1
@@ -33,9 +33,11 @@ import {
33
33
  import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
34
34
  import { INTERCOM_BRIDGE_MARKER } from "../../intercom/intercom-bridge.ts";
35
35
  import { runSync } from "./execution.ts";
36
+ import { beginForegroundChild, finishForegroundChild, updateForegroundChild } from "./foreground-control.ts";
36
37
  import { buildChainSummary } from "../../shared/formatters.ts";
37
38
  import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
38
39
  import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
40
+ import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup } from "../shared/parallel-handoff.ts";
39
41
  import { recordRun } from "../shared/run-history.ts";
40
42
  import {
41
43
  cleanupWorktrees,
@@ -48,11 +50,13 @@ import {
48
50
  } from "../shared/worktree.ts";
49
51
  import {
50
52
  type ActivityState,
53
+ type AgentContract,
51
54
  type AgentProgress,
52
55
  type ArtifactConfig,
53
56
  type ArtifactPaths,
54
57
  type ControlEvent,
55
58
  type Details,
59
+ type ForegroundRunControl,
56
60
  type IntercomEventBus,
57
61
  type NestedRouteInfo,
58
62
  type ResolvedControlConfig,
@@ -63,16 +67,19 @@ import {
63
67
  MAX_CONCURRENCY,
64
68
  resolveChildMaxSubagentDepth,
65
69
  } from "../../shared/types.ts";
66
- import { resolveSubagentModelOverride } from "../shared/model-fallback.ts";
70
+ import { resolveEffectiveSubagentModel } from "../shared/model-fallback.ts";
67
71
  import type { ModelScopeConfig } from "../shared/model-scope.ts";
68
- import { validateFileOnlyOutputMode } from "../shared/single-output.ts";
72
+ import { injectSingleOutputInstruction, validateFileOnlyOutputMode } from "../shared/single-output.ts";
69
73
  import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
70
74
  import { ChainOutputValidationError, outputEntryFromResult, resolveOutputReferences, validateChainOutputBindings } from "../shared/chain-outputs.ts";
71
75
  import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
72
76
  import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection, type DynamicCollectedResult } from "../shared/dynamic-fanout.ts";
73
77
  import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, resolveEffectiveAcceptance } from "../shared/acceptance.ts";
78
+ import { isAgentContractV1 } from "../shared/agent-contract.ts";
74
79
  import type { ChainOutputMap } from "../../shared/types.ts";
75
80
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
81
+ import type { ContextMode } from "../shared/context-mode.ts";
82
+ import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
76
83
 
77
84
  interface ChainExecutionDetailsInput {
78
85
  results: SingleResult[];
@@ -88,7 +95,8 @@ interface ChainExecutionDetailsInput {
88
95
  outputs?: ChainOutputMap;
89
96
  currentFlatIndex?: number;
90
97
  dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
91
- dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
98
+ dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "stopped" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
99
+ parallelHandoff?: Details["parallelHandoff"];
92
100
  }
93
101
 
94
102
  interface ParallelChainRunInput {
@@ -109,8 +117,9 @@ interface ParallelChainRunInput {
109
117
  globalTaskIndex: number;
110
118
  sessionDirForIndex: (idx?: number) => string | undefined;
111
119
  sessionFileForIndex?: (idx?: number) => string | undefined;
112
- sessionFileForTask?: (agentName: string, idx?: number) => string | undefined;
113
- thinkingOverrideForTask?: (agentName: string, idx?: number) => AgentConfig["thinking"] | undefined;
120
+ sessionFileForTask?: (agentName: string, idx?: number, modelOverride?: string) => string | undefined;
121
+ thinkingOverrideForTask?: (agentName: string, idx?: number, modelOverride?: string) => AgentConfig["thinking"] | undefined;
122
+ contextForAgent?: (agentName: string) => ContextMode;
114
123
  shareEnabled: boolean;
115
124
  artifactConfig: ArtifactConfig;
116
125
  artifactsDir: string;
@@ -118,22 +127,10 @@ interface ParallelChainRunInput {
118
127
  onUpdate?: (r: AgentToolResult<Details>) => void;
119
128
  onControlEvent?: (event: ControlEvent) => void;
120
129
  controlConfig: ResolvedControlConfig;
130
+ agentContract?: AgentContract;
121
131
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
122
132
  orchestratorIntercomTarget?: string;
123
- foregroundControl?: {
124
- updatedAt: number;
125
- currentAgent?: string;
126
- currentIndex?: number;
127
- currentActivityState?: ActivityState;
128
- lastActivityAt?: number;
129
- currentTool?: string;
130
- currentToolStartedAt?: number;
131
- currentPath?: string;
132
- turnCount?: number;
133
- tokens?: number;
134
- toolCount?: number;
135
- interrupt?: () => boolean;
136
- };
133
+ foregroundControl?: ForegroundRunControl;
137
134
  results: SingleResult[];
138
135
  allProgress: AgentProgress[];
139
136
  outputs: ChainOutputMap;
@@ -152,6 +149,8 @@ interface ParallelChainRunInput {
152
149
  toolBudget?: ResolvedToolBudget;
153
150
  configToolBudget?: ToolBudgetConfig;
154
151
  globalSemaphore?: Semaphore;
152
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
153
+ dynamic?: boolean;
155
154
  }
156
155
 
157
156
  function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details {
@@ -166,6 +165,7 @@ function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details
166
165
  outputs: input.outputs,
167
166
  totalChildUsage: sumResultsUsage(input.results),
168
167
  totalCost: sumResultsCost(input.results),
168
+ ...(input.parallelHandoff ? { parallelHandoff: input.parallelHandoff } : {}),
169
169
  workflowGraph: buildWorkflowGraphSnapshot({
170
170
  runId: input.runId,
171
171
  mode: "chain",
@@ -199,17 +199,48 @@ function ensureParallelProgressFile(
199
199
  return true;
200
200
  }
201
201
 
202
- function appendParallelWorktreeSummary(
203
- output: string,
204
- worktreeSetup: WorktreeSetup | undefined,
205
- diffsDir: string,
206
- agents: string[],
207
- ): string {
208
- if (!worktreeSetup) return output;
209
- const diffs = diffWorktrees(worktreeSetup, agents, diffsDir);
202
+ function finalizeParallelWorktreeHandoff(input: {
203
+ output: string;
204
+ worktreeSetup: WorktreeSetup;
205
+ artifactsDir: string;
206
+ runId: string;
207
+ cwd: string;
208
+ stepIndex: number;
209
+ flatStartIndex: number;
210
+ agents: string[];
211
+ results: SingleResult[];
212
+ }): { output: string; reference?: NonNullable<Details["parallelHandoff"]> } {
213
+ const diffs = diffWorktrees(input.worktreeSetup, input.agents, path.join(input.artifactsDir, "worktree-diffs", input.runId, `step-${input.stepIndex}`));
214
+ const cleanup = cleanupWorktrees(input.worktreeSetup);
210
215
  const diffSummary = formatWorktreeDiffSummary(diffs);
211
- if (!diffSummary) return output;
212
- return `${output}\n\n${diffSummary}`;
216
+ try {
217
+ const reference = writeParallelHandoffGroup({
218
+ manifestPath: parallelHandoffPath(input.artifactsDir, input.runId),
219
+ runId: input.runId,
220
+ mode: "chain",
221
+ source: "foreground",
222
+ cwd: input.cwd,
223
+ stepIndex: input.stepIndex,
224
+ flatStartIndex: input.flatStartIndex,
225
+ setup: input.worktreeSetup,
226
+ diffs,
227
+ cleanup,
228
+ results: input.results.map((result) => ({
229
+ agent: result.agent,
230
+ status: result.stopped ? "stopped" : result.detached ? "detached" : result.interrupted ? "paused" : result.exitCode === 0 ? "completed" : "failed",
231
+ summary: getSingleResultOutput(result) || result.error || "(no output)",
232
+ ...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
233
+ ...(result.structuredOutput !== undefined ? { structuredOutput: result.structuredOutput } : {}),
234
+ ...(result.structuredOutputPath ? { structuredOutputPath: result.structuredOutputPath } : {}),
235
+ ...(result.sessionFile ? { sessionPath: result.sessionFile } : {}),
236
+ })),
237
+ });
238
+ const suffix = [diffSummary, formatParallelHandoffReference(reference)].filter(Boolean).join("\n\n");
239
+ return { output: suffix ? `${input.output}\n\n${suffix}` : input.output, reference };
240
+ } catch (error) {
241
+ const suffix = [diffSummary, formatParallelHandoffError(error)].filter(Boolean).join("\n\n");
242
+ return { output: suffix ? `${input.output}\n\n${suffix}` : input.output };
243
+ }
213
244
  }
214
245
 
215
246
  function resolveChainToolBudget(input: { stepBudget?: ToolBudgetConfig; runBudget?: ResolvedToolBudget; agentBudget?: ToolBudgetConfig; configBudget?: ToolBudgetConfig }): { toolBudget?: ResolvedToolBudget; error?: string } {
@@ -230,6 +261,21 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
230
261
  const concurrency = input.step.concurrency ?? MAX_CONCURRENCY;
231
262
  const failFast = input.step.failFast ?? false;
232
263
  let aborted = false;
264
+ const effectiveModels = input.step.parallel.map((task) => {
265
+ const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
266
+ return resolveEffectiveSubagentModel(
267
+ task.model,
268
+ taskAgentConfig?.model,
269
+ input.ctx.model,
270
+ input.availableModels,
271
+ input.ctx.model?.provider,
272
+ { scope: input.modelScope },
273
+ );
274
+ });
275
+ for (let taskIndex = 0; taskIndex < input.step.parallel.length; taskIndex++) {
276
+ const task = input.step.parallel[taskIndex]!;
277
+ input.sessionFileForTask?.(task.agent, input.globalTaskIndex + taskIndex, effectiveModels[taskIndex]);
278
+ }
233
279
 
234
280
  const parallelResults = await mapConcurrent(
235
281
  input.step.parallel,
@@ -248,9 +294,10 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
248
294
 
249
295
  const taskTemplate = input.parallelTemplates[taskIndex] ?? "{previous}";
250
296
  const behavior = suppressProgressForReadOnlyTask(input.parallelBehaviors[taskIndex]!, taskTemplate, input.originalTask);
297
+ const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
251
298
  const templateHasPrevious = taskTemplate.includes("{previous}");
252
299
  const { prefix, suffix } = buildChainInstructions(
253
- behavior,
300
+ { ...behavior, output: false },
254
301
  input.chainDir,
255
302
  false,
256
303
  templateHasPrevious ? undefined : input.prev,
@@ -263,14 +310,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
263
310
  const cleanTask = taskStr;
264
311
  taskStr = prefix + taskStr + suffix;
265
312
 
266
- const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
267
- const effectiveModel = resolveSubagentModelOverride(
268
- task.model ?? taskAgentConfig?.model,
269
- input.ctx.model,
270
- input.availableModels,
271
- input.ctx.model?.provider,
272
- { scope: input.modelScope, source: task.model ? "explicit" : "inherited" },
273
- );
313
+ const effectiveModel = effectiveModels[taskIndex];
274
314
  const maxSubagentDepth = resolveChildMaxSubagentDepth(input.maxSubagentDepth, taskAgentConfig?.maxSubagentDepth);
275
315
  const toolBudget = resolveChainToolBudget({ stepBudget: task.toolBudget, runBudget: input.toolBudget, agentBudget: taskAgentConfig?.toolBudget, configBudget: input.configToolBudget });
276
316
  if (toolBudget.error) throw new Error(toolBudget.error);
@@ -282,37 +322,41 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
282
322
  const outputPath = typeof behavior.output === "string"
283
323
  ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(input.chainDir, behavior.output))
284
324
  : undefined;
325
+ taskStr = injectSingleOutputInstruction(taskStr, outputPath, taskAgentConfig);
285
326
  const interruptController = new AbortController();
327
+ const childIndex = input.globalTaskIndex + taskIndex;
286
328
  if (input.foregroundControl) {
287
- input.foregroundControl.currentAgent = task.agent;
288
- input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
289
- input.foregroundControl.currentActivityState = undefined;
290
- input.foregroundControl.updatedAt = Date.now();
291
- input.foregroundControl.interrupt = () => {
292
- if (interruptController.signal.aborted) return false;
293
- interruptController.abort();
294
- input.foregroundControl!.currentActivityState = undefined;
295
- input.foregroundControl!.updatedAt = Date.now();
296
- return true;
297
- };
329
+ beginForegroundChild(input.foregroundControl, {
330
+ index: childIndex,
331
+ agent: task.agent,
332
+ description: cleanTask.trim(),
333
+ interrupt: () => {
334
+ if (interruptController.signal.aborted) return false;
335
+ interruptController.abort();
336
+ return true;
337
+ },
338
+ });
298
339
  }
299
340
 
300
341
  const structuredRuntime = task.outputSchema
301
342
  ? createStructuredOutputRuntime(task.outputSchema, path.join(input.chainDir, "structured-output"))
302
343
  : undefined;
344
+ const agentContract = task.agentContract ?? input.step.agentContract ?? input.agentContract;
303
345
  const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
304
346
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
347
+ capabilityCeiling: input.capabilityCeiling,
348
+ context: input.contextForAgent?.(task.agent),
305
349
  cwd: taskCwd,
306
350
  signal: input.signal,
307
351
  interruptSignal: interruptController.signal,
308
352
  allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
309
353
  intercomEvents: input.intercomEvents,
310
354
  runId: input.runId,
311
- index: input.globalTaskIndex + taskIndex,
312
- sessionDir: input.sessionDirForIndex(input.globalTaskIndex + taskIndex),
313
- sessionFile: input.sessionFileForTask?.(task.agent, input.globalTaskIndex + taskIndex)
314
- ?? input.sessionFileForIndex?.(input.globalTaskIndex + taskIndex),
315
- thinkingOverride: input.thinkingOverrideForTask?.(task.agent, input.globalTaskIndex + taskIndex),
355
+ index: childIndex,
356
+ sessionDir: input.sessionDirForIndex(childIndex),
357
+ sessionFile: input.sessionFileForTask?.(task.agent, childIndex, effectiveModel)
358
+ ?? input.sessionFileForIndex?.(childIndex),
359
+ thinkingOverride: input.thinkingOverrideForTask?.(task.agent, childIndex, effectiveModel),
316
360
  share: input.shareEnabled,
317
361
  artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
318
362
  artifactConfig: input.artifactConfig,
@@ -321,7 +365,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
321
365
  maxSubagentDepth,
322
366
  controlConfig: input.controlConfig,
323
367
  onControlEvent: input.onControlEvent,
324
- intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
368
+ intercomSessionName: input.childIntercomTarget?.(task.agent, childIndex),
325
369
  orchestratorIntercomTarget: input.orchestratorIntercomTarget,
326
370
  nestedRoute: input.nestedRoute,
327
371
  modelOverride: effectiveModel,
@@ -330,13 +374,14 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
330
374
  modelScope: input.modelScope,
331
375
  skills: behavior.skills === false ? [] : behavior.skills,
332
376
  structuredOutput: structuredRuntime,
377
+ agentContract,
333
378
  acceptance: task.acceptance,
334
- acceptanceContext: { mode: "chain" },
379
+ acceptanceContext: { mode: "chain", dynamic: input.dynamic && task.acceptance === undefined },
335
380
  timeoutMs: input.timeoutMs,
336
381
  deadlineAt: input.deadlineAt,
337
382
  turnBudget: input.turnBudget,
338
383
  onDetachedExit: input.onDetachedExit
339
- ? (result) => input.onDetachedExit?.(input.globalTaskIndex + taskIndex, result)
384
+ ? (result) => input.onDetachedExit?.(childIndex, result)
340
385
  : undefined,
341
386
  toolBudget: toolBudget.toolBudget,
342
387
  onUpdate: input.onUpdate
@@ -344,18 +389,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
344
389
  const stepResults = progressUpdate.details?.results || [];
345
390
  const stepProgress = progressUpdate.details?.progress || [];
346
391
  if (input.foregroundControl && stepProgress.length > 0) {
347
- const current = stepProgress[0];
348
- input.foregroundControl.currentAgent = task.agent;
349
- input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
350
- input.foregroundControl.currentActivityState = current?.activityState;
351
- input.foregroundControl.lastActivityAt = current?.lastActivityAt;
352
- input.foregroundControl.currentTool = current?.currentTool;
353
- input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
354
- input.foregroundControl.currentPath = current?.currentPath;
355
- input.foregroundControl.turnCount = current?.turnCount;
356
- input.foregroundControl.tokens = current?.tokens;
357
- input.foregroundControl.toolCount = current?.toolCount;
358
- input.foregroundControl.updatedAt = Date.now();
392
+ updateForegroundChild(input.foregroundControl, childIndex, stepProgress[0]);
359
393
  }
360
394
  input.onUpdate?.({
361
395
  ...progressUpdate,
@@ -374,7 +408,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
374
408
  steps: input.chainSteps,
375
409
  results: input.results.concat(stepResults),
376
410
  currentStepIndex: input.stepIndex,
377
- currentFlatIndex: input.globalTaskIndex + taskIndex,
411
+ currentFlatIndex: childIndex,
378
412
  dynamicChildren: input.dynamicChildren,
379
413
  dynamicGroupStatuses: input.dynamicGroupStatuses,
380
414
  }),
@@ -383,10 +417,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
383
417
  }
384
418
  : undefined,
385
419
  });
386
- if (input.foregroundControl?.currentIndex === input.globalTaskIndex + taskIndex) {
387
- input.foregroundControl.interrupt = undefined;
388
- input.foregroundControl.updatedAt = Date.now();
389
- }
420
+ if (input.foregroundControl) finishForegroundChild(input.foregroundControl, childIndex);
390
421
 
391
422
  if (result.exitCode !== 0 && failFast) {
392
423
  aborted = true;
@@ -412,8 +443,10 @@ interface ChainExecutionParams {
412
443
  shareEnabled: boolean;
413
444
  sessionDirForIndex: (idx?: number) => string | undefined;
414
445
  sessionFileForIndex?: (idx?: number) => string | undefined;
415
- sessionFileForTask?: (agentName: string, idx?: number) => string | undefined;
416
- thinkingOverrideForTask?: (agentName: string, idx?: number) => AgentConfig["thinking"] | undefined;
446
+ sessionFileForTask?: (agentName: string, idx?: number, modelOverride?: string) => string | undefined;
447
+ thinkingOverrideForTask?: (agentName: string, idx?: number, modelOverride?: string) => AgentConfig["thinking"] | undefined;
448
+ contextForAgent?: (agentName: string) => ContextMode;
449
+ modelScope?: ModelScopeConfig;
417
450
  artifactsDir: string;
418
451
  artifactConfig: ArtifactConfig;
419
452
  includeProgress?: boolean;
@@ -421,22 +454,10 @@ interface ChainExecutionParams {
421
454
  onUpdate?: (r: AgentToolResult<Details>) => void;
422
455
  onControlEvent?: (event: ControlEvent) => void;
423
456
  controlConfig: ResolvedControlConfig;
457
+ agentContract?: AgentContract;
424
458
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
425
459
  orchestratorIntercomTarget?: string;
426
- foregroundControl?: {
427
- updatedAt: number;
428
- currentAgent?: string;
429
- currentIndex?: number;
430
- currentActivityState?: ActivityState;
431
- lastActivityAt?: number;
432
- currentTool?: string;
433
- currentToolStartedAt?: number;
434
- currentPath?: string;
435
- turnCount?: number;
436
- tokens?: number;
437
- toolCount?: number;
438
- interrupt?: () => boolean;
439
- };
460
+ foregroundControl?: ForegroundRunControl;
440
461
  chainSkills?: string[];
441
462
  chainDir?: string;
442
463
  dynamicFanoutMaxItems?: number;
@@ -453,6 +474,7 @@ interface ChainExecutionParams {
453
474
  configToolBudget?: ToolBudgetConfig;
454
475
  /** Global cap on simultaneously-running tasks within this chain. Defaults to DEFAULT_GLOBAL_CONCURRENCY_LIMIT. */
455
476
  globalConcurrencyLimit?: number;
477
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
456
478
  }
457
479
 
458
480
  interface ChainExecutionResult {
@@ -506,6 +528,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
506
528
  const dynamicGroupStatuses: ChainExecutionDetailsInput["dynamicGroupStatuses"] = {};
507
529
  const allProgress: AgentProgress[] = [];
508
530
  const allArtifactPaths: ArtifactPaths[] = [];
531
+ let parallelHandoff: Details["parallelHandoff"];
509
532
 
510
533
  const chainAgents: string[] = chainSteps.map((step) =>
511
534
  isParallelStep(step)
@@ -529,6 +552,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
529
552
  outputs,
530
553
  dynamicChildren,
531
554
  dynamicGroupStatuses,
555
+ ...(parallelHandoff ? { parallelHandoff } : {}),
532
556
  ...overrides,
533
557
  });
534
558
 
@@ -627,7 +651,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
627
651
  return {
628
652
  ...step,
629
653
  task: result.templates[i]!,
630
- ...(override?.model ? { model: override.model } : {}),
654
+ ...(override?.model !== undefined ? { model: override.model } : {}),
631
655
  ...(override?.output !== undefined ? { output: override.output } : {}),
632
656
  ...("outputMode" in step && step.outputMode !== undefined ? { outputMode: step.outputMode } : {}),
633
657
  ...(override?.reads !== undefined ? { reads: override.reads } : {}),
@@ -660,6 +684,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
660
684
  const parallelTemplates = stepTemplates as string[];
661
685
  const parallelCwd = resolveChildCwd(cwd ?? ctx.cwd, step.cwd);
662
686
  let worktreeSetup: WorktreeSetup | undefined;
687
+ let worktreeFinalized = false;
663
688
  if (step.worktree) {
664
689
  const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(step.parallel, parallelCwd);
665
690
  if (worktreeTaskCwdConflict) {
@@ -717,6 +742,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
717
742
  sessionFileForIndex,
718
743
  sessionFileForTask,
719
744
  thinkingOverrideForTask,
745
+ contextForAgent: params.contextForAgent,
720
746
  shareEnabled,
721
747
  artifactConfig,
722
748
  artifactsDir,
@@ -731,6 +757,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
731
757
  dynamicChildren,
732
758
  dynamicGroupStatuses,
733
759
  controlConfig,
760
+ agentContract: params.agentContract,
734
761
  onControlEvent,
735
762
  childIntercomTarget,
736
763
  orchestratorIntercomTarget,
@@ -753,6 +780,23 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
753
780
  if (result.progress) allProgress.push(result.progress);
754
781
  if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
755
782
  }
783
+ let worktreeSuffix = "";
784
+ if (worktreeSetup) {
785
+ worktreeFinalized = true;
786
+ const finalized = finalizeParallelWorktreeHandoff({
787
+ output: "",
788
+ worktreeSetup,
789
+ artifactsDir,
790
+ runId,
791
+ cwd: parallelCwd,
792
+ stepIndex,
793
+ flatStartIndex: globalTaskIndex - step.parallel.length,
794
+ agents: agentNames,
795
+ results: parallelResults,
796
+ });
797
+ if (finalized.reference) parallelHandoff = finalized.reference;
798
+ worktreeSuffix = finalized.output.trim();
799
+ }
756
800
  const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
757
801
  const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
758
802
  if (interrupted) {
@@ -768,7 +812,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
768
812
  const detached = detachedIndexInStep >= 0 ? parallelResults[detachedIndexInStep] : undefined;
769
813
  if (detached) {
770
814
  return {
771
- content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
815
+ content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.` }],
772
816
  details: buildChainExecutionDetails(makeDetailsInput({
773
817
  currentStepIndex: stepIndex,
774
818
  currentFlatIndex: globalTaskIndex - step.parallel.length + detachedIndexInStep,
@@ -797,6 +841,24 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
797
841
  })),
798
842
  };
799
843
  }
844
+ const acceptanceFailures = parallelResults
845
+ .map((result, originalIndex) => ({ result, originalIndex, task: step.parallel[originalIndex] }))
846
+ .filter(({ result, task }) => isAgentContractV1(task?.agentContract ?? step.agentContract ?? params.agentContract) && (task?.gateOn ?? step.gateOn) === "acceptance" && result.acceptance?.status === "rejected");
847
+ if (acceptanceFailures.length > 0) {
848
+ const acceptanceSummary = acceptanceFailures
849
+ .map(({ result, originalIndex }) => `- Task ${originalIndex + 1} (${result.agent}): ${acceptanceFailureMessage(result.acceptance) ?? "acceptance rejected"}`)
850
+ .join("\n");
851
+ const errorMsg = `Parallel step ${stepIndex + 1} acceptance gate failed:\n${acceptanceSummary}`;
852
+ const summary = buildChainSummary(chainSteps, results, chainDir, "failed", { index: stepIndex, error: errorMsg });
853
+ return {
854
+ content: [{ type: "text", text: summary }],
855
+ isError: true,
856
+ details: buildChainExecutionDetails(makeDetailsInput({
857
+ currentStepIndex: stepIndex,
858
+ currentFlatIndex: globalTaskIndex - step.parallel.length + acceptanceFailures[0]!.originalIndex,
859
+ })),
860
+ };
861
+ }
800
862
 
801
863
  for (let taskIndex = 0; taskIndex < parallelResults.length; taskIndex++) {
802
864
  const outputName = step.parallel[taskIndex]?.as;
@@ -820,14 +882,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
820
882
  };
821
883
  });
822
884
  prev = aggregateParallelOutputs(taskResults);
823
- prev = appendParallelWorktreeSummary(
824
- prev,
825
- worktreeSetup,
826
- path.join(chainDir, "worktree-diffs", `step-${stepIndex}`),
827
- agentNames,
828
- );
885
+ if (worktreeSuffix) prev = `${prev}\n\n${worktreeSuffix}`;
829
886
  } finally {
830
- if (worktreeSetup) cleanupWorktrees(worktreeSetup);
887
+ if (worktreeSetup && !worktreeFinalized) cleanupWorktrees(worktreeSetup);
831
888
  }
832
889
  } else if (isDynamicParallelStep(step)) {
833
890
  const dynamicStartIndex = globalTaskIndex;
@@ -852,7 +909,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
852
909
  if (materialized.parallel.length === 0) {
853
910
  const collection: DynamicCollectedResult[] = [];
854
911
  try {
855
- validateDynamicCollection(step.collect.outputSchema, collection);
912
+ await validateDynamicCollection(step.collect.outputSchema, collection);
856
913
  } catch (error) {
857
914
  const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
858
915
  dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
@@ -869,9 +926,11 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
869
926
  const effectiveGroupAcceptance = resolveEffectiveAcceptance({
870
927
  explicit: step.acceptance,
871
928
  agentName: step.parallel.agent,
872
- task: step.parallel.task ?? originalTask,
929
+ acceptanceRole: agents.find((agent) => agent.name === step.parallel.agent)?.acceptanceRole,
930
+ task: (step.parallel.task ?? originalTask ?? "").replace(/\{task\}/g, originalTask ?? ""),
873
931
  mode: "chain",
874
932
  dynamicGroup: true,
933
+ agentContract: step.agentContract ?? params.agentContract,
875
934
  });
876
935
  const groupAcceptance = await evaluateAcceptance({
877
936
  acceptance: effectiveGroupAcceptance,
@@ -881,9 +940,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
881
940
  notes: "Dynamic fanout produced 0 results.",
882
941
  }),
883
942
  cwd: cwd ?? ctx.cwd,
943
+ reportOptional: isAgentContractV1(step.agentContract ?? params.agentContract),
884
944
  });
885
945
  dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
886
- const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
946
+ const groupAcceptanceFailure = !isAgentContractV1(step.agentContract ?? params.agentContract) || step.gateOn === "acceptance" ? acceptanceFailureMessage(groupAcceptance) : undefined;
887
947
  if (groupAcceptanceFailure) {
888
948
  dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
889
949
  return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
@@ -898,6 +958,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
898
958
  parallel: materialized.parallel,
899
959
  concurrency: step.concurrency,
900
960
  failFast: step.failFast,
961
+ agentContract: step.agentContract,
962
+ gateOn: step.gateOn,
901
963
  };
902
964
  const parallelTemplates = materialized.parallel.map((task) => task.task ?? "{previous}");
903
965
  const parallelBehaviors = resolveParallelBehaviors(dynamicParallelStep.parallel, agents, stepIndex, chainSkills)
@@ -937,6 +999,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
937
999
  sessionFileForIndex,
938
1000
  sessionFileForTask,
939
1001
  thinkingOverrideForTask,
1002
+ contextForAgent: params.contextForAgent,
940
1003
  shareEnabled,
941
1004
  artifactConfig,
942
1005
  artifactsDir,
@@ -951,6 +1014,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
951
1014
  dynamicChildren,
952
1015
  dynamicGroupStatuses,
953
1016
  controlConfig,
1017
+ agentContract: params.agentContract,
954
1018
  onControlEvent,
955
1019
  childIntercomTarget,
956
1020
  orchestratorIntercomTarget,
@@ -964,6 +1028,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
964
1028
  toolBudget: params.toolBudget,
965
1029
  configToolBudget: params.configToolBudget,
966
1030
  globalSemaphore,
1031
+ dynamic: true,
967
1032
  });
968
1033
  globalTaskIndex = dynamicStartIndex + reservedDynamicItems;
969
1034
 
@@ -988,7 +1053,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
988
1053
  const detached = detachedIndexInStep >= 0 ? parallelResults[detachedIndexInStep] : undefined;
989
1054
  if (detached) {
990
1055
  return {
991
- content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
1056
+ content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${detached.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.` }],
992
1057
  details: buildChainExecutionDetails(makeDetailsInput({
993
1058
  currentStepIndex: stepIndex,
994
1059
  currentFlatIndex: dynamicStartIndex + detachedIndexInStep,
@@ -1017,8 +1082,24 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1017
1082
  })),
1018
1083
  };
1019
1084
  }
1085
+ const acceptanceFailures = parallelResults
1086
+ .map((result, originalIndex) => ({ result, originalIndex, task: dynamicParallelStep.parallel[originalIndex] }))
1087
+ .filter(({ result, task }) => isAgentContractV1(task?.agentContract ?? dynamicParallelStep.agentContract ?? params.agentContract) && (task?.gateOn ?? dynamicParallelStep.gateOn) === "acceptance" && result.acceptance?.status === "rejected");
1088
+ if (acceptanceFailures.length > 0) {
1089
+ const acceptanceSummary = acceptanceFailures
1090
+ .map(({ result, originalIndex }) => `- Item ${originalIndex + 1} (${result.agent}, key ${materialized.items[originalIndex]?.key ?? originalIndex}): ${acceptanceFailureMessage(result.acceptance) ?? "acceptance rejected"}`)
1091
+ .join("\n");
1092
+ const errorMsg = `Dynamic step ${stepIndex + 1} acceptance gate failed:\n${acceptanceSummary}`;
1093
+ dynamicGroupStatuses[stepIndex] = { status: "failed", error: errorMsg };
1094
+ const summary = buildChainSummary(chainSteps, results, chainDir, "failed", { index: stepIndex, error: errorMsg });
1095
+ return {
1096
+ content: [{ type: "text", text: summary }],
1097
+ isError: true,
1098
+ details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: dynamicStartIndex + acceptanceFailures[0]!.originalIndex })),
1099
+ };
1100
+ }
1020
1101
  try {
1021
- validateDynamicCollection(step.collect.outputSchema, collected);
1102
+ await validateDynamicCollection(step.collect.outputSchema, collected);
1022
1103
  } catch (error) {
1023
1104
  const message = error instanceof DynamicFanoutError ? error.message : error instanceof Error ? error.message : String(error);
1024
1105
  dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
@@ -1034,9 +1115,13 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1034
1115
  const effectiveGroupAcceptance = resolveEffectiveAcceptance({
1035
1116
  explicit: step.acceptance,
1036
1117
  agentName: step.parallel.agent,
1037
- task: step.parallel.task ?? originalTask,
1118
+ acceptanceRole: agents.find((agent) => agent.name === step.parallel.agent)?.acceptanceRole,
1119
+ task: materialized.parallel
1120
+ .map((task) => (task.task ?? originalTask ?? "").replace(/\{task\}/g, originalTask ?? ""))
1121
+ .join("\n"),
1038
1122
  mode: "chain",
1039
1123
  dynamicGroup: true,
1124
+ agentContract: step.agentContract ?? params.agentContract,
1040
1125
  });
1041
1126
  const groupAcceptance = await evaluateAcceptance({
1042
1127
  acceptance: effectiveGroupAcceptance,
@@ -1046,9 +1131,10 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1046
1131
  notes: `Dynamic fanout collected ${collected.length} result(s) into ${step.collect.as}.`,
1047
1132
  }),
1048
1133
  cwd: cwd ?? ctx.cwd,
1134
+ reportOptional: isAgentContractV1(step.agentContract ?? params.agentContract),
1049
1135
  });
1050
1136
  dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
1051
- const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
1137
+ const groupAcceptanceFailure = effectiveGroupAcceptance.explicit && (!isAgentContractV1(step.agentContract ?? params.agentContract) || step.gateOn === "acceptance") ? acceptanceFailureMessage(groupAcceptance) : undefined;
1052
1138
  if (groupAcceptanceFailure) {
1053
1139
  dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
1054
1140
  return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
@@ -1096,7 +1182,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1096
1182
 
1097
1183
  const templateHasPrevious = stepTemplate.includes("{previous}");
1098
1184
  const { prefix, suffix } = buildChainInstructions(
1099
- behavior,
1185
+ { ...behavior, output: false },
1100
1186
  chainDir,
1101
1187
  isFirstProgress,
1102
1188
  templateHasPrevious ? undefined : prev,
@@ -1110,17 +1196,19 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1110
1196
  stepTask = prefix + stepTask + suffix;
1111
1197
 
1112
1198
  const explicitStepModel = tuiOverride?.model ?? seqStep.model;
1113
- const effectiveModel = resolveSubagentModelOverride(
1114
- explicitStepModel ?? agentConfig.model,
1199
+ const effectiveModel = resolveEffectiveSubagentModel(
1200
+ explicitStepModel,
1201
+ agentConfig.model,
1115
1202
  ctx.model,
1116
1203
  availableModels,
1117
1204
  ctx.model?.provider,
1118
- { scope: modelScope, source: explicitStepModel ? "explicit" : "inherited" },
1205
+ { scope: modelScope },
1119
1206
  );
1120
1207
 
1121
1208
  const outputPath = typeof behavior.output === "string"
1122
1209
  ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
1123
1210
  : undefined;
1211
+ stepTask = injectSingleOutputInstruction(stepTask, outputPath, agentConfig);
1124
1212
  const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
1125
1213
  if (validationError) {
1126
1214
  return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
@@ -1129,22 +1217,22 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1129
1217
  const childIndex = globalTaskIndex;
1130
1218
  const interruptController = new AbortController();
1131
1219
  if (foregroundControl) {
1132
- foregroundControl.currentAgent = seqStep.agent;
1133
- foregroundControl.currentIndex = childIndex;
1134
- foregroundControl.currentActivityState = undefined;
1135
- foregroundControl.updatedAt = Date.now();
1136
- foregroundControl.interrupt = () => {
1137
- if (interruptController.signal.aborted) return false;
1138
- interruptController.abort();
1139
- foregroundControl.currentActivityState = undefined;
1140
- foregroundControl.updatedAt = Date.now();
1141
- return true;
1142
- };
1220
+ beginForegroundChild(foregroundControl, {
1221
+ index: childIndex,
1222
+ agent: seqStep.agent,
1223
+ description: cleanTask.trim(),
1224
+ interrupt: () => {
1225
+ if (interruptController.signal.aborted) return false;
1226
+ interruptController.abort();
1227
+ return true;
1228
+ },
1229
+ });
1143
1230
  }
1144
1231
 
1145
1232
  const structuredRuntime = seqStep.outputSchema
1146
1233
  ? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
1147
1234
  : undefined;
1235
+ const agentContract = seqStep.agentContract ?? params.agentContract;
1148
1236
  const toolBudget = resolveChainToolBudget({ stepBudget: seqStep.toolBudget, runBudget: params.toolBudget, agentBudget: agentConfig?.toolBudget, configBudget: params.configToolBudget });
1149
1237
  if (toolBudget.error) return buildChainExecutionErrorResult(toolBudget.error, {
1150
1238
  results,
@@ -1164,6 +1252,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1164
1252
  });
1165
1253
  const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
1166
1254
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
1255
+ capabilityCeiling: params.capabilityCeiling,
1256
+ context: params.contextForAgent?.(seqStep.agent),
1167
1257
  cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
1168
1258
  signal,
1169
1259
  interruptSignal: interruptController.signal,
@@ -1172,9 +1262,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1172
1262
  runId,
1173
1263
  index: childIndex,
1174
1264
  sessionDir: sessionDirForIndex(childIndex),
1175
- sessionFile: sessionFileForTask?.(seqStep.agent, childIndex)
1265
+ sessionFile: sessionFileForTask?.(seqStep.agent, childIndex, effectiveModel)
1176
1266
  ?? sessionFileForIndex?.(childIndex),
1177
- thinkingOverride: thinkingOverrideForTask?.(seqStep.agent, childIndex),
1267
+ thinkingOverride: thinkingOverrideForTask?.(seqStep.agent, childIndex, effectiveModel),
1178
1268
  share: shareEnabled,
1179
1269
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1180
1270
  artifactConfig,
@@ -1192,6 +1282,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1192
1282
  modelScope,
1193
1283
  skills: behavior.skills === false ? [] : behavior.skills,
1194
1284
  structuredOutput: structuredRuntime,
1285
+ agentContract,
1195
1286
  acceptance: seqStep.acceptance,
1196
1287
  acceptanceContext: { mode: "chain" },
1197
1288
  timeoutMs: params.timeoutMs,
@@ -1206,18 +1297,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1206
1297
  const stepResults = p.details?.results || [];
1207
1298
  const stepProgress = p.details?.progress || [];
1208
1299
  if (foregroundControl && stepProgress.length > 0) {
1209
- const current = stepProgress[0];
1210
- foregroundControl.currentAgent = seqStep.agent;
1211
- foregroundControl.currentIndex = childIndex;
1212
- foregroundControl.currentActivityState = current?.activityState;
1213
- foregroundControl.lastActivityAt = current?.lastActivityAt;
1214
- foregroundControl.currentTool = current?.currentTool;
1215
- foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
1216
- foregroundControl.currentPath = current?.currentPath;
1217
- foregroundControl.turnCount = current?.turnCount;
1218
- foregroundControl.tokens = current?.tokens;
1219
- foregroundControl.toolCount = current?.toolCount;
1220
- foregroundControl.updatedAt = Date.now();
1300
+ updateForegroundChild(foregroundControl, childIndex, stepProgress[0]);
1221
1301
  }
1222
1302
  onUpdate({
1223
1303
  ...p,
@@ -1245,10 +1325,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1245
1325
  }
1246
1326
  : undefined,
1247
1327
  });
1248
- if (foregroundControl?.currentIndex === childIndex) {
1249
- foregroundControl.interrupt = undefined;
1250
- foregroundControl.updatedAt = Date.now();
1251
- }
1328
+ if (foregroundControl) finishForegroundChild(foregroundControl, childIndex);
1252
1329
  recordRun(seqStep.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
1253
1330
 
1254
1331
  globalTaskIndex++;
@@ -1264,7 +1341,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1264
1341
  }
1265
1342
  if (r.detached) {
1266
1343
  return {
1267
- content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${r.agent}). Reply to the supervisor request first. After the child exits, start a fresh follow-up if needed.` }],
1344
+ content: [{ type: "text", text: `Chain detached for intercom coordination at step ${stepIndex + 1} (${r.agent}). Reply to the supervisor request first, then wait with subagent_wait({ id: "${runId}" }). Use subagent({ action: "status", id: "${runId}" }) to recover the result; do not resume or launch a replacement while it remains detached.` }],
1268
1345
  details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: childIndex })),
1269
1346
  };
1270
1347
  }
@@ -1280,6 +1357,17 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1280
1357
  isError: true,
1281
1358
  };
1282
1359
  }
1360
+ if (isAgentContractV1(agentContract) && seqStep.gateOn === "acceptance" && r.acceptance?.status === "rejected") {
1361
+ const summary = buildChainSummary(chainSteps, results, chainDir, "failed", {
1362
+ index: stepIndex,
1363
+ error: acceptanceFailureMessage(r.acceptance) ?? "Chain acceptance gate rejected the step.",
1364
+ });
1365
+ return {
1366
+ content: [{ type: "text", text: summary }],
1367
+ details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: childIndex })),
1368
+ isError: true,
1369
+ };
1370
+ }
1283
1371
 
1284
1372
  if (behavior.output) {
1285
1373
  try {