@selesai/code 0.5.20 → 0.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/dist/extensions/handoff-new.test.ts +8 -9
  2. package/dist/extensions/handoff-new.ts +6 -8
  3. package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
  4. package/dist/extensions/pi-subagents/README.md +602 -220
  5. package/dist/extensions/pi-subagents/banner.png +0 -0
  6. package/dist/extensions/pi-subagents/index.ts +1 -0
  7. package/dist/extensions/pi-subagents/install.mjs +3 -3
  8. package/dist/extensions/pi-subagents/package-lock.json +3405 -0
  9. package/dist/extensions/pi-subagents/package.json +26 -14
  10. package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  11. package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
  12. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
  13. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
  14. package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
  15. package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
  16. package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
  18. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
  19. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
  20. package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
  21. package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
  22. package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
  23. package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
  24. package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
  25. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
  26. package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
  27. package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
  28. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
  29. package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
  30. package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
  31. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
  32. package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
  33. package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
  34. package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
  35. package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
  36. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
  37. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
  38. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
  39. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
  40. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
  41. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
  42. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
  43. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
  44. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
  45. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
  46. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
  48. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
  49. package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
  50. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
  51. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
  53. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
  54. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
  55. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
  56. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
  57. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
  58. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
  59. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
  60. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
  61. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
  62. package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
  63. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
  64. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
  65. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
  66. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
  67. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
  68. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
  69. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
  70. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
  71. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
  72. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
  73. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
  74. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
  75. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
  76. package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
  77. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
  78. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
  79. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
  80. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
  81. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
  82. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
  83. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
  87. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
  88. package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
  89. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
  90. package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
  92. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
  94. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
  97. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
  98. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
  99. package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
  100. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
  101. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
  102. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
  103. package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
  104. package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
  105. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
  106. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
  107. package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
  108. package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
  109. package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
  110. package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
  111. package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
  112. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
  113. package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
  114. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
  115. package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
  116. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
  117. package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
  120. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
  121. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
  122. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
  123. package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
  124. package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
  125. package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
  126. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
  127. package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
  128. package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
  129. package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
  130. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
  131. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
  132. package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
  133. package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
  134. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
  135. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
  136. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
  137. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
  138. package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
  139. package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
  140. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
  141. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
  142. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
  143. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
  144. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
  145. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
  146. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
  147. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
  148. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
  149. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
  150. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
  151. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
  152. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
  153. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
  154. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
  155. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
  156. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
  157. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
  158. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
  159. package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
  160. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
  161. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
  162. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
  163. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
  164. package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
  165. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
  166. package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
  167. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
  168. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
  169. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
  170. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
  171. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
  172. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
  173. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
  174. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
  175. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
  176. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
  177. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
  178. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
  179. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
  180. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
  181. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
  182. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
  183. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
  184. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
  185. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
  186. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
  187. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
  188. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
  189. package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
  190. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
  191. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
  192. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
  194. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
  195. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
  196. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
  197. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
  198. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
  199. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
  200. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
  201. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
  202. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
  203. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
  204. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
  205. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
  206. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
  207. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
  208. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
  209. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
  210. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
  211. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
  212. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
  213. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
  214. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
  215. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
  216. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
  217. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
  218. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
  219. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
  220. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
  221. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
  222. package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
  223. package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
  224. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
  225. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
  226. package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
  227. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
  228. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
  229. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
  230. package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
  231. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
  232. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
  233. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
  234. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
  235. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
  236. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
  237. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
  238. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
  239. package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
  240. package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
  241. package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
  245. package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
  246. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
  247. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
  248. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
  249. package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
  250. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
  251. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
  252. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
  253. package/dist/extensions/question/batch.ts +39 -0
  254. package/dist/extensions/question/dialog-adapter.ts +23 -1
  255. package/dist/extensions/question/index.ts +237 -7
  256. package/dist/extensions/question/schemas.ts +28 -15
  257. package/dist/extensions/question/tests/batch.test.ts +26 -0
  258. package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
  259. package/dist/extensions/question/tui-adapter.ts +2 -3
  260. package/dist/extensions/question/types.ts +37 -0
  261. package/dist/extensions/question/ui-protocol.ts +3 -3
  262. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  263. package/package.json +1 -1
@@ -1,14 +1,21 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
+ import "../../shared/env.ts";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { encodeNestedPathEnv, parseNestedPathEnv, type NestedPathEntry } from "./nested-path.ts";
6
- import { resolveMcpDirectToolNames } from "./mcp-direct-tool-allowlist.ts";
7
+ import { resolveMcpDirectToolSelections, type ResolvedMcpDirectToolSelection } from "./mcp-direct-tool-allowlist.ts";
8
+ import { resolvePiPackageRoot } from "./pi-spawn.ts";
7
9
  import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from "./structured-output.ts";
