@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
@@ -1,13 +1,16 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
3
4
  import { getMarkdownTheme, type ExtensionContext } from "@selesai/code";
4
5
  import { Key, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component, type MarkdownTheme } from "@earendil-works/pi-tui";
5
6
  import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
6
- import { formatDuration, formatTokens, shortenPath } from "../shared/formatters.ts";
7
- import { RESULTS_DIR, type AsyncJobState, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
7
+ import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../shared/formatters.ts";
8
+ import { RESULTS_DIR, type AsyncJobState, type Details, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
8
9
  import { readStatus } from "../shared/utils.ts";
9
10
  import { formatAsyncRunTranscript } from "../runs/background/fleet-view.ts";
10
11
  import { listAsyncRuns, type AsyncRunSummary } from "../runs/background/async-status.ts";
12
+ import { steerAsyncRun } from "../runs/foreground/async-steering-action.ts";
13
+ import { stopAsyncRun } from "../runs/foreground/async-stop-action.ts";
11
14
  import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.ts";
12
15
  import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
13
16
  import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
@@ -34,12 +37,23 @@ export interface FleetSnapshot {
34
37
  error?: string;
35
38
  }
36
39
 
40
+ export interface FleetActionResult {
41
+ text: string;
42
+ isError?: boolean;
43
+ }
44
+
45
+ export interface FleetActionHandlers {
46
+ steer(input: { runId: string; asyncDir: string; index?: number; message: string }): Promise<FleetActionResult>;
47
+ stop(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult> | FleetActionResult;
48
+ }
49
+
37
50
  export interface FleetViewOptions {
38
51
  asyncDirRoot?: string;
39
52
  resultsDir?: string;
40
53
  refreshMs?: number;
41
54
  initialKey?: string;
42
55
  markdownTheme?: MarkdownTheme;
56
+ actions?: FleetActionHandlers;
43
57
  }
44
58
 
45
59
  function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
@@ -209,12 +223,14 @@ function statusGlyph(item: FleetItem, theme: Theme): string {
209
223
  function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-active" }>): string[] {
210
224
  const { control } = item;
211
225
  const live = item.activeChild ?? control;
226
+ const modelThinking = formatModelThinking(live.model, live.thinking);
212
227
  const lines = [
213
228
  `Run: ${item.runId}`,
214
229
  "Source: foreground",
215
230
  `State: running`,
216
231
  `Mode: ${control.mode}`,
217
232
  item.index !== undefined ? `Child: ${item.index} (${item.agent})` : `Agent: ${item.agent}`,
233
+ modelThinking ? `Model: ${modelThinking}` : undefined,
218
234
  `Started: ${new Date(live.startedAt).toISOString()}`,
219
235
  live.currentTool ? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : ""}` : undefined,
220
236
  live.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,
@@ -230,12 +246,14 @@ function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-act
230
246
  function foregroundRecentDetail(item: Extract<FleetItem, { kind: "foreground-recent" }>): string[] {
231
247
  const { child, run } = item;
232
248
  const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
249
+ const modelThinking = formatModelThinking(child.model, child.thinking);
233
250
  const lines = [
234
251
  `Run: ${item.runId}`,
235
252
  "Source: foreground",
236
253
  `State: ${child.status}`,
237
254
  `Mode: ${run.mode}`,
238
255
  `Child: ${child.index} (${child.agent})${contextModeLabel(child.context) ? ` ${contextModeLabel(child.context)}` : ""}`,
256
+ modelThinking ? `Model: ${modelThinking}` : undefined,
239
257
  `Updated: ${new Date(child.updatedAt ?? run.updatedAt).toISOString()}`,
240
258
  outputPath ? `Output: ${outputPath}` : undefined,
241
259
  child.sessionFile ? `Session: ${child.sessionFile}` : undefined,
@@ -282,6 +300,16 @@ function detailLines(item: FleetItem | undefined, error: string | undefined): st
282
300
  return lines;
283
301
  }
284
302
 
303
+ function isActionableAsyncState(state: string): boolean {
304
+ return state === "running" || state === "queued" || state === "pending";
305
+ }
306
+
307
+ function firstToolResultText(result: AgentToolResult<Details> | null, fallback: string): FleetActionResult {
308
+ if (!result) return { text: fallback, isError: true };
309
+ const text = result.content.find((item) => item.type === "text")?.text ?? fallback;
310
+ return { text, ...(result.isError ? { isError: true } : {}) };
311
+ }
312
+
285
313
  function uniquePaths(values: Array<string | undefined>): string[] {
286
314
  return [...new Set(values.filter((value): value is string => Boolean(value)).map((value) => path.resolve(value)))];
287
315
  }
@@ -353,10 +381,12 @@ function itemStats(item: FleetItem): string[] {
353
381
  let durationMs: number | undefined;
354
382
  if (item.kind === "foreground-active") {
355
383
  const live = item.activeChild ?? item.control;
384
+ model = formatModelThinking(live.model, live.thinking) || undefined;
356
385
  tokens = live.tokens;
357
386
  tools = live.toolCount;
358
387
  durationMs = Math.max(0, Date.now() - live.startedAt);
359
388
  } else if (item.kind === "foreground-recent") {
389
+ model = formatModelThinking(item.child.model, item.child.thinking) || undefined;
360
390
  tokens = item.child.tokens;
361
391
  tools = item.child.toolCount;
362
392
  } else {
@@ -436,6 +466,10 @@ export class SubagentFleetComponent implements Component {
436
466
  private detailViewportHeight = 8;
437
467
  private bodyHeight = 8;
438
468
  private expandedTools = false;
469
+ private actionNotice: FleetActionResult | undefined;
470
+ private steerDraft: string | undefined;
471
+ private stopConfirming = false;
472
+ private actionBusy = false;
439
473
  private transcriptCache: FleetTranscriptCache | undefined;
440
474
  private disposed = false;
441
475
  private readonly timer: ReturnType<typeof setInterval>;
@@ -463,7 +497,7 @@ export class SubagentFleetComponent implements Component {
463
497
  this.refresh();
464
498
  this.timer = setInterval(() => {
465
499
  if (this.disposed) return;
466
- this.refresh();
500
+ this.invalidate();
467
501
  this.tui.requestRender();
468
502
  }, options.refreshMs ?? REFRESH_MS);
469
503
  this.timer.unref?.();
@@ -482,9 +516,67 @@ export class SubagentFleetComponent implements Component {
482
516
  this.selected = Math.max(0, Math.min(this.snapshot.items.length - 1, this.selected + delta));
483
517
  this.selectedKey = this.snapshot.items[this.selected]?.key;
484
518
  this.detailAutoFollow = true;
519
+ this.resetActionInput();
520
+ this.tui.requestRender();
521
+ }
522
+
523
+ private resetActionInput(): void {
524
+ this.steerDraft = undefined;
525
+ this.stopConfirming = false;
526
+ }
527
+
528
+ private selectedAsyncAction(): { item: Extract<FleetItem, { kind: "async" }> } | { reason: string } {
529
+ const item = this.snapshot.items[this.selected];
530
+ if (!item) return { reason: "No child is selected." };
531
+ if (item.kind !== "async") return { reason: "Fleet controls are available for current-session top-level async runs only." };
532
+ if (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state)) return { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };
533
+ return { item };
534
+ }
535
+
536
+ private actionLines(): string[] {
537
+ const lines: string[] = [];
538
+ if (this.actionBusy) lines.push(this.theme.fg("accent", "Action pending..."));
539
+ if (this.steerDraft !== undefined) {
540
+ lines.push(this.theme.fg("accent", `Steer message: ${this.steerDraft}${this.theme.fg("dim", "▌")}`));
541
+ lines.push(this.theme.fg("dim", "Enter sends · Esc cancels · Backspace edits"));
542
+ } else if (this.stopConfirming) {
543
+ const selected = this.snapshot.items[this.selected];
544
+ lines.push(this.theme.fg("warning", `Confirm stop for async run ${selected?.runId ?? "selected run"}?`));
545
+ lines.push(this.theme.fg("dim", "Stop ends the run; use interrupt for a resumable pause. Enter/Y confirms · N returns · Esc cancels"));
546
+ } else if (this.actionNotice) {
547
+ lines.push(this.theme.fg(this.actionNotice.isError ? "error" : "success", this.actionNotice.text));
548
+ }
549
+ return lines;
550
+ }
551
+
552
+ private withActionLines(body: string[]): string[] {
553
+ const actionLines = this.actionLines();
554
+ return actionLines.length ? [...actionLines, "", ...body] : body;
555
+ }
556
+
557
+ private setActionNotice(result: FleetActionResult): void {
558
+ this.actionNotice = result;
559
+ this.resetActionInput();
560
+ this.detailAutoFollow = false;
561
+ this.detailScroll = 0;
562
+ this.refresh();
485
563
  this.tui.requestRender();
486
564
  }
487
565
 
566
+ private runAction(action: () => Promise<FleetActionResult>): void {
567
+ if (this.actionBusy) return;
568
+ this.actionBusy = true;
569
+ this.actionNotice = undefined;
570
+ this.tui.requestRender();
571
+ void action()
572
+ .then((result) => this.setActionNotice(result))
573
+ .catch((error) => this.setActionNotice({ text: error instanceof Error ? error.message : String(error), isError: true }))
574
+ .finally(() => {
575
+ this.actionBusy = false;
576
+ if (!this.disposed) this.tui.requestRender();
577
+ });
578
+ }
579
+
488
580
  private scrollDetail(delta: number): void {
489
581
  const maxScroll = Math.max(0, this.detailLineCount - this.detailViewportHeight);
490
582
  this.detailScroll = Math.max(0, Math.min(maxScroll, this.detailScroll + delta));
@@ -493,6 +585,53 @@ export class SubagentFleetComponent implements Component {
493
585
  }
494
586
 
495
587
  handleInput(data: string): void {
588
+ if (this.steerDraft !== undefined) {
589
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
590
+ this.resetActionInput();
591
+ this.tui.requestRender();
592
+ return;
593
+ }
594
+ if (matchesKey(data, "return") || data === "\r" || data === "\n") {
595
+ const message = this.steerDraft.trim();
596
+ if (!message) {
597
+ this.setActionNotice({ text: "Steer message cannot be empty.", isError: true });
598
+ return;
599
+ }
600
+ const target = this.selectedAsyncAction();
601
+ if ("reason" in target || !this.options.actions) {
602
+ this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
603
+ return;
604
+ }
605
+ this.runAction(() => this.options.actions!.steer({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}), message }));
606
+ return;
607
+ }
608
+ if (matchesKey(data, "backspace") || data === "\x7f") {
609
+ this.steerDraft = this.steerDraft.slice(0, -1);
610
+ this.tui.requestRender();
611
+ return;
612
+ }
613
+ if (data.length === 1 && data >= " " && data !== "\x7f") {
614
+ this.steerDraft += data;
615
+ this.tui.requestRender();
616
+ }
617
+ return;
618
+ }
619
+ if (this.stopConfirming) {
620
+ if (matchesKey(data, "return") || data.toLowerCase() === "y") {
621
+ const target = this.selectedAsyncAction();
622
+ if ("reason" in target || !this.options.actions) {
623
+ this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
624
+ return;
625
+ }
626
+ this.runAction(() => Promise.resolve(this.options.actions!.stop({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}) })));
627
+ return;
628
+ }
629
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || data.toLowerCase() === "n" || matchesKey(data, "backspace")) {
630
+ this.resetActionInput();
631
+ this.tui.requestRender();
632
+ }
633
+ return;
634
+ }
496
635
  if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "q")) {
497
636
  this.done(undefined);
498
637
  return;
@@ -511,6 +650,30 @@ export class SubagentFleetComponent implements Component {
511
650
  this.tui.requestRender();
512
651
  return;
513
652
  }
653
+ if (data === "s") {
654
+ const target = this.selectedAsyncAction();
655
+ if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
656
+ else {
657
+ this.actionNotice = undefined;
658
+ this.steerDraft = "";
659
+ this.detailAutoFollow = false;
660
+ this.detailScroll = 0;
661
+ this.tui.requestRender();
662
+ }
663
+ return;
664
+ }
665
+ if (data === "D") {
666
+ const target = this.selectedAsyncAction();
667
+ if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
668
+ else {
669
+ this.actionNotice = undefined;
670
+ this.stopConfirming = true;
671
+ this.detailAutoFollow = false;
672
+ this.detailScroll = 0;
673
+ this.tui.requestRender();
674
+ }
675
+ return;
676
+ }
514
677
  if (data.toLowerCase() === "x" || matchesKey(data, "ctrl+o")) {
515
678
  this.expandedTools = !this.expandedTools;
516
679
  this.transcriptCache = undefined;
@@ -567,7 +730,7 @@ export class SubagentFleetComponent implements Component {
567
730
  : latest?.kind === "tool"
568
731
  ? `${latest.name} · ${latest.status}`
569
732
  : "activity";
570
- return { header: structuredHeader(selected, width, this.theme, conversationState), body };
733
+ return { header: structuredHeader(selected, width, this.theme, conversationState), body: this.withActionLines(body) };
571
734
  }
572
735
  }
573
736
  }
@@ -576,7 +739,7 @@ export class SubagentFleetComponent implements Component {
576
739
  if (transcriptWarning) raw.unshift(`Transcript preview warning: ${transcriptWarning}`, "");
577
740
  const lines: string[] = [];
578
741
  for (const line of raw) {
579
- const styled = /^(Run|State|Mode|Source|Child|Agent):/.test(line)
742
+ const styled = /^(Run|State|Mode|Source|Child|Agent|Model):/.test(line)
580
743
  ? this.theme.bold(line)
581
744
  : /^(Transcript|Result transcript tail)/.test(line)
582
745
  ? this.theme.fg("accent", line)
@@ -588,7 +751,7 @@ export class SubagentFleetComponent implements Component {
588
751
  const wrapped = wrapTextWithAnsi(styled, Math.max(1, width));
589
752
  lines.push(...(wrapped.length ? wrapped : [""]));
590
753
  }
591
- return { header: [], body: lines };
754
+ return { header: [], body: this.withActionLines(lines) };
592
755
  }
593
756
 
594
757
  render(width: number): string[] {
@@ -612,7 +775,7 @@ export class SubagentFleetComponent implements Component {
612
775
  ];
613
776
  const lines = [this.theme.fg("border", `╭${"─".repeat(innerWidth)}╮`)];
614
777
  const selected = this.snapshot.items[this.selected];
615
- const title = ` ${this.theme.bold("Subagent fleet inspector")} ${this.theme.fg("dim", "· inspection only · live")}`;
778
+ const title = ` ${this.theme.bold("Subagent fleet inspector")} ${this.theme.fg("dim", "· live controls")}`;
616
779
  const selectedStatus = selected
617
780
  ? `${statusGlyph(selected, this.theme)} ${selected.agent} · ${selected.state} `
618
781
  : this.theme.fg("dim", "no children ");
@@ -629,7 +792,7 @@ export class SubagentFleetComponent implements Component {
629
792
  }
630
793
  lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┴${"─".repeat(detailWidth)}┤`));
631
794
  const position = this.snapshot.items.length ? `${this.selected + 1}/${this.snapshot.items.length}` : "0/0";
632
- const footer = ` ↑↓/jk agent · ⇧k/⇧j scroll · PgUp/PgDn page · x/Ctrl+O tools · r refresh · Esc close · ${position}`;
795
+ const footer = ` ↑↓/jk agent · s steer · D stop · x/Ctrl+O tools · r refresh · Esc close · ${position}`;
633
796
  lines.push(this.theme.fg("border", "│") + fit(this.theme.fg("dim", footer), innerWidth) + this.theme.fg("border", "│"));
634
797
  lines.push(this.theme.fg("border", `╰${"─".repeat(innerWidth)}╯`));
635
798
  return lines.map((line) => truncateToWidth(line, width));
@@ -650,9 +813,19 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
650
813
  const wasOpen = state.fleetInspectorOpen === true;
651
814
  state.fleetInspectorOpen = true;
652
815
  if (typeof ctx.ui.setWidget === "function") ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);
816
+ const actions = options.actions ?? {
817
+ steer: async (input: { runId: string; asyncDir: string; index?: number; message: string }) => firstToolResultText(await steerAsyncRun({
818
+ state,
819
+ runId: input.runId,
820
+ ...(input.index !== undefined ? { index: input.index } : {}),
821
+ message: input.message,
822
+ location: { asyncDir: input.asyncDir, resolvedId: input.runId },
823
+ }), `Failed to steer async run ${input.runId}.`),
824
+ stop: (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(stopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }), `Failed to stop async run ${input.runId}.`),
825
+ } satisfies FleetActionHandlers;
653
826
  try {
654
827
  await ctx.ui.custom<undefined>(
655
- (tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, options),
828
+ (tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, { ...options, actions }),
656
829
  );
657
830
  } finally {
658
831
  state.fleetInspectorOpen = wasOpen;
@@ -2,6 +2,7 @@
2
2
  * Rendering functions for subagent results
3
3
  */
4
4
 
5
+ import * as fs from "node:fs";
5
6
  import * as path from "node:path";
6
7
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
7
8
  import { getMarkdownTheme, keyText, type ExtensionContext } from "@selesai/code";
@@ -27,6 +28,27 @@ import { contextModeBadge, contextModePrefix } from "../runs/shared/context-mode
27
28
 
28
29
  type Theme = ExtensionContext["ui"]["theme"];
29
30
 
31
+ /**
32
+ * UI-side output projection: completed terminal results strip `finalOutput`/
33
+ * `truncation` when an authoritative saved output path exists (see
34
+ * compactForegroundResult). The UI recovers the full text from that path so
35
+ * widgets keep showing output without reintroducing it into model-facing
36
+ * details.
37
+ */
38
+ function resultOutputForUi(r: Details["results"][number]): string {
39
+ const output = r.truncation?.text || getSingleResultOutput(r);
40
+ if (output) return output;
41
+ if (r.savedOutputPath) {
42
+ try {
43
+ const content = fs.readFileSync(r.savedOutputPath, "utf-8").trim();
44
+ return content || output;
45
+ } catch {
46
+ return output;
47
+ }
48
+ }
49
+ return output;
50
+ }
51
+
30
52
  function liveDetailKeyText(): string {
31
53
  return keyText("app.tools.expand");
32
54
  }
@@ -40,6 +62,7 @@ function getTermWidth(): number {
40
62
  }
41
63
 
42
64
  const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
65
+ const ansiStylePattern = /\x1b\[[0-9;]*m/y;
43
66
 
44
67
  /**
45
68
  * Truncate a line to maxWidth, preserving ANSI styling through the ellipsis.
@@ -50,7 +73,7 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
50
73
  *
51
74
  * Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
52
75
  */
53
- function truncLine(text: string, maxWidth: number): string {
76
+ export function truncLine(text: string, maxWidth: number): string {
54
77
  if (visibleWidth(text) <= maxWidth) return text;
55
78
 
56
79
  const targetWidth = maxWidth - 1;
@@ -60,7 +83,8 @@ function truncLine(text: string, maxWidth: number): string {
60
83
  let i = 0;
61
84
 
62
85
  while (i < text.length) {
63
- const ansiMatch = text.slice(i).match(/^\x1b\[[0-9;]*m/);
86
+ ansiStylePattern.lastIndex = i;
87
+ const ansiMatch = ansiStylePattern.exec(text);
64
88
  if (ansiMatch) {
65
89
  const code = ansiMatch[0];
66
90
  result += code;
@@ -74,11 +98,9 @@ function truncLine(text: string, maxWidth: number): string {
74
98
  continue;
75
99
  }
76
100
 
77
- let end = i;
78
- while (end < text.length && !text.slice(end).match(/^\x1b\[[0-9;]*m/)) {
79
- end++;
80
- }
81
-
101
+ let end = text.indexOf("\x1b[", i);
102
+ if (end === i) end = text.indexOf("\x1b[", i + 2);
103
+ if (end === -1) end = text.length;
82
104
  const textPortion = text.slice(i, end);
83
105
  for (const seg of segmenter.segment(textPortion)) {
84
106
  const grapheme = seg.segment;
@@ -529,17 +551,15 @@ function buildChainStepSpans(details: Pick<Details, "chainAgents" | "workflowGra
529
551
  return spans;
530
552
  }
531
553
 
532
- function isChainParallelGroupActive(details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex" | "workflowGraph">): boolean {
533
- if (details.mode !== "chain") return false;
534
- if (details.currentStepIndex === undefined) return false;
535
- return buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
536
- }
537
-
538
554
  function buildAsyncChainStepSpans(total: number, stepCount: number, parallelGroups: AsyncParallelGroupStatus[] = []): ChainStepSpan[] {
555
+ const groupsByStep = new Map<number, AsyncParallelGroupStatus>();
556
+ for (const group of parallelGroups) {
557
+ if (!groupsByStep.has(group.stepIndex)) groupsByStep.set(group.stepIndex, group);
558
+ }
539
559
  const spans: ChainStepSpan[] = [];
540
560
  let flatIndex = 0;
541
561
  for (let stepIndex = 0; stepIndex < total; stepIndex++) {
542
- const group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
562
+ const group = groupsByStep.get(stepIndex);
543
563
  if (group) {
544
564
  spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
545
565
  flatIndex = Math.max(flatIndex, group.start + group.count);
@@ -567,6 +587,7 @@ interface ChainRenderResultEntry {
567
587
  kind: "result";
568
588
  resultIndex: number;
569
589
  rowNumber: number;
590
+ rowLabel?: string;
570
591
  agentName: string;
571
592
  }
572
593
 
@@ -601,6 +622,7 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
601
622
  kind: "result",
602
623
  resultIndex: index,
603
624
  rowNumber: index + 1,
625
+ rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
604
626
  agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
605
627
  });
606
628
  }
@@ -622,7 +644,9 @@ interface MultiProgressLabel {
622
644
  function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "progress" | "totalSteps" | "currentStepIndex" | "chainAgents" | "workflowGraph">, hasRunning: boolean): MultiProgressLabel {
623
645
  const stepSpans = buildChainStepSpans(details);
624
646
  const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
625
- const activeParallelGroup = isChainParallelGroupActive(details);
647
+ const activeParallelGroup = details.mode === "chain"
648
+ && details.currentStepIndex !== undefined
649
+ && stepSpans.some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
626
650
  const itemTitle: "Step" | "Agent" = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
627
651
 
628
652
  if (details.mode === "parallel") {
@@ -708,15 +732,10 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
708
732
  return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
709
733
  }
710
734
 
711
- function resultRowLabel(details: Pick<Details, "mode" | "chainAgents" | "workflowGraph">, label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
712
- if (details.mode === "chain" && label.hasParallelInChain) {
713
- const span = buildChainStepSpans(details).find((candidate) => resultIndex >= candidate.start && resultIndex < candidate.start + candidate.count);
714
- if (span?.isParallel) return `Agent ${resultIndex - span.start + 1}/${span.count}`;
715
- if (span) return `Step ${span.stepIndex + 1}`;
716
- }
735
+ function resultRowLabel(label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
717
736
  if (label.itemTitle === "Agent") {
718
737
  const localStepNumber = label.activeParallelGroup
719
- ? Math.max(1, stepNumber - label.groupStartIndex)
738
+ ? resultIndex - label.groupStartIndex + 1
720
739
  : stepNumber;
721
740
  return `Agent ${localStepNumber}/${label.totalCount}`;
722
741
  }
@@ -1311,7 +1330,7 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void
1311
1330
  }
1312
1331
 
1313
1332
  function renderSingleCompact(d: Details, r: Details["results"][number], theme: Theme, frame?: number): Component {
1314
- const output = r.truncation?.text || getSingleResultOutput(r);
1333
+ const output = resultOutputForUi(r);
1315
1334
  const progress = r.progress || r.progressSummary;
1316
1335
  const isRunning = r.progress?.status === "running";
1317
1336
  const contextBadge = contextModeBadge(theme, r.context ?? d.context);
@@ -1321,7 +1340,7 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
1321
1340
  ]);
1322
1341
  const c = new Container();
1323
1342
  const width = getTermWidth() - 4;
1324
- const modelDisplay = modelThinkingBadge(theme, r.model);
1343
+ const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
1325
1344
  c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
1326
1345
 
1327
1346
  if (isRunning && r.progress) {
@@ -1417,11 +1436,11 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
1417
1436
  const rowNumber = entry.rowNumber;
1418
1437
  const agentName = entry.agentName;
1419
1438
  if (!r) {
1420
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1439
+ const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
1421
1440
  c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
1422
1441
  continue;
1423
1442
  }
1424
- const output = getSingleResultOutput(r);
1443
+ const output = resultOutputForUi(r);
1425
1444
  const progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running");
1426
1445
  const rProg = r.progress || progressFromArray || r.progressSummary;
1427
1446
  const rRunning = rProg && "status" in rProg && rProg.status === "running";
@@ -1430,8 +1449,10 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
1430
1449
  const stepStats = formatProgressStats(theme, rProg);
1431
1450
  const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
1432
1451
  const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
1433
- const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
1434
- const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
1452
+ const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
1453
+ const rowProgressModel = rProg && "status" in rProg ? rProg : undefined;
1454
+ const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
1455
+ const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
1435
1456
  c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
1436
1457
  if (rRunning && rProg && "status" in rProg) {
1437
1458
  const activity = compactCurrentActivity(rProg);
@@ -1501,7 +1522,7 @@ export function renderSubagentResult(
1501
1522
  ? theme.fg("success", "ok")
1502
1523
  : theme.fg("error", "failed");
1503
1524
  const contextBadge = contextModeBadge(theme, r.context ?? d.context);
1504
- const output = r.truncation?.text || getSingleResultOutput(r);
1525
+ const output = resultOutputForUi(r);
1505
1526
 
1506
1527
  const progressInfo = isRunning && r.progress
1507
1528
  ? ` | ${r.progress.toolCount} tools, ${formatTokens(r.progress.tokens)} tok, ${formatDuration(r.progress.durationMs)}`
@@ -1601,7 +1622,7 @@ export function renderSubagentResult(
1601
1622
  const hasEmptyWithoutTarget = d.results.some((r) =>
1602
1623
  r.exitCode === 0
1603
1624
  && r.progress?.status !== "running"
1604
- && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)),
1625
+ && hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutputForUi(r)),
1605
1626
  );
1606
1627
  const hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]);
1607
1628
  const hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]);
@@ -1659,7 +1680,7 @@ export function renderSubagentResult(
1659
1680
  const isComplete = result && result.exitCode === 0 && result.progress?.status !== "running";
1660
1681
  const isEmptyWithoutTarget = Boolean(result)
1661
1682
  && Boolean(isComplete)
1662
- && hasEmptyTextOutputWithoutOutputTarget(result.task, getSingleResultOutput(result));
1683
+ && hasEmptyTextOutputWithoutOutputTarget(result.task, resultOutputForUi(result));
1663
1684
  const isCurrent = i === (d.currentStepIndex ?? d.results.length);
1664
1685
  const stepIcon = isFailed
1665
1686
  ? theme.fg("error", "failed")
@@ -1717,7 +1738,7 @@ export function renderSubagentResult(
1717
1738
  const agentName = entry.agentName;
1718
1739
 
1719
1740
  if (!r) {
1720
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1741
+ const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
1721
1742
  c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
1722
1743
  c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
1723
1744
  c.addChild(new Spacer(1));
@@ -1730,7 +1751,7 @@ export function renderSubagentResult(
1730
1751
  const rRunning = rProg?.status === "running";
1731
1752
  const stepNumber = typeof rProg?.index === "number" ? rProg.index + 1 : i + 1;
1732
1753
 
1733
- const resultOutput = getSingleResultOutput(r);
1754
+ const resultOutput = resultOutputForUi(r);
1734
1755
  const statusIcon = rRunning
1735
1756
  ? theme.fg("warning", "running")
1736
1757
  : r.exitCode !== 0
@@ -1740,7 +1761,7 @@ export function renderSubagentResult(
1740
1761
  : theme.fg("success", "done");
1741
1762
  const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
1742
1763
  const modelDisplay = modelThinkingBadge(theme, r.model);
1743
- const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
1764
+ const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
1744
1765
  const contextBadge = contextModeBadge(theme, r.context);
1745
1766
  const stepHeader = rRunning
1746
1767
  ? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${contextBadge}${modelDisplay}${stats}`
@@ -111,6 +111,8 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
111
111
  `Main: ${boolLabel(snapshot.enabled)}${!snapshot.config.enabled && snapshot.sessionOverride === undefined ? " (default off)" : ""}`,
112
112
  `Runtime: ${statusLabel(snapshot.status)}${snapshot.bufferedDeltas > 0 ? ` · buffered deltas ${snapshot.bufferedDeltas}` : ""}`,
113
113
  `Review trigger: ${snapshot.reviewTrigger === "repo-edits" ? "repo edits only" : "every non-empty turn delta"}`,
114
+ `Scope context: ${snapshot.config.scope.enabled ? "on" : "off"}`,
115
+ `Cadence: ${snapshot.config.cadence.everyNTools === null ? "boundary only" : `every ${snapshot.config.cadence.everyNTools} tools + boundary`}`,
114
116
  lspLine(snapshot),
115
117
  `Session override: ${snapshot.sessionOverride === undefined ? "none" : boolLabel(snapshot.sessionOverride)}`,
116
118
  mainModelLine(snapshot, ctx),
@@ -118,7 +120,7 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
118
120
  childrenLine(snapshot),
119
121
  recommendationLine(ctx),
120
122
  `Agent-end timeout: ${snapshot.config.agentEndTimeoutMs}ms`,
121
- `Auto-follow: not implemented${snapshot.autoFollowQueued ? " (queued)" : ""}`,
123
+ `Auto-follow: ${snapshot.enabled && snapshot.config.autoFollow.blockers ? "on for blockers" : "off"} · attempts ${snapshot.autoFollowAttempts}${snapshot.config.autoFollow.maxAttempts === null ? "" : `/${snapshot.config.autoFollow.maxAttempts}`}${snapshot.autoFollowQueued ? " · queued" : ""}${snapshot.autoFollowStalemate ? " · stalemate" : ""}`,
122
124
  `Review model call: ${snapshot.reviewDescription}`,
123
125
  ];
124
126
  if (snapshot.failedReviews > 0) lines.push(`Failed reviews: ${snapshot.failedReviews}`);
@@ -236,7 +238,7 @@ function createTestWarning(severity: "concern" | "blocker", text: string): Watch
236
238
  summary: text,
237
239
  evidence: `Manual /subagents-watchdog test ${severity} message from the main session.`,
238
240
  recommendedAction: severity === "blocker"
239
- ? "Verify the renderer and transcript delivery; no automatic follow-up is queued in Gate 1B."
241
+ ? "Verify the renderer, transcript delivery, and auto-follow policy."
240
242
  : "Verify the renderer and transcript delivery; decide manually whether any action is needed.",
241
243
  };
242
244
  }
@@ -381,9 +383,10 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
381
383
  review: options.review ?? createMainWatchdogReview(() => currentContext, { getThinkingLevel: () => pi.getThinkingLevel() }),
382
384
  reviewDescription: options.review ? "injected seam" : "real model review",
383
385
  reviewChangesOnly: true,
384
- displayWarning: (details) => {
385
- pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }));
386
+ displayWarning: (details, delivery) => {
387
+ pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }), delivery?.deliverAs === "steer" ? { deliverAs: "steer" } : undefined);
386
388
  },
389
+ sendUserMessage: (message) => pi.sendUserMessage(message),
387
390
  });
388
391
 
389
392
  pi.registerMessageRenderer<WatchdogWarningDetails>(SUBAGENT_WATCHDOG_WARNING_TYPE, (message, renderOptions, theme) => {
@@ -417,13 +420,17 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
417
420
  rememberContext(ctx);
418
421
  runtime.handleTurnEnd(event, ctx);
419
422
  });
423
+ pi.on("tool_result", (_event, ctx) => {
424
+ rememberContext(ctx);
425
+ runtime.handleToolResult(ctx);
426
+ });
420
427
  pi.on("agent_end", (event, ctx) => {
421
428
  rememberContext(ctx);
422
429
  return runtime.handleAgentEnd(event, ctx);
423
430
  });
424
- pi.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true }));
425
- pi.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true }));
426
- pi.on("session_compact", () => runtime.reset("session compact"));
431
+ pi.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
432
+ pi.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
433
+ pi.on("session_compact", () => runtime.reset("session compact", { clearScope: true }));
427
434
  pi.on("session_shutdown", () => {
428
435
  currentContext = undefined;
429
436
  runtime.dispose();