@selesai/code 0.5.28 → 0.6.1

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 (341) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/credential-print.d.ts +23 -0
  10. package/dist/cli/credential-print.d.ts.map +1 -0
  11. package/dist/cli/credential-print.js +117 -0
  12. package/dist/cli/credential-print.js.map +1 -0
  13. package/dist/cli/startup-ui.d.ts.map +1 -1
  14. package/dist/cli/startup-ui.js +1 -1
  15. package/dist/cli/startup-ui.js.map +1 -1
  16. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  17. package/dist/core/agent-session-runtime.js +3 -0
  18. package/dist/core/agent-session-runtime.js.map +1 -1
  19. package/dist/core/agent-session.d.ts +12 -1
  20. package/dist/core/agent-session.d.ts.map +1 -1
  21. package/dist/core/agent-session.js +20 -14
  22. package/dist/core/agent-session.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts.map +1 -1
  24. package/dist/core/compaction/compaction.js +11 -3
  25. package/dist/core/compaction/compaction.js.map +1 -1
  26. package/dist/core/extensions/runner.d.ts +1 -0
  27. package/dist/core/extensions/runner.d.ts.map +1 -1
  28. package/dist/core/extensions/runner.js +11 -0
  29. package/dist/core/extensions/runner.js.map +1 -1
  30. package/dist/core/extensions/types.d.ts +14 -1
  31. package/dist/core/extensions/types.d.ts.map +1 -1
  32. package/dist/core/extensions/types.js.map +1 -1
  33. package/dist/core/footer-data-provider.d.ts +10 -0
  34. package/dist/core/footer-data-provider.d.ts.map +1 -1
  35. package/dist/core/footer-data-provider.js +1 -1
  36. package/dist/core/footer-data-provider.js.map +1 -1
  37. package/dist/core/llama/provider.d.ts.map +1 -1
  38. package/dist/core/llama/provider.js +8 -3
  39. package/dist/core/llama/provider.js.map +1 -1
  40. package/dist/core/model-config.d.ts +30 -0
  41. package/dist/core/model-config.d.ts.map +1 -1
  42. package/dist/core/model-config.js +6 -0
  43. package/dist/core/model-config.js.map +1 -1
  44. package/dist/core/model-registry.d.ts.map +1 -1
  45. package/dist/core/model-registry.js +2 -2
  46. package/dist/core/model-registry.js.map +1 -1
  47. package/dist/core/model-resolver.d.ts +1 -0
  48. package/dist/core/model-resolver.d.ts.map +1 -1
  49. package/dist/core/model-resolver.js +20 -3
  50. package/dist/core/model-resolver.js.map +1 -1
  51. package/dist/core/model-runtime.d.ts +2 -0
  52. package/dist/core/model-runtime.d.ts.map +1 -1
  53. package/dist/core/model-runtime.js +5 -4
  54. package/dist/core/model-runtime.js.map +1 -1
  55. package/dist/core/package-manager.d.ts.map +1 -1
  56. package/dist/core/package-manager.js +13 -6
  57. package/dist/core/package-manager.js.map +1 -1
  58. package/dist/core/remote-catalog-provider.d.ts +1 -1
  59. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  60. package/dist/core/remote-catalog-provider.js +24 -11
  61. package/dist/core/remote-catalog-provider.js.map +1 -1
  62. package/dist/core/resource-loader.d.ts +15 -0
  63. package/dist/core/resource-loader.d.ts.map +1 -1
  64. package/dist/core/resource-loader.js +66 -9
  65. package/dist/core/resource-loader.js.map +1 -1
  66. package/dist/core/settings-manager.d.ts +1 -1
  67. package/dist/core/settings-manager.d.ts.map +1 -1
  68. package/dist/core/settings-manager.js.map +1 -1
  69. package/dist/core/system-prompt.d.ts.map +1 -1
  70. package/dist/core/system-prompt.js +19 -1
  71. package/dist/core/system-prompt.js.map +1 -1
  72. package/dist/core/system-prompt.test.d.ts +2 -0
  73. package/dist/core/system-prompt.test.d.ts.map +1 -0
  74. package/dist/core/system-prompt.test.js +89 -0
  75. package/dist/core/system-prompt.test.js.map +1 -0
  76. package/dist/core/tools/bash.d.ts +2 -0
  77. package/dist/core/tools/bash.d.ts.map +1 -1
  78. package/dist/core/tools/bash.js +34 -5
  79. package/dist/core/tools/bash.js.map +1 -1
  80. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  81. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  82. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  83. package/dist/defaults/models.json +13 -45
  84. package/dist/defaults/settings.json +1 -2
  85. package/dist/extensions/copy-turn.test.ts +131 -0
  86. package/dist/extensions/copy-turn.ts +6 -1
  87. package/dist/extensions/package.json +0 -1
  88. package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
  89. package/dist/extensions/pi-intercom/README.md +102 -39
  90. package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
  91. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
  92. package/dist/extensions/pi-intercom/broker/client.ts +315 -12
  93. package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
  94. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
  95. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
  96. package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
  97. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
  98. package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
  99. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
  100. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
  101. package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
  102. package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
  103. package/dist/extensions/pi-intercom/config.test.ts +93 -0
  104. package/dist/extensions/pi-intercom/config.ts +55 -6
  105. package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
  106. package/dist/extensions/pi-intercom/cwd.ts +31 -0
  107. package/dist/extensions/pi-intercom/extension-api.ts +44 -0
  108. package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
  109. package/dist/extensions/pi-intercom/format-context.ts +32 -0
  110. package/dist/extensions/pi-intercom/index.ts +742 -145
  111. package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
  112. package/dist/extensions/pi-intercom/package.json +15 -5
  113. package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
  114. package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
  115. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
  116. package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
  117. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
  118. package/dist/extensions/pi-intercom/types.ts +94 -4
  119. package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
  120. package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
  121. package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
  122. package/dist/extensions/pi-subagents/CHANGELOG.md +88 -0
  123. package/dist/extensions/pi-subagents/LICENSE +21 -0
  124. package/dist/extensions/pi-subagents/README.md +158 -69
  125. package/dist/extensions/pi-subagents/agents/architect.md +5 -4
  126. package/dist/extensions/pi-subagents/agents/builder.md +6 -4
  127. package/dist/extensions/pi-subagents/agents/commentator.md +3 -2
  128. package/dist/extensions/pi-subagents/agents/explorer.md +3 -2
  129. package/dist/extensions/pi-subagents/agents/recapper.md +3 -2
  130. package/dist/extensions/pi-subagents/agents/researcher.md +4 -3
  131. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  132. package/dist/extensions/pi-subagents/package.json +3 -3
  133. package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -988
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +257 -0
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +431 -0
  137. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
  138. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +281 -0
  139. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +125 -37
  140. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
  141. package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
  142. package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
  143. package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +125 -0
  144. package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
  145. package/dist/extensions/pi-subagents/src/api/preflight.ts +17 -13
  146. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
  147. package/dist/extensions/pi-subagents/src/extension/index.ts +22 -8
  148. package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
  149. package/dist/extensions/pi-subagents/src/extension/schemas.ts +33 -8
  150. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +32 -14
  151. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
  152. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
  153. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +86 -21
  154. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
  155. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
  156. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  157. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
  158. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
  159. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +56 -5
  160. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +92 -11
  161. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +388 -29
  163. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
  164. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
  165. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +214 -60
  166. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +546 -252
  167. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
  168. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +553 -154
  169. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +19 -13
  170. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
  171. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
  172. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  173. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
  174. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
  175. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
  176. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
  177. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
  178. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
  179. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
  180. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
  181. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +63 -9
  182. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
  183. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
  184. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +22 -1
  185. package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
  186. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
  187. package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
  188. package/dist/extensions/pi-subagents/src/shared/types.ts +196 -12
  189. package/dist/extensions/pi-subagents/src/shared/utils.ts +102 -5
  190. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +10 -1
  191. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
  192. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
  193. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
  194. package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
  195. package/dist/extensions/pi-subagents/src/tui/render.ts +55 -34
  196. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
  197. package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
  198. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
  199. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
  200. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
  201. package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
  202. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +113 -8
  203. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +419 -47
  204. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
  205. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
  206. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
  207. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +62 -22
  208. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
  209. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +8 -6
  210. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +231 -19
  211. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +48 -7
  212. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
  213. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +199 -17
  214. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +898 -16
  215. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
  216. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
  217. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  218. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +18 -2
  219. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
  220. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
  221. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
  222. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +198 -6
  223. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +161 -1
  224. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
  225. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
  226. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
  227. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +63 -0
  228. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +136 -0
  229. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
  230. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
  231. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
  232. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
  233. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
  234. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
  235. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +28 -2
  236. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
  237. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
  238. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
  239. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
  240. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +17 -12
  241. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
  242. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
  243. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
  244. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +39 -2
  245. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
  246. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
  247. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
  248. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +48 -0
  249. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
  250. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
  251. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
  252. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
  253. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
  254. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +46 -2
  255. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +91 -1
  256. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  257. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
  258. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
  259. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
  260. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
  261. package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +213 -0
  262. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -1
  263. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +61 -10
  264. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
  265. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
  266. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
  267. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
  268. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
  269. package/dist/main.d.ts.map +1 -1
  270. package/dist/main.js +43 -0
  271. package/dist/main.js.map +1 -1
  272. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  273. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  274. package/dist/modes/interactive/components/custom-message.js +10 -2
  275. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  276. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  277. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  278. package/dist/modes/interactive/components/extension-editor.js +16 -46
  279. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  280. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/model-selector.js +4 -1
  282. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  283. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  284. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  285. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  286. package/dist/modes/interactive/external-editor.d.ts +12 -0
  287. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  288. package/dist/modes/interactive/external-editor.js +37 -0
  289. package/dist/modes/interactive/external-editor.js.map +1 -0
  290. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  291. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  292. package/dist/modes/interactive/interactive-mode.js +72 -64
  293. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  294. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  295. package/dist/modes/rpc/rpc-mode.js +14 -0
  296. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  297. package/dist/skills/ponytail/SKILL.md +1 -3
  298. package/dist/utils/clipboard.d.ts.map +1 -1
  299. package/dist/utils/clipboard.js +19 -8
  300. package/dist/utils/clipboard.js.map +1 -1
  301. package/dist/utils/version-check.d.ts.map +1 -1
  302. package/dist/utils/version-check.js +1 -1
  303. package/dist/utils/version-check.js.map +1 -1
  304. package/docs/compaction.md +1 -1
  305. package/docs/custom-provider.md +14 -5
  306. package/docs/environment-variables.md +86 -0
  307. package/docs/extensions.md +15 -5
  308. package/docs/index.md +1 -0
  309. package/docs/models.md +9 -2
  310. package/docs/plans/subagent-delegation/phase-0-correctness.md +265 -0
  311. package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +486 -0
  312. package/docs/plans/subagent-delegation/phase-2-context-controls.md +282 -0
  313. package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +362 -0
  314. package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +381 -0
  315. package/docs/providers.md +21 -2
  316. package/docs/rpc.md +23 -6
  317. package/docs/session-format.md +2 -0
  318. package/docs/settings.md +1 -1
  319. package/docs/usage.md +0 -13
  320. package/examples/extensions/custom-compaction.ts +5 -2
  321. package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
  322. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  323. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  324. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  325. package/examples/extensions/gondolin/package-lock.json +2 -2
  326. package/examples/extensions/gondolin/package.json +1 -1
  327. package/examples/extensions/handoff.ts +11 -3
  328. package/examples/extensions/message-renderer.ts +3 -3
  329. package/examples/extensions/sandbox/package-lock.json +2 -2
  330. package/examples/extensions/sandbox/package.json +1 -1
  331. package/examples/extensions/summarize.ts +5 -2
  332. package/examples/extensions/with-deps/package-lock.json +2 -2
  333. package/examples/extensions/with-deps/package.json +1 -1
  334. package/examples/sdk/12-full-control.ts +3 -1
  335. package/package.json +16 -7
  336. package/dist/extensions/caveman/caveman-instructions.cjs +0 -11
  337. package/dist/extensions/caveman/index.js +0 -118
  338. package/dist/extensions/caveman/package.json +0 -8
  339. package/dist/extensions/caveman/test/extension.test.js +0 -203
  340. package/dist/extensions/caveman/test/helpers.test.js +0 -58
  341. package/dist/skills/caveman/SKILL.md +0 -50
