@selesai/code 0.5.19 → 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 (265) 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-intercom/broker/spawn.test.ts +31 -0
  4. package/dist/extensions/pi-intercom/broker/spawn.ts +5 -3
  5. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  6. package/dist/extensions/pi-subagents/README.md +602 -220
  7. package/dist/extensions/pi-subagents/banner.png +0 -0
  8. package/dist/extensions/pi-subagents/index.ts +1 -0
  9. package/dist/extensions/pi-subagents/install.mjs +3 -3
  10. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  11. package/dist/extensions/pi-subagents/package.json +26 -14
  12. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  13. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  14. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  15. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  16. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  17. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  18. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  19. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  20. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  21. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  22. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  23. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  24. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  25. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  26. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  27. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  28. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  29. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  30. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  31. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  32. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  33. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  34. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  35. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  36. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  37. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  38. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  39. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  40. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  41. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  42. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  43. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  44. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  45. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  46. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  47. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  48. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  49. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  50. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  51. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  52. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  53. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  54. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  55. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  56. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  57. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  58. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  59. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  60. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  61. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  62. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  63. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  65. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  66. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  67. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  68. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  69. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  70. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  71. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  72. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  73. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  74. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  75. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  76. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  77. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  78. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  79. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  80. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  81. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  82. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  83. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  84. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  85. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  86. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  87. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  88. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  89. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  90. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  92. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  93. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  94. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  95. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  96. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  97. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  98. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  99. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  100. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  101. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  102. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  103. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  104. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  105. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  106. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  107. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  108. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  109. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  110. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  111. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  112. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  113. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  114. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  115. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  116. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  117. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  118. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  119. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  120. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  121. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  122. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  123. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  124. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  125. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  126. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  127. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  128. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  129. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  130. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  131. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  132. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  133. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  134. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  135. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  136. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  137. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  138. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  139. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  140. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  141. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  142. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  143. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  144. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  145. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  146. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  147. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  148. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  149. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  150. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  151. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  152. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  153. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  154. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  155. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  156. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  157. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  158. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  159. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  160. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  161. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  162. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  163. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  164. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  165. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  166. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  167. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  168. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  169. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  170. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  171. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  172. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  173. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  174. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  175. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  176. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  177. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  178. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  179. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  180. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  181. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  182. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  183. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  184. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  185. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  186. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  187. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  188. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  189. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  190. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  191. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  192. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  193. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  194. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  195. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  196. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  197. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  198. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  199. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  200. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  201. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  202. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  203. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  204. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  205. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  206. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  207. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  208. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  209. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  210. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  211. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  212. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  213. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  214. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  215. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  216. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  217. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  218. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  219. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  220. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  221. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  222. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  223. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  224. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  225. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  226. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  227. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  228. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  229. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  230. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  231. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  232. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  233. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  234. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  235. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  236. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  237. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  238. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  239. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  240. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  241. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  245. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  246. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  247. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  248. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  249. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  250. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  251. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  252. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  253. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  254. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  255. package/dist/extensions/question/batch.ts +39 -0
  256. package/dist/extensions/question/dialog-adapter.ts +23 -1
  257. package/dist/extensions/question/index.ts +237 -7
  258. package/dist/extensions/question/schemas.ts +28 -15
  259. package/dist/extensions/question/tests/batch.test.ts +26 -0
  260. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  261. package/dist/extensions/question/tui-adapter.ts +2 -3
  262. package/dist/extensions/question/types.ts +37 -0
  263. package/dist/extensions/question/ui-protocol.ts +3 -3
  264. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  265. package/package.json +2 -1
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { afterEach, describe, it } from "node:test";
6
- import { writeSteerRequestToDir } from "../../src/runs/background/control-channel.ts";
6
+ import { consumeSteerAcks, writeSteerRequestToDir } from "../../src/runs/background/control-channel.ts";
7
7
  import {
8
8
  SUBAGENT_CHILD_AGENT_ENV,
9
9
  SUBAGENT_CHILD_INDEX_ENV,
@@ -11,15 +11,21 @@ import {
11
11
  SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV,
12
12
  SUBAGENT_ORCHESTRATOR_TARGET_ENV,
13
13
  SUBAGENT_RUN_ID_ENV,
14
+ SUBAGENT_STEER_ACK_DIR_ENV,
15
+ SUBAGENT_STEER_CAPABILITY_ENV,
14
16
  SUBAGENT_STEER_INBOX_ENV,
15
17
  SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV,
16
18
  } from "../../src/runs/shared/pi-args.ts";
17
19
  import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from "../../src/runs/shared/structured-output.ts";
18
20
  import { TOOL_BUDGET_ENV } from "../../src/runs/shared/tool-budget.ts";
21
+ import { CHILD_TOOL_DIAGNOSTIC_PATH_ENV, formatChildToolDiagnostic, MCP_DIRECT_CHILD_TOOLS_ENV, readChildToolDiagnostic, REQUIRED_CHILD_TOOLS_ENV } from "../../src/runs/shared/tool-availability.ts";
22
+ import { CHILD_WATCHDOG_CONFIG_ENV } from "../../src/watchdog/child-status.ts";
23
+ import { SUBAGENT_WATCHDOG_WARNING_TYPE } from "../../src/watchdog/types.ts";
19
24
  import registerSubagentPromptRuntime, {
20
25
  CHILD_FANOUT_BOUNDARY_INSTRUCTIONS,
21
26
  CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS,
22
27
  SUBAGENT_INTERCOM_SESSION_NAME_ENV,
28
+ registerSteeringInbox,
23
29
  rewriteSubagentPrompt,
24
30
  stripInheritedSkills,
25
31
  stripParentOnlySubagentMessages,
@@ -33,15 +39,21 @@ const envSnapshot = {
33
39
  SELESAI_SUBAGENT_INTERCOM_SESSION_NAME: process.env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME,
34
40
  SELESAI_SUBAGENT_FANOUT_CHILD: process.env.SELESAI_SUBAGENT_FANOUT_CHILD,
35
41
  SELESAI_SUBAGENT_STEER_INBOX: process.env.SELESAI_SUBAGENT_STEER_INBOX,
42
+ SELESAI_SUBAGENT_STEER_CAPABILITY: process.env.SELESAI_SUBAGENT_STEER_CAPABILITY,
43
+ SELESAI_SUBAGENT_STEER_ACK_DIR: process.env.SELESAI_SUBAGENT_STEER_ACK_DIR,
36
44
  SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE: process.env.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE,
37
45
  SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA: process.env.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA,
38
46
  SELESAI_SUBAGENT_TOOL_BUDGET: process.env.SELESAI_SUBAGENT_TOOL_BUDGET,
47
+ SELESAI_SUBAGENT_REQUIRED_TOOLS: process.env.SELESAI_SUBAGENT_REQUIRED_TOOLS,
48
+ SELESAI_SUBAGENT_MCP_DIRECT_TOOLS: process.env.SELESAI_SUBAGENT_MCP_DIRECT_TOOLS,
49
+ SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH: process.env.SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH,
39
50
  SELESAI_SUBAGENT_ORCHESTRATOR_TARGET: process.env.SELESAI_SUBAGENT_ORCHESTRATOR_TARGET,
40
51
  SELESAI_SUBAGENT_ORCHESTRATOR_SESSION_ID: process.env.SELESAI_SUBAGENT_ORCHESTRATOR_SESSION_ID,
41
52
  SELESAI_SUBAGENT_SUPERVISOR_CHANNEL_DIR: process.env.SELESAI_SUBAGENT_SUPERVISOR_CHANNEL_DIR,
42
53
  SELESAI_SUBAGENT_RUN_ID: process.env.SELESAI_SUBAGENT_RUN_ID,
43
54
  SELESAI_SUBAGENT_CHILD_AGENT: process.env.SELESAI_SUBAGENT_CHILD_AGENT,
44
55
  SELESAI_SUBAGENT_CHILD_INDEX: process.env.SELESAI_SUBAGENT_CHILD_INDEX,
56
+ SELESAI_SUBAGENT_WATCHDOG_CHILD_CONFIG: process.env.SELESAI_SUBAGENT_WATCHDOG_CHILD_CONFIG,
45
57
  };
46
58
 
47
59
  const SKILLS_SECTION = "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n<available_skills>\n <skill>\n <name>safe-bash</name>\n <description>desc</description>\n <location>/tmp/SKILL.md</location>\n </skill>\n <skill>\n <name>pi-subagents</name>\n <description>delegate to subagents</description>\n <location>/tmp/pi-subagents/SKILL.md</location>\n </skill>\n</available_skills>";
@@ -74,12 +86,22 @@ afterEach(() => {
74
86
  else process.env.SELESAI_SUBAGENT_FANOUT_CHILD = envSnapshot.SELESAI_SUBAGENT_FANOUT_CHILD;
75
87
  if (envSnapshot.SELESAI_SUBAGENT_STEER_INBOX === undefined) delete process.env[SUBAGENT_STEER_INBOX_ENV];
76
88
  else process.env[SUBAGENT_STEER_INBOX_ENV] = envSnapshot.SELESAI_SUBAGENT_STEER_INBOX;
89
+ if (envSnapshot.SELESAI_SUBAGENT_STEER_CAPABILITY === undefined) delete process.env[SUBAGENT_STEER_CAPABILITY_ENV];
90
+ else process.env[SUBAGENT_STEER_CAPABILITY_ENV] = envSnapshot.SELESAI_SUBAGENT_STEER_CAPABILITY;
91
+ if (envSnapshot.SELESAI_SUBAGENT_STEER_ACK_DIR === undefined) delete process.env[SUBAGENT_STEER_ACK_DIR_ENV];
92
+ else process.env[SUBAGENT_STEER_ACK_DIR_ENV] = envSnapshot.SELESAI_SUBAGENT_STEER_ACK_DIR;
77
93
  if (envSnapshot.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE === undefined) delete process.env[STRUCTURED_OUTPUT_CAPTURE_ENV];
78
94
  else process.env[STRUCTURED_OUTPUT_CAPTURE_ENV] = envSnapshot.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE;
79
95
  if (envSnapshot.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA === undefined) delete process.env[STRUCTURED_OUTPUT_SCHEMA_ENV];
80
96
  else process.env[STRUCTURED_OUTPUT_SCHEMA_ENV] = envSnapshot.SELESAI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA;
81
97
  if (envSnapshot.SELESAI_SUBAGENT_TOOL_BUDGET === undefined) delete process.env[TOOL_BUDGET_ENV];
82
98
  else process.env[TOOL_BUDGET_ENV] = envSnapshot.SELESAI_SUBAGENT_TOOL_BUDGET;
99
+ if (envSnapshot.SELESAI_SUBAGENT_REQUIRED_TOOLS === undefined) delete process.env[REQUIRED_CHILD_TOOLS_ENV];
100
+ else process.env[REQUIRED_CHILD_TOOLS_ENV] = envSnapshot.SELESAI_SUBAGENT_REQUIRED_TOOLS;
101
+ if (envSnapshot.SELESAI_SUBAGENT_MCP_DIRECT_TOOLS === undefined) delete process.env[MCP_DIRECT_CHILD_TOOLS_ENV];
102
+ else process.env[MCP_DIRECT_CHILD_TOOLS_ENV] = envSnapshot.SELESAI_SUBAGENT_MCP_DIRECT_TOOLS;
103
+ if (envSnapshot.SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH === undefined) delete process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV];
104
+ else process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = envSnapshot.SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH;
83
105
  if (envSnapshot.SELESAI_SUBAGENT_ORCHESTRATOR_TARGET === undefined) delete process.env[SUBAGENT_ORCHESTRATOR_TARGET_ENV];
84
106
  else process.env[SUBAGENT_ORCHESTRATOR_TARGET_ENV] = envSnapshot.SELESAI_SUBAGENT_ORCHESTRATOR_TARGET;
85
107
  if (envSnapshot.SELESAI_SUBAGENT_ORCHESTRATOR_SESSION_ID === undefined) delete process.env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV];
@@ -92,6 +114,8 @@ afterEach(() => {
92
114
  else process.env[SUBAGENT_CHILD_AGENT_ENV] = envSnapshot.SELESAI_SUBAGENT_CHILD_AGENT;
93
115
  if (envSnapshot.SELESAI_SUBAGENT_CHILD_INDEX === undefined) delete process.env[SUBAGENT_CHILD_INDEX_ENV];
94
116
  else process.env[SUBAGENT_CHILD_INDEX_ENV] = envSnapshot.SELESAI_SUBAGENT_CHILD_INDEX;
117
+ if (envSnapshot.SELESAI_SUBAGENT_WATCHDOG_CHILD_CONFIG === undefined) delete process.env[CHILD_WATCHDOG_CONFIG_ENV];
118
+ else process.env[CHILD_WATCHDOG_CONFIG_ENV] = envSnapshot.SELESAI_SUBAGENT_WATCHDOG_CHILD_CONFIG;
95
119
  });
96
120
 
97
121
  function setSupervisorEnv(): void {
@@ -131,6 +155,40 @@ describe("subagent prompt runtime", () => {
131
155
  assert.equal(toolCall({ toolName: "write" }), undefined);
132
156
  });
133
157
 
158
+ it("registers the native canonical steering inbox path", () => {
159
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-steering-watch-runtime-"));
160
+ try {
161
+ const inbox = path.join(dir, "steer");
162
+ const nativeInbox = path.join(dir, "native-steer");
163
+ process.env[SUBAGENT_STEER_INBOX_ENV] = inbox;
164
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
165
+ let watchedDir: fs.PathLike | undefined;
166
+ const fakeWatcher = { on() { return fakeWatcher; }, close() {} } as fs.FSWatcher;
167
+
168
+ registerSteeringInbox({
169
+ on(event: string, handler: (payload?: unknown) => unknown) {
170
+ handlers.set(event, handler);
171
+ },
172
+ sendUserMessage() {},
173
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; sendUserMessage(): void }, {
174
+ nativeRealpath(target) {
175
+ assert.equal(target, inbox);
176
+ return nativeInbox;
177
+ },
178
+ watch: ((target: fs.PathLike) => {
179
+ watchedDir = target;
180
+ return fakeWatcher;
181
+ }) as typeof fs.watch,
182
+ });
183
+
184
+ handlers.get("session_start")?.({});
185
+ assert.equal(watchedDir, nativeInbox);
186
+ handlers.get("session_shutdown")?.({});
187
+ } finally {
188
+ fs.rmSync(dir, { recursive: true, force: true });
189
+ }
190
+ });
191
+
134
192
  it("delivers steering inbox requests as mid-run user messages", () => {
135
193
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-steering-runtime-"));
136
194
  try {
@@ -162,6 +220,122 @@ describe("subagent prompt runtime", () => {
162
220
  }
163
221
  });
164
222
 
223
+ it("does not acknowledge sendUserMessage until the correlated Pi input event arrives", () => {
224
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-steering-ack-runtime-"));
225
+ try {
226
+ const inbox = path.join(dir, "inbox");
227
+ const capability = path.join(dir, "capability.json");
228
+ const ackDir = path.join(dir, "control", "steer-acks", "0");
229
+ process.env[SUBAGENT_STEER_INBOX_ENV] = inbox;
230
+ process.env[SUBAGENT_STEER_CAPABILITY_ENV] = capability;
231
+ process.env[SUBAGENT_STEER_ACK_DIR_ENV] = ackDir;
232
+ process.env[SUBAGENT_CHILD_INDEX_ENV] = "0";
233
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
234
+ const sent: string[] = [];
235
+ registerSteeringInbox({
236
+ on(event: string, handler: (payload?: unknown) => unknown) { handlers.set(event, handler); },
237
+ sendUserMessage(content: string) { sent.push(content); },
238
+ } as never);
239
+ handlers.get("session_start")?.({});
240
+ writeSteerRequestToDir(inbox, { type: "steer", id: "first", ts: 1, message: "Focus on tests." });
241
+ handlers.get("message_start")?.({});
242
+ assert.equal(sent.length, 1);
243
+ assert.deepEqual(fs.existsSync(ackDir) ? fs.readdirSync(ackDir) : [], []);
244
+ handlers.get("input")?.({ source: "extension", streamingBehavior: "steer", text: sent[0] });
245
+ const acks = consumeSteerAcks(dir);
246
+ assert.equal(acks.length, 1);
247
+ assert.equal(acks[0]?.requestId, "first");
248
+ assert.equal(acks[0]?.state, "delivered");
249
+ assert.equal(acks[0]?.message, "Pi accepted the correlated steering input.");
250
+ handlers.get("session_shutdown")?.({});
251
+ } finally {
252
+ fs.rmSync(dir, { recursive: true, force: true });
253
+ }
254
+ });
255
+
256
+ it("correlates duplicate guidance FIFO without a visible marker", () => {
257
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-steering-fifo-runtime-"));
258
+ try {
259
+ const inbox = path.join(dir, "inbox");
260
+ process.env[SUBAGENT_STEER_INBOX_ENV] = inbox;
261
+ process.env[SUBAGENT_STEER_CAPABILITY_ENV] = path.join(dir, "capability.json");
262
+ process.env[SUBAGENT_STEER_ACK_DIR_ENV] = path.join(dir, "control", "steer-acks", "0");
263
+ process.env[SUBAGENT_CHILD_INDEX_ENV] = "0";
264
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
265
+ const sent: string[] = [];
266
+ registerSteeringInbox({ on(event: string, handler: (payload?: unknown) => unknown) { handlers.set(event, handler); }, sendUserMessage(content: string) { sent.push(content); } } as never);
267
+ handlers.get("session_start")?.({});
268
+ writeSteerRequestToDir(inbox, { type: "steer", id: "one", ts: 1, message: "same guidance" });
269
+ writeSteerRequestToDir(inbox, { type: "steer", id: "two", ts: 2, message: "same guidance" });
270
+ handlers.get("message_start")?.({});
271
+ handlers.get("input")?.({ source: "extension", streamingBehavior: "steer", text: sent[0] });
272
+ handlers.get("input")?.({ source: "extension", streamingBehavior: "steer", text: sent[1] });
273
+ assert.deepEqual(consumeSteerAcks(dir).map((ack) => ack.requestId), ["one", "two"]);
274
+ handlers.get("session_shutdown")?.({});
275
+ } finally {
276
+ fs.rmSync(dir, { recursive: true, force: true });
277
+ }
278
+ });
279
+
280
+ it("publishes an unsupported capability and failed acknowledgments without sendUserMessage", () => {
281
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-steering-unsupported-runtime-"));
282
+ try {
283
+ const inbox = path.join(dir, "inbox");
284
+ process.env[SUBAGENT_STEER_INBOX_ENV] = inbox;
285
+ process.env[SUBAGENT_STEER_CAPABILITY_ENV] = path.join(dir, "capability.json");
286
+ process.env[SUBAGENT_STEER_ACK_DIR_ENV] = path.join(dir, "control", "steer-acks", "0");
287
+ process.env[SUBAGENT_CHILD_INDEX_ENV] = "0";
288
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
289
+ registerSteeringInbox({ on(event: string, handler: (payload?: unknown) => unknown) { handlers.set(event, handler); } } as never);
290
+ handlers.get("session_start")?.({});
291
+ assert.equal(JSON.parse(fs.readFileSync(path.join(dir, "capability.json"), "utf-8")).supported, false);
292
+ writeSteerRequestToDir(inbox, { type: "steer", id: "unsupported", ts: 1, message: "guidance" });
293
+ handlers.get("message_start")?.({});
294
+ assert.equal(consumeSteerAcks(dir)[0]?.state, "failed");
295
+ handlers.get("session_shutdown")?.({});
296
+ } finally {
297
+ fs.rmSync(dir, { recursive: true, force: true });
298
+ }
299
+ });
300
+
301
+ it("registers child watchdog lifecycle handlers only when enabled by env", () => {
302
+ const handlersWithout = new Map<string, unknown[]>();
303
+ registerSubagentPromptRuntime({
304
+ on(event: string, handler: unknown) {
305
+ handlersWithout.set(event, [...(handlersWithout.get(event) ?? []), handler]);
306
+ },
307
+ } as { on(event: string, handler: unknown): void });
308
+ assert.equal(handlersWithout.get("agent_end")?.length ?? 0, 1, "headless auto-drain is always registered");
309
+
310
+ process.env[CHILD_WATCHDOG_CONFIG_ENV] = JSON.stringify({
311
+ enabled: true,
312
+ runId: "run-1",
313
+ agent: "worker",
314
+ childIndex: 0,
315
+ watchdogTailTimeoutMs: 1000,
316
+ agentEndTimeoutMs: 500,
317
+ maxWarnings: null,
318
+ lsp: { enabled: false, timeoutMs: 3000, maxFiles: 20, maxDiagnostics: 50 },
319
+ autoFollowBlockers: false,
320
+ autoFollowMaxAttempts: 3,
321
+ stalemateRepeats: 2,
322
+ });
323
+ const handlersWith = new Map<string, unknown[]>();
324
+ registerSubagentPromptRuntime({
325
+ on(event: string, handler: unknown) {
326
+ handlersWith.set(event, [...(handlersWith.get(event) ?? []), handler]);
327
+ },
328
+ getThinkingLevel() {
329
+ return "off";
330
+ },
331
+ sendMessage() {},
332
+ } as { on(event: string, handler: unknown): void; getThinkingLevel(): string; sendMessage(): void });
333
+
334
+ assert.ok((handlersWith.get("before_agent_start")?.length ?? 0) >= 2);
335
+ assert.ok((handlersWith.get("turn_end")?.length ?? 0) >= 1);
336
+ assert.ok((handlersWith.get("agent_end")?.length ?? 0) >= 2, "watchdog and auto-drain both observe agent_end");
337
+ });
338
+
165
339
  it("registered structured_output tool accepts valid schema output and writes the capture file", async () => {
166
340
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-structured-runtime-"));
167
341
  try {
@@ -171,15 +345,25 @@ describe("subagent prompt runtime", () => {
171
345
  process.env[STRUCTURED_OUTPUT_SCHEMA_ENV] = schemaPath;
172
346
  process.env[STRUCTURED_OUTPUT_CAPTURE_ENV] = outputPath;
173
347
  let execute: ((_id: string, params: { value: unknown }) => Promise<{ terminate?: boolean }>) | undefined;
348
+ let parameters: unknown;
174
349
 
175
350
  registerSubagentPromptRuntime({
176
- registerTool(tool: { name: string; execute: (_id: string, params: { value: unknown }) => Promise<{ terminate?: boolean }> }) {
177
- if (tool.name === "structured_output") execute = tool.execute;
351
+ registerTool(tool: { name: string; parameters: unknown; execute: (_id: string, params: { value: unknown }) => Promise<{ terminate?: boolean }> }) {
352
+ if (tool.name === "structured_output") {
353
+ execute = tool.execute;
354
+ parameters = tool.parameters;
355
+ }
178
356
  },
179
357
  on() {},
180
- } as { registerTool(tool: { name: string; execute: (_id: string, params: { value: unknown }) => Promise<{ terminate?: boolean }> }): void; on(): void });
358
+ } as { registerTool(tool: { name: string; parameters: unknown; execute: (_id: string, params: { value: unknown }) => Promise<{ terminate?: boolean }> }): void; on(): void });
181
359
 
182
360
  assert.ok(execute, "structured_output tool should be registered");
361
+ assert.deepEqual(parameters, {
362
+ type: "object",
363
+ properties: { value: { type: "object", required: ["ok"], properties: { ok: { type: "boolean" } } } },
364
+ required: ["value"],
365
+ additionalProperties: false,
366
+ });
183
367
  const result = await execute("tool-1", { value: { ok: true } });
184
368
  assert.equal(result.terminate, true);
185
369
  assert.deepEqual(JSON.parse(fs.readFileSync(outputPath, "utf-8")), { ok: true });
@@ -188,6 +372,40 @@ describe("subagent prompt runtime", () => {
188
372
  }
189
373
  });
190
374
 
375
+ it("scopes local structured_output schema refs under the value parameter", () => {
376
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-structured-refs-"));
377
+ try {
378
+ const schemaPath = path.join(dir, "schema.json");
379
+ const outputPath = path.join(dir, "output.json");
380
+ fs.writeFileSync(schemaPath, JSON.stringify({
381
+ $defs: { item: { type: "string" } },
382
+ type: "object",
383
+ properties: {
384
+ name: { $ref: "#/$defs/item" },
385
+ nested: {
386
+ type: "object",
387
+ properties: { label: { $ref: "#/$defs/item" } },
388
+ },
389
+ },
390
+ }), "utf-8");
391
+ process.env[STRUCTURED_OUTPUT_SCHEMA_ENV] = schemaPath;
392
+ process.env[STRUCTURED_OUTPUT_CAPTURE_ENV] = outputPath;
393
+ let parameters = {} as { properties?: { value?: { properties?: { name?: { $ref?: string }; nested?: { properties?: { label?: { $ref?: string } } } } } } };
394
+
395
+ registerSubagentPromptRuntime({
396
+ registerTool(tool: { name: string; parameters: unknown }) {
397
+ if (tool.name === "structured_output") parameters = tool.parameters as typeof parameters;
398
+ },
399
+ on() {},
400
+ } as { registerTool(tool: { name: string; parameters: unknown }): void; on(): void });
401
+
402
+ assert.equal(parameters.properties?.value?.properties?.name?.$ref, "#/properties/value/$defs/item");
403
+ assert.equal(parameters.properties?.value?.properties?.nested?.properties?.label?.$ref, "#/properties/value/$defs/item");
404
+ } finally {
405
+ fs.rmSync(dir, { recursive: true, force: true });
406
+ }
407
+ });
408
+
191
409
  it("strips only the project context block", () => {
192
410
  const rewritten = stripProjectContext(BASE_PROMPT);
193
411
  assert.ok(!rewritten.includes("# Project Context"));
@@ -310,9 +528,11 @@ describe("subagent prompt runtime", () => {
310
528
  const slashTextResult = { role: "custom", customType: "subagent-slash-text-result", content: "Subagent profiles" };
311
529
  const notify = { role: "custom", customType: "subagent-notify", content: "Background task completed" };
312
530
  const control = { role: "custom", customType: "subagent_control_notice", content: "needs attention" };
531
+ const watchdogWarning = { role: "custom", customType: SUBAGENT_WATCHDOG_WARNING_TYPE, content: "<subagent_watchdog>parent-only</subagent_watchdog>" };
532
+ const childWatchdogWarning = { role: "custom", customType: SUBAGENT_WATCHDOG_WARNING_TYPE, content: "<subagent_watchdog>child-visible</subagent_watchdog>", details: { source: "child" } };
313
533
  const otherCustom = { role: "custom", customType: "other", content: "keep" };
314
534
 
315
- assert.deepEqual(stripParentOnlySubagentMessages([user, instruction, slashResult, slashTextResult, notify, control, otherCustom]), [user, otherCustom]);
535
+ assert.deepEqual(stripParentOnlySubagentMessages([user, instruction, slashResult, slashTextResult, notify, control, watchdogWarning, childWatchdogWarning, otherCustom]), [user, otherCustom]);
316
536
  });
317
537
 
318
538
  it("strips prior parent subagent tool calls and results from forked child context", () => {
@@ -373,10 +593,40 @@ describe("subagent prompt runtime", () => {
373
593
  },
374
594
  } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(tool: { name: string }): void });
375
595
 
376
- assert.deepEqual(registered, []);
596
+ assert.deepEqual(registered, ["subagent_wait"]);
377
597
  handlers.get("session_start")?.({});
378
598
  await handlers.get("before_agent_start")?.({ systemPrompt: BASE_PROMPT });
379
- assert.deepEqual(registered, []);
599
+ assert.deepEqual(registered, ["subagent_wait"]);
600
+ });
601
+
602
+ it("registers native intercom before the final strict allowlist check", () => {
603
+ setSupervisorEnv();
604
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-intercom-diagnostic-"));
605
+ try {
606
+ const diagnosticPath = path.join(dir, "tools.json");
607
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
608
+ const registered: string[] = [];
609
+ process.env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(["read", "grep", "find", "ls", "bash", "edit", "write", "intercom"]);
610
+ process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = diagnosticPath;
611
+ process.env[SUBAGENT_CHILD_AGENT_ENV] = "scout";
612
+
613
+ registerSubagentPromptRuntime({
614
+ on(event: string, handler: (payload?: unknown) => unknown) {
615
+ handlers.set(event, handler);
616
+ },
617
+ getAllTools: () => registered.map((name) => ({ name })),
618
+ registerTool(tool: { name: string }) {
619
+ registered.push(tool.name);
620
+ },
621
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(tool: { name: string }): void });
622
+
623
+ handlers.get("session_start")?.({});
624
+ assert.deepEqual(registered, ["subagent_wait", "contact_supervisor", "intercom"]);
625
+ handlers.get("agent_start")?.({});
626
+ assert.equal(fs.existsSync(diagnosticPath), false);
627
+ } finally {
628
+ fs.rmSync(dir, { recursive: true, force: true });
629
+ }
380
630
  });
381
631
 
382
632
  it("keeps installed pi-intercom while filling only a missing child contact_supervisor tool", async () => {
@@ -397,29 +647,139 @@ describe("subagent prompt runtime", () => {
397
647
  handlers.get("session_start")?.({});
398
648
  await handlers.get("before_agent_start")?.({ systemPrompt: BASE_PROMPT });
399
649
 
400
- assert.deepEqual(registered, ["contact_supervisor"]);
650
+ assert.deepEqual(registered, ["subagent_wait", "contact_supervisor"]);
401
651
  });
402
652
 
403
653
  it("registers native supervisor tools at runtime when pi-intercom is absent", async () => {
404
654
  setSupervisorEnv();
655
+ const previousRequiredTools = process.env[REQUIRED_CHILD_TOOLS_ENV];
656
+ delete process.env[REQUIRED_CHILD_TOOLS_ENV];
405
657
  const handlers = new Map<string, (payload?: unknown) => unknown>();
406
658
  const registered: string[] = [];
407
659
 
408
- registerSubagentPromptRuntime({
409
- on(event: string, handler: (payload?: unknown) => unknown) {
410
- handlers.set(event, handler);
411
- },
412
- getAllTools: () => registered.map((name) => ({ name })),
413
- registerTool(tool: { name: string }) {
414
- registered.push(tool.name);
415
- },
416
- } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(tool: { name: string }): void });
660
+ try {
661
+ registerSubagentPromptRuntime({
662
+ on(event: string, handler: (payload?: unknown) => unknown) {
663
+ handlers.set(event, handler);
664
+ },
665
+ getAllTools: () => registered.map((name) => ({ name })),
666
+ registerTool(tool: { name: string }) {
667
+ registered.push(tool.name);
668
+ },
669
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(tool: { name: string }): void });
417
670
 
418
- handlers.get("session_start")?.({});
419
- assert.deepEqual(registered, ["contact_supervisor"]);
671
+ handlers.get("session_start")?.({});
672
+ assert.deepEqual(registered, ["subagent_wait", "contact_supervisor"]);
420
673
 
421
- await handlers.get("before_agent_start")?.({ systemPrompt: BASE_PROMPT });
422
- assert.deepEqual(registered, ["contact_supervisor", "intercom"]);
674
+ await handlers.get("before_agent_start")?.({ systemPrompt: BASE_PROMPT });
675
+ assert.deepEqual(registered, ["subagent_wait", "contact_supervisor", "intercom"]);
676
+ } finally {
677
+ if (previousRequiredTools === undefined) delete process.env[REQUIRED_CHILD_TOOLS_ENV];
678
+ else process.env[REQUIRED_CHILD_TOOLS_ENV] = previousRequiredTools;
679
+ }
680
+ });
681
+
682
+ it("records requested tools missing from the child registry after startup hooks settle", async () => {
683
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-tool-diagnostic-"));
684
+ try {
685
+ const diagnosticPath = path.join(dir, "tools.json");
686
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
687
+ const available = ["read"];
688
+ process.env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(["read", "fixture_search"]);
689
+ process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = diagnosticPath;
690
+ process.env[SUBAGENT_CHILD_AGENT_ENV] = "extension-worker";
691
+
692
+ registerSubagentPromptRuntime({
693
+ on(event: string, handler: (payload?: unknown) => unknown) {
694
+ handlers.set(event, handler);
695
+ },
696
+ getAllTools: () => available.map((name) => ({ name })),
697
+ registerTool() {},
698
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(): void });
699
+
700
+ const promptRewrite = await handlers.get("before_agent_start")?.({ systemPrompt: BASE_PROMPT }) as { systemPrompt?: string } | undefined;
701
+ assert.equal(fs.existsSync(diagnosticPath), false);
702
+ assert.doesNotMatch(promptRewrite?.systemPrompt ?? "", /requested unavailable child tools/);
703
+
704
+ handlers.get("agent_start")?.({});
705
+ assert.deepEqual(readChildToolDiagnostic(diagnosticPath), {
706
+ agent: "extension-worker",
707
+ required: ["read", "fixture_search"],
708
+ available: ["read"],
709
+ missing: ["fixture_search"],
710
+ });
711
+
712
+ available.push("fixture_search");
713
+ handlers.get("agent_start")?.({});
714
+ assert.equal(fs.existsSync(diagnosticPath), false);
715
+ } finally {
716
+ fs.rmSync(dir, { recursive: true, force: true });
717
+ }
718
+ });
719
+
720
+ it("ignores malformed inherited MCP metadata before strict availability diagnostics", () => {
721
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-stale-mcp-tool-diagnostic-"));
722
+ try {
723
+ const diagnosticPath = path.join(dir, "tools.json");
724
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
725
+ process.env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(["read", "fixture_search"]);
726
+ process.env[MCP_DIRECT_CHILD_TOOLS_ENV] = "not-json";
727
+ process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = diagnosticPath;
728
+ process.env[SUBAGENT_CHILD_AGENT_ENV] = "worker";
729
+
730
+ registerSubagentPromptRuntime({
731
+ on(event: string, handler: (payload?: unknown) => unknown) {
732
+ handlers.set(event, handler);
733
+ },
734
+ getAllTools: () => [{ name: "read" }],
735
+ registerTool() {},
736
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(): void });
737
+
738
+ assert.doesNotThrow(() => handlers.get("agent_start")?.({}));
739
+ assert.deepEqual(readChildToolDiagnostic(diagnosticPath), {
740
+ agent: "worker",
741
+ required: ["read", "fixture_search"],
742
+ available: ["read"],
743
+ missing: ["fixture_search"],
744
+ });
745
+ } finally {
746
+ fs.rmSync(dir, { recursive: true, force: true });
747
+ }
748
+ });
749
+
750
+ it("classifies missing resolved MCP direct tools without softening strict diagnostics", () => {
751
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-mcp-tool-diagnostic-"));
752
+ try {
753
+ const diagnosticPath = path.join(dir, "tools.json");
754
+ const handlers = new Map<string, (payload?: unknown) => unknown>();
755
+ process.env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(["read", "rust_symbols_workspace_symbols", "fixture_search"]);
756
+ process.env[MCP_DIRECT_CHILD_TOOLS_ENV] = JSON.stringify(["rust_symbols_workspace_symbols"]);
757
+ process.env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = diagnosticPath;
758
+ process.env[SUBAGENT_CHILD_AGENT_ENV] = "worker";
759
+
760
+ registerSubagentPromptRuntime({
761
+ on(event: string, handler: (payload?: unknown) => unknown) {
762
+ handlers.set(event, handler);
763
+ },
764
+ getAllTools: () => [{ name: "read" }],
765
+ registerTool() {},
766
+ } as { on(event: string, handler: (payload?: unknown) => unknown): void; getAllTools(): Array<{ name: string }>; registerTool(): void });
767
+
768
+ handlers.get("agent_start")?.({});
769
+ const diagnostic = readChildToolDiagnostic(diagnosticPath);
770
+ assert.deepEqual(diagnostic, {
771
+ agent: "worker",
772
+ required: ["read", "rust_symbols_workspace_symbols", "fixture_search"],
773
+ available: ["read"],
774
+ missing: ["rust_symbols_workspace_symbols", "fixture_search"],
775
+ missingMcpDirectTools: ["rust_symbols_workspace_symbols"],
776
+ });
777
+ assert.match(formatChildToolDiagnostic(diagnostic!), /host\/pi-mcp-adapter registration problem/);
778
+ assert.match(formatChildToolDiagnostic(diagnostic!), /fixture_search/);
779
+ assert.equal(fs.existsSync(diagnosticPath), true);
780
+ } finally {
781
+ fs.rmSync(dir, { recursive: true, force: true });
782
+ }
423
783
  });
424
784
 
425
785
  it("sets the child intercom session name from env during agent startup", async () => {
@@ -431,10 +791,11 @@ describe("subagent prompt runtime", () => {
431
791
  on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>) {
432
792
  if (event === "before_agent_start") beforeAgentStart = handler;
433
793
  },
794
+ getAllTools: () => [{ name: "intercom" }, { name: "contact_supervisor" }],
434
795
  setSessionName(name: string) {
435
796
  sessionName = name;
436
797
  },
437
- } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void; setSessionName(name: string): void });
798
+ } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void; getAllTools(): Array<{ name: string }>; setSessionName(name: string): void });
438
799
 
439
800
  await beforeAgentStart?.({ systemPrompt: BASE_PROMPT });
440
801
 
@@ -447,7 +808,8 @@ describe("subagent prompt runtime", () => {
447
808
  on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>) {
448
809
  if (event === "before_agent_start") beforeAgentStart = handler;
449
810
  },
450
- } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void });
811
+ getAllTools: () => [{ name: "intercom" }, { name: "contact_supervisor" }],
812
+ } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void; getAllTools(): Array<{ name: string }> });
451
813
 
452
814
  assert.ok(beforeAgentStart, "expected before_agent_start handler");
453
815
  process.env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT = "0";
@@ -466,7 +828,8 @@ describe("subagent prompt runtime", () => {
466
828
  on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>) {
467
829
  if (event === "before_agent_start") beforeAgentStart = handler;
468
830
  },
469
- } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void });
831
+ getAllTools: () => [{ name: "intercom" }, { name: "contact_supervisor" }],
832
+ } as { on(event: string, handler: (payload: { systemPrompt: string }) => Promise<{ systemPrompt: string } | undefined>): void; getAllTools(): Array<{ name: string }> });
470
833
 
