@iowarp/clio-coder 0.3.0 → 0.3.1

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 (988) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +50 -53
  3. package/dist/acp-FPR54DGL.js +138 -0
  4. package/dist/{agents-Q4MYPMUW.js → agents-OGPIHPJH.js} +33 -25
  5. package/dist/assets/codewiki.json +1 -0
  6. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
  7. package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
  8. package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
  9. package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
  10. package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
  11. package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
  12. package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
  13. package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
  14. package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
  15. package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
  16. package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
  17. package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
  18. package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
  19. package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
  20. package/dist/assets/grammars/tree-sitter.wasm +0 -0
  21. package/dist/{auth-O6HYIJ6J.js → auth-IC3K6NIZ.js} +22 -15
  22. package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
  23. package/dist/chunk-3I7MS7N2.js +40 -0
  24. package/dist/chunk-3R73A4XB.js +69 -0
  25. package/dist/{chunk-26BZQOAD.js → chunk-474KN5II.js} +86 -115
  26. package/dist/{chunk-NI4DEJMC.js → chunk-4QKXUHSR.js} +26 -3
  27. package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
  28. package/dist/{chunk-TFSSEXL6.js → chunk-673JJUWJ.js} +14 -4
  29. package/dist/{chunk-GNGDQYDU.js → chunk-7SS2CTV2.js} +27922 -1249
  30. package/dist/{chunk-7RBKDI66.js → chunk-ASND7OZK.js} +104 -12
  31. package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
  32. package/dist/{chunk-RQDWMVRB.js → chunk-BSU2YIWB.js} +9 -3
  33. package/dist/{chunk-CLJ5HLUD.js → chunk-C4PTHK7P.js} +24 -5
  34. package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
  35. package/dist/{chunk-BYEU76JP.js → chunk-DKGKUHFA.js} +34 -9
  36. package/dist/chunk-EYOKLTMF.js +170 -0
  37. package/dist/{chunk-PS4PFJQP.js → chunk-FCIH3BIZ.js} +15258 -14130
  38. package/dist/{chunk-2J63S4SF.js → chunk-FST4FYJB.js} +82 -14
  39. package/dist/{chunk-3DANZDGR.js → chunk-G34LV2PF.js} +34 -9
  40. package/dist/{chunk-BBUH4VAA.js → chunk-GAEBEQVI.js} +34 -21
  41. package/dist/chunk-GB6QRBXN.js +8543 -0
  42. package/dist/chunk-GEP36Y4X.js +12796 -0
  43. package/dist/{chunk-TKHQ4DGZ.js → chunk-H6F6BYOH.js} +496 -121
  44. package/dist/{chunk-UGYAX5YI.js → chunk-HQQID6OA.js} +10 -5
  45. package/dist/chunk-IHXBNWMM.js +137 -0
  46. package/dist/{chunk-ME6DNWIU.js → chunk-IS3ONKU3.js} +7 -1
  47. package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
  48. package/dist/chunk-K2ITRMHZ.js +642 -0
  49. package/dist/{chunk-KBR4MZZR.js → chunk-K5XEMXTI.js} +7 -1
  50. package/dist/{chunk-AMFR5YA3.js → chunk-KKNLWXI6.js} +24 -10
  51. package/dist/{chunk-JWHCJDCI.js → chunk-KY56HMHH.js} +145 -61
  52. package/dist/{chunk-E3NYWENM.js → chunk-LU4TK2PR.js} +52 -7
  53. package/dist/{chunk-UWIJNAOB.js → chunk-M5T5VO65.js} +109 -42
  54. package/dist/{chunk-JPOWPFCU.js → chunk-MAR7Y6HW.js} +38 -16
  55. package/dist/chunk-N6F52NLF.js +6951 -0
  56. package/dist/{chunk-UHTSULZS.js → chunk-NMOX6HFD.js} +20 -7
  57. package/dist/{chunk-WD6AJM35.js → chunk-OLBBMFRD.js} +30 -13
  58. package/dist/{chunk-VOO7NYPP.js → chunk-PFEFKVGL.js} +22 -6
  59. package/dist/{chunk-6CWDTGUC.js → chunk-PIUMUEMV.js} +7 -1
  60. package/dist/chunk-PV4JUBVJ.js +462 -0
  61. package/dist/{chunk-527KG6XR.js → chunk-Q3RUPKEJ.js} +22 -4
  62. package/dist/{chunk-D5YD55AR.js → chunk-Q5WJOSJ7.js} +11 -1
  63. package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
  64. package/dist/{chunk-4UQA7NCT.js → chunk-R5KLMSBV.js} +7 -2
  65. package/dist/{chunk-PIDUD6M2.js → chunk-RDLVBZEO.js} +11 -6
  66. package/dist/{chunk-O4EJEDHO.js → chunk-RPTR2H26.js} +40 -31
  67. package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
  68. package/dist/{chunk-XNISANGE.js → chunk-TEKV33Q5.js} +122 -57
  69. package/dist/chunk-TXOTCRLG.js +15 -0
  70. package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
  71. package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
  72. package/dist/{chunk-UU3R62TT.js → chunk-XYWBQRDM.js} +13 -4
  73. package/dist/{chunk-7BHLZB3A.js → chunk-ZPY3JZ5E.js} +32 -10
  74. package/dist/cli/index.js +39 -33
  75. package/dist/clio-BZVGEUFJ.js +58 -0
  76. package/dist/{components-JVHMUBEB.js → components-DMAOEKFB.js} +26 -9
  77. package/dist/{config-ZFCDBMDC.js → config-IRUQ7SE4.js} +42 -31
  78. package/dist/configure-S7S6F6CL.js +32 -0
  79. package/dist/{context-CDXTP2MP.js → context-3KWFLHJG.js} +17 -11
  80. package/dist/{context-E3KIFVXI.js → context-5RADCKTR.js} +33 -28
  81. package/dist/{context-clear-3F4PLXOS.js → context-clear-7TSNPAAI.js} +36 -28
  82. package/dist/{context-index-Q7YSYTR3.js → context-index-W4RLWOQH.js} +10 -4
  83. package/dist/{docs-YIETIWZI.js → docs-5AWSPS37.js} +16 -10
  84. package/dist/doctor-UC5NAJYQ.js +77 -0
  85. package/dist/{eval-B3KZZESM.js → eval-U6TJHRLX.js} +77 -24
  86. package/dist/{evidence-V67CHM35.js → evidence-YEGUW4L3.js} +34 -26
  87. package/dist/{evolve-YDZSUQYA.js → evolve-TXARCTPG.js} +39 -26
  88. package/dist/{extensions-SRG7XCAH.js → extensions-OZFJ3A3G.js} +13 -7
  89. package/dist/{fleet-CA2CRTVG.js → fleet-6G3DHNYE.js} +45 -37
  90. package/dist/fleet-preflight-DSNT37JK.js +23 -0
  91. package/dist/{init-2OZDJE2D.js → init-KZ5QTF6M.js} +42 -34
  92. package/dist/{memory-3PIQQAKX.js → memory-73ESV5YC.js} +35 -27
  93. package/dist/{models-DY35XI7Y.js → models-A4PVNWJK.js} +34 -23
  94. package/dist/{paths-5OMXW7Z4.js → paths-C4H6IV77.js} +12 -6
  95. package/dist/preload-6WVMHX3A.js +13 -0
  96. package/dist/{reset-PIFYNOS3.js → reset-BGW6OGMV.js} +17 -11
  97. package/dist/{run-3VSPP24F.js → run-YTPEYQOH.js} +60 -48
  98. package/dist/{share-D36RQCXM.js → share-YIFFV4NQ.js} +16 -10
  99. package/dist/{skills-F2MRLELY.js → skills-2V6RA3OQ.js} +18 -11
  100. package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-S2TVJO4F.js} +45 -36
  101. package/dist/{targets-DZMEZAH4.js → targets-TYXLPB23.js} +61 -59
  102. package/dist/{trace-7NYCUI2J.js → trace-GGOJ6Q6Z.js} +9 -3
  103. package/dist/{uninstall-AD3JWHBB.js → uninstall-LLLT4F4W.js} +14 -8
  104. package/dist/{upgrade-WYYBKGDY.js → upgrade-33G2LMM5.js} +45 -23
  105. package/dist/{usage-ULIDAGFF.js → usage-ZAFSXKKG.js} +35 -27
  106. package/dist/version-KGEG7EPO.js +21 -0
  107. package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-NUQCVOQ3.js} +55 -45
  108. package/dist/worker/entry.js +101 -43
  109. package/docs/README.md +12 -9
  110. package/docs/acp.md +28 -5
  111. package/docs/alcf-provider.md +1 -1
  112. package/docs/architecture.md +23 -2
  113. package/docs/artifact-placement.md +101 -0
  114. package/docs/artifact-versions.md +1 -1
  115. package/docs/built-in-agents.md +4 -2
  116. package/docs/capacity-and-scheduling.md +1 -1
  117. package/docs/commands-and-modes.md +125 -41
  118. package/docs/configuration-and-targets.md +88 -18
  119. package/docs/context-engine.md +6 -2
  120. package/docs/development-pipeline.md +77 -0
  121. package/docs/documentation-coverage.md +4 -4
  122. package/docs/documentation-guide.md +3 -3
  123. package/docs/environment-variables.md +2 -1
  124. package/docs/eval-runner.md +3 -3
  125. package/docs/evals-internal.md +2 -2
  126. package/docs/evidence-and-memory.md +6 -2
  127. package/docs/evolution.md +1 -1
  128. package/docs/exit-codes-and-output.md +1 -1
  129. package/docs/extensions-and-sharing.md +18 -11
  130. package/docs/fleet-demo-runbook.md +22 -23
  131. package/docs/fleet-dispatch.md +111 -12
  132. package/docs/glossary.md +107 -15
  133. package/docs/html/agents_blueprint.html +3 -3
  134. package/docs/html/alcf_blueprint.html +3 -3
  135. package/docs/html/architecture_blueprint.html +3 -3
  136. package/docs/html/commands_blueprint.html +187 -42
  137. package/docs/html/configuration_blueprint.html +7 -7
  138. package/docs/html/context_blueprint.html +3 -3
  139. package/docs/html/documentation_blueprint.html +1 -1
  140. package/docs/html/environment_blueprint.html +3 -3
  141. package/docs/html/eval_blueprint.html +4 -4
  142. package/docs/html/evals_internal_blueprint.html +3 -3
  143. package/docs/html/evolution_blueprint.html +3 -3
  144. package/docs/html/extensions_blueprint.html +3 -3
  145. package/docs/html/fleet_dispatch_blueprint.html +8 -8
  146. package/docs/html/index.html +5 -5
  147. package/docs/html/lifecycle_blueprint.html +4 -4
  148. package/docs/html/memory_blueprint.html +3 -3
  149. package/docs/html/middleware_blueprint.html +3 -3
  150. package/docs/html/models_blueprint.html +2 -2
  151. package/docs/html/observability_blueprint.html +2 -2
  152. package/docs/html/provider_adapter_blueprint.html +3 -3
  153. package/docs/html/safety_blueprint.html +3 -3
  154. package/docs/html/skills_blueprint.html +2 -2
  155. package/docs/html/soak_blueprint.html +3 -3
  156. package/docs/html/tool_usage_blueprint.html +4 -4
  157. package/docs/html/tools_blueprint.html +2 -2
  158. package/docs/html/trace_blueprint.html +3 -3
  159. package/docs/html/tui_design_blueprint.html +100 -40
  160. package/docs/html/validation_blueprint.html +1 -1
  161. package/docs/html/worker_dispatch_blueprint.html +3 -3
  162. package/docs/installation-and-lifecycle.md +62 -11
  163. package/docs/middleware-and-components.md +1 -1
  164. package/docs/model-catalog.md +2 -2
  165. package/docs/observability.md +3 -3
  166. package/docs/proactive-memory.md +12 -12
  167. package/docs/prompt-envelope-and-tools.md +2 -2
  168. package/docs/provider-adapter-cookbook.md +1 -1
  169. package/docs/release-cut-checklist.md +4 -0
  170. package/docs/safety-model.md +21 -5
  171. package/docs/scientific-validation.md +3 -3
  172. package/docs/session-lifecycle.md +2 -2
  173. package/docs/skills-marketplace.md +1 -1
  174. package/docs/time-conventions.md +101 -0
  175. package/docs/tool-usage.md +5 -4
  176. package/docs/trace-store.md +1 -1
  177. package/docs/troubleshooting.md +1 -1
  178. package/docs/tui-design.md +83 -27
  179. package/docs/worker-dispatch-mechanics.md +3 -3
  180. package/package.json +16 -16
  181. package/skills/README.md +5 -6
  182. package/skills/git/file-ticket/SKILL.md +107 -0
  183. package/skills/git/file-ticket/evals.md +54 -0
  184. package/skills/git/fix-issue/SKILL.md +106 -0
  185. package/skills/git/fix-issue/evals.md +71 -0
  186. package/skills/git/ship/SKILL.md +92 -0
  187. package/skills/git/ship/evals.md +72 -0
  188. package/skills/skill-marketplace.json +12 -20
  189. package/src/cli/acp.ts +88 -0
  190. package/src/cli/agents.ts +60 -0
  191. package/src/cli/args.ts +280 -0
  192. package/src/cli/argv.ts +170 -0
  193. package/src/cli/ask.ts +34 -0
  194. package/src/cli/auth.ts +468 -0
  195. package/src/cli/bootstrap-generate.ts +498 -0
  196. package/src/cli/clio.ts +34 -0
  197. package/src/cli/components.ts +195 -0
  198. package/src/cli/config-inspect.ts +330 -0
  199. package/src/cli/config.ts +72 -0
  200. package/src/cli/configure-interop.ts +67 -0
  201. package/src/cli/configure-layout.ts +172 -0
  202. package/src/cli/configure.ts +1487 -0
  203. package/src/cli/context-clear.ts +76 -0
  204. package/src/cli/context-index.ts +102 -0
  205. package/src/cli/context.ts +316 -0
  206. package/src/cli/default-target.ts +63 -0
  207. package/src/cli/docs.ts +340 -0
  208. package/src/cli/doctor.ts +54 -0
  209. package/src/cli/eval.ts +315 -0
  210. package/src/cli/evidence.ts +222 -0
  211. package/src/cli/evolve.ts +219 -0
  212. package/src/cli/extensions.ts +205 -0
  213. package/src/cli/fleet.ts +827 -0
  214. package/src/cli/index.ts +309 -0
  215. package/src/cli/init.ts +219 -0
  216. package/src/cli/initial-message.ts +47 -0
  217. package/src/cli/internal-dispatch.ts +55 -0
  218. package/src/cli/memory.ts +180 -0
  219. package/src/cli/models.ts +257 -0
  220. package/src/cli/modes/json-stream.ts +85 -0
  221. package/src/cli/modes/jsonl.ts +9 -0
  222. package/src/cli/modes/print.ts +589 -0
  223. package/src/cli/oauth-manual-input.ts +72 -0
  224. package/src/cli/oauth-select.ts +36 -0
  225. package/src/cli/output-guard.ts +48 -0
  226. package/src/cli/paths.ts +44 -0
  227. package/src/cli/provider-target.ts +113 -0
  228. package/src/cli/removal.ts +72 -0
  229. package/src/cli/reset.ts +253 -0
  230. package/src/cli/run.ts +497 -0
  231. package/src/cli/share.ts +243 -0
  232. package/src/cli/shared.ts +60 -0
  233. package/src/cli/skills-eval.ts +1225 -0
  234. package/src/cli/skills.ts +470 -0
  235. package/src/cli/steer-channel.ts +86 -0
  236. package/src/cli/targets.ts +1150 -0
  237. package/src/cli/text-layout.ts +102 -0
  238. package/src/cli/trace.ts +274 -0
  239. package/src/cli/uninstall.ts +405 -0
  240. package/src/cli/upgrade.ts +270 -0
  241. package/src/cli/usage.ts +869 -0
  242. package/src/cli/validate-model.ts +28 -0
  243. package/src/cli/version.ts +7 -0
  244. package/src/cli/wiki-generate.ts +509 -0
  245. package/src/core/artifact-paths.ts +43 -0
  246. package/src/core/bash-exec.ts +242 -0
  247. package/src/core/boot-trace.ts +37 -0
  248. package/src/core/bus-events.ts +728 -0
  249. package/src/core/bus-trace.ts +53 -0
  250. package/src/core/c-header-language.ts +265 -0
  251. package/src/core/clio-repo.ts +78 -0
  252. package/src/core/config.ts +1593 -0
  253. package/src/core/context-floor.ts +33 -0
  254. package/src/core/defaults.ts +625 -0
  255. package/src/core/domain-loader.ts +170 -0
  256. package/src/core/event-bus.ts +94 -0
  257. package/src/core/file-references.ts +282 -0
  258. package/src/core/guardrails.ts +185 -0
  259. package/src/core/headless-permission.ts +21 -0
  260. package/src/core/init.ts +223 -0
  261. package/src/core/json-payload.ts +58 -0
  262. package/src/core/package-root.ts +54 -0
  263. package/src/core/path-canonical.ts +37 -0
  264. package/src/core/process-identity.ts +41 -0
  265. package/src/core/recent-models.ts +91 -0
  266. package/src/core/release.ts +2 -0
  267. package/src/core/residency-protection.ts +56 -0
  268. package/src/core/resolve-config-value.ts +88 -0
  269. package/src/core/response-schema.ts +279 -0
  270. package/src/core/run-overrides.ts +77 -0
  271. package/src/core/safe-exec.ts +193 -0
  272. package/src/core/safe-id.ts +37 -0
  273. package/src/core/safe-resource-write.ts +162 -0
  274. package/src/core/session-routing.ts +339 -0
  275. package/src/core/settings-layers.ts +220 -0
  276. package/src/core/shared-bus.ts +8 -0
  277. package/src/core/shell-quote.ts +26 -0
  278. package/src/core/skill-activation.ts +190 -0
  279. package/src/core/startup-timer.ts +37 -0
  280. package/src/core/state-file-lock.ts +207 -0
  281. package/src/core/termination.ts +202 -0
  282. package/src/core/timers.ts +38 -0
  283. package/src/core/tool-names.ts +65 -0
  284. package/src/core/verification-scripts.ts +13 -0
  285. package/src/core/workspace-files.ts +492 -0
  286. package/src/core/xdg.ts +177 -0
  287. package/src/domains/agents/builtins/architect.md +4 -4
  288. package/src/domains/agents/builtins/coder.md +3 -2
  289. package/src/domains/agents/builtins/debugger.md +1 -1
  290. package/src/domains/agents/builtins/documenter.md +1 -1
  291. package/src/domains/agents/builtins/git-master.md +3 -3
  292. package/src/domains/agents/builtins/provenance.md +1 -1
  293. package/src/domains/agents/builtins/researcher.md +1 -1
  294. package/src/domains/agents/builtins/scout.md +1 -1
  295. package/src/domains/agents/builtins/tester.md +1 -1
  296. package/src/domains/agents/builtins/verifier.md +1 -1
  297. package/src/domains/agents/builtins/wiki-writer.md +1 -1
  298. package/src/domains/agents/catalog.ts +172 -0
  299. package/src/domains/agents/contract.ts +16 -0
  300. package/src/domains/agents/extension.ts +110 -0
  301. package/src/domains/agents/fleet-commands.ts +154 -0
  302. package/src/domains/agents/fleet-contract.ts +797 -0
  303. package/src/domains/agents/frontmatter.ts +40 -0
  304. package/src/domains/agents/index.ts +102 -0
  305. package/src/domains/agents/manifest.ts +6 -0
  306. package/src/domains/agents/recipe-schema.ts +202 -0
  307. package/src/domains/agents/recipe.ts +125 -0
  308. package/src/domains/agents/registry.ts +111 -0
  309. package/src/domains/agents/result-contract.ts +1265 -0
  310. package/src/domains/agents/spec.ts +371 -0
  311. package/src/domains/agents/write-boundary.ts +109 -0
  312. package/src/domains/components/diff.ts +99 -0
  313. package/src/domains/components/hash.ts +21 -0
  314. package/src/domains/components/index.ts +3 -0
  315. package/src/domains/components/scan.ts +304 -0
  316. package/src/domains/components/snapshot.ts +108 -0
  317. package/src/domains/components/types.ts +82 -0
  318. package/src/domains/config/agent-namespace.ts +13 -0
  319. package/src/domains/config/classify.ts +94 -0
  320. package/src/domains/config/contract.ts +22 -0
  321. package/src/domains/config/extension.ts +136 -0
  322. package/src/domains/config/index.ts +13 -0
  323. package/src/domains/config/keybindings.ts +164 -0
  324. package/src/domains/config/manifest.ts +6 -0
  325. package/src/domains/config/watcher.ts +41 -0
  326. package/src/domains/context/adoption.ts +880 -0
  327. package/src/domains/context/bootstrap-prompt.ts +273 -0
  328. package/src/domains/context/bootstrap.ts +1409 -0
  329. package/src/domains/context/clear.ts +86 -0
  330. package/src/domains/context/clio-md.ts +327 -0
  331. package/src/domains/context/codewiki/cmake.ts +356 -0
  332. package/src/domains/context/codewiki/cooperative.ts +46 -0
  333. package/src/domains/context/codewiki/digest.ts +135 -0
  334. package/src/domains/context/codewiki/grammar-assets.ts +33 -0
  335. package/src/domains/context/codewiki/indexer.ts +1614 -0
  336. package/src/domains/context/codewiki/tree-sitter.ts +771 -0
  337. package/src/domains/context/contract.ts +62 -0
  338. package/src/domains/context/excluded-dirs.ts +1 -0
  339. package/src/domains/context/extension.ts +407 -0
  340. package/src/domains/context/fingerprint.ts +196 -0
  341. package/src/domains/context/index.ts +164 -0
  342. package/src/domains/context/init-options.ts +69 -0
  343. package/src/domains/context/manifest.ts +6 -0
  344. package/src/domains/context/operator-profile.ts +157 -0
  345. package/src/domains/context/project-metadata.ts +510 -0
  346. package/src/domains/context/project-rules.ts +227 -0
  347. package/src/domains/context/prompt-context.ts +62 -0
  348. package/src/domains/context/refresh.ts +170 -0
  349. package/src/domains/context/sibling-files.ts +10 -0
  350. package/src/domains/context/state.ts +237 -0
  351. package/src/domains/context/wiki/assemble.ts +303 -0
  352. package/src/domains/context/wiki/frontmatter.ts +210 -0
  353. package/src/domains/context/wiki/generate.ts +590 -0
  354. package/src/domains/context/wiki/layout.ts +111 -0
  355. package/src/domains/context/wiki/meta.ts +221 -0
  356. package/src/domains/context/wiki/plan-store.ts +217 -0
  357. package/src/domains/context/wiki/plan.ts +250 -0
  358. package/src/domains/context/wiki/prompts.ts +270 -0
  359. package/src/domains/context/wiki/staleness.ts +198 -0
  360. package/src/domains/dispatch/active-route-planner.ts +168 -0
  361. package/src/domains/dispatch/admission-queue.ts +150 -0
  362. package/src/domains/dispatch/admission.ts +334 -0
  363. package/src/domains/dispatch/agent-candidates.ts +307 -0
  364. package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
  365. package/src/domains/dispatch/agent-ledger-store.ts +257 -0
  366. package/src/domains/dispatch/agent-ledger.ts +212 -0
  367. package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
  368. package/src/domains/dispatch/assignment-events.ts +132 -0
  369. package/src/domains/dispatch/assignment-reconcile.ts +76 -0
  370. package/src/domains/dispatch/assignment-store.ts +162 -0
  371. package/src/domains/dispatch/assignment.ts +278 -0
  372. package/src/domains/dispatch/backoff.ts +59 -0
  373. package/src/domains/dispatch/batch-store.ts +134 -0
  374. package/src/domains/dispatch/batch-tracker.ts +66 -0
  375. package/src/domains/dispatch/capability-match.ts +155 -0
  376. package/src/domains/dispatch/capacity-lease.ts +413 -0
  377. package/src/domains/dispatch/code-step-store.ts +52 -0
  378. package/src/domains/dispatch/code-step.ts +368 -0
  379. package/src/domains/dispatch/contract.ts +278 -0
  380. package/src/domains/dispatch/dedup.ts +133 -0
  381. package/src/domains/dispatch/event-pump.ts +251 -0
  382. package/src/domains/dispatch/execution-handoff.ts +29 -0
  383. package/src/domains/dispatch/execution-plan.ts +328 -0
  384. package/src/domains/dispatch/execution-role.ts +277 -0
  385. package/src/domains/dispatch/execution-scheduler.ts +583 -0
  386. package/src/domains/dispatch/extension.ts +5869 -0
  387. package/src/domains/dispatch/failure-classification.ts +141 -0
  388. package/src/domains/dispatch/fleet-plan.ts +225 -0
  389. package/src/domains/dispatch/fleet-preflight.ts +394 -0
  390. package/src/domains/dispatch/gate-decisions.ts +1040 -0
  391. package/src/domains/dispatch/gate-role-prompts.ts +58 -0
  392. package/src/domains/dispatch/heartbeat.ts +27 -0
  393. package/src/domains/dispatch/index.ts +137 -0
  394. package/src/domains/dispatch/joint-route-adapter.ts +114 -0
  395. package/src/domains/dispatch/joint-route-resolver.ts +283 -0
  396. package/src/domains/dispatch/manifest.ts +6 -0
  397. package/src/domains/dispatch/orphan-recovery.ts +224 -0
  398. package/src/domains/dispatch/outcome.ts +109 -0
  399. package/src/domains/dispatch/phase-timing.ts +47 -0
  400. package/src/domains/dispatch/placement.ts +202 -0
  401. package/src/domains/dispatch/receipt-findings.ts +255 -0
  402. package/src/domains/dispatch/receipt-integrity.ts +378 -0
  403. package/src/domains/dispatch/recovery-candidates.ts +74 -0
  404. package/src/domains/dispatch/reproducibility.ts +48 -0
  405. package/src/domains/dispatch/reservation-store.ts +501 -0
  406. package/src/domains/dispatch/route-approval.ts +177 -0
  407. package/src/domains/dispatch/route-candidates.ts +57 -0
  408. package/src/domains/dispatch/route-decision.ts +1079 -0
  409. package/src/domains/dispatch/route-facts.ts +187 -0
  410. package/src/domains/dispatch/route-history.ts +238 -0
  411. package/src/domains/dispatch/route-observer.ts +488 -0
  412. package/src/domains/dispatch/route-policy.ts +327 -0
  413. package/src/domains/dispatch/route-quality.ts +256 -0
  414. package/src/domains/dispatch/route-readiness.ts +120 -0
  415. package/src/domains/dispatch/routing-intent.ts +287 -0
  416. package/src/domains/dispatch/run-identity.ts +56 -0
  417. package/src/domains/dispatch/scout-transition.ts +130 -0
  418. package/src/domains/dispatch/state.ts +277 -0
  419. package/src/domains/dispatch/tool-stats.ts +132 -0
  420. package/src/domains/dispatch/transport.ts +209 -0
  421. package/src/domains/dispatch/types.ts +785 -0
  422. package/src/domains/dispatch/validation-grounding.ts +154 -0
  423. package/src/domains/dispatch/validation.ts +721 -0
  424. package/src/domains/dispatch/worker-protocol.ts +275 -0
  425. package/src/domains/dispatch/worker-spawn.ts +566 -0
  426. package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
  427. package/src/domains/dispatch/write-boundary.ts +426 -0
  428. package/src/domains/eval/artifacts/redact.ts +42 -0
  429. package/src/domains/eval/artifacts/store.ts +173 -0
  430. package/src/domains/eval/compare/compare.ts +33 -0
  431. package/src/domains/eval/compare/gates.ts +66 -0
  432. package/src/domains/eval/compare/thresholds.ts +92 -0
  433. package/src/domains/eval/harness-metrics.ts +62 -0
  434. package/src/domains/eval/index.ts +66 -0
  435. package/src/domains/eval/metrics/chaos-stream.ts +93 -0
  436. package/src/domains/eval/metrics/context.ts +34 -0
  437. package/src/domains/eval/metrics/coverage.ts +21 -0
  438. package/src/domains/eval/metrics/evidence.ts +182 -0
  439. package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
  440. package/src/domains/eval/metrics/invariants.ts +736 -0
  441. package/src/domains/eval/metrics/latency.ts +4 -0
  442. package/src/domains/eval/metrics/token-stream.ts +139 -0
  443. package/src/domains/eval/metrics/tokens.ts +51 -0
  444. package/src/domains/eval/metrics/tool-calls.ts +7 -0
  445. package/src/domains/eval/proactive-memory.ts +279 -0
  446. package/src/domains/eval/provenance.ts +35 -0
  447. package/src/domains/eval/report.ts +234 -0
  448. package/src/domains/eval/reports/json.ts +5 -0
  449. package/src/domains/eval/reports/junit.ts +23 -0
  450. package/src/domains/eval/reports/markdown.ts +20 -0
  451. package/src/domains/eval/reports/swe-jsonl.ts +16 -0
  452. package/src/domains/eval/reports/text.ts +25 -0
  453. package/src/domains/eval/run-compare.ts +274 -0
  454. package/src/domains/eval/runner.ts +244 -0
  455. package/src/domains/eval/runners/clio-run.ts +148 -0
  456. package/src/domains/eval/runners/context-index.ts +56 -0
  457. package/src/domains/eval/runners/context-init.ts +203 -0
  458. package/src/domains/eval/runners/external-command.ts +350 -0
  459. package/src/domains/eval/runtime-paths.ts +171 -0
  460. package/src/domains/eval/schema/artifact.ts +75 -0
  461. package/src/domains/eval/schema/suite.ts +235 -0
  462. package/src/domains/eval/schema/validate.ts +339 -0
  463. package/src/domains/eval/store.ts +392 -0
  464. package/src/domains/eval/suites/load.ts +74 -0
  465. package/src/domains/eval/suites/matrix.ts +19 -0
  466. package/src/domains/eval/suites/resolve.ts +44 -0
  467. package/src/domains/eval/suites/run.ts +325 -0
  468. package/src/domains/eval/task-file.ts +186 -0
  469. package/src/domains/eval/types.ts +141 -0
  470. package/src/domains/eval/verifiers/command.ts +27 -0
  471. package/src/domains/eval/verifiers/file-exists.ts +6 -0
  472. package/src/domains/eval/verifiers/patch.ts +22 -0
  473. package/src/domains/eval/workspaces/git.ts +41 -0
  474. package/src/domains/eval/workspaces/local.ts +17 -0
  475. package/src/domains/eval/workspaces/temp-copy.ts +29 -0
  476. package/src/domains/evidence/build.ts +1714 -0
  477. package/src/domains/evidence/eval.ts +605 -0
  478. package/src/domains/evidence/failure-attribution.ts +54 -0
  479. package/src/domains/evidence/finish-contract-map.ts +42 -0
  480. package/src/domains/evidence/index.ts +55 -0
  481. package/src/domains/evidence/provenance.ts +200 -0
  482. package/src/domains/evidence/redact.ts +81 -0
  483. package/src/domains/evidence/store.ts +291 -0
  484. package/src/domains/evidence/types.ts +325 -0
  485. package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
  486. package/src/domains/evolution/index.ts +26 -0
  487. package/src/domains/evolution/manifest.ts +154 -0
  488. package/src/domains/evolution/validate.ts +304 -0
  489. package/src/domains/extensions/contract.ts +22 -0
  490. package/src/domains/extensions/discovery.ts +193 -0
  491. package/src/domains/extensions/extension.ts +46 -0
  492. package/src/domains/extensions/index.ts +34 -0
  493. package/src/domains/extensions/manager.ts +30 -0
  494. package/src/domains/extensions/manifest.ts +6 -0
  495. package/src/domains/extensions/resources.ts +38 -0
  496. package/src/domains/extensions/state.ts +224 -0
  497. package/src/domains/extensions/types.ts +83 -0
  498. package/src/domains/interop/consent.ts +185 -0
  499. package/src/domains/interop/contract.ts +29 -0
  500. package/src/domains/interop/detect.ts +173 -0
  501. package/src/domains/interop/extension.ts +59 -0
  502. package/src/domains/interop/index.ts +34 -0
  503. package/src/domains/interop/manifest.ts +6 -0
  504. package/src/domains/interop/registry.ts +139 -0
  505. package/src/domains/interop/state.ts +92 -0
  506. package/src/domains/interop/types.ts +109 -0
  507. package/src/domains/lifecycle/contract.ts +11 -0
  508. package/src/domains/lifecycle/doctor.ts +536 -0
  509. package/src/domains/lifecycle/extension.ts +23 -0
  510. package/src/domains/lifecycle/index.ts +21 -0
  511. package/src/domains/lifecycle/install-method.ts +51 -0
  512. package/src/domains/lifecycle/manifest.ts +6 -0
  513. package/src/domains/lifecycle/migrations/index.ts +94 -0
  514. package/src/domains/lifecycle/state.ts +93 -0
  515. package/src/domains/lifecycle/upgrade-notice.ts +35 -0
  516. package/src/domains/lifecycle/version.ts +35 -0
  517. package/src/domains/memory/index.ts +128 -0
  518. package/src/domains/memory/operations.ts +143 -0
  519. package/src/domains/memory/prompt-section.ts +86 -0
  520. package/src/domains/memory/proposal.ts +180 -0
  521. package/src/domains/memory/store.ts +163 -0
  522. package/src/domains/memory/task-bank.ts +229 -0
  523. package/src/domains/memory/task-memory-handoff.ts +222 -0
  524. package/src/domains/memory/task-memory-policy.ts +602 -0
  525. package/src/domains/memory/task-memory-status.ts +58 -0
  526. package/src/domains/memory/task-memory-telemetry.ts +346 -0
  527. package/src/domains/memory/task-memory-trace.ts +89 -0
  528. package/src/domains/memory/types.ts +74 -0
  529. package/src/domains/memory/validate.ts +297 -0
  530. package/src/domains/middleware/budget.ts +216 -0
  531. package/src/domains/middleware/contract.ts +35 -0
  532. package/src/domains/middleware/dispatch-nudge.ts +469 -0
  533. package/src/domains/middleware/extension.ts +129 -0
  534. package/src/domains/middleware/hook-receipts.ts +70 -0
  535. package/src/domains/middleware/hooks-io.ts +174 -0
  536. package/src/domains/middleware/hooks.ts +432 -0
  537. package/src/domains/middleware/index.ts +154 -0
  538. package/src/domains/middleware/manifest.ts +6 -0
  539. package/src/domains/middleware/memory-intervention.ts +701 -0
  540. package/src/domains/middleware/rules.ts +42 -0
  541. package/src/domains/middleware/runtime.ts +333 -0
  542. package/src/domains/middleware/skills-reminder.ts +187 -0
  543. package/src/domains/middleware/snapshot.ts +71 -0
  544. package/src/domains/middleware/stalled-turn.ts +71 -0
  545. package/src/domains/middleware/task-board-reminder.ts +87 -0
  546. package/src/domains/middleware/task-nudge.ts +77 -0
  547. package/src/domains/middleware/tool-choice-control.ts +42 -0
  548. package/src/domains/middleware/types.ts +131 -0
  549. package/src/domains/middleware/validate.ts +329 -0
  550. package/src/domains/observability/accountability.ts +67 -0
  551. package/src/domains/observability/contract.ts +168 -0
  552. package/src/domains/observability/cost.ts +206 -0
  553. package/src/domains/observability/evidence-index.ts +123 -0
  554. package/src/domains/observability/extension.ts +281 -0
  555. package/src/domains/observability/index.ts +78 -0
  556. package/src/domains/observability/manifest.ts +10 -0
  557. package/src/domains/observability/metrics.ts +37 -0
  558. package/src/domains/observability/projection.ts +477 -0
  559. package/src/domains/observability/telemetry.ts +89 -0
  560. package/src/domains/observability/trace-store.ts +1454 -0
  561. package/src/domains/prompts/compiler.ts +556 -0
  562. package/src/domains/prompts/contract.ts +30 -0
  563. package/src/domains/prompts/extension.ts +304 -0
  564. package/src/domains/prompts/fragment-loader.ts +139 -0
  565. package/src/domains/prompts/fragments/identity/clio.md +3 -2
  566. package/src/domains/prompts/fragments/identity/self-awareness.md +63 -0
  567. package/src/domains/prompts/fragments/operating/contract.md +10 -1
  568. package/src/domains/prompts/hash.ts +73 -0
  569. package/src/domains/prompts/index.ts +32 -0
  570. package/src/domains/prompts/manifest.ts +8 -0
  571. package/src/domains/prompts/memory-intervention.ts +57 -0
  572. package/src/domains/prompts/preload.ts +82 -0
  573. package/src/domains/providers/auth/api-key.ts +47 -0
  574. package/src/domains/providers/auth/backend-file.ts +69 -0
  575. package/src/domains/providers/auth/backend-memory.ts +17 -0
  576. package/src/domains/providers/auth/index.ts +37 -0
  577. package/src/domains/providers/auth/oauth.ts +36 -0
  578. package/src/domains/providers/auth/storage.ts +667 -0
  579. package/src/domains/providers/capabilities.ts +50 -0
  580. package/src/domains/providers/catalog.ts +162 -0
  581. package/src/domains/providers/contract.ts +151 -0
  582. package/src/domains/providers/credentials.ts +20 -0
  583. package/src/domains/providers/eligibility.ts +24 -0
  584. package/src/domains/providers/errors.ts +64 -0
  585. package/src/domains/providers/extension.ts +536 -0
  586. package/src/domains/providers/index.ts +142 -0
  587. package/src/domains/providers/knowledge-base-path.ts +102 -0
  588. package/src/domains/providers/manifest.ts +6 -0
  589. package/src/domains/providers/model-capabilities.ts +136 -0
  590. package/src/domains/providers/model-discovery.ts +144 -0
  591. package/src/domains/providers/model-family.ts +32 -0
  592. package/src/domains/providers/model-runtime-capabilities.ts +712 -0
  593. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +2 -2
  594. package/src/domains/providers/plugins.ts +59 -0
  595. package/src/domains/providers/probe/fingerprint.ts +44 -0
  596. package/src/domains/providers/probe/http.ts +114 -0
  597. package/src/domains/providers/probe/reasoning.ts +111 -0
  598. package/src/domains/providers/registry.ts +166 -0
  599. package/src/domains/providers/resolver.ts +156 -0
  600. package/src/domains/providers/runtime-resolution.ts +720 -0
  601. package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
  602. package/src/domains/providers/runtimes/builtins.ts +78 -0
  603. package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
  604. package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
  605. package/src/domains/providers/runtimes/claude/common.ts +55 -0
  606. package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
  607. package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
  608. package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
  609. package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
  610. package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
  611. package/src/domains/providers/runtimes/cloud/google.ts +41 -0
  612. package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
  613. package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
  614. package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
  615. package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
  616. package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
  617. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +32 -0
  618. package/src/domains/providers/runtimes/common/local-synth.ts +165 -0
  619. package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
  620. package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
  621. package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
  622. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
  623. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
  624. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
  625. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
  626. package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
  627. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +491 -0
  628. package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
  629. package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
  630. package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
  631. package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
  632. package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
  633. package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
  634. package/src/domains/providers/runtimes/protocol/google.ts +27 -0
  635. package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
  636. package/src/domains/providers/support.ts +238 -0
  637. package/src/domains/providers/thinking-control-policy.ts +85 -0
  638. package/src/domains/providers/types/capability-flags.ts +114 -0
  639. package/src/domains/providers/types/cost-provenance.ts +7 -0
  640. package/src/domains/providers/types/inference.ts +41 -0
  641. package/src/domains/providers/types/knowledge-base.ts +156 -0
  642. package/src/domains/providers/types/local-model-quirks.ts +218 -0
  643. package/src/domains/providers/types/runtime-descriptor.ts +170 -0
  644. package/src/domains/providers/types/target-descriptor.ts +38 -0
  645. package/src/domains/resources/collision.ts +92 -0
  646. package/src/domains/resources/common-loader.ts +140 -0
  647. package/src/domains/resources/contract.ts +25 -0
  648. package/src/domains/resources/extension.ts +61 -0
  649. package/src/domains/resources/index.ts +74 -0
  650. package/src/domains/resources/loader.ts +86 -0
  651. package/src/domains/resources/manifest.ts +6 -0
  652. package/src/domains/resources/prompts/loader.ts +232 -0
  653. package/src/domains/resources/prompts/substitute.ts +65 -0
  654. package/src/domains/resources/skills/content-hash.ts +105 -0
  655. package/src/domains/resources/skills/evals.ts +175 -0
  656. package/src/domains/resources/skills/install.ts +410 -0
  657. package/src/domains/resources/skills/loader.ts +978 -0
  658. package/src/domains/resources/skills/marketplace.ts +255 -0
  659. package/src/domains/resources/skills/provenance-pin.ts +115 -0
  660. package/src/domains/safety/action-classifier.ts +268 -0
  661. package/src/domains/safety/approval-axis.ts +23 -0
  662. package/src/domains/safety/audit.ts +487 -0
  663. package/src/domains/safety/autonomy.ts +174 -0
  664. package/src/domains/safety/call-target.ts +59 -0
  665. package/src/domains/safety/contract.ts +65 -0
  666. package/src/domains/safety/damage-control.ts +44 -0
  667. package/src/domains/safety/default-path-policy.ts +117 -0
  668. package/src/domains/safety/extension.ts +383 -0
  669. package/src/domains/safety/finish-contract-registration.ts +128 -0
  670. package/src/domains/safety/finish-contract.ts +554 -0
  671. package/src/domains/safety/index.ts +44 -0
  672. package/src/domains/safety/loop-detector.ts +143 -0
  673. package/src/domains/safety/manifest.ts +6 -0
  674. package/src/domains/safety/path-policy.ts +164 -0
  675. package/src/domains/safety/policy-engine.ts +994 -0
  676. package/src/domains/safety/project-policy.ts +391 -0
  677. package/src/domains/safety/protected-artifacts-registration.ts +189 -0
  678. package/src/domains/safety/protected-artifacts.ts +993 -0
  679. package/src/domains/safety/rejection-feedback.ts +102 -0
  680. package/src/domains/safety/rigor.ts +72 -0
  681. package/src/domains/safety/rule-compiler.ts +48 -0
  682. package/src/domains/safety/rule-pack-loader.ts +92 -0
  683. package/src/domains/safety/run-effects.ts +197 -0
  684. package/src/domains/safety/scope.ts +61 -0
  685. package/src/domains/scheduling/budget.ts +35 -0
  686. package/src/domains/scheduling/cluster.ts +175 -0
  687. package/src/domains/scheduling/contract.ts +30 -0
  688. package/src/domains/scheduling/extension.ts +91 -0
  689. package/src/domains/scheduling/index.ts +12 -0
  690. package/src/domains/scheduling/manifest.ts +6 -0
  691. package/src/domains/session/archive-readers.ts +208 -0
  692. package/src/domains/session/checkpoint.ts +38 -0
  693. package/src/domains/session/compaction/auto.ts +89 -0
  694. package/src/domains/session/compaction/branch-summary.ts +186 -0
  695. package/src/domains/session/compaction/compact.ts +493 -0
  696. package/src/domains/session/compaction/cut-point.ts +171 -0
  697. package/src/domains/session/compaction/defaults.ts +34 -0
  698. package/src/domains/session/compaction/mask-observations.ts +265 -0
  699. package/src/domains/session/compaction/session-entries.ts +18 -0
  700. package/src/domains/session/compaction/tokens.ts +147 -0
  701. package/src/domains/session/context-accounting.ts +583 -0
  702. package/src/domains/session/context-ledger.ts +348 -0
  703. package/src/domains/session/contract.ts +144 -0
  704. package/src/domains/session/cwd-fallback.ts +65 -0
  705. package/src/domains/session/entries.ts +509 -0
  706. package/src/domains/session/extension.ts +322 -0
  707. package/src/domains/session/history.ts +172 -0
  708. package/src/domains/session/index.ts +84 -0
  709. package/src/domains/session/manager.ts +128 -0
  710. package/src/domains/session/manifest.ts +6 -0
  711. package/src/domains/session/migrations/index.ts +27 -0
  712. package/src/domains/session/prompt-manifest.ts +173 -0
  713. package/src/domains/session/protected-artifact-journal.ts +241 -0
  714. package/src/domains/session/protected-artifacts.ts +90 -0
  715. package/src/domains/session/retry.ts +238 -0
  716. package/src/domains/session/task-board.ts +375 -0
  717. package/src/domains/session/tree/active-path.ts +98 -0
  718. package/src/domains/session/tree/fork.ts +160 -0
  719. package/src/domains/session/tree/manager.ts +128 -0
  720. package/src/domains/session/tree/navigator.ts +177 -0
  721. package/src/domains/session/tree/preview.ts +259 -0
  722. package/src/domains/session/usage.ts +148 -0
  723. package/src/domains/session/workspace/git-probe.ts +161 -0
  724. package/src/domains/session/workspace/index.ts +4 -0
  725. package/src/domains/session/workspace/project-type.ts +217 -0
  726. package/src/domains/session/workspace/snapshot.ts +74 -0
  727. package/src/domains/share/archive.ts +684 -0
  728. package/src/domains/share/contract.ts +9 -0
  729. package/src/domains/share/extension.ts +26 -0
  730. package/src/domains/share/index.ts +28 -0
  731. package/src/domains/share/manifest.ts +6 -0
  732. package/src/engine/acp/adapter.ts +397 -0
  733. package/src/engine/acp/errors.ts +32 -0
  734. package/src/engine/acp/event-mapper.ts +154 -0
  735. package/src/engine/acp/server.ts +656 -0
  736. package/src/engine/acp/tool-mediator.ts +686 -0
  737. package/src/engine/acp/transport.ts +654 -0
  738. package/src/engine/acp/types.ts +170 -0
  739. package/src/engine/agent.ts +29 -0
  740. package/src/engine/ai.ts +202 -0
  741. package/src/engine/alcf-oauth.ts +198 -0
  742. package/src/engine/antigravity/subprocess-runtime.ts +205 -0
  743. package/src/engine/apis/degraded-inference.ts +95 -0
  744. package/src/engine/apis/index.ts +20 -0
  745. package/src/engine/apis/llamacpp-residency.ts +252 -0
  746. package/src/engine/apis/lmstudio-native.ts +1438 -0
  747. package/src/engine/apis/lmstudio-residency.ts +92 -0
  748. package/src/engine/apis/ollama-native.ts +507 -0
  749. package/src/engine/apis/openai-completions.ts +716 -0
  750. package/src/engine/apis/output-budget.ts +99 -0
  751. package/src/engine/apis/residency-lock.ts +29 -0
  752. package/src/engine/apis/residency.ts +640 -0
  753. package/src/engine/apis/resident-models.ts +42 -0
  754. package/src/engine/apis/sampling-overrides.ts +38 -0
  755. package/src/engine/apis/thinking-replay.ts +11 -0
  756. package/src/engine/claude/sdk-runtime.ts +685 -0
  757. package/src/engine/claude/session-id.ts +5 -0
  758. package/src/engine/claude/subprocess-runtime.ts +316 -0
  759. package/src/engine/claude/tool-safety.ts +400 -0
  760. package/src/engine/external-subprocess.ts +23 -0
  761. package/src/engine/harmony-response.ts +165 -0
  762. package/src/engine/loop-guard.ts +1127 -0
  763. package/src/engine/oauth.ts +192 -0
  764. package/src/engine/pi-mono-names.ts +13 -0
  765. package/src/engine/provider-payload.ts +315 -0
  766. package/src/engine/select-list.ts +53 -0
  767. package/src/engine/session.ts +834 -0
  768. package/src/engine/strip-tokenizer-sentinels.ts +125 -0
  769. package/src/engine/tui.ts +103 -0
  770. package/src/engine/types.ts +66 -0
  771. package/src/engine/worker-events.ts +100 -0
  772. package/src/engine/worker-runtime.ts +1022 -0
  773. package/src/engine/worker-tools.ts +203 -0
  774. package/src/entry/orchestrator.ts +1873 -0
  775. package/src/interactive/application-controller.ts +346 -0
  776. package/src/interactive/bus-notices.ts +265 -0
  777. package/src/interactive/chat-loop-messages.ts +626 -0
  778. package/src/interactive/chat-loop-policy.ts +18 -0
  779. package/src/interactive/chat-loop.ts +865 -0
  780. package/src/interactive/chat-panel.ts +1694 -0
  781. package/src/interactive/chat-renderer.ts +1038 -0
  782. package/src/interactive/clio-editor.ts +166 -0
  783. package/src/interactive/command-fallbacks.ts +23 -0
  784. package/src/interactive/command-output.ts +85 -0
  785. package/src/interactive/context-activity.ts +203 -0
  786. package/src/interactive/context-meter.ts +223 -0
  787. package/src/interactive/context-overlay.ts +220 -0
  788. package/src/interactive/cost-overlay.ts +279 -0
  789. package/src/interactive/dispatch-board.ts +1319 -0
  790. package/src/interactive/dispatch-steering.ts +86 -0
  791. package/src/interactive/editor-bash.ts +46 -0
  792. package/src/interactive/editor-steer.ts +51 -0
  793. package/src/interactive/editor-submit.ts +303 -0
  794. package/src/interactive/external-editor.ts +67 -0
  795. package/src/interactive/follow-up-queue-panel.ts +75 -0
  796. package/src/interactive/footer/dashboard.ts +534 -0
  797. package/src/interactive/footer/notifications.ts +275 -0
  798. package/src/interactive/footer/widgets.ts +1246 -0
  799. package/src/interactive/footer-panel.ts +187 -0
  800. package/src/interactive/format-time.ts +85 -0
  801. package/src/interactive/index.ts +11 -0
  802. package/src/interactive/interactive-application.ts +755 -0
  803. package/src/interactive/interactive-event-projection.ts +380 -0
  804. package/src/interactive/interactive-input-runtime.ts +261 -0
  805. package/src/interactive/interactive-presentation.ts +452 -0
  806. package/src/interactive/interactive-shell.ts +134 -0
  807. package/src/interactive/interactive-slash-runtime.ts +492 -0
  808. package/src/interactive/interactive-subscriptions.ts +111 -0
  809. package/src/interactive/interactive-tickers.ts +152 -0
  810. package/src/interactive/keybinding-manager.ts +496 -0
  811. package/src/interactive/layout.ts +20 -0
  812. package/src/interactive/leader-key.ts +143 -0
  813. package/src/interactive/loop-guard-interrupt.ts +101 -0
  814. package/src/interactive/memory-overlay.ts +280 -0
  815. package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
  816. package/src/interactive/overlay-auth-lifecycle.ts +308 -0
  817. package/src/interactive/overlay-frame.ts +502 -0
  818. package/src/interactive/overlay-general-openers.ts +217 -0
  819. package/src/interactive/overlay-key-routing.ts +213 -0
  820. package/src/interactive/overlay-lifecycle.ts +413 -0
  821. package/src/interactive/overlay-model-selectors.ts +127 -0
  822. package/src/interactive/overlay-permission-lifecycle.ts +344 -0
  823. package/src/interactive/overlay-resource-openers.ts +112 -0
  824. package/src/interactive/overlay-session-lifecycle.ts +270 -0
  825. package/src/interactive/overlay-transitions.ts +57 -0
  826. package/src/interactive/overlays/agents.ts +69 -0
  827. package/src/interactive/overlays/ask-user.ts +921 -0
  828. package/src/interactive/overlays/auth-dialog.ts +221 -0
  829. package/src/interactive/overlays/context-reset.ts +108 -0
  830. package/src/interactive/overlays/cwd-fallback.ts +141 -0
  831. package/src/interactive/overlays/extensions.ts +54 -0
  832. package/src/interactive/overlays/help-reference.ts +140 -0
  833. package/src/interactive/overlays/interop.ts +139 -0
  834. package/src/interactive/overlays/keybinding-detail.ts +90 -0
  835. package/src/interactive/overlays/list-overlay.ts +606 -0
  836. package/src/interactive/overlays/message-picker.ts +155 -0
  837. package/src/interactive/overlays/model-selector.ts +1091 -0
  838. package/src/interactive/overlays/prompts.ts +72 -0
  839. package/src/interactive/overlays/session-selector-search.ts +31 -0
  840. package/src/interactive/overlays/session-selector.ts +266 -0
  841. package/src/interactive/overlays/settings.ts +3363 -0
  842. package/src/interactive/overlays/skills-hub.ts +237 -0
  843. package/src/interactive/overlays/tree-selector.ts +387 -0
  844. package/src/interactive/permission-overlay.ts +170 -0
  845. package/src/interactive/render-trace.ts +125 -0
  846. package/src/interactive/renderers/branch-summary.ts +86 -0
  847. package/src/interactive/renderers/code-ink.ts +420 -0
  848. package/src/interactive/renderers/compaction-summary.ts +112 -0
  849. package/src/interactive/renderers/diff.ts +126 -0
  850. package/src/interactive/renderers/notice.ts +28 -0
  851. package/src/interactive/renderers/retry-status.ts +31 -0
  852. package/src/interactive/renderers/structured.ts +89 -0
  853. package/src/interactive/renderers/tool-execution.ts +1104 -0
  854. package/src/interactive/renderers/worker-entry.ts +264 -0
  855. package/src/interactive/session-last-turn.ts +186 -0
  856. package/src/interactive/session-transcript.ts +105 -0
  857. package/src/interactive/session-usage-reseed.ts +74 -0
  858. package/src/interactive/slash-autocomplete.ts +364 -0
  859. package/src/interactive/slash-commands.ts +1319 -0
  860. package/src/interactive/slash-spec.ts +453 -0
  861. package/src/interactive/status/controller.ts +256 -0
  862. package/src/interactive/status/index.ts +23 -0
  863. package/src/interactive/status/state-machine.ts +530 -0
  864. package/src/interactive/status/summary.ts +170 -0
  865. package/src/interactive/status/types.ts +121 -0
  866. package/src/interactive/status/verbs.ts +163 -0
  867. package/src/interactive/status/watchdog.ts +24 -0
  868. package/src/interactive/tasks-overlay.ts +186 -0
  869. package/src/interactive/theme/components.ts +55 -0
  870. package/src/interactive/theme/glyphs.ts +70 -0
  871. package/src/interactive/theme/index.ts +15 -0
  872. package/src/interactive/theme/labels.ts +88 -0
  873. package/src/interactive/theme/rules.ts +138 -0
  874. package/src/interactive/theme/segments.ts +88 -0
  875. package/src/interactive/theme/tokens.ts +121 -0
  876. package/src/interactive/thinking-level-policy.ts +6 -0
  877. package/src/interactive/tool-prose-loop.ts +122 -0
  878. package/src/interactive/tool-prose-registration.ts +48 -0
  879. package/src/interactive/turn-context.ts +854 -0
  880. package/src/interactive/turn-middleware.ts +283 -0
  881. package/src/interactive/turn-persistence.ts +441 -0
  882. package/src/interactive/turn-queues.ts +169 -0
  883. package/src/interactive/turn-recovery.ts +306 -0
  884. package/src/interactive/turn-runtime.ts +802 -0
  885. package/src/interactive/turn-state.ts +123 -0
  886. package/src/interactive/view/artifacts.ts +1342 -0
  887. package/src/interactive/view/view-overlay.ts +732 -0
  888. package/src/interactive/welcome-dashboard.ts +536 -0
  889. package/src/interactive/worker-receipts.ts +94 -0
  890. package/src/interactive/worker-replay.ts +111 -0
  891. package/src/interactive/worker-share.ts +100 -0
  892. package/src/interactive/worker-stream.ts +442 -0
  893. package/src/interactive/workspace-facts.ts +141 -0
  894. package/src/tools/agent-tools.ts +382 -0
  895. package/src/tools/artifact.ts +103 -0
  896. package/src/tools/ask-user.ts +656 -0
  897. package/src/tools/bash.ts +176 -0
  898. package/src/tools/bootstrap.ts +474 -0
  899. package/src/tools/codewiki/code-nav.ts +563 -0
  900. package/src/tools/codewiki/shared.ts +115 -0
  901. package/src/tools/compete-worktrees.ts +898 -0
  902. package/src/tools/context/docs-engine.ts +619 -0
  903. package/src/tools/context/index.ts +435 -0
  904. package/src/tools/credential-present.ts +151 -0
  905. package/src/tools/dispatch-arguments.ts +228 -0
  906. package/src/tools/dispatch-plan.ts +741 -0
  907. package/src/tools/dispatch-scout.ts +461 -0
  908. package/src/tools/dispatch.ts +3338 -0
  909. package/src/tools/edit-diff.ts +534 -0
  910. package/src/tools/edit.ts +128 -0
  911. package/src/tools/executables.ts +22 -0
  912. package/src/tools/file-mutation-queue.ts +35 -0
  913. package/src/tools/find.ts +292 -0
  914. package/src/tools/grep.ts +495 -0
  915. package/src/tools/ignore-policy.ts +169 -0
  916. package/src/tools/ledger.ts +206 -0
  917. package/src/tools/ls.ts +110 -0
  918. package/src/tools/monitor.ts +797 -0
  919. package/src/tools/network-policy.ts +29 -0
  920. package/src/tools/observation.ts +421 -0
  921. package/src/tools/observers.ts +99 -0
  922. package/src/tools/path-utils.ts +79 -0
  923. package/src/tools/policy.ts +154 -0
  924. package/src/tools/profiles.ts +108 -0
  925. package/src/tools/read.ts +173 -0
  926. package/src/tools/registry.ts +1157 -0
  927. package/src/tools/result-shaping.ts +204 -0
  928. package/src/tools/safe-exec.ts +128 -0
  929. package/src/tools/spawn-hygiene.ts +182 -0
  930. package/src/tools/steer.ts +88 -0
  931. package/src/tools/string-enum.ts +15 -0
  932. package/src/tools/tasks.ts +168 -0
  933. package/src/tools/truncate-utf8.ts +17 -0
  934. package/src/tools/truncate.ts +202 -0
  935. package/src/tools/verify/frontend.ts +685 -0
  936. package/src/tools/verify/index.ts +66 -0
  937. package/src/tools/verify/scripts.ts +108 -0
  938. package/src/tools/web-fetch.ts +777 -0
  939. package/src/tools/worker-evidence.ts +215 -0
  940. package/src/tools/write.ts +52 -0
  941. package/src/utils/exif-orientation.ts +192 -0
  942. package/src/utils/git.ts +24 -0
  943. package/src/utils/image-resize.ts +148 -0
  944. package/src/utils/photon.ts +103 -0
  945. package/src/worker/control-lane.ts +22 -0
  946. package/src/worker/entry.ts +257 -0
  947. package/src/worker/event-projection.ts +56 -0
  948. package/src/worker/heartbeat.ts +24 -0
  949. package/src/worker/ledger-mirror.ts +95 -0
  950. package/src/worker/ndjson.ts +40 -0
  951. package/src/worker/protocol.ts +529 -0
  952. package/src/worker/resource-facts.ts +79 -0
  953. package/src/worker/runtime-registry.ts +12 -0
  954. package/src/worker/spec-contract.ts +654 -0
  955. package/src/worker/stdin-demux.ts +316 -0
  956. package/dist/acp-UMLFVA3F.js +0 -92
  957. package/dist/chunk-262G75JS.js +0 -35
  958. package/dist/chunk-5LDRNKX2.js +0 -1063
  959. package/dist/chunk-67MTHP2E.js +0 -135
  960. package/dist/chunk-GOTUR54M.js +0 -9
  961. package/dist/chunk-TPOCL34A.js +0 -2876
  962. package/dist/chunk-XV4ZJ6ZM.js +0 -3177
  963. package/dist/clio-KIQ5SNDS.js +0 -53
  964. package/dist/configure-G4E3A2PG.js +0 -27
  965. package/dist/doctor-M5HJJZOL.js +0 -61
  966. package/dist/domains/agents/builtins/architect.md +0 -33
  967. package/dist/domains/agents/builtins/coder.md +0 -31
  968. package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
  969. package/dist/domains/agents/builtins/debugger.md +0 -30
  970. package/dist/domains/agents/builtins/documenter.md +0 -31
  971. package/dist/domains/agents/builtins/git-master.md +0 -30
  972. package/dist/domains/agents/builtins/provenance.md +0 -30
  973. package/dist/domains/agents/builtins/researcher.md +0 -71
  974. package/dist/domains/agents/builtins/scout.md +0 -42
  975. package/dist/domains/agents/builtins/tester.md +0 -31
  976. package/dist/domains/agents/builtins/verifier.md +0 -30
  977. package/dist/domains/agents/builtins/wiki-writer.md +0 -41
  978. package/dist/fleet-preflight-CLIAX7YR.js +0 -21
  979. package/dist/preload-KZVHET2B.js +0 -11
  980. package/dist/version-ROZ6CZKH.js +0 -16
  981. package/skills/git/commit-crafting/SKILL.md +0 -79
  982. package/skills/git/commit-crafting/evals.md +0 -92
  983. package/skills/git/create-pr/SKILL.md +0 -116
  984. package/skills/git/create-pr/evals.md +0 -114
  985. package/skills/git/investigate-issue/SKILL.md +0 -139
  986. package/skills/git/investigate-issue/evals.md +0 -94
  987. package/skills/git/review-changes/SKILL.md +0 -103
  988. package/skills/git/review-changes/evals.md +0 -85
