@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
@@ -520,10 +520,10 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
520
520
 
521
521
  const ctx = createCommandContext({ sessionManager });
522
522
  registerSlashCommands!(pi, createState(process.cwd()));
523
- await commands.get("run")!.handler("scout", ctx);
523
+ await commands.get("run")!.handler("explorer", ctx);
524
524
  await new Promise<void>((resolve) => setImmediate(resolve));
525
525
 
526
- assert.deepEqual(requestedParams, { agent: "scout", task: "", clarify: false, agentScope: "both" });
526
+ assert.deepEqual(requestedParams, { agent: "explorer", task: "", clarify: false, agentScope: "both" });
527
527
  assert.equal(requestedCtx, ctx);
528
528
  assert.equal(sent.length, 2);
529
529
  assert.equal((sent[0] as { display?: boolean }).display, true);
@@ -565,7 +565,7 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
565
565
  };
566
566
 
567
567
  registerSlashCommands!(pi, createState(process.cwd()));
568
- await commands.get("run")!.handler("scout inspect this", createCommandContext({
568
+ await commands.get("run")!.handler("explorer inspect this", createCommandContext({
569
569
  hasUI: true,
570
570
  setStatus: (_key, text) => {
571
571
  log.push(`status:${text ?? "clear"}`);
@@ -615,7 +615,7 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
615
615
  };
616
616
 
617
617
  registerSlashCommands!(pi, createState(process.cwd()));
618
- await commands.get("run")!.handler("scout inspect this", createCommandContext({
618
+ await commands.get("run")!.handler("explorer inspect this", createCommandContext({
619
619
  hasUI: true,
620
620
  setToolsExpanded: (expanded) => log.push(`expanded:${String(expanded)}`),
621
621
  }));
@@ -655,7 +655,7 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
655
655
  };
656
656
 
657
657
  registerSlashCommands!(pi, createState(process.cwd()));
658
- await commands.get("run")!.handler("scout inspect this", createCommandContext({
658
+ await commands.get("run")!.handler("explorer inspect this", createCommandContext({
659
659
  hasUI: true,
660
660
  setStatus: (_key, text) => {
661
661
  log.push(`status:${text ?? "clear"}`);
@@ -706,10 +706,10 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
706
706
  };
707
707
 
708
708
  registerSlashCommands!(pi, createState(process.cwd()));
709
- await commands.get("parallel")!.handler("scout[output=x.md,outputMode=file-only,reads=a.md+b.md,progress] -- Review", createCommandContext());
709
+ await commands.get("parallel")!.handler("explorer[output=x.md,outputMode=file-only,reads=a.md+b.md,progress] -- Review", createCommandContext());
710
710
 
711
711
  assert.deepEqual(requestedParams, {
712
- tasks: [{ agent: "scout", task: "Review", output: "x.md", outputMode: "file-only", reads: ["a.md", "b.md"], progress: true }],
712
+ tasks: [{ agent: "explorer", task: "Review", output: "x.md", outputMode: "file-only", reads: ["a.md", "b.md"], progress: true }],
713
713
  clarify: false,
714
714
  agentScope: "both",
715
715
  });
@@ -746,7 +746,7 @@ describe("slash command custom message delivery", { skip: !available ? "slash-co
746
746
  };
747
747
 
748
748
  registerSlashCommands!(pi, createState(process.cwd()));
749
- const args = Array.from({ length: 9 }, (_, index) => `scout \"task ${index + 1}\"`).join(" -> ");
749
+ const args = Array.from({ length: 9 }, (_, index) => `explorer \"task ${index + 1}\"`).join(" -> ");
750
750
  await commands.get("parallel")!.handler(args, createCommandContext());
751
751
  await new Promise<void>((resolve) => setImmediate(resolve));
752
752
 
@@ -1271,8 +1271,8 @@ describe("subagents-models slash command", { skip: !available ? "slash-commands.
1271
1271
  });
1272
1272
 
1273
1273
  it("passes an optional builtin filter", async () => {
1274
- const { params } = await captureSlashCommandParams("subagents-models", "scout", process.cwd());
1275
- assert.deepEqual(params, { action: "models", agent: "scout" });
1274
+ const { params } = await captureSlashCommandParams("subagents-models", "explorer", process.cwd());
1275
+ assert.deepEqual(params, { action: "models", agent: "explorer" });
1276
1276
  });
1277
1277
 
1278
1278
  it("rejects invalid builtin filters without launching", async () => {
@@ -1294,8 +1294,8 @@ describe("subagents-models slash command", { skip: !available ? "slash-commands.
1294
1294
  };
1295
1295
 
1296
1296
  registerSlashCommands!(pi, createState(process.cwd()));
1297
- const completions = commands.get("subagents-models")!.getArgumentCompletions!("sc") as Array<{ value: string; label: string }>;
1298
- assert.deepEqual(completions.map((completion) => completion.value), ["scout"]);
1297
+ const completions = commands.get("subagents-models")!.getArgumentCompletions!("ex") as Array<{ value: string; label: string }>;
1298
+ assert.deepEqual(completions.map((completion) => completion.value), ["explorer"]);
1299
1299
  });
1300
1300
  });
1301
1301
  });
@@ -1421,7 +1421,7 @@ describe("subagent profiles slash commands", { skip: !available ? "slash-command
1421
1421
  const profilesDir = path.join(process.env.HOME!, ".selesai", "agent", "profiles", "pi-subagents");
1422
1422
  fs.mkdirSync(profilesDir, { recursive: true });
1423
1423
  fs.writeFileSync(path.join(profilesDir, "openai-codex.quota.json"), JSON.stringify({
1424
- subagents: { agentOverrides: { worker: { model: "gpt-5.4:high" } } },
1424
+ subagents: { agentOverrides: { builder: { model: "gpt-5.4:high" } } },
1425
1425
  }, null, 2));
1426
1426
  const sent: unknown[] = [];
1427
1427
  const commands = new Map<string, RegisteredSlashCommand>();
@@ -1593,7 +1593,7 @@ describe("subagents admin slash command", { skip: !available ? "slash-commands.t
1593
1593
  notify: (message) => notifications.push(message),
1594
1594
  }));
1595
1595
 
1596
- await commands.get("subagents")!.handler("worker model", createCommandContext({
1596
+ await commands.get("subagents")!.handler("builder model", createCommandContext({
1597
1597
  cwd: root,
1598
1598
  hasUI: true,
1599
1599
  modelRegistry: { getAvailable: () => [{ provider: "test", id: "new-model", reasoning: true }] },
@@ -1611,13 +1611,13 @@ describe("subagents admin slash command", { skip: !available ? "slash-commands.t
1611
1611
  it("reports duplicate names as ambiguous without UI", async () => {
1612
1612
  await withIsolatedHome(async () => {
1613
1613
  await withTempProject("pi-subagents-admin-ambiguous-", async (root) => {
1614
- fs.writeFileSync(path.join(root, ".selesai", "agents", "worker.md"), "---\nname: worker\ndescription: Project worker\n---\n\nWork.\n", "utf-8");
1614
+ fs.writeFileSync(path.join(root, ".selesai", "agents", "builder.md"), "---\nname: builder\ndescription: Project builder\n---\n\nWork.\n", "utf-8");
1615
1615
  const sent: unknown[] = [];
1616
1616
  const commands = registerAdmin(sent);
1617
- await commands.get("subagents")!.handler("worker", createCommandContext({ cwd: root }));
1617
+ await commands.get("subagents")!.handler("builder", createCommandContext({ cwd: root }));
1618
1618
  assert.equal(sent.length, 1);
1619
1619
  const content = (sent[0] as { content?: string }).content ?? "";
1620
- assert.match(content, /Subagent 'worker' is ambiguous/);
1620
+ assert.match(content, /Subagent 'builder' is ambiguous/);
1621
1621
  assert.match(content, /project:/);
1622
1622
  assert.match(content, /builtin:/);
1623
1623
  });
@@ -1729,7 +1729,7 @@ describe("subagents admin slash command", { skip: !available ? "slash-commands.t
1729
1729
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
1730
1730
  fs.writeFileSync(settingsPath, JSON.stringify({ subagents: { disableThinking: true } }, null, 2), "utf-8");
1731
1731
  const commands = registerAdmin([]);
1732
- await commands.get("subagents")!.handler("worker model", createCommandContext({
1732
+ await commands.get("subagents")!.handler("builder model", createCommandContext({
1733
1733
  cwd: root,
1734
1734
  hasUI: true,
1735
1735
  modelRegistry: { getAvailable: () => [{ provider: "test", id: "new-model", reasoning: true }] },
@@ -1738,7 +1738,7 @@ describe("subagents admin slash command", { skip: !available ? "slash-commands.t
1738
1738
 
1739
1739
  assert.deepEqual(JSON.parse(fs.readFileSync(settingsPath, "utf-8")).subagents, {
1740
1740
  disableThinking: true,
1741
- agentOverrides: { worker: { model: "test/new-model" } },
1741
+ agentOverrides: { builder: { model: "test/new-model" } },
1742
1742
  });
1743
1743
  });
1744
1744
  });
@@ -2063,7 +2063,7 @@ describe("subagents-doctor slash command", { skip: !available ? "slash-commands.
2063
2063
 
2064
2064
  assert.equal(opened, 2);
2065
2065
  assert.match(rendered, /Subagent fleet/);
2066
- assert.match(rendered, /inspection only/);
2066
+ assert.match(rendered, /live controls/);
2067
2067
  });
2068
2068
 
2069
2069
  it("routes subagents-stop with an id directly to the stop action", async () => {
@@ -2071,6 +2071,66 @@ describe("subagents-doctor slash command", { skip: !available ? "slash-commands.
2071
2071
  assert.deepEqual(params, { action: "stop", id: "run-123" });
2072
2072
  });
2073
2073
 
2074
+ it("detaches the latest active foreground single run without terminating it", async () => {
2075
+ const commands = new Map<string, RegisteredSlashCommand>();
2076
+ const sent: unknown[] = [];
2077
+ const state = createState(process.cwd());
2078
+ let detached = 0;
2079
+ state.foregroundControls.set("foreground-single", {
2080
+ runId: "foreground-single",
2081
+ mode: "single",
2082
+ startedAt: 1,
2083
+ updatedAt: 2,
2084
+ detach: () => { detached++; return true; },
2085
+ });
2086
+ state.foregroundControls.set("newer-parallel", {
2087
+ runId: "newer-parallel",
2088
+ mode: "parallel",
2089
+ startedAt: 2,
2090
+ updatedAt: 3,
2091
+ });
2092
+ state.lastForegroundControlId = "newer-parallel";
2093
+ registerSlashCommands!({
2094
+ events: createEventBus(),
2095
+ registerCommand(name: string, spec: RegisteredSlashCommand) { commands.set(name, spec); },
2096
+ registerShortcut() {},
2097
+ sendMessage(message: unknown) { sent.push(message); },
2098
+ }, state);
2099
+
2100
+ await commands.get("subagents-detach")!.handler("", createCommandContext());
2101
+
2102
+ assert.equal(detached, 1);
2103
+ const content = String((sent[0] as { content?: unknown }).content ?? "");
2104
+ assert.match(content, /Detached foreground run foreground-single without terminating its child/);
2105
+ assert.match(content, /does not daemonize the process or guarantee survival across Pi reload\/restart/);
2106
+ });
2107
+
2108
+ it("reports missing and unsupported foreground detach targets", async () => {
2109
+ const commands = new Map<string, RegisteredSlashCommand>();
2110
+ const notifications: string[] = [];
2111
+ const state = createState(process.cwd());
2112
+ state.foregroundControls.set("parallel-run", {
2113
+ runId: "parallel-run",
2114
+ mode: "parallel",
2115
+ startedAt: 1,
2116
+ updatedAt: 1,
2117
+ detach: () => true,
2118
+ });
2119
+ registerSlashCommands!({
2120
+ events: createEventBus(),
2121
+ registerCommand(name: string, spec: RegisteredSlashCommand) { commands.set(name, spec); },
2122
+ registerShortcut() {},
2123
+ sendMessage() {},
2124
+ }, state);
2125
+ const ctx = createCommandContext({ notify: (message) => notifications.push(message) });
2126
+
2127
+ await commands.get("subagents-detach")!.handler("missing", ctx);
2128
+ await commands.get("subagents-detach")!.handler("parallel", ctx);
2129
+
2130
+ assert.match(notifications[0] ?? "", /No active foreground run found for 'missing'/);
2131
+ assert.match(notifications[1] ?? "", /currently supports single-subagent runs only/);
2132
+ });
2133
+
2074
2134
  it("prints exact stop commands when subagents-stop has no UI", async () => {
2075
2135
  await withIsolatedHome(async () => {
2076
2136
  const runId = `slash-stop-${Date.now().toString(36)}`;
@@ -2162,6 +2222,17 @@ describe("subagents-doctor slash command", { skip: !available ? "slash-commands.
2162
2222
  cwd: root,
2163
2223
  sessionId: "session-test",
2164
2224
  jobs: [{
2225
+ id: "job-1",
2226
+ name: "early scout",
2227
+ schedule: "+5m",
2228
+ runAt: Date.now() + 300_000,
2229
+ state: "scheduled",
2230
+ createdAt: Date.now(),
2231
+ updatedAt: Date.now(),
2232
+ cwd: root,
2233
+ sessionId: "session-test",
2234
+ params: { agent: "scout", task: "soon", async: true },
2235
+ }, {
2165
2236
  id: "job-2",
2166
2237
  name: "delayed worker",
2167
2238
  schedule: "+10m",
@@ -2200,9 +2271,27 @@ describe("subagents-doctor slash command", { skip: !available ? "slash-commands.
2200
2271
  await commands.get("subagents-stop")!.handler("", createCommandContext({
2201
2272
  cwd: root,
2202
2273
  hasUI: true,
2203
- custom: async () => ({
2204
- confirmed: true,
2205
- target: { kind: "scheduled", id: "job-2", label: "job-2", detail: "scheduled", actionLabel: "cancel scheduled run" },
2274
+ custom: async (...args: unknown[]) => new Promise((resolve) => {
2275
+ const factory = args[0] as (
2276
+ tui: { requestRender(): void },
2277
+ theme: { fg(key: string, text: string): string; bold(text: string): string },
2278
+ keybindings: unknown,
2279
+ done: (result: unknown) => void,
2280
+ ) => { handleInput(data: string): void; render(width: number): string[] };
2281
+ const component = factory(
2282
+ { requestRender() {} },
2283
+ { fg(_key, text) { return text; }, bold(text) { return text; } },
2284
+ {},
2285
+ resolve,
2286
+ );
2287
+ assert.match(component.render(84).join("\n"), /↑↓\/jk select/);
2288
+ component.handleInput("j");
2289
+ assert.ok(component.render(84).some((line) => line.startsWith("›") && line.includes("job-2")));
2290
+ component.handleInput("k");
2291
+ assert.ok(component.render(84).some((line) => line.startsWith("›") && line.includes("job-1")));
2292
+ component.handleInput("j");
2293
+ component.handleInput("\r");
2294
+ component.handleInput("y");
2206
2295
  }),
2207
2296
  }));
2208
2297
 
@@ -185,6 +185,14 @@ function writeStructuredOutputCapture(response) {
185
185
  fs.writeFileSync(outputPath, JSON.stringify(response.structuredOutputCapture), "utf-8");
186
186
  }
187
187
 
188
+ function writeRuntimeAcknowledgedExtensions(response) {
189
+ if (!Object.prototype.hasOwnProperty.call(response, "runtimeAcknowledgedExtensions")) return;
190
+ const outputPath = process.env.SELESAI_SUBAGENT_RUNTIME_ACKNOWLEDGED_EXTENSIONS;
191
+ if (!outputPath) return;
192
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
193
+ fs.writeFileSync(outputPath, JSON.stringify(response.runtimeAcknowledgedExtensions), "utf-8");
194
+ }
195
+
188
196
  function writeToolDiagnostic(response) {
189
197
  if (!Array.isArray(response.missingTools) || response.missingTools.length === 0) return;
190
198
  const diagnosticPath = process.env.SELESAI_SUBAGENT_TOOL_DIAGNOSTIC_PATH;
@@ -327,11 +335,10 @@ async function main() {
327
335
  }
328
336
  writeSessionFile(args);
329
337
  writeToolDiagnostic(response);
330
- fs.writeFileSync(
331
- path.join(queueDir, `call-${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}.json`),
332
- JSON.stringify({ args, systemPrompts: readSystemPromptRecords(args) }),
333
- "utf-8",
334
- );
338
+ const callPath = path.join(queueDir, `call-${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}.json`);
339
+ const callTempPath = `${callPath}.tmp-${process.pid}-${Date.now()}`;
340
+ fs.writeFileSync(callTempPath, JSON.stringify({ args, systemPrompts: readSystemPromptRecords(args) }), "utf-8");
341
+ fs.renameSync(callTempPath, callPath);
335
342
 
336
343
  if (typeof response.delay === "number" && response.delay > 0) {
337
344
  await new Promise((resolve) => setTimeout(resolve, response.delay));
@@ -346,6 +353,7 @@ async function main() {
346
353
 
347
354
  writeDeclaredFiles(response);
348
355
  writeStructuredOutputCapture(response);
356
+ writeRuntimeAcknowledgedExtensions(response);
349
357
 
350
358
  if (Array.isArray(response.steps) && response.steps.length > 0) {
351
359
  for (const step of response.steps) {
@@ -384,6 +392,10 @@ async function main() {
384
392
  await new Promise((resolve) => setTimeout(resolve, response.keepAliveAfterFinalMessageMs));
385
393
  }
386
394
 
395
+ if (typeof response.signal === "string") {
396
+ process.kill(process.pid, response.signal);
397
+ return;
398
+ }
387
399
  exitAfterFlush(typeof response.exitCode === "number" ? response.exitCode : 0);
388
400
  }
389
401
 
@@ -7,6 +7,7 @@ interface MockPiResponse {
7
7
  output?: string;
8
8
  stderr?: string;
9
9
  exitCode?: number;
10
+ signal?: NodeJS.Signals;
10
11
  delay?: number;
11
12
  waitForPath?: string;
12
13
  keepAliveAfterFinalMessageMs?: number;
@@ -28,6 +29,7 @@ interface MockPiResponse {
28
29
  writeFiles?: Array<{ path: string; content: string }>;
29
30
  /** Writes the structured-output capture file without emitting a structured_output tool event. */
30
31
  structuredOutputCapture?: unknown;
32
+ runtimeAcknowledgedExtensions?: unknown;
31
33
  }
32
34
 
33
35
  export interface MockPi {
@@ -1,4 +1,4 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -47,6 +47,21 @@ export function subagentCall(args: Record<string, unknown>, id = "call-subagent-
47
47
  return fauxToolCall("subagent", args, { id });
48
48
  }
49
49
 
50
+ function resolvedSubagentResultText(resultText: string): string {
51
+ // Reference-first completion: the tool result carries "Output saved to:
52
+ // <path> (…)". Resolve the durable file so the scripted parent can relay the
53
+ // exact child output.
54
+ if (!resultText.includes("Output saved to: ")) return resultText;
55
+ const rest = resultText.split("Output saved to: ")[1];
56
+ if (!rest) return resultText;
57
+ const outputPath = rest.split(" (")[0];
58
+ try {
59
+ return readFileSync(outputPath, "utf-8");
60
+ } catch {
61
+ return resultText;
62
+ }
63
+ }
64
+
50
65
  export function routeParentThroughSubagent(input: {
51
66
  childMarker: string;
52
67
  subagentArgs: Record<string, unknown>;
@@ -56,7 +71,8 @@ export function routeParentThroughSubagent(input: {
56
71
  if (!isParent) return "Unexpected non-parent model call.";
57
72
  const resultText = latestSubagentToolResultText(context.messages as Array<{ role?: string; toolName?: string; content?: unknown }>);
58
73
  if (resultText !== undefined) {
59
- return `Parent relays: ${resultText.includes(input.childMarker) ? input.childMarker : "CHILD_MISSING"}`;
74
+ const resolved = resolvedSubagentResultText(resultText);
75
+ return `Parent relays: ${resolved.includes(input.childMarker) ? input.childMarker : "CHILD_MISSING"}`;
60
76
  }
61
77
  return subagentCall(input.subagentArgs);
62
78
  };
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Register the .js → .ts loader hook for integration tests.
3
3
  *
4
- * Usage: node --experimental-transform-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts
4
+ * Usage: node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts
5
5
  *
6
6
  * Handles two issues:
7
7
  * 1. Source files use .js import extensions (TypeScript ESM convention) but
8
8
  * files on disk are .ts — the loader rewrites .js → .ts at resolve time.
9
- * 2. Some source files use TypeScript parameter properties (constructor(private x: T))
10
- * which require --experimental-transform-types (not just strip-types).
9
+ * 2. Source stays within Node's erasable TypeScript syntax so the supported
10
+ * --experimental-strip-types path is sufficient.
11
11
  */
12
12
 
13
13
  import { register } from "node:module";
@@ -1046,7 +1046,9 @@ describe("acceptance gates", () => {
1046
1046
  { id: "release check", must: "second" },
1047
1047
  ] }).join("\n"), /acceptance\.criteria\[1\]\.id duplicates normalized criterion id 'release-check'/);
1048
1048
  assert.match(validateAcceptanceInput({ criteria: [123] }).join("\n"), /acceptance\.criteria\[0\] must be a string or an object/);
1049
- assert.match(validateAcceptanceInput({ evidence: ["bogus"] }).join("\n"), /acceptance\.evidence\[0\] is not a supported evidence kind/);
1049
+ assert.match(validateAcceptanceInput({ evidence: "commands-run" }).join("\n"), /acceptance\.evidence must be an array.*Supported evidence kinds:.*commands-run.*changed-files/s);
1050
+ assert.match(validateAcceptanceInput({ evidence: ["bogus"] }).join("\n"), /acceptance\.evidence\[0\] "bogus" is not a supported evidence kind.*Supported evidence kinds:.*manual-notes.*Example:/s);
1051
+ assert.match(validateAcceptanceInput({ criteria: [{ id: "ship", must: "ship", evidence: ["commands_run"] }] }).join("\n"), /acceptance\.criteria\[0\]\.evidence\[0\] "commands_run" is not a supported evidence kind.*Supported evidence kinds:/s);
1050
1052
  assert.match(validateAcceptanceInput({ review: true }).join("\n"), /acceptance\.review must be false or an object/);
1051
1053
  assert.match(validateAcceptanceInput({ review: { required: "yes" } }).join("\n"), /acceptance\.review\.required must be a boolean/);
1052
1054
  assert.match(validateAcceptanceInput({ stopRules: [123] }).join("\n"), /acceptance\.stopRules\[0\] must be a string/);
@@ -177,7 +177,7 @@ describe("builtin agent disabling", () => {
177
177
  { cwd: tempProject, modelRegistry: { getAvailable: () => [] } },
178
178
  ));
179
179
 
180
- assert.match(text, /Executable agents:\n- helper \(project\): Helper/);
180
+ assert.match(text, /Executable agents:\n- helper \(project, context: fresh\): Helper/);
181
181
  assert.doesNotMatch(text, /Disabled builtins:/);
182
182
  for (const name of disabledBuiltinNames) {
183
183
  assert.doesNotMatch(text, new RegExp(`^- ${name} \\(builtin`, "m"));
@@ -6,7 +6,7 @@ import { afterEach, describe, it } from "node:test";
6
6
  import { handleManagementAction } from "../../src/agents/agent-management.ts";
7
7
  import { serializeAgent } from "../../src/agents/agent-serializer.ts";
8
8
  import { parseChain, serializeChain } from "../../src/agents/chain-serializer.ts";
9
- import { discoverAgents, discoverAgentsAll, type AgentConfig } from "../../src/agents/agents.ts";
9
+ import { BUILTIN_AGENT_NAMES, discoverAgents, discoverAgentsAll, type AgentConfig } from "../../src/agents/agents.ts";
10
10
  import { parseFrontmatter } from "../../src/agents/frontmatter.ts";
11
11
  import { buildPiArgs } from "../../src/runs/shared/pi-args.ts";
12
12
  import { THINKING_LEVELS } from "../../src/shared/model-info.ts";
@@ -153,6 +153,47 @@ body`);
153
153
  }));
154
154
  });
155
155
 
156
+ describe("agent aliases", () => {
157
+ it("parses and serializes agent aliases", () => withTempHome(() => {
158
+ const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-alias-agent-"));
159
+ tempDirs.push(project);
160
+ writeAgent(path.join(project, ".selesai", "agents", "worker.md"), `---
161
+ name: worker
162
+ description: Worker
163
+ aliases: shipper, tester, worker
164
+ ---
165
+ body`);
166
+
167
+ const worker = discoverAgents(project, "both").agents.find((agent) => agent.name === "worker")!;
168
+ assert.deepEqual(worker.aliases, ["shipper", "tester"]);
169
+ assert.match(serializeAgent(worker), /^aliases: shipper, tester$/m);
170
+
171
+ const ctx = { cwd: project, modelRegistry: { getAvailable: () => [] } };
172
+ assert.match(handleManagementAction("list", {}, ctx).content[0]?.text ?? "", /aliases: shipper, tester/);
173
+ assert.match(handleManagementAction("get", { agent: "shipper" }, ctx).content[0]?.text ?? "", /Agent: worker/);
174
+ }));
175
+
176
+ it("reports management alias collisions as ambiguous", () => withTempHome(() => {
177
+ const project = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-alias-collision-"));
178
+ tempDirs.push(project);
179
+ writeAgent(path.join(project, ".selesai", "agents", "review-agent.md"), `---
180
+ name: review-agent
181
+ description: Custom reviewer
182
+ aliases: developer
183
+ ---
184
+ body`);
185
+
186
+ const ctx = { cwd: project, modelRegistry: { getAvailable: () => [] } };
187
+ const getResult = handleManagementAction("get", { agent: "developer" }, ctx);
188
+ assert.equal(getResult.isError, true);
189
+ assert.match(getResult.content[0]?.text ?? "", /Ambiguous agent alias or name 'developer': builder, review-agent|Ambiguous agent alias or name 'developer': review-agent, builder/);
190
+
191
+ const disableResult = handleManagementAction("disable", { agent: "developer" }, ctx);
192
+ assert.equal(disableResult.isError, true);
193
+ assert.match(disableResult.content[0]?.text ?? "", /Ambiguous agent alias 'developer': builder, review-agent|Ambiguous agent alias 'developer': review-agent, builder/);
194
+ }));
195
+ });
196
+
156
197
  describe("agent simple-scalar list frontmatter", () => {
157
198
  it("discovers newline block lists for all list fields and routes MCP tools", () => {
158
199
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-agent-block-list-frontmatter-"));
@@ -316,14 +357,54 @@ Do work
316
357
  assert.equal(worker?.defaultContext, "fork");
317
358
  });
318
359
 
319
- it("loads packaged architect and recapper with fork defaultContext", () => {
360
+ it("loads builtin agents with the authoritative six-agent defaultContext map", () => {
320
361
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-default-context-"));
321
362
  tempDirs.push(dir);
322
363
  const agents = discoverAgentsAll(dir).builtin;
323
364
 
324
- for (const name of ["architect", "recapper"]) {
365
+ const expected: Record<string, "fresh" | "fork"> = {
366
+ architect: "fork",
367
+ recapper: "fork",
368
+ builder: "fresh",
369
+ commentator: "fresh",
370
+ explorer: "fresh",
371
+ researcher: "fresh",
372
+ };
373
+ assert.deepEqual(
374
+ agents.map((agent) => agent.name).sort(),
375
+ [...BUILTIN_AGENT_NAMES].sort(),
376
+ );
377
+ for (const [name, context] of Object.entries(expected)) {
325
378
  const agent = agents.find((candidate) => candidate.name === name);
326
- assert.equal(agent?.defaultContext, "fork", `${name} should default to fork context`);
379
+ assert.equal(agent?.defaultContext, context, `${name} should default to ${context} context`);
380
+ }
381
+ });
382
+ });
383
+
384
+ describe("bundled agent descriptions and acceptance roles", () => {
385
+ it("survives discovery with declared descriptions and acceptance roles", () => {
386
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-bundled-catalog-roles-"));
387
+ tempDirs.push(dir);
388
+ const agents = discoverAgentsAll(dir).builtin;
389
+
390
+ const expected: Record<string, { description: string; acceptanceRole: "read-only" | "writer" }> = {
391
+ architect: { description: "Read-only architecture and implementation planning", acceptanceRole: "read-only" },
392
+ builder: { description: "Mutation-capable scoped implementation", acceptanceRole: "writer" },
393
+ commentator: { description: "Read-only evidence-based review", acceptanceRole: "read-only" },
394
+ explorer: { description: "Read-only local codebase reconnaissance", acceptanceRole: "read-only" },
395
+ recapper: { description: "Read-only handoff and context synthesis", acceptanceRole: "read-only" },
396
+ researcher: { description: "Read-only external and code-first research", acceptanceRole: "read-only" },
397
+ };
398
+
399
+ assert.deepEqual(
400
+ agents.map((agent) => agent.name).sort(),
401
+ [...BUILTIN_AGENT_NAMES].sort(),
402
+ );
403
+ for (const [name, expectedEntry] of Object.entries(expected)) {
404
+ const agent = agents.find((candidate) => candidate.name === name);
405
+ assert.ok(agent, `bundled agent ${name} not discovered`);
406
+ assert.equal(agent.description, expectedEntry.description, `${name} description`);
407
+ assert.equal(agent.acceptanceRole, expectedEntry.acceptanceRole, `${name} acceptanceRole`);
327
408
  }
328
409
  });
329
410
  });
@@ -603,6 +684,72 @@ Review nested project work.
603
684
  assert.equal(agent.filePath, path.join(packageRoot, "agents", "reviewer.md"));
604
685
  }));
605
686
 
687
+ it("keeps nearest project root discovery by default when a nested .pi exists", () => withTempHome(() => {
688
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-nearest-root-default-"));
689
+ tempDirs.push(dir);
690
+ const nested = path.join(dir, "packages", "app", "src");
691
+ const nestedConfigDir = path.join(dir, "packages", "app", ".selesai");
692
+ const packageRoot = path.join(dir, ".selesai", "npm", "node_modules", "outer-workflow");
693
+ fs.mkdirSync(nested, { recursive: true });
694
+ fs.mkdirSync(nestedConfigDir, { recursive: true });
695
+ fs.mkdirSync(path.join(dir, ".git"), { recursive: true });
696
+ writeJson(path.join(packageRoot, "package.json"), {
697
+ name: "outer-workflow",
698
+ "pi-subagents": { agents: ["./agents"] },
699
+ });
700
+ writeAgent(path.join(packageRoot, "agents", "planner.md"), `---
701
+ name: planner
702
+ package: outer-workflow
703
+ description: Plan from the outer project package.
704
+ ---
705
+
706
+ Plan outer project work.
707
+ `);
708
+
709
+ const agent = discoverAgents(nested, "both").agents.find((candidate) => candidate.name === "outer-workflow.planner");
710
+ assert.equal(agent, undefined);
711
+ }));
712
+
713
+ it("can resolve project packages and overrides from the git root", () => withTempHome(() => {
714
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-git-root-resolution-"));
715
+ tempDirs.push(dir);
716
+ const nested = path.join(dir, "packages", "app", "src");
717
+ const nestedConfigDir = path.join(dir, "packages", "app", ".selesai");
718
+ const packageRoot = path.join(dir, ".selesai", "npm", "node_modules", "outer-workflow");
719
+ fs.mkdirSync(nested, { recursive: true });
720
+ fs.mkdirSync(nestedConfigDir, { recursive: true });
721
+ fs.writeFileSync(path.join(dir, ".git"), "gitdir: ../.git/worktrees/app\n", "utf-8");
722
+ writeJson(path.join(dir, ".selesai", "settings.json"), {
723
+ subagents: {
724
+ projectRootResolution: "git-root",
725
+ agentOverrides: {
726
+ commentator: { model: "openai/gpt-5.4" },
727
+ },
728
+ },
729
+ });
730
+ writeJson(path.join(packageRoot, "package.json"), {
731
+ name: "outer-workflow",
732
+ "pi-subagents": { agents: ["./agents"] },
733
+ });
734
+ writeAgent(path.join(packageRoot, "agents", "planner.md"), `---
735
+ name: planner
736
+ package: outer-workflow
737
+ description: Plan from the outer project package.
738
+ ---
739
+
740
+ Plan outer project work.
741
+ `);
742
+
743
+ const agents = discoverAgents(nested, "both").agents;
744
+ const packageAgent = agents.find((candidate) => candidate.name === "outer-workflow.planner");
745
+ assert.ok(packageAgent);
746
+ assert.equal(packageAgent.source, "package");
747
+ assert.equal(packageAgent.filePath, path.join(packageRoot, "agents", "planner.md"));
748
+ const commentator = agents.find((candidate) => candidate.name === "commentator");
749
+ assert.equal(commentator?.model, "openai/gpt-5.4");
750
+ assert.equal(commentator?.override?.path, path.join(dir, ".selesai", "settings.json"));
751
+ }));
752
+
606
753
  it("does not register legacy skill files from broad package agent roots", () => withTempHome(() => {
607
754
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-broad-package-skills-"));
608
755
  tempDirs.push(dir);
@@ -1203,7 +1350,28 @@ Do work
1203
1350
  }
1204
1351
  });
1205
1352
 
1206
- it("builder includes the child-facing supervisor tool", () => {
1353
+ it("keeps the bundled architect read-only for repository tools", () => {
1354
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-architect-tools-"));
1355
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-architect-home-"));
1356
+ tempDirs.push(dir, homeDir);
1357
+ const previousHome = process.env.HOME;
1358
+ const previousUserProfile = process.env.USERPROFILE;
1359
+ try {
1360
+ process.env.HOME = homeDir;
1361
+ process.env.USERPROFILE = homeDir;
1362
+ const architect = discoverAgentsAll(dir).builtin.find((candidate) => candidate.name === "architect");
1363
+ assert.ok(architect);
1364
+ assert.deepEqual(architect.tools, ["read", "grep", "find", "ls"]);
1365
+ assert.equal(architect.acceptanceRole, "read-only");
1366
+ } finally {
1367
+ if (previousHome === undefined) delete process.env.HOME;
1368
+ else process.env.HOME = previousHome;
1369
+ if (previousUserProfile === undefined) delete process.env.USERPROFILE;
1370
+ else process.env.USERPROFILE = previousUserProfile;
1371
+ }
1372
+ });
1373
+
1374
+ it("builder builtin tools exclude the bridge-injected supervisor tool", () => {
1207
1375
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-supervisor-tool-"));
1208
1376
  const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-supervisor-tool-home-"));
1209
1377
  tempDirs.push(dir);
@@ -1216,7 +1384,31 @@ Do work
1216
1384
  process.env.USERPROFILE = homeDir;
1217
1385
  const agent = discoverAgentsAll(dir).builtin.find((candidate) => candidate.name === "builder");
1218
1386
  assert.ok(agent, "builder builtin should be discovered");
1219
- assert.deepEqual(agent?.tools, ["read", "grep", "find", "ls", "bash", "edit", "write", "contact_supervisor"]);
1387
+ assert.deepEqual(agent?.tools, ["read", "grep", "find", "ls", "bash", "edit", "write"]);
1388
+ assert.equal(agent?.tools?.includes("contact_supervisor"), false);
1389
+ } finally {
1390
+ if (previousHome === undefined) delete process.env.HOME;
1391
+ else process.env.HOME = previousHome;
1392
+ if (previousUserProfile === undefined) delete process.env.USERPROFILE;
1393
+ else process.env.USERPROFILE = previousUserProfile;
1394
+ }
1395
+ });
1396
+
1397
+ it("researcher builtin routes grep.app tools into mcpDirectTools", () => {
1398
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-researcher-tools-"));
1399
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-builtin-researcher-tools-home-"));
1400
+ tempDirs.push(dir);
1401
+ tempDirs.push(homeDir);
1402
+ const previousHome = process.env.HOME;
1403
+ const previousUserProfile = process.env.USERPROFILE;
1404
+
1405
+ try {
1406
+ process.env.HOME = homeDir;
1407
+ process.env.USERPROFILE = homeDir;
1408
+ const agent = discoverAgentsAll(dir).builtin.find((candidate) => candidate.name === "researcher");
1409
+ assert.ok(agent, "researcher builtin should be discovered");
1410
+ assert.deepEqual(agent?.tools, ["read", "web_explore"]);
1411
+ assert.deepEqual(agent?.mcpDirectTools, ["grep_app_search", "grep_app_fetch"]);
1220
1412
  } finally {
1221
1413
  if (previousHome === undefined) delete process.env.HOME;
1222
1414
  else process.env.HOME = previousHome;