@selesai/code 0.5.20 → 0.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/extensions/handoff-new.test.ts +8 -9
  2. package/dist/extensions/handoff-new.ts +6 -8
  3. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  4. package/dist/extensions/pi-subagents/README.md +602 -220
  5. package/dist/extensions/pi-subagents/banner.png +0 -0
  6. package/dist/extensions/pi-subagents/index.ts +1 -0
  7. package/dist/extensions/pi-subagents/install.mjs +3 -3
  8. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  9. package/dist/extensions/pi-subagents/package.json +26 -14
  10. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  11. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  12. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  13. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  14. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  15. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  16. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  18. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  19. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  20. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  21. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  22. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  23. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  24. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  25. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  26. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  27. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  28. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  29. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  30. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  31. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  32. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  33. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  34. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  35. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  36. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  37. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  38. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  39. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  40. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  41. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  42. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  43. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  44. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  45. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  46. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  48. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  49. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  50. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  51. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  53. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  54. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  55. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  56. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  57. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  58. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  59. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  60. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  61. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  62. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  63. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  65. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  66. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  67. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  68. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  69. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  70. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  71. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  72. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  73. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  74. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  75. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  76. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  77. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  78. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  79. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  80. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  81. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  82. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  83. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  87. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  88. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  89. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  90. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  92. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  94. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  97. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  98. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  99. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  100. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  101. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  102. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  103. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  104. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  105. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  106. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  107. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  108. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  109. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  110. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  111. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  112. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  113. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  114. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  115. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  116. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  117. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  120. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  121. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  122. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  123. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  124. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  125. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  126. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  127. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  128. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  129. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  130. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  131. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  132. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  133. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  134. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  135. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  136. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  137. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  138. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  139. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  140. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  141. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  142. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  143. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  144. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  145. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  146. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  147. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  148. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  149. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  150. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  151. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  152. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  153. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  154. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  155. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  156. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  157. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  158. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  159. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  160. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  161. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  162. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  163. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  164. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  165. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  166. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  167. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  168. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  169. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  170. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  171. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  172. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  173. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  174. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  175. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  176. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  177. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  178. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  179. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  180. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  181. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  182. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  183. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  184. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  185. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  186. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  187. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  188. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  189. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  190. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  191. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  192. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  194. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  195. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  196. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  197. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  198. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  199. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  200. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  201. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  202. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  203. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  204. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  205. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  206. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  207. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  208. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  209. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  210. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  211. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  212. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  213. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  214. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  215. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  216. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  217. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  218. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  219. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  220. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  221. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  222. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  223. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  224. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  225. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  226. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  227. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  228. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  229. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  230. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  231. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  232. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  233. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  234. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  235. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  236. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  237. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  238. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  239. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  240. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  241. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  245. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  246. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  247. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  248. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  249. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  250. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  251. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  252. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  253. package/dist/extensions/question/batch.ts +39 -0
  254. package/dist/extensions/question/dialog-adapter.ts +23 -1
  255. package/dist/extensions/question/index.ts +237 -7
  256. package/dist/extensions/question/schemas.ts +28 -15
  257. package/dist/extensions/question/tests/batch.test.ts +26 -0
  258. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  259. package/dist/extensions/question/tui-adapter.ts +2 -3
  260. package/dist/extensions/question/types.ts +37 -0
  261. package/dist/extensions/question/ui-protocol.ts +3 -3
  262. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  263. package/package.json +1 -1
@@ -1,288 +1,67 @@
1
- export const PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT = "prompt-template:subagent:request";
2
- export const PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT = "prompt-template:subagent:started";
3
- export const PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT = "prompt-template:subagent:response";
4
- export const PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT = "prompt-template:subagent:update";
5
- export const PROMPT_TEMPLATE_SUBAGENT_CANCEL_EVENT = "prompt-template:subagent:cancel";
1
+ import {
2
+ SUBAGENT_DELEGATION_CANCEL_EVENT,
3
+ SUBAGENT_DELEGATION_PROTOCOL_VERSION,
4
+ SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
5
+ SUBAGENT_DELEGATION_REQUEST_EVENT,
6
+ SUBAGENT_DELEGATION_RESPONSE_EVENT,
7
+ SUBAGENT_DELEGATION_STARTED_EVENT,
8
+ SUBAGENT_DELEGATION_UPDATE_EVENT,
9
+ type SubagentDelegationRequest,
10
+ type SubagentDelegationResponse,
11
+ type SubagentDelegationV2InvalidResponse,
12
+ type SubagentDelegationV2Request,
13
+ type SubagentDelegationV2Response,
14
+ } from "../api/delegation.ts";
15
+ import { parseSubagentDelegationRequest } from "./delegation-request.ts";
16
+ import {
17
+ parsePromptTemplateRequest,
18
+ toDelegationUpdate,
19
+ toLegacyExecutionParams,
20
+ toPromptTemplateResponse,
21
+ toSubagentDelegationExecutionParams,
22
+ toSubagentDelegationResponse,
23
+ toSubagentDelegationUpdate,
24
+ toSubagentDelegationV2ExecutionParams,
25
+ toSubagentDelegationV2Response,
26
+ toSubagentDelegationV2Update,
27
+ type DelegatedSubagentExecutionParams,
28
+ type PromptTemplateBridgeResult,
29
+ type PromptTemplateDelegationRequest,
30
+ type PromptTemplateDelegationResponse,
31
+ } from "./delegation-adapters.ts";
6
32
 