471
834
  process.env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT = "1";
472
835
  process.env.SELESAI_SUBAGENT_INHERIT_SKILLS = "1";
@@ -491,9 +854,11 @@ describe("subagent prompt runtime", () => {
491
854
  const slashResult = { role: "custom", customType: "subagent-slash-result", content: "## Orchestration" };
492
855
  const subagentResult = { role: "toolResult", toolName: "subagent", content: "subagent results" };
493
856
  const subagentCall = { role: "assistant", content: [{ type: "toolCall", name: "subagent", input: { agent: "worker" } }] };
857
+ const watchdogWarning = { role: "custom", customType: SUBAGENT_WATCHDOG_WARNING_TYPE, content: "<subagent_watchdog>parent-only</subagent_watchdog>" };
858
+ const childWatchdogWarning = { role: "custom", customType: SUBAGENT_WATCHDOG_WARNING_TYPE, content: "<subagent_watchdog>child-visible</subagent_watchdog>", details: { source: "child" } };
494
859
  const otherCustom = { role: "custom", customType: "other", content: "keep" };
495
860
 
496
- assert.deepEqual(contextHandler?.({ messages: [priorParentTurn, instruction, slashResult, subagentCall, subagentResult, otherCustom, currentTask] }), {
861
+ assert.deepEqual(contextHandler?.({ messages: [priorParentTurn, instruction, slashResult, subagentCall, subagentResult, watchdogWarning, childWatchdogWarning, otherCustom, currentTask] }), {
497
862
  messages: [priorParentTurn, otherCustom, currentTask],
498
863
  });
499
864
  });