@iowarp/clio-coder 0.3.6 → 0.3.8

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 (323) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +22 -6
  3. package/dist/{acp-2BEHC4DL.js → acp-U67UHUK2.js} +14 -14
  4. package/dist/{agents-LNNFTM53.js → agents-YU6SGALZ.js} +42 -35
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-KXXFI2VS.js → auth-5ZPJOIVG.js} +32 -24
  7. package/dist/builtins-C6JMZVV6.js +17 -0
  8. package/dist/{chunk-E25LMLRW.js → chunk-26LEYJZH.js} +2 -2
  9. package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
  10. package/dist/{chunk-PBTHKCPN.js → chunk-2HFZQUHL.js} +7 -7
  11. package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
  12. package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
  13. package/dist/{chunk-CKXWIANG.js → chunk-4SPRNWDE.js} +18 -16
  14. package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
  15. package/dist/{chunk-XF5N4U5A.js → chunk-5DHKRSMQ.js} +9 -8
  16. package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
  17. package/dist/{chunk-EKY57CSP.js → chunk-5H3GB5BO.js} +68 -772
  18. package/dist/{chunk-6XXKFVSN.js → chunk-5Q2VVUKB.js} +4 -4
  19. package/dist/{chunk-DJVECN66.js → chunk-7RFXX52T.js} +295 -3705
  20. package/dist/{chunk-ZXF4XRKW.js → chunk-7RGZWPB6.js} +158 -8
  21. package/dist/{chunk-FYYLNIL5.js → chunk-A2NJGIB3.js} +2 -2
  22. package/dist/{chunk-XXQNGV4M.js → chunk-A3WNZD3P.js} +548 -72
  23. package/dist/{chunk-VEZEGCGW.js → chunk-B5CSFE7B.js} +23 -21
  24. package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
  25. package/dist/chunk-DR52UMZW.js +21 -0
  26. package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
  27. package/dist/{chunk-QKMUKYO7.js → chunk-E77JEWSD.js} +270 -125
  28. package/dist/{chunk-LYF7OHWH.js → chunk-EMYUUSFG.js} +12 -467
  29. package/dist/{chunk-4VP4KH3K.js → chunk-EQ63NRB7.js} +8 -8
  30. package/dist/chunk-FBVTI2TJ.js +518 -0
  31. package/dist/{chunk-R46L2BIR.js → chunk-FHJEP5SW.js} +19 -25
  32. package/dist/{chunk-WR67VIZY.js → chunk-GN57SG4G.js} +66 -8
  33. package/dist/{chunk-RD5U66HV.js → chunk-GPIEI3LY.js} +9 -9
  34. package/dist/{chunk-PCZJO5TI.js → chunk-GU2UIAFZ.js} +13 -178
  35. package/dist/chunk-GWS3VEIW.js +195 -0
  36. package/dist/chunk-H7IXIC72.js +103 -0
  37. package/dist/{chunk-3BPUFZDL.js → chunk-HLE42MG7.js} +3 -3
  38. package/dist/{chunk-24I7BN55.js → chunk-IFBNV6H6.js} +3 -3
  39. package/dist/{chunk-QNQHSOLF.js → chunk-IGWKHNIQ.js} +114 -55
  40. package/dist/chunk-IIZWH4XA.js +172 -0
  41. package/dist/{chunk-AD2SYQYC.js → chunk-IJ7RPIYJ.js} +124 -6
  42. package/dist/chunk-J3YUBZWY.js +382 -0
  43. package/dist/{chunk-5JGRAMKL.js → chunk-JOZYP4GM.js} +8 -6
  44. package/dist/{chunk-OH3TOQTB.js → chunk-K4XHGFR5.js} +751 -15
  45. package/dist/{chunk-EYPA3EGJ.js → chunk-KTYTFRMB.js} +178 -14
  46. package/dist/chunk-LU7P4LHA.js +33 -0
  47. package/dist/chunk-ME6CCNFO.js +108 -0
  48. package/dist/chunk-MXKJU4JB.js +1100 -0
  49. package/dist/{chunk-RY3LY4J5.js → chunk-N22QMJKY.js} +21 -18
  50. package/dist/chunk-NMPKI6XL.js +3006 -0
  51. package/dist/chunk-NUGM5KR6.js +165 -0
  52. package/dist/{chunk-22NAGB7X.js → chunk-P43ETTHK.js} +5 -94
  53. package/dist/{chunk-NILBFAPG.js → chunk-PMDBGQSJ.js} +2 -2
  54. package/dist/chunk-PT7HYKEM.js +165 -0
  55. package/dist/chunk-RVG5JXAL.js +41 -0
  56. package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
  57. package/dist/{chunk-4BPJXDWC.js → chunk-TANS5ZJS.js} +35 -19
  58. package/dist/{verifiers-NCBTHHN2.js → chunk-TB5666IT.js} +67 -324
  59. package/dist/{chunk-QM3F2GKX.js → chunk-TLQJPP24.js} +7927 -8088
  60. package/dist/{chunk-G7MUEIGA.js → chunk-TT36MB5S.js} +2 -1
  61. package/dist/chunk-TTHACPOM.js +961 -0
  62. package/dist/{chunk-6US73PDB.js → chunk-TYPGUK6W.js} +7 -7
  63. package/dist/{chunk-MFFY33HR.js → chunk-U6MBIEMB.js} +554 -209
  64. package/dist/chunk-VAWNZU7Z.js +242 -0
  65. package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
  66. package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
  67. package/dist/{chunk-XYDYPRZI.js → chunk-VWZOAB7K.js} +10 -10
  68. package/dist/{chunk-ZRGEBJ4T.js → chunk-WLFILSD5.js} +48 -48
  69. package/dist/{chunk-K7T3E2SR.js → chunk-WNIJTQQK.js} +12 -11
  70. package/dist/{chunk-CYQKWTG3.js → chunk-WSB3FPX7.js} +68 -20
  71. package/dist/{chunk-KHSFENX2.js → chunk-WWCZ5F23.js} +116 -14
  72. package/dist/{chunk-XE2VEJHX.js → chunk-WXY7KU3G.js} +2 -2
  73. package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
  74. package/dist/{chunk-KOHPCX4K.js → chunk-XWSF374K.js} +5 -5
  75. package/dist/{chunk-CJUB2JJ2.js → chunk-YS5VLNH5.js} +10 -10
  76. package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
  77. package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
  78. package/dist/cli/index.js +33 -31
  79. package/dist/{clio-M2KGYUFZ.js → clio-QVTYJ57A.js} +10 -10
  80. package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +5 -5
  81. package/dist/codewiki/build-worker.js +4 -4
  82. package/dist/{components-5TTYYX6G.js → components-ZFA3SAER.js} +9 -9
  83. package/dist/{config-XUUYQIWO.js → config-LW5IJFQN.js} +65 -55
  84. package/dist/{configure-IHJ7YOMV.js → configure-7XIZCOU4.js} +29 -24
  85. package/dist/{context-75MIWW3U.js → context-L3WL3X7K.js} +54 -43
  86. package/dist/{context-ZQ7SIFJV.js → context-N52ZA626.js} +30 -14
  87. package/dist/{context-74JLXAWD.js → context-Y6Y7QPR6.js} +12 -12
  88. package/dist/{context-clear-GYKWNUML.js → context-clear-MBQRLSDQ.js} +54 -43
  89. package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
  90. package/dist/{context-working-set-UX5KEP4J.js → context-working-set-GS6DSO7F.js} +18 -18
  91. package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-22ZCNOM3.js} +367 -79
  92. package/dist/{docs-6FZSCG5B.js → docs-7LQ23DLM.js} +9 -9
  93. package/dist/{doctor-SVJ5BZCW.js → doctor-M7YEDGAE.js} +27 -23
  94. package/dist/{eval-CG6LLBLD.js → eval-BEC2WHDA.js} +73 -20
  95. package/dist/{evidence-ZYFIEN42.js → evidence-REJUMSKM.js} +70 -59
  96. package/dist/{evolve-QGEXEMDW.js → evolve-PY5ZBA5K.js} +50 -39
  97. package/dist/{extensions-ADGNCJJD.js → extensions-HVKU65YU.js} +7 -7
  98. package/dist/{fleet-S5R4ZOQY.js → fleet-7WZEWRFA.js} +236 -377
  99. package/dist/fleet-commands-UVHWM76J.js +70 -0
  100. package/dist/fleet-graph-6ULH7PES.js +125 -0
  101. package/dist/fleet-new-RDVJLHHH.js +48 -0
  102. package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
  103. package/dist/fleet-validate-72PC4SLA.js +79 -0
  104. package/dist/{init-5DRU55YR.js → init-OG3TPGQG.js} +69 -57
  105. package/dist/library-CNTMPLRF.js +217 -0
  106. package/dist/{memory-7YKKR6UC.js → memory-6IS7F275.js} +52 -41
  107. package/dist/{models-ZPOLRU2C.js → models-ENRJDA5W.js} +37 -31
  108. package/dist/{monitor-US5F5YGZ.js → monitor-XLDVO7TN.js} +79 -42
  109. package/dist/{orchestrator-E2AL4T5N.js → orchestrator-6KSPYRHA.js} +4126 -762
  110. package/dist/{paths-E7KYAQWE.js → paths-DBXMZMDU.js} +6 -6
  111. package/dist/registry-LG64LTF4.js +11 -0
  112. package/dist/{reset-KZ652EK6.js → reset-RZ4ER727.js} +12 -12
  113. package/dist/{run-SRNBKDWD.js → run-Y2CNK5RU.js} +89 -76
  114. package/dist/{share-CGZE33UP.js → share-A55GYP6Z.js} +37 -13
  115. package/dist/{skills-S2X4DLY5.js → skills-ALC5J6AT.js} +29 -13
  116. package/dist/{skills-eval-W2GGIC4R.js → skills-eval-JPBEBYQU.js} +60 -48
  117. package/dist/support-MIETYA5E.js +38 -0
  118. package/dist/{targets-54SWINWB.js → targets-VGNXIR3S.js} +46 -36
  119. package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WOBR64YA.js} +4 -4
  120. package/dist/{uninstall-BVLWXKBT.js → uninstall-ZJF5H5ZN.js} +9 -9
  121. package/dist/{upgrade-JKAR27XC.js → upgrade-FUSUAGHR.js} +31 -28
  122. package/dist/{usage-MSAWCLX4.js → usage-N4MKVHKD.js} +140 -68
  123. package/dist/verifiers-YAWOJ3H2.js +336 -0
  124. package/dist/{verify-X5HDROLA.js → verify-LTDHYBGY.js} +10 -9
  125. package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-6M7GHTBJ.js} +75 -62
  126. package/dist/worker/entry.js +67 -57
  127. package/docs/README.md +3 -2
  128. package/docs/acp.md +1 -1
  129. package/docs/alcf-provider.md +1 -1
  130. package/docs/architecture.md +2 -2
  131. package/docs/artifact-versions.md +11 -6
  132. package/docs/built-in-agents.md +26 -2
  133. package/docs/capacity-and-scheduling.md +1 -1
  134. package/docs/commands-and-modes.md +83 -3
  135. package/docs/configuration-and-targets.md +86 -4
  136. package/docs/context-engine.md +1 -1
  137. package/docs/development-pipeline.md +1 -1
  138. package/docs/dispatch-architecture-rationale.md +1 -1
  139. package/docs/documentation-coverage.md +3 -3
  140. package/docs/documentation-guide.md +3 -3
  141. package/docs/eval-runner.md +1 -1
  142. package/docs/evals-internal.md +1 -1
  143. package/docs/evidence-and-memory.md +74 -10
  144. package/docs/evolution.md +1 -1
  145. package/docs/exit-codes-and-output.md +4 -1
  146. package/docs/extensions-and-sharing.md +6 -2
  147. package/docs/fleet-demo-runbook.md +2 -2
  148. package/docs/fleet-dispatch.md +228 -16
  149. package/docs/git-commit-provenance.md +2 -2
  150. package/docs/glossary.md +22 -2
  151. package/docs/installation-and-lifecycle.md +2 -2
  152. package/docs/middleware-and-components.md +2 -1
  153. package/docs/model-catalog.md +1 -1
  154. package/docs/observability.md +56 -9
  155. package/docs/proactive-memory.md +1 -1
  156. package/docs/prompt-envelope-and-tools.md +4 -2
  157. package/docs/provider-adapter-cookbook.md +1 -1
  158. package/docs/release-cut-checklist.md +83 -65
  159. package/docs/resource-library.md +59 -0
  160. package/docs/safety-model.md +2 -2
  161. package/docs/scientific-validation.md +3 -3
  162. package/docs/session-lifecycle.md +37 -1
  163. package/docs/skills-marketplace.md +16 -3
  164. package/docs/tool-usage.md +14 -7
  165. package/docs/trace-store.md +1 -1
  166. package/docs/troubleshooting.md +1 -1
  167. package/docs/tui-design.md +1 -1
  168. package/docs/worker-dispatch-mechanics.md +3 -3
  169. package/package.json +1 -2
  170. package/src/cli/argv.ts +5 -0
  171. package/src/cli/configure.ts +107 -23
  172. package/src/cli/doctor.ts +5 -1
  173. package/src/cli/evidence.ts +30 -25
  174. package/src/cli/fleet-commands.ts +37 -0
  175. package/src/cli/fleet-graph.ts +102 -0
  176. package/src/cli/fleet-new.ts +36 -0
  177. package/src/cli/fleet-preflight.ts +111 -0
  178. package/src/cli/fleet-validate.ts +30 -0
  179. package/src/cli/fleet.ts +173 -335
  180. package/src/cli/index.ts +3 -1
  181. package/src/cli/library.ts +190 -0
  182. package/src/cli/share.ts +13 -1
  183. package/src/cli/shared.ts +1 -0
  184. package/src/cli/targets.ts +4 -1
  185. package/src/cli/usage.ts +111 -19
  186. package/src/cli/validate-model.ts +60 -5
  187. package/src/core/bus-events.ts +29 -0
  188. package/src/core/commit-attribution.ts +4 -4
  189. package/src/core/config.ts +130 -0
  190. package/src/core/defaults.ts +81 -0
  191. package/src/core/path-boundary.ts +100 -0
  192. package/src/domains/agents/builtins/architect.md +1 -0
  193. package/src/domains/agents/builtins/oracle.md +33 -0
  194. package/src/domains/agents/catalog.ts +13 -1
  195. package/src/domains/agents/extension.ts +2 -11
  196. package/src/domains/agents/fleet-contract.ts +304 -24
  197. package/src/domains/agents/index.ts +14 -0
  198. package/src/domains/agents/recipe.ts +7 -1
  199. package/src/domains/agents/registry.ts +73 -5
  200. package/src/domains/agents/result-contract.ts +360 -15
  201. package/src/domains/agents/write-boundary.ts +15 -50
  202. package/src/domains/config/classify.ts +4 -0
  203. package/src/domains/context/project-rules.ts +51 -1
  204. package/src/domains/dispatch/active-route-planner.ts +14 -0
  205. package/src/domains/dispatch/assignment-reconcile.ts +22 -5
  206. package/src/domains/dispatch/assignment-store.ts +151 -14
  207. package/src/domains/dispatch/backoff.ts +2 -1
  208. package/src/domains/dispatch/capability-match.ts +1 -0
  209. package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
  210. package/src/domains/dispatch/contract.ts +48 -0
  211. package/src/domains/dispatch/delegation-plan.ts +164 -0
  212. package/src/domains/dispatch/execution-plan.ts +76 -5
  213. package/src/domains/dispatch/execution-role.ts +12 -2
  214. package/src/domains/dispatch/execution-scheduler.ts +183 -67
  215. package/src/domains/dispatch/extension.ts +412 -74
  216. package/src/domains/dispatch/fleet-gate.ts +14 -0
  217. package/src/domains/dispatch/fleet-plan.ts +63 -3
  218. package/src/domains/dispatch/fleet-run.ts +791 -0
  219. package/src/domains/dispatch/gate-role-prompts.ts +47 -0
  220. package/src/domains/dispatch/host-verification.ts +178 -0
  221. package/src/domains/dispatch/index.ts +41 -1
  222. package/src/domains/dispatch/intent-requirements.ts +40 -0
  223. package/src/domains/dispatch/intent.ts +235 -0
  224. package/src/domains/dispatch/path-scope.ts +370 -0
  225. package/src/domains/dispatch/receipt-integrity.ts +9 -4
  226. package/src/domains/dispatch/state.ts +36 -3
  227. package/src/domains/dispatch/types.ts +64 -12
  228. package/src/domains/dispatch/validation.ts +69 -6
  229. package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
  230. package/src/domains/dispatch/write-boundary.ts +201 -22
  231. package/src/domains/eval/metrics/evidence.ts +79 -2
  232. package/src/domains/eval/runners/clio-run.ts +12 -2
  233. package/src/domains/evidence/build.ts +69 -11
  234. package/src/domains/evidence/index.ts +21 -0
  235. package/src/domains/evidence/provenance.ts +46 -11
  236. package/src/domains/evidence/trust-projection.ts +274 -0
  237. package/src/domains/evidence/trust-status.ts +155 -18
  238. package/src/domains/evidence/types.ts +4 -0
  239. package/src/domains/extensions/discovery.ts +88 -1
  240. package/src/domains/extensions/resources.ts +20 -8
  241. package/src/domains/extensions/state.ts +6 -2
  242. package/src/domains/extensions/types.ts +4 -1
  243. package/src/domains/lifecycle/doctor.ts +140 -1
  244. package/src/domains/middleware/index.ts +15 -0
  245. package/src/domains/middleware/watchdog.ts +281 -0
  246. package/src/domains/observability/contract.ts +3 -1
  247. package/src/domains/observability/cost.ts +12 -1
  248. package/src/domains/observability/extension.ts +2 -2
  249. package/src/domains/observability/index.ts +10 -0
  250. package/src/domains/observability/out-of-turn-usage.ts +223 -0
  251. package/src/domains/prompts/contract.ts +3 -5
  252. package/src/domains/providers/extension.ts +30 -2
  253. package/src/domains/resources/common-loader.ts +3 -0
  254. package/src/domains/resources/index.ts +20 -0
  255. package/src/domains/resources/library.ts +326 -0
  256. package/src/domains/resources/prompts/loader.ts +119 -14
  257. package/src/domains/resources/skills/marketplace.ts +37 -12
  258. package/src/domains/safety/policy-engine.ts +5 -5
  259. package/src/domains/safety/run-effects.ts +64 -1
  260. package/src/domains/safety/scope.ts +7 -12
  261. package/src/domains/session/handoff.ts +629 -0
  262. package/src/domains/share/archive.ts +67 -2
  263. package/src/engine/acp/server.ts +4 -1
  264. package/src/engine/prompt-templates.ts +18 -1
  265. package/src/engine/worker-runtime.ts +6 -3
  266. package/src/entry/orchestrator.ts +37 -0
  267. package/src/interactive/bus-notices.ts +26 -0
  268. package/src/interactive/chat-loop.ts +235 -1
  269. package/src/interactive/chat-renderer.ts +22 -0
  270. package/src/interactive/cost-overlay.ts +31 -3
  271. package/src/interactive/council-dispatch.ts +30 -0
  272. package/src/interactive/council-grid.ts +213 -0
  273. package/src/interactive/council.ts +99 -0
  274. package/src/interactive/dispatch-board.ts +311 -17
  275. package/src/interactive/fleet-run-preview.ts +307 -0
  276. package/src/interactive/footer/notifications.ts +219 -0
  277. package/src/interactive/handoff-round.ts +56 -0
  278. package/src/interactive/interactive-application.ts +43 -1
  279. package/src/interactive/interactive-event-projection.ts +23 -1
  280. package/src/interactive/interactive-slash-runtime.ts +52 -2
  281. package/src/interactive/interactive-subscriptions.ts +14 -2
  282. package/src/interactive/oracle.ts +179 -0
  283. package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
  284. package/src/interactive/overlay-general-openers.ts +190 -1
  285. package/src/interactive/overlay-key-routing.ts +17 -1
  286. package/src/interactive/overlay-lifecycle.ts +41 -1
  287. package/src/interactive/overlay-permission-lifecycle.ts +10 -0
  288. package/src/interactive/overlay-resource-openers.ts +11 -3
  289. package/src/interactive/overlay-session-lifecycle.ts +234 -2
  290. package/src/interactive/overlays/fleet-run-approval.ts +208 -0
  291. package/src/interactive/overlays/handoff-review.ts +185 -0
  292. package/src/interactive/overlays/library-install-confirm.ts +151 -0
  293. package/src/interactive/overlays/list-overlay.ts +168 -2
  294. package/src/interactive/overlays/settings.ts +221 -30
  295. package/src/interactive/overlays/side-question.ts +139 -0
  296. package/src/interactive/overlays/skills-hub.ts +401 -15
  297. package/src/interactive/side-question.ts +171 -0
  298. package/src/interactive/slash-commands.ts +439 -7
  299. package/src/interactive/slash-spec.ts +19 -6
  300. package/src/interactive/theme/tokens.ts +30 -0
  301. package/src/interactive/turn-middleware.ts +15 -1
  302. package/src/interactive/view/artifacts.ts +42 -9
  303. package/src/interactive/view/view-overlay.ts +15 -3
  304. package/src/interactive/watchdog-run.ts +75 -0
  305. package/src/interactive/worker-receipts.ts +14 -2
  306. package/src/interactive/worker-share.ts +56 -1
  307. package/src/interactive/worker-stream.ts +15 -0
  308. package/src/tools/bootstrap.ts +3 -0
  309. package/src/tools/compete-worktrees.ts +13 -79
  310. package/src/tools/dispatch-admission.ts +251 -18
  311. package/src/tools/dispatch-arguments.ts +84 -1
  312. package/src/tools/dispatch-plan.ts +165 -6
  313. package/src/tools/dispatch-runner.ts +364 -23
  314. package/src/tools/dispatch-types.ts +20 -1
  315. package/src/tools/dispatch.ts +72 -2
  316. package/src/tools/monitor.ts +29 -0
  317. package/src/tools/profiles.ts +18 -4
  318. package/src/tools/task-worktree.ts +238 -0
  319. package/src/tools/verify/authoring.ts +61 -1
  320. package/src/tools/verify/scripts.ts +62 -0
  321. package/src/tools/worker-evidence.ts +21 -14
  322. package/src/worker/spec-contract.ts +3 -1
  323. package/dist/chunk-HC4CLZ2Y.js +0 -68
