@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
package/src/cli/index.ts CHANGED
@@ -52,17 +52,20 @@ Usage:
52
52
  clio-coder context wiki [--update] [--status] [--depth auto|simple|medium|detailed] [--target <id>] [--model <id>]
53
53
  clio-coder context reset [--all] [--yes] clear accumulated project context artifacts
54
54
  clio-coder context index [--json] build the codewiki index without model calls
55
+ clio-coder context replay --sessions <path>... compare working-set policies over Clio ledgers
56
+ clio-coder context working-set --session <id|path> inspect one session's working-set state
55
57
  clio-coder uninstall remove all Clio Coder state; --remove-binary also unlinks the launcher
56
58
  clio-coder upgrade upgrade Clio Coder and run pending migrations
57
59
  clio-coder agents list discovered agent recipes
58
60
  clio-coder fleet list|run|status|drain|resume fleet contracts, status, and admission control
59
61
  clio-coder evidence build, list, or inspect evidence artifacts
60
62
  clio-coder eval run, report, or compare local eval task files
61
- clio-coder memory list, propose, approve, reject, or prune memory
63
+ clio-coder memory list, propose, promote, approve, reject, or prune memory
62
64
  clio-coder usage report cross-session usage facts and opportunities (experimental)
63
65
  clio-coder trace query or view the durable dispatch trace mirror
64
66
  clio-coder extensions install, list, enable, disable, or remove extension packages
65
67
  clio-coder skills list, inspect, validate, or install skills
68
+ clio-coder verifiers discover, author, validate, edit, or dry-run project checks
66
69
  clio-coder docs [topic] serve the HTML docs of a source checkout on 127.0.0.1 (--no-open to skip browser)
67
70
  clio-coder dev <command> harness instruments; run 'clio-coder dev' for the list
68
71
  clio-coder --help, -h this message
