@iowarp/clio-coder 0.3.8 → 0.4.0

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 (552) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE +33 -0
  3. package/README.md +12 -3
  4. package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
  8. package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  11. package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
  12. package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
  13. package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
  14. package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
  15. package/dist/chunk-3DPEIQKN.js +113 -0
  16. package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
  17. package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
  18. package/dist/chunk-42FMPA75.js +101 -0
  19. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  20. package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
  21. package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
  22. package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
  23. package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
  24. package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
  25. package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
  26. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  27. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  28. package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
  29. package/dist/chunk-BKFM6EJV.js +462 -0
  30. package/dist/chunk-BMS5RKQY.js +27 -0
  31. package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
  32. package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
  33. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  34. package/dist/chunk-C2LTL2W6.js +2447 -0
  35. package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
  36. package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
  37. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  38. package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
  39. package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
  40. package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
  41. package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
  42. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  43. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  44. package/dist/chunk-GKF55TAZ.js +391 -0
  45. package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
  46. package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
  47. package/dist/chunk-HHV2GANA.js +88 -0
  48. package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
  49. package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
  50. package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
  51. package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
  52. package/dist/chunk-HZHHCK24.js +1631 -0
  53. package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
  54. package/dist/chunk-IBEBSCYA.js +564 -0
  55. package/dist/chunk-IQ7KR472.js +362 -0
  56. package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
  57. package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
  58. package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
  59. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  60. package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
  61. package/dist/chunk-KMVISBZR.js +132 -0
  62. package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
  63. package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
  64. package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
  65. package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
  66. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  67. package/dist/chunk-NEKRRTYW.js +56 -0
  68. package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
  69. package/dist/chunk-NHLBIGRH.js +1506 -0
  70. package/dist/chunk-NQQH3YT7.js +302 -0
  71. package/dist/chunk-NYS75XW5.js +15 -0
  72. package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
  73. package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
  74. package/dist/chunk-OQBA45DZ.js +97 -0
  75. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  76. package/dist/chunk-PMZCIOCJ.js +25 -0
  77. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  78. package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
  79. package/dist/chunk-QDC3K2U3.js +262 -0
  80. package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
  81. package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
  82. package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
  83. package/dist/chunk-QSNYB6ZV.js +195 -0
  84. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  85. package/dist/chunk-RKKLTLYB.js +45 -0
  86. package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
  87. package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
  88. package/dist/chunk-SUCTJL45.js +45 -0
  89. package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
  90. package/dist/chunk-T56WDKA5.js +183 -0
  91. package/dist/chunk-TVHHYFHE.js +255 -0
  92. package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
  93. package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
  94. package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
  95. package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
  96. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  97. package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
  98. package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
  99. package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
  100. package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
  101. package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
  102. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  103. package/dist/chunk-VO67MWHC.js +75 -0
  104. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  105. package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
  106. package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
  107. package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
  108. package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
  109. package/dist/chunk-X75E3D2N.js +686 -0
  110. package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
  111. package/dist/chunk-YJX4SHTD.js +40 -0
  112. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  113. package/dist/chunk-Z2RR6MAK.js +127 -0
  114. package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
  115. package/dist/cli/index.js +47 -36
  116. package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
  117. package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
  118. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  119. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  120. package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
  121. package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
  122. package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
  123. package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
  124. package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
  125. package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
  126. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  127. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
  128. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
  129. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  130. package/dist/doctor-QOA5FNY5.js +313 -0
  131. package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
  132. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  133. package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
  134. package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
  135. package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
  136. package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
  137. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
  138. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  139. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
  140. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  141. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
  142. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
  143. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  144. package/dist/fleet-view-ZCON35AG.js +102 -0
  145. package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
  146. package/dist/install-XGLBQY5E.js +13 -0
  147. package/dist/interop-OZBKXAYL.js +114 -0
  148. package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
  149. package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
  150. package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
  151. package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
  152. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
  153. package/dist/panes-HMABYVO4.js +58 -0
  154. package/dist/panes-KY6W3V2E.js +103 -0
  155. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  156. package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
  157. package/dist/resources-BB3MVJMD.js +111 -0
  158. package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
  159. package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
  160. package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
  161. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
  162. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  163. package/dist/slash-commands-ZSGASKJC.js +77 -0
  164. package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
  165. package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
  166. package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
  167. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
  168. package/dist/tools-DAF3DI3C.js +27 -0
  169. package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
  170. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  171. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  172. package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
  173. package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
  174. package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
  175. package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
  176. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  177. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
  178. package/dist/with-panes-MKB46MPQ.js +782 -0
  179. package/dist/worker/entry.js +106 -75
  180. package/docs/README.md +3 -2
  181. package/docs/acp.md +24 -3
  182. package/docs/alcf-provider.md +1 -1
  183. package/docs/architecture.md +2 -2
  184. package/docs/artifact-versions.md +9 -1
  185. package/docs/built-in-agents.md +1 -1
  186. package/docs/capacity-and-scheduling.md +62 -3
  187. package/docs/commands-and-modes.md +31 -2
  188. package/docs/configuration-and-targets.md +69 -12
  189. package/docs/context-engine.md +63 -4
  190. package/docs/development-pipeline.md +19 -0
  191. package/docs/dispatch-typed-intent.md +385 -0
  192. package/docs/documentation-coverage.md +5 -5
  193. package/docs/documentation-guide.md +1 -1
  194. package/docs/environment-variables.md +3 -0
  195. package/docs/eval-runner.md +262 -11
  196. package/docs/evals-internal.md +72 -2
  197. package/docs/evidence-and-memory.md +12 -11
  198. package/docs/evolution.md +1 -1
  199. package/docs/exit-codes-and-output.md +1 -1
  200. package/docs/extensions-and-sharing.md +27 -1
  201. package/docs/fleet-dispatch.md +25 -4
  202. package/docs/installation-and-lifecycle.md +15 -2
  203. package/docs/middleware-and-components.md +1 -1
  204. package/docs/model-catalog.md +10 -1
  205. package/docs/observability.md +55 -4
  206. package/docs/proactive-memory.md +127 -14
  207. package/docs/prompt-envelope-and-tools.md +19 -1
  208. package/docs/provider-adapter-cookbook.md +1 -1
  209. package/docs/release-cut-checklist.md +19 -3
  210. package/docs/safety-model.md +2 -2
  211. package/docs/scientific-validation.md +3 -3
  212. package/docs/session-lifecycle.md +1 -1
  213. package/docs/skills-marketplace.md +1 -1
  214. package/docs/tool-usage.md +18 -8
  215. package/docs/trace-store.md +1 -1
  216. package/docs/troubleshooting.md +88 -1
  217. package/docs/tui-design.md +1 -1
  218. package/docs/worker-dispatch-mechanics.md +1 -1
  219. package/package.json +5 -2
  220. package/src/cli/acp.ts +6 -2
  221. package/src/cli/agents.ts +1 -1
  222. package/src/cli/argv.ts +25 -0
  223. package/src/cli/config-inspect.ts +33 -6
  224. package/src/cli/config.ts +1 -1
  225. package/src/cli/configure.ts +23 -21
  226. package/src/cli/doctor-panes.ts +124 -0
  227. package/src/cli/doctor-state-size.ts +82 -0
  228. package/src/cli/doctor-toolchain.ts +57 -0
  229. package/src/cli/doctor.ts +22 -1
  230. package/src/cli/eval-inventory.ts +436 -0
  231. package/src/cli/eval.ts +93 -16
  232. package/src/cli/evidence-detail.ts +88 -0
  233. package/src/cli/evidence-inventory.ts +183 -0
  234. package/src/cli/evidence.ts +30 -5
  235. package/src/cli/extensions.ts +5 -1
  236. package/src/cli/fleet-decisions.ts +69 -0
  237. package/src/cli/fleet-inspect.ts +334 -0
  238. package/src/cli/fleet-verify.ts +133 -0
  239. package/src/cli/fleet-view.ts +810 -0
  240. package/src/cli/fleet.ts +179 -39
  241. package/src/cli/index.ts +14 -2
  242. package/src/cli/interop-inspect.ts +128 -0
  243. package/src/cli/interop.ts +34 -0
  244. package/src/cli/panes.ts +35 -0
  245. package/src/cli/reset.ts +5 -2
  246. package/src/cli/run.ts +58 -0
  247. package/src/cli/skills-inventory.ts +185 -0
  248. package/src/cli/skills.ts +16 -13
  249. package/src/cli/targets.ts +44 -13
  250. package/src/cli/tools.ts +321 -0
  251. package/src/cli/trace-inspect.ts +252 -0
  252. package/src/cli/trace.ts +85 -5
  253. package/src/cli/usage.ts +63 -14
  254. package/src/cli/verifiers-inspect.ts +347 -0
  255. package/src/cli/verifiers.ts +9 -0
  256. package/src/core/bus-events.ts +33 -1
  257. package/src/core/cache-telemetry.ts +42 -0
  258. package/src/core/config.ts +67 -0
  259. package/src/core/defaults.ts +124 -8
  260. package/src/core/endpoint-key.ts +27 -0
  261. package/src/core/residency-target-key.ts +25 -0
  262. package/src/core/response-schema.ts +80 -6
  263. package/src/core/theme-token-hex.ts +43 -0
  264. package/src/core/tool-names.ts +2 -1
  265. package/src/core/xdg.ts +1 -1
  266. package/src/domains/agents/fleets/build-review.md +0 -3
  267. package/src/domains/agents/fleets/build-test.md +0 -3
  268. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  269. package/src/domains/agents/result-contract.ts +164 -35
  270. package/src/domains/config/classify.ts +6 -0
  271. package/src/domains/context/codewiki/coordinator.ts +12 -4
  272. package/src/domains/dispatch/admission-error.ts +9 -0
  273. package/src/domains/dispatch/admission.ts +52 -14
  274. package/src/domains/dispatch/capacity-lease.ts +118 -9
  275. package/src/domains/dispatch/contract.ts +11 -0
  276. package/src/domains/dispatch/council-topology.ts +398 -0
  277. package/src/domains/dispatch/execution-plan.ts +44 -4
  278. package/src/domains/dispatch/extension.ts +378 -82
  279. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  280. package/src/domains/dispatch/fleet-plan.ts +7 -2
  281. package/src/domains/dispatch/fleet-run.ts +87 -4
  282. package/src/domains/dispatch/gate-decisions.ts +11 -1
  283. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  284. package/src/domains/dispatch/gate-topology.ts +289 -0
  285. package/src/domains/dispatch/heartbeat.ts +32 -8
  286. package/src/domains/dispatch/index.ts +22 -0
  287. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  288. package/src/domains/dispatch/intent.ts +85 -1
  289. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  290. package/src/domains/dispatch/reservation-store.ts +139 -11
  291. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  292. package/src/domains/dispatch/run-event-journal.ts +598 -0
  293. package/src/domains/dispatch/state.ts +49 -1
  294. package/src/domains/dispatch/types.ts +13 -0
  295. package/src/domains/dispatch/validation.ts +33 -8
  296. package/src/domains/dispatch/worker-spawn.ts +25 -11
  297. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  298. package/src/domains/dispatch/write-boundary.ts +62 -1
  299. package/src/domains/eval/artifacts/store.ts +62 -0
  300. package/src/domains/eval/compare/behavioral.ts +224 -0
  301. package/src/domains/eval/compare/compare.ts +342 -2
  302. package/src/domains/eval/compare/envelope.ts +128 -0
  303. package/src/domains/eval/compare/gates.ts +24 -6
  304. package/src/domains/eval/compare/thresholds.ts +30 -3
  305. package/src/domains/eval/execution-provenance.ts +240 -0
  306. package/src/domains/eval/inventory.ts +113 -0
  307. package/src/domains/eval/metrics/aggregate.ts +136 -0
  308. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  309. package/src/domains/eval/metrics/tracked.ts +413 -0
  310. package/src/domains/eval/provenance.ts +117 -0
  311. package/src/domains/eval/reports/comparison.ts +128 -0
  312. package/src/domains/eval/reports/junit.ts +17 -3
  313. package/src/domains/eval/reports/markdown.ts +3 -3
  314. package/src/domains/eval/reports/text.ts +14 -0
  315. package/src/domains/eval/run-compare.ts +20 -0
  316. package/src/domains/eval/runners/clio-run.ts +127 -0
  317. package/src/domains/eval/runners/external-command.ts +28 -3
  318. package/src/domains/eval/schema/adapter.ts +111 -0
  319. package/src/domains/eval/schema/artifact.ts +20 -0
  320. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  321. package/src/domains/eval/schema/behavioral.ts +520 -0
  322. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  323. package/src/domains/eval/schema/serving.ts +105 -0
  324. package/src/domains/eval/schema/suite.ts +38 -8
  325. package/src/domains/eval/schema/validate.ts +58 -3
  326. package/src/domains/eval/schema/verdict.ts +237 -0
  327. package/src/domains/eval/suites/resolve.ts +2 -0
  328. package/src/domains/eval/suites/run.ts +264 -33
  329. package/src/domains/eval/verifiers/command.ts +2 -1
  330. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  331. package/src/domains/evidence/build.ts +2 -13
  332. package/src/domains/evidence/eval.ts +2 -12
  333. package/src/domains/evidence/findings-markdown.ts +33 -0
  334. package/src/domains/evidence/run-trust.ts +7 -113
  335. package/src/domains/evidence/store.ts +6 -0
  336. package/src/domains/evidence/trust-projection.ts +2 -2
  337. package/src/domains/extensions/compatibility.ts +285 -0
  338. package/src/domains/extensions/discovery.ts +38 -3
  339. package/src/domains/extensions/resources.ts +1 -1
  340. package/src/domains/extensions/state.ts +12 -3
  341. package/src/domains/extensions/types.ts +2 -0
  342. package/src/domains/lifecycle/doctor.ts +69 -1
  343. package/src/domains/memory/index.ts +14 -1
  344. package/src/domains/memory/task-bank-promotion.ts +64 -0
  345. package/src/domains/memory/task-memory-policy.ts +82 -17
  346. package/src/domains/memory/task-memory-spend.ts +131 -0
  347. package/src/domains/memory/task-memory-status.ts +7 -0
  348. package/src/domains/memory/task-memory-telemetry.ts +3 -0
  349. package/src/domains/middleware/index.ts +1 -0
  350. package/src/domains/middleware/memory-intervention.ts +97 -21
  351. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  352. package/src/domains/mux/contract.ts +434 -0
  353. package/src/domains/mux/detect.ts +158 -0
  354. package/src/domains/mux/extension.ts +47 -0
  355. package/src/domains/mux/index.ts +96 -0
  356. package/src/domains/mux/manifest.ts +6 -0
  357. package/src/domains/mux/operations.ts +164 -0
  358. package/src/domains/mux/pane-registry.ts +90 -0
  359. package/src/domains/mux/protocol.ts +49 -0
  360. package/src/domains/mux/socket-client.ts +816 -0
  361. package/src/domains/mux/types.ts +222 -0
  362. package/src/domains/mux/viewer-command.ts +59 -0
  363. package/src/domains/mux/yazi/assets/init.lua +2 -0
  364. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  365. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  366. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  367. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  368. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  369. package/src/domains/mux/yazi/event-stream.ts +180 -0
  370. package/src/domains/mux/yazi/profile.ts +299 -0
  371. package/src/domains/mux/yazi/session.ts +228 -0
  372. package/src/domains/mux/yazi/theme.ts +30 -0
  373. package/src/domains/observability/background-memory-usage.ts +140 -0
  374. package/src/domains/observability/cost.ts +22 -1
  375. package/src/domains/observability/index.ts +9 -0
  376. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  377. package/src/domains/observability/trace-store.ts +234 -2
  378. package/src/domains/prompts/compiler.ts +100 -13
  379. package/src/domains/providers/endpoint-capacity.ts +228 -0
  380. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  381. package/src/domains/providers/extension.ts +20 -3
  382. package/src/domains/providers/index.ts +32 -0
  383. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  384. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  385. package/src/domains/providers/runtime-resolution.ts +8 -1
  386. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  387. package/src/domains/providers/runtimes/builtins.ts +2 -0
  388. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  389. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  390. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  391. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  392. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  393. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  394. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  395. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  396. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  397. package/src/domains/providers/support.ts +1 -0
  398. package/src/domains/providers/target-model-cache.ts +124 -0
  399. package/src/domains/providers/types/capability-flags.ts +2 -0
  400. package/src/domains/providers/types/target-descriptor.ts +2 -0
  401. package/src/domains/resources/index.ts +3 -0
  402. package/src/domains/resources/prompts/loader.ts +95 -33
  403. package/src/domains/resources/skills/loader.ts +33 -0
  404. package/src/domains/safety/action-classifier.ts +6 -0
  405. package/src/domains/safety/call-target.ts +52 -0
  406. package/src/domains/safety/run-effects.ts +35 -4
  407. package/src/domains/session/context-accounting.ts +52 -1
  408. package/src/domains/session/context-ledger.ts +37 -13
  409. package/src/domains/session/index.ts +6 -0
  410. package/src/domains/session/prompt-cache.ts +140 -0
  411. package/src/domains/session/prompt-manifest.ts +42 -0
  412. package/src/domains/toolchain/archive.ts +175 -0
  413. package/src/domains/toolchain/contract.ts +28 -0
  414. package/src/domains/toolchain/extension.ts +47 -0
  415. package/src/domains/toolchain/index.ts +39 -0
  416. package/src/domains/toolchain/install.ts +327 -0
  417. package/src/domains/toolchain/manifest.ts +8 -0
  418. package/src/domains/toolchain/paths.ts +34 -0
  419. package/src/domains/toolchain/registry.ts +265 -0
  420. package/src/domains/toolchain/remove.ts +218 -0
  421. package/src/domains/toolchain/resolve.ts +182 -0
  422. package/src/domains/toolchain/types.ts +113 -0
  423. package/src/domains/toolchain/version.ts +88 -0
  424. package/src/engine/acp/adapter.ts +18 -3
  425. package/src/engine/acp/server.ts +413 -70
  426. package/src/engine/acp/types.ts +19 -1
  427. package/src/engine/ai.ts +35 -0
  428. package/src/engine/apis/llamacpp-residency.ts +55 -3
  429. package/src/engine/apis/lmstudio.ts +25 -5
  430. package/src/engine/apis/ollama-native.ts +2 -1
  431. package/src/engine/apis/openai-completions.ts +80 -17
  432. package/src/engine/apis/residency-lock.ts +3 -1
  433. package/src/engine/apis/residency.ts +34 -1
  434. package/src/engine/claude/sdk-module.ts +98 -0
  435. package/src/engine/claude/sdk-runtime.ts +19 -11
  436. package/src/engine/provider-payload.ts +29 -1
  437. package/src/engine/tui-primitives.ts +21 -0
  438. package/src/engine/tui.ts +1 -0
  439. package/src/engine/worker-runtime.ts +2 -13
  440. package/src/entry/boot-options.ts +2 -0
  441. package/src/entry/orchestrator.ts +279 -35
  442. package/src/entry/panes-activation.ts +31 -0
  443. package/src/entry/with-panes.ts +20 -0
  444. package/src/interactive/chat-loop-messages.ts +26 -7
  445. package/src/interactive/chat-loop.ts +318 -41
  446. package/src/interactive/chat-panel.ts +62 -8
  447. package/src/interactive/clio-editor.ts +45 -8
  448. package/src/interactive/context-activity.ts +5 -1
  449. package/src/interactive/context-meter.ts +1 -1
  450. package/src/interactive/context-overlay.ts +41 -10
  451. package/src/interactive/cost-overlay.ts +66 -6
  452. package/src/interactive/council-grid.ts +1 -3
  453. package/src/interactive/council.ts +11 -0
  454. package/src/interactive/dispatch-board.ts +102 -20
  455. package/src/interactive/fleet-run-preview.ts +41 -15
  456. package/src/interactive/handoff-round.ts +41 -2
  457. package/src/interactive/interactive-application.ts +177 -7
  458. package/src/interactive/interactive-input-runtime.ts +15 -0
  459. package/src/interactive/interactive-presentation.ts +4 -0
  460. package/src/interactive/interactive-shell.ts +20 -17
  461. package/src/interactive/interactive-slash-runtime.ts +63 -10
  462. package/src/interactive/memory-overlay.ts +9 -0
  463. package/src/interactive/modal-marker.ts +170 -0
  464. package/src/interactive/mutation-preview.ts +295 -0
  465. package/src/interactive/mux-bridge.ts +214 -0
  466. package/src/interactive/overlay-frame.ts +58 -2
  467. package/src/interactive/overlay-general-openers.ts +17 -0
  468. package/src/interactive/overlay-key-routing.ts +52 -3
  469. package/src/interactive/overlay-lifecycle.ts +56 -7
  470. package/src/interactive/overlay-model-selectors.ts +40 -3
  471. package/src/interactive/overlay-permission-lifecycle.ts +112 -24
  472. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  473. package/src/interactive/overlay-transitions.ts +18 -4
  474. package/src/interactive/overlays/agents.ts +1 -0
  475. package/src/interactive/overlays/ask-user.ts +227 -49
  476. package/src/interactive/overlays/auth-dialog.ts +1 -0
  477. package/src/interactive/overlays/context-reset.ts +1 -0
  478. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  479. package/src/interactive/overlays/decisions.ts +11 -11
  480. package/src/interactive/overlays/extensions.ts +1 -0
  481. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  482. package/src/interactive/overlays/handoff-review.ts +1 -0
  483. package/src/interactive/overlays/help-reference.ts +6 -0
  484. package/src/interactive/overlays/interop.ts +1 -0
  485. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  486. package/src/interactive/overlays/library-tabs.ts +28 -0
  487. package/src/interactive/overlays/list-overlay.ts +10 -1
  488. package/src/interactive/overlays/message-picker.ts +1 -0
  489. package/src/interactive/overlays/model-scope.ts +86 -0
  490. package/src/interactive/overlays/model-selector.ts +1 -0
  491. package/src/interactive/overlays/prompts.ts +12 -1
  492. package/src/interactive/overlays/session-selector.ts +1 -0
  493. package/src/interactive/overlays/settings-sections.ts +30 -0
  494. package/src/interactive/overlays/settings.ts +614 -45
  495. package/src/interactive/overlays/side-question.ts +1 -0
  496. package/src/interactive/overlays/skills-hub.ts +3 -11
  497. package/src/interactive/overlays/tree-selector.ts +1 -0
  498. package/src/interactive/pane-policy.ts +46 -0
  499. package/src/interactive/panes-runtime.ts +292 -0
  500. package/src/interactive/permission-hint.ts +34 -2
  501. package/src/interactive/permission-overlay.ts +159 -9
  502. package/src/interactive/prewarm.ts +197 -0
  503. package/src/interactive/render-trace.ts +162 -15
  504. package/src/interactive/renderers/compaction-summary.ts +29 -0
  505. package/src/interactive/renderers/tool-execution.ts +4 -0
  506. package/src/interactive/renderers/worker-entry.ts +122 -14
  507. package/src/interactive/side-question.ts +58 -1
  508. package/src/interactive/slash-commands.ts +251 -15
  509. package/src/interactive/status/controller.ts +11 -0
  510. package/src/interactive/status/state-machine.ts +54 -2
  511. package/src/interactive/status/types.ts +7 -0
  512. package/src/interactive/tasks-overlay.ts +1 -0
  513. package/src/interactive/terminal-lease.ts +2 -0
  514. package/src/interactive/theme/tokens.ts +3 -14
  515. package/src/interactive/turn-context.ts +346 -33
  516. package/src/interactive/turn-persistence.ts +14 -4
  517. package/src/interactive/turn-prewarm.ts +364 -0
  518. package/src/interactive/turn-queues.ts +7 -4
  519. package/src/interactive/turn-runtime.ts +8 -1
  520. package/src/interactive/turn-state.ts +23 -0
  521. package/src/interactive/view/artifacts.ts +109 -1
  522. package/src/interactive/view/view-overlay.ts +29 -3
  523. package/src/interactive/watch-pane.ts +152 -0
  524. package/src/interactive/worker-progress.ts +7 -1
  525. package/src/interactive/worker-receipts.ts +19 -1
  526. package/src/interactive/worker-stream.ts +5 -0
  527. package/src/interactive/yazi-bridge.ts +444 -0
  528. package/src/tools/ask-user.ts +43 -2
  529. package/src/tools/bootstrap.ts +26 -2
  530. package/src/tools/builtin-tool-catalog.ts +15 -0
  531. package/src/tools/compete-worktrees.ts +83 -2
  532. package/src/tools/core-bootstrap.ts +2 -1
  533. package/src/tools/dispatch-admission.ts +14 -3
  534. package/src/tools/dispatch-arguments.ts +20 -20
  535. package/src/tools/dispatch-plan.ts +17 -9
  536. package/src/tools/dispatch-run-events.ts +134 -19
  537. package/src/tools/dispatch-runner.ts +29 -7
  538. package/src/tools/dispatch-scout.ts +1 -1
  539. package/src/tools/dispatch-types.ts +15 -3
  540. package/src/tools/dispatch.ts +1 -1
  541. package/src/tools/executables.ts +17 -14
  542. package/src/tools/observation.ts +54 -4
  543. package/src/tools/panes-surface.ts +38 -0
  544. package/src/tools/panes.ts +112 -0
  545. package/src/tools/policy.ts +10 -1
  546. package/src/tools/presentation.ts +1 -0
  547. package/src/tools/registry.ts +16 -0
  548. package/dist/chunk-AOCYTWAV.js +0 -449
  549. package/dist/chunk-HLE42MG7.js +0 -37
  550. package/dist/chunk-HWUFFB6L.js +0 -83
  551. package/dist/chunk-JOZYP4GM.js +0 -279
  552. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -0,0 +1,170 @@