8
10
  import { TEMP_ROOT_DIR, type JsonSchemaObject, type ResolvedToolBudget } from "../../shared/types.ts";
9
- import { TOOL_BUDGET_ENV, encodeToolBudgetEnv } from "./tool-budget.ts";
11
+ import { THINKING_LEVELS } from "../../shared/model-info.ts";
12
+ import { TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV, encodeToolBudgetEnv } from "./tool-budget.ts";
13
+ import { CHILD_TOOL_DIAGNOSTIC_PATH_ENV, MCP_DIRECT_CHILD_TOOLS_ENV, REQUIRED_CHILD_TOOLS_ENV } from "./tool-availability.ts";
14
+ import { CHILD_WATCHDOG_CONFIG_ENV, encodeChildWatchdogConfig, type ChildWatchdogConfig } from "../../watchdog/child-status.ts";
15
+ import { WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
16
+ 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";
10
18
 
11
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
12
19
  const TASK_ARG_LIMIT = 8000;
13
20
  const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
14
21
  const FANOUT_CHILD_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "extension", "fanout-child.ts");
@@ -30,8 +37,10 @@ export const SUBAGENT_PARENT_PATH_ENV = "SELESAI_SUBAGENT_PARENT_PATH";
30
37
  export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV = "SELESAI_SUBAGENT_PARENT_CAPABILITY_TOKEN";
31
38
  export const SUBAGENT_PARENT_SESSION_ENV = "SELESAI_SUBAGENT_PARENT_SESSION";
32
39
  export const SUBAGENT_STEER_INBOX_ENV = "SELESAI_SUBAGENT_STEER_INBOX";
40
+ export const SUBAGENT_STEER_CAPABILITY_ENV = "SELESAI_SUBAGENT_STEER_CAPABILITY";
41
+ export const SUBAGENT_STEER_ACK_DIR_ENV = "SELESAI_SUBAGENT_STEER_ACK_DIR";
33
42
 