@@ -0,0 +1,1319 @@
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 type { SafeEventBus } from "../core/event-bus.js";
10
+ import { rawDurationMs } from "../core/timers.js";
11
+ import type { AgentAudience } from "../domains/agents/spec.js";
12
+ import type { DispatchSnapshot } from "../domains/dispatch/contract.js";
13
+ import {
14
+ type DispatchRequestOrigin,
15
+ type RunKind,
16
+ type RunStatus,
17
+ runKindSupportsLiveSteering,
18
+ } from "../domains/dispatch/types.js";
19
+ import {
20
+ COST_NOT_MEASURED,
21
+ costAggregateForAmount,
22
+ formatCostAggregate,
23
+ type ObservabilityNotice,
24
+ type ObservabilitySnapshot,
25
+ } from "../domains/observability/index.js";
26
+ import type { CostProvenance } from "../domains/providers/index.js";
27
+ import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
28
+ import { type Component, truncateToWidth, visibleWidth } from "../engine/tui.js";
29
+ import { formatWorkerContextMeter } from "./context-meter.js";
30
+ import { formatFooterTokens } from "./footer-panel.js";
31
+ import {
32
+ type ClioTheme,
33
+ type ClioToken,
34
+ clioTheme,
35
+ dotSep,
36
+ fitUnits,
37
+ formatCompactMs,
38
+ frame,
39
+ GLYPH,
40
+ innerDivider,
41
+ padAnsi,
42
+ screenTitle,
43
+ spinnerFrame,
44
+ } from "./theme/index.js";
45
+
46
+ export type DispatchBoardStatus =
47
+ | Extract<RunStatus, "running" | "completed" | "failed" | "stale" | "dead">
48
+ | "aborted"
49
+ | "cancelling"
50
+ | "enqueued"
51
+ | "retrying";
52
+
53
+ export interface DispatchRetryPresentation {
54
+ attempt: number;
55
+ dueAtMs: number;
56
+ reason: string;
57
+ }
58
+
59
+ export interface DispatchSteerAcknowledgement {
60
+ receivedAtMs: number;
61
+ chars: number;
62
+ }
63
+
64
+ export interface DispatchBoardRow {
65
+ runId: string;
66
+ agentId: string;
67
+ agentAudience?: AgentAudience;
68
+ requestOrigin?: DispatchRequestOrigin;
69
+ runtimeKind: RunKind;
70
+ runtimeId: string;
71
+ targetId: string;
72
+ wireModelId: string;
73
+ /** Sanitized, one-line summary of the task assigned to this run. */
74
+ taskSummary?: string;
75
+ status: DispatchBoardStatus;
76
+ elapsedMs: number;
77
+ tokenCount: number;
78
+ costUsd: number;
79
+ /** Pricing truth for costUsd; a progress event that omits it reads as unknown. */
80
+ costProvenance?: CostProvenance;
81
+ inputTokens: number;
82
+ outputTokens: number;
83
+ ttftMs: number | null;
84
+ outcomeDetail?: string | null;
85
+ /** Fleet node id; absent renders as the local node. */
86
+ node?: string;
87
+ /** Review/compete gate badge (role + cycle). */
88
+ gate?: { role: string; cycle: number };
89
+ /** Dead-node failover hops recorded on this run's chain. */
90
+ rerouteCount?: number;
91
+ /** Assignment retry attempts observed on the assignment event stream. */
92
+ failoverHops?: number;
93
+ /** Model context window for the per-worker context meter. */
94
+ contextWindow?: number;
95
+ /** Last assistant message's input+cacheRead+output: current context occupancy. */
96
+ lastContextTokens?: number;
97
+ /** Tool currently executing in the worker; null between calls. */
98
+ currentTool?: string | null;
99
+ /** Recently finished tools, newest first, bounded. */
100
+ recentTools?: ReadonlyArray<string>;
101
+ /**
102
+ * Id of the receipt sealed for this run (`receipts/<runId>.json`, the id
103
+ * `clio-coder trace` takes). Set only once a terminal dispatch event has
104
+ * been published, which the dispatch domain does after recordReceipt, so a
105
+ * running row carries no receipt id and never implies evidence that does
106
+ * not exist yet.
107
+ */
108
+ receiptId?: string;
109
+ /** Waiting retry projected from the live dispatch snapshot. */
110
+ retry?: DispatchRetryPresentation;
111
+ /** Most recent worker delivery acknowledgement for an operator steer. */
112
+ steerAcknowledgement?: DispatchSteerAcknowledgement;
113
+ }
114
+
115
+ /** Live HTTP/SDK rows are the only rows whose runtime can consume operator guidance. */
116
+ export function isDispatchBoardRowSteerable(row: DispatchBoardRow): boolean {
117
+ return (
118
+ runKindSupportsLiveSteering(row.runtimeKind) &&
119
+ (row.status === "running" || row.status === "stale" || row.status === "enqueued")
120
+ );
121
+ }
122
+
123
+ /** Dispatch abort owns active workers and retry timers, but never terminal history. */
124
+ export function isDispatchBoardRowCancellable(row: DispatchBoardRow): boolean {
125
+ return row.status === "running" || row.status === "stale" || row.status === "enqueued" || row.status === "retrying";
126
+ }
127
+
128
+ interface DispatchBoardEntry
129
+ extends Omit<DispatchBoardRow, "elapsedMs" | "recentTools" | "lastContextTokens" | "currentTool" | "retry"> {
130
+ sequence: number;
131
+ enqueuedAtMs: number;
132
+ startedAtMs: number | null;
133
+ /**
134
+ * The monotonic twin of `startedAtMs`, set from the same observation. TTFT
135
+ * is a span, so it is measured against this and never against the wall
136
+ * stamps, which exist to order and label rows.
137
+ */
138
+ startedAtClockMs: number | null;
139
+ finishedAtMs: number | null;
140
+ durationMs: number | null;
141
+ lastContextTokens: number;
142
+ currentTool: string | null;
143
+ recentTools: string[];
144
+ retry?: DispatchRetryPresentation;
145
+ }
146
+
147
+ /** Bound on the recent-tool trail rendered on a card. */
148
+ const RECENT_TOOL_TRAIL_LIMIT = 4;
149
+ /** Keep raw task text out of long-lived TUI rows and bound hostile/user-sized input. */
150
+ const TASK_SUMMARY_MAX_WIDTH = 240;
151
+
152
+ interface WorkerEventShape {
153
+ type?: unknown;
154
+ message?: {
155
+ role?: unknown;
156
+ usage?: {
157
+ input?: unknown;
158
+ output?: unknown;
159
+ cacheRead?: unknown;
160
+ cacheWrite?: unknown;
161
+ };
162
+ };
163
+ messages?: unknown;
164
+ }
165
+
166
+ interface AssistantMessageShape {
167
+ role?: unknown;
168
+ stopReason?: unknown;
169
+ }
170
+
171
+ export const TASK_ISLAND_WIDTH = 44;
172
+
173
+ const STATUS_ORDER: Record<DispatchBoardStatus, number> = {
174
+ running: 0,
175
+ cancelling: 1,
176
+ stale: 2,
177
+ retrying: 3,
178
+ enqueued: 4,
179
+ dead: 5,
180
+ failed: 6,
181
+ aborted: 7,
182
+ completed: 8,
183
+ };
184
+ const MAX_DISPATCH_BOARD_ROWS = 50;
185
+
186
+ /**
187
+ * The agent's own name, unadorned. Audience is carried by
188
+ * {@link agentAudiencePresentation}, never by a name prefix: `sh:scout` reads
189
+ * as an agent literally called "sh:scout", and an operator cannot tell a
190
+ * prefix apart from user data at a glance.
191
+ */
192
+ export function agentDisplayLabel(row: Pick<DispatchBoardRow, "agentId" | "agentAudience">): string {
193
+ return row.agentId;
194
+ }
195
+
196
+ export interface AgentAudiencePresentation {
197
+ glyph: string;
198
+ token: ClioToken;
199
+ }
200
+
201
+ /**
202
+ * Visual treatment for a run Clio started for itself: the sub-process glyph in
203
+ * a muted tone for a shadow worker, the same glyph dimmed for internal harness
204
+ * machinery. Returns null for base/custom agents, which the operator asked for
205
+ * directly and which therefore render like any other row.
206
+ */
207
+ export function agentAudiencePresentation(
208
+ row: Pick<DispatchBoardRow, "agentAudience">,
209
+ ): AgentAudiencePresentation | null {
210
+ if (row.agentAudience === "shadow") return { glyph: GLYPH.subProcess, token: "muted" };
211
+ if (row.agentAudience === "internal") return { glyph: GLYPH.subProcess, token: "dim" };
212
+ return null;
213
+ }
214
+
215
+ export interface DispatchOriginPresentation {
216
+ glyph: string;
217
+ token: ClioToken;
218
+ }
219
+
220
+ /**
221
+ * Who asked for this run, as the transcript spells it: hollow for the
222
+ * operator's own `/run` or `/delegate`, filled for one the model started by
223
+ * calling a dispatch tool, a quiet dot for the runs Clio starts for itself and
224
+ * never puts on the transcript. A running `◇` on the board is therefore the
225
+ * operator's own work, on the board and in the footer both.
226
+ *
227
+ * This is a different axis from {@link agentAudiencePresentation}, which says
228
+ * who picked the agent. An operator who approves a plan gets user-origin runs
229
+ * on shadow agents, so a row can carry both marks and mean two true things.
230
+ *
231
+ * Returns null for a row whose origin never reached the projection, so an
232
+ * unknown origin renders as nothing rather than claiming to be internal.
233
+ *
234
+ * Only the operator's own run takes a color. The transcript block paints an
235
+ * agent-origin run in action orange because it is the one signal on that
236
+ * surface; a board row sits under the fleet summary, which already owns the
237
+ * quadrant's single orange, so here the filled glyph carries origin by shape.
238
+ */
239
+ export function dispatchOriginPresentation(
240
+ row: Pick<DispatchBoardRow, "requestOrigin">,
241
+ ): DispatchOriginPresentation | null {
242
+ if (row.requestOrigin === "user") return { glyph: GLYPH.workerHuman, token: "accent" };
243
+ if (row.requestOrigin === "agent") return { glyph: GLYPH.workerAgent, token: "muted" };
244
+ if (row.requestOrigin === "internal") return { glyph: GLYPH.workerInternal, token: "dim" };
245
+ return null;
246
+ }
247
+
248
+ /**
249
+ * The full glyph lead for a row: origin first, then audience. Reported with its
250
+ * own display width so each caller can reserve columns for it without
251
+ * measuring ANSI.
252
+ */
253
+ export function dispatchRowPrefix(
254
+ theme: ClioTheme,
255
+ row: Pick<DispatchBoardRow, "agentAudience" | "requestOrigin">,
256
+ ): { text: string; width: number } {
257
+ const origin = dispatchOriginPresentation(row);
258
+ const audience = agentAudiencePresentation(row);
259
+ const text = `${origin === null ? "" : `${theme.fg(origin.token, origin.glyph)} `}${audience === null ? "" : `${theme.fg(audience.token, audience.glyph)} `}`;
260
+ const width =
261
+ (origin === null ? 0 : visibleWidth(origin.glyph) + 1) + (audience === null ? 0 : visibleWidth(audience.glyph) + 1);
262
+ return { text, width };
263
+ }
264
+
265
+ export interface DispatchStatusPresentation {
266
+ glyph: string;
267
+ label: string;
268
+ token: ClioToken;
269
+ }
270
+
271
+ /**
272
+ * One presentation per run status, shared by the dispatch cards, the task
273
+ * island, and the footer worker lines so a status never changes glyph or color
274
+ * between surfaces. `compact` shortens labels for narrow rows; a `tick`
275
+ * animates the running glyph.
276
+ */
277
+ export function dispatchStatusPresentation(
278
+ status: DispatchBoardStatus,
279
+ options: { compact?: boolean; tick?: number } = {},
280
+ ): DispatchStatusPresentation {
281
+ const compact = options.compact === true;
282
+ switch (status) {
283
+ case "running":
284
+ // Running fleet work is Clio acting, so it joins queued work under the
285
+ // action token rather than the old teal-running/orange-queued split.
286
+ return {
287
+ glyph: options.tick !== undefined ? spinnerFrame(options.tick) : GLYPH.running,
288
+ label: "running",
289
+ token: "action",
290
+ };
291
+ case "cancelling":
292
+ return { glyph: GLYPH.cancelled, label: "cancelling", token: "warning" };
293
+ case "retrying":
294
+ return { glyph: GLYPH.phaseRetry, label: "retrying", token: "warning" };
295
+ case "completed":
296
+ return { glyph: GLYPH.ok, label: compact ? "done" : "completed", token: "success" };
297
+ case "failed":
298
+ return { glyph: GLYPH.error, label: compact ? "fail" : "failed", token: "error" };
299
+ case "dead":
300
+ return { glyph: GLYPH.error, label: "dead", token: "error" };
301
+ case "aborted":
302
+ return { glyph: GLYPH.cancelled, label: compact ? "abort" : "aborted", token: "dim" };
303
+ case "stale":
304
+ return { glyph: GLYPH.warnInline, label: "stale", token: "warning" };
305
+ case "enqueued":
306
+ return { glyph: GLYPH.queued, label: "queued", token: "action" };
307
+ }
308
+ }
309
+
310
+ /** Evidence-readiness state for a dispatch run, derived from the observability projection. */
311
+ export type EvidenceState = "pending" | "ready" | "failed" | "none";
312
+
313
+ /**
314
+ * Compact, presentation-ready evidence readiness for one run. Derived purely
315
+ * from an ObservabilitySnapshot: no evidence files are read and no /view
316
+ * artifact bodies are inspected. `viewFilter` is the `/view` deep-link an
317
+ * operator would type to inspect the run or its bundle.
318
+ */
319
+ export interface RunEvidencePresentation {
320
+ state: EvidenceState;
321
+ /** Bundle id, present only when the evidence is ready. */
322
+ evidenceId?: string;
323
+ /** Short failure reason drawn from the latest evidence notice, when failed. */
324
+ reason?: string;
325
+ /** The `/view` filter text: evidence:<id|runId>, or dispatch:<runId> when absent. */
326
+ viewFilter: string;
327
+ }
328
+
329
+ // A run's evidence failure is signalled by its latest evidence notice carrying
330
+ // the error level and this run's id; a trailing warning/info notice for the same
331
+ // run is not a failure, and a notice without ref.runId is not attributed here.
332
+ function latestEvidenceFailureReason(notices: readonly ObservabilityNotice[], runId: string): string | null {
333
+ for (let index = notices.length - 1; index >= 0; index -= 1) {
334
+ const notice = notices[index];
335
+ if (!notice) continue;
336
+ if (notice.kind !== "evidence" || notice.ref?.runId !== runId) continue;
337
+ if (notice.level !== "error") return null;
338
+ const reason = notice.message.replace(/\s+/g, " ").trim();
339
+ return reason.length > 0 ? reason : null;
340
+ }
341
+ return null;
342
+ }
343
+
344
+ /**
345
+ * Fold an ObservabilitySnapshot into the evidence readiness for one run. Cheap
346
+ * and pure: it scans the bounded run and notice rings the projection already
347
+ * maintains and reads no evidence files. Pending outranks a stale ready bundle
348
+ * because an in-flight rebuild is the more current signal; a matching run
349
+ * summary with an evidence bundle is ready; the latest error-level evidence
350
+ * notice for the run marks it failed.
351
+ */
352
+ export function deriveRunEvidenceState(
353
+ snapshot: ObservabilitySnapshot | undefined,
354
+ runId: string,
355
+ ): RunEvidencePresentation {
356
+ if (!snapshot || runId.length === 0) return { state: "none", viewFilter: `dispatch:${runId}` };
357
+ if (snapshot.pendingEvidenceBuildRunIds.includes(runId)) {
358
+ return { state: "pending", viewFilter: `evidence:${runId}` };
359
+ }
360
+ const evidence = snapshot.runs.find((run) => run.runId === runId)?.evidence;
361
+ if (evidence) {
362
+ return { state: "ready", evidenceId: evidence.evidenceId, viewFilter: `evidence:${evidence.evidenceId}` };
363
+ }
364
+ const reason = latestEvidenceFailureReason(snapshot.notices, runId);
365
+ if (reason !== null) return { state: "failed", reason, viewFilter: `evidence:${runId}` };
366
+ return { state: "none", viewFilter: `dispatch:${runId}` };
367
+ }
368
+
369
+ interface EvidenceGlyphPresentation {
370
+ glyph: string;
371
+ word: string;
372
+ token: ClioToken;
373
+ }
374
+
375
+ // Proof state shares the section 5 glyph language with run status: the queued
376
+ // glyph for pending build work, the ok check for a ready bundle, and the error
377
+ // cross for a failed build. `none` renders no marker.
378
+ function evidenceStatePresentation(state: EvidenceState): EvidenceGlyphPresentation | null {
379
+ switch (state) {
380
+ case "pending":
381
+ return { glyph: GLYPH.queued, word: "pending", token: "info" };
382
+ case "ready":
383
+ return { glyph: GLYPH.ok, word: "ready", token: "success" };
384
+ case "failed":
385
+ return { glyph: GLYPH.error, word: "failed", token: "error" };
386
+ case "none":
387
+ return null;
388
+ }
389
+ }
390
+
391
+ // Dispatch card rows follow the footer dashboard key-value grammar: a dim key
392
+ // padded to a shared column, then one trailing space before the value. The
393
+ // widest key ("telemetry") sets the column so every value starts aligned.
394
+ const CARD_KV_KEY_WIDTH = "telemetry".length;
395
+
396
+ function cardKvKey(theme: ClioTheme, key: string): string {
397
+ return theme.fg("dim", `${key.padEnd(CARD_KV_KEY_WIDTH)} `);
398
+ }
399
+
400
+ // A card key-value row built from whole units. A row that fits keeps its dim
401
+ // middot join; an overflowing one is refit unit-by-unit so it closes on a
402
+ // whole unit or a dim ellipsis instead of a mid-token clip that reads as a
403
+ // complete fact ("total 5" for 5.2k) or a dangling ` · `.
404
+ function cardUnitsLine(theme: ClioTheme, key: string, units: ReadonlyArray<string>, contentWidth: number): string {
405
+ const prefix = cardKvKey(theme, key);
406
+ const full = `${prefix}${units.join(dotSep(theme))}`;
407
+ return visibleWidth(full) <= contentWidth ? full : fitUnits(theme, prefix, units, contentWidth);
408
+ }
409
+
410
+ // The `proof` row: the colored state marker, an optional failure reason, then
411
+ // the dim `/view` filter an operator would type.
412
+ function evidenceCardLine(theme: ClioTheme, evidence: RunEvidencePresentation, contentWidth: number): string | null {
413
+ const presentation = evidenceStatePresentation(evidence.state);
414
+ if (!presentation) return null;
415
+ const units = [theme.fg(presentation.token, `${presentation.glyph} ${presentation.word}`)];
416
+ if (evidence.state === "failed" && evidence.reason) units.push(theme.fg("muted", evidence.reason));
417
+ units.push(theme.fg("dim", evidence.viewFilter));
418
+ return cardUnitsLine(theme, "proof", units, contentWidth);
419
+ }
420
+
421
+ /** Sanitize lifecycle task text at the terminal boundary and collapse it to one bounded line. */
422
+ export function sanitizeDispatchTaskSummary(value: unknown): string | undefined {
423
+ if (typeof value !== "string") return undefined;
424
+ const sanitized = sanitizeCallTargetText(value);
425
+ if (sanitized.length === 0) return undefined;
426
+ // pi-tui's truncator appends reset sequences when it elides. Strip those
427
+ // again so the stored projection remains plain text, not terminal styling.
428
+ return sanitizeCallTargetText(truncateToWidth(sanitized, TASK_SUMMARY_MAX_WIDTH, "…", false));
429
+ }
430
+
431
+ function retryCountdown(retry: DispatchRetryPresentation, now = Date.now()): string {
432
+ const remainingMs = Math.max(0, retry.dueAtMs - now);
433
+ return remainingMs === 0 ? "now" : `in ${formatCompactMs(remainingMs)}`;
434
+ }
435
+
436
+ export function renderDispatchCard(
437
+ row: DispatchBoardRow,
438
+ width: number,
439
+ evidence?: RunEvidencePresentation,
440
+ options: { selected?: boolean } = {},
441
+ ): string[] {
442
+ const theme = clioTheme();
443
+ const contentWidth = Math.max(0, width - 4);
444
+ const agentLabel = agentDisplayLabel(row);
445
+ const elapsed = formatCompactMs(row.elapsedMs);
446
+ const cost = formatCostAggregate(costAggregateForAmount(row.costUsd, row.costProvenance)) ?? COST_NOT_MEASURED;
447
+ const detail = terminalDetail(row);
448
+
449
+ const presentation = dispatchStatusPresentation(row.status, {
450
+ ...(row.status === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
451
+ });
452
+ // The status value (glyph plus word) is the single status-colored element on
453
+ // the card. Cost and TTFT are neutral telemetry, so they render muted rather
454
+ // than amber or the accentDeep structure color.
455
+ const statusStr = theme.fg(presentation.token, `${presentation.glyph} ${presentation.label}`);
456
+
457
+ const ttft = row.ttftMs !== null ? `${row.ttftMs}ms` : row.status === "running" ? `waiting${GLYPH.ellipsis}` : "n/a";
458
+ const target = `${theme.fg("muted", `${row.runtimeKind}:${row.targetId}`)} ${theme.fg("dim", "▸")} ${theme.fg("muted", row.wireModelId)}`;
459
+
460
+ // The agent label is the frame title and can be arbitrarily long (agent ids
461
+ // are user data); clamp it so the title plus the elapsed meta never pushes
462
+ // the right corner past the card width.
463
+ const selectionWidth = options.selected === true ? visibleWidth(GLYPH.cursor) + 1 : 0;
464
+ const rowPrefix = dispatchRowPrefix(theme, row);
465
+ const labelBudget = Math.max(1, width - visibleWidth(elapsed) - 10 - selectionWidth - rowPrefix.width);
466
+ const clampedLabel = truncateToWidth(agentLabel, labelBudget, GLYPH.ellipsis, false);
467
+ const cardTitle =
468
+ options.selected === true
469
+ ? `${theme.fg("accent", GLYPH.cursor)} ${rowPrefix.text}${screenTitle(theme, clampedLabel)}`
470
+ : `${rowPrefix.text}${clampedLabel}`;
471
+
472
+ const elapsedSec = row.elapsedMs / 1000;
473
+ const tokensPerSec = elapsedSec > 0.1 ? Math.round(row.outputTokens / elapsedSec) : 0;
474
+ // A queued run has produced nothing yet, so it never carries a throughput.
475
+ const showRate = row.status !== "enqueued" && tokensPerSec > 0;
476
+ const up = theme.fg("muted", `${GLYPH.up} ${formatFooterTokens(row.inputTokens)}`);
477
+ const down = theme.fg(
478
+ "muted",
479
+ `${GLYPH.down} ${formatFooterTokens(row.outputTokens)}${showRate ? ` (${tokensPerSec}/s)` : ""}`,
480
+ );
481
+ const total = theme.fg("muted", `total ${formatFooterTokens(row.tokenCount)}`);
482
+
483
+ // The model id is user data and can outrun the card; mark the cut with `…`
484
+ // rather than hard-clipping it mid-token into a string that reads whole.
485
+ const targetLine = truncateToWidth(`${cardKvKey(theme, "target")}${target}`, contentWidth, "…", false);
486
+ // Fleet facts: node placement (absent means local), gate role badge, and
487
+ // reroute lineage. Whole units so overflow drops a fact, never clips one.
488
+ const statusUnits = [
489
+ statusStr,
490
+ theme.fg("muted", `node ${row.node ?? "local"}`),
491
+ ...(row.gate !== undefined ? [theme.fg("info", `gate ${row.gate.role} c${row.gate.cycle}`)] : []),
492
+ ...(row.rerouteCount !== undefined && row.rerouteCount > 0
493
+ ? [theme.fg("warning", `rerouted x${row.rerouteCount}`)]
494
+ : []),
495
+ ...(row.failoverHops !== undefined && row.failoverHops > 0
496
+ ? [theme.fg("warning", `failed over x${row.failoverHops}`)]
497
+ : []),
498
+ `${theme.fg("dim", "ttft")} ${theme.fg("muted", ttft)}`,
499
+ `${theme.fg("dim", "cost")} ${theme.fg("muted", cost)}`,
500
+ ];
501
+ const contextUnit = formatWorkerContextMeter(row.lastContextTokens ?? 0, row.contextWindow, theme);
502
+ const bodyLines = [
503
+ cardUnitsLine(theme, "run", [theme.fg("dim", row.runId)], contentWidth),
504
+ targetLine,
505
+ ...(row.taskSummary
506
+ ? [truncateToWidth(`${cardKvKey(theme, "task")}${theme.fg("muted", row.taskSummary)}`, contentWidth, "…", false)]
507
+ : []),
508
+ cardUnitsLine(theme, "status", statusUnits, contentWidth),
509
+ cardUnitsLine(theme, "telemetry", [up, down, total, ...(contextUnit !== null ? [contextUnit] : [])], contentWidth),
510
+ ];
511
+ if (row.retry) {
512
+ bodyLines.push(
513
+ cardUnitsLine(
514
+ theme,
515
+ "retry",
516
+ [
517
+ theme.fg("warning", `attempt ${row.retry.attempt}`),
518
+ theme.fg("muted", retryCountdown(row.retry)),
519
+ ...(row.retry.reason.length > 0 ? [theme.fg("muted", row.retry.reason)] : []),
520
+ ],
521
+ contentWidth,
522
+ ),
523
+ );
524
+ }
525
+ // Live tool activity: the executing tool (worker telemetry carries names,
526
+ // never arguments, across the stdout seam) and the recent-tool trail.
527
+ const currentTool = row.currentTool ?? null;
528
+ const recentTools = row.recentTools ?? [];
529
+ if (currentTool !== null || recentTools.length > 0) {
530
+ const toolUnits = [
531
+ currentTool !== null ? theme.fg("action", `${currentTool} running`) : theme.fg("dim", "idle"),
532
+ ...(recentTools.length > 0 ? [theme.fg("muted", `recent ${recentTools.join(" ")}`)] : []),
533
+ ];
534
+ bodyLines.push(cardUnitsLine(theme, "tools", toolUnits, contentWidth));
535
+ }
536
+ if (row.steerAcknowledgement) {
537
+ bodyLines.push(
538
+ cardUnitsLine(
539
+ theme,
540
+ "control",
541
+ [theme.fg("success", `${GLYPH.ok} steer received`), theme.fg("muted", `${row.steerAcknowledgement.chars} chars`)],
542
+ contentWidth,
543
+ ),
544
+ );
545
+ }
546
+ // The proof row is present only when the observability projection knows an
547
+ // evidence state for this run; an unknown/none state adds no line, so cards
548
+ // without evidence keep their existing three-line body.
549
+ if (evidence) {
550
+ const proofLine = evidenceCardLine(theme, evidence, contentWidth);
551
+ if (proofLine !== null) bodyLines.push(proofLine);
552
+ }
553
+ if (detail !== null) bodyLines.push(`${cardKvKey(theme, "detail")}${theme.fg("dim", detail)}`);
554
+
555
+ return frame(theme, cardTitle, bodyLines, width, { rightMeta: elapsed });
556
+ }
557
+
558
+ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
559
+ const theme = clioTheme();
560
+ const agentLabel = agentDisplayLabel(row);
561
+ const elapsed = formatCompactMs(row.elapsedMs);
562
+ const cost = formatCostAggregate(costAggregateForAmount(row.costUsd, row.costProvenance)) ?? COST_NOT_MEASURED;
563
+
564
+ const dot = dotSep(theme);
565
+ const presentation = dispatchStatusPresentation(row.status, {
566
+ compact: true,
567
+ ...(row.status === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
568
+ });
569
+ const glyph = theme.fg(presentation.token, presentation.glyph);
570
+ const statusStr = theme.fg(presentation.token, presentation.label);
571
+
572
+ // Reserve the glyph, separators, status word, and elapsed so a long agent
573
+ // label is clipped with a `…` marker rather than shoved off the row unmarked.
574
+ const rowPrefix = dispatchRowPrefix(theme, row);
575
+ const labelChrome =
576
+ visibleWidth(presentation.glyph) +
577
+ 1 +
578
+ rowPrefix.width +
579
+ 3 +
580
+ visibleWidth(presentation.label) +
581
+ 3 +
582
+ visibleWidth(elapsed);
583
+ const clampedLabel = truncateToWidth(agentLabel, Math.max(1, width - labelChrome), "…", false);
584
+
585
+ // The agent label drops its accent color for plain bold; the status word is
586
+ // the only status-colored element on the row, with dim middot separators.
587
+ const line1 = `${glyph} ${rowPrefix.text}${theme.paint(clampedLabel, { bold: true })}${dot}${statusStr}${dot}${theme.fg("muted", elapsed)}`;
588
+
589
+ const elapsedSec = row.elapsedMs / 1000;
590
+ const tokensPerSec = elapsedSec > 0.1 ? Math.round(row.outputTokens / elapsedSec) : 0;
591
+ // A queued run has produced nothing yet, so it never carries a throughput.
592
+ const showRate = row.status !== "enqueued" && tokensPerSec > 0;
593
+ const up = theme.fg("muted", `${GLYPH.up} ${formatFooterTokens(row.inputTokens)}`);
594
+ const down = theme.fg(
595
+ "muted",
596
+ `${GLYPH.down} ${formatFooterTokens(row.outputTokens)}${showRate ? ` (${tokensPerSec}/s)` : ""}`,
597
+ );
598
+ const telemetry = row.retry
599
+ ? ` ${theme.fg("warning", `attempt ${row.retry.attempt}`)}${dot}${theme.fg("muted", retryCountdown(row.retry))}`
600
+ : ` ${up}${dot}${down}${dot}${theme.fg("muted", cost)}`;
601
+ const task = row.taskSummary
602
+ ? ` ${theme.fg("muted", truncateToWidth(row.taskSummary, Math.max(1, width - 2), "…", false))}`
603
+ : null;
604
+
605
+ return [padAnsi(line1, width), ...(task ? [padAnsi(task, width)] : []), padAnsi(telemetry, width)];
606
+ }
607
+
608
+ export function formatDispatchBoardLines(
609
+ rows: ReadonlyArray<DispatchBoardRow>,
610
+ width = 76,
611
+ observability?: ObservabilitySnapshot,
612
+ selectedRunId?: string | null,
613
+ ): string[] {
614
+ if (rows.length === 0) {
615
+ const theme = clioTheme();
616
+ const lines = ["", "No fleet runs yet", "Delegated runs appear here with task, status, and telemetry.", ""];
617
+ return lines.map((line) => {
618
+ const padding = Math.max(0, Math.floor((width - visibleWidth(line)) / 2));
619
+ return theme.fg("dim", " ".repeat(padding) + line);
620
+ });
621
+ }
622
+
623
+ const cards = rows.map((row) =>
624
+ renderDispatchCard(row, width, deriveRunEvidenceState(observability, row.runId), {
625
+ selected: row.runId === selectedRunId,
626
+ }),
627
+ );
628
+ const body: string[] = [];
629
+ for (const card of cards) {
630
+ if (body.length > 0) body.push("");
631
+ body.push(...card);
632
+ }
633
+ return body;
634
+ }
635
+
636
+ /**
637
+ * Live dispatch-board component. Cards render at the width the TUI actually
638
+ * grants instead of a baked-in 76-column layout, so a narrow terminal no
639
+ * longer clips telemetry mid-token and a wide one no longer wastes the frame.
640
+ * Rows and the observability snapshot are read per render. The board is a pure
641
+ * consumer: its owner reconciles the store on the shared UI polling cycle.
642
+ * The only local state is the selected run id, kept stable across
643
+ * lifecycle-driven reorders; ticker plus bus-driven requestRender calls drive
644
+ * repaints.
645
+ */
646
+ export interface DispatchBoardView extends Component {
647
+ selectedRow(): DispatchBoardRow | null;
648
+ selectPrevious(): void;
649
+ selectNext(): void;
650
+ resetSelection(): void;
651
+ }
652
+
653
+ export function createDispatchBoardView(
654
+ rows: () => ReadonlyArray<DispatchBoardRow>,
655
+ observability: () => ObservabilitySnapshot | undefined,
656
+ ): DispatchBoardView {
657
+ let selectedRunId: string | null = null;
658
+
659
+ const normalizeSelection = (currentRows: ReadonlyArray<DispatchBoardRow>): DispatchBoardRow | null => {
660
+ if (currentRows.length === 0) {
661
+ selectedRunId = null;
662
+ return null;
663
+ }
664
+ const selected = currentRows.find((row) => row.runId === selectedRunId) ?? currentRows[0] ?? null;
665
+ selectedRunId = selected?.runId ?? null;
666
+ return selected;
667
+ };
668
+
669
+ const moveSelection = (delta: -1 | 1): void => {
670
+ const currentRows = rows();
671
+ const selected = normalizeSelection(currentRows);
672
+ if (!selected || currentRows.length < 2) return;
673
+ const index = currentRows.findIndex((row) => row.runId === selected.runId);
674
+ const nextIndex = (index + delta + currentRows.length) % currentRows.length;
675
+ selectedRunId = currentRows[nextIndex]?.runId ?? selected.runId;
676
+ };
677
+
678
+ return {
679
+ render(width: number): string[] {
680
+ const currentRows = rows();
681
+ normalizeSelection(currentRows);
682
+ return formatDispatchBoardLines(currentRows, Math.max(1, width), observability(), selectedRunId);
683
+ },
684
+ selectedRow(): DispatchBoardRow | null {
685
+ return normalizeSelection(rows());
686
+ },
687
+ selectPrevious(): void {
688
+ moveSelection(-1);
689
+ },
690
+ selectNext(): void {
691
+ moveSelection(1);
692
+ },
693
+ resetSelection(): void {
694
+ selectedRunId = null;
695
+ normalizeSelection(rows());
696
+ },
697
+ invalidate(): void {},
698
+ };
699
+ }
700
+
701
+ export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, maxRows = 4): string[] {
702
+ const visibleRows = rows.slice(0, Math.max(1, maxRows));
703
+ const body: string[] = [];
704
+
705
+ if (visibleRows.length === 0) {
706
+ const theme = clioTheme();
707
+ body.push(theme.fg("dim", "No active fleet runs."));
708
+ body.push(theme.fg("dim", "Use /run or /delegate to spawn agents."));
709
+ } else {
710
+ for (let i = 0; i < visibleRows.length; i++) {
711
+ const row = visibleRows[i];
712
+ if (!row) continue;
713
+ if (i > 0) {
714
+ body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
715
+ }
716
+ body.push(...renderTaskIslandRow(row, TASK_ISLAND_WIDTH));
717
+ }
718
+ const hidden = rows.length - visibleRows.length;
719
+ if (hidden > 0) {
720
+ body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
721
+ body.push(clioTheme().fg("dim", `+ ${hidden} more`));
722
+ }
723
+ }
724
+
725
+ // Body rows are already ANSI-padded to TASK_ISLAND_WIDTH by the row renderer
726
+ // (or are fixed-width dividers/empty-state lines). The canonical frame re-pads
727
+ // each row ANSI-aware, so passing the styled lines through is safe.
728
+ return frame(clioTheme(), "Fleet runs", body, TASK_ISLAND_WIDTH + 4);
729
+ }
730
+
731
+ function parseRunId(value: unknown): string | null {
732
+ return typeof value === "string" && value.length > 0 ? value : null;
733
+ }
734
+
735
+ function parseText(value: unknown, fallback: string): string {
736
+ return typeof value === "string" && value.length > 0 ? value : fallback;
737
+ }
738
+
739
+ function parseRuntimeKind(value: unknown): RunKind {
740
+ if (value === "sdk" || value === "subprocess" || value === "acp-delegation") return value;
741
+ return "http";
742
+ }
743
+
744
+ function parseAgentAudience(value: unknown, fallback: AgentAudience | undefined): AgentAudience | undefined {
745
+ if (value === "base" || value === "shadow" || value === "custom" || value === "internal") return value;
746
+ return fallback;
747
+ }
748
+
749
+ function parseRequestOrigin(
750
+ value: unknown,
751
+ fallback: DispatchRequestOrigin | undefined,
752
+ ): DispatchRequestOrigin | undefined {
753
+ if (value === "user" || value === "agent" || value === "internal") return value;
754
+ return fallback;
755
+ }
756
+
757
+ function parseFiniteNumber(value: unknown, fallback: number): number {
758
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
759
+ }
760
+
761
+ function parseNonEmptyString(value: unknown): string | undefined {
762
+ return typeof value === "string" && value.length > 0 ? value : undefined;
763
+ }
764
+
765
+ function parseTaskSummary(
766
+ raw: Partial<DispatchRunIdentity> & { task?: unknown; taskSummary?: unknown },
767
+ fallback: string | undefined,
768
+ ): string | undefined {
769
+ return sanitizeDispatchTaskSummary(raw.task) ?? sanitizeDispatchTaskSummary(raw.taskSummary) ?? fallback;
770
+ }
771
+
772
+ function parsePositiveInt(value: unknown): number | undefined {
773
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
774
+ }
775
+
776
+ function parseGateBadge(value: unknown): { role: string; cycle: number } | undefined {
777
+ if (typeof value !== "object" || value === null) return undefined;
778
+ const record = value as { role?: unknown; cycle?: unknown };
779
+ if (typeof record.role !== "string" || record.role.length === 0) return undefined;
780
+ const cycle = parsePositiveInt(record.cycle) ?? 1;
781
+ return { role: record.role, cycle };
782
+ }
783
+
784
+ function parseFiniteNumberOrZero(value: unknown): number {
785
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
786
+ }
787
+
788
+ function parseOptionalDetail(value: unknown): string | null {
789
+ if (typeof value !== "string") return null;
790
+ const detail = value.replace(/\s+/g, " ").trim();
791
+ return detail.length > 0 ? detail : null;
792
+ }
793
+
794
+ function terminalDetail(row: DispatchBoardRow): string | null {
795
+ if (row.status !== "failed" && row.status !== "dead" && row.status !== "aborted") return null;
796
+ return parseOptionalDetail(row.outcomeDetail);
797
+ }
798
+
799
+ function resolveAgentEndStatus(rawMessages: unknown): DispatchBoardStatus | null {
800
+ if (!Array.isArray(rawMessages)) return null;
801
+ for (let index = rawMessages.length - 1; index >= 0; index -= 1) {
802
+ const message = (rawMessages[index] ?? {}) as AssistantMessageShape;
803
+ if (message.role !== "assistant") continue;
804
+ if (message.stopReason === "stop") return "completed";
805
+ if (message.stopReason === "error") return "failed";
806
+ if (message.stopReason === "aborted") return "aborted";
807
+ return null;
808
+ }
809
+ return null;
810
+ }
811
+
812
+ function resolveFailedStatus(reason: unknown): DispatchBoardStatus {
813
+ if (reason === "dead" || reason === "stalled") return "dead";
814
+ if (reason === "interrupted" || reason === "canceled") return "aborted";
815
+ return "failed";
816
+ }
817
+
818
+ function resolveFailureDetail(
819
+ payload: Partial<DispatchFailedPayload>,
820
+ fallback: string | null | undefined,
821
+ ): string | null {
822
+ const detail = parseOptionalDetail(payload.outcomeDetail);
823
+ if (detail !== null) return detail;
824
+ if (payload.reason === "timed_out") return "turn timeout exceeded";
825
+ return fallback ?? null;
826
+ }
827
+
828
+ function resolveHeartbeatStatus(status: unknown): DispatchBoardStatus | null {
829
+ if (status === "alive") return "running";
830
+ if (status === "stale" || status === "dead") return status;
831
+ return null;
832
+ }
833
+
834
+ function isTerminalStatus(status: DispatchBoardStatus): boolean {
835
+ return status === "completed" || status === "failed" || status === "aborted" || status === "dead";
836
+ }
837
+
838
+ function parseRetrySnapshot(value: DispatchSnapshot["retrying"][number]): DispatchRetryPresentation | null {
839
+ const attempt = parsePositiveInt(value.attempt);
840
+ const dueAtMs = Date.parse(value.dueAt);
841
+ if (attempt === undefined || !Number.isFinite(dueAtMs)) return null;
842
+ return {
843
+ attempt,
844
+ dueAtMs,
845
+ reason: sanitizeCallTargetText(value.reason),
846
+ };
847
+ }
848
+
849
+ function readRetrySnapshot(
850
+ snapshot: DispatchSnapshot,
851
+ ): Map<string, { agentId: string; taskSummary?: string; retry: DispatchRetryPresentation }> {
852
+ const retrying = new Map<string, { agentId: string; taskSummary?: string; retry: DispatchRetryPresentation }>();
853
+ try {
854
+ for (const value of snapshot.retrying) {
855
+ const runId = parseRunId(value.runId);
856
+ const agentId = parseNonEmptyString(value.agentId);
857
+ const retry = parseRetrySnapshot(value);
858
+ const taskSummary = sanitizeDispatchTaskSummary(value.task);
859
+ if (runId && agentId && retry) {
860
+ retrying.set(runId, {
861
+ agentId,
862
+ ...(taskSummary !== undefined ? { taskSummary } : {}),
863
+ retry,
864
+ });
865
+ }
866
+ }
867
+ } catch {
868
+ // The board remains lifecycle-event driven if an optional snapshot fails.
869
+ }
870
+ return retrying;
871
+ }
872
+
873
+ function readRunningSnapshot(snapshot: DispatchSnapshot): Map<
874
+ string,
875
+ {
876
+ inputTokens: number;
877
+ outputTokens: number;
878
+ tokenCount: number;
879
+ costUsd: number;
880
+ costProvenance: CostProvenance;
881
+ outcomePhase: string;
882
+ }
883
+ > {
884
+ const running = new Map<
885
+ string,
886
+ {
887
+ inputTokens: number;
888
+ outputTokens: number;
889
+ tokenCount: number;
890
+ costUsd: number;
891
+ costProvenance: CostProvenance;
892
+ outcomePhase: string;
893
+ }
894
+ >();
895
+ try {
896
+ for (const value of snapshot.running) {
897
+ const runId = parseRunId(value.runId);
898
+ if (!runId) continue;
899
+ running.set(runId, {
900
+ inputTokens: parseFiniteNumberOrZero(value.tokens.input),
901
+ outputTokens: parseFiniteNumberOrZero(value.tokens.output),
902
+ tokenCount: parseFiniteNumberOrZero(value.tokens.total),
903
+ costUsd: parseFiniteNumberOrZero(value.costUsd),
904
+ costProvenance: value.costProvenance ?? "unknown",
905
+ outcomePhase: value.outcomePhase,
906
+ });
907
+ }
908
+ } catch {
909
+ // Lifecycle/progress events remain authoritative if the optional snapshot fails.
910
+ }
911
+ return running;
912
+ }
913
+
914
+ function resolveElapsedMs(entry: DispatchBoardEntry, now: number): number {
915
+ const startedAtMs = entry.startedAtMs ?? entry.enqueuedAtMs;
916
+ if (entry.durationMs !== null) return entry.durationMs;
917
+ const endMs = entry.finishedAtMs ?? now;
918
+ return Math.max(0, rawDurationMs(startedAtMs, endMs));
919
+ }
920
+
921
+ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
922
+ const retry = entry.retry;
923
+ return {
924
+ runId: entry.runId,
925
+ agentId: entry.agentId,
926
+ ...(entry.agentAudience !== undefined ? { agentAudience: entry.agentAudience } : {}),
927
+ ...(entry.requestOrigin !== undefined ? { requestOrigin: entry.requestOrigin } : {}),
928
+ runtimeKind: entry.runtimeKind,
929
+ runtimeId: entry.runtimeId,
930
+ targetId: entry.targetId,
931
+ wireModelId: entry.wireModelId,
932
+ ...(entry.taskSummary !== undefined ? { taskSummary: entry.taskSummary } : {}),
933
+ status: retry ? "retrying" : entry.status,
934
+ elapsedMs: resolveElapsedMs(entry, now),
935
+ tokenCount: entry.tokenCount,
936
+ costUsd: entry.costUsd,
937
+ ...(entry.costProvenance !== undefined ? { costProvenance: entry.costProvenance } : {}),
938
+ inputTokens: entry.inputTokens,
939
+ outputTokens: entry.outputTokens,
940
+ ttftMs: entry.ttftMs,
941
+ ...(entry.outcomeDetail !== undefined ? { outcomeDetail: entry.outcomeDetail } : {}),
942
+ ...(entry.node !== undefined ? { node: entry.node } : {}),
943
+ ...(entry.gate !== undefined ? { gate: { ...entry.gate } } : {}),
944
+ ...(entry.rerouteCount !== undefined ? { rerouteCount: entry.rerouteCount } : {}),
945
+ ...(entry.failoverHops !== undefined ? { failoverHops: entry.failoverHops } : {}),
946
+ ...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
947
+ lastContextTokens: entry.lastContextTokens,
948
+ ...(entry.receiptId !== undefined ? { receiptId: entry.receiptId } : {}),
949
+ currentTool: retry ? null : entry.currentTool,
950
+ recentTools: [...entry.recentTools],
951
+ ...(retry ? { retry: { ...retry } } : {}),
952
+ ...(entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}),
953
+ };
954
+ }
955
+
956
+ function sortEntries(a: DispatchBoardEntry, b: DispatchBoardEntry): number {
957
+ const aStatus = a.retry ? "retrying" : a.status;
958
+ const bStatus = b.retry ? "retrying" : b.status;
959
+ const rank = STATUS_ORDER[aStatus] - STATUS_ORDER[bStatus];
960
+ if (rank !== 0) return rank;
961
+ const aTime = a.finishedAtMs ?? a.startedAtMs ?? a.enqueuedAtMs;
962
+ const bTime = b.finishedAtMs ?? b.startedAtMs ?? b.enqueuedAtMs;
963
+ if (aTime !== bTime) return bTime - aTime;
964
+ return a.sequence - b.sequence;
965
+ }
966
+
967
+ function pruneEntries(entries: Map<string, DispatchBoardEntry>): void {
968
+ if (entries.size <= MAX_DISPATCH_BOARD_ROWS) return;
969
+ const terminalEntries = [...entries.values()]
970
+ .filter((entry) => !entry.retry && isTerminalStatus(entry.status))
971
+ .sort((a, b) => a.sequence - b.sequence);
972
+ const evictionQueue =
973
+ terminalEntries.length > 0 ? terminalEntries : [...entries.values()].sort((a, b) => a.sequence - b.sequence);
974
+ for (const entry of evictionQueue) {
975
+ if (entries.size <= MAX_DISPATCH_BOARD_ROWS) break;
976
+ entries.delete(entry.runId);
977
+ }
978
+ }
979
+
980
+ export function createDispatchBoardStore(
981
+ bus: SafeEventBus,
982
+ snapshot?: () => DispatchSnapshot,
983
+ ): {
984
+ rows(): ReadonlyArray<DispatchBoardRow>;
985
+ activeRows(): ReadonlyArray<DispatchBoardRow>;
986
+ reconcile(): void;
987
+ unsubscribe(): void;
988
+ } {
989
+ const entries = new Map<string, DispatchBoardEntry>();
990
+ let nextSequence = 0;
991
+ let reconciledAtMs = Date.now();
992
+
993
+ // Payloads arrive typed off the bus, but the board keeps its runtime
994
+ // parsing (parse* helpers) because events are not validated at runtime.
995
+ const upsertBase = (
996
+ raw: Partial<DispatchRunIdentity>,
997
+ status: DispatchBoardStatus,
998
+ now: number,
999
+ ): DispatchBoardEntry | null => {
1000
+ const runId = parseRunId(raw.runId);
1001
+ if (!runId) return null;
1002
+ const previous = entries.get(runId);
1003
+ const agentAudience = parseAgentAudience(raw.agentAudience, previous?.agentAudience);
1004
+ const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
1005
+ const node = parseNonEmptyString(raw.node) ?? previous?.node;
1006
+ const gate = parseGateBadge(raw.gate) ?? previous?.gate;
1007
+ const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
1008
+ const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
1009
+ const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
1010
+ const entry: DispatchBoardEntry = {
1011
+ runId,
1012
+ agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
1013
+ ...(agentAudience !== undefined ? { agentAudience } : {}),
1014
+ ...(requestOrigin !== undefined ? { requestOrigin } : {}),
1015
+ runtimeKind: parseRuntimeKind(raw.runtimeKind ?? previous?.runtimeKind),
1016
+ runtimeId: parseText(raw.runtimeId, previous?.runtimeId ?? "-"),
1017
+ targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
1018
+ wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
1019
+ ...(taskSummary !== undefined ? { taskSummary } : {}),
1020
+ status,
1021
+ tokenCount: previous?.tokenCount ?? 0,
1022
+ costUsd: previous?.costUsd ?? 0,
1023
+ costProvenance: previous?.costProvenance ?? "unknown",
1024
+ sequence: previous?.sequence ?? nextSequence++,
1025
+ enqueuedAtMs: previous?.enqueuedAtMs ?? now,
1026
+ startedAtMs: previous?.startedAtMs ?? null,
1027
+ startedAtClockMs: previous?.startedAtClockMs ?? null,
1028
+ finishedAtMs: previous?.finishedAtMs ?? null,
1029
+ durationMs: previous?.durationMs ?? null,
1030
+ inputTokens: previous?.inputTokens ?? 0,
1031
+ outputTokens: previous?.outputTokens ?? 0,
1032
+ ttftMs: previous?.ttftMs ?? null,
1033
+ outcomeDetail: previous?.outcomeDetail ?? null,
1034
+ ...(node !== undefined ? { node } : {}),
1035
+ ...(gate !== undefined ? { gate } : {}),
1036
+ ...(rerouteCount !== undefined ? { rerouteCount } : {}),
1037
+ ...(contextWindow !== undefined ? { contextWindow } : {}),
1038
+ lastContextTokens: previous?.lastContextTokens ?? 0,
1039
+ ...(previous?.receiptId !== undefined ? { receiptId: previous.receiptId } : {}),
1040
+ currentTool: previous?.currentTool ?? null,
1041
+ recentTools: previous?.recentTools ?? [],
1042
+ ...(previous?.retry ? { retry: { ...previous.retry } } : {}),
1043
+ ...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
1044
+ };
1045
+ entries.set(runId, entry);
1046
+ pruneEntries(entries);
1047
+ return entry;
1048
+ };
1049
+
1050
+ const unsubscribers = [
1051
+ bus.on(BusChannels.DispatchEnqueued, (raw) => {
1052
+ const entry = upsertBase(raw ?? {}, "enqueued", Date.now());
1053
+ if (entry) delete entry.retry;
1054
+ }),
1055
+ bus.on(BusChannels.DispatchStarted, (raw) => {
1056
+ const now = Date.now();
1057
+ const entry = upsertBase(raw ?? {}, "running", now);
1058
+ if (!entry) return;
1059
+ entry.startedAtMs ??= now;
1060
+ entry.startedAtClockMs ??= performance.now();
1061
+ entry.finishedAtMs = null;
1062
+ entry.durationMs = null;
1063
+ delete entry.retry;
1064
+ }),
1065
+ bus.on(BusChannels.DispatchCompleted, (raw) => {
1066
+ const now = Date.now();
1067
+ const payload: Partial<DispatchCompletedPayload> = raw ?? {};
1068
+ const entry = upsertBase(payload, "completed", now);
1069
+ if (!entry) return;
1070
+ entry.startedAtMs ??= entry.enqueuedAtMs;
1071
+ entry.finishedAtMs = now;
1072
+ entry.durationMs = parseFiniteNumber(payload.durationMs, Math.max(0, rawDurationMs(entry.startedAtMs, now)));
1073
+ entry.tokenCount = parseFiniteNumber(payload.tokenCount, entry.tokenCount);
1074
+ entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
1075
+ entry.costProvenance = payload.costProvenance ?? "unknown";
1076
+ entry.outcomeDetail = null;
1077
+ entry.currentTool = null;
1078
+ // A terminal dispatch event is published only after the run's receipt is
1079
+ // sealed at receipts/<runId>.json, so the run id is the receipt id here.
1080
+ entry.receiptId = entry.runId;
1081
+ delete entry.retry;
1082
+ if (typeof payload.inputTokenCount === "number") {
1083
+ entry.inputTokens = payload.inputTokenCount + parseFiniteNumberOrZero(payload.cacheReadTokenCount);
1084
+ }
1085
+ if (typeof payload.outputTokenCount === "number") {
1086
+ entry.outputTokens = payload.outputTokenCount;
1087
+ }
1088
+ }),
1089
+ bus.on(BusChannels.DispatchFailed, (raw) => {
1090
+ const now = Date.now();
1091
+ const payload: Partial<DispatchFailedPayload> = raw ?? {};
1092
+ const runId = parseRunId(payload.runId);
1093
+ const previousStatus = runId !== null ? entries.get(runId)?.status : undefined;
1094
+ const resolvedStatus = resolveFailedStatus(payload.reason);
1095
+ const status = previousStatus === "dead" && resolvedStatus === "failed" ? "dead" : resolvedStatus;
1096
+ const entry = upsertBase(payload, status, now);
1097
+ if (!entry) return;
1098
+ entry.startedAtMs ??= entry.enqueuedAtMs;
1099
+ entry.finishedAtMs = now;
1100
+ entry.durationMs = parseFiniteNumber(payload.durationMs, Math.max(0, rawDurationMs(entry.startedAtMs, now)));
1101
+ entry.tokenCount = parseFiniteNumber(payload.tokenCount, entry.tokenCount);
1102
+ entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
1103
+ entry.costProvenance = payload.costProvenance ?? "unknown";
1104
+ entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
1105
+ entry.currentTool = null;
1106
+ // A denied retry never reached a run, so no receipt was sealed for it;
1107
+ // every other failure finalized through recordReceipt like a success.
1108
+ if (payload.reason !== "retry_denied") entry.receiptId = entry.runId;
1109
+ delete entry.retry;
1110
+ if (typeof payload.inputTokenCount === "number") {
1111
+ entry.inputTokens = payload.inputTokenCount + parseFiniteNumberOrZero(payload.cacheReadTokenCount);
1112
+ }
1113
+ if (typeof payload.outputTokenCount === "number") {
1114
+ entry.outputTokens = payload.outputTokenCount;
1115
+ }
1116
+ }),
1117
+ bus.on(BusChannels.RunAborted, (raw) => {
1118
+ const runId = parseRunId(raw?.runId);
1119
+ if (!runId) return;
1120
+ const entry = entries.get(runId);
1121
+ if (!entry) return;
1122
+ const wasRetrying = entry.retry !== undefined;
1123
+ delete entry.retry;
1124
+ if (wasRetrying && raw?.startedAt === null) {
1125
+ // Canceling a retry timer is synchronous: there is no worker left to
1126
+ // wind down, so the history row can become terminal immediately.
1127
+ entry.status = "aborted";
1128
+ entry.finishedAtMs = Date.now();
1129
+ entry.currentTool = null;
1130
+ } else {
1131
+ if (isTerminalStatus(entry.status) && !wasRetrying) return;
1132
+ entry.status = "cancelling";
1133
+ }
1134
+ entry.outcomeDetail = parseOptionalDetail(raw?.reason) ?? entry.outcomeDetail ?? null;
1135
+ }),
1136
+ bus.on(BusChannels.DispatchProgress, (raw) => {
1137
+ const payload: Partial<DispatchProgressPayload> = raw ?? {};
1138
+ const runId = parseRunId(payload.runId);
1139
+ if (!runId) return;
1140
+ const entry = entries.get(runId);
1141
+ if (!entry) return;
1142
+ const workerEvent = (payload.event ?? {}) as WorkerEventShape;
1143
+ const type = typeof workerEvent.type === "string" ? workerEvent.type : "";
1144
+ if (type === "heartbeat_status") {
1145
+ if (isTerminalStatus(entry.status) || entry.status === "cancelling") return;
1146
+ const status = resolveHeartbeatStatus((workerEvent as { status?: unknown }).status);
1147
+ if (!status) return;
1148
+ entry.status = status;
1149
+ if (status === "dead") {
1150
+ entry.finishedAtMs ??= Date.now();
1151
+ entry.currentTool = null;
1152
+ delete entry.retry;
1153
+ }
1154
+ return;
1155
+ }
1156
+ if (type === "attempt_start") {
1157
+ // The assignment stream hands the root run a failover hop: a later
1158
+ // attempt took over, so the row is live again on the new route.
1159
+ entry.failoverHops = (entry.failoverHops ?? 0) + 1;
1160
+ entry.status = "running";
1161
+ entry.finishedAtMs = null;
1162
+ entry.currentTool = null;
1163
+ delete entry.retry;
1164
+ return;
1165
+ }
1166
+ if (type === "agent_start") {
1167
+ entry.startedAtMs = Date.now();
1168
+ entry.startedAtClockMs = performance.now();
1169
+ }
1170
+ if (type === "message_update") {
1171
+ const assistantEvent =
1172
+ "assistantMessageEvent" in workerEvent &&
1173
+ typeof workerEvent.assistantMessageEvent === "object" &&
1174
+ workerEvent.assistantMessageEvent !== null
1175
+ ? (workerEvent.assistantMessageEvent as { type?: unknown })
1176
+ : {};
1177
+ const hasDelta =
1178
+ assistantEvent.type === "text_delta" ||
1179
+ assistantEvent.type === "thinking_delta" ||
1180
+ assistantEvent.type === "toolcall_start" ||
1181
+ assistantEvent.type === "toolcall_delta";
1182
+ if (hasDelta && entry.ttftMs === null && entry.startedAtClockMs !== null) {
1183
+ entry.ttftMs = Math.round(performance.now() - entry.startedAtClockMs);
1184
+ }
1185
+ }
1186
+ if (type === "clio_tool_start") {
1187
+ const payload = (workerEvent as { payload?: { tool?: unknown } }).payload;
1188
+ const tool = parseNonEmptyString(payload?.tool);
1189
+ if (tool !== undefined) entry.currentTool = tool;
1190
+ }
1191
+ if (type === "clio_tool_finish") {
1192
+ const payload = (workerEvent as { payload?: { tool?: unknown } }).payload;
1193
+ const tool = parseNonEmptyString(payload?.tool);
1194
+ entry.currentTool = null;
1195
+ if (tool !== undefined) {
1196
+ entry.recentTools.unshift(tool);
1197
+ if (entry.recentTools.length > RECENT_TOOL_TRAIL_LIMIT) entry.recentTools.length = RECENT_TOOL_TRAIL_LIMIT;
1198
+ }
1199
+ }
1200
+ if (type === "clio_steer_received") {
1201
+ const steerPayload = (workerEvent as { payload?: { chars?: unknown } }).payload;
1202
+ entry.steerAcknowledgement = {
1203
+ receivedAtMs: Date.now(),
1204
+ chars: Math.max(0, Math.floor(parseFiniteNumberOrZero(steerPayload?.chars))),
1205
+ };
1206
+ }
1207
+ if (isTerminalStatus(entry.status)) return;
1208
+ if (type === "message_end" && workerEvent.message?.role === "assistant") {
1209
+ const usage = workerEvent.message.usage;
1210
+ const input = parseFiniteNumberOrZero(usage?.input) + parseFiniteNumberOrZero(usage?.cacheRead);
1211
+ const output = parseFiniteNumberOrZero(usage?.output);
1212
+ entry.inputTokens += input;
1213
+ entry.outputTokens += output;
1214
+ entry.tokenCount += input + output + parseFiniteNumberOrZero(usage?.cacheWrite);
1215
+ // The last assistant message's input+cacheRead+output approximates the
1216
+ // worker's current context occupancy for the per-worker meter.
1217
+ entry.lastContextTokens = input + output;
1218
+ }
1219
+ if (type === "agent_end") {
1220
+ const status = resolveAgentEndStatus(workerEvent.messages);
1221
+ if (!status) return;
1222
+ entry.status = status;
1223
+ entry.finishedAtMs ??= Date.now();
1224
+ entry.currentTool = null;
1225
+ delete entry.retry;
1226
+ }
1227
+ }),
1228
+ ];
1229
+
1230
+ let closed = false;
1231
+ const reconcile = (): void => {
1232
+ const now = Date.now();
1233
+ if (!snapshot) {
1234
+ reconciledAtMs = now;
1235
+ return;
1236
+ }
1237
+ let currentSnapshot: DispatchSnapshot;
1238
+ try {
1239
+ currentSnapshot = snapshot();
1240
+ } catch {
1241
+ // Lifecycle and progress events remain authoritative if snapshot polling fails.
1242
+ return;
1243
+ }
1244
+ if (!Array.isArray(currentSnapshot?.retrying) || !Array.isArray(currentSnapshot?.running)) return;
1245
+ reconciledAtMs = now;
1246
+ const retrying = readRetrySnapshot(currentSnapshot);
1247
+ const running = readRunningSnapshot(currentSnapshot);
1248
+ for (const entry of entries.values()) {
1249
+ if (entry.retry && !retrying.has(entry.runId)) {
1250
+ // The timer left the queue (launched or was otherwise consumed). The
1251
+ // parent attempt's original terminal state becomes visible again; the
1252
+ // successor gets its own row.
1253
+ delete entry.retry;
1254
+ }
1255
+ }
1256
+ for (const [runId, projection] of retrying) {
1257
+ const existing = entries.get(runId);
1258
+ if (existing && existing.status !== "failed" && existing.status !== "dead") {
1259
+ continue;
1260
+ }
1261
+ const entry =
1262
+ existing ??
1263
+ upsertBase(
1264
+ {
1265
+ runId,
1266
+ agentId: projection.agentId,
1267
+ ...(projection.taskSummary !== undefined ? { task: projection.taskSummary } : {}),
1268
+ },
1269
+ "failed",
1270
+ now,
1271
+ );
1272
+ if (entry) {
1273
+ entry.currentTool = null;
1274
+ entry.retry = { ...projection.retry };
1275
+ if (entry.taskSummary === undefined && projection.taskSummary !== undefined) {
1276
+ entry.taskSummary = projection.taskSummary;
1277
+ }
1278
+ }
1279
+ }
1280
+ for (const [runId, live] of running) {
1281
+ const entry = entries.get(runId);
1282
+ if (!entry) continue;
1283
+ if (isTerminalStatus(entry.status) && live.outcomePhase !== "aborting") continue;
1284
+ entry.inputTokens = live.inputTokens;
1285
+ entry.outputTokens = live.outputTokens;
1286
+ entry.tokenCount = live.tokenCount;
1287
+ entry.costUsd = live.costUsd;
1288
+ entry.costProvenance = live.costProvenance;
1289
+ if (live.outcomePhase === "aborting") {
1290
+ if (entry.status !== "completed" && entry.status !== "aborted") {
1291
+ entry.status = "cancelling";
1292
+ delete entry.retry;
1293
+ }
1294
+ }
1295
+ }
1296
+ };
1297
+
1298
+ const projectRows = (activeOnly: boolean): ReadonlyArray<DispatchBoardRow> => {
1299
+ return [...entries.values()]
1300
+ .sort(sortEntries)
1301
+ .map((entry) => toRow(entry, reconciledAtMs))
1302
+ .filter((row) => !activeOnly || !isTerminalStatus(row.status));
1303
+ };
1304
+
1305
+ return {
1306
+ rows() {
1307
+ return projectRows(false);
1308
+ },
1309
+ activeRows() {
1310
+ return projectRows(true);
1311
+ },
1312
+ reconcile,
1313
+ unsubscribe() {
1314
+ if (closed) return;
1315
+ closed = true;
1316
+ for (const unsubscribe of unsubscribers) unsubscribe();
1317
+ },
1318
+ };
1319
+ }