@iowarp/clio-coder 0.3.7 → 0.3.9

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 (400) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +13 -4
  3. package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
  4. package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
  7. package/dist/builtins-AD25UL3C.js +17 -0
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
  11. package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
  13. package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
  14. package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
  17. package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
  18. package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
  21. package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
  22. package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
  23. package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/chunk-774ILSRL.js +172 -0
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
  27. package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
  28. package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
  29. package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
  30. package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
  31. package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
  32. package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
  33. package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
  34. package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
  35. package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
  36. package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
  37. package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
  38. package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
  39. package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
  40. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  41. package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
  42. package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
  43. package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
  44. package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
  45. package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
  46. package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
  47. package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
  48. package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
  49. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  50. package/dist/chunk-JEQQR47K.js +3025 -0
  51. package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
  52. package/dist/chunk-LU7P4LHA.js +33 -0
  53. package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
  54. package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
  55. package/dist/chunk-MLOK6ZOS.js +2888 -0
  56. package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
  57. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  58. package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
  59. package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
  60. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  61. package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
  62. package/dist/chunk-NUGM5KR6.js +165 -0
  63. package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
  64. package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
  65. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  66. package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
  67. package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
  68. package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
  69. package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
  70. package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
  71. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  72. package/dist/chunk-RKKLTLYB.js +45 -0
  73. package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
  74. package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
  75. package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
  76. package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
  77. package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
  78. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  79. package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
  80. package/dist/chunk-VO2LKSTM.js +165 -0
  81. package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
  82. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  83. package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
  84. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  85. package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
  86. package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
  87. package/dist/chunk-YSEHGPCT.js +127 -0
  88. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  89. package/dist/cli/index.js +32 -32
  90. package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
  91. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
  92. package/dist/codewiki/build-worker.js +4 -4
  93. package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
  94. package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
  95. package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
  96. package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
  97. package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
  98. package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
  99. package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
  100. package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
  101. package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
  102. package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
  103. package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
  104. package/dist/doctor-TWBWFK5V.js +165 -0
  105. package/dist/eval-IJ5VEZDJ.js +4483 -0
  106. package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
  107. package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
  108. package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
  109. package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
  110. package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
  111. package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
  112. package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
  113. package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
  114. package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
  115. package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
  116. package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
  117. package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
  118. package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
  119. package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
  120. package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
  121. package/dist/registry-LG64LTF4.js +11 -0
  122. package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
  123. package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
  124. package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
  125. package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
  126. package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
  127. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  128. package/dist/support-I7LOJLIF.js +38 -0
  129. package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
  130. package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
  131. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  132. package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
  133. package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
  134. package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
  135. package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
  136. package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
  137. package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
  138. package/dist/worker/entry.js +69 -66
  139. package/docs/alcf-provider.md +1 -1
  140. package/docs/architecture.md +1 -1
  141. package/docs/artifact-versions.md +11 -5
  142. package/docs/built-in-agents.md +1 -1
  143. package/docs/capacity-and-scheduling.md +23 -2
  144. package/docs/commands-and-modes.md +2 -2
  145. package/docs/configuration-and-targets.md +37 -5
  146. package/docs/context-engine.md +63 -4
  147. package/docs/documentation-coverage.md +3 -3
  148. package/docs/documentation-guide.md +1 -1
  149. package/docs/environment-variables.md +2 -0
  150. package/docs/eval-runner.md +262 -11
  151. package/docs/evals-internal.md +72 -2
  152. package/docs/evidence-and-memory.md +77 -12
  153. package/docs/evolution.md +1 -1
  154. package/docs/extensions-and-sharing.md +3 -1
  155. package/docs/fleet-dispatch.md +34 -9
  156. package/docs/glossary.md +21 -1
  157. package/docs/installation-and-lifecycle.md +1 -1
  158. package/docs/middleware-and-components.md +1 -1
  159. package/docs/model-catalog.md +1 -1
  160. package/docs/observability.md +54 -3
  161. package/docs/proactive-memory.md +127 -14
  162. package/docs/prompt-envelope-and-tools.md +22 -2
  163. package/docs/provider-adapter-cookbook.md +1 -1
  164. package/docs/release-cut-checklist.md +60 -41
  165. package/docs/safety-model.md +1 -1
  166. package/docs/scientific-validation.md +1 -1
  167. package/docs/skills-marketplace.md +1 -1
  168. package/docs/tool-usage.md +1 -1
  169. package/docs/trace-store.md +1 -1
  170. package/docs/troubleshooting.md +87 -0
  171. package/docs/tui-design.md +1 -1
  172. package/docs/worker-dispatch-mechanics.md +1 -1
  173. package/package.json +2 -2
  174. package/src/cli/agents.ts +1 -1
  175. package/src/cli/argv.ts +5 -0
  176. package/src/cli/config-inspect.ts +33 -6
  177. package/src/cli/config.ts +1 -1
  178. package/src/cli/configure.ts +107 -23
  179. package/src/cli/doctor-state-size.ts +82 -0
  180. package/src/cli/doctor.ts +7 -1
  181. package/src/cli/eval.ts +80 -16
  182. package/src/cli/evidence.ts +30 -25
  183. package/src/cli/extensions.ts +5 -1
  184. package/src/cli/fleet-preflight.ts +2 -12
  185. package/src/cli/fleet.ts +32 -3
  186. package/src/cli/shared.ts +1 -0
  187. package/src/cli/targets.ts +45 -11
  188. package/src/cli/trace.ts +63 -4
  189. package/src/cli/usage.ts +63 -14
  190. package/src/cli/validate-model.ts +60 -5
  191. package/src/core/bus-events.ts +54 -1
  192. package/src/core/cache-telemetry.ts +42 -0
  193. package/src/core/commit-attribution.ts +4 -4
  194. package/src/core/config.ts +18 -0
  195. package/src/core/defaults.ts +36 -6
  196. package/src/core/endpoint-key.ts +27 -0
  197. package/src/core/path-boundary.ts +100 -0
  198. package/src/core/residency-target-key.ts +25 -0
  199. package/src/core/response-schema.ts +36 -2
  200. package/src/domains/agents/extension.ts +2 -11
  201. package/src/domains/agents/fleet-contract.ts +30 -12
  202. package/src/domains/agents/recipe.ts +7 -1
  203. package/src/domains/agents/registry.ts +73 -5
  204. package/src/domains/agents/result-contract.ts +128 -17
  205. package/src/domains/agents/write-boundary.ts +15 -50
  206. package/src/domains/config/classify.ts +3 -0
  207. package/src/domains/context/codewiki/coordinator.ts +12 -4
  208. package/src/domains/context/project-rules.ts +51 -1
  209. package/src/domains/dispatch/admission.ts +40 -3
  210. package/src/domains/dispatch/assignment-reconcile.ts +22 -5
  211. package/src/domains/dispatch/assignment-store.ts +151 -14
  212. package/src/domains/dispatch/capacity-lease.ts +98 -9
  213. package/src/domains/dispatch/contract.ts +26 -1
  214. package/src/domains/dispatch/delegation-plan.ts +2 -5
  215. package/src/domains/dispatch/execution-plan.ts +44 -4
  216. package/src/domains/dispatch/execution-role.ts +9 -1
  217. package/src/domains/dispatch/extension.ts +309 -96
  218. package/src/domains/dispatch/fleet-run.ts +78 -4
  219. package/src/domains/dispatch/gate-role-prompts.ts +38 -0
  220. package/src/domains/dispatch/heartbeat.ts +32 -8
  221. package/src/domains/dispatch/index.ts +6 -1
  222. package/src/domains/dispatch/intent-requirements.ts +40 -0
  223. package/src/domains/dispatch/intent.ts +84 -8
  224. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  225. package/src/domains/dispatch/path-scope.ts +370 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +2 -1
  227. package/src/domains/dispatch/reservation-store.ts +116 -8
  228. package/src/domains/dispatch/state.ts +4 -0
  229. package/src/domains/dispatch/types.ts +14 -7
  230. package/src/domains/dispatch/validation.ts +6 -3
  231. package/src/domains/dispatch/worker-spawn.ts +25 -11
  232. package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
  233. package/src/domains/dispatch/write-boundary.ts +262 -22
  234. package/src/domains/eval/artifacts/store.ts +62 -0
  235. package/src/domains/eval/compare/behavioral.ts +224 -0
  236. package/src/domains/eval/compare/compare.ts +355 -2
  237. package/src/domains/eval/compare/envelope.ts +128 -0
  238. package/src/domains/eval/compare/gates.ts +24 -6
  239. package/src/domains/eval/compare/thresholds.ts +30 -3
  240. package/src/domains/eval/execution-provenance.ts +240 -0
  241. package/src/domains/eval/metrics/aggregate.ts +136 -0
  242. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  243. package/src/domains/eval/metrics/evidence.ts +79 -2
  244. package/src/domains/eval/metrics/tracked.ts +413 -0
  245. package/src/domains/eval/provenance.ts +117 -0
  246. package/src/domains/eval/reports/comparison.ts +128 -0
  247. package/src/domains/eval/reports/junit.ts +17 -3
  248. package/src/domains/eval/reports/markdown.ts +3 -3
  249. package/src/domains/eval/reports/text.ts +14 -0
  250. package/src/domains/eval/run-compare.ts +20 -0
  251. package/src/domains/eval/runners/clio-run.ts +139 -2
  252. package/src/domains/eval/runners/external-command.ts +28 -3
  253. package/src/domains/eval/schema/adapter.ts +111 -0
  254. package/src/domains/eval/schema/artifact.ts +20 -0
  255. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  256. package/src/domains/eval/schema/behavioral.ts +520 -0
  257. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  258. package/src/domains/eval/schema/serving.ts +74 -0
  259. package/src/domains/eval/schema/suite.ts +38 -8
  260. package/src/domains/eval/schema/validate.ts +58 -3
  261. package/src/domains/eval/schema/verdict.ts +237 -0
  262. package/src/domains/eval/suites/resolve.ts +2 -0
  263. package/src/domains/eval/suites/run.ts +264 -33
  264. package/src/domains/eval/verifiers/command.ts +2 -1
  265. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  266. package/src/domains/evidence/build.ts +68 -21
  267. package/src/domains/evidence/eval.ts +2 -12
  268. package/src/domains/evidence/findings-markdown.ts +33 -0
  269. package/src/domains/evidence/index.ts +21 -0
  270. package/src/domains/evidence/provenance.ts +46 -11
  271. package/src/domains/evidence/run-trust.ts +7 -113
  272. package/src/domains/evidence/trust-projection.ts +274 -0
  273. package/src/domains/evidence/trust-status.ts +145 -17
  274. package/src/domains/evidence/types.ts +4 -0
  275. package/src/domains/extensions/compatibility.ts +285 -0
  276. package/src/domains/extensions/discovery.ts +126 -4
  277. package/src/domains/extensions/resources.ts +21 -9
  278. package/src/domains/extensions/state.ts +18 -5
  279. package/src/domains/extensions/types.ts +6 -1
  280. package/src/domains/lifecycle/doctor.ts +209 -2
  281. package/src/domains/memory/index.ts +14 -0
  282. package/src/domains/memory/task-bank-promotion.ts +64 -0
  283. package/src/domains/memory/task-memory-policy.ts +77 -8
  284. package/src/domains/memory/task-memory-spend.ts +131 -0
  285. package/src/domains/memory/task-memory-status.ts +7 -0
  286. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  287. package/src/domains/middleware/index.ts +1 -0
  288. package/src/domains/middleware/memory-intervention.ts +69 -5
  289. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  290. package/src/domains/observability/background-memory-usage.ts +140 -0
  291. package/src/domains/observability/cost.ts +1 -1
  292. package/src/domains/observability/index.ts +7 -0
  293. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  294. package/src/domains/observability/trace-store.ts +192 -2
  295. package/src/domains/prompts/compiler.ts +100 -13
  296. package/src/domains/prompts/contract.ts +3 -5
  297. package/src/domains/providers/endpoint-capacity.ts +96 -0
  298. package/src/domains/providers/extension.ts +30 -2
  299. package/src/domains/providers/index.ts +10 -0
  300. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  301. package/src/domains/providers/runtime-resolution.ts +8 -1
  302. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  303. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  304. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  305. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  306. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  307. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  308. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  309. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  310. package/src/domains/providers/types/capability-flags.ts +2 -0
  311. package/src/domains/providers/types/target-descriptor.ts +2 -0
  312. package/src/domains/resources/common-loader.ts +3 -0
  313. package/src/domains/resources/prompts/loader.ts +184 -17
  314. package/src/domains/safety/call-target.ts +52 -0
  315. package/src/domains/safety/policy-engine.ts +5 -5
  316. package/src/domains/safety/run-effects.ts +96 -2
  317. package/src/domains/safety/scope.ts +7 -12
  318. package/src/domains/session/context-accounting.ts +52 -1
  319. package/src/domains/session/context-ledger.ts +37 -13
  320. package/src/domains/session/index.ts +6 -0
  321. package/src/domains/session/prompt-cache.ts +140 -0
  322. package/src/domains/session/prompt-manifest.ts +42 -0
  323. package/src/engine/acp/adapter.ts +18 -3
  324. package/src/engine/acp/server.ts +4 -1
  325. package/src/engine/ai.ts +35 -0
  326. package/src/engine/apis/llamacpp-residency.ts +55 -3
  327. package/src/engine/apis/lmstudio.ts +25 -5
  328. package/src/engine/apis/ollama-native.ts +2 -1
  329. package/src/engine/apis/openai-completions.ts +80 -17
  330. package/src/engine/apis/residency-lock.ts +3 -1
  331. package/src/engine/apis/residency.ts +34 -1
  332. package/src/engine/prompt-templates.ts +18 -1
  333. package/src/engine/provider-payload.ts +29 -1
  334. package/src/engine/worker-runtime.ts +6 -3
  335. package/src/entry/orchestrator.ts +176 -30
  336. package/src/interactive/chat-loop-messages.ts +26 -7
  337. package/src/interactive/chat-loop.ts +318 -41
  338. package/src/interactive/chat-panel.ts +62 -8
  339. package/src/interactive/clio-editor.ts +45 -8
  340. package/src/interactive/context-activity.ts +5 -1
  341. package/src/interactive/context-meter.ts +1 -1
  342. package/src/interactive/context-overlay.ts +40 -10
  343. package/src/interactive/cost-overlay.ts +64 -6
  344. package/src/interactive/dispatch-board.ts +127 -5
  345. package/src/interactive/fleet-run-preview.ts +41 -15
  346. package/src/interactive/handoff-round.ts +41 -2
  347. package/src/interactive/interactive-application.ts +24 -1
  348. package/src/interactive/interactive-event-projection.ts +14 -0
  349. package/src/interactive/interactive-input-runtime.ts +8 -0
  350. package/src/interactive/interactive-presentation.ts +4 -0
  351. package/src/interactive/interactive-shell.ts +20 -17
  352. package/src/interactive/interactive-slash-runtime.ts +27 -4
  353. package/src/interactive/memory-overlay.ts +8 -0
  354. package/src/interactive/mutation-preview.ts +295 -0
  355. package/src/interactive/overlay-general-openers.ts +16 -0
  356. package/src/interactive/overlay-key-routing.ts +38 -0
  357. package/src/interactive/overlay-lifecycle.ts +38 -5
  358. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  359. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  360. package/src/interactive/overlays/ask-user.ts +91 -19
  361. package/src/interactive/overlays/help-reference.ts +4 -0
  362. package/src/interactive/overlays/prompts.ts +11 -1
  363. package/src/interactive/overlays/settings.ts +176 -48
  364. package/src/interactive/permission-hint.ts +34 -2
  365. package/src/interactive/permission-overlay.ts +159 -9
  366. package/src/interactive/prewarm.ts +197 -0
  367. package/src/interactive/render-trace.ts +162 -15
  368. package/src/interactive/renderers/tool-execution.ts +4 -0
  369. package/src/interactive/side-question.ts +58 -1
  370. package/src/interactive/slash-commands.ts +7 -2
  371. package/src/interactive/status/controller.ts +11 -0
  372. package/src/interactive/status/state-machine.ts +54 -2
  373. package/src/interactive/status/types.ts +7 -0
  374. package/src/interactive/terminal-lease.ts +2 -0
  375. package/src/interactive/turn-context.ts +299 -31
  376. package/src/interactive/turn-persistence.ts +14 -4
  377. package/src/interactive/turn-prewarm.ts +364 -0
  378. package/src/interactive/turn-queues.ts +7 -4
  379. package/src/interactive/turn-runtime.ts +8 -1
  380. package/src/interactive/turn-state.ts +23 -0
  381. package/src/interactive/view/artifacts.ts +42 -9
  382. package/src/interactive/view/view-overlay.ts +43 -6
  383. package/src/interactive/worker-receipts.ts +14 -2
  384. package/src/interactive/worker-stream.ts +8 -0
  385. package/src/tools/ask-user.ts +43 -2
  386. package/src/tools/dispatch-admission.ts +12 -13
  387. package/src/tools/dispatch-arguments.ts +27 -0
  388. package/src/tools/dispatch-plan.ts +46 -9
  389. package/src/tools/dispatch-runner.ts +48 -13
  390. package/src/tools/dispatch-scout.ts +1 -1
  391. package/src/tools/monitor.ts +13 -0
  392. package/src/tools/registry.ts +16 -0
  393. package/src/tools/worker-evidence.ts +19 -13
  394. package/src/worker/spec-contract.ts +2 -1
  395. package/dist/chunk-AOCYTWAV.js +0 -449
  396. package/dist/chunk-HWUFFB6L.js +0 -83
  397. package/dist/chunk-R346GLFC.js +0 -31
  398. package/dist/chunk-ZGH7FGS5.js +0 -1079
  399. package/dist/doctor-RN4YKO2X.js +0 -87
  400. package/dist/eval-RUBJVSNQ.js +0 -2557
