@iowarp/clio-coder 0.3.8 → 0.4.0

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 (552) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/NOTICE +33 -0
  3. package/README.md +12 -3
  4. package/dist/{acp-U67UHUK2.js → acp-G5WJBNCT.js} +13 -12
  5. package/dist/{agents-YU6SGALZ.js → agents-FMV2Q5G4.js} +41 -36
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5ZPJOIVG.js → auth-3IDSJEIK.js} +18 -18
  8. package/dist/{builtins-C6JMZVV6.js → builtins-XCZWXSC7.js} +5 -5
  9. package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
  10. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  11. package/dist/{chunk-XWSF374K.js → chunk-2OQE55CK.js} +3 -3
  12. package/dist/{chunk-5H3GB5BO.js → chunk-32KWKNSF.js} +8 -384
  13. package/dist/{chunk-KTYTFRMB.js → chunk-36EJLSQQ.js} +34 -36
  14. package/dist/{chunk-FHJEP5SW.js → chunk-3BT2XMV4.js} +19 -13
  15. package/dist/chunk-3DPEIQKN.js +113 -0
  16. package/dist/{chunk-GU2UIAFZ.js → chunk-3URVFKWK.js} +7 -7
  17. package/dist/{chunk-WEH5XRJQ.js → chunk-3XML7CDN.js} +3 -3
  18. package/dist/chunk-42FMPA75.js +101 -0
  19. package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
  20. package/dist/{chunk-VCBR6CU7.js → chunk-5PSMVOLM.js} +2 -2
  21. package/dist/{chunk-NMPKI6XL.js → chunk-6DB53AJS.js} +203 -25
  22. package/dist/{chunk-3BINW3FP.js → chunk-76ONBSIA.js} +2 -2
  23. package/dist/{chunk-26LEYJZH.js → chunk-7MCTRUCE.js} +2 -2
  24. package/dist/{chunk-TYPGUK6W.js → chunk-AB44T6BB.js} +111 -5
  25. package/dist/{chunk-K4XHGFR5.js → chunk-B7OBL7PK.js} +317 -721
  26. package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
  27. package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
  28. package/dist/{chunk-ZVJ5BLO2.js → chunk-BKFJHQCA.js} +154 -16
  29. package/dist/chunk-BKFM6EJV.js +462 -0
  30. package/dist/chunk-BMS5RKQY.js +27 -0
  31. package/dist/{chunk-ZNLWCMVZ.js → chunk-BPKCPIL7.js} +2 -2
  32. package/dist/{chunk-TLQJPP24.js → chunk-BUMFYQFY.js} +1394 -1349
  33. package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
  34. package/dist/chunk-C2LTL2W6.js +2447 -0
  35. package/dist/{chunk-ME6CCNFO.js → chunk-CODPRO7Q.js} +8 -8
  36. package/dist/{chunk-E77JEWSD.js → chunk-CTJ4RNAA.js} +7 -37
  37. package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
  38. package/dist/{chunk-7RFXX52T.js → chunk-DQITNCXG.js} +642 -172
  39. package/dist/{chunk-TTHACPOM.js → chunk-DYJP44XW.js} +578 -117
  40. package/dist/{chunk-2HFZQUHL.js → chunk-F4CKPOEQ.js} +18 -8
  41. package/dist/{chunk-DGSYXYMX.js → chunk-FEFIFZTL.js} +3 -3
  42. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  43. package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
  44. package/dist/chunk-GKF55TAZ.js +391 -0
  45. package/dist/{chunk-VWZOAB7K.js → chunk-GR5G2PVF.js} +9 -8
  46. package/dist/{chunk-4DGYLA73.js → chunk-GXNLGKAB.js} +80 -9
  47. package/dist/chunk-HHV2GANA.js +88 -0
  48. package/dist/{chunk-IIZWH4XA.js → chunk-HI63TFOG.js} +5 -4
  49. package/dist/{chunk-WLFILSD5.js → chunk-HJJTYUHX.js} +113 -83
  50. package/dist/{chunk-TT36MB5S.js → chunk-HLW2MRKE.js} +3 -1
  51. package/dist/{chunk-PMDBGQSJ.js → chunk-HWHKMHUA.js} +7 -7
  52. package/dist/chunk-HZHHCK24.js +1631 -0
  53. package/dist/{chunk-WSB3FPX7.js → chunk-I5VEOC6I.js} +39 -143
  54. package/dist/chunk-IBEBSCYA.js +564 -0
  55. package/dist/chunk-IQ7KR472.js +362 -0
  56. package/dist/{chunk-A3WNZD3P.js → chunk-J4W7KFM7.js} +949 -972
  57. package/dist/{chunk-TB5666IT.js → chunk-JDG2WCRO.js} +5 -5
  58. package/dist/{chunk-N22QMJKY.js → chunk-K5C3NCBD.js} +4 -4
  59. package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
  60. package/dist/{chunk-5C3AQNDW.js → chunk-KFZI4NIL.js} +216 -38
  61. package/dist/chunk-KMVISBZR.js +132 -0
  62. package/dist/{chunk-WXY7KU3G.js → chunk-LDQ2ZF2M.js} +2 -2
  63. package/dist/{chunk-XN3L4EYL.js → chunk-LQ3DZAMX.js} +3 -3
  64. package/dist/{chunk-U6MBIEMB.js → chunk-LY4S7GJC.js} +173 -144
  65. package/dist/{chunk-4SPRNWDE.js → chunk-MLKNTWH2.js} +19 -19
  66. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  67. package/dist/chunk-NEKRRTYW.js +56 -0
  68. package/dist/{chunk-SPULKLCF.js → chunk-NHCZP4K7.js} +3 -3
  69. package/dist/chunk-NHLBIGRH.js +1506 -0
  70. package/dist/chunk-NQQH3YT7.js +302 -0
  71. package/dist/chunk-NYS75XW5.js +15 -0
  72. package/dist/{chunk-GN57SG4G.js → chunk-O4XIVISU.js} +10 -8
  73. package/dist/{chunk-EMYUUSFG.js → chunk-O6TL7WWY.js} +6 -6
  74. package/dist/chunk-OQBA45DZ.js +97 -0
  75. package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
  76. package/dist/chunk-PMZCIOCJ.js +25 -0
  77. package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
  78. package/dist/{chunk-J3YUBZWY.js → chunk-QBJA7R7N.js} +62 -6
  79. package/dist/chunk-QDC3K2U3.js +262 -0
  80. package/dist/{chunk-2HEJ2F35.js → chunk-QLFS5GO2.js} +22 -10
  81. package/dist/{chunk-7RGZWPB6.js → chunk-QLL7ILRG.js} +95 -32
  82. package/dist/{chunk-YS5VLNH5.js → chunk-QREDIESB.js} +6 -6
  83. package/dist/chunk-QSNYB6ZV.js +195 -0
  84. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  85. package/dist/chunk-RKKLTLYB.js +45 -0
  86. package/dist/{chunk-P43ETTHK.js → chunk-SJ5ZKQ4S.js} +2 -2
  87. package/dist/{chunk-GPIEI3LY.js → chunk-SP2RXXYO.js} +6 -54
  88. package/dist/chunk-SUCTJL45.js +45 -0
  89. package/dist/{chunk-DYIM5TJT.js → chunk-SUW5DORT.js} +263 -7
  90. package/dist/chunk-T56WDKA5.js +183 -0
  91. package/dist/chunk-TVHHYFHE.js +255 -0
  92. package/dist/{chunk-FJ3H4MN5.js → chunk-TZ3SGWZZ.js} +3 -3
  93. package/dist/{chunk-MXKJU4JB.js → chunk-U77AMWDL.js} +91 -10
  94. package/dist/{chunk-5DHKRSMQ.js → chunk-ULC6OTWO.js} +11 -7
  95. package/dist/{chunk-RWSI4YD7.js → chunk-UM7N4G5A.js} +33 -12
  96. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  97. package/dist/{chunk-IGWKHNIQ.js → chunk-UXMFQ54G.js} +44 -37
  98. package/dist/{chunk-HFSBBKSQ.js → chunk-V5DHCITQ.js} +171 -3
  99. package/dist/{chunk-5WIGXA4T.js → chunk-VAZSBTKF.js} +111 -4
  100. package/dist/{chunk-VHN4MY6O.js → chunk-VEO4AP2K.js} +2 -2
  101. package/dist/{chunk-IJ7RPIYJ.js → chunk-VFA6GDY5.js} +65 -4
  102. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  103. package/dist/chunk-VO67MWHC.js +75 -0
  104. package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
  105. package/dist/{chunk-TANS5ZJS.js → chunk-VYMXRQI6.js} +36 -22
  106. package/dist/{chunk-WWCZ5F23.js → chunk-W5VSYASO.js} +77 -16
  107. package/dist/{chunk-WNIJTQQK.js → chunk-WZR7K7ZX.js} +72 -116
  108. package/dist/{chunk-5Q2VVUKB.js → chunk-X3YGUTOB.js} +4 -4
  109. package/dist/chunk-X75E3D2N.js +686 -0
  110. package/dist/{chunk-VAWNZU7Z.js → chunk-YDFRH54B.js} +4 -4
  111. package/dist/chunk-YJX4SHTD.js +40 -0
  112. package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
  113. package/dist/chunk-Z2RR6MAK.js +127 -0
  114. package/dist/{chunk-FBVTI2TJ.js → chunk-Z4TXYIEG.js} +12 -131
  115. package/dist/cli/index.js +47 -36
  116. package/dist/{clio-QVTYJ57A.js → clio-2JXHBBY5.js} +7 -7
  117. package/dist/{code-nav-FGGFIE7L.js → code-nav-3YYRMYNF.js} +8 -8
  118. package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
  119. package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
  120. package/dist/{config-LW5IJFQN.js → config-QZPCMYSO.js} +99 -63
  121. package/dist/{configure-7XIZCOU4.js → configure-TEGEBYCA.js} +23 -22
  122. package/dist/{context-Y6Y7QPR6.js → context-AV7OEZ4D.js} +12 -12
  123. package/dist/{context-L3WL3X7K.js → context-E6H5RNMC.js} +56 -47
  124. package/dist/{context-N52ZA626.js → context-GSXUE4CT.js} +29 -27
  125. package/dist/{context-clear-MBQRLSDQ.js → context-clear-SHIBYK6T.js} +55 -46
  126. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  127. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-5ZGKPGZQ.js} +13 -13
  128. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-EFMJT4LD.js} +93 -64
  129. package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
  130. package/dist/doctor-QOA5FNY5.js +313 -0
  131. package/dist/{eval-BEC2WHDA.js → eval-TFBYQH4H.js} +2032 -156
  132. package/dist/eval-inventory-SXH7PDKX.js +316 -0
  133. package/dist/{evidence-REJUMSKM.js → evidence-ERGESKGN.js} +203 -48
  134. package/dist/{evolve-PY5ZBA5K.js → evolve-VDXTSYCJ.js} +52 -43
  135. package/dist/{extensions-HVKU65YU.js → extensions-7BGBHN57.js} +13 -7
  136. package/dist/{fleet-7WZEWRFA.js → fleet-2RRVDF2V.js} +228 -108
  137. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-VJ726XIA.js} +11 -11
  138. package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
  139. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-JF5QOATM.js} +18 -15
  140. package/dist/fleet-inspect-VLY4S7QM.js +442 -0
  141. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-AIZUEJOY.js} +5 -5
  142. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-AJRPDMDV.js} +22 -19
  143. package/dist/fleet-verify-JFEL2L3H.js +175 -0
  144. package/dist/fleet-view-ZCON35AG.js +102 -0
  145. package/dist/{init-OG3TPGQG.js → init-DN2WWLFE.js} +72 -62
  146. package/dist/install-XGLBQY5E.js +13 -0
  147. package/dist/interop-OZBKXAYL.js +114 -0
  148. package/dist/{library-CNTMPLRF.js → library-YWZG7IMW.js} +21 -18
  149. package/dist/{memory-6IS7F275.js → memory-I4C4HMLW.js} +54 -45
  150. package/dist/{models-ENRJDA5W.js → models-CEYXJBO6.js} +33 -30
  151. package/dist/{monitor-XLDVO7TN.js → monitor-NZ6GCI3P.js} +59 -52
  152. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-GCGQ4N5I.js} +7827 -7328
  153. package/dist/panes-HMABYVO4.js +58 -0
  154. package/dist/panes-KY6W3V2E.js +103 -0
  155. package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
  156. package/dist/{reset-RZ4ER727.js → reset-DQ6FGCSH.js} +13 -11
  157. package/dist/resources-BB3MVJMD.js +111 -0
  158. package/dist/{run-Y2CNK5RU.js → run-H2GQDUER.js} +119 -87
  159. package/dist/{share-A55GYP6Z.js → share-GTJN6A5O.js} +20 -17
  160. package/dist/{skills-ALC5J6AT.js → skills-L55TEW6R.js} +33 -24
  161. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-XVXPH2JI.js} +67 -56
  162. package/dist/skills-inventory-S4MXPJFV.js +126 -0
  163. package/dist/slash-commands-ZSGASKJC.js +77 -0
  164. package/dist/{steer-GGWFUJUD.js → steer-RZGSCY4R.js} +4 -4
  165. package/dist/{support-MIETYA5E.js → support-PKEUNNQL.js} +6 -6
  166. package/dist/{targets-VGNXIR3S.js → targets-NCPZ644J.js} +68 -40
  167. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-44SV3YCN.js} +6 -4
  168. package/dist/tools-DAF3DI3C.js +27 -0
  169. package/dist/{trace-PNCASAXC.js → trace-FYVW2MQA.js} +207 -10
  170. package/dist/tui-primitives-2AKXQNZK.js +13 -0
  171. package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
  172. package/dist/{upgrade-FUSUAGHR.js → upgrade-3XPP6OQL.js} +27 -24
  173. package/dist/{usage-N4MKVHKD.js → usage-3NLHGTU2.js} +114 -62
  174. package/dist/{verifiers-YAWOJ3H2.js → verifiers-SSQONKRT.js} +172 -13
  175. package/dist/{verify-LTDHYBGY.js → verify-3U6J7FZI.js} +10 -10
  176. package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
  177. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-CEHYGPGQ.js} +75 -65
  178. package/dist/with-panes-MKB46MPQ.js +782 -0
  179. package/dist/worker/entry.js +106 -75
  180. package/docs/README.md +3 -2
  181. package/docs/acp.md +24 -3
  182. package/docs/alcf-provider.md +1 -1
  183. package/docs/architecture.md +2 -2
  184. package/docs/artifact-versions.md +9 -1
  185. package/docs/built-in-agents.md +1 -1
  186. package/docs/capacity-and-scheduling.md +62 -3
  187. package/docs/commands-and-modes.md +31 -2
  188. package/docs/configuration-and-targets.md +69 -12
  189. package/docs/context-engine.md +63 -4
  190. package/docs/development-pipeline.md +19 -0
  191. package/docs/dispatch-typed-intent.md +385 -0
  192. package/docs/documentation-coverage.md +5 -5
  193. package/docs/documentation-guide.md +1 -1
  194. package/docs/environment-variables.md +3 -0
  195. package/docs/eval-runner.md +262 -11
  196. package/docs/evals-internal.md +72 -2
  197. package/docs/evidence-and-memory.md +12 -11
  198. package/docs/evolution.md +1 -1
  199. package/docs/exit-codes-and-output.md +1 -1
  200. package/docs/extensions-and-sharing.md +27 -1
  201. package/docs/fleet-dispatch.md +25 -4
  202. package/docs/installation-and-lifecycle.md +15 -2
  203. package/docs/middleware-and-components.md +1 -1
  204. package/docs/model-catalog.md +10 -1
  205. package/docs/observability.md +55 -4
  206. package/docs/proactive-memory.md +127 -14
  207. package/docs/prompt-envelope-and-tools.md +19 -1
  208. package/docs/provider-adapter-cookbook.md +1 -1
  209. package/docs/release-cut-checklist.md +19 -3
  210. package/docs/safety-model.md +2 -2
  211. package/docs/scientific-validation.md +3 -3
  212. package/docs/session-lifecycle.md +1 -1
  213. package/docs/skills-marketplace.md +1 -1
  214. package/docs/tool-usage.md +18 -8
  215. package/docs/trace-store.md +1 -1
  216. package/docs/troubleshooting.md +88 -1
  217. package/docs/tui-design.md +1 -1
  218. package/docs/worker-dispatch-mechanics.md +1 -1
  219. package/package.json +5 -2
  220. package/src/cli/acp.ts +6 -2
  221. package/src/cli/agents.ts +1 -1
  222. package/src/cli/argv.ts +25 -0
  223. package/src/cli/config-inspect.ts +33 -6
  224. package/src/cli/config.ts +1 -1
  225. package/src/cli/configure.ts +23 -21
  226. package/src/cli/doctor-panes.ts +124 -0
  227. package/src/cli/doctor-state-size.ts +82 -0
  228. package/src/cli/doctor-toolchain.ts +57 -0
  229. package/src/cli/doctor.ts +22 -1
  230. package/src/cli/eval-inventory.ts +436 -0
  231. package/src/cli/eval.ts +93 -16
  232. package/src/cli/evidence-detail.ts +88 -0
  233. package/src/cli/evidence-inventory.ts +183 -0
  234. package/src/cli/evidence.ts +30 -5
  235. package/src/cli/extensions.ts +5 -1
  236. package/src/cli/fleet-decisions.ts +69 -0
  237. package/src/cli/fleet-inspect.ts +334 -0
  238. package/src/cli/fleet-verify.ts +133 -0
  239. package/src/cli/fleet-view.ts +810 -0
  240. package/src/cli/fleet.ts +179 -39
  241. package/src/cli/index.ts +14 -2
  242. package/src/cli/interop-inspect.ts +128 -0
  243. package/src/cli/interop.ts +34 -0
  244. package/src/cli/panes.ts +35 -0
  245. package/src/cli/reset.ts +5 -2
  246. package/src/cli/run.ts +58 -0
  247. package/src/cli/skills-inventory.ts +185 -0
  248. package/src/cli/skills.ts +16 -13
  249. package/src/cli/targets.ts +44 -13
  250. package/src/cli/tools.ts +321 -0
  251. package/src/cli/trace-inspect.ts +252 -0
  252. package/src/cli/trace.ts +85 -5
  253. package/src/cli/usage.ts +63 -14
  254. package/src/cli/verifiers-inspect.ts +347 -0
  255. package/src/cli/verifiers.ts +9 -0
  256. package/src/core/bus-events.ts +33 -1
  257. package/src/core/cache-telemetry.ts +42 -0
  258. package/src/core/config.ts +67 -0
  259. package/src/core/defaults.ts +124 -8
  260. package/src/core/endpoint-key.ts +27 -0
  261. package/src/core/residency-target-key.ts +25 -0
  262. package/src/core/response-schema.ts +80 -6
  263. package/src/core/theme-token-hex.ts +43 -0
  264. package/src/core/tool-names.ts +2 -1
  265. package/src/core/xdg.ts +1 -1
  266. package/src/domains/agents/fleets/build-review.md +0 -3
  267. package/src/domains/agents/fleets/build-test.md +0 -3
  268. package/src/domains/agents/result-contract-filesystem.ts +32 -0
  269. package/src/domains/agents/result-contract.ts +164 -35
  270. package/src/domains/config/classify.ts +6 -0
  271. package/src/domains/context/codewiki/coordinator.ts +12 -4
  272. package/src/domains/dispatch/admission-error.ts +9 -0
  273. package/src/domains/dispatch/admission.ts +52 -14
  274. package/src/domains/dispatch/capacity-lease.ts +118 -9
  275. package/src/domains/dispatch/contract.ts +11 -0
  276. package/src/domains/dispatch/council-topology.ts +398 -0
  277. package/src/domains/dispatch/execution-plan.ts +44 -4
  278. package/src/domains/dispatch/extension.ts +378 -82
  279. package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
  280. package/src/domains/dispatch/fleet-plan.ts +7 -2
  281. package/src/domains/dispatch/fleet-run.ts +87 -4
  282. package/src/domains/dispatch/gate-decisions.ts +11 -1
  283. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  284. package/src/domains/dispatch/gate-topology.ts +289 -0
  285. package/src/domains/dispatch/heartbeat.ts +32 -8
  286. package/src/domains/dispatch/index.ts +22 -0
  287. package/src/domains/dispatch/intent-compatibility.ts +330 -0
  288. package/src/domains/dispatch/intent.ts +85 -1
  289. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  290. package/src/domains/dispatch/reservation-store.ts +139 -11
  291. package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
  292. package/src/domains/dispatch/run-event-journal.ts +598 -0
  293. package/src/domains/dispatch/state.ts +49 -1
  294. package/src/domains/dispatch/types.ts +13 -0
  295. package/src/domains/dispatch/validation.ts +33 -8
  296. package/src/domains/dispatch/worker-spawn.ts +25 -11
  297. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  298. package/src/domains/dispatch/write-boundary.ts +62 -1
  299. package/src/domains/eval/artifacts/store.ts +62 -0
  300. package/src/domains/eval/compare/behavioral.ts +224 -0
  301. package/src/domains/eval/compare/compare.ts +342 -2
  302. package/src/domains/eval/compare/envelope.ts +128 -0
  303. package/src/domains/eval/compare/gates.ts +24 -6
  304. package/src/domains/eval/compare/thresholds.ts +30 -3
  305. package/src/domains/eval/execution-provenance.ts +240 -0
  306. package/src/domains/eval/inventory.ts +113 -0
  307. package/src/domains/eval/metrics/aggregate.ts +136 -0
  308. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  309. package/src/domains/eval/metrics/tracked.ts +413 -0
  310. package/src/domains/eval/provenance.ts +117 -0
  311. package/src/domains/eval/reports/comparison.ts +128 -0
  312. package/src/domains/eval/reports/junit.ts +17 -3
  313. package/src/domains/eval/reports/markdown.ts +3 -3
  314. package/src/domains/eval/reports/text.ts +14 -0
  315. package/src/domains/eval/run-compare.ts +20 -0
  316. package/src/domains/eval/runners/clio-run.ts +127 -0
  317. package/src/domains/eval/runners/external-command.ts +28 -3
  318. package/src/domains/eval/schema/adapter.ts +111 -0
  319. package/src/domains/eval/schema/artifact.ts +20 -0
  320. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  321. package/src/domains/eval/schema/behavioral.ts +520 -0
  322. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  323. package/src/domains/eval/schema/serving.ts +105 -0
  324. package/src/domains/eval/schema/suite.ts +38 -8
  325. package/src/domains/eval/schema/validate.ts +58 -3
  326. package/src/domains/eval/schema/verdict.ts +237 -0
  327. package/src/domains/eval/suites/resolve.ts +2 -0
  328. package/src/domains/eval/suites/run.ts +264 -33
  329. package/src/domains/eval/verifiers/command.ts +2 -1
  330. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  331. package/src/domains/evidence/build.ts +2 -13
  332. package/src/domains/evidence/eval.ts +2 -12
  333. package/src/domains/evidence/findings-markdown.ts +33 -0
  334. package/src/domains/evidence/run-trust.ts +7 -113
  335. package/src/domains/evidence/store.ts +6 -0
  336. package/src/domains/evidence/trust-projection.ts +2 -2
  337. package/src/domains/extensions/compatibility.ts +285 -0
  338. package/src/domains/extensions/discovery.ts +38 -3
  339. package/src/domains/extensions/resources.ts +1 -1
  340. package/src/domains/extensions/state.ts +12 -3
  341. package/src/domains/extensions/types.ts +2 -0
  342. package/src/domains/lifecycle/doctor.ts +69 -1
  343. package/src/domains/memory/index.ts +14 -1
  344. package/src/domains/memory/task-bank-promotion.ts +64 -0
  345. package/src/domains/memory/task-memory-policy.ts +82 -17
  346. package/src/domains/memory/task-memory-spend.ts +131 -0
  347. package/src/domains/memory/task-memory-status.ts +7 -0
  348. package/src/domains/memory/task-memory-telemetry.ts +3 -0
  349. package/src/domains/middleware/index.ts +1 -0
  350. package/src/domains/middleware/memory-intervention.ts +97 -21
  351. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  352. package/src/domains/mux/contract.ts +434 -0
  353. package/src/domains/mux/detect.ts +158 -0
  354. package/src/domains/mux/extension.ts +47 -0
  355. package/src/domains/mux/index.ts +96 -0
  356. package/src/domains/mux/manifest.ts +6 -0
  357. package/src/domains/mux/operations.ts +164 -0
  358. package/src/domains/mux/pane-registry.ts +90 -0
  359. package/src/domains/mux/protocol.ts +49 -0
  360. package/src/domains/mux/socket-client.ts +816 -0
  361. package/src/domains/mux/types.ts +222 -0
  362. package/src/domains/mux/viewer-command.ts +59 -0
  363. package/src/domains/mux/yazi/assets/init.lua +2 -0
  364. package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
  365. package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
  366. package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
  367. package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
  368. package/src/domains/mux/yazi/assets/yazi.toml +17 -0
  369. package/src/domains/mux/yazi/event-stream.ts +180 -0
  370. package/src/domains/mux/yazi/profile.ts +299 -0
  371. package/src/domains/mux/yazi/session.ts +228 -0
  372. package/src/domains/mux/yazi/theme.ts +30 -0
  373. package/src/domains/observability/background-memory-usage.ts +140 -0
  374. package/src/domains/observability/cost.ts +22 -1
  375. package/src/domains/observability/index.ts +9 -0
  376. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  377. package/src/domains/observability/trace-store.ts +234 -2
  378. package/src/domains/prompts/compiler.ts +100 -13
  379. package/src/domains/providers/endpoint-capacity.ts +228 -0
  380. package/src/domains/providers/endpoint-slots-store.ts +189 -0
  381. package/src/domains/providers/extension.ts +20 -3
  382. package/src/domains/providers/index.ts +32 -0
  383. package/src/domains/providers/model-runtime-capabilities.ts +32 -0
  384. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  385. package/src/domains/providers/runtime-resolution.ts +8 -1
  386. package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
  387. package/src/domains/providers/runtimes/builtins.ts +2 -0
  388. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  389. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  390. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  391. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  392. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  393. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  394. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  395. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  396. package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
  397. package/src/domains/providers/support.ts +1 -0
  398. package/src/domains/providers/target-model-cache.ts +124 -0
  399. package/src/domains/providers/types/capability-flags.ts +2 -0
  400. package/src/domains/providers/types/target-descriptor.ts +2 -0
  401. package/src/domains/resources/index.ts +3 -0
  402. package/src/domains/resources/prompts/loader.ts +95 -33
  403. package/src/domains/resources/skills/loader.ts +33 -0
  404. package/src/domains/safety/action-classifier.ts +6 -0
  405. package/src/domains/safety/call-target.ts +52 -0
  406. package/src/domains/safety/run-effects.ts +35 -4
  407. package/src/domains/session/context-accounting.ts +52 -1
  408. package/src/domains/session/context-ledger.ts +37 -13
  409. package/src/domains/session/index.ts +6 -0
  410. package/src/domains/session/prompt-cache.ts +140 -0
  411. package/src/domains/session/prompt-manifest.ts +42 -0
  412. package/src/domains/toolchain/archive.ts +175 -0
  413. package/src/domains/toolchain/contract.ts +28 -0
  414. package/src/domains/toolchain/extension.ts +47 -0
  415. package/src/domains/toolchain/index.ts +39 -0
  416. package/src/domains/toolchain/install.ts +327 -0
  417. package/src/domains/toolchain/manifest.ts +8 -0
  418. package/src/domains/toolchain/paths.ts +34 -0
  419. package/src/domains/toolchain/registry.ts +265 -0
  420. package/src/domains/toolchain/remove.ts +218 -0
  421. package/src/domains/toolchain/resolve.ts +182 -0
  422. package/src/domains/toolchain/types.ts +113 -0
  423. package/src/domains/toolchain/version.ts +88 -0
  424. package/src/engine/acp/adapter.ts +18 -3
  425. package/src/engine/acp/server.ts +413 -70
  426. package/src/engine/acp/types.ts +19 -1
  427. package/src/engine/ai.ts +35 -0
  428. package/src/engine/apis/llamacpp-residency.ts +55 -3
  429. package/src/engine/apis/lmstudio.ts +25 -5
  430. package/src/engine/apis/ollama-native.ts +2 -1
  431. package/src/engine/apis/openai-completions.ts +80 -17
  432. package/src/engine/apis/residency-lock.ts +3 -1
  433. package/src/engine/apis/residency.ts +34 -1
  434. package/src/engine/claude/sdk-module.ts +98 -0
  435. package/src/engine/claude/sdk-runtime.ts +19 -11
  436. package/src/engine/provider-payload.ts +29 -1
  437. package/src/engine/tui-primitives.ts +21 -0
  438. package/src/engine/tui.ts +1 -0
  439. package/src/engine/worker-runtime.ts +2 -13
  440. package/src/entry/boot-options.ts +2 -0
  441. package/src/entry/orchestrator.ts +279 -35
  442. package/src/entry/panes-activation.ts +31 -0
  443. package/src/entry/with-panes.ts +20 -0
  444. package/src/interactive/chat-loop-messages.ts +26 -7
  445. package/src/interactive/chat-loop.ts +318 -41
  446. package/src/interactive/chat-panel.ts +62 -8
  447. package/src/interactive/clio-editor.ts +45 -8
  448. package/src/interactive/context-activity.ts +5 -1
  449. package/src/interactive/context-meter.ts +1 -1
  450. package/src/interactive/context-overlay.ts +41 -10
  451. package/src/interactive/cost-overlay.ts +66 -6
  452. package/src/interactive/council-grid.ts +1 -3
  453. package/src/interactive/council.ts +11 -0
  454. package/src/interactive/dispatch-board.ts +102 -20
  455. package/src/interactive/fleet-run-preview.ts +41 -15
  456. package/src/interactive/handoff-round.ts +41 -2
  457. package/src/interactive/interactive-application.ts +177 -7
  458. package/src/interactive/interactive-input-runtime.ts +15 -0
  459. package/src/interactive/interactive-presentation.ts +4 -0
  460. package/src/interactive/interactive-shell.ts +20 -17
  461. package/src/interactive/interactive-slash-runtime.ts +63 -10
  462. package/src/interactive/memory-overlay.ts +9 -0
  463. package/src/interactive/modal-marker.ts +170 -0
  464. package/src/interactive/mutation-preview.ts +295 -0
  465. package/src/interactive/mux-bridge.ts +214 -0
  466. package/src/interactive/overlay-frame.ts +58 -2
  467. package/src/interactive/overlay-general-openers.ts +17 -0
  468. package/src/interactive/overlay-key-routing.ts +52 -3
  469. package/src/interactive/overlay-lifecycle.ts +56 -7
  470. package/src/interactive/overlay-model-selectors.ts +40 -3
  471. package/src/interactive/overlay-permission-lifecycle.ts +112 -24
  472. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  473. package/src/interactive/overlay-transitions.ts +18 -4
  474. package/src/interactive/overlays/agents.ts +1 -0
  475. package/src/interactive/overlays/ask-user.ts +227 -49
  476. package/src/interactive/overlays/auth-dialog.ts +1 -0
  477. package/src/interactive/overlays/context-reset.ts +1 -0
  478. package/src/interactive/overlays/cwd-fallback.ts +1 -0
  479. package/src/interactive/overlays/decisions.ts +11 -11
  480. package/src/interactive/overlays/extensions.ts +1 -0
  481. package/src/interactive/overlays/fleet-run-approval.ts +1 -0
  482. package/src/interactive/overlays/handoff-review.ts +1 -0
  483. package/src/interactive/overlays/help-reference.ts +6 -0
  484. package/src/interactive/overlays/interop.ts +1 -0
  485. package/src/interactive/overlays/library-install-confirm.ts +1 -0
  486. package/src/interactive/overlays/library-tabs.ts +28 -0
  487. package/src/interactive/overlays/list-overlay.ts +10 -1
  488. package/src/interactive/overlays/message-picker.ts +1 -0
  489. package/src/interactive/overlays/model-scope.ts +86 -0
  490. package/src/interactive/overlays/model-selector.ts +1 -0
  491. package/src/interactive/overlays/prompts.ts +12 -1
  492. package/src/interactive/overlays/session-selector.ts +1 -0
  493. package/src/interactive/overlays/settings-sections.ts +30 -0
  494. package/src/interactive/overlays/settings.ts +614 -45
  495. package/src/interactive/overlays/side-question.ts +1 -0
  496. package/src/interactive/overlays/skills-hub.ts +3 -11
  497. package/src/interactive/overlays/tree-selector.ts +1 -0
  498. package/src/interactive/pane-policy.ts +46 -0
  499. package/src/interactive/panes-runtime.ts +292 -0
  500. package/src/interactive/permission-hint.ts +34 -2
  501. package/src/interactive/permission-overlay.ts +159 -9
  502. package/src/interactive/prewarm.ts +197 -0
  503. package/src/interactive/render-trace.ts +162 -15
  504. package/src/interactive/renderers/compaction-summary.ts +29 -0
  505. package/src/interactive/renderers/tool-execution.ts +4 -0
  506. package/src/interactive/renderers/worker-entry.ts +122 -14
  507. package/src/interactive/side-question.ts +58 -1
  508. package/src/interactive/slash-commands.ts +251 -15
  509. package/src/interactive/status/controller.ts +11 -0
  510. package/src/interactive/status/state-machine.ts +54 -2
  511. package/src/interactive/status/types.ts +7 -0
  512. package/src/interactive/tasks-overlay.ts +1 -0
  513. package/src/interactive/terminal-lease.ts +2 -0
  514. package/src/interactive/theme/tokens.ts +3 -14
  515. package/src/interactive/turn-context.ts +346 -33
  516. package/src/interactive/turn-persistence.ts +14 -4
  517. package/src/interactive/turn-prewarm.ts +364 -0
  518. package/src/interactive/turn-queues.ts +7 -4
  519. package/src/interactive/turn-runtime.ts +8 -1
  520. package/src/interactive/turn-state.ts +23 -0
  521. package/src/interactive/view/artifacts.ts +109 -1
  522. package/src/interactive/view/view-overlay.ts +29 -3
  523. package/src/interactive/watch-pane.ts +152 -0
  524. package/src/interactive/worker-progress.ts +7 -1
  525. package/src/interactive/worker-receipts.ts +19 -1
  526. package/src/interactive/worker-stream.ts +5 -0
  527. package/src/interactive/yazi-bridge.ts +444 -0
  528. package/src/tools/ask-user.ts +43 -2
  529. package/src/tools/bootstrap.ts +26 -2
  530. package/src/tools/builtin-tool-catalog.ts +15 -0
  531. package/src/tools/compete-worktrees.ts +83 -2
  532. package/src/tools/core-bootstrap.ts +2 -1
  533. package/src/tools/dispatch-admission.ts +14 -3
  534. package/src/tools/dispatch-arguments.ts +20 -20
  535. package/src/tools/dispatch-plan.ts +17 -9
  536. package/src/tools/dispatch-run-events.ts +134 -19
  537. package/src/tools/dispatch-runner.ts +29 -7
  538. package/src/tools/dispatch-scout.ts +1 -1
  539. package/src/tools/dispatch-types.ts +15 -3
  540. package/src/tools/dispatch.ts +1 -1
  541. package/src/tools/executables.ts +17 -14
  542. package/src/tools/observation.ts +54 -4
  543. package/src/tools/panes-surface.ts +38 -0
  544. package/src/tools/panes.ts +112 -0
  545. package/src/tools/policy.ts +10 -1
  546. package/src/tools/presentation.ts +1 -0
  547. package/src/tools/registry.ts +16 -0
  548. package/dist/chunk-AOCYTWAV.js +0 -449
  549. package/dist/chunk-HLE42MG7.js +0 -37
  550. package/dist/chunk-HWUFFB6L.js +0 -83
  551. package/dist/chunk-JOZYP4GM.js +0 -279
  552. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -12,12 +12,21 @@ 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";