7
- interface PromptTemplateDelegationTask {
8
- agent: string;
9
- task: string;
10
- model?: string;
11
- cwd?: string;
12
- }
13
-
14
- interface PromptTemplateDelegationParallelResult {
15
- agent: string;
16
- messages: unknown[];
17
- isError: boolean;
18
- errorText?: string;
19
- }
20
-
21
- interface PromptTemplateDelegationRequest {
22
- requestId: string;
23
- agent: string;
24
- task: string;
25
- tasks?: PromptTemplateDelegationTask[];
26
- context: "fresh" | "fork";
27
- model: string;
28
- cwd: string;
29
- worktree?: boolean;
30
- }
31
-
32
- interface PromptTemplateDelegationResponse extends PromptTemplateDelegationRequest {
33
- messages: unknown[];
34
- parallelResults?: PromptTemplateDelegationParallelResult[];
35
- contentText?: string;
36
- isError: boolean;
37
- errorText?: string;
38
- }
39
-
40
- interface PromptTemplateDelegationTaskProgress {
41
- index?: number;
42
- agent: string;
43
- status?: string;
44
- currentTool?: string;
45
- currentToolArgs?: string;
46
- recentOutput?: string;
47
- recentOutputLines?: string[];
48
- recentTools?: Array<{ tool: string; args: string }>;
49
- model?: string;
50
- toolCount?: number;
51
- durationMs?: number;
52
- tokens?: number;
53
- }
54
-
55
- interface PromptTemplateDelegationUpdate {
56
- requestId: string;
57
- currentTool?: string;
58
- currentToolArgs?: string;
59
- recentOutput?: string;
60
- recentOutputLines?: string[];
61
- recentTools?: Array<{ tool: string; args: string }>;
62
- model?: string;
63
- toolCount?: number;
64
- durationMs?: number;
65
- tokens?: number;
66
- taskProgress?: PromptTemplateDelegationTaskProgress[];
67
- }
33
+ export const PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT = SUBAGENT_DELEGATION_REQUEST_EVENT;
34
+ export const PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT = SUBAGENT_DELEGATION_STARTED_EVENT;
35
+ export const PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT = SUBAGENT_DELEGATION_RESPONSE_EVENT;
36
+ export const PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT = SUBAGENT_DELEGATION_UPDATE_EVENT;
37
+ export const PROMPT_TEMPLATE_SUBAGENT_CANCEL_EVENT = SUBAGENT_DELEGATION_CANCEL_EVENT;
68
38
 
69
39
  export interface PromptTemplateBridgeEvents {
70
40
  on(event: string, handler: (data: unknown) => void): (() => void) | void;
71
41
  emit(event: string, data: unknown): void;
72
42
  }
73
43
 
