@selesai/code 0.5.20 → 0.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/extensions/handoff-new.test.ts +8 -9
  2. package/dist/extensions/handoff-new.ts +6 -8
  3. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  4. package/dist/extensions/pi-subagents/README.md +602 -220
  5. package/dist/extensions/pi-subagents/banner.png +0 -0
  6. package/dist/extensions/pi-subagents/index.ts +1 -0
  7. package/dist/extensions/pi-subagents/install.mjs +3 -3
  8. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  9. package/dist/extensions/pi-subagents/package.json +26 -14
  10. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  11. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  12. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  13. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  14. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  15. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  16. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  18. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  19. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  20. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  21. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  22. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  23. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  24. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  25. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  26. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  27. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  28. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  29. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  30. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  31. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  32. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  33. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  34. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  35. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  36. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  37. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  38. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  39. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  40. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  41. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  42. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  43. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  44. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  45. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  46. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  48. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  49. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  50. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  51. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  53. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  54. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  55. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  56. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  57. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  58. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  59. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  60. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  61. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  62. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  63. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  65. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  66. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  67. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  68. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  69. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  70. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  71. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  72. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  73. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  74. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  75. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  76. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  77. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  78. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  79. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  80. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  81. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  82. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  83. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  87. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  88. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  89. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  90. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  92. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  94. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  97. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  98. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  99. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  100. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  101. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  102. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  103. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  104. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  105. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  106. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  107. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  108. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  109. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  110. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  111. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  112. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  113. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  114. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  115. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  116. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  117. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  120. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  121. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  122. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  123. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  124. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  125. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  126. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  127. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  128. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  129. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  130. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  131. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  132. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  133. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  134. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  135. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  136. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  137. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  138. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  139. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  140. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  141. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  142. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  143. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  144. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  145. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  146. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  147. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  148. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  149. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  150. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  151. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  152. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  153. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  154. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  155. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  156. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  157. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  158. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  159. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  160. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  161. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  162. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  163. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  164. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  165. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  166. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  167. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  168. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  169. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  170. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  171. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  172. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  173. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  174. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  175. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  176. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  177. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  178. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  179. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  180. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  181. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  182. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  183. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  184. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  185. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  186. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  187. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  188. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  189. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  190. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  191. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  192. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  194. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  195. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  196. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  197. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  198. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  199. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  200. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  201. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  202. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  203. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  204. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  205. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  206. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  207. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  208. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  209. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  210. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  211. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  212. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  213. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  214. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  215. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  216. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  217. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  218. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  219. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  220. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  221. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  222. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  223. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  224. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  225. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  226. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  227. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  228. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  229. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  230. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  231. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  232. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  233. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  234. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  235. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  236. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  237. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  238. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  239. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  240. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  241. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  245. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  246. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  247. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  248. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  249. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  250. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  251. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  252. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  253. package/dist/extensions/question/batch.ts +39 -0
  254. package/dist/extensions/question/dialog-adapter.ts +23 -1
  255. package/dist/extensions/question/index.ts +237 -7
  256. package/dist/extensions/question/schemas.ts +28 -15
  257. package/dist/extensions/question/tests/batch.test.ts +26 -0
  258. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  259. package/dist/extensions/question/tui-adapter.ts +2 -3
  260. package/dist/extensions/question/types.ts +37 -0
  261. package/dist/extensions/question/ui-protocol.ts +3 -3
  262. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  263. package/package.json +1 -1
@@ -16,29 +16,39 @@ import { randomUUID } from "node:crypto";
16
16
  import * as fs from "node:fs";
17
17
  import * as os from "node:os";
18
18
  import * as path from "node:path";