@@ -243,6 +246,7 @@ const COMMAND_HANDLERS = new Map<string, CommandHandler>([
243
246
  ["ext", extensionsCommand],
244
247
  ["fleet", async (subArgs) => (await import("./fleet.js")).runFleetCommand(subArgs)],
245
248
  ["skills", async (subArgs) => (await import("./skills.js")).runSkillsCommand(subArgs)],
249
+ ["verifiers", async (subArgs) => (await import("./verifiers.js")).runVerifiersCommand(subArgs)],
246
250
  ["docs", async (subArgs) => (await import("./docs.js")).runDocsCommand(subArgs)],
247
251
  ["share", async (subArgs) => (await import("./share.js")).runShareCommand(subArgs)],
248
252
  ["export", async (subArgs) => (await import("./share.js")).runExportCommand(subArgs)],
package/src/cli/memory.ts CHANGED
@@ -1,36 +1,62 @@
1
+ import { readFile, stat } from "node:fs/promises";
1
2
  import { clioDataDir } from "../core/xdg.js";
2
3
  import {
3
4
  approveMemoryRecord,
5
+ canonicalMemoryRepositoryIdentity,
4
6
  loadMemoryRecords,
5
7
  type MemoryRecord,
8
+ type MemoryScopeSelection,
9
+ memoryAgentIdentity,
10
+ memoryRuntimeIdentity,
6
11
  memoryStatus,
12
+ parseTaskMemoryHandoffSnapshot,
7
13
  proposeMemoryFromEvidence,
14
+ proposeMemoryPromotion,
8
15
  pruneStaleMemory,
16
+ type ReviewedTaskMemoryHandoffSnapshot,
9
17
  rejectMemoryRecord,
18
+ type TaskMemoryHandoffEntry,
10
19
  } from "../domains/memory/index.js";
11
20
  import { printError, printOk } from "./shared.js";
12
21
 
13
22
  const HELP = `clio-coder memory list
14
- clio-coder memory propose --from-evidence <evidenceId>
23
+ clio-coder memory propose --from-evidence <evidenceId> [scope options]
24
+ clio-coder memory promote --from-handoff <path> [--entry <id>...] --scope <scope> [scope options]
15
25
  clio-coder memory approve <memoryId>
16
26
  clio-coder memory reject <memoryId>
17
27
  clio-coder memory prune --stale
18
28
 
19
29
  Manage scoped, approved, evidence-linked local memory records.
30
+
31
+ Scope options:
32
+ --scope repo --repository <canonical-absolute-path>
33
+ --scope global --acknowledge-global
34
+ --scope runtime --runtime <source-runtime-id>
35
+ --scope agent --agent <source-agent-id>
36
+
37
+ Omit scope options from propose to preserve evidence-inferred scope. Promotion
38
+ always requires an explicit scope and creates unapproved records for review.
20
39
  `;
21
40
 
22
- type MemoryCommand = "list" | "propose" | "approve" | "reject" | "prune";
41
+ type MemoryCommand = "list" | "propose" | "promote" | "approve" | "reject" | "prune";
23
42
 
24
43
  interface ParsedMemoryArgs {
25
44
  command?: MemoryCommand;
26
45
  evidenceId?: string;
46
+ handoffPath?: string;
47
+ entryIds: string[];
27
48
  memoryId?: string;
49
+ scope?: "repo" | "global" | "runtime" | "agent";
50
+ repositoryPath?: string;
51
+ acknowledgeGlobal: boolean;
52
+ runtimeId?: string;
53
+ agentId?: string;
28
54
  stale: boolean;
29
55
  help: boolean;
30
56
  }
31
57
 
32
58
  function parseMemoryArgs(args: ReadonlyArray<string>): ParsedMemoryArgs {
33
- const parsed: ParsedMemoryArgs = { stale: false, help: false };
59
+ const parsed: ParsedMemoryArgs = { entryIds: [], acknowledgeGlobal: false, stale: false, help: false };
34
60
  for (let index = 0; index < args.length; index += 1) {
35
61
  const arg = args[index];
36
62
  if (arg === undefined) continue;
@@ -39,21 +65,70 @@ function parseMemoryArgs(args: ReadonlyArray<string>): ParsedMemoryArgs {
39
65
  continue;
40
66
  }
41
67
  if (parsed.command === undefined) {
42
- if (arg === "list" || arg === "propose" || arg === "approve" || arg === "reject" || arg === "prune") {
68
+ if (
69
+ arg === "list" ||
70
+ arg === "propose" ||
71
+ arg === "promote" ||
72
+ arg === "approve" ||
73
+ arg === "reject" ||
74
+ arg === "prune"
75
+ ) {
43
76
  parsed.command = arg;
44
77
  continue;
45
78
  }
46
79
  throw new Error(`unknown memory command: ${arg}`);
47
80
  }
48
- if (parsed.command === "propose") {
81
+ if (parsed.command === "propose" || parsed.command === "promote") {
49
82
  if (arg === "--from-evidence") {
83
+ if (parsed.command !== "propose") throw new Error("--from-evidence is valid only for memory propose");
50
84
  const value = args[index + 1];
51
85
  if (value === undefined || value.startsWith("-")) throw new Error("--from-evidence requires an evidence id");
52
86
  parsed.evidenceId = value;
53
87
  index += 1;
54
88
  continue;
55
89
  }
56
- throw new Error(`unknown memory propose argument: ${arg}`);
90
+ if (arg === "--from-handoff") {
91
+ if (parsed.command !== "promote") throw new Error("--from-handoff is valid only for memory promote");
92
+ const value = requireOptionValue(args, index, "--from-handoff", "a path");
93
+ parsed.handoffPath = value;
94
+ index += 1;
95
+ continue;
96
+ }
97
+ if (arg === "--entry") {
98
+ if (parsed.command !== "promote") throw new Error("--entry is valid only for memory promote");
99
+ parsed.entryIds.push(requireOptionValue(args, index, "--entry", "an entry id"));
100
+ index += 1;
101
+ continue;
102
+ }
103
+ if (arg === "--scope") {
104
+ const value = requireOptionValue(args, index, "--scope", "repo, global, runtime, or agent");
105
+ if (value !== "repo" && value !== "global" && value !== "runtime" && value !== "agent") {
106
+ throw new Error("--scope requires repo, global, runtime, or agent");
107
+ }
108
+ parsed.scope = value;
109
+ index += 1;
110
+ continue;
111
+ }
112
+ if (arg === "--repository") {
113
+ parsed.repositoryPath = requireOptionValue(args, index, "--repository", "a canonical absolute path");
114
+ index += 1;
115
+ continue;
116
+ }
117
+ if (arg === "--acknowledge-global") {
118
+ parsed.acknowledgeGlobal = true;
119
+ continue;
120
+ }
121
+ if (arg === "--runtime") {
122
+ parsed.runtimeId = requireOptionValue(args, index, "--runtime", "a runtime id");
123
+ index += 1;
124
+ continue;
125
+ }
126
+ if (arg === "--agent") {
127
+ parsed.agentId = requireOptionValue(args, index, "--agent", "an agent id");
128
+ index += 1;
129
+ continue;
130
+ }
131
+ throw new Error(`unknown memory ${parsed.command} argument: ${arg}`);
57
132
  }
58
133
  if (parsed.command === "approve" || parsed.command === "reject") {
59
134
  if (parsed.memoryId === undefined && !arg.startsWith("-")) {
@@ -72,20 +147,79 @@ function parseMemoryArgs(args: ReadonlyArray<string>): ParsedMemoryArgs {
72
147
  throw new Error(`unexpected memory argument: ${arg}`);
73
148
  }
74
149
  if (parsed.help) return parsed;
75
- if (parsed.command === undefined) throw new Error("memory requires list, propose, approve, reject, or prune");
150
+ if (parsed.command === undefined) throw new Error("memory requires list, propose, promote, approve, reject, or prune");
76
151
  if (parsed.command === "propose" && parsed.evidenceId === undefined) {
77
152
  throw new Error("propose requires --from-evidence <evidenceId>");
78
153
  }
154
+ if (parsed.command === "promote" && parsed.handoffPath === undefined) {
155
+ throw new Error("promote requires --from-handoff <path>");
156
+ }
79
157
  if ((parsed.command === "approve" || parsed.command === "reject") && parsed.memoryId === undefined) {
80
158
  throw new Error(`${parsed.command} requires a memory id`);
81
159
  }
82
160
  if (parsed.command === "prune" && !parsed.stale) throw new Error("prune requires --stale");
83
- if (parsed.command === "list" && (parsed.evidenceId !== undefined || parsed.memoryId !== undefined || parsed.stale)) {
161
+ if (parsed.command === "promote" && parsed.scope === undefined) {
162
+ throw new Error("promote requires --scope <repo|global|runtime|agent>");
163
+ }
164
+ validateScopeArguments(parsed);
165
+ if (
166
+ parsed.command === "list" &&
167
+ (parsed.evidenceId !== undefined ||
168
+ parsed.handoffPath !== undefined ||
169
+ parsed.entryIds.length > 0 ||
170
+ parsed.memoryId !== undefined ||
171
+ parsed.stale ||
172
+ parsed.scope !== undefined)
173
+ ) {
84
174
  throw new Error("list does not accept extra arguments");
85
175
  }
86
176
  return parsed;
87
177
  }
88
178
 
179
+ function requireOptionValue(args: ReadonlyArray<string>, index: number, option: string, expected: string): string {
180
+ const value = args[index + 1];
181
+ if (value === undefined || value.startsWith("-")) throw new Error(`${option} requires ${expected}`);
182
+ return value;
183
+ }
184
+
185
+ function validateScopeArguments(parsed: ParsedMemoryArgs): void {
186
+ const hasIdentityOption =
187
+ parsed.repositoryPath !== undefined ||
188
+ parsed.acknowledgeGlobal ||
189
+ parsed.runtimeId !== undefined ||
190
+ parsed.agentId !== undefined;
191
+ if (parsed.scope === undefined) {
192
+ if (hasIdentityOption) throw new Error("scope identity options require --scope");
193
+ return;
194
+ }
195
+ if (parsed.scope === "repo") {
196
+ if (parsed.repositoryPath === undefined)
197
+ throw new Error("repo scope requires --repository <canonical-absolute-path>");
198
+ if (parsed.acknowledgeGlobal || parsed.runtimeId !== undefined || parsed.agentId !== undefined) {
199
+ throw new Error("repo scope accepts only --repository");
200
+ }
201
+ return;
202
+ }
203
+ if (parsed.scope === "global") {
204
+ if (!parsed.acknowledgeGlobal) throw new Error("global scope requires --acknowledge-global");
205
+ if (parsed.repositoryPath !== undefined || parsed.runtimeId !== undefined || parsed.agentId !== undefined) {
206
+ throw new Error("global scope accepts only --acknowledge-global");
207
+ }
208
+ return;
209
+ }
210
+ if (parsed.scope === "runtime") {
211
+ if (parsed.runtimeId === undefined) throw new Error("runtime scope requires --runtime <source-runtime-id>");
212
+ if (parsed.repositoryPath !== undefined || parsed.acknowledgeGlobal || parsed.agentId !== undefined) {
213
+ throw new Error("runtime scope accepts only --runtime");
214
+ }
215
+ return;
216
+ }
217
+ if (parsed.agentId === undefined) throw new Error("agent scope requires --agent <source-agent-id>");
218
+ if (parsed.repositoryPath !== undefined || parsed.acknowledgeGlobal || parsed.runtimeId !== undefined) {
219
+ throw new Error("agent scope accepts only --agent");
220
+ }
221
+ }
222
+
89
223
  export async function runMemoryCommand(args: ReadonlyArray<string>): Promise<number> {
90
224
  let parsed: ParsedMemoryArgs;
91
225
  try {
@@ -114,10 +248,40 @@ export async function runMemoryCommand(args: ReadonlyArray<string>): Promise<num
114
248
  printError("propose requires --from-evidence <evidenceId>");
115
249
  return 2;
116
250
  }
117
- const result = await proposeMemoryFromEvidence(dataDir, evidenceId);
251
+ const result = await proposeMemoryFromEvidence(dataDir, evidenceId, scopeSelection(parsed));
118
252
  renderProposal(result.record, result.created);
119
253
  return 0;
120
254
  }
255
+ if (parsed.command === "promote") {
256
+ const handoffPath = parsed.handoffPath;
257
+ const selection = scopeSelection(parsed);
258
+ if (handoffPath === undefined || selection === undefined) {
259
+ printError("promote requires a handoff and explicit scope");
260
+ return 2;
261
+ }
262
+ const snapshot = await readReviewedHandoff(handoffPath);
263
+ const entries = selectHandoffEntries(snapshot, parsed.entryIds);
264
+ for (const entry of entries) {
265
+ const result = await proposeMemoryPromotion(
266
+ dataDir,
267
+ {
268
+ kind: "handoff-snapshot",
269
+ sessionId: snapshot.source.sessionId,
270
+ evidenceRefs: snapshot.source.evidenceRefs,
271
+ runtimeIds: snapshot.source.runtimeIds,
272
+ agentIds: snapshot.source.agentIds,
273
+ entry: handoffTaskEntry(entry),
274
+ redaction: {
275
+ replacementCount: snapshot.redaction.replacementCount,
276
+ sourceFields: snapshot.redaction.sourceFields,
277
+ },
278
+ },
279
+ selection,
280
+ );
281
+ renderProposal(result.record, result.created);
282
+ }
283
+ return 0;
284
+ }
121
285
  if (parsed.command === "approve") {
122
286
  const memoryId = parsed.memoryId;
123
287
  if (memoryId === undefined) {
@@ -143,7 +307,7 @@ export async function runMemoryCommand(args: ReadonlyArray<string>): Promise<num
143
307
  printOk(`pruned ${pruned.length} stale memory record${pruned.length === 1 ? "" : "s"}`);
144
308
  return 0;
145
309
  }
146
- printError("memory requires list, propose, approve, reject, or prune");
310
+ printError("memory requires list, propose, promote, approve, reject, or prune");
147
311
  return 2;
148
312
  } catch (error) {
149
313
  printError(error instanceof Error ? error.message : String(error));
@@ -151,14 +315,112 @@ export async function runMemoryCommand(args: ReadonlyArray<string>): Promise<num
151
315
  }
152
316
  }
153
317
 
318
+ function scopeSelection(parsed: ParsedMemoryArgs): MemoryScopeSelection | undefined {
319
+ if (parsed.scope === undefined) return undefined;
320
+ if (parsed.scope === "repo") {
321
+ const repositoryPath = parsed.repositoryPath;
322
+ if (repositoryPath === undefined) throw new Error("repo scope requires --repository");
323
+ const repository = canonicalMemoryRepositoryIdentity(repositoryPath);
324
+ if (repository === null || repository.key !== repositoryPath) {
325
+ throw new Error("--repository must be an existing canonical absolute repository path");
326
+ }
327
+ return { scope: "repo", repository };
328
+ }
329
+ if (parsed.scope === "global") {
330
+ if (!parsed.acknowledgeGlobal) throw new Error("global scope requires --acknowledge-global");
331
+ return { scope: "global", acknowledgeGlobal: true };
332
+ }
333
+ if (parsed.scope === "runtime") {
334
+ if (parsed.runtimeId === undefined) throw new Error("runtime scope requires --runtime");
335
+ return { scope: "runtime", runtime: memoryRuntimeIdentity(parsed.runtimeId) };
336
+ }
337
+ if (parsed.agentId === undefined) throw new Error("agent scope requires --agent");
338
+ return { scope: "agent", agent: memoryAgentIdentity(parsed.agentId) };
339
+ }
340
+
341
+ async function readReviewedHandoff(path: string): Promise<ReviewedTaskMemoryHandoffSnapshot> {
342
+ const info = await stat(path);
343
+ if (!info.isFile()) throw new Error(`task-memory handoff is not a file: ${path}`);
344
+ if (info.size > 1_000_000) throw new Error(`task-memory handoff exceeds 1000000 bytes: ${path}`);
345
+ const snapshot = parseTaskMemoryHandoffSnapshot(await readFile(path, "utf8"));
346
+ if (snapshot === null) throw new Error(`task-memory handoff is invalid: ${path}`);
347
+ if (snapshot.version !== 2) {
348
+ throw new Error("task-memory handoff lacks source session and evidence provenance; create a new redacted handoff");
349
+ }
350
+ return snapshot;
351
+ }
352
+
353
+ interface SelectedHandoffEntry {
354
+ kind: "knowledge" | "procedural";
355
+ entry: TaskMemoryHandoffEntry;
356
+ }
357
+
358
+ function selectHandoffEntries(
359
+ snapshot: ReviewedTaskMemoryHandoffSnapshot,
360
+ entryIds: ReadonlyArray<string>,
361
+ ): SelectedHandoffEntry[] {
362
+ const entries: SelectedHandoffEntry[] = [
363
+ ...snapshot.knowledge.map((entry) => ({ kind: "knowledge" as const, entry })),
364
+ ...snapshot.procedural.map((entry) => ({ kind: "procedural" as const, entry })),
365
+ ];
366
+ if (entries.length === 0) throw new Error("task-memory handoff contains no promotable entries");
367
+ if (entryIds.length === 0) return entries;
368
+ const requested = new Set(entryIds);
369
+ const selected = entries.filter(({ entry }) => requested.has(entry.id));
370
+ const missing = [...requested].filter((id) => !selected.some(({ entry }) => entry.id === id));
371
+ if (missing.length > 0) throw new Error(`task-memory handoff entry not found: ${missing.join(", ")}`);
372
+ return selected;
373
+ }
374
+
375
+ function handoffTaskEntry(selected: SelectedHandoffEntry): {
376
+ id: string;
377
+ kind: "knowledge" | "procedural";
378
+ content: string;
379
+ createdAt: string;
380
+ lastTouchedAt: string;
381
+ injectionCount: number;
382
+ } {
383
+ const createdAt = selected.entry.createdAt;
384
+ const lastTouchedAt = selected.entry.lastTouchedAt;
385
+ if (createdAt === undefined || lastTouchedAt === undefined) {
386
+ throw new Error(`task-memory handoff entry lacks timestamps: ${selected.entry.id}`);
387
+ }
388
+ return {
389
+ id: selected.entry.id,
390
+ kind: selected.kind,
391
+ content: selected.entry.content,
392
+ createdAt,
393
+ lastTouchedAt,
394
+ injectionCount: selected.entry.injectionCount,
395
+ };
396
+ }
397
+
154
398
  function renderProposal(record: MemoryRecord, created: boolean): void {
155
399
  process.stdout.write(`memory: ${record.id}\n`);
156
400
  process.stdout.write(`status: ${created ? "proposed" : memoryStatus(record)}\n`);
157
401
  process.stdout.write(`scope: ${record.scope}\n`);
402
+ if (record.repository !== undefined) process.stdout.write(`repository: ${record.repository.key}\n`);
403
+ if (record.runtime !== undefined) process.stdout.write(`runtime: ${record.runtime.key}\n`);
404
+ if (record.agent !== undefined) process.stdout.write(`agent: ${record.agent.key}\n`);
158
405
  process.stdout.write(`key: ${record.key}\n`);
159
406
  process.stdout.write(`evidence: ${record.evidenceRefs.join(", ")}\n`);
160
407
  process.stdout.write(`confidence: ${record.confidence.toFixed(2)}\n`);
161
408
  process.stdout.write(`lesson: ${record.lesson}\n`);
409
+ if (record.provenance !== undefined) {
410
+ process.stdout.write(`source: ${record.provenance.sourceKind}\n`);
411
+ if (record.provenance.sourceSessionId !== undefined) {
412
+ process.stdout.write(`source-session: ${record.provenance.sourceSessionId}\n`);
413
+ }
414
+ if (record.provenance.sourceEntryId !== undefined) {
415
+ process.stdout.write(`source-entry: ${record.provenance.sourceEntryId}\n`);
416
+ }
417
+ if (record.provenance.redaction !== undefined) {
418
+ process.stdout.write(
419
+ `redaction: ${record.provenance.redaction.replacementCount} replacement${record.provenance.redaction.replacementCount === 1 ? "" : "s"}; fields=${record.provenance.redaction.sourceFields.join(",") || "none"}\n`,
420
+ );
421
+ }
422
+ }
423
+ process.stdout.write(`review: clio-coder memory approve ${record.id}\n`);
162
424
  }
163
425
 
164
426
  function renderMemoryList(records: ReadonlyArray<MemoryRecord>): void {
@@ -4,8 +4,8 @@
4
4
  * The stream is append-oriented: it carries each piece of content exactly
5
5
  * once, as an increment while it streams and as one completed message when it
6
6
  * lands. It never repeats the growing snapshot of an in-progress message,
7
- * because that is quadratic in a long turn. One tool-heavy SciCode sub-step
8
- * wrote 802 MB of stdout, 99.3% of it `message_update` snapshots of a message
7
+ * because that is quadratic in a long turn. One tool-heavy headless turn wrote
8
+ * 802 MB of stdout, 99.3% of it `message_update` snapshots of a message
9
9
  * whose final form is 44 KB.
10
10
  *
11
11
  * The rules, in one place:
@@ -1,4 +1,8 @@
1
1
  import { readClioVersion, readPiMonoVersion } from "../../core/package-root.js";
2
+ import {
3
+ addResponseModelIdObservationCounts,
4
+ emptyResponseModelIdObservationCounts,
5
+ } from "../../core/response-model-id.js";
2
6
  import { withRunOverrides } from "../../core/run-overrides.js";
3
7
  import {
4
8
  type PendingSkillRequest,
@@ -187,6 +191,10 @@ function recordToolEnd(stats: HeadlessMainAgentReceiptStats, event: ChatLoopEven
187
191
  }
188
192
 
189
193
  function addRunUsage(left: RunUsageSummary, right: RunUsageSummary): RunUsageSummary {
194
+ const responseModelIdObservationCounts = emptyResponseModelIdObservationCounts();
195
+ addResponseModelIdObservationCounts(responseModelIdObservationCounts, left.responseModelIdObservationCounts);
196
+ addResponseModelIdObservationCounts(responseModelIdObservationCounts, right.responseModelIdObservationCounts);
197
+ const last = right.hadUsage ? right : left;
190
198
  return {
191
199
  tokens: left.tokens + right.tokens,
192
200
  costUsd: left.costUsd + right.costUsd,
@@ -197,7 +205,10 @@ function addRunUsage(left: RunUsageSummary, right: RunUsageSummary): RunUsageSum
197
205
  reasoning: left.reasoning + right.reasoning,
198
206
  apiCalls: left.apiCalls + right.apiCalls,
199
207
  hadReasoning: left.hadReasoning || right.hadReasoning,
200
- hadUsage: true,
208
+ hadUsage: left.hadUsage || right.hadUsage,
209
+ responseModelIdObservationCounts,
210
+ lastResponseModelIdObservation: last.lastResponseModelIdObservation,
211
+ lastDifferingResponseModelId: last.lastDifferingResponseModelId,
201
212
  };
202
213
  }
203
214
 
package/src/cli/run.ts CHANGED
@@ -28,7 +28,7 @@ import { SafetyDomainModule } from "../domains/safety/index.js";
28
28
  import { SchedulingDomainModule } from "../domains/scheduling/index.js";
29
29
  import { SessionDomainModule } from "../domains/session/index.js";
30
30
  import type { ImageContent } from "../engine/types.js";
31
- import { assistantTextFromEvent } from "../tools/dispatch-event-text.js";
31
+ import { assistantTextFromEvent, receiptResponseModelIdObservationLabel } from "../tools/dispatch-event-text.js";
32
32
  import { isToolProfileName } from "../tools/profiles.js";
33
33
  import { parseRunCliArgs, type RunCliArgs } from "./args.js";
34
34
  import { runClioCommand } from "./clio.js";
@@ -393,8 +393,27 @@ async function runDispatch(
393
393
  let memorySection = "";
394
394
  try {
395
395
  const records = loadMemoryRecordsSync(clioDataDir());
396
+ const boundProfileName = parsed.agentProfile ?? effectiveSettings.workers.agentBindings[parsed.agentId];
397
+ const boundProfile = boundProfileName ? effectiveSettings.workers.profiles[boundProfileName] : undefined;
398
+ const configuredRuntime = (targetId: string | null | undefined): string | undefined =>
399
+ effectiveSettings.targets.find((target) => target.id === targetId)?.runtime;
400
+ const profileRuntimeId = configuredRuntime(boundProfile?.target);
401
+ // The memory section is compiled before fleet routing settles. Admit a
402
+ // runtime-scoped record only when every permitted initial and fallback
403
+ // route is constrained to the same runtime.
404
+ const memoryRuntimeId =
405
+ parsed.target !== undefined
406
+ ? configuredRuntime(parsed.target)
407
+ : boundProfileName !== undefined
408
+ ? parsed.agentRuntime === profileRuntimeId
409
+ ? profileRuntimeId
410
+ : undefined
411
+ : parsed.agentRuntime;
396
412
  memorySection = buildMemoryPromptSection(records, {
413
+ scopes: ["global", "repo", "runtime", "agent"],
397
414
  activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
415
+ activeRuntime: memoryRuntimeId === undefined ? null : { kind: "runtime", key: memoryRuntimeId },
416
+ activeAgent: { kind: "agent", key: parsed.agentId },
398
417
  }).section;
399
418
  } catch (err) {
400
419
  process.stderr.write(
@@ -488,7 +507,8 @@ function formatReceipt(r: RunReceipt): string {
488
507
  const reasoning =
489
508
  typeof r.reasoningTokenCount === "number" && r.reasoningTokenCount > 0 ? ` reasoning=${r.reasoningTokenCount}` : "";
490
509
  const failure = r.failureMessage ? ` error=${r.failureMessage}` : "";
491
- return `receipt: ${r.runId} agent=${r.agentId} exit=${r.exitCode} target=${r.targetId} model=${r.wireModelId} tokens=${r.tokenCount}${reasoning}${failure} start=${r.startedAt} end=${r.endedAt}`;
510
+ const responseModelIdObservation = receiptResponseModelIdObservationLabel(r);
511
+ return `receipt: ${r.runId} agent=${r.agentId} exit=${r.exitCode} target=${r.targetId} requested_model_id=${r.wireModelId}${responseModelIdObservation ? ` ${responseModelIdObservation}` : ""} tokens=${r.tokenCount}${reasoning}${failure} start=${r.startedAt} end=${r.endedAt}`;
492
512
  }
493
513
 
494
514
  function mapExitCode(r: RunReceipt): number {
@@ -1028,10 +1028,19 @@ export function residentModelsSummary(states: TargetStatus["discoveredModelState
1028
1028
  * capability the target reported. Saying so is the difference between a number
1029
1029
  * a user can plan against and one they cannot.
1030
1030
  */
1031
- export function formatContextWindow(status: Pick<TargetStatus, "capabilities" | "contextWindowProvenance">): string {
1031
+ export function formatContextWindow(
1032
+ status: Pick<TargetStatus, "capabilities" | "contextWindowProvenance"> &
1033
+ Partial<Pick<TargetStatus, "target" | "discoveredModelStates">>,
1034
+ ): string {
1032
1035
  const window = status.capabilities.contextWindow;
1033
- return status.contextWindowProvenance === "runtime-default"
1034
- ? `ctx ${window} (unverified runtime default)`
1036
+ if (status.contextWindowProvenance === "runtime-default") return `ctx ${window} (unverified runtime default)`;
1037
+ // A llama.cpp window that is one slot's share of `--ctx-size` names the
1038
+ // split, so `ctx 196608` is not mistaken for the whole server (issue #187).
1039
+ const slots = status.target?.defaultModel
1040
+ ? status.discoveredModelStates?.[status.target.defaultModel]?.contextSlots
1041
+ : undefined;
1042
+ return slots && Math.floor(slots.totalContextSize / slots.slots) === window
1043
+ ? `ctx ${window} (${slots.totalContextSize} / ${slots.slots} slots)`
1035
1044
  : `ctx ${window}`;
1036
1045
  }
1037
1046
 
package/src/cli/usage.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { readdir, readFile, stat } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
+ import {
4
+ addResponseModelIdObservationCount,
5
+ emptyResponseModelIdObservationCounts,
6
+ type ResponseModelIdObservationCounts,
7
+ responseModelIdObservationCountsLabel,
8
+ } from "../core/response-model-id.js";
3
9
  import { clioDataDir, clioStateDir } from "../core/xdg.js";
4
10
  import { loadMemoryRecordsSync, type MemoryRecord } from "../domains/memory/index.js";
5
11
  import { readEvidenceIndex } from "../domains/observability/evidence-index.js";
@@ -88,6 +94,10 @@ function emptyUsageTotals(): UsageTotals {
88
94
  };
89
95
  }
90
96
 
97
+ function addResponseModelIdObservation(counts: ResponseModelIdObservationCounts, call: LedgerUsageCall): void {
98
+ addResponseModelIdObservationCount(counts, call.responseModelIdObservation, call.apiCalls ?? 1);
99
+ }
100
+
91
101
  function addUsageCall(totals: UsageTotals, call: LedgerUsageCall): void {
92
102
  totals.apiCalls += call.apiCalls ?? 1;
93
103
  totals.input += call.input;
@@ -321,17 +331,33 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
321
331
  // Token and cost facts fold the same per-call usage the `/cost` overlay
322
332
  // reseeds from, through the same session-domain function, so the report and
323
333
  // the overlay cannot disagree about what a session spent.
324
- const usageByModel = new Map<string, { providerId: string; modelId: string; totals: UsageTotals }>();
334
+ // Keyed by the model id accounting attributes the call to. A reported LM
335
+ // Link peer id gets its own row, while an observed omission becomes unknown.
336
+ // Requested ids and observation-state counts remain alongside that row.
337
+ const usageByModel = new Map<
338
+ string,
339
+ {
340
+ providerId: string;
341
+ attributedModelId: string;
342
+ requestedModelIds: Set<string>;
343
+ responseModelIdObservationCounts: ResponseModelIdObservationCounts;
344
+ totals: UsageTotals;
345
+ }
346
+ >();
325
347
  const usageTotals = emptyUsageTotals();
326
348
  for (const session of sessions) {
327
349
  for (const call of session.usageCalls) {
328
350
  addUsageCall(usageTotals, call);
329
- const key = `${call.providerId}::${call.modelId}`;
351
+ const key = `${call.providerId}::${call.attributedModelId}`;
330
352
  const entry = usageByModel.get(key) ?? {
331
353
  providerId: call.providerId,
332
- modelId: call.modelId,
354
+ attributedModelId: call.attributedModelId,
355
+ requestedModelIds: new Set<string>(),
356
+ responseModelIdObservationCounts: emptyResponseModelIdObservationCounts(),
333
357
  totals: emptyUsageTotals(),
334
358
  };
359
+ entry.requestedModelIds.add(call.requestedModelId);
360
+ addResponseModelIdObservation(entry.responseModelIdObservationCounts, call);
335
361
  addUsageCall(entry.totals, call);
336
362
  usageByModel.set(key, entry);
337
363
  }
@@ -420,7 +446,15 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
420
446
  if (presence.sessionsPresent) {
421
447
  emit({ kind: "fact", fact: "tokens", ...usageTotals });
422
448
  for (const row of usageRows) {
423
- emit({ kind: "fact", fact: "model-usage", providerId: row.providerId, modelId: row.modelId, ...row.totals });
449
+ emit({
450
+ kind: "fact",
451
+ fact: "model-usage",
452
+ providerId: row.providerId,
453
+ attributedModelId: row.attributedModelId,
454
+ requestedModelIds: [...row.requestedModelIds].sort(),
455
+ responseModelIdObservationCounts: row.responseModelIdObservationCounts,
456
+ ...row.totals,
457
+ });
424
458
  }
425
459
  }
426
460
  for (const [tool, totals] of topTools) emit({ kind: "fact", fact: "top-tool", tool, ...totals });
@@ -479,14 +513,28 @@ export async function runUsageCommand(argv: ReadonlyArray<string>): Promise<numb
479
513
  }
480
514
  if (usageRows.length > 0) {
481
515
  out("");
482
- out(" tokens by model (from session ledgers, provider-reported)");
516
+ out(" tokens by attributed model (from session ledgers and provider-reported usage)");
483
517
  process.stdout.write(
484
518
  indent(
485
519
  formatColumns([
486
- ["provider", "model", "calls", "input", "output", "cache read", "reasoning", "tokens", "cost"],
520
+ [
521
+ "provider",
522
+ "attributed model",
523
+ "requested model ids",
524
+ "response model id observation",
525
+ "calls",
526
+ "input",
527
+ "output",
528
+ "cache read",
529
+ "reasoning",
530
+ "tokens",
531
+ "cost",
532
+ ],
487
533
  ...usageRows.map((row) => [
488
534
  row.providerId,
489
- row.modelId,
535
+ row.attributedModelId,
536
+ [...row.requestedModelIds].sort().join(","),
537
+ responseModelIdObservationCountsLabel(row.responseModelIdObservationCounts),
490
538
  String(row.totals.apiCalls),
491
539
  String(row.totals.input),
492
540
  String(row.totals.output),