74
- interface PromptTemplateBridgeResult {
75
- isError?: boolean;
76
- content?: unknown;
77
- details?: {
78
- results?: Array<{
79
- agent?: string;
80
- messages?: unknown[];
81
- finalOutput?: string;
82
- toolCalls?: Array<{ text?: string; expandedText?: string }>;
83
- exitCode?: number;
84
- error?: string;
85
- model?: string;
86
- }>;
87
- progress?: Array<{
88
- index?: number;
89
- agent?: string;
90
- status?: string;
91
- currentTool?: string;
92
- currentToolArgs?: string;
93
- recentOutput?: string[];
94
- recentTools?: Array<{ tool?: string; args?: string }>;
95
- toolCount?: number;
96
- durationMs?: number;
97
- tokens?: number;
98
- }>;
99
- };
100
- }
101
-
102
44
  interface PromptTemplateBridgeOptions<Ctx extends { cwd?: string }> {
103
45
  events: PromptTemplateBridgeEvents;
104
46
  getContext: () => Ctx | null;
105
47
  execute: (
106
48
  requestId: string,
107
- request: PromptTemplateDelegationRequest,
49
+ params: DelegatedSubagentExecutionParams,
50
+ signal: AbortSignal,
51
+ ctx: Ctx,
52
+ onUpdate: (result: PromptTemplateBridgeResult) => void,
53
+ ) => Promise<PromptTemplateBridgeResult>;
54
+ /**
55
+ * Concurrent-safe executor for strict versioned delegation requests.
56
+ * Non-versioned prompt-template requests retain the ordinary single-dispatch guard.
57
+ */
58
+ executeVersioned?: (
59
+ requestId: string,
60
+ params: DelegatedSubagentExecutionParams,
108
61
  signal: AbortSignal,
109
62
  ctx: Ctx,
110
63
  onUpdate: (result: PromptTemplateBridgeResult) => void,
111
64
  ) => Promise<PromptTemplateBridgeResult>;
112
- }
113
-
114
- function parseDelegationTasks(tasks: unknown): PromptTemplateDelegationTask[] {
115
- if (!Array.isArray(tasks)) return [];
116
- const parsed: PromptTemplateDelegationTask[] = [];
117
- for (const item of tasks) {
118
- if (!item || typeof item !== "object") return [];
119
- const value = item as Partial<PromptTemplateDelegationTask>;
120
- if (typeof value.agent !== "string" || !value.agent.trim()) return [];
121
- if (typeof value.task !== "string" || !value.task.trim()) return [];
122
- const model = typeof value.model === "string" && value.model.trim().length > 0 ? value.model : undefined;
123
- const cwd = typeof value.cwd === "string" && value.cwd.trim().length > 0 ? value.cwd : undefined;
124
- parsed.push({
125
- agent: value.agent,
126
- task: value.task,
127
- ...(model ? { model } : {}),
128
- ...(cwd ? { cwd } : {}),
129
- });
130
- }
131
- return parsed;
132
- }
133
-
134
- function parsePromptTemplateRequest(data: unknown): PromptTemplateDelegationRequest | undefined {
135
- if (!data || typeof data !== "object") return undefined;
136
- const value = data as Partial<PromptTemplateDelegationRequest> & { tasks?: unknown };
137
- if (typeof value.requestId !== "string" || !value.requestId) return undefined;
138
- if (typeof value.model !== "string" || !value.model) return undefined;
139
- if (typeof value.cwd !== "string" || !value.cwd) return undefined;
140
- if (value.context !== "fresh" && value.context !== "fork") return undefined;
141
- const tasks = parseDelegationTasks(value.tasks);
142
- const worktree = value.worktree === true ? true : undefined;
143
- const hasSingle =
144
- typeof value.agent === "string" &&
145
- value.agent.length > 0 &&
146
- typeof value.task === "string" &&
147
- value.task.length > 0;
148
- if (!hasSingle && tasks.length === 0) return undefined;
149
-
150
- const fallbackTask = tasks[0];
151
- return {
152
- requestId: value.requestId,
153
- agent: hasSingle ? value.agent : fallbackTask!.agent,
154
- task: hasSingle ? value.task : fallbackTask!.task,
155
- ...(tasks.length > 0 ? { tasks } : {}),
156
- context: value.context,
157
- model: value.model,
158
- cwd: value.cwd,
159
- ...(worktree ? { worktree } : {}),
160
- };
161
- }
162
-
163
- function firstTextContent(content: unknown): string | undefined {
164
- if (!Array.isArray(content)) return undefined;
165
- for (const part of content) {
166
- if (!part || typeof part !== "object") continue;
167
- if ((part as { type?: string }).type !== "text") continue;
168
- const text = (part as { text?: unknown }).text;
169
- if (typeof text === "string" && text.trim()) return text.trim();
170
- }
171
- return undefined;
172
- }
173
-
174
- function filterRecentOutput(lines: string[] | undefined): string[] | undefined {
175
- if (!lines || lines.length === 0) return undefined;
176
- const filtered = lines.filter((line) => typeof line === "string" && line.trim() && line.trim() !== "(running...)");
177
- if (filtered.length === 0) return undefined;
178
- return filtered;
179
- }
180
-
181
- function sanitizeRecentTools(
182
- tools: Array<{ tool?: string; args?: string }> | undefined,
183
- ): Array<{ tool: string; args: string }> | undefined {
184
- if (!tools || tools.length === 0) return undefined;
185
- const sanitized = tools.flatMap((entry) => {
186
- if (typeof entry.tool !== "string" || entry.tool.trim().length === 0) return [];
187
- return [{
188
- tool: entry.tool,
189
- args: typeof entry.args === "string" ? entry.args : String(entry.args ?? ""),
190
- }];
191
- });
192
- return sanitized.length > 0 ? sanitized : undefined;
193
- }
194
-
195
- function resolveProgressModel(
196
- update: PromptTemplateBridgeResult,
197
- entry: { index?: number; agent?: string },
198
- ): string | undefined {
199
- const results = update.details?.results;
200
- if (!results || results.length === 0) return undefined;
201
- if (typeof entry.index === "number" && entry.index >= 0) {
202
- const byIndex = results[entry.index];
203
- if (typeof byIndex?.model === "string") return byIndex.model;
204
- }
205
- if (entry.agent) {
206
- const byAgent = results.find((result) => result.agent === entry.agent && typeof result.model === "string");
207
- if (byAgent?.model) return byAgent.model;
208
- }
209
- const firstWithModel = results.find((result) => typeof result.model === "string");
210
- return firstWithModel?.model;
211
- }
212
-
213
- function toolCallNameFromSummary(summary: { text?: string; expandedText?: string }): string | undefined {
214
- const text = typeof summary.expandedText === "string" && summary.expandedText.trim().length > 0
215
- ? summary.expandedText.trim()
216
- : typeof summary.text === "string"
217
- ? summary.text.trim()
218
- : "";
219
- if (!text) return undefined;
220
- if (text.startsWith("$ ")) return "bash";
221
- return text.match(/^[A-Za-z_][\w.-]*/)?.[0];
222
- }
223
-
224
- function buildDelegationMessages(
225
- result: { messages?: unknown[]; finalOutput?: string; toolCalls?: Array<{ text?: string; expandedText?: string }> },
226
- fallbackText?: string,
227
- ): unknown[] {
228
- if (Array.isArray(result.messages) && result.messages.length > 0) return result.messages;
229
- const toolCallParts = (result.toolCalls ?? []).flatMap((summary) => {
230
- const name = toolCallNameFromSummary(summary);
231
- return name ? [{ type: "toolCall", name, arguments: { summary: summary.expandedText ?? summary.text ?? "" } }] : [];
232
- });
233
- const text = typeof result.finalOutput === "string" && result.finalOutput.trim().length > 0
234
- ? result.finalOutput.trim()
235
- : fallbackText;
236
- const content = [
237
- ...toolCallParts,
238
- ...(text ? [{ type: "text", text }] : []),
239
- ];
240
- if (content.length === 0) return [];
241
- return [{ role: "assistant", content }];
242
- }
243
-
244
- function toDelegationUpdate(requestId: string, update: PromptTemplateBridgeResult): PromptTemplateDelegationUpdate | undefined {
245
- const progress = update.details?.progress?.[0];
246
- const taskProgress = update.details?.progress?.map((entry) => {
247
- const lastOutput = entry.recentOutput?.[entry.recentOutput.length - 1];
248
- const safeLastOutput =
249
- typeof lastOutput === "string" && lastOutput.trim() && lastOutput !== "(running...)"
250
- ? lastOutput
251
- : undefined;
252
- return {
253
- index: entry.index,
254
- agent: entry.agent ?? "delegate",
255
- status: entry.status,
256
- currentTool: entry.currentTool,
257
- currentToolArgs: entry.currentToolArgs,
258
- recentOutput: safeLastOutput,
259
- recentOutputLines: filterRecentOutput(entry.recentOutput),
260
- recentTools: sanitizeRecentTools(entry.recentTools),
261
- model: resolveProgressModel(update, entry),
262
- toolCount: entry.toolCount,
263
- durationMs: entry.durationMs,
264
- tokens: entry.tokens,
265
- };
266
- });
267
- if (!progress && (!taskProgress || taskProgress.length === 0)) return undefined;
268
- const lastOutput = progress?.recentOutput?.[progress.recentOutput.length - 1];
269
- const safeLastOutput =
270
- typeof lastOutput === "string" && lastOutput.trim() && lastOutput !== "(running...)"
271
- ? lastOutput
272
- : undefined;
273
- return {
274
- requestId,
275
- currentTool: progress?.currentTool,
276
- currentToolArgs: progress?.currentToolArgs,
277
- recentOutput: safeLastOutput,
278
- recentOutputLines: filterRecentOutput(progress?.recentOutput),
279
- recentTools: sanitizeRecentTools(progress?.recentTools),
280
- model: progress ? resolveProgressModel(update, progress) : undefined,
281
- toolCount: progress?.toolCount,
282
- durationMs: progress?.durationMs,
283
- tokens: progress?.tokens,
284
- taskProgress,
285
- };
286
65
  }
