@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
@@ -1,22 +1,24 @@
1
+ import { createHash } from "node:crypto";
1
2
  import * as fs from "node:fs";
2
3
  import * as os from "node:os";
3
4
  import * as path from "node:path";
4
- import "../../shared/env.ts";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { encodeNestedPathEnv, parseNestedPathEnv, type NestedPathEntry } from "./nested-path.ts";
7
7
  import { resolveMcpDirectToolSelections, type ResolvedMcpDirectToolSelection } from "./mcp-direct-tool-allowlist.ts";
8
8
  import { resolvePiPackageRoot } from "./pi-spawn.ts";
9
+ import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
9
10
  import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from "./structured-output.ts";
10
- import { TEMP_ROOT_DIR, type JsonSchemaObject, type ResolvedToolBudget } from "../../shared/types.ts";
11
+ import { TEMP_ROOT_DIR, type JsonSchemaObject, type LaunchResolvedChildExtensionsV1, type ResolvedToolBudget } from "../../shared/types.ts";
11
12
  import { THINKING_LEVELS } from "../../shared/model-info.ts";
12
13
  import { TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV, encodeToolBudgetEnv } from "./tool-budget.ts";
13
14
  import { CHILD_TOOL_DIAGNOSTIC_PATH_ENV, MCP_DIRECT_CHILD_TOOLS_ENV, REQUIRED_CHILD_TOOLS_ENV } from "./tool-availability.ts";
14
15
  import { CHILD_WATCHDOG_CONFIG_ENV, encodeChildWatchdogConfig, type ChildWatchdogConfig } from "../../watchdog/child-status.ts";
