@selesai/code 0.5.27 → 0.5.29

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 (310) hide show
  1. package/dist/cli/args.d.ts.map +1 -1
  2. package/dist/cli/args.js +9 -1
  3. package/dist/cli/args.js.map +1 -1
  4. package/dist/cli/config-selector.d.ts.map +1 -1
  5. package/dist/cli/config-selector.js +1 -1
  6. package/dist/cli/config-selector.js.map +1 -1
  7. package/dist/cli/credential-print.d.ts +23 -0
  8. package/dist/cli/credential-print.d.ts.map +1 -0
  9. package/dist/cli/credential-print.js +117 -0
  10. package/dist/cli/credential-print.js.map +1 -0
  11. package/dist/cli/startup-ui.d.ts.map +1 -1
  12. package/dist/cli/startup-ui.js +1 -1
  13. package/dist/cli/startup-ui.js.map +1 -1
  14. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  15. package/dist/core/agent-session-runtime.js +3 -0
  16. package/dist/core/agent-session-runtime.js.map +1 -1
  17. package/dist/core/agent-session.d.ts +12 -1
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +20 -14
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/compaction/compaction.d.ts.map +1 -1
  22. package/dist/core/compaction/compaction.js +11 -3
  23. package/dist/core/compaction/compaction.js.map +1 -1
  24. package/dist/core/extensions/runner.d.ts +1 -0
  25. package/dist/core/extensions/runner.d.ts.map +1 -1
  26. package/dist/core/extensions/runner.js +11 -0
  27. package/dist/core/extensions/runner.js.map +1 -1
  28. package/dist/core/extensions/types.d.ts +14 -1
  29. package/dist/core/extensions/types.d.ts.map +1 -1
  30. package/dist/core/extensions/types.js.map +1 -1
  31. package/dist/core/footer-data-provider.d.ts +10 -0
  32. package/dist/core/footer-data-provider.d.ts.map +1 -1
  33. package/dist/core/footer-data-provider.js +1 -1
  34. package/dist/core/footer-data-provider.js.map +1 -1
  35. package/dist/core/llama/provider.d.ts.map +1 -1
  36. package/dist/core/llama/provider.js +8 -3
  37. package/dist/core/llama/provider.js.map +1 -1
  38. package/dist/core/model-config.d.ts +30 -0
  39. package/dist/core/model-config.d.ts.map +1 -1
  40. package/dist/core/model-config.js +6 -0
  41. package/dist/core/model-config.js.map +1 -1
  42. package/dist/core/model-registry.d.ts.map +1 -1
  43. package/dist/core/model-registry.js +2 -2
  44. package/dist/core/model-registry.js.map +1 -1
  45. package/dist/core/model-resolver.d.ts +1 -0
  46. package/dist/core/model-resolver.d.ts.map +1 -1
  47. package/dist/core/model-resolver.js +20 -3
  48. package/dist/core/model-resolver.js.map +1 -1
  49. package/dist/core/model-runtime.d.ts +2 -0
  50. package/dist/core/model-runtime.d.ts.map +1 -1
  51. package/dist/core/model-runtime.js +5 -4
  52. package/dist/core/model-runtime.js.map +1 -1
  53. package/dist/core/package-manager.d.ts.map +1 -1
  54. package/dist/core/package-manager.js +13 -6
  55. package/dist/core/package-manager.js.map +1 -1
  56. package/dist/core/remote-catalog-provider.d.ts +1 -1
  57. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  58. package/dist/core/remote-catalog-provider.js +24 -11
  59. package/dist/core/remote-catalog-provider.js.map +1 -1
  60. package/dist/core/resource-loader.d.ts +15 -0
  61. package/dist/core/resource-loader.d.ts.map +1 -1
  62. package/dist/core/resource-loader.js +66 -9
  63. package/dist/core/resource-loader.js.map +1 -1
  64. package/dist/core/settings-manager.d.ts +1 -1
  65. package/dist/core/settings-manager.d.ts.map +1 -1
  66. package/dist/core/settings-manager.js.map +1 -1
  67. package/dist/core/system-prompt.d.ts.map +1 -1
  68. package/dist/core/system-prompt.js +1 -1
  69. package/dist/core/system-prompt.js.map +1 -1
  70. package/dist/core/tools/bash.d.ts +2 -0
  71. package/dist/core/tools/bash.d.ts.map +1 -1
  72. package/dist/core/tools/bash.js +34 -5
  73. package/dist/core/tools/bash.js.map +1 -1
  74. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  75. package/dist/core/tools/tool-definition-wrapper.js +3 -1
  76. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  77. package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
  78. package/dist/extensions/pi-intercom/README.md +102 -39
  79. package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
  80. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
  81. package/dist/extensions/pi-intercom/broker/client.ts +315 -12
  82. package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
  83. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
  84. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
  85. package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
  86. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
  87. package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
  88. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
  89. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
  90. package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
  91. package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
  92. package/dist/extensions/pi-intercom/config.test.ts +93 -0
  93. package/dist/extensions/pi-intercom/config.ts +55 -6
  94. package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
  95. package/dist/extensions/pi-intercom/cwd.ts +31 -0
  96. package/dist/extensions/pi-intercom/extension-api.ts +44 -0
  97. package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
  98. package/dist/extensions/pi-intercom/format-context.ts +32 -0
  99. package/dist/extensions/pi-intercom/index.ts +742 -145
  100. package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
  101. package/dist/extensions/pi-intercom/package.json +15 -5
  102. package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
  103. package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
  104. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
  105. package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
  106. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
  107. package/dist/extensions/pi-intercom/types.ts +94 -4
  108. package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
  109. package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
  110. package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
  111. package/dist/extensions/pi-subagents/CHANGELOG.md +85 -0
  112. package/dist/extensions/pi-subagents/LICENSE +21 -0
  113. package/dist/extensions/pi-subagents/README.md +146 -52
  114. package/dist/extensions/pi-subagents/agents/architect.md +1 -0
  115. package/dist/extensions/pi-subagents/agents/builder.md +1 -0
  116. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  117. package/dist/extensions/pi-subagents/package.json +3 -3
  118. package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
  119. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +21 -989
  120. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
  121. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +430 -0
  122. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
  123. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +282 -0
  124. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +71 -30
  125. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
  126. package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
  127. package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
  128. package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
  129. package/dist/extensions/pi-subagents/src/api/preflight.ts +16 -12
  130. package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
  131. package/dist/extensions/pi-subagents/src/extension/index.ts +17 -7
  132. package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
  133. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -6
  134. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +8 -7
  135. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
  136. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
  137. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +63 -16
  138. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
  139. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
  140. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
  141. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
  142. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
  143. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +29 -4
  144. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +29 -6
  145. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
  146. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +372 -28
  147. package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
  148. package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
  149. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +143 -43
  150. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +527 -247
  151. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
  152. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +426 -123
  153. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +15 -7
  154. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
  155. package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
  156. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
  157. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
  158. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
  159. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
  160. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
  161. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
  162. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
  163. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
  164. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
  165. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
  166. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
  167. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +1 -1
  168. package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
  169. package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
  170. package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
  171. package/dist/extensions/pi-subagents/src/shared/types.ts +155 -10
  172. package/dist/extensions/pi-subagents/src/shared/utils.ts +73 -4
  173. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -0
  174. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
  175. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
  176. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
  177. package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
  178. package/dist/extensions/pi-subagents/src/tui/render.ts +27 -28
  179. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
  180. package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
  181. package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
  182. package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
  183. package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
  184. package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
  185. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  186. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +348 -7
  187. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
  188. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
  189. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
  190. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +26 -1
  191. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
  192. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +3 -3
  193. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +213 -13
  194. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +34 -0
  195. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
  196. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +118 -12
  197. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +850 -7
  198. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
  199. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
  200. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
  201. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
  202. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
  203. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +128 -0
  204. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
  205. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
  206. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
  207. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +102 -0
  208. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
  209. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
  210. package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
  211. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
  212. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
  213. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
  214. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +4 -2
  215. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
  216. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
  217. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
  218. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
  219. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +11 -11
  220. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
  221. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
  222. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
  223. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +10 -2
  224. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
  225. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
  226. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
  227. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +46 -0
  228. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
  229. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
  230. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
  231. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
  232. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
  233. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +34 -2
  234. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  235. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
  236. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
  237. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
  238. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
  239. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +3 -0
  240. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +1 -1
  241. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
  242. package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
  243. package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
  244. package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
  245. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
  246. package/dist/extensions/question/index.ts +14 -1
  247. package/dist/extensions/question/tests/wizard.test.ts +11 -8
  248. package/dist/main.d.ts.map +1 -1
  249. package/dist/main.js +43 -0
  250. package/dist/main.js.map +1 -1
  251. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  252. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  253. package/dist/modes/interactive/components/custom-message.js +10 -2
  254. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  255. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  256. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  257. package/dist/modes/interactive/components/extension-editor.js +16 -46
  258. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  259. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  260. package/dist/modes/interactive/components/model-selector.js +4 -1
  261. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  262. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  263. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  264. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  265. package/dist/modes/interactive/external-editor.d.ts +12 -0
  266. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  267. package/dist/modes/interactive/external-editor.js +37 -0
  268. package/dist/modes/interactive/external-editor.js.map +1 -0
  269. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  270. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  271. package/dist/modes/interactive/interactive-mode.js +72 -64
  272. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  273. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  274. package/dist/modes/rpc/rpc-mode.js +14 -0
  275. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  276. package/dist/skills/batch-grill-me/SKILL.md +3 -1
  277. package/dist/utils/clipboard.d.ts.map +1 -1
  278. package/dist/utils/clipboard.js +19 -8
  279. package/dist/utils/clipboard.js.map +1 -1
  280. package/dist/utils/version-check.d.ts.map +1 -1
  281. package/dist/utils/version-check.js +1 -1
  282. package/dist/utils/version-check.js.map +1 -1
  283. package/docs/compaction.md +1 -1
  284. package/docs/custom-provider.md +14 -5
  285. package/docs/environment-variables.md +86 -0
  286. package/docs/extensions.md +15 -5
  287. package/docs/index.md +1 -0
  288. package/docs/models.md +9 -2
  289. package/docs/providers.md +21 -2
  290. package/docs/rpc.md +23 -6
  291. package/docs/session-format.md +2 -0
  292. package/docs/settings.md +1 -1
  293. package/docs/usage.md +0 -13
  294. package/examples/extensions/custom-compaction.ts +5 -2
  295. package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
  296. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  297. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  298. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  299. package/examples/extensions/gondolin/package-lock.json +2 -2
  300. package/examples/extensions/gondolin/package.json +1 -1
  301. package/examples/extensions/handoff.ts +11 -3
  302. package/examples/extensions/message-renderer.ts +3 -3
  303. package/examples/extensions/sandbox/package-lock.json +2 -2
  304. package/examples/extensions/sandbox/package.json +1 -1
  305. package/examples/extensions/summarize.ts +5 -2
  306. package/examples/extensions/with-deps/package-lock.json +2 -2
  307. package/examples/extensions/with-deps/package.json +1 -1
  308. package/examples/sdk/12-full-control.ts +3 -1
  309. package/package.json +15 -6
  310. package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -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;