@@ -0,0 +1,65 @@
1
+ import * as path from "node:path";
2
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
3
+ import type { Details, SubagentState } from "../../shared/types.ts";
4
+ import { deliverStopRequest } from "../background/control-channel.ts";
5
+ import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
6
+
7
+ function getAsyncStopTarget(
8
+ state: SubagentState,
9
+ runId: string | undefined,
10
+ location?: { asyncDir: string | null; resolvedId?: string },
11
+ ): { asyncId: string; asyncDir: string } | undefined {
12
+ if (location?.asyncDir) {
13
+ return {
14
+ asyncId: location.resolvedId ?? runId ?? path.basename(location.asyncDir),
15
+ asyncDir: location.asyncDir,
16
+ };
17
+ }
18
+ if (!runId) return undefined;
19
+ const direct = state.asyncJobs.get(runId);
20
+ return direct ? { asyncId: direct.asyncId, asyncDir: direct.asyncDir } : undefined;
21
+ }
22
+
23
+ export function stopAsyncRun(
24
+ state: SubagentState,
25
+ runId: string | undefined,
26
+ kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean,
27
+ location?: { asyncDir: string | null; resolvedId?: string },
28
+ ): AgentToolResult<Details> | null {
29
+ const target = getAsyncStopTarget(state, runId, location);
30
+ if (!target) return null;
31
+ const status = reconcileAsyncRun(target.asyncDir, { kill }).status;
32
+ if (state.currentSessionId && status?.sessionId !== state.currentSessionId) {
33
+ return {
34
+ content: [{ type: "text", text: `Async run '${target.asyncId}' was not found in the active session.` }],
35
+ isError: true,
36
+ details: { mode: "management", results: [] },
37
+ };
38
+ }
39
+ if (!status || (status.state !== "running" && status.state !== "queued")) {
40
+ return {
41
+ content: [{ type: "text", text: `No running or queued async run was found for '${runId ?? "current"}'.` }],
42
+ isError: true,
43
+ details: { mode: "management", results: [] },
44
+ };
45
+ }
46
+ try {
47
+ deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action" });
48
+ const tracked = state.asyncJobs.get(target.asyncId);
49
+ if (tracked) {
50
+ tracked.activityState = undefined;
51
+ tracked.updatedAt = Date.now();
52
+ }
53
+ return {
54
+ content: [{ type: "text", text: `Stop requested for async run ${target.asyncId}.` }],
55
+ details: { mode: "management", results: [] },
56
+ };
57
+ } catch (error) {
58
+ const message = error instanceof Error ? error.message : String(error);
59
+ return {
60
+ content: [{ type: "text", text: `Failed to stop async run ${target.asyncId}: ${message}` }],
61
+ isError: true,
62
+ details: { mode: "management", results: [] },
63
+ };
64
+ }
65
+ }
@@ -451,13 +451,13 @@ export class ChainClarifyComponent implements Component {
451
451
  return;
452
452
  }