34
- interface BuildPiArgsInput {
43
+ export interface BuildPiArgsInput {
35
44
  parentSessionId?: string;
36
45
  baseArgs: string[];
37
46
  task: string;
@@ -39,7 +48,7 @@ interface BuildPiArgsInput {
39
48
  sessionDir?: string;
40
49
  sessionFile?: string;
41
50
  model?: string;
42
- thinking?: string;
51
+ thinking?: string | false;
43
52
  systemPromptMode?: "append" | "replace";
44
53
  inheritProjectContext: boolean;
45
54
  inheritSkills: boolean;
@@ -65,18 +74,26 @@ interface BuildPiArgsInput {
65
74
  parentPath?: NestedPathEntry[];
66
75
  parentCapabilityToken?: string;
67
76
  steerInboxDir?: string;
77
+ steerCapabilityPath?: string;
78
+ steerAckDir?: string;
68
79
  structuredOutput?: {
69
80
  schema: JsonSchemaObject;
70
81
  schemaPath: string;
71
82
  outputPath: string;
72
83
  };
73
84
  toolBudget?: ResolvedToolBudget;
85
+ allowZeroToolBudget?: boolean;
86
+ childWatchdog?: ChildWatchdogConfig;
87
+ waitToolEnabled?: boolean;
88
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
74
89
  }
75
90
 
76
- interface BuildPiArgsResult {
91
+ export interface BuildPiArgsResult {
77
92
  args: string[];
78
93
  env: Record<string, string | undefined>;
79
94
  tempDir?: string;
95
+ toolDiagnosticPath?: string;
96
+ capabilityAudit?: SubagentCapabilityAudit;
80
97
  }
81
98
 
82
99
  function sanitizeSupervisorChannelSegment(value: string): string {
@@ -87,15 +104,115 @@ function supervisorChannelDir(runId: string, agent: string, childIndex: number):
87
104
  return path.join(TEMP_ROOT_DIR, "supervisor-channels", `${sanitizeSupervisorChannelSegment(runId)}-${sanitizeSupervisorChannelSegment(agent)}-${childIndex}`);
88
105
  }
89
106
 
90
- export function applyThinkingSuffix(model: string | undefined, thinking: string | undefined, replaceExisting = false): string | undefined {
107
+ export function applyThinkingSuffix(model: string | undefined, thinking: string | false | undefined, replaceExisting = false): string | undefined {
91
108
  if (!model || !thinking) return model;
92
109
  const colonIdx = model.lastIndexOf(":");
93
- if (colonIdx !== -1 && THINKING_LEVELS.includes(model.substring(colonIdx + 1))) {
110
+ if (colonIdx !== -1 && THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))) {
94
111
  return replaceExisting ? `${model.slice(0, colonIdx)}:${thinking}` : model;
95
112
  }
96
113
  return `${model}:${thinking}`;
97
114
  }
98
115
 
116
+ export interface ResolvePiLaunchToolPlanInput {
117
+ tools?: string[];
118
+ extensions?: string[];
119
+ subagentOnlyExtensions?: string[];
120
+ mcpDirectTools?: string[];
121
+ cwd?: string;
122
+ requireReadTool?: boolean;
123
+ structuredOutput?: boolean;
124
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
125
+ inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
126
+ }
127
+
128
+ export interface PiLaunchToolPlan {
129
+ capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
130
+ requestedBuiltinTools: string[];
131
+ declaredBuiltinTools: string[];
132
+ toolExtensionPaths: string[];
133
+ resolvedMcpSelections: ResolvedMcpDirectToolSelection[];
134
+ effectiveMcpSelections: ResolvedMcpDirectToolSelection[];
135
+ effectiveMcpTools: string[];
136
+ explicitToolAllowlist: boolean;
137
+ internalTools: string[];
138
+ effectiveToolAllowlist: string[];
139
+ requiredChildTools: string[];
140
+ fanoutAuthorized: boolean;
141
+ runtimeExtensions: string[];
142
+ configuredExtensions: string[];
143
+ extensionArgs: string[];
144
+ disableAmbientExtensions: boolean;
145
+ capabilityAudit?: SubagentCapabilityAudit;
146
+ }
147
+
148
+ export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): PiLaunchToolPlan {
149
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
150
+ const allowedToolSet = capabilityCeiling?.allowedTools === undefined ? undefined : new Set(capabilityCeiling.allowedTools);
151
+ const requestedBuiltinTools = input.tools?.filter((tool) => !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) ?? [];
152
+ if (input.requireReadTool && allowedToolSet && !allowedToolSet.has("read")) {
153
+ throw new Error(`Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`);
154
+ }
155
+ const declaredBuiltinTools = input.tools === undefined
156
+ ? (allowedToolSet ? [...allowedToolSet] : [])
157
+ : (input.requireReadTool && requestedBuiltinTools.length > 0 && !requestedBuiltinTools.includes("read") && !allowedToolSet
158
+ ? ["read", ...requestedBuiltinTools]
159
+ : requestedBuiltinTools).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
160
+ const fanoutAuthorized = declaredBuiltinTools.includes("subagent");
161
+ const toolExtensionPaths: string[] = capabilityCeiling?.denyExtensions ? [] : (input.tools ?? []).filter((tool) => !requestedBuiltinTools.includes(tool) && (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")));
162
+ const resolvedMcpSelections = capabilityCeiling?.denyExtensions ? [] : resolveMcpDirectToolSelections(input.mcpDirectTools, input.cwd);
163
+ const effectiveMcpSelections = resolvedMcpSelections.filter((selection) => !allowedToolSet || allowedToolSet.has(selection.name));
164
+ const effectiveMcpTools = effectiveMcpSelections.map((selection) => selection.name);
165
+ const explicitToolAllowlist = input.tools !== undefined || (input.mcpDirectTools?.length ?? 0) > 0 || allowedToolSet !== undefined;
166
+ const internalTools = input.structuredOutput ? ["structured_output"] : [];
167
+ const effectiveToolAllowlist = [...new Set([...declaredBuiltinTools, ...effectiveMcpTools, ...internalTools])];
168
+ const requiredChildTools = explicitToolAllowlist ? [...new Set([
169
+ ...(input.tools !== undefined ? declaredBuiltinTools : []),
170
+ ...(input.mcpDirectTools?.length ? effectiveMcpTools : []),
171
+ ...internalTools,
172
+ ])] : [];
173
+ const runtimeExtensions = fanoutAuthorized
174
+ ? [PROMPT_RUNTIME_EXTENSION_PATH, FANOUT_CHILD_EXTENSION_PATH]
175
+ : [PROMPT_RUNTIME_EXTENSION_PATH];
176
+ const disableAmbientExtensions = capabilityCeiling?.denyExtensions === true || input.extensions !== undefined;
177
+ const configuredExtensions = capabilityCeiling?.denyExtensions ? [] : [...toolExtensionPaths, ...(input.extensions ?? []), ...(input.subagentOnlyExtensions ?? [])];
178
+ const extensionArgs = disableAmbientExtensions
179
+ ? [...new Set([...runtimeExtensions, ...configuredExtensions])]
180
+ : [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...(input.subagentOnlyExtensions ?? [])])];
181
+ const requestedToolNames = input.tools !== undefined
182
+ ? [...new Set([...requestedBuiltinTools, ...resolvedMcpSelections.map((selection) => selection.name)])]
183
+ : undefined;
184
+ const capabilityAudit = capabilityCeiling ? {
185
+ ceiling: capabilityCeiling,
186
+ ...(requestedToolNames ? { requestedTools: requestedToolNames } : {}),
187
+ effectiveTools: effectiveToolAllowlist,
188
+ removedTools: requestedToolNames?.filter((tool) => !effectiveToolAllowlist.includes(tool)) ?? [],
189
+ internalTools,
190
+ extensionsDenied: capabilityCeiling.denyExtensions,
191
+ 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
+ requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
193
+ effectiveMcpTools,
194
+ } satisfies SubagentCapabilityAudit : undefined;
195
+ return {
196
+ ...(capabilityCeiling ? { capabilityCeiling } : {}),
197
+ requestedBuiltinTools,
198
+ declaredBuiltinTools,
199
+ toolExtensionPaths,
200
+ resolvedMcpSelections,
201
+ effectiveMcpSelections,
202
+ effectiveMcpTools,
203
+ explicitToolAllowlist,
204
+ internalTools,
205
+ effectiveToolAllowlist,
206
+ requiredChildTools,
207
+ fanoutAuthorized,
208
+ runtimeExtensions,
209
+ configuredExtensions,
210
+ extensionArgs,
211
+ disableAmbientExtensions,
212
+ ...(capabilityAudit ? { capabilityAudit } : {}),
213
+ };
214
+ }
215
+
99
216
  export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
100
217
  const args = [...input.baseArgs];
101
218
 
@@ -117,40 +234,25 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
117
234
  args.push("--model", modelArg);
118
235
  }
119
236
 
120
- const declaredBuiltinToolsBase = input.tools?.filter((tool) => !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) ?? [];
121
- const declaredBuiltinTools = input.requireReadTool && input.tools?.length && !declaredBuiltinToolsBase.includes("read")
122
- ? ["read", ...declaredBuiltinToolsBase]
123
- : declaredBuiltinToolsBase;
124
- const fanoutAuthorized = declaredBuiltinTools.includes("subagent");
125
- const toolExtensionPaths: string[] = [];
126
- if (input.tools?.length) {
127
- const builtinTools = [...declaredBuiltinTools];
128
- for (const tool of input.tools) {
129
- if (!declaredBuiltinTools.includes(tool) && (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) {
130
- toolExtensionPaths.push(tool);
131
- }
132
- }
133
- if (builtinTools.length > 0) {
134
- if (input.mcpDirectTools?.length) {
135
- builtinTools.push(...resolveMcpDirectToolNames(input.mcpDirectTools, input.cwd));
136
- }
137
- args.push("--tools", builtinTools.join(","));
138
- }
237
+ const toolPlan = resolvePiLaunchToolPlan({
238
+ tools: input.tools,
239
+ extensions: input.extensions,
240
+ subagentOnlyExtensions: input.subagentOnlyExtensions,
241
+ mcpDirectTools: input.mcpDirectTools,
242
+ cwd: input.cwd,
243
+ requireReadTool: input.requireReadTool,
244
+ structuredOutput: input.structuredOutput,
245
+ capabilityCeiling: input.capabilityCeiling,
246
+ inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
247
+ });
248
+ if (toolPlan.explicitToolAllowlist) {
249
+ args.push(toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools");
250
+ if (toolPlan.effectiveToolAllowlist.length > 0) args.push(toolPlan.effectiveToolAllowlist.join(","));
139
251
  }
140
-
141
- const runtimeExtensions = fanoutAuthorized
142
- ? [PROMPT_RUNTIME_EXTENSION_PATH, FANOUT_CHILD_EXTENSION_PATH]
143
- : [PROMPT_RUNTIME_EXTENSION_PATH];
144
- if (input.extensions !== undefined) {
252
+ if (toolPlan.disableAmbientExtensions) {
145
253
  args.push("--no-extensions");
146
- for (const extPath of [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...input.extensions, ...(input.subagentOnlyExtensions ?? [])])]) {
147
- args.push("--extension", extPath);
148
- }
149
- } else {
150
- for (const extPath of [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...(input.subagentOnlyExtensions ?? [])])]) {
151
- args.push("--extension", extPath);
152
- }
153
254
  }
255
+ for (const extPath of toolPlan.extensionArgs) args.push("--extension", extPath);
154
256
 
155
257
  if (!input.inheritSkills) {
156
258
  args.push("--no-skills");
@@ -177,8 +279,21 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
177
279
  }
178
280
 
179
281
  const env: Record<string, string | undefined> = {};
282
+ const piPackageRoot = process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
283
+ if (piPackageRoot) env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
284
+ let toolDiagnosticPath: string | undefined;
285
+ if (toolPlan.requiredChildTools.length > 0) {
286
+ if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
287
+ toolDiagnosticPath = path.join(tempDir, "tool-diagnostic.json");
288
+ env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(toolPlan.requiredChildTools);
289
+ env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = toolDiagnosticPath;
290
+ }
291
+ env[MCP_DIRECT_CHILD_TOOLS_ENV] = toolPlan.effectiveMcpTools.length > 0 ? JSON.stringify(toolPlan.effectiveMcpTools) : undefined;
180
292
  env[SUBAGENT_CHILD_ENV] = "1";
181
- env[SUBAGENT_FANOUT_CHILD_ENV] = fanoutAuthorized ? "1" : "0";
293
+ env[SUBAGENT_FANOUT_CHILD_ENV] = toolPlan.fanoutAuthorized ? "1" : "0";
294
+ if (input.waitToolEnabled !== undefined) {
295
+ env[WAIT_TOOL_ENABLED_ENV] = input.waitToolEnabled ? "true" : "false";
296
+ }
182
297
  const inheritedNestedRoute = Boolean(process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] && process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] && process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV]);