287
66
 
288
67
  export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: string }>(
@@ -291,130 +70,359 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
291
70
  cancelAll: () => void;
292
71
  dispose: () => void;
293
72
  } {
73
+ // Legacy and V1 retain requestId-only correlation. V2 attempts are isolated by
74
+ // their complete wire identity, while logical-node ownership is tracked separately.
294
75
  const controllers = new Map<string, AbortController>();
295
- const pendingCancels = new Set<string>();
76
+ const pendingCancels = new Map<string, true>();
77
+ const v2Controllers = new Map<string, AbortController>();
78
+ const pendingV2Cancels = new Map<string, true>();
79
+ const activeV2Nodes = new Map<string, { attemptKey: string; controller: AbortController }>();
80
+ const settledV2Attempts = new Map<string, true>();
296
81
  const subscriptions: Array<() => void> = [];
82
+ let disposed = false;
83
+ let v2IdentitySaturated = false;
297
84
 
298
85
  const subscribe = (event: string, handler: (data: unknown) => void): void => {
299
86
  const unsubscribe = options.events.on(event, handler);
300
87
  if (typeof unsubscribe === "function") subscriptions.push(unsubscribe);
301
88
  };
89
+ const ownsRequest = (requestId: string, controller: AbortController): boolean =>
90
+ !disposed && controllers.get(requestId) === controller;
91
+ const ownsV2Attempt = (attemptKey: string, controller: AbortController): boolean =>
92
+ !disposed && v2Controllers.get(attemptKey) === controller;
93
+ const boundedRemember = (map: Map<string, true>, key: string): void => {
94
+ map.delete(key);
95
+ map.set(key, true);
96
+ while (map.size > 256) {
97
+ const oldest = map.keys().next().value;
98
+ if (typeof oldest !== "string") break;
99
+ map.delete(oldest);
100
+ }
101
+ };
102
+ const rememberV2Identity = (map: Map<string, true>, key: string): void => {
103
+ if (map.has(key) || v2IdentitySaturated) return;
104
+ if (map.size >= 8_192) {
105
+ v2IdentitySaturated = true;
106
+ return;
107
+ }
108
+ map.set(key, true);
109
+ if (map.size === 8_192) {
110
+ // V2 identity facts are security state, not an LRU cache. Saturate as
111
+ // soon as the bounded history fills so no later untracked attempt can
112
+ // start, rather than evicting a cancellation or settled-attempt fact.
113
+ v2IdentitySaturated = true;
114
+ }
115
+ };
116
+ const v2NodeKey = (ownerRunId: string, nodeId: string): string => JSON.stringify([ownerRunId, nodeId]);
117
+ const v2AttemptKey = (requestId: string, ownerRunId: string, nodeId: string): string => JSON.stringify([requestId, ownerRunId, nodeId]);
118
+ const rememberPendingCancel = (requestId: string): void => {
119
+ boundedRemember(pendingCancels, requestId);
120
+ };
121
+ const emitV2Terminal = (attemptKey: string, payload: SubagentDelegationV2Response): void => {
122
+ if (disposed || settledV2Attempts.has(attemptKey)) return;
123
+ rememberV2Identity(settledV2Attempts, attemptKey);
124
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, payload);
125
+ };
302
126
 