29
+ import { isTurnStart } from "../domains/context/working-set/horizon.js";
21
30
  import { resolveWorkingSetPolicy } from "../domains/context/working-set/policies/index.js";
22
31
  import { selectVisibleEntries } from "../domains/context/working-set/visible.js";
23
32
  import type { ObservabilityContract } from "../domains/observability/contract.js";
@@ -26,6 +35,7 @@ import type { PromptsContract } from "../domains/prompts/contract.js";
26
35
  import {
27
36
  type ContextWindowDetails,
28
37
  type ContextWindowSource,
38
+ canonicalEndpointKey,
29
39
  type ProvidersContract,
30
40
  resolveRuntimeTarget,
31
41
  } from "../domains/providers/index.js";
@@ -50,18 +60,29 @@ import {
50
60
  contextUsageSnapshot,
51
61
  estimateAgentContextBreakdown,
52
62
  estimateAgentContextTokens,
63
+ estimateAgentMessageTokens,
53
64
  getLatestContextSnapshot,
65
+ lastLoadedContextWindow,
54
66
  reconcileSnapshot,
55
67
  snapshotInputTokens,
56
68
  } from "../domains/session/context-accounting.js";
57
- import { buildContextLedger, type ContextLedger, type PromptCacheStats } from "../domains/session/context-ledger.js";
69
+ import {
70
+ buildContextLedger,
71
+ type ContextLedger,
72
+ type PrewarmStats,
73
+ type PromptCacheStats,
74
+ } from "../domains/session/context-ledger.js";
58
75
  import type { SessionContract } from "../domains/session/contract.js";