453
453
 
454
- if (matchesKey(data, "up")) {
454
+ if (matchesKey(data, "up") || matchesKey(data, "k")) {
455
455
  this.selectedStep = Math.max(0, this.selectedStep - 1);
456
456
  this.tui.requestRender();
457
457
  return;
458
458
  }
459
459
 
460
- if (matchesKey(data, "down")) {
460
+ if (matchesKey(data, "down") || matchesKey(data, "j")) {
461
461
  const maxStep = Math.max(0, this.agentConfigs.length - 1);
462
462
  this.selectedStep = Math.min(maxStep, this.selectedStep + 1);
463
463
  this.tui.requestRender();
@@ -1123,7 +1123,7 @@ export class ChainClarifyComponent implements Component {
1123
1123
  private getFooterText(): string {
1124
1124
  return this.mode === 'single'
1125
1125
  ? " [Enter] Run • [Esc] Cancel "
1126
- : " [Enter] Run • [Esc] Cancel • [↑↓] Navigate ";
1126
+ : " [Enter] Run • [Esc] Cancel • [↑↓/jk] Navigate ";
1127
1127
  }
1128
1128
 
1129
1129
  private appendNotice(lines: string[]): void {
@@ -8,7 +8,7 @@ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
8
8
  import type { ExtensionContext } from "@selesai/code";
9
9
  import type { AgentConfig } from "../../agents/agents.ts";
10
10
  import { ChainClarifyComponent, type ChainClarifyResult, type BehaviorOverride } from "./chain-clarify.ts";
11
- import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
11
+ import { resolveEffectiveThinking, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
12
12
  import {
13
13
  resolveChainTemplates,
14
14
  createChainDir,
@@ -20,6 +20,7 @@ import {
20
20
  createParallelDirs,
21
21
  suppressProgressForReadOnlyTask,
22
22
  aggregateParallelOutputs,
23
+ isCheckpointStep,
23
24
  isDynamicParallelStep,
24
25
  isParallelStep,
25
26
  type StepOverrides,
@@ -33,7 +34,13 @@ import {
33
34
  import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
34
35
  import { INTERCOM_BRIDGE_MARKER } from "../../intercom/intercom-bridge.ts";
35
36
  import { runSync } from "./execution.ts";
36
- import { beginForegroundChild, finishForegroundChild, updateForegroundChild } from "./foreground-control.ts";
37
+ import {
38
+ beginForegroundChild,
39
+ finishForegroundChild,
40
+ retainForegroundSchedulingOwner,
41
+ settleForegroundSchedulingOwner,
42
+ updateForegroundChild,
43
+ } from "./foreground-control.ts";
37
44
  import { buildChainSummary } from "../../shared/formatters.ts";
38
45
  import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, resolveChildCwd, sumResultsCost, sumResultsUsage } from "../../shared/utils.ts";
39
46
  import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../shared/parallel-utils.ts";
@@ -64,6 +71,8 @@ import {
64
71
  type ResolvedToolBudget,
65
72
  type SingleResult,
66
73
  type ToolBudgetConfig,
74
+ type ChainCheckpointState,
75
+ type UsageBudgetConfig,
67
76
  MAX_CONCURRENCY,
68
77
  resolveChildMaxSubagentDepth,
69
78
  } from "../../shared/types.ts";
@@ -78,9 +87,38 @@ import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance
78
87
  import { isAgentContractV1 } from "../shared/agent-contract.ts";
79
88
  import type { ChainOutputMap } from "../../shared/types.ts";
80
89
  import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
90
+ import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
81
91
  import type { ContextMode } from "../shared/context-mode.ts";
82
92
  import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
83
93
 
94
+ /**
95
+ * Resolve a chain step's output path and effective output mode.
96
+ *
97
+ * Explicit `output` paths pass through unchanged (absolute as-is, relative under
98
+ * the chain dir). Omitted output generates a collision-safe per-child durable
99
+ * path `<chainDir>/outputs/<flat-index>-<agent>.md` unless the caller explicitly
100
+ * used `output: false`. Omitted `outputMode` resolves to `file-only` when an
101
+ * output path is active; explicit `outputMode: "inline"` is preserved exactly.
102
+ */
103
+ function resolveChainStepOutput(input: {
104
+ behaviorOutput: string | false;
105
+ rawOutput: string | false | undefined;
106
+ rawOutputMode: "inline" | "file-only" | undefined;
107
+ chainDir: string;
108
+ flatIndex: number;
109
+ agent: string;
110
+ }): { outputPath?: string; outputMode: "inline" | "file-only" } {
111
+ const explicit = typeof input.behaviorOutput === "string" ? input.behaviorOutput : undefined;
112
+ let outputPath: string | undefined;
113
+ if (explicit) {
114
+ outputPath = path.isAbsolute(explicit) ? explicit : path.join(input.chainDir, explicit);
115
+ } else if (input.behaviorOutput === false && input.rawOutput !== false && input.rawOutput !== "false") {
116
+ outputPath = path.join(input.chainDir, "outputs", `${input.flatIndex}-${input.agent}.md`);
117
+ }
118
+ return { outputPath, outputMode: input.rawOutputMode ?? (outputPath ? "file-only" : "inline") };
119
+ }
120
+
121
+
84
122
  interface ChainExecutionDetailsInput {
85
123
  results: SingleResult[];
86
124
  includeProgress?: boolean;
@@ -97,6 +135,8 @@ interface ChainExecutionDetailsInput {
97
135
  dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
98
136
  dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "stopped" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
99
137
  parallelHandoff?: Details["parallelHandoff"];
138
+ checkpoint?: ChainCheckpointState;
139
+ usageBudget?: UsageBudgetConfig;
100
140
  }
101
141
 
102
142
  interface ParallelChainRunInput {
@@ -145,7 +185,10 @@ interface ParallelChainRunInput {
145
185
  timeoutMs?: number;
146
186
  deadlineAt?: number;
147
187
  turnBudget?: ResolvedTurnBudget;
188
+ usageBudget?: UsageBudgetConfig;
148
189
  onDetachedExit?: (index: number, result: SingleResult) => void;
190
+ /** Called after an attached child releases its foreground ownership. */
191
+ onForegroundChildSettled?: () => void;
149
192
  toolBudget?: ResolvedToolBudget;
150
193
  configToolBudget?: ToolBudgetConfig;
151
194
  globalSemaphore?: Semaphore;
@@ -165,6 +208,8 @@ function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details
165
208
  outputs: input.outputs,
166
209
  totalChildUsage: sumResultsUsage(input.results),
167
210
  totalCost: sumResultsCost(input.results),
211
+ usageBudget: usageBudgetState(input.usageBudget, sumResultsCost(input.results)),
212
+ ...(input.checkpoint ? { checkpoint: input.checkpoint } : {}),
168
213
  ...(input.parallelHandoff ? { parallelHandoff: input.parallelHandoff } : {}),
169
214
  workflowGraph: buildWorkflowGraphSnapshot({
170
215
  runId: input.runId,
@@ -277,12 +322,29 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
277
322
  input.sessionFileForTask?.(task.agent, input.globalTaskIndex + taskIndex, effectiveModels[taskIndex]);
278
323
  }
279
324
 
325
+ const completedResults: SingleResult[] = [];
326
+ if (input.foregroundControl) retainForegroundSchedulingOwner(input.foregroundControl);
280
327
  const parallelResults = await mapConcurrent(
281
328
  input.step.parallel,
282
329
  concurrency,
283
330
  async (task, taskIndex) => {
331
+ const childIndex = input.globalTaskIndex + taskIndex;
332
+ const budgetState = usageBudgetState(input.usageBudget, sumResultsCost(input.results.concat(completedResults)));
333
+ if (budgetState?.exhausted) {
334
+ return {
335
+ index: childIndex,
336
+ agent: task.agent,
337
+ task: input.parallelTemplates[taskIndex] ?? "(skipped)",
338
+ exitCode: 1,
339
+ messages: [],
340
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
341
+ error: usageBudgetExceededMessage(budgetState),
342
+ skipped: true,
343
+ } as SingleResult;
344
+ }
284
345
  if (aborted && failFast) {
285
346
  return {
347
+ index: childIndex,
286
348
  agent: task.agent,
287
349
  task: "(skipped)",
288
350
  exitCode: -1,
@@ -319,17 +381,26 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
319
381
  ? input.worktreeSetup.worktrees[taskIndex]!.agentCwd
320
382
  : resolveChildCwd(input.cwd ?? input.ctx.cwd, task.cwd);
321
383
 
322
- const outputPath = typeof behavior.output === "string"
323
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(input.chainDir, behavior.output))
324
- : undefined;
384
+ const resolvedTaskOutput = resolveChainStepOutput({
385
+ behaviorOutput: behavior.output,
386
+ rawOutput: task.output,
387
+ rawOutputMode: task.outputMode,
388
+ chainDir: input.chainDir,
389
+ flatIndex: childIndex,
390
+ agent: task.agent,
391
+ });
392
+ const outputPath = resolvedTaskOutput.outputPath;
393
+ const effectiveOutputMode = resolvedTaskOutput.outputMode;
325
394
  taskStr = injectSingleOutputInstruction(taskStr, outputPath, taskAgentConfig);
326
395
  const interruptController = new AbortController();
327
- const childIndex = input.globalTaskIndex + taskIndex;
328
396
  if (input.foregroundControl) {
397
+ const thinking = resolveEffectiveThinking(effectiveModel, input.thinkingOverrideForTask?.(task.agent, childIndex, effectiveModel));
329
398
  beginForegroundChild(input.foregroundControl, {
330
399
  index: childIndex,
331
400
  agent: task.agent,
332
401
  description: cleanTask.trim(),
402
+ ...(effectiveModel ? { model: effectiveModel } : {}),
403
+ ...(thinking ? { thinking } : {}),
333
404
  interrupt: () => {
334
405
  if (interruptController.signal.aborted) return false;
335
406
  interruptController.abort();
@@ -342,7 +413,9 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
342
413
  ? createStructuredOutputRuntime(task.outputSchema, path.join(input.chainDir, "structured-output"))
343
414
  : undefined;
344
415
  const agentContract = task.agentContract ?? input.step.agentContract ?? input.agentContract;
345
- const result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
416
+ let result!: SingleResult;
417
+ try {
418
+ result = await runSync(input.ctx.cwd, input.agents, task.agent, taskStr, {
346
419
  parentSessionId: input.ctx.sessionManager.getSessionId() ?? undefined,
347
420
  capabilityCeiling: input.capabilityCeiling,
348
421
  context: input.contextForAgent?.(task.agent),
@@ -361,7 +434,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
361
434
  artifactsDir: input.artifactConfig.enabled ? input.artifactsDir : undefined,
362
435
  artifactConfig: input.artifactConfig,
363
436
  outputPath,
364
- outputMode: behavior.outputMode,
437
+ outputMode: effectiveOutputMode,
365
438
  maxSubagentDepth,
366
439
  controlConfig: input.controlConfig,
367
440
  onControlEvent: input.onControlEvent,
@@ -380,9 +453,13 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
380
453
  timeoutMs: input.timeoutMs,
381
454
  deadlineAt: input.deadlineAt,
382
455
  turnBudget: input.turnBudget,
383
- onDetachedExit: input.onDetachedExit
384
- ? (result) => input.onDetachedExit?.(childIndex, result)
385
- : undefined,
456
+ onDetachedExit: (result) => {
457
+ try {
458
+ if (input.foregroundControl) finishForegroundChild(input.foregroundControl, childIndex);
459
+ } finally {
460
+ input.onDetachedExit?.(childIndex, result);
461
+ }
462
+ },
386
463
  toolBudget: toolBudget.toolBudget,
387
464
  onUpdate: input.onUpdate
388
465
  ? (progressUpdate) => {
@@ -416,16 +493,28 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
416
493
  });
417
494
  }
418
495
  : undefined,
419
- });
420
- if (input.foregroundControl) finishForegroundChild(input.foregroundControl, childIndex);
496
+ });
497
+ } finally {
498
+ // Rejected attached attempts still release their parallel child control;
499
+ // detached receipts transfer ownership to onDetachedExit.
500
+ if (!result?.detached) {
501
+ if (input.foregroundControl) finishForegroundChild(input.foregroundControl, childIndex);
502
+ input.onForegroundChildSettled?.();
503
+ }
504
+ }
421
505
 
422
506
  if (result.exitCode !== 0 && failFast) {
423
507
  aborted = true;
424
508
  }
425
509
  recordRun(task.agent, cleanTask, result.exitCode, result.progressSummary?.durationMs ?? 0);
510
+ completedResults.push(result);
426
511
  return result;
427
512
  },
428
513
  input.globalSemaphore,
514
+ () => {
515
+ if (input.foregroundControl) settleForegroundSchedulingOwner(input.foregroundControl);
516
+ input.onForegroundChildSettled?.();
517
+ },
429
518
  );
430
519
 
431
520
  return parallelResults;
@@ -470,7 +559,10 @@ interface ChainExecutionParams {
470
559
  deadlineAt?: number;
471
560
  turnBudget?: ResolvedTurnBudget;
472
561
  onDetachedExit?: (index: number, result: SingleResult) => void;
562
+ /** Ownership callback used when parallel siblings outlive executeChain rejection. */
563
+ onForegroundChildSettled?: () => void;
473
564
  toolBudget?: ResolvedToolBudget;
565
+ usageBudget?: UsageBudgetConfig;
474
566
  configToolBudget?: ToolBudgetConfig;
475
567
  /** Global cap on simultaneously-running tasks within this chain. Defaults to DEFAULT_GLOBAL_CONCURRENCY_LIMIT. */
476
568
  globalConcurrencyLimit?: number;
@@ -531,7 +623,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
531
623
  let parallelHandoff: Details["parallelHandoff"];
532
624
 
533
625
  const chainAgents: string[] = chainSteps.map((step) =>
534
- isParallelStep(step)
626
+ isCheckpointStep(step)
627
+ ? `checkpoint:${step.checkpoint}`
628
+ : isParallelStep(step)
535
629
  ? `[${step.parallel.map((t) => t.agent).join("+")}]`
536
630
  : isDynamicParallelStep(step)
537
631
  ? `expand:${step.parallel.agent}`
@@ -539,7 +633,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
539
633
  );
540
634
  const totalSteps = chainSteps.length;
541
635
 
542
- const makeDetailsInput = (overrides: Pick<Partial<ChainExecutionDetailsInput>, "currentStepIndex" | "currentFlatIndex"> = {}): ChainExecutionDetailsInput => ({
636
+ const makeDetailsInput = (overrides: Pick<Partial<ChainExecutionDetailsInput>, "currentStepIndex" | "currentFlatIndex" | "checkpoint"> = {}): ChainExecutionDetailsInput => ({
543
637
  results,
544
638
  ...(includeProgress !== undefined ? { includeProgress } : {}),
545
639
  allProgress,
@@ -553,16 +647,25 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
553
647
  dynamicChildren,
554
648
  dynamicGroupStatuses,
555
649
  ...(parallelHandoff ? { parallelHandoff } : {}),
650
+ usageBudget: params.usageBudget,
556
651
  ...overrides,
557
652
  });
558
653
 
654
+ const usageBudgetError = (stepIndex: number, flatIndex: number): ChainExecutionResult | undefined => {
655
+ const state = usageBudgetState(params.usageBudget, sumResultsCost(results));
656
+ if (!state?.exhausted) return undefined;
657
+ return buildChainExecutionErrorResult(usageBudgetExceededMessage(state), makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: flatIndex }));
658
+ };
659
+
559
660
  const firstStep = chainSteps[0]!;
560
661
  const originalTask = params.task
561
- ?? (isParallelStep(firstStep)
562
- ? firstStep.parallel[0]!.task!
563
- : isDynamicParallelStep(firstStep)
564
- ? firstStep.parallel.task!
565
- : (firstStep as SequentialStep).task!);
662
+ ?? (isCheckpointStep(firstStep)
663
+ ? undefined
664
+ : isParallelStep(firstStep)
665
+ ? firstStep.parallel[0]!.task!
666
+ : isDynamicParallelStep(firstStep)
667
+ ? firstStep.parallel.task!
668
+ : (firstStep as SequentialStep).task!);
566
669
  try {
567
670
  validateChainOutputBindings(chainSteps, { maxItems: params.dynamicFanoutMaxItems });
568
671
  } catch (error) {
@@ -577,7 +680,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
577
680
  }
578
681
 
579
682
  const chainDir = createChainDir(runId, chainDirBase);
580
- const hasParallelSteps = chainSteps.some((step) => isParallelStep(step) || isDynamicParallelStep(step));
683
+ const hasParallelSteps = chainSteps.some((step) => isParallelStep(step) || isDynamicParallelStep(step) || isCheckpointStep(step));
581
684
  let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
582
685
  const shouldClarify = clarify === true && ctx.hasUI && !hasParallelSteps;
583
686
  let tuiBehaviorOverrides: (BehaviorOverride | undefined)[] | undefined;
@@ -677,9 +780,21 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
677
780
  let progressCreated = false;
678
781
 
679
782
  for (let stepIndex = 0; stepIndex < chainSteps.length; stepIndex++) {
783
+ const budgetError = usageBudgetError(stepIndex, globalTaskIndex);
784
+ if (budgetError) return budgetError;
680
785
  const step = chainSteps[stepIndex]!;
681
786
  const stepTemplates = templates[stepIndex]!;
682
787
 
788
+ if (isCheckpointStep(step)) {
789
+ const checkpoint = { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: "pending" as const, stepIndex };
790
+ return {
791
+ content: [{ type: "text", text: `Chain paused at checkpoint '${step.checkpoint}'. Approve with subagent({ action: "approve-checkpoint", id: "${runId}" }) or reject with subagent({ action: "reject-checkpoint", id: "${runId}" }).${step.message ? `
792
+
793
+ ${step.message}` : ""}` }],
794
+ details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex, checkpoint })),
795
+ };
796
+ }
797
+
683
798
  if (isParallelStep(step)) {
684
799
  const parallelTemplates = stepTemplates as string[];
685
800
  const parallelCwd = resolveChildCwd(cwd ?? ctx.cwd, step.cwd);
@@ -713,10 +828,16 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
713
828
  .map((behavior, taskIndex) => suppressProgressForReadOnlyTask(behavior, parallelTemplates[taskIndex] ?? step.parallel[taskIndex]?.task, originalTask));
714
829
  for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
715
830
  const behavior = parallelBehaviors[taskIndex]!;
716
- const outputPath = typeof behavior.output === "string"
717
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
718
- : undefined;
719
- const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Parallel chain step ${stepIndex + 1} task ${taskIndex + 1} (${step.parallel[taskIndex]!.agent})`);
831
+ const task = step.parallel[taskIndex]!;
832
+ const resolvedOutput = resolveChainStepOutput({
833
+ behaviorOutput: behavior.output,
834
+ rawOutput: task.output,
835
+ rawOutputMode: task.outputMode,
836
+ chainDir,
837
+ flatIndex: globalTaskIndex + taskIndex,
838
+ agent: task.agent,
839
+ });
840
+ const validationError = validateFileOnlyOutputMode(resolvedOutput.outputMode, resolvedOutput.outputPath, `Parallel chain step ${stepIndex + 1} task ${taskIndex + 1} (${task.agent})`);
720
841
  if (validationError) return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
721
842
  }
722
843
  progressCreated = ensureParallelProgressFile(chainDir, progressCreated, parallelBehaviors);
@@ -768,7 +889,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
768
889
  timeoutMs: params.timeoutMs,
769
890
  deadlineAt,
770
891
  turnBudget: params.turnBudget,
892
+ usageBudget: params.usageBudget,
771
893
  onDetachedExit,
894
+ onForegroundChildSettled: params.onForegroundChildSettled,
772
895
  toolBudget: params.toolBudget,
773
896
  configToolBudget: params.configToolBudget,
774
897
  globalSemaphore,
@@ -967,10 +1090,16 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
967
1090
 
968
1091
  for (let taskIndex = 0; taskIndex < dynamicParallelStep.parallel.length; taskIndex++) {
969
1092
  const behavior = parallelBehaviors[taskIndex]!;
970
- const outputPath = typeof behavior.output === "string"
971
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
972
- : undefined;
973
- const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Dynamic chain step ${stepIndex + 1} item ${taskIndex + 1} (${dynamicParallelStep.parallel[taskIndex]!.agent})`);
1093
+ const task = dynamicParallelStep.parallel[taskIndex]!;
1094
+ const resolvedOutput = resolveChainStepOutput({
1095
+ behaviorOutput: behavior.output,
1096
+ rawOutput: task.output,
1097
+ rawOutputMode: task.outputMode,
1098
+ chainDir,
1099
+ flatIndex: globalTaskIndex + taskIndex,
1100
+ agent: task.agent,
1101
+ });
1102
+ const validationError = validateFileOnlyOutputMode(resolvedOutput.outputMode, resolvedOutput.outputPath, `Dynamic chain step ${stepIndex + 1} item ${taskIndex + 1} (${task.agent})`);
974
1103
  if (validationError) {
975
1104
  dynamicGroupStatuses[stepIndex] = { status: "failed", error: validationError };
976
1105
  return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex + taskIndex }));
@@ -1024,7 +1153,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1024
1153
  timeoutMs: params.timeoutMs,
1025
1154
  deadlineAt,
1026
1155
  turnBudget: params.turnBudget,
1156
+ usageBudget: params.usageBudget,
1027
1157
  onDetachedExit,
1158
+ onForegroundChildSettled: params.onForegroundChildSettled,
1028
1159
  toolBudget: params.toolBudget,
1029
1160
  configToolBudget: params.configToolBudget,
1030
1161
  globalSemaphore,
@@ -1205,11 +1336,18 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1205
1336
  { scope: modelScope },
1206
1337
  );
1207
1338
 
1208
- const outputPath = typeof behavior.output === "string"
1209
- ? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
1210
- : undefined;
1339
+ const resolvedStepOutput = resolveChainStepOutput({
1340
+ behaviorOutput: behavior.output,
1341
+ rawOutput: stepOverride.output,
1342
+ rawOutputMode: seqStep.outputMode,
1343
+ chainDir,
1344
+ flatIndex: globalTaskIndex,
1345
+ agent: seqStep.agent,
1346
+ });
1347
+ const outputPath = resolvedStepOutput.outputPath;
1348
+ const effectiveOutputMode = resolvedStepOutput.outputMode;
1211
1349
  stepTask = injectSingleOutputInstruction(stepTask, outputPath, agentConfig);
1212
- const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
1350
+ const validationError = validateFileOnlyOutputMode(effectiveOutputMode, outputPath, `Chain step ${stepIndex + 1} (${seqStep.agent})`);
1213
1351
  if (validationError) {
1214
1352
  return buildChainExecutionErrorResult(validationError, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
1215
1353
  }
@@ -1217,10 +1355,13 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1217
1355
  const childIndex = globalTaskIndex;
1218
1356
  const interruptController = new AbortController();
1219
1357
  if (foregroundControl) {
1358
+ const thinking = resolveEffectiveThinking(effectiveModel, params.thinkingOverrideForTask?.(seqStep.agent, childIndex, effectiveModel));
1220
1359
  beginForegroundChild(foregroundControl, {
1221
1360
  index: childIndex,
1222
1361
  agent: seqStep.agent,
1223
1362
  description: cleanTask.trim(),
1363
+ ...(effectiveModel ? { model: effectiveModel } : {}),
1364
+ ...(thinking ? { thinking } : {}),
1224
1365
  interrupt: () => {
1225
1366
  if (interruptController.signal.aborted) return false;
1226
1367
  interruptController.abort();
@@ -1229,28 +1370,30 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1229
1370
  });
1230
1371
  }
1231
1372
 
1232
- const structuredRuntime = seqStep.outputSchema
1233
- ? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
1234
- : undefined;
1235
1373
  const agentContract = seqStep.agentContract ?? params.agentContract;
1236
- const toolBudget = resolveChainToolBudget({ stepBudget: seqStep.toolBudget, runBudget: params.toolBudget, agentBudget: agentConfig?.toolBudget, configBudget: params.configToolBudget });
1237
- if (toolBudget.error) return buildChainExecutionErrorResult(toolBudget.error, {
1238
- results,
1239
- includeProgress,
1240
- allProgress,
1241
- allArtifactPaths,
1242
- artifactsDir: params.artifactsDir,
1243
- chainAgents,
1244
- chainSteps,
1245
- totalSteps,
1246
- currentStepIndex: stepIndex,
1247
- runId: params.runId,
1248
- outputs,
1249
- currentFlatIndex: globalTaskIndex,
1250
- dynamicChildren,
1251
- dynamicGroupStatuses,
1252
- });
1253
- const r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
1374
+ let r!: SingleResult;
1375
+ try {
1376
+ const structuredRuntime = seqStep.outputSchema
1377
+ ? createStructuredOutputRuntime(seqStep.outputSchema, path.join(chainDir, "structured-output"))
1378
+ : undefined;
1379
+ const toolBudget = resolveChainToolBudget({ stepBudget: seqStep.toolBudget, runBudget: params.toolBudget, agentBudget: agentConfig?.toolBudget, configBudget: params.configToolBudget });
1380
+ if (toolBudget.error) return buildChainExecutionErrorResult(toolBudget.error, {
1381
+ results,
1382
+ includeProgress,
1383
+ allProgress,
1384
+ allArtifactPaths,
1385
+ artifactsDir: params.artifactsDir,
1386
+ chainAgents,
1387
+ chainSteps,
1388
+ totalSteps,
1389
+ currentStepIndex: stepIndex,
1390
+ runId: params.runId,
1391
+ outputs,
1392
+ currentFlatIndex: globalTaskIndex,
1393
+ dynamicChildren,
1394
+ dynamicGroupStatuses,
1395
+ });
1396
+ r = await runSync(ctx.cwd, agents, seqStep.agent, stepTask, {
1254
1397
  parentSessionId: ctx.sessionManager.getSessionId() ?? undefined,
1255
1398
  capabilityCeiling: params.capabilityCeiling,
1256
1399
  context: params.contextForAgent?.(seqStep.agent),
@@ -1269,7 +1412,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1269
1412
  artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
1270
1413
  artifactConfig,
1271
1414
  outputPath,
1272
- outputMode: behavior.outputMode,
1415
+ outputMode: effectiveOutputMode,
1273
1416
  maxSubagentDepth,
1274
1417
  controlConfig,
1275
1418
  onControlEvent,
@@ -1288,9 +1431,13 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1288
1431
  timeoutMs: params.timeoutMs,
1289
1432
  deadlineAt,
1290
1433
  turnBudget: params.turnBudget,
1291
- onDetachedExit: onDetachedExit
1292
- ? (result) => onDetachedExit(childIndex, result)
1293
- : undefined,
1434
+ onDetachedExit: (result) => {
1435
+ try {
1436
+ if (foregroundControl) finishForegroundChild(foregroundControl, childIndex);
1437
+ } finally {
1438
+ onDetachedExit?.(childIndex, result);
1439
+ }
1440
+ },
1294
1441
  toolBudget: toolBudget.toolBudget,
1295
1442
  onUpdate: onUpdate
1296
1443
  ? (p) => {
@@ -1324,8 +1471,15 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
1324
1471
  });
1325
1472
  }
1326
1473
  : undefined,
1327
- });
1328
- if (foregroundControl) finishForegroundChild(foregroundControl, childIndex);
1474
+ });
1475
+ } finally {
1476
+ // Rejected attempts and early validation returns release the child here.
1477
+ // A detached receipt transfers cleanup ownership to onDetachedExit.
1478
+ if (!r?.detached) {
1479
+ if (foregroundControl) finishForegroundChild(foregroundControl, childIndex);
1480
+ params.onForegroundChildSettled?.();
1481
+ }
1482
+ }
1329
1483
  recordRun(seqStep.agent, cleanTask, r.exitCode, r.progressSummary?.durationMs ?? 0);
1330
1484
 
1331
1485
  globalTaskIndex++;