@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
@@ -23,8 +23,12 @@ import {
23
23
  SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV,
24
24
  SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV,
25
25
  SUBAGENT_RUN_ID_ENV,
26
+ PI_INTERCOM_STABLE_ID_ENV,
27
+ PI_INTERCOM_SESSION_ID_ENV,
26
28
  applyThinkingSuffix,
27
29
  buildPiArgs,
30
+ projectLaunchResolvedChildExtensions,
31
+ resolvePiLaunchToolPlan,
28
32
  } from "../../src/runs/shared/pi-args.ts";
29
33
 
30
34
  const originalEnv = {
@@ -45,6 +49,10 @@ const originalEnv = {
45
49
  [MCP_DIRECT_CHILD_TOOLS_ENV]: process.env[MCP_DIRECT_CHILD_TOOLS_ENV],
46
50
  [TOOL_BUDGET_ZERO_AUTH_ENV]: process.env[TOOL_BUDGET_ZERO_AUTH_ENV],
47
51
  [SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV]: process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV],
52
+ [PI_INTERCOM_STABLE_ID_ENV]: process.env[PI_INTERCOM_STABLE_ID_ENV],
53
+ [PI_INTERCOM_SESSION_ID_ENV]: process.env[PI_INTERCOM_SESSION_ID_ENV],
54
+ MCP_HASH_ROOT: process.env.MCP_HASH_ROOT,
55
+ MCP_HASH_TOKEN: process.env.MCP_HASH_TOKEN,
48
56
  };
49
57
  const originalCwd = process.cwd();
50
58
  const tempRoots: string[] = [];
@@ -126,6 +134,29 @@ afterEach(() => {
126
134
  });
127
135
 