19
+ import "../shared/env.ts";
19
20
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
20
21
  import { keyText, type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@selesai/code";
21
22
  import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
22
- import { discoverAgents, ensureDefaultUserSubagentSettings } from "../agents/agents.ts";
23
+ import { discoverAgents } from "../agents/agents.ts";
24
+ import { ensureAccessibleDir } from "../shared/accessible-dir.ts";
23
25
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
24
26
  import { resolveCurrentSessionId } from "../shared/session-identity.ts";
25
27
  import { cleanupOldChainDirs } from "../shared/settings.ts";
26
- import { clearLegacyResultAnimationTimer, renderWidget, renderSubagentResult } from "../tui/render.ts";
27
- import { SubagentParams, WaitParams } from "./schemas.ts";
28
+ import { clearLegacyResultAnimationTimer, renderSubagentResult } from "../tui/render.ts";
29
+ import { openSubagentFleet } from "../tui/fleet.ts";
30
+ import { SubagentFleetStatus } from "../tui/fleet-status.ts";
31
+ import { SubagentParams } from "./schemas.ts";
32
+ import { validateChainInput } from "./chain-validation.ts";
28
33
  import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
29
34
  import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
30
35
  import { createResultWatcher } from "../runs/background/result-watcher.ts";
31
36
  import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
32
37
  import { registerSlashCommands } from "../slash/slash-commands.ts";
33
38
  import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.ts";
39
+ import { registerMainWatchdog } from "../watchdog/register-main.ts";
34
40
  import { registerSlashSubagentBridge } from "../slash/slash-bridge.ts";
35
41
  import { createNativeSupervisorChannel } from "../intercom/native-supervisor-channel.ts";
36
42
  import { registerSubagentRpcBridge } from "./rpc.ts";
37
43
  import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots, type SlashMessageDetails } from "../slash/slash-live-state.ts";
38
44
  import { inspectSubagentStatus } from "../runs/background/run-status.ts";
39
- import { waitForSubagents } from "../runs/background/wait.ts";
40
- import registerSubagentNotify, { type SubagentNotifyDetails } from "../runs/background/notify.ts";
45
+ import { resolveWaitToolConfig } from "../runs/background/subagent-wait.ts";
46
+ import { registerWaitTool } from "../runs/background/wait-tool.ts";
47
+ import { drainOutstandingWork } from "../runs/background/auto-drain.ts";
48
+ import registerSubagentNotify, { parseSubagentNotifyContent, type SubagentNotifyDetails } from "../runs/background/notify.ts";
49
+ import { formatSteeringNotice, handleSubagentSteeringNotice, SUBAGENT_STEERING_MESSAGE_TYPE, type SubagentSteeringMessageDetails } from "./steering-notices.ts";
41
50
  import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/pi-args.ts";
51
+ import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
42
52
  import { formatDuration, shortenPath } from "../shared/formatters.ts";
43
53
  import { loadConfig } from "./config.ts";
44
54
  import { buildSubagentToolDescription } from "./tool-description.ts";
@@ -53,7 +63,9 @@ import {
53
63
  SUBAGENT_ASYNC_COMPLETE_EVENT,
54
64
  SUBAGENT_ASYNC_STARTED_EVENT,
55
65
  SUBAGENT_CONTROL_EVENT,
66
+ SUBAGENT_STEERING_NOTICE_EVENT,
56
67
  WIDGET_KEY,
68
+ resolveMaxSubagentSpawnsPerSession,
57
69
  } from "../shared/types.ts";
