@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
@@ -114,6 +114,27 @@ function createUiContext() {
114
114
  }
115
115
 
116
116
  describe("async job tracker", { skip: !available ? "pi packages not available" : undefined }, () => {
117
+ it("drops stale captured extension contexts instead of crashing background timers", () => {
118
+ const asyncRoot = createTempDir("pi-async-job-tracker-stale-context-");
119
+ try {
120
+ const state = createState();
121
+ const recorder = createEventRecorder();
122
+ const tracker = trackerMod!.createAsyncJobTracker(recorder.pi, state as never, asyncRoot);
123
+ (state as { lastUiContext: unknown }).lastUiContext = {
124
+ get hasUI() {
125
+ throw new Error("This extension ctx is stale after session replacement or reload.");
126
+ },
127
+ };
128
+
129
+ tracker.handleStarted({ id: "run-stale-context", asyncDir: path.join(asyncRoot, "run-stale-context"), agent: "worker" });
130
+
131
+ assert.equal(state.lastUiContext, null);
132
+ if (state.poller) clearInterval(state.poller);
133
+ } finally {
134
+ removeTempDir(asyncRoot);
135
+ }
136
+ });
137
+
117
138
  it("removes completed jobs after retention and requests a rerender", async () => {
118
139
  const asyncRoot = createTempDir("pi-async-job-tracker-");
119
140
  try {
@@ -856,6 +877,69 @@ describe("async job tracker", { skip: !available ? "pi packages not available" :
856
877
  }
857
878
  });
858
879
 
880
+ it("resynchronizes when a saved cursor resumes inside an oversized JSONL record", async () => {
881
+ const asyncRoot = createTempDir("pi-async-job-tracker-");
882
+ const originalError = console.error;
883
+ const errors: unknown[][] = [];
884
+ console.error = (...args: unknown[]) => {
885
+ errors.push(args);
886
+ };
887
+ try {
888
+ const runDir = path.join(asyncRoot, "run-midline-oversized-control");
889
+ fs.mkdirSync(runDir, { recursive: true });
890
+ fs.writeFileSync(path.join(runDir, "status.json"), JSON.stringify({
891
+ runId: "run-midline-oversized-control",
892
+ mode: "single",
893
+ state: "running",
894
+ startedAt: Date.now() - 1000,
895
+ lastUpdate: Date.now(),
896
+ steps: [{ agent: "worker", status: "running" }],
897
+ }), "utf-8");
898
+ const largeDiagnostic = JSON.stringify({
899
+ type: "message_update",
900
+ message: { role: "assistant", content: [{ type: "text", text: "x".repeat(2_500_000) }] },
901
+ });
902
+ const controlEvent = JSON.stringify({
903
+ type: "subagent.control",
904
+ channels: ["event"],
905
+ event: {
906
+ type: "needs_attention",
907
+ to: "needs_attention",
908
+ ts: 123,
909
+ runId: "run-midline-oversized-control",
910
+ agent: "worker",
911
+ message: "worker needs attention",
912
+ },
913
+ });
914
+ fs.writeFileSync(path.join(runDir, "events.jsonl"), `${largeDiagnostic}\n${controlEvent}\n`, "utf-8");
915
+
916
+ const state = createState();
917
+ state.asyncJobs.set("run-midline-oversized-control", {
918
+ asyncId: "run-midline-oversized-control",
919
+ asyncDir: runDir,
920
+ status: "running",
921
+ agents: ["worker"],
922
+ startedAt: Date.now() - 1000,
923
+ updatedAt: Date.now(),
924
+ controlEventCursor: 0,
925
+ });
926
+ const recorder = createEventRecorder();
927
+ const tracker = trackerMod!.createAsyncJobTracker(recorder.pi, state as never, asyncRoot, {
928
+ pollIntervalMs: 10,
929
+ });
930
+ tracker.ensurePoller();
931
+
932
+ await waitForCondition(
933
+ () => recorder.events.some((event) => event.channel === "subagent:control-event"),
934
+ "control event after oversized diagnostic",
935
+ );
936
+ assert.equal(errors.some((args) => String(args[0]).includes("Ignoring malformed async control event")), false);
937
+ } finally {
938
+ console.error = originalError;
939
+ removeTempDir(asyncRoot);
940
+ }
941
+ });
942
+
859
943
  it("does not tail-skip control events for newly tracked large logs", async () => {
860
944
  const asyncRoot = createTempDir("pi-async-job-tracker-");
861
945
  try {
@@ -27,8 +27,8 @@ describe("async status helpers", () => {
27
27
  currentStep: 1,
28
28
  outputFile,
29
29
  steps: [
30
- { agent: "scout", status: "complete", durationMs: 10 },
31
- { agent: "worker", status: "running", durationMs: 20 },
30
+ { agent: "scout", status: "complete", durationMs: 10, description: "Inspect auth only" },
31
+ { agent: "worker", status: "running", durationMs: 20, description: "Patch billing only" },
32
32
  ],
33
33
  });
34
34
  createAsyncDir(root, "run-b", {
@@ -47,6 +47,8 @@ describe("async status helpers", () => {
47
47
  assert.equal(runs[0]?.steps.length, 2);
48
48
  assert.equal(runs[0]?.steps[1]?.agent, "worker");
49
49
  assert.equal(runs[0]?.steps[1]?.status, "running");
50
+ assert.equal(runs[0]?.steps[0]?.description, "Inspect auth only");
51
+ assert.equal(runs[0]?.steps[1]?.description, "Patch billing only");
50
52
  assert.match(formatAsyncRunList(runs), /output: .*output-1\.log/);
51
53
  } finally {
52
54
  fs.rmSync(root, { recursive: true, force: true });
@@ -349,9 +349,9 @@ describe("chain clarify model display", { skip: !available ? "pi packages not av
349
349
  assert.match(initial, /\[Enter\] Run • \[Esc\] Cancel/);
350
350
  if (mode === "single") {
351
351
  assert.match(initial, /\[w\]Output/);
352
- assert.doesNotMatch(initial, /\[↑↓\] Navigate/);
352
+ assert.doesNotMatch(initial, /\[↑↓\/jk\] Navigate/);
353
353
  } else {
354
- assert.match(initial, /\[↑↓\] Navigate/);
354
+ assert.match(initial, /\[↑↓\/jk\] Navigate/);
355
355
  }
356
356
  if (mode === "chain") {
357
357
  assert.match(initial, /\[w\]Output/);
@@ -368,4 +368,53 @@ describe("chain clarify model display", { skip: !available ? "pi packages not av
368
368
  assert.match(component.render(84).map(stripAnsi).join("\n"), /\[b\]Background:ON/);
369
369
  }
370
370
  });
371
+
372
+ it("navigates non-editing chain steps with j and k", () => {
373
+ const agents = ["scout", "worker"].map((name) => ({
374
+ name,
375
+ description: "",
376
+ systemPrompt: "",
377
+ systemPromptMode: "replace",
378
+ inheritProjectContext: false,
379
+ inheritSkills: false,
380
+ source: "user",
381
+ filePath: `${name}.md`,
382
+ }));
383
+ const component = new ChainClarifyComponent(
384
+ { requestRender() {} },
385
+ { fg(_key: string, text: string) { return text; } },
386
+ agents,
387
+ ["Scout", "Implement"],
388
+ "Task",
389
+ undefined,
390
+ agents.map(() => ({ output: false, outputMode: "inline", reads: false, progress: false, skills: [], model: undefined })),
391
+ [],
392
+ undefined,
393
+ [{ name: "jk-skill", source: "user" }],
394
+ () => {},
395
+ "chain",
396
+ );
397
+
398
+ component.handleInput("j");
399
+ assert.equal(component.selectedStep, 1);
400
+ component.handleInput("k");
401
+ assert.equal(component.selectedStep, 0);
402
+
403
+ component.handleInput("e");
404
+ component.handleInput("j");
405
+ component.handleInput("k");
406
+ assert.match(component.render(84).map(stripAnsi).join("\n"), /jkScout/);
407
+ component.handleInput("\x1b");
408
+
409
+ component.handleInput("m");
410
+ component.handleInput("j");
411
+ component.handleInput("k");
412
+ assert.match(component.render(84).map(stripAnsi).join("\n"), /Search: jk/);
413
+ component.handleInput("\x1b");
414
+
415
+ component.handleInput("s");
416
+ component.handleInput("j");
417
+ component.handleInput("k");
418
+ assert.match(component.render(84).map(stripAnsi).join("\n"), /Search: jk/);
419
+ });
371
420
  });
@@ -64,7 +64,7 @@ interface TestParallelTask {
64
64
  gateOn?: "execution" | "acceptance";
65
65
  }
66
66
 
67
- type TestChainStep = TestSequentialStep | {
67
+ type TestChainStep = { checkpoint: string; message?: string; label?: string; phase?: string } | TestSequentialStep | {
68
68
  parallel: TestParallelTask[];
69
69
  concurrency?: number;
70
70
  failFast?: boolean;
@@ -91,6 +91,7 @@ type TestChainStep = TestSequentialStep | {
91
91
  };
92
92
 
93
93
  interface ChainResultItem {
94
+ index: number;
94
95
  agent: string;
95
96
  exitCode: number;
96
97
  finalOutput?: string;
@@ -118,6 +119,7 @@ interface ChainExecutionResult {
118
119
  currentStepIndex?: number;
119
120
  outputs?: Record<string, { text: string; structured?: unknown }>;
120
121
  parallelHandoff?: { version: number; path: string; groupCount: number; childCount: number; cleanupState: string };
122
+ checkpoint?: { name?: string; status?: string; message?: string };
121
123
  };
122
124
  }
123
125
 
@@ -215,6 +217,27 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
215
217
  );
216
218
  }
217
219
 
220
+ it("pauses foreground chains at approval checkpoints without launching the next child", async () => {
221
+ mockPi.onCall({ output: "analysis done" });
222
+ mockPi.onCall({ output: "should not run" });
223
+ const agents = [makeAgent("analyst"), makeAgent("worker")];
224
+
225
+ const result = await executeChain!(makeChainParams([
226
+ { agent: "analyst", task: "Analyze" },
227
+ { checkpoint: "review", message: "Approve implementation?" },
228
+ { agent: "worker", task: "Implement" },
229
+ ], agents));
230
+
231
+ assert.equal(result.isError, undefined);
232
+ assert.match(result.content[0]!.text, /Chain paused at checkpoint 'review'/);
233
+ assert.equal(result.details.results.length, 1);
234
+ assert.equal(result.details.checkpoint?.name, "review");
235
+ assert.equal(result.details.checkpoint?.status, "pending");
236
+ assert.equal(result.details.workflowGraph?.nodes[1]?.kind, "checkpoint");
237
+ assert.equal(result.details.workflowGraph?.nodes[1]?.status, "paused");
238
+ assert.equal(mockPi.callCount(), 1);
239
+ });
240
+
218
241
  it("runs a 2-step chain", async () => {
219
242
  mockPi.onCall({ output: "Analysis complete: found 3 issues" });
220
243
  const agents = [makeAgent("analyst"), makeAgent("reporter")];
@@ -344,8 +367,9 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
344
367
  assert.doesNotMatch(firstTaskArg, /\[Write to:|Write your findings to exactly this path/);
345
368
  assert.ok(result.details.results[0]?.savedOutputPath);
346
369
  assert.equal(fs.readFileSync(result.details.results[0].savedOutputPath, "utf-8"), "full chain output\nwith details");
347
- assert.match(result.details.results[0]?.finalOutput ?? "", /Output saved to:/);
348
- assert.doesNotMatch(result.details.results[0]?.finalOutput ?? "", /full chain output/);
370
+ assert.equal(result.details.results[0]?.finalOutput, undefined);
371
+ assert.match(result.details.results[0]?.outputReference?.message ?? "", /Output saved to:/);
372
+ assert.doesNotMatch(result.details.results[0]?.outputReference?.message ?? "", /full chain output/);
349
373
  const secondTaskArg = readCallArgs(1).at(-1) ?? "";
350
374
  assert.match(secondTaskArg, /Output saved to:/);
351
375
  assert.match(secondTaskArg, /2 lines/);
@@ -381,7 +405,8 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
381
405
  );
382
406
 
383
407
  assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
384
- assert.match(result.details.results[0]?.finalOutput ?? "", /Output saved to:/);
408
+ assert.equal(result.details.results[0]?.finalOutput, undefined);
409
+ assert.match(result.details.results[0]?.outputReference?.message ?? "", /Output saved to:/);
385
410
  assert.equal(result.details.results[0]?.acceptance?.status, "checked");
386
411
  assert.ok(result.details.results[0]?.acceptance?.childReport);
387
412
 
@@ -607,10 +632,13 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
607
632
 
608
633
  assert.ok(!result.isError);
609
634
  const step2Task = result.details.results[1].task;
635
+ // Reference-first handoff: {previous} carries the saved-output reference and
636
+ // the step reads the named path instead of re-inlined prose.
610
637
  assert.ok(
611
- step2Task.includes("MARKER_ABC_123"),
612
- `step 2 task should contain step 1 output via {previous}: ${step2Task.slice(0, 200)}`,
638
+ step2Task.includes("Output saved to:"),
639
+ `step 2 task should contain the saved-output reference via {previous}: ${step2Task.slice(0, 200)}`,
613
640
  );
641
+ assert.doesNotMatch(step2Task, /MARKER_ABC_123/);
614
642
  });
615
643
 
616
644
  it("passes named sequential outputs through {outputs.name}", async () => {
@@ -629,7 +657,8 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
629
657
  );
630
658
 
631
659
  assert.ok(!result.isError);
632
- assert.match(readCallArgs(1).at(-1) ?? "", /CTX_123/);
660
+ assert.match(readCallArgs(1).at(-1) ?? "", /Output saved to: /);
661
+ assert.doesNotMatch(readCallArgs(1).at(-1) ?? "", /CTX_123/);
633
662
  assert.equal(result.details.workflowGraph?.nodes[0]?.outputName, "contextOutput");
634
663
  });
635
664
 
@@ -669,9 +698,10 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
669
698
  assert.match(readCallArgs(1).at(-1) ?? "", /Review src\/a\.ts/);
670
699
  assert.match(readCallArgs(2).at(-1) ?? "", /Review src\/b\.ts/);
671
700
  assert.match(readCallArgs(3).at(-1) ?? "", /"key":"src\/a\.ts"/);
672
- const collected = result.details.outputs?.reviews?.structured as Array<{ key: string; structured: unknown }>;
673
- assert.deepEqual(collected.map((item) => item.key), ["src/a.ts", "src/b.ts"]);
674
- assert.deepEqual(collected.map((item) => item.structured), [{ ok: "a" }, { ok: "b" }]);
701
+ // Terminal details strip chain outputs text/structured; bindings remain
702
+ // reference-first via {outputs.name} and the workflow graph keeps item keys.
703
+ assert.equal(result.details.outputs?.reviews?.text, "");
704
+ assert.equal(result.details.outputs?.reviews?.structured, undefined);
675
705
  const dynamicNode = result.details.workflowGraph?.nodes[1];
676
706
  assert.equal(dynamicNode?.kind, "dynamic-parallel-group");
677
707
  assert.deepEqual(dynamicNode?.children?.map((child) => child.itemKey), ["src/a.ts", "src/b.ts"]);
@@ -845,7 +875,7 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
845
875
  { agent: "scout", task: "Return targets", as: "targets", outputSchema: { type: "object" } },
846
876
  {
847
877
  expand: { from: { output: "targets", path: "/items" }, key: "/path", maxItems: 4 },
848
- parallel: { agent: "reviewer", task: "Review {item.path}", outputMode: "file-only" },
878
+ parallel: { agent: "reviewer", task: "Review {item.path}", output: false, outputMode: "file-only" },
849
879
  collect: { as: "reviews" },
850
880
  },
851
881
  ],
@@ -883,7 +913,8 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
883
913
 
884
914
  assert.ok(!result.isError, `chain should succeed: ${JSON.stringify(result.content)}`);
885
915
  assert.equal(mockPi.callCount(), 2);
886
- assert.deepEqual(result.details.outputs?.reviews?.structured, []);
916
+ assert.equal(result.details.outputs?.reviews?.text, "");
917
+ assert.equal(result.details.outputs?.reviews?.structured, undefined);
887
918
  assert.equal(result.details.workflowGraph?.nodes[1]?.status, "completed");
888
919
  assert.deepEqual(result.details.workflowGraph?.nodes[1]?.children, []);
889
920
  });
@@ -1100,7 +1131,9 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
1100
1131
  assert.equal(result.details.results.length, 2);
1101
1132
  assert.equal(result.details.results[0]?.acceptance?.status, "rejected");
1102
1133
  assert.equal(result.details.results[0]?.exitCode, 0);
1103
- assert.match(result.details.results[1]?.finalOutput ?? "", /Step 2 ran/);
1134
+ assert.equal(result.details.results[1]?.finalOutput, undefined);
1135
+ assert.ok(result.details.results[1]?.savedOutputPath);
1136
+ assert.equal(fs.readFileSync(result.details.results[1]!.savedOutputPath!, "utf-8"), "Step 2 ran");
1104
1137
  });
1105
1138
 
1106
1139
  it("agent contract v1 chain can gate progression on acceptance", async () => {
@@ -1172,7 +1205,8 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
1172
1205
  );
1173
1206
 
1174
1207
  const finalTaskArg = readCallArgs(chainLength - 1).at(-1) ?? "";
1175
- assert.match(finalTaskArg, /step-38-output/);
1208
+ assert.match(finalTaskArg, /Output saved to: /);
1209
+ assert.doesNotMatch(finalTaskArg, /step-38-output/);
1176
1210
  assert.doesNotMatch(finalTaskArg, /step-37-output/);
1177
1211
  assert.match(result.content[0]?.text ?? "", /40 steps/);
1178
1212
  });
@@ -1261,7 +1295,9 @@ describe("chain execution — sequential", { skip: !available ? "pi packages not
1261
1295
  );
1262
1296
 
1263
1297
  assert.ok(!result.isError);
1264
- assert.deepEqual(JSON.parse(result.details.results[0].finalOutput ?? "{}"), {
1298
+ const depthChild = result.details.results[0];
1299
+ assert.ok(depthChild.savedOutputPath);
1300
+ assert.deepEqual(JSON.parse(fs.readFileSync(depthChild.savedOutputPath, "utf-8")), {
1265
1301
  SELESAI_SUBAGENT_DEPTH: "1",
1266
1302
  SELESAI_SUBAGENT_MAX_DEPTH: "1",
1267
1303
  });
@@ -1361,6 +1397,7 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
1361
1397
 
1362
1398
  assert.ok(!result.isError, `should succeed: ${JSON.stringify(result.content)}`);
1363
1399
  assert.equal(result.details.results.length, 2);
1400
+ assert.deepEqual(result.details.results.map((row) => row.index), [0, 1]);
1364
1401
  });
1365
1402
 
1366
1403
  it("aggregates worktree handoffs across foreground chain groups", { skip: process.platform === "win32" ? "worktree paths differ on Windows" : undefined }, async () => {
@@ -1450,8 +1487,9 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
1450
1487
 
1451
1488
  assert.ok(!result.isError);
1452
1489
  const finalTask = readCallArgs(2).at(-1) ?? "";
1453
- assert.match(finalTask, /Alpha named output/);
1454
- assert.match(finalTask, /Beta named output/);
1490
+ assert.match(finalTask, /Output saved to: /);
1491
+ assert.doesNotMatch(finalTask, /Alpha named output/);
1492
+ assert.doesNotMatch(finalTask, /Beta named output/);
1455
1493
  });
1456
1494
 
1457
1495
  it("funnels an initial parallel step through one agent, then fans the funnel output back out", async () => {
@@ -1488,13 +1526,14 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
1488
1526
  assert.equal(result.details.totalSteps, 3);
1489
1527
  const funnelTask = readCallArgsMatching("Synthesize:").at(-1) ?? "";
1490
1528
  assert.match(funnelTask, /=== Parallel Task 1 \(scout-a\) ===/);
1491
- assert.match(funnelTask, /Scout A findings/);
1492
1529
  assert.match(funnelTask, /=== Parallel Task 2 \(scout-b\) ===/);
1493
- assert.match(funnelTask, /Scout B findings/);
1530
+ assert.match(funnelTask, /Output saved to: /);
1531
+ assert.doesNotMatch(funnelTask, /Scout A findings/);
1532
+ assert.doesNotMatch(funnelTask, /Scout B findings/);
1494
1533
  const fanoutTaskA = readCallArgsMatching("Review funnel A:").at(-1) ?? "";
1495
1534
  const fanoutTaskB = readCallArgsMatching("Review funnel B:").at(-1) ?? "";
1496
- assert.match(fanoutTaskA, /Review funnel A:\nFunnel synthesis/);
1497
- assert.match(fanoutTaskB, /Review funnel B:\nFunnel synthesis/);
1535
+ assert.match(fanoutTaskA, /Review funnel A:\nOutput saved to: /);
1536
+ assert.match(fanoutTaskB, /Review funnel B:\nOutput saved to: /);
1498
1537
  assert.equal(result.details.workflowGraph?.nodes[0]?.kind, "parallel-group");
1499
1538
  assert.equal(result.details.workflowGraph?.nodes[1]?.kind, "step");
1500
1539
  assert.equal(result.details.workflowGraph?.nodes[2]?.kind, "parallel-group");
@@ -1536,7 +1575,7 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
1536
1575
  makeChainParams(
1537
1576
  [{
1538
1577
  parallel: [
1539
- { agent: "reviewer-a", task: "Review A", outputMode: "file-only" },
1578
+ { agent: "reviewer-a", task: "Review A", output: false, outputMode: "file-only" },
1540
1579
  { agent: "reviewer-b", task: "Review B", output: "b.md" },
1541
1580
  ],
1542
1581
  }],
@@ -1700,6 +1739,7 @@ describe("chain execution — parallel steps", { skip: !available ? "pi packages
1700
1739
 
1701
1740
  assert.ok(!result.isError);
1702
1741
  assert.equal(result.details.results.length, 4);
1742
+ assert.deepEqual(result.details.results.map((row) => row.index), [0, 1, 2, 3]);
1703
1743
  assert.equal(result.details.totalSteps, 3);
1704
1744
  });
1705
1745
  });
@@ -53,6 +53,18 @@ function assistantToolCall(toolName: string): Record<string, unknown> {
53
53
  };
54
54
  }
55
55
 
56
+ function emptyTerminalAssistant(): Record<string, unknown> {
57
+ return {
58
+ role: "assistant",
59
+ content: [],
60
+ api: "test",
61
+ provider: "test",
62
+ model: "test",
63
+ stopReason: "stop",
64
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } },
65
+ };
66
+ }
67
+
56
68
  describe("detectSubagentError", { skip: !available ? "utils not importable" : undefined }, () => {
57
69
  // ---- Basic detection (must still work) ----
58
70
 
@@ -197,6 +209,42 @@ describe("detectSubagentError", { skip: !available ? "utils not importable" : un
197
209
  "tool-call assistant message without text is not a recovery");
198
210
  });
199
211
 
212
+ it("accepts a successful tool recovery followed by a clean empty terminal stop", () => {
213
+ const messages = [
214
+ toolResult("bash", "error: process exited with code 1", true),
215
+ assistantToolCall("edit"),
216
+ toolResult("edit", "Updated src/file.ts"),
217
+ emptyTerminalAssistant(),
218
+ ];
219
+ const result = detectSubagentError(messages);
220
+ assert.equal(result.hasError, false,
221
+ "later successful work plus a clean terminal stop recovers the earlier tool error");
222
+ });
223
+
224
+ it("keeps the final failed tool result despite a clean empty terminal stop", () => {
225
+ const messages = [
226
+ toolResult("edit", "Updated src/file.ts"),
227
+ assistantToolCall("bash"),
228
+ toolResult("bash", "error: process exited with code 2", true),
229
+ emptyTerminalAssistant(),
230
+ ];
231
+ const result = detectSubagentError(messages);
232
+ assert.equal(result.hasError, true,
233
+ "a clean terminal stop alone cannot recover the final failed tool result");
234
+ assert.equal(result.exitCode, 2);
235
+ });
236
+
237
+ it("does not recover an earlier tool error after an unsuccessful terminal stop", () => {
238
+ const messages = [
239
+ toolResult("bash", "error: process exited with code 1", true),
240
+ toolResult("edit", "Updated src/file.ts"),
241
+ { ...emptyTerminalAssistant(), errorMessage: "provider transport failed" },
242
+ ];
243
+ const result = detectSubagentError(messages);
244
+ assert.equal(result.hasError, true,
245
+ "the terminal assistant stop must itself be clean");
246
+ });
247
+
200
248
  it("does not treat empty/whitespace assistant message as recovery", () => {
201
249
  const messages = [
202
250
  toolResult("read", "EISDIR: illegal operation on a directory", true),
@@ -310,7 +310,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
310
310
 
311
311
  assert.equal(result.isError, undefined);
312
312
  const args = readCallArgs();
313
- assert.ok((args.at(-1) ?? "").startsWith("Task: \n\n## Acceptance Contract"));
313
+ assert.ok((args.at(-1) ?? "").startsWith("Task: \n\n---\n**Output:**"));
314
+ assert.match(args.at(-1) ?? "", /## Acceptance Contract/);
314
315
  });
315
316
 
316
317
  it("does not treat top-level agent as single mode when tasks are present", async () => {
@@ -327,7 +328,8 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
327
328
 
328
329
  assert.equal(result.isError, undefined);
329
330
  const args = readCallArgs();
330
- assert.ok((args.at(-1) ?? "").startsWith("Task: parallel task\n\n## Acceptance Contract"));
331
+ assert.ok((args.at(-1) ?? "").startsWith("Task: parallel task\n\n---\n**Output:**"));
332
+ assert.match(args.at(-1) ?? "", /## Acceptance Contract/);
331
333
  });
332
334
 
333
335
  it("uses agent defaultContext fork when launch context is omitted", async () => {
@@ -1827,7 +1829,7 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
1827
1829
  );
1828
1830
 
1829
1831
  assert.equal(result.isError, undefined);
1830
- const args = readAllCallArgs().find((callArgs) => (callArgs.at(-1) ?? "").startsWith(`Task: ${task}\n\n## Acceptance Contract`));
1832
+ const args = readAllCallArgs().find((callArgs) => (callArgs.at(-1) ?? "").startsWith(`Task: ${task}\n\n---\n**Output:**`));
1831
1833
  assert.ok(args, "expected a recorded mock pi call for this test task");
1832
1834
  const modelIndex = args.indexOf("--model");
1833
1835
  assert.notEqual(modelIndex, -1);
@@ -1863,14 +1865,14 @@ describe("fork context execution wiring", { skip: !available ? "subagent executo
1863
1865
  process.env.USERPROFILE = tempHome;
1864
1866
  const worktreeDir = path.join(tempDir, "worktree");
1865
1867
  fs.mkdirSync(worktreeDir, { recursive: true });
1866
- writeProjectOverride(tempDir, "reviewer", "openai/gpt-5-main");
1867
- writeProjectOverride(worktreeDir, "reviewer", "openai/gpt-5-worktree");
1868
+ writeProjectOverride(tempDir, "commentator", "openai/gpt-5-main");
1869
+ writeProjectOverride(worktreeDir, "commentator", "openai/gpt-5-worktree");
1868
1870
  const executor = makeExecutor();
1869
1871
 
1870
1872
  try {
1871
1873
  const result = await executor.execute(
1872
1874
  "id",
1873
- { action: "get", agent: "reviewer", cwd: "worktree" },
1875
+ { action: "get", agent: "commentator", cwd: "worktree" },
1874
1876
  new AbortController().signal,
1875
1877
  undefined,
1876
1878
  makeCtx(makeSessionManagerRecorder().manager),