303
127
  subscribe(PROMPT_TEMPLATE_SUBAGENT_CANCEL_EVENT, (data) => {
304
- if (!data || typeof data !== "object") return;
305
- const requestId = (data as { requestId?: unknown }).requestId;
306
- if (typeof requestId !== "string") return;
128
+ if (!data || typeof data !== "object" || Array.isArray(data)) return;
129
+ const value = data as Record<string, unknown>;
130
+ const requestId = value.requestId;
131
+ if (typeof requestId !== "string" || !requestId.trim() || requestId.length > 256 || /[\r\n]/.test(requestId)) return;
132
+ if (Object.hasOwn(value, "version")) {
133
+ if (value.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
134
+ if (Object.keys(value).some((key) => key !== "version" && key !== "requestId" && key !== "ownerRunId" && key !== "nodeId")) return;
135
+ const ownerRunId = value.ownerRunId;
136
+ const nodeId = value.nodeId;
137
+ if (typeof ownerRunId !== "string" || !ownerRunId.trim() || ownerRunId.length > 256 || /[\r\n]/.test(ownerRunId)) return;
138
+ if (typeof nodeId !== "string" || !nodeId.trim() || nodeId.length > 256 || /[\r\n]/.test(nodeId)) return;
139
+ const attemptKey = v2AttemptKey(requestId, ownerRunId, nodeId);
140
+ const controller = v2Controllers.get(attemptKey);
141
+ if (controller) controller.abort();
142
+ else rememberV2Identity(pendingV2Cancels, attemptKey);
143
+ return;
144
+ }
145
+ if (value.version !== SUBAGENT_DELEGATION_PROTOCOL_VERSION) return;
146
+ if (Object.keys(value).some((key) => key !== "version" && key !== "requestId")) return;
147
+ }
307
148
  const controller = controllers.get(requestId);
308
149
  if (controller) {
309
150
  controller.abort();
310
151
  return;
311
152
  }
312
- pendingCancels.add(requestId);
153
+ rememberPendingCancel(requestId);
313
154
  });
314
155
 