183
298
  const parentRunId = input.parentRunId ?? input.runId ?? (inheritedNestedRoute ? process.env[SUBAGENT_RUN_ID_ENV] : undefined) ?? process.env[SUBAGENT_PARENT_RUN_ID_ENV] ?? "";
184
299
  const parentChildIndex = input.parentChildIndex !== undefined
@@ -196,20 +311,20 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
196
311
  ...(input.childAgentName ? { agent: input.childAgentName } : {}),
197
312
  }] : []),
198
313
  ];
199
- env[SUBAGENT_PARENT_EVENT_SINK_ENV] = fanoutAuthorized
314
+ env[SUBAGENT_PARENT_EVENT_SINK_ENV] = toolPlan.fanoutAuthorized
200
315
  ? input.parentEventSink ?? process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] ?? ""
201
316
  : "";
202
- env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] = fanoutAuthorized
317
+ env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] = toolPlan.fanoutAuthorized
203
318
  ? input.parentControlInbox ?? process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] ?? ""
204
319
  : "";
205
- env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = fanoutAuthorized
320
+ env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = toolPlan.fanoutAuthorized
206
321
  ? input.parentRootRunId ?? process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] ?? input.runId ?? ""
207
322
  : "";
208
- env[SUBAGENT_PARENT_RUN_ID_ENV] = fanoutAuthorized ? parentRunId : "";
209
- env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = fanoutAuthorized ? parentChildIndex : "";
210
- env[SUBAGENT_PARENT_DEPTH_ENV] = fanoutAuthorized ? String(parentDepth) : "";
211
- env[SUBAGENT_PARENT_PATH_ENV] = fanoutAuthorized ? encodeNestedPathEnv(parentPath) : "";
212
- env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = fanoutAuthorized
323
+ env[SUBAGENT_PARENT_RUN_ID_ENV] = toolPlan.fanoutAuthorized ? parentRunId : "";
324
+ env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = toolPlan.fanoutAuthorized ? parentChildIndex : "";
325
+ env[SUBAGENT_PARENT_DEPTH_ENV] = toolPlan.fanoutAuthorized ? String(parentDepth) : "";
326
+ env[SUBAGENT_PARENT_PATH_ENV] = toolPlan.fanoutAuthorized ? encodeNestedPathEnv(parentPath) : "";
327
+ env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = toolPlan.fanoutAuthorized
213
328
  ? input.parentCapabilityToken ?? process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] ?? ""
