@selesai/code 0.5.20 → 0.5.22

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 (302) hide show
  1. package/README.md +1 -1
  2. package/dist/core/agent-session-skill-block.test.d.ts +2 -0
  3. package/dist/core/agent-session-skill-block.test.d.ts.map +1 -0
  4. package/dist/core/agent-session-skill-block.test.js +20 -0
  5. package/dist/core/agent-session-skill-block.test.js.map +1 -0
  6. package/dist/core/agent-session.d.ts +7 -4
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +22 -12
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/skills.d.ts.map +1 -1
  11. package/dist/core/skills.js +1 -1
  12. package/dist/core/skills.js.map +1 -1
  13. package/dist/extensions/handoff-new.test.ts +8 -9
  14. package/dist/extensions/handoff-new.ts +6 -8
  15. package/dist/extensions/inline-skills.test.ts +96 -0
  16. package/dist/extensions/inline-skills.ts +92 -0
  17. package/dist/extensions/package.json +1 -0
  18. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +39 -0
  19. package/dist/extensions/pi-powerline-footer/tps.ts +172 -113
  20. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  21. package/dist/extensions/pi-subagents/README.md +602 -220
  22. package/dist/extensions/pi-subagents/banner.png +0 -0
  23. package/dist/extensions/pi-subagents/index.ts +1 -0
  24. package/dist/extensions/pi-subagents/install.mjs +3 -3
  25. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  26. package/dist/extensions/pi-subagents/package.json +26 -14
  27. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  28. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  29. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  30. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  31. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  32. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  33. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  34. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  35. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  36. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  37. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  38. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  39. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  40. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  41. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  42. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  43. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  44. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  45. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  46. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  47. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  48. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  49. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  50. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  51. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  52. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  53. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  54. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  55. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  56. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  57. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  58. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  59. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  60. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  61. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  62. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  63. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  65. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  66. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  67. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  68. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  69. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  70. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  71. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  72. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  73. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  74. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  75. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  76. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  77. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  78. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  79. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  80. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  81. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  82. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  83. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  84. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  85. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  86. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  87. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  88. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  89. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  90. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  92. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  93. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  94. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  95. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  97. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  98. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  99. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  100. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  101. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  102. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  103. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  104. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  105. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  106. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  107. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  108. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  109. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  110. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  111. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  112. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  113. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  114. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  115. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  116. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  117. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  118. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  119. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  120. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  121. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  122. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  123. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  124. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  125. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  126. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  127. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  128. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  129. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  130. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  131. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  132. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  133. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  134. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  135. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  136. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  137. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  138. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  139. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  140. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  141. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  142. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  143. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  144. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  145. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  146. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  147. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  148. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  149. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  150. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  151. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  152. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  153. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  154. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  155. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  156. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  157. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  158. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  159. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  160. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  161. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  162. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  163. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  164. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  165. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  166. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  167. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  168. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  169. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  170. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  171. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  172. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  173. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  174. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  175. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  176. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  177. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  178. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  179. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  180. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  181. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  182. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  183. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  184. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  185. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  186. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  187. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  188. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  189. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  190. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  191. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  192. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  193. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  194. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  195. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  196. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  197. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  198. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  199. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  200. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  201. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  202. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  203. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  204. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  205. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  206. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  207. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  208. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  209. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  210. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  211. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  212. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  213. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  214. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  215. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  216. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  217. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  218. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  219. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  220. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  221. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  222. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  223. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  224. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  225. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  226. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  227. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  228. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  229. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  230. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  231. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  232. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  233. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  234. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  235. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  236. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  237. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  238. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  239. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  240. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  241. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  242. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  243. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  244. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  245. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  246. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  247. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  248. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  249. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  250. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  251. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  252. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  253. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  254. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  255. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  256. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  257. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  258. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  259. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  260. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  261. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  262. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  263. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  264. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  265. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  266. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  267. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  268. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  269. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  270. package/dist/extensions/question/batch.ts +103 -0
  271. package/dist/extensions/question/constants.ts +1 -1
  272. package/dist/extensions/question/dialog-adapter.ts +23 -1
  273. package/dist/extensions/question/helpers.ts +34 -78
  274. package/dist/extensions/question/index.ts +412 -419
  275. package/dist/extensions/question/package.json +2 -1
  276. package/dist/extensions/question/question-list.ts +21 -14
  277. package/dist/extensions/question/row-layout.ts +2 -2
  278. package/dist/extensions/question/schemas.ts +38 -39
  279. package/dist/extensions/question/selection-mode.ts +5 -7
  280. package/dist/extensions/question/tests/batch.test.ts +75 -0
  281. package/dist/extensions/question/tests/helpers.test.ts +31 -156
  282. package/dist/extensions/question/tests/question-list.test.ts +17 -3
  283. package/dist/extensions/question/tests/row-layout.test.ts +4 -4
  284. package/dist/extensions/question/tests/schemas.test.ts +30 -0
  285. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  286. package/dist/extensions/question/tests/wizard.test.ts +99 -0
  287. package/dist/extensions/question/tui-adapter.ts +2 -3
  288. package/dist/extensions/question/types.ts +75 -39
  289. package/dist/extensions/question/ui-protocol.ts +3 -3
  290. package/dist/extensions/workflow/adapter.ts +4 -76
  291. package/dist/index.d.ts +1 -1
  292. package/dist/index.d.ts.map +1 -1
  293. package/dist/index.js +1 -1
  294. package/dist/index.js.map +1 -1
  295. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  296. package/dist/modes/interactive/interactive-mode.js +10 -10
  297. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  298. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  299. package/dist/skills/workflow-creation/SKILL.md +2 -2
  300. package/docs/skills.md +8 -0
  301. package/docs/workflows.md +4 -4
  302. package/package.json +1 -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
  });