58
70
  import {
59
71
  clearPendingForegroundControlNotices,
@@ -85,58 +97,12 @@ function expandTilde(p: string): string {
85
97
  return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
86
98
  }
87
99
 
88
- /**
89
- * Create a directory and verify it is actually accessible.
90
- * On Windows with Azure AD/Entra ID, directories created shortly after
91
- * wake-from-sleep can end up with broken NTFS ACLs (null DACL) when the
92
- * cloud SID cannot be resolved without network connectivity. This leaves
93
- * the directory completely inaccessible to the creating user.
94
- */
95
- function ensureAccessibleDir(dirPath: string): void {
96
- fs.mkdirSync(dirPath, { recursive: true });
97
- try {
98
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
99
- } catch {
100
- try {
101
- fs.rmSync(dirPath, { recursive: true, force: true });
102
- } catch {
103
- // Best effort: retry mkdir/access even if cleanup fails.
104
- }
105
- fs.mkdirSync(dirPath, { recursive: true });
106
- fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
107
- }
108
- }
109
-
110
100
  function isSlashResultRunning(result: { details?: Details }): boolean {
111
101
  return result.details?.progress?.some((entry) => entry.status === "running")
112
102
  || result.details?.results.some((entry) => entry.progress?.status === "running")
113
103
  || false;
114
104
  }
115
105
 
116
- // Drives the inline running-indicator braille animation for foreground subagent
117
- // results. Foreground runs receive progress only on child events, so the glyph
118
- // (derived from progress fields) would freeze between events. While a result is
119
- // running we tick a frame counter + invalidate() every 80ms so renderSubagentResult
120
- // can blend the frame into runningGlyph and produce a smooth spinner.
121
- function subagentResultIsRunning(result: { details?: Details }): boolean {
122
- return result.details?.progress?.some((entry) => entry.status === "running")
123
- || result.details?.results.some((entry) => entry.progress?.status === "running")
124
- || false;
125
- }
126
-
127
- function ensureSubagentResultAnimation(context: { state: Record<string, unknown>; invalidate?: () => void }): void {
128
- const state = context.state as { subagentResultAnimationTimer?: ReturnType<typeof setInterval>; frame?: number };
129
- if (state.subagentResultAnimationTimer) return;
130
- if (typeof context.invalidate !== "function") return;
131
- if (state.frame === undefined) state.frame = 0;
132
- state.subagentResultAnimationTimer = setInterval(() => {
133
- state.frame = ((state.frame ?? 0) + 1) % 10;
134
- try {
135
- context.invalidate();
136
- } catch {}
137
- }, 80);
138
- }
139
-
140
106
  function isSlashResultError(result: { details?: Details }): boolean {
141
107
  return result.details?.results.some((entry) => entry.exitCode !== 0 && entry.progress?.status !== "running") || false;
142
108
  }
@@ -177,38 +143,6 @@ function createSlashResultComponent(
177
143
  return container;
178
144
  }
179
145
 
180
- function parseSubagentNotifyContent(content: string): SubagentNotifyDetails | undefined {
181
- const lines = content.split("\n");
182
- const header = lines[0] ?? "";
183
- const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
184
- if (!match) return undefined;
185
- const body = lines.slice(2);
186
- let sessionIndex = -1;
187
- for (let i = body.length - 1; i >= 1; i--) {
188
- if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i]!)) {
189
- sessionIndex = i;
190
- break;
191
- }
192
- }
193
- const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
194
- const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
195
- const resultPreview = resultLines.join("\n").trim() || "(no output)";
196
- let sessionLabel: string | undefined;
197
- let sessionValue: string | undefined;
198
- if (sessionLine) {
199
- const separator = sessionLine.indexOf(":");
200
- sessionLabel = sessionLine.slice(0, separator).toLowerCase();
201
- sessionValue = sessionLine.slice(separator + 1).trim();
202
- }
203
- return {
204
- agent: match[2]!,
205
- status: match[1] as SubagentNotifyDetails["status"],
206
- ...(match[3] ? { taskInfo: match[3] } : {}),
207
- resultPreview,
208
- ...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
209
- };
210
- }
211
-
212
146
  class SubagentControlNoticeComponent implements Component {
213
147
  constructor(
214
148
  private readonly details: SubagentControlMessageDetails,
@@ -257,16 +191,28 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
257
191
  cleanupOldChainDirs();
258
192
 
259
193
  const config = loadConfig();
194
+ const waitToolConfig = resolveWaitToolConfig(config.waitTool);
260
195
  const asyncByDefault = config.asyncByDefault === true;
196
+ const fleetViewEnabled = config.fleetView !== false;
197
+ const asyncWidgetEnabled = config.asyncWidget === true || (!fleetViewEnabled && config.asyncWidget !== false);
261
198
  const tempArtifactsDir = getArtifactsDir(null);
262
199
  cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
263
200
 
264
201
  const state: SubagentState = {
265
202
  baseCwd: "",
266
203
  currentSessionId: null,
204
+ artifactDirPreference: config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
205
+ parentSessionFile: null,
267
206
  subagentInProgress: false,
268
- subagentSpawns: { sessionId: null, count: 0 },
207
+ subagentSpawns: {
208
+ sessionId: null,
209
+ count: 0,
210
+ configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
211
+ granted: 0,
212
+ grantHistory: [],
213
+ },
269
214
  asyncJobs: new Map(),
215
+ fleetJobs: new Map(),
270
216
  foregroundRuns: new Map(),
271
217
  foregroundControls: new Map(),
272
218
  lastForegroundControlId: null,
@@ -284,19 +230,31 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
284
230
  };
285
231
 
286
232
  const supervisorChannel = createNativeSupervisorChannel(pi, state);
233
+ const mainWatchdog = registerMainWatchdog(pi);
234
+ const completionNotifier = registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
235
+ const fleetStatus = fleetViewEnabled
236
+ ? new SubagentFleetStatus(state, async (itemKey) => {
237
+ const ctx = state.lastUiContext;
238
+ if (!ctx?.hasUI) return;
239
+ await openSubagentFleet(ctx, state, { initialKey: itemKey });
240
+ })
241
+ : undefined;
287
242
  const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(
288
243
  pi,
289
244
  state,
290
245
  RESULTS_DIR,
291
246
  10 * 60 * 1000,
247
+ { notifier: completionNotifier },
292
248
  );
293
- startResultWatcher();
294
- primeExistingResults();
295
249
 
296
250
  const runtimeCleanup = () => {
297
251
  stopResultWatcher();
252
+ state.currentSessionId = null;
253
+ completionNotifier.dispose();
254
+ mainWatchdog.dispose();
298
255
  scheduledRunManager.stop();
299
256
  supervisorChannel.dispose();
257
+ fleetStatus?.dispose();
300
258
  clearPendingForegroundControlNotices(state);
301
259
  if (state.poller) {
302
260
  clearInterval(state.poller);
@@ -305,7 +263,9 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
305
263
  };
306
264
  globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
307
265
 
308
- const { ensurePoller, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
266
+ const { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
267
+ widgetEnabled: asyncWidgetEnabled,
268
+ });
309
269
  let executorExecute: ((id: string, params: SubagentParamsLike, signal: AbortSignal, onUpdate: ((r: AgentToolResult<Details>) => void) | undefined, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>) | undefined;
310
270
  const scheduledRunManager = createScheduledRunManager({
311
271
  config,
@@ -319,13 +279,16 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
319
279
  }
320
280
  return executorExecute(randomUUID(), params, signal, undefined, ctx);
321
281
  },
282
+ resolveCapabilityCeiling: (sessionId) => resolveCurrentSubagentCapabilityCeiling(sessionId),
322
283
  });
323
284
  const executor = createSubagentExecutor({
324
285
  pi,
325
286
  state,
326
287
  config,
327
288
  asyncByDefault,
289
+ waitToolEnabled: waitToolConfig.enabled,
328
290
  handleScheduledRunAction: (params, ctx) => scheduledRunManager.handleToolCall(params, ctx),
291
+ watchdog: mainWatchdog,
329
292
  tempArtifactsDir,
330
293
  getSubagentSessionRoot,
331
294
  expandTilde,
@@ -380,6 +343,12 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
380
343
  return new Text(text, 0, 0);
381
344
  });
382
345
 
346
+ pi.registerMessageRenderer<SubagentSteeringMessageDetails>(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
347
+ const details = message.details as SubagentSteeringMessageDetails | undefined;
348
+ if (!details) return undefined;
349
+ return new Text(theme.fg(details.state === "recovered" ? "warning" : "error", formatSteeringNotice(details)), 0, 0);
350
+ });
351
+
383
352
  pi.registerMessageRenderer<SubagentControlMessageDetails>(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
384
353
  const details = message.details as SubagentControlMessageDetails | undefined;
385
354
  if (!details?.event) return undefined;
@@ -402,38 +371,11 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
402
371
  const promptTemplateBridge = registerPromptTemplateDelegationBridge({
403
372
  events: pi.events,
404
373
  getContext: () => state.lastUiContext,
405
- execute: async (requestId, request, signal, ctx, onUpdate) => {
406
- if (request.tasks && request.tasks.length > 0) {
407
- return executeSubagentCollapsed(
408
- requestId,
409
- {
410
- tasks: request.tasks,
411
- context: request.context,
412
- cwd: request.cwd,
413
- worktree: request.worktree,
414
- async: false,
415
- clarify: false,
416
- },
417
- signal,
418
- onUpdate,
419
- ctx,
420
- );
421
- }
422
- return executeSubagentCollapsed(
423
- requestId,
424
- {
425
- agent: request.agent,
426
- task: request.task,
427
- context: request.context,
428
- cwd: request.cwd,
429
- model: request.model,
430
- async: false,
431
- clarify: false,
432
- },
433
- signal,
434
- onUpdate,
435
- ctx,
436
- );
374
+ execute: (requestId, params, signal, ctx, onUpdate) =>
375
+ executeSubagentCollapsed(requestId, params, signal, onUpdate, ctx),
376
+ executeVersioned: (requestId, params, signal, ctx, onUpdate) => {
377
+ if (ctx.hasUI) ctx.ui.setToolsExpanded(false);
378
+ return executor.executeDelegated(requestId, params, signal, onUpdate, ctx);
437
379
  },
438
380
  });
439
381
 
@@ -457,6 +399,14 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
457
399
  description: buildSubagentToolDescription(config),
458
400
  parameters: SubagentParams,
459
401
 
402
+ prepareArguments(args) {
403
+ // Run friendly chain validation before pi-ai's raw TypeBox schema check
404
+ // so the model sees which property is disallowed, what is allowed, and a
405
+ // valid example instead of `chain.N: must not have additional properties`.
406
+ validateChainInput(args);
407
+ return args as never;
408
+ },
409
+
460
410
  execute(id, params, signal, onUpdate, ctx) {
461
411
  return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
462
412
  },
@@ -492,38 +442,20 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
492
442
  },
493
443
 
494
444
  renderResult(result, options, theme, context) {
495
- if (subagentResultIsRunning(result)) {
496
- ensureSubagentResultAnimation(context);
497
- } else {
498
- clearLegacyResultAnimationTimer(context);
499
- }
500
- const frame = (context.state as { frame?: number } | undefined)?.frame ?? 0;
501
- return renderSubagentResult(result, options, theme, frame);
445
+ clearLegacyResultAnimationTimer(context);
446
+ return renderSubagentResult(result, options, theme);
502
447
  },
503
448
 
504
449
  };
505
450
 
506
451
  pi.registerTool(tool);
507
452
 
508
- const waitTool: ToolDefinition<typeof WaitParams, Details> = {
509
- name: "wait",
510
- label: "Wait",
511
- description: `Block until background (async) subagent runs started in this session finish, then return.
453
+ registerWaitTool(pi, state, waitToolConfig.enabled);
512
454
 
513
- Use this after launching async subagents when you have no independent work left and must not end your turn — for example inside a skill that has to run to completion, or any non-interactive run (\`pi -p ...\`) where the whole task is a single turn and ending it would abandon the still-running children.
514
-
515
- • { } — return as soon as the FIRST active run finishes (default). Ideal for a rolling fleet: launch N, wait, spawn a replacement for the one that finished, wait again — keeping N in flight.
516
- • { all: true } — block until EVERY active run in this session is finished.
517
- • { id: "..." } — wait for one specific run (id or prefix) to finish.
518
- • { timeoutMs: 600000 } — stop waiting after N ms (the runs keep going regardless; default 30 min)
519
-
520
- wait also returns when a run needs attention (a child that went idle or blocked for a decision), not only on completion — so a stuck child never stalls the loop; the summary names the run(s) to inspect/nudge/resume/interrupt. It wakes the instant a completion or control event arrives (subscribed to Pi's event bus, with a poll fallback that reconciles crashed runners), keeps the turn alive for normal notification delivery, and resolves early if the turn is aborted.`,
521
- parameters: WaitParams,
522
- execute(_id, params, signal, _onUpdate, _ctx) {
523
- return waitForSubagents(params, signal, { state, events: pi.events });
524
- },
525
- };
526
- pi.registerTool(waitTool);
455
+ pi.on("agent_end", async (_event, ctx) => {
456
+ if (ctx.hasUI) return;
457
+ await drainOutstandingWork({ state, events: pi.events });
458
+ });
527
459
 
528
460
  registerSlashCommands(pi, state);
529
461
 
@@ -540,8 +472,6 @@ wait also returns when a run needs attention (a child that went idle or blocked
540
472
  }
541
473
  }