59
76
  import type { CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
60
- import { appendPromptCompileRecord, type SessionPromptCompileRecord } from "../domains/session/prompt-manifest.js";
77
+ import {
78
+ appendPromptCompileRecord,
79
+ PROMPT_MANIFEST_VERSION,
80
+ readPromptCompileRecords,
81
+ type SessionPromptCompileRecord,
82
+ } from "../domains/session/prompt-manifest.js";
61
83
  import type { AgentMessage, Usage } from "../engine/types.js";
62
84
  import type { ToolRegistry } from "../tools/registry.js";
63
85
  import {
64
- type BackendCacheVerdict,
65
86
  backendCacheVerdict,
66
87
  extractUserText,
67
88
  runtimeSupportsTools,
@@ -69,7 +90,7 @@ import {
69
90
  toolNamesFromAgentState,
70
91
  } from "./chat-loop-messages.js";
71
92
  import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
72
- import { renderCompactionSummaryLine } from "./renderers/compaction-summary.js";
93
+ import { renderCompactionSummaryLine, renderEvictionSkipLine } from "./renderers/compaction-summary.js";
73
94
  import type { TurnMiddleware } from "./turn-middleware.js";
74
95
  import type { AgentRuntime, ChatTurnState } from "./turn-state.js";
75
96
 
@@ -92,11 +113,43 @@ export interface TurnContextDeps {
92
113
  }
93
114
 
94
115
  export interface LiveContextEstimate {
116
+ /**
117
+ * The figure to budget against: the reconciled total when the provider has
118
+ * attested one for this conversation, otherwise the estimate. Never below
119
+ * the estimate, because the estimate covers material the last provider call
120
+ * did not see.
121
+ */
95
122
  tokens: number;
123
+ /** Pure chars/4 projection, with the pre-existing provider-usage anchor. */
124
+ estimatedTokens: number;
125
+ /**
126
+ * Provider-attested prompt tokens for the messages up to the last reconciled
127
+ * call, plus a chars/4 estimate of everything appended since. Null before the
128
+ * first reconcile of a session and after a summary compaction rewrites the
129
+ * history the attestation described.
130
+ */
131
+ reconciledTokens: number | null;
96
132
  contextWindow: number;
97
133
  breakdown: ReturnType<typeof estimateAgentContextBreakdown>;
98
134
  }
99
135
 
136
+ /** Known causes that make the next provider prefix cache miss expected. */
137
+ export type ExpectedColdReason =
138
+ | "dispatch"
139
+ | "compaction"
140
+ | "working_set_evict"
141
+ | "residency"
142
+ | "thinking_change"
143
+ | "tool_surface_change"
144
+ | "prompt_recompiled"
145
+ /**
146
+ * A proactive-memory step ran between turns on the endpoint this session
147
+ * streams against. Its prompt is a trajectory rather than the chat prefix, so
148
+ * on a local server that keeps one prefix cache the next turn re-prefills
149
+ * (#229).
150
+ */
151
+ | "background_memory";
152
+
100
153
  export interface TurnContext {
101
154
  ensureSessionPrompt(agentRuntime: AgentRuntime): Promise<CompiledSessionPrompt | null>;
102
155
  logPromptCompileIfPending(): void;
@@ -120,6 +173,12 @@ export interface TurnContext {
120
173
  */
121
174
  promptSideTokens(): number;
122
175
  liveContextEstimate(agentRuntime: AgentRuntime, pendingUserText?: string): LiveContextEstimate;
176
+ /**
177
+ * The loaded context window this session already recorded for a target and
178
+ * model, so a resume budgets against it instead of re-probing. Null when the
179
+ * ledger has no such measurement.
180
+ */
181
+ rememberedLoadedContextWindow(targetId: string, modelId: string): number | null;
123
182
  refreshAgentMessagesFromSession(agentRuntime: AgentRuntime): ReadonlyArray<SessionEntry>;
124
183
  runAutoCompact(
125
184
  agentRuntime: AgentRuntime,
@@ -142,27 +201,74 @@ export interface TurnContext {
142
201
  contextUsage(): ContextUsageSnapshot;
143
202
  contextLedger(): ContextLedger;
144
203
  emitContextWindowWarningTransition(warning: string | null): void;
204
+ /** Record one known cache disturbance for the current or next call. */
205
+ noteColdReason(reason: ExpectedColdReason): void;
145
206
  /** Consume disturbances since the last settled run (T3.3 honesty). */
146
207
  consumeExpectedColdReasons(runtimeId: string): void;
147
208
  /** Prompt-cache record for one persisted assistant call, with cold-reason stamp. */
148
- promptCachePayloadForAssistant(usage: Usage): Record<string, unknown>;
209
+ promptCachePayloadForAssistant(usage: Usage, backend?: BackendCompletionTimings): Record<string, unknown>;
149
210
  /** Record the settled run's cache summary for /context. */
150
211
  noteRunCacheSummary(messages: ReadonlyArray<AgentMessage>, runFirstCallVerdict: BackendCacheVerdict | null): void;
212
+ /**
213
+ * Record one pre-warm for `/context`. It contributes no tokens to the context
214
+ * estimate and is never an expected-cold reason: a pre-warm is the opposite
215
+ * of a disturbance, it is the prefix the next turn wants already resident.
216
+ */
217
+ notePrewarm(prewarm: PrewarmStats): void;
151
218
  resetForSession(): void;
152
219
  dispose(): void;
153
220
  }
154
221
 
222
+ /**
223
+ * Why the working-set stage declined, said in the terms the operator can act
224
+ * on. The two cases are worth separating: a session whose every turn is inside
225
+ * `protectLastTurns` was never offered a candidate, while a longer one was
226
+ * offered candidates the policy refused. The first is the shape smoke pass 2
227
+ * found (G1), and the setting it names is the one that changes it.
228
+ */
229
+ function evictionSkipMessage(
230
+ visibleEntries: ReadonlyArray<SessionEntry>,
231
+ workingSet: Readonly<{ protectLastTurns: number; policy: string }>,
232
+ policyId: string,
233
+ ): string {
234
+ const turns = visibleEntries.filter(isTurnStart).length;
235
+ return renderEvictionSkipLine({
236
+ reason: turns <= workingSet.protectLastTurns ? "all-protected" : "nothing-evictable",
237
+ turns,
238
+ protectLastTurns: workingSet.protectLastTurns,
239
+ policyId,
240
+ });
241
+ }
242
+
155
243
  export function createTurnContext(deps: TurnContextDeps): TurnContext {
156
244
  const { state, middleware } = deps;
157
245
  const compactionTrigger = new AutoCompactionTrigger<CompactResult | null>();
158
246
 
159
247
  let currentContextSnapshot: ContextSnapshot | null = null;
248
+ /**
249
+ * The last provider-attested prompt size, and how many agent messages it
250
+ * covered (issue #227).
251
+ *
252
+ * `tokens` is the provider's own prompt count for that call plus the output
253
+ * it produced, which together are what the next call's prompt carries for
254
+ * the same messages. `anchoredMessageCount` is the length of the live
255
+ * message list at that moment, so anything appended since is priced by
256
+ * estimate and added on top. Unlike the per-message usage anchor inside
257
+ * `estimateAgentContextTokens`, this survives `contextUsageInvalidated`:
258
+ * a working-set projection subtracts the tokens it removed rather than
259
+ * throwing the attestation away.
260
+ */
261
+ let reconciledAnchor: { tokens: number; anchoredMessageCount: number } | null = null;
160
262
  let lastCompactionEvent: { stage: string; tokensBefore: number; tokensAfter: number; trigger: string } | null = null;
161
263
  // Last settled run's provider cache usage plus whether the compiled system
162
264
  // prompt was reused. Shown together in /context so "prompt reused" can
163
265
  // never imply provider cache reuse the backend did not report.
164
266
  let lastPromptCache: PromptCacheStats | null = null;
165
267
  let lastSystemPromptReused = false;
268
+ // The last pre-warm, shown by /context until the next settled run answers the
269
+ // question it was asked about: did the prefix the next turn needs get there
270
+ // before the operator did.
271
+ let lastPrewarm: PrewarmStats | null = null;
166
272
  // The session system prompt, compiled once per session. Recompiles happen
167
273
  // only on explicit events: the compile key (target, model, safety level,
168
274
  // session id) changes, or a config hot-reload invalidates the cache. A
@@ -170,6 +276,19 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
170
276
  // ledger entry so a cold provider cache is always explainable.
171
277
  let sessionPrompt: CompiledSessionPrompt | null = null;
172
278
  let sessionPromptKey: string | null = null;
279
+ // The hash this process compiled *for the session that is current now*, and
280
+ // the manifest's `previousHash` whenever it is set. It is not read off
281
+ // `sessionPrompt`: an in-process `/resume` leaves that compile in place so
282
+ // the ledger keeps its project-preload labels until the next compile, but
283
+ // manifest provenance follows the session, so a switch drops the hash here
284
+ // and the next compile falls through to the incoming session's own manifest
285
+ // exactly as a cold-start resume does (#249).
286
+ let sessionPromptHash: string | null = null;
287
+ // Whether this process has already looked up the resumed session's last
288
+ // recorded prompt hash. The lookup reads the manifest file, so it happens
289
+ // once, on the first compile, and never on the reuse path.
290
+ let resumedPromptHashRead = false;
291
+ let resumedPromptHash: string | null = null;
173
292
  const sessionWorkingContextPaths = new Set<string>();
174
293
  let pendingPromptLogEntry: SessionPromptCompileRecord | null = null;
175
294
  // A post-tool guard can run after every tool result in one model turn. Once
@@ -178,22 +297,25 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
178
297
  // and summary probes. A new submitted user turn gets a new id naturally.
179
298
  let emptyAutoCompactTurnId: string | null = null;
180
299
 
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 =>
300
+ // Cache-disturbance honesty (T3.3). Accumulate every known local-runtime
301
+ // disturbance and prefix-byte change since the last settled run. The next
302
+ // submit consumes the set, stamps `promptCache.expectedColdReasons` on its
303
+ // first assistant entry, and shows one dim notice.
304
+ const pendingColdReasons = new Set<ExpectedColdReason>();
305
+ let runExpectedColdReasons: ExpectedColdReason[] = [];
306
+ let nextAssistantColdReasons: ExpectedColdReason[] = [];
307
+ // Prefix-byte changes cool every tier. Residency, thinking changes, dispatch
308
+ // traffic, and compaction keep the local-native gate because they disturb a
309
+ // local server or its rendered template.
310
+ const TIER_INDEPENDENT_COLD_REASONS: ReadonlySet<ExpectedColdReason> = new Set([
311
+ "working_set_evict",
312
+ "tool_surface_change",
313
+ "prompt_recompiled",
314
+ ]);
315
+ const stampsOnTier = (reason: ExpectedColdReason, runtimeId: string | undefined): boolean =>
194
316
  TIER_INDEPENDENT_COLD_REASONS.has(reason) ||
195
317
  (runtimeId !== undefined && deps.providers.getRuntime(runtimeId)?.tier === "local-native");
196
- const noteColdReason = (reason: string): void => {
318
+ const noteColdReason = (reason: ExpectedColdReason): void => {
197
319
  if (!state.streaming) {
198
320
  pendingColdReasons.add(reason);
199
321
  return;
@@ -217,6 +339,21 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
217
339
  noteColdReason("compaction");
218
340
  }) ?? null,
219
341
  ),
342
+ deps.bus?.on(BusChannels.ResidencyMutation, (payload: ResidencyMutationPayload) => {
343
+ const runtime = state.runtime;
344
+ const model = runtime?.agent.state.model as { baseUrl?: unknown } | undefined;
345
+ const baseUrl = typeof model?.baseUrl === "string" ? model.baseUrl : null;
346
+ if (payload.targetKey !== residencyTargetKey(runtime?.runtimeId ?? "", baseUrl)) return;
347
+ noteColdReason("residency");
348
+ }) ?? null,
349
+ // A memory step on another endpoint disturbs nothing here, so the stamp is
350
+ // gated on the step having called the very server this session streams to.
351
+ deps.bus?.on(BusChannels.MemoryStepCompleted, (payload: MemoryStepCompletedPayload) => {
352
+ const target = state.runtime?.runtimeResolution.target;
353
+ if (target === undefined) return;
354
+ if (canonicalEndpointKey(target) !== payload.endpointKey) return;
355
+ noteColdReason("background_memory");
356
+ }) ?? null,
220
357
  deps.bus?.on(BusChannels.ContextRecalled, (payload: ContextRecalledPayload) => {
221
358
  middleware.fireCompactionHook("working_set_recall", payload.trigger);
222
359
  }) ?? null,
@@ -322,6 +459,33 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
322
459
  * (issue #189). Null when no target is configured or it does not resolve,
323
460
  * and the overlay's "unknown" is then true.
324
461
  */
462
+ const rememberedLoadedContextWindow = (targetId: string, modelId: string): number | null => {
463
+ const session = deps.session?.current();
464
+ if (!session) return null;
465
+ return lastLoadedContextWindow(session, targetId, modelId);
466
+ };
467
+
468
+ /**
469
+ * The prompt hash this session last recorded, for the first compile of a
470
+ * resumed process. Without it a resume reports `previousHash: null`, which
471
+ * reads as "there was no prompt before" when in fact there was one and it
472
+ * is exactly what a reader needs to attribute the cold cache to: a layout
473
+ * bump, a fragment edit, or a moved context window (issue #249).
474
+ */
475
+ const lastRecordedPromptHash = (): string | null => {
476
+ if (resumedPromptHashRead) return resumedPromptHash;
477
+ resumedPromptHashRead = true;
478
+ const currentSession = deps.session?.current();
479
+ if (!currentSession) {
480
+ // No session to read; leave the lookup open for the next compile.
481
+ resumedPromptHashRead = false;
482
+ return null;
483
+ }
484
+ const records = readPromptCompileRecords(currentSession);
485
+ resumedPromptHash = records[records.length - 1]?.systemPromptHash ?? null;
486
+ return resumedPromptHash;
487
+ };
488
+
325
489
  const resolveWindowWithoutRuntime = (): ContextWindowDetails | null => {
326
490
  const settings = deps.getSettings();
327
491
  const targetId = settings.orchestrator?.target?.trim();
@@ -334,6 +498,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
334
498
  use: "orchestrator",
335
499
  requireTools: false,
336
500
  requireOutputBudget: true,
501
+ knownLoadedContextWindow: rememberedLoadedContextWindow(targetId, wireModelId),
337
502
  });
338
503
  return resolved.ok ? resolved.target.contextWindowDetails : null;
339
504
  };
@@ -383,6 +548,29 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
383
548
  return { contextWindow: 0, contextWindowSource: null, contextWindowSlots: null };
384
549
  };
385
550
 
551
+ /**
552
+ * Carry the reconciled anchor onto the current message list: the attested
553
+ * prompt for the messages it covered, plus a chars/4 estimate of everything
554
+ * appended since, plus text that has not been submitted yet. The tool
555
+ * schemas and the system prompt are inside the attested figure already, so
556
+ * they are not added again. Null when there is no attestation, or when the
557
+ * list is shorter than the anchor covered, which means the history it
558
+ * described was rewritten beneath it.
559
+ */
560
+ const reconciledAnchoredTokens = (agentRuntime: AgentRuntime, pendingUserTokens: number): number | null => {
561
+ const anchor = reconciledAnchor;
562
+ if (!anchor) return null;
563
+ const messages = agentRuntime.agent.state.messages;
564
+ if (anchor.anchoredMessageCount > messages.length) return null;
565
+ let tail = 0;
566
+ for (let i = anchor.anchoredMessageCount; i < messages.length; i += 1) {
567
+ const message = messages[i];
568
+ if (message === undefined) continue;
569
+ tail += estimateAgentMessageTokens(message);
570
+ }
571
+ return anchor.tokens + tail + pendingUserTokens;
572
+ };
573
+
386
574
  const liveContextEstimate = (agentRuntime: AgentRuntime, pendingUserText?: string): LiveContextEstimate => {
387
575
  const contextWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
388
576
  const estimateInput = {
@@ -391,10 +579,34 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
391
579
  tools: agentRuntime.agent.state.tools,
392
580
  ...(pendingUserText !== undefined ? { pendingUserText } : {}),
393
581
  };
582
+ const breakdown = estimateAgentContextBreakdown(estimateInput);
583
+ const estimatedTokens = estimateAgentContextTokens(estimateInput);
584
+ const reconciledTokens = reconciledAnchoredTokens(agentRuntime, breakdown.pendingUserTokens);
394
585
  return {
395
- tokens: estimateAgentContextTokens(estimateInput),
586
+ // The estimate is a floor, not a competing verdict: it prices material
587
+ // the attested call never saw, so a provider count below it would be
588
+ // answering about a smaller conversation.
589
+ tokens: Math.max(estimatedTokens, reconciledTokens ?? 0),
590
+ estimatedTokens,
591
+ reconciledTokens,
396
592
  contextWindow,
397
- breakdown: estimateAgentContextBreakdown(estimateInput),
593
+ breakdown,
594
+ };
595
+ };
596
+
597
+ /**
598
+ * A working-set projection removes tokens from messages the attestation
599
+ * covered; it does not make the attestation wrong about the rest. Subtract
600
+ * what the planner priced out against the same projection and re-anchor on
601
+ * the refreshed list, instead of discarding the figure and falling back to
602
+ * pure chars/4 exactly when the accounting matters most (issue #227).
603
+ * Call after the message list has been rebuilt.
604
+ */
605
+ const carryReconciledAnchorThroughProjection = (agentRuntime: AgentRuntime, tokensRemoved: number): void => {
606
+ if (!reconciledAnchor) return;
607
+ reconciledAnchor = {
608
+ tokens: Math.max(0, reconciledAnchor.tokens - Math.max(0, tokensRemoved)),
609
+ anchoredMessageCount: agentRuntime.agent.state.messages.length,
398
610
  };
399
611
  };
400
612
 
@@ -414,7 +626,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
414
626
  const emitCompactionActivity = (status: ContextActivityStatus, message: string): void => {
415
627
  deps.bus?.emit(BusChannels.ContextActivity, {
416
628
  kind: "compaction",
417
- phase: "done",
629
+ phase: status === "completed" ? "done" : "compact",
418
630
  status,
419
631
  message,
420
632
  at: Date.now(),
@@ -471,6 +683,12 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
471
683
  const autoEnabled = cfg?.auto !== false;
472
684
  if (!force && !autoEnabled) return false;
473
685
  let preSummaryStageActed = false;
686
+ // G1 from smoke pass 2: a short session generates all its pressure inside
687
+ // the protection window, the non-destructive layer finds nothing, and the
688
+ // destructive summary ran with nothing in the transcript saying eviction
689
+ // was considered and declined. Held here and emitted only if a summary
690
+ // actually follows.
691
+ let evictionSkipNotice: string | null = null;
474
692
  const rememberEmptyAutomaticAttempt = (): void => {
475
693
  if (!force && !preSummaryStageActed && activeAutoTurnId) emptyAutoCompactTurnId = activeAutoTurnId;
476
694
  };
@@ -507,6 +725,8 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
507
725
  emitCompactionActivity("started", "compacting context (mask stage)");
508
726
  deps.session.replaceEntries(masked.entries);
509
727
  refreshAgentMessagesFromSession(agentRuntime);
728
+ // The masked history is not the one the provider counted.
729
+ reconciledAnchor = null;
510
730
  deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
511
731
 
512
732
  const postMaskSnapshot = captureRuntimeContextSnapshot(
@@ -551,12 +771,16 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
551
771
  }
552
772
  } else if (settings.context.workingSet.enabled) {
553
773
  let planned: ReturnType<typeof planEviction>;
774
+ let visibleEntries: ReadonlyArray<SessionEntry> = [];
775
+ let policyId = settings.context.workingSet.policy;
554
776
  try {
555
777
  const entries = deps.readSessionEntries() ?? [];
556
778
  const view = foldWorkingSet(entries, state.lastTurnId ?? undefined);
557
779
  const policy = resolveWorkingSetPolicy(settings.context.workingSet.policy);
780
+ policyId = policy.id;
781
+ visibleEntries = selectVisibleEntries(entries, state.lastTurnId ?? undefined);
558
782
  planned = (deps.planEviction ?? planEviction)(policy, {
559
- entries: selectVisibleEntries(entries, state.lastTurnId ?? undefined),
783
+ entries: visibleEntries,
560
784
  view,
561
785
  cwd: deps.session.current()?.cwd ?? null,
562
786
  settings: settings.context.workingSet,
@@ -591,6 +815,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
591
815
  });
592
816
  noteColdReason("working_set_evict");
593
817
  refreshAgentMessagesFromSession(agentRuntime);
818
+ carryReconciledAnchorThroughProjection(agentRuntime, planned.tokensBefore - planned.tokensAfter);
594
819
 
595
820
  const postEvictionSnapshot = captureRuntimeContextSnapshot(
596
821
  agentRuntime,
@@ -637,7 +862,16 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
637
862
  emitCompactionActivity("failed", compactionFailureMessage(error));
638
863
  throw error;
639
864
  }
865
+ } else {
866
+ evictionSkipNotice = evictionSkipMessage(visibleEntries, settings.context.workingSet, policyId);
640
867
  }
868
+ } else {
869
+ evictionSkipNotice = renderEvictionSkipLine({
870
+ reason: "disabled",
871
+ turns: 0,
872
+ protectLastTurns: settings.context.workingSet.protectLastTurns,
873
+ policyId: settings.context.workingSet.policy,
874
+ });
641
875
  }
642
876
  }
643
877
  }
@@ -654,6 +888,10 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
654
888
  summaryLifecycleStarted = true;
655
889
  };
656
890
  if (force) startSummaryLifecycle();
891
+ // Said before the summary call, not after it: the operator is about to wait
892
+ // on a destructive stage, and the reason the cheap one was skipped is only
893
+ // useful ahead of it.
894
+ if (evictionSkipNotice) deps.emitNotice(evictionSkipNotice);
657
895
  let result: CompactResult | null = null;
658
896
  const beforeSnapshotId = currentContextSnapshot?.snapshotId ?? null;
659
897
  try {
@@ -683,6 +921,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
683
921
  recordCompactionUsage(agentRuntime, result);
684
922
 
685
923
  refreshAgentMessagesFromSession(agentRuntime);
924
+ // A summary replaces the conversation the attestation described, so no
925
+ // arithmetic carries it forward; the next call re-anchors it.
926
+ reconciledAnchor = null;
686
927
 
687
928
  const postCompactSnapshot = captureRuntimeContextSnapshot(
688
929
  agentRuntime,
@@ -741,6 +982,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
741
982
  captureRuntimeContextSnapshot,
742
983
  persistContextSnapshot,
743
984
  liveContextEstimate,
985
+ rememberedLoadedContextWindow,
744
986
  refreshAgentMessagesFromSession,
745
987
  runAutoCompact,
746
988
 
@@ -754,6 +996,18 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
754
996
  },
755
997
 
756
998
  reconcileUsage(usage: Usage): void {
999
+ // Cached prompt tokens still occupy the window; providers report them
1000
+ // outside `input`, so the attested prompt is the three summed.
1001
+ const promptTokens = (usage.input || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
1002
+ const messages = state.runtime?.agent.state.messages;
1003
+ if (promptTokens > 0 && messages) {
1004
+ // The output of this call is part of the next call's prompt for the
1005
+ // same messages, which is why it is folded in here.
1006
+ reconciledAnchor = {
1007
+ tokens: promptTokens + (usage.output || 0),
1008
+ anchoredMessageCount: messages.length,
1009
+ };
1010
+ }
757
1011
  if (!currentContextSnapshot) return;
758
1012
  // Reconcile in memory on every API call so the live meters
759
1013
  // track usage; persistence waits for the run to settle.
@@ -787,7 +1041,22 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
787
1041
  const modelState = agentRuntime.agent.state.model as
788
1042
  | (typeof agentRuntime.agent.state.model & { clio?: { quirks?: LocalModelQuirks } })
789
1043
  | undefined;
790
- const contextWindow = typeof modelState?.contextWindow === "number" ? modelState.contextWindow : null;
1044
+ // `Context window: N` is the window the backend will actually serve
1045
+ // this session, taken from the resolution the turn already ran rather
1046
+ // than from the model descriptor's copy of it (issue #249). The
1047
+ // ranking that puts a loaded figure ahead of a probed one lives in
1048
+ // `resolveContextWindowDetails`, which tests `loadedContextWindow`
1049
+ // first and only then `probedContextWindow`, so `effectiveContextWindow`
1050
+ // already is the loaded window whenever one is known and the source
1051
+ // already reads "loaded"; brief #227's carry-forward is what puts that
1052
+ // figure in reach on a resume. The source rides into the prompt
1053
+ // manifest, so a recompile after the loaded window is first observed
1054
+ // is explained by the record.
1055
+ const windowDetails = agentRuntime.runtimeResolution.contextWindowDetails;
1056
+ const resolvedWindow = windowDetails.effectiveContextWindow;
1057
+ const contextWindow = typeof resolvedWindow === "number" && resolvedWindow > 0 ? resolvedWindow : null;
1058
+ const contextWindowSource: ContextWindowSource | null =
1059
+ contextWindow === null ? null : windowDetails.contextWindowSource;
791
1060
  const guidance = modelState?.clio?.quirks?.thinking?.guidance;
792
1061
  // Per-tool prompt hints come from registry metadata, derived once from
793
1062
  // the frozen surface per compile. The compiler renders them sorted by
@@ -823,16 +1092,19 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
823
1092
  cwd: process.cwd(),
824
1093
  workingContextPaths: [...sessionWorkingContextPaths],
825
1094
  });
826
- const previousHash = sessionPrompt?.systemPromptHash ?? null;
1095
+ const previousHash = sessionPromptHash ?? lastRecordedPromptHash();
827
1096
  const changed = agentRuntime.agent.state.systemPrompt !== result.systemPrompt;
828
1097
  if (changed) {
829
1098
  agentRuntime.agent.state.systemPrompt = result.systemPrompt;
830
1099
  pendingPromptLogEntry = {
1100
+ version: PROMPT_MANIFEST_VERSION,
831
1101
  at: new Date().toISOString(),
832
1102
  previousHash,
833
1103
  systemPromptHash: result.systemPromptHash,
834
1104
  tokenEstimate: result.tokenEstimate,
835
1105
  thinkingLevel: agentRuntime.agent.state.thinkingLevel ?? null,
1106
+ contextWindow,
1107
+ contextWindowSource,
836
1108
  projectPreload: result.projectPreload ?? null,
837
1109
  sections: result.sections.map((s) => ({ id: s.id, tokenEstimate: s.tokenEstimate })),
838
1110
  fragments: result.fragmentManifest.map((f) => ({
@@ -845,6 +1117,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
845
1117
  }
846
1118
  lastSystemPromptReused = !changed;
847
1119
  sessionPrompt = result;
1120
+ sessionPromptHash = result.systemPromptHash;
848
1121
  sessionPromptKey = key;
849
1122
  return result;
850
1123
  } catch (err) {
@@ -877,6 +1150,11 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
877
1150
  tokenEstimate: entry.tokenEstimate,
878
1151
  },
879
1152
  });
1153
+ // A resumed process names the session's last recorded hash as previousHash (#249), so
1154
+ // the stamp requires the hash to have moved, not merely to have a predecessor.
1155
+ if (entry.previousHash !== null && entry.previousHash !== entry.systemPromptHash) {
1156
+ noteColdReason("prompt_recompiled");
1157
+ }
880
1158
  } catch {
881
1159
  // Ledger logging is diagnostics, not control flow; never abort a turn.
882
1160
  }
@@ -966,6 +1244,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
966
1244
  compactionThreshold,
967
1245
  compactionAuto,
968
1246
  promptCache: lastPromptCache,
1247
+ prewarm: lastPrewarm,
969
1248
  });
970
1249
  }
971
1250
 
@@ -998,6 +1277,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
998
1277
  measured,
999
1278
  lastCompaction: lastCompactionEvent,
1000
1279
  promptCache: lastPromptCache,
1280
+ prewarm: lastPrewarm,
1001
1281
  });