1
+ /**
2
+ * The outward signal that a modal overlay owns the keyboard.
3
+ *
4
+ * Every framed Clio overlay captures input for as long as it is up, and until
5
+ * now it announced that only by being on screen. An external driver feeding
6
+ * keys into the pane, and herdr deciding whether the pane is idle, working, or
7
+ * blocked, had to infer modality from whatever the overlay happened to draw:
8
+ * its box title, its hint row, a footer pill. Those are per-overlay strings
9
+ * that move on every reskin, and the tall modals cover the very rows a screen
10
+ * scraper reads, so the signal is weakest exactly when a modal is largest.
11
+ *
12
+ * The marker publishes the fact out of band instead, as an OSC 0 terminal
13
+ * title. Three properties make the title the reliable channel here:
14
+ *
15
+ * 1. It is not screen content. No overlay can composite over it, no
16
+ * transcript line can counterfeit it, and no terminal height changes
17
+ * whether it is readable.
18
+ * 2. herdr already consumes it. `region = "osc_title"` is a first-class
19
+ * detection region (herdr `src/detect/manifest.rs`), fed from the pane's
20
+ * own emulator, and its rules outrank every screen rule.
21
+ * 3. Clio has never written a title, so the channel carries exactly one
22
+ * meaning and nothing else contends for it.
23
+ *
24
+ * The grammar is fixed and machine-first:
25
+ *
26
+ * clio no modal owns the keys
27
+ * clio [modal:permission-confirm] one modal, named
28
+ * clio [modal:library-install+2] top modal named, two more beneath it
29
+ *
30
+ * Nothing is written until the first modal opens, so a Clio that never opens
31
+ * an overlay leaves the pane's inherited title alone. From the first modal
32
+ * onward the title toggles between the marker and the bare base, which is what
33
+ * makes the absence of a marker a positive statement rather than an unknown.
34
+ */
35
+
36
+ /** The write side of a terminal title. `ProcessTerminal` satisfies it structurally. */
37
+ export interface ModalMarkerSink {
38
+ setTitle(title: string): void;
39
+ }
40
+
41
+ /** What the title reads when nothing modal owns the keyboard. */
42
+ export const MODAL_MARKER_BASE_TITLE = "clio";
43
+
44
+ /**
45
+ * The one regular expression an external reader needs. Exported so Clio's own
46
+ * contract test and herdr's manifest rule are written against the same shape
47
+ * rather than two hand-copied spellings of it.
48
+ */
49
+ export const MODAL_MARKER_TITLE_PATTERN = /^clio \[modal:([a-z0-9][a-z0-9-]*)(?:\+([1-9]\d*))?\]$/u;
50
+
51
+ /** Render the title for a modal stack, outermost first. */
52
+ export function formatModalMarkerTitle(stack: readonly string[]): string {
53
+ const top = stack.at(-1);
54
+ if (top === undefined) return MODAL_MARKER_BASE_TITLE;
55
+ const beneath = stack.length - 1;
56
+ return `${MODAL_MARKER_BASE_TITLE} [modal:${top}${beneath > 0 ? `+${beneath}` : ""}]`;
57
+ }
58
+
59
+ /** Read a marker title back. Returns null for the base title and for anything else. */
60
+ export function parseModalMarkerTitle(title: string): { id: string; depth: number } | null {
61
+ const match = MODAL_MARKER_TITLE_PATTERN.exec(title);
62
+ const id = match?.[1];
63
+ if (id === undefined) return null;
64
+ const beneath = match?.[2];
65
+ return { id, depth: beneath === undefined ? 1 : 1 + Number.parseInt(beneath, 10) };
66
+ }
67
+
68
+ /** One modal's claim on the keyboard, released when the overlay goes away. */
69
+ export interface ModalMarkerHandle {
70
+ /** Drop this modal from the stack. Idempotent; a second call is inert. */
71
+ release(): void;
72
+ /**
73
+ * Whether this modal currently owns keys. The engine's `setHidden` takes a
74
+ * visible overlay out of the focus order without destroying it, which is how
75
+ * a surface that shells out to a child process gets off the screen, so a
76
+ * hidden modal must stop claiming the keyboard without leaving the stack.
77
+ */
78
+ setActive(active: boolean): void;
79
+ /** Bring this modal back to the top of the stack, as `handle.focus()` does on screen. */
80
+ raise(): void;
81
+ }
82
+
83
+ interface ModalMarkerRegistry {
84
+ /**
85
+ * Push a modal. The sink is taken from the caller rather than installed at
86
+ * startup so the marker has no lifecycle of its own: it exists exactly when
87
+ * an overlay exists, and a stub terminal with no `setTitle` makes the whole
88
+ * mechanism inert instead of throwing.
89
+ */
90
+ enter(id: string, sink?: ModalMarkerSink | null): ModalMarkerHandle;
91
+ /** The ids that currently own keys, outermost first. */
92
+ stack(): readonly string[];
93
+ }
94
+
95
+ interface ModalMarkerEntry {
96
+ readonly id: string;
97
+ active: boolean;
98
+ }
99
+
100
+ function createModalMarkerRegistry(): ModalMarkerRegistry {
101
+ const entries: ModalMarkerEntry[] = [];
102
+ let sink: ModalMarkerSink | null = null;
103
+ let published: string | null = null;
104
+
105
+ function activeIds(): string[] {
106
+ return entries.filter((entry) => entry.active).map((entry) => entry.id);
107
+ }
108
+
109
+ function publish(): void {
110
+ if (sink === null) return;
111
+ const title = formatModalMarkerTitle(activeIds());
112
+ // Before the first marker there is no stale claim to clear, so the pane
113
+ // keeps whatever title the shell or the multiplexer gave it.
114
+ if (published === null && title === MODAL_MARKER_BASE_TITLE) return;
115
+ if (title === published) return;
116
+ published = title;
117
+ sink.setTitle(title);
118
+ }
119
+
120
+ function enter(id: string, nextSink?: ModalMarkerSink | null): ModalMarkerHandle {
121
+ if (nextSink) sink = nextSink;
122
+ const entry: ModalMarkerEntry = { id, active: true };
123
+ entries.push(entry);
124
+ publish();
125
+ let released = false;
126
+ return {
127
+ release(): void {
128
+ if (released) return;
129
+ released = true;
130
+ const index = entries.indexOf(entry);
131
+ if (index !== -1) entries.splice(index, 1);
132
+ publish();
133
+ },
134
+ setActive(active: boolean): void {
135
+ if (released || entry.active === active) return;
136
+ entry.active = active;
137
+ publish();
138
+ },
139
+ raise(): void {
140
+ if (released) return;
141
+ entry.active = true;
142
+ const index = entries.indexOf(entry);
143
+ if (index !== -1 && index !== entries.length - 1) {
144
+ entries.splice(index, 1);
145
+ entries.push(entry);
146
+ }
147
+ publish();
148
+ },
149
+ };
150
+ }
151
+
152
+ return { enter, stack: (): readonly string[] => activeIds() };
153
+ }
154
+
155
+ let registry = createModalMarkerRegistry();
156
+
157
+ /** Push a modal onto the process-wide stack. See {@link ModalMarkerRegistry.enter}. */
158
+ export function enterModal(id: string, sink?: ModalMarkerSink | null): ModalMarkerHandle {
159
+ return registry.enter(id, sink);
160
+ }
161
+
162
+ /** The ids that currently own keys, outermost first. */
163
+ export function modalMarkerStack(): readonly string[] {
164
+ return registry.stack();
165
+ }
166
+
167
+ /** Drop every claim and forget the sink. Tests use it to isolate; nothing in the app does. */
168
+ export function resetModalMarker(): void {
169
+ registry = createModalMarkerRegistry();
170
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Local inspection of the exact mutation a parked `write` or `edit` would make.
3
+ *
4
+ * The approval card describes an unlisted argument by type and size, so a
5
+ * parked write read `content=<string 482 bytes>` and a parked edit read
6
+ * `edits=<array 3 items>`: the operator authorized bytes they had never seen,
7
+ * and the only way to read them was the external session ledger (issue #254).
8
+ *
9
+ * Two rules shape everything here. The preview is derived from the same
10
+ * arguments object the decision resumes, and it carries a digest of those
11
+ * arguments so the bytes on screen are verifiably the bytes being approved.
12
+ * And the mutation text is process-local: it is built lazily when the operator
13
+ * asks for it, it is handed only to the overlay body, and it is never placed on
14
+ * {@link import("./permission-overlay.js").ApprovalRequestView}, which is what
15
+ * crosses into the transcript, the notices, and the worker seam.
16
+ */
17
+
18
+ import { createHash } from "node:crypto";
19
+ import { readFileSync } from "node:fs";
20
+ import { ToolNames } from "../core/tool-names.js";
21
+ import { sanitizeCallTargetText, sanitizeMultilineDisplayText } from "../domains/safety/call-target.js";
22
+ import {
23
+ applyEditsToNormalizedContent,
24
+ type Edit,
25
+ generateDiffString,
26
+ normalizeToLF,
27
+ stripBom,
28
+ } from "../tools/edit-diff.js";
29
+ import { resolveToCwd } from "../tools/path-utils.js";
30
+
31
+ export { MUTATION_PREVIEW_KEY } from "./permission-hint.js";
32
+
33
+ /** Rows of mutation shown at once. Anything longer scrolls, as `/handoff` review does. */
34
+ export const MUTATION_PREVIEW_VISIBLE_ROWS = 16;
35
+
36
+ /**
37
+ * Characters of proposed content the preview renders. The diff builder has its
38
+ * own byte backstop; this bounds the write path, whose payload is a whole file.
39
+ * Reaching it prints an explicit line naming what was withheld.
40
+ */
41
+ export const MUTATION_PREVIEW_MAX_CHARS = 262_144;
42
+
43
+ /**
44
+ * Facts about a parked mutation that may cross every process boundary: what
45
+ * kind it is, how big it is, and the digest that binds a preview to it. No part
46
+ * of the mutation text appears here, which is why this is the shape the
47
+ * approval view, the transcript row, and any notice may carry.
48
+ */
49
+ export interface MutationFacts {
50
+ kind: "write" | "edit";
51
+ /** Bytes of proposed content (write), or of the replacement text across every edit. */
52
+ bytes: number;
53
+ /** Replacements in the edit list. One for a write. */
54
+ replacements: number;
55
+ /** Truncated SHA-256 over the exact call arguments this decision applies to. */
56
+ digest: string;
57
+ }
58
+
59
+ export interface MutationPreview {
60
+ facts: MutationFacts;
61
+ /** What the body is, named honestly on the overlay's first line. */
62
+ heading: string;
63
+ /** The complete rendered mutation, already sanitized and one entry per display row. */
64
+ body: ReadonlyArray<string>;
65
+ /** A control byte or escape sequence in the payload was neutralized for display. */
66
+ neutralized: boolean;
67
+ /** A tab was expanded, so columns are display columns. */
68
+ tabsExpanded: boolean;
69
+ }
70
+
71
+ /**
72
+ * Build the complete preview for the parked call, or explain why it cannot be
73
+ * built. Never throws: an unreadable file or an edit list that will not apply
74
+ * is itself something the operator needs to read before deciding.
75
+ */
76
+ export type MutationInspector = () => MutationPreview;
77
+
78
+ function canonicalJson(value: unknown): string {
79
+ if (value === null || value === undefined) return "null";
80
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
81
+ if (typeof value === "object") {
82
+ const entries = Object.entries(value as Record<string, unknown>)
83
+ .filter(([, entry]) => entry !== undefined)
84
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
85
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
86
+ }
87
+ if (typeof value === "string") return JSON.stringify(value);
88
+ if (typeof value === "number") return Number.isFinite(value) ? JSON.stringify(value) : "null";
89
+ if (typeof value === "boolean") return value ? "true" : "false";
90
+ return "null";
91
+ }
92
+
93
+ /**
94
+ * The identity a decision is bound to. Key order and absent optional fields do
95
+ * not change it, so the same call digests the same way wherever it is read;
96
+ * anything else about the arguments does, which is the point: a call that
97
+ * differs from the previewed one cannot inherit its preview.
98
+ */
99
+ export function callArgumentsDigest(args: Record<string, unknown> | undefined): string {
100
+ return createHash("sha256")
101
+ .update(canonicalJson(args ?? {}), "utf8")
102
+ .digest("hex")
103
+ .slice(0, 16);
104
+ }
105
+
106
+ function readEdits(value: unknown): Edit[] | null {
107
+ if (!Array.isArray(value)) return null;
108
+ const edits: Edit[] = [];
109
+ for (const entry of value) {
110
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
111
+ const record = entry as Record<string, unknown>;
112
+ if (typeof record.oldText !== "string" || typeof record.newText !== "string") return null;
113
+ edits.push({ oldText: record.oldText, newText: record.newText });
114
+ }
115
+ return edits;
116
+ }
117
+
118
+ /**
119
+ * The size-and-digest facts for a parked call, or null when the tool is not a
120
+ * file mutation or its arguments are not the shape this can describe. The card
121
+ * keeps rendering its allowlisted target either way.
122
+ */
123
+ export function mutationFacts(tool: string, args: Record<string, unknown> | undefined): MutationFacts | null {
124
+ if (!args || typeof args.path !== "string" || args.path.length === 0) return null;
125
+ if (tool === ToolNames.Write) {
126
+ if (typeof args.content !== "string") return null;
127
+ return {
128
+ kind: "write",
129
+ bytes: Buffer.byteLength(args.content, "utf8"),
130
+ replacements: 1,
131
+ digest: callArgumentsDigest(args),
132
+ };
133
+ }
134
+ if (tool === ToolNames.Edit) {
135
+ const edits = readEdits(args.edits);
136
+ if (edits === null || edits.length === 0) return null;
137
+ const bytes = edits.reduce((total, edit) => total + Buffer.byteLength(edit.newText, "utf8"), 0);
138
+ return { kind: "edit", bytes, replacements: edits.length, digest: callArgumentsDigest(args) };
139
+ }
140
+ return null;
141
+ }
142
+
143
+ /** `write · 482 B · sha256 3f9a1c22ee04b7d1`, the collapsed card's whole mutation line. */
144
+ export function mutationFactsLine(facts: MutationFacts): string {
145
+ const shape =
146
+ facts.kind === "write"
147
+ ? `write ${facts.bytes} B`
148
+ : `edit ${facts.replacements} replacement${facts.replacements === 1 ? "" : "s"} · ${facts.bytes} B`;
149
+ return `${shape} · sha256 ${facts.digest}`;
150
+ }
151
+
152
+ function numberedLines(text: string): string[] {
153
+ const raw = text.split("\n");
154
+ const width = String(raw.length).length;
155
+ return raw.map((line, index) => `${String(index + 1).padStart(width, " ")} ${line}`);
156
+ }
157
+
158
+ function boundedContent(content: string): { text: string; withheld: number } {
159
+ if (content.length <= MUTATION_PREVIEW_MAX_CHARS) return { text: content, withheld: 0 };
160
+ return {
161
+ text: content.slice(0, MUTATION_PREVIEW_MAX_CHARS),
162
+ withheld: content.length - MUTATION_PREVIEW_MAX_CHARS,
163
+ };
164
+ }
165
+
166
+ function previewFrom(
167
+ facts: MutationFacts,
168
+ heading: string,
169
+ text: string,
170
+ options: { numbered?: boolean; trailer?: string } = {},
171
+ ): MutationPreview {
172
+ const clean = sanitizeMultilineDisplayText(text);
173
+ const body = options.numbered ? numberedLines(clean.text) : clean.text.split("\n");
174
+ if (options.trailer !== undefined) body.push(options.trailer);
175
+ return {
176
+ facts,
177
+ heading,
178
+ body,
179
+ neutralized: clean.neutralized,
180
+ tabsExpanded: clean.tabsExpanded,
181
+ };
182
+ }
183
+
184
+ function refusal(facts: MutationFacts, heading: string, reason: string): MutationPreview {
185
+ return { facts, heading, body: [reason], neutralized: false, tabsExpanded: false };
186
+ }
187
+
188
+ /**
189
+ * The requested replacements, rendered as a diff would render them, for the
190
+ * case where the effective diff cannot be computed. The operator still needs to
191
+ * read what the model asked for; the heading says this is the request rather
192
+ * than the effect.
193
+ */
194
+ function replacementListing(edits: ReadonlyArray<Edit>): string {
195
+ const blocks: string[] = [];
196
+ for (const [index, edit] of edits.entries()) {
197
+ blocks.push(`@@ replacement ${index + 1} of ${edits.length}`);
198
+ for (const line of normalizeToLF(edit.oldText).split("\n")) blocks.push(`- ${line}`);
199
+ for (const line of normalizeToLF(edit.newText).split("\n")) blocks.push(`+ ${line}`);
200
+ }
201
+ return blocks.join("\n");
202
+ }
203
+
204
+ function editPreview(facts: MutationFacts, path: string, edits: ReadonlyArray<Edit>, read: (path: string) => string) {
205
+ let current: string;
206
+ try {
207
+ current = read(resolveToCwd(path));
208
+ } catch (error) {
209
+ const message = sanitizeCallTargetText(error instanceof Error ? error.message : String(error));
210
+ return previewFrom(
211
+ facts,
212
+ `Requested replacements for ${sanitizeCallTargetText(path)} (the file could not be read: ${message})`,
213
+ replacementListing(edits),
214
+ );
215
+ }
216
+ try {
217
+ const { text } = stripBom(current);
218
+ const applied = applyEditsToNormalizedContent(normalizeToLF(text), edits, path);
219
+ const diff = generateDiffString(applied.baseContent, applied.newContent);
220
+ return previewFrom(facts, `Effective diff for ${sanitizeCallTargetText(path)}`, diff.diff);
221
+ } catch (error) {
222
+ const message = sanitizeCallTargetText(error instanceof Error ? error.message : String(error));
223
+ return previewFrom(
224
+ facts,
225
+ `Requested replacements for ${sanitizeCallTargetText(path)} (they do not apply: ${message})`,
226
+ replacementListing(edits),
227
+ );
228
+ }
229
+ }
230
+
231
+ export interface MutationInspectorOptions {
232
+ /** Reads the current file bytes for an edit's effective diff. Injected so the contract test owns the filesystem. */
233
+ readFile?: (path: string) => string;
234
+ }
235
+
236
+ /**
237
+ * Bind an inspector to one parked call. The returned closure reads the file and
238
+ * computes the diff only when the operator presses the inspect key, and it
239
+ * re-derives the digest from the same arguments it renders: a mismatch means
240
+ * the arguments changed under the decision and the preview refuses rather than
241
+ * showing bytes that are no longer the ones being approved.
242
+ */
243
+ export function createMutationInspector(
244
+ tool: string,
245
+ args: Record<string, unknown> | undefined,
246
+ facts: MutationFacts,
247
+ options: MutationInspectorOptions = {},
248
+ ): MutationInspector {
249
+ const read = options.readFile ?? ((path: string) => readFileSync(path, "utf8"));
250
+ return () => {
251
+ const current = mutationFacts(tool, args);
252
+ if (current === null || current.digest !== facts.digest) {
253
+ return refusal(
254
+ facts,
255
+ "Preview unavailable",
256
+ `These arguments no longer digest to sha256 ${facts.digest}. Deny this call rather than approving bytes that cannot be shown.`,
257
+ );
258
+ }
259
+ const path = typeof args?.path === "string" ? args.path : "";
260
+ if (facts.kind === "write") {
261
+ const content = typeof args?.content === "string" ? args.content : "";
262
+ const bounded = boundedContent(content);
263
+ return previewFrom(facts, `Proposed content for ${sanitizeCallTargetText(path)}`, bounded.text, {
264
+ numbered: true,
265
+ ...(bounded.withheld > 0
266
+ ? { trailer: `… ${bounded.withheld} more characters not shown (preview capped at ${MUTATION_PREVIEW_MAX_CHARS})` }
267
+ : {}),
268
+ });
269
+ }
270
+ const edits = readEdits(args?.edits) ?? [];
271
+ return editPreview(facts, path, edits, read);
272
+ };
273
+ }
274
+
275
+ /**
276
+ * The scrolled window of a preview, plus the position line that makes the cut
277
+ * visible. Mirrors the `/handoff` review overlay so both scrollable inspection
278
+ * surfaces read the same way.
279
+ */
280
+ export function mutationPreviewWindow(
281
+ lines: ReadonlyArray<string>,
282
+ scroll: number,
283
+ rows = MUTATION_PREVIEW_VISIBLE_ROWS,
284
+ ): { window: ReadonlyArray<string>; position: string | null; maxScroll: number } {
285
+ const visible = Math.max(1, rows);
286
+ const maxScroll = Math.max(0, lines.length - visible);
287
+ const start = Math.max(0, Math.min(scroll, maxScroll));
288
+ const window = lines.slice(start, start + visible);
289
+ if (lines.length <= visible) return { window, position: null, maxScroll };
290
+ return {
291
+ window,
292
+ position: `(${start + 1}-${start + window.length} of ${lines.length} lines)`,
293
+ maxScroll,
294
+ };
295
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The dispatch-to-pane-host bridge.
3
+ *
4
+ * Two responsibilities, both projections of state the session already owns:
5
+ *
6
+ * - **Terminal-run notifications.** A finished dispatch raises a pane-host
7
+ * toast per the `panes.notifications` policy, and a failure additionally
8
+ * leaves a persistent transcript notice, which unlike a toast remains
9
+ * auditable in the TUI and does not depend on the pane host being alive.
10
+ *
11
+ * - **Self-report (SA-3).** Clio's own hosting pane, in guest mode, carries
12
+ * the interactive status machine's phase as a semantic agent state, on a
13
+ * trailing-edge throttle: herdr answers one request per connection, and an
14
+ * operator turn passes through several phases inside one window, of which
15
+ * only the last is worth a wire call.
16
+ *
17
+ * What this bridge deliberately no longer does is open panes. The phase 3/4
18
+ * integration projected every dispatch onto a viewer pane in a hidden Fleet
19
+ * tab (an `agents` policy knob, a keep-failed close policy, resume adoption,
20
+ * role-vocabulary sidebar labels). All of it duplicated the native fleet
21
+ * surfaces into panes the operator had not asked for; the one pane that
22
+ * renders runs is now the operator-pulled watch pane (`watch-pane.ts`), and
23
+ * the policy line lives in `pane-policy.ts`.
24
+ */
25
+
26
+ import { type AgentStatusChangedPayload, BusChannels, type DispatchRunIdentity } from "../core/bus-events.js";
27
+ import type { SafeEventBus } from "../core/event-bus.js";
28
+ import type { MuxContract, MuxLog } from "../domains/mux/index.js";
29
+ import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
30
+
31
+ /** Trailing-edge window for the self-report; matches `WAIT_POLL_MS`. */
32
+ export const MUX_BRIDGE_THROTTLE_MS = 250;
33
+
34
+ /**
35
+ * A success worth a sound. Below it a finished run is one of many and a chime
36
+ * per run is noise; above it the operator has moved on and wants telling.
37
+ */
38
+ const LONG_RUN_MS = 60_000;
39
+
40
+ /** How much terminal detail may reach a toast body or a notice line. */
41
+ const DETAIL_MAX_CHARS = 96;
42
+
43
+ export type PanesNotificationsPolicy = "failures" | "all" | "off";
44
+
45
+ /** Terminal outcomes that read as failure for the notification policy. */
46
+ type BridgeRunOutcome = "succeeded" | "failed" | "canceled" | "timed_out";
47
+
48
+ export interface MuxBridgeDeps {
49
+ bus: SafeEventBus;
50
+ mux: MuxContract;
51
+ /** Read live so a `/settings` change takes effect on the next terminal run. */
52
+ notificationsPolicy: () => PanesNotificationsPolicy;
53
+ /** Persistent transcript notice; unlike a herdr toast, this remains auditable in the TUI. */
54
+ notice?: (level: "error", text: string) => void;
55
+ log?: MuxLog;
56
+ now?: () => number;
57
+ throttleMs?: number;
58
+ setTimeoutFn?: typeof setTimeout;
59
+ clearTimeoutFn?: typeof clearTimeout;
60
+ }
61
+
62
+ export interface MuxBridge {
63
+ /** Settle every in-flight notification and self-report. Tests await it. */
64
+ flush(): Promise<void>;
65
+ dispose(): void;
66
+ }
67
+
68
+ function isFailure(outcome: BridgeRunOutcome): boolean {
69
+ return outcome === "failed" || outcome === "timed_out";
70
+ }
71
+
72
+ /** Clio's own pane state (SA-3), mapped from the interactive status machine. */
73
+ function selfStateFor(phase: AgentStatusChangedPayload["phase"]): "working" | "blocked" | "idle" {
74
+ if (phase === "tool_blocked") return "blocked";
75
+ // `ended` is the settle step on the way back to idle, so both read idle.
76
+ return phase === "idle" || phase === "ended" ? "idle" : "working";
77
+ }
78
+
79
+ /**
80
+ * Clio's seven-member run outcome taxonomy narrowed to the four states the
81
+ * notification policy distinguishes. `stalled`, `denied_by_policy`,
82
+ * `spawn_failed`, and the synthetic `retry_denied` all read as failure, which
83
+ * is the safe direction: an unrecognized terminal state must not silently
84
+ * skip the failure notice.
85
+ */
86
+ function terminalOutcome(outcome: string): BridgeRunOutcome {
87
+ if (outcome === "succeeded") return "succeeded";
88
+ if (outcome === "canceled") return "canceled";
89
+ if (outcome === "timed_out") return "timed_out";
90
+ return "failed";
91
+ }
92
+
93
+ export function createMuxBridge(deps: MuxBridgeDeps): MuxBridge {
94
+ const log = deps.log ?? ((): void => undefined);
95
+ const now = deps.now ?? Date.now;
96
+ const throttleMs = deps.throttleMs ?? MUX_BRIDGE_THROTTLE_MS;
97
+ const setTimeoutFn = deps.setTimeoutFn ?? setTimeout;
98
+ const clearTimeoutFn = deps.clearTimeoutFn ?? clearTimeout;
99
+
100
+ /** Start stamps, so a terminal event without a duration still gets one. */
101
+ const startedAt = new Map<string, number>();
102
+ let disposed = false;
103
+ let timer: ReturnType<typeof setTimeout> | null = null;
104
+ /** Serializes wire work: notifies and self-reports must not interleave. */
105
+ let draining: Promise<void> = Promise.resolve();
106
+ /** Pending self-report state, or null when nothing changed since the last flush. */
107
+ let pendingSelf: "working" | "blocked" | "idle" | null = null;
108
+ /** The last state actually reported, so a repeated phase costs no wire call. */
109
+ let reportedSelf: "working" | "blocked" | "idle" | null = null;
110
+
111
+ const schedule = (): void => {
112
+ if (disposed || timer !== null) return;
113
+ timer = setTimeoutFn(() => {
114
+ timer = null;
115
+ void drainSelf();
116
+ }, throttleMs);
117
+ timer.unref?.();
118
+ };
119
+
120
+ const drainSelf = (): Promise<void> => {
121
+ draining = draining.then(async () => {
122
+ if (disposed) return;
123
+ const self = pendingSelf;
124
+ pendingSelf = null;
125
+ if (self !== null && self !== reportedSelf) {
126
+ reportedSelf = self;
127
+ await deps.mux.reportSelf({ state: self }).catch(() => false);
128
+ }
129
+ });
130
+ return draining;
131
+ };
132
+
133
+ const boundedDetail = (value: string | null | undefined): string | null => {
134
+ if (typeof value !== "string" || value.length === 0) return null;
135
+ return sanitizeCallTargetText(value).slice(0, DETAIL_MAX_CHARS);
136
+ };
137
+
138
+ const onTerminal = (
139
+ identity: DispatchRunIdentity,
140
+ rawOutcome: string,
141
+ fields: { durationMs?: number | undefined; detail?: string | null | undefined },
142
+ ): void => {
143
+ const outcome = terminalOutcome(rawOutcome);
144
+ const begun = startedAt.get(identity.runId);
145
+ startedAt.delete(identity.runId);
146
+ const policy = deps.notificationsPolicy();
147
+ const failed = isFailure(outcome);
148
+ const qualifies = policy === "all" || (policy === "failures" && failed);
149
+ if (!qualifies) return;
150
+ const detail = boundedDetail(fields.detail);
151
+ const durationMs =
152
+ typeof fields.durationMs === "number" ? fields.durationMs : begun === undefined ? null : now() - begun;
153
+ if (failed) {
154
+ deps.notice?.("error", `${identity.agentId} ${outcome} (${identity.runId})${detail ? `: ${detail}` : ""}`);
155
+ }
156
+ if (!deps.mux.available()) return;
157
+ draining = draining.then(async () => {
158
+ if (disposed) return;
159
+ try {
160
+ await deps.mux.notify({
161
+ title: `${identity.agentId} ${outcome}`,
162
+ ...(detail ? { body: detail } : {}),
163
+ // A failure interrupts; a long success announces. A short success
164
+ // in `all` mode gets the toast without the chime.
165
+ sound: failed ? "request" : durationMs !== null && durationMs >= LONG_RUN_MS ? "done" : "none",
166
+ });
167
+ } catch (error) {
168
+ // The contract swallows its own failures; anything arriving here is a
169
+ // bug rather than a dead socket, and must still not kill the chain.
170
+ log("debug", `mux bridge notify failed: ${error instanceof Error ? error.message : String(error)}`);
171
+ }
172
+ });
173
+ };
174
+
175
+ const unsubscribers = [
176
+ deps.bus.on(BusChannels.DispatchStarted, (payload) => {
177
+ startedAt.set(payload.runId, now());
178
+ }),
179
+ deps.bus.on(BusChannels.DispatchCompleted, (payload) => {
180
+ onTerminal(payload, payload.outcome, { durationMs: payload.durationMs, detail: payload.outcomeDetail });
181
+ }),
182
+ deps.bus.on(BusChannels.DispatchFailed, (payload) => {
183
+ onTerminal(payload, payload.reason, { durationMs: payload.durationMs, detail: payload.outcomeDetail });
184
+ }),
185
+ // SA-3. The interactive status machine is the one surface that already
186
+ // knows when a turn starts, when it ends, and when a tool call is parked
187
+ // on an approval, so the self-report rides its transitions rather than
188
+ // growing a second turn-lifecycle listener.
189
+ deps.bus.on(BusChannels.AgentStatusChanged, (payload) => {
190
+ const state = selfStateFor(payload.phase);
191
+ if (state === reportedSelf && pendingSelf === null) return;
192
+ pendingSelf = state;
193
+ schedule();
194
+ }),
195
+ ];
196
+
197
+ return {
198
+ flush(): Promise<void> {
199
+ if (timer !== null) {
200
+ clearTimeoutFn(timer);
201
+ timer = null;
202
+ }
203
+ return drainSelf();
204
+ },
205
+ dispose(): void {
206
+ if (disposed) return;
207
+ disposed = true;
208
+ if (timer !== null) clearTimeoutFn(timer);
209
+ timer = null;
210
+ for (const unsubscribe of unsubscribers) unsubscribe();
211
+ startedAt.clear();
212
+ },
213
+ };
214
+ }