@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
@@ -26,7 +26,7 @@ function assistantText(text: string): Message {
26
26
 
27
27
  test("implementation task with no mutation triggers the completion guard", () => {
28
28
  const result = evaluateCompletionMutationGuard({
29
- agent: "worker",
29
+ agent: "builder",
30
30
  task: "Implement the approved fix",
31
31
  messages: [assistantText("Plan: update the files...")],
32
32
  });
@@ -67,9 +67,9 @@ test("read-only issue drafting tasks do not trigger on suggested fix wording", (
67
67
  attemptedMutation: false,
68
68
  triggered: false,
69
69
  });
70
- assert.equal(expectsImplementationMutation("worker", task), false);
70
+ assert.equal(expectsImplementationMutation("builder", task), false);
71
71
  assert.equal(
72
- expectsImplementationMutation("worker", "Draft GitHub issue for a bug. Include suspected cause and suggested fix."),
72
+ expectsImplementationMutation("builder", "Draft GitHub issue for a bug. Include suspected cause and suggested fix."),
73
73
  false,
74
74
  );
75
75
  });
@@ -89,9 +89,9 @@ test("omitted, bash, unknown, write, and MCP tool capabilities stay conservative
89
89
  assert.equal(evaluateCompletionMutationGuard({ ...base, tools: ["read", "grep"], mcpDirectTools: ["github/search"] }).triggered, true);
90
90
  });
91
91
 