315
156
  subscribe(PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT, async (data) => {
316
- const request = parsePromptTemplateRequest(data);
317
- if (!request) return;
157
+ const isVersioned = !!data && typeof data === "object" && Object.hasOwn(data, "version");
158
+ let requestId: string;
159
+ let params: DelegatedSubagentExecutionParams;
160
+ let versionedRequest: SubagentDelegationRequest | SubagentDelegationV2Request | undefined;
161
+ let v2Request: SubagentDelegationV2Request | undefined;
162
+ let v2Key: string | undefined;
163
+ let legacyRequest: PromptTemplateDelegationRequest | undefined;
164
+
165
+ if (isVersioned) {
166
+ const parsed = parseSubagentDelegationRequest(data);
167
+ if (parsed.ok === false) {
168
+ if (!disposed && parsed.requestId) {
169
+ if (parsed.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
170
+ const payload = {
171
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
172
+ requestId: parsed.requestId,
173
+ ...(parsed.ownerRunId ? { ownerRunId: parsed.ownerRunId } : {}),
174
+ ...(parsed.nodeId ? { nodeId: parsed.nodeId } : {}),
175
+ status: "invalid_request",
176
+ error: parsed.error,
177
+ } satisfies SubagentDelegationV2InvalidResponse;
178
+ if (parsed.ownerRunId && parsed.nodeId) {
179
+ const attemptKey = v2AttemptKey(parsed.requestId, parsed.ownerRunId, parsed.nodeId);
180
+ if (!v2Controllers.has(attemptKey)) emitV2Terminal(attemptKey, payload);
181
+ } else {
182
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, payload);
183
+ }
184
+ } else if (!controllers.has(parsed.requestId)) {
185
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
186
+ version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
187
+ requestId: parsed.requestId,
188
+ status: "invalid_request",
189
+ error: parsed.error,
190
+ } satisfies SubagentDelegationResponse);
191
+ }
192
+ }
193
+ return;
194
+ }
195
+ versionedRequest = parsed.request;
196
+ requestId = parsed.request.requestId;
197
+ if (parsed.request.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
198
+ v2Request = parsed.request;
199
+ v2Key = v2AttemptKey(requestId, parsed.request.ownerRunId, parsed.request.nodeId);
200
+ params = toSubagentDelegationV2ExecutionParams(parsed.request);
201
+ } else {
202
+ params = toSubagentDelegationExecutionParams(parsed.request);
203
+ }
204
+ } else {
205
+ legacyRequest = parsePromptTemplateRequest(data);
206
+ if (!legacyRequest) return;
207
+ requestId = legacyRequest.requestId;
208
+ params = toLegacyExecutionParams(legacyRequest);
209
+ }
318
210
 
211
+ // Legacy and V1 keep requestId-only ownership. V2 retransmission and terminal
212
+ // suppression use the full tuple, independently from logical-node ownership.
213
+ if (!v2Request && controllers.has(requestId)) return;
214
+ if (v2Request && v2Key) {
215
+ if (v2Controllers.has(v2Key) || settledV2Attempts.has(v2Key)) return;
216
+ if (pendingV2Cancels.delete(v2Key)) {
217
+ emitV2Terminal(v2Key, {
218
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
219
+ requestId,
220
+ ownerRunId: v2Request.ownerRunId,
221
+ nodeId: v2Request.nodeId,
222
+ status: "cancelled",
223
+ });
224
+ return;
225
+ }
226
+ if (v2IdentitySaturated) {
227
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
228
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
229
+ requestId,
230
+ ownerRunId: v2Request.ownerRunId,
231
+ nodeId: v2Request.nodeId,
232
+ status: "unavailable_context",
233
+ error: "Delegation v2 identity capacity is exhausted for this extension context.",
234
+ } satisfies SubagentDelegationV2Response);
235
+ return;
236
+ }
237
+ const active = activeV2Nodes.get(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId));
238
+ if (active) {
239
+ emitV2Terminal(v2Key, {
240
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
241
+ requestId,
242
+ ownerRunId: v2Request.ownerRunId,
243
+ nodeId: v2Request.nodeId,
244
+ status: "duplicate_node",
245
+ });
246
+ return;
247
+ }
248
+ }
319
249
  const ctx = options.getContext();
320
250
  if (!ctx) {
321
- const response: PromptTemplateDelegationResponse = {
322
- ...request,
323
- messages: [],
324
- isError: true,
325
- errorText: "No active extension context for delegated subagent execution.",
326
- };
327
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
251
+ if (v2Request && v2Key) {
252
+ emitV2Terminal(v2Key, {
253
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
254
+ requestId,
255
+ ownerRunId: v2Request.ownerRunId,
256
+ nodeId: v2Request.nodeId,
257
+ status: "unavailable_context",
258
+ error: "No active extension context for delegated subagent execution.",
259
+ });
260
+ } else if (versionedRequest) {
261
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
262
+ version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
263
+ requestId,
264
+ status: "unavailable_context",
265
+ error: "No active extension context for delegated subagent execution.",
266
+ } satisfies SubagentDelegationResponse);
267
+ } else if (legacyRequest) {
268
+ options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
269
+ ...legacyRequest,
270
+ messages: [],
271
+ isError: true,
272
+ errorText: "No active extension context for delegated subagent execution.",
273
+ } satisfies PromptTemplateDelegationResponse);
274
+ }
328
275
  return;