@@ -40,6 +40,7 @@ function getTermWidth(): number {
40
40
  }
41
41
 
42
42
  const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
43
+ const ansiStylePattern = /\x1b\[[0-9;]*m/y;
43
44
 
44
45
  /**
45
46
  * Truncate a line to maxWidth, preserving ANSI styling through the ellipsis.
@@ -50,7 +51,7 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
50
51
  *
51
52
  * Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
52
53
  */
53
- function truncLine(text: string, maxWidth: number): string {
54
+ export function truncLine(text: string, maxWidth: number): string {
54
55
  if (visibleWidth(text) <= maxWidth) return text;
55
56
 
56
57
  const targetWidth = maxWidth - 1;
@@ -60,7 +61,8 @@ function truncLine(text: string, maxWidth: number): string {
60
61
  let i = 0;
61
62
 
62
63
  while (i < text.length) {
63
- const ansiMatch = text.slice(i).match(/^\x1b\[[0-9;]*m/);
64
+ ansiStylePattern.lastIndex = i;
65
+ const ansiMatch = ansiStylePattern.exec(text);
64
66
  if (ansiMatch) {
65
67
  const code = ansiMatch[0];
66
68
  result += code;
@@ -74,11 +76,9 @@ function truncLine(text: string, maxWidth: number): string {
74
76
  continue;
75
77
  }
76
78
 
77
- let end = i;
78
- while (end < text.length && !text.slice(end).match(/^\x1b\[[0-9;]*m/)) {
79
- end++;
80
- }
81
-
79
+ let end = text.indexOf("\x1b[", i);
80
+ if (end === i) end = text.indexOf("\x1b[", i + 2);
81
+ if (end === -1) end = text.length;
82
82
  const textPortion = text.slice(i, end);
83
83
  for (const seg of segmenter.segment(textPortion)) {
84
84
  const grapheme = seg.segment;
@@ -529,17 +529,15 @@ function buildChainStepSpans(details: Pick<Details, "chainAgents" | "workflowGra
529
529
  return spans;
530
530
  }
531
531
 
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
532
  function buildAsyncChainStepSpans(total: number, stepCount: number, parallelGroups: AsyncParallelGroupStatus[] = []): ChainStepSpan[] {
533
+ const groupsByStep = new Map<number, AsyncParallelGroupStatus>();
534
+ for (const group of parallelGroups) {
535
+ if (!groupsByStep.has(group.stepIndex)) groupsByStep.set(group.stepIndex, group);
536
+ }
539
537
  const spans: ChainStepSpan[] = [];
540
538
  let flatIndex = 0;
541
539
  for (let stepIndex = 0; stepIndex < total; stepIndex++) {
542
- const group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
540
+ const group = groupsByStep.get(stepIndex);
543
541
  if (group) {
544
542
  spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
545
543
  flatIndex = Math.max(flatIndex, group.start + group.count);
@@ -567,6 +565,7 @@ interface ChainRenderResultEntry {
567
565
  kind: "result";
568
566
  resultIndex: number;
569
567
  rowNumber: number;
568
+ rowLabel?: string;
570
569
  agentName: string;
571
570
  }
572
571
 
@@ -601,6 +600,7 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
601
600
  kind: "result",
602
601
  resultIndex: index,
603
602
  rowNumber: index + 1,
603
+ rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
604
604
  agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
605
605
  });
606
606
  }
@@ -622,7 +622,9 @@ interface MultiProgressLabel {
622
622
  function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "progress" | "totalSteps" | "currentStepIndex" | "chainAgents" | "workflowGraph">, hasRunning: boolean): MultiProgressLabel {
623
623
  const stepSpans = buildChainStepSpans(details);
624
624
  const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
625
- const activeParallelGroup = isChainParallelGroupActive(details);
625
+ const activeParallelGroup = details.mode === "chain"
626
+ && details.currentStepIndex !== undefined
627
+ && stepSpans.some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
626
628
  const itemTitle: "Step" | "Agent" = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
627
629
 
628
630
  if (details.mode === "parallel") {
@@ -708,15 +710,10 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
708
710
  return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
709
711
  }
710
712
 
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
- }
713
+ function resultRowLabel(label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
717
714
  if (label.itemTitle === "Agent") {
718
715
  const localStepNumber = label.activeParallelGroup
719
- ? Math.max(1, stepNumber - label.groupStartIndex)
716
+ ? resultIndex - label.groupStartIndex + 1
720
717
  : stepNumber;
721
718
  return `Agent ${localStepNumber}/${label.totalCount}`;
722
719
  }
@@ -1321,7 +1318,7 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
1321
1318
  ]);
1322
1319
  const c = new Container();
1323
1320
  const width = getTermWidth() - 4;
1324
- const modelDisplay = modelThinkingBadge(theme, r.model);
1321
+ const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
1325
1322
  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
1323
 
1327
1324
  if (isRunning && r.progress) {
@@ -1417,7 +1414,7 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
1417
1414
  const rowNumber = entry.rowNumber;
1418
1415
  const agentName = entry.agentName;
1419
1416
  if (!r) {
1420
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1417
+ const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
1421
1418
  c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
1422
1419
  continue;
1423
1420
  }
@@ -1430,8 +1427,10 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
1430
1427
  const stepStats = formatProgressStats(theme, rProg);
1431
1428
  const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
1432
1429
  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}`;
1430
+ const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
1431
+ const rowProgressModel = rProg && "status" in rProg ? rProg : undefined;
1432
+ const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
1433
+ const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
1435
1434
  c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
1436
1435
  if (rRunning && rProg && "status" in rProg) {
1437
1436
  const activity = compactCurrentActivity(rProg);
@@ -1717,7 +1716,7 @@ export function renderSubagentResult(
1717
1716
  const agentName = entry.agentName;
1718
1717
 
1719
1718
  if (!r) {
1720
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1719
+ const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
1721
1720
  c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
1722
1721
  c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
1723
1722
  c.addChild(new Spacer(1));
@@ -1740,7 +1739,7 @@ export function renderSubagentResult(
1740
1739
  : theme.fg("success", "done");
1741
1740
  const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
1742
1741
  const modelDisplay = modelThinkingBadge(theme, r.model);
1743
- const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
1742
+ const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
1744
1743
  const contextBadge = contextModeBadge(theme, r.context);
1745
1744
  const stepHeader = rRunning
1746
1745
  ? `${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();
@@ -201,18 +201,19 @@ function createWatchdogWarnTool(request: WatchdogReviewRequest): AgentTool<typeo
201
201
  };
202
202
  }
203
203
 
204
- function buildWatchdogSystemPrompt(ctx: ExtensionContext): string {
204
+ function buildWatchdogSystemPrompt(ctx: ExtensionContext, options: { hasScope?: boolean } = {}): string {
205
205
  return [
206
206
  "You are the main-session subagent watchdog for Pi.",
207
207
  `Working directory: ${ctx.cwd}`,
208
208
  "Review only the supplied parent turn delta. Inspect repository files only when needed to verify a concrete concern.",
209
+ options.hasScope ? "When the review input includes a Current scope block, treat newer scope prompts as superseding/mutating older prompts and use category='scope-drift' for work that serves no current scope item." : undefined,
209
210
  "You are read-only. You may use read, grep, find, and ls. Do not edit files, run shell commands, spawn agents, or mutate state.",
210
211
  "Emit warnings only by calling watchdog_warn. Freeform assistant text is ignored and must not be used to report warnings.",
211
212
  "Emit only medium/high confidence actionable concerns or blockers: missed user constraints, correctness risks, test gaps that matter, unsafe changes, stale facts, loop risks, or scope drift.",
212
213
  "Do not emit nits, style preferences, low-confidence guesses, informational notes, praise, or summaries.",
213
214
  "If the turn is clean, call no tools and end normally.",
214
215
  "Use severity='blocker' only when the issue should stop acceptance until addressed; otherwise use severity='concern'.",
215
- ].join("\n");
216
+ ].filter((line): line is string => Boolean(line)).join("\n");
216
217
  }
217
218
 
218
219
  function buildReviewPrompt(request: WatchdogReviewRequest, selection: WatchdogReviewModelSelection): string {
@@ -270,13 +271,13 @@ export function createMainWatchdogReview(provider: WatchdogContextProvider, opti
270
271
  ];
271
272
  const agent = new Agent({
272
273
  initialState: {
273
- systemPrompt: buildWatchdogSystemPrompt(ctx),
274
+ systemPrompt: buildWatchdogSystemPrompt(ctx, { hasScope: request.hasScope }),
274
275
  model: selection.model,
275
276
  thinkingLevel: selection.thinkingLevel,
276
277
  tools,
277
278
  },
278
279
  convertToLlm,
279
- streamFunction: streamFn,
280
+ streamFn,
280
281
  getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : undefined,
281
282
  beforeToolCall: async ({ toolCall }) => WATCHDOG_ALLOWED_TOOL_NAMES.has(toolCall.name)
282
283
  ? undefined