214
329
  : "";
215
330
  env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
@@ -239,11 +354,11 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
239
354
  if (input.childIndex !== undefined) {
240
355
  env[SUBAGENT_CHILD_INDEX_ENV] = String(input.childIndex);
241
356
  }
242
- if (input.mcpDirectTools?.length) {
243
- env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
244
- } else {
245
- env.MCP_DIRECT_TOOLS = "__none__";
246
- }
357
+ if (!toolPlan.capabilityCeiling && input.mcpDirectTools?.length) env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
358
+ else if (toolPlan.capabilityCeiling && toolPlan.effectiveMcpSelections.length && !toolPlan.capabilityCeiling.denyExtensions) env.MCP_DIRECT_TOOLS = toolPlan.effectiveMcpSelections.map((selection) => selection.selector).join(",");
359
+ else env.MCP_DIRECT_TOOLS = "__none__";
360
+ const encodedCapabilityCeiling = encodeSubagentCapabilityCeiling(toolPlan.capabilityCeiling);
361
+ if (encodedCapabilityCeiling) env[SUBAGENT_CAPABILITY_CEILING_ENV] = encodedCapabilityCeiling;
247
362
  if (input.structuredOutput) {
248
363
  env[STRUCTURED_OUTPUT_CAPTURE_ENV] = input.structuredOutput.outputPath;
249
364
  env[STRUCTURED_OUTPUT_SCHEMA_ENV] = input.structuredOutput.schemaPath;
@@ -251,12 +366,17 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
251
366
  if (input.steerInboxDir) {
252
367
  env[SUBAGENT_STEER_INBOX_ENV] = input.steerInboxDir;
253
368
  }
369
+ if (input.steerCapabilityPath) env[SUBAGENT_STEER_CAPABILITY_ENV] = input.steerCapabilityPath;
370
+ if (input.steerAckDir) env[SUBAGENT_STEER_ACK_DIR_ENV] = input.steerAckDir;
254
371
  const encodedToolBudget = encodeToolBudgetEnv(input.toolBudget);
255
372
  if (encodedToolBudget) env[TOOL_BUDGET_ENV] = encodedToolBudget;
373
+ env[TOOL_BUDGET_ZERO_AUTH_ENV] = input.allowZeroToolBudget ? "1" : undefined;
374
+ const encodedChildWatchdog = encodeChildWatchdogConfig(input.childWatchdog);
375
+ if (encodedChildWatchdog) env[CHILD_WATCHDOG_CONFIG_ENV] = encodedChildWatchdog;
256
376
 
257
377
  env[SUBAGENT_PARENT_SESSION_ENV] = input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
258
378
 
259
- return { args, env, tempDir };
379
+ return { args, env, tempDir, toolDiagnosticPath, capabilityAudit: toolPlan.capabilityAudit };
260
380
  }