542
474
  }
543
- registerSubagentNotify(pi, state, { batchConfig: config.completionBatch });
544
-
545
475
  const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
546
476
  const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices as Set<string> : new Set<string>();
547
477
  globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
@@ -553,10 +483,22 @@ wait also returns when a run needs attention (a child that went idle or blocked
553
483
  details: payload as SubagentControlMessageDetails,
554
484
  });
555
485
  };
486
+ const steeringNoticeHandler = (payload: unknown) => {
487
+ handleSubagentSteeringNotice({ pi, state, details: payload as SubagentSteeringMessageDetails });
488
+ };
489
+ const asyncStartedHandler = (payload: unknown) => {
490
+ handleStarted(payload);
491
+ fleetStatus?.refresh();
492
+ };
493
+ const asyncCompleteHandler = (payload: unknown) => {
494
+ handleComplete(payload);
495
+ fleetStatus?.refresh();
496
+ };
556
497
  const eventUnsubscribes = [
557
- pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, handleStarted),
558
- pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete),
498
+ pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, asyncStartedHandler),
499
+ pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, asyncCompleteHandler),
559
500
  pi.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
501
+ pi.events.on(SUBAGENT_STEERING_NOTICE_EVENT, steeringNoticeHandler),
560
502
  rpcBridge.dispose,