@@ -1,7 +1,10 @@
1
+ import { randomBytes } from "node:crypto";
1
2
  import type { ClioSettings } from "../core/config.js";
2
3
  import type { SafeEventBus } from "../core/event-bus.js";
4
+ import type { AgentsContract } from "../domains/agents/contract.js";
3
5
  import { foldWorkingSet } from "../domains/context/working-set/fold.js";
4
- import type { DispatchContract } from "../domains/dispatch/index.js";
6
+ import type { DispatchContract, DispatchRequest } from "../domains/dispatch/index.js";
7
+ import { agentRoleFactsResolver, executeFleetRun, requestExecutionRole } from "../domains/dispatch/index.js";
5
8
  import {
6
9
  canonicalMemoryRepositoryIdentity,
7
10
  loadMemoryRecordsSync,
@@ -23,6 +26,7 @@ import {
23
26
  isDispatchBoardRowCancellable,
24
27
  isDispatchBoardRowSteerable,
25
28
  } from "./dispatch-board.js";
29
+ import { compileFleetRunPreview, type FleetRunPreview, type FleetRunPreviewInput } from "./fleet-run-preview.js";
26
30
  import { openMemoryOverlay } from "./memory-overlay.js";
27
31
  import { buildHint, showClioOverlayFrame } from "./overlay-frame.js";
28
32
  import type { OverlayTransitions } from "./overlay-transitions.js";
@@ -32,6 +36,8 @@ import {
32
36
  openContextResetOverlay,
33
37
  } from "./overlays/context-reset.js";
34
38
  import { formatDecisionCorrectionTurn, openDecisionsOverlay } from "./overlays/decisions.js";
39
+ import { openFleetRunApprovalOverlay } from "./overlays/fleet-run-approval.js";
40
+ import { openSideQuestionOverlay } from "./overlays/side-question.js";
35
41
  import { type ContextClearCommandOptions, formatUserTaskHandoff } from "./slash-commands.js";
36
42
  import { openTasksOverlay } from "./tasks-overlay.js";
37
43
  import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./view/artifacts.js";
@@ -77,6 +83,37 @@ export interface OverlayGeneralOpenersDeps {
77
83
  openDecisionsOverlay?: typeof openDecisionsOverlay;
78
84
  openMemoryOverlay?: typeof openMemoryOverlay;
79
85
  openViewOverlay?: typeof openViewOverlay;
86
+ openSideQuestionOverlay?: typeof openSideQuestionOverlay;
87
+ openFleetRunApprovalOverlay?: typeof openFleetRunApprovalOverlay;
88
+ /** Recipe registry `/fleet run` resolves every step's agent against. */
89
+ agents?: AgentsContract;
90
+ /** Session budget state the run would be admitted under; absent leaves it unknown. */
91
+ getBudgetPreflight?: () => { ceilingUsd: number; currentUsd: number; verdict: "under" | "at" | "over" };
92
+ /**
93
+ * True while a turn is streaming. `/fleet run` is refused then, never
94
+ * queued: an approved plan describes the workspace as it stands, and a turn
95
+ * still in flight is about to change it.
96
+ */
97
+ isTurnInFlight?: () => boolean;
98
+ /** Record which wave and step a dispatched run belongs to, for the board's phase column. */
99
+ setFleetRunPhase?: (runId: string, phase: { wave: number; stepId: string }) => void;
100
+ /** The shared fleet-run path; overridable so a test can observe one call. */
101
+ runFleet?: typeof executeFleetRun;
102
+ /** Contract and command-registry loader; overridable so a test projects a fixture. */
103
+ loadFleetSources?: FleetRunPreviewInput["load"];
104
+ /**
105
+ * Run one `/btw` round. Absent on a host with no chat loop, in which case
106
+ * `/btw` reports that instead of opening an empty overlay.
107
+ */
108
+ askSideQuestion?: (
109
+ question: string,
110
+ options: { signal: AbortSignal; onDelta: (partialText: string) => void },
111
+ ) => Promise<
112
+ | { status: "answered"; text: string }
113
+ | { status: "aborted"; text: string }
114
+ | { status: "refused"; reason: string }
115
+ | { status: "failed"; reason: string }
116
+ >;
80
117
  }
81
118
 
82
119
  export interface OverlayGeneralOpeners {
@@ -89,6 +126,9 @@ export interface OverlayGeneralOpeners {
89
126
  openMemory(): void;
90
127
  openView(initialFilter?: string): void;
91
128
  toggleDispatchBoard(): void;
129
+ openSideQuestion(question: string): void;
130
+ /** `/fleet run <name> [--var k=v ...]`: preview the plan, then dispatch on approval. */
131
+ startFleetRun(name: string, vars: Readonly<Record<string, string>>): void;
92
132
  }
93
133
 
94
134
  export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): OverlayGeneralOpeners {
@@ -100,6 +140,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
100
140
  const openMemoryOverlayFactory = deps.openMemoryOverlay ?? openMemoryOverlay;
101
141
  const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
102
142
  const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
143
+ const openSideQuestionOverlayFactory = deps.openSideQuestionOverlay ?? openSideQuestionOverlay;
103
144
 
104
145
  const openCost = (): void => {
105
146
  if (deps.transitions.state !== "closed") return;
@@ -305,6 +346,152 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
305
346
  return buildHint(entries);
306
347
  };
307
348
 
349
+ /**
350
+ * `/btw <question>`: one model round beside the session, rendered in an
351
+ * overlay and nowhere else. Esc aborts a round that is still streaming and
352
+ * closes one that has settled; both paths run through the same abort
353
+ * controller, so a cancelled round never keeps a stream alive behind a closed
354
+ * overlay.
355
+ */
356
+ const openSideQuestion = (question: string): void => {
357
+ if (deps.transitions.state !== "closed") return;
358
+ const ask = deps.askSideQuestion;
359
+ if (!ask) {
360
+ deps.notify("error", "/btw is not wired in this session", "btw:unavailable");
361
+ return;
362
+ }
363
+ const controller = new AbortController();
364
+ deps.transitions.state = "side-question";
365
+ const session = openSideQuestionOverlayFactory(deps.tui, {
366
+ question,
367
+ columns: deps.terminal.columns,
368
+ onClose: () => controller.abort(),
369
+ });
370
+ deps.transitions.handle = session;
371
+ deps.requestRender();
372
+ void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) })
373
+ .then((outcome) => {
374
+ if (outcome.status === "answered") {
375
+ session.settle({ kind: "answered", text: outcome.text });
376
+ return;
377
+ }
378
+ if (outcome.status === "aborted") {
379
+ session.settle({ kind: "aborted", text: outcome.text });
380
+ return;
381
+ }
382
+ session.settle({ kind: "error", reason: outcome.reason });
383
+ })
384
+ .catch((error: unknown) => {
385
+ session.settle({ kind: "error", reason: error instanceof Error ? error.message : String(error) });
386
+ });
387
+ };
388
+
389
+ /**
390
+ * `/fleet run <name>`: compile the plan, show it, and dispatch only what the
391
+ * operator accepted.
392
+ *
393
+ * Nothing is dispatched and nothing is written before the accept key. A
394
+ * contract that fails preflight opens the same overlay with its diagnostics
395
+ * and no accept action, so the only exit from a broken plan is to leave it.
396
+ */
397
+ const startFleetRun = (name: string, vars: Readonly<Record<string, string>>): void => {
398
+ if (deps.transitions.state !== "closed") return;
399
+ // Refused, never queued: an approved plan describes the workspace as it
400
+ // stands, and a turn still in flight is about to change it.
401
+ if (deps.isTurnInFlight?.() === true) {
402
+ deps.notify("warning", "a turn is in flight; /fleet run is refused rather than queued", "fleet-run:in-flight");
403
+ return;
404
+ }
405
+ const agents = deps.agents;
406
+ if (!agents) {
407
+ deps.notify("error", "/fleet run needs the agents domain, which is not wired in this session", "fleet-run:agents");
408
+ return;
409
+ }
410
+ const workspaceRoot = deps.getSessionMeta()?.cwd ?? process.cwd();
411
+ const roleFacts = agentRoleFactsResolver((id) => agents.getSpec(id));
412
+ const budget = deps.getBudgetPreflight?.();
413
+ const result = compileFleetRunPreview({
414
+ workspaceRoot,
415
+ name,
416
+ vars,
417
+ getAgentSpec: (agentId) => agents.getSpec(agentId),
418
+ roleFacts,
419
+ ...(budget ? { budget } : {}),
420
+ ...(deps.loadFleetSources ? { load: deps.loadFleetSources } : {}),
421
+ resolveRoute: (step) => {
422
+ const request: DispatchRequest = {
423
+ agentId: step.agentId,
424
+ executionRole: requestExecutionRole({ agentId: step.agentId, resolveFacts: roleFacts }),
425
+ task: step.task,
426
+ ...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
427
+ ...(step.target !== undefined ? { target: step.target } : {}),
428
+ ...(step.profile !== undefined ? { workerProfile: step.profile } : {}),
429
+ };
430
+ const resolution = deps.dispatch.preview?.(request);
431
+ if (!resolution) return null;
432
+ return {
433
+ targetId: resolution.targetId,
434
+ wireModelId: resolution.wireModelId,
435
+ nodeId: resolution.node.id,
436
+ };
437
+ },
438
+ });
439
+
440
+ deps.transitions.state = "fleet-run-approval";
441
+ deps.transitions.handle = (deps.openFleetRunApprovalOverlay ?? openFleetRunApprovalOverlay)(deps.tui, {
442
+ subject: result.ok ? { ok: true, preview: result.preview } : { ok: false, name, diagnostics: result.diagnostics },
443
+ columns: deps.terminal.columns,
444
+ onAccept: () => {
445
+ deps.closeOverlay();
446
+ if (result.ok) dispatchFleetRun(result.preview, agents);
447
+ },
448
+ onCancel: () => {
449
+ deps.closeOverlay();
450
+ deps.notify("info", `fleet ${name}: cancelled; nothing was dispatched`, "fleet-run:cancelled");
451
+ },
452
+ });
453
+ deps.requestRender();
454
+ };
455
+
456
+ const dispatchFleetRun = (preview: FleetRunPreview, agents: AgentsContract): void => {
457
+ const fleetRootId = `fleet-${randomBytes(6).toString("hex")}`;
458
+ const run = deps.runFleet ?? executeFleetRun;
459
+ deps.notify(
460
+ "info",
461
+ `fleet ${preview.name}: root=${fleetRootId} plan=${preview.planHash.slice(0, 12)} steps=${preview.plan.steps.length}`,
462
+ "fleet-run:started",
463
+ );
464
+ void run({
465
+ plan: preview.plan,
466
+ contractName: preview.name,
467
+ commands: preview.commands,
468
+ workspaceRoot: deps.getSessionMeta()?.cwd ?? process.cwd(),
469
+ fleetRootId,
470
+ dispatch: deps.dispatch,
471
+ agents: { getSpec: (agentId) => agents.getSpec(agentId) },
472
+ attributionEnabled: deps.getSettings?.().attribution.gitCommits ?? true,
473
+ vars: preview.vars,
474
+ onStepDispatched: (event) => {
475
+ deps.setFleetRunPhase?.(event.assignmentId, { wave: event.waveIndex, stepId: event.stepId });
476
+ },
477
+ onNotice: (text) => deps.stderr(`[fleet ${preview.name}] ${text}\n`),
478
+ })
479
+ .then((outcome) => {
480
+ deps.notify(
481
+ outcome.cleanRun ? "success" : "warning",
482
+ `fleet ${preview.name}: ${outcome.succeededStepCount}/${outcome.requiredStepCount} steps succeeded, cost $${outcome.totalCostUsd.toFixed(4)}`,
483
+ "fleet-run:settled",
484
+ );
485
+ })
486
+ .catch((error: unknown) => {
487
+ deps.notify(
488
+ "error",
489
+ `fleet ${preview.name}: ${error instanceof Error ? error.message : String(error)}`,
490
+ "fleet-run:failed",
491
+ );
492
+ });
493
+ };
494
+
308
495
  return {
309
496
  openCost,
310
497
  openContextView,
@@ -315,5 +502,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
315
502
  openMemory,
316
503
  openView,
317
504
  toggleDispatchBoard,
505
+ openSideQuestion,
506
+ startFleetRun,
318
507
  };
