@iowarp/clio-coder 0.4.2 → 0.4.4

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 (523) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/CONTRIBUTING.md +86 -19
  3. package/README.md +35 -6
  4. package/dist/{acp-TMDQZDIG.js → acp-WNAYYF4F.js} +12 -13
  5. package/dist/{agents-5N5NG3XG.js → agents-3OKXHLOI.js} +60 -57
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-Z5CCBXKQ.js → auth-VKNNMGPU.js} +21 -19
  8. package/dist/{builtins-K6TNDT24.js → builtins-WGALA46I.js} +9 -4
  9. package/dist/{chunk-XE3PCIXH.js → chunk-23L32XTI.js} +12 -9
  10. package/dist/{chunk-I64IFBLB.js → chunk-25QBEXRS.js} +18 -11
  11. package/dist/{chunk-CDNVLKUX.js → chunk-26QSH3EJ.js} +13 -7
  12. package/dist/{chunk-QQLGQY2A.js → chunk-2ASED4PZ.js} +22 -22
  13. package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
  14. package/dist/chunk-2DSOYNFC.js +108 -0
  15. package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
  16. package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
  17. package/dist/{chunk-O3YUNJZ2.js → chunk-2ZSONWVL.js} +82 -25
  18. package/dist/{chunk-2NHR3NAY.js → chunk-36CT5VVL.js} +331 -42
  19. package/dist/{chunk-2X4RYJTJ.js → chunk-3GY4F45V.js} +3 -3
  20. package/dist/{chunk-ZW55JB7N.js → chunk-3ODX73FK.js} +4 -6
  21. package/dist/{chunk-PBP4B7XR.js → chunk-3UNOLWNZ.js} +3 -3
  22. package/dist/{chunk-4JDLP6ZS.js → chunk-3UUXNFEX.js} +14 -10
  23. package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
  24. package/dist/{chunk-ZW4HH5JJ.js → chunk-4M6Z5QVF.js} +6 -6
  25. package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
  26. package/dist/{chunk-M2DAX4F6.js → chunk-4WR7VSYB.js} +2 -2
  27. package/dist/{chunk-FSP7CMNU.js → chunk-54X7T7DK.js} +61 -6
  28. package/dist/{chunk-54ODD65L.js → chunk-5636DCO5.js} +4 -4
  29. package/dist/chunk-57XXR6DR.js +3763 -0
  30. package/dist/{chunk-3KIPBMUA.js → chunk-5ICU3EUH.js} +2 -2
  31. package/dist/{chunk-77QIVUZB.js → chunk-5MEZN6CB.js} +4 -4
  32. package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
  33. package/dist/{chunk-YJISEZKC.js → chunk-5TUB6SLS.js} +6 -6
  34. package/dist/{chunk-IMXMHHMQ.js → chunk-6OSVSQL5.js} +341 -57
  35. package/dist/{chunk-Q4XWMHX6.js → chunk-6PAZTBPA.js} +14 -2
  36. package/dist/{chunk-FVDGR2ZL.js → chunk-6Q3CYFD3.js} +112 -39
  37. package/dist/{chunk-IDNA72AH.js → chunk-6QOTUPRG.js} +155 -36
  38. package/dist/{chunk-X7IARSHT.js → chunk-6UINWWS6.js} +16 -10
  39. package/dist/{chunk-CYZW7JHJ.js → chunk-72YIHOZQ.js} +9 -9
  40. package/dist/{chunk-IKSLQ4XV.js → chunk-75W7L2E2.js} +752 -861
  41. package/dist/{chunk-CRFOIAX3.js → chunk-7UGL4MB5.js} +6 -6
  42. package/dist/{chunk-HIICAHCJ.js → chunk-AUPNRN7C.js} +2 -2
  43. package/dist/{chunk-7BHIY2MW.js → chunk-BJVFZO5U.js} +8 -50
  44. package/dist/{chunk-B74PXLU7.js → chunk-CUSRQKPU.js} +65 -3
  45. package/dist/chunk-DQOVN6KV.js +386 -0
  46. package/dist/{chunk-E7GT7O5N.js → chunk-DT3LWJOB.js} +7 -4
  47. package/dist/chunk-DXKJURES.js +671 -0
  48. package/dist/{chunk-JBCS7CRR.js → chunk-EL24TAU4.js} +10 -10
  49. package/dist/{chunk-TPEQIQIE.js → chunk-ELWDPP3Y.js} +8 -8
  50. package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
  51. package/dist/chunk-EXLD33WO.js +381 -0
  52. package/dist/chunk-FEAXX7B6.js +101 -0
  53. package/dist/{chunk-RLYRBIYQ.js → chunk-FFUPXJC4.js} +90 -331
  54. package/dist/{chunk-5PFYMY2V.js → chunk-FTMGRKEF.js} +2 -2
  55. package/dist/{chunk-34BHNEE3.js → chunk-GHS5EBTQ.js} +58 -7
  56. package/dist/{chunk-DYHAXKHD.js → chunk-GWZNEVM2.js} +12 -8
  57. package/dist/chunk-GX5WYQO4.js +59 -0
  58. package/dist/chunk-GYV6VZOC.js +26 -0
  59. package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
  60. package/dist/chunk-I2DWJ4GM.js +390 -0
  61. package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
  62. package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
  63. package/dist/{chunk-XQRY4DTA.js → chunk-IGLP3ODT.js} +10 -10
  64. package/dist/chunk-IRXAATOX.js +539 -0
  65. package/dist/chunk-IXIY2H4R.js +44 -0
  66. package/dist/{chunk-SSEYRH53.js → chunk-IZXGRF7P.js} +92 -147
  67. package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
  68. package/dist/{chunk-JWJGP5DQ.js → chunk-JEIYHLOR.js} +7 -7
  69. package/dist/{chunk-F2I26BDK.js → chunk-JQLNNIKT.js} +4 -4
  70. package/dist/{chunk-BYMNWQ7O.js → chunk-JSD46VO2.js} +315 -63
  71. package/dist/{chunk-AK5XEFVZ.js → chunk-JT2RFCC5.js} +64 -14
  72. package/dist/{chunk-MCMZMDAC.js → chunk-K6T2ZAMZ.js} +168 -6
  73. package/dist/{chunk-PGF63K6I.js → chunk-KFV5L5SK.js} +73 -4
  74. package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
  75. package/dist/{chunk-PJX3WQUQ.js → chunk-LLXSDWXS.js} +3 -3
  76. package/dist/{chunk-DZAW46HP.js → chunk-LTIKRKFL.js} +3 -3
  77. package/dist/{chunk-DZEK6CJN.js → chunk-N56KALIC.js} +21 -21
  78. package/dist/{chunk-B7HM5Z7T.js → chunk-NAI6ZFCY.js} +9 -5
  79. package/dist/{chunk-I66ZTYNP.js → chunk-NRO2BJRH.js} +2656 -2213
  80. package/dist/{chunk-ZGNYYXQ6.js → chunk-NXIMQY5W.js} +3 -3
  81. package/dist/{chunk-IKOZFYBN.js → chunk-NXYCB2VD.js} +149 -106
  82. package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
  83. package/dist/{chunk-462T4EGZ.js → chunk-O5CVSAG5.js} +2 -2
  84. package/dist/chunk-ODGTEFFI.js +50 -0
  85. package/dist/{chunk-3F7VUY77.js → chunk-OEJSLEPW.js} +2 -2
  86. package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
  87. package/dist/{chunk-5KW52TEP.js → chunk-Q4WO54TA.js} +132 -77
  88. package/dist/{chunk-W6NIE6OW.js → chunk-QUFRYSWI.js} +13 -7
  89. package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
  90. package/dist/chunk-R6Q67RJH.js +134 -0
  91. package/dist/{chunk-W4YEMFBX.js → chunk-RAY4OVGZ.js} +3 -3
  92. package/dist/{chunk-ZNT2M6TG.js → chunk-RQCKCSRL.js} +17 -17
  93. package/dist/{chunk-LJID3DYZ.js → chunk-RXTN6AKH.js} +3 -3
  94. package/dist/{chunk-P75RZCJW.js → chunk-RZDWV63N.js} +3 -3
  95. package/dist/{chunk-UH632ZYL.js → chunk-S6PYF2XF.js} +2 -2
  96. package/dist/{chunk-HJWWJ6IL.js → chunk-TOIVGRUX.js} +17 -5
  97. package/dist/{chunk-HLAFFSEK.js → chunk-TQAHXW6Y.js} +2 -2
  98. package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
  99. package/dist/{chunk-2HFQNRV3.js → chunk-UEPWCCTY.js} +12 -12
  100. package/dist/chunk-UOIZ7DA4.js +41 -0
  101. package/dist/{chunk-UH347SHR.js → chunk-USR47QNF.js} +11 -11
  102. package/dist/{chunk-AZ4WMN4W.js → chunk-V6HJFQZE.js} +2 -2
  103. package/dist/chunk-V76WTFTW.js +318 -0
  104. package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
  105. package/dist/{chunk-KPXDY6QF.js → chunk-XRZT5WY5.js} +2 -2
  106. package/dist/{chunk-UBRFI4HS.js → chunk-XULDXHTN.js} +142 -50
  107. package/dist/chunk-XXYSBZIQ.js +283 -0
  108. package/dist/{chunk-HKMD33FO.js → chunk-Y55JBDO5.js} +405 -122
  109. package/dist/{chunk-XOXV5GKE.js → chunk-YD5GIKET.js} +17 -8
  110. package/dist/{chunk-XGDPUNND.js → chunk-YECAMM3D.js} +2 -2
  111. package/dist/{chunk-BO7Y52RY.js → chunk-YNFKXPEC.js} +7 -7
  112. package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
  113. package/dist/{chunk-M2WXEHER.js → chunk-ZA4VCIGV.js} +2 -2
  114. package/dist/cli/index.js +42 -40
  115. package/dist/{clio-7VB377CC.js → clio-QLICPCF5.js} +7 -7
  116. package/dist/{code-nav-YVLCYA7V.js → code-nav-IJR2DBPR.js} +9 -9
  117. package/dist/{components-UBWCQSRW.js → components-2TGAI2RC.js} +5 -6
  118. package/dist/{config-4HVOS65E.js → config-IUA6OYNS.js} +88 -81
  119. package/dist/{configure-PIWO7B24.js → configure-VEPX4NMX.js} +26 -25
  120. package/dist/{context-KQYIWPWT.js → context-2DKHWH2T.js} +60 -45
  121. package/dist/{context-IYEHL3WQ.js → context-4MPR7WKB.js} +78 -69
  122. package/dist/{context-N6ZE3LGJ.js → context-BOYF5EJM.js} +15 -11
  123. package/dist/{context-clear-G4OGZJDS.js → context-clear-S4ZJCQUX.js} +73 -65
  124. package/dist/context-map-COB37XXN.js +505 -0
  125. package/dist/{context-working-set-BWLF6LJP.js → context-working-set-3I3FYX6Y.js} +18 -17
  126. package/dist/detail-A7JAVSIG.js +98 -0
  127. package/dist/{dispatch-runner-2QQAITS3.js → dispatch-runner-RJ5I2F2O.js} +99 -75
  128. package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
  129. package/dist/{doctor-LHBD36VU.js → doctor-DKICC2SN.js} +71 -48
  130. package/dist/{eval-C45FYRJ6.js → eval-OQOQUDHK.js} +308 -146
  131. package/dist/{eval-inventory-6DEJPLBF.js → eval-inventory-Y6QRFOH5.js} +4 -4
  132. package/dist/{evidence-6SHONYAF.js → evidence-4DQ25GUQ.js} +79 -175
  133. package/dist/evidence-4F5USFKH.js +208 -0
  134. package/dist/{evolve-KRKMV72X.js → evolve-GSS52E5J.js} +71 -67
  135. package/dist/{extensions-KPZ2UHBB.js → extensions-G7MFLYHT.js} +8 -9
  136. package/dist/{fleet-IVTCKDHT.js → fleet-Q37YHHAQ.js} +126 -118
  137. package/dist/{fleet-commands-EDWL3IT7.js → fleet-commands-G7E4N7SM.js} +16 -13
  138. package/dist/{fleet-decisions-YP3YEFGK.js → fleet-decisions-O7M6QBA2.js} +9 -8
  139. package/dist/{fleet-graph-ZFWKHY2M.js → fleet-graph-TOUBW6OW.js} +21 -20
  140. package/dist/{fleet-inspect-FVUNCBML.js → fleet-inspect-SW33JJNI.js} +65 -60
  141. package/dist/{fleet-preflight-UN5XED4R.js → fleet-preflight-CV2655TW.js} +4 -5
  142. package/dist/{fleet-validate-XOWC4HSX.js → fleet-validate-KESZX2YH.js} +25 -24
  143. package/dist/{fleet-verify-UN3SODEL.js → fleet-verify-UQPMTVE3.js} +66 -61
  144. package/dist/{fleet-view-TWHJKCN6.js → fleet-view-MN2VG4MR.js} +65 -60
  145. package/dist/{init-T2QORQ3Y.js → init-PXEXQSBF.js} +90 -82
  146. package/dist/{interop-IN5I2A66.js → interop-ZG5T62U3.js} +12 -13
  147. package/dist/inventory-C26CFDRR.js +101 -0
  148. package/dist/{library-LSCATDLZ.js → library-B2W4N74O.js} +29 -29
  149. package/dist/{memory-HYOKAGGJ.js → memory-YCANYS5A.js} +73 -69
  150. package/dist/{models-2GPMFYCM.js → models-GERTU3YI.js} +51 -47
  151. package/dist/{monitor-E4ASVUJH.js → monitor-CPNIUULB.js} +74 -67
  152. package/dist/{orchestrator-DDMPR3PY.js → orchestrator-J4BSH4WQ.js} +1288 -1606
  153. package/dist/{panes-E3RUXOW5.js → panes-BOHAEGYC.js} +4 -4
  154. package/dist/{panes-IXKLOKA2.js → panes-NXSLDQZ2.js} +10 -11
  155. package/dist/{paths-L7LGY6RN.js → paths-VSUWNC22.js} +6 -7
  156. package/dist/reset-TNWTB5LU.js +343 -0
  157. package/dist/{resources-OTRSN34L.js → resources-4PXNMD5G.js} +29 -22
  158. package/dist/{run-5DEYH5QK.js → run-D6XJ34CN.js} +132 -132
  159. package/dist/{share-IHWTLO3M.js → share-2NWMJJEE.js} +27 -27
  160. package/dist/{skills-IYMXMKW4.js → skills-KR7WON5G.js} +40 -33
  161. package/dist/{skills-eval-DROHSJAR.js → skills-eval-O2ZNOLDS.js} +81 -77
  162. package/dist/{skills-inventory-D7X4L4ZX.js → skills-inventory-ZZOUBK7O.js} +23 -22
  163. package/dist/{slash-commands-QBM7UZ3B.js → slash-commands-ZXPJD64J.js} +47 -37
  164. package/dist/{steer-Z5DO23FJ.js → steer-XA25PSCS.js} +4 -4
  165. package/dist/{support-U7QOWY26.js → support-7EMVWYG2.js} +6 -6
  166. package/dist/{targets-P2FUC4IL.js → targets-OMH2XCSN.js} +50 -49
  167. package/dist/tasks-IPAGMEIX.js +36 -0
  168. package/dist/{terminal-lease-YREJ3JX2.js → terminal-lease-C2J3JYRE.js} +4 -4
  169. package/dist/{tools-5B7RO6MV.js → tools-EFFEAIDP.js} +8 -9
  170. package/dist/{trace-YMGMUM6A.js → trace-FXMXUZUF.js} +7 -7
  171. package/dist/uninstall-HALS6BLF.js +407 -0
  172. package/dist/upgrade-MS72RJEP.js +306 -0
  173. package/dist/{usage-ME5MPXGX.js → usage-NHG6MCJM.js} +162 -108
  174. package/dist/{verifiers-BVZ7IWOO.js → verifiers-7AUNVXDY.js} +155 -22
  175. package/dist/{verify-5K7ZKQFC.js → verify-FWYGPKMR.js} +14 -12
  176. package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
  177. package/dist/{wiki-generate-F5W5QTYY.js → wiki-generate-743CIGJW.js} +99 -90
  178. package/dist/{with-panes-BYOJCLAM.js → with-panes-BDQEWBRT.js} +10 -10
  179. package/dist/worker/entry.js +72 -68
  180. package/docs/README.md +3 -2
  181. package/docs/architecture/acp.md +17 -0
  182. package/docs/architecture/artifact-placement.md +1 -0
  183. package/docs/architecture/artifact-versions.md +2 -2
  184. package/docs/architecture/context-engine.md +4 -0
  185. package/docs/architecture/dispatch-typed-intent.md +1 -1
  186. package/docs/architecture/evidence-and-memory.md +1 -1
  187. package/docs/architecture/middleware-and-components.md +1 -1
  188. package/docs/architecture/model-catalog.md +21 -10
  189. package/docs/architecture/observability.md +19 -2
  190. package/docs/architecture/prompt-envelope-and-tools.md +17 -5
  191. package/docs/architecture/provider-adapter-cookbook.md +63 -0
  192. package/docs/architecture/safety-model.md +25 -22
  193. package/docs/architecture/tui-design.md +1 -1
  194. package/docs/guide/built-in-agents.md +25 -11
  195. package/docs/guide/commands-and-modes.md +18 -3
  196. package/docs/guide/configuration-and-targets.md +100 -10
  197. package/docs/guide/configuration-reference.md +17 -7
  198. package/docs/guide/environment-variables.md +4 -2
  199. package/docs/guide/installation-and-lifecycle.md +37 -4
  200. package/docs/guide/proactive-memory.md +66 -55
  201. package/docs/guide/skills-marketplace.md +18 -0
  202. package/docs/guide/tool-usage.md +78 -3
  203. package/docs/history/config-knobs-audit.md +2 -2
  204. package/docs/process/development-pipeline.md +40 -2
  205. package/docs/process/eval-runner.md +67 -3
  206. package/docs/process/git-commit-provenance.md +15 -0
  207. package/docs/process/release-cut-checklist.md +207 -0
  208. package/docs/process/scientific-validation.md +18 -17
  209. package/evals/behavioral-machinery-support.ts +1 -0
  210. package/evals/behavioral-machinery.yaml +1 -1
  211. package/evals/behavioral-model.yaml +3 -2
  212. package/package.json +2 -2
  213. package/skills/README.md +7 -5
  214. package/skills/coding/ast-grep/SKILL.md +101 -30
  215. package/skills/coding/ast-grep/evals.md +26 -0
  216. package/skills/coding/coding-standards/SKILL.md +47 -2
  217. package/skills/coding/coding-standards/evals.md +23 -0
  218. package/skills/coding/prototype/SKILL.md +87 -28
  219. package/skills/coding/prototype/evals.md +19 -0
  220. package/skills/coding/tdd/SKILL.md +80 -53
  221. package/skills/coding/tdd/evals.md +20 -0
  222. package/skills/context/context-handoff/SKILL.md +43 -2
  223. package/skills/context/context-handoff/evals.md +44 -0
  224. package/skills/context/context-prime/SKILL.md +45 -15
  225. package/skills/context/context-prime/evals.md +45 -0
  226. package/skills/git/branch-closeout/SKILL.md +132 -0
  227. package/skills/git/branch-closeout/evals.md +133 -0
  228. package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
  229. package/skills/git/file-ticket/SKILL.md +77 -63
  230. package/skills/git/file-ticket/assets/issue-template.md +22 -0
  231. package/skills/git/file-ticket/evals.md +31 -26
  232. package/skills/git/file-ticket/references/issue-discovery.md +49 -0
  233. package/skills/git/fix-issue/SKILL.md +87 -64
  234. package/skills/git/fix-issue/evals.md +35 -31
  235. package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
  236. package/skills/git/resolve-merge-conflicts/SKILL.md +100 -51
  237. package/skills/git/resolve-merge-conflicts/evals.md +52 -25
  238. package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
  239. package/skills/git/ship/SKILL.md +103 -67
  240. package/skills/git/ship/assets/pr-template.md +21 -0
  241. package/skills/git/ship/evals.md +44 -28
  242. package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
  243. package/skills/git/worktree-create/SKILL.md +80 -50
  244. package/skills/git/worktree-create/evals.md +40 -33
  245. package/skills/git/worktree-create/references/worktree-setup.md +62 -66
  246. package/skills/git/worktree-merge/SKILL.md +112 -65
  247. package/skills/git/worktree-merge/evals.md +42 -34
  248. package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
  249. package/skills/planning/archify/SKILL.md +196 -0
  250. package/skills/planning/archify/evals.md +65 -0
  251. package/skills/planning/architecture/SKILL.md +61 -12
  252. package/skills/planning/architecture/evals.md +65 -0
  253. package/skills/planning/backlog/SKILL.md +130 -14
  254. package/skills/planning/backlog/evals.md +142 -0
  255. package/skills/planning/prd/SKILL.md +47 -6
  256. package/skills/planning/prd/evals.md +54 -0
  257. package/skills/planning/product-intent/SKILL.md +57 -2
  258. package/skills/planning/product-intent/evals.md +70 -0
  259. package/skills/planning/tech-spec/SKILL.md +53 -2
  260. package/skills/planning/tech-spec/evals.md +73 -0
  261. package/skills/registry.yaml +58 -50
  262. package/skills/remote.yaml +13 -0
  263. package/skills/research/arxiv-literature/SKILL.md +76 -18
  264. package/skills/research/arxiv-literature/evals.md +50 -0
  265. package/skills/research/experiment-protocol/SKILL.md +20 -1
  266. package/skills/research/experiment-protocol/evals.md +23 -0
  267. package/skills/research/scientific-debugging/SKILL.md +24 -1
  268. package/skills/research/scientific-debugging/evals.md +18 -0
  269. package/skills/research/scientific-modernization/SKILL.md +26 -1
  270. package/skills/research/scientific-modernization/evals.md +27 -0
  271. package/skills/skill-marketplace.json +63 -28
  272. package/skills/workflow/cut-it/SKILL.md +64 -5
  273. package/skills/workflow/cut-it/evals.md +101 -0
  274. package/skills/workflow/design-council/SKILL.md +112 -27
  275. package/skills/workflow/design-council/evals.md +161 -0
  276. package/skills/workflow/grill-me/SKILL.md +85 -10
  277. package/skills/workflow/grill-me/evals.md +153 -0
  278. package/skills/workflow/workflow-distiller/SKILL.md +76 -17
  279. package/skills/workflow/workflow-distiller/evals.md +118 -0
  280. package/src/cli/args.ts +0 -8
  281. package/src/cli/configure-interop.ts +105 -13
  282. package/src/cli/configure-oauth.ts +57 -0
  283. package/src/cli/configure-onboarding.ts +980 -0
  284. package/src/cli/configure-target.ts +594 -0
  285. package/src/cli/configure.ts +1084 -529
  286. package/src/cli/context-map.ts +114 -0
  287. package/src/cli/context.ts +4 -0
  288. package/src/cli/doctor-state-size.ts +1 -12
  289. package/src/cli/doctor-validation-contract.ts +28 -0
  290. package/src/cli/doctor.ts +5 -0
  291. package/src/cli/evidence-detail.ts +1 -75
  292. package/src/cli/evidence-inventory.ts +1 -167
  293. package/src/cli/index.ts +3 -0
  294. package/src/cli/lifecycle-presenter.ts +436 -0
  295. package/src/cli/models.ts +10 -2
  296. package/src/cli/modes/print.ts +5 -1
  297. package/src/cli/reset.ts +228 -106
  298. package/src/cli/run.ts +7 -4
  299. package/src/cli/select.ts +664 -0
  300. package/src/cli/skills.ts +9 -2
  301. package/src/cli/targets.ts +3 -0
  302. package/src/cli/tasks.ts +84 -0
  303. package/src/cli/uninstall.ts +233 -165
  304. package/src/cli/upgrade.ts +210 -150
  305. package/src/cli/usage.ts +92 -27
  306. package/src/cli/validate-model.ts +3 -3
  307. package/src/cli/verifiers.ts +147 -1
  308. package/src/cli/wiki-generate.ts +1 -0
  309. package/src/core/commit-attribution.ts +41 -1
  310. package/src/core/config.ts +56 -0
  311. package/src/core/external-diagnostic.ts +44 -0
  312. package/src/core/gateway-routing.ts +157 -0
  313. package/src/core/git-commit-attribution.ts +46 -3
  314. package/src/core/run-overrides.ts +0 -5
  315. package/src/core/safe-exec.ts +17 -2
  316. package/src/core/skill-activation.ts +92 -2
  317. package/src/core/tool-names.ts +5 -2
  318. package/src/domains/agents/builtins/architect.md +1 -1
  319. package/src/domains/agents/builtins/coder.md +1 -1
  320. package/src/domains/agents/builtins/documenter.md +1 -1
  321. package/src/domains/agents/builtins/git-master.md +1 -1
  322. package/src/domains/agents/builtins/provenance.md +7 -7
  323. package/src/domains/agents/builtins/tester.md +1 -1
  324. package/src/domains/agents/builtins/verifier.md +2 -2
  325. package/src/domains/agents/builtins/wiki-writer.md +4 -3
  326. package/src/domains/agents/builtins/world-knowledge.md +31 -0
  327. package/src/domains/agents/catalog.ts +1 -1
  328. package/src/domains/agents/result-contract.ts +70 -0
  329. package/src/domains/context/extension.ts +31 -7
  330. package/src/domains/context/refresh.ts +3 -0
  331. package/src/domains/context/wiki/frontmatter.ts +5 -2
  332. package/src/domains/context/wiki/generate.ts +6 -0
  333. package/src/domains/context/wiki/map-seed.ts +589 -0
  334. package/src/domains/context/wiki/plan.ts +2 -2
  335. package/src/domains/context/wiki/prompts.ts +43 -0
  336. package/src/domains/dispatch/active-route-planner.ts +4 -0
  337. package/src/domains/dispatch/admission.ts +29 -0
  338. package/src/domains/dispatch/agent-candidates.ts +10 -0
  339. package/src/domains/dispatch/budget-envelope.ts +86 -1
  340. package/src/domains/dispatch/capability-match.ts +1 -0
  341. package/src/domains/dispatch/capacity-lease.ts +17 -0
  342. package/src/domains/dispatch/code-step.ts +11 -4
  343. package/src/domains/dispatch/contract.ts +42 -8
  344. package/src/domains/dispatch/execution-scheduler.ts +2 -0
  345. package/src/domains/dispatch/extension.ts +184 -56
  346. package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
  347. package/src/domains/dispatch/fleet-run.ts +1 -0
  348. package/src/domains/dispatch/host-verification.ts +114 -13
  349. package/src/domains/dispatch/intent.ts +28 -18
  350. package/src/domains/dispatch/orphan-recovery.ts +2 -0
  351. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  352. package/src/domains/dispatch/reservation-store.ts +5 -3
  353. package/src/domains/dispatch/state.ts +15 -2
  354. package/src/domains/dispatch/types.ts +20 -2
  355. package/src/domains/dispatch/worker-model-metadata.ts +38 -0
  356. package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
  357. package/src/domains/eval/metrics/token-stream.ts +201 -31
  358. package/src/domains/eval/metrics/tracked.ts +40 -4
  359. package/src/domains/eval/runners/clio-run.ts +17 -11
  360. package/src/domains/eval/runners/context-index.ts +2 -7
  361. package/src/domains/eval/runners/context-init.ts +3 -6
  362. package/src/domains/eval/runners/external-command.ts +29 -11
  363. package/src/domains/eval/schema/suite.ts +28 -0
  364. package/src/domains/eval/schema/verdict.ts +2 -2
  365. package/src/domains/eval/suites/resolve.ts +13 -1
  366. package/src/domains/eval/suites/run.ts +24 -3
  367. package/src/domains/evidence/build.ts +102 -15
  368. package/src/domains/evidence/detail.ts +69 -0
  369. package/src/domains/evidence/eval.ts +13 -1
  370. package/src/domains/evidence/finish-contract-map.ts +5 -1
  371. package/src/domains/evidence/inventory.ts +167 -0
  372. package/src/domains/evidence/store.ts +16 -0
  373. package/src/domains/evidence/types.ts +12 -0
  374. package/src/domains/extensions/resources.ts +7 -0
  375. package/src/domains/interop/registry.ts +6 -2
  376. package/src/domains/interop/types.ts +4 -0
  377. package/src/domains/lifecycle/migrations/index.ts +4 -0
  378. package/src/domains/memory/task-memory-policy.ts +70 -26
  379. package/src/domains/memory/task-memory-telemetry.ts +1 -0
  380. package/src/domains/middleware/index.ts +0 -1
  381. package/src/domains/middleware/marketplace-offer.ts +22 -35
  382. package/src/domains/middleware/memory-intervention.ts +127 -32
  383. package/src/domains/middleware/memory-step-endpoint.ts +3 -2
  384. package/src/domains/middleware/runtime.ts +7 -3
  385. package/src/domains/middleware/skills-reminder.ts +31 -2
  386. package/src/domains/mux/detect.ts +3 -6
  387. package/src/domains/observability/accountability.ts +15 -1
  388. package/src/domains/observability/compaction-usage.ts +118 -0
  389. package/src/domains/observability/contract.ts +52 -7
  390. package/src/domains/observability/cost.ts +1 -1
  391. package/src/domains/observability/evidence-index.ts +10 -0
  392. package/src/domains/observability/extension.ts +15 -6
  393. package/src/domains/observability/out-of-turn-usage.ts +52 -21
  394. package/src/domains/observability/projection.ts +394 -45
  395. package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
  396. package/src/domains/prompts/fragments/operating/contract.md +2 -0
  397. package/src/domains/prompts/fragments/wiki/page.md +8 -0
  398. package/src/domains/providers/contract.ts +4 -1
  399. package/src/domains/providers/extension.ts +40 -9
  400. package/src/domains/providers/model-capabilities.ts +9 -0
  401. package/src/domains/providers/model-discovery.ts +3 -4
  402. package/src/domains/providers/model-runtime-capabilities.ts +15 -5
  403. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +48 -26
  404. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
  405. package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
  406. package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
  407. package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
  408. package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
  409. package/src/domains/providers/support.ts +11 -5
  410. package/src/domains/providers/target-model-cache.ts +25 -2
  411. package/src/domains/providers/types/capability-flags.ts +2 -0
  412. package/src/domains/providers/types/runtime-descriptor.ts +20 -1
  413. package/src/domains/providers/types/target-descriptor.ts +19 -0
  414. package/src/domains/resources/index.ts +3 -0
  415. package/src/domains/resources/skills/install.ts +72 -7
  416. package/src/domains/resources/skills/loader.ts +7 -0
  417. package/src/domains/resources/skills/marketplace.ts +63 -11
  418. package/src/domains/safety/action-classifier.ts +7 -0
  419. package/src/domains/safety/autonomy.ts +15 -0
  420. package/src/domains/safety/default-path-policy.ts +2 -0
  421. package/src/domains/safety/finish-contract-registration.ts +29 -14
  422. package/src/domains/safety/finish-contract.ts +252 -40
  423. package/src/domains/safety/index.ts +21 -1
  424. package/src/domains/safety/path-policy.ts +1 -1
  425. package/src/domains/safety/policy-engine.ts +60 -17
  426. package/src/domains/safety/protected-artifacts.ts +191 -88
  427. package/src/domains/safety/rigor.ts +53 -39
  428. package/src/domains/safety/run-effects.ts +2 -22
  429. package/src/domains/safety/skill-authority.ts +55 -0
  430. package/src/domains/safety/validation-contract.ts +388 -0
  431. package/src/domains/session/archive-readers.ts +10 -1
  432. package/src/domains/session/compaction/compact.ts +72 -22
  433. package/src/domains/session/decision-board.ts +101 -2
  434. package/src/domains/session/entries.ts +50 -7
  435. package/src/domains/session/extension.ts +4 -4
  436. package/src/domains/session/handoff.ts +2 -1
  437. package/src/domains/session/manager.ts +2 -3
  438. package/src/domains/session/task-board.ts +14 -1
  439. package/src/domains/session/tree/fork.ts +1 -2
  440. package/src/domains/session/tree/navigator.ts +1 -1
  441. package/src/domains/session/usage.ts +3 -3
  442. package/src/domains/user-tasks/acceptance.ts +56 -0
  443. package/src/domains/user-tasks/active-acceptance.ts +40 -0
  444. package/src/domains/user-tasks/store.ts +34 -3
  445. package/src/engine/acp/adapter.ts +24 -6
  446. package/src/engine/acp/server.ts +21 -4
  447. package/src/engine/acp/transport.ts +53 -8
  448. package/src/engine/acp/types.ts +4 -0
  449. package/src/engine/agent.ts +13 -3
  450. package/src/engine/ai.ts +26 -8
  451. package/src/engine/antigravity/subprocess-runtime.ts +386 -120
  452. package/src/engine/api-registry.ts +3 -0
  453. package/src/engine/apis/ollama-native.ts +15 -0
  454. package/src/engine/apis/openai-completions.ts +117 -14
  455. package/src/engine/claude/subprocess-runtime.ts +107 -60
  456. package/src/engine/external-subprocess.ts +122 -6
  457. package/src/entry/background-model-metadata.ts +18 -0
  458. package/src/entry/compaction-prompt.ts +57 -0
  459. package/src/entry/orchestrator.ts +416 -218
  460. package/src/entry/task-memory-lifecycle.ts +35 -0
  461. package/src/interactive/chat-loop-messages.ts +13 -4
  462. package/src/interactive/chat-loop.ts +65 -2
  463. package/src/interactive/chat-renderer.ts +1 -0
  464. package/src/interactive/cost-overlay.ts +26 -2
  465. package/src/interactive/dispatch-board.ts +46 -717
  466. package/src/interactive/fleet-run-preview.ts +2 -1
  467. package/src/interactive/interactive-application.ts +3 -2
  468. package/src/interactive/interactive-presentation.ts +55 -12
  469. package/src/interactive/interactive-slash-runtime.ts +4 -2
  470. package/src/interactive/oracle.ts +5 -2
  471. package/src/interactive/overlays/fleet-run-approval.ts +3 -2
  472. package/src/interactive/overlays/message-picker.ts +2 -2
  473. package/src/interactive/overlays/settings.ts +2 -2
  474. package/src/interactive/overlays/tree-selector.ts +2 -2
  475. package/src/interactive/renderers/branch-summary.ts +1 -1
  476. package/src/interactive/renderers/worker-entry.ts +32 -0
  477. package/src/interactive/slash-autocomplete.ts +4 -6
  478. package/src/interactive/slash-commands.ts +49 -45
  479. package/src/interactive/slash-spec.ts +28 -0
  480. package/src/interactive/theme/labels.ts +19 -13
  481. package/src/interactive/turn-context.ts +9 -5
  482. package/src/interactive/turn-recovery.ts +8 -0
  483. package/src/interactive/turn-runtime.ts +27 -11
  484. package/src/interactive/turn-state.ts +7 -0
  485. package/src/interactive/view/artifacts.ts +2 -0
  486. package/src/interactive/worker-receipts.ts +1 -0
  487. package/src/interactive/worker-stream.ts +13 -4
  488. package/src/tools/bootstrap.ts +4 -0
  489. package/src/tools/builtin-tool-catalog.ts +31 -0
  490. package/src/tools/compete-worktrees.ts +7 -1
  491. package/src/tools/context/index.ts +30 -9
  492. package/src/tools/core-bootstrap.ts +16 -0
  493. package/src/tools/decide.ts +136 -0
  494. package/src/tools/dispatch-admission.ts +21 -0
  495. package/src/tools/dispatch-arguments.ts +1 -0
  496. package/src/tools/dispatch-event-text.ts +10 -0
  497. package/src/tools/dispatch-plan.ts +6 -2
  498. package/src/tools/dispatch-runner.ts +27 -1
  499. package/src/tools/dispatch-types.ts +6 -0
  500. package/src/tools/evidence.ts +96 -0
  501. package/src/tools/limitation.ts +76 -0
  502. package/src/tools/policy.ts +9 -0
  503. package/src/tools/presentation.ts +3 -0
  504. package/src/tools/registry.ts +11 -5
  505. package/src/tools/result-shaping.ts +17 -5
  506. package/src/tools/task-worktree.ts +13 -3
  507. package/src/tools/tasks.ts +10 -1
  508. package/src/tools/verify/authoring.ts +170 -83
  509. package/src/tools/verify/catalog.ts +122 -5
  510. package/src/tools/verify/index.ts +2 -1
  511. package/src/tools/verify/numeric.ts +298 -0
  512. package/src/tools/verify/perf.ts +143 -0
  513. package/src/tools/verify/scripts.ts +229 -2
  514. package/src/tools/worker-evidence.ts +3 -1
  515. package/src/worker/spec-contract.ts +4 -0
  516. package/dist/chunk-2Z2IKEXI.js +0 -1554
  517. package/dist/chunk-RVG5JXAL.js +0 -41
  518. package/dist/chunk-T56WDKA5.js +0 -183
  519. package/dist/chunk-VN3SHNBN.js +0 -313
  520. package/dist/chunk-VPKWYKEY.js +0 -169
  521. package/dist/reset-OAQP3W4O.js +0 -230
  522. package/dist/uninstall-N34PCTGJ.js +0 -331
  523. package/dist/upgrade-PXK3S2YM.js +0 -325