128
136
  describe("buildPiArgs session wiring", () => {
137
+ it("projects launch-resolved extension identifiers without raw paths", () => {
138
+ const privateExt = path.join(os.tmpdir(), "private-extension-root", "secret-extension.ts");
139
+ const toolExt = path.join(os.tmpdir(), "tool-extension-root", "tool-extension.ts");
140
+ const plan = resolvePiLaunchToolPlan({
141
+ tools: ["read", toolExt],
142
+ extensions: [privateExt],
143
+ subagentOnlyExtensions: ["package-extension"],
144
+ });
145
+
146
+ const projection = projectLaunchResolvedChildExtensions(plan);
147
+
148
+ assert.equal(projection.version, 1);
149
+ assert.equal(projection.source, "launch-resolved");
150
+ assert.equal(projection.disableAmbientExtensions, true);
151
+ assert.equal(projection.runtime.length, 1);
152
+ assert.equal(projection.configured.length, 3);
153
+ assert.equal(projection.effective.length, 4);
154
+ for (const id of [...projection.runtime, ...projection.configured, ...projection.effective]) {
155
+ assert.match(id, /^sha256:[a-f0-9]{16}$/);
156
+ }
157
+ assert.ok(!JSON.stringify(projection).includes(os.tmpdir()), "projection should not expose raw extension paths");
158
+ });
159
+
129
160
  it("uses --session when sessionFile is provided", () => {
130
161
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-args-session-"));
131
162
  try {
@@ -415,11 +446,24 @@ describe("buildPiArgs system prompt mode wiring", () => {
415
446
 
416
447
  const extensionArgs = args.filter((arg, index) => args[index - 1] === "--extension");
417
448
  assert.ok(extensionArgs.some((arg) => arg.endsWith(path.join("src", "runs", "shared", "subagent-prompt-runtime.ts"))));
449
+ assert.ok(args.includes("--no-context-files"));
418
450
  assert.equal(env.SELESAI_SUBAGENT_CHILD, "1");
419
451
  assert.equal(env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT, "0");
420
452
  assert.equal(env.SELESAI_SUBAGENT_INHERIT_SKILLS, "1");
421
453
  });
422
454
 
455
+ it("keeps context file loading enabled when project context is inherited", () => {
456
+ const { args } = buildPiArgs({
457
+ baseArgs: ["-p"],
458
+ task: "hello",
459
+ sessionEnabled: false,
460
+ inheritProjectContext: true,
461
+ inheritSkills: true,
462
+ });
463
+
464
+ assert.equal(args.includes("--no-context-files"), false);
465
+ });
466
+
423
467
  it("passes tool budget through env", () => {
424
468
  const { env } = buildPiArgs({
425
469
  baseArgs: ["-p"],
@@ -475,6 +519,8 @@ describe("buildPiArgs system prompt mode wiring", () => {
475
519
  });
476
520
 
477
521
  it("passes child intercom and orchestrator metadata through env", () => {
522
+ process.env[PI_INTERCOM_STABLE_ID_ENV] = "subagent-chat-parent";
523
+ process.env[PI_INTERCOM_SESSION_ID_ENV] = "session-parent-runtime";
478
524
  const { env } = buildPiArgs({
479
525
  baseArgs: ["-p"],
480
526
  task: "hello",
@@ -490,6 +536,8 @@ describe("buildPiArgs system prompt mode wiring", () => {
490
536
  });
491
537
 
492
538
  assert.equal(env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME, "subagent-worker-78f659a3");
539
+ assert.equal(env[PI_INTERCOM_STABLE_ID_ENV], "subagent-worker-78f659a3");
540
+ assert.equal(env[PI_INTERCOM_SESSION_ID_ENV], undefined);
493
541
  assert.equal(env.SELESAI_SUBAGENT_ORCHESTRATOR_TARGET, "subagent-chat-parent");
494
542
  assert.equal(env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV], "session-parent-123");
495
543
  assert.equal(env.SELESAI_SUBAGENT_RUN_ID, "78f659a3");
@@ -499,6 +547,21 @@ describe("buildPiArgs system prompt mode wiring", () => {
499
547
  assert.match(env[SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV] ?? "", /supervisor-channels/);
500
548
  });
501
549
 
550
+ it("clears inherited pi-intercom identity when no child intercom session name is set", () => {
551
+ process.env[PI_INTERCOM_STABLE_ID_ENV] = "subagent-chat-parent";
552
+ process.env[PI_INTERCOM_SESSION_ID_ENV] = "session-parent-runtime";
553
+ const { env } = buildPiArgs({
554
+ baseArgs: ["-p"],
555
+ task: "hello",
556
+ sessionEnabled: false,
557
+ inheritProjectContext: true,
558
+ inheritSkills: true,
559
+ });
560
+
561
+ assert.equal(env[PI_INTERCOM_STABLE_ID_ENV], undefined);
562
+ assert.equal(env[PI_INTERCOM_SESSION_ID_ENV], undefined);
563
+ });
564
+
502
565
  it("does not create a supervisor channel without an exact parent session id", () => {
503
566
  const { env } = buildPiArgs({
504
567
  baseArgs: ["-p"],
@@ -592,6 +655,46 @@ describe("buildPiArgs system prompt mode wiring", () => {
592
655
  assert.equal(args[args.indexOf("--tools") + 1], "read,bash");
593
656
  });
594
657
 
658
+ it("includes adapter tool filters in MCP cache identity", () => {
659
+ const base = { command: "npx", args: ["browser-mcp"] };
660
+
661
+ assert.notEqual(
662
+ computeMcpServerHash(base),
663
+ computeMcpServerHash({ ...base, includeTools: ["browser_navigate"] }),
664
+ );
665
+ });
666
+
667
+ it("matches pi-mcp-adapter 2.15.0 metadata cache hashes", () => {
668
+ process.env.MCP_HASH_ROOT = "/tmp/mcp-root";
669
+ process.env.MCP_HASH_TOKEN = "token-value";
670
+
671
+ assert.deepEqual(
672
+ [
673
+ computeMcpServerHash({
674
+ command: "npx",
675
+ args: ["-y", "browser-mcp"],
676
+ env: { ROOT: "{env:MCP_HASH_ROOT}", SECRET_COMMAND: "!op read test" },
677
+ cwd: "${MCP_HASH_ROOT}/server",
678
+ exposeResources: false,
679
+ includeTools: ["browser_navigate"],
680
+ excludeTools: ["browser_close"],
681
+ }),
682
+ computeMcpServerHash({
683
+ url: "https://example.test/$env:MCP_HASH_TOKEN",
684
+ headers: { Authorization: "Bearer ${MCP_HASH_TOKEN}", Secret: "!op read test" },
685
+ auth: "bearer",
686
+ bearerTokenEnv: "MCP_HASH_TOKEN",
687
+ }),
688
+ computeMcpServerHash({ socket: "{env:MCP_HASH_ROOT}/rmcp.sock" }),
689
+ ],
690
+ [
691
+ "8af47bd5a801f42bd252789826df883c6f4db6f1d425b82f8561d75063fbe3a8",
692
+ "77db141e556d24c3740a4b4f0cd50d8c23309a28062282cc1ca724207359ef5a",
693
+ "77f7f77a3e8df990d8c78e99ca81508fd8d1f954e814ee7cbfa46719f71eb46b",
694
+ ],
695
+ );
696
+ });
697
+
595
698
  it("augments explicit builtin allowlists with selected direct MCP tool names", () => {
596
699
  const fixture = createMcpFixture();
597
700
  writeMcpFixture(fixture);
@@ -252,6 +252,24 @@ Package skill content.
252
252
  assert.equal(history[1]?.taskHash, taskHash("legacy customer secret"));
253
253
  });
254
254
 
255
+ it("re-sanitizes run history after an external write", () => {
256
+ recordRun("env-agent", "first secret", 0, 1);
257
+ const historyPath = path.join(agentDir, "run-history.jsonl");
258
+ fs.appendFileSync(historyPath, `${JSON.stringify({
259
+ agent: "env-agent",
260
+ task: "externally written secret",
261
+ ts: 2,
262
+ status: "ok",
263
+ duration: 2,
264
+ })}\n`);
265
+
266
+ recordRun("env-agent", "second secret", 0, 3);
267
+
268
+ const rawHistory = fs.readFileSync(historyPath, "utf-8");
269
+ assert.doesNotMatch(rawHistory, /first secret|externally written secret|second secret/);
270
+ assert.equal(loadRunsForAgent("env-agent").length, 3);
271
+ });
272
+
255
273
  it("uses the configured agent dir for subagent bridge instruction files", () => {
256
274
  const instructionPath = path.join(agentDir, "extensions", "subagent", "bridge.md");
257
275
  writeFile(instructionPath, "Native bridge for {orchestratorTarget}");
@@ -107,6 +107,7 @@ Project prompt.
107
107
  { provider: "test", id: "fallback", fullId: "test/fallback" },
108
108
  ],
109
109
  capabilityCeiling: ceiling,
110
+ artifacts: true,
110
111
  });
111
112
 
112
113
  assert.equal(result.ok, true);
@@ -143,6 +144,7 @@ Project prompt.
143
144
  { provider: "test", id: "fallback", fullId: "test/fallback" },
144
145
  ],
145
146
  capabilityCeiling: ceiling,
147
+ artifacts: true,
146
148
  });
147
149
  assert.equal(repeated.ok, true);
148
150
  assert.equal(repeated.contract.digest, result.contract.digest);
@@ -153,6 +155,52 @@ Project prompt.
153
155
  }
154
156
  });
155
157
 
158
+ it("resolves agent aliases to the canonical launch contract agent", async () => {
159
+ const cwd = path.join(tempDir, "repo-alias");
160
+ fs.mkdirSync(cwd, { recursive: true });
161
+ writeAgent(path.join(cwd, ".selesai", "agents", "worker.md"), `---
162
+ name: worker
163
+ description: Project worker
164
+ aliases: shipper, worker-alias
165
+ ---
166
+ Project prompt.
167
+ `);
168
+
169
+ const result = await resolveSubagentLaunchContract({
170
+ agent: "shipper",
171
+ cwd,
172
+ task: "Implement the change",
173
+ });
174
+
175
+ assert.equal(result.ok, true);
176
+ assert.equal(result.contract.agent.name, "worker");
177
+ });
178
+
179
+ it("reports alias collisions as ambiguous agents", async () => {
180
+ const cwd = path.join(tempDir, "repo-alias-collision");
181
+ fs.mkdirSync(cwd, { recursive: true });
182
+ writeAgent(path.join(cwd, ".selesai", "agents", "worker.md"), `---
183
+ name: worker
184
+ description: Project worker
185
+ aliases: shared-alias
186
+ ---
187
+ Project prompt.
188
+ `);
189
+ writeAgent(path.join(cwd, ".selesai", "agents", "reviewer.md"), `---
190
+ name: reviewer
191
+ description: Project reviewer
192
+ aliases: shared-alias
193
+ ---
194
+ Review prompt.
195
+ `);
196
+
197
+ const result = await resolveSubagentLaunchContract({ agent: "shared-alias", cwd });
198
+
199
+ assert.equal(result.ok, false);
200
+ assert.equal(result.code, "ambiguous_agent");
201
+ assert.match(result.message, /Ambiguous agent alias 'shared-alias': reviewer, worker|Ambiguous agent alias 'shared-alias': worker, reviewer/);
202
+ });
203
+
156
204
  it("changes definition and launch digests when selected agent content changes", async () => {
157
205
  const cwd = path.join(tempDir, "repo");
158
206
  fs.mkdirSync(cwd, { recursive: true });
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
3
3
 
4
4
  import { visibleWidth } from "@earendil-works/pi-tui";
5
5
  import { row } from "../../src/tui/render-helpers.ts";
6
- import { renderSubagentResult } from "../../src/tui/render.ts";
6
+ import { renderSubagentResult, truncLine } from "../../src/tui/render.ts";
7
7
 
8
8
  const theme = {
9
9
  fg(_name: string, text: string): string {
@@ -49,6 +49,18 @@ test("row keeps styled multiline content within the available width", () => {
49
49
  assert.doesNotMatch(rendered, /[\r\n\t]/);
50
50
  });
51
51
 
52
+ test("truncLine preserves ANSI styles and resets through the ellipsis", () => {
53
+ assert.equal(truncLine("\u001b[31mabcdef\u001b[0m", 4), "\u001b[31mabc\u001b[31m…");
54
+ assert.equal(truncLine("\u001b[31mab\u001b[0mcdef", 4), "\u001b[31mab\u001b[0mc…");
55
+ assert.equal(truncLine("ab\u001b[xcd", 4), "ab\u001b[…");
56
+ });
57
+
58
+ test("truncLine respects grapheme display width", () => {
59
+ const rendered = truncLine("🙂🙂🙂", 5);
60
+ assert.equal(rendered, "🙂🙂…");
61
+ assert.equal(visibleWidth(rendered), 5);
62
+ });
63
+
52
64
  test("compact chain rendering uses workflow graph spans for dynamic fanout results", () => {
53
65
  const component = renderSubagentResult({
54
66
  content: [{ type: "text", text: "done" }],
@@ -43,13 +43,14 @@ describe("result intercom formatter", () => {
43
43
  assert.match(payload.message, /^subagent results/m);
44
44
  assert.match(payload.message, /Run: run-123/);
45
45
  assert.match(payload.message, /Mode: chain/);
46
- assert.match(payload.message, /Status: failed/);
46
+ assert.match(payload.message, /Process status: failed/);
47
47
  assert.match(payload.message, /Children: 1 completed, 1 failed/);
48
+ assert.match(payload.message, /Outputs: 2 unknown \(semantic adequacy unassessed\)/);
48
49
  assert.match(payload.message, /Chain steps: 4/);
49
50
  assert.match(payload.message, /Intercom targets below identify child sessions used while they were running/);
50
- assert.match(payload.message, /1\. reviewer-a — completed/);
51
+ assert.match(payload.message, /1\. reviewer-a — process completed · output unknown/);
51
52
  assert.match(payload.message, /Run intercom target: subagent-reviewer-a-run-123-1/);
52
- assert.match(payload.message, /2\. reviewer-b — failed/);
53
+ assert.match(payload.message, /2\. reviewer-b — process failed · output unknown/);
53
54
  assert.match(payload.message, /Output artifact: \/tmp\/a\.md/);
54
55
  assert.match(payload.message, /Session: \/tmp\/a-session\.jsonl/);
55
56
  });
@@ -161,6 +162,27 @@ describe("result intercom formatter", () => {
161
162
  assert.match(payload.message, /↳ reviewer — complete \[nested-a\]/);
162
163
  });
163
164
 
165
+ it("separates process failure from output presence and gives salvage guidance", () => {
166
+ const payload = buildSubagentResultIntercomPayload({
167
+ to: "chat",
168
+ runId: "run-salvage",
169
+ mode: "parallel",
170
+ source: "foreground",
171
+ children: [
172
+ { agent: "a", status: "completed", outputState: "present", summary: "answer a" },
173
+ { agent: "b", status: "failed", outputState: "present", summary: "runner crashed after answer b" },
174
+ { agent: "c", status: "failed", outputState: "absent", summary: "startup failed" },
175
+ ],
176
+ });
177
+
178
+ assert.equal(payload.status, "failed");
179
+ assert.equal(payload.children[1]?.outputState, "present");
180
+ assert.match(payload.message, /Process status: failed/);
181
+ assert.match(payload.message, /Outputs: 2 present, 1 absent \(semantic adequacy unassessed\)/);
182
+ assert.match(payload.message, /Inspect that output before retrying/);
183
+ assert.match(payload.message, /2\. b — process failed · output present/);
184
+ });
185
+
164
186
  it("keeps full child summaries inside grouped payloads", () => {
165
187
  const longSummary = `${"x".repeat(2000)}\n${"y".repeat(2000)}`;
166
188
  const payload = buildSubagentResultIntercomPayload({
@@ -217,9 +239,12 @@ describe("result intercom formatter", () => {
217
239
  assert.equal(stripped.results[0]?.truncation, undefined);
218
240
  });
219
241
 
220
- it("resolves paused and detached statuses", () => {
242
+ it("resolves paused, detached, and signal-terminated statuses", () => {
221
243
  assert.equal(resolveSubagentResultStatus({ interrupted: true }), "paused");
222
244
  assert.equal(resolveSubagentResultStatus({ detached: true }), "detached");
245
+ assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 1 }), "stopped");
246
+ assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 1, timedOut: true }), "failed");
247
+ assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 0, success: true }), "completed");
223
248
  assert.equal(resolveSubagentResultStatus({ success: true }), "completed");
224
249
  assert.equal(resolveSubagentResultStatus({ exitCode: 1 }), "failed");
225
250
  });
@@ -42,12 +42,12 @@ function once(events: FakeEvents, event: string): Promise<unknown> {
42
42
  });
43
43
  }
44
44
 
45
- function ctx() {
45
+ function ctx(sessionId = "session-123", sessionFile = "/sessions/parent.jsonl") {
46
46
  return {
47
47
  cwd: "/repo",
48
48
  sessionManager: {
49
- getSessionId: () => "session-123",
50
- getSessionFile: () => "/sessions/parent.jsonl",
49
+ getSessionId: () => sessionId,
50
+ getSessionFile: () => sessionFile,
51
51
  },
52
52
  } as any;
53
53
  }
@@ -91,6 +91,14 @@ describe("subagent extension RPC bridge", () => {
91
91
  (reply as { data: { capabilities?: { nonRecoveringSteer?: boolean } } }).data.capabilities?.nonRecoveringSteer,
92
92
  true,
93
93
  );
94
+ assert.equal(
95
+ (reply as { data: { capabilities?: { resume?: boolean } } }).data.capabilities?.resume,
96
+ true,
97
+ );
98
+ assert.deepEqual(
99
+ (reply as { data: { capabilities?: { fleetStatus?: unknown } } }).data.capabilities?.fleetStatus,
100
+ { version: 1 },
101
+ );
94
102
 
95
103
  bridge.dispose();
96
104
  });
@@ -137,7 +145,149 @@ describe("subagent extension RPC bridge", () => {
137
145
  assert.equal(reply.success, true);
138
146
  assert.deepEqual(executedParams, { action: "status", id: "abc123" });
139
147
  assert.equal((reply as { data: { text?: string } }).data.text, "Run: abc123");
148
+ assert.deepEqual((reply as { data: { fleet?: unknown } }).data.fleet, {
149
+ version: 1, entries: [], totalActive: 0, omitted: 0,
150
+ });
151
+
152
+ bridge.dispose();
153
+ });
154
+
155
+ it("projects bounded display-safe active fleet records without internal ids", async () => {
156
+ const events = new FakeEvents();
157
+ const state = {
158
+ currentSessionId: "/sessions/parent.jsonl",
159
+ foregroundControls: new Map(),
160
+ asyncJobs: new Map([["async-private-id", {
161
+ asyncId: "async-private-id", sessionId: "/sessions/parent.jsonl", status: "running", mode: "single",
162
+ description: ["Review", "\u001b]8;;hostile\u0007", "the diff"].join("\n"),
163
+ startedAt: 100, steps: [{ agent: "reviewer", label: "opaque label", status: "running", startedAt: 120, model: "anthropic/claude-opus-4-8:high", thinking: "high", tokens: { input: 12, output: 34, total: 46 } }],
164
+ }]]),
165
+ } as any;
166
+ const bridge = registerSubagentRpcBridge({
167
+ events, getContext: () => ctx("runtime-session-id", "/sessions/parent.jsonl"), state,
168
+ execute: async () => ({ content: [{ type: "text", text: "Active async runs: 1" }], details: { mode: "management", results: [] } } as any),
169
+ });
170
+ const reply = await request(events, "fleet-status", "status");
171
+ const fleet = (reply as { data: { fleet: { entries: Array<Record<string, unknown>> } } }).data.fleet;
172
+ assert.equal(fleet.entries.length, 1);
173
+ assert.equal((fleet as { totalActive?: number }).totalActive, 1);
174
+ assert.equal((fleet as { omitted?: number }).omitted, 0);
175
+ assert.deepEqual(fleet.entries[0], {
176
+ key: "fleet-1", agent: "reviewer", role: "opaque label", model: "anthropic/claude-opus-4-8:high", effort: "high",
177
+ startedAt: 120, tokens: { input: 12, output: 34, total: 46 }, goal: "Review the diff",
178
+ });
179
+ assert.equal(JSON.stringify(fleet).includes("async-private-id"), false);
180
+ bridge.dispose();
181
+ });
182
+
183
+ it("projects resolved foreground model, effort, split usage, and goal", async () => {
184
+ const events = new FakeEvents();
185
+ const state = {
186
+ currentSessionId: "session-123",
187
+ foregroundControls: new Map([["private-run", {
188
+ runId: "private-run",
189
+ sessionId: "session-123",
190
+ mode: "single",
191
+ startedAt: 90,
192
+ activeChildren: new Map([[0, {
193
+ index: 0,
194
+ agent: "worker",
195
+ description: "Implement the fix",
196
+ startedAt: 100,
197
+ updatedAt: 110,
198
+ model: "openai/gpt-5.6-terra:high",
199
+ thinking: "high",
200
+ inputTokens: 321,
201
+ outputTokens: 45,
202
+ tokens: 366,
203
+ }]]),
204
+ }]]),
205
+ asyncJobs: new Map(),
206
+ } as any;
207
+ state.foregroundControls.set("private-old", {
208
+ runId: "private-old",
209
+ sessionId: "old-session",
210
+ mode: "single",
211
+ startedAt: 50,
212
+ currentAgent: "reviewer",
213
+ description: "Old work",
214
+ });
215
+ const bridge = registerSubagentRpcBridge({
216
+ events,
217
+ getContext: () => ctx("session-123", "session-123"),
218
+ state,
219
+ execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any),
220
+ });
221
+ const reply = await request(events, "foreground-fleet", "status");
222
+ assert.deepEqual((reply as any).data.fleet, {
223
+ version: 1,
224
+ totalActive: 1,
225
+ omitted: 0,
226
+ entries: [{
227
+ key: "fleet-1",
228
+ agent: "worker",
229
+ model: "openai/gpt-5.6-terra:high",
230
+ effort: "high",
231
+ startedAt: 100,
232
+ tokens: { input: 321, output: 45, total: 366 },
233
+ goal: "Implement the fix",
234
+ }],
235
+ });
236
+ assert.equal(JSON.stringify((reply as any).data.fleet).includes("private-run"), false);
237
+ bridge.dispose();
238
+ });
239
+
240
+ it("uses monotonic opaque keys across removal/insertion and resets them per session", async () => {
241
+ const events = new FakeEvents();
242
+ const jobs = new Map<string, any>([
243
+ ["private-a", { asyncId: "private-a", sessionId: "A", status: "running", mode: "single", startedAt: 1, agents: ["alpha"] }],
244
+ ["private-b", { asyncId: "private-b", sessionId: "A", status: "running", mode: "single", startedAt: 2, agents: ["beta"] }],
245
+ ["private-unattributed", { asyncId: "private-unattributed", status: "running", mode: "single", startedAt: 3, agents: ["unknown"] }],
246
+ ]);
247
+ const state = { currentSessionId: "A", foregroundControls: new Map(), asyncJobs: jobs } as any;
248
+ let activeSession = "A";
249
+ const bridge = registerSubagentRpcBridge({ events, getContext: () => ctx(activeSession, activeSession), state, execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any) });
250
+ const keys = async (id: string) => ((await request(events, id, "status")) as any).data.fleet.entries.map((entry: { key: string }) => entry.key);
251
+ assert.deepEqual(await keys("keys-a"), ["fleet-1", "fleet-2"]);
252
+ jobs.delete("private-b"); jobs.set("private-c", { asyncId: "private-c", sessionId: "A", status: "running", mode: "single", startedAt: 3, agents: ["gamma"] });
253
+ assert.deepEqual(await keys("keys-b"), ["fleet-1", "fleet-3"]);
254
+ state.currentSessionId = "B";
255
+ activeSession = "B";
256
+ assert.deepEqual(await keys("keys-c"), []);
257
+ jobs.set("private-d", { asyncId: "private-d", sessionId: "B", status: "running", mode: "single", startedAt: 4, agents: ["delta"] });
258
+ assert.deepEqual(await keys("keys-d"), ["fleet-1"]);
259
+ bridge.dispose();
260
+ });
140
261
 
262
+ it("reports bounded overflow and excludes unattributed or foreign-session jobs", async () => {
263
+ const events = new FakeEvents();
264
+ const jobs = new Map<string, any>();
265
+ for (let index = 0; index < 18; index += 1) {
266
+ jobs.set(`private-${index}`, {
267
+ asyncId: `private-${index}`,
268
+ sessionId: "session-123",
269
+ status: "running",
270
+ mode: "single",
271
+ startedAt: index + 1,
272
+ agents: [`worker-${index}`],
273
+ });
274
+ }
275
+ jobs.set("unattributed", { asyncId: "unattributed", status: "running", mode: "single", startedAt: 20, agents: ["hidden"] });
276
+ jobs.set("foreign", { asyncId: "foreign", sessionId: "other", status: "running", mode: "single", startedAt: 21, agents: ["hidden"] });
277
+ const state = { currentSessionId: "session-123", foregroundControls: new Map(), asyncJobs: jobs } as any;
278
+ const bridge = registerSubagentRpcBridge({
279
+ events,
280
+ getContext: () => ctx("session-123", "session-123"),
281
+ state,
282
+ execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any),
283
+ });
284
+ const reply = await request(events, "fleet-overflow", "status");
285
+ const fleet = (reply as any).data.fleet;
286
+ assert.equal(fleet.entries.length, 16);
287
+ assert.equal(fleet.totalActive, 18);
288
+ assert.equal(fleet.omitted, 2);
289
+ assert.equal(JSON.stringify(fleet).includes("unattributed"), false);
290
+ assert.equal(JSON.stringify(fleet).includes("foreign"), false);
141
291
  bridge.dispose();
142
292
  });
143
293
 
@@ -298,6 +448,75 @@ describe("subagent extension RPC bridge", () => {
298
448
  bridge.dispose();
299
449
  });
300
450
 
451
+ it("delegates resume through the existing package-owned revival action", async () => {
452
+ const events = new FakeEvents();
453
+ let executedParams: unknown;
454
+ const bridge = registerSubagentRpcBridge({
455
+ events,
456
+ getContext: () => ctx(),
457
+ execute: async (_id, params) => {
458
+ executedParams = params;
459
+ return {
460
+ content: [{ type: "text", text: "Revived async subagent from run-1." }],
461
+ details: { mode: "single", results: [], asyncId: "run-2", asyncDir: "/tmp/run-2" },
462
+ } as any;
463
+ },
464
+ });
465
+
466
+ const reply = await request(events, "resume-1", "resume", {
467
+ id: "run-1",
468
+ index: 0,
469
+ message: " Continue with the focused review. ",
470
+ output: " /tmp/revived-output.md ",
471
+ outputMode: "file-only",
472
+ });
473
+
474
+ assert.equal(reply.success, true);
475
+ assert.deepEqual(executedParams, {
476
+ action: "resume",
477
+ id: "run-1",
478
+ index: 0,
479
+ message: "Continue with the focused review.",
480
+ output: "/tmp/revived-output.md",
481
+ outputMode: "file-only",
482
+ });
483
+ assert.equal((reply as { data: { details?: { asyncId?: string } } }).data.details?.asyncId, "run-2");
484
+
485
+ bridge.dispose();
486
+ });
487
+
488
+ it("rejects targetless or empty RPC resume before executor dispatch", async () => {
489
+ const events = new FakeEvents();
490
+ let executeCalls = 0;
491
+ const bridge = registerSubagentRpcBridge({
492
+ events,
493
+ getContext: () => ctx(),
494
+ execute: async () => {
495
+ executeCalls++;
496
+ return { content: [], details: { mode: "management", results: [] } } as any;
497
+ },
498
+ });
499
+
500
+ const targetless = await request(events, "resume-no-target", "resume", { message: "continue" });
501
+ const empty = await request(events, "resume-empty", "resume", { id: "run-1", message: " " });
502
+ const inlineOutput = await request(events, "resume-inline", "resume", {
503
+ id: "run-1",
504
+ message: "continue",
505
+ output: "/tmp/output.md",
506
+ outputMode: "inline",
507
+ });
508
+
509
+ assert.equal(targetless.success, false);
510
+ assert.equal((targetless as { error: { code: string } }).error.code, "invalid_params");
511
+ assert.match((targetless as { error: { message: string } }).error.message, /requires id, runId, or dir/);
512
+ assert.equal(empty.success, false);
513
+ assert.equal((empty as { error: { code: string } }).error.code, "invalid_params");
514
+ assert.equal(inlineOutput.success, false);
515
+ assert.match((inlineOutput as { error: { message: string } }).error.message, /file-only/);
516
+ assert.equal(executeCalls, 0);
517
+ bridge.dispose();
518
+ });
519
+
301
520
  it("delegates interrupt through the existing executor action", async () => {
302
521
  const events = new FakeEvents();
303
522
  let executedParams: unknown;
@@ -328,7 +547,7 @@ describe("subagent extension RPC bridge", () => {
328
547
  fs.mkdirSync(asyncDir, { recursive: true });
329
548
  fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
330
549
  runId: "run-stop",
331
- sessionId: "session-123",
550
+ sessionId: "/sessions/parent.jsonl",
332
551
  mode: "single",
333
552
  state: "running",
334
553
  pid: 4242,
@@ -994,6 +994,36 @@ describe("async run status inspection", () => {
994
994
  }
995
995
  });
996
996
 
997
+ it("shows signal-terminated result-only runs as stopped", () => {
998
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-signal-result-"));
999
+ try {
1000
+ const asyncRoot = path.join(root, "runs");
1001
+ const resultsDir = path.join(root, "results");
1002
+ fs.mkdirSync(path.join(asyncRoot, "run-signal-result"), { recursive: true });
1003
+ fs.mkdirSync(resultsDir, { recursive: true });
1004
+ fs.writeFileSync(path.join(resultsDir, "run-signal-result.json"), JSON.stringify({
1005
+ id: "run-signal-result",
1006
+ agent: "worker",
1007
+ success: false,
1008
+ state: "failed",
1009
+ summary: "Subagent process terminated by signal SIGTERM.",
1010
+ results: [{ agent: "worker", success: false, exitCode: 1, processSignal: "SIGTERM" }],
1011
+ }, null, 2), "utf-8");
1012
+
1013
+ const result = inspectSubagentStatus({ id: "run-signal-result" }, {
1014
+ asyncDirRoot: asyncRoot,
1015
+ resultsDir,
1016
+ });
1017
+
1018
+ const text = textContent(result);
1019
+ assert.equal(result.isError, undefined);
1020
+ assert.match(text, /State: stopped/);
1021
+ assert.match(text, /Resume: unavailable; stopped runs are not resumable/);
1022
+ } finally {
1023
+ fs.rmSync(root, { recursive: true, force: true });
1024
+ }
1025
+ });
1026
+
997
1027
  it("falls back to an existing result when async dir has no status file", () => {
998
1028
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-result-fallback-"));
999
1029
  try {