@iowarp/clio-coder 0.5.0 → 0.5.2

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 (312) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/CHANGELOG.md +35 -0
  3. package/CONTRIBUTING.md +9 -0
  4. package/README.md +12 -9
  5. package/ROADMAP.md +30 -5
  6. package/dist/{acp-KS7ARGV6.js → acp-F6PGOFZ4.js} +2 -2
  7. package/dist/{agents-YOGJ2YEJ.js → agents-ZHP4R6EX.js} +22 -21
  8. package/dist/assets/codewiki.json +1 -1
  9. package/dist/{auth-CEDCO6O6.js → auth-N75U2H3Z.js} +4 -4
  10. package/dist/{chunk-D3KWFOUT.js → chunk-23UIXH6Z.js} +3 -3
  11. package/dist/{chunk-ZR5YA6ZF.js → chunk-36XVK5P5.js} +5 -5
  12. package/dist/{chunk-AVSR2HKI.js → chunk-3DZ4LPLS.js} +6 -6
  13. package/dist/{chunk-LYJJ6546.js → chunk-3GSPCWFX.js} +12 -12
  14. package/dist/{chunk-CRHPCPZP.js → chunk-4KC4CHKJ.js} +2 -2
  15. package/dist/{chunk-4VDHLCF5.js → chunk-4WJBWKMI.js} +2 -2
  16. package/dist/{chunk-KBUTSJSR.js → chunk-4XBYHXGK.js} +2 -2
  17. package/dist/{chunk-6W75D37F.js → chunk-53P775GE.js} +6 -3
  18. package/dist/{chunk-XZWSP67B.js → chunk-5APYAXCB.js} +2 -2
  19. package/dist/{chunk-D65E25JG.js → chunk-67HPYNTJ.js} +2 -2
  20. package/dist/{chunk-5JQR5B4D.js → chunk-6KV4KCQZ.js} +4 -4
  21. package/dist/{chunk-V4XJWDHR.js → chunk-6UKFRTXI.js} +2 -1
  22. package/dist/{chunk-NDPF2N2L.js → chunk-7DW7CJW2.js} +2 -2
  23. package/dist/{chunk-5KQEUIWM.js → chunk-7LUEQODV.js} +4 -4
  24. package/dist/{chunk-LNOXCGN2.js → chunk-7UFDIOBT.js} +3 -3
  25. package/dist/{chunk-DXSETN72.js → chunk-7WUGNN43.js} +2 -2
  26. package/dist/{chunk-V35KZDUZ.js → chunk-AA3GRZWS.js} +2 -2
  27. package/dist/{chunk-X4CEFOSG.js → chunk-ADO3OQIC.js} +4 -4
  28. package/dist/{chunk-UX2L5CUD.js → chunk-AI36V3ON.js} +3 -3
  29. package/dist/{chunk-B7GY6QER.js → chunk-B26OUMJW.js} +2 -2
  30. package/dist/{chunk-LAR7DX5E.js → chunk-B5TASTCK.js} +153 -62
  31. package/dist/{chunk-TGWZXJ7L.js → chunk-BEMQK5Q7.js} +92 -84
  32. package/dist/{chunk-RNDCROSG.js → chunk-CNXU7TR2.js} +3 -3
  33. package/dist/{chunk-3JLTRPN7.js → chunk-CVMUXT64.js} +35 -66
  34. package/dist/{chunk-733ZBT5P.js → chunk-E74MJF6G.js} +4 -4
  35. package/dist/{chunk-T2MDZES3.js → chunk-EBMTBE76.js} +3 -3
  36. package/dist/{chunk-OZ76TLGU.js → chunk-EEITU7S6.js} +2 -2
  37. package/dist/{chunk-GRHOXDKB.js → chunk-ELDJWJKM.js} +97 -14
  38. package/dist/{chunk-GCQFN7YI.js → chunk-EMYEQLWO.js} +2 -2
  39. package/dist/{chunk-LTWH2ACR.js → chunk-EQPFEDFV.js} +222 -127
  40. package/dist/{chunk-TDGYTWM6.js → chunk-F4I3H56H.js} +2 -2
  41. package/dist/chunk-F6KCJO3U.js +523 -0
  42. package/dist/{chunk-FGSQHUCK.js → chunk-F6VKWTTY.js} +5 -5
  43. package/dist/{chunk-UJD5MPP6.js → chunk-FLNXQQ5B.js} +2 -2
  44. package/dist/{chunk-EX57VVWW.js → chunk-FOSRJ2VZ.js} +4 -4
  45. package/dist/{chunk-ZARZ4POR.js → chunk-G5ZDQQE3.js} +3 -3
  46. package/dist/{chunk-HQVN7G4F.js → chunk-G62JBPXN.js} +7 -7
  47. package/dist/{chunk-ZJPORZTC.js → chunk-GIAI7A2K.js} +2 -2
  48. package/dist/chunk-GPTR6OTU.js +2551 -0
  49. package/dist/{chunk-EZD2BXGP.js → chunk-GUFQPTQK.js} +2 -2
  50. package/dist/{chunk-OPD7GL6F.js → chunk-H6FQVNZC.js} +8 -7
  51. package/dist/chunk-H7REJ7L5.js +80 -0
  52. package/dist/{chunk-EYTLV3W3.js → chunk-HTWTORF7.js} +2 -2
  53. package/dist/{chunk-DF6PI6GN.js → chunk-I6UKQPGS.js} +3 -3
  54. package/dist/{chunk-ISHUS7HC.js → chunk-JRW236JT.js} +3 -3
  55. package/dist/{chunk-DE2LU267.js → chunk-KKAJGT3B.js} +3 -2
  56. package/dist/{chunk-OV62D6KO.js → chunk-KQMYL5GR.js} +3 -3
  57. package/dist/{chunk-QDUUOZ3S.js → chunk-KWMFC7FN.js} +3 -3
  58. package/dist/{chunk-R7FGRZNF.js → chunk-LXYHCCIZ.js} +9 -9
  59. package/dist/{chunk-NFSK2VNU.js → chunk-LZMTVVN4.js} +2 -2
  60. package/dist/{chunk-62UK7AGW.js → chunk-M5PMRZSR.js} +397 -44
  61. package/dist/chunk-MMZMM6SW.js +131 -0
  62. package/dist/{chunk-FFYTC2KQ.js → chunk-MNABNEBX.js} +2 -2
  63. package/dist/{chunk-XMDBTYCH.js → chunk-N3CWOGVF.js} +3 -3
  64. package/dist/{chunk-VYRQHORQ.js → chunk-NMQSO6Z6.js} +4 -3
  65. package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
  66. package/dist/{chunk-4SYHGVDE.js → chunk-OQNCLZI4.js} +4 -4
  67. package/dist/{chunk-EG4ARXPC.js → chunk-OX6QTA4N.js} +3 -3
  68. package/dist/{chunk-P4GNZPXO.js → chunk-OYUS3UZP.js} +2 -2
  69. package/dist/{chunk-OQ2EOAHA.js → chunk-PF36KOGI.js} +291 -112
  70. package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
  71. package/dist/{chunk-CBAQPTDU.js → chunk-PIU6BXXW.js} +2 -2
  72. package/dist/{chunk-EKV4UBCM.js → chunk-PZBQUJ2F.js} +2 -2
  73. package/dist/{chunk-7RET77VB.js → chunk-Q2HKY32Y.js} +2 -2
  74. package/dist/{chunk-U6YTUVOX.js → chunk-Q4RMNWMZ.js} +4 -4
  75. package/dist/chunk-Q6FRR3AQ.js +48 -0
  76. package/dist/{chunk-6RJMYYIE.js → chunk-QJSIAJBY.js} +3 -3
  77. package/dist/{chunk-DOUISWEE.js → chunk-QOJ4ERMU.js} +5 -5
  78. package/dist/{chunk-IFPDAD7R.js → chunk-RWSRKXLT.js} +3 -3
  79. package/dist/{chunk-T33IYTZM.js → chunk-SAASVMIY.js} +3 -3
  80. package/dist/{chunk-2DTIWSAW.js → chunk-SEQDH6JC.js} +3 -3
  81. package/dist/{chunk-BRCOSE7O.js → chunk-SZUO5BSE.js} +2 -2
  82. package/dist/{chunk-HOEDA42N.js → chunk-T7HZ7KJF.js} +2 -2
  83. package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
  84. package/dist/{chunk-UCINIRHW.js → chunk-TPCZXWLT.js} +3 -3
  85. package/dist/{chunk-ZUKUCZYZ.js → chunk-TQ2KTH4A.js} +2 -2
  86. package/dist/{chunk-4WOOTKFD.js → chunk-U3FETNQB.js} +341 -20
  87. package/dist/{chunk-5B3RTYAK.js → chunk-UGDSB4AN.js} +2 -2
  88. package/dist/{chunk-VKHLUZNO.js → chunk-UZ7YBL43.js} +3 -3
  89. package/dist/{chunk-QNUQ7K7D.js → chunk-VKMSEO7Y.js} +4 -4
  90. package/dist/{chunk-IN7DGBVS.js → chunk-VLX5VZ35.js} +5 -5
  91. package/dist/{chunk-SJGS3GDI.js → chunk-WAGBMMNX.js} +5 -5
  92. package/dist/{chunk-BFOSV5EZ.js → chunk-WX2YCH7F.js} +2 -2
  93. package/dist/{chunk-3LT34CAM.js → chunk-XBIGUILU.js} +32 -17
  94. package/dist/{chunk-XDHUDE5K.js → chunk-XKYBFRWR.js} +5 -5
  95. package/dist/{chunk-U5QU5ZOD.js → chunk-XYPWFSU5.js} +3 -3
  96. package/dist/{chunk-YBUECLAF.js → chunk-YEJQDODA.js} +2 -2
  97. package/dist/{chunk-YQ6XEFVK.js → chunk-Z74OGONW.js} +39 -7
  98. package/dist/{chunk-I4ELN5BX.js → chunk-ZTNOEJRI.js} +2 -2
  99. package/dist/cli/index.js +26 -26
  100. package/dist/{clio-K2PBVCEM.js → clio-4IKFE25F.js} +2 -2
  101. package/dist/{clio-context-tools-GR4OHFSO.js → clio-context-tools-OHJXYX4R.js} +25 -23
  102. package/dist/{code-nav-4X3OI6DK.js → code-nav-XPM3MY5I.js} +7 -7
  103. package/dist/{config-Q5J7UBAB.js → config-343YS25T.js} +43 -41
  104. package/dist/{config-graph-P3555574.js → config-graph-WI263KDX.js} +43 -41
  105. package/dist/{configure-KIJ32ZO6.js → configure-GZDG4HER.js} +21 -21
  106. package/dist/{context-LFBQOSXT.js → context-4OFO7N7Y.js} +12 -12
  107. package/dist/{context-WBH3KSVR.js → context-JLK2RLOG.js} +42 -40
  108. package/dist/{context-NNLBES2Y.js → context-O6DIWCF6.js} +23 -21
  109. package/dist/{context-clear-YOHMQ6SQ.js → context-clear-MR7KOGQI.js} +42 -40
  110. package/dist/{context-working-set-MRAEVDNV.js → context-working-set-YZAD3KWO.js} +14 -13
  111. package/dist/{data-tool-JJPBGMQJ.js → data-tool-EKD7DFHG.js} +7 -7
  112. package/dist/{detail-RHJOTGN4.js → detail-YZPHFEVJ.js} +43 -41
  113. package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-VKVXVTWJ.js} +44 -42
  114. package/dist/{doctor-S5KZ2GTN.js → doctor-53AVQXAO.js} +17 -16
  115. package/dist/{doctor-deep-2EJQSHIZ.js → doctor-deep-BLRB2NCG.js} +5 -5
  116. package/dist/{eval-3D6M36N7.js → eval-P3BQSLQE.js} +21 -20
  117. package/dist/{evidence-XNVPPLVK.js → evidence-6LLI6SZH.js} +44 -42
  118. package/dist/{evidence-6IYN52ET.js → evidence-HTN3JLUO.js} +42 -40
  119. package/dist/{evidence-JVECPI3F.js → evidence-NJRE3R5H.js} +42 -40
  120. package/dist/{evolve-TLPUDJDM.js → evolve-WWZHJVJP.js} +42 -40
  121. package/dist/{fleet-WKVBF5SZ.js → fleet-GGZ4BVJE.js} +64 -62
  122. package/dist/{fleet-6RJWIOEO.js → fleet-QG3IIWWM.js} +44 -42
  123. package/dist/{fleet-commands-L6WFT7KZ.js → fleet-commands-J2ZIKISQ.js} +7 -7
  124. package/dist/{fleet-decisions-BTQMWU5A.js → fleet-decisions-Y2Q7375Q.js} +8 -8
  125. package/dist/{fleet-graph-553DZ67Q.js → fleet-graph-TCBCCCBP.js} +11 -11
  126. package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-KOLKBC56.js} +44 -42
  127. package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-44UNCC4Y.js} +27 -25
  128. package/dist/{fleet-validate-7NLAE26R.js → fleet-validate-K2FXRZVA.js} +13 -13
  129. package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-AS444WDW.js} +42 -40
  130. package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-X4JU4J6O.js} +43 -41
  131. package/dist/gui/ops-worker.js +9 -9
  132. package/dist/gui/reads-worker.js +9 -8
  133. package/dist/{init-DV5B6HQG.js → init-VSOWFHVL.js} +54 -52
  134. package/dist/{interactive-OXGYZYAS.js → interactive-JRCBENLZ.js} +1733 -577
  135. package/dist/{interop-J7UOBOSF.js → interop-JR6BCY7R.js} +10 -10
  136. package/dist/{inventory-OEGIL6PY.js → inventory-3JUGLVJI.js} +43 -41
  137. package/dist/{library-ROZAMUIL.js → library-7BOH3Y7N.js} +15 -15
  138. package/dist/{library-B3UPGY4V.js → library-7PFONXUI.js} +9 -9
  139. package/dist/{library-MXQA5DLT.js → library-TUYZMFIA.js} +7 -7
  140. package/dist/{library-import-2LTWOVBD.js → library-import-TJVXKLDK.js} +10 -10
  141. package/dist/{library-inventory-LJ3XHLWO.js → library-inventory-MD3AYTP4.js} +9 -9
  142. package/dist/{library-validation-DARHHUNW.js → library-validation-ZDLITEA3.js} +7 -7
  143. package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
  144. package/dist/{memory-J3STUUIK.js → memory-Q2YVOKK5.js} +42 -40
  145. package/dist/{models-Y3GMFJ2V.js → models-M7BOCW7F.js} +17 -16
  146. package/dist/{monitor-LHNH577J.js → monitor-2HD3MC67.js} +47 -45
  147. package/dist/{orchestrator-DTAPXULA.js → orchestrator-OUW3ZN6Y.js} +1894 -226
  148. package/dist/{panes-A743ONZ5.js → panes-TTPPSYCL.js} +3 -3
  149. package/dist/{preload-OJBMAIXF.js → preload-A47C2NUF.js} +42 -40
  150. package/dist/{providers-CMETPZNS.js → providers-T43W7EL4.js} +4 -4
  151. package/dist/{resources-FY6XYFN2.js → resources-TP6X2V6W.js} +20 -11
  152. package/dist/{run-H64MEVTA.js → run-LDSBPMCS.js} +61 -59
  153. package/dist/{share-CAFMMQLM.js → share-URHKKMHR.js} +9 -9
  154. package/dist/{skills-ERKQUC7L.js → skills-E477MOOO.js} +13 -11
  155. package/dist/{skills-eval-CQ23MYQK.js → skills-eval-AIBR2L7H.js} +583 -93
  156. package/dist/{skills-inventory-TVUYJDVN.js → skills-inventory-HLMKY3R5.js} +13 -11
  157. package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-LIIZKA5Y.js} +28 -26
  158. package/dist/{startup-background-KDWCQMWT.js → startup-background-JR2KDK6R.js} +43 -41
  159. package/dist/{steer-KKYXJSH4.js → steer-D7RTAPCM.js} +3 -3
  160. package/dist/{system-ZDW44ZGX.js → system-X2P5N4IY.js} +14 -13
  161. package/dist/{targets-AJWA6E7Z.js → targets-VFFAKUFM.js} +25 -24
  162. package/dist/{tasks-6L2JJRC5.js → tasks-ERAWZA22.js} +6 -6
  163. package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
  164. package/dist/{trace-LA5MVR4Y.js → trace-MA5BT7XY.js} +3 -3
  165. package/dist/{usage-XSILDHKF.js → usage-WYFY2L6M.js} +50 -48
  166. package/dist/{verifiers-ECA5GIHL.js → verifiers-TYIW2XKF.js} +7 -7
  167. package/dist/{verify-R6QQCCEB.js → verify-242UFYTP.js} +6 -6
  168. package/dist/{web-fetch-PVZNMBOB.js → web-fetch-LRAGYU6Y.js} +3 -3
  169. package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-OFKOX2T6.js} +53 -51
  170. package/dist/worker/entry.js +31 -29
  171. package/docs/architecture/architecture.md +1 -0
  172. package/docs/architecture/context-engine.md +4 -2
  173. package/docs/architecture/observability.md +6 -4
  174. package/docs/architecture/prompt-envelope-and-tools.md +1 -1
  175. package/docs/architecture/session-lifecycle.md +1 -1
  176. package/docs/architecture/tui-design.md +12 -9
  177. package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
  178. package/docs/guide/commands-and-modes.md +91 -5
  179. package/docs/guide/configuration-and-targets.md +2 -2
  180. package/docs/guide/context-continuity.md +49 -0
  181. package/docs/guide/proactive-memory.md +6 -3
  182. package/library/registry.yaml +292 -32
  183. package/library/skills/README.md +27 -2
  184. package/library/skills/context/context-handoff/SKILL.md +10 -4
  185. package/library/skills/meta/clio-coder-dev/SKILL.md +92 -88
  186. package/library/skills/meta/clio-coder-dev/evals.md +51 -46
  187. package/library/skills/meta/clio-coder-dev/plugin.json +2 -2
  188. package/library/skills/meta/clio-coder-dev/references/change-map.md +49 -0
  189. package/library/skills/meta/clio-coder-test/SKILL.md +89 -142
  190. package/library/skills/meta/clio-coder-test/evals.md +58 -62
  191. package/library/skills/meta/clio-coder-test/plugin.json +2 -2
  192. package/library/skills/meta/clio-coder-test/references/harness.md +3 -3
  193. package/library/skills/meta/clio-coder-test/references/lifecycle-validation.md +30 -0
  194. package/library/skills/meta/clio-coder-test/references/test-map.md +60 -89
  195. package/library/skills/registry.yaml +5 -5
  196. package/library/skills/skill-marketplace.json +12 -14
  197. package/package.json +1 -1
  198. package/src/cli/skills-eval.ts +1064 -54
  199. package/src/cli/usage.ts +2 -2
  200. package/src/core/clio-repo.ts +3 -0
  201. package/src/core/tool-names.ts +1 -0
  202. package/src/domains/context/budget/inspection.ts +9 -0
  203. package/src/domains/context/budget/live-view.ts +418 -0
  204. package/src/domains/context/budget/pressure.ts +393 -0
  205. package/src/domains/context/budget/request-fit.ts +15 -0
  206. package/src/domains/evidence/build.ts +24 -0
  207. package/src/domains/gateway/mcp/client.ts +1 -1
  208. package/src/domains/memory/commit-state.ts +160 -0
  209. package/src/domains/memory/operations.ts +18 -10
  210. package/src/domains/memory/prompt-cache.ts +95 -0
  211. package/src/domains/memory/prompt-section.ts +50 -7
  212. package/src/domains/memory/relevance.ts +93 -0
  213. package/src/domains/memory/restoration.ts +78 -0
  214. package/src/domains/memory/store.ts +39 -1
  215. package/src/domains/middleware/memory-intervention.ts +116 -24
  216. package/src/domains/observability/background-memory-usage.ts +1 -1
  217. package/src/domains/observability/cost.ts +3 -3
  218. package/src/domains/observability/extension.ts +1 -1
  219. package/src/domains/observability/metrics.ts +1 -1
  220. package/src/domains/prompts/compiler.ts +1 -1
  221. package/src/domains/prompts/extension.ts +38 -1
  222. package/src/domains/quota/anthropic-max-provider.ts +134 -0
  223. package/src/domains/quota/anthropic-usage.ts +220 -0
  224. package/src/domains/quota/antigravity-provider.ts +339 -0
  225. package/src/domains/quota/cache.ts +87 -0
  226. package/src/domains/quota/claude-code-provider.ts +169 -0
  227. package/src/domains/quota/codex-provider.ts +237 -0
  228. package/src/domains/quota/presentation.ts +240 -0
  229. package/src/domains/quota/registry.ts +23 -0
  230. package/src/domains/quota/service.ts +93 -0
  231. package/src/domains/quota/summary-feed.ts +86 -0
  232. package/src/domains/quota/types.ts +84 -0
  233. package/src/domains/resources/index.ts +11 -0
  234. package/src/domains/resources/skills/catalog-view.ts +571 -0
  235. package/src/domains/resources/skills/lexical-match.ts +136 -0
  236. package/src/domains/resources/skills/loader.ts +38 -0
  237. package/src/domains/resources/skills/promotion.ts +1 -55
  238. package/src/domains/resources/skills/provenance-pin.ts +50 -20
  239. package/src/domains/safety/action-classifier.ts +1 -0
  240. package/src/domains/session/compaction/branch-summary.ts +4 -1
  241. package/src/domains/session/compaction/compact.ts +18 -2
  242. package/src/domains/session/compaction/cut-point.ts +20 -1
  243. package/src/domains/session/compaction/tokens.ts +48 -3
  244. package/src/domains/session/context-accounting.ts +8 -1
  245. package/src/domains/session/continuity/carry.ts +59 -0
  246. package/src/domains/session/continuity/contract.ts +592 -0
  247. package/src/domains/session/continuity/evidence.ts +290 -0
  248. package/src/domains/session/continuity/fold.ts +1075 -0
  249. package/src/domains/session/continuity/note.ts +79 -0
  250. package/src/domains/session/continuity/operator-request.ts +104 -0
  251. package/src/domains/session/continuity/persistence.ts +408 -0
  252. package/src/domains/session/continuity/ports.ts +231 -0
  253. package/src/domains/session/continuity/projection.ts +538 -0
  254. package/src/domains/session/continuity/validate.ts +352 -0
  255. package/src/domains/session/entries.ts +50 -3
  256. package/src/domains/session/index.ts +48 -0
  257. package/src/domains/session/migrations/index.ts +7 -3
  258. package/src/domains/session/tree/fork.ts +15 -1
  259. package/src/domains/session/usage.ts +2 -2
  260. package/src/engine/acp/commands.ts +1 -1
  261. package/src/engine/agent.ts +133 -9
  262. package/src/engine/session.ts +13 -6
  263. package/src/entry/orchestrator.ts +116 -33
  264. package/src/interactive/chat-loop-messages.ts +2 -2
  265. package/src/interactive/chat-loop.ts +219 -63
  266. package/src/interactive/chat-renderer.ts +99 -8
  267. package/src/interactive/context-overlay.ts +24 -3
  268. package/src/interactive/continuity-controller.ts +526 -0
  269. package/src/interactive/dispatch-board.ts +21 -3
  270. package/src/interactive/footer/dashboard.ts +25 -1
  271. package/src/interactive/footer/key-hints.ts +2 -2
  272. package/src/interactive/footer/pages.ts +70 -27
  273. package/src/interactive/footer/widgets.ts +32 -7
  274. package/src/interactive/footer-panel.ts +1 -1
  275. package/src/interactive/interactive-application.ts +7 -2
  276. package/src/interactive/interactive-input-runtime.ts +2 -2
  277. package/src/interactive/interactive-presentation.ts +15 -0
  278. package/src/interactive/interactive-slash-runtime.ts +45 -8
  279. package/src/interactive/interactive-tickers.ts +7 -1
  280. package/src/interactive/model-session-replay.ts +130 -3
  281. package/src/interactive/output-reserve.ts +35 -0
  282. package/src/interactive/overlay-general-openers.ts +12 -8
  283. package/src/interactive/overlay-key-routing.ts +6 -9
  284. package/src/interactive/overlay-lifecycle.ts +10 -6
  285. package/src/interactive/overlay-session-lifecycle.ts +21 -14
  286. package/src/interactive/quota-view.ts +229 -0
  287. package/src/interactive/session-last-turn.ts +1 -1
  288. package/src/interactive/session-transcript.ts +2 -1
  289. package/src/interactive/session-usage-reseed.ts +2 -2
  290. package/src/interactive/side-question.ts +2 -2
  291. package/src/interactive/slash-commands.ts +28 -9
  292. package/src/interactive/turn-context.ts +686 -63
  293. package/src/interactive/turn-middleware.ts +39 -6
  294. package/src/interactive/turn-persistence.ts +15 -2
  295. package/src/interactive/turn-prewarm.ts +1 -1
  296. package/src/interactive/turn-runtime.ts +48 -5
  297. package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
  298. package/src/interactive/welcome-dashboard.ts +26 -3
  299. package/src/tools/agent-tools.ts +3 -1
  300. package/src/tools/bootstrap.ts +6 -0
  301. package/src/tools/builtin-tool-catalog.ts +9 -0
  302. package/src/tools/context/index.ts +148 -113
  303. package/src/tools/context/surface.ts +11 -7
  304. package/src/tools/core-bootstrap.ts +4 -0
  305. package/src/tools/observation.ts +8 -2
  306. package/src/tools/policy.ts +3 -0
  307. package/src/tools/self-compact.ts +31 -0
  308. package/src/tools/surface.ts +1 -0
  309. package/src/tools/tasks.ts +1 -1
  310. package/dist/chunk-CTFPFW3H.js +0 -44
  311. package/dist/chunk-EOJPDNUP.js +0 -1325
  312. package/dist/chunk-I4Y4WKZR.js +0 -386
