@selesai/code 0.5.28 → 0.6.1

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 (341) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/credential-print.d.ts +23 -0
  10. package/dist/cli/credential-print.d.ts.map +1 -0
  11. package/dist/cli/credential-print.js +117 -0
  12. package/dist/cli/credential-print.js.map +1 -0
  13. package/dist/cli/startup-ui.d.ts.map +1 -1
  14. package/dist/cli/startup-ui.js +1 -1
  15. package/dist/cli/startup-ui.js.map +1 -1
  16. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  17. package/dist/core/agent-session-runtime.js +3 -0
  18. package/dist/core/agent-session-runtime.js.map +1 -1
  19. package/dist/core/agent-session.d.ts +12 -1
  20. package/dist/core/agent-session.d.ts.map +1 -1
  21. package/dist/core/agent-session.js +20 -14
  22. package/dist/core/agent-session.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts.map +1 -1
  24. package/dist/core/compaction/compaction.js +11 -3
  25. package/dist/core/compaction/compaction.js.map +1 -1
  26. package/dist/core/extensions/runner.d.ts +1 -0
  27. package/dist/core/extensions/runner.d.ts.map +1 -1
  28. package/dist/core/extensions/runner.js +11 -0
  29. package/dist/core/extensions/runner.js.map +1 -1
  30. package/dist/core/extensions/types.d.ts +14 -1
  31. package/dist/core/extensions/types.d.ts.map +1 -1
  32. package/dist/core/extensions/types.js.map +1 -1
  33. package/dist/core/footer-data-provider.d.ts +10 -0
  34. package/dist/core/footer-data-provider.d.ts.map +1 -1
  35. package/dist/core/footer-data-provider.js +1 -1
  36. package/dist/core/footer-data-provider.js.map +1 -1
  37. package/dist/core/llama/provider.d.ts.map +1 -1
  38. package/dist/core/llama/provider.js +8 -3
  39. package/dist/core/llama/provider.js.map +1 -1
  40. package/dist/core/model-config.d.ts +30 -0
  41. package/dist/core/model-config.d.ts.map +1 -1
  42. package/dist/core/model-config.js +6 -0
  43. package/dist/core/model-config.js.map +1 -1
  44. package/dist/core/model-registry.d.ts.map +1 -1
  45. package/dist/core/model-registry.js +2 -2
  46. package/dist/core/model-registry.js.map +1 -1
  47. package/dist/core/model-resolver.d.ts +1 -0
  48. package/dist/core/model-resolver.d.ts.map +1 -1
  49. package/dist/core/model-resolver.js +20 -3
  50. package/dist/core/model-resolver.js.map +1 -1
  51. package/dist/core/model-runtime.d.ts +2 -0
  52. package/dist/core/model-runtime.d.ts.map +1 -1
  53. package/dist/core/model-runtime.js +5 -4
  54. package/dist/core/model-runtime.js.map +1 -1
  55. package/dist/core/package-manager.d.ts.map +1 -1
  56. package/dist/core/package-manager.js +13 -6
  57. package/dist/core/package-manager.js.map +1 -1
  58. package/dist/core/remote-catalog-provider.d.ts +1 -1
  59. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  60. package/dist/core/remote-catalog-provider.js +24 -11
  61. package/dist/core/remote-catalog-provider.js.map +1 -1
  62. package/dist/core/resource-loader.d.ts +15 -0
  63. package/dist/core/resource-loader.d.ts.map +1 -1
  64. package/dist/core/resource-loader.js +66 -9
  65. package/dist/core/resource-loader.js.map +1 -1
  66. package/dist/core/settings-manager.d.ts +1 -1
  67. package/dist/core/settings-manager.d.ts.map +1 -1
  68. package/dist/core/settings-manager.js.map +1 -1
  69. package/dist/core/system-prompt.d.ts.map +1 -1
  70. package/dist/core/system-prompt.js +19 -1
  71. package/dist/core/system-prompt.js.map +1 -1
  72. package/dist/core/system-prompt.test.d.ts +2 -0
  73. package/dist/core/system-prompt.test.d.ts.map +1 -0
  74. package/dist/core/system-prompt.test.js +89 -0
  75. package/dist/core/system-prompt.test.js.map +1 -0
  76. package/dist/core/tools/bash.d.ts +2 -0
  77. package/dist/core/tools/bash.d.ts.map +1 -1
  78. package/dist/core/tools/bash.js +34 -5
  79. package/dist/core/tools/bash.js.map +1 -1
  80. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  81. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  82. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  83. package/dist/defaults/models.json +13 -45
  84. package/dist/defaults/settings.json +1 -2
  85. package/dist/extensions/copy-turn.test.ts +131 -0
  86. package/dist/extensions/copy-turn.ts +6 -1
  87. package/dist/extensions/package.json +0 -1
  88. package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
  89. package/dist/extensions/pi-intercom/README.md +102 -39
  90. package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
  91. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
  92. package/dist/extensions/pi-intercom/broker/client.ts +315 -12
  93. package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
  94. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
  95. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
  96. package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
  97. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
  98. package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
  99. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
  100. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
  101. package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
  102. package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
  103. package/dist/extensions/pi-intercom/config.test.ts +93 -0
  104. package/dist/extensions/pi-intercom/config.ts +55 -6
  105. package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
  106. package/dist/extensions/pi-intercom/cwd.ts +31 -0
  107. package/dist/extensions/pi-intercom/extension-api.ts +44 -0
  108. package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
  109. package/dist/extensions/pi-intercom/format-context.ts +32 -0
  110. package/dist/extensions/pi-intercom/index.ts +742 -145
  111. package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
  112. package/dist/extensions/pi-intercom/package.json +15 -5
  113. package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
  114. package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
  115. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
  116. package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
  117. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
  118. package/dist/extensions/pi-intercom/types.ts +94 -4
  119. package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
  120. package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
  121. package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
  122. package/dist/extensions/pi-subagents/CHANGELOG.md +88 -0
  123. package/dist/extensions/pi-subagents/LICENSE +21 -0
  124. package/dist/extensions/pi-subagents/README.md +158 -69
  125. package/dist/extensions/pi-subagents/agents/architect.md +5 -4
  126. package/dist/extensions/pi-subagents/agents/builder.md +6 -4
  127. package/dist/extensions/pi-subagents/agents/commentator.md +3 -2
  128. package/dist/extensions/pi-subagents/agents/explorer.md +3 -2
  129. package/dist/extensions/pi-subagents/agents/recapper.md +3 -2
  130. package/dist/extensions/pi-subagents/agents/researcher.md +4 -3
  131. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  132. package/dist/extensions/pi-subagents/package.json +3 -3
  133. package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -988
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +257 -0
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +431 -0
  137. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
  138. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +281 -0
  139. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +125 -37
  140. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
  141. package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
  142. package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
  143. package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +125 -0
  144. package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
  145. package/dist/extensions/pi-subagents/src/api/preflight.ts +17 -13
  146. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
  147. package/dist/extensions/pi-subagents/src/extension/index.ts +22 -8
  148. package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
  149. package/dist/extensions/pi-subagents/src/extension/schemas.ts +33 -8
  150. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +32 -14
  151. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
  152. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
  153. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +86 -21
  154. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
  155. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
  156. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  157. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
  158. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
  159. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +56 -5
  160. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +92 -11
  161. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +388 -29
  163. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
  164. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
  165. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +214 -60
  166. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +546 -252
  167. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
  168. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +553 -154
  169. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +19 -13
  170. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
  171. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
  172. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  173. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
  174. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
  175. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
  176. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
  177. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
  178. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
  179. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
  180. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
  181. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +63 -9
  182. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
  183. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
  184. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +22 -1
  185. package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
  186. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
  187. package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
  188. package/dist/extensions/pi-subagents/src/shared/types.ts +196 -12
  189. package/dist/extensions/pi-subagents/src/shared/utils.ts +102 -5
  190. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +10 -1
  191. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
  192. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
  193. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
  194. package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
  195. package/dist/extensions/pi-subagents/src/tui/render.ts +55 -34
  196. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
  197. package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
  198. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
  199. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
  200. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
  201. package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
  202. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +113 -8
  203. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +419 -47
  204. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
  205. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
  206. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
  207. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +62 -22
  208. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
  209. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +8 -6
  210. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +231 -19
  211. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +48 -7
  212. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
  213. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +199 -17
  214. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +898 -16
  215. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
  216. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
  217. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  218. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +18 -2
  219. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
  220. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
  221. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
  222. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +198 -6
  223. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +161 -1
  224. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
  225. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
  226. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
  227. package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +63 -0
  228. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +136 -0
  229. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
  230. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
  231. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
  232. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
  233. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
  234. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
  235. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +28 -2
  236. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
  237. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
  238. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
  239. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
  240. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +17 -12
  241. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
  242. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
  243. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
  244. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +39 -2
  245. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
  246. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
  247. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
  248. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +48 -0
  249. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
  250. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
  251. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
  252. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
  253. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
  254. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +46 -2
  255. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +91 -1
  256. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  257. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
  258. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
  259. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
  260. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
  261. package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +213 -0
  262. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -1
  263. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +61 -10
  264. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
  265. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
  266. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
  267. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
  268. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
  269. package/dist/main.d.ts.map +1 -1
  270. package/dist/main.js +43 -0
  271. package/dist/main.js.map +1 -1
  272. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  273. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  274. package/dist/modes/interactive/components/custom-message.js +10 -2
  275. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  276. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  277. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  278. package/dist/modes/interactive/components/extension-editor.js +16 -46
  279. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  280. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/model-selector.js +4 -1
  282. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  283. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  284. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  285. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  286. package/dist/modes/interactive/external-editor.d.ts +12 -0
  287. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  288. package/dist/modes/interactive/external-editor.js +37 -0
  289. package/dist/modes/interactive/external-editor.js.map +1 -0
  290. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  291. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  292. package/dist/modes/interactive/interactive-mode.js +72 -64
  293. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  294. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  295. package/dist/modes/rpc/rpc-mode.js +14 -0
  296. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  297. package/dist/skills/ponytail/SKILL.md +1 -3
  298. package/dist/utils/clipboard.d.ts.map +1 -1
  299. package/dist/utils/clipboard.js +19 -8
  300. package/dist/utils/clipboard.js.map +1 -1
  301. package/dist/utils/version-check.d.ts.map +1 -1
  302. package/dist/utils/version-check.js +1 -1
  303. package/dist/utils/version-check.js.map +1 -1
  304. package/docs/compaction.md +1 -1
  305. package/docs/custom-provider.md +14 -5
  306. package/docs/environment-variables.md +86 -0
  307. package/docs/extensions.md +15 -5
  308. package/docs/index.md +1 -0
  309. package/docs/models.md +9 -2
  310. package/docs/plans/subagent-delegation/phase-0-correctness.md +265 -0
  311. package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +486 -0
  312. package/docs/plans/subagent-delegation/phase-2-context-controls.md +282 -0
  313. package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +362 -0
  314. package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +381 -0
  315. package/docs/providers.md +21 -2
  316. package/docs/rpc.md +23 -6
  317. package/docs/session-format.md +2 -0
  318. package/docs/settings.md +1 -1
  319. package/docs/usage.md +0 -13
  320. package/examples/extensions/custom-compaction.ts +5 -2
  321. package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
  322. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  323. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  324. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  325. package/examples/extensions/gondolin/package-lock.json +2 -2
  326. package/examples/extensions/gondolin/package.json +1 -1
  327. package/examples/extensions/handoff.ts +11 -3
  328. package/examples/extensions/message-renderer.ts +3 -3
  329. package/examples/extensions/sandbox/package-lock.json +2 -2
  330. package/examples/extensions/sandbox/package.json +1 -1
  331. package/examples/extensions/summarize.ts +5 -2
  332. package/examples/extensions/with-deps/package-lock.json +2 -2
  333. package/examples/extensions/with-deps/package.json +1 -1
  334. package/examples/sdk/12-full-control.ts +3 -1
  335. package/package.json +16 -7
  336. package/dist/extensions/caveman/caveman-instructions.cjs +0 -11
  337. package/dist/extensions/caveman/index.js +0 -118
  338. package/dist/extensions/caveman/package.json +0 -8
  339. package/dist/extensions/caveman/test/extension.test.js +0 -203
  340. package/dist/extensions/caveman/test/helpers.test.js +0 -58
  341. package/dist/skills/caveman/SKILL.md +0 -50