@@ -12,9 +12,17 @@ import {
12
12
  type ContextPrunedPayload,
13
13
  type ContextRecalledPayload,
14
14
  type ContextWarningPayload,
15
+ type MemoryStepCompletedPayload,
16
+ type ResidencyMutationPayload,
15
17
  } from "../core/bus-events.js";
18
+ import {
19
+ type BackendCacheVerdict,
20
+ type BackendCompletionTimings,
21
+ uncachedPrefillTokens,
22
+ } from "../core/cache-telemetry.js";
16
23
  import type { ClioSettings } from "../core/config.js";
17
24
  import type { SafeEventBus } from "../core/event-bus.js";
25
+ import { residencyTargetKey } from "../core/residency-target-key.js";
18
26
  import type { ToolName } from "../core/tool-names.js";
19
27
  import { buildEvictionFields, planEviction } from "../domains/context/working-set/engine.js";
20
28
  import { foldWorkingSet } from "../domains/context/working-set/fold.js";
@@ -26,6 +34,7 @@ import type { PromptsContract } from "../domains/prompts/contract.js";
26
34
  import {
27
35
  type ContextWindowDetails,
28
36
  type ContextWindowSource,
37
+ canonicalEndpointKey,
29
38
  type ProvidersContract,
30
39
  resolveRuntimeTarget,
31
40
  } from "../domains/providers/index.js";
