@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
@@ -368,7 +368,7 @@ describe("native subagent fleet", () => {
368
368
  currentIndex: 1,
369
369
  description: "Review the active task",
370
370
  activeChildren: new Map([
371
- [0, { index: 0, agent: "worker", description: "Implement the active task", startedAt: now - 900, updatedAt: now - 100, tokens: 120 }],
371
+ [0, { index: 0, agent: "worker", description: "Implement the active task", startedAt: now - 900, updatedAt: now - 100, tokens: 120, model: "provider/live-model", thinking: "high" }],
372
372
  [1, { index: 1, agent: "reviewer", description: "Review the active task", startedAt: now - 800, updatedAt: now, tokens: 240 }],
373
373
  ]),
374
374
  });
@@ -388,6 +388,7 @@ describe("native subagent fleet", () => {
388
388
  assert.ok(lines.some((line) => line.includes("worker")));
389
389
  assert.ok(lines.some((line) => line.includes("reviewer")));
390
390
  assert.ok(lines.some((line) => line.includes("foreground · live")));
391
+ assert.ok(lines.some((line) => line.includes("live-model · thinking high")));
391
392
  assert.ok(lines.some((line) => line.includes("Task") && line.includes("Implement the active task")));
392
393
  assert.ok(lines.some((line) => line.includes("Conversation") && line.includes("assistant response")));
393
394
  assert.ok(lines.some((line) => line.includes("Worker live result")));
@@ -442,7 +443,7 @@ describe("native subagent fleet", () => {
442
443
  cwd: baseCwd,
443
444
  sessionId: "session-current",
444
445
  updatedAt: 200,
445
- children: [{ agent: "reviewer", index: 0, status: "completed", transcriptPath: recentTranscript }],
446
+ children: [{ agent: "reviewer", index: 0, status: "completed", transcriptPath: recentTranscript, model: "provider/recent-model", thinking: "xhigh" }],
446
447
  });
447
448
  state.asyncJobs.set(asyncId, {
448
449
  asyncId,
@@ -469,7 +470,11 @@ describe("native subagent fleet", () => {
469
470
  { initialKey, refreshMs: 60_000, markdownTheme },
470
471
  );
471
472
  try {
472
- assert.ok(component.render(100).some((line) => line.includes(expected)), `missing ${expected}`);
473
+ const lines = component.render(100);
474
+ assert.ok(lines.some((line) => line.includes(expected)), `missing ${expected}`);
475
+ if (initialKey.startsWith("foreground-recent:")) {
476
+ assert.ok(lines.some((line) => line.includes("recent-model · thinking xhigh")));
477
+ }
473
478
  } finally {
474
479
  component.dispose();
475
480
  }
@@ -506,7 +511,7 @@ describe("native subagent fleet", () => {
506
511
  assert.equal(state.fleetInspectorOpen, false);
507
512
  });
508
513
 
509
- it("opens the inspector with the FleetView-selected child focused", () => {
514
+ it("focuses the selected child and renders raw foreground model details", () => {
510
515
  const state = stateForTest();
511
516
  state.foregroundControls.set("run-worker", {
512
517
  runId: "run-worker",
@@ -515,6 +520,8 @@ describe("native subagent fleet", () => {
515
520
  updatedAt: 20,
516
521
  currentAgent: "worker",
517
522
  currentIndex: 0,
523
+ model: "provider/raw-model",
524
+ thinking: "medium",
518
525
  });
519
526
  state.foregroundControls.set("run-reviewer", {
520
527
  runId: "run-reviewer",
@@ -524,6 +531,14 @@ describe("native subagent fleet", () => {
524
531
  currentAgent: "reviewer",
525
532
  currentIndex: 0,
526
533
  });
534
+ state.foregroundRuns!.set("run-recent", {
535
+ runId: "run-recent",
536
+ mode: "single",
537
+ cwd: process.cwd(),
538
+ sessionId: "session-current",
539
+ updatedAt: 19,
540
+ children: [{ agent: "reviewer", index: 0, status: "completed", model: "provider/recent-raw-model", thinking: "xhigh" }],
541
+ });
527
542
  const component = new SubagentFleetComponent(
528
543
  { terminal: { rows: 28, columns: 90 }, requestRender() {} } as never,
529
544
  theme as never,
@@ -532,11 +547,148 @@ describe("native subagent fleet", () => {
532
547
  { initialKey: "foreground-active:run-worker:0", refreshMs: 60_000 },
533
548
  );
534
549
  try {
535
- const selectedLine = component.render(90).find((line) => line.includes("›"));
550
+ const lines = component.render(90);
551
+ const selectedLine = lines.find((line) => line.includes("›"));
536
552
  assert.ok(selectedLine?.includes("run-work"), `unexpected selected row: ${selectedLine}`);
553
+ assert.ok(lines.some((line) => line.includes("Model: raw-model · thinking medium")));
537
554
  } finally {
538
555
  component.dispose();
539
556
  }
557
+
558
+ const recentComponent = new SubagentFleetComponent(
559
+ { terminal: { rows: 28, columns: 90 }, requestRender() {} } as never,
560
+ theme as never,
561
+ state,
562
+ () => {},
563
+ { initialKey: "foreground-recent:run-recent:0", refreshMs: 60_000 },
564
+ );
565
+ try {
566
+ assert.ok(recentComponent.render(90).some((line) => line.includes("Model: recent-raw-model · thinking xhigh")));
567
+ } finally {
568
+ recentComponent.dispose();
569
+ }
570
+ });
571
+
572
+ it("steers the selected async child with an inline message", async () => {
573
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-steer-"));
574
+ try {
575
+ const asyncDir = writeAsyncRun(root, { id: "async-steer", agents: ["worker", "reviewer"] });
576
+ const state = stateForTest();
577
+ const calls: Array<{ runId: string; asyncDir: string; index?: number; message: string }> = [];
578
+ const component = new SubagentFleetComponent(
579
+ { terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
580
+ theme as never,
581
+ state,
582
+ () => {},
583
+ {
584
+ asyncDirRoot: root,
585
+ resultsDir: path.join(root, "results"),
586
+ initialKey: "async:async-steer:0",
587
+ refreshMs: 60_000,
588
+ actions: {
589
+ async steer(input) {
590
+ calls.push(input);
591
+ return { text: "Steering queued." };
592
+ },
593
+ stop() {
594
+ return { text: "unused" };
595
+ },
596
+ },
597
+ },
598
+ );
599
+ try {
600
+ component.handleInput("s");
601
+ assert.ok(component.render(100).some((line) => line.includes("Steer message:")));
602
+ for (const char of "please continue") component.handleInput(char);
603
+ component.handleInput("\r");
604
+ await new Promise((resolve) => setImmediate(resolve));
605
+ assert.deepEqual(calls, [{ runId: "async-steer", asyncDir, index: 0, message: "please continue" }]);
606
+ assert.ok(component.render(100).some((line) => line.includes("Steering queued.")));
607
+ } finally {
608
+ component.dispose();
609
+ }
610
+ } finally {
611
+ fs.rmSync(root, { recursive: true, force: true });
612
+ }
613
+ });
614
+
615
+ it("confirms stop for the selected async child before calling the action", async () => {
616
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-stop-"));
617
+ try {
618
+ const asyncDir = writeAsyncRun(root, { id: "async-stop" });
619
+ const state = stateForTest();
620
+ const calls: Array<{ runId: string; asyncDir: string; index?: number }> = [];
621
+ const component = new SubagentFleetComponent(
622
+ { terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
623
+ theme as never,
624
+ state,
625
+ () => {},
626
+ {
627
+ asyncDirRoot: root,
628
+ resultsDir: path.join(root, "results"),
629
+ refreshMs: 60_000,
630
+ actions: {
631
+ async steer() {
632
+ return { text: "unused" };
633
+ },
634
+ stop(input) {
635
+ calls.push(input);
636
+ return { text: "Stop requested." };
637
+ },
638
+ },
639
+ },
640
+ );
641
+ try {
642
+ component.handleInput("D");
643
+ assert.ok(component.render(100).some((line) => line.includes("Confirm stop for async run async-stop")));
644
+ component.handleInput("n");
645
+ assert.deepEqual(calls, []);
646
+ component.handleInput("D");
647
+ component.handleInput("y");
648
+ await new Promise((resolve) => setImmediate(resolve));
649
+ assert.deepEqual(calls, [{ runId: "async-stop", asyncDir, index: 0 }]);
650
+ assert.ok(component.render(100).some((line) => line.includes("Stop requested.")));
651
+ } finally {
652
+ component.dispose();
653
+ }
654
+ } finally {
655
+ fs.rmSync(root, { recursive: true, force: true });
656
+ }
657
+ });
658
+
659
+ it("explains unavailable controls for completed async children", () => {
660
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-unavailable-"));
661
+ try {
662
+ writeAsyncRun(root, { id: "async-complete", state: "complete" });
663
+ const state = stateForTest();
664
+ const component = new SubagentFleetComponent(
665
+ { terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
666
+ theme as never,
667
+ state,
668
+ () => {},
669
+ {
670
+ asyncDirRoot: root,
671
+ resultsDir: path.join(root, "results"),
672
+ refreshMs: 60_000,
673
+ actions: {
674
+ async steer() {
675
+ return { text: "unused" };
676
+ },
677
+ stop() {
678
+ return { text: "unused" };
679
+ },
680
+ },
681
+ },
682
+ );
683
+ try {
684
+ component.handleInput("s");
685
+ assert.ok(component.render(100).some((line) => line.includes("Selected child is complete")));
686
+ } finally {
687
+ component.dispose();
688
+ }
689
+ } finally {
690
+ fs.rmSync(root, { recursive: true, force: true });
691
+ }
540
692
  });
541
693
 
542
694
  it("refreshes the roster while the inspector remains open", async () => {
@@ -552,6 +704,12 @@ describe("native subagent fleet", () => {
552
704
  () => {},
553
705
  { asyncDirRoot: root, resultsDir: path.join(root, "results"), refreshMs: 10 },
554
706
  );
707
+ let invalidations = 0;
708
+ const originalInvalidate = component.invalidate.bind(component);
709
+ component.invalidate = () => {
710
+ invalidations++;
711
+ originalInvalidate();
712
+ };
555
713
  try {
556
714
  assert.ok(component.render(90).some((line) => line.includes("No tracked children")));
557
715
  const initialOutput = Array.from({ length: 40 }, (_, index) => `output line ${index}`).join("\n");
@@ -565,6 +723,7 @@ describe("native subagent fleet", () => {
565
723
  lines = component.render(90);
566
724
  assert.ok(lines.some((line) => line.includes("LATEST LIVE OUTPUT")), "live transcript should keep following new output");
567
725
  assert.ok(renderRequests > 0);
726
+ assert.ok(invalidations > 0, "live refresh must invalidate cached TUI frames before rendering");
568
727
  } finally {
569
728
  component.dispose();
570
729
  }
@@ -1,7 +1,14 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
3
  import type { AgentProgress, ForegroundRunControl } from "../../src/shared/types.ts";
4
- import { beginForegroundChild, finishForegroundChild, updateForegroundChild } from "../../src/runs/foreground/foreground-control.ts";
4
+ import {
5
+ beginForegroundChild,
6
+ finishForegroundChild,
7
+ foregroundSchedulingSettled,
8
+ retainForegroundSchedulingOwner,
9
+ settleForegroundSchedulingOwner,
10
+ updateForegroundChild,
11
+ } from "../../src/runs/foreground/foreground-control.ts";
5
12
 
6
13
  function progress(index: number, agent: string, tokens: number): AgentProgress {
7
14
  return {
@@ -13,6 +20,10 @@ function progress(index: number, agent: string, tokens: number): AgentProgress {
13
20
  recentOutput: [],
14
21
  toolCount: index + 1,
15
22
  tokens,
23
+ model: "openai/gpt-5.6-terra:high",
24
+ thinking: "high",
25
+ inputTokens: tokens - 20,
26
+ outputTokens: 20,
16
27
  durationMs: 10,
17
28
  };
18
29
  }
@@ -28,11 +39,13 @@ describe("foreground child control", () => {
28
39
  };
29
40
  let firstInterrupts = 0;
30
41
  let secondInterrupts = 0;
42
+ let firstDetaches = 0;
31
43
  beginForegroundChild(control, {
32
44
  index: 0,
33
45
  agent: "reviewer",
34
46
  description: "Review correctness",
35
47
  interrupt: () => { firstInterrupts++; return true; },
48
+ detach: () => { firstDetaches++; return true; },
36
49
  });
37
50
  beginForegroundChild(control, {
38
51
  index: 1,
@@ -46,10 +59,16 @@ describe("foreground child control", () => {
46
59
  updateForegroundChild(control, 0, progress(0, "reviewer", 120));
47
60
  assert.equal(control.currentIndex, 0);
48
61
  assert.equal(control.tokens, 120);
62
+ assert.equal(control.inputTokens, 100);
63
+ assert.equal(control.outputTokens, 20);
64
+ assert.equal(control.model, "openai/gpt-5.6-terra:high");
65
+ assert.equal(control.thinking, "high");
49
66
  assert.equal(control.activeChildren?.get(1)?.tokens, undefined);
50
67
  assert.equal(control.interrupt?.(), true);
51
68
  assert.equal(firstInterrupts, 1);
52
69
  assert.equal(secondInterrupts, 0);
70
+ assert.equal(control.detach?.(), true);
71
+ assert.equal(firstDetaches, 1);
53
72
 
54
73
  updateForegroundChild(control, 1, progress(1, "reviewer", 240));
55
74
  finishForegroundChild(control, 1);
@@ -60,6 +79,27 @@ describe("foreground child control", () => {
60
79
  finishForegroundChild(control, 0);
61
80
  assert.equal(control.activeChildren?.size, 0);
62
81
  assert.equal(control.currentIndex, undefined);
82
+ assert.equal(control.model, undefined);
83
+ assert.equal(control.inputTokens, undefined);
84
+ assert.equal(control.outputTokens, undefined);
63
85
  assert.equal(control.interrupt, undefined);
86
+ assert.equal(control.detach, undefined);
87
+ });
88
+
89
+ it("settles scheduling only after every owner releases", () => {
90
+ const control: ForegroundRunControl = {
91
+ runId: "owned-run",
92
+ mode: "parallel",
93
+ startedAt: 1,
94
+ updatedAt: 1,
95
+ schedulingOwners: 1,
96
+ };
97
+
98
+ retainForegroundSchedulingOwner(control);
99
+ settleForegroundSchedulingOwner(control);
100
+ assert.equal(foregroundSchedulingSettled(control), false);
101
+ settleForegroundSchedulingOwner(control);
102
+ assert.equal(foregroundSchedulingSettled(control), true);
103
+ assert.equal(control.schedulingOwners, 0);
64
104
  });
65
105
  });
@@ -60,7 +60,7 @@ describe("subagent extension child mode", () => {
60
60
  execFileSync(
61
61
  process.execPath,
62
62
  [
63
- "--experimental-transform-types",
63
+ "--experimental-strip-types",
64
64
  "--import",
65
65
  "./test/support/register-loader.mjs",
66
66
  "--input-type=module",
@@ -104,7 +104,7 @@ describe("subagent extension child mode", () => {
104
104
  execFileSync(
105
105
  process.execPath,
106
106
  [
107
- "--experimental-transform-types",
107
+ "--experimental-strip-types",
108
108
  "--import",
109
109
  "./test/support/register-loader.mjs",
110
110
  "--input-type=module",
@@ -155,7 +155,7 @@ describe("subagent extension child mode", () => {
155
155
  execFileSync(
156
156
  process.execPath,
157
157
  [
158
- "--experimental-transform-types",
158
+ "--experimental-strip-types",
159
159
  "--import",
160
160
  "./test/support/register-loader.mjs",
161
161
  "--input-type=module",
@@ -207,7 +207,7 @@ describe("subagent extension child mode", () => {
207
207
  const output = execFileSync(
208
208
  process.execPath,
209
209
  [
210
- "--experimental-transform-types",
210
+ "--experimental-strip-types",
211
211
  "--import",
212
212
  "./test/support/register-loader.mjs",
213
213
  "--input-type=module",
@@ -328,7 +328,7 @@ describe("subagent extension child mode", () => {
328
328
  env.SELESAI_CODING_AGENT_DIR = agentDir;
329
329
  execFileSync(
330
330
  process.execPath,
331
- ["--experimental-transform-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
331
+ ["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
332
332
  { cwd: projectRoot, env, stdio: "pipe" },
333
333
  );
334
334
  } finally {
@@ -422,7 +422,7 @@ describe("subagent extension child mode", () => {
422
422
  env.SELESAI_CODING_AGENT_DIR = agentDir;
423
423
  execFileSync(
424
424
  process.execPath,
425
- ["--experimental-transform-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
425
+ ["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
426
426
  { cwd: projectRoot, env, stdio: "pipe" },
427
427
  );
428
428
  } finally {
@@ -458,7 +458,7 @@ describe("subagent extension child mode", () => {
458
458
  execFileSync(
459
459
  process.execPath,
460
460
  [
461
- "--experimental-transform-types",
461
+ "--experimental-strip-types",
462
462
  "--import",
463
463
  "./test/support/register-loader.mjs",
464
464
  "--input-type=module",
@@ -493,7 +493,7 @@ describe("subagent extension child mode", () => {
493
493
  execFileSync(
494
494
  process.execPath,
495
495
  [
496
- "--experimental-transform-types",
496
+ "--experimental-strip-types",
497
497
  "--import",
498
498
  "./test/support/register-loader.mjs",
499
499
  "--input-type=module",
@@ -529,7 +529,7 @@ describe("subagent extension child mode", () => {
529
529
  execFileSync(
530
530
  process.execPath,
531
531
  [
532
- "--experimental-transform-types",
532
+ "--experimental-strip-types",
533
533
  "--import",
534
534
  "./test/support/register-loader.mjs",
535
535
  "--input-type=module",
@@ -574,7 +574,7 @@ describe("subagent extension child mode", () => {
574
574
  execFileSync(
575
575
  process.execPath,
576
576
  [
577
- "--experimental-transform-types",
577
+ "--experimental-strip-types",
578
578
  "--import",
579
579
  "./test/support/register-loader.mjs",
580
580
  "--input-type=module",
@@ -599,14 +599,19 @@ describe("subagent extension child mode", () => {
599
599
  };
600
600
  registerFanoutChildSubagentExtension(fakePi);
601
601
  if (!registeredTool) throw new Error("tool not registered");
602
+ if (registeredTool.promptGuidelines !== undefined) {
603
+ throw new Error("child-safe fanout tool must not carry parent-only routing promptGuidelines");
604
+ }
602
605
  const ctx = {
603
606
  cwd: process.cwd(),
604
607
  hasUI: false,
605
608
  sessionManager: { getSessionId() { return "session-test"; }, getSessionFile() { return null; } },
606
609
  modelRegistry: { getAvailable() { return []; } },
607
610
  };
608
- const list = await registeredTool.execute("list-check", { action: "list" }, new AbortController().signal, undefined, ctx);
611
+ const list = await registeredTool.execute("list-check", { action: "list", task: "Review only; do not edit files" }, new AbortController().signal, undefined, ctx);
609
612
  if (list.isError) throw new Error("list should be allowed: " + JSON.stringify(list.content));
613
+ const listText = list.content?.[0]?.text ?? "";
614
+ if (!listText.includes("Task-aware advisory routing:")) throw new Error("list task advice missing in child mode: " + listText.slice(0, 200));
610
615
  const create = await registeredTool.execute("create-check", { action: "create", config: { name: "x" } }, new AbortController().signal, undefined, ctx);
611
616
  if (!create.isError) throw new Error("create should be blocked");
612
617
  const text = create.content?.[0]?.text ?? "";
@@ -620,7 +625,7 @@ describe("subagent extension child mode", () => {
620
625
  execFileSync(
621
626
  process.execPath,
622
627
  [
623
- "--experimental-transform-types",
628
+ "--experimental-strip-types",
624
629
  "--import",
625
630
  "./test/support/register-loader.mjs",
626
631
  "--input-type=module",
@@ -44,11 +44,15 @@ describe("resolveIntercomBridgeMode", () => {
44
44
 
45
45
  describe("resolveIntercomSessionTarget", () => {
46
46
  it("prefers an explicit session name", () => {
47
- assert.equal(resolveIntercomSessionTarget("planner", "session-12345678"), "planner");
47
+ assert.equal(resolveIntercomSessionTarget("planner", "session-12345678", "session-stableabcdef"), "planner");
48
48
  });
49
49
 
50
50
  it("uses a runtime-only subagent chat alias when unnamed", () => {
51
- assert.equal(resolveIntercomSessionTarget(undefined, "session-12345678"), "subagent-chat-12345678");
51
+ assert.equal(resolveIntercomSessionTarget(undefined, "session-12345678", ""), "subagent-chat-12345678");
52
+ });
53
+
54
+ it("uses the current pi-intercom runtime id for unnamed fallback when present", () => {
55
+ assert.equal(resolveIntercomSessionTarget(undefined, "session-12345678", "session-stableabcdef"), "subagent-chat-stableab");
52
56
  });
53
57
  });
54
58
 
@@ -94,10 +98,21 @@ describe("resolveIntercomBridge", () => {
94
98
  });
95
99
 
96
100
  assert.equal(bridge.active, true);
101
+ assert.equal(bridge.resultDelivery, true);
97
102
  assert.equal(bridge.orchestratorTarget, "main");
98
103
  assert.equal(bridge.extensionDir, NATIVE_INTERCOM_EXTENSION_DIR);
99
104
  });
100
105
 
106
+ it("can disable external grouped-result delivery without disabling supervisor coordination", () => {
107
+ const bridge = resolveIntercomBridge({
108
+ config: { mode: "always", resultDelivery: false },
109
+ context: "fresh",
110
+ orchestratorTarget: "main",
111
+ });
112
+ assert.equal(bridge.active, true);
113
+ assert.equal(bridge.resultDelivery, false);
114
+ });
115
+
101
116
  it("stays inactive for fresh context when mode is fork-only", () => {
102
117
  const bridge = resolveIntercomBridge({
103
118
  config: { mode: "fork-only" },
@@ -144,6 +159,7 @@ describe("applyIntercomBridgeToAgent", () => {
144
159
  const activeBridge: IntercomBridgeState = {
145
160
  active: true,
146
161
  mode: "always",
162
+ resultDelivery: true,
147
163
  orchestratorTarget: "main",
148
164
  extensionDir: NATIVE_INTERCOM_EXTENSION_DIR,
149
165
  instruction: "Intercom orchestration channel:\n- Need a decision or blocked: contact_supervisor({ reason: \"need_decision\", message: \"<question>\" })\n- Blocked/update: contact_supervisor({ reason: \"progress_update\", message: \"UPDATE: <summary>\" })",
@@ -5,7 +5,7 @@ import * as path from "node:path";
5
5
  import { afterEach, describe, it } from "node:test";
6
6
  import registerFanoutChildSubagentExtension from "../../src/extension/fanout-child.ts";
7
7
  import { createSubagentExecutor } from "../../src/runs/foreground/subagent-executor.ts";
8
- import { createNestedRoute, projectNestedEvents, readNestedControlRequests, readNestedControlResults, writeNestedControlRequest, writeNestedControlResult, writeNestedEvent } from "../../src/runs/shared/nested-events.ts";
8
+ import { createNestedRoute, findNestedControlResult, projectNestedEvents, readNestedControlRequests, readNestedControlResults, snapshotNestedEventFiles, writeNestedControlRequest, writeNestedControlResult, writeNestedEvent } from "../../src/runs/shared/nested-events.ts";
9
9
  import {
10
10
  SUBAGENT_CHILD_ENV,
11
11
  SUBAGENT_FANOUT_CHILD_ENV,
@@ -129,6 +129,25 @@ async function waitFor(predicate: () => boolean, timeoutMs = 1_000): Promise<voi
129
129
  }
130
130
 
131
131
  describe("nested control routing", () => {
132
+ it("finds a control result without considering files present before its request", () => {
133
+ const route = createNestedRoute("root-targeted-result");
134
+ routeRoots.push(path.dirname(route.eventSink));
135
+ const requestedAt = Date.now();
136
+ writeNestedControlResult(route, { ts: requestedAt, requestId: "older", targetRunId: "child", ok: true, message: "older result" });
137
+ writeNestedEvent(route, {
138
+ type: "subagent.nested.updated",
139
+ ts: requestedAt + 1,
140
+ parentRunId: route.rootRunId,
141
+ child: { id: "child", parentRunId: route.rootRunId, depth: 1, path: [], state: "running" },
142
+ });
143
+ const ignoredFiles = snapshotNestedEventFiles(route);
144
+ writeNestedControlResult(route, { ts: requestedAt - 1, requestId: "target", targetRunId: "child", ok: true, message: "target result" });
145
+
146
+ assert.equal(findNestedControlResult(route, "target", "child", ignoredFiles)?.message, "target result");
147
+ assert.equal(findNestedControlResult(route, "older", "child", ignoredFiles), undefined);
148
+ assert.equal(readNestedControlResults(route).length, 2);
149
+ });
150
+
132
151
  it("routes interrupt to an explicit nested id through the control inbox", async () => {
133
152
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-nested-control-"));
134
153
  try {
@@ -157,14 +157,18 @@ describe("nested event parsing and projection", () => {
157
157
  ts: 200,
158
158
  parentRunId: "root-run",
159
159
  parentStepIndex: 1,
160
- child: { ...child("nested-a", "running", 200), currentTool: "read" },
160
+ child: {
161
+ ...child("nested-a", "running", 200),
162
+ currentTool: "read",
163
+ runtimeAcknowledgedExtensions: { version: 1, source: "child-runtime", ids: ["ext.ok", "bad/path", "ext.ok"], omitted: 1 },
164
+ },
161
165
  });
162
166
  writeNestedEvent(route, {
163
167
  type: "subagent.nested.completed",
164
168
  ts: 300,
165
169
  parentRunId: "root-run",
166
170
  parentStepIndex: 1,
167
- child: child("nested-a", "complete", 300),
171
+ child: { ...child("nested-a", "complete", 300), runtimeAcknowledgedExtensions: { version: 1, source: "child-runtime", ids: ["ext.ok", "bad/path", "ext.ok"], omitted: 1 } },
168
172
  });
169
173
 
170
174
  const registry = projectNestedEvents(route);
@@ -172,6 +176,12 @@ describe("nested event parsing and projection", () => {
172
176
  assert.equal(registry.children[0]?.id, "nested-a");
173
177
  assert.equal(registry.children[0]?.state, "complete");
174
178
  assert.equal(registry.children[0]?.steps?.[0]?.agent, "leaf");
179
+ assert.deepEqual(registry.children[0]?.runtimeAcknowledgedExtensions, {
180
+ version: 1,
181
+ source: "child-runtime",
182
+ ids: ["ext.ok"],
183
+ omitted: 1,
184
+ });
175
185
 
176
186
  const job: AsyncJobState = {
177
187
  asyncId: "root-run",
@@ -349,6 +359,24 @@ describe("nested event parsing and projection", () => {
349
359
  assert.equal(summary.steps?.[0]?.processTerminal?.reason, "proof-write-failed");
350
360
  });
351
361
 
362
+ it("sanitizes runtime acknowledged extensions in nested status summaries", () => {
363
+ const summary = nestedSummaryFromAsyncStatus({
364
+ runId: "child-run",
365
+ mode: "single",
366
+ state: "complete",
367
+ startedAt: 1,
368
+ runtimeAcknowledgedExtensions: { version: 1, source: "child-runtime", ids: ["/Users/alice/.secret-extension", "ok-ext", "x".repeat(5000)], omitted: 0 } as never,
369
+ steps: [{
370
+ agent: "worker",
371
+ status: "complete",
372
+ runtimeAcknowledgedExtensions: { version: 1, source: "child-runtime", ids: ["C:/Users/alice/secret"], omitted: 0 } as never,
373
+ }],
374
+ }, "/tmp/child-run", { id: "child-run", parentRunId: "parent-run", depth: 1, mode: "single", ts: 2 });
375
+
376
+ assert.deepEqual(summary.runtimeAcknowledgedExtensions?.ids, ["ok-ext"]);
377
+ assert.equal(summary.steps?.[0]?.runtimeAcknowledgedExtensions, undefined);
378
+ });
379
+
352
380
  it("removes evicted status files without replaying completed children", () => {
353
381
  const route = trackRoute();
354
382
  for (let index = 0; index < 1000; index++) {
@@ -140,6 +140,12 @@ describe("registerSubagentNotify", () => {
140
140
  assert.equal(sent.length, 1);
141
141
  });
142
142
 
143
+ it("suppresses local delivery after an acknowledged grouped intercom relay", async () => {
144
+ const { notifier, sent } = createPi("session-a");
145
+ assert.equal(await notifier.deliver(completionResult({ id: "intercom-delivered", intercomDelivered: true })), true);
146
+ assert.equal(sent.length, 0);
147
+ });
148
+
143
149
  it("rejects a pending batch when the notifier is disposed", async () => {
144
150
  const clock = createFakeClock();
145
151
  const { notifier, sent } = createBatchingPi(clock);
@@ -466,10 +472,12 @@ describe("completion formatting helpers", () => {
466
472
  notifier.dispose();
467
473
  });
468
474
 
469
- it("buildCompletionDetails derives paused status from state and summary", () => {
475
+ it("buildCompletionDetails derives paused and stopped statuses", () => {
470
476
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, state: "paused", summary: "Paused after interrupt.", timestamp: 1 }).status, "paused");
471
477
  assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "boom", exitCode: 1, timestamp: 1 }).status, "failed");
472
- assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: true, summary: "ok", exitCode: 0, timestamp: 1 }).status, "completed");
478
+ assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", exitCode: 1, processSignal: "SIGTERM", timestamp: 1 }).status, "stopped");
479
+ assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", results: [{ success: false, exitCode: 1, processSignal: "SIGTERM" }], timestamp: 1 }).status, "stopped");
480
+ assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: true, summary: "ok", exitCode: 0, processSignal: "SIGTERM", timestamp: 1 }).status, "completed");
473
481
  });
474
482
 
475
483
  it("buildCompletionDetails falls back to the unknown agent label", () => {
@@ -477,4 +485,33 @@ describe("completion formatting helpers", () => {
477
485
  assert.equal(details.agent, "unknown");
478
486
  assert.equal(details.status, "completed");
479
487
  });
488
+
489
+ it("buildCompletionDetails prefers compact child summaries over the run summary", () => {
490
+ const details = buildCompletionDetails({
491
+ id: "x",
492
+ agent: "worker",
493
+ success: true,
494
+ summary: "alpha:\nfull child prose that must not leak",
495
+ timestamp: 1,
496
+ results: [
497
+ { agent: "alpha", status: "completed", summary: "Output saved to: /tmp/alpha.md (12 B, 1 line). Read this file if needed." },
498
+ { agent: "beta", status: "failed", summary: "boom\n\nOutput saved to: /tmp/beta.md (5 B, 1 line). Read this file if needed." },
499
+ ],
500
+ });
501
+ assert.match(details.resultPreview, /1\. alpha\nOutput saved to: \/tmp\/alpha\.md/);
502
+ assert.match(details.resultPreview, /2\. beta\nboom\n\nOutput saved to: \/tmp\/beta\.md/);
503
+ assert.doesNotMatch(details.resultPreview, /full child prose that must not leak/);
504
+ });
505
+
506
+ it("buildCompletionDetails uses the single child summary without an index prefix", () => {
507
+ const details = buildCompletionDetails({
508
+ id: "x",
509
+ agent: "worker",
510
+ success: true,
511
+ summary: "ignored run summary",
512
+ timestamp: 1,
513
+ results: [{ agent: "worker", status: "completed", summary: "Output saved to: /tmp/out.md (8 B, 1 line). Read this file if needed." }],
514
+ });
515
+ assert.equal(details.resultPreview, "Output saved to: /tmp/out.md (8 B, 1 line). Read this file if needed.");
516
+ });
480
517
  });
@@ -153,6 +153,28 @@ describe("mapConcurrent", () => {
153
153
  assert.ok(d2 < 20, `worker 2 should start immediately, got ${d2}ms delay`);
154
154
  });
155
155
 
156
+ it("notifies scheduling settlement only after workers outliving an early rejection stop", async () => {
157
+ let release!: () => void;
158
+ const gate = new Promise<void>((resolve) => { release = resolve; });
159
+ const started: number[] = [];
160
+ let schedulingSettled = false;
161
+ const run = mapConcurrent([0, 1, 2], 2, async (item) => {
162
+ started.push(item);
163
+ if (item === 0) throw new Error("early rejection");
164
+ await gate;
165
+ return item;
166
+ }, undefined, () => { schedulingSettled = true; });
167
+
168
+ await assert.rejects(run, /early rejection/);
169
+ assert.equal(schedulingSettled, false);
170
+ release();
171
+ for (let attempt = 0; attempt < 20 && !schedulingSettled; attempt++) {
172
+ await new Promise((resolve) => setTimeout(resolve, 5));
173
+ }
174
+ assert.equal(schedulingSettled, true);
175
+ assert.deepEqual(started, [0, 1, 2]);
176
+ });
177
+
156
178
  it("respects a shared global semaphore across simultaneous calls", async () => {
157
179
  const globalSemaphore = new Semaphore(2);
158
180
  let running = 0;