@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
@@ -53,7 +53,7 @@ describe("agent management config parsing", () => {
53
53
  );
54
54
 
55
55
  assert.equal(result.isError, false);
56
- assert.match(readText(result), /- scout \(project\): Project scout override/);
56
+ assert.match(readText(result), /- scout \(project, context: fresh\): Project scout override/);
57
57
  assert.doesNotMatch(readText(result), /- scout \(builtin/);
58
58
  });
59
59
 
@@ -722,6 +722,126 @@ Drive the failing test first.
722
722
  assert.match(content, /inheritSkills: false/);
723
723
  });
724
724
 
725
+ it("exposes a rich human and machine catalog for a custom project agent and chain", () => {
726
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
727
+ const created = handleCreate(
728
+ {
729
+ config: {
730
+ name: "Catalog Writer",
731
+ description: "Scoped catalog writer",
732
+ scope: "project",
733
+ aliases: "catw",
734
+ acceptanceRole: "writer",
735
+ defaultContext: "fork",
736
+ tools: "read, grep, find, ls, bash, edit, write, mcp:fixture_search",
737
+ systemPrompt: "Implement scoped changes.",
738
+ },
739
+ },
740
+ ctx,
741
+ );
742
+ assert.equal(created.isError, false);
743
+
744
+ const chainCreated = handleCreate(
745
+ { config: { name: "Review Flow", description: "Catalog review flow", scope: "project", steps: [{ agent: "catalog-writer", task: "Inspect" }] } },
746
+ ctx,
747
+ );
748
+ assert.equal(chainCreated.isError, false);
749
+
750
+ const listed = handleManagementAction("list", {}, ctx);
751
+ assert.equal(listed.isError, false);
752
+ const text = readText(listed);
753
+ assert.match(text, /Executable agents:/);
754
+ assert.match(
755
+ text,
756
+ /- catalog-writer \(project, context: fork, role: writer, aliases: catw, tools: read, grep, find, ls, bash, edit, write, mcp:fixture_search\): Scoped catalog writer/,
757
+ );
758
+ assert.match(text, /Chains:\n- review-flow \(project\): Catalog review flow/);
759
+
760
+ const details = (listed as { details: { catalog: unknown } }).details.catalog as {
761
+ version: number;
762
+ agents: Array<{
763
+ name: string;
764
+ source: string;
765
+ description: string;
766
+ executable: boolean;
767
+ aliases?: string[];
768
+ defaultContext: string;
769
+ acceptanceRole?: string;
770
+ tools?: string[];
771
+ }>;
772
+ chains: Array<{ name: string; source: string; description: string }>;
773
+ };
774
+ assert.equal(details.version, 1);
775
+ const entry = details.agents.find((agent) => agent.name === "catalog-writer");
776
+ assert.ok(entry, "catalog must include the custom project agent");
777
+ assert.equal(entry.source, "project");
778
+ assert.equal(entry.description, "Scoped catalog writer");
779
+ assert.equal(entry.executable, true);
780
+ assert.deepEqual(entry.aliases, ["catw"]);
781
+ assert.equal(entry.defaultContext, "fork");
782
+ assert.equal(entry.acceptanceRole, "writer");
783
+ assert.deepEqual(entry.tools, ["read", "grep", "find", "ls", "bash", "edit", "write", "mcp:fixture_search"]);
784
+ assert.deepEqual(details.chains, [{ name: "review-flow", source: "project", description: "Catalog review flow" }]);
785
+ });
786
+
787
+ it("normalizes unset defaultContext to fresh in the machine catalog", () => {
788
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
789
+ const created = handleCreate(
790
+ { config: { name: "No Context", description: "No explicit context", scope: "project", systemPrompt: "Work" } },
791
+ ctx,
792
+ );
793
+ assert.equal(created.isError, false);
794
+
795
+ const listed = handleManagementAction("list", {}, ctx);
796
+ const details = (listed as { details: { catalog: unknown } }).details.catalog as {
797
+ agents: Array<{ name: string; defaultContext: string }>;
798
+ };
799
+ const entry = details.agents.find((agent) => agent.name === "no-context");
800
+ assert.ok(entry);
801
+ assert.equal(entry.defaultContext, "fresh");
802
+ assert.match(readText(listed), /- no-context \(project, context: fresh\)/);
803
+ });
804
+
805
+ it("omits disabled agents from both human text and machine catalog metadata", () => {
806
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
807
+ const created = handleCreate(
808
+ { config: { name: "Hidden Writer", description: "Should stay hidden", scope: "project", systemPrompt: "Work" } },
809
+ ctx,
810
+ );
811
+ assert.equal(created.isError, false);
812
+
813
+ const disabled = handleManagementAction("disable", { agent: "hidden-writer" }, ctx);
814
+ assert.equal(disabled.isError, false);
815
+
816
+ const listed = handleManagementAction("list", {}, ctx);
817
+ assert.equal(listed.isError, false);
818
+ assert.doesNotMatch(readText(listed), /hidden-writer/);
819
+ const details = (listed as { details: { catalog: unknown } }).details.catalog as {
820
+ agents: Array<{ name: string }>;
821
+ };
822
+ assert.equal(details.agents.some((agent) => agent.name === "hidden-writer"), false);
823
+ });
824
+
825
+ it("returns (none) text and an empty versioned catalog when no agents are discovered", () => {
826
+ fs.mkdirSync(path.join(tempDir, "agent-home"), { recursive: true });
827
+ fs.writeFileSync(
828
+ path.join(tempDir, "agent-home", "settings.json"),
829
+ JSON.stringify({ subagents: { disableBuiltins: true } }, null, 2),
830
+ "utf-8",
831
+ );
832
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
833
+ const listed = handleManagementAction("list", {}, ctx);
834
+ assert.equal(listed.isError, false);
835
+ const text = readText(listed);
836
+ assert.match(text, /Executable agents:\n- \(none\)/);
837
+ assert.match(text, /Chains:\n- \(none\)/);
838
+ assert.deepEqual((listed as { details: { catalog: unknown } }).details.catalog, {
839
+ version: 1,
840
+ agents: [],
841
+ chains: [],
842
+ });
843
+ });
844
+
725
845
  it("lists proactive skill subagent suggestions from repeated configured skill use", () => {
726
846
  const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
727
847
  fs.mkdirSync(path.join(tempDir, ".selesai", "agents"), { recursive: true });
@@ -774,4 +894,44 @@ Inspect cleanup.
774
894
  assert.doesNotMatch(readText(listed), /Proactive skill subagent suggestions:/);
775
895
  });
