@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
@@ -5,8 +5,8 @@ import { writeAtomicJson } from "../../shared/atomic-json.ts";
5
5
  import { appendJsonl } from "../../shared/artifacts.ts";
6
6
  import type { AsyncParallelGroupStatus, AsyncStatus, WorkflowGraphNode, WorkflowGraphSnapshot } from "../../shared/types.ts";
7
7
  import { readStatus } from "../../shared/utils.ts";
8
- import type { DynamicRunnerGroup, ParallelStepGroup, RunnerStep, RunnerSubagentStep } from "../shared/parallel-utils.ts";
9
- import { isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.ts";
8
+ import type { DynamicRunnerGroup, ParallelStepGroup, RunnerStep, RunnerSubagentStep, RunnerCheckpointStep } from "../shared/parallel-utils.ts";
9
+ import { isCheckpointRunnerStep, isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.ts";
10
10
 
11
11
  const APPEND_REQUESTS_DIR = "append-requests";
12
12
 
@@ -51,7 +51,9 @@ export function countPendingChainAppendRequests(asyncDir: string): number {
51
51
  export function runnerStepOutputNames(steps: RunnerStep[]): string[] {
52
52
  const names: string[] = [];
53
53
  for (const step of steps) {
54
- if (isParallelGroup(step)) {
54
+ if (isCheckpointRunnerStep(step)) {
55
+ continue;
56
+ } else if (isParallelGroup(step)) {
55
57
  names.push(...step.parallel.map((task) => task.outputName).filter((name): name is string => Boolean(name)));
56
58
  } else if (isDynamicRunnerGroup(step)) {
57
59
  if (step.collect.as) names.push(step.collect.as);
@@ -128,9 +130,22 @@ export function consumeChainAppendRequests(asyncDir: string): ChainAppendRequest
128
130
  return requests.sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id));
129
131
  }
130
132
 
133
+ const MAX_STATUS_STEP_DESCRIPTION_CHARS = 160;
134
+
135
+ /** Bounded one-line per-step task description persisted into status.json for fleet display. */
136
+ export function statusStepDescription(task: string | undefined): string | undefined {
137
+ const description = task?.replace(/\s+/g, " ").trim();
138
+ if (!description) return undefined;
139
+ return description.length > MAX_STATUS_STEP_DESCRIPTION_CHARS
140
+ ? `${description.slice(0, MAX_STATUS_STEP_DESCRIPTION_CHARS - 1)}…`
141
+ : description;
142
+ }
143
+
131
144
  function statusStepForTask(task: RunnerSubagentStep): StatusStep {
145
+ const description = statusStepDescription(task.task);
132
146
  return {
133
147
  agent: task.agent,
148
+ ...(description ? { description } : {}),
134
149
  ...(task.context ? { context: task.context } : {}),
135
150
  phase: task.phase,
136
151
  label: task.label,
@@ -147,7 +162,20 @@ function statusStepForTask(task: RunnerSubagentStep): StatusStep {
147
162
  };
148
163
  }
149
164
 
150
- function statusStepsForRunnerStep(step: RunnerStep): StatusStep[] {
165
+ function statusStepForCheckpoint(step: RunnerCheckpointStep, stepIndex: number): StatusStep {
166
+ return {
167
+ agent: `checkpoint:${step.checkpoint}`,
168
+ phase: step.phase,
169
+ label: step.label ?? step.checkpoint,
170
+ status: "pending",
171
+ checkpoint: { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: "pending", stepIndex },
172
+ recentTools: [],
173
+ recentOutput: [],
174
+ };
175
+ }
176
+
177
+ function statusStepsForRunnerStep(step: RunnerStep, stepIndex: number): StatusStep[] {
178
+ if (isCheckpointRunnerStep(step)) return [statusStepForCheckpoint(step, stepIndex)];
151
179
  if (isParallelGroup(step)) return step.parallel.map(statusStepForTask);
152
180
  if (isDynamicRunnerGroup(step)) {
153
181
  return [{
@@ -239,6 +267,21 @@ function graphNodeForDynamic(step: DynamicRunnerGroup, stepIndex: number): Workf
239
267
 
240
268
  function appendWorkflowNode(graph: WorkflowGraphSnapshot | undefined, step: RunnerStep, stepIndex: number, flatIndex: number): void {
241
269
  if (!graph) return;
270
+ if (isCheckpointRunnerStep(step)) {
271
+ const node: WorkflowGraphNode = {
272
+ id: `step-${stepIndex}`,
273
+ kind: "checkpoint",
274
+ phase: step.phase,
275
+ label: step.label?.trim() || step.checkpoint,
276
+ status: "pending",
277
+ flatIndex,
278
+ stepIndex,
279
+ checkpoint: { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: "pending", stepIndex },
280
+ };
281
+ graph.nodes.push(node);
282
+ pushPhase(graph, step.phase, node.id);
283
+ return;
284
+ }
242
285
  if (isParallelGroup(step)) {
243
286
  graph.nodes.push(graphNodeForParallel(step, stepIndex, flatIndex, graph));
244
287
  return;
@@ -263,7 +306,7 @@ export function appendRunnerStepsToStatus(input: {
263
306
  for (const step of input.steps) {
264
307
  const stepIndex = input.status.chainStepCount ?? input.status.steps?.length ?? 0;
265
308
  const flatIndex = input.status.steps?.length ?? 0;
266
- const statusSteps = statusStepsForRunnerStep(step);
309
+ const statusSteps = statusStepsForRunnerStep(step, stepIndex);
267
310
  input.status.steps ??= [];
268
311
  input.status.steps.push(...statusSteps);
269
312
  if (isParallelGroup(step)) {
@@ -53,6 +53,13 @@ export interface StopRequest {
53
53
  reason?: string;
54
54
  }
55
55
 
56
+ export interface CheckpointDecisionRequest {
57
+ type: "approve-checkpoint" | "reject-checkpoint";
58
+ ts?: number;
59
+ source?: string;
60
+ reason?: string;
61
+ }
62
+
56
63
  export interface SteerRequest {
57
64
  type: "steer";
58
65
  id: string;
@@ -110,6 +117,14 @@ export function stopRequestPath(asyncDir: string): string {
110
117
  return path.join(controlInboxDir(asyncDir), "stop.json");
111
118
  }
112
119
 
120
+ export function approveCheckpointRequestPath(asyncDir: string): string {
121
+ return path.join(controlInboxDir(asyncDir), "approve-checkpoint.json");
122
+ }
123
+
124
+ export function rejectCheckpointRequestPath(asyncDir: string): string {
125
+ return path.join(controlInboxDir(asyncDir), "reject-checkpoint.json");
126
+ }
127
+
113
128
  /** Directory of parent-to-runner steering requests. */
114
129
  export function steerRequestsDir(asyncDir: string): string {
115
130
  return path.join(controlInboxDir(asyncDir), STEER_REQUESTS_DIR);
@@ -254,6 +269,18 @@ export function requestAsyncStop(
254
269
  return requestPath;
255
270
  }
256
271
 
272
+ export function requestAsyncCheckpointDecision(
273
+ asyncDir: string,
274
+ type: CheckpointDecisionRequest["type"],
275
+ payload: Omit<CheckpointDecisionRequest, "type"> = {},
276
+ deps: { now?: () => number } = {},
277
+ ): string {
278
+ const requestPath = type === "approve-checkpoint" ? approveCheckpointRequestPath(asyncDir) : rejectCheckpointRequestPath(asyncDir);
279
+ const request: CheckpointDecisionRequest = { ...payload, ts: payload.ts ?? deps.now?.() ?? Date.now(), type };
280
+ writeAtomicJson(requestPath, request);
281
+ return requestPath;
282
+ }
283
+
257
284
  export function requestAsyncSteer(
258
285
  asyncDir: string,
259
286
  payload: { message: string; targetIndex?: number; targetIndexes?: number[]; source?: string; id?: string; ts?: number },
@@ -380,8 +407,15 @@ function parseSteerRequest(raw: unknown): SteerRequest | undefined {
380
407
 
381
408
  export function consumeSteerRequestsFromDir(dir: string, fsImpl: Pick<typeof fs, "existsSync" | "rmSync" | "readdirSync" | "readFileSync"> = fs): SteerRequest[] {
382
409
  if (!fsImpl.existsSync(dir)) return [];
410
+ let entries: string[];
411
+ try {
412
+ entries = fsImpl.readdirSync(dir).filter((name) => name.endsWith(".json")).sort();
413
+ } catch {
414
+ // Leave requests in place so the periodic poll can retry the scan.
415
+ return [];
416
+ }
383
417
  const requests: SteerRequest[] = [];
384
- for (const entry of fsImpl.readdirSync(dir).filter((name) => name.endsWith(".json")).sort()) {
418
+ for (const entry of entries) {
385
419
  const requestPath = path.join(dir, entry);
386
420
  let parsed: SteerRequest | undefined;
387
421
  try {
@@ -450,6 +484,21 @@ export function consumeStopRequest(
450
484
  return true;
451
485
  }
452
486
 
487
+ export function consumeCheckpointDecisionRequest(
488
+ asyncDir: string,
489
+ fsImpl: Pick<typeof fs, "existsSync" | "rmSync"> = fs,
490
+ ): "approved" | "rejected" | undefined {
491
+ if (fsImpl.existsSync(rejectCheckpointRequestPath(asyncDir))) {
492
+ try { fsImpl.rmSync(rejectCheckpointRequestPath(asyncDir), { force: true, recursive: true }); } catch {}
493
+ return "rejected";
494
+ }
495
+ if (fsImpl.existsSync(approveCheckpointRequestPath(asyncDir))) {
496
+ try { fsImpl.rmSync(approveCheckpointRequestPath(asyncDir), { force: true, recursive: true }); } catch {}
497
+ return "approved";
498
+ }
499
+ return undefined;
500
+ }
501
+
453
502
  /**
454
503
  * Parent side: portable interrupt = authoritative file request + best-effort OS
455
504
  * signal. The signal is only a latency optimization on Unix; ENOSYS on Windows
@@ -506,6 +555,21 @@ export function deliverStopRequest(input: {
506
555
  requestAsyncStop(input.asyncDir, input.source ? { source: input.source } : {}, { now: input.now });
507
556
  }
508
557
 
558
+ export function deliverCheckpointDecisionRequest(input: {
559
+ asyncDir: string;
560
+ decision: "approved" | "rejected";
561
+ now?: () => number;
562
+ source?: string;
563
+ reason?: string;
564
+ }): void {
565
+ requestAsyncCheckpointDecision(
566
+ input.asyncDir,
567
+ input.decision === "approved" ? "approve-checkpoint" : "reject-checkpoint",
568
+ { ...(input.source ? { source: input.source } : {}), ...(input.reason ? { reason: input.reason } : {}) },
569
+ { now: input.now },
570
+ );
571
+ }
572
+
509
573
  /**
510
574
  * Runner side: watch the control inbox and route interrupt requests into
511
575
  * `onInterrupt`. Uses `fs.watch` when available plus an interval poll as a
@@ -519,6 +583,7 @@ export function watchAsyncControlInbox(
519
583
  onTimeout?: () => void;
520
584
  onStop?: () => void;
521
585
  onSteer?: (request: SteerRequest) => void;
586
+ onCheckpointDecision?: (decision: "approved" | "rejected") => void;
522
587
  onSteerCapability?: (capability: SteerCapability) => void;
523
588
  onSteerAck?: (ack: SteerAck) => void;
524
589
  pollIntervalMs?: number;
@@ -542,6 +607,8 @@ export function watchAsyncControlInbox(
542
607
  if (consumeStopRequest(asyncDir, fsImpl)) opts.onStop?.();
543
608
  if (consumeTimeoutRequest(asyncDir, fsImpl)) opts.onTimeout?.();
544
609
  if (consumeInterruptRequest(asyncDir, fsImpl)) opts.onInterrupt();
610
+ const checkpointDecision = consumeCheckpointDecisionRequest(asyncDir, fsImpl);
611
+ if (checkpointDecision) opts.onCheckpointDecision?.(checkpointDecision);
545
612
  for (const request of consumeSteerRequests(asyncDir, fsImpl)) opts.onSteer?.(request);
546
613
  for (const capability of consumeSteerCapabilities(asyncDir, fsImpl)) opts.onSteerCapability?.(capability);
547
614
  for (const ack of consumeSteerAcks(asyncDir, fsImpl)) opts.onSteerAck?.(ack);
@@ -15,10 +15,11 @@ import {
15
15
  resolveCompletionBatchConfig,
16
16
  } from "./completion-batcher.ts";
17
17
  import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type SubagentState } from "../../shared/types.ts";
18
+ import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
18
19
 
19
20
  export interface SubagentNotifyDetails {
20
21
  agent: string;
21
- status: "completed" | "failed" | "paused";
22
+ status: "completed" | "failed" | "paused" | "stopped";
22
23
  source?: "async" | "foreground";
23
24
  taskInfo?: string;
24
25
  resultPreview: string;
@@ -37,6 +38,21 @@ export interface CompletionNotification {
37
38
  summary?: string;
38
39
  exitCode?: number;
39
40
  state?: string;
41
+ processSignal?: string | null;
42
+ interrupted?: boolean;
43
+ timedOut?: boolean;
44
+ stopped?: boolean;
45
+ turnBudgetExceeded?: boolean;
46
+ results?: Array<{
47
+ status?: string;
48
+ success?: boolean;
49
+ exitCode?: number | null;
50
+ processSignal?: string | null;
51
+ interrupted?: boolean;
52
+ timedOut?: boolean;
53
+ stopped?: boolean;
54
+ turnBudgetExceeded?: boolean;
55
+ }>;
40
56
  timestamp?: number;
41
57
  durationMs?: number;
42
58
  cwd?: string;
@@ -48,6 +64,8 @@ export interface CompletionNotification {
48
64
  totalTasks?: number;
49
65
  sessionId?: string | null;
50
66
  triggerTurn?: boolean;
67
+ /** True when an acknowledged grouped intercom relay already delivered this run. */
68
+ intercomDelivered?: boolean;
51
69
  parallelHandoff?: ParallelHandoffReference;
52
70
  }
53
71
 
@@ -90,7 +108,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
90
108
 
91
109
  export function parseSubagentNotifyContent(content: string): SubagentNotifyDetails | undefined {
92
110
  const lines = content.split("\n");
93
- const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
111
+ const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
94
112
  if (!match) return undefined;
95
113
  const body = lines.slice(2);
96
114
  let sessionIndex = -1;
@@ -174,15 +192,47 @@ function completionBatchKey(result: CompletionNotification): string {
174
192
  return cwd ? `cwd:${cwd}` : "unknown";
175
193
  }
176
194
 
195
+ /**
196
+ * Reference-first notification preview: compact child summaries built by the
197
+ * result watcher from each child's authoritative output path/log. Never uses
198
+ * `results[].output` or the run-level `summary` as parent-facing text; the run
199
+ * summary remains only a defensive fallback for legacy payloads without results.
200
+ */
201
+ function buildReferenceFirstPreview(result: CompletionNotification): string {
202
+ const children = Array.isArray(result.results) ? result.results : [];
203
+ if (children.length === 0) {
204
+ return typeof result.summary === "string" ? result.summary : "";
205
+ }
206
+ if (children.length === 1) {
207
+ const childSummary = typeof children[0]?.summary === "string" && children[0].summary.trim()
208
+ ? children[0].summary
209
+ : "(no output)";
210
+ return childSummary;
211
+ }
212
+ return children.map((child, index) => {
213
+ const agentLabel = typeof child?.agent === "string" ? child.agent : `step-${index + 1}`;
214
+ const childSummary = typeof child?.summary === "string" && child.summary.trim()
215
+ ? child.summary
216
+ : "(no output)";
217
+ return `${index + 1}. ${agentLabel}\n${childSummary}`;
218
+ }).join("\n\n");
219
+ }
220
+
177
221
  export function buildCompletionDetails(result: CompletionNotification): SubagentNotifyDetails {
178
222
  const agent = result.agent ?? "unknown";
179
223
  const summary = typeof result.summary === "string" ? result.summary : "";
180
- const paused = !result.success && (
224
+ const stopped = result.stopped === true
225
+ || result.state === "stopped"
226
+ || (result.success !== true && result.exitCode !== 0 && isUnexplainedProcessSignal(result))
227
+ || result.results?.some((child) => child.stopped === true
228
+ || child.status === "stopped"
229
+ || (child.success !== true && child.exitCode !== 0 && isUnexplainedProcessSignal(child))) === true;
230
+ const paused = !stopped && !result.success && (
181
231
  result.exitCode === 0
182
232
  || result.state === "paused"
183
233
  || summary.startsWith("Paused after interrupt.")
184
234
  );
185
- const status = paused ? "paused" : result.success ? "completed" : "failed";
235
+ const status = stopped ? "stopped" : paused ? "paused" : result.success ? "completed" : "failed";
186
236
  const taskInfo =
187
237
  result.taskIndex !== undefined && result.totalTasks !== undefined
188
238
  ? ` (${result.taskIndex + 1}/${result.totalTasks})`
@@ -205,7 +255,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
205
255
  status,
206
256
  ...(result.source ? { source: result.source } : {}),
207
257
  ...(taskInfo ? { taskInfo } : {}),
208
- resultPreview: summary,
258
+ resultPreview: buildReferenceFirstPreview(result),
209
259
  ...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
210
260
  ...(handoffPath ? { handoffPath } : {}),
211
261
  ...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
@@ -250,6 +300,7 @@ export default function registerSubagentNotify(
250
300
 
251
301
  const deliver = (result: CompletionNotification): Promise<boolean> => {
252
302
  if (disposed || typeof result.sessionId !== "string" || result.sessionId !== state.currentSessionId) return Promise.resolve(false);
303
+ if (result.intercomDelivered === true) return Promise.resolve(true);
253
304
  const key = buildCompletionKey(result, "notify");
254
305
  const seenAt = seen.get(key);
255
306
  if (seenAt !== undefined && now() - seenAt <= ttlMs) return Promise.resolve(true);
@@ -8,6 +8,7 @@ import {
8
8
  type NestedRunSummary,
9
9
  type ParallelHandoffReference,
10
10
  type SubagentResultIntercomChild,
11
+ type SubagentOutputState,
11
12
  type SubagentState,
12
13
  } from "../../shared/types.ts";
13
14
  import {
@@ -19,6 +20,7 @@ import {
19
20
  } from "../../intercom/result-intercom.ts";
20
21
  import { projectNestedRegistryForRoot, sanitizeSummary } from "../shared/nested-events.ts";
21
22
  import { resolveWatchPath } from "../../shared/utils.ts";
23
+ import { formatBoundedPersistenceFallback, formatSavedOutputReference } from "../shared/single-output.ts";
22
24
  import type { CompletionNotifier, CompletionNotification } from "./notify.ts";
23
25
 
24
26
  const WATCHER_RESTART_DELAY_MS = 3000;
@@ -38,15 +40,26 @@ type ResultWatcherDeps = {
38
40
  fs?: ResultWatcherFs;
39
41
  timers?: ResultWatcherTimers;
40
42
  notifier?: Pick<CompletionNotifier, "deliver">;
43
+ /** External grouped-result transport. Disable when native completion notifications own delivery. */
44
+ deliverIntercomResults?: boolean;
41
45
  };
42
46
 
43
47
  type ResultFileChild = {
44
48
  agent?: string;
45
49
  output?: string;
50
+ /** Authoritative durable output location: explicit/generated saved path or async output log. */
51
+ outputPath?: string;
52
+ savedOutputPath?: string;
53
+ outputState?: SubagentOutputState;
46
54
  error?: string;
47
55
  success?: boolean;
48
56
  state?: string;
57
+ exitCode?: number | null;
58
+ interrupted?: boolean;
59
+ timedOut?: boolean;
49
60
  stopped?: boolean;
61
+ turnBudgetExceeded?: boolean;
62
+ processSignal?: string | null;
50
63
  sessionFile?: string;
51
64
  artifactPaths?: { outputPath?: string };
52
65
  intercomTarget?: string;
@@ -84,6 +97,57 @@ function isNotFound(error: unknown): boolean {
84
97
  return errorCode(error) === "ENOENT";
85
98
  }
86
99
 
100
+ /**
101
+ * Resolve the authoritative output location for one async child: explicit/generated
102
+ * saved output path first, else the persisted per-child output log
103
+ * (`asyncDir/output-<flat-index>.log`). Legacy result files without saved-path
104
+ * fields fall back to the output log when the run directory is known.
105
+ */
106
+ function childOutputPath(data: ResultFileData, result: ResultFileChild | undefined, index: number): string | undefined {
107
+ if (typeof result?.outputPath === "string" && result.outputPath.trim()) return result.outputPath;
108
+ if (typeof result?.savedOutputPath === "string" && result.savedOutputPath.trim()) return result.savedOutputPath;
109
+ if (typeof data.asyncDir === "string" && data.asyncDir.trim()) return path.join(data.asyncDir, `output-${index}.log`);
110
+ return undefined;
111
+ }
112
+
113
+ /**
114
+ * Reference-only child summary for async delivery: the saved-output reference
115
+ * (or output-log reference) plus process error/status for failed children. Never
116
+ * emits `results[].output` or the run `summary` as parent-facing text; when the
117
+ * output location is absent or unreadable, a bounded fallback is used instead.
118
+ */
119
+ function buildChildReferenceSummary(fsApi: ResultWatcherFs, input: {
120
+ outputPath: string | undefined;
121
+ outputText: string;
122
+ exitCode: number;
123
+ error?: string;
124
+ }): string {
125
+ const statusError = input.exitCode !== 0 && input.error ? input.error : undefined;
126
+ if (input.outputPath) {
127
+ try {
128
+ const content = fsApi.readFileSync(input.outputPath, "utf-8");
129
+ const ref = formatSavedOutputReference(input.outputPath, content);
130
+ return statusError ? `${statusError}\n\n${ref.message}` : ref.message;
131
+ } catch (error) {
132
+ const fallback = formatBoundedPersistenceFallback({
133
+ error: `Failed to read saved output: ${error instanceof Error ? error.message : String(error)}`,
134
+ outputPath: input.outputPath,
135
+ fullOutput: input.outputText,
136
+ exitCode: input.exitCode,
137
+ processError: input.error,
138
+ });
139
+ return statusError ? `${statusError}\n\n${fallback}` : fallback;
140
+ }
141
+ }
142
+ const fallback = formatBoundedPersistenceFallback({
143
+ error: "No saved output path is available for this child.",
144
+ fullOutput: input.outputText,
145
+ exitCode: input.exitCode,
146
+ processError: input.error,
147
+ });
148
+ return statusError ? `${statusError}\n\n${fallback}` : fallback;
149
+ }
150
+
87
151
  function shouldPoll(error: unknown): boolean {
88
152
  const code = errorCode(error);
89
153
  return code === "EMFILE" || code === "ENOSPC";
@@ -108,6 +172,7 @@ export function createResultWatcher(
108
172
  const fsApi = deps.fs ?? fs;
109
173
  const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
110
174
  const notifier = deps.notifier ?? { deliver: async () => true };
175
+ const deliverIntercomResults = deps.deliverIntercomResults !== false;
111
176
  const pendingTriggerTurn = new Map<string, boolean>();
112
177
  const processing = new Set<string>();
113
178
  let deliveryActive = true;
@@ -171,14 +236,16 @@ export function createResultWatcher(
171
236
  const hasResultChildren = Array.isArray(data.results) && data.results.length > 0;
172
237
  const resultChildren: ResultFileChild[] = hasResultChildren
173
238
  ? data.results!
174
- : [{ agent: data.agent ?? undefined, output: data.summary, success: data.success }];
239
+ : [{ agent: data.agent ?? undefined, outputPath: childOutputPath(data, undefined, 0), outputState: "unknown", success: data.success }];
175
240
  const normalizedChildren = attachNestedChildrenToResultChildren(runId, resultChildren.map((result = {}, index): SubagentResultIntercomChild => {
176
- const baseOutput = result.output ?? data.summary;
177
- const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
178
- const output = hasRealOutput ? baseOutput : "(no output)";
179
- const summary = result.success === false && result.error
180
- ? `${result.error}${hasRealOutput ? `\n\nOutput:\n${baseOutput}` : ""}`
181
- : output;
241
+ const baseOutput = hasResultChildren ? result.output : result.output ?? data.summary;
242
+ const outputText = typeof baseOutput === "string" ? baseOutput : "";
243
+ const summary = buildChildReferenceSummary(fsApi, {
244
+ outputPath: childOutputPath(data, result, index),
245
+ outputText,
246
+ exitCode: result.success === false ? 1 : result.success === true ? 0 : typeof result.exitCode === "number" ? result.exitCode : data.success === false ? 1 : 0,
247
+ error: result.error ?? (result.success === false ? (typeof data.error === "string" ? data.error : undefined) : undefined),
248
+ });
182
249
  const sessionPath = result.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : undefined);
183
250
  const childNestedChildren = sanitizeNestedResultChildren(result.children, resultPath, `results[${index}].children`);
184
251
  const childState = result.state === "paused" || result.state === "stopped"
@@ -190,7 +257,18 @@ export function createResultWatcher(
190
257
  : undefined;
191
258
  return {
192
259
  agent: result.agent ?? data.agent ?? `step-${index + 1}`,
193
- status: resolveSubagentResultStatus({ success: result.success, state: childState }),
260
+ status: resolveSubagentResultStatus({
261
+ success: result.success,
262
+ state: childState,
263
+ interrupted: result.interrupted,
264
+ timedOut: result.timedOut,
265
+ stopped: result.stopped,
266
+ turnBudgetExceeded: result.turnBudgetExceeded,
267
+ processSignal: result.processSignal,
268
+ }),
269
+ outputState: result.outputState === "present" || result.outputState === "absent" || result.outputState === "unknown"
270
+ ? result.outputState
271
+ : "unknown",
194
272
  summary,
195
273
  index,
196
274
  artifactPath: result.artifactPaths?.outputPath,
@@ -201,11 +279,12 @@ export function createResultWatcher(
201
279
  }), nestedChildren);
202
280
 
203
281
  const intercomTarget = data.intercomTarget?.trim();
204
- if (intercomTarget && triggerTurn) {
282
+ let intercomDelivered = false;
283
+ if (deliverIntercomResults && intercomTarget && triggerTurn) {
205
284
  const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
206
285
  ? data.mode
207
286
  : resultChildren.length > 1 ? "chain" : "single";
208
- const delivered = await deliverSubagentResultIntercomEvent(pi.events, buildSubagentResultIntercomPayload({
287
+ intercomDelivered = await deliverSubagentResultIntercomEvent(pi.events, buildSubagentResultIntercomPayload({
209
288
  to: intercomTarget,
210
289
  runId,
211
290
  mode,
@@ -216,7 +295,7 @@ export function createResultWatcher(
216
295
  ...(data.parallelHandoff ? { parallelHandoff: data.parallelHandoff } : {}),
217
296
  }));
218
297
  if (!ownsSession(data.sessionId, epoch)) return;
219
- if (!delivered) console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
298
+ if (!intercomDelivered) console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
220
299
  }
221
300
 
222
301
  const accepted = await notifier.deliver({
@@ -224,6 +303,7 @@ export function createResultWatcher(
224
303
  id: data.id ?? runId,
225
304
  runId,
226
305
  triggerTurn,
306
+ intercomDelivered,
227
307
  ...(nestedChildren?.length ? { nestedChildren } : {}),
228
308
  ...(Array.isArray(data.results) ? {
229
309
  results: hasResultChildren ? normalizedChildren.map((child, index) => ({
@@ -249,6 +329,7 @@ export function createResultWatcher(
249
329
  ...data,
250
330
  runId,
251
331
  triggerTurn,
332
+ intercomDelivered,
252
333
  ...(nestedChildren?.length ? { nestedChildren } : {}),
253
334
  ...(Array.isArray(data.results) ? {
254
335
  results: hasResultChildren ? normalizedChildren.map((child, index) => ({
@@ -8,6 +8,7 @@ import { formatModelThinking } from "../../shared/formatters.ts";
8
8
  import { formatActivityLabel } from "../../shared/status-format.ts";
9
9
  import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details, type ForegroundResumeRun, type NestedRunSummary, type SteeringStatus, type SubagentState } from "../../shared/types.ts";
10
10
  import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
11
+ import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
11
12
  import { readProcessTerminal, sanitizeProcessTerminal } from "./process-terminal.ts";
12
13
  import { resolveAsyncRunLocation } from "./async-resume.ts";
13
14
  import { resolveSubagentRunId } from "./run-id-resolver.ts";
@@ -39,6 +40,11 @@ function hasExistingSessionFile(value: unknown): value is string {
39
40
  return typeof value === "string" && fs.existsSync(value);
40
41
  }
41
42
 
43
+ function formatCheckpointGuidance(runId: string | undefined, checkpoint: AsyncStatus["checkpoint"] | undefined): string | undefined {
44
+ if (!runId || !checkpoint || checkpoint.status !== "pending") return undefined;
45
+ return `Checkpoint: ${checkpoint.name}${checkpoint.message ? ` — ${checkpoint.message}` : ""}\nApprove: subagent({ action: "approve-checkpoint", id: "${runId}" })\nReject: subagent({ action: "reject-checkpoint", id: "${runId}" })`;
46
+ }
47
+
42
48
  function formatResumeGuidance(runId: string | undefined, children: Array<{ agent?: unknown; sessionFile?: unknown }>, fallbackSessionFile?: unknown, options: { stopped?: boolean } = {}): string {
43
49
  if (options.stopped) return "Resume: unavailable; stopped runs are not resumable. Start a new run instead.";
44
50
  const knownChildren = children
@@ -126,6 +132,12 @@ function formatRememberedForegroundStatus(run: ForegroundResumeRun): string {
126
132
  else lines.push(`Transcript: subagent({ action: "status", id: "${run.runId}", index: 0, view: "transcript" })`);
127
133
  const detached = run.children.some((child) => child.status === "detached");
128
134
  const resumable = run.children.find((child) => hasExistingSessionFile(child.sessionFile));
135
+ if (run.checkpoint?.status === "pending") {
136
+ lines.push(`Checkpoint: ${run.checkpoint.name}${run.checkpoint.message ? ` — ${run.checkpoint.message}` : ""}`);
137
+ lines.push(`Approve: subagent({ action: "approve-checkpoint", id: "${run.runId}" })`);
138
+ lines.push(`Reject: subagent({ action: "reject-checkpoint", id: "${run.runId}" })`);
139
+ return lines.join("\n");
140
+ }
129
141
  if (detached) {
130
142
  lines.push(`Recovery: reply to the supervisor request first, then wait with subagent_wait({ id: "${run.runId}" }); do not resume or launch a replacement while any child remains detached.`);
131
143
  } else if (resumable) {
@@ -403,7 +415,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
403
415
  if (status.sessionFile) lines.push(`Session: ${status.sessionFile}`);
404
416
  if (status.state === "running") lines.push(`Steer running child: subagent({ action: "steer", id: "${status.runId}", message: "..." })`);
405
417
  if (status.state !== "running") {
406
- lines.push(formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile, { stopped: status.state === "stopped" || status.stopped === true }));
418
+ lines.push(formatCheckpointGuidance(status.runId, status.checkpoint) ?? formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile, { stopped: status.state === "stopped" || status.stopped === true }));
407
419
  }
408
420
  if (fs.existsSync(logPath)) lines.push(`Log: ${logPath}`);
409
421
  if (fs.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
@@ -415,7 +427,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
415
427
  if (resultPath) {
416
428
  try {
417
429
  const raw = fs.readFileSync(resultPath, "utf-8");
418
- const data = JSON.parse(raw) as { id?: string; runId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; sessionFile?: string; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }> };
430
+ const data = JSON.parse(raw) as { id?: string; runId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; processSignal?: string | null; sessionFile?: string; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; interrupted?: boolean; processSignal?: string | null }> };
419
431
  if (params.view === "transcript") {
420
432
  try {
421
433
  return { content: [{ type: "text", text: formatAsyncResultTranscript(data, resultPath, { index: params.index, lines: params.lines }) }], details: { mode: "single", results: [] } };
@@ -424,7 +436,21 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
424
436
  return { content: [{ type: "text", text: message }], isError: true, details: { mode: "single", results: [] } };
425
437
  }
426
438
  }
427
- const status = data.state === "stopped" ? "stopped" : data.success ? "complete" : data.state === "paused" || data.exitCode === 0 ? "paused" : "failed";
439
+ const childStatuses = Array.isArray(data.results)
440
+ ? data.results.map((child) => resolveSubagentResultStatus({
441
+ success: child.success,
442
+ state: child.state,
443
+ interrupted: child.interrupted,
444
+ timedOut: child.timedOut,
445
+ stopped: child.stopped,
446
+ turnBudgetExceeded: child.turnBudgetExceeded,
447
+ processSignal: child.processSignal,
448
+ exitCode: typeof child.exitCode === "number" ? child.exitCode : undefined,
449
+ }))
450
+ : [];
451
+ const status = data.state === "stopped" || data.stopped === true || childStatuses.includes("stopped")
452
+ ? "stopped"
453
+ : data.success ? "complete" : data.state === "paused" || data.exitCode === 0 ? "paused" : "failed";
428
454
  const runId = data.runId ?? data.id ?? resolvedId;
429
455
  const lines = [`Run: ${runId}`, `State: ${status}`, `Result: ${resultPath}`];
430
456
  if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);