@@ -1,36 +1,15 @@
1
- import { performance } from "node:perf_hooks";
2
- import {
3
- BusChannels,
4
- type DispatchCompletedPayload,
5
- type DispatchFailedPayload,
6
- type DispatchProgressPayload,
7
- type DispatchRunIdentity,
8
- } from "../core/bus-events.js";
9
- import { resolveDispatchFailureStatus } from "../core/dispatch-outcome.js";
10
- import type { SafeEventBus } from "../core/event-bus.js";
11
- import { rawDurationMs } from "../core/timers.js";
12
1
  import type { AgentAudience } from "../domains/agents/spec.js";
13
2
  import {
14
- cloneRunToolBudgetEnvelope,
15
3
  formatBudgetPolicy,
16
4
  formatBudgetReasons,
17
5
  formatBudgetRequest,
18
6
  formatEffectiveBudget,
19
7
  type RunToolBudgetEnvelope,
20
8
  } from "../domains/dispatch/budget-envelope.js";
21
- import type { DispatchSnapshot } from "../domains/dispatch/contract.js";
22
- import {
23
- type DispatchRequestOrigin,
24
- type RunKind,
25
- type RunStatus,
26
- runKindSupportsLiveSteering,
27
- } from "../domains/dispatch/types.js";
9
+ import { type DispatchRequestOrigin, type RunKind, runKindSupportsLiveSteering } from "../domains/dispatch/types.js";
28
10
  import { describeWriteBoundaryAttributionDowngrade } from "../domains/dispatch/write-boundary.js";
