@iowarp/clio-coder 0.3.3 → 0.3.6

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 (370) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/CONTRIBUTING.md +7 -7
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-2BEHC4DL.js} +9 -8
  5. package/dist/{agents-72W3BI7I.js → agents-LNNFTM53.js} +29 -24
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-KXXFI2VS.js} +14 -10
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
  10. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  11. package/dist/chunk-3BPUFZDL.js +37 -0
  12. package/dist/chunk-43AOLP7E.js +375 -0
  13. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  14. package/dist/{chunk-6SGHMWE3.js → chunk-5JGRAMKL.js} +5 -5
  15. package/dist/{chunk-V6RTAOC2.js → chunk-6US73PDB.js} +572 -51
  16. package/dist/{chunk-5UFT4SUX.js → chunk-6XXKFVSN.js} +3 -3
  17. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  18. package/dist/chunk-AOCYTWAV.js +449 -0
  19. package/dist/chunk-CFGTUFWB.js +67 -0
  20. package/dist/chunk-CJUB2JJ2.js +1478 -0
  21. package/dist/{chunk-FNTMWMX5.js → chunk-CKXWIANG.js} +14 -12
  22. package/dist/{chunk-PIWWS5BL.js → chunk-CYQKWTG3.js} +63 -78
  23. package/dist/{chunk-LZSJBIVT.js → chunk-DJVECN66.js} +271 -762
  24. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  25. package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
  26. package/dist/{chunk-STBPMHSX.js → chunk-EKY57CSP.js} +51 -84
  27. package/dist/{chunk-DUYJ5IO6.js → chunk-EYPA3EGJ.js} +12 -4
  28. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  29. package/dist/chunk-FYYLNIL5.js +313 -0
  30. package/dist/{chunk-OQ33BKR3.js → chunk-G7MUEIGA.js} +3 -60
  31. package/dist/chunk-GEYXPTRF.js +613 -0
  32. package/dist/chunk-GOXNB3AO.js +261 -0
  33. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  34. package/dist/chunk-HWUFFB6L.js +83 -0
  35. package/dist/{chunk-4XUGQOHA.js → chunk-K7T3E2SR.js} +15 -8
  36. package/dist/chunk-K7VKOLQQ.js +15 -0
  37. package/dist/{chunk-UFIIWP2H.js → chunk-KHSFENX2.js} +8 -8
  38. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  39. package/dist/chunk-LCGCVYZ4.js +57 -0
  40. package/dist/chunk-LL4KHSZI.js +22 -0
  41. package/dist/{chunk-PAJK6MAQ.js → chunk-LYF7OHWH.js} +42 -15
  42. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  43. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  44. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  45. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  46. package/dist/chunk-ODFEOB4F.js +1082 -0
  47. package/dist/{chunk-AGYYIBLL.js → chunk-OH3TOQTB.js} +6 -2
  48. package/dist/chunk-OZNBF4L3.js +23 -0
  49. package/dist/{chunk-DSELYM6W.js → chunk-PBTHKCPN.js} +30 -10
  50. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  51. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  52. package/dist/{chunk-SRDMMSEP.js → chunk-QM3F2GKX.js} +1063 -1645
  53. package/dist/{chunk-X6IAEBZR.js → chunk-QNQHSOLF.js} +7 -7
  54. package/dist/{chunk-OC7FIQPC.js → chunk-R46L2BIR.js} +10 -7
  55. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  56. package/dist/{chunk-6N5PTWMY.js → chunk-RY3LY4J5.js} +50 -13
  57. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  58. package/dist/{chunk-OOJYHWRB.js → chunk-TSHXZTOQ.js} +6 -5
  59. package/dist/chunk-TZSKNMZG.js +434 -0
  60. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  61. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  62. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  63. package/dist/chunk-W6GROXXM.js +69 -0
  64. package/dist/{chunk-OAO4GE4M.js → chunk-WHGPSPT5.js} +2 -2
  65. package/dist/chunk-WHJYKASB.js +677 -0
  66. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  67. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  68. package/dist/{chunk-TZTZS7QK.js → chunk-XE2VEJHX.js} +5 -3
  69. package/dist/{chunk-LM5TQCJZ.js → chunk-XF5N4U5A.js} +8 -7
  70. package/dist/{chunk-LWLEKMDQ.js → chunk-XXQNGV4M.js} +1073 -552
  71. package/dist/{chunk-KZWTDYJF.js → chunk-XYDYPRZI.js} +7 -7
  72. package/dist/chunk-ZGVHUX3M.js +66 -0
  73. package/dist/{chunk-LW6DSM3M.js → chunk-ZRGEBJ4T.js} +1192 -1119
  74. package/dist/{chunk-2DJ2KNFG.js → chunk-ZXF4XRKW.js} +202 -40
  75. package/dist/chunk-ZZMN5OM4.js +122 -0
  76. package/dist/cli/index.js +34 -30
  77. package/dist/{clio-JOU4FXVA.js → clio-M2KGYUFZ.js} +7 -6
  78. package/dist/{code-nav-7AX6FYE6.js → code-nav-GQNL7XA6.js} +8 -6
  79. package/dist/codewiki/build-worker.js +4 -4
  80. package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
  81. package/dist/{config-XCDVKR23.js → config-XUUYQIWO.js} +47 -35
  82. package/dist/{configure-4GAP54ZW.js → configure-IHJ7YOMV.js} +18 -15
  83. package/dist/{context-77FM5DV5.js → context-74JLXAWD.js} +18 -10
  84. package/dist/{context-4UOGGLQ5.js → context-75MIWW3U.js} +41 -29
  85. package/dist/{context-5VKGUVJJ.js → context-ZQ7SIFJV.js} +85 -9
  86. package/dist/{context-clear-XXJRLCJJ.js → context-clear-GYKWNUML.js} +41 -29
  87. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  88. package/dist/context-working-set-UX5KEP4J.js +1553 -0
  89. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-GIJBHNFL.js} +47 -32
  90. package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
  91. package/dist/{doctor-HR46URBJ.js → doctor-SVJ5BZCW.js} +12 -12
  92. package/dist/{eval-XSSNATB4.js → eval-CG6LLBLD.js} +54 -238
  93. package/dist/{evidence-6HG2PY2B.js → evidence-ZYFIEN42.js} +57 -28
  94. package/dist/{evolve-K7YU3NCY.js → evolve-QGEXEMDW.js} +36 -25
  95. package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
  96. package/dist/{fleet-VY3HHKN6.js → fleet-S5R4ZOQY.js} +73 -44
  97. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-BHSNPBMH.js} +3 -3
  98. package/dist/{init-JYGXI3FK.js → init-5DRU55YR.js} +49 -37
  99. package/dist/memory-7YKKR6UC.js +467 -0
  100. package/dist/{models-I5QWSEOM.js → models-ZPOLRU2C.js} +24 -21
  101. package/dist/{monitor-GE4ID3IA.js → monitor-US5F5YGZ.js} +73 -46
  102. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-E2AL4T5N.js} +1624 -1007
  103. package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
  104. package/dist/{reset-L2FQEE3E.js → reset-KZ652EK6.js} +6 -5
  105. package/dist/{run-ZU3QMZPZ.js → run-SRNBKDWD.js} +76 -54
  106. package/dist/{share-S5BZQC5I.js → share-CGZE33UP.js} +7 -6
  107. package/dist/{skills-X5VXCRNQ.js → skills-S2X4DLY5.js} +4 -4
  108. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-W2GGIC4R.js} +40 -29
  109. package/dist/{targets-SNCPI2NR.js → targets-54SWINWB.js} +28 -23
  110. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-SAIF2OGY.js} +6 -4
  111. package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
  112. package/dist/{upgrade-JQHHPQ4K.js → upgrade-JKAR27XC.js} +20 -19
  113. package/dist/{usage-OR4O5SMZ.js → usage-MSAWCLX4.js} +79 -36
  114. package/dist/verifiers-NCBTHHN2.js +1220 -0
  115. package/dist/verify-X5HDROLA.js +25 -0
  116. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-GUSOQ6ZP.js} +50 -37
  117. package/dist/worker/entry.js +90 -70
  118. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  119. package/docs/README.md +8 -7
  120. package/docs/acp.md +1 -1
  121. package/docs/alcf-provider.md +1 -1
  122. package/docs/architecture.md +2 -2
  123. package/docs/artifact-placement.md +1 -2
  124. package/docs/artifact-versions.md +1 -1
  125. package/docs/built-in-agents.md +1 -1
  126. package/docs/capacity-and-scheduling.md +1 -1
  127. package/docs/commands-and-modes.md +60 -26
  128. package/docs/config-knobs-audit.md +1 -2
  129. package/docs/configuration-and-targets.md +26 -1
  130. package/docs/context-engine.md +67 -13
  131. package/docs/context-working-set.md +194 -0
  132. package/docs/development-pipeline.md +1 -1
  133. package/docs/documentation-coverage.md +6 -6
  134. package/docs/documentation-guide.md +7 -6
  135. package/docs/environment-variables.md +2 -1
  136. package/docs/eval-runner.md +1 -1
  137. package/docs/evals-internal.md +4 -32
  138. package/docs/evidence-and-memory.md +139 -7
  139. package/docs/evolution.md +1 -1
  140. package/docs/exit-codes-and-output.md +1 -1
  141. package/docs/extensions-and-sharing.md +2 -2
  142. package/docs/fleet-dispatch.md +49 -8
  143. package/docs/glossary.md +21 -1
  144. package/docs/installation-and-lifecycle.md +2 -2
  145. package/docs/middleware-and-components.md +19 -2
  146. package/docs/model-catalog.md +7 -9
  147. package/docs/observability.md +4 -4
  148. package/docs/proactive-memory.md +26 -16
  149. package/docs/prompt-envelope-and-tools.md +7 -5
  150. package/docs/provider-adapter-cookbook.md +1 -1
  151. package/docs/release-cut-checklist.md +43 -40
  152. package/docs/safety-model.md +49 -8
  153. package/docs/scientific-validation.md +21 -3
  154. package/docs/session-lifecycle.md +3 -3
  155. package/docs/skills-marketplace.md +1 -1
  156. package/docs/tool-usage.md +79 -12
  157. package/docs/trace-store.md +1 -1
  158. package/docs/troubleshooting.md +1 -1
  159. package/docs/tui-design.md +38 -4
  160. package/docs/worker-dispatch-mechanics.md +11 -1
  161. package/package.json +13 -13
  162. package/skills/meta/clio-test/SKILL.md +20 -17
  163. package/skills/meta/clio-test/evals.md +3 -3
  164. package/skills/meta/clio-test/references/harness.md +35 -6
  165. package/skills/meta/clio-test/references/test-map.md +20 -10
  166. package/skills/registry.yaml +2 -2
  167. package/skills/skill-marketplace.json +1 -1
  168. package/src/cli/agents.ts +2 -3
  169. package/src/cli/argv.ts +14 -1
  170. package/src/cli/context-working-set.ts +513 -0
  171. package/src/cli/context.ts +8 -0
  172. package/src/cli/evidence.ts +20 -2
  173. package/src/cli/fleet.ts +15 -0
  174. package/src/cli/index.ts +5 -1
  175. package/src/cli/memory.ts +272 -10
  176. package/src/cli/modes/json-stream.ts +2 -2
  177. package/src/cli/modes/print.ts +12 -1
  178. package/src/cli/run.ts +22 -2
  179. package/src/cli/targets.ts +12 -3
  180. package/src/cli/usage.ts +55 -7
  181. package/src/cli/verifiers.ts +325 -0
  182. package/src/core/bash-exec.ts +39 -14
  183. package/src/core/bus-events.ts +22 -4
  184. package/src/core/config.ts +54 -0
  185. package/src/core/defaults.ts +50 -3
  186. package/src/core/response-model-id.ts +134 -0
  187. package/src/core/toml.ts +62 -0
  188. package/src/core/verification-scripts.ts +6 -0
  189. package/src/core/workspace-files.ts +0 -1
  190. package/src/domains/agents/builtins/architect.md +1 -1
  191. package/src/domains/agents/builtins/verifier.md +3 -0
  192. package/src/domains/agents/catalog.ts +5 -4
  193. package/src/domains/agents/recipe.ts +54 -14
  194. package/src/domains/agents/result-contract.ts +7 -4
  195. package/src/domains/config/classify.ts +1 -0
  196. package/src/domains/context/bootstrap.ts +36 -27
  197. package/src/domains/context/project-metadata.ts +19 -63
  198. package/src/domains/context/prompt-context.ts +8 -0
  199. package/src/domains/context/working-set/contract.ts +161 -0
  200. package/src/domains/context/working-set/defaults.ts +28 -0
  201. package/src/domains/context/working-set/engine.ts +203 -0
  202. package/src/domains/context/working-set/fold.ts +62 -0
  203. package/src/domains/context/working-set/horizon.ts +38 -0
  204. package/src/domains/context/working-set/marker.ts +103 -0
  205. package/src/domains/context/working-set/path-index.ts +436 -0
  206. package/src/domains/context/working-set/payload.ts +152 -0
  207. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  208. package/src/domains/context/working-set/policies/index.ts +20 -0
  209. package/src/domains/context/working-set/policies/structural.ts +160 -0
  210. package/src/domains/context/working-set/project.ts +132 -0
  211. package/src/domains/context/working-set/protect.ts +109 -0
  212. package/src/domains/context/working-set/recall.ts +177 -0
  213. package/src/domains/context/working-set/replay/controls.ts +112 -0
  214. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  215. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  216. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  217. package/src/domains/context/working-set/replay/report.ts +139 -0
  218. package/src/domains/context/working-set/replay/runner.ts +325 -0
  219. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  220. package/src/domains/context/working-set/replay/trace.ts +21 -0
  221. package/src/domains/context/working-set/visible.ts +54 -0
  222. package/src/domains/dispatch/budget-envelope.ts +396 -0
  223. package/src/domains/dispatch/contract.ts +2 -0
  224. package/src/domains/dispatch/extension.ts +81 -27
  225. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  227. package/src/domains/dispatch/state.ts +1 -0
  228. package/src/domains/dispatch/types.ts +10 -3
  229. package/src/domains/dispatch/validation.ts +14 -0
  230. package/src/domains/dispatch/worker-spawn.ts +14 -3
  231. package/src/domains/eval/metrics/evidence.ts +0 -116
  232. package/src/domains/eval/metrics/invariants.ts +1 -1
  233. package/src/domains/eval/runners/clio-run.ts +1 -10
  234. package/src/domains/eval/runners/external-command.ts +2 -29
  235. package/src/domains/eval/schema/suite.ts +0 -7
  236. package/src/domains/eval/suites/run.ts +1 -7
  237. package/src/domains/evidence/build.ts +112 -45
  238. package/src/domains/evidence/eval.ts +24 -7
  239. package/src/domains/evidence/index.ts +53 -0
  240. package/src/domains/evidence/ordering.ts +12 -0
  241. package/src/domains/evidence/run-trust.ts +221 -0
  242. package/src/domains/evidence/store.ts +46 -6
  243. package/src/domains/evidence/trust-status.ts +854 -0
  244. package/src/domains/evidence/types.ts +26 -0
  245. package/src/domains/memory/index.ts +22 -0
  246. package/src/domains/memory/operations.ts +58 -1
  247. package/src/domains/memory/promotion.ts +281 -0
  248. package/src/domains/memory/prompt-section.ts +25 -5
  249. package/src/domains/memory/proposal.ts +51 -7
  250. package/src/domains/memory/task-bank.ts +3 -2
  251. package/src/domains/memory/task-memory-handoff.ts +181 -24
  252. package/src/domains/memory/task-memory-policy.ts +3 -1
  253. package/src/domains/memory/types.ts +37 -0
  254. package/src/domains/memory/validate.ts +178 -0
  255. package/src/domains/middleware/memory-intervention.ts +38 -25
  256. package/src/domains/middleware/runtime.ts +6 -0
  257. package/src/domains/middleware/skills-reminder.ts +19 -4
  258. package/src/domains/middleware/stalled-turn.ts +208 -5
  259. package/src/domains/middleware/types.ts +10 -0
  260. package/src/domains/observability/contract.ts +6 -1
  261. package/src/domains/observability/cost.ts +20 -4
  262. package/src/domains/observability/extension.ts +2 -2
  263. package/src/domains/providers/index.ts +3 -0
  264. package/src/domains/providers/model-discovery.ts +9 -0
  265. package/src/domains/providers/runtime-resolution.ts +38 -1
  266. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  267. package/src/domains/providers/types/context-window-slots.ts +18 -0
  268. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  269. package/src/domains/safety/autonomy.ts +1 -1
  270. package/src/domains/safety/call-target.ts +211 -14
  271. package/src/domains/safety/decision-presentation.ts +268 -0
  272. package/src/domains/safety/default-path-policy.ts +8 -0
  273. package/src/domains/safety/finish-contract.ts +4 -3
  274. package/src/domains/safety/policy-engine.ts +48 -6
  275. package/src/domains/safety/redaction.ts +73 -0
  276. package/src/domains/session/compaction/compact.ts +23 -1
  277. package/src/domains/session/compaction/cut-point.ts +2 -0
  278. package/src/domains/session/compaction/tokens.ts +16 -1
  279. package/src/domains/session/context-ledger.ts +12 -1
  280. package/src/domains/session/decision-board.ts +4 -0
  281. package/src/domains/session/entries.ts +110 -1
  282. package/src/domains/session/history.ts +68 -19
  283. package/src/domains/session/manager.ts +9 -2
  284. package/src/domains/session/migrations/index.ts +22 -3
  285. package/src/domains/session/usage.ts +24 -7
  286. package/src/engine/acp/event-mapper.ts +7 -0
  287. package/src/engine/acp/server.ts +32 -2
  288. package/src/engine/agent.ts +18 -1
  289. package/src/engine/apis/lmstudio.ts +25 -4
  290. package/src/engine/apis/openai-completions.ts +147 -22
  291. package/src/engine/claude/sdk-runtime.ts +8 -2
  292. package/src/engine/claude/tool-safety.ts +13 -0
  293. package/src/engine/loop-guard.ts +27 -3
  294. package/src/engine/session.ts +9 -3
  295. package/src/engine/worker-events.ts +4 -3
  296. package/src/engine/worker-runtime.ts +59 -54
  297. package/src/entry/orchestrator.ts +34 -5
  298. package/src/interactive/chat-loop-messages.ts +40 -6
  299. package/src/interactive/chat-loop.ts +13 -0
  300. package/src/interactive/chat-panel.ts +17 -1
  301. package/src/interactive/chat-renderer.ts +49 -24
  302. package/src/interactive/clio-editor.ts +44 -7
  303. package/src/interactive/context-meter.ts +10 -0
  304. package/src/interactive/context-overlay.ts +120 -7
  305. package/src/interactive/context-recall-command.ts +110 -0
  306. package/src/interactive/cost-overlay.ts +39 -8
  307. package/src/interactive/dispatch-board.ts +212 -35
  308. package/src/interactive/footer/widgets.ts +13 -0
  309. package/src/interactive/interactive-application.ts +6 -1
  310. package/src/interactive/interactive-input-runtime.ts +11 -1
  311. package/src/interactive/interactive-presentation.ts +11 -1
  312. package/src/interactive/interactive-slash-runtime.ts +37 -1
  313. package/src/interactive/memory-overlay.ts +89 -4
  314. package/src/interactive/model-session-replay.ts +21 -0
  315. package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
  316. package/src/interactive/overlay-frame.ts +5 -2
  317. package/src/interactive/overlay-general-openers.ts +46 -1
  318. package/src/interactive/overlay-key-routing.ts +41 -1
  319. package/src/interactive/overlay-lifecycle.ts +11 -4
  320. package/src/interactive/overlay-permission-lifecycle.ts +23 -8
  321. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  322. package/src/interactive/overlay-transitions.ts +11 -0
  323. package/src/interactive/overlays/ask-user.ts +74 -30
  324. package/src/interactive/overlays/decisions.ts +3 -1
  325. package/src/interactive/permission-hint.ts +35 -0
  326. package/src/interactive/permission-overlay.ts +95 -45
  327. package/src/interactive/renderers/tool-execution.ts +37 -51
  328. package/src/interactive/session-last-turn.ts +8 -1
  329. package/src/interactive/session-transcript.ts +2 -2
  330. package/src/interactive/session-usage-reseed.ts +36 -10
  331. package/src/interactive/slash-commands.ts +31 -4
  332. package/src/interactive/status/summary.ts +5 -0
  333. package/src/interactive/status/types.ts +5 -0
  334. package/src/interactive/terminal-lease.ts +1 -0
  335. package/src/interactive/turn-context.ts +333 -110
  336. package/src/interactive/turn-middleware.ts +7 -6
  337. package/src/interactive/turn-runtime.ts +37 -8
  338. package/src/interactive/turn-state.ts +3 -0
  339. package/src/interactive/worker-progress.ts +440 -0
  340. package/src/interactive/worker-stream.ts +51 -110
  341. package/src/tools/agent-tools.ts +39 -7
  342. package/src/tools/ask-user.ts +21 -1
  343. package/src/tools/bash.ts +144 -82
  344. package/src/tools/builtin-tool-catalog.ts +11 -5
  345. package/src/tools/context/index.ts +107 -5
  346. package/src/tools/context/surface.ts +3 -2
  347. package/src/tools/core-bootstrap.ts +21 -0
  348. package/src/tools/dispatch-arguments.ts +8 -0
  349. package/src/tools/dispatch-event-text.ts +19 -0
  350. package/src/tools/dispatch-runner.ts +9 -7
  351. package/src/tools/dispatch.ts +24 -1
  352. package/src/tools/monitor.ts +43 -20
  353. package/src/tools/registry.ts +72 -10
  354. package/src/tools/result-disposition.ts +706 -0
  355. package/src/tools/result-shaping.ts +321 -20
  356. package/src/tools/safe-exec.ts +2 -0
  357. package/src/tools/verify/authoring.ts +1120 -0
  358. package/src/tools/verify/catalog.ts +346 -0
  359. package/src/tools/verify/index.ts +13 -3
  360. package/src/tools/verify/scripts.ts +135 -37
  361. package/src/tools/verify/surface.ts +9 -5
  362. package/src/tools/worker-evidence.ts +54 -12
  363. package/src/worker/spec-contract.ts +43 -3
  364. package/dist/chunk-J7CWMCQD.js +0 -255
  365. package/dist/chunk-T6YILFSB.js +0 -80
  366. package/dist/chunk-VAKQQHWR.js +0 -434
  367. package/dist/chunk-VPAYEGVX.js +0 -184
  368. package/dist/chunk-XBXAASKX.js +0 -18
  369. package/dist/memory-WFZMGYHX.js +0 -236
  370. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -0,0 +1,268 @@