@@ -50,18 +59,29 @@ import {
50
59
  contextUsageSnapshot,
51
60
  estimateAgentContextBreakdown,
52
61
  estimateAgentContextTokens,
62
+ estimateAgentMessageTokens,
53
63
  getLatestContextSnapshot,
64
+ lastLoadedContextWindow,
54
65
  reconcileSnapshot,
55
66
  snapshotInputTokens,
56
67
  } from "../domains/session/context-accounting.js";
57
- import { buildContextLedger, type ContextLedger, type PromptCacheStats } from "../domains/session/context-ledger.js";
68
+ import {
69
+ buildContextLedger,
70
+ type ContextLedger,
71
+ type PrewarmStats,
72
+ type PromptCacheStats,
73
+ } from "../domains/session/context-ledger.js";
58
74
  import type { SessionContract } from "../domains/session/contract.js";
59
75
  import type { CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
60
- import { appendPromptCompileRecord, type SessionPromptCompileRecord } from "../domains/session/prompt-manifest.js";
76
+ import {
77
+ appendPromptCompileRecord,
78
+ PROMPT_MANIFEST_VERSION,
79
+ readPromptCompileRecords,
80
+ type SessionPromptCompileRecord,
81
+ } from "../domains/session/prompt-manifest.js";
61
82
  import type { AgentMessage, Usage } from "../engine/types.js";
62
83
  import type { ToolRegistry } from "../tools/registry.js";
63
84
  import {
64
- type BackendCacheVerdict,
65
85
  backendCacheVerdict,
66
86
  extractUserText,
67
87
  runtimeSupportsTools,
@@ -92,11 +112,43 @@ export interface TurnContextDeps {
92
112
  }
93
113
 
94
114
  export interface LiveContextEstimate {
115
+ /**
116
+ * The figure to budget against: the reconciled total when the provider has
117
+ * attested one for this conversation, otherwise the estimate. Never below
118
+ * the estimate, because the estimate covers material the last provider call
119
+ * did not see.
120
+ */
95
121
  tokens: number;
122
+ /** Pure chars/4 projection, with the pre-existing provider-usage anchor. */
123
+ estimatedTokens: number;
124
+ /**
125
+ * Provider-attested prompt tokens for the messages up to the last reconciled
126
+ * call, plus a chars/4 estimate of everything appended since. Null before the
127
+ * first reconcile of a session and after a summary compaction rewrites the
128
+ * history the attestation described.
129
+ */
130
+ reconciledTokens: number | null;
96
131
  contextWindow: number;
97
132
  breakdown: ReturnType<typeof estimateAgentContextBreakdown>;
98
133
  }
99
134
 
135
+ /** Known causes that make the next provider prefix cache miss expected. */
136
+ export type ExpectedColdReason =
137
+ | "dispatch"
138
+ | "compaction"
139
+ | "working_set_evict"
140
+ | "residency"
141
+ | "thinking_change"
142
+ | "tool_surface_change"
143
+ | "prompt_recompiled"
144
+ /**
145
+ * A proactive-memory step ran between turns on the endpoint this session
146
+ * streams against. Its prompt is a trajectory rather than the chat prefix, so
147
+ * on a local server that keeps one prefix cache the next turn re-prefills
148
+ * (#229).
149
+ */
150
+ | "background_memory";
151
+
100
152
  export interface TurnContext {
101
153
  ensureSessionPrompt(agentRuntime: AgentRuntime): Promise<CompiledSessionPrompt | null>;
102
154
  logPromptCompileIfPending(): void;
@@ -120,6 +172,12 @@ export interface TurnContext {
120
172
  */
121
173
  promptSideTokens(): number;
122
174
  liveContextEstimate(agentRuntime: AgentRuntime, pendingUserText?: string): LiveContextEstimate;
175
+ /**
176
+ * The loaded context window this session already recorded for a target and
177
+ * model, so a resume budgets against it instead of re-probing. Null when the
178
+ * ledger has no such measurement.
179
+ */
180
+ rememberedLoadedContextWindow(targetId: string, modelId: string): number | null;
123
181
  refreshAgentMessagesFromSession(agentRuntime: AgentRuntime): ReadonlyArray<SessionEntry>;
124
182
  runAutoCompact(
125
183
  agentRuntime: AgentRuntime,
@@ -142,12 +200,20 @@ export interface TurnContext {
142
200
  contextUsage(): ContextUsageSnapshot;
143
201
  contextLedger(): ContextLedger;
144
202
  emitContextWindowWarningTransition(warning: string | null): void;
203
+ /** Record one known cache disturbance for the current or next call. */
204
+ noteColdReason(reason: ExpectedColdReason): void;
145
205
  /** Consume disturbances since the last settled run (T3.3 honesty). */
146
206
  consumeExpectedColdReasons(runtimeId: string): void;
147
207
  /** Prompt-cache record for one persisted assistant call, with cold-reason stamp. */
148
- promptCachePayloadForAssistant(usage: Usage): Record<string, unknown>;
208
+ promptCachePayloadForAssistant(usage: Usage, backend?: BackendCompletionTimings): Record<string, unknown>;
149
209
  /** Record the settled run's cache summary for /context. */
150
210
  noteRunCacheSummary(messages: ReadonlyArray<AgentMessage>, runFirstCallVerdict: BackendCacheVerdict | null): void;
211
+ /**
212
+ * Record one pre-warm for `/context`. It contributes no tokens to the context
213
+ * estimate and is never an expected-cold reason: a pre-warm is the opposite
214
+ * of a disturbance, it is the prefix the next turn wants already resident.
215
+ */
216
+ notePrewarm(prewarm: PrewarmStats): void;
151
217
  resetForSession(): void;
152
218
  dispose(): void;
153
219
  }
@@ -157,12 +223,30 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
157
223
  const compactionTrigger = new AutoCompactionTrigger<CompactResult | null>();
158
224
 
159
225
  let currentContextSnapshot: ContextSnapshot | null = null;
226
+ /**
227
+ * The last provider-attested prompt size, and how many agent messages it
228
+ * covered (issue #227).
229
+ *
230
+ * `tokens` is the provider's own prompt count for that call plus the output
231
+ * it produced, which together are what the next call's prompt carries for
232
+ * the same messages. `anchoredMessageCount` is the length of the live
233
+ * message list at that moment, so anything appended since is priced by
234
+ * estimate and added on top. Unlike the per-message usage anchor inside
235
+ * `estimateAgentContextTokens`, this survives `contextUsageInvalidated`:
236
+ * a working-set projection subtracts the tokens it removed rather than
237
+ * throwing the attestation away.
238
+ */
239
+ let reconciledAnchor: { tokens: number; anchoredMessageCount: number } | null = null;
160
240
  let lastCompactionEvent: { stage: string; tokensBefore: number; tokensAfter: number; trigger: string } | null = null;
161
241
  // Last settled run's provider cache usage plus whether the compiled system
162
242
  // prompt was reused. Shown together in /context so "prompt reused" can
163
243
  // never imply provider cache reuse the backend did not report.
164
244
  let lastPromptCache: PromptCacheStats | null = null;
165
245
  let lastSystemPromptReused = false;
246
+ // The last pre-warm, shown by /context until the next settled run answers the
247
+ // question it was asked about: did the prefix the next turn needs get there
248
+ // before the operator did.
249
+ let lastPrewarm: PrewarmStats | null = null;
166
250
  // The session system prompt, compiled once per session. Recompiles happen
167
251
  // only on explicit events: the compile key (target, model, safety level,
168
252
  // session id) changes, or a config hot-reload invalidates the cache. A
@@ -170,6 +254,19 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
170
254
  // ledger entry so a cold provider cache is always explainable.
171
255
  let sessionPrompt: CompiledSessionPrompt | null = null;
172
256
  let sessionPromptKey: string | null = null;
257
+ // The hash this process compiled *for the session that is current now*, and
258
+ // the manifest's `previousHash` whenever it is set. It is not read off
259
+ // `sessionPrompt`: an in-process `/resume` leaves that compile in place so
260
+ // the ledger keeps its project-preload labels until the next compile, but
261
+ // manifest provenance follows the session, so a switch drops the hash here
262
+ // and the next compile falls through to the incoming session's own manifest
263
+ // exactly as a cold-start resume does (#249).
264
+ let sessionPromptHash: string | null = null;
265
+ // Whether this process has already looked up the resumed session's last
266
+ // recorded prompt hash. The lookup reads the manifest file, so it happens
267
+ // once, on the first compile, and never on the reuse path.
268
+ let resumedPromptHashRead = false;
269
+ let resumedPromptHash: string | null = null;
173
270
  const sessionWorkingContextPaths = new Set<string>();
174
271
  let pendingPromptLogEntry: SessionPromptCompileRecord | null = null;
175
272
  // A post-tool guard can run after every tool result in one model turn. Once
@@ -178,22 +275,25 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
178
275
  // and summary probes. A new submitted user turn gets a new id naturally.
179
276
  let emptyAutoCompactTurnId: string | null = null;
180
277
 
181
- // Cache-disturbance honesty (T3.3). Dispatch traffic and history
182
- // compaction invalidate a single-slot local backend's prefix cache.
183
- // Accumulate every disturbance since the last settled run; the next
184
- // submit consumes the set, stamps `promptCache.expectedColdReasons` on
185
- // its first assistant entry, and shows one dim notice.
186
- const pendingColdReasons = new Set<string>();
187
- let runExpectedColdReasons: string[] = [];
188
- let nextAssistantColdReasons: string[] = [];
189
- // A working-set eviction changes the prefix itself, so it cools every
190
- // tier's cache, not only a single-slot local one. Dispatch and compaction
191
- // disturbances keep the local-native gate below.
192
- const TIER_INDEPENDENT_COLD_REASONS: ReadonlySet<string> = new Set(["working_set_evict"]);
193
- const stampsOnTier = (reason: string, runtimeId: string | undefined): boolean =>
278
+ // Cache-disturbance honesty (T3.3). Accumulate every known local-runtime
279
+ // disturbance and prefix-byte change since the last settled run. The next
280
+ // submit consumes the set, stamps `promptCache.expectedColdReasons` on its
281
+ // first assistant entry, and shows one dim notice.
282
+ const pendingColdReasons = new Set<ExpectedColdReason>();
283
+ let runExpectedColdReasons: ExpectedColdReason[] = [];
284
+ let nextAssistantColdReasons: ExpectedColdReason[] = [];
285
+ // Prefix-byte changes cool every tier. Residency, thinking changes, dispatch
286
+ // traffic, and compaction keep the local-native gate because they disturb a
287
+ // local server or its rendered template.
288
+ const TIER_INDEPENDENT_COLD_REASONS: ReadonlySet<ExpectedColdReason> = new Set([
289
+ "working_set_evict",
290
+ "tool_surface_change",
291
+ "prompt_recompiled",
292
+ ]);
293
+ const stampsOnTier = (reason: ExpectedColdReason, runtimeId: string | undefined): boolean =>
194
294
  TIER_INDEPENDENT_COLD_REASONS.has(reason) ||
195
295
  (runtimeId !== undefined && deps.providers.getRuntime(runtimeId)?.tier === "local-native");
196
- const noteColdReason = (reason: string): void => {
296
+ const noteColdReason = (reason: ExpectedColdReason): void => {
197
297
  if (!state.streaming) {
198
298
  pendingColdReasons.add(reason);
199
299
  return;
@@ -217,6 +317,21 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
217
317
  noteColdReason("compaction");
218
318
  }) ?? null,
219
319
  ),
320
+ deps.bus?.on(BusChannels.ResidencyMutation, (payload: ResidencyMutationPayload) => {
321
+ const runtime = state.runtime;
322
+ const model = runtime?.agent.state.model as { baseUrl?: unknown } | undefined;
323
+ const baseUrl = typeof model?.baseUrl === "string" ? model.baseUrl : null;
324
+ if (payload.targetKey !== residencyTargetKey(runtime?.runtimeId ?? "", baseUrl)) return;
325
+ noteColdReason("residency");
326
+ }) ?? null,
327
+ // A memory step on another endpoint disturbs nothing here, so the stamp is
328
+ // gated on the step having called the very server this session streams to.
329
+ deps.bus?.on(BusChannels.MemoryStepCompleted, (payload: MemoryStepCompletedPayload) => {
330
+ const target = state.runtime?.runtimeResolution.target;
331
+ if (target === undefined) return;
332
+ if (canonicalEndpointKey(target) !== payload.endpointKey) return;
333
+ noteColdReason("background_memory");
334
+ }) ?? null,
220
335
  deps.bus?.on(BusChannels.ContextRecalled, (payload: ContextRecalledPayload) => {
221
336
  middleware.fireCompactionHook("working_set_recall", payload.trigger);
222
337
  }) ?? null,
@@ -322,6 +437,33 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
322
437
  * (issue #189). Null when no target is configured or it does not resolve,
323
438
  * and the overlay's "unknown" is then true.
324
439
  */
440
+ const rememberedLoadedContextWindow = (targetId: string, modelId: string): number | null => {
441
+ const session = deps.session?.current();
442
+ if (!session) return null;
443
+ return lastLoadedContextWindow(session, targetId, modelId);
444
+ };
445
+
446
+ /**
447
+ * The prompt hash this session last recorded, for the first compile of a
448
+ * resumed process. Without it a resume reports `previousHash: null`, which
449
+ * reads as "there was no prompt before" when in fact there was one and it
450
+ * is exactly what a reader needs to attribute the cold cache to: a layout
451
+ * bump, a fragment edit, or a moved context window (issue #249).
452
+ */
453
+ const lastRecordedPromptHash = (): string | null => {
454
+ if (resumedPromptHashRead) return resumedPromptHash;
455
+ resumedPromptHashRead = true;
456
+ const currentSession = deps.session?.current();
457
+ if (!currentSession) {
458
+ // No session to read; leave the lookup open for the next compile.
459
+ resumedPromptHashRead = false;
460
+ return null;
461
+ }
462
+ const records = readPromptCompileRecords(currentSession);
463
+ resumedPromptHash = records[records.length - 1]?.systemPromptHash ?? null;
464
+ return resumedPromptHash;
465
+ };
466
+
325
467
  const resolveWindowWithoutRuntime = (): ContextWindowDetails | null => {
326
468
  const settings = deps.getSettings();
327
469
  const targetId = settings.orchestrator?.target?.trim();
@@ -334,6 +476,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
334
476
  use: "orchestrator",
335
477
  requireTools: false,
336
478
  requireOutputBudget: true,
479
+ knownLoadedContextWindow: rememberedLoadedContextWindow(targetId, wireModelId),
337
480
  });
338
481
  return resolved.ok ? resolved.target.contextWindowDetails : null;
339
482
  };
@@ -383,6 +526,29 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
383
526
  return { contextWindow: 0, contextWindowSource: null, contextWindowSlots: null };
384
527
  };
385
528
 
529
+ /**
530
+ * Carry the reconciled anchor onto the current message list: the attested
531
+ * prompt for the messages it covered, plus a chars/4 estimate of everything
532
+ * appended since, plus text that has not been submitted yet. The tool
533
+ * schemas and the system prompt are inside the attested figure already, so
534
+ * they are not added again. Null when there is no attestation, or when the
535
+ * list is shorter than the anchor covered, which means the history it
536
+ * described was rewritten beneath it.
537
+ */
538
+ const reconciledAnchoredTokens = (agentRuntime: AgentRuntime, pendingUserTokens: number): number | null => {
539
+ const anchor = reconciledAnchor;
540
+ if (!anchor) return null;
541
+ const messages = agentRuntime.agent.state.messages;
542
+ if (anchor.anchoredMessageCount > messages.length) return null;
543
+ let tail = 0;
544
+ for (let i = anchor.anchoredMessageCount; i < messages.length; i += 1) {
545
+ const message = messages[i];
546
+ if (message === undefined) continue;
547
+ tail += estimateAgentMessageTokens(message);
548
+ }
549
+ return anchor.tokens + tail + pendingUserTokens;
550
+ };
551
+
386
552
  const liveContextEstimate = (agentRuntime: AgentRuntime, pendingUserText?: string): LiveContextEstimate => {
387
553
  const contextWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
388
554
  const estimateInput = {
@@ -391,10 +557,34 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
391
557
  tools: agentRuntime.agent.state.tools,
392
558
  ...(pendingUserText !== undefined ? { pendingUserText } : {}),
393
559
  };
560
+ const breakdown = estimateAgentContextBreakdown(estimateInput);
561
+ const estimatedTokens = estimateAgentContextTokens(estimateInput);
562
+ const reconciledTokens = reconciledAnchoredTokens(agentRuntime, breakdown.pendingUserTokens);
394
563
  return {
395
- tokens: estimateAgentContextTokens(estimateInput),
564
+ // The estimate is a floor, not a competing verdict: it prices material
565
+ // the attested call never saw, so a provider count below it would be
566
+ // answering about a smaller conversation.
567
+ tokens: Math.max(estimatedTokens, reconciledTokens ?? 0),
568
+ estimatedTokens,
569
+ reconciledTokens,
396
570
  contextWindow,
397
- breakdown: estimateAgentContextBreakdown(estimateInput),
571
+ breakdown,
572
+ };
573
+ };
574
+
575
+ /**
576
+ * A working-set projection removes tokens from messages the attestation
577
+ * covered; it does not make the attestation wrong about the rest. Subtract
578
+ * what the planner priced out against the same projection and re-anchor on
579
+ * the refreshed list, instead of discarding the figure and falling back to
580
+ * pure chars/4 exactly when the accounting matters most (issue #227).
581
+ * Call after the message list has been rebuilt.
582
+ */
583
+ const carryReconciledAnchorThroughProjection = (agentRuntime: AgentRuntime, tokensRemoved: number): void => {
584
+ if (!reconciledAnchor) return;
585
+ reconciledAnchor = {
586
+ tokens: Math.max(0, reconciledAnchor.tokens - Math.max(0, tokensRemoved)),
587
+ anchoredMessageCount: agentRuntime.agent.state.messages.length,
398
588
  };
399
589
  };
400
590
 
@@ -414,7 +604,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
414
604
  const emitCompactionActivity = (status: ContextActivityStatus, message: string): void => {
415
605
  deps.bus?.emit(BusChannels.ContextActivity, {
416
606
  kind: "compaction",
417
- phase: "done",
607
+ phase: status === "completed" ? "done" : "compact",
418
608
  status,
419
609
  message,
420
610
  at: Date.now(),
@@ -507,6 +697,8 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
507
697
  emitCompactionActivity("started", "compacting context (mask stage)");
508
698
  deps.session.replaceEntries(masked.entries);
509
699
  refreshAgentMessagesFromSession(agentRuntime);
700
+ // The masked history is not the one the provider counted.
701
+ reconciledAnchor = null;
510
702
  deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
511
703
 
512
704
  const postMaskSnapshot = captureRuntimeContextSnapshot(
@@ -591,6 +783,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
591
783
  });
592
784
  noteColdReason("working_set_evict");
593
785
  refreshAgentMessagesFromSession(agentRuntime);
786
+ carryReconciledAnchorThroughProjection(agentRuntime, planned.tokensBefore - planned.tokensAfter);
594
787
 
595
788
  const postEvictionSnapshot = captureRuntimeContextSnapshot(
596
789
  agentRuntime,
@@ -683,6 +876,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
683
876
  recordCompactionUsage(agentRuntime, result);
684
877
 
685
878
  refreshAgentMessagesFromSession(agentRuntime);
879
+ // A summary replaces the conversation the attestation described, so no
880
+ // arithmetic carries it forward; the next call re-anchors it.
881
+ reconciledAnchor = null;
686
882
 
687
883
  const postCompactSnapshot = captureRuntimeContextSnapshot(
688
884
  agentRuntime,
@@ -741,6 +937,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
741
937
  captureRuntimeContextSnapshot,
742
938
  persistContextSnapshot,
743
939
  liveContextEstimate,
940
+ rememberedLoadedContextWindow,
744
941
  refreshAgentMessagesFromSession,
745
942
  runAutoCompact,
746
943
 
@@ -754,6 +951,18 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
754
951
  },
755
952
 
756
953
  reconcileUsage(usage: Usage): void {
954
+ // Cached prompt tokens still occupy the window; providers report them
955
+ // outside `input`, so the attested prompt is the three summed.
956
+ const promptTokens = (usage.input || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
957
+ const messages = state.runtime?.agent.state.messages;
958
+ if (promptTokens > 0 && messages) {
959
+ // The output of this call is part of the next call's prompt for the
960
+ // same messages, which is why it is folded in here.
961
+ reconciledAnchor = {
962
+ tokens: promptTokens + (usage.output || 0),
963
+ anchoredMessageCount: messages.length,
964
+ };
965
+ }
757
966
  if (!currentContextSnapshot) return;
758
967
  // Reconcile in memory on every API call so the live meters
759
968
  // track usage; persistence waits for the run to settle.
@@ -787,7 +996,22 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
787
996
  const modelState = agentRuntime.agent.state.model as
788
997
  | (typeof agentRuntime.agent.state.model & { clio?: { quirks?: LocalModelQuirks } })
789
998
  | undefined;
790
- const contextWindow = typeof modelState?.contextWindow === "number" ? modelState.contextWindow : null;
999
+ // `Context window: N` is the window the backend will actually serve
1000
+ // this session, taken from the resolution the turn already ran rather
1001
+ // than from the model descriptor's copy of it (issue #249). The
1002
+ // ranking that puts a loaded figure ahead of a probed one lives in
1003
+ // `resolveContextWindowDetails`, which tests `loadedContextWindow`
1004
+ // first and only then `probedContextWindow`, so `effectiveContextWindow`
1005
+ // already is the loaded window whenever one is known and the source
1006
+ // already reads "loaded"; brief #227's carry-forward is what puts that
1007
+ // figure in reach on a resume. The source rides into the prompt
1008
+ // manifest, so a recompile after the loaded window is first observed
1009
+ // is explained by the record.
1010
+ const windowDetails = agentRuntime.runtimeResolution.contextWindowDetails;
1011
+ const resolvedWindow = windowDetails.effectiveContextWindow;
1012
+ const contextWindow = typeof resolvedWindow === "number" && resolvedWindow > 0 ? resolvedWindow : null;
1013
+ const contextWindowSource: ContextWindowSource | null =
1014
+ contextWindow === null ? null : windowDetails.contextWindowSource;
791
1015
  const guidance = modelState?.clio?.quirks?.thinking?.guidance;
792
1016
  // Per-tool prompt hints come from registry metadata, derived once from
793
1017
  // the frozen surface per compile. The compiler renders them sorted by
@@ -823,16 +1047,19 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
823
1047
  cwd: process.cwd(),
824
1048
  workingContextPaths: [...sessionWorkingContextPaths],
825
1049
  });
826
- const previousHash = sessionPrompt?.systemPromptHash ?? null;
1050
+ const previousHash = sessionPromptHash ?? lastRecordedPromptHash();
827
1051
  const changed = agentRuntime.agent.state.systemPrompt !== result.systemPrompt;
828
1052
  if (changed) {
829
1053
  agentRuntime.agent.state.systemPrompt = result.systemPrompt;
830
1054
  pendingPromptLogEntry = {
1055
+ version: PROMPT_MANIFEST_VERSION,
831
1056
  at: new Date().toISOString(),
832
1057
  previousHash,
833
1058
  systemPromptHash: result.systemPromptHash,
834
1059
  tokenEstimate: result.tokenEstimate,
835
1060
  thinkingLevel: agentRuntime.agent.state.thinkingLevel ?? null,
1061
+ contextWindow,
1062
+ contextWindowSource,
836
1063
  projectPreload: result.projectPreload ?? null,
837
1064
  sections: result.sections.map((s) => ({ id: s.id, tokenEstimate: s.tokenEstimate })),
838
1065
  fragments: result.fragmentManifest.map((f) => ({
@@ -845,6 +1072,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
845
1072
  }
846
1073
  lastSystemPromptReused = !changed;
847
1074
  sessionPrompt = result;
1075
+ sessionPromptHash = result.systemPromptHash;
848
1076
  sessionPromptKey = key;
849
1077
  return result;
850
1078
  } catch (err) {
@@ -877,6 +1105,11 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
877
1105
  tokenEstimate: entry.tokenEstimate,
878
1106
  },
879
1107
  });
1108
+ // A resumed process names the session's last recorded hash as previousHash (#249), so
1109
+ // the stamp requires the hash to have moved, not merely to have a predecessor.
1110
+ if (entry.previousHash !== null && entry.previousHash !== entry.systemPromptHash) {
1111
+ noteColdReason("prompt_recompiled");
1112
+ }
880
1113
  } catch {
881
1114
  // Ledger logging is diagnostics, not control flow; never abort a turn.
882
1115
  }
@@ -966,6 +1199,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
966
1199
  compactionThreshold,
967
1200
  compactionAuto,
968
1201
  promptCache: lastPromptCache,
1202
+ prewarm: lastPrewarm,
969
1203
  });
970
1204
  }
971
1205
 
@@ -998,6 +1232,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
998
1232
  measured,
999
1233
  lastCompaction: lastCompactionEvent,
1000
1234
  promptCache: lastPromptCache,
1235
+ prewarm: lastPrewarm,
1001
1236
  });
1002
1237
  },