@@ -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
+ }
@@ -1,9 +1,19 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import type { Message } from "@earendil-works/pi-ai";
4
- import type { OutputMode, SavedOutputReference } from "../../shared/types.ts";
4
+ import type { MaxOutputConfig, OutputMode, SavedOutputReference } from "../../shared/types.ts";
5
+ import { truncateOutput } from "../../shared/types.ts";
5
6
  import { hasMutationToolCapability } from "./completion-guard.ts";
6
7
 
8
+ /**
9
+ * Fixed internal context-fallback cap used when persisted output is unavailable
10
+ * (persistence/read failure, `output: false`, or legacy results without a saved
11
+ * path). Deliberately not exposed as a public parameter; reuse `truncateOutput()`
12
+ * so the excerpt is bounded by both lines and bytes and never falls back to
13
+ * unbounded output.
14
+ */
15
+ export const CONTEXT_FALLBACK_LIMIT: Required<MaxOutputConfig> = { bytes: 4096, lines: 80 };
16
+
7
17
  export interface SingleOutputSnapshot {
8
18
  exists: boolean;
9
19
  mtimeMs?: number;
@@ -125,6 +135,34 @@ function formatByteSize(bytes: number): string {
125
135
  return `${value.toFixed(1)} ${units[unitIndex]}`;
126
136
  }
127
137
 
138
+ /**
139
+ * Bounded fallback for deliveries where the durable full output cannot be
140
+ * referenced: persistence/write errors, unreadable saved paths, or no saved
141
+ * path at all (legacy/`output: false`). Contains process status, the intended
142
+ * output path, the concrete error, and a bounded 80-line/4-KiB excerpt of the
143
+ * available output. Never claims a full artifact exists.
144
+ */
145
+ export function formatBoundedPersistenceFallback(input: {
146
+ error: string;
147
+ outputPath?: string;
148
+ fullOutput: string;
149
+ exitCode: number;
150
+ processError?: string;
151
+ }): string {
152
+ const status = input.processError?.trim()
153
+ ? `Process status: ${input.processError}`
154
+ : `Process status: ${input.exitCode === 0 ? "completed" : `failed (exit ${input.exitCode})`}`;
155
+ const bounded = truncateOutput(input.fullOutput, CONTEXT_FALLBACK_LIMIT);
156
+ const lines = [
157
+ `[Full output unavailable] ${input.error}`,
158
+ status,
159
+ ...(input.outputPath ? [`Intended output path: ${input.outputPath}`] : []),
160
+ "Full output is unavailable; showing a bounded excerpt (first 80 lines / 4 KiB).",
161
+ bounded.text,
162
+ ];
163
+ return lines.join("\n");
164
+ }
165
+
128
166
  export function formatSavedOutputReference(savedPath: string, fullOutput: string): SavedOutputReference {
129
167
  const absolutePath = path.resolve(savedPath);
130
168
  const bytes = Buffer.byteLength(fullOutput, "utf-8");
@@ -217,19 +255,35 @@ export function finalizeSingleOutput(params: {
217
255
  savedPath?: string;
218
256
  outputReference?: SavedOutputReference;
219
257
  saveError?: string;
258
+ error?: string;
220
259
  }): { displayOutput: string; savedPath?: string; outputReference?: SavedOutputReference; saveError?: string } {
221
- let displayOutput = params.truncatedOutput || params.fullOutput;
222
- if (params.exitCode === 0 && params.savedPath) {
260
+ if (params.savedPath) {
223
261
  const outputReference = params.outputReference ?? formatSavedOutputReference(params.savedPath, params.fullOutput);
224
- if (params.outputMode === "file-only") {
262
+ if (params.exitCode === 0 && params.outputMode === "file-only") {
225
263
  return { displayOutput: outputReference.message, savedPath: params.savedPath, outputReference };
226
264
  }
227
- displayOutput += `\n\n${outputReference.message}`;
265
+ if (params.exitCode !== 0) {
266
+ // Failed runs with a successfully persisted result surface the error/status
267
+ // plus the saved-output reference, never raw child output.
268
+ const status = params.error?.trim() || `Subagent failed with exit code ${params.exitCode}`;
269
+ return { displayOutput: `${status}\n\n${outputReference.message}`, savedPath: params.savedPath, outputReference };
270
+ }
271
+ const displayOutput = `${params.truncatedOutput || params.fullOutput}\n\n${outputReference.message}`;
228
272
  return { displayOutput, savedPath: params.savedPath, outputReference };
229
273
  }
230
- if (params.exitCode === 0 && params.saveError && params.outputPath) {
231
- displayOutput += `\n\nOutput file error: ${params.outputPath}\n${params.saveError}`;
232
- return { displayOutput, saveError: params.saveError };
274
+ if (params.saveError && params.outputPath) {
275
+ // Persistence/read failure: only the bounded fallback is delivered. It must
276
+ // not claim a full artifact exists.
277
+ return {
278
+ displayOutput: formatBoundedPersistenceFallback({
279
+ error: params.saveError,
280
+ outputPath: params.outputPath,
281
+ fullOutput: params.fullOutput,
282
+ exitCode: params.exitCode,
283
+ processError: params.error,
284
+ }),
285
+ saveError: params.saveError,
286
+ };
233
287
  }
234
- return { displayOutput };
288
+ return { displayOutput: params.truncatedOutput || params.fullOutput };
235
289
  }
@@ -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
+ }
@@ -1,3 +1,5 @@
1
+ import type { AcceptanceRole } from "../../shared/types.ts";
2
+
1
3
  /**
2
4
  * Shared task mutation-intent classifier.
3
5
  *
@@ -40,7 +42,7 @@ const REVIEWER_REQUIRED_EDIT_PATTERNS = [
40
42
  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
43
 
42
44
  /** 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;
45
+ 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
46
 
45
47
  const SCOPED_NO_EDIT_CONSTRAINT_PATTERNS = [
46
48
  /\bdo not edit files?\s+outside\b/i,
@@ -139,6 +141,25 @@ function isReviewerStyleAgent(agent: string): boolean {
139
141
  return /\b(?:architect|commentator|explorer|recapper|researcher)\b/i.test(agent);
140
142
  }
141
143
 
144
+ /**
145
+ * Resolve an agent's acceptance routing role: a declared `acceptanceRole`
146
+ * always wins; otherwise the established name heuristics apply (builder-named
147
+ * agents are writers; architect/commentator/explorer/recapper/researcher/
148
+ * analyst-named agents are read-only). This is the single shared source of
149
+ * truth for acceptance inference and task-aware advisory routing so the two
150
+ * cannot drift. Alias values are never role signals.
151
+ */
152
+ export function resolveAgentRoutingRole(
153
+ agentName: string,
154
+ acceptanceRole?: AcceptanceRole,
155
+ ): "writer" | "read-only" | undefined {
156
+ if (acceptanceRole !== undefined) return acceptanceRole;
157
+ const agent = agentName.toLowerCase();
158
+ if (/\bbuilder\b/.test(agent)) return "writer";
159
+ if (/\b(?:architect|commentator|explorer|recapper|researcher|analyst)\b/.test(agent)) return "read-only";
160
+ return undefined;
161
+ }
162
+
142
163
  function hasImplementationIntent(agent: string, taskText: string): boolean {
143
164
  if (isReviewerStyleAgent(agent)) return REVIEWER_REQUIRED_EDIT_PATTERNS.some((pattern) => pattern.test(taskText));
144
165
  if (agent === "builder") return WORKER_IMPLEMENTATION_PATTERNS.some((pattern) => pattern.test(taskText));