1
+ import type { ActionClass } from "./action-classifier.js";
2
+ import type { AutonomyExposure } from "./autonomy.js";
3
+
4
+ /** Closed consequence categories used only to explain an already-made decision request. */
5
+ export const DECISION_TIERS = ["conversation", "workspace", "outward", "safety-net", "system", "worker"] as const;
6
+
7
+ export type DecisionTier = (typeof DECISION_TIERS)[number];
8
+ export type DecisionSemanticToken = "accent" | "action" | "warning";
9
+ export type DecisionRequestKind = "question" | "permission";
10
+ export type DecisionAffectedScope = "conversation" | "workspace" | "outward" | "system";
11
+ export type DecisionReversibility = "reversible" | "limited" | "unknown";
12
+ export type DecisionAuthorityEffect = "records-answer" | "grants-once";
13
+ export type DecisionActionId = "record-answer" | "cancel" | "approve-once" | "deny" | "stop";
14
+
15
+ export type DecisionAxis =
16
+ | { kind: "answer" }
17
+ | { kind: "autonomy"; level: string }
18
+ | { kind: "safety-net"; ruleId: string };
19
+
20
+ export type DecisionOrigin = { kind: "main" } | { kind: "worker"; agentId: string; runId: string };
21
+
22
+ /**
23
+ * Facts the host has already authenticated or derived from enforced policy.
24
+ * No model-authored title, question, reason, summary, or option label belongs
25
+ * here, so prose has no path to the tier selection.
26
+ */
27
+ export interface TrustedDecisionFacts {
28
+ requestKind: DecisionRequestKind;
29
+ axis: DecisionAxis;
30
+ exposure: AutonomyExposure;
31
+ affectedScope: DecisionAffectedScope;
32
+ reversibility: DecisionReversibility;
33
+ origin: DecisionOrigin;
34
+ authorityEffect: DecisionAuthorityEffect;
35
+ tool?: string;
36
+ actionClass?: ActionClass;
37
+ }
38
+
39
+ export interface DecisionPresentationAction {
40
+ id: DecisionActionId;
41
+ label: string;
42
+ consequence: string;
43
+ }
44
+
45
+ /**
46
+ * This projection contains display data only. Registry admission and interview
47
+ * settlement never consume it, and it contains no allow, deny, or autonomy
48
+ * disposition that an execution layer could mistake for authority.
49
+ */
50
+ export interface DecisionPresentation {
51
+ tier: DecisionTier;
52
+ tierLabel: string;
53
+ title: string;
54
+ semanticToken: DecisionSemanticToken;
55
+ authorizationCopy: string;
56
+ consequenceCopy: string;
57
+ reversibilityCopy: string;
58
+ requestedByCopy: string;
59
+ requiredActions: ReadonlyArray<DecisionPresentationAction>;
60
+ }
61
+
62
+ const ACTION_CLASSES = new Set<ActionClass>([
63
+ "read",
64
+ "write",
65
+ "execute",
66
+ "dispatch",
67
+ "system_modify",
68
+ "git_destructive",
69
+ "unknown",
70
+ ]);
71
+
72
+ /** Unknown or malformed action classes classify conservatively as unknown. */
73
+ export function decisionActionClass(value: unknown): ActionClass {
74
+ return typeof value === "string" && ACTION_CLASSES.has(value as ActionClass) ? (value as ActionClass) : "unknown";
75
+ }
76
+
77
+ function actionConsequence(actionClass: ActionClass): {
78
+ affectedScope: DecisionAffectedScope;
79
+ reversibility: DecisionReversibility;
80
+ } {
81
+ switch (actionClass) {
82
+ case "read":
83
+ return { affectedScope: "workspace", reversibility: "reversible" };
84
+ case "write":
85
+ return { affectedScope: "workspace", reversibility: "reversible" };
86
+ case "execute":
87
+ case "dispatch":
88
+ return { affectedScope: "workspace", reversibility: "limited" };
89
+ case "system_modify":
90
+ case "git_destructive":
91
+ case "unknown":
92
+ return { affectedScope: "system", reversibility: "unknown" };
93
+ }
94
+ }
95
+
96
+ export function decisionFactsForPermission(input: {
97
+ tool: string;
98
+ actionClass: ActionClass;
99
+ axis: Exclude<DecisionAxis, { kind: "answer" }>;
100
+ origin: DecisionOrigin;
101
+ exposure?: AutonomyExposure;
102
+ }): TrustedDecisionFacts {
103
+ const exposure = input.exposure ?? "local";
104
+ const consequence = actionConsequence(input.actionClass);
105
+ return {
106
+ requestKind: "permission",
107
+ axis: input.axis,
108
+ exposure,
109
+ affectedScope: exposure === "outward" ? "outward" : consequence.affectedScope,
110
+ reversibility: exposure === "outward" ? "limited" : consequence.reversibility,
111
+ origin: input.origin,
112
+ authorityEffect: "grants-once",
113
+ tool: input.tool,
114
+ actionClass: input.actionClass,
115
+ };
116
+ }
117
+
118
+ export function decisionFactsForAnswer(exposure: AutonomyExposure): TrustedDecisionFacts {
119
+ return {
120
+ requestKind: "question",
121
+ axis: { kind: "answer" },
122
+ exposure,
123
+ affectedScope: exposure === "outward" ? "outward" : "conversation",
124
+ reversibility: exposure === "outward" ? "limited" : "reversible",
125
+ origin: { kind: "main" },
126
+ authorityEffect: "records-answer",
127
+ };
128
+ }
129
+
130
+ function classifyTier(facts: TrustedDecisionFacts): DecisionTier {
131
+ if (facts.origin.kind === "worker") return "worker";
132
+ if (facts.affectedScope === "system") return "system";
133
+ if (facts.exposure === "outward" || facts.affectedScope === "outward") return "outward";
134
+ if (facts.axis.kind === "safety-net") return "safety-net";
135
+ if (facts.authorityEffect === "grants-once") return "workspace";
136
+ return "conversation";
137
+ }
138
+
139
+ function tierIdentity(tier: DecisionTier): {
140
+ tierLabel: string;
141
+ title: string;
142
+ semanticToken: DecisionSemanticToken;
143
+ } {
144
+ switch (tier) {
145
+ case "conversation":
146
+ return { tierLabel: "Conversational answer", title: "Answer a question", semanticToken: "accent" };
147
+ case "workspace":
148
+ return { tierLabel: "Workspace authority", title: "Approve workspace action", semanticToken: "action" };
149
+ case "outward":
150
+ return { tierLabel: "Outward consequence", title: "Confirm outward consequence", semanticToken: "warning" };
151
+ case "safety-net":
152
+ return { tierLabel: "Safety-net confirmation", title: "Safety-net confirmation", semanticToken: "warning" };
153
+ case "system":
154
+ return { tierLabel: "System change", title: "Approve system change", semanticToken: "warning" };
155
+ case "worker":
156
+ return { tierLabel: "Worker escalation", title: "Worker needs approval", semanticToken: "action" };
157
+ }
158
+ }
159
+
160
+ function requestedByCopy(facts: TrustedDecisionFacts): string {
161
+ const requester =
162
+ facts.origin.kind === "worker" ? `worker ${facts.origin.agentId} (run ${facts.origin.runId})` : "main agent";
163
+ if (facts.axis.kind === "answer") return requester;
164
+ if (facts.axis.kind === "safety-net") return `${requester} through safety-net rail ${facts.axis.ruleId}`;
165
+ return `${requester} through autonomy level (${facts.axis.level})`;
166
+ }
167
+
168
+ function authorizationCopy(facts: TrustedDecisionFacts): string {
169
+ if (facts.authorityEffect === "records-answer") {
170
+ return facts.exposure === "outward"
171
+ ? "Your response records an answer about an outward step. It does not publish or send anything by itself."
172
+ : "Your response records an answer for this interview. It grants no tool authority.";
173
+ }
174
+ const tool = facts.tool ?? "the presented tool";
175
+ const actionClass = facts.actionClass ?? "unknown";
176
+ if (tool === "ask_user" && facts.exposure === "outward") {
177
+ return "Approval opens this one outward-decision interview. It does not publish or send anything by itself.";
178
+ }
179
+ return `Approval authorizes one ${actionClass} call to ${tool}. It does not change the autonomy level.`;
180
+ }
181
+
182
+ function consequenceCopy(facts: TrustedDecisionFacts, tier: DecisionTier): string {
183
+ switch (tier) {
184
+ case "conversation":
185
+ return "The answer stays in this session's decision record.";
186
+ case "workspace":
187
+ return facts.actionClass === "read"
188
+ ? "The call reads within the workspace without mutating it."
189
+ : "The call can affect files or processes inside the workspace.";
190
+ case "outward":
191
+ return "The resulting step can reach people or systems outside the workspace.";
192
+ case "safety-net":
193
+ return "An always-on safety-net rail requires a one-shot operator decision before this call can run.";
194
+ case "system":
195
+ return "The call can change state outside the workspace or state the classifier cannot safely bound.";
196
+ case "worker":
197
+ return "A dispatched worker is parked. Your answer returns only to that run's exact request.";
198
+ }
199
+ }
200
+
201
+ function reversibilityCopy(facts: TrustedDecisionFacts): string {
202
+ if (facts.requestKind === "question") {
203
+ return facts.reversibility === "reversible"
204
+ ? "Reversible: yes. The recorded answer can be superseded later."
205
+ : "Reversible: not guaranteed. An outward step that later uses this answer may have lasting effects.";
206
+ }
207
+ if (facts.actionClass === "read" && facts.reversibility === "reversible") {
208
+ return "Reversible: yes. This call does not mutate the workspace.";
209
+ }
210
+ switch (facts.reversibility) {
211
+ case "reversible":
212
+ return "Reversible: yes. Workspace changes can be reviewed and reverted.";
213
+ case "limited":
214
+ return "Reversible: not guaranteed. Effects that already ran or reached an outward system may remain.";
215
+ case "unknown":
216
+ return "Reversible: unknown. Treat the effect as not safely reversible.";
217
+ }
218
+ }
219
+
220
+ function requiredActions(facts: TrustedDecisionFacts, tier: DecisionTier): ReadonlyArray<DecisionPresentationAction> {
221
+ if (facts.authorityEffect === "records-answer") {
222
+ return [
223
+ {
224
+ id: "record-answer",
225
+ label: facts.exposure === "outward" ? "Record outward answer" : "Record answer",
226
+ consequence: "Records the selected or typed answer without granting tool authority.",
227
+ },
228
+ { id: "cancel", label: "Cancel", consequence: "Closes the interview without recording an answer for this round." },
229
+ ];
230
+ }
231
+ const approveLabel =
232
+ tier === "system"
233
+ ? "Approve system change once"
234
+ : tier === "safety-net"
235
+ ? "Approve guarded action once"
236
+ : tier === "worker"
237
+ ? "Approve worker request once"
238
+ : tier === "outward"
239
+ ? "Approve outward decision once"
240
+ : "Approve workspace action once";
241
+ const stopConsequence =
242
+ facts.origin.kind === "worker"
243
+ ? "Denies this worker request and ends the active main-agent turn. Other worker requests retain their timeout policy."
244
+ : "Denies every parked request from this turn and ends the run.";
245
+ return [
246
+ {
247
+ id: "approve-once",
248
+ label: approveLabel,
249
+ consequence: "Runs only the presented request and does not change the autonomy level.",
250
+ },
251
+ { id: "deny", label: "Deny this request", consequence: "Denies only the presented request and advances the queue." },
252
+ { id: "stop", label: "Deny and stop", consequence: stopConsequence },
253
+ ];
254
+ }
255
+
256
+ /** Pure presentation classifier. It performs no I/O and has no execution authority. */
257
+ export function classifyDecisionPresentation(facts: TrustedDecisionFacts): DecisionPresentation {
258
+ const tier = classifyTier(facts);
259
+ return {
260
+ tier,
261
+ ...tierIdentity(tier),
262
+ authorizationCopy: authorizationCopy(facts),
263
+ consequenceCopy: consequenceCopy(facts, tier),
264
+ reversibilityCopy: reversibilityCopy(facts),
265
+ requestedByCopy: requestedByCopy(facts),
266
+ requiredActions: requiredActions(facts, tier),
267
+ };
268
+ }
@@ -61,6 +61,14 @@ export const DEFAULT_DAMAGE_CONTROL_PATH_POLICY: PathPolicyInput = {
61
61
  "*.dump",
62
62
  ],