329
276
  }
330
277
 
331
278
  const controller = new AbortController();
332
- controllers.set(request.requestId, controller);
333
-
334
- if (pendingCancels.delete(request.requestId)) {
335
- controller.abort();
336
- const response: PromptTemplateDelegationResponse = {
337
- ...request,
338
- messages: [],
339
- isError: true,
340
- errorText: "Delegated prompt cancelled.",
341
- };
342
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
343
- controllers.delete(request.requestId);
279
+ if (v2Request && v2Key) {
280
+ v2Controllers.set(v2Key, controller);
281
+ activeV2Nodes.set(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId), { attemptKey: v2Key, controller });
282
+ } else {
283
+ controllers.set(requestId, controller);
284
+ if (pendingCancels.delete(requestId)) controller.abort();
285
+ }
286
+ if (controller.signal.aborted) {
287
+ if (v2Request && v2Key) {
288
+ emitV2Terminal(v2Key, {
289
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
290
+ requestId,
291
+ ownerRunId: v2Request.ownerRunId,
292
+ nodeId: v2Request.nodeId,
293
+ status: "cancelled",
294
+ });
295
+ activeV2Nodes.delete(v2NodeKey(v2Request.ownerRunId, v2Request.nodeId));
296
+ } else if (versionedRequest) {
297
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
298
+ version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
299
+ requestId,
300
+ status: "cancelled",
301
+ } satisfies SubagentDelegationResponse);
302
+ } else if (legacyRequest) {
303
+ options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
304
+ ...legacyRequest,
305
+ messages: [],
306
+ isError: true,
307
+ errorText: "Delegated prompt cancelled.",
308
+ } satisfies PromptTemplateDelegationResponse);
309
+ }
310
+ if (v2Key) v2Controllers.delete(v2Key);
311
+ else controllers.delete(requestId);
344
312
  return;
345
313
  }
346
314
 
347
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT, { requestId: request.requestId });
315
+ options.events.emit(
316
+ versionedRequest ? SUBAGENT_DELEGATION_STARTED_EVENT : PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT,
317
+ v2Request
318
+ ? { version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION, requestId, ownerRunId: v2Request.ownerRunId, nodeId: v2Request.nodeId }
319
+ : versionedRequest
320
+ ? { version: SUBAGENT_DELEGATION_PROTOCOL_VERSION, requestId }
321
+ : { requestId },
322
+ );
348
323
 