1002
1282
  },
1003
1283
 
@@ -1007,12 +1287,14 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1007
1287
  deps.bus?.emit(BusChannels.ContextWarning, { warning } satisfies ContextWarningPayload);
1008
1288
  },
1009
1289
 
1290
+ noteColdReason,
1291
+
1010
1292
  consumeExpectedColdReasons(runtimeId: string): void {
1011
1293
  // Cache-disturbance honesty (T3.3): consume disturbances since
1012
1294
  // 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).
1295
+ // prefix cache to interleaved work, so residency, thinking changes,
1296
+ // dispatch, and compaction stamp only on local-native targets. Reasons
1297
+ // that change the prefix bytes stamp every tier (see stampsOnTier).
1016
1298
  runExpectedColdReasons = [];
1017
1299
  nextAssistantColdReasons = [];
1018
1300
  if (pendingColdReasons.size > 0) {
@@ -1026,10 +1308,10 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1026
1308
  }
1027
1309
  },
1028
1310
 
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.
1311
+ promptCachePayloadForAssistant(usage: Usage, backend?: BackendCompletionTimings): Record<string, unknown> {
1312
+ // Per-call prompt-cache record (T3.2) keeps normalized provider usage
1313
+ // beside any timings captured from the server response. The run's first
1314
+ // persisted call also carries any expected-cold reasons.
1033
1315
  const input = typeof usage.input === "number" ? usage.input : 0;
1034
1316
  const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
1035
1317
  const cacheWrite = typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0;
@@ -1037,8 +1319,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1037
1319
  input,
1038
1320
  cacheRead,
1039
1321
  cacheWrite,
1040
- backendVerdict: backendCacheVerdict(input, cacheRead),
1322
+ backendVerdict: backendCacheVerdict(input, cacheRead, backend),
1041
1323
  };