63
63
  readOnlyPaths: [
64
+ // Clio's own operator-authority files in the project. `safety.yaml` is the
65
+ // policy that gates the model's tools, and `verifiers.yaml` is an argv
66
+ // catalog that verify() executes; a model that can author either one can
67
+ // widen its own permissions in two tool calls. Both are authored by the
68
+ // operator through the CLI (`clio-coder verifiers author` lands nothing
69
+ // before `--yes`), so model-side writes and deletes are refused.
70
+ ".clio-coder/safety.yaml",
71
+ ".clio-coder/verifiers.yaml",
64
72
  "/etc/",
65
73
  "/usr/",
66
74
  "/bin/",
@@ -1,5 +1,5 @@
1
1
  import { ToolNames } from "../../core/tool-names.js";
2
- import { isVerificationScriptName } from "../../core/verification-scripts.js";
2
+ import { isProjectVerifierCheckId, isVerificationScriptName } from "../../core/verification-scripts.js";
3
3
  import {
4
4
  detectValidationCommand,
5
5
  extractCommandDeleteTargets,
@@ -399,8 +399,9 @@ export function typedValidationSummary(toolName: string, payload: Record<string,
399
399
  const path = typeof args?.path === "string" && args.path.trim().length > 0 ? args.path.trim() : "artifact";
400
400
  return `verify frontend ${path}`;
401
401
  }
402
- if (!isVerificationScriptName(check)) return null;
403
- return `npm run ${check}`;
402
+ if (isVerificationScriptName(check)) return `npm run ${check}`;
403
+ if (isProjectVerifierCheckId(check)) return `verify ${check}`;
404
+ return null;
404
405
  }
405
406
  return null;
406
407
  }
