@selesai/code 0.5.28 → 0.5.29

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 (307) hide show
  1. package/dist/cli/args.d.ts.map +1 -1
  2. package/dist/cli/args.js +9 -1
  3. package/dist/cli/args.js.map +1 -1
  4. package/dist/cli/config-selector.d.ts.map +1 -1
  5. package/dist/cli/config-selector.js +1 -1
  6. package/dist/cli/config-selector.js.map +1 -1
  7. package/dist/cli/credential-print.d.ts +23 -0
  8. package/dist/cli/credential-print.d.ts.map +1 -0
  9. package/dist/cli/credential-print.js +117 -0
  10. package/dist/cli/credential-print.js.map +1 -0
  11. package/dist/cli/startup-ui.d.ts.map +1 -1
  12. package/dist/cli/startup-ui.js +1 -1
  13. package/dist/cli/startup-ui.js.map +1 -1
  14. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  15. package/dist/core/agent-session-runtime.js +3 -0
  16. package/dist/core/agent-session-runtime.js.map +1 -1
  17. package/dist/core/agent-session.d.ts +12 -1
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +20 -14
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/compaction/compaction.d.ts.map +1 -1
  22. package/dist/core/compaction/compaction.js +11 -3
  23. package/dist/core/compaction/compaction.js.map +1 -1
  24. package/dist/core/extensions/runner.d.ts +1 -0
  25. package/dist/core/extensions/runner.d.ts.map +1 -1
  26. package/dist/core/extensions/runner.js +11 -0
  27. package/dist/core/extensions/runner.js.map +1 -1
  28. package/dist/core/extensions/types.d.ts +14 -1
  29. package/dist/core/extensions/types.d.ts.map +1 -1
  30. package/dist/core/extensions/types.js.map +1 -1
  31. package/dist/core/footer-data-provider.d.ts +10 -0
  32. package/dist/core/footer-data-provider.d.ts.map +1 -1
  33. package/dist/core/footer-data-provider.js +1 -1
  34. package/dist/core/footer-data-provider.js.map +1 -1
  35. package/dist/core/llama/provider.d.ts.map +1 -1
  36. package/dist/core/llama/provider.js +8 -3
  37. package/dist/core/llama/provider.js.map +1 -1
  38. package/dist/core/model-config.d.ts +30 -0
  39. package/dist/core/model-config.d.ts.map +1 -1
  40. package/dist/core/model-config.js +6 -0
  41. package/dist/core/model-config.js.map +1 -1
  42. package/dist/core/model-registry.d.ts.map +1 -1
  43. package/dist/core/model-registry.js +2 -2
  44. package/dist/core/model-registry.js.map +1 -1
  45. package/dist/core/model-resolver.d.ts +1 -0
  46. package/dist/core/model-resolver.d.ts.map +1 -1
  47. package/dist/core/model-resolver.js +20 -3
  48. package/dist/core/model-resolver.js.map +1 -1
  49. package/dist/core/model-runtime.d.ts +2 -0
  50. package/dist/core/model-runtime.d.ts.map +1 -1
  51. package/dist/core/model-runtime.js +5 -4
  52. package/dist/core/model-runtime.js.map +1 -1
  53. package/dist/core/package-manager.d.ts.map +1 -1
  54. package/dist/core/package-manager.js +13 -6
  55. package/dist/core/package-manager.js.map +1 -1
  56. package/dist/core/remote-catalog-provider.d.ts +1 -1
  57. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  58. package/dist/core/remote-catalog-provider.js +24 -11
  59. package/dist/core/remote-catalog-provider.js.map +1 -1
  60. package/dist/core/resource-loader.d.ts +15 -0
  61. package/dist/core/resource-loader.d.ts.map +1 -1
  62. package/dist/core/resource-loader.js +66 -9
  63. package/dist/core/resource-loader.js.map +1 -1
  64. package/dist/core/settings-manager.d.ts +1 -1
  65. package/dist/core/settings-manager.d.ts.map +1 -1
  66. package/dist/core/settings-manager.js.map +1 -1
  67. package/dist/core/system-prompt.d.ts.map +1 -1
  68. package/dist/core/system-prompt.js +1 -1
  69. package/dist/core/system-prompt.js.map +1 -1
  70. package/dist/core/tools/bash.d.ts +2 -0
  71. package/dist/core/tools/bash.d.ts.map +1 -1
  72. package/dist/core/tools/bash.js +34 -5
  73. package/dist/core/tools/bash.js.map +1 -1
  74. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  75. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  76. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  77. package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
  78. package/dist/extensions/pi-intercom/README.md +102 -39
  79. package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
  80. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
  81. package/dist/extensions/pi-intercom/broker/client.ts +315 -12
  82. package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
  83. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
  84. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
  85. package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
  86. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
  87. package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
  88. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
  89. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
  90. package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
  91. package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
  92. package/dist/extensions/pi-intercom/config.test.ts +93 -0
  93. package/dist/extensions/pi-intercom/config.ts +55 -6
  94. package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
  95. package/dist/extensions/pi-intercom/cwd.ts +31 -0
  96. package/dist/extensions/pi-intercom/extension-api.ts +44 -0
  97. package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
  98. package/dist/extensions/pi-intercom/format-context.ts +32 -0
  99. package/dist/extensions/pi-intercom/index.ts +742 -145
  100. package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
  101. package/dist/extensions/pi-intercom/package.json +15 -5
  102. package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
  103. package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
  104. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
  105. package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
  106. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
  107. package/dist/extensions/pi-intercom/types.ts +94 -4
  108. package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
  109. package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
  110. package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
  111. package/dist/extensions/pi-subagents/CHANGELOG.md +85 -0
  112. package/dist/extensions/pi-subagents/LICENSE +21 -0
  113. package/dist/extensions/pi-subagents/README.md +146 -52
  114. package/dist/extensions/pi-subagents/agents/architect.md +1 -0
  115. package/dist/extensions/pi-subagents/agents/builder.md +1 -0
  116. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  117. package/dist/extensions/pi-subagents/package.json +3 -3
  118. package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
  119. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +21 -989
  120. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
  121. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +430 -0
  122. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
  123. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +282 -0
  124. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +71 -30
  125. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
  126. package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
  127. package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
  128. package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
  129. package/dist/extensions/pi-subagents/src/api/preflight.ts +16 -12
  130. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
  131. package/dist/extensions/pi-subagents/src/extension/index.ts +17 -7
  132. package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
  133. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -6
  134. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +8 -7
  135. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
  136. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
  137. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +63 -16
  138. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
  139. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
  140. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  141. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
  142. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
  143. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +29 -4
  144. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +29 -6
  145. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
  146. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +372 -28
  147. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
  148. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
  149. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +143 -43
  150. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +527 -247
  151. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
  152. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +426 -123
  153. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +15 -7
  154. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
  155. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
  156. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  157. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
  158. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
  159. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
  160. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
  161. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
  162. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
  163. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
  164. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
  165. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
  166. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
  167. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +1 -1
  168. package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
  169. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
  170. package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
  171. package/dist/extensions/pi-subagents/src/shared/types.ts +155 -10
  172. package/dist/extensions/pi-subagents/src/shared/utils.ts +73 -4
  173. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -0
  174. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
  175. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
  176. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
  177. package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
  178. package/dist/extensions/pi-subagents/src/tui/render.ts +27 -28
  179. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
  180. package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
  181. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
  182. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
  183. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
  184. package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
  185. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  186. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +348 -7
  187. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
  188. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
  189. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
  190. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +26 -1
  191. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
  192. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +213 -13
  194. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +34 -0
  195. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
  196. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +118 -12
  197. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +850 -7
  198. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
  199. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
  200. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  201. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
  202. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
  203. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +128 -0
  204. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
  205. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
  206. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
  207. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +102 -0
  208. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
  209. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
  210. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
  211. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
  212. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
  213. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
  214. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +4 -2
  215. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
  216. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
  217. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
  218. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
  219. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +11 -11
  220. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
  221. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
  222. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
  223. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +10 -2
  224. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
  225. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
  226. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
  227. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +46 -0
  228. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
  229. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
  230. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
  231. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
  232. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
  233. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +34 -2
  234. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  235. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
  236. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
  237. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
  238. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
  239. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +3 -0
  240. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +1 -1
  241. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
  245. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
  246. package/dist/main.d.ts.map +1 -1
  247. package/dist/main.js +43 -0
  248. package/dist/main.js.map +1 -1
  249. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  250. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  251. package/dist/modes/interactive/components/custom-message.js +10 -2
  252. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  253. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  254. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  255. package/dist/modes/interactive/components/extension-editor.js +16 -46
  256. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  257. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  258. package/dist/modes/interactive/components/model-selector.js +4 -1
  259. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  260. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  261. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  262. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  263. package/dist/modes/interactive/external-editor.d.ts +12 -0
  264. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  265. package/dist/modes/interactive/external-editor.js +37 -0
  266. package/dist/modes/interactive/external-editor.js.map +1 -0
  267. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  268. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  269. package/dist/modes/interactive/interactive-mode.js +72 -64
  270. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  271. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  272. package/dist/modes/rpc/rpc-mode.js +14 -0
  273. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  274. package/dist/utils/clipboard.d.ts.map +1 -1
  275. package/dist/utils/clipboard.js +19 -8
  276. package/dist/utils/clipboard.js.map +1 -1
  277. package/dist/utils/version-check.d.ts.map +1 -1
  278. package/dist/utils/version-check.js +1 -1
  279. package/dist/utils/version-check.js.map +1 -1
  280. package/docs/compaction.md +1 -1
  281. package/docs/custom-provider.md +14 -5
  282. package/docs/environment-variables.md +86 -0
  283. package/docs/extensions.md +15 -5
  284. package/docs/index.md +1 -0
  285. package/docs/models.md +9 -2
  286. package/docs/providers.md +21 -2
  287. package/docs/rpc.md +23 -6
  288. package/docs/session-format.md +2 -0
  289. package/docs/settings.md +1 -1
  290. package/docs/usage.md +0 -13
  291. package/examples/extensions/custom-compaction.ts +5 -2
  292. package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
  293. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  294. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  295. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  296. package/examples/extensions/gondolin/package-lock.json +2 -2
  297. package/examples/extensions/gondolin/package.json +1 -1
  298. package/examples/extensions/handoff.ts +11 -3
  299. package/examples/extensions/message-renderer.ts +3 -3
  300. package/examples/extensions/sandbox/package-lock.json +2 -2
  301. package/examples/extensions/sandbox/package.json +1 -1
  302. package/examples/extensions/summarize.ts +5 -2
  303. package/examples/extensions/with-deps/package-lock.json +2 -2
  304. package/examples/extensions/with-deps/package.json +1 -1
  305. package/examples/sdk/12-full-control.ts +3 -1
  306. package/package.json +15 -6
  307. package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -32,7 +32,7 @@ const LEVEL_RANK: Record<Exclude<AcceptanceLevel, "auto">, number> = {
32
32
  };