15
16
  import { WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
16
17
  import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../shared/utils.ts";
17
- import { SUBAGENT_CAPABILITY_CEILING_ENV, decodeSubagentCapabilityCeiling, encodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "./capability-ceiling.ts";
18
+ import { SUBAGENT_CAPABILITY_CEILING_ENV, capabilityCeilingAgentRestrictionSources, decodeSubagentCapabilityCeiling, encodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, isAgentAllowedByCapabilityCeiling, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "./capability-ceiling.ts";
18
19
 
19
20
  const TASK_ARG_LIMIT = 8000;
21
+ const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
20
22
  const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
21
23
  const FANOUT_CHILD_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "extension", "fanout-child.ts");
22
24
  export const SUBAGENT_CHILD_ENV = "SELESAI_SUBAGENT_CHILD";
@@ -39,6 +41,8 @@ export const SUBAGENT_PARENT_SESSION_ENV = "SELESAI_SUBAGENT_PARENT_SESSION";
39
41
  export const SUBAGENT_STEER_INBOX_ENV = "SELESAI_SUBAGENT_STEER_INBOX";
40
42
  export const SUBAGENT_STEER_CAPABILITY_ENV = "SELESAI_SUBAGENT_STEER_CAPABILITY";
41
43
  export const SUBAGENT_STEER_ACK_DIR_ENV = "SELESAI_SUBAGENT_STEER_ACK_DIR";
44
+ export const PI_INTERCOM_STABLE_ID_ENV = "PI_INTERCOM_STABLE_ID";
45
+ export const PI_INTERCOM_SESSION_ID_ENV = "PI_INTERCOM_SESSION_ID";
42
46
 
43
47
  export interface BuildPiArgsInput {
44
48
  parentSessionId?: string;
@@ -93,6 +97,7 @@ export interface BuildPiArgsResult {
93
97
  env: Record<string, string | undefined>;
94
98
  tempDir?: string;
95
99
  toolDiagnosticPath?: string;
100
+ runtimeAcknowledgedExtensionsPath?: string;
96
101
  capabilityAudit?: SubagentCapabilityAudit;
97
102
  }
98
103
 
@@ -123,6 +128,7 @@ export interface ResolvePiLaunchToolPlanInput {
123
128
  structuredOutput?: boolean;
124
129
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
125
130
  inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
131
+ agentName?: string;
126
132
  }
127
133
 
128
134
  export interface PiLaunchToolPlan {
@@ -145,6 +151,37 @@ export interface PiLaunchToolPlan {
145
151
  capabilityAudit?: SubagentCapabilityAudit;
146
152
  }
147
153
 
154
+ function extensionIdentifier(value: string): string {
155
+ return `sha256:${createHash("sha256").update(path.normalize(value.trim())).digest("hex").slice(0, 16)}`;
156
+ }
157
+
158
+ function boundedExtensionIdentifiers(values: string[]): { ids: string[]; omitted: number } {
159
+ const ids = [...new Set(values.map(extensionIdentifier))];
160
+ return {
161
+ ids: ids.slice(0, MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
162
+ omitted: Math.max(0, ids.length - MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
163
+ };
164
+ }
165
+
166
+ export function projectLaunchResolvedChildExtensions(toolPlan: Pick<PiLaunchToolPlan, "runtimeExtensions" | "configuredExtensions" | "extensionArgs" | "disableAmbientExtensions">): LaunchResolvedChildExtensionsV1 {
167
+ const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
168
+ const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
169
+ const effective = boundedExtensionIdentifiers(toolPlan.extensionArgs);
170
+ return {
171
+ version: 1,
172
+ source: "launch-resolved",
173
+ disableAmbientExtensions: toolPlan.disableAmbientExtensions,
174
+ runtime: runtime.ids,
175
+ configured: configured.ids,
176
+ effective: effective.ids,
177
+ omitted: {
178
+ runtime: runtime.omitted,
179
+ configured: configured.omitted,
180
+ effective: effective.omitted,
181
+ },
182
+ };
183
+ }
184
+
148
185
  export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): PiLaunchToolPlan {
149
186
  const capabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
150
187
  const allowedToolSet = capabilityCeiling?.allowedTools === undefined ? undefined : new Set(capabilityCeiling.allowedTools);
@@ -191,6 +228,8 @@ export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): Pi
191
228
  removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + ((input.tools ?? []).filter((tool) => tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")).length) : 0,
192
229
  requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
193
230
  effectiveMcpTools,
231
+ agentAllowed: input.agentName === undefined ? true : isAgentAllowedByCapabilityCeiling(input.agentName, capabilityCeiling),
232
+ ...(capabilityCeilingAgentRestrictionSources(capabilityCeiling) ? { agentRestrictionSources: capabilityCeilingAgentRestrictionSources(capabilityCeiling) } : {}),
194
233
  } satisfies SubagentCapabilityAudit : undefined;
195
234
  return {
196
235
  ...(capabilityCeiling ? { capabilityCeiling } : {}),
@@ -244,6 +283,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
244
283
  structuredOutput: input.structuredOutput,
245
284
  capabilityCeiling: input.capabilityCeiling,
246
285
  inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
286
+ agentName: input.childAgentName,
247
287
  });
248
288
  if (toolPlan.explicitToolAllowlist) {
249
289
  args.push(toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools");
@@ -254,6 +294,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
254
294
  }
255
295
  for (const extPath of toolPlan.extensionArgs) args.push("--extension", extPath);
256
296
 
297
+ if (!input.inheritProjectContext) {
298
+ args.push("--no-context-files");
299
+ }
257
300
  if (!input.inheritSkills) {
258
301
  args.push("--no-skills");
259
302
  }
@@ -281,6 +324,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
281
324
  const env: Record<string, string | undefined> = {};
282
325
  const piPackageRoot = process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
283
326
  if (piPackageRoot) env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
327
+ if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
328
+ const runtimeAcknowledgedExtensionsPath = path.join(tempDir, "runtime-acknowledged-extensions.json");
329
+ env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
284
330
  let toolDiagnosticPath: string | undefined;
285
331
  if (toolPlan.requiredChildTools.length > 0) {
286
332
  if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
@@ -329,6 +375,8 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
329
375
  : "";
330
376
  env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
331
377
  env.SELESAI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
378
+ env[PI_INTERCOM_STABLE_ID_ENV] = input.intercomSessionName || undefined;
379
+ env[PI_INTERCOM_SESSION_ID_ENV] = undefined;
332
380
  if (input.intercomSessionName) {
333
381
  env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME = input.intercomSessionName;
334
382
  }
@@ -376,7 +424,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
376
424
 
377
425
  env[SUBAGENT_PARENT_SESSION_ENV] = input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
378
426
 
379
- return { args, env, tempDir, toolDiagnosticPath, capabilityAudit: toolPlan.capabilityAudit };
427
+ return { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: toolPlan.capabilityAudit };
380
428
  }
381
429
 
382
430
  export const parseParentPathEnv = parseNestedPathEnv;
@@ -0,0 +1,19 @@
1
+ export function formatProcessSignalError(signal: string): string {
2
+ return `Subagent process terminated by signal ${signal}.`;
3
+ }
4
+
5
+ export function isUnexplainedProcessSignal(input: {
6
+ processSignal?: string | null;
7
+ interrupted?: boolean;
8
+ timedOut?: boolean;
9
+ stopped?: boolean;
10
+ turnBudgetExceeded?: boolean;
11
+ forcedDrainAfterFinalSuccess?: boolean;
12
+ }): boolean {
13
+ return Boolean(input.processSignal)
14
+ && input.interrupted !== true
15
+ && input.timedOut !== true
16
+ && input.stopped !== true
17
+ && input.turnBudgetExceeded !== true
18
+ && input.forcedDrainAfterFinalSuccess !== true;
19
+ }
@@ -18,6 +18,7 @@ const ROTATE_KEEP = 1000;
18
18
  const PRIVATE_DIR_MODE = 0o700;
19
19
  const PRIVATE_FILE_MODE = 0o600;
20
20
  const REDACTED_TASK = "[redacted]";
21
+ const historyFileStates = new Map<string, { mtimeMs: number; ctimeMs: number; size: number; ino: number; lineCount: number }>();
21
22
 
22
23
  function getHistoryPath(): string {
23
24
  return path.join(getAgentDir(), "run-history.jsonl");
@@ -30,8 +31,12 @@ function hashTask(task: string): string {
30
31
  function hardenHistoryStorage(historyPath: string): void {
31
32
  const historyDir = path.dirname(historyPath);
32
33
  fs.mkdirSync(historyDir, { recursive: true, mode: PRIVATE_DIR_MODE });
33
- try { fs.chmodSync(historyDir, PRIVATE_DIR_MODE); } catch {}
34
- try { if (fs.existsSync(historyPath)) fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
34
+ try {
35
+ if ((fs.statSync(historyDir).mode & 0o777) !== PRIVATE_DIR_MODE) fs.chmodSync(historyDir, PRIVATE_DIR_MODE);
36
+ } catch {}
37
+ try {
38
+ if ((fs.statSync(historyPath).mode & 0o777) !== PRIVATE_FILE_MODE) fs.chmodSync(historyPath, PRIVATE_FILE_MODE);
39
+ } catch {}
35
40
  }
36
41
 
37
42
  function sanitizeHistoryLine(line: string): string | undefined {
@@ -80,11 +85,39 @@ function writePrivateHistory(historyPath: string, lines: string[]): void {
80
85
  try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
81
86
  }
82
87
 
83
- function sanitizeHistoryFile(historyPath: string): void {
84
- if (!fs.existsSync(historyPath)) return;
88
+ function rememberHistoryFile(historyPath: string, lineCount: number): void {
89
+ const stat = fs.statSync(historyPath);
90
+ historyFileStates.set(historyPath, {
91
+ mtimeMs: stat.mtimeMs,
92
+ ctimeMs: stat.ctimeMs,
93
+ size: stat.size,
94
+ ino: stat.ino,
95
+ lineCount,
96
+ });
97
+ if (historyFileStates.size > 8) historyFileStates.delete(historyFileStates.keys().next().value!);
98
+ }
99
+
100
+ function sanitizeHistoryFile(historyPath: string): number {
101
+ let stat: fs.Stats;
102
+ try {
103
+ stat = fs.statSync(historyPath);
104
+ } catch (error) {
105
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return 0;
106
+ throw error;
107
+ }
108
+ const cached = historyFileStates.get(historyPath);
109
+ if (cached
110
+ && cached.mtimeMs === stat.mtimeMs
111
+ && cached.ctimeMs === stat.ctimeMs
112
+ && cached.size === stat.size
113
+ && cached.ino === stat.ino) {
114
+ return cached.lineCount;
115
+ }
85
116
  const raw = fs.readFileSync(historyPath, "utf-8");
86
117
  const { lines, changed } = sanitizeHistoryLines(raw);
87
118
  if (changed) writePrivateHistory(historyPath, lines);
119
+ rememberHistoryFile(historyPath, lines.length);
120
+ return lines.length;
88
121
  }
89
122
 
90
123
  function appendPrivateHistoryLine(historyPath: string, line: string): void {
@@ -94,7 +127,6 @@ function appendPrivateHistoryLine(historyPath: string, line: string): void {
94
127
  } finally {
95
128
  fs.closeSync(fd);
96
129
  }
97
- try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
98
130
  }
99
131
 
100
132
  export function recordRun(agent: string, task: string, exitCode: number, durationMs: number): void {
@@ -110,8 +142,11 @@ export function recordRun(agent: string, task: string, exitCode: number, duratio
110
142
  };
111
143
  const historyPath = getHistoryPath();
112
144
  hardenHistoryStorage(historyPath);
113
- try { sanitizeHistoryFile(historyPath); } catch {}
145
+ let lineCount: number | undefined;
146
+ try { lineCount = sanitizeHistoryFile(historyPath); } catch {}
114
147
  appendPrivateHistoryLine(historyPath, JSON.stringify(entry));
148
+ if (lineCount === undefined) historyFileStates.delete(historyPath);
149
+ else rememberHistoryFile(historyPath, lineCount + 1);
115
150
  } catch {
116
151
  // Best-effort — never crash the execution flow for history recording
117
152
  }
@@ -134,9 +169,10 @@ export function loadRunsForAgent(agent: string): RunEntry[] {
134
169
  lines = lines.slice(-ROTATE_KEEP);
135
170
  changed = true;
136
171
  }
137
- if (changed) {
138
- try { writePrivateHistory(historyPath, lines); } catch {}
139
- }
172
+ try {
173
+ if (changed) writePrivateHistory(historyPath, lines);
174
+ rememberHistoryFile(historyPath, lines.length);
175
+ } catch {}
140
176
 
141
177
  return lines
142
178
  .map((line) => { try { return JSON.parse(line) as RunEntry; } catch { return undefined; } })
@@ -0,0 +1,71 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { RuntimeAcknowledgedChildExtensionsV1 } from "../../shared/types.ts";
4
+
5
+ export const RUNTIME_EXTENSION_ACK_EVENT = "subagent:acknowledge-extension";
6
+ export const RUNTIME_EXTENSION_ACK_PATH_ENV = "SELESAI_SUBAGENT_RUNTIME_ACKNOWLEDGED_EXTENSIONS";
7
+ export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS = 32;
8
+ export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH = 128;
9
+ // Generous bound for a valid capture (32 ids x 128 chars plus envelope); larger files are child misbehavior.
10
+ export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_FILE_BYTES = 64 * 1024;
11
+
12
+ export function isRuntimeAcknowledgedExtensionId(value: unknown): value is string {
13
+ return typeof value === "string"
14
+ && value.length > 0
15
+ && value.length <= MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH
16
+ && /^[A-Za-z0-9._:@+-]+$/.test(value)
17
+ && !value.includes("..")
18
+ && !value.includes("/")
19
+ && !value.includes("\\");
20
+ }
21
+
22
+ export function projectRuntimeAcknowledgedExtensions(ids: Iterable<unknown>): RuntimeAcknowledgedChildExtensionsV1 | undefined {
23
+ const unique: string[] = [];
24
+ const seen = new Set<string>();
25
+ for (const id of ids) {
26
+ if (!isRuntimeAcknowledgedExtensionId(id) || seen.has(id)) continue;
27
+ seen.add(id);
28
+ unique.push(id);
29
+ }
30
+ if (unique.length === 0) return undefined;
31
+ return {
32
+ version: 1,
33
+ source: "child-runtime",
34
+ ids: unique.slice(0, MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS),
35
+ omitted: Math.max(0, unique.length - MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS),
36
+ };
37
+ }
38
+
39
+ export function sanitizeRuntimeAcknowledgedExtensions(value: unknown): RuntimeAcknowledgedChildExtensionsV1 | undefined {
40
+ if (!value || typeof value !== "object") return undefined;
41
+ const raw = value as Record<string, unknown>;
42
+ if (raw.version !== 1 || raw.source !== "child-runtime" || !Array.isArray(raw.ids)) return undefined;
43
+ const projected = projectRuntimeAcknowledgedExtensions(raw.ids);
44
+ if (!projected) return undefined;
45
+ const omitted = typeof raw.omitted === "number" && Number.isFinite(raw.omitted)
46
+ ? Math.max(0, Math.floor(raw.omitted))
47
+ : 0;
48
+ return { ...projected, omitted: projected.omitted + omitted };
49
+ }
50
+
51
+ export function readRuntimeAcknowledgedExtensions(filePath: string | undefined): RuntimeAcknowledgedChildExtensionsV1 | undefined {
52
+ if (!filePath) return undefined;
53
+ try {
54
+ if (fs.statSync(filePath).size > MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_FILE_BYTES) return undefined;
55
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
56
+ return sanitizeRuntimeAcknowledgedExtensions(parsed);
57
+ } catch {
58
+ return undefined;
59
+ }
60
+ }
61
+
62
+ export function writeRuntimeAcknowledgedExtensions(filePath: string, ids: Iterable<unknown>): void {
63
+ const projection = projectRuntimeAcknowledgedExtensions(ids);
64
+ try {
65
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
66
+ if (projection) fs.writeFileSync(filePath, JSON.stringify(projection), { mode: 0o600 });
67
+ else fs.rmSync(filePath, { force: true });
68
+ } catch {
69
+ // Runtime acknowledgement is best-effort observability only.
70
+ }
71
+ }
@@ -4,6 +4,7 @@ import type { ExtensionAPI } from "@selesai/code";
4
4
  import { registerNativeSupervisorClient } from "../../intercom/native-supervisor-channel.ts";
5
5
  import { consumeSteerRequestsFromDir, steerAckPathFromDir, writeSteerAckAt, writeSteerCapabilityAt, writeSteerRequestToDir, type SteerRequest } from "../background/control-channel.ts";
6
6
  import { SUBAGENT_CHILD_AGENT_ENV, SUBAGENT_CHILD_INDEX_ENV, SUBAGENT_FANOUT_CHILD_ENV, SUBAGENT_STEER_ACK_DIR_ENV, SUBAGENT_STEER_CAPABILITY_ENV, SUBAGENT_STEER_INBOX_ENV } from "./pi-args.ts";
7
+ import { RUNTIME_EXTENSION_ACK_EVENT, RUNTIME_EXTENSION_ACK_PATH_ENV, isRuntimeAcknowledgedExtensionId, writeRuntimeAcknowledgedExtensions } from "./runtime-acknowledged-extensions.ts";
7
8
  import { createStructuredOutputToolParameters, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
8
9
  import {
9
10
  CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
@@ -34,7 +35,7 @@ const STRUCTURED_OUTPUT_INSTRUCTIONS = [
34
35
 
35
36
  export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
36
37
  "You are a child subagent, not the parent orchestrator.",
37
- "The parent session owns delegation, orchestration, review fanout, and follow-up worker launches.",
38
+ "The parent session owns delegation, orchestration, review fanout, and follow-up builder launches.",
38
39
  "Ignore prior parent-only orchestration instructions in inherited conversation history.",
39
40
  "Do not propose or run subagents. Complete only your assigned role-specific task with the tools available to you.",
40
41
  "If you need to edit files, use the available editing tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
@@ -99,6 +100,34 @@ function refreshChildToolDiagnostic(pi: ExtensionAPI): ChildToolDiagnostic | und
99
100
  return writeChildToolDiagnostic(filePath, required, available, process.env[SUBAGENT_CHILD_AGENT_ENV]?.trim(), readMcpDirectChildTools());
100
101
  }
101
102
 
103
+ function registerRuntimeExtensionAcknowledgements(pi: ExtensionAPI): void {
104
+ const outputPath = process.env[RUNTIME_EXTENSION_ACK_PATH_ENV]?.trim();
105
+ if (!outputPath) return;
106
+ const ids: string[] = [];
107
+ let finalized = false;
108
+ const acknowledge = (payload: unknown): undefined => {
109
+ if (finalized || !payload || typeof payload !== "object") return undefined;
110
+ const id = (payload as { id?: unknown }).id;
111
+ if (isRuntimeAcknowledgedExtensionId(id)) ids.push(id);
112
+ return undefined;
113
+ };
114
+ const finalize = (): undefined => {
115
+ if (finalized) return undefined;
116
+ finalized = true;
117
+ writeRuntimeAcknowledgedExtensions(outputPath, ids);
118
+ return undefined;
119
+ };
120
+ try {
121
+ const events = (pi as { events?: { on?: (event: string, handler: (payload: unknown) => unknown) => unknown } }).events;
122
+ events?.on?.(RUNTIME_EXTENSION_ACK_EVENT, acknowledge);
123
+ const onRuntimeEvent = pi.on as unknown as (event: string, handler: (event?: unknown, ctx?: unknown) => unknown) => void;
124
+ onRuntimeEvent("agent_end", finalize);
125
+ onRuntimeEvent("session_shutdown", finalize);
126
+ } catch {
127
+ // Acknowledgement collection is optional observability and must not affect child execution.
128
+ }
129
+ }
130
+
102
131
  function findSectionEnd(prompt: string, startIndex: number, nextHeaders: string[]): number {
103
132
  let endIndex = prompt.length;
104
133
  for (const header of nextHeaders) {
@@ -346,6 +375,7 @@ export function registerSteeringInbox(
346
375
  }
347
376
 
348
377
  export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
378
+ registerRuntimeExtensionAcknowledgements(pi);
349
379
  registerSteeringInbox(pi);
350
380
  registerToolBudget(pi, decodeToolBudgetEnv(process.env[TOOL_BUDGET_ENV], { allowZero: process.env[TOOL_BUDGET_ZERO_AUTH_ENV] === "1" }));
351
381
  registerChildWatchdog(pi);
@@ -0,0 +1,101 @@
1
+ import type { Usage } from "../../shared/types.ts";
2
+
3
+ /**
4
+ * Retry delays for child processes that exit before any model or tool activity.
5
+ * The schedule is intentionally short and bounded to avoid amplifying persistent
6
+ * launch failures while allowing concurrent Pi startup locks to clear.
7
+ */
8
+ export const SUBAGENT_STARTUP_RETRY_DELAYS_MS = [250, 750, 1500] as const;
9
+
10
+ /** A genuine startup race should fail well before a model request can complete. */
11
+ export const MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS = 2000;
12
+
13
+ export interface SubagentStartupFailureEvidence {
14
+ exitCode: number | null | undefined;
15
+ error?: string;
16
+ finalOutput?: string;
17
+ messageCount: number;
18
+ toolCount: number;
19
+ usage: Usage;
20
+ durationMs: number;
21
+ protocolError?: unknown;
22
+ processSignal?: string | null;
23
+ observedMutationAttempt?: boolean;
24
+ detached?: boolean;
25
+ interrupted?: boolean;
26
+ timedOut?: boolean;
27
+ stopped?: boolean;
28
+ turnBudgetExceeded?: boolean;
29
+ }
30
+
31
+ function hasUsage(usage: Usage): boolean {
32
+ return usage.input !== 0
33
+ || usage.output !== 0
34
+ || usage.cacheRead !== 0
35
+ || usage.cacheWrite !== 0
36
+ || usage.cost !== 0
37
+ || usage.turns !== 0;
38
+ }
39
+
40
+ /**
41
+ * Classify only unexplained, zero-activity child exits as startup failures.
42
+ * This intentionally fails closed: any model output, tool activity, usage,
43
+ * mutation evidence, diagnostic, or lifecycle interruption prevents a retry.
44
+ */
45
+ export function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailureEvidence): boolean {
46
+ return evidence.exitCode !== undefined
47
+ && evidence.exitCode !== null
48
+ && evidence.exitCode !== 0
49
+ && !evidence.error?.trim()
50
+ && !evidence.finalOutput?.trim()
51
+ && evidence.messageCount === 0
52
+ && evidence.toolCount === 0
53
+ && !hasUsage(evidence.usage)
54
+ && evidence.durationMs >= 0
55
+ && evidence.durationMs <= MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS
56
+ && evidence.protocolError === undefined
57
+ && (evidence.processSignal === undefined || evidence.processSignal === null)
58
+ && evidence.observedMutationAttempt !== true
59
+ && evidence.detached !== true
60
+ && evidence.interrupted !== true
61
+ && evidence.timedOut !== true
62
+ && evidence.stopped !== true
63
+ && evidence.turnBudgetExceeded !== true;
64
+ }
65
+
66
+ export function formatSubagentStartupRetryNote(input: {
67
+ model: string;
68
+ attempt: number;
69
+ maxAttempts: number;
70
+ delayMs: number;
71
+ }): string {
72
+ return `[startup-retry] ${input.model} exited before model or tool activity (attempt ${input.attempt}/${input.maxAttempts}). Retrying the same model in ${input.delayMs}ms.`;
73
+ }
74
+
75
+ export function formatSubagentStartupRetryExhaustedError(input: {
76
+ model: string;
77
+ attempts: number;
78
+ }): string {
79
+ return `Subagent failed to start after ${input.attempts} attempts on ${input.model}; no model, tool, output, usage, or diagnostic activity was observed. This may be a concurrent Pi startup race. Retry the run or temporarily lower subagent concurrency.`;
80
+ }
81
+
82
+ /** Wait for a retry delay, returning false when any lifecycle signal aborts it. */
83
+ export async function waitForSubagentStartupRetry(delayMs: number, signals: Array<AbortSignal | undefined>): Promise<boolean> {
84
+ const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
85
+ if (activeSignals.some((signal) => signal.aborted)) return false;
86
+ if (delayMs <= 0) return true;
87
+
88
+ return await new Promise<boolean>((resolve) => {
89
+ let settled = false;
90
+ const finish = (shouldRetry: boolean): void => {
91
+ if (settled) return;
92
+ settled = true;
93
+ clearTimeout(timer);
94
+ for (const signal of activeSignals) signal.removeEventListener("abort", abort);
95
+ resolve(shouldRetry);
96
+ };
97
+ const abort = (): void => finish(false);
98
+ const timer = setTimeout(() => finish(true), delayMs);
99
+ for (const signal of activeSignals) signal.addEventListener("abort", abort, { once: true });
100
+ });
101
+ }
@@ -40,7 +40,7 @@ const REVIEWER_REQUIRED_EDIT_PATTERNS = [
40
40
  const NO_EDIT_PROHIBITION_PATTERN = /\b(?:do not|don't|must not)\s+(?:edit|modify|write(?:\s+to)?|touch|change)\b((?:(?!\b(?:but|and|then)\b)[^.;,:!?\n–—-])*)/gi;
41
41
 
42
42
  /** Objects of a no-edit prohibition that mean "the codebase in general" rather than a named scope. */
43
- const GENERIC_PROHIBITION_OBJECT = /^\s*(?:(?:any|all|the|these|those|your|our|existing|project|source|sources|repo|repository)[\s/,-]*)*(?:files?|code|codebase|sources?|anything|repo(?:sitory)?)?\s*$/i;
43
+ const GENERIC_PROHIBITION_OBJECT = /^\s*(?:(?:any|all|the|these|those|your|our|existing|project|product|source|sources|config|configs|repo|repository)[\s/,-]*)*(?:files?|code|codebase|sources?|anything|repo(?:sitory)?)?\s*$/i;
44
44
 
45
45
  const SCOPED_NO_EDIT_CONSTRAINT_PATTERNS = [
46
46
  /\bdo not edit files?\s+outside\b/i,
@@ -0,0 +1,65 @@
1
+ import type { CostSummary, UsageBudgetConfig, UsageBudgetLimitConfig, UsageBudgetState } from "../../shared/types.ts";
2
+
3
+ function validateLimit(value: unknown, label: string): { limit?: UsageBudgetLimitConfig; error?: string } {
4
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { error: `${label} must be an object.` };
5
+ const raw = value as Record<string, unknown>;
6
+ const unknown = Object.keys(raw).find((key) => key !== "soft" && key !== "hard");
7
+ if (unknown) return { error: `${label}.${unknown} is not supported.` };
8
+ if (typeof raw.hard !== "number" || !Number.isFinite(raw.hard) || raw.hard <= 0) return { error: `${label}.hard must be a positive number.` };
9
+ if (raw.soft !== undefined && (typeof raw.soft !== "number" || !Number.isFinite(raw.soft) || raw.soft <= 0)) return { error: `${label}.soft must be a positive number.` };
10
+ if (typeof raw.soft === "number" && raw.soft > raw.hard) return { error: `${label}.soft must be less than or equal to ${label}.hard.` };
11
+ return { limit: { ...(typeof raw.soft === "number" ? { soft: raw.soft } : {}), hard: raw.hard } };
12
+ }
13
+
14
+ export function validateUsageBudgetConfig(value: unknown, label = "usageBudget"): { budget?: UsageBudgetConfig; error?: string } {
15
+ if (value === undefined) return {};
16
+ if (!value || typeof value !== "object" || Array.isArray(value)) return { error: `${label} must be an object.` };
17
+ const raw = value as Record<string, unknown>;
18
+ const unknown = Object.keys(raw).find((key) => key !== "tokens" && key !== "costUsd");
19
+ if (unknown) return { error: `${label}.${unknown} is not supported.` };
20
+ const budget: UsageBudgetConfig = {};
21
+ if (raw.tokens !== undefined) {
22
+ const tokens = validateLimit(raw.tokens, `${label}.tokens`);
23
+ if (tokens.error) return { error: tokens.error };
24
+ budget.tokens = tokens.limit;
25
+ }
26
+ if (raw.costUsd !== undefined) {
27
+ const costUsd = validateLimit(raw.costUsd, `${label}.costUsd`);
28
+ if (costUsd.error) return { error: costUsd.error };
29
+ budget.costUsd = costUsd.limit;
30
+ }
31
+ if (!budget.tokens && !budget.costUsd) return { error: `${label} must include tokens or costUsd.` };
32
+ return { budget };
33
+ }
34
+
35
+ function metricState(limit: UsageBudgetLimitConfig | undefined, used: number): UsageBudgetState["tokens"] {
36
+ if (!limit) return undefined;
37
+ return {
38
+ ...limit,
39
+ used,
40
+ outcome: used >= limit.hard ? "hard-exceeded" : limit.soft !== undefined && used >= limit.soft ? "soft-exceeded" : "within-budget",
41
+ };
42
+ }
43
+
44
+ export function usageBudgetState(config: UsageBudgetConfig | undefined, totals: CostSummary | undefined): UsageBudgetState | undefined {
45
+ if (!config) return undefined;
46
+ const inputTokens = totals?.inputTokens ?? 0;
47
+ const outputTokens = totals?.outputTokens ?? 0;
48
+ const tokens = metricState(config.tokens, inputTokens + outputTokens);
49
+ const costUsd = metricState(config.costUsd, totals?.costUsd ?? 0);
50
+ const reason = tokens?.outcome === "hard-exceeded" ? "tokens" : costUsd?.outcome === "hard-exceeded" ? "costUsd" : undefined;
51
+ return {
52
+ version: 1,
53
+ source: "reported",
54
+ ...(tokens ? { tokens } : {}),
55
+ ...(costUsd ? { costUsd } : {}),
56
+ exhausted: reason !== undefined,
57
+ ...(reason ? { reason } : {}),
58
+ };
59
+ }
60
+
61
+ export function usageBudgetExceededMessage(state: UsageBudgetState): string {
62
+ if (state.reason === "tokens" && state.tokens) return `Usage budget exhausted: reported tokens ${state.tokens.used} reached hard limit ${state.tokens.hard}.`;
63
+ if (state.reason === "costUsd" && state.costUsd) return `Usage budget exhausted: reported cost $${state.costUsd.used.toFixed(6)} reached hard limit $${state.costUsd.hard.toFixed(6)}.`;
64
+ return "Usage budget exhausted.";
65
+ }
@@ -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 { SingleResult, SubagentRunMode, WorkflowGraphNode, WorkflowGraphSnapshot, WorkflowNodeStatus } from "../../shared/types.ts";
3
3
 
4
4
  export interface WorkflowGraphBuildInput {
@@ -26,6 +26,8 @@ function normalizeStatus(status: string | undefined): WorkflowNodeStatus | undef
26
26
  return "paused";
27
27
  case "detached":
28
28
  return "detached";
29
+ case "rejected":
30
+ return "rejected";
29
31
  case "pending":
30
32
  return "pending";
31
33
  default:
@@ -62,6 +64,7 @@ function seqLabel(step: SequentialStep, stepIndex: number): string {
62
64
 
63
65
  function summarizeParallelStatuses(statuses: WorkflowNodeStatus[]): WorkflowNodeStatus {
64
66
  if (statuses.some((status) => status === "running")) return "running";
67
+ if (statuses.some((status) => status === "rejected")) return "rejected";
65
68
  if (statuses.some((status) => status === "failed")) return "failed";
66
69
  if (statuses.some((status) => status === "paused")) return "paused";
67
70
  if (statuses.some((status) => status === "detached")) return "detached";
@@ -78,6 +81,28 @@ export function buildWorkflowGraphSnapshot(input: WorkflowGraphBuildInput): Work
78
81
 
79
82
  for (let stepIndex = 0; stepIndex < input.steps.length; stepIndex++) {
80
83
  const step = input.steps[stepIndex]!;
84
+ if (isCheckpointStep(step)) {
85
+ const status = normalizeStatus(input.stepStatuses?.[flatIndex]?.status)
86
+ ?? (input.currentStepIndex === stepIndex ? "paused" : "pending");
87
+ const checkpoint = { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: status === "rejected" ? "rejected" as const : status === "completed" ? "approved" as const : "pending" as const, stepIndex };
88
+ const id = `step-${stepIndex}`;
89
+ nodes.push({
90
+ id,
91
+ kind: "checkpoint",
92
+ phase: step.phase,
93
+ label: step.label?.trim() || step.checkpoint,
94
+ status,
95
+ flatIndex,
96
+ stepIndex,
97
+ checkpoint,
98
+ error: input.stepStatuses?.[flatIndex]?.error,
99
+ });
100
+ pushPhase(phases, step.phase, id);
101
+ if (status === "paused" || input.currentStepIndex === stepIndex) currentNodeId = id;
102
+ flatIndex++;
103
+ continue;
104
+ }
105
+
81
106
  if (isParallelStep(step)) {
82
107
  const groupId = `step-${stepIndex}`;
83
108
  const children: WorkflowGraphNode[] = [];