92
- test("worker with mutating-capable tools still triggers when no mutation is observed", () => {
92
+ test("builder with mutating-capable tools still triggers when no mutation is observed", () => {
93
93
  const result = evaluateCompletionMutationGuard({
94
- agent: "worker",
94
+ agent: "builder",
95
95
  task: "Fix the test implementation",
96
96
  messages: [assistantText("I will edit it next")],
97
97
  tools: ["read", "edit"],
@@ -104,10 +104,10 @@ test("worker with mutating-capable tools still triggers when no mutation is obse
104
104
  });
105
105
  });
106
106
 
107
- test("oracle review tasks with bash available do not require mutation", () => {
107
+ test("commentator review tasks with bash available do not require mutation", () => {
108
108
  const task = "Review prep findings and determine what to implement with playbooks instead of before.";
109
109
  const result = evaluateCompletionMutationGuard({
110
- agent: "oracle",
110
+ agent: "commentator",
111
111
  task,
112
112
  messages: [assistantText("Review complete with file-backed findings.")],
113
113
  tools: ["read", "grep", "find", "ls", "bash", "intercom"],
@@ -121,57 +121,57 @@ test("oracle review tasks with bash available do not require mutation", () => {
121
121
  });
122
122
 
123
123
  test("review-only, research, and framework output instructions do not expect mutation", () => {
124
- assert.equal(expectsImplementationMutation("worker", "Review only: return findings, do not edit"), false);
125
- assert.equal(expectsImplementationMutation("worker", "Do not edit files. Tell me how to fix the bug."), false);
126
- assert.equal(expectsImplementationMutation("worker", "Review the diff and suggest fixes only. Do not edit files."), false);
127
- assert.equal(expectsImplementationMutation("worker", "Implement this. Do not edit files outside this repo. Do not edit files."), false);
128
- assert.equal(expectsImplementationMutation("worker", "Investigate why this failed"), false);
124
+ assert.equal(expectsImplementationMutation("builder", "Review only: return findings, do not edit"), false);
125
+ assert.equal(expectsImplementationMutation("builder", "Do not edit files. Tell me how to fix the bug."), false);
126
+ assert.equal(expectsImplementationMutation("builder", "Review the diff and suggest fixes only. Do not edit files."), false);
127
+ assert.equal(expectsImplementationMutation("builder", "Implement this. Do not edit files outside this repo. Do not edit files."), false);
128
+ assert.equal(expectsImplementationMutation("builder", "Investigate why this failed"), false);
129
129
  assert.equal(expectsImplementationMutation("researcher", "Research the API behavior"), false);
130
130
  assert.equal(expectsImplementationMutation("researcher", "Research this and patch the bug"), false);
131
- assert.equal(expectsImplementationMutation("reviewer", "Review this and fix any real issues"), false);
132
- assert.equal(expectsImplementationMutation("reviewer", "Review this and fix any real issues; regardless of findings, apply changes directly"), true);
133
- assert.equal(expectsImplementationMutation("worker", "[Write to: /tmp/result.md]\n\nSummarize findings"), false);
134
- assert.equal(expectsImplementationMutation("worker", "Write report"), false);
135
- assert.equal(expectsImplementationMutation("worker", "Create a report"), false);
136
- assert.equal(expectsImplementationMutation("worker", "Create a summary"), false);
137
- assert.equal(expectsImplementationMutation("worker", "Add a report"), false);
138
- assert.equal(expectsImplementationMutation("worker", "Update a summary"), false);
139
- assert.equal(expectsImplementationMutation("worker", "Write to {chain_dir}"), false);
131
+ assert.equal(expectsImplementationMutation("commentator", "Review this and fix any real issues"), false);
132
+ assert.equal(expectsImplementationMutation("commentator", "Review this and fix any real issues; regardless of findings, apply changes directly"), true);
133
+ assert.equal(expectsImplementationMutation("builder", "[Write to: /tmp/result.md]\n\nSummarize findings"), false);
134
+ assert.equal(expectsImplementationMutation("builder", "Write report"), false);
135
+ assert.equal(expectsImplementationMutation("builder", "Create a report"), false);
136
+ assert.equal(expectsImplementationMutation("builder", "Create a summary"), false);
137
+ assert.equal(expectsImplementationMutation("builder", "Add a report"), false);
138
+ assert.equal(expectsImplementationMutation("builder", "Update a summary"), false);
139
+ assert.equal(expectsImplementationMutation("builder", "Write to {chain_dir}"), false);
140
140
  assert.equal(
141
- expectsImplementationMutation("worker", "Do async work\nUpdate progress at: /tmp/progress.md\n**Output:**\nWrite your findings to exactly this path: /tmp/out.md\nThis path is authoritative for this run.\nIgnore any other output filename or output path mentioned elsewhere."),
141
+ expectsImplementationMutation("builder", "Do async work\nUpdate progress at: /tmp/progress.md\n**Output:**\nWrite your findings to exactly this path: /tmp/out.md\nThis path is authoritative for this run.\nIgnore any other output filename or output path mentioned elsewhere."),
142
142
  false,
143
143
  );
144
144
  });
145
145
 
146
- test("worker implementation verbs win over investigative wording and scoped prohibitions", () => {
147
- assert.equal(expectsImplementationMutation("worker", "Investigate why the worker did not edit files and fix it"), true);
148
- assert.equal(expectsImplementationMutation("worker", "Do not modify tests; implement the fix"), true);
149
- assert.equal(expectsImplementationMutation("worker", "Do not modify tests — implement the fix"), true);
150
- assert.equal(expectsImplementationMutation("worker", "Research the current code path and patch the bug"), true);
151
- assert.equal(expectsImplementationMutation("worker", "Fix the bug where no edits were made"), true);
152
- assert.equal(expectsImplementationMutation("worker", "Fix lint"), true);
153
- assert.equal(expectsImplementationMutation("worker", "Fix the build"), true);
154
- assert.equal(expectsImplementationMutation("worker", "Fix TypeScript errors"), true);
155
- assert.equal(expectsImplementationMutation("worker", "Fix CI"), true);
156
- assert.equal(expectsImplementationMutation("worker", "Fix the failing test"), true);
157
- assert.equal(expectsImplementationMutation("worker", "Patch the cold start test"), true);
158
- assert.equal(expectsImplementationMutation("worker", "Implement the fix and return findings."), true);
146
+ test("builder implementation verbs win over investigative wording and scoped prohibitions", () => {
147
+ assert.equal(expectsImplementationMutation("builder", "Investigate why the worker did not edit files and fix it"), true);
148
+ assert.equal(expectsImplementationMutation("builder", "Do not modify tests; implement the fix"), true);
149
+ assert.equal(expectsImplementationMutation("builder", "Do not modify tests — implement the fix"), true);
150
+ assert.equal(expectsImplementationMutation("builder", "Research the current code path and patch the bug"), true);
151
+ assert.equal(expectsImplementationMutation("builder", "Fix the bug where no edits were made"), true);
152
+ assert.equal(expectsImplementationMutation("builder", "Fix lint"), true);
153
+ assert.equal(expectsImplementationMutation("builder", "Fix the build"), true);
154
+ assert.equal(expectsImplementationMutation("builder", "Fix TypeScript errors"), true);
155
+ assert.equal(expectsImplementationMutation("builder", "Fix CI"), true);
156
+ assert.equal(expectsImplementationMutation("builder", "Fix the failing test"), true);
157
+ assert.equal(expectsImplementationMutation("builder", "Patch the cold start test"), true);
158
+ assert.equal(expectsImplementationMutation("builder", "Implement the fix and return findings."), true);
159
159
  });
160
160
 
161
161
  test("non-worker implementation tasks still expect mutation", () => {
162
162
  assert.equal(expectsImplementationMutation("delegate", "Fix the bug where no edits were made"), true);
163
163
  assert.equal(expectsImplementationMutation("delegate", "Apply the suggested fix to src/runs/shared/completion-guard.ts"), true);
164
- assert.equal(expectsImplementationMutation("worker", "Draft a GitHub issue, then implement the fix"), true);
164
+ assert.equal(expectsImplementationMutation("builder", "Draft a GitHub issue, then implement the fix"), true);
165
165
  });
166
166
 
167
- test("worker edit intent covers common docs, config, and source tasks", () => {
168
- assert.equal(expectsImplementationMutation("worker", "Update README to mention the native tool"), true);
169
- assert.equal(expectsImplementationMutation("worker", "Remove share functionality and all Vercel references"), true);
170
- assert.equal(expectsImplementationMutation("worker", "Replace the registered command with a render tool"), true);
171
- assert.equal(expectsImplementationMutation("worker", "Create completion-guard.ts"), true);
172
- assert.equal(expectsImplementationMutation("worker", "Add tests for the completion guard"), true);
173
- assert.equal(expectsImplementationMutation("worker", "Implement the approved fixes. Do not edit files outside this repo."), true);
174
- assert.equal(expectsImplementationMutation("worker", "Implement the fix. Do not edit unrelated files."), true);
167
+ test("builder edit intent covers common docs, config, and source tasks", () => {
168
+ assert.equal(expectsImplementationMutation("builder", "Update README to mention the native tool"), true);
169
+ assert.equal(expectsImplementationMutation("builder", "Remove share functionality and all Vercel references"), true);
170
+ assert.equal(expectsImplementationMutation("builder", "Replace the registered command with a render tool"), true);
171
+ assert.equal(expectsImplementationMutation("builder", "Create completion-guard.ts"), true);
172
+ assert.equal(expectsImplementationMutation("builder", "Add tests for the completion guard"), true);
173
+ assert.equal(expectsImplementationMutation("builder", "Implement the approved fixes. Do not edit files outside this repo."), true);
174
+ assert.equal(expectsImplementationMutation("builder", "Implement the fix. Do not edit unrelated files."), true);
175
175
  });
176
176
 
177
177
  test("edit and write tool calls count as mutation attempts", () => {
@@ -195,7 +195,7 @@ test("obvious mutating bash commands count as mutation attempts", () => {
195
195
 
196
196
  test("implementation task with mutation attempts does not trigger", () => {
197
197
  const result = evaluateCompletionMutationGuard({
198
- agent: "worker",
198
+ agent: "builder",
199
199
  task: "Fix the failing test",
200
200
  messages: [assistantToolCall("edit", { path: "test.ts" })],
201
201
  });
@@ -279,7 +279,7 @@ test("provider checkpoint with a changed commit counts as mutation evidence", ()
279
279
  assert.equal(hasMutationToolCall([message]), true);
280
280
  assert.equal(
281
281
  evaluateCompletionMutationGuard({
282
- agent: "worker",
282
+ agent: "builder",
283
283
  task: "Edit the target source file",
284
284
  messages: [message],
285
285
  }).triggered,
@@ -297,7 +297,7 @@ test("unchanged provider checkpoint does not bypass the completion guard", () =>
297
297
  assert.equal(hasMutationToolCall([message]), false);
298
298
  assert.equal(
299
299
  evaluateCompletionMutationGuard({
300
- agent: "worker",
300
+ agent: "builder",
301
301
  task: "Edit the target source file",
302
302
  messages: [message],
303
303
  }).triggered,
@@ -308,7 +308,7 @@ test("unchanged provider checkpoint does not bypass the completion guard", () =>
308
308
 
309
309
  test("implementation task with Cursor edit thinking does not trigger", () => {
310
310
  const result = evaluateCompletionMutationGuard({
311
- agent: "worker",
311
+ agent: "builder",
312
312
  task: "Edit docs/BACKEND_ARCHITECTURE.md",
313
313
  messages: [
314
314
  assistantThinking("Cursor edit: docs/BACKEND_ARCHITECTURE.md added 1 line\n"),
@@ -65,6 +65,29 @@ describe("config directory resolution", () => {
65
65
  }
66
66
  });
67
67
 
68
+ it("invalidates cached runtime resolution when the package root changes", () => {
69
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-config-cache-"));
70
+ try {
71
+ const firstRoot = path.join(tempDir, "first");
72
+ const secondRoot = path.join(tempDir, "second");
73
+ for (const [root, configDir] of [[firstRoot, ".first-pi"], [secondRoot, ".second-pi"]] as const) {
74
+ fs.mkdirSync(root, { recursive: true });
75
+ fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({
76
+ name: "@selesai/code",
77
+ piConfig: { configDir },
78
+ }), "utf-8");
79
+ }
80
+
81
+ process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] = firstRoot;
82
+ assert.equal(getConfigDirName(), ".first-pi");
83
+ assert.equal(getConfigDirName(), ".first-pi");
84
+ process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] = secondRoot;
85
+ assert.equal(getConfigDirName(), ".second-pi");
86
+ } finally {
87
+ fs.rmSync(tempDir, { recursive: true, force: true });
88
+ }
89
+ });
90
+
68
91
  it("canonicalizes watcher paths and preserves the original path when native realpath fails", () => {
69
92
  assert.equal(resolveWatchPath("C:\\SHORT~1\\watch", () => "C:\\Long Path\\watch"), "C:\\Long Path\\watch");
70
93
  assert.equal(resolveWatchPath("C:\\SHORT~1\\watch", () => { throw new Error("missing"); }), "C:\\SHORT~1\\watch");
@@ -148,6 +148,33 @@ describe("control channel: request file", () => {
148
148
  }
149
149
  });
150
150
 
151
+ it("keeps steer requests for retry when the inbox cannot be scanned", () => {
152
+ const asyncDir = tmpAsyncDir("pi-control-steer-scan-retry-");
153
+ try {
154
+ requestAsyncSteer(asyncDir, { message: "retry me", id: "retry", ts: 1 });
155
+ let failScan = true;
156
+ const fsImpl = {
157
+ existsSync: fs.existsSync,
158
+ readdirSync: ((target: fs.PathLike) => {
159
+ if (failScan) {
160
+ failScan = false;
161
+ throw Object.assign(new Error("file table overflow"), { code: "ENFILE" });
162
+ }
163
+ return fs.readdirSync(target);
164
+ }) as typeof fs.readdirSync,
165
+ readFileSync: fs.readFileSync,
166
+ rmSync: fs.rmSync,
167
+ };
168
+
169
+ assert.deepEqual(consumeSteerRequests(asyncDir, fsImpl), []);
170
+ assert.deepEqual(consumeSteerRequests(asyncDir, fsImpl), [
171
+ { type: "steer", id: "retry", ts: 1, message: "retry me" },
172
+ ]);
173
+ } finally {
174
+ cleanup(asyncDir);
175
+ }
176
+ });
177
+
151
178
  it("does not deliver a steer request if another consumer removed it first", () => {
152
179
  const asyncDir = tmpAsyncDir("pi-control-steer-concurrent-");
153
180
  try {
@@ -17,6 +17,7 @@ import {
17
17
  type SubagentDelegationV2Response,
18
18
  } from "../../src/api/delegation.ts";
19
19
  import { parseSubagentDelegationRequest } from "../../src/slash/delegation-request.ts";
20
+ import { toSubagentDelegationExecutionParams } from "../../src/slash/delegation-adapters.ts";
20
21
  import {
21
22
  registerPromptTemplateDelegationBridge,
22
23
  type PromptTemplateBridgeEvents,
@@ -213,7 +214,7 @@ describe("public subagent delegation contract", () => {
213
214
  },
214
215
  executeVersioned: async (_id, params, _signal, _ctx, onUpdate) => {
215
216
  observedParams = params as unknown as Record<string, unknown>;
216
- onUpdate({ details: { mode: "single", results: [{ agent: "reviewer", model: "openai/gpt-5", thinking: "high" }], progress: [{ currentTool: "read" }] } });
217
+ onUpdate({ details: { mode: "single", runId: "run-v2", results: [{ agent: "reviewer", model: "openai/gpt-5", thinking: "high" }], progress: [{ currentTool: "read" }] } });
217
218
  return {
218
219
  details: {
219
220
  mode: "single",
@@ -223,6 +224,7 @@ describe("public subagent delegation contract", () => {
223
224
  exitCode: 0,
224
225
  model: "openai/gpt-5",
225
226
  thinking: "high",
227
+ launchContractDigest: "launch-contract-digest",
226
228
  finalOutput: '{"looks":"json"}',
227
229
  usage: { input: 2, output: 3, cacheRead: 4, cacheWrite: 5, cost: 0.01, turns: 2 },
228
230
  progressSummary: { toolCount: 6, tokens: 5, durationMs: 7 },
@@ -237,7 +239,7 @@ describe("public subagent delegation contract", () => {
237
239
  const responsePromise = once(events, SUBAGENT_DELEGATION_RESPONSE_EVENT);
238
240
  events.emit(SUBAGENT_DELEGATION_REQUEST_EVENT, textRequest);
239
241
  assert.deepEqual(await startedPromise, { version: 2, requestId: "attempt-1", ownerRunId: "owner-1", nodeId: "node-1" });
240
- assert.deepEqual(await updatePromise, { version: 2, requestId: "attempt-1", ownerRunId: "owner-1", nodeId: "node-1", currentTool: "read", model: "openai/gpt-5" });
242
+ assert.deepEqual(await updatePromise, { version: 2, requestId: "attempt-1", ownerRunId: "owner-1", nodeId: "node-1", runId: "run-v2", currentTool: "read", model: "openai/gpt-5" });
241
243
  assert.deepEqual(await responsePromise, {
242
244
  version: 2,
243
245
  requestId: "attempt-1",
@@ -249,6 +251,7 @@ describe("public subagent delegation contract", () => {
249
251
  model: "openai/gpt-5",
250
252
  thinking: "high",
251
253
  exitCode: 0,
254
+ launchContractDigest: "launch-contract-digest",
252
255
  result: { kind: "text", text: '{"looks":"json"}' },
253
256
  usage: { input: 2, output: 3, cacheRead: 4, cacheWrite: 5, cost: 0.01, turns: 2, toolCalls: 6, durationMs: 7 },
254
257
  } satisfies SubagentDelegationV2Response);
@@ -506,6 +509,29 @@ describe("public subagent delegation contract", () => {
506
509
  assert.deepEqual(responses, []);
507
510
  });
508
511
 
512
+ it("resolves an omitted v1 outputMode to explicit inline so response.output stays populated", () => {
513
+ const minimal: SubagentDelegationRequest = {
514
+ version: 1,
515
+ requestId: "adapter-default-1",
516
+ agent: "reviewer",
517
+ task: "Review evidence",
518
+ context: "fresh",
519
+ cwd: "/repo",
520
+ };
521
+ // Omitted outputMode on a strict v1 request keeps the legacy full-text
522
+ // contract: explicit inline, so `SubagentDelegationResponse.output` stays
523
+ // populated instead of degrading to the reference-first file-only default.
524
+ const params = toSubagentDelegationExecutionParams(minimal);
525
+ assert.equal(params.outputMode, "inline");
526
+ assert.equal(params.output, undefined);
527
+
528
+ // Explicit v1 modes are forwarded exactly; v2 stays unaffected (output: false).
529
+ assert.equal(toSubagentDelegationExecutionParams(request).outputMode, "file-only");
530
+ const inline = toSubagentDelegationExecutionParams({ ...request, outputMode: "inline" });
531
+ assert.equal(inline.outputMode, "inline");
532
+ assert.equal(toSubagentDelegationExecutionParams({ ...request, outputMode: undefined }).outputMode, "inline");
533
+ });
534
+
509
535
  it("runs one v1 request through the existing executor and returns structured metadata", async () => {
510
536
  const events = new FakeEvents();
511
537
  let executeCalls = 0;
@@ -200,6 +200,7 @@ describe("dynamic fanout helpers", () => {
200
200
  const result = (agent: string, structuredOutput: unknown): SingleResult => ({
201
201
  agent,
202
202
  task: "t",
203
+ index: 0,
203
204
  exitCode: 0,
204
205
  messages: [],
205
206
  usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import { Editor, visibleWidth } from "@earendil-works/pi-tui";
3
+ import { Editor, type EditorComponent, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import type { ExtensionContext } from "@selesai/code";
5
5
  import type { SubagentState } from "../../src/shared/types.ts";
6
6
  import { collectFleetSnapshot } from "../../src/tui/fleet.ts";
@@ -10,6 +10,7 @@ import {
10
10
  collectFleetStatusEntries,
11
11
  formatFleetElapsed,
12
12
  formatFleetTokens,
13
+ resolveFleetViewPlacement,
13
14
  } from "../../src/tui/fleet-status.ts";
14
15
 
15
16
  function stateForTest(): SubagentState {
@@ -45,6 +46,13 @@ describe("below-editor subagent FleetView", () => {
45
46
  assert.equal(formatFleetTokens(1_250_000), "↓ 1.3M tokens");
46
47
  });
47
48
 
49
+ it("resolves configured FleetView placement with a below-editor fallback", () => {
50
+ assert.equal(resolveFleetViewPlacement(undefined), "belowEditor");
51
+ assert.equal(resolveFleetViewPlacement("belowEditor"), "belowEditor");
52
+ assert.equal(resolveFleetViewPlacement("aboveEditor"), "aboveEditor");
53
+ assert.equal(resolveFleetViewPlacement("side"), "belowEditor");
54
+ });
55
+
48
56
  it("renders main plus active children below the editor and bounds every line", () => {
49
57
  const state = stateForTest();
50
58
  const now = Date.now();
@@ -56,6 +64,7 @@ describe("below-editor subagent FleetView", () => {
56
64
  updatedAt: now,
57
65
  currentAgent: `worker-${index}`,
58
66
  description: index === 0 ? "Inspect\nmodule 0" : `Inspect module ${index}`,
67
+ ...(index === 0 ? { model: "anthropic/fable-5", thinking: "low" } : {}),
59
68
  tokens: index === 0 ? 13_100 : index,
60
69
  });
61
70
  }
@@ -85,7 +94,7 @@ describe("below-editor subagent FleetView", () => {
85
94
  const component = widgetFactory!({ requestRender() {} }, theme);
86
95
  const lines = component.render(80);
87
96
  assert.ok(lines.some((line) => line.includes("⏺ main")));
88
- assert.ok(lines.some((line) => line.includes("worker-0") && line.includes("Inspect module 0")));
97
+ assert.ok(lines.some((line) => line.includes("worker-0 (fable-5 · thinking low)") && line.includes("Inspect module 0")));
89
98
  assert.ok(lines.some((line) => line.includes("11s · ↓ 13.1k tokens")));
90
99
  assert.ok(lines.some((line) => line.includes("↓ 2 more")));
91
100
  for (const line of lines) assert.ok(visibleWidth(line) <= 80, `line exceeded width: ${line}`);
@@ -94,6 +103,143 @@ describe("below-editor subagent FleetView", () => {
94
103
  }
95
104
  });
96
105
 
106
+ it("registers above the editor when configured", () => {
107
+ const state = stateForTest();
108
+ state.foregroundControls.set("run-worker", {
109
+ runId: "run-worker",
110
+ mode: "single",
111
+ startedAt: 10,
112
+ updatedAt: 20,
113
+ currentAgent: "worker",
114
+ });
115
+ let placement: string | undefined;
116
+ const ctx = {
117
+ hasUI: true,
118
+ ui: {
119
+ setWidget(_key: string, content: unknown, options?: { placement?: string }) {
120
+ if (content) placement = options?.placement;
121
+ },
122
+ onTerminalInput() { return () => {}; },
123
+ getEditorText() { return ""; },
124
+ notify() {},
125
+ theme,
126
+ },
127
+ } as unknown as ExtensionContext;
128
+ const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000, placement: "aboveEditor" });
129
+ try {
130
+ fleet.setContext(ctx);
131
+ assert.equal(placement, "aboveEditor");
132
+ } finally {
133
+ fleet.dispose();
134
+ }
135
+ });
136
+
137
+ it("stops refreshing when the captured extension context becomes stale", () => {
138
+ const state = stateForTest();
139
+ state.foregroundControls.set("run-worker", {
140
+ runId: "run-worker",
141
+ mode: "single",
142
+ startedAt: 10,
143
+ updatedAt: 20,
144
+ currentAgent: "worker",
145
+ });
146
+ let stale = false;
147
+ let contextReads = 0;
148
+ let inputUnsubscribes = 0;
149
+ let widgetRemovals = 0;
150
+ const ctx = {
151
+ get hasUI() {
152
+ contextReads++;
153
+ if (stale) {
154
+ throw new Error("This extension ctx is stale after session replacement or reload.");
155
+ }
156
+ return true;
157
+ },
158
+ ui: {
159
+ setWidget(_key: string, content: unknown) {
160
+ if (content === undefined) widgetRemovals++;
161
+ },
162
+ onTerminalInput() { return () => { inputUnsubscribes++; }; },
163
+ getEditorText() { return ""; },
164
+ notify() {},
165
+ theme,
166
+ },
167
+ } as unknown as ExtensionContext;
168
+ const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
169
+ try {
170
+ fleet.setContext(ctx);
171
+ stale = true;
172
+ assert.doesNotThrow(() => fleet.refresh());
173
+ assert.equal(inputUnsubscribes, 1);
174
+ assert.equal(widgetRemovals, 1);
175
+ assert.equal((fleet as unknown as { timer?: unknown }).timer, undefined);
176
+ const readsAfterStaleRefresh = contextReads;
177
+ fleet.refresh();
178
+ assert.equal(contextReads, readsAfterStaleRefresh, "later refreshes must not reuse the stale context");
179
+ } finally {
180
+ fleet.dispose();
181
+ }
182
+ });
183
+
184
+ it("does not swallow unrelated widget cleanup errors", () => {
185
+ const state = stateForTest();
186
+ state.foregroundControls.set("run-worker", {
187
+ runId: "run-worker",
188
+ mode: "single",
189
+ startedAt: 10,
190
+ updatedAt: 20,
191
+ currentAgent: "worker",
192
+ });
193
+ const ctx = {
194
+ hasUI: true,
195
+ ui: {
196
+ setWidget(_key: string, content: unknown) {
197
+ if (content === undefined) throw new Error("widget cleanup failed");
198
+ },
199
+ onTerminalInput() { return () => {}; },
200
+ getEditorText() { return ""; },
201
+ notify() {},
202
+ theme,
203
+ },
204
+ } as unknown as ExtensionContext;
205
+ const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
206
+ fleet.setContext(ctx);
207
+ assert.throws(() => fleet.dispose(), /widget cleanup failed/);
208
+ });
209
+
210
+ it("preserves multiple unrelated UI cleanup errors", () => {
211
+ const state = stateForTest();
212
+ state.foregroundControls.set("run-worker", {
213
+ runId: "run-worker",
214
+ mode: "single",
215
+ startedAt: 10,
216
+ updatedAt: 20,
217
+ currentAgent: "worker",
218
+ });
219
+ const ctx = {
220
+ hasUI: true,
221
+ ui: {
222
+ setWidget(_key: string, content: unknown) {
223
+ if (content === undefined) throw new Error("widget cleanup failed");
224
+ },
225
+ onTerminalInput() {
226
+ return () => { throw new Error("input cleanup failed"); };
227
+ },
228
+ getEditorText() { return ""; },
229
+ notify() {},
230
+ theme,
231
+ },
232
+ } as unknown as ExtensionContext;
233
+ const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
234
+ fleet.setContext(ctx);
235
+ assert.throws(
236
+ () => fleet.dispose(),
237
+ (error: unknown) => error instanceof AggregateError
238
+ && error.errors.map(String).join("\n").includes("input cleanup failed")
239
+ && error.errors.map(String).join("\n").includes("widget cleanup failed"),
240
+ );
241
+ });
242
+
97
243
  it("keeps widget ownership through invalidation so an empty refresh removes it", () => {
98
244
  const state = stateForTest();
99
245
  state.foregroundControls.set("run-worker", {
@@ -268,8 +414,8 @@ describe("below-editor subagent FleetView", () => {
268
414
  startedAt: 100,
269
415
  updatedAt: 200,
270
416
  steps: [
271
- { agent: "reviewer", index: 0, status: "running", startedAt: 120, tokens: { input: 4_000, output: 200, total: 4_200 } },
272
- { agent: "worker", index: 1, status: "complete", tokens: { input: 100, output: 20, total: 120 } },
417
+ { agent: "reviewer", index: 0, status: "running", description: "Review only authentication", startedAt: 120, model: "openai/gpt-5", thinking: "medium", tokens: { input: 4_000, output: 200, total: 4_200 } },
418
+ { agent: "worker", index: 1, status: "running", description: "Implement only billing", startedAt: 121, tokens: { input: 100, output: 20, total: 120 } },
273
419
  ],
274
420
  });
275
421
  const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
@@ -288,9 +434,10 @@ describe("below-editor subagent FleetView", () => {
288
434
  try {
289
435
  fleet.setContext(ctx);
290
436
  const lines = widgetFactory!({ requestRender() {} }, theme).render(100);
291
- assert.ok(lines.some((line) => line.includes("reviewer") && line.includes("Review the authentication changes")));
437
+ assert.ok(lines.some((line) => line.includes("reviewer (gpt-5 · thinking medium)") && line.includes("Review only authentication")));
438
+ assert.ok(lines.some((line) => line.includes("worker") && line.includes("Implement only billing")));
439
+ assert.ok(lines.every((line) => !line.includes("Review the authentication changes")), "per-child descriptions should replace the run-level fallback when present");
292
440
  assert.ok(lines.some((line) => line.includes("↓ 4.2k tokens")));
293
- assert.ok(!lines.some((line) => line.includes("worker")), "completed async children should leave the status fleet");
294
441
  } finally {
295
442
  fleet.dispose();
296
443
  }
@@ -331,11 +478,32 @@ describe("below-editor subagent FleetView", () => {
331
478
 
332
479
  assert.equal(inputHandler!("\x1b[B"), undefined, "non-empty editor should retain Down");
333
480
  editorText = "";
334
- tui.focusedComponent = {} as Editor;
481
+ tui.focusedComponent = {
482
+ render() { return []; },
483
+ invalidate() {},
484
+ handleInput() {},
485
+ } as unknown as Editor;
335
486
  assert.equal(inputHandler!("\x1b[B"), undefined, "non-editor focus should retain Down");
487
+
488
+ const crossModuleCustomEditor = {
489
+ render() { return []; },
490
+ invalidate() {},
491
+ handleInput() {},
492
+ getText() { return ""; },
493
+ setText() {},
494
+ } satisfies EditorComponent;
495
+ assert.equal(crossModuleCustomEditor instanceof Editor, false, "regression setup must cross the instanceof boundary");
496
+ tui.focusedComponent = crossModuleCustomEditor as unknown as Editor;
497
+ assert.equal(inputHandler!("j"), undefined, "inactive FleetView should retain printable navigation keys");
498
+ assert.equal(inputHandler!("k"), undefined, "inactive FleetView should retain printable navigation keys");
499
+ assert.deepEqual(inputHandler!("\x1b[B"), { consume: true }, "custom editors should activate FleetView across jiti boundaries");
500
+ assert.deepEqual(inputHandler!("j"), { consume: true }, "active FleetView should navigate down with j");
501
+ assert.ok(component.render(100).some((line) => line.includes("⏺ worker")));
502
+ assert.deepEqual(inputHandler!("k"), { consume: true }, "active FleetView should navigate up with k");
503
+ assert.ok(component.render(100).some((line) => line.includes("⏺ main")));
504
+
336
505
  tui.focusedComponent = Object.create(Editor.prototype) as Editor;
337
506
  assert.deepEqual(inputHandler!("\x1b[B"), { consume: true });
338
- assert.deepEqual(inputHandler!("\x1b[B"), { consume: true });
339
507
  assert.ok(component.render(100).some((line) => line.includes("⏺ worker")));
340
508
  assert.deepEqual(inputHandler!("\r"), { consume: true });
341
509
  await Promise.resolve();