33
33
 
34
34
  const VALID_LEVELS = new Set<AcceptanceLevel>(["auto", "none", "attested", "checked", "verified"]);
35
- const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>([
35
+ const VALID_EVIDENCE_KINDS: AcceptanceEvidenceKind[] = [
36
36
  "changed-files",
37
37
  "tests-added",
38
38
  "commands-run",
@@ -42,7 +42,10 @@ const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>([
42
42
  "diff-summary",
43
43
  "review-findings",
44
44
  "manual-notes",
45
- ]);
45
+ ];
46
+ const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>(VALID_EVIDENCE_KINDS);
47
+ const ACCEPTANCE_EVIDENCE_HELP = `Supported evidence kinds: ${VALID_EVIDENCE_KINDS.join(", ")}. Example: { level: "checked", evidence: ["commands-run", "changed-files"] }.`;
48
+ const ACCEPTANCE_OBJECT_EXAMPLE = "Example: { level: \"checked\", evidence: [\"commands-run\", \"changed-files\"] }.";
46
49
  const ACCEPTANCE_CONFIG_KEYS = new Set(["level", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
47
50
  const ACCEPTANCE_GATE_KEYS = new Set(["id", "must", "evidence", "severity"]);
48
51
  const ACCEPTANCE_VERIFY_KEYS = new Set(["id", "command", "timeoutMs", "cwd", "env", "allowFailure"]);
@@ -153,6 +156,11 @@ function explicitAcceptanceCanDisable(explicit: AcceptanceConfig): boolean {
153
156
  return explicit.level === "none" && typeof explicit.reason === "string" && explicit.reason.trim().length > 0;
154
157
  }
155
158
 
159
+ function unsupportedEvidenceKindMessage(pathLabel: string, item: unknown): string {
160
+ const value = typeof item === "string" ? ` "${item}"` : "";
161
+ return `${pathLabel}${value} is not a supported evidence kind. ${ACCEPTANCE_EVIDENCE_HELP}`;
162
+ }
163
+
156
164
  export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"): string[] {
157
165
  const errors: string[] = [];
158
166
  if (input === undefined) return errors;
@@ -164,7 +172,7 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
164
172
  return errors;
165
173
  }
166
174
  if (!input || typeof input !== "object" || Array.isArray(input)) {
167
- errors.push(`${pathLabel} must be a string level, false, or an object.`);
175
+ errors.push(`${pathLabel} must be a string level, false, or an object. ${ACCEPTANCE_OBJECT_EXAMPLE}`);
168
176
  return errors;
169
177
  }
170
178
  const value = input as Record<string, unknown>;
@@ -202,11 +210,11 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
202
210
  criterionIds.add(normalizedId);
203
211
  }
204
212
  if (typeof gate.must !== "string" || !gate.must.trim()) errors.push(`${criterionPath}.must is required.`);
205
- if (gate.evidence !== undefined && !Array.isArray(gate.evidence)) errors.push(`${criterionPath}.evidence must be an array.`);
213
+ if (gate.evidence !== undefined && !Array.isArray(gate.evidence)) errors.push(`${criterionPath}.evidence must be an array. ${ACCEPTANCE_EVIDENCE_HELP}`);
206
214
  if (Array.isArray(gate.evidence)) {
207
215
  for (const [evidenceIndex, item] of gate.evidence.entries()) {
208
216
  if (typeof item !== "string" || !VALID_EVIDENCE.has(item as AcceptanceEvidenceKind)) {
209
- errors.push(`${criterionPath}.evidence[${evidenceIndex}] is not a supported evidence kind.`);
217
+ errors.push(unsupportedEvidenceKindMessage(`${criterionPath}.evidence[${evidenceIndex}]`, item));
210
218
  }
211
219
  }
212
220
  }
@@ -218,11 +226,11 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
218
226
  if (Array.isArray(value.evidence)) {
219
227
  for (const [index, item] of value.evidence.entries()) {
220
228
  if (typeof item !== "string" || !VALID_EVIDENCE.has(item as AcceptanceEvidenceKind)) {
221
- errors.push(`${pathLabel}.evidence[${index}] is not a supported evidence kind.`);
229
+ errors.push(unsupportedEvidenceKindMessage(`${pathLabel}.evidence[${index}]`, item));
222
230
  }
223
231
  }
224
232
  } else if (value.evidence !== undefined) {
225
- errors.push(`${pathLabel}.evidence must be an array.`);
233
+ errors.push(`${pathLabel}.evidence must be an array. ${ACCEPTANCE_EVIDENCE_HELP}`);
226
234
  }
227
235
  if (value.verify !== undefined && !Array.isArray(value.verify)) errors.push(`${pathLabel}.verify must be an array.`);
228
236
  if (Array.isArray(value.verify)) {
@@ -6,12 +6,14 @@ export const SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = "pi-subagents.capability
6
6
  export const SUBAGENT_CAPABILITY_CEILING_ENV = "SELESAI_SUBAGENT_CAPABILITY_CEILING_V1";
7
7
 
8
8
  export type SubagentCapabilityCeiling =
9
- | { allowedTools: readonly string[]; denyExtensions?: boolean }
10
- | { allowedTools?: readonly string[]; denyExtensions: boolean };
9
+ | { allowedTools: readonly string[]; allowedAgents?: readonly string[]; denyExtensions?: boolean }
10
+ | { allowedTools?: readonly string[]; allowedAgents?: readonly string[]; denyExtensions: boolean }
11
+ | { allowedTools?: readonly string[]; allowedAgents: readonly string[]; denyExtensions?: boolean };
11
12
 
12
13
  export interface ResolvedSubagentCapabilityCeiling {
13
14
  version: typeof SUBAGENT_CAPABILITY_CEILING_VERSION;
14
15
  allowedTools?: string[];
16
+ allowedAgents?: string[];
15
17
  denyExtensions: boolean;
16
18
  sources: string[];
17
19
  }
@@ -26,6 +28,8 @@ export interface SubagentCapabilityAudit {
26
28
  removedExtensionCount: number;
27
29
  requestedMcpToolCount: number;
28
30
  effectiveMcpTools: string[];
31
+ agentAllowed: boolean;
32
+ agentRestrictionSources?: string[];
29
33
  }
30
34
 
31
35
  export interface RegisterSubagentCapabilityCeilingOptions {
@@ -62,23 +66,28 @@ function validateText(value: unknown, field: string): string {
62
66
  function normalizeCeiling(ceiling: SubagentCapabilityCeiling): ResolvedSubagentCapabilityCeiling {
63
67
  if (!ceiling || typeof ceiling !== "object" || Array.isArray(ceiling)) throw new Error("Invalid capability ceiling; expected an object.");
64
68
  const hasAllowedTools = Object.hasOwn(ceiling, "allowedTools");
69
+ const hasAllowedAgents = Object.hasOwn(ceiling, "allowedAgents");
65
70
  const hasDenyExtensions = Object.hasOwn(ceiling, "denyExtensions");
66
- if (!hasAllowedTools && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools or denyExtensions.");
71
+ if (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.");
67
72
  if (hasDenyExtensions && typeof ceiling.denyExtensions !== "boolean") throw new Error("Invalid capability ceiling denyExtensions; expected a boolean.");
68
- let allowedTools: string[] | undefined;
69
- if (hasAllowedTools) {
70
- if (!Array.isArray(ceiling.allowedTools)) throw new Error("Invalid capability ceiling allowedTools; expected an array.");
71
- if (ceiling.allowedTools.length > 256) throw new Error("Invalid capability ceiling allowedTools; expected at most 256 names.");
72
- allowedTools = [...new Set(ceiling.allowedTools.map((tool) => {
73
- const name = validateText(tool, "allowedTools entry");
74
- if (!/^[A-Za-z0-9_.:-]+$/u.test(name)) throw new Error(`Invalid capability ceiling allowedTools entry '${name}'.`);
75
- if (Buffer.byteLength(name, "utf8") > 128) throw new Error(`Invalid capability ceiling allowedTools entry '${name}'; max 128 UTF-8 bytes.`);
73
+ const normalizeList = (field: "allowedTools" | "allowedAgents", pattern: RegExp): string[] | undefined => {
74
+ if (!Object.hasOwn(ceiling, field)) return undefined;
75
+ const values = ceiling[field];
76
+ if (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);
77
+ if (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);
78
+ return [...new Set(values.map((entry) => {
79
+ const name = validateText(entry, `${field} entry`);
80
+ if (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);
81
+ if (Buffer.byteLength(name, "utf8") > 128) throw new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);
76
82
  return name;
77
83
  }))].sort();
78
- }
84
+ };
85
+ const allowedTools = normalizeList("allowedTools", /^[A-Za-z0-9_.:-]+$/u);
86
+ const allowedAgents = normalizeList("allowedAgents", /^[A-Za-z0-9_.:-]+$/u);
79
87
  return {
80
88
  version: SUBAGENT_CAPABILITY_CEILING_VERSION,
81
- ...(allowedTools ? { allowedTools } : {}),
89
+ ...(allowedTools !== undefined ? { allowedTools } : {}),
90
+ ...(allowedAgents !== undefined ? { allowedAgents } : {}),
82
91
  denyExtensions: ceiling.denyExtensions === true,
83
92
  sources: [],
84
93
  };
@@ -132,14 +141,17 @@ export function registerSubagentCapabilityCeiling(options: RegisterSubagentCapab
132
141
  export function intersectSubagentCapabilityCeilings(...ceilings: Array<ResolvedSubagentCapabilityCeiling | undefined>): ResolvedSubagentCapabilityCeiling | undefined {
133
142
  const active = ceilings.filter((ceiling): ceiling is ResolvedSubagentCapabilityCeiling => ceiling !== undefined);
134
143
  if (active.length === 0) return undefined;
135
- const definedLists = active.filter((ceiling) => ceiling.allowedTools !== undefined).map((ceiling) => new Set(ceiling.allowedTools));
136
- let allowedTools: string[] | undefined;
137
- if (definedLists.length > 0) {
138
- allowedTools = [...definedLists[0]!].filter((tool) => definedLists.every((list) => list.has(tool))).sort();
139
- }
144
+ const intersectLists = (field: "allowedTools" | "allowedAgents"): string[] | undefined => {
145
+ const definedLists = active.filter((ceiling) => ceiling[field] !== undefined).map((ceiling) => new Set(ceiling[field]));
146
+ if (definedLists.length === 0) return undefined;
147
+ return [...definedLists[0]!].filter((entry) => definedLists.every((list) => list.has(entry))).sort();
148
+ };
149
+ const allowedTools = intersectLists("allowedTools");
150
+ const allowedAgents = intersectLists("allowedAgents");
140
151
  return {
141
152
  version: SUBAGENT_CAPABILITY_CEILING_VERSION,
142
- ...(allowedTools ? { allowedTools } : {}),
153
+ ...(allowedTools !== undefined ? { allowedTools } : {}),
154
+ ...(allowedAgents !== undefined ? { allowedAgents } : {}),
143
155
  denyExtensions: active.some((ceiling) => ceiling.denyExtensions),
144
156
  sources: [...new Set(active.flatMap((ceiling) => ceiling.sources))].sort(),
145
157
  };
@@ -158,6 +170,26 @@ export function resolveCurrentSubagentCapabilityCeiling(sessionId: string | unde
158
170
  return resolveSubagentCapabilityCeiling(sessionId, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
159
171
  }
160
172
 
173
+ export function isAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): boolean {
174
+ return ceiling?.allowedAgents === undefined || ceiling.allowedAgents.includes(agentName);
175
+ }
176
+
177
+ export function capabilityCeilingAgentRestrictionMessage(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {
178
+ if (isAgentAllowedByCapabilityCeiling(agentName, ceiling)) return undefined;
179
+ const sources = ceiling?.sources.length ? ceiling.sources.join(", ") : "unknown source";
180
+ const allowed = ceiling?.allowedAgents?.length ? ceiling.allowedAgents.join(", ") : "(none)";
181
+ return `Capability ceiling from ${sources} does not allow agent '${agentName}'. Allowed agents: ${allowed}.`;
182
+ }
183
+
184
+ export function assertAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): void {
185
+ const message = capabilityCeilingAgentRestrictionMessage(agentName, ceiling);
186
+ if (message) throw new Error(message);
187
+ }
188
+
189
+ export function capabilityCeilingAgentRestrictionSources(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string[] | undefined {
190
+ return ceiling?.allowedAgents === undefined ? undefined : [...ceiling.sources];
191
+ }
192
+
161
193
  export function encodeSubagentCapabilityCeiling(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {
162
194
  if (!ceiling) return undefined;
163
195
  return Buffer.from(JSON.stringify(ceiling), "utf8").toString("base64url");
@@ -1,4 +1,4 @@
1
- import { isDynamicParallelStep, isParallelStep, type ChainStep, type SequentialStep } from "../../shared/settings.ts";
1
+ import { isCheckpointStep, isDynamicParallelStep, isParallelStep, type ChainStep, type SequentialStep } from "../../shared/settings.ts";
2
2
  import type { ChainOutputMap, ChainOutputMapEntry, SingleResult } from "../../shared/types.ts";
3
3
  import { getSingleResultOutput } from "../../shared/utils.ts";
4
4
  import { DynamicFanoutError, hasDynamicFanoutFields, type DynamicFanoutConfig, validateDynamicStepShape } from "./dynamic-fanout.ts";
@@ -14,6 +14,7 @@ export interface ChainOutputValidationContext {
14
14
  }
15
15
 
16
16
  function outputNamesForStep(step: ChainStep): string[] {
17
+ if (isCheckpointStep(step)) return [];
17
18
  if (isParallelStep(step)) return step.parallel.map((task) => task.as).filter((name): name is string => Boolean(name));
18
19
  if (isDynamicParallelStep(step)) return [step.collect.as];
19
20
  const name = (step as SequentialStep).as;
@@ -21,6 +22,7 @@ function outputNamesForStep(step: ChainStep): string[] {
21
22
  }
22
23
 
23
24
  function taskTemplatesForStep(step: ChainStep): string[] {
25
+ if (isCheckpointStep(step)) return [];
24
26
  if (isParallelStep(step)) return step.parallel.map((task) => task.task ?? "{previous}");
25
27
  if (isDynamicParallelStep(step)) return [step.parallel.task ?? "{previous}", step.parallel.label ?? ""].filter(Boolean);
26
28
  return [(step as SequentialStep).task ?? "{previous}"];
@@ -51,7 +51,7 @@ const RUNNER_DYNAMIC_PARALLEL_KEYS = new Set([
51
51
  ...DYNAMIC_PARALLEL_KEYS,
52
52
  "outputName", "structured", "inheritProjectContext", "inheritSkills", "skills", "outputPath", "namespaceOutputPath", "maxSubagentDepth", "waitToolEnabled",
53
53
  "structuredOutput", "structuredOutputSchema", "tools", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "capabilityCeiling", "completionGuard", "systemPrompt",
54
- "systemPromptMode", "thinking", "modelCandidates", "sessionFile", "effectiveAcceptance", "acceptanceInput", "acceptanceRole", "parentSessionId",
54
+ "systemPromptMode", "thinking", "modelCandidates", "sessionFile", "effectiveAcceptance", "acceptanceInput", "acceptanceRole", "parentSessionId", "launchResolvedExtensions",
55
55
  ]);
56
56
  const DYNAMIC_COLLECT_KEYS = new Set(["as", "outputSchema"]);
57
57
 
@@ -27,6 +27,7 @@ type ImportKind = keyof typeof IMPORT_PATHS;
27
27
  interface ServerEntry {
28
28
  command?: string;
29
29
  args?: string[];
30
+ socket?: string;
30
31
  env?: Record<string, string>;
31
32
  cwd?: string;
32
33
  url?: string;
@@ -35,6 +36,7 @@ interface ServerEntry {
35
36
  bearerToken?: string;
36
37
  bearerTokenEnv?: string;
37
38
  exposeResources?: boolean;
39
+ includeTools?: string[];
38
40
  excludeTools?: string[];
39
41
  directTools?: boolean | string[];
40
42
  }
@@ -271,14 +273,16 @@ export function computeMcpServerHash(definition: ServerEntry): string {
271
273
  const identity: Record<string, unknown> = {
272
274
  command: definition.command,
273
275
  args: definition.args,
276
+ socket: resolveConfigPath(definition.socket),
274
277
  env: interpolateEnvRecord(definition.env),
275
278
  cwd: resolveConfigPath(definition.cwd),
276
- url: definition.url,
279
+ url: resolveServerUrl(definition),
277
280
  headers: interpolateEnvRecord(definition.headers),
278
281
  auth: definition.auth,
279
282
  bearerToken: resolveBearerToken(definition),
280
283
  bearerTokenEnv: definition.bearerTokenEnv,
281
284
  exposeResources: definition.exposeResources,
285
+ includeTools: definition.includeTools,
282
286
  excludeTools: definition.excludeTools,
283
287
  };
284
288
  return createHash("sha256").update(stableStringify(identity)).digest("hex");
@@ -333,22 +337,51 @@ function resourceNameToToolName(name: string): string {
333
337
  }
334
338
 
335
339
  function interpolateEnvRecord(values: Record<string, string> | undefined): Record<string, string> | undefined {
336
- if (!values || typeof values !== "object" || Array.isArray(values)) return undefined;
337
- const resolved: Record<string, string> = {};
338
- for (const [key, value] of Object.entries(values)) {
339
- if (typeof value === "string") resolved[key] = interpolateEnvVars(value);
340
- }
341
- return resolved;
340
+ if (!values) return undefined;
341
+ return Object.fromEntries(Object.entries(values).map(([key, value]) => [key, interpolateSecretExpression(value)]));
342
342
  }
343
343
 
344
344
  function interpolateEnvVars(value: string): string {
345
345
  return value
346
346
  .replace(/\$\{(\w+)\}/g, (_, name: string) => process.env[name] ?? "")
347
- .replace(/\$env:(\w+)/g, (_, name: string) => process.env[name] ?? "");
347
+ .replace(/\$env:(\w+)/g, (_, name: string) => process.env[name] ?? "")
348
+ .replace(/\{env:(\w+)\}/g, (_, name: string) => process.env[name] ?? "");
349
+ }
350
+
351
+ function interpolateSecretExpression(value: string): string {
352
+ if (value.startsWith("!!")) return interpolateEnvVars(value.slice(1));
353
+ return value.startsWith("!") ? value : interpolateEnvVars(value);
354
+ }
355
+
356
+ function getMissingEnvVars(value: string): string[] {
357
+ const missing = new Set<string>();
358
+ for (const match of value.matchAll(/\$\{(\w+)\}|\$env:(\w+)|\{env:(\w+)\}/g)) {
359
+ const name = match[1] ?? match[2] ?? match[3];
360
+ if (name && process.env[name] === undefined) missing.add(name);
361
+ }
362
+ return [...missing];
363
+ }
364
+
365
+ function resolveServerUrl(definition: Pick<ServerEntry, "url">): string | undefined {
366
+ if (definition.url == null) return undefined;
367
+ if (typeof definition.url !== "string") throw new Error("MCP server URL must be a string");
368
+
369
+ const missing = getMissingEnvVars(definition.url);
370
+ if (missing.length > 0) {
371
+ throw new Error(`Missing environment variable${missing.length === 1 ? "" : "s"} in MCP server URL: ${missing.join(", ")}`);
372
+ }
373
+
374
+ const resolved = interpolateEnvVars(definition.url);
375
+ try {
376
+ new URL(resolved);
377
+ } catch (error) {
378
+ throw new Error(`Invalid MCP server URL after environment interpolation: ${resolved}`, { cause: error });
379
+ }
380
+ return resolved;
348
381
  }
349
382
 
350
383
  function resolveConfigPath(value: string | undefined): string | undefined {
351
- if (typeof value !== "string") return undefined;
384
+ if (value === undefined) return undefined;
352
385
  const resolved = interpolateEnvVars(value);
353
386
  if (resolved === "~") return os.homedir();
354
387
  if (resolved.startsWith("~/") || resolved.startsWith("~\\")) return path.join(os.homedir(), resolved.slice(2));
@@ -356,8 +389,8 @@ function resolveConfigPath(value: string | undefined): string | undefined {
356
389
  }
357
390
 
358
391
  function resolveBearerToken(definition: Pick<ServerEntry, "bearerToken" | "bearerTokenEnv">): string | undefined {
359
- if (typeof definition.bearerToken === "string") return interpolateEnvVars(definition.bearerToken);
360
- return typeof definition.bearerTokenEnv === "string" ? process.env[definition.bearerTokenEnv] : undefined;
392
+ if (definition.bearerToken !== undefined) return interpolateSecretExpression(definition.bearerToken);
393
+ return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined;
361
394
  }
362
395
 
363
396
  function stableStringify(value: unknown): string {
@@ -30,6 +30,14 @@ export interface ParentModel {
30
30
  id: string;
31
31
  }
32
32
 
33
+ export function normalizeParentModel(model: unknown): ParentModel | undefined {
34
+ if (!model || typeof model !== "object") return undefined;
35
+ const candidate = model as { provider?: unknown; id?: unknown };
36
+ if (typeof candidate.provider !== "string" || typeof candidate.id !== "string") return undefined;
37
+ if (!candidate.provider || !candidate.id) return undefined;
38
+ return { provider: candidate.provider, id: candidate.id };
39
+ }
40
+
33
41
  /**
34
42
  * Normalize a model id or provider segment for fuzzy comparison: case-fold,
35
43
  * treat dots/underscores as dashes (so `4.5` matches `4-5`), and collapse
@@ -7,6 +7,8 @@ import {
7
7
  TEMP_ROOT_DIR,
8
8
  type AsyncJobState,
9
9
  type AsyncStatus,
10
+ type LaunchResolvedChildExtensionsV1,
11
+ type RuntimeAcknowledgedChildExtensionsV1,
10
12
  type NestedRouteInfo,
11
13
  type TurnBudgetState,
12
14
  type NestedRunSummary,
@@ -197,6 +199,55 @@ function sanitizeCost(value: unknown): NestedRunSummary["totalCost"] | undefined
197
199
  : undefined;
198
200
  }
199
201
 
202
+ function sanitizeLaunchResolvedExtensions(value: unknown): LaunchResolvedChildExtensionsV1 | undefined {
203
+ if (!value || typeof value !== "object") return undefined;
204
+ const raw = value as Record<string, unknown>;
205
+ if (raw.version !== 1 || raw.source !== "launch-resolved" || typeof raw.disableAmbientExtensions !== "boolean") return undefined;
206
+ const stringList = (input: unknown): string[] => Array.isArray(input)
207
+ ? input.filter((item): item is string => typeof item === "string" && /^sha256:[a-f0-9]{16}$/.test(item)).slice(0, 32)
208
+ : [];
209
+ const omitted = raw.omitted && typeof raw.omitted === "object" ? raw.omitted as Record<string, unknown> : {};
210
+ const omittedCount = (key: string): number => Math.max(0, Math.floor(clampNumber(omitted[key]) ?? 0));
211
+ return {
212
+ version: 1,
213
+ source: "launch-resolved",
214
+ disableAmbientExtensions: raw.disableAmbientExtensions,
215
+ runtime: stringList(raw.runtime),
216
+ configured: stringList(raw.configured),
217
+ effective: stringList(raw.effective),
218
+ omitted: {
219
+ runtime: omittedCount("runtime"),
220
+ configured: omittedCount("configured"),
221
+ effective: omittedCount("effective"),
222
+ },
223
+ };
224
+ }
225
+
226
+ function sanitizeRuntimeAcknowledgedExtensions(value: unknown): RuntimeAcknowledgedChildExtensionsV1 | undefined {
227
+ if (!value || typeof value !== "object") return undefined;
228
+ const raw = value as Record<string, unknown>;
229
+ if (raw.version !== 1 || raw.source !== "child-runtime" || !Array.isArray(raw.ids)) return undefined;
230
+ const ids: string[] = [];
231
+ const seen = new Set<string>();
232
+ for (const item of raw.ids) {
233
+ if (typeof item !== "string" || item.length === 0 || item.length > 128 || !/^[A-Za-z0-9._:@+-]+$/.test(item) || item.includes("..") || item.includes("/") || item.includes("\\") || seen.has(item)) continue;
234
+ seen.add(item);
235
+ ids.push(item);
236
+ }
237
+ if (ids.length === 0) return undefined;
238
+ return {
239
+ version: 1,
240
+ source: "child-runtime",
241
+ ids: ids.slice(0, 32),
242
+ omitted: Math.max(0, ids.length - 32) + Math.max(0, Math.floor(clampNumber(raw.omitted) ?? 0)),
243
+ };
244
+ }
245
+
246
+ function runtimeAcknowledgedEntry(value: unknown): { runtimeAcknowledgedExtensions: RuntimeAcknowledgedChildExtensionsV1 } | Record<string, never> {
247
+ const sanitized = sanitizeRuntimeAcknowledgedExtensions(value);
248
+ return sanitized ? { runtimeAcknowledgedExtensions: sanitized } : {};
249
+ }
250
+
200
251
  function sanitizeTurnBudget(value: unknown): TurnBudgetState | undefined {
201
252
  if (!value || typeof value !== "object") return undefined;
202
253
  const raw = value as Record<string, unknown>;
@@ -249,6 +300,8 @@ function sanitizeStep(input: unknown, depth: number): NestedStepSummary | undefi
249
300
  ...(sanitizeTurnBudget(raw.turnBudget) ? { turnBudget: sanitizeTurnBudget(raw.turnBudget) } : {}),
250
301
  ...(raw.turnBudgetExceeded === true ? { turnBudgetExceeded: true } : {}),
251
302
  ...(raw.wrapUpRequested === true ? { wrapUpRequested: true } : {}),
303
+ ...(sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) ? { launchResolvedExtensions: sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) } : {}),
304
+ ...(sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) ? { runtimeAcknowledgedExtensions: sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) } : {}),
252
305
  ...(depth < MAX_DEPTH && Array.isArray(raw.children) ? { children: raw.children.map((child) => sanitizeSummary(child, depth + 1)).filter((child): child is NestedRunSummary => Boolean(child)).slice(0, MAX_CHILDREN) } : {}),
253
306
  };
254
307
  }
@@ -306,6 +359,8 @@ export function sanitizeSummary(input: unknown, depth = 0): NestedRunSummary | u
306
359
  ...(raw.turnBudgetExceeded === true ? { turnBudgetExceeded: true } : {}),
307
360
  ...(raw.wrapUpRequested === true ? { wrapUpRequested: true } : {}),
308
361
  ...(stringValue(raw.error, 1024) ? { error: stringValue(raw.error, 1024) } : {}),
362
+ ...(sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) ? { launchResolvedExtensions: sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) } : {}),
363
+ ...(sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) ? { runtimeAcknowledgedExtensions: sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) } : {}),
309
364
  ...(steps && steps.length > 0 ? { steps } : {}),
310
365
  ...(depth < MAX_DEPTH && Array.isArray(raw.children) ? { children: raw.children.map((child) => sanitizeSummary(child, depth + 1)).filter((child): child is NestedRunSummary => Boolean(child)).slice(0, MAX_CHILDREN) } : {}),
311
366
  };
@@ -788,32 +843,50 @@ export function writeNestedControlResult(route: NestedRoute, result: Omit<Nested
788
843
  writeRouteRecord(route.eventSink, sanitized.ts, sanitized);
789
844
  }
790
845
 
791
- export function readNestedControlResults(route: NestedRoute): NestedControlResultRecord[] {
846
+ function readControlResultsFromFile(route: NestedRoute, eventPath: string): NestedControlResultRecord[] {
847
+ if (!containedPath(route.eventSink, eventPath)) return [];
848
+ try {
849
+ const stat = fs.statSync(eventPath);
850
+ if (!stat.isFile() || stat.size > MAX_EVENT_BYTES) return [];
851
+ const content = fs.readFileSync(eventPath, "utf-8");
852
+ const lines = content.includes("\n") ? content.split("\n").filter((line) => line.trim()) : [content];
853
+ return lines.map((line) => parseControlResult(line, route)).filter((result): result is NestedControlResultRecord => Boolean(result));
854
+ } catch {
855
+ return [];
856
+ }
857
+ }
858
+
859
+ function listNestedEventFiles(route: NestedRoute): string[] {
792
860
  validateRouteShape(route);
793
- let entries: string[] = [];
794
861
  try {
795
- entries = fs.readdirSync(route.eventSink).filter((entry) => entry.endsWith(".json") || entry.endsWith(".jsonl")).sort();
862
+ return fs.readdirSync(route.eventSink).filter((entry) => entry.endsWith(".json") || entry.endsWith(".jsonl"));
796
863
  } catch (error) {
797
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
864
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
865
+ throw error;
798
866
  }
799
- const results: NestedControlResultRecord[] = [];
867
+ }
868
+
869
+ export function snapshotNestedEventFiles(route: NestedRoute): Set<string> {
870
+ return new Set(listNestedEventFiles(route));
871
+ }
872
+
873
+ export function findNestedControlResult(route: NestedRoute, requestId: string, targetRunId: string, ignoredFiles: ReadonlySet<string>): NestedControlResultRecord | undefined {
874
+ const entries = listNestedEventFiles(route)
875
+ .filter((entry) => !ignoredFiles.has(entry))
876
+ .sort()
877
+ .reverse();
800
878
  for (const entry of entries) {
801
- const eventPath = path.join(route.eventSink, entry);
802
- if (!containedPath(route.eventSink, eventPath)) continue;
803
- try {
804
- const stat = fs.statSync(eventPath);
805
- if (!stat.isFile() || stat.size > MAX_EVENT_BYTES) continue;
806
- const content = fs.readFileSync(eventPath, "utf-8");
807
- const lines = content.includes("\n") ? content.split("\n").filter((line) => line.trim()) : [content];
808
- for (const line of lines) {
809
- const result = parseControlResult(line, route);
810
- if (result) results.push(result);
811
- }
812
- } catch {
813
- continue;
814
- }
879
+ const result = readControlResultsFromFile(route, path.join(route.eventSink, entry))
880
+ .find((candidate) => candidate.requestId === requestId && candidate.targetRunId === targetRunId);
881
+ if (result) return result;
815
882
  }
816
- return results;
883
+ return undefined;
884
+ }
885
+
886
+ export function readNestedControlResults(route: NestedRoute): NestedControlResultRecord[] {
887
+ return listNestedEventFiles(route)
888
+ .sort()
889
+ .flatMap((entry) => readControlResultsFromFile(route, path.join(route.eventSink, entry)));
817
890
  }
818
891
 
819
892
  export function nestedRouteEnv(route: NestedRoute): Record<string, string> {
@@ -875,6 +948,8 @@ export function nestedSummaryFromAsyncStatus(status: AsyncStatus, asyncDir: stri
875
948
  ...(status.sessionId ? { sessionId: status.sessionId } : {}),
876
949
  mode: status.mode ?? fallback.mode,
877
950
  ...(status.processTerminal ? { processTerminal: sanitizeProcessTerminal(status.processTerminal, { runId: status.runId || fallback.id, runnerProcessInstanceId: status.processTerminal.runnerProcessInstanceId }, `${asyncDir}/status.json`) } : {}),
951
+ ...(status.launchResolvedExtensions ? { launchResolvedExtensions: status.launchResolvedExtensions } : {}),
952
+ ...runtimeAcknowledgedEntry(status.runtimeAcknowledgedExtensions),
878
953
  ...(status.capabilityCeiling ? { capabilityCeiling: status.capabilityCeiling } : {}),
879
954
  ...(status.capabilityAudit ? { capabilityAudit: status.capabilityAudit } : {}),
880
955
  state: status.state,
@@ -914,6 +989,8 @@ export function nestedSummaryFromAsyncStatus(status: AsyncStatus, asyncDir: stri
914
989
  ...(step.startedAt !== undefined ? { startedAt: step.startedAt } : {}),
915
990
  ...(step.endedAt !== undefined ? { endedAt: step.endedAt } : {}),
916
991
  ...(step.error ? { error: step.error } : {}),
992
+ ...(step.launchResolvedExtensions ? { launchResolvedExtensions: step.launchResolvedExtensions } : {}),
993
+ ...runtimeAcknowledgedEntry(step.runtimeAcknowledgedExtensions),
917
994
  ...(step.timedOut !== undefined ? { timedOut: step.timedOut } : {}),
918
995
  ...(step.stopped !== undefined ? { stopped: step.stopped } : {}),
919
996
  ...(step.turnBudget ? { turnBudget: step.turnBudget } : {}),
@@ -46,6 +46,8 @@ export interface RunnerSubagentStep {
46
46
  definitionDigest?: string;
47
47
  launchBindingTask?: string;
48
48
  launchContractDigest?: string;
49
+ launchResolvedExtensions?: import("../../shared/types.ts").LaunchResolvedChildExtensionsV1;
50
+ runtimeAcknowledgedExtensions?: import("../../shared/types.ts").RuntimeAcknowledgedChildExtensionsV1;
49
51
  effectiveAcceptance?: import("../../shared/types.ts").ResolvedAcceptanceConfig;
50
52
  acceptanceInput?: import("../../shared/types.ts").AcceptanceInput;
51
53
  acceptanceRole?: import("../../shared/types.ts").AcceptanceRole;
@@ -55,6 +57,13 @@ export interface RunnerSubagentStep {
55
57
  capabilityAudit?: import("./capability-ceiling.ts").SubagentCapabilityAudit;
56
58
  }
57
59
 
60
+ export interface RunnerCheckpointStep {
61
+ checkpoint: string;
62
+ message?: string;
63
+ phase?: string;
64
+ label?: string;
65
+ }
66
+
58
67
  export interface ParallelStepGroup {
59
68
  parallel: RunnerSubagentStep[];
60
69
  concurrency?: number;
@@ -79,7 +88,11 @@ export interface DynamicRunnerGroup {
79
88
  gateOn?: import("../../shared/types.ts").ChainGateLayer;
80
89
  }
81
90
 
82
- export type RunnerStep = RunnerSubagentStep | ParallelStepGroup | DynamicRunnerGroup;
91
+ export type RunnerStep = RunnerSubagentStep | ParallelStepGroup | DynamicRunnerGroup | RunnerCheckpointStep;
92
+
93
+ export function isCheckpointRunnerStep(step: RunnerStep): step is RunnerCheckpointStep {
94
+ return "checkpoint" in step;
95
+ }
83
96
 
84
97
  export function isParallelGroup(step: RunnerStep): step is ParallelStepGroup {
85
98
  return "parallel" in step && Array.isArray(step.parallel);
@@ -92,7 +105,9 @@ export function isDynamicRunnerGroup(step: RunnerStep): step is DynamicRunnerGro
92
105
  export function flattenSteps(steps: RunnerStep[]): RunnerSubagentStep[] {
93
106
  const flat: RunnerSubagentStep[] = [];
94
107
  for (const step of steps) {
95
- if (isParallelGroup(step)) {
108
+ if (isCheckpointRunnerStep(step)) {
109
+ continue;
110
+ } else if (isParallelGroup(step)) {
96
111
  for (const task of step.parallel) flat.push(task);
97
112
  } else if (isDynamicRunnerGroup(step)) {
98
113
  continue;
@@ -144,29 +159,45 @@ export async function mapConcurrent<T, R>(
144
159
  limit: number,
145
160
  fn: (item: T, i: number) => Promise<R>,
146
161
  globalSemaphore?: Semaphore,
162
+ /** Invoked after every worker has stopped, including workers outliving an early rejection. */
163
+ onSchedulingSettled?: () => void,
147
164
  ): Promise<R[]> {
148
165
  const safeLimit = Math.max(1, Math.floor(limit) || 1);
149
166
  const results: R[] = new Array(items.length);
150
167
  let next = 0;
168
+ let liveWorkers = Math.min(safeLimit, items.length);
169
+ const notifySchedulingSettled = () => {
170
+ try {
171
+ onSchedulingSettled?.();
172
+ } catch {
173
+ // Scheduling lifecycle cleanup must not replace the worker result.
174
+ }
175
+ };
151
176
 
152
177
  async function worker(_workerIndex: number): Promise<void> {
153
- while (next < items.length) {
154
- const i = next++;
155
- if (globalSemaphore) {
156
- await globalSemaphore.acquire();
157
- try {
178
+ try {
179
+ while (next < items.length) {
180
+ const i = next++;
181
+ if (globalSemaphore) {
182
+ await globalSemaphore.acquire();
183
+ try {
184
+ results[i] = await fn(items[i], i);
185
+ } finally {
186
+ globalSemaphore.release();
187
+ }
188
+ } else {
158
189
  results[i] = await fn(items[i], i);
159
- } finally {
160
- globalSemaphore.release();
161
190
  }
162
- } else {
163
- results[i] = await fn(items[i], i);
164
191
  }
192
+ } finally {
193
+ liveWorkers--;
194
+ if (liveWorkers === 0) notifySchedulingSettled();
165
195
  }
166
196
  }
167
197
 
198
+ if (liveWorkers === 0) notifySchedulingSettled();
168
199
  await Promise.all(
169
- Array.from({ length: Math.min(safeLimit, items.length) }, (_, wi) => worker(wi)),
200
+ Array.from({ length: liveWorkers }, (_, wi) => worker(wi)),
170
201
  );
171
202
  return results;
172
203
  }