319
508
  }
@@ -25,7 +25,10 @@ export type OverlayState =
25
25
  | "prompts"
26
26
  | "extensions"
27
27
  | "interop"
28
- | "skills-hub";
28
+ | "skills-hub"
29
+ | "side-question"
30
+ | "handoff-review"
31
+ | "fleet-run-approval";
29
32
 
30
33
  export interface PermissionOverlayKeyDeps {
31
34
  cancelPermission: () => void;
@@ -220,6 +223,19 @@ export function routeOverlayKey(
220
223
  routeCostOverlayKey(data, deps);
221
224
  return true;
222
225
  }
226
+ // Esc is the only key the side-question overlay answers: it aborts a round
227
+ // that is still streaming and closes one that has settled. Everything else is
228
+ // swallowed so a keystroke cannot reach the composer behind it.
229
+ if (overlayState === "side-question") {
230
+ routeCostOverlayKey(data, deps);
231
+ return true;
232
+ }
233
+ // The handoff review overlay owns Enter, `e`, the arrows, and Esc itself, so
234
+ // every key goes to its own focus box rather than through the router.
235
+ if (overlayState === "handoff-review") return false;
236
+ // The fleet-run approval overlay owns Enter, the arrows, and Esc itself, so
237
+ // every key goes to its own focus box rather than through the router.
238
+ if (overlayState === "fleet-run-approval") return false;
223
239
  if (overlayState === "context-reset") return false;
224
240
  if (overlayState === "tasks") return false;
225
241
  if (overlayState === "decisions") return false;
@@ -1,3 +1,4 @@
1
+ import type { LibraryEntryKind } from "../domains/resources/index.js";
1
2
  import { appendNotice } from "./command-output.js";
2
3
  import { createOverlayAskUserLifecycle, type OverlayAskUserLifecycle } from "./overlay-ask-user-lifecycle.js";
3
4
  import { createOverlayAuthLifecycle } from "./overlay-auth-lifecycle.js";
@@ -26,6 +27,7 @@ import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings
26
27
  // application composition root no longer owns the mutable overlay state.
27
28
  export type OverlayLifecycleApplicationDeps = Pick<
28
29
  import("./interactive-application.js").InteractiveDeps,
30
+ | "agents"
29
31
  | "bus"
30
32
  | "chat"
31
33
  | "commitSetting"
@@ -42,12 +44,14 @@ export type OverlayLifecycleApplicationDeps = Pick<
42
44
  | "observability"
43
45
  | "onContextClear"
44
46
  | "onForkSession"
47
+ | "onNewSession"
45
48
  | "onResumeSession"
46
49
  | "onSelectModel"
47
50
  | "providers"
48
51
  | "readSessionEntries"
49
52
  | "registerAskUserHandler"
50
53
  | "resources"
54
+ | "scheduling"
51
55
  | "session"
52
56
  | "stateDir"
53
57
  | "supersedeDecision"
@@ -75,9 +79,17 @@ export interface OverlayLifecycleRuntimeDeps {
75
79
  notify: (level: import("./interactive-subscriptions.js").InteractiveNoticeLevel, text: string, key?: string) => void;
76
80
  terminal: Pick<import("../engine/tui.js").ProcessTerminal, "columns">;
77
81
  dispatchBoard: ReturnType<typeof import("./dispatch-board.js").createDispatchBoardView>;
82
+ /** Record a dispatched fleet step's plan position for the board's phase column. */
83
+ setFleetRunPhase?: (runId: string, phase: { wave: number; stepId: string }) => void;
78
84
  chatPanel: import("./chat-panel.js").ChatPanel;
79
85
  /** Clears the transcript and every view folded alongside it; the session overlays call it before a replay. */
80
86
  resetTranscript: () => void;
87
+ /**
88
+ * Stop and restart the terminal around a child process that owns the screen.
89
+ * `/handoff` uses it for the external editor, the same way the composer's
90
+ * own `$EDITOR` opener does.
91
+ */
92
+ suspendTerminal: <T>(run: () => T) => T;
81
93
  io: import("./slash-commands.js").RunIo;
82
94
  readStructuredEntries: (sessionId: string) => import("../domains/session/index.js").SessionEntry[];
83
95
  announceTaskMemorySeedOffer: () => void;
@@ -86,6 +98,11 @@ export interface OverlayLifecycleRuntimeDeps {
86
98
  keybindings: ReturnType<typeof import("./keybinding-manager.js").createKeybindingManager>;
87
99
  editor: Pick<import("./clio-editor.js").ClioEditor, "getText" | "render" | "setText">;
88
100
  getSlashContext: () => import("./slash-commands.js").SlashCommandContext;
101
+ /**
102
+ * A worker permission or ask_user request parked waiting for the operator.
103
+ * Wired to the desktop notification; absent hosts simply do not notify.
104
+ */
105
+ onOperatorParked?: () => void;
89
106
  showOverlayFrame?: typeof showClioOverlayFrame;
90
107
  openAuthDialog?: typeof import("./overlays/auth-dialog.js").openAuthDialog;
91
108
  openAskUserOverlay?: typeof import("./overlays/ask-user.js").openAskUserOverlay;
@@ -127,6 +144,11 @@ export interface OverlayLifecycleController {
127
144
  openDecisionsOverlayState(): void;
128
145
  openMemoryOverlayState(): void;
129
146
  openViewOverlayState(initialFilter?: string): void;
147
+ /** `/btw <question>`: one side-question round rendered in its own overlay. */
148
+ openSideQuestionOverlayState(question: string): void;
149
+ /** `/handoff <goal>`: extract, review, and seed a successor session. */
150
+ startHandoffState(goal: string): void;
151
+ startFleetRunState(name: string, vars: Readonly<Record<string, string>>): void;
130
152
  openModelOverlayState(): void;
131
153
  openSettingsOverlayState(section?: SettingsSectionId, rowId?: SettingsCenterRowId): void;
132
154
  openResumeOverlayState(): void;
@@ -134,7 +156,7 @@ export interface OverlayLifecycleController {
134
156
  openMessagePickerOverlayState(): void;
135
157
  openHelpOverlayState(query?: string): void;
136
158
  openAgentsOverlayState(): void;
137
- openSkillsHubState(): void;
159
+ openSkillsHubState(tab?: LibraryEntryKind): void;
138
160
  openPromptsOverlayState(): void;
139
161
  openExtensionsOverlayState(): void;
140
162
  openInteropOverlayState(): void;
@@ -253,6 +275,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
253
275
  source: "stream_cancel",
254
276
  auditReason: "operator stopped the turn at a permission prompt",
255
277
  }),
278
+ ...(deps.onOperatorParked ? { onOperatorParked: deps.onOperatorParked } : {}),
256
279
  });
257
280
 
258
281
  overlayAskUser = createOverlayAskUserLifecycle({
@@ -270,6 +293,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
270
293
  requestRender: () => tui.requestRender(),
271
294
  ...(deps.app.registerAskUserHandler ? { registerHandler: deps.app.registerAskUserHandler } : {}),
272
295
  ...(openAskUserOverlayFactory ? { openAskUserOverlay: openAskUserOverlayFactory } : {}),
296
+ ...(deps.onOperatorParked ? { onOperatorParked: deps.onOperatorParked } : {}),
273
297
  });
274
298
 
275
299
  const overlayModelSelectors = createOverlayModelSelectors({
@@ -318,6 +342,10 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
318
342
  getSlashNotice: () => deps.getSlashContext().notice,
319
343
  ...(deps.app.onResumeSession ? { onResumeSession: deps.app.onResumeSession } : {}),
320
344
  ...(deps.app.onForkSession ? { onForkSession: deps.app.onForkSession } : {}),
345
+ ...(deps.app.onNewSession ? { onNewSession: deps.app.onNewSession } : {}),
346
+ ...(deps.app.getDecisionBoard ? { getDecisionBoard: deps.app.getDecisionBoard } : {}),
347
+ terminal: deps.terminal,
348
+ suspendTerminal: deps.suspendTerminal,
321
349
  announceTaskMemorySeedOffer,
322
350
  sessionUsage: deps.app.observability,
323
351
  ...(setLastTurnSummary ? { setLastTurnSummary } : {}),
@@ -331,6 +359,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
331
359
  ...(openCwdFallbackOverlayFactory ? { openCwdFallbackOverlay: openCwdFallbackOverlayFactory } : {}),
332
360
  });
333
361
 
362
+ const scheduling = deps.app.scheduling;
334
363
  const overlayGeneralOpeners = createOverlayGeneralOpeners({
335
364
  tui,
336
365
  transitions: overlayTransitions,
@@ -370,6 +399,11 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
370
399
  ...(openDecisionsOverlayFactory ? { openDecisionsOverlay: openDecisionsOverlayFactory } : {}),
371
400
  ...(openMemoryOverlayFactory ? { openMemoryOverlay: openMemoryOverlayFactory } : {}),
372
401
  ...(openViewOverlayFactory ? { openViewOverlay: openViewOverlayFactory } : {}),
402
+ askSideQuestion: (question, options) => deps.app.chat.askSideQuestion(question, options),
403
+ ...(deps.app.agents ? { agents: deps.app.agents } : {}),
404
+ ...(scheduling ? { getBudgetPreflight: () => scheduling.preflight() } : {}),
405
+ isTurnInFlight: () => deps.app.chat.isStreaming(),
406
+ ...(deps.setFleetRunPhase ? { setFleetRunPhase: deps.setFleetRunPhase } : {}),
373
407
  });
374
408
 
375
409
  const openResumeOverlayState = overlaySessions.openResume;
@@ -383,6 +417,9 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
383
417
  const openDecisionsOverlayState = overlayGeneralOpeners.openDecisions;
384
418
  const openMemoryOverlayState = overlayGeneralOpeners.openMemory;
385
419
  const openViewOverlayState = overlayGeneralOpeners.openView;
420
+ const openSideQuestionOverlayState = overlayGeneralOpeners.openSideQuestion;
421
+ const startFleetRunState = overlayGeneralOpeners.startFleetRun;
422
+ const startHandoffState = overlaySessions.startHandoff;
386
423
  const toggleDispatchBoardOverlay = overlayGeneralOpeners.toggleDispatchBoard;
387
424
 
388
425
  return {
@@ -402,6 +439,9 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
402
439
  openDecisionsOverlayState,
403
440
  openMemoryOverlayState,
404
441
  openViewOverlayState,
442
+ openSideQuestionOverlayState,
443
+ startFleetRunState,
444
+ startHandoffState,
405
445
  openModelOverlayState: overlayModelSelectors.openModelOverlayState,
406
446
  openSettingsOverlayState: overlayModelSelectors.openSettingsOverlayState,
407
447
  openResumeOverlayState,
@@ -50,6 +50,12 @@ export interface OverlayPermissionLifecycleDeps {
50
50
  requestRender(): void;
51
51
  /** End the in-flight run, carrying the text the operator will see. */
52
52
  stopActiveTurn(reason: string): void;
53
+ /**
54
+ * A worker permission request parked waiting for the operator. Fired once per
55
+ * request, on the same dedup the operator-facing surfaces use, so a
56
+ * re-presented dialog does not notify twice.
57
+ */
58
+ onOperatorParked?(): void;
53
59
  }
54
60
 
55
61
  export interface OverlayPermissionLifecycle {
@@ -184,6 +190,8 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
184
190
  let pendingWorker: Pick<WorkerEscalationEntry, "agentId" | "requestId" | "runId"> | null = null;
185
191
  const workerQueue: WorkerEscalationEntry[] = [];
186
192
  const announcedRequestIds = new Set<string>();
193
+ /** Worker escalations already surfaced, so one request parks the operator once. */
194
+ const announcedWorkerRequestIds = new Set<string>();
187
195
  let confirmed = false;
188
196
  let stopping = false;
189
197
 
@@ -245,8 +253,10 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
245
253
  const unsubscribeWorker = deps.bus.on(BusChannels.PermissionRequested, (payload) => {
246
254
  const entry = workerEscalationEntry(payload, deps.getAutonomy());
247
255
  if (!entry) return;
256
+ if (!markPermissionRequestSurfaced(announcedWorkerRequestIds, entry.requestId)) return;
248
257
  const notice = workerEscalationNotice(payload);
249
258
  if (notice !== null) deps.appendNotice(notice.level, notice.text);
259
+ deps.onOperatorParked?.();
250
260
  workerQueue.push(entry);
251
261
  maybeOpenWorker();
252
262
  });
@@ -1,4 +1,4 @@
1
- import type { ResourcesContract } from "../domains/resources/index.js";
1
+ import type { LibraryEntryKind, ResourcesContract } from "../domains/resources/index.js";
2
2
  import { installSkill as installMarketplaceSkill } from "../domains/resources/skills/marketplace.js";
3
3
  import type { TUI } from "../engine/tui.js";
4
4
  import type { ClioEditor } from "./clio-editor.js";
@@ -32,7 +32,7 @@ export interface OverlayResourceOpenersDeps {
32
32
  export interface OverlayResourceOpeners {
33
33
  openHelpOverlayState(query?: string): void;
34
34
  openAgentsOverlayState(): void;
35
- openSkillsHubState(): void;
35
+ openSkillsHubState(tab?: LibraryEntryKind): void;
36
36
  openPromptsOverlayState(): void;
37
37
  openExtensionsOverlayState(): void;
38
38
  openInteropOverlayState(): void;
@@ -61,10 +61,18 @@ export function createOverlayResourceOpeners(deps: OverlayResourceOpenersDeps):
61
61
  deps.tui.requestRender();
62
62
  };
63
63
 
64
- const openSkillsHubState = (): void => {
64
+ const openSkillsHubState = (tab?: LibraryEntryKind): void => {
65
65
  if (deps.transitions.state !== "closed") return;
66
66
  deps.transitions.state = "skills-hub";
67
67
  deps.transitions.handle = openSkills(deps.tui, {
68
+ ...(tab ? { initialTab: tab } : {}),
69
+ // A fleet's `use` is its approval preview, which is a surface of its own.
70
+ // The hub closes first so the preview owns the overlay slot, exactly as
71
+ // `/fleet run <name>` typed into the composer would.
72
+ openFleetRun: (name) => {
73
+ deps.closeOverlay();
74
+ deps.getSlashContext().startFleetRun?.(name, {});
75
+ },
68
76
  listSkills: () => deps.resources?.skills(process.cwd()) ?? { items: [], diagnostics: [] },
69
77
  setEditorText: (text) => {
70
78
  deps.editor.setText(text);