1003
1238
 
@@ -1007,12 +1242,14 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1007
1242
  deps.bus?.emit(BusChannels.ContextWarning, { warning } satisfies ContextWarningPayload);
1008
1243
  },
1009
1244
 
1245
+ noteColdReason,
1246
+
1010
1247
  consumeExpectedColdReasons(runtimeId: string): void {
1011
1248
  // Cache-disturbance honesty (T3.3): consume disturbances since
1012
1249
  // the last settled run. Only single-slot local backends lose their
1013
- // prefix cache to interleaved work, so dispatch and compaction
1014
- // stamp only on local-native targets; a working-set eviction moved
1015
- // the prefix itself and stamps on every tier (see stampsOnTier).
1250
+ // prefix cache to interleaved work, so residency, thinking changes,
1251
+ // dispatch, and compaction stamp only on local-native targets. Reasons
1252
+ // that change the prefix bytes stamp every tier (see stampsOnTier).
1016
1253
  runExpectedColdReasons = [];
1017
1254
  nextAssistantColdReasons = [];
1018
1255
  if (pendingColdReasons.size > 0) {
@@ -1026,10 +1263,10 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1026
1263
  }
1027
1264
  },
1028
1265
 
1029
- promptCachePayloadForAssistant(usage: Usage): Record<string, unknown> {
1030
- // Per-call prompt-cache record (T3.2): provider-reported numbers only,
1031
- // classified by backendCacheVerdict in chat-loop-messages.ts. The
1032
- // run's first persisted call also carries any expected-cold reasons.
1266
+ promptCachePayloadForAssistant(usage: Usage, backend?: BackendCompletionTimings): Record<string, unknown> {
1267
+ // Per-call prompt-cache record (T3.2) keeps normalized provider usage
1268
+ // beside any timings captured from the server response. The run's first
1269
+ // persisted call also carries any expected-cold reasons.
1033
1270
  const input = typeof usage.input === "number" ? usage.input : 0;
1034
1271
  const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
1035
1272
  const cacheWrite = typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0;
@@ -1037,8 +1274,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1037
1274
  input,
1038
1275
  cacheRead,
1039
1276
  cacheWrite,
1040
- backendVerdict: backendCacheVerdict(input, cacheRead),
1277
+ backendVerdict: backendCacheVerdict(input, cacheRead, backend),
1041
1278
  };
1279
+ if (backend !== undefined) promptCache.backend = { ...backend };
1042
1280
  if (nextAssistantColdReasons.length > 0) {
1043
1281
  promptCache.expectedColdReasons = [...nextAssistantColdReasons];
1044
1282
  nextAssistantColdReasons = [];
@@ -1046,14 +1284,30 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1046
1284
  return promptCache;
1047
1285
  },
1048
1286
 
1287
+ notePrewarm(prewarm: PrewarmStats): void {
1288
+ lastPrewarm = prewarm;
1289
+ },
1290
+
1049
1291
  noteRunCacheSummary(messages, runFirstCallVerdict): void {
1292
+ // The settled run is the answer to the pre-warm's question, so the
1293
+ // pre-warm line stops being the newest fact about the prefix here.
1294
+ lastPrewarm = null;
1050
1295
  const cacheSummary = sumRunUsage(messages);
1051
1296
  if (cacheSummary.hadUsage) {
1297
+ let lastBackend: BackendCompletionTimings | null = null;
1298
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1299
+ const message = messages[index];
1300
+ if (message?.role !== "assistant") continue;
1301
+ lastBackend = (message as { backendTimings?: BackendCompletionTimings }).backendTimings ?? null;
1302
+ break;
1303
+ }
1052
1304
  lastPromptCache = {
1053
1305
  shellReused: lastSystemPromptReused,
1054
1306
  cacheReadTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheRead : null,
1055
1307
  cacheWriteTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheWrite : null,
1056
1308
  uncachedInputTokens: cacheSummary.input,
1309
+ backend: lastBackend,
1310
+ uncachedPrefillTokens: uncachedPrefillTokens(lastBackend),
1057
1311
  backendVerdict: runFirstCallVerdict,
1058
1312
  ...(runExpectedColdReasons.length > 0 ? { expectedColdReasons: [...runExpectedColdReasons] } : {}),
1059
1313
  };
@@ -1061,12 +1315,26 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1061
1315
  },
1062
1316
 
1063
1317
  resetForSession(): void {
1318
+ // An in-process switch replaces the whole prefix: the backend's slot
1319
+ // still holds the outgoing session's prompt and history, so the first
1320
+ // turn on the incoming one is expected-cold on every tier, exactly as
1321
+ // a recompile is. The manifest can no longer say so on its own once
1322
+ // provenance follows the session, because the incoming session's last
1323
+ // recorded hash usually equals its fresh compile, so the stamp comes
1324
+ // from here. Only when this process had actually applied a prompt:
1325
+ // before the first compile there is nothing in the slot to have lost.
1326
+ if (sessionPromptHash !== null) noteColdReason("prompt_recompiled");
1064
1327
  lastPromptCache = null;
1328
+ lastPrewarm = null;
1065
1329
  lastSystemPromptReused = false;
1330
+ sessionPromptHash = null;
1066
1331
  sessionPromptKey = null;
1332
+ resumedPromptHashRead = false;
1333
+ resumedPromptHash = null;
1067
1334
  sessionWorkingContextPaths.clear();
1068
1335
  pendingPromptLogEntry = null;
1069
1336
  emptyAutoCompactTurnId = null;
1337
+ reconciledAnchor = null;
1070
1338
  const session = deps.session?.current();
1071
1339
  currentContextSnapshot = session ? getLatestContextSnapshot(session) : null;
1072
1340
  },
@@ -5,6 +5,7 @@
5
5
  * append advances `state.lastTurnId`.
6
6
  */
7
7
 
8
+ import type { BackendCompletionTimings } from "../core/cache-telemetry.js";
8
9
  import type { ClioSettings } from "../core/config.js";
9
10
  import type { MiddlewareToolChoiceControl } from "../domains/middleware/index.js";
10
11
  import type { ObservabilityContract } from "../domains/observability/contract.js";
@@ -39,7 +40,7 @@ export interface TurnPersistenceDeps {
39
40
  /** Remove a queued-mirror entry once the engine injects it. */
40
41
  removeQueuedMirrorEntry: (text: string) => void;
41
42
  /** Prompt-cache record for a persisted assistant call (T3.2 + cold stamp). */
42
- promptCachePayloadForAssistant: (usage: Usage) => Record<string, unknown>;
43
+ promptCachePayloadForAssistant: (usage: Usage, backend?: BackendCompletionTimings) => Record<string, unknown>;
43
44
  /**
44
45
  * Prompt-side tokens the live context accounting holds for the turn in
45
46
  * flight. Used only to record what an interrupted call is known to have
@@ -228,7 +229,8 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
228
229
  if (timing) payload.timing = timing;
229
230
  const usage = (message as { usage?: Usage }).usage;
230
231
  if (usage && typeof usage === "object") {
231
- payload.promptCache = deps.promptCachePayloadForAssistant(usage);
232
+ const backendTimings = (message as { backendTimings?: BackendCompletionTimings }).backendTimings;
233
+ payload.promptCache = deps.promptCachePayloadForAssistant(usage, backendTimings);
232
234
  }
233
235
  // A cancelled turn's provider usage is the object the stream started with
234
236
  // and never updated. Record what the call is known to have spent instead of
@@ -278,8 +280,16 @@ export function createTurnPersistence(deps: TurnPersistenceDeps): TurnPersistenc
278
280
 
279
281
  appendQueuedUserTurn(message: AgentMessage): void {
280
282
  if (message?.role !== "user") return;
281
- const text = extractUserText(message).trim();
282
- if (text.length === 0) return;
283
+ // Byte-exact. Trimming here wrote a second, shortened row that the
284
+ // assistant then parented to, so the model's parent turn disagreed with
285
+ // the expansion the operator actually submitted: `/raw bar` produced a
286
+ // correct 5-byte row and a 3-byte one, and the 3-byte one won (issue
287
+ // #244). Only the emptiness test reads the trimmed form, and the
288
+ // echo/mirror lookups match on the same bytes the engine was given, so a
289
+ // payload with edge whitespace is recognized as the echo it is instead
290
+ // of being persisted a second time.
291
+ const text = extractUserText(message);
292
+ if (text.trim().length === 0) return;
283
293
  if (deps.consumePersistedEcho(text)) return;
284
294
  deps.removeQueuedMirrorEntry(text);
285
295
  if (!deps.session) return;