29
- import {
30
- summarizeTrustStatus,
31
- type TrustSummaryProjection,
32
- type TrustVerdict,
33
- } from "../domains/evidence/trust-projection.js";
11
+ import type { TrustSummaryProjection, TrustVerdict } from "../domains/evidence/trust-projection.js";
12
+ import type { ObservabilityContract, ObservabilityRunSummary } from "../domains/observability/contract.js";
34
13
  import {
35
14
  COST_NOT_MEASURED,
36
15
  costAggregateForAmount,
@@ -38,7 +17,8 @@ import {
38
17
  type ObservabilityNotice,
39
18
  type ObservabilitySnapshot,
40
19
  } from "../domains/observability/index.js";
41
- import { type CostProvenance, foregroundStreamUsage, resolveCostProvenance } from "../domains/providers/index.js";
20
+ import type { WorkerAction, WorkerProgressSnapshot } from "../domains/observability/worker-progress.js";
21
+ import { type CostProvenance, foregroundStreamUsage } from "../domains/providers/index.js";
42
22
  import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
43
23
  import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
44
24
  import { formatWorkerContextMeter } from "./context-meter.js";
@@ -59,20 +39,8 @@ import {
59
39
  screenTitle,
60
40
  spinnerFrame,
61
41
  } from "./theme/index.js";
62
- import {
63
- createWorkerProgressFold,
64
- type WorkerAction,
65
- type WorkerProgressFold,
66
- type WorkerProgressSnapshot,
67
- } from "./worker-progress.js";
68
- import type { WorkerReceiptReader } from "./worker-stream.js";
69
-
70
- export type DispatchBoardStatus =
71
- | Extract<RunStatus, "running" | "completed" | "failed" | "stale" | "dead">
72
- | "aborted"
73
- | "cancelling"
74
- | "enqueued"
75
- | "retrying";
42
+
43
+ export type DispatchBoardStatus = ObservabilityRunSummary["status"];
76
44
 
77
45
  export interface DispatchRetryPresentation {
78
46
  attempt: number;
@@ -206,56 +174,9 @@ export function isDispatchBoardRowCancellable(row: DispatchBoardRow): boolean {
206
174
  return row.status === "running" || row.status === "stale" || row.status === "enqueued" || row.status === "retrying";
207
175
  }
208
176
 
209
- interface DispatchBoardEntry
210
- extends Omit<
211
- DispatchBoardRow,
212
- "elapsedMs" | "recentTools" | "lastContextTokens" | "currentTool" | "retry" | "progress"
213
- > {
214
- sequence: number;
215
- enqueuedAtMs: number;
216
- startedAtMs: number | null;
217
- /**
218
- * The monotonic twin of `startedAtMs`, set from the same observation. TTFT
219
- * is a span, so it is measured against this and never against the wall
220
- * stamps, which exist to order and label rows.
221
- */
222
- startedAtClockMs: number | null;
223
- finishedAtMs: number | null;
224
- durationMs: number | null;
225
- lastContextTokens: number;
226
- /**
227
- * The one fold that reads this run's worker events. Tool activity and the
228
- * answer tail are its output, so the board no longer interprets the stream
229
- * alongside the transcript.
230
- */
231
- progress: WorkerProgressFold;
232
- retry?: DispatchRetryPresentation;
233
- }
234
-
235
- /** Keep raw task text out of long-lived TUI rows and bound hostile/user-sized input. */
236
- const TASK_SUMMARY_MAX_WIDTH = 240;
237
177
  /** Rows of worker prose an expanded card shows before it defers to `/view`. */
238
178
  const WORKER_PROGRESS_CARD_ROWS = 6;
239
179
 
240
- interface WorkerEventShape {
241
- type?: unknown;
242
- message?: {
243
- role?: unknown;
244
- usage?: {
245
- input?: unknown;
246
- output?: unknown;
247
- cacheRead?: unknown;
248
- cacheWrite?: unknown;
249
- };
250
- };
251
- messages?: unknown;
252
- }
253
-
254
- interface AssistantMessageShape {
255
- role?: unknown;
256
- stopReason?: unknown;
257
- }
258
-
259
180
  export const TASK_ISLAND_WIDTH = 44;
260
181
 
261
182
  const STATUS_ORDER: Record<DispatchBoardStatus, number> = {
@@ -269,7 +190,6 @@ const STATUS_ORDER: Record<DispatchBoardStatus, number> = {
269
190
  aborted: 7,
270
191
  completed: 8,
271
192
  };
272
- const MAX_DISPATCH_BOARD_ROWS = 50;
273
193
 
274
194
  /**
275
195
  * The agent's own name, unadorned. Audience is carried by
@@ -511,16 +431,6 @@ function evidenceCardLine(theme: ClioTheme, evidence: RunEvidencePresentation, c
511
431
  return cardUnitsLine(theme, "proof", units, contentWidth);
512
432
  }
513
433
 
514
- /** Sanitize lifecycle task text at the terminal boundary and collapse it to one bounded line. */
515
- function sanitizeDispatchTaskSummary(value: unknown): string | undefined {
516
- if (typeof value !== "string") return undefined;
517
- const sanitized = sanitizeCallTargetText(value);
518
- if (sanitized.length === 0) return undefined;
519
- // pi-tui's truncator appends reset sequences when it elides. Strip those
520
- // again so the stored projection remains plain text, not terminal styling.
521
- return sanitizeCallTargetText(truncateToWidth(sanitized, TASK_SUMMARY_MAX_WIDTH, "…", false));
522
- }
523
-
524
434
  function retryCountdown(retry: DispatchRetryPresentation, now = Date.now()): string {
525
435
  const remainingMs = Math.max(0, retry.dueAtMs - now);
526
436
  return remainingMs === 0 ? "now" : `in ${formatCompactMs(remainingMs)}`;
@@ -1154,87 +1064,6 @@ export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, max
1154
1064
  return frame(clioTheme(), "Fleet runs", body, TASK_ISLAND_WIDTH + 4);
1155
1065
  }
1156
1066
 
1157
- function parseRunId(value: unknown): string | null {
1158
- return typeof value === "string" && value.length > 0 ? value : null;
1159
- }
1160
-
1161
- function parseText(value: unknown, fallback: string): string {
1162
- return typeof value === "string" && value.length > 0 ? value : fallback;
1163
- }
1164
-
1165
- function parseRuntimeKind(value: unknown): RunKind {
1166
- if (value === "sdk" || value === "subprocess" || value === "acp-delegation") return value;
1167
- return "http";
1168
- }
1169
-
1170
- function parseAgentAudience(value: unknown, fallback: AgentAudience | undefined): AgentAudience | undefined {
1171
- if (value === "base" || value === "shadow" || value === "custom" || value === "internal") return value;
1172
- return fallback;
1173
- }
1174
-
1175
- function parseRequestOrigin(
1176
- value: unknown,
1177
- fallback: DispatchRequestOrigin | undefined,
1178
- ): DispatchRequestOrigin | undefined {
1179
- if (value === "user" || value === "agent" || value === "internal") return value;
1180
- return fallback;
1181
- }
1182
-
1183
- function parseFiniteNumber(value: unknown, fallback: number): number {
1184
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
1185
- }
1186
-
1187
- function parseNonEmptyString(value: unknown): string | undefined {
1188
- return typeof value === "string" && value.length > 0 ? value : undefined;
1189
- }
1190
-
1191
- function parseTaskSummary(
1192
- raw: Partial<DispatchRunIdentity> & { task?: unknown; taskSummary?: unknown },
1193
- fallback: string | undefined,
1194
- ): string | undefined {
1195
- return sanitizeDispatchTaskSummary(raw.task) ?? sanitizeDispatchTaskSummary(raw.taskSummary) ?? fallback;
1196
- }
1197
-
1198
- function parsePositiveInt(value: unknown): number | undefined {
1199
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
1200
- }
1201
-
1202
- function parseGateBadge(value: unknown): { role: string; cycle: number } | undefined {
1203
- if (typeof value !== "object" || value === null) return undefined;
1204
- const record = value as { role?: unknown; cycle?: unknown };
1205
- if (typeof record.role !== "string" || record.role.length === 0) return undefined;
1206
- const cycle = parsePositiveInt(record.cycle) ?? 1;
1207
- return { role: record.role, cycle };
1208
- }
1209
-
1210
- function parseEndpointCapacity(value: unknown): DispatchBoardRow["endpoint"] | undefined {
1211
- if (typeof value !== "object" || value === null) return undefined;
1212
- const record = value as { key?: unknown; label?: unknown; limit?: unknown };
1213
- const key = parseNonEmptyString(record.key);
1214
- const label = parseNonEmptyString(record.label);
1215
- const limit = parsePositiveInt(record.limit);
1216
- return key === undefined || label === undefined || limit === undefined ? undefined : { key, label, limit };
1217
- }
1218
-
1219
- function parseCouncilBadge(value: unknown): DispatchBoardRow["council"] | undefined {
1220
- if (typeof value !== "object" || value === null) return undefined;
1221
- const record = value as { group?: unknown; label?: unknown; color?: unknown; round?: unknown };
1222
- const group = parseNonEmptyString(record.group);
1223
- const label = parseNonEmptyString(record.label);
1224
- const round = parsePositiveInt(record.round);
1225
- if (group === undefined || label === undefined || round === undefined) return undefined;
1226
- return {
1227
- group,
1228
- label,
1229
- ...(typeof record.color === "string" && record.color.length > 0 ? { color: record.color } : {}),
1230
- round,
1231
- };
1232
- }
1233
-
1234
- function parseFiniteNumberOrZero(value: unknown): number {
1235
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
1236
- }
1237
-
1238
1067
  function parseOptionalDetail(value: unknown): string | null {
1239
1068
  if (typeof value !== "string") return null;
1240
1069
  const detail = value.replace(/\s+/g, " ").trim();
@@ -1273,150 +1102,34 @@ function terminalDetail(row: DispatchBoardRow): string | null {
1273
1102
  return parseOptionalDetail(row.outcomeDetail);
1274
1103
  }
1275
1104
 
1276
- function resolveAgentEndStatus(rawMessages: unknown): DispatchBoardStatus | null {
1277
- if (!Array.isArray(rawMessages)) return null;
1278
- for (let index = rawMessages.length - 1; index >= 0; index -= 1) {
1279
- const message = (rawMessages[index] ?? {}) as AssistantMessageShape;
1280
- if (message.role !== "assistant") continue;
1281
- if (message.stopReason === "stop") return "completed";
1282
- if (message.stopReason === "error") return "failed";
1283
- if (message.stopReason === "aborted") return "aborted";
1284
- return null;
1285
- }
1286
- return null;
1287
- }
1288
-
1289
- function resolveFailureDetail(
1290
- payload: Partial<DispatchFailedPayload>,
1291
- fallback: string | null | undefined,
1292
- ): string | null {
1293
- const detail = parseOptionalDetail(payload.outcomeDetail);
1294
- if (detail !== null) return detail;
1295
- if (payload.reason === "timed_out") return "turn timeout exceeded";
1296
- return fallback ?? null;
1297
- }
1298
-
1299
- function resolveHeartbeatStatus(status: unknown): DispatchBoardStatus | null {
1300
- if (status === "alive") return "running";
1301
- if (status === "stale" || status === "dead") return status;
1302
- return null;
1303
- }
1304
-
1305
1105
  function isTerminalStatus(status: DispatchBoardStatus): boolean {
1306
1106
  return status === "completed" || status === "failed" || status === "aborted" || status === "dead";
1307
1107
  }
1308
1108
 
1309
- function parseRetrySnapshot(value: DispatchSnapshot["retrying"][number]): DispatchRetryPresentation | null {
1310
- const attempt = parsePositiveInt(value.attempt);
1311
- const dueAtMs = Date.parse(value.dueAt);
1312
- if (attempt === undefined || !Number.isFinite(dueAtMs)) return null;
1313
- return {
1314
- attempt,
1315
- dueAtMs,
1316
- reason: sanitizeCallTargetText(value.reason),
1317
- };
1318
- }
1319
-
1320
- function readRetrySnapshot(
1321
- snapshot: DispatchSnapshot,
1322
- ): Map<string, { agentId: string; taskSummary?: string; retry: DispatchRetryPresentation }> {
1323
- const retrying = new Map<string, { agentId: string; taskSummary?: string; retry: DispatchRetryPresentation }>();
1324
- try {
1325
- for (const value of snapshot.retrying) {
1326
- const runId = parseRunId(value.runId);
1327
- const agentId = parseNonEmptyString(value.agentId);
1328
- const retry = parseRetrySnapshot(value);
1329
- const taskSummary = sanitizeDispatchTaskSummary(value.task);
1330
- if (runId && agentId && retry) {
1331
- retrying.set(runId, {
1332
- agentId,
1333
- ...(taskSummary !== undefined ? { taskSummary } : {}),
1334
- retry,
1335
- });
1336
- }
1337
- }
1338
- } catch {
1339
- // The board remains lifecycle-event driven if an optional snapshot fails.
1340
- }
1341
- return retrying;
1342
- }
1343
-
1344
- function readRunningSnapshot(snapshot: DispatchSnapshot): Map<
1345
- string,
1346
- {
1347
- inputTokens: number;
1348
- outputTokens: number;
1349
- tokenCount: number;
1350
- costUsd: number;
1351
- costProvenance: CostProvenance;
1352
- outcomePhase: string;
1353
- budget?: RunToolBudgetEnvelope;
1354
- }
1355
- > {
1356
- const running = new Map<
1357
- string,
1358
- {
1359
- inputTokens: number;
1360
- outputTokens: number;
1361
- tokenCount: number;
1362
- costUsd: number;
1363
- costProvenance: CostProvenance;
1364
- outcomePhase: string;
1365
- budget?: RunToolBudgetEnvelope;
1366
- }
1367
- >();
1368
- try {
1369
- for (const value of snapshot.running) {
1370
- const runId = parseRunId(value.runId);
1371
- if (!runId) continue;
1372
- const budget = cloneRunToolBudgetEnvelope(value.budget);
1373
- running.set(runId, {
1374
- inputTokens: parseFiniteNumberOrZero(value.tokens.input),
1375
- outputTokens: parseFiniteNumberOrZero(value.tokens.output),
1376
- tokenCount: parseFiniteNumberOrZero(value.tokens.total),
1377
- costUsd: parseFiniteNumberOrZero(value.costUsd),
1378
- costProvenance: value.costProvenance ?? "unknown",
1379
- outcomePhase: value.outcomePhase,
1380
- ...(budget !== undefined ? { budget } : {}),
1381
- });
1382
- }
1383
- } catch {
1384
- // Lifecycle/progress events remain authoritative if the optional snapshot fails.
1385
- }
1386
- return running;
1387
- }
1388
-
1389
- function resolveElapsedMs(entry: DispatchBoardEntry, now: number): number {
1390
- const startedAtMs = entry.startedAtMs ?? entry.enqueuedAtMs;
1391
- if (entry.durationMs !== null) return entry.durationMs;
1392
- const endMs = entry.finishedAtMs ?? now;
1393
- return Math.max(0, rawDurationMs(startedAtMs, endMs));
1394
- }
1395
-
1396
- function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
1109
+ function toRow(entry: ObservabilityRunSummary, now: number): DispatchBoardRow {
1397
1110
  const retry = entry.retry;
1398
- const progress = entry.progress.snapshot();
1111
+ const progress = entry.progress;
1399
1112
  return {
1400
- progress,
1113
+ ...(progress !== undefined ? { progress } : {}),
1401
1114
  runId: entry.runId,
1402
1115
  agentId: entry.agentId,
1403
1116
  ...(entry.agentAudience !== undefined ? { agentAudience: entry.agentAudience } : {}),
1404
1117
  ...(entry.requestOrigin !== undefined ? { requestOrigin: entry.requestOrigin } : {}),
1405
- runtimeKind: entry.runtimeKind,
1406
- runtimeId: entry.runtimeId,
1407
- targetId: entry.targetId,
1408
- wireModelId: entry.wireModelId,
1118
+ runtimeKind: entry.runtimeKind ?? "http",
1119
+ runtimeId: entry.runtimeId ?? "-",
1120
+ targetId: entry.targetId ?? "-",
1121
+ wireModelId: entry.modelId ?? "-",
1409
1122
  ...(entry.endpoint !== undefined ? { endpoint: { ...entry.endpoint } } : {}),
1410
1123
  ...(entry.taskSummary !== undefined ? { taskSummary: entry.taskSummary } : {}),
1411
1124
  ...(entry.budget !== undefined ? { budget: entry.budget } : {}),
1412
- status: retry ? "retrying" : entry.status,
1413
- elapsedMs: resolveElapsedMs(entry, now),
1414
- tokenCount: entry.tokenCount,
1125
+ status: entry.status,
1126
+ elapsedMs: entry.durationMs ?? Math.max(0, (entry.finishedAtMs ?? now) - entry.startedAtMs),
1127
+ tokenCount: entry.tokens.total,
1415
1128
  costUsd: entry.costUsd,
1416
1129
  ...(entry.costProvenance !== undefined ? { costProvenance: entry.costProvenance } : {}),
1417
- inputTokens: entry.inputTokens,
1418
- outputTokens: entry.outputTokens,
1419
- ttftMs: entry.ttftMs,
1130
+ inputTokens: entry.tokens.input,
1131
+ outputTokens: entry.tokens.output,
1132
+ ttftMs: entry.ttftMs ?? null,
1420
1133
  ...(entry.outcomeDetail !== undefined ? { outcomeDetail: entry.outcomeDetail } : {}),
1421
1134
  ...(entry.node !== undefined ? { node: entry.node } : {}),
1422
1135
  ...(entry.gate !== undefined ? { gate: { ...entry.gate } } : {}),
@@ -1426,439 +1139,55 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
1426
1139
  ...(entry.rerouteCount !== undefined ? { rerouteCount: entry.rerouteCount } : {}),
1427
1140
  ...(entry.failoverHops !== undefined ? { failoverHops: entry.failoverHops } : {}),
1428
1141
  ...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
1429
- lastContextTokens: entry.lastContextTokens,
1142
+ lastContextTokens: entry.lastContextTokens ?? 0,
1430
1143
  ...(entry.receiptId !== undefined ? { receiptId: entry.receiptId } : {}),
1431
1144
  ...(entry.phase !== undefined ? { phase: { ...entry.phase } } : {}),
1432
1145
  // Both tool fields are views of the one projection, so the compact card and
1433
1146
  // the expanded detail can never disagree about what is running.
1434
- currentTool: retry ? null : (progress.currentAction?.tool ?? null),
1435
- recentTools: progress.recentActions.map((action) => action.tool),
1147
+ currentTool: retry ? null : (progress?.currentAction?.tool ?? null),
1148
+ recentTools: progress?.recentActions.map((action) => action.tool) ?? [],
1436
1149
  ...(retry ? { retry: { ...retry } } : {}),
1437
1150
  ...(entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}),
1438
1151
  ...(entry.writeRecordDowngrade ? { writeRecordDowngrade: { ...entry.writeRecordDowngrade } } : {}),
1439
1152
  };
1440
1153
  }
1441
1154
 
1442
- function sortEntries(a: DispatchBoardEntry, b: DispatchBoardEntry): number {
1443
- const aStatus = a.retry ? "retrying" : a.status;
1444
- const bStatus = b.retry ? "retrying" : b.status;
1445
- const rank = STATUS_ORDER[aStatus] - STATUS_ORDER[bStatus];
1446
- if (rank !== 0) return rank;
1447
- const aTime = a.finishedAtMs ?? a.startedAtMs ?? a.enqueuedAtMs;
1448
- const bTime = b.finishedAtMs ?? b.startedAtMs ?? b.enqueuedAtMs;
1449
- if (aTime !== bTime) return bTime - aTime;
1450
- return a.sequence - b.sequence;
1451
- }
1452
-
1453
- function pruneEntries(entries: Map<string, DispatchBoardEntry>, onEvict?: (runId: string) => void): void {
1454
- if (entries.size <= MAX_DISPATCH_BOARD_ROWS) return;
1455
- const terminalEntries = [...entries.values()]
1456
- .filter((entry) => !entry.retry && isTerminalStatus(entry.status))
1457
- .sort((a, b) => a.sequence - b.sequence);
1458
- const evictionQueue =
1459
- terminalEntries.length > 0 ? terminalEntries : [...entries.values()].sort((a, b) => a.sequence - b.sequence);
1460
- for (const entry of evictionQueue) {
1461
- if (entries.size <= MAX_DISPATCH_BOARD_ROWS) break;
1462
- entries.delete(entry.runId);
1463
- onEvict?.(entry.runId);
1464
- }
1465
- }
1466
-
1155
+ /** The board keeps display order and reads every run fact from observability. */
1467
1156
  export function createDispatchBoardStore(
1468
- bus: SafeEventBus,
1469
- snapshot?: () => DispatchSnapshot,
1470
- /**
1471
- * Sealed terminal facts for a finished run. Settlement replaces the
1472
- * provisional live tail with the receipt's answer where one can be read; the
1473
- * run's own durable message stands in where it cannot.
1474
- */
1475
- readReceipt?: WorkerReceiptReader,
1157
+ projection: Pick<ObservabilityContract, "snapshot" | "subscribe" | "reconcileRuns" | "setFleetPhase">,
1476
1158
  ): {
1477
1159
  rows(): ReadonlyArray<DispatchBoardRow>;
1478
1160
  activeRows(): ReadonlyArray<DispatchBoardRow>;
1479
1161
  reconcile(): void;
1480
- /**
1481
- * Record the fleet-plan position of a run the caller just dispatched. The
1482
- * board never infers a position: a run carries one only because whoever
1483
- * dispatched the plan said so, which is why a non-fleet run can never grow
1484
- * a phase cell.
1485
- */
1486
1162
  setFleetPhase(runId: string, phase: DispatchBoardPhase): void;
1487
1163
  unsubscribe(): void;
1488
1164
  } {
1489
- const entries = new Map<string, DispatchBoardEntry>();
1490
- // Positions arrive from the fleet-run path, which may hand one over before
1491
- // the run's first lifecycle event creates the entry.
1492
- const fleetPhases = new Map<string, DispatchBoardPhase>();
1493
- let nextSequence = 0;
1494
- let reconciledAtMs = Date.now();
1495
-
1496
- /**
1497
- * Seal a run's projection on the sealed receipt's answer. Only for the
1498
- * terminal bus events: the dispatch domain writes `receipts/<runId>.json`
1499
- * before it publishes one, so this is the first moment the file exists.
1500
- */
1501
- const settleFromReceipt = (entry: DispatchBoardEntry): void => {
1502
- const facts = readReceipt?.(entry.runId);
1503
- const text = facts?.text;
1504
- entry.progress.settle(typeof text === "string" && text.trim().length > 0 ? text : undefined);
1505
- if (facts?.trust !== undefined) entry.trust = summarizeTrustStatus(facts.trust);
1506
- };
1507
-
1508
- // Payloads arrive typed off the bus, but the board keeps its runtime
1509
- // parsing (parse* helpers) because events are not validated at runtime.
1510
- const upsertBase = (
1511
- raw: Partial<DispatchRunIdentity>,
1512
- status: DispatchBoardStatus,
1513
- now: number,
1514
- ): DispatchBoardEntry | null => {
1515
- const runId = parseRunId(raw.runId);
1516
- if (!runId) return null;
1517
- const previous = entries.get(runId);
1518
- const agentAudience = parseAgentAudience(raw.agentAudience, previous?.agentAudience);
1519
- const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
1520
- const node = parseNonEmptyString(raw.node) ?? previous?.node;
1521
- const endpoint = parseEndpointCapacity(raw.endpoint) ?? previous?.endpoint;
1522
- const gate = parseGateBadge(raw.gate) ?? previous?.gate;
1523
- const council = parseCouncilBadge(raw.council) ?? previous?.council;
1524
- const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
1525
- const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
1526
- const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
1527
- const budget = cloneRunToolBudgetEnvelope(raw.budget) ?? previous?.budget;
1528
- const phase = fleetPhases.get(runId) ?? previous?.phase;
1529
- const entry: DispatchBoardEntry = {
1530
- runId,
1531
- agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
1532
- ...(agentAudience !== undefined ? { agentAudience } : {}),
1533
- ...(requestOrigin !== undefined ? { requestOrigin } : {}),
1534
- runtimeKind: parseRuntimeKind(raw.runtimeKind ?? previous?.runtimeKind),
1535
- runtimeId: parseText(raw.runtimeId, previous?.runtimeId ?? "-"),
1536
- targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
1537
- wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
1538
- ...(endpoint !== undefined ? { endpoint } : {}),
1539
- ...(taskSummary !== undefined ? { taskSummary } : {}),
1540
- ...(budget !== undefined ? { budget } : {}),
1541
- status,
1542
- tokenCount: previous?.tokenCount ?? 0,
1543
- costUsd: previous?.costUsd ?? 0,
1544
- costProvenance: previous?.costProvenance ?? "unknown",
1545
- sequence: previous?.sequence ?? nextSequence++,
1546
- enqueuedAtMs: previous?.enqueuedAtMs ?? now,
1547
- startedAtMs: previous?.startedAtMs ?? null,
1548
- startedAtClockMs: previous?.startedAtClockMs ?? null,
1549
- finishedAtMs: previous?.finishedAtMs ?? null,
1550
- durationMs: previous?.durationMs ?? null,
1551
- inputTokens: previous?.inputTokens ?? 0,
1552
- outputTokens: previous?.outputTokens ?? 0,
1553
- ttftMs: previous?.ttftMs ?? null,
1554
- outcomeDetail: previous?.outcomeDetail ?? null,
1555
- ...(node !== undefined ? { node } : {}),
1556
- ...(gate !== undefined ? { gate } : {}),
1557
- ...(council !== undefined ? { council } : {}),
1558
- ...(previous?.hostVerification !== undefined ? { hostVerification: previous.hostVerification } : {}),
1559
- ...(rerouteCount !== undefined ? { rerouteCount } : {}),
1560
- ...(contextWindow !== undefined ? { contextWindow } : {}),
1561
- lastContextTokens: previous?.lastContextTokens ?? 0,
1562
- ...(previous?.receiptId !== undefined ? { receiptId: previous.receiptId } : {}),
1563
- ...(phase !== undefined ? { phase } : {}),
1564
- progress: previous?.progress ?? createWorkerProgressFold(),
1565
- ...(previous?.retry ? { retry: { ...previous.retry } } : {}),
1566
- ...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
1567
- ...(previous?.writeRecordDowngrade ? { writeRecordDowngrade: { ...previous.writeRecordDowngrade } } : {}),
1568
- };
1569
- entries.set(runId, entry);
1570
- pruneEntries(entries, (evicted) => fleetPhases.delete(evicted));
1571
- return entry;
1572
- };
1573
-
1574
- const unsubscribers = [
1575
- bus.on(BusChannels.DispatchEnqueued, (raw) => {
1576
- const entry = upsertBase(raw ?? {}, "enqueued", Date.now());
1577
- if (entry) delete entry.retry;
1578
- }),
1579
- bus.on(BusChannels.DispatchStarted, (raw) => {
1580
- const now = Date.now();
1581
- const entry = upsertBase(raw ?? {}, "running", now);
1582
- if (!entry) return;
1583
- entry.startedAtMs ??= now;
1584
- entry.startedAtClockMs ??= performance.now();
1585
- entry.finishedAtMs = null;
1586
- entry.durationMs = null;
1587
- delete entry.retry;
1588
- }),
1589
- bus.on(BusChannels.DispatchCompleted, (raw) => {
1590
- const now = Date.now();
1591
- const payload: Partial<DispatchCompletedPayload> = raw ?? {};
1592
- const entry = upsertBase(payload, "completed", now);
1593
- if (!entry) return;
1594
- entry.startedAtMs ??= entry.enqueuedAtMs;
1595
- entry.finishedAtMs = now;
1596
- entry.durationMs = parseFiniteNumber(payload.durationMs, Math.max(0, rawDurationMs(entry.startedAtMs, now)));
1597
- entry.tokenCount = parseFiniteNumber(payload.tokenCount, entry.tokenCount);
1598
- entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
1599
- entry.costProvenance = resolveCostProvenance(payload.costProvenance, entry.costProvenance ?? "unknown");
1600
- entry.outcomeDetail = null;
1601
- if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
1602
- settleFromReceipt(entry);
1603
- // A terminal dispatch event is published only after the run's receipt is
1604
- // sealed at receipts/<runId>.json, so the run id is the receipt id here.
1605
- entry.receiptId = entry.runId;
1606
- delete entry.retry;
1607
- if (typeof payload.inputTokenCount === "number") {
1608
- entry.inputTokens = payload.inputTokenCount + parseFiniteNumberOrZero(payload.cacheReadTokenCount);
1609
- }
1610
- if (typeof payload.outputTokenCount === "number") {
1611
- entry.outputTokens = payload.outputTokenCount;
1612
- }
1613
- }),
1614
- bus.on(BusChannels.DispatchFailed, (raw) => {
1615
- const now = Date.now();
1616
- const payload: Partial<DispatchFailedPayload> = raw ?? {};
1617
- const runId = parseRunId(payload.runId);
1618
- const previousStatus = runId !== null ? entries.get(runId)?.status : undefined;
1619
- const resolvedStatus = resolveDispatchFailureStatus(payload.reason);
1620
- const status = previousStatus === "dead" && resolvedStatus === "failed" ? "dead" : resolvedStatus;
1621
- const entry = upsertBase(payload, status, now);
1622
- if (!entry) return;
1623
- entry.startedAtMs ??= entry.enqueuedAtMs;
1624
- entry.finishedAtMs = now;
1625
- entry.durationMs = parseFiniteNumber(payload.durationMs, Math.max(0, rawDurationMs(entry.startedAtMs, now)));
1626
- entry.tokenCount = parseFiniteNumber(payload.tokenCount, entry.tokenCount);
1627
- entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
1628
- entry.costProvenance = resolveCostProvenance(payload.costProvenance, entry.costProvenance ?? "unknown");
1629
- entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
1630
- if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
1631
- settleFromReceipt(entry);
1632
- // A denied retry never reached a run, so no receipt was sealed for it;
1633
- // every other failure finalized through recordReceipt like a success.
1634
- if (payload.reason !== "retry_denied") entry.receiptId = entry.runId;
1635
- delete entry.retry;
1636
- if (typeof payload.inputTokenCount === "number") {
1637
- entry.inputTokens = payload.inputTokenCount + parseFiniteNumberOrZero(payload.cacheReadTokenCount);
1638
- }
1639
- if (typeof payload.outputTokenCount === "number") {
1640
- entry.outputTokens = payload.outputTokenCount;
1641
- }
1642
- }),
1643
- bus.on(BusChannels.RunAborted, (raw) => {
1644
- const runId = parseRunId(raw?.runId);
1645
- if (!runId) return;
1646
- const entry = entries.get(runId);
1647
- if (!entry) return;
1648
- const wasRetrying = entry.retry !== undefined;
1649
- delete entry.retry;
1650
- if (wasRetrying && raw?.startedAt === null) {
1651
- // Canceling a retry timer is synchronous: there is no worker left to
1652
- // wind down, so the history row can become terminal immediately.
1653
- entry.status = "aborted";
1654
- entry.finishedAtMs = Date.now();
1655
- entry.progress.settle();
1656
- } else {
1657
- if (isTerminalStatus(entry.status) && !wasRetrying) return;
1658
- entry.status = "cancelling";
1659
- }
1660
- entry.outcomeDetail = parseOptionalDetail(raw?.reason) ?? entry.outcomeDetail ?? null;
1661
- }),
1662
- bus.on(BusChannels.DispatchProgress, (raw) => {
1663
- const payload: Partial<DispatchProgressPayload> = raw ?? {};
1664
- const runId = parseRunId(payload.runId);
1665
- if (!runId) return;
1666
- const entry = entries.get(runId);
1667
- if (!entry) return;
1668
- const workerEvent = (payload.event ?? {}) as WorkerEventShape;
1669
- const type = typeof workerEvent.type === "string" ? workerEvent.type : "";
1670
- if (type === "heartbeat_status") {
1671
- if (isTerminalStatus(entry.status) || entry.status === "cancelling") return;
1672
- const status = resolveHeartbeatStatus((workerEvent as { status?: unknown }).status);
1673
- if (!status) return;
1674
- entry.status = status;
1675
- if (status === "dead") {
1676
- entry.finishedAtMs ??= Date.now();
1677
- // A worker-side terminal signal arrives before any receipt is
1678
- // sealed, so the run's own last durable message is the best truth
1679
- // there is; the terminal bus event replaces it with the sealed one.
1680
- entry.progress.settle();
1681
- delete entry.retry;
1682
- }
1683
- return;
1684
- }
1685
- if (type === "attempt_start") {
1686
- // The assignment stream hands the root run a failover hop: a later
1687
- // attempt took over, so the row is live again on the new route.
1688
- entry.failoverHops = (entry.failoverHops ?? 0) + 1;
1689
- entry.status = "running";
1690
- entry.finishedAtMs = null;
1691
- // The projection keeps the tail the operator is reading and drops the
1692
- // finished attempt's live state, exactly as the transcript block does.
1693
- entry.progress.restart();
1694
- delete entry.retry;
1695
- return;
1696
- }
1697
- if (type === "agent_start") {
1698
- entry.startedAtMs = Date.now();
1699
- entry.startedAtClockMs = performance.now();
1700
- }
1701
- if (type === "message_update") {
1702
- const assistantEvent =
1703
- "assistantMessageEvent" in workerEvent &&
1704
- typeof workerEvent.assistantMessageEvent === "object" &&
1705
- workerEvent.assistantMessageEvent !== null
1706
- ? (workerEvent.assistantMessageEvent as { type?: unknown })
1707
- : {};
1708
- const hasDelta =
1709
- assistantEvent.type === "text_delta" ||
1710
- assistantEvent.type === "thinking_delta" ||
1711
- assistantEvent.type === "toolcall_start" ||
1712
- assistantEvent.type === "toolcall_delta";
1713
- if (hasDelta && entry.ttftMs === null && entry.startedAtClockMs !== null) {
1714
- entry.ttftMs = Math.round(performance.now() - entry.startedAtClockMs);
1715
- }
1716
- }
1717
- if (type === "clio_coder_write_record_downgraded") {
1718
- const rawPayload = (workerEvent as { payload?: unknown }).payload;
1719
- const payload =
1720
- typeof rawPayload === "object" && rawPayload !== null ? (rawPayload as Record<string, unknown>) : null;
1721
- const tool = payload === null ? undefined : parseNonEmptyString(payload.tool);
1722
- const toolCallId = payload === null ? undefined : parseNonEmptyString(payload.toolCallId);
1723
- if (payload?.reason === "opaque_tool_succeeded" && tool !== undefined && toolCallId !== undefined) {
1724
- entry.writeRecordDowngrade = {
1725
- reason: "opaque_tool_succeeded",
1726
- tool: sanitizeCallTargetText(tool),
1727
- toolCallId: sanitizeCallTargetText(toolCallId),
1728
- };
1729
- }
1730
- }
1731
- // The one fold that reads worker prose and tool activity. Everything the
1732
- // board says about what a worker is saying or touching comes from here,
1733
- // so the board and the transcript block cannot tell two stories.
1734
- entry.progress.observe(payload.event);
1735
- if (type === "clio_coder_steer_received") {
1736
- const steerPayload = (workerEvent as { payload?: { chars?: unknown } }).payload;
1737
- entry.steerAcknowledgement = {
1738
- receivedAtMs: Date.now(),
1739
- chars: Math.max(0, Math.floor(parseFiniteNumberOrZero(steerPayload?.chars))),
1740
- };
1741
- }
1742
- if (isTerminalStatus(entry.status)) return;
1743
- if (type === "message_end" && workerEvent.message?.role === "assistant") {
1744
- const usage = workerEvent.message.usage;
1745
- const input = parseFiniteNumberOrZero(usage?.input) + parseFiniteNumberOrZero(usage?.cacheRead);
1746
- const output = parseFiniteNumberOrZero(usage?.output);
1747
- entry.inputTokens += input;
1748
- entry.outputTokens += output;
1749
- entry.tokenCount += input + output + parseFiniteNumberOrZero(usage?.cacheWrite);
1750
- // The last assistant message's input+cacheRead+output approximates the
1751
- // worker's current context occupancy for the per-worker meter.
1752
- entry.lastContextTokens = input + output;
1753
- }
1754
- if (type === "agent_end") {
1755
- const status = resolveAgentEndStatus(workerEvent.messages);
1756
- if (!status) return;
1757
- entry.status = status;
1758
- entry.finishedAtMs ??= Date.now();
1759
- // The worker says it is done before the domain seals its receipt. Settle
1760
- // on the durable message now so the card stops reading as live; the
1761
- // terminal bus event seals it on the receipt shortly after.
1762
- entry.progress.settle();
1763
- delete entry.retry;
1764
- }
1765
- }),
1766
- ];
1767
-
1768
- let closed = false;
1769
- const reconcile = (): void => {
1770
- const now = Date.now();
1771
- if (!snapshot) {
1772
- reconciledAtMs = now;
1773
- return;
1774
- }
1775
- let currentSnapshot: DispatchSnapshot;
1776
- try {
1777
- currentSnapshot = snapshot();
1778
- } catch {
1779
- // Lifecycle and progress events remain authoritative if snapshot polling fails.
1780
- return;
1781
- }
1782
- if (!Array.isArray(currentSnapshot?.retrying) || !Array.isArray(currentSnapshot?.running)) return;
1783
- reconciledAtMs = now;
1784
- const retrying = readRetrySnapshot(currentSnapshot);
1785
- const running = readRunningSnapshot(currentSnapshot);
1786
- for (const entry of entries.values()) {
1787
- if (entry.retry && !retrying.has(entry.runId)) {
1788
- // The timer left the queue (launched or was otherwise consumed). The
1789
- // parent attempt's original terminal state becomes visible again; the
1790
- // successor gets its own row.
1791
- delete entry.retry;
1792
- }
1793
- }
1794
- for (const [runId, projection] of retrying) {
1795
- const existing = entries.get(runId);
1796
- if (existing && existing.status !== "failed" && existing.status !== "dead") {
1797
- continue;
1798
- }
1799
- const entry =
1800
- existing ??
1801
- upsertBase(
1802
- {
1803
- runId,
1804
- agentId: projection.agentId,
1805
- ...(projection.taskSummary !== undefined ? { task: projection.taskSummary } : {}),
1806
- },
1807
- "failed",
1808
- now,
1809
- );
1810
- if (entry) {
1811
- // A waiting retry has no live worker; the row projection already
1812
- // renders a retrying row with no current tool.
1813
- entry.retry = { ...projection.retry };
1814
- if (entry.taskSummary === undefined && projection.taskSummary !== undefined) {
1815
- entry.taskSummary = projection.taskSummary;
1816
- }
1817
- }
1818
- }
1819
- for (const [runId, live] of running) {
1820
- const entry = entries.get(runId);
1821
- if (!entry) continue;
1822
- if (isTerminalStatus(entry.status) && live.outcomePhase !== "aborting") continue;
1823
- entry.inputTokens = live.inputTokens;
1824
- entry.outputTokens = live.outputTokens;
1825
- entry.tokenCount = live.tokenCount;
1826
- entry.costUsd = live.costUsd;
1827
- entry.costProvenance = live.costProvenance;
1828
- if (live.budget !== undefined) entry.budget = live.budget;
1829
- if (live.outcomePhase === "aborting") {
1830
- if (entry.status !== "completed" && entry.status !== "aborted") {
1831
- entry.status = "cancelling";
1832
- delete entry.retry;
1833
- }
1834
- }
1835
- }
1836
- };
1837
-
1165
+ let current = projection.snapshot();
1166
+ const unsubscribe = projection.subscribe((snapshot) => {
1167
+ current = snapshot;
1168
+ });
1838
1169
  const projectRows = (activeOnly: boolean): ReadonlyArray<DispatchBoardRow> => {
1839
- return [...entries.values()]
1840
- .sort(sortEntries)
1841
- .map((entry) => toRow(entry, reconciledAtMs))
1170
+ return [...current.runs]
1171
+ .reverse()
1172
+ .sort(
1173
+ (a, b) =>
1174
+ STATUS_ORDER[a.status] - STATUS_ORDER[b.status] ||
1175
+ (b.finishedAtMs ?? b.startedAtMs) - (a.finishedAtMs ?? a.startedAtMs),
1176
+ )
1177
+ .map((run) => toRow(run, current.generatedAt))
1842
1178
  .filter((row) => !activeOnly || !isTerminalStatus(row.status));
1843
1179
  };
1844
-
1845
1180
  return {
1846
- rows() {
1847
- return projectRows(false);
1848
- },
1849
- activeRows() {
1850
- return projectRows(true);
1851
- },
1852
- reconcile,
1853
- setFleetPhase(runId: string, phase: DispatchBoardPhase): void {
1854
- fleetPhases.set(runId, phase);
1855
- const entry = entries.get(runId);
1856
- if (entry) entry.phase = { ...phase };
1181
+ rows: () => projectRows(false),
1182
+ activeRows: () => projectRows(true),
1183
+ reconcile() {
1184
+ projection.reconcileRuns();
1185
+ current = projection.snapshot();
1857
1186
  },
1858
- unsubscribe() {
1859
- if (closed) return;
1860
- closed = true;
1861
- for (const unsubscribe of unsubscribers) unsubscribe();
1187
+ setFleetPhase(runId, phase) {
1188
+ projection.setFleetPhase(runId, phase);
1189
+ current = projection.snapshot();
1862
1190
  },
1191
+ unsubscribe,
1863
1192
  };
1864
1193
  }