1324
+ if (backend !== undefined) promptCache.backend = { ...backend };
1042
1325
  if (nextAssistantColdReasons.length > 0) {
1043
1326
  promptCache.expectedColdReasons = [...nextAssistantColdReasons];
1044
1327
  nextAssistantColdReasons = [];
@@ -1046,14 +1329,30 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1046
1329
  return promptCache;
1047
1330
  },
1048
1331
 
1332
+ notePrewarm(prewarm: PrewarmStats): void {
1333
+ lastPrewarm = prewarm;
1334
+ },
1335
+
1049
1336
  noteRunCacheSummary(messages, runFirstCallVerdict): void {
1337
+ // The settled run is the answer to the pre-warm's question, so the
1338
+ // pre-warm line stops being the newest fact about the prefix here.
1339
+ lastPrewarm = null;
1050
1340
  const cacheSummary = sumRunUsage(messages);
1051
1341
  if (cacheSummary.hadUsage) {
1342
+ let lastBackend: BackendCompletionTimings | null = null;
1343
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1344
+ const message = messages[index];
1345
+ if (message?.role !== "assistant") continue;
1346
+ lastBackend = (message as { backendTimings?: BackendCompletionTimings }).backendTimings ?? null;
1347
+ break;
1348
+ }
1052
1349
  lastPromptCache = {
1053
1350
  shellReused: lastSystemPromptReused,
1054
1351
  cacheReadTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheRead : null,
1055
1352
  cacheWriteTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheWrite : null,
1056
1353
  uncachedInputTokens: cacheSummary.input,
1354
+ backend: lastBackend,
1355
+ uncachedPrefillTokens: uncachedPrefillTokens(lastBackend),
1057
1356
  backendVerdict: runFirstCallVerdict,
1058
1357
  ...(runExpectedColdReasons.length > 0 ? { expectedColdReasons: [...runExpectedColdReasons] } : {}),
1059
1358
  };
@@ -1061,12 +1360,26 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
1061
1360
  },