561
503
  ];
562
504
  globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
@@ -565,9 +507,10 @@ wait also returns when a run needs attention (a child that went idle or blocked
565
507
  if (event.toolName !== "subagent") return;
566
508
  if (!ctx.hasUI) return;
567
509
  state.lastUiContext = ctx;
510
+ fleetStatus?.setContext(ctx);
511
+ fleetStatus?.refresh();
568
512
  if (state.asyncJobs.size > 0) {
569
- renderWidget(ctx, Array.from(state.asyncJobs.values()));
570
- ctx.ui.requestRender?.();
513
+ refreshWidget(ctx);
571
514
  ensurePoller();
572
515
  }
573
516
  });
@@ -583,11 +526,17 @@ wait also returns when a run needs attention (a child that went idle or blocked
583
526
  }
584
527
  };
585
528
 
586
- const resetSessionState = (ctx: ExtensionContext) => {
587
- ensureDefaultUserSubagentSettings(ctx.model);
529
+ const resetSessionState = (ctx: ExtensionContext, recovering: boolean) => {
588
530
  state.baseCwd = ctx.cwd;
589
531
  state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
590
- state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 };
532
+ state.parentSessionFile = ctx.sessionManager.getSessionFile();
533
+ state.subagentSpawns = {
534
+ sessionId: state.currentSessionId,
535
+ count: 0,
536
+ configuredLimit: resolveMaxSubagentSpawnsPerSession(config.maxSubagentSpawnsPerSession) ?? null,
537
+ granted: 0,
538
+ grantHistory: [],
539
+ };
591
540
  // Set SELESAI_SUBAGENT_PARENT_SESSION for permission-system forwarding.
592
541
  // Only set in the root session (the interactive UI session), not in
593
542
  // child subagent processes — children inherit the parent's value
@@ -606,16 +555,23 @@ wait also returns when a run needs attention (a child that went idle or blocked
606
555
  restoreActiveJobs(ctx);
607
556
  scheduledRunManager.bindSession(ctx);
608
557
  restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
609
- primeExistingResults();
558
+ startResultWatcher();
559
+ primeExistingResults({ triggerTurn: !recovering });
560
+ fleetStatus?.setContext(ctx);
610
561
  };
611
562
 
612
- pi.on("session_start", (_event, ctx) => {
613
- resetSessionState(ctx);
563
+ pi.on("session_start", (event, ctx) => {
564
+ const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
565
+ resetSessionState(ctx, recovering);
614
566
  rpcBridge.emitReady(ctx);
615
567
  supervisorChannel.start();
616
568
  });
617
569
 
618
570
  pi.on("session_shutdown", () => {
571
+ stopResultWatcher();
572
+ state.currentSessionId = null;
573
+ state.parentSessionFile = null;
574
+ completionNotifier.dispose();
619
575
  delete process.env[SUBAGENT_PARENT_SESSION_ENV];
620
576
  for (const unsubscribe of eventUnsubscribes) {
621
577
  try {
@@ -627,7 +583,6 @@ wait also returns when a run needs attention (a child that went idle or blocked
627
583
  if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
628
584
  delete globalStore[eventUnsubscribeStoreKey];
629
585
  }
630
- stopResultWatcher();
631
586
  scheduledRunManager.stop();
632
587
  if (state.poller) clearInterval(state.poller);
633
588
  state.poller = null;
@@ -643,6 +598,7 @@ wait also returns when a run needs attention (a child that went idle or blocked
643
598
  promptTemplateBridge.cancelAll();
644
599
  promptTemplateBridge.dispose();
645
600
  supervisorChannel.dispose();
601
+ fleetStatus?.dispose();
646
602
  if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
647
603
  delete globalStore[runtimeCleanupStoreKey];
648
604
  }
@@ -3,19 +3,27 @@ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
3
3
  import type { ExtensionContext } from "@selesai/code";
4
4
  import { Compile } from "typebox/compile";
5
5
  import { resolveAsyncRunLocation } from "../runs/background/async-resume.ts";
6
- import { deliverTimeoutRequest } from "../runs/background/control-channel.ts";
6
+ import { deliverStopRequest } from "../runs/background/control-channel.ts";
7
7
  import { reconcileAsyncRun } from "../runs/background/stale-run-reconciler.ts";
8
8
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
9
- import { type Details, ASYNC_DIR, RESULTS_DIR } from "../shared/types.ts";
9
+ import {
10
+ type Details,
11
+ ASYNC_DIR,
12
+ RESULTS_DIR,
13
+ SUBAGENT_ASYNC_COMPLETE_EVENT,
14
+ SUBAGENT_PROCESS_TERMINAL_EVENT,
15
+ SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
16
+ } from "../shared/types.ts";
10
17
  import { readStatus } from "../shared/utils.ts";
11
18
  import { SubagentParams } from "./schemas.ts";
19
+ import { validateChainInput } from "./chain-validation.ts";
12
20
 
13
21
  export const SUBAGENT_RPC_PROTOCOL_VERSION = 1;
14
22
  export const SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request";
15
23
  export const SUBAGENT_RPC_READY_EVENT = "subagents:rpc:v1:ready";
16
24
  export const SUBAGENT_RPC_REPLY_EVENT_PREFIX = "subagents:rpc:v1:reply:";
17
25
 
18
- export const SUBAGENT_RPC_METHODS = ["ping", "status", "spawn", "interrupt", "stop"] as const;
26
+ export const SUBAGENT_RPC_METHODS = ["ping", "status", "spawn", "steer", "interrupt", "stop"] as const;
19
27
  export type SubagentRpcMethod = typeof SUBAGENT_RPC_METHODS[number];
20
28
 
21
29
  export interface SubagentRpcRequestEnvelope {
@@ -111,6 +119,13 @@ function assertRecordParams(params: unknown, method: SubagentRpcMethod): Record<
111
119
  }
112
120
 
113
121
  function assertSubagentParams(params: SubagentParamsLike, label: string): void {
122
+ // Friendly chain validation first: name the disallowed property, list allowed
123
+ // ones, and show a valid example instead of raw TypeBox diagnostics.
124
+ try {
125
+ validateChainInput(params);
126
+ } catch (error) {
127
+ throw new SubagentRpcError("invalid_params", `${label}: ${error instanceof Error ? error.message : String(error)}`);
128
+ }
114
129
  if (subagentParamsValidator.Check(params)) return;
115
130
  const messages = [...subagentParamsValidator.Errors(params)]
116
131
  .slice(0, 4)
@@ -164,13 +179,18 @@ function pingData(ctx: ExtensionContext | null) {
164
179
  capabilities: {
165
180
  status: true,
166
181
  asyncSpawn: true,
182
+ steer: true,
183
+ nonRecoveringSteer: true,
167
184
  interrupt: true,
168
185
  stop: true,
186
+ processTerminalProof: { version: 1, lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION },
169
187
  },
170
188
  events: {
171
189
  ready: SUBAGENT_RPC_READY_EVENT,
172
190
  request: SUBAGENT_RPC_REQUEST_EVENT,
173
191
  replyPrefix: SUBAGENT_RPC_REPLY_EVENT_PREFIX,
192
+ asyncComplete: SUBAGENT_ASYNC_COMPLETE_EVENT,
193
+ processTerminal: SUBAGENT_PROCESS_TERMINAL_EVENT,
174
194
  },
175
195
  session: sessionData(ctx),
176
196
  };
@@ -204,6 +224,20 @@ function spawnParams(params: unknown): SubagentParamsLike {
204
224
  return { ...(input as SubagentParamsLike), async: true, clarify: false };
205
225
  }
206
226
 
227
+ function steerParams(params: unknown): SubagentParamsLike {
228
+ const input = assertRecordParams(params, "steer");
229
+ if (typeof input.message !== "string" || !input.message.trim())
230
+ throw new SubagentRpcError("invalid_params", "RPC steer requires a non-empty message.");
231
+ const target = normalizeTargetParams(input, "steer");
232
+ if (!target.id && !target.runId && !target.dir) throw new SubagentRpcError("invalid_params", "RPC steer requires id, runId, or dir.");
233
+ return {
234
+ action: "steer",
235
+ ...target,
236
+ message: input.message.trim(),
237
+ steeringRecovery: false,
238
+ };
239
+ }
240
+
207
241
  function stopAsyncRun(
208
242
  params: unknown,
209
243
  options: RegisterSubagentRpcBridgeOptions,
@@ -247,7 +281,7 @@ function stopAsyncRun(
247
281
  }
248
282
 
249
283
  try {
250
- deliverTimeoutRequest({
284
+ deliverStopRequest({
251
285
  asyncDir: location.asyncDir,
252
286
  pid: status.pid,
253
287
  kill: options.kill,
@@ -281,6 +315,9 @@ async function handleRequest(
281
315
  if (request.method === "status") {
282
316
  return executeChecked(options, ctx, request.requestId, request.method, { action: "status", ...normalizeTargetParams(request.params, "status") });
283
317
  }
318
+ if (request.method === "steer") {
319
+ return executeChecked(options, ctx, request.requestId, request.method, steerParams(request.params));
320
+ }
284
321
  if (request.method === "interrupt") {
285
322
  return executeChecked(options, ctx, request.requestId, request.method, { action: "interrupt", ...normalizeTargetParams(request.params, "interrupt") });
286
323
  }