349
324
  try {
350
- const result = await options.execute(
351
- request.requestId,
352
- request,
325
+ const executeRequest = versionedRequest && options.executeVersioned
326
+ ? options.executeVersioned
327
+ : options.execute;
328
+ const result = await executeRequest(
329
+ requestId,
330
+ params,
353
331
  controller.signal,
354
332
  ctx,
355
333
  (update) => {
356
- const payload = toDelegationUpdate(request.requestId, update);
357
- if (!payload) return;
358
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT, payload);
334
+ if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
335
+ if (v2Request) {
336
+ const payload = toSubagentDelegationV2Update(v2Request, update);
337
+ if (payload) options.events.emit(SUBAGENT_DELEGATION_UPDATE_EVENT, payload);
338
+ return;
339
+ }
340
+ if (versionedRequest) {
341
+ const payload = toSubagentDelegationUpdate(requestId, update);
342
+ if (payload) options.events.emit(SUBAGENT_DELEGATION_UPDATE_EVENT, payload);
343
+ return;
344
+ }
345
+ const payload = toDelegationUpdate(requestId, update);
346
+ if (payload) options.events.emit(PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT, payload);
359
347
  },
360
348
  );
361
- const contentText = firstTextContent(result.content);
362
- const messages = buildDelegationMessages(result.details?.results?.[0] ?? {}, contentText);
363
- const parallelResults = request.tasks
364
- ? request.tasks.map<PromptTemplateDelegationParallelResult>((task, index) => {
365
- const step = result.details?.results?.[index];
366
- if (!step) {
367
- return {
368
- agent: task.agent,
369
- messages: [],
370
- isError: true,
371
- errorText: "Missing result for delegated parallel task.",
372
- };
373
- }
374
- const exitCode = typeof step.exitCode === "number" ? step.exitCode : undefined;
375
- const errorText = step.error;
376
- return {
377
- agent: step.agent ?? task.agent,
378
- messages: buildDelegationMessages(step),
379
- isError: (exitCode !== undefined && exitCode !== 0) || !!errorText,
380
- errorText: errorText || undefined,
381
- };
382
- })
383
- : undefined;
384
- const response: PromptTemplateDelegationResponse = {
385
- ...request,
386
- messages,
387
- ...(parallelResults ? { parallelResults } : {}),
388
- ...(contentText ? { contentText } : {}),
389
- isError: result.isError === true,
390
- errorText: result.isError ? contentText : undefined,
391
- };
392
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
349
+ if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
350
+ if (v2Request && v2Key) {
351
+ emitV2Terminal(v2Key, toSubagentDelegationV2Response(v2Request, result, controller.signal.aborted));
352
+ } else if (versionedRequest) {
353
+ options.events.emit(
354
+ SUBAGENT_DELEGATION_RESPONSE_EVENT,
355
+ toSubagentDelegationResponse(requestId, result, controller.signal.aborted),
356
+ );
357
+ } else if (legacyRequest) {
358
+ options.events.emit(
359
+ PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT,
360
+ controller.signal.aborted
361
+ ? { ...legacyRequest, messages: [], isError: true, errorText: "Delegated prompt cancelled." }
362
+ : toPromptTemplateResponse(legacyRequest, result),
363
+ );
364
+ }
393
365
  } catch (error) {
394
- const response: PromptTemplateDelegationResponse = {
395
- ...request,
396
- messages: [],
397
- isError: true,
398
- errorText: error instanceof Error ? error.message : String(error),
399
- };
400
- options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
366
+ if (v2Key ? !ownsV2Attempt(v2Key, controller) : !ownsRequest(requestId, controller)) return;
367
+ if (v2Request && v2Key) {
368
+ emitV2Terminal(v2Key, {
369
+ version: SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
370
+ requestId,
371
+ ownerRunId: v2Request.ownerRunId,
372
+ nodeId: v2Request.nodeId,
373
+ status: controller.signal.aborted ? "cancelled" : "failed",
374
+ ...(controller.signal.aborted ? {} : { error: error instanceof Error ? error.message : String(error) }),
375
+ });
376
+ } else if (versionedRequest) {
377
+ options.events.emit(SUBAGENT_DELEGATION_RESPONSE_EVENT, {
378
+ version: SUBAGENT_DELEGATION_PROTOCOL_VERSION,
379
+ requestId,
380
+ status: controller.signal.aborted ? "cancelled" : "failed",
381
+ ...(controller.signal.aborted ? {} : { error: error instanceof Error ? error.message : String(error) }),
382
+ } satisfies SubagentDelegationResponse);
383
+ } else if (legacyRequest) {
384
+ options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, {
385
+ ...legacyRequest,
386
+ messages: [],
387
+ isError: true,
388
+ errorText: error instanceof Error ? error.message : String(error),
389
+ } satisfies PromptTemplateDelegationResponse);
390
+ }
401
391
  } finally {
402
- controllers.delete(request.requestId);
392
+ if (v2Key) {
393
+ if (v2Controllers.get(v2Key) === controller) v2Controllers.delete(v2Key);
394
+ } else if (controllers.get(requestId) === controller) controllers.delete(requestId);
395
+ if (v2Request) {
396
+ const key = v2NodeKey(v2Request.ownerRunId, v2Request.nodeId);
397
+ if (activeV2Nodes.get(key)?.controller === controller) activeV2Nodes.delete(key);
398
+ }
403
399
  }
404
400
  });
405
401
 
406
402
  return {
407
403
  cancelAll: () => {
408
- for (const controller of controllers.values()) {
409
- controller.abort();
410
- }
404
+ for (const controller of controllers.values()) controller.abort();
405
+ for (const controller of v2Controllers.values()) controller.abort();
411
406
  controllers.clear();
407
+ v2Controllers.clear();
412
408
  pendingCancels.clear();
409
+ pendingV2Cancels.clear();
410
+ activeV2Nodes.clear();
411
+ settledV2Attempts.clear();
412
+ v2IdentitySaturated = false;
413
413
  },
414
414
  dispose: () => {
415
+ disposed = true;
416
+ for (const controller of controllers.values()) controller.abort();
417
+ for (const controller of v2Controllers.values()) controller.abort();
418
+ controllers.clear();
419
+ v2Controllers.clear();
415
420
  for (const unsubscribe of subscriptions) unsubscribe();
416
421
  subscriptions.length = 0;
417
422
  pendingCancels.clear();
423
+ pendingV2Cancels.clear();
424
+ activeV2Nodes.clear();
425
+ settledV2Attempts.clear();
418
426
  },
419
427
  };
420
428
  }