@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,598 @@
1
+ /**
2
+ * Append-only run event journal.
3
+ *
4
+ * Live dispatch events exist only in `DispatchRunEventRegistry`'s in-memory
5
+ * tail (src/tools/dispatch-run-events.ts), so a separate process cannot see a
6
+ * run in flight: the ledger and receipts are durable but the event stream is
7
+ * not. This module tees that same display tail to disk so a viewer process
8
+ * (`clio-coder fleet view`) can follow a run without sharing the orchestrator's
9
+ * heap.
10
+ *
11
+ * On-disk layout under `clioStateDir()`:
12
+ * runs/<runId>/events.ndjson one JSON object per line, newest last
13
+ *
14
+ * Every line carries a monotonically increasing `seq` (per run, starting at 1)
15
+ * and a wall-clock `at`. Line kinds:
16
+ * open run opened, carries agentId
17
+ * event one display-tail entry; the only droppable kind
18
+ * journal_truncated written once, the moment dropping begins
19
+ * receipt sealed receipt facts (outcome, exit code, digest)
20
+ * terminal always the last write for a run
21
+ *
22
+ * Bounding follows the worker bulk lane's philosophy. Display-only `event`
23
+ * lines are dropped once a run's journal crosses {@link
24
+ * RUN_EVENT_JOURNAL_CAP_BYTES}, and a single `journal_truncated` marker records
25
+ * that dropping began. Lifecycle (`open`, `terminal`) and receipt-bearing lines
26
+ * are never dropped, so a capped journal still answers what the run was and how
27
+ * it ended.
28
+ *
29
+ * Writes are buffered and flushed with one `appendFileSync` per batch rather
30
+ * than queued onto an async chain. A queue would need a timer to drain, and a
31
+ * timer on the dispatch event path either keeps a finished process alive or has
32
+ * to be unref'd and then races finalization; batching keeps the syscall count
33
+ * proportional to bytes written and leaves no handle behind. Every
34
+ * non-droppable line flushes immediately, so the durable file is never behind
35
+ * on the facts a viewer needs.
36
+ *
37
+ * A sink failure (ENOSPC, EPERM, a removed state root) degrades the whole
38
+ * journal to off after one notice. Nothing here ever throws into a dispatch.
39
+ */
40
+
41
+ import { appendFileSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
42
+ import { join } from "node:path";
43
+ import { clioStateDir, stateRootRemoved } from "../../core/xdg.js";
44
+
45
+ /** Directory name under the state root holding one subdirectory per run. */
46
+ export const RUN_EVENT_JOURNAL_DIR = "runs";
47
+ /** File name inside each run's journal directory. */
48
+ export const RUN_EVENT_JOURNAL_FILE = "events.ndjson";
49
+ /** Per-run size cap; crossing it drops display-only lines behind one marker. */
50
+ export const RUN_EVENT_JOURNAL_CAP_BYTES = 2 * 1024 * 1024;
51
+ /** Pending bytes that trigger a flush of droppable lines. */
52
+ export const RUN_EVENT_JOURNAL_FLUSH_BYTES = 8 * 1024;
53
+ /** Per-process env override, resolved ahead of settings (see runEventJournalEnabled). */
54
+ export const RUN_EVENT_JOURNAL_ENV_VAR = "CLIO_CODER_RUN_JOURNAL";
55
+
56
+ interface JournalLineBase {
57
+ seq: number;
58
+ at: string;
59
+ }
60
+
61
+ /** Everything on a journal line except the `seq`/`at` envelope the writer adds. */
62
+ export type RunEventJournalBody =
63
+ | { kind: "open"; runId: string; agentId: string }
64
+ | { kind: "event"; type: string; detail?: string }
65
+ | { kind: "journal_truncated"; reason: string; droppedFromSeq: number }
66
+ | {
67
+ kind: "receipt";
68
+ outcome: string;
69
+ exitCode: number | null;
70
+ digest?: string;
71
+ receiptPath?: string;
72
+ attemptRunId?: string;
73
+ }
74
+ | { kind: "terminal"; outcome: string; detail?: string };
75
+
76
+ export type RunEventJournalLine = JournalLineBase & RunEventJournalBody;
77
+
78
+ /** One display-tail entry, structurally the registry's `RunTailEntry`. */
79
+ export interface RunEventJournalEntry {
80
+ at: string;
81
+ type: string;
82
+ detail?: string;
83
+ }
84
+
85
+ /** Sealed receipt facts worth keeping even when display lines are being dropped. */
86
+ export interface RunEventJournalReceipt {
87
+ outcome: string;
88
+ exitCode: number | null;
89
+ digest?: string | undefined;
90
+ receiptPath?: string | undefined;
91
+ /** The terminal attempt's run id, recorded only when it differs from the logical run. */
92
+ attemptRunId?: string | undefined;
93
+ }
94
+
95
+ /**
96
+ * What the run event registry writes to. Every method is best-effort and
97
+ * returns void: a journal that cannot write degrades itself and the dispatch
98
+ * never learns about it.
99
+ */
100
+ export interface RunEventJournalSink {
101
+ open(runId: string, agentId: string): void;
102
+ append(runId: string, entry: RunEventJournalEntry): void;
103
+ receipt(runId: string, receipt: RunEventJournalReceipt): void;
104
+ terminal(runId: string, outcome: string, detail?: string): void;
105
+ }
106
+
107
+ export interface RunEventJournal extends RunEventJournalSink {
108
+ /** Flush any buffered droppable lines. Called by tests and by terminal writes. */
109
+ flush(): void;
110
+ /** True once a write failed and the journal turned itself off. */
111
+ degraded(): boolean;
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Enablement
116
+ // ---------------------------------------------------------------------------
117
+
118
+ let configuredEnabled: boolean | undefined;
119
+
120
+ /**
121
+ * Install the `panes.journal` setting. Called from the composition root beside
122
+ * `configureGuardrails` so the journal never reads settings.yaml itself; a
123
+ * settings read on the dispatch event path would be both a cost and a throw
124
+ * site (readSettings rejects an invalid file).
125
+ */
126
+ export function configureRunEventJournal(enabled: boolean | undefined): void {
127
+ configuredEnabled = enabled;
128
+ }
129
+
130
+ function parseEnvFlag(raw: string | undefined): boolean | undefined {
131
+ if (raw === undefined) return undefined;
132
+ const normalized = raw.trim().toLowerCase();
133
+ if (normalized === "1" || normalized === "true" || normalized === "on") return true;
134
+ if (normalized === "0" || normalized === "false" || normalized === "off") return false;
135
+ return undefined;
136
+ }
137
+
138
+ /** env override > configured settings > on. */
139
+ export function runEventJournalEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
140
+ return parseEnvFlag(env[RUN_EVENT_JOURNAL_ENV_VAR]) ?? configuredEnabled ?? true;
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Paths
145
+ // ---------------------------------------------------------------------------
146
+
147
+ /** Root holding one journal directory per run. */
148
+ export function runEventJournalRoot(): string {
149
+ return join(clioStateDir(), RUN_EVENT_JOURNAL_DIR);
150
+ }
151
+
152
+ /** One run's journal directory. */
153
+ export function runEventJournalDir(runId: string, root?: string): string {
154
+ return join(root ?? runEventJournalRoot(), runId);
155
+ }
156
+
157
+ /** One run's NDJSON file. */
158
+ export function runEventJournalPath(runId: string, root?: string): string {
159
+ return join(runEventJournalDir(runId, root), RUN_EVENT_JOURNAL_FILE);
160
+ }
161
+
162
+ /**
163
+ * The run whose journal was written to most recently, or null when none exists.
164
+ *
165
+ * `/panes open logs` tails it. Reading mtimes off the journal files is the
166
+ * cheapest honest answer: the ledger orders runs by admission, and the run an
167
+ * operator wants to watch is the one still producing lines, which is not always
168
+ * the newest admitted one.
169
+ */
170
+ export function newestRunEventJournalRunId(root?: string): string | null {
171
+ if (stateRootRemoved()) return null;
172
+ const dir = root ?? runEventJournalRoot();
173
+ let names: string[];
174
+ try {
175
+ names = readdirSync(dir);
176
+ } catch {
177
+ return null;
178
+ }
179
+ let newest: { runId: string; at: number } | null = null;
180
+ for (const runId of names) {
181
+ if (!isSafeRunId(runId)) continue;
182
+ const stats = statSync(runEventJournalPath(runId, dir), { throwIfNoEntry: false });
183
+ if (stats === undefined || !stats.isFile()) continue;
184
+ const at = stats.mtimeMs;
185
+ if (newest === null || at > newest.at) newest = { runId, at };
186
+ }
187
+ return newest?.runId ?? null;
188
+ }
189
+
190
+ /**
191
+ * Drop the journal directories of runs that left the ledger ring. Best-effort:
192
+ * retention is bookkeeping and must never fail the persist that triggered it.
193
+ */
194
+ export function removeRunEventJournals(runIds: Iterable<string>, root?: string): void {
195
+ if (stateRootRemoved()) return;
196
+ for (const runId of runIds) {
197
+ if (!isSafeRunId(runId)) continue;
198
+ try {
199
+ rmSync(runEventJournalDir(runId, root), { recursive: true, force: true });
200
+ } catch {
201
+ // A directory we could not remove is retained, not an error.
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Run ids are generated by `newRunId()` as base36, and fleet/gate ids add a
208
+ * prefix and hex. Nothing legitimate contains a path separator, so anything
209
+ * that does is refused rather than resolved: this function's only two callers
210
+ * build a path from the id and then remove or write it.
211
+ */
212
+ function isSafeRunId(runId: string): boolean {
213
+ return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(runId) && runId !== "." && runId !== "..";
214
+ }
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // Writer
218
+ // ---------------------------------------------------------------------------
219
+
220
+ interface RunJournalState {
221
+ path: string;
222
+ seq: number;
223
+ bytes: number;
224
+ pending: string[];
225
+ pendingBytes: number;
226
+ dropping: boolean;
227
+ closed: boolean;
228
+ touchedAt: number;
229
+ }
230
+
231
+ /**
232
+ * Runs tracked at once. Bounded so a long-lived orchestrator that never sees a
233
+ * terminal event for some run (a crash, an abandoned batch) cannot grow this
234
+ * map without limit. Eviction only forgets the byte counter; the file itself is
235
+ * untouched and a later write to an evicted run reopens it with a fresh count.
236
+ */
237
+ const RUN_STATE_LIMIT = 256;
238
+
239
+ export interface CreateRunEventJournalOptions {
240
+ /** Override the journal root. Defaults to `clioStateDir()/runs`, resolved per write. */
241
+ root?: string;
242
+ capBytes?: number;
243
+ flushBytes?: number;
244
+ warn?: (message: string) => void;
245
+ now?: () => Date;
246
+ /** Override enablement. Defaults to {@link runEventJournalEnabled}. */
247
+ isEnabled?: () => boolean;
248
+ }
249
+
250
+ export function createRunEventJournal(options: CreateRunEventJournalOptions = {}): RunEventJournal {
251
+ const capBytes = options.capBytes ?? RUN_EVENT_JOURNAL_CAP_BYTES;
252
+ const flushBytes = options.flushBytes ?? RUN_EVENT_JOURNAL_FLUSH_BYTES;
253
+ const warn = options.warn ?? ((message: string) => process.stderr.write(`[clio:journal] ${message}\n`));
254
+ const now = options.now ?? (() => new Date());
255
+ const isEnabled = options.isEnabled ?? (() => runEventJournalEnabled());
256
+ const runs = new Map<string, RunJournalState>();
257
+ let degraded = false;
258
+
259
+ const degrade = (error: unknown): void => {
260
+ if (degraded) return;
261
+ degraded = true;
262
+ runs.clear();
263
+ warn(
264
+ `run event journal disabled for this process: ${error instanceof Error ? error.message : String(error)}. ` +
265
+ "Dispatch is unaffected; `clio-coder fleet view` will have no transcript for runs started from here.",
266
+ );
267
+ };
268
+
269
+ const pruneRunStates = (): void => {
270
+ while (runs.size > RUN_STATE_LIMIT) {
271
+ let oldestKey: string | null = null;
272
+ let oldestSeen = Number.POSITIVE_INFINITY;
273
+ for (const [key, state] of runs) {
274
+ if (state.pending.length > 0 || state.touchedAt >= oldestSeen) continue;
275
+ oldestKey = key;
276
+ oldestSeen = state.touchedAt;
277
+ }
278
+ if (oldestKey === null) break;
279
+ runs.delete(oldestKey);
280
+ }
281
+ };
282
+
283
+ const stateFor = (runId: string): RunJournalState | null => {
284
+ if (degraded || !isSafeRunId(runId) || !isEnabled()) return null;
285
+ const existing = runs.get(runId);
286
+ if (existing !== undefined) {
287
+ if (existing.closed) return null;
288
+ existing.touchedAt = Date.now();
289
+ return existing;
290
+ }
291
+ if (stateRootRemoved()) return null;
292
+ let path: string;
293
+ try {
294
+ const dir = runEventJournalDir(runId, options.root);
295
+ mkdirSync(dir, { recursive: true });
296
+ path = join(dir, RUN_EVENT_JOURNAL_FILE);
297
+ } catch (error) {
298
+ degrade(error);
299
+ return null;
300
+ }
301
+ // A run reopened after state eviction (or after a resume) continues the
302
+ // existing file, so the byte counter starts from what is already there
303
+ // and the cap keeps meaning the same thing.
304
+ let bytes = 0;
305
+ let seq = 0;
306
+ try {
307
+ const stat = statSync(path);
308
+ bytes = stat.size;
309
+ } catch {
310
+ // First write for this run; the file does not exist yet.
311
+ }
312
+ if (bytes > 0) seq = countJournalSeq(path);
313
+ const state: RunJournalState = {
314
+ path,
315
+ seq,
316
+ bytes,
317
+ pending: [],
318
+ pendingBytes: 0,
319
+ dropping: bytes >= capBytes,
320
+ closed: false,
321
+ touchedAt: Date.now(),
322
+ };
323
+ runs.set(runId, state);
324
+ pruneRunStates();
325
+ return state;
326
+ };
327
+
328
+ const flushState = (state: RunJournalState): void => {
329
+ if (state.pending.length === 0) return;
330
+ const payload = state.pending.join("");
331
+ state.pending = [];
332
+ state.pendingBytes = 0;
333
+ if (stateRootRemoved()) return;
334
+ try {
335
+ appendFileSync(state.path, payload, "utf8");
336
+ } catch (error) {
337
+ degrade(error);
338
+ }
339
+ };
340
+
341
+ const write = (state: RunJournalState, line: RunEventJournalBody, droppable: boolean): void => {
342
+ if (droppable && state.dropping) return;
343
+ if (droppable && state.bytes + state.pendingBytes >= capBytes) {
344
+ state.dropping = true;
345
+ write(state, { kind: "journal_truncated", reason: "per-run size cap", droppedFromSeq: state.seq + 1 }, false);
346
+ return;
347
+ }
348
+ state.seq += 1;
349
+ const serialized = `${JSON.stringify({ seq: state.seq, at: now().toISOString(), ...line })}\n`;
350
+ const size = Buffer.byteLength(serialized, "utf8");
351
+ state.pending.push(serialized);
352
+ state.pendingBytes += size;
353
+ state.bytes += size;
354
+ if (!droppable || state.pendingBytes >= flushBytes) flushState(state);
355
+ };
356
+
357
+ return {
358
+ open(runId, agentId): void {
359
+ const state = stateFor(runId);
360
+ if (state === null) return;
361
+ // Reopening a run that already has lines is a resume or a second
362
+ // registration of the same logical run; the file keeps its history and
363
+ // gains a fresh open line rather than being rewritten.
364
+ write(state, { kind: "open", runId, agentId }, false);
365
+ },
366
+ append(runId, entry): void {
367
+ const state = stateFor(runId);
368
+ if (state === null) return;
369
+ write(
370
+ state,
371
+ entry.detail === undefined
372
+ ? { kind: "event", type: entry.type }
373
+ : { kind: "event", type: entry.type, detail: entry.detail },
374
+ true,
375
+ );
376
+ },
377
+ receipt(runId, receipt): void {
378
+ const state = stateFor(runId);
379
+ if (state === null) return;
380
+ write(
381
+ state,
382
+ {
383
+ kind: "receipt",
384
+ outcome: receipt.outcome,
385
+ exitCode: receipt.exitCode,
386
+ ...(receipt.digest !== undefined ? { digest: receipt.digest } : {}),
387
+ ...(receipt.receiptPath !== undefined ? { receiptPath: receipt.receiptPath } : {}),
388
+ ...(receipt.attemptRunId !== undefined ? { attemptRunId: receipt.attemptRunId } : {}),
389
+ },
390
+ false,
391
+ );
392
+ },
393
+ terminal(runId, outcome, detail): void {
394
+ const state = stateFor(runId);
395
+ if (state === null) return;
396
+ write(state, detail === undefined ? { kind: "terminal", outcome } : { kind: "terminal", outcome, detail }, false);
397
+ flushState(state);
398
+ // Closing here is what makes the terminal line the last write: any
399
+ // later event for this run finds a closed state and is discarded.
400
+ state.closed = true;
401
+ },
402
+ flush(): void {
403
+ for (const state of runs.values()) flushState(state);
404
+ },
405
+ degraded(): boolean {
406
+ return degraded;
407
+ },
408
+ };
409
+ }
410
+
411
+ /**
412
+ * Highest `seq` already in a run's file. Only read when a journal is reopened,
413
+ * which happens on resume or after state eviction, never on the common path.
414
+ */
415
+ function countJournalSeq(path: string): number {
416
+ try {
417
+ const raw = readFileSync(path, "utf8");
418
+ let highest = 0;
419
+ for (const line of raw.split("\n")) {
420
+ if (line.length === 0) continue;
421
+ const parsed = parseJournalLine(line);
422
+ if (parsed !== null && parsed.seq > highest) highest = parsed.seq;
423
+ }
424
+ return highest;
425
+ } catch {
426
+ return 0;
427
+ }
428
+ }
429
+
430
+ // ---------------------------------------------------------------------------
431
+ // Process-wide default sink
432
+ // ---------------------------------------------------------------------------
433
+
434
+ let defaultJournal: RunEventJournal | null = null;
435
+
436
+ /**
437
+ * The sink every run event registry attaches to unless it was handed one.
438
+ * Created lazily so importing the registry costs nothing, and shared so two
439
+ * registries in one process cannot interleave two byte counters over one file.
440
+ *
441
+ * A run's file path is resolved on its first write and then held, so a state
442
+ * root that moves mid-process (only tests do that) leaves already-opened runs
443
+ * pointing at the old root. Run ids are random, so a fresh root always brings
444
+ * fresh ids with it and nothing observable depends on re-resolving.
445
+ */
446
+ export function defaultRunEventJournal(): RunEventJournal {
447
+ defaultJournal ??= createRunEventJournal();
448
+ return defaultJournal;
449
+ }
450
+
451
+ // ---------------------------------------------------------------------------
452
+ // Reader
453
+ // ---------------------------------------------------------------------------
454
+
455
+ /** Tail bytes a reader will pull, so following a capped journal stays cheap. */
456
+ export const RUN_EVENT_JOURNAL_READ_TAIL_BYTES = 256 * 1024;
457
+
458
+ export interface ReadRunEventJournalOptions {
459
+ root?: string;
460
+ /** Keep at most this many lines, newest last. */
461
+ maxLines?: number;
462
+ tailBytes?: number;
463
+ }
464
+
465
+ export interface RunEventJournalRead {
466
+ path: string;
467
+ present: boolean;
468
+ bytes: number;
469
+ lines: RunEventJournalLine[];
470
+ /** True when the writer dropped display lines, or when this read skipped a file head. */
471
+ truncated: boolean;
472
+ terminal: { outcome: string; detail?: string } | null;
473
+ agentId: string | null;
474
+ }
475
+
476
+ function parseJournalLine(raw: string): RunEventJournalLine | null {
477
+ let parsed: unknown;
478
+ try {
479
+ parsed = JSON.parse(raw);
480
+ } catch {
481
+ return null;
482
+ }
483
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
484
+ const record = parsed as Record<string, unknown>;
485
+ if (typeof record.seq !== "number" || typeof record.at !== "string" || typeof record.kind !== "string") return null;
486
+ switch (record.kind) {
487
+ case "open":
488
+ if (typeof record.runId !== "string" || typeof record.agentId !== "string") return null;
489
+ return { seq: record.seq, at: record.at, kind: "open", runId: record.runId, agentId: record.agentId };
490
+ case "event": {
491
+ if (typeof record.type !== "string") return null;
492
+ const detail = typeof record.detail === "string" ? record.detail : undefined;
493
+ return {
494
+ seq: record.seq,
495
+ at: record.at,
496
+ kind: "event",
497
+ type: record.type,
498
+ ...(detail === undefined ? {} : { detail }),
499
+ };
500
+ }
501
+ case "journal_truncated":
502
+ return {
503
+ seq: record.seq,
504
+ at: record.at,
505
+ kind: "journal_truncated",
506
+ reason: typeof record.reason === "string" ? record.reason : "unknown",
507
+ droppedFromSeq: typeof record.droppedFromSeq === "number" ? record.droppedFromSeq : record.seq,
508
+ };
509
+ case "receipt": {
510
+ if (typeof record.outcome !== "string") return null;
511
+ return {
512
+ seq: record.seq,
513
+ at: record.at,
514
+ kind: "receipt",
515
+ outcome: record.outcome,
516
+ exitCode: typeof record.exitCode === "number" ? record.exitCode : null,
517
+ ...(typeof record.digest === "string" ? { digest: record.digest } : {}),
518
+ ...(typeof record.receiptPath === "string" ? { receiptPath: record.receiptPath } : {}),
519
+ ...(typeof record.attemptRunId === "string" ? { attemptRunId: record.attemptRunId } : {}),
520
+ };
521
+ }
522
+ case "terminal": {
523
+ if (typeof record.outcome !== "string") return null;
524
+ const detail = typeof record.detail === "string" ? record.detail : undefined;
525
+ return {
526
+ seq: record.seq,
527
+ at: record.at,
528
+ kind: "terminal",
529
+ outcome: record.outcome,
530
+ ...(detail === undefined ? {} : { detail }),
531
+ };
532
+ }
533
+ default:
534
+ return null;
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Read one run's journal. Reads at most the last {@link
540
+ * RUN_EVENT_JOURNAL_READ_TAIL_BYTES}, discarding a leading partial line, so a
541
+ * viewer polling a capped journal never pulls megabytes per tick. A head that
542
+ * was skipped reports as `truncated` for the same reason the writer's marker
543
+ * does: the transcript on screen is not the whole run.
544
+ */
545
+ export function readRunEventJournal(runId: string, options: ReadRunEventJournalOptions = {}): RunEventJournalRead {
546
+ const path = runEventJournalPath(runId, options.root);
547
+ const empty: RunEventJournalRead = {
548
+ path,
549
+ present: false,
550
+ bytes: 0,
551
+ lines: [],
552
+ truncated: false,
553
+ terminal: null,
554
+ agentId: null,
555
+ };
556
+ if (!isSafeRunId(runId)) return empty;
557
+ const tailBytes = options.tailBytes ?? RUN_EVENT_JOURNAL_READ_TAIL_BYTES;
558
+ let raw: string;
559
+ let size: number;
560
+ let headDropped = false;
561
+ try {
562
+ size = statSync(path).size;
563
+ if (size <= tailBytes) {
564
+ raw = readFileSync(path, "utf8");
565
+ } else {
566
+ const buffer = readFileSync(path);
567
+ raw = buffer.subarray(size - tailBytes).toString("utf8");
568
+ const firstBreak = raw.indexOf("\n");
569
+ raw = firstBreak === -1 ? "" : raw.slice(firstBreak + 1);
570
+ headDropped = true;
571
+ }
572
+ } catch {
573
+ return empty;
574
+ }
575
+ const lines: RunEventJournalLine[] = [];
576
+ let truncated = headDropped;
577
+ let terminal: { outcome: string; detail?: string } | null = null;
578
+ let agentId: string | null = null;
579
+ for (const candidate of raw.split("\n")) {
580
+ if (candidate.length === 0) continue;
581
+ const parsed = parseJournalLine(candidate);
582
+ // A trailing partial line is the writer mid-flush, not corruption.
583
+ if (parsed === null) continue;
584
+ if (parsed.kind === "journal_truncated") truncated = true;
585
+ if (parsed.kind === "open") agentId = parsed.agentId;
586
+ if (parsed.kind === "terminal") {
587
+ terminal =
588
+ parsed.detail === undefined ? { outcome: parsed.outcome } : { outcome: parsed.outcome, detail: parsed.detail };
589
+ }
590
+ lines.push(parsed);
591
+ }
592
+ const maxLines = options.maxLines;
593
+ if (maxLines !== undefined && lines.length > maxLines) {
594
+ lines.splice(0, lines.length - maxLines);
595
+ truncated = true;
596
+ }
597
+ return { path, present: true, bytes: size, lines, truncated, terminal, agentId };
598
+ }
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  import { randomBytes } from "node:crypto";
22
- import { existsSync, readFileSync } from "node:fs";
22
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
23
23
  import { join } from "node:path";
24
24
  import { resolveGuardrail } from "../../core/guardrails.js";
25
25
  import { withStateFileLock } from "../../core/state-file-lock.js";
@@ -28,6 +28,7 @@ import { atomicWrite } from "../../engine/session.js";
28
28
  import type { ExecutionStepResult } from "./execution-scheduler.js";
29
29
  import { computeReceiptFindingsSummary } from "./receipt-findings.js";
30
30
  import { withReceiptIntegrity } from "./receipt-integrity.js";
31
+ import { removeRunEventJournals } from "./run-event-journal.js";
31
32
  import type { RunEnvelope, RunReceipt, RunReceiptDraft, RunStatus } from "./types.js";
32
33
 
33
34
  export interface FleetRunRecord {
@@ -107,6 +108,45 @@ export async function writeFleetRun(record: FleetRunRecord): Promise<void> {
107
108
  await atomicWrite(fleetRunPath(record.id), `${JSON.stringify(record, null, 2)}\n`);
108
109
  }
109
110
 
111
+ /**
112
+ * How many fleet-run records a single listing will open.
113
+ *
114
+ * The directory is unbounded and the newest root is only discoverable by
115
+ * reading each record's start stamp, so a scan is the only ordering available.
116
+ * The cap keeps that scan proportional on an installation that has accumulated
117
+ * roots for months, at the cost of missing a root older than the cap. That is
118
+ * the right trade for every caller here: they all want the recent end.
119
+ */
120
+ export const MAX_FLEET_RUN_SCAN = 64;
121
+
122
+ /**
123
+ * Durable fleet-run roots, newest start first.
124
+ *
125
+ * Reads defensively for the same reason {@link readFleetRun} does: one record
126
+ * written by another build, half-written, or removed between the listing and
127
+ * the open must cost that root its row, never the whole listing.
128
+ */
129
+ export function listFleetRuns(limit = MAX_FLEET_RUN_SCAN): FleetRunRecord[] {
130
+ let entries: string[];
131
+ try {
132
+ entries = readdirSync(join(clioStateDir(), "fleet-runs"));
133
+ } catch {
134
+ return [];
135
+ }
136
+ const records: FleetRunRecord[] = [];
137
+ for (const name of entries.slice(0, MAX_FLEET_RUN_SCAN)) {
138
+ if (!name.endsWith(".json")) continue;
139
+ const record = readFleetRun(name.slice(0, -".json".length));
140
+ if (record !== null) records.push(record);
141
+ }
142
+ records.sort((a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0));
143
+ return records.slice(0, Math.max(0, limit));
144
+ }
145
+
146
+ // `RunEnvelope.heartbeatAt` deliberately persists only the absolute wall-clock
147
+ // anchor used for operator display and crash recovery. The monotonic heartbeat
148
+ // stamp belongs to the live transport in this process; serializing it would
149
+ // give a restarted process a number from an origin it cannot share.
110
150
  function readRuns(): RunEnvelope[] {
111
151
  const path = runsPath();
112
152
  if (!existsSync(path)) return [];
@@ -319,6 +359,14 @@ export function openLedger(opts?: LedgerOptions): Ledger {
319
359
  const capped = capRuns(merged, maxRuns);
320
360
  runs = capped;
321
361
  atomicWrite(target, JSON.stringify(capped, null, 2));
362
+ // A run that left the ring has no ledger row to view any more, so its
363
+ // event journal is unreachable state. Retention is bound to the ring
364
+ // rather than to a second policy so the two can never disagree about
365
+ // how much history exists.
366
+ if (merged.length > capped.length) {
367
+ const kept = new Set(capped.map((run) => run.id));
368
+ removeRunEventJournals(merged.filter((run) => !kept.has(run.id)).map((run) => run.id));
369
+ }
322
370
  // Everything this process claimed is now on disk, so the mirror holds
323
371
  // no newer state until the next write. Clearing here is what stops a
324
372
  // long-lived process from re-asserting an old snapshot of its own rows
@@ -225,6 +225,17 @@ export interface RunGateSubjectRef {
225
225
  digest: string | null;
226
226
  }
227
227
 
228
+ /** Storage route used for one compete candidate's isolated checkout. */
229
+ export interface RunGateWorktreeProvenance {
230
+ backend: "herdr" | "native";
231
+ path: string;
232
+ branch: string;
233
+ /** Herdr workspace created around the worktree; absent for the native route. */
234
+ workspaceId?: string;
235
+ /** Why the native route was selected when mux was configured. */
236
+ fallback?: "mux-unavailable" | "mux-operation-failed";
237
+ }
238
+
228
239
  /**
229
240
  * Review/compete gate provenance, carried on the request and sealed into the
230
241
  * receipt. References point backward only: a reviewer references the builder
@@ -244,6 +255,8 @@ export interface RunGateProvenance {
244
255
  subjects?: RunGateSubjectRef[];
245
256
  /** Reviewer verdict that caused this run; only on revise builders. */
246
257
  verdict?: "pass" | "fail" | "revise";
258
+ /** Compete-only worktree route, sealed into the run receipt with the gate. */
259
+ worktree?: RunGateWorktreeProvenance;
247
260
  }
248
261
 
249
262
  export interface RunCouncilProvenance {