@@ -4,6 +4,7 @@ import { artifactDefaultPath } from "../../core/artifact-paths.js";
4
4
  import { canonicalizeExistingPath } from "../../core/path-canonical.js";
5
5
  import { ToolNames } from "../../core/tool-names.js";
6
6
  import { clioConfigDir } from "../../core/xdg.js";
7
+ import { type DeclaredCheck, loadProjectVerifierCatalog } from "../../tools/verify/catalog.js";
7
8
  import { type ActionClass, type Classification, type ClassifierCall, classify } from "./action-classifier.js";
8
9
  import type { DamageControlMatch, DamageControlRule } from "./damage-control.js";
9
10
  import { DEFAULT_DAMAGE_CONTROL_PATH_POLICY, mergePathPolicyInputs } from "./default-path-policy.js";
@@ -225,7 +226,9 @@ export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}
225
226
  const rawClassification = classify(call);
226
227
  const command = commandArg(call.args);
227
228
  const callCwd = cwdArg(call.args, cwd);
228
- const scan = damageControlScan(call);
229
+ const catalogCheck = resolveVerifyCatalogCheck(call, cwd);
230
+ const catalogCommand = catalogCheck === null ? null : catalogCheck.command.join(" ");
231
+ const scan = catalogCommand ?? damageControlScan(call);
229
232
  const hit = scan ? matchSourcedRule(scan, sourcedRules) : null;
