@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
@@ -10,10 +10,12 @@ import type { DispatchContract } from "../domains/dispatch/contract.js";
10
10
  import type { ExtensionsContract } from "../domains/extensions/index.js";
11
11
  import type { InteropContract } from "../domains/interop/index.js";
12
12
  import type { TaskMemoryOperatorStatus } from "../domains/memory/index.js";
13
+ import { openDetachedBatchViews } from "../domains/middleware/index.js";
13
14
  import type { ObservabilityContract } from "../domains/observability/index.js";
14
15
  import type { ProvidersContract, ThinkingLevel } from "../domains/providers/index.js";
15
16
  import type { ResourcesContract } from "../domains/resources/index.js";
16
17
  import type { FleetNodeSnapshot } from "../domains/scheduling/cluster.js";
18
+ import type { SchedulingContract } from "../domains/scheduling/contract.js";
17
19
  import type { DecisionLedgerEntry } from "../domains/session/entries.js";
18
20
  import type { SessionContract, SessionEntry, TaskBoardSnapshot } from "../domains/session/index.js";
19
21
  import type { ShareContract } from "../domains/share/index.js";
@@ -26,8 +28,10 @@ import { APPLICATION_DOUBLE_TAP_MS, type ApplicationController } from "./applica
26
28
  import type { ChatLoop, ChatLoopEvent } from "./chat-loop.js";
27
29
  import { emitCommandNotice } from "./command-fallbacks.js";
28
30
  import { appendNotice } from "./command-output.js";
31
+ import { dispatchCouncilThroughRegistry } from "./council-dispatch.js";
29
32
  import { createDispatchSteering } from "./dispatch-steering.js";
30
33
  import { createEditorSubmitController } from "./editor-submit.js";
34
+ import { createInteractiveDesktopNotifications } from "./footer/notifications.js";
31
35
  import { createInteractiveEventProjection } from "./interactive-event-projection.js";
32
36
  import { createInteractiveInputRuntime } from "./interactive-input-runtime.js";
33
37
  import { createInteractivePresentation } from "./interactive-presentation.js";
@@ -108,6 +112,8 @@ export interface InteractiveDeps {
108
112
  providers: ProvidersContract;
109
113
  dispatch: DispatchContract;
110
114
  agents?: AgentsContract;
115
+ /** Session budget state `/fleet run` shows as the ceiling a plan is admitted under. */
116
+ scheduling?: SchedulingContract;
111
117
  observability: ObservabilityContract;
112
118
  chat: ChatLoop;
113
119
  /** Fired once after the first real TUI render transaction issued all of its terminal writes. */
@@ -541,6 +547,16 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
541
547
  } = presentation;
542
548
  refreshPresentationFooter = () => footer.refresh();
543
549
  const agentProgress = createAgentProgress(terminal);
