@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,1246 @@
1
+ import type { OutputVerbosity } from "../../core/defaults.js";
2
+ import { ToolNames } from "../../core/tool-names.js";
3
+ import {
4
+ type CostAggregate,
5
+ formatCostAggregate,
6
+ type TokenThroughputSnapshot,
7
+ type UsageBreakdown,
8
+ } from "../../domains/observability/index.js";
9
+ import type { ContextUsageBreakdown } from "../../domains/session/context-accounting.js";
10
+ import type { ContextLedger, ContextLedgerCategory } from "../../domains/session/context-ledger.js";
11
+ import { type TaskBoardSnapshot, taskBoardCounts } from "../../domains/session/task-board.js";
12
+ import { truncateToWidth, visibleWidth } from "../../engine/tui.js";
13
+ import { CONTEXT_CATEGORY_TOKEN, contextCategorySwatch, renderContextMeterBar } from "../context-meter.js";
14
+ import {
15
+ agentDisplayLabel,
16
+ type DispatchBoardRow,
17
+ dispatchOriginPresentation,
18
+ dispatchRowPrefix,
19
+ dispatchStatusPresentation,
20
+ } from "../dispatch-board.js";
21
+ import { buildSegmentedContextBar, CONTEXT_BAR_LABEL_WIDTH, formatFooterTokens } from "../footer-panel.js";
22
+ import { type AgentStatus, spinnerFrame, type TurnSummary } from "../status/index.js";
23
+ import {
24
+ type ClioTheme,
25
+ type ClioToken,
26
+ clioTheme,
27
+ fitUnits,
28
+ formatCompactMs,
29
+ formatContextPercent,
30
+ GLYPH,
31
+ joinChips,
32
+ joinSections,
33
+ sectionTag,
34
+ } from "../theme/index.js";
35
+
36
+ export interface ToolTallySnapshot {
37
+ tools: Readonly<Record<string, number>>;
38
+ errors: number;
39
+ active?: number;
40
+ truncatedResults?: number;
41
+ }
42
+
43
+ /** Live workspace facts. Owned by the footer (the welcome header no longer repeats the branch). */
44
+ export interface WorkspaceFacts {
45
+ cwd: string;
46
+ branch: string | null;
47
+ dirty: boolean | null;
48
+ projectType: string | null;
49
+ remote: string | null;
50
+ }
51
+
52
+ export interface SessionFacts {
53
+ name: string | null;
54
+ id: string | null;
55
+ version: string;
56
+ turns: number | null;
57
+ tokens: string | null;
58
+ throughput: string | null;
59
+ throughputDetail: string | null;
60
+ cost: string | null;
61
+ target: string | null;
62
+ thinking: string | null;
63
+ capabilities: string[] | null;
64
+ safety: string | null;
65
+ toolProfile: string | null;
66
+ /** Active transcript detail mode, shown in the dashboard so visibility is never implicit. */
67
+ outputVerbosity?: OutputVerbosity | null;
68
+ /**
69
+ * Ctrl+G armed the portable leader and is waiting for the next key. Shown
70
+ * because the frame between the two keystrokes was otherwise identical to
71
+ * the idle frame, on the fallback whose users have no working Alt to check
72
+ * it against.
73
+ */
74
+ leaderArmed?: boolean;
75
+ /** Proactive-memory status; kept as one atomic fact row in the expanded dashboard. */
76
+ memoryIntervention?: {
77
+ enabled: boolean;
78
+ tier: "rules" | "llm";
79
+ size: number;
80
+ stepInFlight?: boolean;
81
+ lastDecision?: string | null;
82
+ } | null;
83
+ }
84
+
85
+ /** Context engine telemetry. */
86
+ export interface ContextEngineFacts {
87
+ label: string | null;
88
+ used: number | null;
89
+ contextWindow: number | null;
90
+ toolSchemaTokens: number | null;
91
+ compactionThreshold: number | null;
92
+ compactionAuto: boolean | null;
93
+ compactionActive?: boolean;
94
+ clioMd: string | null;
95
+ memory: string | null;
96
+ extensions: { active: number; installed: number } | null;
97
+ breakdown?: ContextUsageBreakdown | null;
98
+ /** Full categorized ledger; when present the quadrant renders the richer meter. */
99
+ ledger?: ContextLedger | null;
100
+ }
101
+
102
+ /** Dynamic agent work: the live action quadrant. */
103
+ export interface AgentWorkFacts {
104
+ statusText: string | null;
105
+ dispatchSummary: string | null;
106
+ toolTally: string;
107
+ dispatchRows: ReadonlyArray<DispatchBoardRow>;
108
+ contextActivity?: {
109
+ message: string;
110
+ detail: string | null;
111
+ status: "started" | "running" | "completed" | "failed";
112
+ } | null;
113
+ /** Metrics for the most recent completed turn, surfaced when the agent is idle. */
114
+ lastTurn: TurnSummary | null;
115
+ /** Session task board declared through the tasks tool; null before any plan. */
116
+ taskBoard?: TaskBoardSnapshot | null;
117
+ }
118
+
119
+ /** Responsive bands for the expanded footer. */
120
+ export const EXPANDED_WIDE = 80;
121
+ export const EXPANDED_MID = 70;
122
+ export const EXPANDED_ULTRAWIDE = 120;
123
+
124
+ /** Compact footer shows the git section only when there is room for it. */
125
+ const COMPACT_GIT_MIN_WIDTH = 72;
126
+
127
+ /**
128
+ * The `visibleWidth` guard looks redundant against `truncateToWidth`, which
129
+ * measures internally, but it is not: dropping it pads wide-char truncations
130
+ * with a trailing space and strips a bare ANSI reset at width 0. Both are
131
+ * visible output, so the extra measurement stays.
132
+ */
133
+ export function fitDashboardLine(line: string, width: number): string {
134
+ return visibleWidth(line) > width ? truncateToWidth(line, width, "…", true) : line;
135
+ }
136
+
137
+ /**
138
+ * Pad or truncate an already styled string to an exact column width. A cut is
139
+ * marked with an ellipsis so a clipped value ("proj 1.", "read 14 · bash 9 ")
140
+ * never reads as a complete fact.
141
+ */
142
+ function cell(text: string, width: number): string {
143
+ const safe = Math.max(0, width);
144
+ const clipped = truncateToWidth(text, safe, "…", true);
145
+ return `${clipped}${" ".repeat(Math.max(0, safe - visibleWidth(clipped)))}`;
146
+ }
147
+
148
+ function joinColumns(left: string, right: string, width: number): string {
149
+ const safe = Math.max(0, Math.floor(width));
150
+ if (safe === 0) return "";
151
+ if (visibleWidth(right) === 0) return cell(left, safe);
152
+ const rightWidth = visibleWidth(right);
153
+ if (rightWidth >= safe) return cell(right, safe);
154
+ const leftBudget = Math.max(0, safe - rightWidth - 1);
155
+ const fittedLeft = visibleWidth(left) > leftBudget ? truncateToWidth(left, leftBudget, "…", true) : left;
156
+ const gap = Math.max(1, safe - visibleWidth(fittedLeft) - rightWidth);
157
+ return cell(`${fittedLeft}${" ".repeat(gap)}${right}`, safe);
158
+ }
159
+
160
+ function finiteNonNegative(value: number | null | undefined): number {
161
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
162
+ }
163
+
164
+ /**
165
+ * The tools row answers two different questions and used to answer only the
166
+ * second: what the model can reach, and what it has called. `registered` is the
167
+ * count of tool schemas sent to the provider this turn, so a session that has
168
+ * only dispatched still reports the tools it holds instead of reading "none",
169
+ * which said the model had no tools at all.
170
+ */
171
+ export function formatToolTally(snapshot: ToolTallySnapshot | null | undefined, registered?: number | null): string {
172
+ const available =
173
+ typeof registered === "number" && Number.isFinite(registered) && registered > 0
174
+ ? `${Math.floor(registered)} avail`
175
+ : null;
176
+ if (!snapshot) return `${available ?? "none"} · 0${GLYPH.error}`;
177
+ const entries = Object.entries(snapshot.tools)
178
+ .filter(([name, count]) => count > 0 && name.toLowerCase() !== "dispatch")
179
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
180
+ .slice(0, 4)
181
+ .map(([name, count]) => `${name} ${formatFooterTokens(count)}`);
182
+ const called = entries.length > 0 ? entries.join(" · ") : available !== null ? null : "none";
183
+ const prefix = [available, called].filter((part): part is string => part !== null).join(" · ");
184
+ const active =
185
+ typeof snapshot.active === "number" && snapshot.active > 0 ? ` · active ${formatFooterTokens(snapshot.active)}` : "";
186
+ const truncated =
187
+ typeof snapshot.truncatedResults === "number" && snapshot.truncatedResults > 0
188
+ ? ` · trunc ${formatFooterTokens(snapshot.truncatedResults)}`
189
+ : "";
190
+ return `${prefix}${active}${truncated} · ${formatFooterTokens(snapshot.errors)}${GLYPH.error}`;
191
+ }
192
+
193
+ export function formatUsd(value: number): string {
194
+ if (!Number.isFinite(value) || value <= 0) return "$0.00";
195
+ if (value < 0.01) return `$${value.toFixed(4)}`;
196
+ return `$${value.toFixed(2)}`;
197
+ }
198
+
199
+ function gitMarker(theme: ClioTheme, dirty: boolean | null): string {
200
+ if (dirty === false) return theme.fg("success", "✓");
201
+ if (dirty === true) return theme.fg("warning", "!");
202
+ return theme.fg("dim", "?");
203
+ }
204
+
205
+ /** Git chip carrying a `git` label so a version-shaped branch never reads as a duplicate version. */
206
+ function gitChip(theme: ClioTheme, branch: string | null, dirty: boolean | null): string | null {
207
+ if (!branch) return null;
208
+ return `${theme.fg("dim", "git ")}${theme.fg("success", branch)} ${gitMarker(theme, dirty)}`;
209
+ }
210
+
211
+ function gitValue(theme: ClioTheme, branch: string | null, dirty: boolean | null): string | null {
212
+ if (!branch) return null;
213
+ return `${theme.fg("success", branch)} ${gitMarker(theme, dirty)}`;
214
+ }
215
+
216
+ /** `github.com/owner/repo` → `owner/repo`; otherwise the host or the raw value, trimmed. */
217
+ function collapseRemote(remote: string | null): string | null {
218
+ if (!remote) return null;
219
+ const cleaned = remote
220
+ .replace(/^git@/, "")
221
+ .replace(/^[a-z]+:\/\//, "")
222
+ .replace(/\.git$/, "");
223
+ const parts = cleaned.split(/[/:]/).filter(Boolean);
224
+ if (parts.length >= 2) return parts.slice(-2).join("/");
225
+ return parts[0] ?? null;
226
+ }
227
+
228
+ /**
229
+ * Compact primary row: workspace identity on the left and a meaningful work
230
+ * phase on the right. The editor rail owns model and thinking labels. The
231
+ * branch appears here, and only here, across the whole screen.
232
+ */
233
+ export function compactPrimaryLine(
234
+ workspace: WorkspaceFacts,
235
+ _session: SessionFacts,
236
+ width: number,
237
+ theme: ClioTheme = clioTheme(),
238
+ status: AgentStatus = {
239
+ phase: "idle",
240
+ since: 0,
241
+ lastMeaningfulAt: 0,
242
+ watchdogTier: 0,
243
+ watchdogPeak: 0,
244
+ localRuntime: false,
245
+ },
246
+ toolCounts: ToolTallySnapshot = { tools: {}, errors: 0 },
247
+ dispatchRows: ReadonlyArray<DispatchBoardRow> = [],
248
+ tick = 0,
249
+ now = Date.now(),
250
+ ): string {
251
+ const safeWidth = Math.max(1, Math.floor(width));
252
+ let git = safeWidth >= COMPACT_GIT_MIN_WIDTH ? gitChip(theme, workspace.branch, workspace.dirty) : null;
253
+ let right = buildHarnessStatePill(theme, status, toolCounts, dispatchRows, tick, now, safeWidth, true);
254
+ let left = joinSections(theme, [theme.fg("muted", workspace.cwd), git]);
255
+
256
+ if (visibleWidth(left) + 1 + visibleWidth(right) > safeWidth) {
257
+ right = buildHarnessStatePill(theme, status, toolCounts, dispatchRows, tick, now, safeWidth, false);
258
+ }
259
+
260
+ if (git && visibleWidth(left) + 1 + visibleWidth(right) > safeWidth) {
261
+ git = null;
262
+ left = theme.fg("muted", workspace.cwd);
263
+ }
264
+
265
+ if (visibleWidth(left) + 1 + visibleWidth(right) > safeWidth) {
266
+ const maxCwdWidth = Math.max(1, safeWidth - visibleWidth(right) - 1);
267
+ left = theme.fg("muted", truncateToWidth(workspace.cwd, maxCwdWidth, "…", true));
268
+ }
269
+
270
+ return joinColumns(left, right, safeWidth);
271
+ }
272
+
273
+ /**
274
+ * Compact secondary row: context fill + the single most important activity on
275
+ * the left, dispatch summary + tool tally on the right.
276
+ */
277
+ function compactMetricChipLimit(width: number): number {
278
+ if (width < 48) return 1;
279
+ if (width < 72) return 2;
280
+ if (width < 100) return 4;
281
+ return 6;
282
+ }
283
+
284
+ function contextBarCellBounds(width: number): { min: number; max: number } {
285
+ if (width < 48) return { min: 6, max: 6 };
286
+ if (width < 72) return { min: 8, max: 8 };
287
+ if (width < 100) return { min: 12, max: 12 };
288
+ return { min: 14, max: 16 };
289
+ }
290
+
291
+ function compactContextBarWidth(width: number): number {
292
+ const bounds = contextBarCellBounds(width);
293
+ const leftHalfBudget = Math.max(0, Math.floor(width / 2));
294
+ const budgetCells = Math.max(0, leftHalfBudget - CONTEXT_BAR_LABEL_WIDTH);
295
+ const wideScale = width >= 100 ? 14 + Math.min(2, Math.max(0, Math.floor((width - 100) / 10))) : bounds.max;
296
+ return Math.max(bounds.min, Math.min(bounds.max, budgetCells, wideScale));
297
+ }
298
+
299
+ function contextBreakdownForBar(context: ContextEngineFacts): ContextUsageBreakdown | undefined {
300
+ const reportedUsed = finiteNonNegative(context.used);
301
+ const toolTokens = finiteNonNegative(context.toolSchemaTokens);
302
+ const source = context.breakdown;
303
+ if (!source) {
304
+ if (reportedUsed <= 0 && toolTokens <= 0) return undefined;
305
+ return {
306
+ systemPromptTokens: 0,
307
+ toolSchemaTokens: Math.min(toolTokens, reportedUsed),
308
+ messageTokens: Math.max(0, reportedUsed - toolTokens),
309
+ pendingUserTokens: 0,
310
+ };
311
+ }
312
+ const system = finiteNonNegative(source.systemPromptTokens);
313
+ const tools = finiteNonNegative(source.toolSchemaTokens);
314
+ const conversation = finiteNonNegative(source.messageTokens) + finiteNonNegative(source.pendingUserTokens);
315
+ const total = system + tools + conversation;
316
+ if (reportedUsed <= 0 || total <= 0) {
317
+ return {
318
+ systemPromptTokens: system,
319
+ toolSchemaTokens: tools,
320
+ messageTokens: conversation,
321
+ pendingUserTokens: 0,
322
+ };
323
+ }
324
+ if (reportedUsed >= total) {
325
+ return {
326
+ systemPromptTokens: system,
327
+ toolSchemaTokens: tools,
328
+ messageTokens: conversation + (reportedUsed - total),
329
+ pendingUserTokens: 0,
330
+ };
331
+ }
332
+ const scale = reportedUsed / total;
333
+ return {
334
+ systemPromptTokens: system * scale,
335
+ toolSchemaTokens: tools * scale,
336
+ messageTokens: conversation * scale,
337
+ pendingUserTokens: 0,
338
+ };
339
+ }
340
+
341
+ function contextComposition(context: ContextEngineFacts): {
342
+ system: number;
343
+ tools: number;
344
+ chat: number;
345
+ free: number | null;
346
+ } {
347
+ const used = finiteNonNegative(context.used);
348
+ const window = finiteNonNegative(context.contextWindow);
349
+ const breakdown = contextBreakdownForBar(context);
350
+ return {
351
+ system: finiteNonNegative(breakdown?.systemPromptTokens),
352
+ tools: finiteNonNegative(breakdown?.toolSchemaTokens),
353
+ chat: finiteNonNegative(breakdown?.messageTokens) + finiteNonNegative(breakdown?.pendingUserTokens),
354
+ free: window > 0 ? Math.max(0, window - used) : null,
355
+ };
356
+ }
357
+
358
+ export function compactSecondaryLine(
359
+ context: ContextEngineFacts,
360
+ agent: AgentWorkFacts,
361
+ width: number,
362
+ theme: ClioTheme = clioTheme(),
363
+ status: AgentStatus = {
364
+ phase: "idle",
365
+ since: 0,
366
+ lastMeaningfulAt: 0,
367
+ watchdogTier: 0,
368
+ watchdogPeak: 0,
369
+ localRuntime: false,
370
+ },
371
+ throughput: TokenThroughputSnapshot | null = null,
372
+ sessionTokens: UsageBreakdown | null = null,
373
+ sessionCost: CostAggregate | null = null,
374
+ outputVerbosity?: OutputVerbosity | null,
375
+ leaderArmed = false,
376
+ ): string {
377
+ const safeWidth = Math.max(1, Math.floor(width));
378
+ const barCells = compactContextBarWidth(safeWidth);
379
+ let left = "";
380
+ if (context.ledger) {
381
+ // The row follows the kv grammar: dim key, muted value. An unmeasured
382
+ // percent renders the ?% placeholder dim because it is scaffolding for a
383
+ // number that has not arrived, not a measurement.
384
+ const percent = theme.fg(
385
+ context.ledger.percent !== null ? "muted" : "dim",
386
+ formatContextPercent(context.ledger.percent),
387
+ );
388
+ left = `${theme.fg("dim", "ctx")} ${renderContextMeterBar(context.ledger, barCells, theme)} ${percent}`;
389
+ } else {
390
+ left = buildSegmentedContextBar(theme, barCells, context.contextWindow ?? 0, contextBreakdownForBar(context));
391
+ }
392
+ const maxRightWidth = Math.max(0, safeWidth - visibleWidth(left) - 1);
393
+ const right = buildMetricStrip(
394
+ theme,
395
+ status,
396
+ throughput,
397
+ agent.lastTurn,
398
+ sessionTokens,
399
+ sessionCost,
400
+ context.used ?? undefined,
401
+ maxRightWidth,
402
+ compactMetricChipLimit(safeWidth),
403
+ outputVerbosity,
404
+ leaderArmed,
405
+ );
406
+ // At the smallest widths the context meter consumes the entire secondary
407
+ // row, so keep a compact mode marker on the left instead of silently hiding
408
+ // the active transcript setting.
409
+ if (outputVerbosity && outputVerbosity !== "default" && visibleWidth(right) === 0) {
410
+ const marker = outputVerbosity === "minimal" ? "m" : outputVerbosity === "verbose" ? "v" : "d";
411
+ left = `${left}${theme.fg("dim", ` out:${marker}`)}`;
412
+ }
413
+ return joinColumns(left, right, safeWidth);
414
+ }
415
+
416
+ type DashboardRow =
417
+ | { kind: "kv"; key: string; value: string | null | undefined; valueToken?: ClioToken; styled?: boolean }
418
+ | { kind: "status"; value: string | null | undefined }
419
+ | { kind: "legend"; value: string | null | undefined };
420
+
421
+ interface ExpandedQuadrantOptions {
422
+ width?: number;
423
+ }
424
+
425
+ function renderDashboardRow(theme: ClioTheme, row: DashboardRow, keyWidth: number): string | null {
426
+ if (!row.value) return null;
427
+ if (row.kind !== "kv") return row.value;
428
+ const key = theme.fg("dim", `${row.key.padEnd(keyWidth)} `);
429
+ const value = row.styled ? row.value : theme.fg(row.valueToken ?? "muted", row.value);
430
+ return `${key}${value}`;
431
+ }
432
+
433
+ function dashboardBlock(theme: ClioTheme, label: string, rows: ReadonlyArray<DashboardRow>): string[] {
434
+ const keyWidth = rows.reduce((max, row) => (row.kind === "kv" ? Math.max(max, row.key.length) : max), 0);
435
+ const body = rows
436
+ .map((row) => renderDashboardRow(theme, row, keyWidth))
437
+ .filter((row): row is string => typeof row === "string" && row.length > 0);
438
+ // Every quadrant tag shares one structure color. The tag names the quadrant;
439
+ // the color is not a per-quadrant signal, so all four render bold accentDeep
440
+ // rather than the old info/accent/reason/success carnival.
441
+ return [sectionTag(theme, "accentDeep", label.toUpperCase(), 0), ...body];
442
+ }
443
+
444
+ function kv(key: string, value: string | null | undefined, valueToken: ClioToken = "muted"): DashboardRow {
445
+ return { kind: "kv", key, value, valueToken };
446
+ }
447
+
448
+ function styledKv(key: string, value: string | null | undefined): DashboardRow {
449
+ return { kind: "kv", key, value, styled: true };
450
+ }
451
+
452
+ function statusRow(value: string | null | undefined): DashboardRow {
453
+ return { kind: "status", value };
454
+ }
455
+
456
+ function legendRow(value: string | null | undefined): DashboardRow {
457
+ return { kind: "legend", value };
458
+ }
459
+
460
+ export function workspaceQuadrant(facts: WorkspaceFacts, _options: ExpandedQuadrantOptions = {}): string[] {
461
+ const theme = clioTheme();
462
+ const remote = collapseRemote(facts.remote);
463
+ return dashboardBlock(theme, "Workspace", [
464
+ kv("cwd", facts.cwd),
465
+ styledKv("git", gitValue(theme, facts.branch, facts.dirty)),
466
+ kv("type", facts.projectType),
467
+ kv("remote", remote),
468
+ ]);
469
+ }
470
+
471
+ function sessionIdentity(facts: SessionFacts): { key: string; value: string } | null {
472
+ if (facts.id) return { key: "id", value: facts.id };
473
+ if (facts.name) return { key: "name", value: facts.name };
474
+ return null;
475
+ }
476
+
477
+ function capabilitiesValue(theme: ClioTheme, capabilities: string[] | null): string | null {
478
+ if (!capabilities || capabilities.length === 0) return null;
479
+ return joinChips(
480
+ theme,
481
+ capabilities.map((capability) => theme.fg("muted", capability)),
482
+ );
483
+ }
484
+
485
+ export function sessionQuadrant(facts: SessionFacts, options: ExpandedQuadrantOptions = {}): string[] {
486
+ const theme = clioTheme();
487
+ const identity = sessionIdentity(facts);
488
+ const memory = facts.memoryIntervention;
489
+ const memoryValue = memory
490
+ ? fitUnits(
491
+ theme,
492
+ "",
493
+ [
494
+ theme.fg(memory.enabled ? "success" : "dim", memory.enabled ? "on" : "off"),
495
+ theme.fg(memory.tier === "llm" ? "reason" : "muted", `tier ${memory.tier === "llm" ? "LLM" : "rules"}`),
496
+ theme.fg("muted", `bank ${memory.size}`),
497
+ // A background step runs for tens of seconds on a small local model.
498
+ // Saying so is the difference between a quiet feature and a dead one.
499
+ ...(memory.stepInFlight ? [theme.fg("reason", "working")] : []),
500
+ ...(memory.lastDecision ? [theme.fg("dim", memory.lastDecision)] : []),
501
+ ],
502
+ Math.max(1, (options.width ?? Number.POSITIVE_INFINITY) - 9),
503
+ )
504
+ : null;
505
+ return dashboardBlock(theme, "Session", [
506
+ identity ? kv(identity.key, identity.value, "accent") : statusRow(null),
507
+ kv("target", facts.target, "accent"),
508
+ kv("think", facts.thinking, "reason"),
509
+ styledKv("caps", capabilitiesValue(theme, facts.capabilities)),
510
+ // accentDeep is a structure color reserved for the section tag; the autonomy
511
+ // value is a plain fact and reads muted like the other neutral values.
512
+ kv("autonomy", facts.safety),
513
+ kv("profile", facts.toolProfile),
514
+ kv(
515
+ "output",
516
+ facts.outputVerbosity && facts.outputVerbosity !== "default" ? facts.outputVerbosity : null,
517
+ facts.outputVerbosity === "verbose" ? "accent" : "muted",
518
+ ),
519
+ styledKv("memory", memoryValue),
520
+ ]);
521
+ }
522
+
523
+ function expandedContextBarCells(width: number | undefined): number {
524
+ if (typeof width !== "number" || !Number.isFinite(width) || width <= 0) return 12;
525
+ const budget = Math.max(6, Math.floor(width) - CONTEXT_BAR_LABEL_WIDTH - 1);
526
+ // A wider quadrant earns a finer meter: every extra cell is real resolution,
527
+ // up to 24 cells so the bar never dwarfs the facts beneath it.
528
+ const desired = width >= 48 ? 24 : width >= 36 ? 16 : width >= 32 ? 14 : 12;
529
+ return Math.max(8, Math.min(24, desired, budget));
530
+ }
531
+
532
+ function formatUsedWindow(used: number | null, contextWindow: number | null): string | null {
533
+ if (used === null || !contextWindow) return null;
534
+ return `${formatFooterTokens(used)} / ${formatFooterTokens(contextWindow)}`;
535
+ }
536
+
537
+ function formatCompaction(facts: ContextEngineFacts): string | null {
538
+ if (facts.compactionThreshold === null) return null;
539
+ const mode = facts.compactionAuto ? "auto" : "manual";
540
+ const threshold = Math.round(facts.compactionThreshold * 100);
541
+ return `${facts.compactionActive ? "active " : ""}${mode} @${threshold}%`;
542
+ }
543
+
544
+ function sourceState(theme: ClioTheme, facts: ContextEngineFacts): string | null {
545
+ const value = joinChips(theme, [
546
+ facts.clioMd ? theme.fg("muted", facts.clioMd) : null,
547
+ facts.memory ? theme.fg("muted", facts.memory) : null,
548
+ ]);
549
+ return value.length > 0 ? value : null;
550
+ }
551
+
552
+ /** Short labels for the dense footer; the overlay carries the full names. */
553
+ const CONTEXT_SHORT_LABEL: Readonly<Record<ContextLedgerCategory, string>> = {
554
+ system: "sys",
555
+ tools: "tools",
556
+ agents: "agt",
557
+ skills: "skl",
558
+ memory: "mem",
559
+ project: "proj",
560
+ messages: "chat",
561
+ pending: "input",
562
+ reserve: "rsv",
563
+ free: "free",
564
+ streaming: "stream",
565
+ };
566
+
567
+ /** Static-side cost chips (system prompt, tools, agents, skills, memory, project), heaviest first. */
568
+ function ledgerSystemChips(theme: ClioTheme, ledger: ContextLedger): string {
569
+ const statics = new Set<ContextLedgerCategory>(["system", "tools", "agents", "skills", "memory", "project"]);
570
+ const chips = ledger.groups
571
+ .filter((group) => statics.has(group.category))
572
+ .sort((a, b) => b.tokens - a.tokens)
573
+ .slice(0, 4)
574
+ .map((group) =>
575
+ theme.fg(
576
+ CONTEXT_CATEGORY_TOKEN[group.category],
577
+ `${CONTEXT_SHORT_LABEL[group.category]} ${formatFooterTokens(group.tokens)}`,
578
+ ),
579
+ );
580
+ return joinChips(theme, chips);
581
+ }
582
+
583
+ /** Conversation, autocompact reserve, and free-space chips. */
584
+ function ledgerChatChips(theme: ClioTheme, ledger: ContextLedger): string {
585
+ const chat = ledger.groups.find((group) => group.category === "messages")?.tokens ?? 0;
586
+ return joinChips(theme, [
587
+ theme.fg("accent", `chat ${formatFooterTokens(chat)}`),
588
+ ledger.reserveTokens > 0 ? theme.fg("dim", `rsv ${formatFooterTokens(ledger.reserveTokens)}`) : null,
589
+ ledger.contextWindow > 0
590
+ ? theme.style("frame", `free ${formatFooterTokens(ledger.freeTokens)}`, { dim: true })
591
+ : null,
592
+ ]);
593
+ }
594
+
595
+ /**
596
+ * Swatch legend covering exactly the categories present in the meter, packed
597
+ * into as many rows as the quadrant needs. A ledger can carry ten categories,
598
+ * more than one quadrant row holds, and dropping tail entries would hide
599
+ * whole categories, so the legend wraps by whole chips instead of clipping.
600
+ */
601
+ function ledgerLegendRows(theme: ClioTheme, ledger: ContextLedger, width: number | undefined): string[] {
602
+ const chips = ledger.meter.map((group) => {
603
+ const labelToken: ClioToken = group.category === "free" || group.category === "reserve" ? "dim" : "muted";
604
+ return `${contextCategorySwatch(group.category, theme)} ${theme.fg(labelToken, CONTEXT_SHORT_LABEL[group.category])}`;
605
+ });
606
+ const budget =
607
+ typeof width === "number" && Number.isFinite(width) && width > 0 ? Math.floor(width) : Number.POSITIVE_INFINITY;
608
+ const rows: string[] = [];
609
+ let current = "";
610
+ for (const chip of chips) {
611
+ const candidate = current.length > 0 ? `${current} ${chip}` : chip;
612
+ if (current.length > 0 && visibleWidth(candidate) > budget) {
613
+ rows.push(current);
614
+ current = chip;
615
+ } else {
616
+ current = candidate;
617
+ }
618
+ }
619
+ if (current.length > 0) rows.push(current);
620
+ return rows;
621
+ }
622
+
623
+ function ledgerBar(theme: ClioTheme, ledger: ContextLedger, cells: number): string {
624
+ const percent = theme.fg(ledger.percent !== null ? "muted" : "dim", formatContextPercent(ledger.percent));
625
+ return `${renderContextMeterBar(ledger, cells, theme)} ${percent}`;
626
+ }
627
+
628
+ export function contextQuadrant(facts: ContextEngineFacts, options: ExpandedQuadrantOptions = {}): string[] {
629
+ const theme = clioTheme();
630
+ const ledger = facts.ledger ?? null;
631
+ const hasLedger = ledger !== null && ledger.contextWindow > 0;
632
+ const barCells = expandedContextBarCells(options.width);
633
+
634
+ let bar: string;
635
+ let fill: string;
636
+ let chatFree: string;
637
+ let legendRows: string[];
638
+ if (hasLedger && ledger) {
639
+ bar = ledgerBar(theme, ledger, barCells);
640
+ fill = ledgerSystemChips(theme, ledger);
641
+ chatFree = ledgerChatChips(theme, ledger);
642
+ legendRows = ledgerLegendRows(theme, ledger, options.width);
643
+ } else {
644
+ const composition = contextComposition(facts);
645
+ fill = joinChips(theme, [
646
+ composition.system > 0 ? theme.fg("info", `sys ${formatFooterTokens(composition.system)}`) : null,
647
+ composition.tools > 0 ? theme.fg("warning", `tools ${formatFooterTokens(composition.tools)}`) : null,
648
+ ]);
649
+ chatFree = joinChips(theme, [
650
+ composition.chat > 0 ? theme.fg("accent", formatFooterTokens(composition.chat)) : null,
651
+ composition.free !== null
652
+ ? theme.style("frame", `free ${formatFooterTokens(composition.free)}`, { dim: true })
653
+ : null,
654
+ ]);
655
+ bar = buildSegmentedContextBar(theme, barCells, facts.contextWindow ?? 0, contextBreakdownForBar(facts));
656
+ const filledChar = visibleWidth(GLYPH.contextFull) === 1 ? GLYPH.contextFull : GLYPH.barFull;
657
+ const freeChar = visibleWidth(GLYPH.contextFree) === 1 ? GLYPH.contextFree : GLYPH.barEmpty;
658
+ legendRows = [
659
+ `${theme.fg("info", `${filledChar} sys`)} ${theme.fg("warning", `${filledChar} tools`)} ${theme.fg("accent", `${filledChar} chat`)} ${theme.style("frame", `${freeChar} free`, { dim: true })}`,
660
+ ];
661
+ }
662
+
663
+ const usedTokens = hasLedger && ledger ? ledger.usedTokens : facts.used;
664
+ const windowTokens = hasLedger && ledger ? ledger.contextWindow : facts.contextWindow;
665
+ return dashboardBlock(theme, "Context", [
666
+ statusRow(bar),
667
+ kv("used", formatUsedWindow(usedTokens, windowTokens)),
668
+ fill ? styledKv("fill", fill) : statusRow(null),
669
+ chatFree ? styledKv("chat", chatFree) : statusRow(null),
670
+ kv("compact", formatCompaction(facts)),
671
+ styledKv("source", sourceState(theme, facts)),
672
+ facts.extensions && facts.extensions.installed > 0
673
+ ? kv("ext", `${facts.extensions.active}/${facts.extensions.installed}`)
674
+ : statusRow(null),
675
+ ...legendRows.map((row) => legendRow(row)),
676
+ ]);
677
+ }
678
+
679
+ function stopReasonStyle(reason: TurnSummary["stopReason"]): { glyph: string; token: ClioToken } {
680
+ if (reason === "error") return { glyph: GLYPH.error, token: "error" };
681
+ if (reason === "aborted" || reason === "cancelled") return { glyph: GLYPH.cancelled, token: "dim" };
682
+ if (reason === "length") return { glyph: GLYPH.warn, token: "warning" };
683
+ return { glyph: GLYPH.ok, token: "success" };
684
+ }
685
+
686
+ /**
687
+ * Elegant single-line readout of the most recent completed turn. This is the
688
+ * footer home for the metrics that used to print faintly under each assistant
689
+ * reply: stop outcome, wall time, token in/out, reasoning, and tool work. The
690
+ * model is intentionally omitted because the editor rail already carries it.
691
+ */
692
+ export function formatLastTurn(theme: ClioTheme, summary: TurnSummary): string {
693
+ const stop = stopReasonStyle(summary.stopReason);
694
+ const parts: string[] = [
695
+ theme.fg(stop.token, `${stop.glyph} ${formatCompactMs(summary.elapsedMs)}`),
696
+ theme.fg("muted", `${GLYPH.up}${summary.inputTokens} ${GLYPH.down}${summary.outputTokens}`),
697
+ ];
698
+ // Zero suppresses the chip, the same rule the chat panel's turn line follows:
699
+ // a turn that spent no reasoning tokens states nothing by printing `r0`.
700
+ if (typeof summary.reasoningTokens === "number" && summary.reasoningTokens > 0) {
701
+ const marker =
702
+ summary.reasoningTokenProvenance === "estimated" || summary.reasoningTokenProvenance === "mixed" ? "≈" : "";
703
+ parts.push(theme.fg("reason", `r${marker}${summary.reasoningTokens}`));
704
+ }
705
+ if (summary.cacheReadTokens > 0 || summary.cacheWriteTokens > 0) {
706
+ parts.push(theme.fg("dim", `cache ${summary.cacheReadTokens}/${summary.cacheWriteTokens}`));
707
+ }
708
+ if (summary.toolCount > 0) {
709
+ const label = `${summary.toolCount} tool${summary.toolCount === 1 ? "" : "s"}`;
710
+ const errors = summary.toolErrorCount > 0 ? theme.fg("error", ` ${summary.toolErrorCount}${GLYPH.error}`) : "";
711
+ parts.push(`${theme.fg("muted", label)}${errors}`);
712
+ }
713
+ if (summary.watchdogPeak >= 2) parts.push(theme.fg("warning", "slow"));
714
+ if (summary.truncated) parts.push(theme.fg("warning", "trunc"));
715
+ return parts.join(theme.fg("dim", " · "));
716
+ }
717
+
718
+ /**
719
+ * One row per live worker: the origin glyph, the sub-process glyph when Clio
720
+ * started the run for itself, the agent's own name, the fleet node it landed
721
+ * on, its status glyph, elapsed, and the receipt id once the run has sealed
722
+ * one. Units are fitted whole, so a narrow quadrant closes on a dim ellipsis
723
+ * rather than clipping a receipt id into a string that still reads like a valid
724
+ * trace argument.
725
+ */
726
+ function workerLine(theme: ClioTheme, row: DispatchBoardRow, width: number): string {
727
+ const presentation = dispatchStatusPresentation(row.status, { compact: true });
728
+ // The Activity section already promotes the fleet summary (or dispatch phase)
729
+ // to action orange. Worker rows remain readable without repeating that signal.
730
+ const token = presentation.token === "action" ? "accent" : presentation.token;
731
+ const units = [
732
+ theme.fg("muted", agentDisplayLabel(row)),
733
+ theme.fg("dim", row.node ?? "local"),
734
+ theme.fg(token, presentation.glyph),
735
+ theme.fg("dim", formatCompactMs(row.elapsedMs)),
736
+ ...(row.receiptId !== undefined ? [theme.fg("dim", row.receiptId)] : []),
737
+ ];
738
+ return fitUnits(theme, dispatchRowPrefix(theme, row).text, units, Math.max(1, Math.floor(width)));
739
+ }
740
+
741
+ interface ActivityQuadrantOptions extends ExpandedQuadrantOptions {
742
+ status?: AgentStatus;
743
+ toolCounts?: ToolTallySnapshot;
744
+ throughput?: TokenThroughputSnapshot | null;
745
+ sessionTokens?: UsageBreakdown | null;
746
+ sessionCost?: CostAggregate | null;
747
+ contextUsed?: number | null;
748
+ tick?: number;
749
+ now?: number;
750
+ maxWorkers?: number;
751
+ }
752
+
753
+ function defaultIdleStatus(): AgentStatus {
754
+ return {
755
+ phase: "idle",
756
+ since: 0,
757
+ lastMeaningfulAt: 0,
758
+ watchdogTier: 0,
759
+ watchdogPeak: 0,
760
+ localRuntime: false,
761
+ };
762
+ }
763
+
764
+ function formattedThroughput(theme: ClioTheme, throughput: TokenThroughputSnapshot | null | undefined): string | null {
765
+ const tps = finiteNonNegative(throughput?.tokensPerSecond);
766
+ if (tps <= 0) return null;
767
+ const rounded = tps >= 10 ? Math.round(tps) : Math.round(tps * 10) / 10;
768
+ const parts = [theme.fg("success", `${GLYPH.speed}${rounded}/s`)];
769
+ const ttft = finiteNonNegative(throughput?.ttftMs);
770
+ if (ttft > 0) parts.push(theme.fg("muted", `ttft ${formatCompactMs(ttft)}`));
771
+ return joinChips(theme, parts);
772
+ }
773
+
774
+ function liveTokenValue(
775
+ theme: ClioTheme,
776
+ status: AgentStatus,
777
+ throughput: TokenThroughputSnapshot | null | undefined,
778
+ lastTurn: TurnSummary | null,
779
+ sessionTokens: UsageBreakdown | null | undefined,
780
+ contextUsed: number | null | undefined,
781
+ ): string | null {
782
+ const output = finiteNonNegative(throughput?.outputTokens);
783
+ const input =
784
+ finiteNonNegative(contextUsed) ||
785
+ finiteNonNegative(status.summary?.inputTokens) ||
786
+ finiteNonNegative(lastTurn?.inputTokens) ||
787
+ finiteNonNegative(sessionTokens?.input);
788
+ const parts = [
789
+ output > 0 ? theme.fg("success", `${GLYPH.down}${formatFooterTokens(output)}`) : null,
790
+ input > 0 ? theme.fg("muted", `${GLYPH.up}${formatFooterTokens(input)}`) : null,
791
+ ];
792
+ const joined = joinChips(theme, parts);
793
+ return joined.length > 0 ? joined : null;
794
+ }
795
+
796
+ function lastTurnOutcome(theme: ClioTheme, lastTurn: TurnSummary): string {
797
+ const stop = stopReasonStyle(lastTurn.stopReason);
798
+ return theme.fg(stop.token, `${stop.glyph} ${formatCompactMs(lastTurn.elapsedMs)}`);
799
+ }
800
+
801
+ function lastTurnDetails(theme: ClioTheme, lastTurn: TurnSummary): string {
802
+ const parts: Array<string | null> = [
803
+ theme.fg(
804
+ "muted",
805
+ `${GLYPH.up}${formatFooterTokens(lastTurn.inputTokens)} ${GLYPH.down}${formatFooterTokens(lastTurn.outputTokens)}`,
806
+ ),
807
+ lastTurn.reasoningTokens !== undefined && lastTurn.reasoningTokens > 0
808
+ ? theme.fg(
809
+ "reason",
810
+ `r${lastTurn.reasoningTokenProvenance === "estimated" || lastTurn.reasoningTokenProvenance === "mixed" ? "≈" : ""}${formatFooterTokens(lastTurn.reasoningTokens)}`,
811
+ )
812
+ : null,
813
+ lastTurn.cacheReadTokens > 0 || lastTurn.cacheWriteTokens > 0
814
+ ? theme.fg(
815
+ "dim",
816
+ `cache ${formatFooterTokens(lastTurn.cacheReadTokens)}/${formatFooterTokens(lastTurn.cacheWriteTokens)}`,
817
+ )
818
+ : null,
819
+ ];
820
+ if (lastTurn.toolCount > 0) {
821
+ const label = `${lastTurn.toolCount} tool${lastTurn.toolCount === 1 ? "" : "s"}`;
822
+ const errors = lastTurn.toolErrorCount > 0 ? theme.fg("error", ` ${lastTurn.toolErrorCount}${GLYPH.error}`) : "";
823
+ parts.push(`${theme.fg("muted", label)}${errors}`);
824
+ }
825
+ if (lastTurn.watchdogPeak >= 2) parts.push(theme.fg("warning", "slow"));
826
+ if (lastTurn.truncated) parts.push(theme.fg("warning", "trunc"));
827
+ return joinChips(theme, parts);
828
+ }
829
+
830
+ function cumulativeTokens(sessionTokens: UsageBreakdown | null | undefined): number {
831
+ const fallback = finiteNonNegative(sessionTokens?.input) + finiteNonNegative(sessionTokens?.output);
832
+ return finiteNonNegative(sessionTokens?.totalTokens) || fallback;
833
+ }
834
+
835
+ function fleetValue(dispatchSummary: string | null, dispatchRows: ReadonlyArray<DispatchBoardRow>): string | null {
836
+ if (dispatchSummary) return dispatchSummary.replace(/^dispatch\s+/, "");
837
+ return dispatchRows.length > 0 ? `${dispatchRows.length} runs` : null;
838
+ }
839
+
840
+ function meaningfulToolTally(value: string): string | null {
841
+ return /^(?:none(?: · 0✗)?|0✗)$/u.test(value.trim()) ? null : value;
842
+ }
843
+
844
+ /** Task-board progress chips: `2/5 done`, with a warning chip when tasks are blocked. */
845
+ function taskBoardValue(theme: ClioTheme, board: TaskBoardSnapshot): string {
846
+ const counts = taskBoardCounts(board);
847
+ const progress = theme.fg(counts.open > 0 ? "muted" : "success", `${counts.completed}/${counts.total} done`);
848
+ const blocked = counts.blocked > 0 ? theme.fg("warning", `${counts.blocked} blocked`) : null;
849
+ return joinChips(theme, [progress, blocked]);
850
+ }
851
+
852
+ /** The board's current focus: the single active task, glyph-led like a worker row. */
853
+ function activeTaskLine(theme: ClioTheme, board: TaskBoardSnapshot): string | null {
854
+ const active = board.tasks.find((task) => task.status === "active");
855
+ if (!active) return null;
856
+ return `${theme.fg("accent", GLYPH.running)} ${theme.fg("dim", active.id)} ${theme.fg("muted", active.title)}`;
857
+ }
858
+
859
+ export function activityQuadrant(facts: AgentWorkFacts, options: ActivityQuadrantOptions = {}): string[] {
860
+ const theme = clioTheme();
861
+ const maxWorkers = Math.max(0, options.maxWorkers ?? 3);
862
+ const status = options.status ?? defaultIdleStatus();
863
+ const toolCounts = options.toolCounts ?? { tools: {}, errors: 0 };
864
+ const statusWidth = Math.max(options.width ?? 120, 48);
865
+ const isStreaming = status.phase !== "idle" && status.phase !== "ended";
866
+ const fleetSummaryIsAction = facts.dispatchSummary !== null && status.phase !== "dispatching";
867
+ const rows: DashboardRow[] = [
868
+ statusRow(
869
+ buildHarnessStatePill(
870
+ theme,
871
+ status,
872
+ toolCounts,
873
+ facts.dispatchRows,
874
+ options.tick ?? 0,
875
+ options.now ?? Date.now(),
876
+ statusWidth,
877
+ true,
878
+ fleetSummaryIsAction,
879
+ ),
880
+ ),
881
+ ];
882
+ if (facts.contextActivity) {
883
+ const token =
884
+ facts.contextActivity.status === "failed"
885
+ ? "error"
886
+ : facts.contextActivity.status === "completed"
887
+ ? "success"
888
+ : "accent";
889
+ rows.push(kv("context", facts.contextActivity.message, token));
890
+ if (facts.contextActivity.detail) rows.push(kv("ctx detail", facts.contextActivity.detail, "dim"));
891
+ }
892
+ if (isStreaming && facts.statusText) rows.push(kv("state", facts.statusText, "accent"));
893
+ if (isStreaming) {
894
+ rows.push(styledKv("speed", formattedThroughput(theme, options.throughput)));
895
+ rows.push(
896
+ styledKv(
897
+ "live",
898
+ liveTokenValue(theme, status, options.throughput, facts.lastTurn, options.sessionTokens, options.contextUsed),
899
+ ),
900
+ );
901
+ } else if (facts.lastTurn) {
902
+ rows.push(styledKv("last", lastTurnOutcome(theme, facts.lastTurn)));
903
+ rows.push(styledKv("turn", lastTurnDetails(theme, facts.lastTurn)));
904
+ rows.push(styledKv("speed", formattedThroughput(theme, options.throughput)));
905
+ }
906
+ const total = cumulativeTokens(options.sessionTokens);
907
+ rows.push(total > 0 ? styledKv("totals", theme.fg("muted", `Σ${formatFooterTokens(total)}`)) : statusRow(null));
908
+ // Null until something has actually been priced, and an absent row is the
909
+ // only honest rendering of that. See formatCostAggregate.
910
+ const cost = formatCostAggregate(options.sessionCost);
911
+ rows.push(cost === null ? statusRow(null) : styledKv("cost", theme.fg("muted", cost)));
912
+ rows.push(kv("fleet", fleetValue(facts.dispatchSummary, facts.dispatchRows), fleetSummaryIsAction ? "action" : "dim"));
913
+ // Every worker up to the panel bound gets its own row; what the bound cuts is
914
+ // counted out loud instead of vanishing, so the row count an operator sees
915
+ // always reconciles with the `fleet` line above it.
916
+ const workerWidth = options.width !== undefined && Number.isFinite(options.width) ? options.width : 48;
917
+ for (const row of facts.dispatchRows.slice(0, maxWorkers)) rows.push(statusRow(workerLine(theme, row, workerWidth)));
918
+ const hiddenWorkers = facts.dispatchRows.length - maxWorkers;
919
+ if (hiddenWorkers > 0) rows.push(statusRow(theme.fg("dim", `+${hiddenWorkers} more`)));
920
+ if (facts.taskBoard && facts.taskBoard.tasks.length > 0) {
921
+ rows.push(styledKv("tasks", taskBoardValue(theme, facts.taskBoard)));
922
+ rows.push(statusRow(activeTaskLine(theme, facts.taskBoard)));
923
+ }
924
+ rows.push(kv("tools", meaningfulToolTally(facts.toolTally)));
925
+ return dashboardBlock(theme, "Activity", rows);
926
+ }
927
+
928
+ /**
929
+ * Zip two quadrant blocks into a side-by-side row, padding the shorter block so
930
+ * both columns stay aligned, and clamping each cell to its column width.
931
+ */
932
+ export function zipColumns(
933
+ left: string[],
934
+ right: string[],
935
+ leftWidth: number,
936
+ rightWidth: number,
937
+ sep: string,
938
+ ): string[] {
939
+ const rowCount = Math.max(left.length, right.length);
940
+ const lines: string[] = [];
941
+ for (let i = 0; i < rowCount; i += 1) {
942
+ lines.push(`${cell(left[i] ?? "", leftWidth)}${sep}${cell(right[i] ?? "", rightWidth)}`);
943
+ }
944
+ return lines;
945
+ }
946
+
947
+ export function zipColumnBlocks(blocks: ReadonlyArray<string[]>, widths: ReadonlyArray<number>, sep: string): string[] {
948
+ const rowCount = blocks.reduce((max, block) => Math.max(max, block.length), 0);
949
+ const lines: string[] = [];
950
+ for (let row = 0; row < rowCount; row += 1) {
951
+ const cells = blocks.map((block, index) => cell(block[row] ?? "", widths[index] ?? 0));
952
+ lines.push(cells.join(sep));
953
+ }
954
+ return lines;
955
+ }
956
+
957
+ type HarnessPhasePresentation = {
958
+ glyph: string;
959
+ label: string;
960
+ token: ClioToken;
961
+ live: boolean;
962
+ };
963
+
964
+ function shortToolLabel(status: AgentStatus, width: number): string {
965
+ const name = status.tool?.toolName?.trim();
966
+ if (name === ToolNames.AskUser) return width < 72 ? "ask" : "waiting for user";
967
+ if (!name || width < 72) return "tool";
968
+ const nameWidth = width >= 100 ? 18 : 12;
969
+ // The pill label is never padded; truncate without pad so the tool name is
970
+ // followed by a single space before the badge, not a column of blanks.
971
+ return `tool ${truncateToWidth(name, nameWidth, "…", false)}`;
972
+ }
973
+
974
+ function harnessPhasePresentation(status: AgentStatus, width: number, now: number): HarnessPhasePresentation {
975
+ const ultraNarrow = width < 48;
976
+ switch (status.phase) {
977
+ case "idle":
978
+ return { glyph: GLYPH.queued, label: "idle", token: "muted", live: false };
979
+ case "preparing":
980
+ return { glyph: GLYPH.phaseWaiting, label: "prep", token: "info", live: true };
981
+ case "waiting_model":
982
+ return { glyph: GLYPH.phaseWaiting, label: "waiting", token: "info", live: true };
983
+ case "thinking":
984
+ return { glyph: GLYPH.phaseThinking, label: "thinking", token: "reason", live: true };
985
+ case "writing":
986
+ return { glyph: GLYPH.phaseWriting, label: "writing", token: "accent", live: true };
987
+ case "tool_running":
988
+ return { glyph: GLYPH.phaseTool, label: shortToolLabel(status, width), token: "accent", live: true };
989
+ case "tool_blocked":
990
+ // Attention states hold a static glyph rather than spinning: the work
991
+ // has paused for a human, so the pill should not read as live progress.
992
+ // The phase fires only on PermissionRequested, so the pill names the
993
+ // wait for confirmation; "blocked" would contradict the ask overlay.
994
+ return { glyph: GLYPH.phaseBlocked, label: "confirm", token: "warning", live: false };
995
+ case "retrying": {
996
+ const attempt = status.retry?.attempt ?? 0;
997
+ const maxAttempts = status.retry?.maxAttempts ?? 0;
998
+ return {
999
+ glyph: GLYPH.phaseRetry,
1000
+ label: ultraNarrow ? "retry" : `retry ${attempt}/${maxAttempts}`,
1001
+ token: "warning",
1002
+ live: false,
1003
+ };
1004
+ }
1005
+ case "compacting":
1006
+ return { glyph: GLYPH.phaseCompact, label: "compacting", token: "reason", live: true };
1007
+ case "dispatching":
1008
+ return { glyph: GLYPH.phaseDispatch, label: "dispatch", token: "action", live: true };
1009
+ case "stuck": {
1010
+ const seconds = Math.max(0, Math.floor((now - status.since) / 1000));
1011
+ return { glyph: GLYPH.warn, label: ultraNarrow ? "stuck" : `stuck ${seconds}s`, token: "error", live: false };
1012
+ }
1013
+ case "ended":
1014
+ return { glyph: GLYPH.ok, label: "done", token: "success", live: false };
1015
+ }
1016
+ }
1017
+
1018
+ function activeWorkerRows(rows: ReadonlyArray<DispatchBoardRow>): ReadonlyArray<DispatchBoardRow> {
1019
+ return rows.filter((row) => row.status === "running" || row.status === "stale" || row.status === "enqueued");
1020
+ }
1021
+
1022
+ /**
1023
+ * The live worker count, split by who asked once more than one kind is running:
1024
+ * `◇1 ◆3` says the operator has one run of their own alongside three the model
1025
+ * started, which `fleet 4` cannot. A single kind keeps the plain count, and so
1026
+ * does a set holding a run whose origin never reached the projection, because a
1027
+ * split that cannot name every active run would report a total that is short.
1028
+ *
1029
+ * The chip keeps one color. Its token says the fleet is busy; origin is carried
1030
+ * by glyph shape, which is the same pair the board rows and the transcript
1031
+ * blocks use.
1032
+ */
1033
+ function activeWorkerChip(rows: ReadonlyArray<DispatchBoardRow>): string {
1034
+ const active = activeWorkerRows(rows);
1035
+ const counts = new Map<string, number>();
1036
+ for (const row of active) {
1037
+ const glyph = dispatchOriginPresentation(row)?.glyph;
1038
+ if (glyph === undefined) return `fleet ${active.length}`;
1039
+ counts.set(glyph, (counts.get(glyph) ?? 0) + 1);
1040
+ }
1041
+ if (counts.size < 2) return `fleet ${active.length}`;
1042
+ const order = [GLYPH.workerHuman, GLYPH.workerAgent, GLYPH.workerInternal];
1043
+ return order
1044
+ .filter((glyph) => counts.has(glyph))
1045
+ .map((glyph) => `${glyph}${counts.get(glyph)}`)
1046
+ .join(" ");
1047
+ }
1048
+
1049
+ function harnessBadge(
1050
+ theme: ClioTheme,
1051
+ status: AgentStatus,
1052
+ toolCounts: ToolTallySnapshot,
1053
+ dispatchRows: ReadonlyArray<DispatchBoardRow>,
1054
+ fleetSummaryIsAction = false,
1055
+ ): string {
1056
+ const workers = activeWorkerRows(dispatchRows).length;
1057
+ const activeTools = finiteNonNegative(toolCounts.active);
1058
+ // Active fleet work is a Clio-signature state; it gets the action color.
1059
+ if (workers > 0) {
1060
+ const token = status.phase === "dispatching" ? "muted" : fleetSummaryIsAction ? "accent" : "action";
1061
+ return theme.fg(token, activeWorkerChip(dispatchRows));
1062
+ }
1063
+ const badgeText = activeTools > 0 ? `tools ${activeTools}` : null;
1064
+ return badgeText ? theme.fg("muted", badgeText) : "";
1065
+ }
1066
+
1067
+ function outputVerbosityLabel(verbosity: OutputVerbosity): string {
1068
+ return verbosity === "minimal" ? "min" : verbosity === "verbose" ? "verbose" : "default";
1069
+ }
1070
+
1071
+ export function buildHarnessStatePill(
1072
+ theme: ClioTheme,
1073
+ status: AgentStatus,
1074
+ toolCounts: ToolTallySnapshot,
1075
+ dispatchRows: ReadonlyArray<DispatchBoardRow>,
1076
+ tick: number,
1077
+ now: number,
1078
+ width: number,
1079
+ showBadge = true,
1080
+ fleetSummaryIsAction = false,
1081
+ ): string {
1082
+ const safeWidth = Math.max(1, Math.floor(width));
1083
+ const badge =
1084
+ showBadge && safeWidth >= 48 ? harnessBadge(theme, status, toolCounts, dispatchRows, fleetSummaryIsAction) : "";
1085
+ // Idleness is absence of work, not a phase worth narrating. If a tool or
1086
+ // fleet remains live while the harness settles, keep that activity without
1087
+ // prefixing it with an idle glyph.
1088
+ if (status.phase === "idle") return badge;
1089
+ const phase = harnessPhasePresentation(status, safeWidth, now);
1090
+ // A live phase leads with the animated spinner; the spinner stands in for the
1091
+ // static phase glyph rather than sitting beside it. Static glyphs render only
1092
+ // for the attention states and ended forms; idle returned quietly above.
1093
+ const lead = phase.live ? spinnerFrame(tick) : phase.glyph;
1094
+ const mainPill = theme.style(phase.token, `${lead} ${phase.label}`);
1095
+ return badge ? `${mainPill} ${theme.fg("dim", "·")} ${badge}` : mainPill;
1096
+ }
1097
+
1098
+ /**
1099
+ * Drop order for the metric strip. The chip budget and the width budget both cut
1100
+ * from the highest rank down, so one ordering decides what an 80-column footer
1101
+ * keeps.
1102
+ *
1103
+ * The session total sits alone at the top. It is measured, it does not depend on
1104
+ * pricing, and it does not go stale between turns, which is not true of anything
1105
+ * else on the strip. At 80 columns the budget is four chips and the per-turn
1106
+ * detail used to spend all of it, so a session holding 9.7k measured tokens
1107
+ * showed none of them.
1108
+ */
1109
+ const CHIP_RANK_LEADER = -1;
1110
+ const CHIP_RANK_TOTALS = 0;
1111
+ const CHIP_RANK_DETAIL = 1;
1112
+ const CHIP_RANK_DEFERRED = 2;
1113
+
1114
+ interface RankedChip {
1115
+ text: string;
1116
+ rank: number;
1117
+ }
1118
+
1119
+ function pushChip(chips: RankedChip[], text: string | null, rank: number): void {
1120
+ if (typeof text === "string" && text.length > 0) chips.push({ text, rank });
1121
+ }
1122
+
1123
+ /**
1124
+ * Cut the strip to the chip budget and then to the width, dropping the
1125
+ * lowest-priority chip still standing each time. Within one rank the rightmost
1126
+ * goes first, which keeps the surviving chips in the order they were built.
1127
+ */
1128
+ function selectChips(
1129
+ theme: ClioTheme,
1130
+ chips: ReadonlyArray<RankedChip>,
1131
+ chipLimit: number,
1132
+ maxWidth: number,
1133
+ ): string[] {
1134
+ const dropOrder = chips
1135
+ .map((chip, index) => ({ rank: chip.rank, index }))
1136
+ .sort((a, b) => b.rank - a.rank || b.index - a.index);
1137
+ const dropped = new Set<number>();
1138
+ const surviving = (): string[] => chips.filter((_, index) => !dropped.has(index)).map((chip) => chip.text);
1139
+ for (const { index } of dropOrder) {
1140
+ if (chips.length - dropped.size <= chipLimit && visibleWidth(joinChips(theme, surviving())) <= maxWidth) break;
1141
+ dropped.add(index);
1142
+ }
1143
+ return surviving();
1144
+ }
1145
+
1146
+ export function buildMetricStrip(
1147
+ theme: ClioTheme,
1148
+ status: AgentStatus,
1149
+ throughput: TokenThroughputSnapshot | null | undefined,
1150
+ lastTurn: TurnSummary | null | undefined,
1151
+ sessionTokens: UsageBreakdown | null | undefined,
1152
+ sessionCost: CostAggregate | null | undefined,
1153
+ liveInputTokens: number | null | undefined,
1154
+ maxWidth: number,
1155
+ maxChipsCount = 6,
1156
+ outputVerbosity?: OutputVerbosity | null,
1157
+ leaderArmed = false,
1158
+ ): string {
1159
+ const safeMaxWidth = Math.max(0, Math.floor(maxWidth));
1160
+ if (safeMaxWidth <= 0) return "";
1161
+ const isStreaming = status.phase !== "idle" && status.phase !== "ended";
1162
+ const meaningfulVerbosity = outputVerbosity && outputVerbosity !== "default" ? outputVerbosity : null;
1163
+ if (!isStreaming && !lastTurn && !meaningfulVerbosity && !leaderArmed) return "";
1164
+
1165
+ const candidates: Array<string | null> = [];
1166
+ /** Per-turn detail that ranks below the session totals when the strip is cut. */
1167
+ const deferred: Array<string | null> = [];
1168
+ if (isStreaming) {
1169
+ const tps = finiteNonNegative(throughput?.tokensPerSecond);
1170
+ const rounded = tps > 0 ? (tps >= 10 ? Math.round(tps) : Math.round(tps * 10) / 10) : null;
1171
+ candidates.push(rounded !== null ? theme.fg("success", `${GLYPH.speed}${rounded}/s`) : null);
1172
+
1173
+ const liveOutput = finiteNonNegative(throughput?.outputTokens);
1174
+ candidates.push(liveOutput > 0 ? theme.fg("success", `${GLYPH.down}${formatFooterTokens(liveOutput)}`) : null);
1175
+
1176
+ const ttftMs = finiteNonNegative(throughput?.ttftMs);
1177
+ candidates.push(ttftMs > 0 ? theme.fg("muted", `ttft ${formatCompactMs(ttftMs)}`) : null);
1178
+
1179
+ const inputTokens =
1180
+ finiteNonNegative(liveInputTokens) ||
1181
+ finiteNonNegative(status.summary?.inputTokens) ||
1182
+ finiteNonNegative(lastTurn?.inputTokens) ||
1183
+ finiteNonNegative(sessionTokens?.input);
1184
+ candidates.push(inputTokens > 0 ? theme.fg("muted", `${GLYPH.up}${formatFooterTokens(inputTokens)}`) : null);
1185
+ } else if (lastTurn) {
1186
+ const stop = stopReasonStyle(lastTurn.stopReason);
1187
+ candidates.push(theme.fg(stop.token, `${stop.glyph} ${formatCompactMs(lastTurn.elapsedMs)}`));
1188
+ candidates.push(
1189
+ theme.fg(
1190
+ "muted",
1191
+ `${GLYPH.up}${formatFooterTokens(lastTurn.inputTokens)} ${GLYPH.down}${formatFooterTokens(lastTurn.outputTokens)}`,
1192
+ ),
1193
+ );
1194
+ candidates.push(
1195
+ lastTurn.reasoningTokens !== undefined && lastTurn.reasoningTokens > 0
1196
+ ? theme.fg(
1197
+ "reason",
1198
+ `r${lastTurn.reasoningTokenProvenance === "estimated" || lastTurn.reasoningTokenProvenance === "mixed" ? "≈" : ""}${formatFooterTokens(lastTurn.reasoningTokens)}`,
1199
+ )
1200
+ : null,
1201
+ );
1202
+ // Held back so the session totals below outrank them. The chip list is cut
1203
+ // to `maxChipsCount` before it is measured, and with cache and tools ahead
1204
+ // of the totals an 80-column terminal spent the whole budget on per-turn
1205
+ // detail: the cost field disappeared from the footer at the exact moment
1206
+ // it acquired a value, while `/cost` on the same session read `cost
1207
+ // unknown`. Two surfaces, two answers, because of a slice.
1208
+ deferred.push(
1209
+ lastTurn.cacheReadTokens > 0 || lastTurn.cacheWriteTokens > 0
1210
+ ? theme.fg(
1211
+ "dim",
1212
+ `cache ${formatFooterTokens(lastTurn.cacheReadTokens)}/${formatFooterTokens(lastTurn.cacheWriteTokens)}`,
1213
+ )
1214
+ : null,
1215
+ );
1216
+ if (lastTurn.toolCount > 0) {
1217
+ const label = `${lastTurn.toolCount} tool${lastTurn.toolCount === 1 ? "" : "s"}`;
1218
+ const errors = lastTurn.toolErrorCount > 0 ? theme.fg("error", ` ${lastTurn.toolErrorCount}${GLYPH.error}`) : "";
1219
+ deferred.push(`${theme.fg("muted", label)}${errors}`);
1220
+ } else {
1221
+ deferred.push(null);
1222
+ }
1223
+ }
1224
+ if (meaningfulVerbosity) deferred.push(theme.fg("muted", `out ${outputVerbosityLabel(meaningfulVerbosity)}`));
1225
+
1226
+ const fallbackTotal = finiteNonNegative(sessionTokens?.input) + finiteNonNegative(sessionTokens?.output);
1227
+ const cumulativeTotal = finiteNonNegative(sessionTokens?.totalTokens) || fallbackTotal;
1228
+ const totalChip = cumulativeTotal > 0 ? theme.fg("muted", `Σ${formatFooterTokens(cumulativeTotal)}`) : null;
1229
+ // Null when nothing priced these tokens, and an absent chip is the honest
1230
+ // rendering of that. `$0.00` on a session that had made no call was a number
1231
+ // nothing measured, and `cost unknown` beside a real Σ read as doubt about
1232
+ // the tokens. See formatCostAggregate.
1233
+ const cost = formatCostAggregate(sessionCost);
1234
+ const costChip = cost === null ? null : theme.fg("muted", cost);
1235
+
1236
+ const chipLimit = Math.max(0, Math.floor(maxChipsCount));
1237
+ const chips: RankedChip[] = [];
1238
+ // Ranked above every measurement: the strip is cut by dropping the
1239
+ // lowest-ranked chip, and this one is the answer to "did that key register".
1240
+ pushChip(chips, leaderArmed ? theme.fg("accent", "leader armed") : null, CHIP_RANK_LEADER);
1241
+ for (const chip of candidates) pushChip(chips, chip, CHIP_RANK_DETAIL);
1242
+ pushChip(chips, totalChip, CHIP_RANK_TOTALS);
1243
+ pushChip(chips, costChip, CHIP_RANK_DETAIL);
1244
+ for (const chip of deferred) pushChip(chips, chip, CHIP_RANK_DEFERRED);
1245
+ return joinChips(theme, selectChips(theme, chips, chipLimit, safeMaxWidth));
1246
+ }