@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
@@ -3,14 +3,9 @@ import path from "node:path";
3
3
  import type { ContextRecalledPayload } from "../../core/bus-events.js";
4
4
  import type { ClioSettings } from "../../core/config.js";
5
5
  import { settingsAwareness } from "../../core/settings-awareness.js";
6
- import {
7
- SKILL_INSTALL_OFFER_OPTION_NEVER,
8
- SKILL_INSTALL_OFFER_OPTION_NOT_NOW,
9
- SKILL_INSTALL_OFFER_OPTION_PROJECT,
10
- SKILL_INSTALL_OFFER_OPTION_USER,
11
- SKILL_SUGGESTION_ANCHOR,
12
- } from "../../core/skill-activation.js";
6
+ import { SKILL_SUGGESTION_ANCHOR } from "../../core/skill-activation.js";
13
7
  import { ToolNames } from "../../core/tool-names.js";
8
+ import type { BudgetProvider } from "../../domains/context/budget/inspection.js";
14
9
  import type { WorkerRecall } from "../../domains/context/worker/recall.js";
15
10
  import { foldWorkingSet } from "../../domains/context/working-set/fold.js";
16
11
  import {
@@ -21,7 +16,9 @@ import {
21
16
  resolveRecall,
22
17
  } from "../../domains/context/working-set/recall.js";
23
18
  import {
19
+ buildSkillCatalogView,
24
20
  checkSkillDrift,
21
+ checkSkillDriftBatch,
25
22
  discoverMarketplaceSkills,
26
23
  installedSkillNames,
27
24
  installedSkillPackages,
@@ -30,6 +27,7 @@ import {
30
27
  type MarketplaceSkill,
31
28
  modelVisibleSkills,
32
29
  type Skill,
30
+ type SkillCatalogRowKind,
33
31
  } from "../../domains/resources/index.js";
34
32
  import type { SessionEntryInput } from "../../domains/session/contract.js";
35
33
  import type { SessionEntry } from "../../domains/session/entries.js";
@@ -78,6 +76,8 @@ export interface ContextSessionDeps {
78
76
  }
79
77
 
80
78
  export interface ContextToolDeps {
79
+ /** Refreshes this run's native budget; absent for external/worker registries. */
80
+ getContextBudget?: BudgetProvider;
81
81
  /** Live session view, including overrides. Omit when no authoritative settings snapshot exists. */
82
82
  getSettings?: () => Readonly<ClioSettings>;
83
83
  /** Run-scoped evidence port; never reads or appends the parent session. */
@@ -294,84 +294,29 @@ function marketplaceRowsFor(
294
294
  }
295
295
  }
296
296
 
297
- function renderSkillsList(
298
- skills: ReadonlyArray<Skill>,
299
- marketplace: ReadonlyArray<MarketplaceSkill>,
300
- marketplaceOffered: boolean,
301
- modelActivation: boolean,
302
- packages: ReturnType<typeof installedSkillPackages> = [],
303
- ): string {
304
- if (skills.length === 0 && marketplace.length === 0 && packages.length === 0) {
305
- // A registry that never offers the marketplace (a worker) must not
306
- // claim none is configured; it simply has nothing to list.
307
- return marketplaceOffered
308
- ? "No skills are available in Clio and no additional marketplace skills were found."
309
- : "No skills are available in Clio.";
310
- }
311
- // The header names the listing and nothing more. The reply protocol is
312
- // stated once, as the recency anchor at the bottom, because that is the
313
- // line literal models act on; a second copy up here cost every listing
314
- // call the same sentences again.
315
- const lines = ["Available skills.", ""];
316
-
317
- const native = skills.filter((skill) => skill.source === "clio-coder" || skill.source === "plugin");
318
- const discovered = skills.filter((skill) => skill.source !== "clio-coder" && skill.source !== "plugin");
319
- lines.push(`Ready skills in Clio (${native.length}):`);
320
- if (native.length === 0) lines.push("- none");
321
- for (const skill of native) {
322
- lines.push(`- ${skill.name} (source: ${skill.source}; scope: ${skill.scope}): ${skill.description}`);
323
- }
324
- if (discovered.length > 0) {
325
- lines.push("", "Explicitly supplied session skills (not installed packages):");
326
- for (const skill of discovered) {
327
- lines.push(
328
- `- ${skill.name} (source: ${skill.source}; scope: ${skill.scope}; file: ${skill.filePath}): ${skill.description}`,
329
- );
330
- }
331
- lines.push(
332
- "These skills were explicitly supplied for this session. Preserve their source; session availability does not mean Clio installed or copied them.",
333
- );
334
- }
335
-
336
- if (packages.length > 0) {
337
- lines.push("", `Installed packages providing skills (${packages.length}):`);
338
- for (const pkg of packages)
339
- lines.push(
340
- `- ${pkg.name} (scope: ${pkg.scope}; origin: ${pkg.origin === "catalog" ? "marketplace catalog" : pkg.origin}; state: ${pkg.state}; path: ${pkg.path})`,
341
- );
342
- }
343
- lines.push(
344
- "",
345
- "Other-agent skill folders are discovery-only. Explicitly import into Clio before use; the trust-imports setting does not install or activate loose files.",
346
- );
347
-
348
- if (marketplace.length > 0) {
349
- // Installable rows are suggested exactly like installed ones: the
350
- // operator's /skill <name> prompts to install before it runs, so the
351
- // model's move is the same suggest-and-wait. Only the body is out of
352
- // reach until then, which is why the description is all that appears.
353
- lines.push("", "Marketplace (additional skills available to install; /skill <name> offers to install):");
354
- for (const entry of marketplace) {
355
- const category = entry.category ? ` [${entry.category}]` : "";
356
- lines.push(`- ${entry.name}${category}: ${entry.description}`);
357
- }
358
- }
359
- // Recency anchor with an exact reply shape: literal models act on an output
360
- // template where they skip conditional prose in the header.
361
- lines.push(
362
- "",
363
- modelActivation
364
- ? `If one skill above matches the current task, load it now with context(scope="skills", name="<name>") and continue in the same turn; at this autonomy level you activate installed skills yourself and do not wait for the operator. Marketplace additions still require operator approval. If none match, do not mention skills.`
365
- : `If one skill above matches the current task, begin your reply with the line \`${SKILL_SUGGESTION_ANCHOR}\` (a comma-separated sequence, in order, when several compose), then continue the task in the same turn without it; only the operator can run it. If none match, do not mention skills.`,
366
- );
367
- if (marketplace.length > 0) {
368
- // The offer protocol mirrors the marketplace-offer middleware: fixed
369
- // option labels so the harness recognizes and handles the answer.
370
- lines.push(
371
- `When no installed skill serves the task but a marketplace skill above genuinely does, you may instead ask the operator with ask_user (mode=single_question, header "Install skill") whether to install it, offering exactly: "${SKILL_INSTALL_OFFER_OPTION_PROJECT}", "${SKILL_INSTALL_OFFER_OPTION_USER}", "${SKILL_INSTALL_OFFER_OPTION_NOT_NOW}", "${SKILL_INSTALL_OFFER_OPTION_NEVER}". The harness handles those exact offer options. An explicit operator request or approval also authorizes the documented library install CLI. After installation, refresh the inventory; distinguish installed from ready and report /library reload when required.`,
372
- );
297
+ /**
298
+ * Ready rows whose installed content no longer matches the hash recorded for
299
+ * it, resolved for the whole listing in one manifest read.
300
+ *
301
+ * Drift used to be checked only in the activation branch below, so the listing
302
+ * reported a drifted skill as ready with no caveat and the warning arrived only
303
+ * after the model had already spent its turn loading it. This is the same
304
+ * comparison, one step earlier, through the batched entry point so a listing
305
+ * does not re-parse the pin manifest once per row.
306
+ *
307
+ * It annotates and nothing else. The skill stays listed, stays ready and stays
308
+ * loadable, the installed copy keeps its ownership, and the activation warning
309
+ * is unchanged. A skill with no recorded hash is not marked, because absence of
310
+ * evidence is not drift, and a failure here yields an unmarked listing rather
311
+ * than no listing.
312
+ */
313
+ function driftedSkillNames(skills: ReadonlyArray<Skill>, cwd: string): Set<string> {
314
+ try {
315
+ const reports = checkSkillDriftBatch(skills, cwd);
316
+ return new Set([...reports].filter(([, report]) => report.verdict === "mismatch").map(([name]) => name));
317
+ } catch {
318
+ return new Set<string>();
373
319
  }
374
- return lines.join("\n");
375
320
  }
376
321
 
377
322
  function runWorkspaceScope(
@@ -496,38 +441,71 @@ function runSkillsScope(
496
441
  cwdFromDeps(deps),
497
442
  deps.getSkillLoaderOptions?.().trustProjectCompatRoots === true,
498
443
  );
499
- const rendered = renderSkillsList(
500
- visible,
501
- marketplace,
502
- deps.skillMarketplace !== false,
503
- options?.pendingSkillPolicy?.modelActivation === true,
444
+ // The catalog is bounded but not small, and it has to fit the per-call cap
445
+ // like any observation. It is cut by whole rows with the reply protocol
446
+ // reserved first, rather than by head-truncating the finished string:
447
+ // head truncation keeps the head, and the tail is the one line a model
448
+ // acts on. `query`, `limit` and `offset` are the context tool's existing
449
+ // optional arguments, wired here so a caller can ask for less than all of
450
+ // it; omitting them still lists every row.
451
+ const view = buildSkillCatalogView({
452
+ skills: visible,
504
453
  packages,
505
- );
506
- // The catalog is bounded but not small: the whole listing must fit the
507
- // per-call cap like any observation, and a cut list says so instead of
508
- // silently dropping the marketplace tail.
509
- const truncation = truncateHead(rendered, { maxBytes: reservation.callCapBytes, maxLines: Number.MAX_SAFE_INTEGER });
510
- const total = visible.length + marketplace.length + packages.length;
454
+ marketplace,
455
+ drifted: driftedSkillNames(visible, cwdFromDeps(deps)),
456
+ marketplaceOffered: deps.skillMarketplace !== false,
457
+ modelActivation: options?.pendingSkillPolicy?.modelActivation === true,
458
+ query: typeof args.query === "string" ? args.query : "",
459
+ limit: typeof args.limit === "number" ? args.limit : undefined,
460
+ offset: typeof args.offset === "number" ? args.offset : 0,
461
+ capBytes: reservation.callCapBytes,
462
+ });
463
+ // Shown rows are selected by the row's own stable key, not by its name. An
464
+ // installed standalone skill has a ready row and a package row under one
465
+ // name, and the same package id can be installed at user and project scope,
466
+ // so a name set puts rows in `details` that the text never carried.
467
+ const shownKeys = new Set(view.rows.map((row) => row.key));
468
+ const shown = (kind: SkillCatalogRowKind, identity: string): boolean => shownKeys.has(`${kind}:${identity}`);
511
469
  return finalizeObservation({
512
470
  tool: ToolNames.Context,
513
471
  unit: "entries",
514
- output: truncation.content,
515
- ...(truncation.truncated ? { fullOutput: rendered } : {}),
516
- shownCount: total,
517
- totalCount: total,
518
- truncated: truncation.truncated,
472
+ output: view.text,
473
+ shownCount: view.shown,
474
+ totalCount: view.total,
475
+ truncated: view.nextOffset !== undefined || view.shown < view.total,
476
+ // Offsets index the filtered result set. A continuation carrying only
477
+ // the offset would be applied to the unfiltered catalog, which repeats
478
+ // rows and skips matches, so the envelope says the query is part of it.
479
+ ...(view.nextOffset !== undefined
480
+ ? {
481
+ next: view.filtered ? `offset=${view.nextOffset} with the same query` : `offset=${view.nextOffset}`,
482
+ }
483
+ : {}),
519
484
  details: {
520
- skills: visible.map((skill) => ({
521
- name: skill.name,
522
- scope: skill.scope,
523
- source: skill.source,
524
- path: skill.filePath,
525
- })),
526
- installedPackages: packages,
527
- marketplace: marketplace.map((entry) => ({
528
- name: entry.name,
529
- ...(entry.category ? { category: entry.category } : {}),
530
- })),
485
+ // Rows on this page, so a caller reading `details` sees the same
486
+ // listing the text carries. The totals beside them are how a
487
+ // filtered or paged view is told apart from a complete one.
488
+ skills: visible
489
+ .filter((skill) => shown("ready", skill.filePath) || shown("session", skill.filePath))
490
+ .map((skill) => ({
491
+ name: skill.name,
492
+ scope: skill.scope,
493
+ source: skill.source,
494
+ path: skill.filePath,
495
+ ...(view.driftedNames.includes(skill.name) ? { drift: "mismatch" } : {}),
496
+ })),
497
+ installedPackages: packages.filter((pkg) => shown("package", `${pkg.scope}:${pkg.path}`)),
498
+ marketplace: marketplace
499
+ .filter((entry) => shown("marketplace", entry.sourceUrl))
500
+ .map((entry) => ({
501
+ name: entry.name,
502
+ ...(entry.category ? { category: entry.category } : {}),
503
+ })),
504
+ totalSkills: visible.length,
505
+ totalPackages: packages.length,
506
+ totalMarketplace: marketplace.length,
507
+ ...(view.filtered ? { query: (args.query as string).trim(), matchMode: view.matchMode } : {}),
508
+ ...(view.driftedNames.length > 0 ? { drifted: view.driftedNames } : {}),
531
509
  },
532
510
  reservation,
533
511
  ...(options ? { options } : {}),
@@ -767,6 +745,52 @@ function runRecallScope(
767
745
  });
768
746
  }
769
747
 
748
+ /** A byte-fitted JSON observation. Inspection never spills to disk. */
749
+ function runBudgetScope(
750
+ deps: ContextToolDeps,
751
+ reservation: ObservationReservation,
752
+ options?: ToolInvokeOptions,
753
+ ): ToolResult {
754
+ const inspection = deps.getContextBudget?.() ?? {
755
+ status: "unavailable",
756
+ reason:
757
+ "This run has no native session budget port. Worker and external-agent budgets are not the parent session's budget.",
758
+ };
759
+ const payload =
760
+ inspection.status === "available"
761
+ ? {
762
+ ...inspection,
763
+ mode: "enforced",
764
+ admissionNote:
765
+ "Submission and continuation enforce input plus reserved output against the effective window. Counts are estimates unless provider-attested; pressure advice remains advisory.",
766
+ }
767
+ : inspection;
768
+ const full = JSON.stringify(payload);
769
+ const fits = Buffer.byteLength(full, "utf8") <= reservation.callCapBytes;
770
+ // Do not pass fullOutput to finalizeObservation: an inspection must not
771
+ // persist its view even when an optional projection contains huge text.
772
+ const output = fits
773
+ ? full
774
+ : JSON.stringify({
775
+ status: "unavailable",
776
+ reason: "observation-limit",
777
+ message:
778
+ "The budget view exceeds the available observation bytes. Continue in a follow-up turn; do not retry this turn.",
779
+ });
780
+ return finalizeObservation({
781
+ tool: ToolNames.Context,
782
+ unit: "results",
783
+ format: "json",
784
+ output,
785
+ shownCount: fits ? 1 : 0,
786
+ totalCount: 1,
787
+ totalBytes: Buffer.byteLength(full, "utf8"),
788
+ truncated: !fits,
789
+ reservation,
790
+ ...(options ? { options } : {}),
791
+ });
792
+ }
793
+
770
794
  export function createContextTool(deps: ContextToolDeps = {}): ToolSpec {
771
795
  return {
772
796
  ...contextToolSurface,
@@ -782,12 +806,21 @@ export function createContextTool(deps: ContextToolDeps = {}): ToolSpec {
782
806
  message: `context: scope "${scope}" is a gateway capability now: call gateway(op="call", capability="${capability}", args={...}) or gateway(op="describe", capability="${capability}") for its arguments.`,
783
807
  };
784
808
  }
785
- if (scope !== "workspace" && scope !== "settings" && scope !== "skills" && scope !== "recall") {
809
+ if (
810
+ scope !== "workspace" &&
811
+ scope !== "settings" &&
812
+ scope !== "skills" &&
813
+ scope !== "recall" &&
814
+ scope !== "budget"
815
+ ) {
786
816
  return {
787
817
  kind: "error",
788
- message: `context: scope must be workspace, settings, skills, or recall; got '${scope}'`,
818
+ message: `context: scope must be workspace, settings, skills, recall, or budget; got '${scope}'`,
789
819
  };
790
820
  }
821
+ if (scope === "budget" && Object.keys(args).some((key) => key !== "scope")) {
822
+ return { kind: "error", message: 'context: scope="budget" accepts only scope; it inspects the current request.' };
823
+ }
791
824
  const selfCap = scope === "skills" ? OBSERVE_SELF_CAPS.contextSkills : OBSERVE_SELF_CAPS.contextWorkspace;
792
825
  // Reserved before any scope handler runs, so an exhausted pool answers
793
826
  // with the notice and no scope does its work for nothing.
@@ -796,6 +829,7 @@ export function createContextTool(deps: ContextToolDeps = {}): ToolSpec {
796
829
  return observationBudgetExhausted({
797
830
  tool: ToolNames.Context,
798
831
  unit: scope === "skills" ? "entries" : "results",
832
+ ...(scope === "budget" ? { format: "json" as const } : {}),
799
833
  reservation,
800
834
  subject: `scope=${scope}`,
801
835
  hint: "Continue in a follow-up turn.",
@@ -838,6 +872,7 @@ export function createContextTool(deps: ContextToolDeps = {}): ToolSpec {
838
872
  ...(options ? { options } : {}),
839
873
  });
840
874
  }
875
+ if (scope === "budget") return runBudgetScope(deps, reservation, options);
841
876
  if (scope === "workspace") return runWorkspaceScope(deps, reservation, options);
842
877
  if (scope === "recall") return runRecallScope(deps, args, reservation, options);
843
878
  return runSkillsScope(deps, args, reservation, options);
@@ -12,22 +12,26 @@ import type { ToolSurface } from "../lazy-tool.js";
12
12
  export const contextToolSurface = {
13
13
  name: ToolNames.Context,
14
14
  description:
15
- "Environment context: scope=settings explains current effective settings, autonomy, configured limits, and exact UI commands without exposing credentials; scope=workspace returns the git/project snapshot, scope=skills lists ready skills, installed package states and marketplace options, or loads a ready skill by name, scope=recall returns an exact persisted evicted or summarized tool result by ref; omit ref to discover historical results by query with bounded limit/offset pages. Clio's documentation and the recipe catalog are gateway capabilities (clio_docs, clio_library). For repository code and the repo's generated wiki use code_nav (mode=wiki).",
15
+ "Environment context: scope=settings explains effective settings, autonomy, limits and UI commands without credentials; workspace returns the git/project snapshot; budget inspects the native session's live request budget (read-only, shadow policy); skills lists ready skills, installed states and marketplace options, or loads a ready skill by name; recall retrieves persisted evicted/summarized tool results by ref, or discovers them by query with limit/offset pages. Clio docs and recipes: gateway capabilities clio_docs and clio_library. Repository code/wiki: code_nav (mode=wiki).",
16
16
  parameters: Type.Object({
17
- scope: StringEnum(["workspace", "settings", "skills", "recall"], { description: "Context source." }),
17
+ scope: StringEnum(["workspace", "settings", "skills", "recall", "budget"], { description: "Context source." }),
18
+ // These three carry three scopes each. The attached-schema byte budget in
19
+ // tests/contracts/gateway-prompt.test.ts had 18 bytes of headroom, so
20
+ // naming scope=skills here is paid for by compressing the wording rather
21
+ // than by widening a budget that exists to keep the prompt small.
18
22
  query: Type.Optional(
19
23
  Type.String({
20
24
  description:
21
- "scope=settings: settings path, section, or terms; scope=recall without ref: path, tool, or ref terms.",
25
+ "scope=settings: path/section/terms; scope=recall (no ref): path/tool/ref terms; scope=skills: narrow the list.",
22
26
  }),
23
27
  ),
24
28
  name: Type.Optional(Type.String({ description: "scope=skills: skill name to load; omit to list." })),
25
- limit: Type.Optional(Type.Number({ description: "scope=settings or recall: bounded page size (max 12)." })),
29
+ limit: Type.Optional(
30
+ Type.Number({ description: "scope=settings/recall: page size (max 12); scope=skills: rows (max 200)." }),
31
+ ),
26
32
  ref: Type.Optional(Type.String({ description: "scope=recall: exact persisted result ref; omit for discovery." })),
27
33
  offset: Type.Optional(
28
- Type.Number({
29
- description: "scope=settings or recall discovery: zero-based offset; follow nextOffset (default 0).",
30
- }),
34
+ Type.Number({ description: "scope=settings, recall, or skills: 0-based offset; follow nextOffset." }),
31
35
  ),
32
36
  include_tree: Type.Optional(Type.Boolean({ description: "scope=skills: list files under the skill base_dir." })),
33
37
  }),
@@ -1,6 +1,7 @@
1
1
  import type { ContextRecalledPayload } from "../core/bus-events.js";
2
2
  import type { ClioSettings } from "../core/config.js";
3
3
  import { ToolNames } from "../core/tool-names.js";
4
+ import type { BudgetProvider } from "../domains/context/budget/inspection.js";
4
5
  import type { WorkerRecall } from "../domains/context/worker/recall.js";
5
6
  import type { LoadSkillsInput } from "../domains/resources/index.js";
6
7
  import type { SessionContract } from "../domains/session/contract.js";
@@ -40,6 +41,8 @@ import { webFetchToolSurface, webReadToolSurface } from "./web-fetch-surface.js"
40
41
  import { writeTool } from "./write.js";
41
42
 
42
43
  export interface CoreToolBootstrapDeps {
44
+ /** Native session accounting only. Worker registries do not inherit it. */
45
+ getContextBudget?: BudgetProvider;
43
46
  getSettings?: () => Readonly<ClioSettings>;
44
47
  workerRecall?: WorkerRecall;
45
48
  session?: SessionContract;
@@ -164,6 +167,7 @@ export function registerCoreTools(registry: ToolRegistry, deps: CoreToolBootstra
164
167
  ),
165
168
  });
166
169
  const skillToolDeps = {
170
+ ...(deps.getContextBudget ? { getContextBudget: deps.getContextBudget } : {}),
167
171
  ...(deps.getSettings ? { getSettings: deps.getSettings } : {}),
168
172
  ...(deps.workerRecall ? { workerRecall: deps.workerRecall } : {}),
169
173
  getCwd: () => deps.session?.current()?.cwd ?? process.cwd(),
@@ -306,6 +306,8 @@ function bumpExhaustedStreak(key: string | null): number {
306
306
  export function observationBudgetExhausted(input: {
307
307
  tool: string;
308
308
  unit: ObservationUnit;
309
+ /** Preserve parseable JSON for structured inspection tools, without offloading. */
310
+ format?: ObservationFormat;
309
311
  reservation: ObservationReservation;
310
312
  subject: string;
311
313
  hint: string;
@@ -322,7 +324,11 @@ export function observationBudgetExhausted(input: {
322
324
  recordSpentBytes(reservation, byteLength(message));
323
325
  return { kind: "error", message };
324
326
  }
325
- const output = `[observation budget exhausted for this turn before ${tool} ${subject}: ${spent}. ${hint}]`;
327
+ const message = `observation budget exhausted for this turn before ${tool} ${subject}: ${spent}. ${hint}`;
328
+ const output =
329
+ input.format === "json"
330
+ ? JSON.stringify({ status: "unavailable", reason: "observation-budget-exhausted", message })
331
+ : `[${message}]`;
326
332
  const shownBytes = byteLength(output);
327
333
  recordSpentBytes(reservation, shownBytes);
328
334
  const budget = budgetDetails(reservation);
@@ -336,7 +342,7 @@ export function observationBudgetExhausted(input: {
336
342
  // the full size is the message itself; keep totalBytes >= shownBytes.
337
343
  totalBytes: shownBytes,
338
344
  truncated: true,
339
- format: "text",
345
+ format: input.format ?? "text",
340
346
  ...(budget !== null ? { budget } : {}),
341
347
  };
342
348
  return { kind: "ok", output, details: { observation } };
@@ -36,6 +36,7 @@ export const TOOL_PLANES: Readonly<Record<BuiltinToolName, PlaneExpectation>> =
36
36
  [ToolNames.Find]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
37
37
  [ToolNames.Ls]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
38
38
  [ToolNames.CodeNav]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
39
+ [ToolNames.SelfCompact]: { plane: "orchestrate", actionClass: "read", executionMode: "sequential" },
39
40
  [ToolNames.Context]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
40
41
  [ToolNames.CredentialPresent]: { plane: "observe", actionClass: "read", executionMode: "parallel" },
41
42
  // clio_docs and clio_library are the bundled-documentation and recipe-catalog
@@ -191,6 +192,8 @@ export function validateBuiltinToolPolicy(
191
192
  const includeLedgerTools = options.includeLedgerTools ?? false;
192
193
  const includeNetworkTools = options.includeNetworkTools ?? true;
193
194
  const required = new Set<ToolName>(Object.values(ToolNames));
195
+ // The native interactive host alone supplies the durable continuity port.
196
+ required.delete(ToolNames.SelfCompact);
194
197
  for (const tool of [...required]) {
195
198
  if (!includeSessionTools && SESSION_BOUND_TOOLS.has(tool)) required.delete(tool);
196
199
  if (!includeDispatchTools && DISPATCH_BOUND_TOOLS.has(tool)) required.delete(tool);
@@ -0,0 +1,31 @@
1
+ import { Type } from "typebox";
2
+ import { ToolNames } from "../core/tool-names.js";
3
+ import type { ToolSpec } from "./registry.js";
4
+
5
+ export type RequestSelfCompact = (note: unknown, toolCallId: string, signal?: AbortSignal) => Promise<string>;
6
+
7
+ /** Admission only. The native host owns reduction after this receipt settles. */
8
+ export function createSelfCompactTool(request: RequestSelfCompact): ToolSpec {
9
+ return {
10
+ name: ToolNames.SelfCompact,
11
+ description:
12
+ "Save an exact handoff note and compact context before continuing this task. Call alone, with no sibling tools. Include current objective, decisions, evidence, paths, and next steps. A pending receipt is not proof of completion.",
13
+ parameters: Type.Object({
14
+ note_to_self: Type.String({
15
+ maxLength: 8192,
16
+ description: "Assistant-authored handoff text; nonblank, at most 8192 UTF-8 bytes. Preserved exactly.",
17
+ }),
18
+ }),
19
+ baseActionClass: "read",
20
+ executionMode: "sequential",
21
+ async run(args, options) {
22
+ try {
23
+ const note = (args as { note_to_self?: unknown }).note_to_self;
24
+ const output = await request(note, options?.toolCallId ?? "", options?.signal);
25
+ return { kind: "ok", output };
26
+ } catch (error) {
27
+ return { kind: "error", message: error instanceof Error ? error.message : String(error) };
28
+ }
29
+ },
30
+ };
31
+ }
@@ -39,6 +39,7 @@ export const TOOL_PLACEMENT: Readonly<Record<BuiltinToolName, ToolPlacement>> =
39
39
  [ToolNames.Ls]: "direct",
40
40
  [ToolNames.CodeNav]: "direct",
41
41
  [ToolNames.Context]: "direct",
42
+ [ToolNames.SelfCompact]: "direct",
42
43
  [ToolNames.CredentialPresent]: "gateway",
43
44
  [ToolNames.ClioDocs]: "gateway",
44
45
  [ToolNames.ClioLibrary]: "gateway",
@@ -174,7 +174,7 @@ export function createTasksTool(deps: TasksToolDeps): ToolSpec {
174
174
  "pick moves one operator task uN onto the board; start marks one task active (the current focus); " +
175
175
  "done completes a task (started or still pending) and requires note as the " +
176
176
  "evidence the work actually finished; block parks it with a required reason; drop cancels it; list shows the board. " +
177
- "For operator handoff, pick the intended uN and use its linked tN; CLI hand does not pick it. Never pick unrelated tasks. " +
177
+ "For operator handoff, pick the intended uN before work and use its linked tN; CLI hand alone does not pick it. Never pick unrelated tasks. " +
178
178
  "Before claiming completion, list: the linked row must be completed and the operator task done on the same session/board link; report IDs and states. " +
179
179
  "Work that did not happen is blocked or dropped, never done. A self-created plan is not operator authorization. " +
180
180
  "For proposal-only work, plan/add with initialStatus=blocked and note naming the pending operator decision; " +
@@ -1,44 +0,0 @@
1
- import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
- import {
3
- StringEnum
4
- } from "./chunk-L7G7K4I2.js";
5
- import {
6
- ToolNames
7
- } from "./chunk-V4XJWDHR.js";
8
- import {
9
- typebox_exports
10
- } from "./chunk-GH5ZR6JP.js";
11
- import {
12
- init_esm_shims
13
- } from "./chunk-ECBHH7LU.js";
14
-
15
- // src/tools/context/surface.ts
16
- init_esm_shims();
17
- var contextToolSurface = {
18
- name: ToolNames.Context,
19
- description: "Environment context: scope=settings explains current effective settings, autonomy, configured limits, and exact UI commands without exposing credentials; scope=workspace returns the git/project snapshot, scope=skills lists ready skills, installed package states and marketplace options, or loads a ready skill by name, scope=recall returns an exact persisted evicted or summarized tool result by ref; omit ref to discover historical results by query with bounded limit/offset pages. Clio's documentation and the recipe catalog are gateway capabilities (clio_docs, clio_library). For repository code and the repo's generated wiki use code_nav (mode=wiki).",
20
- parameters: typebox_exports.Object({
21
- scope: StringEnum(["workspace", "settings", "skills", "recall"], { description: "Context source." }),
22
- query: typebox_exports.Optional(
23
- typebox_exports.String({
24
- description: "scope=settings: settings path, section, or terms; scope=recall without ref: path, tool, or ref terms."
25
- })
26
- ),
27
- name: typebox_exports.Optional(typebox_exports.String({ description: "scope=skills: skill name to load; omit to list." })),
28
- limit: typebox_exports.Optional(typebox_exports.Number({ description: "scope=settings or recall: bounded page size (max 12)." })),
29
- ref: typebox_exports.Optional(typebox_exports.String({ description: "scope=recall: exact persisted result ref; omit for discovery." })),
30
- offset: typebox_exports.Optional(
31
- typebox_exports.Number({
32
- description: "scope=settings or recall discovery: zero-based offset; follow nextOffset (default 0)."
33
- })
34
- ),
35
- include_tree: typebox_exports.Optional(typebox_exports.Boolean({ description: "scope=skills: list files under the skill base_dir." }))
36
- }),
37
- baseActionClass: "read",
38
- executionMode: "parallel"
39
- };
40
-
41
- export {
42
- contextToolSurface
43
- };
44
- //# sourceMappingURL=chunk-CTFPFW3H.js.map