@@ -2,6 +2,7 @@ import { homedir } from "node:os";
2
2
  import { relative } from "node:path";
3
3
  import { BusChannels } from "../core/bus-events.js";
4
4
  import type { SafeEventBus } from "../core/event-bus.js";
5
+ import type { BudgetInspection } from "../domains/context/budget/inspection.js";
5
6
  import type { WorkingSetView } from "../domains/context/working-set/contract.js";
6
7
  import { formatContextWindowSlots } from "../domains/providers/index.js";
7
8
  import type { ContextLedger, ContextLedgerGroup } from "../domains/session/context-ledger.js";
@@ -301,6 +302,7 @@ export interface OpenContextOverlayOptions {
301
302
  chat?: {
302
303
  onEvent(handler: (event: { type: string }) => void): () => void;
303
304
  isStreaming(): boolean;
305
+ inspectLiveBudget?(): BudgetInspection;
304
306
  };
305
307
  /** Working-set fold at the live leaf; null or absent hides the section. */
306
308
  getWorkingSet?: () => WorkingSetView | null;
@@ -320,13 +322,32 @@ export function openContextOverlay(
320
322
  options?: OpenContextOverlayOptions,
321
323
  ): OverlayHandle {
322
324
  let contentWidth = DEFAULT_CONTENT_WIDTH;
323
- const render = (): string =>
324
- renderContextLedgerLines(
325
+ const render = (): string => {
326
+ const lines = renderContextLedgerLines(
325
327
  getLedger(),
326
328
  contentWidth,
327
329
  options?.getWorkingSet?.() ?? null,
328
330
  options?.getWorkingSetConfig?.() ?? null,
329
- ).join("\n");
331
+ );
332
+ const inspection = options?.chat?.inspectLiveBudget?.();
333
+ if (inspection?.status === "available") {
334
+ const view = inspection.view;
335
+ const count = (value: number | null) => (value === null ? "unknown" : formatTokens(value));
336
+ lines.push(
337
+ "",
338
+ `Next request: ${count(view.inputTokens)} input + ${count(view.outputReserveTokens)} output`,
339
+ `Headroom: ${count(view.headroomTokens)} · ${view.phase}`,
340
+ );
341
+ if (view.pendingHandoff) {
342
+ lines.push(
343
+ `Handoff: ${view.lastOutcome?.outcome ?? "pending"}`,
344
+ view.pendingHandoff.id,
345
+ `/context recover ${view.pendingHandoff.id} reduce|deliver`,
346
+ );
347
+ } else if (view.lastOutcome) lines.push(`Last handoff: ${view.lastOutcome.outcome}`);
348
+ }
349
+ return lines.join("\n");
350
+ };
330
351
  const text = new Text(render(), 0, 0);
331
352
  const body = {
332
353
  render(width: number): string[] {
@@ -0,0 +1,526 @@
1
+ /** Live ownership and execution around the session domain's durable protocol. */
2
+ import { randomUUID } from "node:crypto";
3
+ import {
4
+ type AcceptedNote,
5
+ type ContinuityAppendable,
6
+ type ContinuityCheckpointPayload,
7
+ type ContinuityFoldResult,
8
+ type ContinuityPersistencePorts,
9
+ HANDOFF_MAX_WINDOW_MS,
10
+ HANDOFF_POLICY_LIMITS,
11
+ type HandoffEvent,
12
+ type HandoffIdentity,
13
+ type HandoffPolicy,
14
+ type HandoffTransactionEntry,
15
+ } from "../domains/session/continuity/contract.js";
16
+ import { resolveContinuityEvidence } from "../domains/session/continuity/evidence.js";
17
+ import { validateContinuityNote } from "../domains/session/continuity/note.js";
18
+ import {
19
+ HANDOFF_RECOVERY_REQUEST_CUSTOM_TYPE,
20
+ type HandoffRecoveryRequestEntry,
21
+ } from "../domains/session/continuity/operator-request.js";
22
+ import { persistContinuityGroup } from "../domains/session/continuity/persistence.js";
23
+ import { resolveContinuityProjection } from "../domains/session/continuity/projection.js";
24
+ import type { SessionEntry } from "../domains/session/entries.js";
25
+
26
+ export interface ContinuityReductionHooks {
27
+ beforeSummaryCall(): void;
28
+ /** Called before the summary append; the resulting carry reserves the exact commit. */
29
+ checkpointForSummary(summaryRef: string, tokensBefore: number, tokensAfter: number): ContinuityCheckpointPayload;
30
+ }
31
+
32
+ export interface ContinuityOrigin {
33
+ sessionId: string;
34
+ leafTurnId: string;
35
+ initiatingTurnId: string;
36
+ sourceRevision: string;
37
+ ports: ContinuityPersistencePorts;
38
+ }
39
+
40
+ export interface ContinuityControllerDeps {
41
+ /** Captures navigation/runtime ownership by value. Never follows a changed session. */
42
+ captureOrigin(): ContinuityOrigin;
43
+ entries(): ReadonlyArray<SessionEntry>;
44
+ leaf(): string | null;
45
+ admitNote(note: AcceptedNote): boolean;
46
+ fits(): boolean;
47
+ inputTokens(): number;
48
+ reduce(hooks: ContinuityReductionHooks, signal: AbortSignal): Promise<void>;
49
+ installReplay(): void;
50
+ onCommit?(commitId: string, outcome: "summarized" | "evicted" | "continuity_only"): void;
51
+ notice(text: string): void;
52
+ now?: () => number;
53
+ id?: () => string;
54
+ }
55
+
56
+ interface LiveHandoff {
57
+ origin: ContinuityOrigin;
58
+ identity: HandoffIdentity;
59
+ accepted: AcceptedNote;
60
+ policy: HandoffPolicy;
61
+ deadline: number;
62
+ abort: AbortController;
63
+ deliveryId?: string;
64
+ uncertain: boolean;
65
+ deadlineTimer?: ReturnType<typeof setTimeout>;
66
+ }
67
+
68
+ export class ContinuityController {
69
+ private live: LiveHandoff | null = null;
70
+ private busy = false;
71
+ private readonly now: () => number;
72
+ private readonly id: () => string;
73
+
74
+ constructor(private readonly deps: ContinuityControllerDeps) {
75
+ this.now = deps.now ?? Date.now;
76
+ this.id = deps.id ?? randomUUID;
77
+ }
78
+
79
+ private armDeadline(live: LiveHandoff): void {
80
+ live.deadlineTimer = setTimeout(
81
+ () => live.abort.abort(new Error("Handoff automatic deadline expired.")),
82
+ Math.max(1, live.deadline - this.now()),
83
+ );
84
+ live.deadlineTimer.unref?.();
85
+ }
86
+
87
+ private fold(live = this.live): ContinuityFoldResult | null {
88
+ if (!live?.origin.ports.isOriginCurrent() || live.origin.ports.isStateRemoved()) return null;
89
+ return resolveContinuityProjection({
90
+ entries: this.deps.entries(),
91
+ sessionId: live.origin.sessionId,
92
+ nowMs: this.now(),
93
+ }).current;
94
+ }
95
+
96
+ private check(live: LiveHandoff): void {
97
+ live.abort.signal.throwIfAborted();
98
+ if (this.live !== live || !live.origin.ports.isOriginCurrent()) throw new Error("Handoff ownership changed.");
99
+ if (live.uncertain) throw new Error("Handoff persistence is unresolved; inspect before recovery.");
100
+ if (this.now() >= live.deadline) throw new Error("Handoff automatic deadline expired.");
101
+ }
102
+
103
+ private async persist(
104
+ live: LiveHandoff,
105
+ entries: ReadonlyArray<ContinuityAppendable | SessionEntry>,
106
+ checkpoint = false,
107
+ closing = false,
108
+ ): Promise<void> {
109
+ const result = await persistContinuityGroup(
110
+ {
111
+ entries: entries.map((entry) => ({ ...entry })),
112
+ barrier: checkpoint ? { kind: "checkpoint", reason: "context-handoff" } : { kind: "flush" },
113
+ },
114
+ live.origin.ports,
115
+ {
116
+ limit: live.policy.flushRetryLimit,
117
+ deadlineAtMs: closing ? this.now() + 5000 : live.deadline,
118
+ now: this.now,
119
+ wait: async () => {},
120
+ },
121
+ );
122
+ if (result.status !== "durable" || !result.confirmedWithinDeadline) {
123
+ live.uncertain = true;
124
+ throw new Error(
125
+ `Handoff durability unresolved: ${result.status === "durable" ? "deadline expired" : result.reason}`,
126
+ );
127
+ }
128
+ }
129
+
130
+ private async transition(live: LiveHandoff, event: HandoffEvent, closing = false): Promise<void> {
131
+ const fold = this.fold(live);
132
+ if (!fold?.validated || fold.identity?.handoffId !== live.identity.handoffId || !fold.state)
133
+ throw new Error("Handoff head cannot be validated.");
134
+ const entryId = this.id();
135
+ const entry: HandoffTransactionEntry = {
136
+ kind: "handoffTransaction",
137
+ schemaVersion: 1,
138
+ turnId: entryId,
139
+ parentTurnId: live.identity.branchAnchorTurnId,
140
+ timestamp: new Date(this.now()).toISOString(),
141
+ identity: live.identity,
142
+ transition: {
143
+ entryId,
144
+ prevEntryId: fold.state.transition.entryId,
145
+ sequence: fold.state.transition.sequence + 1,
146
+ attempt: fold.attemptsSpent + (event.phase === "reducing" ? 1 : 0),
147
+ },
148
+ event,
149
+ };
150
+ await this.persist(live, [entry], false, closing);
151
+ if (!this.fold(live)?.validated) throw new Error("Persisted handoff transition failed validation.");
152
+ }
153
+
154
+ async request(note: unknown, toolCallId: string, signal?: AbortSignal): Promise<string> {
155
+ const validation = validateContinuityNote(note);
156
+ if (!validation.ok) throw new Error(`Invalid handoff note: ${validation.reason}`);
157
+ if (!toolCallId || this.busy || this.live)
158
+ throw new Error("A context handoff is already pending or no tool identity was supplied.");
159
+ signal?.throwIfAborted();
160
+ const origin = this.deps.captureOrigin();
161
+ if (origin.ports.isStateRemoved() || !origin.ports.isOriginCurrent())
162
+ throw new Error("The originating session is unavailable.");
163
+ if (!this.deps.admitNote(validation.accepted))
164
+ throw new Error("Exact handoff note and protected context exceed the replay budget.");
165
+ const prior = resolveContinuityProjection({
166
+ entries: this.deps.entries(),
167
+ sessionId: origin.sessionId,
168
+ nowMs: this.now(),
169
+ }).current;
170
+ if (prior && prior.phase !== "absent" && prior.phase !== "acknowledged")
171
+ throw new Error("An earlier handoff requires operator recovery.");
172
+ const preparedAtMs = this.now();
173
+ const policy = {
174
+ ...HANDOFF_POLICY_LIMITS,
175
+ preparedAtMs,
176
+ automaticDeadlineAtMs: preparedAtMs + HANDOFF_MAX_WINDOW_MS,
177
+ };
178
+ const identity: HandoffIdentity = {
179
+ handoffId: this.id(),
180
+ preparedEntryId: this.id(),
181
+ commitId: this.id(),
182
+ commitEntryId: this.id(),
183
+ originSessionId: origin.sessionId,
184
+ branchAnchorTurnId: origin.leafTurnId,
185
+ initiatingTurnId: origin.initiatingTurnId,
186
+ toolCallId,
187
+ sourceRevision: origin.sourceRevision,
188
+ };
189
+ const live: LiveHandoff = {
190
+ origin,
191
+ identity,
192
+ accepted: validation.accepted,
193
+ policy,
194
+ deadline: policy.automaticDeadlineAtMs,
195
+ abort: new AbortController(),
196
+ uncertain: false,
197
+ };
198
+ this.live = live;
199
+ this.armDeadline(live);
200
+ const entry: HandoffTransactionEntry = {
201
+ kind: "handoffTransaction",
202
+ schemaVersion: 1,
203
+ turnId: identity.preparedEntryId,
204
+ parentTurnId: identity.branchAnchorTurnId,
205
+ timestamp: new Date(preparedAtMs).toISOString(),
206
+ identity,
207
+ transition: { entryId: identity.preparedEntryId, prevEntryId: null, sequence: 0, attempt: 0 },
208
+ event: { phase: "prepared", accepted: live.accepted, policy },
209
+ };
210
+ try {
211
+ await this.persist(live, [entry]);
212
+ signal?.throwIfAborted();
213
+ this.check(live);
214
+ return `Context handoff ${identity.handoffId} prepared. The exact note is saved. Reduction and continuation follow after this tool receipt is persisted.`;
215
+ } catch (error) {
216
+ await this.pause();
217
+ throw error;
218
+ }
219
+ }
220
+
221
+ private readyPayload(
222
+ live: LiveHandoff,
223
+ outcome: "summarized" | "evicted" | "continuity_only",
224
+ before: number,
225
+ after: number,
226
+ ref?: string,
227
+ ): ContinuityCheckpointPayload {
228
+ this.check(live);
229
+ const fold = this.fold(live);
230
+ if (!fold?.validated || fold.phase !== "reducing" || !fold.state)
231
+ throw new Error("No durable reduction attempt owns this checkpoint.");
232
+ const transition = {
233
+ entryId: live.identity.commitEntryId,
234
+ prevEntryId: fold.state.transition.entryId,
235
+ sequence: fold.state.transition.sequence + 1,
236
+ attempt: fold.attemptsSpent,
237
+ };
238
+ return {
239
+ schemaVersion: 1,
240
+ identity: live.identity,
241
+ accepted: live.accepted,
242
+ policy: live.policy,
243
+ commit: {
244
+ outcome,
245
+ entry: {
246
+ turnId: live.identity.commitEntryId,
247
+ parentTurnId: live.identity.branchAnchorTurnId,
248
+ timestamp: new Date(this.now()).toISOString(),
249
+ },
250
+ transition,
251
+ tokensBefore: before,
252
+ tokensAfter: after,
253
+ ...(outcome === "summarized" ? { summaryRef: ref ?? "" } : outcome === "evicted" ? { evictionRef: ref ?? "" } : {}),
254
+ },
255
+ state: { transition, event: { phase: "ready" }, activeResume: fold.state.activeResume, delivery: null },
256
+ };
257
+ }
258
+
259
+ /** Called only at the settled engine boundary, after awaited result persistence. */
260
+ async settle(signal?: AbortSignal, recovery = false): Promise<boolean> {
261
+ const live = this.live;
262
+ if (!live || this.busy || live.deliveryId) return false;
263
+ this.busy = true;
264
+ const onAbort = () => live.abort.abort(signal?.reason);
265
+ signal?.addEventListener("abort", onAbort, { once: true });
266
+ try {
267
+ if (signal?.aborted) onAbort();
268
+ this.check(live);
269
+ const fold = this.fold(live);
270
+ if (!fold?.validated) throw new Error("Handoff evidence is unresolved.");
271
+ if (!recovery) {
272
+ const entries = this.deps.entries();
273
+ const preparedIndex = entries.findIndex((entry) => entry.turnId === live.identity.preparedEntryId);
274
+ const receipt =
275
+ preparedIndex >= 0 &&
276
+ entries.slice(preparedIndex + 1).some((entry) => {
277
+ if (entry.kind !== "message" || entry.role !== "tool_result") return false;
278
+ const payload = entry.payload as { toolCallId?: string; toolName?: string; isError?: boolean };
279
+ return (
280
+ entry.parentTurnId === live.origin.leafTurnId &&
281
+ payload.toolCallId === live.identity.toolCallId &&
282
+ payload.toolName === "self_compact" &&
283
+ payload.isError === false
284
+ );
285
+ });
286
+ if (!receipt) throw new Error("Matching successful handoff receipt has not been persisted.");
287
+ }
288
+ await this.persist(live, []);
289
+ this.check(live);
290
+ if (!fold.commit) {
291
+ if (fold.attemptsSpent >= live.policy.maxAttempts) throw new Error("Handoff reduction attempts exhausted.");
292
+ const resumeRef = fold.state?.activeResume?.entryId;
293
+ await this.transition(live, { phase: "reducing", ...(resumeRef ? { resumeRef } : {}) });
294
+ const before = this.deps.inputTokens();
295
+ const priorIds = new Set(this.deps.entries().map((entry) => entry.turnId));
296
+ let payload: ContinuityCheckpointPayload | undefined;
297
+ let calls = 0;
298
+ await this.deps.reduce(
299
+ {
300
+ beforeSummaryCall: () => {
301
+ this.check(live);
302
+ if (++calls > live.policy.maxSummaryCallsPerAttempt)
303
+ throw new Error("Handoff summary invocation limit reached.");
304
+ },
305
+ checkpointForSummary: (ref, tokensBefore, tokensAfter) => {
306
+ payload = this.readyPayload(live, "summarized", tokensBefore, tokensAfter, ref);
307
+ return payload;
308
+ },
309
+ },
310
+ live.abort.signal,
311
+ );
312
+ this.check(live);
313
+ if (!payload) {
314
+ this.deps.installReplay();
315
+ if (!this.deps.fits()) throw new Error("Context remains unsafe after reduction; the exact note is retained.");
316
+ const eviction = [...this.deps.entries()]
317
+ .reverse()
318
+ .find((entry) => entry.kind === "contextEviction" && !priorIds.has(entry.turnId));
319
+ payload = this.readyPayload(
320
+ live,
321
+ eviction ? "evicted" : "continuity_only",
322
+ before,
323
+ this.deps.inputTokens(),
324
+ eviction?.turnId,
325
+ );
326
+ }
327
+ await this.persist(
328
+ live,
329
+ [
330
+ {
331
+ kind: "continuityCommit",
332
+ ...payload.commit.entry,
333
+ continuity: { ...payload, state: { ...payload.state, activeResume: null } },
334
+ },
335
+ ],
336
+ true,
337
+ );
338
+ this.check(live);
339
+ if (!this.fold(live)?.validated) throw new Error("Handoff checkpoint cannot be validated.");
340
+ this.deps.onCommit?.(live.identity.commitId, payload.commit.outcome);
341
+ }
342
+ this.deps.installReplay();
343
+ if (!this.deps.fits()) throw new Error("Compacted continuation exceeds the request budget.");
344
+ this.check(live);
345
+ const continuationTurnId = this.deps.leaf();
346
+ if (!continuationTurnId) throw new Error("Continuation has no persisted message anchor.");
347
+ const deliveryId = this.id();
348
+ const deliveryFold = this.fold(live);
349
+ const resumeRef = deliveryFold?.phase === "resumed" ? deliveryFold.state?.activeResume?.entryId : undefined;
350
+ await this.transition(live, {
351
+ phase: "delivered",
352
+ deliveryId,
353
+ continuationTurnId,
354
+ ...(resumeRef ? { resumeRef } : {}),
355
+ });
356
+ this.check(live);
357
+ live.deliveryId = deliveryId;
358
+ return true;
359
+ } catch (error) {
360
+ await this.pause();
361
+ throw error;
362
+ } finally {
363
+ signal?.removeEventListener("abort", onAbort);
364
+ this.busy = false;
365
+ }
366
+ }
367
+
368
+ /** Synchronous final provider boundary, including steering drained after prepare. */
369
+ admission(): { block: false; correlationId?: string } | { block: true; reason: string } {
370
+ const live = this.live;
371
+ if (!live) return { block: false };
372
+ try {
373
+ this.check(live);
374
+ if (!live.deliveryId) throw new Error("Context handoff has not durably reached delivery.");
375
+ return { block: false, correlationId: live.deliveryId };
376
+ } catch (error) {
377
+ return { block: true, reason: error instanceof Error ? error.message : String(error) };
378
+ }
379
+ }
380
+
381
+ async response(entryId: string | undefined, correlationId: string | undefined): Promise<void> {
382
+ const live = this.live;
383
+ if (
384
+ !live ||
385
+ !entryId ||
386
+ !correlationId ||
387
+ correlationId !== live.deliveryId ||
388
+ live.abort.signal.aborted ||
389
+ !live.origin.ports.isOriginCurrent()
390
+ )
391
+ return;
392
+ this.check(live);
393
+ const evidence = resolveContinuityEvidence({
394
+ entries: this.deps.entries(),
395
+ unreadableRecords: 0,
396
+ }).terminalResponses.find((row) => row.entryId === entryId);
397
+ if (evidence?.status !== "success") return;
398
+ await this.transition(live, { phase: "acknowledged", deliveryId: correlationId, terminalResponseEntryId: entryId });
399
+ if (live.deadlineTimer) clearTimeout(live.deadlineTimer);
400
+ this.live = null;
401
+ }
402
+
403
+ cancel(): void {
404
+ this.live?.abort.abort(new Error("Operator cancelled the context handoff."));
405
+ }
406
+
407
+ async pause(): Promise<void> {
408
+ const live = this.live;
409
+ if (!live) return;
410
+ live.abort.abort();
411
+ if (live.deadlineTimer) clearTimeout(live.deadlineTimer);
412
+ try {
413
+ const fold = this.fold(live);
414
+ if (
415
+ !live.uncertain &&
416
+ fold?.validated &&
417
+ ["prepared", "reducing", "ready", "delivered", "resumed"].includes(fold.phase)
418
+ ) {
419
+ await this.transition(
420
+ live,
421
+ { phase: "paused", reason: live.deliveryId ? "delivery_uncertain" : "operator_cancelled" },
422
+ true,
423
+ );
424
+ }
425
+ } catch (error) {
426
+ this.deps.notice(`Handoff ${live.identity.handoffId}: ${error instanceof Error ? error.message : String(error)}`);
427
+ } finally {
428
+ if (this.live === live) this.live = null;
429
+ }
430
+ }
431
+
432
+ /** Host-only operator action; never reachable through tool arguments. */
433
+ async recover(handoffId: string, action: "reduce" | "deliver"): Promise<void> {
434
+ if (this.live || this.busy) throw new Error("A handoff is already active.");
435
+ const origin = this.deps.captureOrigin();
436
+ let fold = resolveContinuityProjection({
437
+ entries: this.deps.entries(),
438
+ sessionId: origin.sessionId,
439
+ nowMs: this.now(),
440
+ }).current;
441
+ if (!fold?.validated || fold.identity?.handoffId !== handoffId || !fold.accepted || !fold.policy || !fold.state)
442
+ throw new Error("No validated handoff matches that identity on this branch.");
443
+ if (["prepared", "reducing", "ready", "delivered", "resumed"].includes(fold.phase)) {
444
+ // A restarted process has no live delivery lease. Record that fact
445
+ // before accepting this explicit operator request; never auto-redeliver.
446
+ this.live = {
447
+ origin,
448
+ identity: fold.identity,
449
+ accepted: fold.accepted,
450
+ policy: fold.policy,
451
+ deadline: this.now() + HANDOFF_MAX_WINDOW_MS,
452
+ abort: new AbortController(),
453
+ uncertain: false,
454
+ };
455
+ if (fold.missingCommit) {
456
+ const expected = { ...fold.missingCommit.entry };
457
+ const readback = origin.ports.readExact(expected);
458
+ if (readback.status !== "absent" && readback.status !== "matching") {
459
+ this.live = null;
460
+ throw new Error("Commit reconstruction is unresolved.");
461
+ }
462
+ await this.persist(this.live, readback.status === "absent" ? [expected] : [], true);
463
+ }
464
+ await this.pause();
465
+ fold = resolveContinuityProjection({
466
+ entries: this.deps.entries(),
467
+ sessionId: origin.sessionId,
468
+ nowMs: this.now(),
469
+ }).current;
470
+ if (!fold?.validated || !fold.identity || !fold.accepted || !fold.policy || !fold.state)
471
+ throw new Error("Interrupted handoff could not be paused durably.");
472
+ }
473
+ if (!["paused", "failed"].includes(fold.phase))
474
+ throw new Error("Only a paused or failed handoff accepts operator recovery.");
475
+ if (
476
+ action === "reduce" ? fold.commit !== null || fold.attemptsSpent >= fold.policy.maxAttempts : fold.commit === null
477
+ )
478
+ throw new Error("That recovery action is unavailable for this handoff.");
479
+ const now = this.now();
480
+ const live: LiveHandoff = {
481
+ origin,
482
+ identity: fold.identity,
483
+ accepted: fold.accepted,
484
+ policy: fold.policy,
485
+ deadline: now + HANDOFF_MAX_WINDOW_MS,
486
+ abort: new AbortController(),
487
+ uncertain: false,
488
+ };
489
+ this.live = live;
490
+ this.armDeadline(live);
491
+ const request: HandoffRecoveryRequestEntry = {
492
+ kind: "custom",
493
+ customType: HANDOFF_RECOVERY_REQUEST_CUSTOM_TYPE,
494
+ turnId: this.id(),
495
+ parentTurnId: origin.leafTurnId,
496
+ timestamp: new Date(now).toISOString(),
497
+ display: false,
498
+ data: {
499
+ version: 1,
500
+ requestKind: "handoff_recovery",
501
+ handoffId,
502
+ action,
503
+ sessionId: origin.sessionId,
504
+ branchAnchorTurnId: fold.identity.branchAnchorTurnId,
505
+ selectedLeafTurnId: origin.leafTurnId,
506
+ pausedOrFailedEntryId: fold.state.transition.entryId,
507
+ },
508
+ };
509
+ try {
510
+ await this.persist(live, [request]);
511
+ await this.transition(live, {
512
+ phase: "resumed",
513
+ authority: {
514
+ operatorRequestEntryId: request.turnId,
515
+ pausedOrFailedEntryId: fold.state.transition.entryId,
516
+ action,
517
+ automaticDeadlineAtMs: live.deadline,
518
+ },
519
+ });
520
+ await this.settle(undefined, true);
521
+ } catch (error) {
522
+ await this.pause();
523
+ throw error;
524
+ }
525
+ }
526
+ }
@@ -23,12 +23,14 @@ import {
23
23
  } from "../domains/observability/index.js";
24
24
  import type { WorkerAction, WorkerProgressSnapshot } from "../domains/observability/worker-progress.js";
25
25
  import { type CostProvenance, foregroundStreamUsage } from "../domains/providers/index.js";
26
+ import type { UsageSnapshot } from "../domains/quota/types.js";
26
27
  import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
27
28
  import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
28
29
  import { formatWorkerContextMeter } from "./context-meter.js";
29
30
  import { COUNCIL_SYNTHESIS_LABEL } from "./council.js";
30
31
  import { type CouncilGroupView, type CouncilMemberView, councilGroupBody, councilIslandLines } from "./council-grid.js";
31
32
  import { formatFooterTokens } from "./footer-panel.js";
33
+ import { routeWeeklyQuota } from "./quota-view.js";
32
34
  import { presentWorkerContractAnswer, safeWorkerAnswerText } from "./renderers/worker-answer.js";
33
35
  import {
34
36
  type ClioTheme,
@@ -780,8 +782,9 @@ function renderDispatchCard(
780
782
  return frame(theme, cardTitle, bodyLines, width, { rightMeta: elapsed });
781
783
  }
782
784
 
783
- function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
785
+ function renderTaskIslandRow(row: DispatchBoardRow, width: number, quota: ReadonlyArray<UsageSnapshot>): string[] {
784
786
  const theme = clioTheme();
787
+ const weekly = routeWeeklyQuota(row, quota);
785
788
  const agentLabel = agentDisplayLabel(row);
786
789
  const elapsed = formatCompactMs(row.elapsedMs);
787
790
  const cost = formatCostAggregate(costAggregateForAmount(row.costUsd, row.costProvenance)) ?? COST_NOT_MEASURED;
@@ -854,6 +857,17 @@ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
854
857
  ...quality.map((line) => padAnsi(theme.fg("muted", line), width)),
855
858
  ...task.map((line) => padAnsi(line, width)),
856
859
  padAnsi(telemetry, width),
860
+ ...(weekly
861
+ ? [
862
+ padAnsi(
863
+ theme.fg(
864
+ weekly.severity === "critical" ? "error" : weekly.severity === "normal" ? "dim" : "warning",
865
+ ` Shared account · ${weekly.label}`,
866
+ ),
867
+ width,
868
+ ),
869
+ ]
870
+ : []),
857
871
  ...wrapTextWithAnsi(theme.fg("dim", ` /view dispatch:${row.runId}`), width),
858
872
  ];
859
873
  }
@@ -1110,7 +1124,11 @@ export function createDispatchBoardView(
1110
1124
  };
1111
1125
  }
1112
1126
 
1113
- export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, maxRows = 4): string[] {
1127
+ export function formatTaskIslandLines(
1128
+ rows: ReadonlyArray<DispatchBoardRow>,
1129
+ maxRows = 4,
1130
+ quota: ReadonlyArray<UsageSnapshot> = [],
1131
+ ): string[] {
1114
1132
  // Councils are folded before the row cap, so a five-member council costs the
1115
1133
  // island one card and never crowds out the runs beside it.
1116
1134
  const items = dispatchBoardItems(rows.filter((row) => !isHelperRun(row)));
@@ -1131,7 +1149,7 @@ export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, max
1131
1149
  body.push(
1132
1150
  ...(item.kind === "council"
1133
1151
  ? councilIslandLines(clioTheme(), item.group, TASK_ISLAND_WIDTH)
1134
- : renderTaskIslandRow(item.row, TASK_ISLAND_WIDTH)),
1152
+ : renderTaskIslandRow(item.row, TASK_ISLAND_WIDTH, quota)),
1135
1153
  );
1136
1154
  }
1137
1155
  const hidden = items.length - visibleItems.length;