550
+ // Desktop notifications are a protocol write on the terminal owner, issued
551
+ // outside any render transaction, so the sequence carries frameId null and
552
+ // never lands inside a frame. A non-TTY process never emits one even when
553
+ // `CLIO_CODER_INTERACTIVE=1` forced the interactive surface on.
554
+ const desktopNotifications = createInteractiveDesktopNotifications({
555
+ write: (data) => terminal.write(data),
556
+ enabled: () => deps.getSettings?.().terminal.notify ?? false,
557
+ interactiveTty: () => process.stdout.isTTY === true,
558
+ getOpenBatches: () => openDetachedBatchViews(deps.dispatch),
559
+ });
544
560
  const busNoticeSink = {
545
561
  appendReplayBlock: (renderBlock: Parameters<typeof chatPanel.appendReplayBlock>[0]) =>
546
562
  chatRenderer.mutate(() => chatPanel.appendReplayBlock(renderBlock), "bus-notice"),
@@ -570,6 +586,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
570
586
  setLastTurnSummary: (summary) => presentation.setLastTurnSummary(summary),
571
587
  startTerminalProgress: () => agentProgress.start(),
572
588
  stopTerminalProgress: () => agentProgress.stop(),
589
+ onTurnEnded: () => desktopNotifications.turnEnded(),
573
590
  refreshLiveWorkspaceGit,
574
591
  refreshFooter: () => footer.refresh(),
575
592
  requestRender: () => tui.requestRender(),
@@ -587,6 +604,10 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
587
604
  // The overlay reads the report this process already produced at boot; it
588
605
  // never probes on a keystroke.
589
606
  const interop = deps.interop;
607
+ // `/council` reaches execution only through the tool registry, so the
608
+ // approval overlay this application already installs on it is the same one an
609
+ // operator-typed council parks.
610
+ const toolRegistry = deps.toolRegistry;
590
611
  const interopSurface = interop ? interopOverlaySurface(interop, (level, text) => notify(level, text)) : null;
591
612
  const slashRuntime = createInteractiveSlashRuntime({
592
613
  io,
@@ -626,8 +647,12 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
626
647
  readStructuredEntries,
627
648
  expandSubmit: (text) => expandInteractiveSubmitAsync(text, deps.resources),
628
649
  openAskUser: (questions, options) => openAskUserOverlayState(questions, options),
629
- openSkillsHub: () => openSkillsHubState(),
650
+ openSkillsHub: (tab) => openSkillsHubState(tab),
630
651
  openCost: () => openCostOverlayState(),
652
+ openSideQuestion: (question) => openSideQuestionOverlayState(question),
653
+ startHandoff: (goal) => startHandoffState(goal),
654
+ startFleetRun: (name, vars) => startFleetRunState(name, vars),
655
+ ...(toolRegistry ? { runCouncilDispatch: (args) => dispatchCouncilThroughRegistry(toolRegistry, args) } : {}),
631
656
  openContextView: () => openContextViewOverlayState(),
632
657
  openTasks: () => openTasksOverlayState(),
633
658
  openDecisions: () => openDecisionsOverlayState(),
@@ -705,6 +730,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
705
730
  notify,
706
731
  terminal,
707
732
  dispatchBoard,
733
+ setFleetRunPhase: (runId, phase) => dispatchBoardStore.setFleetPhase(runId, phase),
708
734
  chatPanel,
709
735
  resetTranscript,
710
736
  io,
@@ -714,11 +740,26 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
714
740
  keybindings,
715
741
  editor,
716
742
  getSlashContext: () => slashRuntime.context,
743
+ // One terminal owner, so the external editor a handoff review opens gets
744
+ // the screen the same way the composer's own `$EDITOR` opener does.
745
+ suspendTerminal: (run) => {
746
+ try {
747
+ tui.stop();
748
+ return run();
749
+ } finally {
750
+ tui.start();
751
+ tui.requestRender(true);
752
+ }
753
+ },
754
+ onOperatorParked: () => desktopNotifications.approvalParked(),
717
755
  });
718
756
  const {
719
757
  closeOverlay,
720
758
  openAskUserOverlayState,
721
759
  openCostOverlayState,
760
+ openSideQuestionOverlayState,
761
+ startHandoffState,
762
+ startFleetRunState,
722
763
  openContextViewOverlayState,
723
764
  openContextResetOverlayState,
724
765
  openTasksOverlayState,
@@ -810,6 +851,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
810
851
  renderContextIsland: interactiveTickers.renderContextIsland,
811
852
  requestRender: () => tui.requestRender(),
812
853
  notify,
854
+ onDispatchSettled: () => desktopNotifications.dispatchSettled(),
813
855
  // The reducer mutates one state object per assignment, so the panel is
814
856
  // handed that object rather than a copy: a streamed delta reaches the
815
857
  // screen by invalidating a cached render, not by rebuilding the entry.
@@ -3,6 +3,7 @@ import {
3
3
  type ConfigReloadFailedPayload,
4
4
  type ContextPrunedPayload,
5
5
  type ContextWarningPayload,
6
+ type DispatchScopeNoticePayload,
6
7
  type LoopBlockedPayload,
7
8
  type RuntimeNoticePayload,
8
9
  type ToolBudgetExceededPayload,
@@ -77,6 +78,11 @@ export interface InteractiveEventProjectionDeps {
77
78
  setLastTurnSummary: (summary: TurnSummary) => void;
78
79
  startTerminalProgress: () => void;
79
80
  stopTerminalProgress: () => void;
81
+ /**
82
+ * One model turn reached its end. Wired to the desktop notification; absent
83
+ * on hosts that do not own a terminal to notify through.
84
+ */
85
+ onTurnEnded?: () => void;
80
86
  refreshLiveWorkspaceGit: (force: boolean) => void;
81
87
  refreshFooter: () => void;
82
88
  requestRender: () => void;
@@ -214,7 +220,10 @@ export function createInteractiveEventProjection(deps: InteractiveEventProjectio
214
220
  deps.chat.onEvent((event) => {
215
221
  const showProgress = deps.getSettings?.().terminal.showTerminalProgress ?? false;
216
222
  if (event.type === "agent_start" && showProgress) deps.startTerminalProgress();
217
- else if (event.type === "agent_end") deps.stopTerminalProgress();
223
+ else if (event.type === "agent_end") {
224
+ deps.stopTerminalProgress();
225
+ deps.onTurnEnded?.();
226
+ }
218
227
  }),
219
228
  deps.bus.on(BusChannels.RunAborted, () => {
220
229
  deps.stopTerminalProgress();
@@ -263,6 +272,19 @@ export function createInteractiveEventProjection(deps: InteractiveEventProjectio
263
272
  );
264
273
 
265
274
  remainingUnsubscribers.push(
275
+ deps.bus.on(BusChannels.DispatchScopeNotice, (payload) => {
276
+ const event = payload as DispatchScopeNoticePayload | null | undefined;
277
+ if (
278
+ !event ||
279
+ typeof event !== "object" ||
280
+ !["typed_scope_replaced_inferred_paths", "legacy_scope_inferred", "legacy_scope_empty"].includes(event.code) ||
281
+ typeof event.message !== "string"
282
+ ) {
283
+ return;
284
+ }
285
+ deps.appendTranscriptNotice("warn", event.message);
286
+ deps.requestRender();
287
+ }),
266
288
  deps.bus.on(BusChannels.LoopBlocked, (payload) => {
267
289
  const event = payload as LoopBlockedPayload | null | undefined;
268
290
  if (!event || typeof event !== "object" || typeof event.tool !== "string" || typeof event.repeatCount !== "number") {
@@ -16,9 +16,12 @@ import {
16
16
  type ThinkingLevel,
17
17
  thinkingLevelChoiceLabel,
18
18
  } from "../domains/providers/index.js";
19
- import type { ResourcesContract } from "../domains/resources/index.js";
19
+ import type { LibraryEntryKind, ResourcesContract } from "../domains/resources/index.js";
20
20
  import { installSkill } from "../domains/resources/skills/marketplace.js";
21
+ import { foldDecisionBoard } from "../domains/session/decision-board.js";
21
22
  import type { SessionContract, SessionEntry } from "../domains/session/index.js";
23
+ import { foldTaskBoard } from "../domains/session/task-board.js";
24
+ import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
22
25
  import type { ShareContract } from "../domains/share/index.js";
23
26
  import type { UserTasksStore } from "../domains/user-tasks/store.js";
24
27
  import { stripTerminalSequences } from "../engine/tui.js";
@@ -32,9 +35,11 @@ import { appendNotice, appendOperatorCommand } from "./command-output.js";
32
35
  import { runOperatorRecall } from "./context-recall-command.js";
33
36
  import { renderSessionHtml } from "./export-html/index.js";
34
37
  import { dateLocal } from "./format-time.js";
38
+ import type { OracleDigestSources } from "./oracle.js";
35
39
  import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings.js";
36
40
  import {
37
41
  type ContextClearCommandOptions,
42
+ type CouncilDispatchOutcome,
38
43
  dispatchSlashCommand,
39
44
  type InitCommandOptions,
40
45
  parseSlashCommand,
@@ -108,8 +113,21 @@ export interface InteractiveSlashRuntimeDeps {
108
113
  session?: Pick<SessionContract, "tree" | "current" | "appendEntry">;
109
114
  expandSubmit: (text: string) => Promise<InteractiveSlashSubmitExpansion>;
110
115
  openAskUser: AskUserHandler;
111
- openSkillsHub: () => void;
116
+ openSkillsHub: (tab?: LibraryEntryKind) => void;
112
117
  openCost: () => void;
118
+ /** Run one `/btw` side question in its own overlay. */
119
+ openSideQuestion: (question: string) => void;
120
+ /** Run one `/handoff <goal>`: extract, review, and seed a successor session. */
121
+ startHandoff: (goal: string) => void;
122
+ /** Run one `/fleet run <name>`: approval preview first, dispatch only on accept. */
123
+ startFleetRun?: (name: string, vars: Readonly<Record<string, string>>) => void;
124
+ /**
125
+ * Run one `/council <task>`: the prepared dispatch-tool arguments go through
126
+ * the ordinary tool-admission path, so supervised autonomy parks the call and
127
+ * the approval overlay opens before any member runs. Absent on a host with no
128
+ * tool registry, in which case `/council` says so.
129
+ */
130
+ runCouncilDispatch?: (args: Readonly<Record<string, unknown>>) => Promise<CouncilDispatchOutcome>;
113
131
  openContextView: () => void;
114
132
  openTasks: () => void;
115
133
  openDecisions: () => void;
@@ -186,6 +204,29 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
186
204
  const resources = deps.resources;
187
205
  const userTasks = deps.userTasks;
188
206
 
207
+ /**
208
+ * The record `/oracle` briefs its advisor on. Entries are filtered to the
209
+ * active branch first, the same way the task board, compaction, and the
210
+ * finish contract read them (issue #94): after a `/tree` switch the raw file
211
+ * still holds the abandoned turns, and an unscoped fold would brief the
212
+ * advisor on decisions the operator walked away from.
213
+ */
214
+ const oracleBriefingSources = (): Omit<OracleDigestSources, "question"> => {
215
+ const sessionId = deps.chat.getSessionId();
216
+ if (sessionId === null) return { decisions: [], tasks: [], compactionSummary: null };
217
+ const leafTurnId = deps.session?.tree(sessionId).leafId ?? undefined;
218
+ const entries = filterEntriesToActivePath(deps.readStructuredEntries(sessionId), leafTurnId);
219
+ let compactionSummary: string | null = null;
220
+ for (const entry of entries) {
221
+ if (entry.kind === "compactionSummary") compactionSummary = entry.summary;
222
+ }
223
+ return {
224
+ decisions: foldDecisionBoard(entries),
225
+ tasks: foldTaskBoard(entries)?.tasks ?? [],
226
+ compactionSummary,
227
+ };
228
+ };
229
+
189
230
  const appendCommandNotice: SlashCommandContext["notice"] = (level, text) => {
190
231
  appendNotice(level, text, {
191
232
  appendReplayBlock: (renderBlock) => deps.chatPanel.appendReplayBlock(renderBlock),
@@ -342,6 +383,15 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
342
383
  : deps.share.importArchive(resolve(archivePath), importOptions);
343
384
  },
344
385
  openCost: deps.openCost,
386
+ openSideQuestion: deps.openSideQuestion,
387
+ // `/oracle` is refused, never queued, while a turn streams: the digest it
388
+ // packs describes the record as it stands.
389
+ isTurnInFlight: () => deps.chat.isStreaming(),
390
+ oracleBriefing: oracleBriefingSources,
391
+ startHandoff: deps.startHandoff,
392
+ ...(deps.startFleetRun ? { startFleetRun: deps.startFleetRun } : {}),
393
+ ...(deps.getSettings ? { getWorkerRosters: () => deps.getSettings?.().workers.rosters ?? {} } : {}),
394
+ ...(deps.runCouncilDispatch ? { runCouncilDispatch: deps.runCouncilDispatch } : {}),
345
395
  openContextView: deps.openContextView,
346
396
  openTasks: deps.openTasks,
347
397
  openDecisions: deps.openDecisions,
@@ -37,6 +37,12 @@ export interface InteractiveSubscriptionsDeps {
37
37
  recordWorkerRun?: (fields: WorkerRunEntryFields) => void;
38
38
  /** Sealed-receipt reader, injected by tests. Defaults to `<state>/receipts/<runId>.json`. */
39
39
  readWorkerReceipt?: WorkerReceiptReader;
40
+ /**
41
+ * A dispatch run reached a terminal state. The desktop notification uses it
42
+ * to look for a detached batch that settled with this run; nothing else
43
+ * derives state from it.
44
+ */
45
+ onDispatchSettled?: () => void;
40
46
  }
41
47
 
42
48
  export interface InteractiveSubscriptions {
@@ -89,8 +95,14 @@ export function createInteractiveSubscriptions(deps: InteractiveSubscriptionsDep
89
95
  }),
90
96
  ),
91
97
  deps.bus.on(BusChannels.RunAborted, folded(workers.aborted)),
92
- deps.bus.on(BusChannels.DispatchCompleted, folded(workers.completed)),
93
- deps.bus.on(BusChannels.DispatchFailed, folded(workers.failed)),
98
+ deps.bus.on(BusChannels.DispatchCompleted, (payload) => {
99
+ folded(workers.completed)(payload);
100
+ deps.onDispatchSettled?.();
101
+ }),
102
+ deps.bus.on(BusChannels.DispatchFailed, (payload) => {
103
+ folded(workers.failed)(payload);
104
+ deps.onDispatchSettled?.();
105
+ }),
94
106
  deps.bus.on(BusChannels.ContextActivity, () => {
95
107
  deps.refreshFooter();
96
108
  deps.renderContextIsland();
@@ -0,0 +1,179 @@
1
+ /**
2
+ * `/oracle <question>`: the briefing an advisor is given, and the note its
3
+ * answer becomes.
4
+ *
5
+ * Oracle never receives a forked transcript. A transcript is the session's raw
6
+ * history, and handing it to an advisor makes the advisor agree with whatever
7
+ * the session already believes, which is the opposite of what an operator asks
8
+ * an oracle for. It is briefed on the record instead: the decisions the
9
+ * operator settled, the work the board still owes, and the last compaction
10
+ * summary when one exists.
11
+ *
12
+ * Pure: no I/O, no bus, no session, no dispatch. The caller owns the run.
13
+ */
14
+
15
+ import type { OracleResult } from "../domains/agents/index.js";
16
+ import type { DecisionLedgerEntry } from "../domains/session/entries.js";
17
+ import type { TaskBoardTask } from "../domains/session/task-board.js";
18
+
19
+ /** The recipe `/oracle` dispatches. Shadow, so `/run` can never reach it. */
20
+ export const ORACLE_AGENT_ID = "oracle";
21
+
22
+ /**
23
+ * Total bound on the packed briefing, in UTF-8 bytes. Well under the 64 KiB
24
+ * an internal-origin dispatch briefing may carry, because the point of the
25
+ * digest is that a small local advisor can hold all of it at once.
26
+ */
27
+ export const ORACLE_DIGEST_MAX_BYTES = 12 * 1024;
28
+
29
+ /**
30
+ * Per-section byte caps. They sum to 11776, which leaves 512 bytes of the
31
+ * total for the section headers and blank lines the packer writes between
32
+ * them. Each section is clamped on its own before the whole digest is clamped
33
+ * again, so one runaway section can never starve the others: a 40 KiB
34
+ * compaction summary loses its own tail rather than the question's.
35
+ */
36
+ export const ORACLE_DECISIONS_MAX_BYTES = 5120;
37
+ export const ORACLE_TASKS_MAX_BYTES = 3072;
38
+ export const ORACLE_COMPACTION_MAX_BYTES = 2048;
39
+ export const ORACLE_QUESTION_MAX_BYTES = 1536;
40
+
41
+ /** Row caps, applied before the byte caps. Newest decisions and lowest task ids first. */
42
+ export const ORACLE_DECISIONS_MAX_ROWS = 24;
43
+ export const ORACLE_TASKS_MAX_ROWS = 24;
44
+
45
+ /** Appended wherever a cap cut content, so the advisor knows it is reading a tail-less record. */
46
+ export const ORACLE_TRUNCATION_MARKER = "\n[truncated]";
47
+
48
+ export const ORACLE_NO_DECISIONS = "(no settled decisions on this branch)";
49
+ export const ORACLE_NO_TASKS = "(no open tasks on the board)";
50
+
51
+ export interface OracleDigestSources {
52
+ /** Settled interviews, newest first, exactly as the decision board folds them. */
53
+ decisions: ReadonlyArray<DecisionLedgerEntry>;
54
+ /** The live task board's tasks; the packer keeps the open ones. */
55
+ tasks: ReadonlyArray<TaskBoardTask>;
56
+ /** The most recent compaction summary on the active branch, or null when the session never compacted. */
57
+ compactionSummary: string | null;
58
+ question: string;
59
+ }
60
+
61
+ export interface OracleDigest {
62
+ text: string;
63
+ /** True when any cap cut content. The digest says so in its own text as well. */
64
+ truncated: boolean;
65
+ }
66
+
67
+ function byteLength(text: string): number {
68
+ return Buffer.byteLength(text, "utf8");
69
+ }
70
+
71
+ /**
72
+ * Clamp to `maxBytes` including the marker, never past a UTF-8 code point. A
73
+ * marker that pushed the result over its own cap would make the section caps
74
+ * unable to add up to the total, which is the one property the packer relies
75
+ * on.
76
+ */
77
+ function clamp(text: string, maxBytes: number): { text: string; truncated: boolean } {
78
+ if (byteLength(text) <= maxBytes) return { text, truncated: false };
79
+ const room = Math.max(0, maxBytes - byteLength(ORACLE_TRUNCATION_MARKER));
80
+ const buffer = Buffer.from(text, "utf8");
81
+ let cut = Math.min(room, buffer.byteLength);
82
+ while (cut > 0) {
83
+ const next = buffer[cut];
84
+ if (next === undefined || (next & 0xc0) !== 0x80) break;
85
+ cut -= 1;
86
+ }
87
+ return { text: `${buffer.subarray(0, cut).toString("utf8")}${ORACLE_TRUNCATION_MARKER}`, truncated: true };
88
+ }
89
+
90
+ /** One decision line. Superseded decisions carry their correction so a reversal is visible. */
91
+ function decisionLine(decision: DecisionLedgerEntry["decisions"][number]): string {
92
+ const label = decision.label ? ` (${decision.label})` : "";
93
+ if (decision.status === "superseded") {
94
+ const correction = decision.correction ? `; corrected to ${decision.correction}` : "";
95
+ return `- ${decision.key}${label}: ${decision.value} [superseded${correction}]`;
96
+ }
97
+ return `- ${decision.key}${label}: ${decision.value}`;
98
+ }
99
+
100
+ function taskLine(task: TaskBoardTask): string {
101
+ return `- ${task.id} [${task.status}] ${task.title}`;
102
+ }
103
+
104
+ function isOpenTask(task: TaskBoardTask): boolean {
105
+ return task.status === "pending" || task.status === "active";
106
+ }
107
+
108
+ /**
109
+ * Pack the briefing. Sections are fixed and always present, so an advisor
110
+ * reading an empty one learns that the board is empty rather than that the
111
+ * packer omitted it.
112
+ */
113
+ export function packOracleDigest(sources: OracleDigestSources): OracleDigest {
114
+ let truncated = false;
115
+
116
+ const decisionRows = sources.decisions
117
+ .flatMap((entry) => entry.decisions.map(decisionLine))
118
+ .slice(0, ORACLE_DECISIONS_MAX_ROWS);
119
+ if (decisionRows.length < sources.decisions.reduce((total, entry) => total + entry.decisions.length, 0)) {
120
+ truncated = true;
121
+ }
122
+ const decisions = clamp(decisionRows.join("\n") || ORACLE_NO_DECISIONS, ORACLE_DECISIONS_MAX_BYTES);
123
+ truncated = truncated || decisions.truncated;
124
+
125
+ const openTasks = sources.tasks.filter(isOpenTask);
126
+ const taskRows = openTasks.map(taskLine).slice(0, ORACLE_TASKS_MAX_ROWS);
127
+ if (taskRows.length < openTasks.length) truncated = true;
128
+ const tasks = clamp(taskRows.join("\n") || ORACLE_NO_TASKS, ORACLE_TASKS_MAX_BYTES);
129
+ truncated = truncated || tasks.truncated;
130
+
131
+ const summaryText = sources.compactionSummary?.trim() ?? "";
132
+ const summary = clamp(summaryText, ORACLE_COMPACTION_MAX_BYTES);
133
+ truncated = truncated || summary.truncated;
134
+
135
+ const question = clamp(sources.question.trim(), ORACLE_QUESTION_MAX_BYTES);
136
+ truncated = truncated || question.truncated;
137
+
138
+ const sections = [
139
+ "## Settled decisions",
140
+ decisions.text,
141
+ "",
142
+ "## Open tasks",
143
+ tasks.text,
144
+ "",
145
+ "## Last compaction summary",
146
+ summary.text.length > 0 ? summary.text : "(this session has not compacted)",
147
+ "",
148
+ "## Question",
149
+ question.text,
150
+ ];
151
+ const whole = clamp(sections.join("\n"), ORACLE_DIGEST_MAX_BYTES);
152
+ return { text: whole.text, truncated: truncated || whole.truncated };
153
+ }
154
+
155
+ /** The bounded task an oracle run is given. The briefing carries the record; this carries the job. */
156
+ export const ORACLE_TASK =
157
+ "Answer the question in the briefing's Question section. Check it against the settled decisions first, " +
158
+ "then return the strongest challenge you can support and the evidence that would change your mind.";
159
+
160
+ /**
161
+ * The advisor's structured answer as the prose the operator note carries. The
162
+ * contract keeps the four fields typed so a run cannot half-answer; the note
163
+ * renders them because the main agent reads prose, not a payload.
164
+ */
165
+ export function formatOracleAnswer(result: OracleResult): string {
166
+ const cited =
167
+ result.citedDecisions.length === 0
168
+ ? "Cited decisions: none bear on this question."
169
+ : `Cited decisions: ${result.citedDecisions.join(", ")}`;
170
+ return [
171
+ `Verdict: ${result.verdict}`,
172
+ "",
173
+ `Strongest challenge: ${result.challenge}`,
174
+ "",
175
+ `What would change its mind: ${result.changesMyMind}`,
176
+ "",
177
+ cited,
178
+ ].join("\n");
179
+ }
@@ -14,6 +14,11 @@ export interface OverlayAskUserLifecycleDeps {
14
14
  requestRender(): void;
15
15
  registerHandler?(handler: AskUserHandler): () => void;
16
16
  openAskUserOverlay?: typeof openAskUserOverlay;
17
+ /**
18
+ * An ask_user request parked waiting for the operator. Fired when the
19
+ * overlay session opens, not per question, so one interview notifies once.
20
+ */
21
+ onOperatorParked?(): void;
17
22
  }
18
23
 
19
24
  export interface OverlayAskUserLifecycle {
@@ -61,6 +66,7 @@ export function createOverlayAskUserLifecycle(deps: OverlayAskUserLifecycleDeps)
61
66
  deps.setOverlayState("ask-user");
62
67
  session = openSession(deps.tui, { onCancel: () => pendingCancel?.() });
63
68
  deps.setOverlayHandle(session);
69
+ deps.onOperatorParked?.();
64
70
  deps.requestRender();
65
71
  return session;
66
72
  };