1062
1361
 
1063
1362
  resetForSession(): void {
1363
+ // An in-process switch replaces the whole prefix: the backend's slot
1364
+ // still holds the outgoing session's prompt and history, so the first
1365
+ // turn on the incoming one is expected-cold on every tier, exactly as
1366
+ // a recompile is. The manifest can no longer say so on its own once
1367
+ // provenance follows the session, because the incoming session's last
1368
+ // recorded hash usually equals its fresh compile, so the stamp comes
1369
+ // from here. Only when this process had actually applied a prompt:
1370
+ // before the first compile there is nothing in the slot to have lost.
1371
+ if (sessionPromptHash !== null) noteColdReason("prompt_recompiled");
1064
1372
  lastPromptCache = null;
1373
+ lastPrewarm = null;
1065
1374
  lastSystemPromptReused = false;
1375
+ sessionPromptHash = null;
1066
1376
  sessionPromptKey = null;
1377
+ resumedPromptHashRead = false;
1378
+ resumedPromptHash = null;
1067
1379
  sessionWorkingContextPaths.clear();
1068
1380
  pendingPromptLogEntry = null;
1069
1381
  emptyAutoCompactTurnId = null;
1382
+ reconciledAnchor = null;
1070
1383
  const session = deps.session?.current();
1071
1384
  currentContextSnapshot = session ? getLatestContextSnapshot(session) : null;
1072
1385
  },