230
233
  const classification = effectiveClassification(rawClassification, hit?.match);
231
234
 
@@ -335,8 +338,9 @@ export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}
335
338
  // command; the one carve-out is the exit-code-only presence check
336
339
  // (`grep -q`/`grep -sq` with a ^NAME= pattern), which is the safe
337
340
  // protocol the credentials skill teaches.
338
- if (call.tool === ToolNames.Bash && command !== null) {
339
- const secretRead = evaluateBashZeroAccessRead(zeroAccessPolicy, command, callCwd);
341
+ const scannedCommand = call.tool === ToolNames.Bash ? command : catalogCommand;
342
+ if (scannedCommand !== null) {
343
+ const secretRead = evaluateBashZeroAccessRead(zeroAccessPolicy, scannedCommand, callCwd);
340
344
  if (secretRead !== null) {
341
345
  const blockInput: Omit<
342
346
  SafetyPolicyDecision,
@@ -407,9 +411,22 @@ export function createSafetyPolicyEngine(options: SafetyPolicyEngineOptions = {}
407
411
  };
408
412
  if (hit?.match.ruleId !== undefined) allowInput.ruleId = hit.match.ruleId;
409
413
  if (hit?.match !== undefined) allowInput.match = hit.match;
410
- // The typed execution tool (verify) is bounded by its own check
411
- // allowlist, so it sits in the no-prompt set.
412
- if (classification.actionClass === "execute") allowInput.execRecognition = "recognized";
414
+ // The typed execution tool (verify) sits in the no-prompt set when the
415
+ // check is a package script or the frontend validator: both are bounded
416
+ // by the verification-script family and run a fixed argv shape. A
417
+ // project-catalog check runs whatever argv the workspace file declares,
418
+ // so it is unrecognized: auto-edit parks it for one confirmation that
419
+ // shows the argv, full-auto runs it, and the damage-control match and
420
+ // zero-access scan above have already seen the declared vector.
421
+ if (classification.actionClass === "execute") {
422
+ allowInput.execRecognition = catalogCheck === null ? "recognized" : "unrecognized";
423
+ if (catalogCheck !== null) {
424
+ allowInput.reasons = [
425
+ ...allowInput.reasons,
426
+ `project verifier '${catalogCheck.id}' runs declared argv: ${catalogCommand ?? ""}`,
427
+ ];
428
+ }
429
+ }
413
430
  return allowDecision(base, allowInput);
414
431
  },
415
432
  metadata() {
@@ -958,6 +975,31 @@ function damageControlScan(call: ClassifierCall): string {
958
975
  return typeof pathArg === "string" ? pathArg : "";
959
976
  }
960
977
 
978
+ /**
979
+ * The project-catalog check a verify call resolves to, or null for a listing,
980
+ * the frontend validator, a package script, or an id the catalog does not
981
+ * declare. The catalog is an argv vector the operator authored, but the file
982
+ * sits in the workspace, so the engine reads it fresh on every verify call and
983
+ * treats the declared argv exactly like a bash command string: it is what the
984
+ * damage-control rules and the zero-access read guard scan, and it is not in
985
+ * the no-prompt set. An unreadable or invalid catalog resolves to null here and
986
+ * fails closed in the tool itself.
987
+ */
988
+ function resolveVerifyCatalogCheck(call: ClassifierCall, workspaceRoot: string): DeclaredCheck | null {
989
+ if (call.tool !== ToolNames.Verify) return null;
990
+ const check = call.args?.check;
991
+ if (typeof check !== "string") return null;
992
+ const id = check.trim();
993
+ if (id.length === 0 || id === "frontend") return null;
994
+ try {
995
+ const catalog = loadProjectVerifierCatalog(workspaceRoot);
996
+ if (!catalog.ok || catalog.source === null) return null;
997
+ return catalog.source.checks.find((candidate) => candidate.id === id) ?? null;
998
+ } catch {
999
+ return null;
1000
+ }
1001
+ }
1002
+
961
1003
  function serializeArgs(args?: Record<string, unknown>): string {
962
1004
  if (!args) return "";
963
1005
  const parts: string[] = [];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Display-time secret redaction for tool arguments.
3
+ *
4
+ * Two surfaces need the same rules and must not drift. The transcript's tool
5
+ * renderer projects a call's arguments for the operator's own screen, and the
6
+ * worker tool-admission seam composes a bounded action descriptor that crosses
7
+ * the NDJSON stdout boundary. A credential that one of them scrubs and the
8
+ * other prints is a leak, so the patterns live here and both import them.
9
+ *
10
+ * This is presentation only. Execution, receipts, and the safety verdict all
11
+ * keep the original argument values; nothing here changes what a tool runs.
12
+ */
13
+
14
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
15
+ return value !== null && typeof value === "object" && !Array.isArray(value);
16
+ }
17
+
18
+ const SECRET_KEY_RE =
19
+ /(?:api[-_]?key|access[-_]?key|auth(?:orization)?|credential|password|private[-_]?key|secret|token)/iu;
20
+ const ENV_KEY_RE = /^env(?:ironment)?$/iu;
21
+ const SECRET_STRING_RE = /(https?:\/\/[^\s/@]+:)[^\s/@]+@/giu;
22
+ const SECRET_URL_PARAM_RE =
23
+ /([?&](?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[^=\s]*=)[^&#\s]+/giu;
24
+ const SECRET_ASSIGNMENT_RE =
25
+ /(?<![A-Z0-9_])((?:[A-Z0-9_]*(?:ACCESS|API|AUTH|CREDENTIAL|KEY|PASS(?:WORD)?|PRIVATE|SECRET|TOKEN)[A-Z0-9_]*)=)[^\s;&|]+/giu;
26
+ const SECRET_FLAG_RE =
27
+ /(--?(?:[a-z0-9-]*(?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[a-z0-9-]*))(=|\s+)([^\s;&|]+)/giu;
28
+
29
+ /** Scrub credentials embedded in one string: URL userinfo, query params, `KEY=value`, and `--flag value`. */
30
+ export function redactSecretString(value: string): string {
31
+ return value
32
+ .replace(SECRET_STRING_RE, "$1[redacted]@")
33
+ .replace(SECRET_URL_PARAM_RE, "$1[redacted]")
34
+ .replace(SECRET_ASSIGNMENT_RE, "$1[redacted]")
35
+ .replace(SECRET_FLAG_RE, "$1$2[redacted]");
36
+ }
37
+
38
+ /** Whether an argument key names a secret outright, so its value never renders. */
39
+ export function isSecretArgKey(key: string): boolean {
40
+ return SECRET_KEY_RE.test(key);
41
+ }
42
+
43
+ /** Whether an argument key names an environment block, whose every string value is a candidate secret. */
44
+ function isEnvironmentArgKey(key: string): boolean {
45
+ return ENV_KEY_RE.test(key);
46
+ }
47
+
48
+ function redactEnvironmentValue(value: unknown, depth: number): unknown {
49
+ if (depth > 8) return "[redacted nested values]";
50
+ if (typeof value === "string") return "[redacted]";
51
+ if (Array.isArray(value)) return value.map((item) => redactEnvironmentValue(item, depth + 1));
52
+ if (!isPlainObject(value)) return value;
53
+ const out: Record<string, unknown> = {};
54
+ for (const [childKey, childValue] of Object.entries(value)) {
55
+ out[childKey] = redactEnvironmentValue(childValue, depth + 1);
56
+ }
57
+ return out;
58
+ }
59
+
60
+ /** Project tool arguments for display only; execution and receipts retain the original values. */
61
+ export function redactToolArgs(value: unknown, key = "", depth = 0): unknown {
62
+ if (isSecretArgKey(key)) return "[redacted]";
63
+ if (isEnvironmentArgKey(key)) return redactEnvironmentValue(value, depth);
64
+ if (typeof value === "string") return redactSecretString(value);
65
+ if (depth > 8) return "[redacted nested values]";
66
+ if (Array.isArray(value)) return value.map((item) => redactToolArgs(item, key, depth + 1));
67
+ if (!isPlainObject(value)) return value;
68
+ const out: Record<string, unknown> = {};
69
+ for (const [childKey, childValue] of Object.entries(value)) {
70
+ out[childKey] = redactToolArgs(childValue, childKey, depth + 1);
71
+ }
72
+ return out;
73
+ }
@@ -12,6 +12,8 @@
12
12
 
13
13
  import { stream } from "../../../engine/ai.js";
14
14
  import type { EngineModel, Usage } from "../../../engine/types.js";
15
+ import { foldWorkingSet } from "../../context/working-set/fold.js";
16
+ import { recallableRefListing } from "../../context/working-set/recall.js";
15
17
  import type { CompactionUsage, SessionEntry } from "../entries.js";
16
18
  import { serializeConversation } from "./branch-summary.js";
17
19
  import { findCutPoint } from "./cut-point.js";
@@ -307,6 +309,23 @@ function formatFileOperations(fileOps: FileOperations): string {
307
309
  return sections.length > 0 ? `\n\n${sections.join("\n\n")}` : "";
308
310
  }
309
311
 
312
+ function formatRecallableRefs(entries: ReadonlyArray<SessionEntry>, firstKeptEntryIndex: number): string {
313
+ const entryIndexes = new Map<string, number>();
314
+ for (let index = 0; index < entries.length; index += 1) {
315
+ const entry = entries[index];
316
+ if (entry) entryIndexes.set(entry.turnId, index);
317
+ }
318
+ const view = foldWorkingSet(entries);
319
+ const evictedBeforeCut = new Map(
320
+ [...view.evicted].filter(([ref]) => (entryIndexes.get(ref) ?? Number.POSITIVE_INFINITY) < firstKeptEntryIndex),
321
+ );
322
+ const listing = recallableRefListing(entries, { ...view, evicted: evictedBeforeCut });
323
+ if (listing.refs.length === 0) return "";
324
+ const rows = [...listing.refs];
325
+ if (listing.remaining > 0) rows.push(`and ${listing.remaining} more`);
326
+ return `\n\n<recallable-refs>\n${rows.join("\n")}\n</recallable-refs>`;
327
+ }
328
+
310
329
  function numberOrZero(value: unknown): number {
311
330
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
312
331
  }
@@ -457,7 +476,10 @@ export async function compact(input: CompactInput): Promise<CompactResult> {
457
476
  }
458
477
  }
459
478
 
460
- const summary = `${summaryParts.join("\n\n---\n\n").trim()}${formatFileOperations(fileOps)}`.trim();
479
+ const summary = `${summaryParts.join("\n\n---\n\n").trim()}${formatFileOperations(fileOps)}${formatRecallableRefs(
480
+ input.entries,
481
+ cut.firstKeptEntryIndex,
482
+ )}`.trim();
461
483
 
462
484
  return {
463
485
  summary,
@@ -72,6 +72,8 @@ function isValidCutPoint(entry: SessionEntry): boolean {
72
72
  case "taskLedger":
73
73
  case "decisionLedger":
74
74
  case "workerRun":
75
+ case "contextEviction":
76
+ case "contextRecall":
75
77
  return false;
76
78
  }
77
79
  }
@@ -34,7 +34,18 @@ export interface TokenEstimator {
34
34
  }
35
35
 
36
36
  function estimateMessage(entry: MessageEntry): number {
37
- return estimateAgentMessageTokens(entry);
37
+ // Usage invalidation is bookkeeping for token-anchor selection. The replay
38
+ // builder carries it as AgentMessage metadata, but providers never see it in
39
+ // message content, so it cannot change a projected entry's prompt cost. Drop
40
+ // it here so plan-time pricing and the post-event projection use one byte
41
+ // definition even though only the latter has applied the stamp.
42
+ if (entry.role !== "assistant" || !entry.payload || typeof entry.payload !== "object") {
43
+ return estimateAgentMessageTokens(entry);
44
+ }
45
+ const payload = entry.payload as Record<string, unknown>;
46
+ if (!("contextUsageInvalidated" in payload)) return estimateAgentMessageTokens(entry);
47
+ const { contextUsageInvalidated: _stamp, ...promptPayload } = payload;
48
+ return estimateAgentMessageTokens({ ...entry, payload: promptPayload });
38
49
  }
39
50
 
40
51
  function estimateBashExecution(entry: BashExecutionEntry): number {
@@ -78,6 +89,10 @@ export function estimateTokens(entry: SessionEntry): number {
78
89
  case "taskLedger":
79
90
  case "decisionLedger":
80
91
  case "workerRun":
92
+ // Working-set bookkeeping: refs and markers, never bodies. The
93
+ // projection accounts for marker cost on the projected messages.
94
+ case "contextEviction":
95
+ case "contextRecall":
81
96
  return 0;
82
97
  }
83
98
  }
@@ -16,7 +16,7 @@
16
16
  * proportional reconciliation the footer bar already performs.
17
17
  */
18
18
 
19
- import type { ContextWindowSource } from "../providers/index.js";
19
+ import type { ContextWindowSlots, ContextWindowSource } from "../providers/index.js";
20
20
  import { DEFAULT_COMPACTION_THRESHOLD } from "./compaction/auto.js";
21
21
 
22
22
  /** Distinct buckets a context window is divided into for display. */
@@ -46,6 +46,8 @@ export interface BuildContextLedgerInput {
46
46
  contextWindow: number | null;
47
47
  /** Which layer answered `contextWindow`; omitted before a runtime resolves. */
48
48
  contextWindowSource?: ContextWindowSource | null;
49
+ /** The server-side split the probed window is a share of, when there is one. */
50
+ contextWindowSlots?: ContextWindowSlots | null;
49
51
  /**
50
52
  * Per-segment token estimates from the prompt compiler's segment manifest.
51
53
  * When empty, `systemPromptTokens` is used as a single opaque system bucket.
@@ -122,6 +124,8 @@ export interface PromptCacheStats {
122
124
  * combination the overlay renders as a warning.
123
125
  */
124
126
  backendVerdict: "hot" | "partial" | "cold" | "small" | null;
127
+ /** Cache disturbances Clio expected before the last settled run. */
128
+ expectedColdReasons?: ReadonlyArray<string>;
125
129
  }
126
130
 
127
131
  export interface ContextLedgerGroup {
@@ -144,6 +148,12 @@ export interface ContextLedger {
144
148
  * the difference decides whether autocompact fires in time.
145
149
  */
146
150
  contextWindowSource: ContextWindowSource | null;
151
+ /**
152
+ * Present when `contextWindow` is one slot's share of the server's KV
153
+ * budget, so the overlay can show `196,608 (786,432 / 4 slots)` instead of
154
+ * a number that looks like the whole server.
155
+ */
156
+ contextWindowSlots: ContextWindowSlots | null;
147
157
  /** Sum of every content category (system..pending). */
148
158
  usedTokens: number;
149
159
  /** Autocompact headroom held in reserve above the conversation. */
@@ -344,6 +354,7 @@ export function buildContextLedger(input: BuildContextLedgerInput): ContextLedge
344
354
  model: input.model ?? null,
345
355
  contextWindow: window,
346
356
  contextWindowSource: input.contextWindowSource ?? null,
357
+ contextWindowSlots: input.contextWindowSlots ?? null,
347
358
  usedTokens,
348
359
  reserveTokens,
349
360
  freeTokens,
@@ -1,4 +1,5 @@
1
1
  import type { AskUserToolPolicy } from "../../tools/registry.js";
2
+ import type { AutonomyExposure } from "../safety/autonomy.js";
2
3
  import type { DecisionLedgerEntry, DecisionRecord } from "./entries.js";
3
4
 
4
5
  export interface DecisionLedgerEntryFields {
@@ -11,6 +12,7 @@ export interface DecisionLedgerEntryFields {
11
12
  roundCount: number;
12
13
  summary?: string;
13
14
  transcriptPath?: string;
15
+ exposure?: AutonomyExposure;
14
16
  decisions: DecisionRecord[];
15
17
  }
16
18
 
@@ -83,6 +85,7 @@ export function finalizedInterviewEntryFields(policy: AskUserToolPolicy): Decisi
83
85
  roundCount: policy.rounds.length,
84
86
  ...(policy.summary ? { summary: policy.summary } : {}),
85
87
  ...(policy.transcriptPath ? { transcriptPath: policy.transcriptPath } : {}),
88
+ exposure: policy.exposure ?? "local",
86
89
  decisions: [...decisionsByKey.values()],
87
90
  };
88
91
  }
@@ -172,6 +175,7 @@ export function createDecisionBoardStore(deps: DecisionBoardStoreDeps = {}): Dec
172
175
  roundCount: interview.roundCount,
173
176
  ...(interview.summary ? { summary: interview.summary } : {}),
174
177
  ...(interview.transcriptPath ? { transcriptPath: interview.transcriptPath } : {}),
178
+ ...(interview.exposure ? { exposure: interview.exposure } : {}),
175
179
  decisions: interview.decisions.map((decision) => {
176
180
  if (decision.key !== selected.key) return { ...decision };
177
181
  const next: DecisionRecord = { ...decision, status: "superseded", revisedAt };