@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
@@ -10,8 +10,9 @@
10
10
  */
11
11
 
12
12
  import { createHash, randomBytes } from "node:crypto";
13
- import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
13
+ import { type Dirent, readdirSync, readFileSync } from "node:fs";
14
14
  import { isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
15
+ import { performance } from "node:perf_hooks";
15
16
  import { BusChannels, type DispatchCompletedPayload, type DispatchRunIdentity } from "../../core/bus-events.js";
16
17
  import { DEFAULT_SETTINGS, type DelegationToolGovernance } from "../../core/defaults.js";
17
18
  import type { DomainBundle, DomainContext, DomainExtension } from "../../core/domain-loader.js";
@@ -57,6 +58,7 @@ import {
57
58
  import type { AgentsContract } from "../agents/contract.js";
58
59
  import type { AgentRecipe } from "../agents/recipe.js";
59
60
  import { validateRecipeResult } from "../agents/result-contract.js";
61
+ import { nodeResultContractFilesystem } from "../agents/result-contract-filesystem.js";
60
62
  import {
61
63
  type AgentAudience,
62
64
  type AgentCapabilityClass,
@@ -76,12 +78,16 @@ import { type EffectivePricing, resolveEffectivePricing } from "../providers/cat
76
78
  import {
77
79
  type CapabilityFlags,
78
80
  canonicalizeWireModelId,
81
+ type EndpointCapacity,
82
+ endpointCapacityFor,
83
+ endpointCapacityForStatus,
79
84
  firstRuntimeResolutionError,
80
85
  isDispatchEligibleRuntime,
81
86
  type ProvidersContract,
82
87
  type ResolvedRuntimeTarget,
83
88
  type RuntimeApiFamily,
84
89
  type RuntimeDescriptor,
90
+ resolveEndpointCapacities,
85
91
  resolveModelCapabilities,
86
92
  resolveRuntimeTarget,
87
93
  runtimeResolutionWarnings,
@@ -108,6 +114,7 @@ import {
108
114
  routeValidationProjection,
109
115
  } from "./active-route-planner.js";
110
116
  import { admit, createCapacityAdmissionController, createLeaseSlotGuard } from "./admission.js";
117
+ import { AdmissionCanceledError } from "./admission-error.js";
111
118
  import { agentRouteCandidates } from "./agent-candidates.js";
112
119
  import { AGENT_LEDGER_PROMPT_MAX_CHARS, renderAgentLedger } from "./agent-ledger.js";
113
120
  import { publishAgentLedgerEntry, subscribeAgentLedger } from "./agent-ledger-hub.js";
@@ -166,7 +173,14 @@ import {
166
173
  } from "./failure-classification.js";
167
174
  import { routeFactVerdict } from "./fleet-preflight.js";
168
175
  import { competeStanceLiner, isBoundedGateRolePrompt } from "./gate-role-prompts.js";
169
- import { classifyHeartbeat, DEFAULT_HEARTBEAT_SPEC, type HeartbeatSpec, type HeartbeatStatus } from "./heartbeat.js";
176
+ import {
177
+ classifyHeartbeat,
178
+ DEFAULT_HEARTBEAT_SPEC,
179
+ type HeartbeatSpec,
180
+ type HeartbeatStamp,
181
+ type HeartbeatStatus,
182
+ heartbeatMonotonicAt,
183
+ } from "./heartbeat.js";
170
184
  import { hostVerificationRejection, runHostVerification } from "./host-verification.js";
171
185
  import { renderDispatchIntentRequirements } from "./intent-requirements.js";
172
186
  import { adaptJointRouteInput } from "./joint-route-adapter.js";
@@ -205,6 +219,7 @@ import {
205
219
  planQueueSlot,
206
220
  type ReservationCapacitySnapshot,
207
221
  rebindDispatchReservationMember,
222
+ releaseDispatchReservation,
208
223
  releaseDispatchReservationMember,
209
224
  reservedBudgetUsd,
210
225
  reservedPlanPeakSlots,
@@ -224,6 +239,7 @@ import {
224
239
  import { createRouteObserver, type RouteObservationHandle, type RouteObserver } from "./route-observer.js";
225
240
  import { reduceRouteQuality } from "./route-quality.js";
226
241
  import { defaultRoutingIntent } from "./routing-intent.js";
242
+ import { attachRunEventJournalBridge, type RunEventJournalBridge } from "./run-event-journal-bridge.js";
227
243
  import { detectRunIdentity } from "./run-identity.js";
228
244
  import { type Ledger, newRunId, openLedger } from "./state.js";
229
245
  import {
@@ -286,6 +302,7 @@ import {
286
302
  type SpawnOptions as WorkerSpawnOptions,
287
303
  type WorkerSpec,
288
304
  } from "./worker-spawn.js";
305
+ import type { WriteBoundaryAttribution, WriteBoundaryAttributionDowngrade } from "./write-boundary.js";
289
306
 
290
307
  interface RunTokenMeter {
291
308
  inputTokens: number;
@@ -337,7 +354,7 @@ interface ActiveRun {
337
354
  /** ACP event-inactivity window; null for native runs (heartbeat spec governs those). */
338
355
  stallTimeoutMs: number | null;
339
356
  lineage: RunLineage;
340
- heartbeatAt: { current: number } | null;
357
+ heartbeatAt: HeartbeatStamp | null;
341
358
  heartbeatStatus: HeartbeatStatus;
342
359
  meter: RunTokenMeter;
343
360
  pricing: EffectivePricing["rates"];
@@ -345,6 +362,12 @@ interface ActiveRun {
345
362
  finalPromise: Promise<RunReceipt>;
346
363
  }
347
364
 
365
+ interface PendingCapacityAdmission {
366
+ identity: DispatchRunIdentity & { requestOrigin: DispatchRequestOrigin };
367
+ timing: RunPhaseMarks;
368
+ node: RunNodeIdentity | null;
369
+ }
370
+
348
371
  interface DispatchFinishContractSnapshot {
349
372
  assessment: FinishContractAssessment;
350
373
  rigor: Rigor;
@@ -373,6 +396,8 @@ export interface DispatchBundleOptions {
373
396
  heartbeatIntervalMs?: number;
374
397
  resilienceCooldownMs?: number;
375
398
  now?: () => number;
399
+ /** Monotonic clock for heartbeat age and ACP event-inactivity spans. */
400
+ monotonicNow?: () => number;
376
401
  /** Immutable per-attempt session settings view; falls back to the shared snapshot. */
377
402
  getSettings?: () => EffectiveSettings;
378
403
  /** Live hard-block state cloned into each mediated worker spec. */
@@ -383,6 +408,15 @@ export interface DispatchBundleOptions {
383
408
  collectReproducibility?: typeof collectReproducibilityMetadata;
384
409
  /** Observer injection seam. Production constructs the durable observer. */
385
410
  routeObserver?: RouteObserver;
411
+ /**
412
+ * Write the durable run event journal for every run this bundle dispatches.
413
+ * Off by default and turned on by the three composition roots
414
+ * (src/entry/orchestrator.ts, src/cli/run.ts, src/cli/fleet.ts), because the
415
+ * process that owns a bundle also decides who owns the journal file: a
416
+ * composed process routes `registerAllTools`' event registry to
417
+ * `journal: null` so a tool-path run is transcribed once, not twice.
418
+ */
419
+ journalRunEvents?: boolean;
386
420
  }
387
421
 
388
422
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 1000;
@@ -2397,18 +2431,20 @@ export function createDispatchBundle(
2397
2431
  return DEFAULT_RESILIENCE_COOLDOWN_MS;
2398
2432
  };
2399
2433
  const now = options?.now ?? (() => Date.now());
2434
+ const monotonicNow = options?.monotonicNow ?? (() => performance.now());
2400
2435
  // Durable evidence owner used by shadow observation and active readiness.
2401
2436
  const routeObserver: RouteObserver = options?.routeObserver ?? createRouteObserver({});
2402
2437
 
2403
2438
  let ledger: Ledger | null = null;
2404
2439
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
2405
2440
  const active = new Map<string, ActiveRun>();
2441
+ const pendingCapacity = new Map<string, PendingCapacityAdmission>();
2406
2442
  const targetCooldowns = new Map<string, { until: number; reason: string }>();
2407
2443
  const ownedReservations = new Set<string>();
2408
2444
  /**
2409
- * Paths each finalized run's own tool calls aimed a successful mutation at,
2410
- * newest last. `null` records a run whose write record is unusable, which is
2411
- * a different answer from an empty list and has to survive as one.
2445
+ * Attribution from each finalized run's successful tool calls, newest last.
2446
+ * An incomplete record retains its paths as a lower bound beside the reasons
2447
+ * it cannot be read as a closed list.
2412
2448
  *
2413
2449
  * Held in memory rather than sealed into the receipt: the only reader is the
2414
2450
  * write boundary closing a window seconds later in this process, and a
@@ -2416,13 +2452,13 @@ export function createDispatchBundle(
2416
2452
  * Bounded because a long session finalizes many runs and none of this is
2417
2453
  * needed once its window has closed.
2418
2454
  */
2419
- const runWriteTargets = new Map<string, ReadonlyArray<string> | null>();
2420
- const recordRunWriteTargets = (runId: string, paths: ReadonlyArray<string> | null): void => {
2421
- runWriteTargets.set(runId, paths);
2422
- while (runWriteTargets.size > RUN_WRITE_TARGET_HISTORY) {
2423
- const oldest = runWriteTargets.keys().next();
2455
+ const runWriteAttributions = new Map<string, WriteBoundaryAttribution>();
2456
+ const recordRunWriteAttribution = (runId: string, attribution: WriteBoundaryAttribution): void => {
2457
+ runWriteAttributions.set(runId, attribution);
2458
+ while (runWriteAttributions.size > RUN_WRITE_TARGET_HISTORY) {
2459
+ const oldest = runWriteAttributions.keys().next();
2424
2460
  if (oldest.done === true) break;
2425
- runWriteTargets.delete(oldest.value);
2461
+ runWriteAttributions.delete(oldest.value);
2426
2462
  }
2427
2463
  };
2428
2464
  const capacityAdmission = createCapacityAdmissionController({
@@ -2430,7 +2466,7 @@ export function createDispatchBundle(
2430
2466
  const settings = getEffectiveSettings();
2431
2467
  const nodes: Record<string, number> = { local: configuredGlobalCapacity(settings) };
2432
2468
  for (const node of settings?.fleet?.nodes ?? []) nodes[node.id] = node.maxWorkers;
2433
- return { global: configuredGlobalCapacity(settings), nodes };
2469
+ return { global: configuredGlobalCapacity(settings), nodes, endpoints: configuredEndpointLimits() };
2434
2470
  },
2435
2471
  now,
2436
2472
  reservedPlanPeak: (planId) =>
@@ -2443,6 +2479,39 @@ export function createDispatchBundle(
2443
2479
  }),
2444
2480
  );
2445
2481
 
2482
+ /**
2483
+ * Every endpoint the configured fleet can reach.
2484
+ *
2485
+ * Deliberately resolved from the configured targets as well as the probed
2486
+ * statuses. A target whose status has not been built yet contributed no key,
2487
+ * an absent key carries no limit, and reservation planning skips a dimension
2488
+ * it has no limit for, so admission against a single-slot server used to fail
2489
+ * open for whoever asked first after boot.
2490
+ */
2491
+ function configuredEndpointCapacities(): Readonly<Record<string, EndpointCapacity>> {
2492
+ return resolveEndpointCapacities({
2493
+ statuses: providers.list(),
2494
+ targets: getEffectiveSettings()?.targets ?? [],
2495
+ runtimeFor: (runtimeId) => providers.getRuntime(runtimeId),
2496
+ });
2497
+ }
2498
+
2499
+ function configuredEndpointLimits(): Readonly<Record<string, number>> {
2500
+ return Object.fromEntries(Object.entries(configuredEndpointCapacities()).map(([key, value]) => [key, value.limit]));
2501
+ }
2502
+
2503
+ function endpointCapacityForTarget(targetId: string): EndpointCapacity | null {
2504
+ const status = providers.list().find((entry) => entry.target.id === targetId);
2505
+ const target = status?.target ?? providers.getTarget(targetId);
2506
+ if (target === null || target === undefined) return null;
2507
+ const endpoint =
2508
+ status === undefined
2509
+ ? endpointCapacityFor({ target, runtime: providers.getRuntime(target.runtime) })
2510
+ : endpointCapacityForStatus(status);
2511
+ if (endpoint === null) return null;
2512
+ return configuredEndpointCapacities()[endpoint.key] ?? endpoint;
2513
+ }
2514
+
2446
2515
  function reservationCapacitySnapshot(settings: EffectiveSettings): ReservationCapacitySnapshot {
2447
2516
  const preflight = scheduling.preflight();
2448
2517
  // Reservation planning compares against a fresh read, never the display cache.
@@ -2452,9 +2521,21 @@ export function createDispatchBundle(
2452
2521
  };
2453
2522
  for (const node of settings?.fleet?.nodes ?? [])
2454
2523
  nodes[node.id] = { active: usage.nodes[node.id] ?? 0, limit: node.maxWorkers };
2524
+ const endpoints = Object.fromEntries(
2525
+ Object.entries(configuredEndpointLimits()).map(([key, limit]) => [
2526
+ key,
2527
+ {
2528
+ active: usage.endpoints[key] ?? 0,
2529
+ limit,
2530
+ leases: usage.endpointHolders[key]?.leases ?? 0,
2531
+ foregroundStreams: usage.endpointHolders[key]?.foregroundStreams ?? 0,
2532
+ },
2533
+ ]),
2534
+ );
2455
2535
  return {
2456
2536
  global: { active: usage.global, limit: configuredGlobalCapacity(settings) },
2457
2537
  nodes,
2538
+ endpoints,
2458
2539
  budget: { currentUsd: preflight.currentUsd, ceilingUsd: preflight.ceilingUsd },
2459
2540
  };
2460
2541
  }
@@ -2466,6 +2547,7 @@ export function createDispatchBundle(
2466
2547
  memberId: task.memberId,
2467
2548
  wave: task.wave,
2468
2549
  nodeId: task.resolution.node.id,
2550
+ ...(task.resolution.endpoint !== undefined ? { endpointKey: task.resolution.endpoint.key } : {}),
2469
2551
  costUpperBoundUsd: task.resolution.routeApproval?.totalCostUpperBoundUsd ?? task.resolution.costUpperBoundUsd,
2470
2552
  })),
2471
2553
  capacity: reservationCapacitySnapshot(getEffectiveSettings()),
@@ -2520,10 +2602,44 @@ export function createDispatchBundle(
2520
2602
  return scheduling.maxWorkers?.() ?? (configured === "auto" || configured === undefined ? 4 : Math.max(1, configured));
2521
2603
  }
2522
2604
 
2523
- async function admitAssignmentCapacity(req: DispatchRequest, nodeId: string, timing: RunPhaseMarks) {
2605
+ function publishCapacityQueued(
2606
+ identity: DispatchRunIdentity & { requestOrigin: DispatchRequestOrigin },
2607
+ timing: RunPhaseMarks,
2608
+ node: RunNodeIdentity | null,
2609
+ ): void {
2610
+ pendingCapacity.set(identity.runId, { identity, timing, node });
2611
+ context.bus.emit(BusChannels.DispatchEnqueued, identity);
2612
+ }
2613
+
2614
+ function publishCapacityAdmissionFailure(
2615
+ identity: DispatchRunIdentity & { requestOrigin: DispatchRequestOrigin },
2616
+ error: unknown,
2617
+ ): void {
2618
+ const detail = error instanceof Error ? error.message : String(error);
2619
+ const outcome: RunOutcome =
2620
+ error instanceof AdmissionCanceledError
2621
+ ? "canceled"
2622
+ : /admission timed out/u.test(detail)
2623
+ ? "timed_out"
2624
+ : "denied_by_policy";
2625
+ context.bus.emit(BusChannels.DispatchFailed, {
2626
+ ...identity,
2627
+ outcome,
2628
+ outcomeDetail: detail,
2629
+ reason: outcome,
2630
+ });
2631
+ }
2632
+
2633
+ async function admitAssignmentCapacity(
2634
+ req: DispatchRequest,
2635
+ nodeId: string,
2636
+ timing: RunPhaseMarks,
2637
+ endpoint?: EndpointCapacity | null,
2638
+ ) {
2524
2639
  const queuedAt = now();
2525
2640
  timing.queuedAt = new Date(queuedAt).toISOString();
2526
- const assignmentId = req.lineage?.rootRunId ?? `pending-${queuedAt.toString(36)}-${randomBytes(6).toString("hex")}`;
2641
+ const assignmentId =
2642
+ req.lineage?.rootRunId ?? req.runIdHint ?? `pending-${queuedAt.toString(36)}-${randomBytes(6).toString("hex")}`;
2527
2643
  const requestedAt = Date.parse(timing.requestedAt ?? timing.queuedAt);
2528
2644
  const deadlineAt = req.assignmentDeadlineAt ?? requestedAt + (req.routingIntent?.deadlineMs ?? 60_000);
2529
2645
  if (req.lineage === undefined) await registerAssignment(assignmentId);
@@ -2538,9 +2654,16 @@ export function createDispatchBundle(
2538
2654
  const admitted = await capacityAdmission.admit({
2539
2655
  assignmentId,
2540
2656
  nodeId,
2657
+ ...(endpoint !== null && endpoint !== undefined ? { endpointKey: endpoint.key } : {}),
2541
2658
  deadlineAt,
2542
2659
  ...plan,
2543
- ...(req.reservation !== undefined && req.lineage === undefined ? { reservation: req.reservation } : {}),
2660
+ // A fleet's first attempt carries lineage and still owns the held
2661
+ // reservation member that admission must transfer into its lease.
2662
+ // Retries carry the same reservation identity after that member was
2663
+ // consumed, so they reacquire/rebind the assignment lease normally.
2664
+ ...(req.reservation !== undefined && (req.lineage === undefined || req.lineage.attempt === 0)
2665
+ ? { reservation: req.reservation }
2666
+ : {}),
2544
2667
  });
2545
2668
  timing.admittedAt = new Date(admitted.admittedAt).toISOString();
2546
2669
  return admitted.lease;
@@ -2557,6 +2680,7 @@ export function createDispatchBundle(
2557
2680
  function rebindReservationSlot(
2558
2681
  req: DispatchRequest,
2559
2682
  nodeId: string,
2683
+ endpoint: EndpointCapacity | null,
2560
2684
  costUsd: number,
2561
2685
  settings: EffectiveSettings,
2562
2686
  ): void {
@@ -2565,6 +2689,7 @@ export function createDispatchBundle(
2565
2689
  const rebind = {
2566
2690
  ...req.reservation,
2567
2691
  nodeId,
2692
+ ...(endpoint !== null ? { endpointKey: endpoint.key } : {}),
2568
2693
  costUpperBoundUsd: req.routeApproval?.totalCostUpperBoundUsd ?? costUsd,
2569
2694
  capacity,
2570
2695
  nowMs: now(),
@@ -2672,6 +2797,21 @@ export function createDispatchBundle(
2672
2797
 
2673
2798
  type RetryScheduleResult = { scheduled: true } | { scheduled: false; settlementDetail?: string };
2674
2799
 
2800
+ /** A reserved plan member owns its retry chain independently of the shared fleet root assignment. */
2801
+ function retryChainIsLive(run: ActiveRun): boolean {
2802
+ if (run.aborted) return false;
2803
+ const reservation = run.req.reservation;
2804
+ if (reservation === undefined) return assignments.get(run.lineage.rootRunId)?.status === "running";
2805
+ try {
2806
+ const record = getDispatchReservation(reservation.ownerId);
2807
+ const member = record?.members.find((entry) => entry.memberId === reservation.memberId);
2808
+ return record?.status === "active" && member !== undefined && member.status !== "released";
2809
+ } catch (error) {
2810
+ reportDispatchDiagnostic(`read retry liveness for reservation member ${reservation.memberId}`, error);
2811
+ return false;
2812
+ }
2813
+ }
2814
+
2675
2815
  function maybeScheduleRetry(
2676
2816
  run: ActiveRun,
2677
2817
  outcome: RunOutcome,
@@ -2681,7 +2821,7 @@ export function createDispatchBundle(
2681
2821
  ): RetryScheduleResult {
2682
2822
  if (draining) return { scheduled: false };
2683
2823
  const rootRunId = run.lineage.rootRunId;
2684
- if (assignments.get(rootRunId)?.status !== "running") return { scheduled: false };
2824
+ if (!retryChainIsLive(run)) return { scheduled: false };
2685
2825
  if (!RETRYABLE_OUTCOMES.has(outcome)) {
2686
2826
  retryBackoff.delete(rootRunId);
2687
2827
  return { scheduled: false };
@@ -2828,7 +2968,7 @@ export function createDispatchBundle(
2828
2968
  reason = "retry",
2829
2969
  decision: RetryDecision = decideRetry("internal", run.lineage.attempt, assignmentPolicyFor(run.req).maxRetries),
2830
2970
  ): Promise<void> {
2831
- if (draining || assignments.get(run.lineage.rootRunId)?.status !== "running") return;
2971
+ if (draining || !retryChainIsLive(run)) return;
2832
2972
  const excludesNode = decision.excludedRouteParts.includes("node");
2833
2973
  // Record the failed node so placement can choose an eligible survivor.
2834
2974
  const rerouteHops =
@@ -2846,6 +2986,10 @@ export function createDispatchBundle(
2846
2986
  depth: run.lineage.depth,
2847
2987
  },
2848
2988
  };
2989
+ // A run-id hint names only the first attempt (and, for task worktrees,
2990
+ // the worktree it created). A retry shares the assignment lineage but
2991
+ // must receive its own ledger/run identity.
2992
+ delete retryReq.runIdHint;
2849
2993
  const reasonKey = retryReasonKey(run.lineage.rootRunId, attempt);
2850
2994
  retryReasons.set(reasonKey, reason);
2851
2995
  try {
@@ -3010,8 +3154,8 @@ export function createDispatchBundle(
3010
3154
  }
3011
3155
  }
3012
3156
 
3013
- function heartbeatIso(heartbeatMs: number): string {
3014
- return new Date(heartbeatMs).toISOString();
3157
+ function heartbeatIso(heartbeat: HeartbeatStamp): string {
3158
+ return new Date(heartbeat.current).toISOString();
3015
3159
  }
3016
3160
 
3017
3161
  function heartbeatRunStatus(status: HeartbeatStatus): RunStatus {
@@ -3032,7 +3176,7 @@ export function createDispatchBundle(
3032
3176
  event: {
3033
3177
  type: "heartbeat_status",
3034
3178
  status,
3035
- heartbeatAt: run.heartbeatAt ? heartbeatIso(run.heartbeatAt.current) : null,
3179
+ heartbeatAt: run.heartbeatAt ? heartbeatIso(run.heartbeatAt) : null,
3036
3180
  },
3037
3181
  });
3038
3182
  }
@@ -3050,16 +3194,16 @@ export function createDispatchBundle(
3050
3194
  */
3051
3195
  function checkActiveHeartbeats(): void {
3052
3196
  if (!ledger) return;
3053
- const tickNow = now();
3197
+ const tickMonotonic = monotonicNow();
3054
3198
  for (const run of active.values()) {
3055
3199
  if (run.aborted || run.stallKilled || !run.heartbeatAt) continue;
3056
- const heartbeatMs = run.heartbeatAt.current;
3057
- if (!Number.isFinite(heartbeatMs)) continue;
3200
+ const heartbeatMonotonic = heartbeatMonotonicAt(run.heartbeatAt);
3201
+ if (!Number.isFinite(heartbeatMonotonic) || !Number.isFinite(run.heartbeatAt.current)) continue;
3058
3202
  if (run.runtimeKind === "acp-delegation") {
3059
- ledger.update(run.runId, { heartbeatAt: heartbeatIso(heartbeatMs) });
3203
+ ledger.update(run.runId, { heartbeatAt: heartbeatIso(run.heartbeatAt) });
3060
3204
  const stallMs = run.stallTimeoutMs;
3061
3205
  if (stallMs === null || stallMs <= 0) continue;
3062
- if (tickNow - heartbeatMs <= stallMs) continue;
3206
+ if (tickMonotonic - heartbeatMonotonic <= stallMs) continue;
3063
3207
  run.stallKilled = true;
3064
3208
  run.heartbeatStatus = "dead";
3065
3209
  emitHeartbeatStatus(run, "dead");
@@ -3070,10 +3214,10 @@ export function createDispatchBundle(
3070
3214
  }
3071
3215
  continue;
3072
3216
  }
3073
- const status = classifyHeartbeat(heartbeatMs, tickNow, heartbeatSpec);
3217
+ const status = classifyHeartbeat(heartbeatMonotonic, tickMonotonic, heartbeatSpec);
3074
3218
  const patch: Partial<RunEnvelope> = {
3075
3219
  status: heartbeatRunStatus(status),
3076
- heartbeatAt: heartbeatIso(heartbeatMs),
3220
+ heartbeatAt: heartbeatIso(run.heartbeatAt),
3077
3221
  };
3078
3222
  ledger.update(run.runId, patch);
3079
3223
  // Node staleness display feeds off the freshest worker heartbeat.
@@ -3493,7 +3637,29 @@ export function createDispatchBundle(
3493
3637
 
3494
3638
  assertBudgetAdmitsRoute(req, { rates: null, provenance: "unknown" }, settings);
3495
3639
 
3496
- const capacityLease = await admitAssignmentCapacity(req, "local", timing);
3640
+ const queuedIdentity =
3641
+ req.lineage === undefined && req.runIdHint !== undefined
3642
+ ? {
3643
+ runId: req.runIdHint,
3644
+ agentId: req.agentId,
3645
+ task: req.task,
3646
+ requestOrigin: lifecycle.requestOrigin,
3647
+ targetId,
3648
+ wireModelId,
3649
+ runtimeId,
3650
+ runtimeKind: "acp-delegation" as const,
3651
+ }
3652
+ : null;
3653
+ if (queuedIdentity !== null) publishCapacityQueued(queuedIdentity, timing, null);
3654
+ let capacityLease: Awaited<ReturnType<typeof admitAssignmentCapacity>>;
3655
+ try {
3656
+ capacityLease = await admitAssignmentCapacity(req, "local", timing, null);
3657
+ } catch (error) {
3658
+ if (queuedIdentity !== null) publishCapacityAdmissionFailure(queuedIdentity, error);
3659
+ throw error;
3660
+ } finally {
3661
+ if (queuedIdentity !== null) pendingCapacity.delete(queuedIdentity.runId);
3662
+ }
3497
3663
  const leaseSlot = createLeaseSlotGuard(capacityAdmission, capacityLease.leaseId, req.lineage !== undefined);
3498
3664
 
3499
3665
  // Resolve the ledger before starting the ACP process: an external agent
@@ -3519,6 +3685,8 @@ export function createDispatchBundle(
3519
3685
  safety,
3520
3686
  autonomy: lifecycle.autonomy,
3521
3687
  clientVersion: readClioVersion(),
3688
+ now,
3689
+ monotonicNow,
3522
3690
  });
3523
3691
  } catch (error) {
3524
3692
  leaseSlot.release();
@@ -3699,15 +3867,17 @@ export function createDispatchBundle(
3699
3867
  runIdForPermissionAudit = envelope.id;
3700
3868
  lineage = lineageFor(req, envelope.id);
3701
3869
  if (req.lineage === undefined) {
3702
- capacityAdmission.rename(capacityLease.leaseId, lineage.rootRunId);
3703
- await renameStoredAssignment(capacityLease.assignmentId, lineage.rootRunId);
3870
+ if (capacityLease.assignmentId !== lineage.rootRunId) {
3871
+ capacityAdmission.rename(capacityLease.leaseId, lineage.rootRunId);
3872
+ await renameStoredAssignment(capacityLease.assignmentId, lineage.rootRunId);
3873
+ }
3704
3874
  leaseSlot.transferToAssignment();
3705
3875
  }
3706
3876
  identity = detectRunIdentity();
3707
3877
  ledgerRef.update(envelope.id, {
3708
3878
  status: "running",
3709
3879
  pid: acp.pid,
3710
- heartbeatAt: heartbeatIso(acp.heartbeatAt.current),
3880
+ heartbeatAt: heartbeatIso(acp.heartbeatAt),
3711
3881
  lineage,
3712
3882
  identity,
3713
3883
  node: LOCAL_RUN_NODE,
@@ -3726,16 +3896,18 @@ export function createDispatchBundle(
3726
3896
  // One durable write at start so sibling processes (clio-coder fleet status)
3727
3897
  // can observe the running row; finalization persists the terminal state.
3728
3898
  await ledgerRef.persist();
3729
- context.bus.emit(BusChannels.DispatchEnqueued, {
3730
- runId: envelope.id,
3731
- agentId: req.agentId,
3732
- task: req.task,
3733
- requestOrigin: lifecycle.requestOrigin,
3734
- targetId,
3735
- wireModelId,
3736
- runtimeId,
3737
- runtimeKind: "acp-delegation",
3738
- });
3899
+ if (req.lineage !== undefined) {
3900
+ context.bus.emit(BusChannels.DispatchEnqueued, {
3901
+ runId: envelope.id,
3902
+ agentId: req.agentId,
3903
+ task: req.task,
3904
+ requestOrigin: lifecycle.requestOrigin,
3905
+ targetId,
3906
+ wireModelId,
3907
+ runtimeId,
3908
+ runtimeKind: "acp-delegation",
3909
+ });
3910
+ }
3739
3911
  context.bus.emit(BusChannels.DispatchStarted, {
3740
3912
  runId: envelope.id,
3741
3913
  agentId: req.agentId,
@@ -4104,7 +4276,7 @@ export function createDispatchBundle(
4104
4276
  cacheReadTokenCount: receiptDraft.cacheReadTokenCount ?? 0,
4105
4277
  cacheWriteTokenCount: receiptDraft.cacheWriteTokenCount ?? 0,
4106
4278
  reasoningTokenCount: receiptDraft.reasoningTokenCount ?? 0,
4107
- heartbeatAt: heartbeatIso(acp.heartbeatAt.current),
4279
+ heartbeatAt: heartbeatIso(acp.heartbeatAt),
4108
4280
  };
4109
4281
  ledgerRef.update(envelope.id, ledgerPatch);
4110
4282
  const receipt = ledgerRef.recordReceipt(envelope.id, sealRouteDecision(receiptDraft, routeDecision));
@@ -4193,6 +4365,10 @@ export function createDispatchBundle(
4193
4365
  throw new Error(`dispatch: invalid spec: ${validated.errors.join("; ")}`);
4194
4366
  }
4195
4367
  req = validation.restore(validated.spec);
4368
+ // A top-level run needs its stable identity before it can wait for
4369
+ // capacity. The board can then render the assignment as queued and the
4370
+ // same id becomes the ledger row once its slot opens.
4371
+ if (req.lineage === undefined && req.runIdHint === undefined) req = { ...req, runIdHint: newRunId() };
4196
4372
  let activeRouteObservation: RouteObservationHandle | null = null;
4197
4373
  if (req.delegationAgentId === undefined) {
4198
4374
  const recipe = agents.get(req.agentId);
@@ -4262,7 +4438,7 @@ export function createDispatchBundle(
4262
4438
  "dispatch: writeRoots cannot be enforced on an ACP delegation target; the external agent runs its own tool surface. Dispatch to a native or claude-sdk worker.",
4263
4439
  );
4264
4440
  }
4265
- rebindReservationSlot(req, "local", UNKNOWN_PRICING_ADMISSION_ESTIMATE_USD, settings);
4441
+ rebindReservationSlot(req, "local", null, UNKNOWN_PRICING_ADMISSION_ESTIMATE_USD, settings);
4266
4442
  routeObservation = observeShadowRoute(req, undefined, settings);
4267
4443
  return attachRouteObservation({
4268
4444
  ...(await dispatchAcpDelegation(req, settings, timing, routeObservation.decision, observer)),
@@ -4296,10 +4472,12 @@ export function createDispatchBundle(
4296
4472
  model: lifecycle.target.wireModelId,
4297
4473
  node: placement?.node?.id ?? "local",
4298
4474
  };
4475
+ const endpoint = endpointCapacityForTarget(lifecycle.target.target.id);
4299
4476
  assertRouteWithinApprovedEnvelope(req, effectiveRoute);
4300
4477
  rebindReservationSlot(
4301
4478
  req,
4302
4479
  effectiveRoute.node,
4480
+ endpoint,
4303
4481
  conservativeRouteAdmissionEstimateUsd(lifecycle.target.effectivePricing, admissionMaxOutputTokens(settings)),
4304
4482
  settings,
4305
4483
  );
@@ -4318,7 +4496,41 @@ export function createDispatchBundle(
4318
4496
  shadow: () => observeShadowRoute(req, placed, settings),
4319
4497
  });
4320
4498
 
4321
- const capacityLease = await admitAssignmentCapacity(req, placement?.node.id ?? "local", timing);
4499
+ const queuedIdentity =
4500
+ req.lineage === undefined && req.runIdHint !== undefined
4501
+ ? {
4502
+ runId: req.runIdHint,
4503
+ agentId: req.agentId,
4504
+ task: req.task,
4505
+ agentAudience: lifecycle.agentAudience,
4506
+ requestOrigin: lifecycle.requestOrigin,
4507
+ targetId: lifecycle.target.target.id,
4508
+ wireModelId: lifecycle.target.wireModelId,
4509
+ runtimeId: lifecycle.target.runtime.id,
4510
+ runtimeKind: lifecycle.runtimeKind,
4511
+ budget: lifecycle.budgetEnvelope,
4512
+ ...(endpoint !== null ? { endpoint: { key: endpoint.key, label: endpoint.label, limit: endpoint.limit } } : {}),
4513
+ ...(placement !== null ? { node: placement.node.id } : {}),
4514
+ ...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
4515
+ ...(req.council !== undefined ? { council: req.council } : {}),
4516
+ ...(req.reroutes !== undefined && req.reroutes.length > 0 ? { rerouteCount: req.reroutes.length } : {}),
4517
+ ...(lifecycle.target.modelCapabilities !== undefined &&
4518
+ lifecycle.target.modelCapabilities !== null &&
4519
+ lifecycle.target.modelCapabilities.contextWindow > 0
4520
+ ? { contextWindow: lifecycle.target.modelCapabilities.contextWindow }
4521
+ : {}),
4522
+ }
4523
+ : null;
4524
+ if (queuedIdentity !== null) publishCapacityQueued(queuedIdentity, timing, placement?.node ?? null);
4525
+ let capacityLease: Awaited<ReturnType<typeof admitAssignmentCapacity>>;
4526
+ try {
4527
+ capacityLease = await admitAssignmentCapacity(req, placement?.node.id ?? "local", timing, endpoint);
4528
+ } catch (error) {
4529
+ if (queuedIdentity !== null) publishCapacityAdmissionFailure(queuedIdentity, error);
4530
+ throw error;
4531
+ } finally {
4532
+ if (queuedIdentity !== null) pendingCapacity.delete(queuedIdentity.runId);
4533
+ }
4322
4534
  const leaseSlot = createLeaseSlotGuard(capacityAdmission, capacityLease.leaseId, req.lineage !== undefined);
4323
4535
 
4324
4536
  const ledgerRef = (() => {
@@ -4387,6 +4599,8 @@ export function createDispatchBundle(
4387
4599
  try {
4388
4600
  worker = (placement?.spawn ?? spawnWorker)(spec, {
4389
4601
  cwd: lifecycle.cwd,
4602
+ now,
4603
+ monotonicNow,
4390
4604
  ...(agentLedgerId !== null ? { onLedgerPost } : {}),
4391
4605
  });
4392
4606
  } catch (error) {
@@ -4443,10 +4657,21 @@ export function createDispatchBundle(
4443
4657
  const upstreamResponses: RunReceiptUpstreamResponse[] = [];
4444
4658
  const skillActivations: SkillActivation[] = [];
4445
4659
  const finishContractEntries: unknown[] = [];
4660
+ let runIdForPermissionAudit: string | null = null;
4446
4661
  // The worker's own tool calls, folded into what this run changed and what
4447
4662
  // it validated. The sealed mutation-report contract is measured against
4448
4663
  // this, so a reported path the run never touched cannot seal as fact.
4449
- const runEffects = createRunEffectsRecorder(lifecycle.cwd);
4664
+ const runEffects = createRunEffectsRecorder(lifecycle.cwd, {
4665
+ onWriteRecordDowngraded(downgrade) {
4666
+ const runId = runIdForPermissionAudit;
4667
+ if (runId === null) return;
4668
+ context.bus.emit(BusChannels.DispatchProgress, {
4669
+ runId,
4670
+ agentId: req.agentId,
4671
+ event: { type: "clio_write_record_downgraded", payload: downgrade },
4672
+ });
4673
+ },
4674
+ });
4450
4675
  let finishContractAssistantText = "";
4451
4676
  let finishContractAssistantTurnId: string | null = null;
4452
4677
  let failureMessage: string | undefined;
@@ -4456,7 +4681,6 @@ export function createDispatchBundle(
4456
4681
  const acceptsOutcomeCodeEvents =
4457
4682
  lifecycle.runtimeKind === "http" ||
4458
4683
  (lifecycle.runtimeKind === "sdk" && lifecycle.target.runtime.id === "claude-sdk");
4459
- let runIdForPermissionAudit: string | null = null;
4460
4684
  let workerPolicyPermissionCounter = 0;
4461
4685
  const outputCapture = createWorkerOutputCapture();
4462
4686
  const markObservedPhase = (field: "firstModelTokenAt" | "firstToolAt"): void => {
@@ -4575,6 +4799,7 @@ export function createDispatchBundle(
4575
4799
  ...(ruleId !== null ? { ruleId } : {}),
4576
4800
  ...(policySource !== null ? { policySource } : {}),
4577
4801
  ...(typeof event.payload.timeoutMs === "number" ? { timeoutMs: event.payload.timeoutMs } : {}),
4802
+ fallback: spec.escalation?.fallback ?? "deny",
4578
4803
  escalation: true,
4579
4804
  });
4580
4805
  }
@@ -4649,6 +4874,7 @@ export function createDispatchBundle(
4649
4874
  decidedBy,
4650
4875
  actionClass,
4651
4876
  reason,
4877
+ ...(source === "timeout" ? { fallback: spec.escalation?.fallback ?? "deny" } : {}),
4652
4878
  ...(runIdForPermissionAudit !== null ? { requestedBy: runIdForPermissionAudit } : {}),
4653
4879
  });
4654
4880
  }
@@ -4704,8 +4930,10 @@ export function createDispatchBundle(
4704
4930
  runIdForPermissionAudit = envelope.id;
4705
4931
  lineage = lineageFor(req, envelope.id);
4706
4932
  if (req.lineage === undefined) {
4707
- capacityAdmission.rename(capacityLease.leaseId, lineage.rootRunId);
4708
- await renameStoredAssignment(capacityLease.assignmentId, lineage.rootRunId);
4933
+ if (capacityLease.assignmentId !== lineage.rootRunId) {
4934
+ capacityAdmission.rename(capacityLease.leaseId, lineage.rootRunId);
4935
+ await renameStoredAssignment(capacityLease.assignmentId, lineage.rootRunId);
4936
+ }
4709
4937
  leaseSlot.transferToAssignment();
4710
4938
  }
4711
4939
  if (agentLedgerId !== null) {
@@ -4739,7 +4967,7 @@ export function createDispatchBundle(
4739
4967
  ...(req.council !== undefined ? { council: req.council } : {}),
4740
4968
  ...(req.plan !== undefined ? { plan: req.plan } : {}),
4741
4969
  ...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
4742
- ...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt.current) } : {}),
4970
+ ...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt) } : {}),
4743
4971
  });
4744
4972
  observer?.onAdmitted({
4745
4973
  runId: envelope.id,
@@ -4755,6 +4983,7 @@ export function createDispatchBundle(
4755
4983
  // per-worker meter. All optional so consumers degrade to local/plain.
4756
4984
  const fleetIdentity = {
4757
4985
  budget: lifecycle.budgetEnvelope,
4986
+ ...(endpoint !== null ? { endpoint: { key: endpoint.key, label: endpoint.label, limit: endpoint.limit } } : {}),
4758
4987
  ...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
4759
4988
  ...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
4760
4989
  ...(req.council !== undefined ? { council: req.council } : {}),
@@ -4765,18 +4994,20 @@ export function createDispatchBundle(
4765
4994
  ? { contextWindow: lifecycle.target.modelCapabilities.contextWindow }
4766
4995
  : {}),
4767
4996
  };
4768
- context.bus.emit(BusChannels.DispatchEnqueued, {
4769
- runId: envelope.id,
4770
- agentId: req.agentId,
4771
- task: req.task,
4772
- agentAudience: lifecycle.agentAudience,
4773
- requestOrigin: lifecycle.requestOrigin,
4774
- targetId: lifecycle.target.target.id,
4775
- wireModelId: lifecycle.target.wireModelId,
4776
- runtimeId: lifecycle.target.runtime.id,
4777
- runtimeKind: lifecycle.runtimeKind,
4778
- ...fleetIdentity,
4779
- });
4997
+ if (req.lineage !== undefined) {
4998
+ context.bus.emit(BusChannels.DispatchEnqueued, {
4999
+ runId: envelope.id,
5000
+ agentId: req.agentId,
5001
+ task: req.task,
5002
+ agentAudience: lifecycle.agentAudience,
5003
+ requestOrigin: lifecycle.requestOrigin,
5004
+ targetId: lifecycle.target.target.id,
5005
+ wireModelId: lifecycle.target.wireModelId,
5006
+ runtimeId: lifecycle.target.runtime.id,
5007
+ runtimeKind: lifecycle.runtimeKind,
5008
+ ...fleetIdentity,
5009
+ });
5010
+ }
4780
5011
  context.bus.emit(BusChannels.DispatchStarted, {
4781
5012
  runId: envelope.id,
4782
5013
  agentId: req.agentId,
@@ -5205,18 +5436,7 @@ export function createDispatchBundle(
5205
5436
  cwd: lifecycle.cwd,
5206
5437
  networkAllowed: lifecycle.admission.allowedTools.includes(ToolNames.WebFetch),
5207
5438
  observedRunEffects,
5208
- filesystem: {
5209
- readFile(filePath) {
5210
- try {
5211
- return readFileSync(filePath, "utf8");
5212
- } catch {
5213
- return null;
5214
- }
5215
- },
5216
- pathExists(filePath) {
5217
- return existsSync(filePath);
5218
- },
5219
- },
5439
+ filesystem: nodeResultContractFilesystem(),
5220
5440
  });
5221
5441
  const resultValidation = resultContract?.applicable === true ? resultContract.validation : null;
5222
5442
  if (finalOutcome === "succeeded" && resultValidation?.conformance === "fail") {
@@ -5331,7 +5551,7 @@ export function createDispatchBundle(
5331
5551
  ...(receiptDraft.cacheWriteTokenCount !== undefined
5332
5552
  ? { cacheWriteTokenCount: receiptDraft.cacheWriteTokenCount }
5333
5553
  : {}),
5334
- ...(activeRun.heartbeatAt ? { heartbeatAt: heartbeatIso(activeRun.heartbeatAt.current) } : {}),
5554
+ ...(activeRun.heartbeatAt ? { heartbeatAt: heartbeatIso(activeRun.heartbeatAt) } : {}),
5335
5555
  };
5336
5556
  if (receiptDraft.reasoningTokenCount !== undefined) {
5337
5557
  ledgerPatch.reasoningTokenCount = receiptDraft.reasoningTokenCount;
@@ -5354,9 +5574,28 @@ export function createDispatchBundle(
5354
5574
  // the set. That run's window keeps today's blame-the-whole-diff
5355
5575
  // behaviour; a run that never touched such a tool keeps the
5356
5576
  // protection.
5357
- const writeRecordUsable =
5358
- receipt.safety?.toolTelemetry?.coverage === "complete" && observedRunEffects.writeRecordComplete;
5359
- recordRunWriteTargets(envelope.id, writeRecordUsable ? [...observedRunEffects.mutatedPaths] : null);
5577
+ const toolTelemetryComplete = receipt.safety?.toolTelemetry?.coverage === "complete";
5578
+ const downgrades: WriteBoundaryAttributionDowngrade[] = observedRunEffects.writeRecordDowngrades.map((entry) => ({
5579
+ reason: entry.reason,
5580
+ tool: entry.tool,
5581
+ toolCallId: entry.toolCallId,
5582
+ runId: envelope.id,
5583
+ stepId: null,
5584
+ }));
5585
+ if (!toolTelemetryComplete) {
5586
+ downgrades.push({
5587
+ reason: "incomplete_tool_telemetry",
5588
+ tool: null,
5589
+ toolCallId: null,
5590
+ runId: envelope.id,
5591
+ stepId: null,
5592
+ });
5593
+ }
5594
+ recordRunWriteAttribution(envelope.id, {
5595
+ recorded: [...observedRunEffects.mutatedPaths],
5596
+ complete: toolTelemetryComplete && observedRunEffects.writeRecordComplete,
5597
+ downgrades,
5598
+ });
5360
5599
  await ledgerRef.persist();
5361
5600
  if (worktreeReceipt?.applied === true && req.taskWorktree !== undefined) {
5362
5601
  try {
@@ -5628,12 +5867,14 @@ export function createDispatchBundle(
5628
5867
  allowedTools: effectiveTools,
5629
5868
  settings,
5630
5869
  });
5870
+ const endpoint = endpointCapacityForTarget(target.target.id);
5631
5871
  return {
5632
5872
  agentId: req.agentId,
5633
5873
  specFingerprint: agentSpecFingerprint(agentSpec),
5634
5874
  targetId: target.target.id,
5635
5875
  wireModelId: target.wireModelId,
5636
5876
  runtimeId: target.runtime.id,
5877
+ ...(endpoint !== null ? { endpoint: { key: endpoint.key, label: endpoint.label, limit: endpoint.limit } } : {}),
5637
5878
  node: previewNode(req).node,
5638
5879
  thinkingLevel: target.thinkingLevel,
5639
5880
  toolSignature: toolSignature(effectiveTools),
@@ -6043,8 +6284,15 @@ export function createDispatchBundle(
6043
6284
  return receipt;
6044
6285
  }
6045
6286
 
6287
+ let journalBridge: RunEventJournalBridge | null = null;
6288
+
6046
6289
  const extension: DomainExtension = {
6047
6290
  async start() {
6291
+ // The journal subscribes before anything can dispatch. It rides the
6292
+ // domain's own progress and terminal channels, so it covers a run
6293
+ // whose caller iterates the handle itself (every operator path) as
6294
+ // well as one drained by the dispatch tool's event registry.
6295
+ if (options?.journalRunEvents === true) journalBridge = attachRunEventJournalBridge(context.bus);
6048
6296
  // No in-memory executor survives a process restart, so every active
6049
6297
  // side-store lease from an earlier bundle is orphaned and must expire.
6050
6298
  cleanupDispatchReservations({ startup: true, nowMs: now() });
@@ -6110,6 +6358,10 @@ export function createDispatchBundle(
6110
6358
  for (const ownerId of ownedReservations) rollbackDispatchReservation(ownerId, now());
6111
6359
  ownedReservations.clear();
6112
6360
  await Promise.allSettled([...assignmentWrites]);
6361
+ // After drain(), so the last run's terminal line is written before the
6362
+ // bridge stops listening.
6363
+ journalBridge?.stop();
6364
+ journalBridge = null;
6113
6365
  },
6114
6366
  };
6115
6367
 
@@ -6133,19 +6385,41 @@ export function createDispatchBundle(
6133
6385
  */
6134
6386
  function snapshot(): DispatchSnapshot {
6135
6387
  const tickNow = now();
6388
+ const tickMonotonic = monotonicNow();
6136
6389
  const running: DispatchSnapshot["running"] = [];
6137
6390
  const totals = { ...finalizedTotals };
6138
6391
  const costAmounts = [...finalizedCosts];
6392
+ for (const pending of pendingCapacity.values()) {
6393
+ const startedAt = pending.timing.queuedAt ?? pending.timing.requestedAt ?? new Date(tickNow).toISOString();
6394
+ running.push({
6395
+ runId: pending.identity.runId,
6396
+ agentId: pending.identity.agentId,
6397
+ ...(pending.identity.task !== undefined ? { task: pending.identity.task } : {}),
6398
+ ...(pending.identity.budget !== undefined ? { budget: pending.identity.budget } : {}),
6399
+ runtimeKind: pending.identity.runtimeKind,
6400
+ outcomePhase: "queued",
6401
+ heartbeat: "n/a",
6402
+ lineage: { parentRunId: null, rootRunId: pending.identity.runId, attempt: 0, depth: 0 },
6403
+ startedAt,
6404
+ elapsedMs: 0,
6405
+ timing: deriveRunPhaseDurations(pending.timing, startedAt, new Date(tickNow).toISOString()),
6406
+ tokens: { input: 0, output: 0, total: 0 },
6407
+ costUsd: 0,
6408
+ costProvenance: "unknown",
6409
+ node: pending.node === null ? null : { ...pending.node },
6410
+ });
6411
+ }
6139
6412
  for (const run of active.values()) {
6140
6413
  let heartbeat: "alive" | "stale" | "dead" | "n/a" = "n/a";
6141
- if (run.heartbeatAt && Number.isFinite(run.heartbeatAt.current)) {
6414
+ if (run.heartbeatAt && Number.isFinite(heartbeatMonotonicAt(run.heartbeatAt))) {
6415
+ const heartbeatMonotonic = heartbeatMonotonicAt(run.heartbeatAt);
6142
6416
  if (run.runtimeKind === "acp-delegation") {
6143
6417
  const stallMs = run.stallTimeoutMs;
6144
6418
  if (stallMs !== null && stallMs > 0) {
6145
- heartbeat = tickNow - run.heartbeatAt.current > stallMs ? "dead" : "alive";
6419
+ heartbeat = tickMonotonic - heartbeatMonotonic > stallMs ? "dead" : "alive";
6146
6420
  }
6147
6421
  } else {
6148
- heartbeat = classifyHeartbeat(run.heartbeatAt.current, tickNow, heartbeatSpec);
6422
+ heartbeat = classifyHeartbeat(heartbeatMonotonic, tickMonotonic, heartbeatSpec);
6149
6423
  }
6150
6424
  }
6151
6425
  const meter = run.meter;
@@ -6271,6 +6545,7 @@ export function createDispatchBundle(
6271
6545
  routeCandidates,
6272
6546
  reservations: {
6273
6547
  prepare: prepareReservation,
6548
+ release: (ownerId) => releaseDispatchReservation(ownerId, now()),
6274
6549
  rollback: (ownerId) => rollbackDispatchReservation(ownerId, now()),
6275
6550
  rollbackUnconsumed: (ownerId) => rollbackUnconsumedDispatchReservation(ownerId, now()),
6276
6551
  get: getDispatchReservation,
@@ -6291,7 +6566,17 @@ export function createDispatchBundle(
6291
6566
  observedRunWrites(runId) {
6292
6567
  // A run this process never finalized, or finalized long enough ago to
6293
6568
  // have been evicted, is unknown rather than silent, so both answer null.
6294
- return runWriteTargets.get(runId) ?? null;
6569
+ const attribution = runWriteAttributions.get(runId);
6570
+ return attribution?.complete === true ? [...attribution.recorded] : null;
6571
+ },
6572
+ observedRunWriteAttribution(runId) {
6573
+ const attribution = runWriteAttributions.get(runId);
6574
+ if (attribution === undefined) return null;
6575
+ return {
6576
+ recorded: [...attribution.recorded],
6577
+ complete: attribution.complete,
6578
+ downgrades: attribution.downgrades?.map((entry) => ({ ...entry })) ?? [],
6579
+ };
6295
6580
  },
6296
6581
  assignments: {
6297
6582
  get: (id) => assignments.get(id),
@@ -6302,6 +6587,17 @@ export function createDispatchBundle(
6302
6587
  },
6303
6588
  abort(runId, reason) {
6304
6589
  const rootRunId = assignmentRootFor(runId);
6590
+ const queued = pendingCapacity.get(rootRunId);
6591
+ if (queued !== undefined && capacityAdmission.cancel(rootRunId)) {
6592
+ persistAssignment(cancelStoredAssignment(rootRunId), `${rootRunId}:cancel-queued`);
6593
+ context.bus.emit(BusChannels.RunAborted, {
6594
+ source: "dispatch_abort",
6595
+ runId: rootRunId,
6596
+ startedAt: null,
6597
+ elapsedMs: null,
6598
+ reason: reason?.detail ?? "operator abort while waiting for an endpoint slot",
6599
+ });
6600
+ }
6305
6601
  const assignment = assignments.get(rootRunId);
6306
6602
  if (assignment?.status === "running") {
6307
6603
  assignments.cancel(assignment.id);