776
896
 
897
+ it("appends explicit-only task-aware advisory routing to list output when task is provided", () => {
898
+ const ctx = { cwd: tempDir, modelRegistry: { getAvailable: () => [] } };
899
+ handleCreate(
900
+ { config: { name: "Code Reviewer", description: "Reviews without editing", scope: "project", acceptanceRole: "read-only", tools: "read, grep, find, ls", systemPrompt: "Review." } },
901
+ ctx,
902
+ );
903
+ handleCreate(
904
+ { config: { name: "Fixer", description: "Implements changes", scope: "project", acceptanceRole: "writer", tools: "read, grep, bash, edit, write", systemPrompt: "Fix." } },
905
+ ctx,
906
+ );
907
+
908
+ const implementation = handleManagementAction("list", { task: "Implement the fix" }, ctx);
909
+ assert.equal(implementation.isError, false);
910
+ assert.deepEqual((implementation as { details: { results: unknown[] } }).details.results, []);
911
+ const implementationText = readText(implementation);
912
+ assert.match(implementationText, /Task-aware advisory routing:/);
913
+ assert.match(implementationText, /- Intent: implementation/);
914
+ assert.match(implementationText, /- Recommended: fixer \(project\)/);
915
+ assert.match(implementationText, /- Advisory only: no subagent was launched\. To proceed, explicitly call subagent with this canonical agent name and the task\./);
916
+
917
+ const review = handleManagementAction("list", { task: "Review only; do not edit files" }, ctx);
918
+ assert.equal(review.isError, false);
919
+ const reviewText = readText(review);
920
+ assert.match(reviewText, /- Intent: read-only/);
921
+ assert.match(reviewText, /- Recommended: code-reviewer \(project\)/);
922
+
923
+ const vague = handleManagementAction("list", { task: "Look into this" }, ctx);
924
+ assert.equal(vague.isError, false);
925
+ const vagueText = readText(vague);
926
+ assert.match(vagueText, /- Intent: unknown/);
927
+ assert.match(vagueText, /- Recommendation: none/);
928
+ assert.match(vagueText, /Clarify whether the task is read-only analysis\/review or implementation allowed to edit files\./);
929
+
930
+ for (const noTaskParams of [{}, { task: " " }]) {
931
+ const noTask = handleManagementAction("list", noTaskParams, ctx);
932
+ assert.equal(noTask.isError, false);
933
+ assert.doesNotMatch(readText(noTask), /Task-aware advisory routing:/);
934
+ }
935
+ });
936
+
777
937
  });
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { afterEach, beforeEach, describe, it } from "node:test";
6
- import { buildBuiltinOverrideConfig, discoverAgents, discoverAgentsAll, removeBuiltinAgentOverride } from "../../src/agents/agents.ts";
6
+ import { buildBuiltinOverrideConfig, discoverAgents, discoverAgentsAll, removeBuiltinAgentOverride, saveBuiltinAgentOverride } from "../../src/agents/agents.ts";
7
7
 