261
381
 
262
382
  export const parseParentPathEnv = parseNestedPathEnv;
@@ -1,11 +1,12 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { readSubagentEnv } from "../../shared/env.ts";
4
5
 
5
- export const SELESAI_PACKAGE = "@selesai/code";
6
+ export const PI_CODING_AGENT_PACKAGE = "@selesai/code";
6
7
  export const SELESAI_SUBAGENT_PI_BINARY_ENV = "SELESAI_SUBAGENT_PI_BINARY";
7
8
 
8
- export function findSelesaiPackageRootFromEntry(
9
+ export function findPiPackageRootFromEntry(
9
10
  entryPoint: string,
10
11
  ): string | undefined {
11
12
  let dir = path.dirname(entryPoint);
@@ -15,24 +16,24 @@ export function findSelesaiPackageRootFromEntry(
15
16
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
16
17
  name?: unknown;
17
18
  };
18
- if (pkg.name === SELESAI_PACKAGE) return dir;
19
+ if (pkg.name === PI_CODING_AGENT_PACKAGE) return dir;
19
20
  }
20
21
  dir = path.dirname(dir);
21
22
  }
22
23
  return undefined;
23
24
  }
24
25
 
25
- export function resolveInstalledSelesaiPackageRoot(): string | undefined {
26
- return findSelesaiPackageRootFromEntry(
27
- fileURLToPath(import.meta.resolve(SELESAI_PACKAGE)),
26
+ export function resolveInstalledPiPackageRoot(): string | undefined {
27
+ return findPiPackageRootFromEntry(
28
+ fileURLToPath(import.meta.resolve(PI_CODING_AGENT_PACKAGE)),
28
29
  );
29
30
  }
30
31
 
31
- export function resolveSelesaiPackageRoot(): string | undefined {
32
+ export function resolvePiPackageRoot(): string | undefined {
32
33
  try {
33
34
  const entry = process.argv[1];
34
35
  return entry
35
- ? findSelesaiPackageRootFromEntry(fs.realpathSync(entry))
36
+ ? findPiPackageRootFromEntry(fs.realpathSync(entry))
36
37
  : undefined;
37
38
  } catch {
38
39
  // process.argv[1] probing is best-effort; callers can fall back to PATH/package resolution.
@@ -40,11 +41,12 @@ export function resolveSelesaiPackageRoot(): string | undefined {
40
41
  }
41
42
  }
42
43
 
43
- export interface SelesaiSpawnDeps {
44
+ export interface PiSpawnDeps {
44
45
  platform?: NodeJS.Platform;
45
46
  execPath?: string;
46
47
  argv1?: string;
47
48
  existsSync?: (filePath: string) => boolean;
49
+ realpathSync?: (filePath: string) => string;
48
50
  readFileSync?: (filePath: string, encoding: "utf-8") => string;
49
51
  resolvePackageJson?: () => string;
50
52
  resolvePackageEntry?: () => string;
@@ -52,7 +54,7 @@ export interface SelesaiSpawnDeps {
52
54
  env?: NodeJS.ProcessEnv;
53
55
  }
54
56
 
55
- interface SelesaiSpawnCommand {
57
+ interface PiSpawnCommand {
56
58
  command: string;
57
59
  args: string[];
58
60
  }
@@ -69,10 +71,11 @@ function normalizePath(filePath: string): string {
69
71
  return path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
70
72
  }
71
73
 
72
- export function resolveWindowsSelesaiCliScript(
73
- deps: SelesaiSpawnDeps = {},
74
+ export function resolvePiCliScript(
75
+ deps: PiSpawnDeps = {},
74
76
  ): string | undefined {
75
77
  const existsSync = deps.existsSync ?? fs.existsSync;
78
+ const realpathSync = deps.realpathSync ?? fs.realpathSync;
76
79
  const readFileSync =
77
80
  deps.readFileSync ??
78
81
  ((filePath, encoding) => fs.readFileSync(filePath, encoding));
@@ -81,7 +84,14 @@ export function resolveWindowsSelesaiCliScript(
81
84
  if (argv1) {
82
85
  const argvPath = normalizePath(argv1);
83
86
  if (isRunnableNodeScript(argvPath, existsSync)) {
84
- return argvPath;
87
+ try {
88
+ const canonicalArgvPath = realpathSync(argvPath);
89
+ if (isRunnableNodeScript(canonicalArgvPath, existsSync) && findPiPackageRootFromEntry(canonicalArgvPath)) {
90
+ return canonicalArgvPath;
91
+ }
92
+ } catch {
93
+ // Host package metadata is untrusted here; keep resolving the installed Pi CLI.
94
+ }
85
95
  }
86
96
  }
87
97
 
@@ -89,14 +99,14 @@ export function resolveWindowsSelesaiCliScript(
89
99
  const resolvePackageJson =
90
100
  deps.resolvePackageJson ??
91
101
  (() => {
92
- const root = deps.piPackageRoot ?? resolveSelesaiPackageRoot();
102
+ const root = deps.piPackageRoot ?? resolvePiPackageRoot();
93
103
  if (root) return path.join(root, "package.json");
94
104
  const packageRoot = deps.resolvePackageEntry
95
- ? findSelesaiPackageRootFromEntry(deps.resolvePackageEntry())
96
- : resolveInstalledSelesaiPackageRoot();
105
+ ? findPiPackageRootFromEntry(deps.resolvePackageEntry())
106
+ : resolveInstalledPiPackageRoot();
97
107
  if (!packageRoot)
98
108
  throw new Error(
99
- `Could not resolve ${SELESAI_PACKAGE} package root`,
109
+ `Could not resolve ${PI_CODING_AGENT_PACKAGE} package root`,
100
110
  );
101
111
  return path.join(packageRoot, "package.json");
102
112
  });
@@ -115,32 +125,29 @@ export function resolveWindowsSelesaiCliScript(
115
125
  return candidate;
116
126
  }
117
127
  } catch {
118
- // Windows CLI resolution is optional; falling back to `pi` lets PATH handle execution.
128
+ // Verified CLI resolution is optional; falling back to `selesai` lets PATH handle execution.
119
129
  return undefined;
120
130
  }
121
131
 
122
132
  return undefined;
123
133
  }
124
134
 
125
- export function getSelesaiSpawnCommand(
135
+ export function getPiSpawnCommand(
126
136
  args: string[],
127
- deps: SelesaiSpawnDeps = {},
128
- ): SelesaiSpawnCommand {
137
+ deps: PiSpawnDeps = {},
138
+ ): PiSpawnCommand {
129
139
  const env = deps.env ?? process.env;
130
- const piBinary = env[SELESAI_SUBAGENT_PI_BINARY_ENV]?.trim();
140
+ const piBinary = readSubagentEnv(env, SELESAI_SUBAGENT_PI_BINARY_ENV)?.trim();
131
141
  if (piBinary) {
132
142
  return { command: piBinary, args };
133
143
  }
134
144
 
135
- const platform = deps.platform ?? process.platform;
136
- if (platform === "win32") {
137
- const piCliPath = resolveWindowsSelesaiCliScript(deps);
138
- if (piCliPath) {
139
- return {
140
- command: deps.execPath ?? process.execPath,
141
- args: [piCliPath, ...args],
142
- };
143
- }
145
+ const piCliPath = resolvePiCliScript(deps);
146
+ if (piCliPath) {
147
+ return {
148
+ command: deps.execPath ?? process.execPath,
149
+ args: [piCliPath, ...args],
150
+ };
144
151
  }
145
152
 
146
153
  return { command: "selesai", args };
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
4
  import { getAgentDir } from "../../shared/utils.ts";
@@ -5,6 +6,7 @@ import { getAgentDir } from "../../shared/utils.ts";
5
6
  export interface RunEntry {
6
7
  agent: string;
7
8
  task: string;
9
+ taskHash?: string;
8
10
  ts: number;
9
11
  status: "ok" | "error";
10
12
  duration: number;
@@ -13,24 +15,103 @@ export interface RunEntry {
13
15
 
14
16
  const ROTATE_READ_THRESHOLD = 1200;
15
17
  const ROTATE_KEEP = 1000;
18
+ const PRIVATE_DIR_MODE = 0o700;
19
+ const PRIVATE_FILE_MODE = 0o600;
20
+ const REDACTED_TASK = "[redacted]";
16
21
 
17
22
  function getHistoryPath(): string {
18
23
  return path.join(getAgentDir(), "run-history.jsonl");
19
24
  }
20
25
 
26
+ function hashTask(task: string): string {
27
+ return createHash("sha256").update(task).digest("hex");
28
+ }
29
+
30
+ function hardenHistoryStorage(historyPath: string): void {
31
+ const historyDir = path.dirname(historyPath);
32
+ 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 {}
35
+ }
36
+
37
+ function sanitizeHistoryLine(line: string): string | undefined {
38
+ let value: unknown;
39
+ try {
40
+ value = JSON.parse(line);
41
+ } catch {
42
+ return undefined;
43
+ }
44
+ if (!value || typeof value !== "object") return undefined;
45
+
46
+ const record = value as Record<string, unknown>;
47
+ const task = typeof record.task === "string" ? record.task : "";
48
+ const taskHash = typeof record.taskHash === "string" && record.taskHash
49
+ ? record.taskHash
50
+ : task && task !== REDACTED_TASK
51
+ ? hashTask(task)
52
+ : undefined;
53
+
54
+ return JSON.stringify({
55
+ ...record,
56
+ task: REDACTED_TASK,
57
+ ...(taskHash ? { taskHash } : {}),
58
+ });
59
+ }
60
+
61
+ function sanitizeHistoryLines(raw: string): { lines: string[]; changed: boolean } {
62
+ const lines: string[] = [];
63
+ let changed = false;
64
+ for (const line of raw.split("\n")) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed) continue;
67
+ const sanitized = sanitizeHistoryLine(trimmed);
68
+ if (!sanitized) {
69
+ changed = true;
70
+ continue;
71
+ }
72
+ if (sanitized !== trimmed) changed = true;
73
+ lines.push(sanitized);
74
+ }
75
+ return { lines, changed };
76
+ }
77
+
78
+ function writePrivateHistory(historyPath: string, lines: string[]): void {
79
+ fs.writeFileSync(historyPath, lines.length ? `${lines.join("\n")}\n` : "", { encoding: "utf-8", mode: PRIVATE_FILE_MODE });
80
+ try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
81
+ }
82
+
83
+ function sanitizeHistoryFile(historyPath: string): void {
84
+ if (!fs.existsSync(historyPath)) return;
85
+ const raw = fs.readFileSync(historyPath, "utf-8");
86
+ const { lines, changed } = sanitizeHistoryLines(raw);
87
+ if (changed) writePrivateHistory(historyPath, lines);
88
+ }
89
+
90
+ function appendPrivateHistoryLine(historyPath: string, line: string): void {
91
+ const fd = fs.openSync(historyPath, fs.constants.O_APPEND | fs.constants.O_CREAT | fs.constants.O_WRONLY, PRIVATE_FILE_MODE);
92
+ try {
93
+ fs.writeSync(fd, `${line}\n`);
94
+ } finally {
95
+ fs.closeSync(fd);
96
+ }
97
+ try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
98
+ }
99
+
21
100
  export function recordRun(agent: string, task: string, exitCode: number, durationMs: number): void {
22
101
  try {
23
102
  const entry: RunEntry = {
24
103
  agent,
25
- task: task.slice(0, 200),
104
+ task: REDACTED_TASK,
105
+ taskHash: hashTask(task),
26
106
  ts: Math.floor(Date.now() / 1000),
27
107
  status: exitCode === 0 ? "ok" : "error",
28
108
  duration: durationMs,
29
109
  ...(exitCode !== 0 ? { exit: exitCode } : {}),
30
110
  };
31
111
  const historyPath = getHistoryPath();
32
- fs.mkdirSync(path.dirname(historyPath), { recursive: true });
33
- fs.appendFileSync(historyPath, `${JSON.stringify(entry)}\n`);
112
+ hardenHistoryStorage(historyPath);
113
+ try { sanitizeHistoryFile(historyPath); } catch {}
114
+ appendPrivateHistoryLine(historyPath, JSON.stringify(entry));
34
115
  } catch {
35
116
  // Best-effort — never crash the execution flow for history recording
36
117
  }
@@ -38,6 +119,7 @@ export function recordRun(agent: string, task: string, exitCode: number, duratio
38
119
 
39
120
  export function loadRunsForAgent(agent: string): RunEntry[] {
40
121
  const historyPath = getHistoryPath();
122
+ try { hardenHistoryStorage(historyPath); } catch {}
41
123
  if (!fs.existsSync(historyPath)) return [];
42
124
  let raw: string;
43
125
  try {
@@ -46,11 +128,14 @@ export function loadRunsForAgent(agent: string): RunEntry[] {
46
128
  return [];
47
129
  }
48
130
 
49
- let lines = raw.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
131
+ let { lines, changed } = sanitizeHistoryLines(raw);
50
132
 
51
133
  if (lines.length > ROTATE_READ_THRESHOLD) {
52
134
  lines = lines.slice(-ROTATE_KEEP);
53
- try { fs.writeFileSync(historyPath, `${lines.join("\n")}\n`, "utf-8"); } catch {}
135
+ changed = true;
136
+ }
137
+ if (changed) {
138
+ try { writePrivateHistory(historyPath, lines); } catch {}
54
139
  }
55
140
 
56
141
  return lines