8
8
  let tempHome = "";
9
9
  let tempProject = "";
@@ -178,6 +178,22 @@ describe("builtin agent overrides", () => {
178
178
  assert.equal(agents.find((agent) => agent.name === "architect-copy")?.model, "deepseek-v4-flash");
179
179
  });
180
180
 
181
+ it("overrides builtin and custom agent descriptions from settings", () => {
182
+ writeJson(path.join(tempHome, ".selesai", "agent", "settings.json"), {
183
+ subagents: {
184
+ agentOverrides: {
185
+ commentator: { description: " Priced commentator " },
186
+ implementer: { description: "Priced implementer" },
187
+ },
188
+ },
189
+ });
190
+ writeProjectAgent(tempProject, "implementer", `---\nname: implementer\ndescription: Original implementer\n---\n\nImplement it.\n`);
191
+
192
+ const agents = discoverAgents(tempProject, "both").agents;
193
+ assert.equal(agents.find((agent) => agent.name === "commentator")?.description, "Priced commentator");
194
+ assert.equal(agents.find((agent) => agent.name === "implementer")?.description, "Priced implementer");
195
+ });
196
+
181
197
  it("applies user settings overrides to builtin agents", () => {
182
198
  writeJson(path.join(tempHome, ".selesai", "agent", "settings.json"), {
183
199
  subagents: {
@@ -625,6 +641,22 @@ describe("builtin agent overrides", () => {
625
641
  );
626
642
  });
627
643
 
644
+ it("surfaces malformed description override values", () => {
645
+ const settingsPath = path.join(tempHome, ".selesai", "agent", "settings.json");
646
+ for (const description of ["", 42]) {
647
+ writeJson(settingsPath, {
648
+ subagents: { agentOverrides: { commentator: { description } } },
649
+ });
650
+ assert.throws(
651
+ () => discoverAgents(tempProject, "both"),
652
+ (error: unknown) => error instanceof Error
653
+ && error.message.includes(settingsPath)
654
+ && error.message.includes("commentator")
655
+ && error.message.includes("description"),
656
+ );
657
+ }
658
+ });
659
+
628
660
  it("surfaces malformed completion guard override values", () => {
629
661
  const settingsPath = path.join(tempHome, ".selesai", "agent", "settings.json");
630
662
  writeJson(settingsPath, {
@@ -646,9 +678,10 @@ describe("builtin agent overrides", () => {
646
678
  );
647
679
  });
648
680
 
649
- it("builds false sentinels when an override clears builtin fields", () => {
681
+ it("builds description changes and false sentinels when an override clears builtin fields", () => {
650
682
  const override = buildBuiltinOverrideConfig(
651
683
  {
684
+ description: "Base description",
652
685
  model: "openai-codex/gpt-5.4-mini",
653
686
  fallbackModels: ["openai/gpt-5-mini"],
654
687
  thinking: "high",
@@ -665,6 +698,7 @@ describe("builtin agent overrides", () => {
665
698
  completionGuard: false,
666
699
  },
667
700
  {
701
+ description: "Override description",
668
702
  model: undefined,
669
703
  fallbackModels: undefined,
670
704
  thinking: undefined,
@@ -683,6 +717,7 @@ describe("builtin agent overrides", () => {
683
717
  );
684
718
 
685
719
  assert.deepEqual(override, {
720
+ description: "Override description",
686
721
  model: false,
687
722
  fallbackModels: false,
688
723
  thinking: false,
@@ -695,5 +730,15 @@ describe("builtin agent overrides", () => {
695
730
  subagentOnlyExtensions: false,
696
731
  completionGuard: true,
697
732
  });
733
+ assert.ok(override);
734
+ fs.mkdirSync(path.join(tempProject, ".selesai"), { recursive: true });
735
+ saveBuiltinAgentOverride(tempProject, "commentator", "project", override);
736
+ assert.equal(discoverAgents(tempProject, "both").agents.find((agent) => agent.name === "commentator")?.description, "Override description");
737
+
738
+ const whitespaceDescription = buildBuiltinOverrideConfig(
739
+ { description: "Base description" },
740
+ { description: " " } as Parameters<typeof buildBuiltinOverrideConfig>[1],
741
+ );
742
+ assert.equal(whitespaceDescription, undefined);
698
743
  });
699
744
  });
@@ -0,0 +1,60 @@
1
+ import assert from "node:assert/strict";
2
+ import * as fs from "node:fs";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+ import { describe, it } from "node:test";
6
+ import { readAsyncRecoveryDescriptor } from "../../src/runs/background/async-resume.ts";
7
+
8
+ describe("async recovery descriptor", () => {
9
+ it("accepts launchContractDigest written by async execution", () => {
10
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-digest-"));
11
+ try {
12
+ const digest = "launch-contract-digest";
13
+ fs.writeFileSync(path.join(root, "recovery-descriptor.json"), JSON.stringify({
14
+ version: 1,
15
+ launchContractDigest: digest,
16
+ sourceRunId: "run-digest",
17
+ agent: "worker",
18
+ cwd: root,
19
+ systemPromptMode: "replace",
20
+ inheritProjectContext: false,
21
+ inheritSkills: false,
22
+ outputMode: "inline",
23
+ maxSubagentDepth: 2,
24
+ share: false,
25
+ }), "utf-8");
26
+
27
+ const descriptor = readAsyncRecoveryDescriptor(root);
28
+
29
+ assert.equal(descriptor?.launchContractDigest, digest);
30
+ } finally {
31
+ fs.rmSync(root, { recursive: true, force: true });
32
+ }
33
+ });
34
+
35
+ it("rejects malformed launchContractDigest values", () => {
36
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-async-recovery-bad-digest-"));
37
+ try {
38
+ fs.writeFileSync(path.join(root, "recovery-descriptor.json"), JSON.stringify({
39
+ version: 1,
40
+ launchContractDigest: {},
41
+ sourceRunId: "run-digest",
42
+ agent: "worker",
43
+ cwd: root,
44
+ systemPromptMode: "replace",
45
+ inheritProjectContext: false,
46
+ inheritSkills: false,
47
+ outputMode: "inline",
48
+ maxSubagentDepth: 2,
49
+ share: false,
50
+ }), "utf-8");
51
+
52
+ assert.throws(
53
+ () => readAsyncRecoveryDescriptor(root),
54
+ /launchContractDigest must be a non-empty string/,
55
+ );
56
+ } finally {
57
+ fs.rmSync(root, { recursive: true, force: true });
58
+ }
59
+ });
60
+ });
@@ -109,6 +109,14 @@ describe("async resume lookup", () => {
109
109
  });
110
110
  assert.throws(() => resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir }), /capabilityCeiling sources/);
111
111
 
112
+ writeJson(path.join(asyncDir, "recovery-descriptor.json"), {
113
+ ...descriptor,
114
+ launchContractDigest: "launch-contract-digest",
115
+ });
116
+ const valid = resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir });
117
+ assert.equal(valid.launchContractDigest, "launch-contract-digest");
118
+ assert.equal(valid.recoveryDescriptor?.launchContractDigest, "launch-contract-digest");
119
+
112
120
  writeJson(path.join(asyncDir, "recovery-descriptor.json"), { ...descriptor, sourceRunId: "another-run" });
113
121
  assert.throws(() => resolveAsyncResumeTarget({ id: "run-descriptor" }, { asyncDirRoot: asyncRoot, resultsDir }), /different source run/);
114
122
 
@@ -0,0 +1,63 @@
1
+ import assert from "node:assert/strict";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { fileURLToPath } from "node:url";
6
+ import { BUILTIN_AGENT_NAMES } from "../../src/agents/agents.ts";
7
+
8
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
9
+
10
+ const README_PATH = path.join(packageRoot, "README.md");
11
+ const PROMPTING_AND_ROLES_PATH = path.join(packageRoot, "skills", "pi-subagents", "references", "prompting-and-roles.md");
12
+
13
+ const README_TABLE_HEADING = "## Builtin agents in plain English";
14
+ const PROMPTING_AND_ROLES_TABLE_HEADING = "## Builtin Agents";
15
+
16
+ /**
17
+ * Extracts the first-column agent names from every table row under the given
18
+ * markdown section heading. Skips the header row and the separator row.
19
+ */
20
+ function tableFirstColumnNames(markdown: string, sectionHeading: string): string[] {
21
+ const lines = markdown.split(/\r?\n/);
22
+ const headingIndex = lines.findIndex((line) => line.trim() === sectionHeading);
23
+ assert.ok(headingIndex >= 0, `missing section heading: ${sectionHeading}`);
24
+
25
+ const names: string[] = [];
26
+ for (let i = headingIndex + 1; i < lines.length; i++) {
27
+ const line = lines[i]!.trim();
28
+ if (line.startsWith("#")) break;
29
+ if (!line.startsWith("|")) continue;
30
+
31
+ const cells = line.split("|").map((cell) => cell.trim());
32
+ const firstCell = (cells[1] ?? "").replaceAll("`", "").trim();
33
+ if (firstCell === "Agent") continue;
34
+ if (/^:?-+:?$/.test(firstCell)) continue;
35
+ if (firstCell.length === 0) continue;
36
+ names.push(firstCell);
37
+ }
38
+ return names;
39
+ }
40
+
41
+ function assertExactlySixBuiltins(tablePath: string, heading: string, markdown: string): void {
42
+ const names = tableFirstColumnNames(markdown, heading);
43
+
44
+ assert.equal(names.length, 6, `${path.basename(tablePath)}: role table must contain exactly six builtin rows`);
45
+ assert.equal(new Set(names).size, names.length, `${path.basename(tablePath)}: role table contains duplicate rows`);
46
+ assert.deepEqual(
47
+ [...names].sort(),
48
+ [...BUILTIN_AGENT_NAMES].sort(),
49
+ `${path.basename(tablePath)}: role table must list exactly ${BUILTIN_AGENT_NAMES.join(", ")} once each, with no aliases`,
50
+ );
51
+ }
52
+
53
+ describe("builtin agent documentation role tables", () => {
54
+ it("README lists exactly the six canonical builtins once, with no duplicates or aliases", () => {
55
+ const markdown = fs.readFileSync(README_PATH, "utf-8");
56
+ assertExactlySixBuiltins(README_PATH, README_TABLE_HEADING, markdown);
57
+ });
58
+
59
+ it("prompting-and-roles reference lists exactly the six canonical builtins once, with no duplicates or aliases", () => {
60
+ const markdown = fs.readFileSync(PROMPTING_AND_ROLES_PATH, "utf-8");
61
+ assertExactlySixBuiltins(PROMPTING_AND_ROLES_PATH, PROMPTING_AND_ROLES_TABLE_HEADING, markdown);
62
+ });
63
+ });
@@ -0,0 +1,136 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { handleList } from "../../src/agents/agent-management.ts";
4
+ import type { AgentConfig } from "../../src/agents/agents.ts";
5
+ import { resolveSubagentLaunchContract } from "../../src/api/preflight.ts";
6
+ import {
7
+ decodeSubagentCapabilityCeiling,
8
+ encodeSubagentCapabilityCeiling,
9
+ intersectSubagentCapabilityCeilings,
10
+ parseSubagentCapabilityCeiling,
11
+ registerSubagentCapabilityCeiling,
12
+ } from "../../src/api/capability-ceiling.ts";
13
+ import { runSync } from "../../src/runs/foreground/execution.ts";
14
+ import { buildPiArgs } from "../../src/runs/shared/pi-args.ts";
15
+
16
+ function agent(name: string): AgentConfig {
17
+ return {
18
+ name,
19
+ description: `${name} agent`,
20
+ systemPrompt: `${name} prompt`,
21
+ systemPromptMode: "replace",
22
+ inheritProjectContext: false,
23
+ inheritSkills: false,
24
+ source: "project",
25
+ filePath: `/tmp/${name}.md`,
26
+ };
27
+ }
28
+
29
+ describe("capability ceiling agent allowlist", () => {
30
+ it("parses, round-trips, and intersects allowedAgents", () => {
31
+ const parsed = parseSubagentCapabilityCeiling({ version: 1, allowedAgents: ["worker", "reviewer", "worker"], denyExtensions: false, sources: ["plan"] });
32
+ assert.deepEqual(parsed.allowedAgents, ["reviewer", "worker"]);
33
+ assert.deepEqual(decodeSubagentCapabilityCeiling(encodeSubagentCapabilityCeiling(parsed)), parsed);
34
+
35
+ assert.deepEqual(intersectSubagentCapabilityCeilings(
36
+ { version: 1, allowedAgents: ["worker", "reviewer"], denyExtensions: false, sources: ["outer"] },
37
+ { version: 1, allowedAgents: ["reviewer", "scout"], denyExtensions: true, sources: ["inner"] },
38
+ ), {
39
+ version: 1,
40
+ allowedAgents: ["reviewer"],
41
+ denyExtensions: true,
42
+ sources: ["inner", "outer"],
43
+ });
44
+
45
+ assert.deepEqual(intersectSubagentCapabilityCeilings(
46
+ { version: 1, allowedTools: ["read"], denyExtensions: false, sources: ["tools-only"] },
47
+ { version: 1, allowedAgents: [], denyExtensions: false, sources: ["none"] },
48
+ )?.allowedAgents, []);
49
+ });
50
+
51
+ it("marks non-allowlisted agents as restricted in list output", () => {
52
+ const sessionId = `allowlist-list-${Date.now()}-${Math.random()}`;
53
+ const handle = registerSubagentCapabilityCeiling({ sessionId, source: "plan-mode", ceiling: { allowedAgents: ["commentator"] } });
54
+ try {
55
+ const result = handleList({}, { cwd: process.cwd(), currentSessionId: sessionId, modelRegistry: { getAvailable: () => [] } });
56
+ const text = result.content[0]?.text ?? "";
57
+ assert.match(text, /Executable agents:/);
58
+ assert.match(text, /- commentator /);
59
+ assert.match(text, /Restricted agents \(not executable in this session; capability ceiling: plan-mode\):/);
60
+ assert.match(text, /- builder /);
61
+
62
+ const catalog = (result as { details: { catalog?: unknown } }).details.catalog as
63
+ | undefined
64
+ | {
65
+ version: number;
66
+ agents: Array<{ name: string; executable: boolean; restrictionSources?: string[]; defaultContext: string }>;
67
+ capabilityCeilingSources?: string[];
68
+ };
69
+ assert.ok(catalog, "list result must include machine catalog metadata");
70
+ assert.equal(catalog.version, 1);
71
+ assert.deepEqual(catalog.capabilityCeilingSources, ["plan-mode"]);
72
+ const commentator = catalog.agents.find((entry) => entry.name === "commentator");
73
+ const builder = catalog.agents.find((entry) => entry.name === "builder");
74
+ assert.equal(commentator?.executable, true);
75
+ assert.equal(commentator?.restrictionSources, undefined);
76
+ assert.equal(commentator?.defaultContext, "fresh");
77
+ assert.equal(builder?.executable, false);
78
+ assert.deepEqual(builder?.restrictionSources, ["plan-mode"]);
79
+ } finally {
80
+ handle.dispose();
81
+ }
82
+ });
83
+
84
+ it("never recommends a capability-restricted writer for implementation list task advice", () => {
85
+ const sessionId = `allowlist-advice-${Date.now()}-${Math.random()}`;
86
+ const handle = registerSubagentCapabilityCeiling({ sessionId, source: "plan-mode", ceiling: { allowedAgents: ["commentator"] } });
87
+ try {
88
+ const result = handleList({ task: "Implement the fix" }, { cwd: process.cwd(), currentSessionId: sessionId, modelRegistry: { getAvailable: () => [] } });
89
+ assert.equal(result.isError, false);
90
+ const text = result.content[0]?.text ?? "";
91
+ assert.match(text, /Task-aware advisory routing:/);
92
+ assert.match(text, /- Intent: implementation/);
93
+ assert.match(text, /- Recommendation: none/);
94
+ assert.doesNotMatch(text, /- Recommended: /);
95
+ } finally {
96
+ handle.dispose();
97
+ }
98
+ });
99
+
100
+ it("rejects a non-allowlisted agent in preflight launch resolution", async () => {
101
+ const result = await resolveSubagentLaunchContract({
102
+ agent: "builder",
103
+ cwd: process.cwd(),
104
+ capabilityCeiling: { version: 1, allowedAgents: ["commentator"], denyExtensions: false, sources: ["plan-mode"] },
105
+ });
106
+ assert.equal(result.ok, false);
107
+ assert.equal(result.code, "restricted_agent");
108
+ assert.match(result.message, /does not allow agent 'builder'/);
109
+ assert.match(result.message, /Allowed agents: commentator/);
110
+ });
111
+
112
+ it("rejects a non-allowlisted foreground launch before spawning", async () => {
113
+ const result = await runSync(process.cwd(), [agent("builder"), agent("commentator")], "builder", "Do work", {
114
+ capabilityCeiling: { version: 1, allowedAgents: ["commentator"], denyExtensions: false, sources: ["plan-mode"] },
115
+ });
116
+ assert.equal(result.exitCode, 1);
117
+ assert.match(result.error ?? "", /does not allow agent 'builder'/);
118
+ assert.deepEqual(result.capabilityCeiling?.allowedAgents, ["commentator"]);
119
+ });
120
+
121
+ it("includes allowedAgents in propagated launch env and audit metadata", () => {
122
+ const { env, capabilityAudit } = buildPiArgs({
123
+ baseArgs: [],
124
+ task: "Review",
125
+ sessionEnabled: false,
126
+ inheritProjectContext: false,
127
+ inheritSkills: false,
128
+ childAgentName: "commentator",
129
+ capabilityCeiling: { version: 1, allowedAgents: ["commentator"], allowedTools: ["read"], denyExtensions: true, sources: ["plan-mode"] },
130
+ });
131
+ assert.equal(capabilityAudit?.agentAllowed, true);
132
+ assert.deepEqual(capabilityAudit?.agentRestrictionSources, ["plan-mode"]);
133
+ assert.ok(env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1);
134
+ assert.deepEqual(decodeSubagentCapabilityCeiling(env.SELESAI_SUBAGENT_CAPABILITY_CEILING_V1)?.allowedAgents, ["commentator"]);
135
+ });
136
+ });
@@ -33,7 +33,7 @@ describe("subagent capability ceiling", () => {
33
33
  });
34
34
 
35
35
  it("rejects malformed policy and disposed updates", () => {
36
- assert.throws(() => registerSubagentCapabilityCeiling({ sessionId: "x", source: "x", ceiling: {} }), /allowedTools or denyExtensions/);
36
+ assert.throws(() => registerSubagentCapabilityCeiling({ sessionId: "x", source: "x", ceiling: {} }), /allowedTools, allowedAgents, or denyExtensions/);
37
37
  const handle = registerSubagentCapabilityCeiling({ sessionId: "disposed", source: "test", ceiling: { denyExtensions: true } });
38
38
  handle.dispose();
39
39
  assert.throws(() => handle.update({ allowedTools: ["read"] }), /disposed/);
@@ -198,6 +198,12 @@ describe("chain append requests", () => {
198
198
  "reviewer:pending",
199
199
  "auditor:pending",
200
200
  ]);
201
+ // Appended steps must carry their own bounded task description for fleet display.
202
+ assert.deepEqual(status.steps?.slice(1).map((step) => step.description), [
203
+ "Use {previous}",
204
+ "Use {previous}",
205
+ "Use {previous}",
206
+ ]);
201
207
  assert.deepEqual(status.parallelGroups, [{ start: 2, count: 2, stepIndex: 2 }]);
202
208
  assert.equal(status.workflowGraph?.nodes[1]?.id, "step-1");
203
209
  assert.equal(status.workflowGraph?.nodes[2]?.kind, "parallel-group");
@@ -285,7 +285,7 @@ describe("registered subagent tool prepareArguments", { skip: !schemasAvailable
285
285
  const output = execFileSync(
286
286
  process.execPath,
287
287
  [
288
- "--experimental-transform-types",
288
+ "--experimental-strip-types",
289
289
  "--import",
290
290
  "./test/support/register-loader.mjs",
291
291
  "--input-type=module",