@iowarp/clio-coder 0.3.8 → 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 (333) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +7 -3
  3. package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
  4. package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
  7. package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
  11. package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
  13. package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
  14. package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
  17. package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
  18. package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
  21. package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
  22. package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
  23. package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
  27. package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
  28. package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
  29. package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
  30. package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
  31. package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
  32. package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
  33. package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
  34. package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
  35. package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
  36. package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
  37. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  38. package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
  39. package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
  40. package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
  41. package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
  42. package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
  43. package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
  44. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  45. package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
  46. package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
  47. package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
  48. package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
  49. package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
  50. package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
  51. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  52. package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
  53. package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
  54. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  55. package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
  56. package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
  57. package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
  58. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  59. package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
  60. package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
  61. package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
  62. package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
  63. package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
  64. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  65. package/dist/chunk-RKKLTLYB.js +45 -0
  66. package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
  67. package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
  68. package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
  69. package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
  70. package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
  71. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  72. package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
  73. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  74. package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
  75. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  76. package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
  77. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  78. package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
  79. package/dist/chunk-YSEHGPCT.js +127 -0
  80. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  81. package/dist/cli/index.js +27 -27
  82. package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
  83. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
  84. package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
  85. package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
  86. package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
  87. package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
  88. package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
  89. package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
  90. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  91. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
  92. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
  93. package/dist/doctor-TWBWFK5V.js +165 -0
  94. package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
  95. package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
  96. package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
  97. package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
  98. package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
  99. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
  100. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
  101. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
  102. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
  103. package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
  104. package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
  105. package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
  106. package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
  107. package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
  108. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
  109. package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
  110. package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
  111. package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
  112. package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
  113. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
  114. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  115. package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
  116. package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
  117. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
  118. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  119. package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
  120. package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
  121. package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
  122. package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
  123. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
  124. package/dist/worker/entry.js +29 -33
  125. package/docs/alcf-provider.md +1 -1
  126. package/docs/architecture.md +1 -1
  127. package/docs/artifact-versions.md +6 -1
  128. package/docs/built-in-agents.md +1 -1
  129. package/docs/capacity-and-scheduling.md +23 -2
  130. package/docs/commands-and-modes.md +1 -1
  131. package/docs/configuration-and-targets.md +30 -3
  132. package/docs/context-engine.md +63 -4
  133. package/docs/documentation-coverage.md +3 -3
  134. package/docs/documentation-guide.md +1 -1
  135. package/docs/environment-variables.md +2 -0
  136. package/docs/eval-runner.md +262 -11
  137. package/docs/evals-internal.md +72 -2
  138. package/docs/evidence-and-memory.md +11 -10
  139. package/docs/evolution.md +1 -1
  140. package/docs/extensions-and-sharing.md +3 -1
  141. package/docs/fleet-dispatch.md +4 -4
  142. package/docs/installation-and-lifecycle.md +1 -1
  143. package/docs/middleware-and-components.md +1 -1
  144. package/docs/model-catalog.md +1 -1
  145. package/docs/observability.md +53 -2
  146. package/docs/proactive-memory.md +127 -14
  147. package/docs/prompt-envelope-and-tools.md +19 -1
  148. package/docs/provider-adapter-cookbook.md +1 -1
  149. package/docs/release-cut-checklist.md +19 -3
  150. package/docs/safety-model.md +1 -1
  151. package/docs/scientific-validation.md +1 -1
  152. package/docs/skills-marketplace.md +1 -1
  153. package/docs/tool-usage.md +1 -1
  154. package/docs/trace-store.md +1 -1
  155. package/docs/troubleshooting.md +87 -0
  156. package/docs/tui-design.md +1 -1
  157. package/docs/worker-dispatch-mechanics.md +1 -1
  158. package/package.json +2 -1
  159. package/src/cli/agents.ts +1 -1
  160. package/src/cli/config-inspect.ts +33 -6
  161. package/src/cli/config.ts +1 -1
  162. package/src/cli/doctor-state-size.ts +82 -0
  163. package/src/cli/doctor.ts +3 -1
  164. package/src/cli/eval.ts +80 -16
  165. package/src/cli/extensions.ts +5 -1
  166. package/src/cli/fleet.ts +32 -3
  167. package/src/cli/targets.ts +44 -13
  168. package/src/cli/trace.ts +63 -4
  169. package/src/cli/usage.ts +63 -14
  170. package/src/core/bus-events.ts +29 -1
  171. package/src/core/cache-telemetry.ts +42 -0
  172. package/src/core/config.ts +18 -0
  173. package/src/core/defaults.ts +36 -6
  174. package/src/core/endpoint-key.ts +27 -0
  175. package/src/core/residency-target-key.ts +25 -0
  176. package/src/core/response-schema.ts +36 -2
  177. package/src/domains/config/classify.ts +3 -0
  178. package/src/domains/context/codewiki/coordinator.ts +12 -4
  179. package/src/domains/dispatch/admission.ts +40 -3
  180. package/src/domains/dispatch/capacity-lease.ts +98 -9
  181. package/src/domains/dispatch/contract.ts +11 -0
  182. package/src/domains/dispatch/execution-plan.ts +44 -4
  183. package/src/domains/dispatch/extension.ts +166 -42
  184. package/src/domains/dispatch/fleet-run.ts +23 -3
  185. package/src/domains/dispatch/heartbeat.ts +32 -8
  186. package/src/domains/dispatch/index.ts +3 -0
  187. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  188. package/src/domains/dispatch/reservation-store.ts +116 -8
  189. package/src/domains/dispatch/state.ts +4 -0
  190. package/src/domains/dispatch/worker-spawn.ts +25 -11
  191. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  192. package/src/domains/dispatch/write-boundary.ts +62 -1
  193. package/src/domains/eval/artifacts/store.ts +62 -0
  194. package/src/domains/eval/compare/behavioral.ts +224 -0
  195. package/src/domains/eval/compare/compare.ts +355 -2
  196. package/src/domains/eval/compare/envelope.ts +128 -0
  197. package/src/domains/eval/compare/gates.ts +24 -6
  198. package/src/domains/eval/compare/thresholds.ts +30 -3
  199. package/src/domains/eval/execution-provenance.ts +240 -0
  200. package/src/domains/eval/metrics/aggregate.ts +136 -0
  201. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  202. package/src/domains/eval/metrics/tracked.ts +413 -0
  203. package/src/domains/eval/provenance.ts +117 -0
  204. package/src/domains/eval/reports/comparison.ts +128 -0
  205. package/src/domains/eval/reports/junit.ts +17 -3
  206. package/src/domains/eval/reports/markdown.ts +3 -3
  207. package/src/domains/eval/reports/text.ts +14 -0
  208. package/src/domains/eval/run-compare.ts +20 -0
  209. package/src/domains/eval/runners/clio-run.ts +127 -0
  210. package/src/domains/eval/runners/external-command.ts +28 -3
  211. package/src/domains/eval/schema/adapter.ts +111 -0
  212. package/src/domains/eval/schema/artifact.ts +20 -0
  213. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  214. package/src/domains/eval/schema/behavioral.ts +520 -0
  215. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  216. package/src/domains/eval/schema/serving.ts +74 -0
  217. package/src/domains/eval/schema/suite.ts +38 -8
  218. package/src/domains/eval/schema/validate.ts +58 -3
  219. package/src/domains/eval/schema/verdict.ts +237 -0
  220. package/src/domains/eval/suites/resolve.ts +2 -0
  221. package/src/domains/eval/suites/run.ts +264 -33
  222. package/src/domains/eval/verifiers/command.ts +2 -1
  223. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  224. package/src/domains/evidence/build.ts +2 -13
  225. package/src/domains/evidence/eval.ts +2 -12
  226. package/src/domains/evidence/findings-markdown.ts +33 -0
  227. package/src/domains/evidence/run-trust.ts +7 -113
  228. package/src/domains/evidence/trust-projection.ts +2 -2
  229. package/src/domains/extensions/compatibility.ts +285 -0
  230. package/src/domains/extensions/discovery.ts +38 -3
  231. package/src/domains/extensions/resources.ts +1 -1
  232. package/src/domains/extensions/state.ts +12 -3
  233. package/src/domains/extensions/types.ts +2 -0
  234. package/src/domains/lifecycle/doctor.ts +69 -1
  235. package/src/domains/memory/index.ts +14 -0
  236. package/src/domains/memory/task-bank-promotion.ts +64 -0
  237. package/src/domains/memory/task-memory-policy.ts +77 -8
  238. package/src/domains/memory/task-memory-spend.ts +131 -0
  239. package/src/domains/memory/task-memory-status.ts +7 -0
  240. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  241. package/src/domains/middleware/index.ts +1 -0
  242. package/src/domains/middleware/memory-intervention.ts +69 -5
  243. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  244. package/src/domains/observability/background-memory-usage.ts +140 -0
  245. package/src/domains/observability/cost.ts +1 -1
  246. package/src/domains/observability/index.ts +7 -0
  247. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  248. package/src/domains/observability/trace-store.ts +192 -2
  249. package/src/domains/prompts/compiler.ts +100 -13
  250. package/src/domains/providers/endpoint-capacity.ts +96 -0
  251. package/src/domains/providers/index.ts +10 -0
  252. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  253. package/src/domains/providers/runtime-resolution.ts +8 -1
  254. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  255. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  256. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  257. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  258. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  259. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  260. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  261. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  262. package/src/domains/providers/types/capability-flags.ts +2 -0
  263. package/src/domains/providers/types/target-descriptor.ts +2 -0
  264. package/src/domains/resources/prompts/loader.ts +95 -33
  265. package/src/domains/safety/call-target.ts +52 -0
  266. package/src/domains/safety/run-effects.ts +35 -4
  267. package/src/domains/session/context-accounting.ts +52 -1
  268. package/src/domains/session/context-ledger.ts +37 -13
  269. package/src/domains/session/index.ts +6 -0
  270. package/src/domains/session/prompt-cache.ts +140 -0
  271. package/src/domains/session/prompt-manifest.ts +42 -0
  272. package/src/engine/acp/adapter.ts +18 -3
  273. package/src/engine/ai.ts +35 -0
  274. package/src/engine/apis/llamacpp-residency.ts +55 -3
  275. package/src/engine/apis/lmstudio.ts +25 -5
  276. package/src/engine/apis/ollama-native.ts +2 -1
  277. package/src/engine/apis/openai-completions.ts +80 -17
  278. package/src/engine/apis/residency-lock.ts +3 -1
  279. package/src/engine/apis/residency.ts +34 -1
  280. package/src/engine/provider-payload.ts +29 -1
  281. package/src/entry/orchestrator.ts +176 -30
  282. package/src/interactive/chat-loop-messages.ts +26 -7
  283. package/src/interactive/chat-loop.ts +318 -41
  284. package/src/interactive/chat-panel.ts +62 -8
  285. package/src/interactive/clio-editor.ts +45 -8
  286. package/src/interactive/context-activity.ts +5 -1
  287. package/src/interactive/context-meter.ts +1 -1
  288. package/src/interactive/context-overlay.ts +40 -10
  289. package/src/interactive/cost-overlay.ts +64 -6
  290. package/src/interactive/dispatch-board.ts +84 -12
  291. package/src/interactive/fleet-run-preview.ts +41 -15
  292. package/src/interactive/handoff-round.ts +41 -2
  293. package/src/interactive/interactive-application.ts +24 -1
  294. package/src/interactive/interactive-input-runtime.ts +8 -0
  295. package/src/interactive/interactive-presentation.ts +4 -0
  296. package/src/interactive/interactive-shell.ts +20 -17
  297. package/src/interactive/interactive-slash-runtime.ts +27 -4
  298. package/src/interactive/memory-overlay.ts +8 -0
  299. package/src/interactive/mutation-preview.ts +295 -0
  300. package/src/interactive/overlay-general-openers.ts +16 -0
  301. package/src/interactive/overlay-key-routing.ts +38 -0
  302. package/src/interactive/overlay-lifecycle.ts +38 -5
  303. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  304. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  305. package/src/interactive/overlays/ask-user.ts +91 -19
  306. package/src/interactive/overlays/help-reference.ts +4 -0
  307. package/src/interactive/overlays/prompts.ts +11 -1
  308. package/src/interactive/overlays/settings.ts +35 -1
  309. package/src/interactive/permission-hint.ts +34 -2
  310. package/src/interactive/permission-overlay.ts +159 -9
  311. package/src/interactive/prewarm.ts +197 -0
  312. package/src/interactive/render-trace.ts +162 -15
  313. package/src/interactive/renderers/tool-execution.ts +4 -0
  314. package/src/interactive/side-question.ts +58 -1
  315. package/src/interactive/status/controller.ts +11 -0
  316. package/src/interactive/status/state-machine.ts +54 -2
  317. package/src/interactive/status/types.ts +7 -0
  318. package/src/interactive/terminal-lease.ts +2 -0
  319. package/src/interactive/turn-context.ts +299 -31
  320. package/src/interactive/turn-persistence.ts +14 -4
  321. package/src/interactive/turn-prewarm.ts +364 -0
  322. package/src/interactive/turn-queues.ts +7 -4
  323. package/src/interactive/turn-runtime.ts +8 -1
  324. package/src/interactive/turn-state.ts +23 -0
  325. package/src/interactive/view/view-overlay.ts +28 -3
  326. package/src/tools/ask-user.ts +43 -2
  327. package/src/tools/dispatch-plan.ts +17 -9
  328. package/src/tools/dispatch-scout.ts +1 -1
  329. package/src/tools/registry.ts +16 -0
  330. package/dist/chunk-AOCYTWAV.js +0 -449
  331. package/dist/chunk-HWUFFB6L.js +0 -83
  332. package/dist/chunk-R346GLFC.js +0 -31
  333. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -1,12 +1,12 @@
1
1
  import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
2
  import {
3
3
  ShareDomainModule
4
- } from "./chunk-GN57SG4G.js";
4
+ } from "./chunk-TD7UE2L5.js";
5
5
  import {
6
6
  bootstrapInputFromInitOptions,
7
7
  modelBootstrapGenerate,
8
8
  resolveBootstrapRoute
9
- } from "./chunk-4SPRNWDE.js";
9
+ } from "./chunk-3DUR4WUA.js";
10
10
  import "./chunk-J5Q24KAG.js";
11
11
  import {
12
12
  DEFAULT_KEEP_RECENT_TOKENS,
@@ -21,7 +21,7 @@ import {
21
21
  projectWorkingSet,
22
22
  resolveWorkingSetPolicy,
23
23
  selectVisibleEntries
24
- } from "./chunk-VHN4MY6O.js";
24
+ } from "./chunk-3MRC2YSQ.js";
25
25
  import {
26
26
  assistantSessionPayload,
27
27
  backendCacheVerdict,
@@ -54,42 +54,42 @@ import {
54
54
  toolNamesFromAgentState,
55
55
  toolResultSummary,
56
56
  toolSignatureFromState
57
- } from "./chunk-2HFZQUHL.js";
57
+ } from "./chunk-N5XKWMDW.js";
58
58
  import {
59
59
  SchedulingDomainModule,
60
60
  createPromptsDomainModule,
61
61
  renderAgentCatalogSectionsFromSpecs
62
- } from "./chunk-5DHKRSMQ.js";
62
+ } from "./chunk-3UUY7R3Z.js";
63
63
  import {
64
64
  flushRawStdout,
65
65
  writeRawStdout
66
66
  } from "./chunk-2VTFPG5O.js";
67
67
  import {
68
68
  SafetyDomainModule
69
- } from "./chunk-EMYUUSFG.js";
69
+ } from "./chunk-WXCJ7VME.js";
70
70
  import {
71
71
  LifecycleDomainModule,
72
72
  describeUpgradeNotice
73
- } from "./chunk-5Q2VVUKB.js";
73
+ } from "./chunk-DG4M6ZUE.js";
74
74
  import {
75
75
  ensureClioState,
76
76
  takeUpgradeNotice
77
- } from "./chunk-WWCZ5F23.js";
77
+ } from "./chunk-T3Z6VAAF.js";
78
78
  import {
79
79
  ConfigDomainModule,
80
80
  createConfigDomainModule,
81
81
  loadDomains
82
- } from "./chunk-FHJEP5SW.js";
82
+ } from "./chunk-465CC7FK.js";
83
83
  import {
84
84
  discoverDeclaredChecks
85
- } from "./chunk-VAWNZU7Z.js";
85
+ } from "./chunk-P3JGPQFL.js";
86
86
  import {
87
87
  buildRecallFields,
88
88
  recallErrorMessage,
89
89
  recallParentTurnId,
90
90
  recallableRefListing,
91
91
  resolveRecall
92
- } from "./chunk-ZNLWCMVZ.js";
92
+ } from "./chunk-MV2VUEJC.js";
93
93
  import {
94
94
  foldWorkingSet
95
95
  } from "./chunk-TZSKNMZG.js";
@@ -103,10 +103,11 @@ import {
103
103
  libraryEntryRef,
104
104
  libraryInstallPath,
105
105
  planLibraryInstall
106
- } from "./chunk-U6MBIEMB.js";
106
+ } from "./chunk-PZ4I4JE2.js";
107
107
  import {
108
108
  AgentsDomainModule
109
- } from "./chunk-FBVTI2TJ.js";
109
+ } from "./chunk-YSEHGPCT.js";
110
+ import "./chunk-HCBCAYZU.js";
110
111
  import "./chunk-DR52UMZW.js";
111
112
  import {
112
113
  MARKETPLACE_UNCONFIGURED,
@@ -115,32 +116,33 @@ import {
115
116
  installSkill,
116
117
  modelVisibleSkills,
117
118
  parseSkillCommand
118
- } from "./chunk-TANS5ZJS.js";
119
- import "./chunk-DGSYXYMX.js";
119
+ } from "./chunk-AD7Y7STJ.js";
120
+ import "./chunk-AMKHQW3C.js";
120
121
  import {
121
122
  compileScoutTransition,
122
123
  createDispatchRunEventRegistry
123
- } from "./chunk-JOZYP4GM.js";
124
+ } from "./chunk-YKOFT37S.js";
124
125
  import {
125
126
  monitorToolSurface
126
- } from "./chunk-FJ3H4MN5.js";
127
+ } from "./chunk-IHKBWSXF.js";
127
128
  import {
128
129
  COST_NOT_MEASURED,
129
130
  ObservabilityDomainModule,
130
131
  aggregateCostAmounts,
131
132
  costAggregateForAmount,
132
133
  formatCostAggregate,
133
- readAccountabilitySummary
134
- } from "./chunk-WNIJTQQK.js";
134
+ readAccountabilitySummary,
135
+ recordBackgroundMemoryStep
136
+ } from "./chunk-5PVQ4SRS.js";
135
137
  import {
136
138
  appendOutOfTurnUsageRow
137
- } from "./chunk-GWS3VEIW.js";
138
- import "./chunk-HFSBBKSQ.js";
139
- import "./chunk-HLE42MG7.js";
139
+ } from "./chunk-FWDFM5ZU.js";
140
+ import "./chunk-YW7UVM5V.js";
141
+ import "./chunk-D73KXYPF.js";
140
142
  import {
141
143
  steerToolSurface
142
- } from "./chunk-WEH5XRJQ.js";
143
- import "./chunk-26LEYJZH.js";
144
+ } from "./chunk-WIE7ZOSW.js";
145
+ import "./chunk-FALJGAWU.js";
144
146
  import {
145
147
  ClioEditor,
146
148
  CombinedAutocompleteProvider,
@@ -150,6 +152,7 @@ import {
150
152
  FocusBox,
151
153
  GLYPH,
152
154
  Input,
155
+ MUTATION_PREVIEW_KEY,
153
156
  Markdown,
154
157
  Marked,
155
158
  SGR_DIM,
@@ -207,39 +210,43 @@ import {
207
210
  truncateToWidth,
208
211
  validateKeybindings,
209
212
  visibleWidth,
210
- wrapTextWithAnsi
211
- } from "./chunk-5C3AQNDW.js";
213
+ wrapTextWithAnsi,
214
+ writeInputWedgeDump
215
+ } from "./chunk-NNNWO6F2.js";
212
216
  import {
213
217
  getTerminationCoordinator
214
- } from "./chunk-WXY7KU3G.js";
218
+ } from "./chunk-BVDVID7E.js";
215
219
  import {
216
220
  createContextDomainModule,
217
221
  loadProjectClioMd
218
- } from "./chunk-WSB3FPX7.js";
222
+ } from "./chunk-HJB5IUKP.js";
219
223
  import "./chunk-LCGCVYZ4.js";
220
224
  import {
221
225
  renderCodewikiDigest
222
- } from "./chunk-5WIGXA4T.js";
226
+ } from "./chunk-47CMYGET.js";
223
227
  import {
224
228
  buildMemoryPromptSection,
225
229
  canonicalMemoryRepositoryIdentity,
226
230
  createTaskMemoryTrace,
227
231
  describeTaskMemoryActivity,
232
+ formatTaskMemorySpend,
228
233
  loadMemoryRecordsSync,
234
+ proposeInjectedTaskMemory,
229
235
  proposeMemoryPromotion,
236
+ readTaskMemorySpendSummary,
230
237
  renderTaskMemoryHandoffSource,
231
238
  seedTaskMemoryFromNewestHandoff,
232
239
  taskMemoryBankSize,
233
240
  taskMemoryHandoffSeedOffer,
234
241
  taskMemoryTracePath
235
- } from "./chunk-TYPGUK6W.js";
242
+ } from "./chunk-5T7RBWN2.js";
236
243
  import {
237
244
  listWikiPages,
238
245
  wikiCompleteness,
239
246
  wikiStaleness,
240
247
  wikiStalenessAsync
241
248
  } from "./chunk-NDINPTJ4.js";
242
- import "./chunk-MVVUPGPW.js";
249
+ import "./chunk-MXHC5QYU.js";
243
250
  import {
244
251
  ACP_MAX_CHUNK_BYTES,
245
252
  ACP_MAX_RAW_RECORD_BYTES,
@@ -252,6 +259,7 @@ import {
252
259
  AcpTimeoutError,
253
260
  WORKER_OUTPUT_MAX_BYTES,
254
261
  acpErrorMessage,
262
+ capacityLeaseUsage,
255
263
  createDispatchDomainModule,
256
264
  createRunReceiptQuality,
257
265
  createStdioServerTransport,
@@ -265,7 +273,16 @@ import {
265
273
  readGateDecisionArtifacts,
266
274
  readPendingGateDecisions,
267
275
  renderDispatchReviewerTask
268
- } from "./chunk-TLQJPP24.js";
276
+ } from "./chunk-TEO2TLVN.js";
277
+ import {
278
+ formatTrustSummaryLine,
279
+ inspectRunReceiptTrustStatus,
280
+ isReceiptIntegrity,
281
+ retiredReceiptIntegrity,
282
+ retiredReceiptIntegrityReason,
283
+ summarizeTrustStatus,
284
+ verifyReceiptIntegrity
285
+ } from "./chunk-MLOK6ZOS.js";
269
286
  import {
270
287
  SessionDomainModule,
271
288
  addResponseModelIdObservationCount,
@@ -279,30 +296,28 @@ import {
279
296
  readAuditRows,
280
297
  responseModelIdObservationCountsLabel,
281
298
  responseModelIdObservationFromRecord
282
- } from "./chunk-YS5VLNH5.js";
283
- import {
284
- formatTrustSummaryLine,
285
- inspectRunReceiptTrustStatus,
286
- isReceiptIntegrity,
287
- retiredReceiptIntegrity,
288
- retiredReceiptIntegrityReason,
289
- summarizeTrustStatus,
290
- verifyReceiptIntegrity
291
- } from "./chunk-K4XHGFR5.js";
299
+ } from "./chunk-LXPJXFM5.js";
292
300
  import {
301
+ describeWriteBoundaryAttributionDowngrade,
293
302
  gitCheckoutRoot,
294
303
  isGitRepository,
295
304
  preflightWriteBoundaries,
296
305
  recoverCleanupReadyCompeteGroups
297
- } from "./chunk-MXKJU4JB.js";
306
+ } from "./chunk-ECUO3KDP.js";
298
307
  import {
308
+ bindExecutionPlanEndpoints,
299
309
  compileFleetExecutionPlan,
300
310
  requireAgentSteps
301
- } from "./chunk-J3YUBZWY.js";
311
+ } from "./chunk-HKO36JWF.js";
302
312
  import {
303
313
  parseRigorOverride,
304
314
  resolveRigor
305
315
  } from "./chunk-RVG5JXAL.js";
316
+ import {
317
+ foldPromptCacheTelemetry,
318
+ hasPromptCacheTelemetry,
319
+ uncachedPrefillTokens
320
+ } from "./chunk-3DPEIQKN.js";
306
321
  import {
307
322
  filterEntriesToActivePath
308
323
  } from "./chunk-W6GROXXM.js";
@@ -315,12 +330,13 @@ import {
315
330
  renderFleetPrompt,
316
331
  validateFleetCommands,
317
332
  validateFleetGraph
318
- } from "./chunk-E77JEWSD.js";
333
+ } from "./chunk-HPCTNZM2.js";
334
+ import "./chunk-RKKLTLYB.js";
319
335
  import {
320
336
  ExtensionsDomainModule
321
- } from "./chunk-DYIM5TJT.js";
337
+ } from "./chunk-5QKCQQ3E.js";
322
338
  import "./chunk-JVCV3ICN.js";
323
- import "./chunk-2HEJ2F35.js";
339
+ import "./chunk-5HFBWUMU.js";
324
340
  import {
325
341
  shellQuote
326
342
  } from "./chunk-TXOTCRLG.js";
@@ -336,6 +352,7 @@ import {
336
352
  import {
337
353
  ASK_USER_OTHER_LABEL,
338
354
  INTERACTIVE_LOOP_BLOCK_BUDGET,
355
+ applyEditsToNormalizedContent,
339
356
  askAxis,
340
357
  askUserExposure,
341
358
  assertRegisteredBuiltinTools,
@@ -350,16 +367,20 @@ import {
350
367
  decisionFactsForPermission,
351
368
  diffWords,
352
369
  finalizeAskUserInterviewForHost,
370
+ generateDiffString,
353
371
  lazyTool,
372
+ normalizeToLF,
354
373
  patchProviderThinkingPayload,
374
+ patchResponseSchemaPayloadForDialect,
355
375
  patchToolChoiceNamedPayload,
356
376
  patchToolChoiceNonePayload,
357
377
  readOrchTurnToolCallBudget,
358
378
  registerCoreTools,
359
379
  resolveFdBinary,
360
380
  resolveSessionTools,
361
- sanitizeLockedSynthesisMessage
362
- } from "./chunk-WLFILSD5.js";
381
+ sanitizeLockedSynthesisMessage,
382
+ stripBom
383
+ } from "./chunk-R7LNVMCS.js";
363
384
  import {
364
385
  HEADLESS_PERMISSION_DENIED_REASON
365
386
  } from "./chunk-K7VKOLQQ.js";
@@ -367,6 +388,7 @@ import {
367
388
  MIDDLEWARE_HOOK_TEXT_MAX_CHARS,
368
389
  MiddlewareDomainModule,
369
390
  WATCHDOG_TASK,
391
+ announceMemoryStepEndpoint,
370
392
  createDetachedDispatchNudgeRegistration,
371
393
  createHookReceiptLog,
372
394
  createMemoryInterventionRegistration,
@@ -381,12 +403,12 @@ import {
381
403
  openDetachedBatchViews,
382
404
  watchdogBriefing,
383
405
  writeMiddlewareDiagnosticToStderr
384
- } from "./chunk-7RGZWPB6.js";
385
- import "./chunk-VCBR6CU7.js";
406
+ } from "./chunk-GAYUJ7LE.js";
407
+ import "./chunk-HAY4ZE2P.js";
386
408
  import "./chunk-UOV2BYIW.js";
387
- import "./chunk-PMDBGQSJ.js";
388
- import "./chunk-XN3L4EYL.js";
389
- import "./chunk-SPULKLCF.js";
409
+ import "./chunk-CA42X6KT.js";
410
+ import "./chunk-OEDBCISO.js";
411
+ import "./chunk-5DQRIYDZ.js";
390
412
  import {
391
413
  DISPATCH_PLAN_PREPARATION_ERROR_ARGUMENT,
392
414
  RESOLVED_DISPATCH_PLAN_ARGUMENT,
@@ -398,11 +420,11 @@ import {
398
420
  stringArg,
399
421
  timeoutMsArg,
400
422
  withResolvedDispatchPlan
401
- } from "./chunk-KTYTFRMB.js";
423
+ } from "./chunk-VKBMFOYV.js";
402
424
  import {
403
425
  TaskMemoryBank,
404
426
  createTaskMemoryTelemetrySink
405
- } from "./chunk-ZVJ5BLO2.js";
427
+ } from "./chunk-O6I4CIEU.js";
406
428
  import {
407
429
  escapeRegExp
408
430
  } from "./chunk-VG7TBQIY.js";
@@ -422,26 +444,29 @@ import {
422
444
  formatBudgetReasons,
423
445
  formatBudgetRequest,
424
446
  formatEffectiveBudget,
447
+ isResponseSchemaRejection,
425
448
  isToolProfileName,
426
449
  normalizeDispatchIntent,
427
450
  redactSecretString,
428
451
  redactToolArgs,
429
452
  renderCouncilVoteMemberTask,
453
+ responseSchemaDialectFor,
430
454
  runBashCommand,
431
455
  runKindSupportsLiveSteering,
432
456
  sanitizeCallTargetText,
457
+ sanitizeMultilineDisplayText,
433
458
  toolPresentationPolicy
434
- } from "./chunk-A3WNZD3P.js";
459
+ } from "./chunk-4H6ULJ3H.js";
460
+ import {
461
+ defaultRoutingIntent,
462
+ sameRouteIdentity
463
+ } from "./chunk-VPTUJU4P.js";
435
464
  import {
436
465
  createTaskBoardStore,
437
466
  foldSessionTaskHistory,
438
467
  foldTaskBoard,
439
468
  taskBoardCounts
440
- } from "./chunk-5FR74PWO.js";
441
- import {
442
- defaultRoutingIntent,
443
- sameRouteIdentity
444
- } from "./chunk-P43ETTHK.js";
469
+ } from "./chunk-2JDWVJND.js";
445
470
  import {
446
471
  agentRoleFactsResolver,
447
472
  gateDeciderAgentId,
@@ -451,29 +476,29 @@ import {
451
476
  import {
452
477
  DEFAULT_RECENT_ENTRY_LIMIT,
453
478
  assessFinishContract
454
- } from "./chunk-5H3GB5BO.js";
479
+ } from "./chunk-7C6RYZGQ.js";
480
+ import "./chunk-N3PBVRTZ.js";
455
481
  import "./chunk-A2NJGIB3.js";
456
- import "./chunk-OQE5J4C6.js";
457
482
  import {
458
- SKILL_SUGGESTION_PREFIX,
459
- isSkillActivation,
460
- skillActivationFromToolDetails
461
- } from "./chunk-GOXNB3AO.js";
462
- import {
463
- DEFAULT_AUTONOMY_LEVEL
464
- } from "./chunk-HWUFFB6L.js";
483
+ resolveToCwd
484
+ } from "./chunk-OQE5J4C6.js";
465
485
  import {
466
486
  parseCouncilReport,
467
487
  parseOracleResult,
468
488
  parseScoutResult,
469
489
  parseVerifierResult,
470
490
  resultContractSourceId
471
- } from "./chunk-TTHACPOM.js";
472
- import "./chunk-AOCYTWAV.js";
491
+ } from "./chunk-S4COXYBG.js";
473
492
  import {
474
493
  CLIO_PROJECT_DIR,
475
494
  toolMutationPaths
476
495
  } from "./chunk-MV3K5QF2.js";
496
+ import {
497
+ DEFAULT_AUTONOMY_LEVEL,
498
+ SKILL_SUGGESTION_PREFIX,
499
+ isSkillActivation,
500
+ skillActivationFromToolDetails
501
+ } from "./chunk-RAPCMZL4.js";
477
502
  import {
478
503
  VERIFICATION_SCRIPT_FAMILY_HINT
479
504
  } from "./chunk-UL3WSD3F.js";
@@ -519,8 +544,8 @@ import {
519
544
  InteropDomainModule,
520
545
  delegationEntryForKind,
521
546
  renderProposalEntry
522
- } from "./chunk-GPIEI3LY.js";
523
- import "./chunk-XWSF374K.js";
547
+ } from "./chunk-QQ7EKM72.js";
548
+ import "./chunk-56KB5IJP.js";
524
549
  import {
525
550
  source_default
526
551
  } from "./chunk-5TSRNF4G.js";
@@ -528,7 +553,11 @@ import {
528
553
  ProvidersDomainModule,
529
554
  VALID_THINKING_LEVELS,
530
555
  applyModelCapabilityPatch,
556
+ canonicalEndpointKey,
557
+ endpointCapacitiesForStatuses,
558
+ endpointCapacityForStatus,
531
559
  firstRuntimeResolutionError,
560
+ foregroundStreamUsage,
532
561
  isDispatchEligibleRuntime,
533
562
  isOrchestratorEligibleRuntime,
534
563
  modelCandidatesForStatus,
@@ -536,6 +565,8 @@ import {
536
565
  modelResidencyForStatus,
537
566
  normalizeCostProvenance,
538
567
  refineRuntimeTargetWithModelHints,
568
+ registerForegroundStream,
569
+ residencyTargetKey,
539
570
  resolveModelCapabilities,
540
571
  resolveModelReference,
541
572
  resolveModelRuntimeCapabilitiesForProviders,
@@ -547,7 +578,7 @@ import {
547
578
  thinkingLevelChoiceLabel,
548
579
  thinkingLevelFromChoiceLabel,
549
580
  withRunOverrides
550
- } from "./chunk-7RFXX52T.js";
581
+ } from "./chunk-MIX5N5AC.js";
551
582
  import "./chunk-CFGTUFWB.js";
552
583
  import "./chunk-LU7P4LHA.js";
553
584
  import {
@@ -561,13 +592,15 @@ import {
561
592
  contextUsageSnapshot,
562
593
  estimateAgentContextBreakdown,
563
594
  estimateAgentContextTokens,
595
+ estimateAgentMessageTokens,
564
596
  estimateReasoningTextTokens,
565
597
  extractReasoningTokens,
566
598
  getLatestContextSnapshot,
599
+ lastLoadedContextWindow,
567
600
  reconcileSnapshot,
568
601
  shouldCompact,
569
602
  snapshotInputTokens
570
- } from "./chunk-RWSI4YD7.js";
603
+ } from "./chunk-AEYBF3TB.js";
571
604
  import {
572
605
  interopAgentKind
573
606
  } from "./chunk-IHXBNWMM.js";
@@ -582,7 +615,7 @@ import {
582
615
  setFleetProfileInSettings,
583
616
  updateSettings,
584
617
  useTargetInSettings
585
- } from "./chunk-IJ7RPIYJ.js";
618
+ } from "./chunk-PNY46YEY.js";
586
619
  import {
587
620
  MAX_TIMER_DELAY_MS,
588
621
  rawDurationMs
@@ -593,7 +626,7 @@ import {
593
626
  import {
594
627
  BusChannels,
595
628
  isRunAbortedPayload
596
- } from "./chunk-TT36MB5S.js";
629
+ } from "./chunk-RKRLDWD3.js";
597
630
  import {
598
631
  atomicWrite,
599
632
  cwdHash,
@@ -601,8 +634,7 @@ import {
601
634
  readSessionTailTurns,
602
635
  sessionCurrentPath,
603
636
  sessionPaths
604
- } from "./chunk-3BINW3FP.js";
605
- import "./chunk-R346GLFC.js";
637
+ } from "./chunk-KV2AOLDF.js";
606
638
  import "./chunk-6EJMN2Y3.js";
607
639
  import {
608
640
  readClioVersion,
@@ -611,19 +643,19 @@ import {
611
643
  import {
612
644
  formatContextWindowSlots,
613
645
  registerBuiltinRuntimes
614
- } from "./chunk-NMPKI6XL.js";
646
+ } from "./chunk-JEQQR47K.js";
615
647
  import {
616
648
  DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS,
617
649
  DEFAULT_SETTINGS,
618
650
  THINKING_LEVELS
619
- } from "./chunk-4DGYLA73.js";
651
+ } from "./chunk-XDOQXGFO.js";
620
652
  import "./chunk-LL4KHSZI.js";
621
653
  import {
622
654
  configureGuardrails
623
655
  } from "./chunk-4ZG3XFUR.js";
624
656
  import {
625
657
  resolveProviderReference
626
- } from "./chunk-IIZWH4XA.js";
658
+ } from "./chunk-774ILSRL.js";
627
659
  import {
628
660
  targetRequiresAuth
629
661
  } from "./chunk-EQ63NRB7.js";
@@ -646,14 +678,15 @@ import {
646
678
  import {
647
679
  AGENT_ROLE_TOOLS_REQUIRED_REASON,
648
680
  supportsAgentRoleTools
649
- } from "./chunk-PT7HYKEM.js";
681
+ } from "./chunk-VO2LKSTM.js";
650
682
  import {
651
683
  StringEnum,
652
684
  cleanupEngineSessionResources,
653
685
  completeEngineText,
654
686
  isEngineRetryableAssistantError,
655
- stream
656
- } from "./chunk-FCSXB6T2.js";
687
+ stream,
688
+ streamSimple
689
+ } from "./chunk-UOSL25KY.js";
657
690
  import "./chunk-YXLYO42X.js";
658
691
  import {
659
692
  init_esm_shims
@@ -4611,10 +4644,29 @@ async function runSideQuestion(input) {
4611
4644
  const { question, ...rest } = input;
4612
4645
  return runOutOfTurnRound({ ...rest, systemPrompt: SIDE_QUESTION_SYSTEM_PROMPT, userText: question });
4613
4646
  }
4647
+ function schemaBindingFor(input) {
4648
+ if (input.responseSchema === void 0 || input.runtimeId === void 0) return null;
4649
+ const dialect = responseSchemaDialectFor(input.runtimeId);
4650
+ return dialect === null ? null : { dialect, ...input.responseSchema };
4651
+ }
4614
4652
  async function runOutOfTurnRound(input) {
4653
+ const binding = schemaBindingFor(input);
4654
+ if (binding === null) return runRound(input, null);
4655
+ try {
4656
+ return await runRound(input, binding);
4657
+ } catch (error) {
4658
+ if (input.signal?.aborted === true) throw error;
4659
+ if (!isResponseSchemaRejection(error instanceof Error ? error.message : String(error))) throw error;
4660
+ return runRound(input, null);
4661
+ }
4662
+ }
4663
+ async function runRound(input, binding) {
4615
4664
  const options = { maxTokens: input.maxTokens ?? SIDE_QUESTION_MAX_TOKENS };
4616
4665
  if (input.apiKey !== void 0) options.apiKey = input.apiKey;
4617
4666
  if (input.signal !== void 0) options.signal = input.signal;
4667
+ if (binding !== null) {
4668
+ options.onPayload = (payload) => patchResponseSchemaPayloadForDialect(payload, binding.dialect, binding.schema, binding.name);
4669
+ }
4618
4670
  const context = {
4619
4671
  systemPrompt: input.systemPrompt,
4620
4672
  messages: [
@@ -4664,13 +4716,31 @@ var HANDOFF_SYSTEM_PROMPT = [
4664
4716
  `The object must match this JSON schema exactly: ${JSON.stringify(HANDOFF_RESPONSE_SCHEMA)}`
4665
4717
  ].join(" ");
4666
4718
  var HANDOFF_MAX_TOKENS = 4096;
4719
+ var HANDOFF_SCHEMA_NAME = "handoff_record";
4720
+ var HANDOFF_REPAIR_QUOTE_CHARS = 600;
4721
+ function repairText(goal, repair) {
4722
+ const quoted = repair.previous.slice(0, HANDOFF_REPAIR_QUOTE_CHARS);
4723
+ return [
4724
+ `The next session's goal is: ${goal}`,
4725
+ "",
4726
+ "Your previous answer could not be read as a handoff record.",
4727
+ `The parser said: ${repair.complaint}`,
4728
+ "This is what you returned, verbatim:",
4729
+ "---",
4730
+ quoted.length > 0 ? quoted : "(nothing)",
4731
+ "---",
4732
+ "Answer again with one JSON object matching the schema and nothing else.",
4733
+ "No prose before it, no prose after it, no code fence."
4734
+ ].join("\n");
4735
+ }
4667
4736
  async function runHandoffRound(input) {
4668
- const { goal, ...rest } = input;
4737
+ const { goal, repair, ...rest } = input;
4669
4738
  return runOutOfTurnRound({
4670
4739
  ...rest,
4671
4740
  maxTokens: input.maxTokens ?? HANDOFF_MAX_TOKENS,
4672
4741
  systemPrompt: HANDOFF_SYSTEM_PROMPT,
4673
- userText: `The next session's goal is: ${goal}
4742
+ responseSchema: { name: HANDOFF_SCHEMA_NAME, schema: HANDOFF_RESPONSE_SCHEMA },
4743
+ userText: repair ? repairText(goal, repair) : `The next session's goal is: ${goal}
4674
4744
 
4675
4745
  Extract the handoff record for that goal now.`
4676
4746
  });
@@ -4877,6 +4947,7 @@ function maskStaleObservations(entries, excludeLastTurns) {
4877
4947
  init_esm_shims();
4878
4948
  import { appendFileSync, existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
4879
4949
  import { dirname, join as join4 } from "node:path";
4950
+ var PROMPT_MANIFEST_VERSION = 2;
4880
4951
  var SHA256_HEX = /^[a-f0-9]{64}$/;
4881
4952
  function isCanonicalUtcTimestamp(value) {
4882
4953
  if (typeof value !== "string") return false;
@@ -4902,9 +4973,18 @@ function isProjectPreloadClass(value) {
4902
4973
  if (!isRecord6(value)) return false;
4903
4974
  return (value.mode === "full" || value.mode === "synopsis" || value.mode === "none") && isNonNegativeInteger(value.chars) && isNonNegativeInteger(value.lines) && (value.reason === null || value.reason === "size" || value.reason === "lines" || value.reason === "no-clio-md") && typeof value.nearLimit === "boolean" && typeof value.label === "string";
4904
4975
  }
4976
+ function isOptional(value, guard) {
4977
+ return value === void 0 || guard(value);
4978
+ }
4905
4979
  function isSessionPromptCompileRecord(value) {
4906
4980
  if (!isRecord6(value)) return false;
4907
- return isCanonicalUtcTimestamp(value.at) && (value.previousHash === null || isSha256(value.previousHash)) && isSha256(value.systemPromptHash) && isNonNegativeInteger(value.tokenEstimate) && (value.thinkingLevel === null || typeof value.thinkingLevel === "string") && (value.projectPreload === null || isProjectPreloadClass(value.projectPreload)) && Array.isArray(value.sections) && value.sections.every(isPromptManifestSection) && Array.isArray(value.fragments) && value.fragments.every(isPromptManifestFragment);
4981
+ return isOptional(value.version, isNonNegativeInteger) && isOptional(
4982
+ value.contextWindow,
4983
+ (candidate) => candidate === null ? true : isNonNegativeInteger(candidate)
4984
+ ) && isOptional(
4985
+ value.contextWindowSource,
4986
+ (candidate) => candidate === null ? true : typeof candidate === "string"
4987
+ ) && isCanonicalUtcTimestamp(value.at) && (value.previousHash === null || isSha256(value.previousHash)) && isSha256(value.systemPromptHash) && isNonNegativeInteger(value.tokenEstimate) && (value.thinkingLevel === null || typeof value.thinkingLevel === "string") && (value.projectPreload === null || isProjectPreloadClass(value.projectPreload)) && Array.isArray(value.sections) && value.sections.every(isPromptManifestSection) && Array.isArray(value.fragments) && value.fragments.every(isPromptManifestFragment);
4908
4988
  }
4909
4989
  function getPromptManifestFilePath(meta, stateDir) {
4910
4990
  const safeMeta = {
@@ -4952,6 +5032,9 @@ function readPromptCompileManifest(meta, stateDir) {
4952
5032
  };
4953
5033
  }
4954
5034
  }
5035
+ function readPromptCompileRecords(meta) {
5036
+ return readPromptCompileManifest(meta).records;
5037
+ }
4955
5038
 
4956
5039
  // src/interactive/model-session-replay.ts
4957
5040
  init_esm_shims();
@@ -5072,6 +5155,160 @@ function formatRetryStatus(status) {
5072
5155
  // src/interactive/renderers/tool-execution.ts
5073
5156
  init_esm_shims();
5074
5157
 
5158
+ // src/interactive/mutation-preview.ts
5159
+ init_esm_shims();
5160
+ import { createHash as createHash2 } from "node:crypto";
5161
+ import { readFileSync as readFileSync5 } from "node:fs";
5162
+ var MUTATION_PREVIEW_VISIBLE_ROWS = 16;
5163
+ var MUTATION_PREVIEW_MAX_CHARS = 262144;
5164
+ function canonicalJson(value) {
5165
+ if (value === null || value === void 0) return "null";
5166
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
5167
+ if (typeof value === "object") {
5168
+ const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5169
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
5170
+ }
5171
+ if (typeof value === "string") return JSON.stringify(value);
5172
+ if (typeof value === "number") return Number.isFinite(value) ? JSON.stringify(value) : "null";
5173
+ if (typeof value === "boolean") return value ? "true" : "false";
5174
+ return "null";
5175
+ }
5176
+ function callArgumentsDigest(args) {
5177
+ return createHash2("sha256").update(canonicalJson(args ?? {}), "utf8").digest("hex").slice(0, 16);
5178
+ }
5179
+ function readEdits(value) {
5180
+ if (!Array.isArray(value)) return null;
5181
+ const edits = [];
5182
+ for (const entry of value) {
5183
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
5184
+ const record2 = entry;
5185
+ if (typeof record2.oldText !== "string" || typeof record2.newText !== "string") return null;
5186
+ edits.push({ oldText: record2.oldText, newText: record2.newText });
5187
+ }
5188
+ return edits;
5189
+ }
5190
+ function mutationFacts(tool, args) {
5191
+ if (!args || typeof args.path !== "string" || args.path.length === 0) return null;
5192
+ if (tool === ToolNames.Write) {
5193
+ if (typeof args.content !== "string") return null;
5194
+ return {
5195
+ kind: "write",
5196
+ bytes: Buffer.byteLength(args.content, "utf8"),
5197
+ replacements: 1,
5198
+ digest: callArgumentsDigest(args)
5199
+ };
5200
+ }
5201
+ if (tool === ToolNames.Edit) {
5202
+ const edits = readEdits(args.edits);
5203
+ if (edits === null || edits.length === 0) return null;
5204
+ const bytes = edits.reduce((total, edit) => total + Buffer.byteLength(edit.newText, "utf8"), 0);
5205
+ return { kind: "edit", bytes, replacements: edits.length, digest: callArgumentsDigest(args) };
5206
+ }
5207
+ return null;
5208
+ }
5209
+ function mutationFactsLine(facts) {
5210
+ const shape = facts.kind === "write" ? `write ${facts.bytes} B` : `edit ${facts.replacements} replacement${facts.replacements === 1 ? "" : "s"} \xB7 ${facts.bytes} B`;
5211
+ return `${shape} \xB7 sha256 ${facts.digest}`;
5212
+ }
5213
+ function numberedLines(text) {
5214
+ const raw = text.split("\n");
5215
+ const width = String(raw.length).length;
5216
+ return raw.map((line, index) => `${String(index + 1).padStart(width, " ")} ${line}`);
5217
+ }
5218
+ function boundedContent(content) {
5219
+ if (content.length <= MUTATION_PREVIEW_MAX_CHARS) return { text: content, withheld: 0 };
5220
+ return {
5221
+ text: content.slice(0, MUTATION_PREVIEW_MAX_CHARS),
5222
+ withheld: content.length - MUTATION_PREVIEW_MAX_CHARS
5223
+ };
5224
+ }
5225
+ function previewFrom(facts, heading, text, options = {}) {
5226
+ const clean = sanitizeMultilineDisplayText(text);
5227
+ const body = options.numbered ? numberedLines(clean.text) : clean.text.split("\n");
5228
+ if (options.trailer !== void 0) body.push(options.trailer);
5229
+ return {
5230
+ facts,
5231
+ heading,
5232
+ body,
5233
+ neutralized: clean.neutralized,
5234
+ tabsExpanded: clean.tabsExpanded
5235
+ };
5236
+ }
5237
+ function refusal(facts, heading, reason) {
5238
+ return { facts, heading, body: [reason], neutralized: false, tabsExpanded: false };
5239
+ }
5240
+ function replacementListing(edits) {
5241
+ const blocks = [];
5242
+ for (const [index, edit] of edits.entries()) {
5243
+ blocks.push(`@@ replacement ${index + 1} of ${edits.length}`);
5244
+ for (const line of normalizeToLF(edit.oldText).split("\n")) blocks.push(`- ${line}`);
5245
+ for (const line of normalizeToLF(edit.newText).split("\n")) blocks.push(`+ ${line}`);
5246
+ }
5247
+ return blocks.join("\n");
5248
+ }
5249
+ function editPreview(facts, path, edits, read) {
5250
+ let current;
5251
+ try {
5252
+ current = read(resolveToCwd(path));
5253
+ } catch (error) {
5254
+ const message = sanitizeCallTargetText(error instanceof Error ? error.message : String(error));
5255
+ return previewFrom(
5256
+ facts,
5257
+ `Requested replacements for ${sanitizeCallTargetText(path)} (the file could not be read: ${message})`,
5258
+ replacementListing(edits)
5259
+ );
5260
+ }
5261
+ try {
5262
+ const { text } = stripBom(current);
5263
+ const applied = applyEditsToNormalizedContent(normalizeToLF(text), edits, path);
5264
+ const diff = generateDiffString(applied.baseContent, applied.newContent);
5265
+ return previewFrom(facts, `Effective diff for ${sanitizeCallTargetText(path)}`, diff.diff);
5266
+ } catch (error) {
5267
+ const message = sanitizeCallTargetText(error instanceof Error ? error.message : String(error));
5268
+ return previewFrom(
5269
+ facts,
5270
+ `Requested replacements for ${sanitizeCallTargetText(path)} (they do not apply: ${message})`,
5271
+ replacementListing(edits)
5272
+ );
5273
+ }
5274
+ }
5275
+ function createMutationInspector(tool, args, facts, options = {}) {
5276
+ const read = options.readFile ?? ((path) => readFileSync5(path, "utf8"));
5277
+ return () => {
5278
+ const current = mutationFacts(tool, args);
5279
+ if (current === null || current.digest !== facts.digest) {
5280
+ return refusal(
5281
+ facts,
5282
+ "Preview unavailable",
5283
+ `These arguments no longer digest to sha256 ${facts.digest}. Deny this call rather than approving bytes that cannot be shown.`
5284
+ );
5285
+ }
5286
+ const path = typeof args?.path === "string" ? args.path : "";
5287
+ if (facts.kind === "write") {
5288
+ const content = typeof args?.content === "string" ? args.content : "";
5289
+ const bounded = boundedContent(content);
5290
+ return previewFrom(facts, `Proposed content for ${sanitizeCallTargetText(path)}`, bounded.text, {
5291
+ numbered: true,
5292
+ ...bounded.withheld > 0 ? { trailer: `\u2026 ${bounded.withheld} more characters not shown (preview capped at ${MUTATION_PREVIEW_MAX_CHARS})` } : {}
5293
+ });
5294
+ }
5295
+ const edits = readEdits(args?.edits) ?? [];
5296
+ return editPreview(facts, path, edits, read);
5297
+ };
5298
+ }
5299
+ function mutationPreviewWindow(lines, scroll, rows = MUTATION_PREVIEW_VISIBLE_ROWS) {
5300
+ const visible = Math.max(1, rows);
5301
+ const maxScroll = Math.max(0, lines.length - visible);
5302
+ const start = Math.max(0, Math.min(scroll, maxScroll));
5303
+ const window = lines.slice(start, start + visible);
5304
+ if (lines.length <= visible) return { window, position: null, maxScroll };
5305
+ return {
5306
+ window,
5307
+ position: `(${start + 1}-${start + window.length} of ${lines.length} lines)`,
5308
+ maxScroll
5309
+ };
5310
+ }
5311
+
5075
5312
  // src/interactive/renderers/diff.ts
5076
5313
  init_esm_shims();
5077
5314
  var ESC = String.fromCharCode(27);
@@ -5546,7 +5783,10 @@ function renderToolAwaitingApproval(call, width, view) {
5546
5783
  const facts = [
5547
5784
  ["action", view.actionClass],
5548
5785
  ["axis", axis],
5549
- ...view.target !== void 0 && view.target.length > 0 ? [["target", view.target]] : []
5786
+ ...view.target !== void 0 && view.target.length > 0 ? [["target", view.target]] : [],
5787
+ // Size and digest, never the mutation text: this row is the transcript,
5788
+ // which is written, replayed, and shared (issue #254).
5789
+ ...view.mutation !== void 0 ? [["mutation", mutationFactsLine(view.mutation)]] : []
5550
5790
  ];
5551
5791
  for (const [label, value] of facts) {
5552
5792
  lines.push(...indentAndWrap(`${dim(`${label} \xB7`)} ${value}`, width, false));
@@ -6582,12 +6822,12 @@ function toggledFold(effective) {
6582
6822
 
6583
6823
  // src/interactive/worker-receipts.ts
6584
6824
  init_esm_shims();
6585
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
6825
+ import { existsSync as existsSync5, readFileSync as readFileSync7 } from "node:fs";
6586
6826
  import { join as join6 } from "node:path";
6587
6827
 
6588
6828
  // src/interactive/view/artifacts.ts
6589
6829
  init_esm_shims();
6590
- import { createReadStream, readFileSync as readFileSync5, statSync } from "node:fs";
6830
+ import { createReadStream, readFileSync as readFileSync6, statSync } from "node:fs";
6591
6831
  import { readFile, realpath, stat } from "node:fs/promises";
6592
6832
  import { basename, isAbsolute as isAbsolute4, join as join5, relative as relative4, resolve as resolve3, sep } from "node:path";
6593
6833
 
@@ -6666,7 +6906,7 @@ var CONTEXT_CATEGORY_TOKEN = {
6666
6906
  project: "muted",
6667
6907
  messages: "accent",
6668
6908
  pending: "accentDeep",
6669
- reserve: "dim",
6909
+ reserve: "frame",
6670
6910
  free: "frame",
6671
6911
  streaming: "accent"
6672
6912
  };
@@ -7530,6 +7770,18 @@ function progressAnswerLines(theme6, progress, runId, contentWidth) {
7530
7770
  }
7531
7771
  return body.map((row2, index) => `${index === 0 ? cardKvKey(theme6, "answer") : " ".repeat(gutter)}${row2}`);
7532
7772
  }
7773
+ function trustCardLines(theme6, row2, contentWidth) {
7774
+ const gutter = CARD_KV_KEY_WIDTH + 1;
7775
+ const valueWidth = Math.max(1, contentWidth - gutter);
7776
+ const trust = row2.trust === void 0 ? theme6.fg("dim", isTerminalStatus(row2.status) ? "receipt not read back" : "not sealed yet") : theme6.fg(
7777
+ trustVerdictToken(row2.trust.verdict),
7778
+ `${trustVerdictGlyph(row2.trust.verdict)} ${row2.trust.verdict}; ${row2.trust.text}`
7779
+ );
7780
+ const host = row2.hostVerification === void 0 ? "" : ` \xB7 ${theme6.fg("muted", `host checks ${row2.hostVerification}`)}`;
7781
+ return wrapTextWithAnsi(`${trust}${host}`, valueWidth).map(
7782
+ (line, index) => `${index === 0 ? cardKvKey(theme6, "trust") : " ".repeat(gutter)}${line}`
7783
+ );
7784
+ }
7533
7785
  function renderDispatchCard(row2, width, evidence, options = {}) {
7534
7786
  const theme6 = clioTheme();
7535
7787
  const contentWidth = Math.max(0, width - 4);
@@ -7561,16 +7813,13 @@ function renderDispatchCard(row2, width, evidence, options = {}) {
7561
7813
  const statusUnits = [
7562
7814
  statusStr,
7563
7815
  theme6.fg("muted", `node ${row2.node ?? "local"}`),
7816
+ ...row2.endpoint !== void 0 ? [theme6.fg("info", `slots ${options.endpointActive ?? 0}/${row2.endpoint.limit} ${row2.endpoint.label}`)] : [],
7564
7817
  ...row2.gate !== void 0 ? [theme6.fg("info", `gate ${row2.gate.role} c${row2.gate.cycle}`)] : [],
7565
7818
  ...row2.rerouteCount !== void 0 && row2.rerouteCount > 0 ? [theme6.fg("warning", `rerouted x${row2.rerouteCount}`)] : [],
7566
7819
  ...row2.failoverHops !== void 0 && row2.failoverHops > 0 ? [theme6.fg("warning", `failed over x${row2.failoverHops}`)] : [],
7567
7820
  `${theme6.fg("dim", "ttft")} ${theme6.fg("muted", ttft)}`,
7568
7821
  `${theme6.fg("dim", "cost")} ${theme6.fg("muted", cost)}`
7569
7822
  ];
7570
- const trustUnits = [
7571
- row2.trust === void 0 ? theme6.fg("dim", isTerminalStatus(row2.status) ? "trust: receipt not read back" : "trust: not sealed yet") : theme6.fg(trustVerdictToken(row2.trust.verdict), `${trustVerdictGlyph(row2.trust.verdict)} ${row2.trust.text}`),
7572
- ...row2.hostVerification !== void 0 ? [theme6.fg("muted", `host checks ${row2.hostVerification}`)] : []
7573
- ];
7574
7823
  const contextUnit = formatWorkerContextMeter(row2.lastContextTokens ?? 0, row2.contextWindow, theme6);
7575
7824
  const phaseCell = formatDispatchPhaseCell(row2.phase, contentWidth);
7576
7825
  const bodyLines2 = [
@@ -7584,7 +7833,7 @@ function renderDispatchCard(row2, width, evidence, options = {}) {
7584
7833
  targetLine,
7585
7834
  ...row2.taskSummary ? [truncateToWidth(`${cardKvKey(theme6, "task")}${theme6.fg("muted", row2.taskSummary)}`, contentWidth, "\u2026", false)] : [],
7586
7835
  cardUnitsLine(theme6, "status", statusUnits, contentWidth),
7587
- cardUnitsLine(theme6, "trust", trustUnits, contentWidth),
7836
+ ...trustCardLines(theme6, row2, contentWidth),
7588
7837
  ...row2.budget !== void 0 ? [
7589
7838
  truncateToWidth(
7590
7839
  `${cardKvKey(theme6, "policy")}${theme6.fg("muted", `${formatBudgetPolicy(row2.budget)}; requested ${formatBudgetRequest(row2.budget)}`)}`,
@@ -7639,6 +7888,16 @@ function renderDispatchCard(row2, width, evidence, options = {}) {
7639
7888
  )
7640
7889
  );
7641
7890
  }
7891
+ if (row2.writeRecordDowngrade) {
7892
+ const warning = describeWriteBoundaryAttributionDowngrade({
7893
+ ...row2.writeRecordDowngrade,
7894
+ runId: row2.runId,
7895
+ stepId: row2.phase?.stepId ?? null
7896
+ });
7897
+ bodyLines2.push(
7898
+ ...wrapTextWithAnsi(`${cardKvKey(theme6, "record")}${theme6.fg("warning", `open: ${warning}`)}`, contentWidth)
7899
+ );
7900
+ }
7642
7901
  if (evidence) {
7643
7902
  const proofLine = evidenceCardLine(theme6, evidence, contentWidth);
7644
7903
  if (proofLine !== null) bodyLines2.push(proofLine);
@@ -7777,10 +8036,17 @@ function formatDispatchBoardLines(rows, width = 76, observability, selectedRunId
7777
8036
  return theme6.fg("dim", " ".repeat(padding) + line);
7778
8037
  });
7779
8038
  }
8039
+ const endpointActive = { ...foregroundStreamUsage() };
8040
+ for (const row2 of rows) {
8041
+ if (row2.endpoint !== void 0 && (row2.status === "running" || row2.status === "stale" || row2.status === "enqueued" || row2.status === "cancelling")) {
8042
+ endpointActive[row2.endpoint.key] = (endpointActive[row2.endpoint.key] ?? 0) + 1;
8043
+ }
8044
+ }
7780
8045
  const cards = dispatchBoardItems(rows, selectedRunId).map(
7781
8046
  (item) => item.kind === "council" ? renderCouncilCard(item.group, width) : renderDispatchCard(item.row, width, deriveRunEvidenceState(observability, item.row.runId), {
7782
8047
  selected: item.row.runId === selectedRunId,
7783
- expanded: detailExpanded && item.row.runId === selectedRunId
8048
+ expanded: detailExpanded && item.row.runId === selectedRunId,
8049
+ ...item.row.endpoint !== void 0 ? { endpointActive: endpointActive[item.row.endpoint.key] ?? 0 } : {}
7784
8050
  })
7785
8051
  );
7786
8052
  const body = [];
@@ -7904,6 +8170,14 @@ function parseGateBadge(value) {
7904
8170
  const cycle = parsePositiveInt(record2.cycle) ?? 1;
7905
8171
  return { role: record2.role, cycle };
7906
8172
  }
8173
+ function parseEndpointCapacity(value) {
8174
+ if (typeof value !== "object" || value === null) return void 0;
8175
+ const record2 = value;
8176
+ const key = parseNonEmptyString(record2.key);
8177
+ const label = parseNonEmptyString(record2.label);
8178
+ const limit = parsePositiveInt(record2.limit);
8179
+ return key === void 0 || label === void 0 || limit === void 0 ? void 0 : { key, label, limit };
8180
+ }
7907
8181
  function parseCouncilBadge(value) {
7908
8182
  if (typeof value !== "object" || value === null) return void 0;
7909
8183
  const record2 = value;
@@ -8055,6 +8329,7 @@ function toRow(entry, now) {
8055
8329
  runtimeId: entry.runtimeId,
8056
8330
  targetId: entry.targetId,
8057
8331
  wireModelId: entry.wireModelId,
8332
+ ...entry.endpoint !== void 0 ? { endpoint: { ...entry.endpoint } } : {},
8058
8333
  ...entry.taskSummary !== void 0 ? { taskSummary: entry.taskSummary } : {},
8059
8334
  ...entry.budget !== void 0 ? { budget: entry.budget } : {},
8060
8335
  status: retry ? "retrying" : entry.status,
@@ -8082,7 +8357,8 @@ function toRow(entry, now) {
8082
8357
  currentTool: retry ? null : progress.currentAction?.tool ?? null,
8083
8358
  recentTools: progress.recentActions.map((action) => action.tool),
8084
8359
  ...retry ? { retry: { ...retry } } : {},
8085
- ...entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}
8360
+ ...entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {},
8361
+ ...entry.writeRecordDowngrade ? { writeRecordDowngrade: { ...entry.writeRecordDowngrade } } : {}
8086
8362
  };
8087
8363
  }
8088
8364
  function sortEntries(a, b) {
@@ -8123,6 +8399,7 @@ function createDispatchBoardStore(bus, snapshot, readReceipt) {
8123
8399
  const agentAudience = parseAgentAudience(raw.agentAudience, previous?.agentAudience);
8124
8400
  const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
8125
8401
  const node = parseNonEmptyString(raw.node) ?? previous?.node;
8402
+ const endpoint = parseEndpointCapacity(raw.endpoint) ?? previous?.endpoint;
8126
8403
  const gate = parseGateBadge(raw.gate) ?? previous?.gate;
8127
8404
  const council = parseCouncilBadge(raw.council) ?? previous?.council;
8128
8405
  const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
@@ -8139,6 +8416,7 @@ function createDispatchBoardStore(bus, snapshot, readReceipt) {
8139
8416
  runtimeId: parseText(raw.runtimeId, previous?.runtimeId ?? "-"),
8140
8417
  targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
8141
8418
  wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
8419
+ ...endpoint !== void 0 ? { endpoint } : {},
8142
8420
  ...taskSummary !== void 0 ? { taskSummary } : {},
8143
8421
  ...budget !== void 0 ? { budget } : {},
8144
8422
  status,
@@ -8166,7 +8444,8 @@ function createDispatchBoardStore(bus, snapshot, readReceipt) {
8166
8444
  ...phase !== void 0 ? { phase } : {},
8167
8445
  progress: previous?.progress ?? createWorkerProgressFold(),
8168
8446
  ...previous?.retry ? { retry: { ...previous.retry } } : {},
8169
- ...previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}
8447
+ ...previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {},
8448
+ ...previous?.writeRecordDowngrade ? { writeRecordDowngrade: { ...previous.writeRecordDowngrade } } : {}
8170
8449
  };
8171
8450
  entries.set(runId, entry);
8172
8451
  pruneEntries(entries, (evicted) => fleetPhases.delete(evicted));
@@ -8293,6 +8572,19 @@ function createDispatchBoardStore(bus, snapshot, readReceipt) {
8293
8572
  entry.ttftMs = Math.round(performance3.now() - entry.startedAtClockMs);
8294
8573
  }
8295
8574
  }
8575
+ if (type === "clio_write_record_downgraded") {
8576
+ const rawPayload = workerEvent.payload;
8577
+ const payload2 = typeof rawPayload === "object" && rawPayload !== null ? rawPayload : null;
8578
+ const tool = payload2 === null ? void 0 : parseNonEmptyString(payload2.tool);
8579
+ const toolCallId = payload2 === null ? void 0 : parseNonEmptyString(payload2.toolCallId);
8580
+ if (payload2?.reason === "opaque_tool_succeeded" && tool !== void 0 && toolCallId !== void 0) {
8581
+ entry.writeRecordDowngrade = {
8582
+ reason: "opaque_tool_succeeded",
8583
+ tool: sanitizeCallTargetText(tool),
8584
+ toolCallId: sanitizeCallTargetText(toolCallId)
8585
+ };
8586
+ }
8587
+ }
8296
8588
  entry.progress.observe(payload.event);
8297
8589
  if (type === "clio_steer_received") {
8298
8590
  const steerPayload = workerEvent.payload;
@@ -8736,8 +9028,41 @@ function retryOverlay(status) {
8736
9028
  }
8737
9029
  function reduceStatus(prev, event, ctx) {
8738
9030
  switch (event.type) {
8739
- case "agent_start":
9031
+ // A consumed prompt occupies `preparing` before any engine event exists,
9032
+ // and the previous turn's summary rides along untouched: a refused
9033
+ // admission puts the footer back on the last turn that actually ran,
9034
+ // which is still the truth about what happened.
9035
+ case "submit_accepted":
8740
9036
  if (isActive(prev.phase)) return prev;
9037
+ return {
9038
+ ...prev,
9039
+ phase: "preparing",
9040
+ since: ctx.now,
9041
+ lastMeaningfulAt: ctx.now,
9042
+ localRuntime: ctx.localRuntime,
9043
+ preparingSubmission: true,
9044
+ watchdogTier: 0,
9045
+ resumePhase: void 0,
9046
+ activePhases: void 0,
9047
+ overlayStack: [],
9048
+ tool: void 0,
9049
+ toolStartedAt: void 0,
9050
+ retry: void 0,
9051
+ dispatch: void 0
9052
+ };
9053
+ case "submit_settled":
9054
+ if (prev.preparingSubmission !== true) return prev;
9055
+ return {
9056
+ ...prev,
9057
+ phase: prev.summary ? "ended" : "idle",
9058
+ since: ctx.now,
9059
+ preparingSubmission: void 0,
9060
+ resumePhase: void 0,
9061
+ activePhases: void 0,
9062
+ overlayStack: []
9063
+ };
9064
+ case "agent_start":
9065
+ if (isActive(prev.phase) && prev.preparingSubmission !== true) return prev;
8741
9066
  return {
8742
9067
  ...INITIAL_STATUS,
8743
9068
  phase: "preparing",
@@ -9057,6 +9382,13 @@ function createStatusController(deps) {
9057
9382
  }
9058
9383
  notify(prev, status, force);
9059
9384
  };
9385
+ if (deps.chat.onTurnPreparation) {
9386
+ unsubscribes.push(
9387
+ deps.chat.onTurnPreparation((phase) => {
9388
+ apply({ type: phase === "idle" ? "submit_settled" : "submit_accepted" }, true);
9389
+ })
9390
+ );
9391
+ }
9060
9392
  unsubscribes.push(
9061
9393
  deps.chat.onEvent((event) => {
9062
9394
  if (event.type === "agent_status") return;
@@ -10151,7 +10483,7 @@ function runLedgerPath(stateDir) {
10151
10483
  }
10152
10484
  function readRunLedger(stateDir) {
10153
10485
  try {
10154
- const raw = readFileSync5(runLedgerPath(stateDir), "utf8").trim();
10486
+ const raw = readFileSync6(runLedgerPath(stateDir), "utf8").trim();
10155
10487
  if (raw.length === 0) return [];
10156
10488
  const parsed = JSON.parse(raw);
10157
10489
  return Array.isArray(parsed) ? parsed.filter(isRecord9) : [];
@@ -10195,7 +10527,7 @@ function validateToolStats(value) {
10195
10527
  }
10196
10528
  function receiptTrustDetail(stateDir, runId) {
10197
10529
  try {
10198
- const receipt = JSON.parse(readFileSync5(receiptFilePath(stateDir, runId), "utf8"));
10530
+ const receipt = JSON.parse(readFileSync6(receiptFilePath(stateDir, runId), "utf8"));
10199
10531
  const ledger = readRunEnvelope(stateDir, runId);
10200
10532
  return formatTrustSummaryLine(inspectRunReceiptTrustStatus(receipt, ledger.ok ? ledger.envelope : null).status);
10201
10533
  } catch {
@@ -10213,7 +10545,7 @@ function verifyReceiptFile(stateDir, runId) {
10213
10545
  const target = receiptFilePath(stateDir, runId);
10214
10546
  let raw;
10215
10547
  try {
10216
- raw = readFileSync5(target, "utf8");
10548
+ raw = readFileSync6(target, "utf8");
10217
10549
  } catch (err) {
10218
10550
  const e = err;
10219
10551
  if (e.code === "ENOENT") return { ok: false, reason: "receipt file not found" };
@@ -11340,7 +11672,7 @@ function workerReceiptFacts(receipt) {
11340
11672
  function readReceiptFileFacts(runId, stateDir) {
11341
11673
  let parsed;
11342
11674
  try {
11343
- parsed = JSON.parse(readFileSync6(receiptFilePath(stateDir, runId), "utf8"));
11675
+ parsed = JSON.parse(readFileSync7(receiptFilePath(stateDir, runId), "utf8"));
11344
11676
  } catch {
11345
11677
  return null;
11346
11678
  }
@@ -11359,7 +11691,7 @@ function findRunRow(runId, stateDir) {
11359
11691
  const path = join6(stateDir, "runs.json");
11360
11692
  if (!existsSync5(path)) return null;
11361
11693
  try {
11362
- const parsed = JSON.parse(readFileSync6(path, "utf8"));
11694
+ const parsed = JSON.parse(readFileSync7(path, "utf8"));
11363
11695
  if (!Array.isArray(parsed)) return null;
11364
11696
  for (const entry of parsed) {
11365
11697
  if (isRecord10(entry) && entry.id === runId) return entry;
@@ -12536,18 +12868,27 @@ function createTurnContext(deps) {
12536
12868
  const { state, middleware } = deps;
12537
12869
  const compactionTrigger = new AutoCompactionTrigger();
12538
12870
  let currentContextSnapshot = null;
12871
+ let reconciledAnchor = null;
12539
12872
  let lastCompactionEvent = null;
12540
12873
  let lastPromptCache = null;
12541
12874
  let lastSystemPromptReused = false;
12875
+ let lastPrewarm = null;
12542
12876
  let sessionPrompt = null;
12543
12877
  let sessionPromptKey = null;
12878
+ let sessionPromptHash = null;
12879
+ let resumedPromptHashRead = false;
12880
+ let resumedPromptHash = null;
12544
12881
  const sessionWorkingContextPaths = /* @__PURE__ */ new Set();
12545
12882
  let pendingPromptLogEntry = null;
12546
12883
  let emptyAutoCompactTurnId = null;
12547
12884
  const pendingColdReasons = /* @__PURE__ */ new Set();
12548
12885
  let runExpectedColdReasons = [];
12549
12886
  let nextAssistantColdReasons = [];
12550
- const TIER_INDEPENDENT_COLD_REASONS = /* @__PURE__ */ new Set(["working_set_evict"]);
12887
+ const TIER_INDEPENDENT_COLD_REASONS = /* @__PURE__ */ new Set([
12888
+ "working_set_evict",
12889
+ "tool_surface_change",
12890
+ "prompt_recompiled"
12891
+ ]);
12551
12892
  const stampsOnTier = (reason, runtimeId) => TIER_INDEPENDENT_COLD_REASONS.has(reason) || runtimeId !== void 0 && deps.providers.getRuntime(runtimeId)?.tier === "local-native";
12552
12893
  const noteColdReason = (reason) => {
12553
12894
  if (!state.streaming) {
@@ -12571,6 +12912,21 @@ function createTurnContext(deps) {
12571
12912
  noteColdReason("compaction");
12572
12913
  }) ?? null
12573
12914
  ),
12915
+ deps.bus?.on(BusChannels.ResidencyMutation, (payload) => {
12916
+ const runtime = state.runtime;
12917
+ const model = runtime?.agent.state.model;
12918
+ const baseUrl = typeof model?.baseUrl === "string" ? model.baseUrl : null;
12919
+ if (payload.targetKey !== residencyTargetKey(runtime?.runtimeId ?? "", baseUrl)) return;
12920
+ noteColdReason("residency");
12921
+ }) ?? null,
12922
+ // A memory step on another endpoint disturbs nothing here, so the stamp is
12923
+ // gated on the step having called the very server this session streams to.
12924
+ deps.bus?.on(BusChannels.MemoryStepCompleted, (payload) => {
12925
+ const target = state.runtime?.runtimeResolution.target;
12926
+ if (target === void 0) return;
12927
+ if (canonicalEndpointKey(target) !== payload.endpointKey) return;
12928
+ noteColdReason("background_memory");
12929
+ }) ?? null,
12574
12930
  deps.bus?.on(BusChannels.ContextRecalled, (payload) => {
12575
12931
  middleware.fireCompactionHook("working_set_recall", payload.trigger);
12576
12932
  }) ?? null
@@ -12625,6 +12981,23 @@ function createTurnContext(deps) {
12625
12981
  }
12626
12982
  return ceilChars(snapshot.pendingUserInput.length);
12627
12983
  };
12984
+ const rememberedLoadedContextWindow = (targetId, modelId) => {
12985
+ const session = deps.session?.current();
12986
+ if (!session) return null;
12987
+ return lastLoadedContextWindow(session, targetId, modelId);
12988
+ };
12989
+ const lastRecordedPromptHash = () => {
12990
+ if (resumedPromptHashRead) return resumedPromptHash;
12991
+ resumedPromptHashRead = true;
12992
+ const currentSession = deps.session?.current();
12993
+ if (!currentSession) {
12994
+ resumedPromptHashRead = false;
12995
+ return null;
12996
+ }
12997
+ const records = readPromptCompileRecords(currentSession);
12998
+ resumedPromptHash = records[records.length - 1]?.systemPromptHash ?? null;
12999
+ return resumedPromptHash;
13000
+ };
12628
13001
  const resolveWindowWithoutRuntime = () => {
12629
13002
  const settings = deps.getSettings();
12630
13003
  const targetId = settings.orchestrator?.target?.trim();
@@ -12636,7 +13009,8 @@ function createTurnContext(deps) {
12636
13009
  requestedThinkingLevel: settings.orchestrator?.thinkingLevel ?? "off",
12637
13010
  use: "orchestrator",
12638
13011
  requireTools: false,
12639
- requireOutputBudget: true
13012
+ requireOutputBudget: true,
13013
+ knownLoadedContextWindow: rememberedLoadedContextWindow(targetId, wireModelId)
12640
13014
  });
12641
13015
  return resolved.ok ? resolved.target.contextWindowDetails : null;
12642
13016
  };
@@ -12669,6 +13043,19 @@ function createTurnContext(deps) {
12669
13043
  }
12670
13044
  return { contextWindow: 0, contextWindowSource: null, contextWindowSlots: null };
12671
13045
  };
13046
+ const reconciledAnchoredTokens = (agentRuntime, pendingUserTokens) => {
13047
+ const anchor = reconciledAnchor;
13048
+ if (!anchor) return null;
13049
+ const messages = agentRuntime.agent.state.messages;
13050
+ if (anchor.anchoredMessageCount > messages.length) return null;
13051
+ let tail = 0;
13052
+ for (let i = anchor.anchoredMessageCount; i < messages.length; i += 1) {
13053
+ const message = messages[i];
13054
+ if (message === void 0) continue;
13055
+ tail += estimateAgentMessageTokens(message);
13056
+ }
13057
+ return anchor.tokens + tail + pendingUserTokens;
13058
+ };
12672
13059
  const liveContextEstimate = (agentRuntime, pendingUserText) => {
12673
13060
  const contextWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
12674
13061
  const estimateInput = {
@@ -12677,10 +13064,25 @@ function createTurnContext(deps) {
12677
13064
  tools: agentRuntime.agent.state.tools,
12678
13065
  ...pendingUserText !== void 0 ? { pendingUserText } : {}
12679
13066
  };
13067
+ const breakdown = estimateAgentContextBreakdown(estimateInput);
13068
+ const estimatedTokens = estimateAgentContextTokens(estimateInput);
13069
+ const reconciledTokens = reconciledAnchoredTokens(agentRuntime, breakdown.pendingUserTokens);
12680
13070
  return {
12681
- tokens: estimateAgentContextTokens(estimateInput),
13071
+ // The estimate is a floor, not a competing verdict: it prices material
13072
+ // the attested call never saw, so a provider count below it would be
13073
+ // answering about a smaller conversation.
13074
+ tokens: Math.max(estimatedTokens, reconciledTokens ?? 0),
13075
+ estimatedTokens,
13076
+ reconciledTokens,
12682
13077
  contextWindow,
12683
- breakdown: estimateAgentContextBreakdown(estimateInput)
13078
+ breakdown
13079
+ };
13080
+ };
13081
+ const carryReconciledAnchorThroughProjection = (agentRuntime, tokensRemoved) => {
13082
+ if (!reconciledAnchor) return;
13083
+ reconciledAnchor = {
13084
+ tokens: Math.max(0, reconciledAnchor.tokens - Math.max(0, tokensRemoved)),
13085
+ anchoredMessageCount: agentRuntime.agent.state.messages.length
12684
13086
  };
12685
13087
  };
12686
13088
  const refreshAgentMessagesFromSession = (agentRuntime) => {
@@ -12694,7 +13096,7 @@ function createTurnContext(deps) {
12694
13096
  const emitCompactionActivity = (status, message) => {
12695
13097
  deps.bus?.emit(BusChannels.ContextActivity, {
12696
13098
  kind: "compaction",
12697
- phase: "done",
13099
+ phase: status === "completed" ? "done" : "compact",
12698
13100
  status,
12699
13101
  message,
12700
13102
  at: Date.now()
@@ -12761,6 +13163,7 @@ function createTurnContext(deps) {
12761
13163
  emitCompactionActivity("started", "compacting context (mask stage)");
12762
13164
  deps.session.replaceEntries(masked.entries);
12763
13165
  refreshAgentMessagesFromSession(agentRuntime);
13166
+ reconciledAnchor = null;
12764
13167
  deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
12765
13168
  const postMaskSnapshot = captureRuntimeContextSnapshot(
12766
13169
  agentRuntime,
@@ -12839,6 +13242,7 @@ function createTurnContext(deps) {
12839
13242
  });
12840
13243
  noteColdReason("working_set_evict");
12841
13244
  refreshAgentMessagesFromSession(agentRuntime);
13245
+ carryReconciledAnchorThroughProjection(agentRuntime, planned.tokensBefore - planned.tokensAfter);
12842
13246
  const postEvictionSnapshot = captureRuntimeContextSnapshot(
12843
13247
  agentRuntime,
12844
13248
  state.activeUserTurnId || "compaction",
@@ -12914,6 +13318,7 @@ function createTurnContext(deps) {
12914
13318
  deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
12915
13319
  recordCompactionUsage(agentRuntime, result);
12916
13320
  refreshAgentMessagesFromSession(agentRuntime);
13321
+ reconciledAnchor = null;
12917
13322
  const postCompactSnapshot = captureRuntimeContextSnapshot(
12918
13323
  agentRuntime,
12919
13324
  state.activeUserTurnId || "compaction",
@@ -12966,6 +13371,7 @@ function createTurnContext(deps) {
12966
13371
  captureRuntimeContextSnapshot,
12967
13372
  persistContextSnapshot,
12968
13373
  liveContextEstimate,
13374
+ rememberedLoadedContextWindow,
12969
13375
  refreshAgentMessagesFromSession,
12970
13376
  runAutoCompact,
12971
13377
  setCurrentSnapshot(snapshot) {
@@ -12976,6 +13382,14 @@ function createTurnContext(deps) {
12976
13382
  persistContextSnapshot(currentContextSnapshot);
12977
13383
  },
12978
13384
  reconcileUsage(usage) {
13385
+ const promptTokens = (usage.input || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
13386
+ const messages = state.runtime?.agent.state.messages;
13387
+ if (promptTokens > 0 && messages) {
13388
+ reconciledAnchor = {
13389
+ tokens: promptTokens + (usage.output || 0),
13390
+ anchoredMessageCount: messages.length
13391
+ };
13392
+ }
12979
13393
  if (!currentContextSnapshot) return;
12980
13394
  currentContextSnapshot = reconcileSnapshot(currentContextSnapshot, usage);
12981
13395
  snapshotPersistPending = true;
@@ -13003,7 +13417,10 @@ function createTurnContext(deps) {
13003
13417
  return sessionPrompt;
13004
13418
  }
13005
13419
  const modelState = agentRuntime.agent.state.model;
13006
- const contextWindow = typeof modelState?.contextWindow === "number" ? modelState.contextWindow : null;
13420
+ const windowDetails = agentRuntime.runtimeResolution.contextWindowDetails;
13421
+ const resolvedWindow = windowDetails.effectiveContextWindow;
13422
+ const contextWindow = typeof resolvedWindow === "number" && resolvedWindow > 0 ? resolvedWindow : null;
13423
+ const contextWindowSource = contextWindow === null ? null : windowDetails.contextWindowSource;
13007
13424
  const guidance = modelState?.clio?.quirks?.thinking?.guidance;
13008
13425
  const toolNames = toolNamesFromAgentState(agentRuntime.agent.state.tools);
13009
13426
  const toolPromptHints = toolNames.flatMap((name) => {
@@ -13036,16 +13453,19 @@ function createTurnContext(deps) {
13036
13453
  cwd: process.cwd(),
13037
13454
  workingContextPaths: [...sessionWorkingContextPaths]
13038
13455
  });
13039
- const previousHash = sessionPrompt?.systemPromptHash ?? null;
13456
+ const previousHash = sessionPromptHash ?? lastRecordedPromptHash();
13040
13457
  const changed = agentRuntime.agent.state.systemPrompt !== result.systemPrompt;
13041
13458
  if (changed) {
13042
13459
  agentRuntime.agent.state.systemPrompt = result.systemPrompt;
13043
13460
  pendingPromptLogEntry = {
13461
+ version: PROMPT_MANIFEST_VERSION,
13044
13462
  at: (/* @__PURE__ */ new Date()).toISOString(),
13045
13463
  previousHash,
13046
13464
  systemPromptHash: result.systemPromptHash,
13047
13465
  tokenEstimate: result.tokenEstimate,
13048
13466
  thinkingLevel: agentRuntime.agent.state.thinkingLevel ?? null,
13467
+ contextWindow,
13468
+ contextWindowSource,
13049
13469
  projectPreload: result.projectPreload ?? null,
13050
13470
  sections: result.sections.map((s) => ({ id: s.id, tokenEstimate: s.tokenEstimate })),
13051
13471
  fragments: result.fragmentManifest.map((f) => ({
@@ -13058,6 +13478,7 @@ function createTurnContext(deps) {
13058
13478
  }
13059
13479
  lastSystemPromptReused = !changed;
13060
13480
  sessionPrompt = result;
13481
+ sessionPromptHash = result.systemPromptHash;
13061
13482
  sessionPromptKey = key;
13062
13483
  return result;
13063
13484
  } catch (err) {
@@ -13089,6 +13510,9 @@ function createTurnContext(deps) {
13089
13510
  tokenEstimate: entry.tokenEstimate
13090
13511
  }
13091
13512
  });
13513
+ if (entry.previousHash !== null && entry.previousHash !== entry.systemPromptHash) {
13514
+ noteColdReason("prompt_recompiled");
13515
+ }
13092
13516
  } catch {
13093
13517
  }
13094
13518
  appendPromptCompileRecord(currentMeta, entry);
@@ -13159,7 +13583,8 @@ function createTurnContext(deps) {
13159
13583
  toolCount: liveToolCount,
13160
13584
  compactionThreshold,
13161
13585
  compactionAuto,
13162
- promptCache: lastPromptCache
13586
+ promptCache: lastPromptCache,
13587
+ prewarm: lastPrewarm
13163
13588
  });
13164
13589
  }
13165
13590
  const streamingOutput = liveStreamingOutputTokens();
@@ -13189,7 +13614,8 @@ function createTurnContext(deps) {
13189
13614
  liveTotalTokens: totalUsed > 0 ? totalUsed : null,
13190
13615
  measured,
13191
13616
  lastCompaction: lastCompactionEvent,
13192
- promptCache: lastPromptCache
13617
+ promptCache: lastPromptCache,
13618
+ prewarm: lastPrewarm
13193
13619
  });
13194
13620
  },
13195
13621
  emitContextWindowWarningTransition(warning) {
@@ -13197,6 +13623,7 @@ function createTurnContext(deps) {
13197
13623
  lastContextWindowWarning = warning;
13198
13624
  deps.bus?.emit(BusChannels.ContextWarning, { warning });
13199
13625
  },
13626
+ noteColdReason,
13200
13627
  consumeExpectedColdReasons(runtimeId) {
13201
13628
  runExpectedColdReasons = [];
13202
13629
  nextAssistantColdReasons = [];
@@ -13210,7 +13637,7 @@ function createTurnContext(deps) {
13210
13637
  }
13211
13638
  }
13212
13639
  },
13213
- promptCachePayloadForAssistant(usage) {
13640
+ promptCachePayloadForAssistant(usage, backend) {
13214
13641
  const input = typeof usage.input === "number" ? usage.input : 0;
13215
13642
  const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
13216
13643
  const cacheWrite = typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0;
@@ -13218,34 +13645,54 @@ function createTurnContext(deps) {
13218
13645
  input,
13219
13646
  cacheRead,
13220
13647
  cacheWrite,
13221
- backendVerdict: backendCacheVerdict(input, cacheRead)
13648
+ backendVerdict: backendCacheVerdict(input, cacheRead, backend)
13222
13649
  };
13650
+ if (backend !== void 0) promptCache.backend = { ...backend };
13223
13651
  if (nextAssistantColdReasons.length > 0) {
13224
13652
  promptCache.expectedColdReasons = [...nextAssistantColdReasons];
13225
13653
  nextAssistantColdReasons = [];
13226
13654
  }
13227
13655
  return promptCache;
13228
13656
  },
13657
+ notePrewarm(prewarm) {
13658
+ lastPrewarm = prewarm;
13659
+ },
13229
13660
  noteRunCacheSummary(messages, runFirstCallVerdict) {
13661
+ lastPrewarm = null;
13230
13662
  const cacheSummary = sumRunUsage(messages);
13231
13663
  if (cacheSummary.hadUsage) {
13664
+ let lastBackend = null;
13665
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
13666
+ const message = messages[index];
13667
+ if (message?.role !== "assistant") continue;
13668
+ lastBackend = message.backendTimings ?? null;
13669
+ break;
13670
+ }
13232
13671
  lastPromptCache = {
13233
13672
  shellReused: lastSystemPromptReused,
13234
13673
  cacheReadTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheRead : null,
13235
13674
  cacheWriteTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheWrite : null,
13236
13675
  uncachedInputTokens: cacheSummary.input,
13676
+ backend: lastBackend,
13677
+ uncachedPrefillTokens: uncachedPrefillTokens(lastBackend),
13237
13678
  backendVerdict: runFirstCallVerdict,
13238
13679
  ...runExpectedColdReasons.length > 0 ? { expectedColdReasons: [...runExpectedColdReasons] } : {}
13239
13680
  };
13240
13681
  }
13241
13682
  },
13242
13683
  resetForSession() {
13684
+ if (sessionPromptHash !== null) noteColdReason("prompt_recompiled");
13243
13685
  lastPromptCache = null;
13686
+ lastPrewarm = null;
13244
13687
  lastSystemPromptReused = false;
13688
+ sessionPromptHash = null;
13245
13689
  sessionPromptKey = null;
13690
+ resumedPromptHashRead = false;
13691
+ resumedPromptHash = null;
13246
13692
  sessionWorkingContextPaths.clear();
13247
13693
  pendingPromptLogEntry = null;
13248
13694
  emptyAutoCompactTurnId = null;
13695
+ reconciledAnchor = null;
13249
13696
  const session = deps.session?.current();
13250
13697
  currentContextSnapshot = session ? getLatestContextSnapshot(session) : null;
13251
13698
  },
@@ -13542,7 +13989,8 @@ function createTurnPersistence(deps) {
13542
13989
  if (timing) payload.timing = timing;
13543
13990
  const usage = message.usage;
13544
13991
  if (usage && typeof usage === "object") {
13545
- payload.promptCache = deps.promptCachePayloadForAssistant(usage);
13992
+ const backendTimings = message.backendTimings;
13993
+ payload.promptCache = deps.promptCachePayloadForAssistant(usage, backendTimings);
13546
13994
  }
13547
13995
  const interruptedUsage = estimatedUsageForInterruptedTurn(message, deps.promptSideTokens());
13548
13996
  if (interruptedUsage !== null) payload.usage = interruptedUsage;
@@ -13583,8 +14031,8 @@ function createTurnPersistence(deps) {
13583
14031
  appendAssistantTurn,
13584
14032
  appendQueuedUserTurn(message) {
13585
14033
  if (message?.role !== "user") return;
13586
- const text = extractUserText(message).trim();
13587
- if (text.length === 0) return;
14034
+ const text = extractUserText(message);
14035
+ if (text.trim().length === 0) return;
13588
14036
  if (deps.consumePersistedEcho(text)) return;
13589
14037
  deps.removeQueuedMirrorEntry(text);
13590
14038
  if (!deps.session) return;
@@ -13745,6 +14193,266 @@ function createTurnPersistence(deps) {
13745
14193
  };
13746
14194
  }
13747
14195
 
14196
+ // src/interactive/turn-prewarm.ts
14197
+ init_esm_shims();
14198
+
14199
+ // src/interactive/prewarm.ts
14200
+ init_esm_shims();
14201
+ var PREWARM_USER_TEXT = ".";
14202
+ var PREWARM_MAX_TOKENS = 1;
14203
+ var LLM_MESSAGE_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "toolResult"]);
14204
+ function prewarmPrefix(state) {
14205
+ return {
14206
+ systemPrompt: state.systemPrompt,
14207
+ messages: state.messages.filter((message) => LLM_MESSAGE_ROLES.has(message.role ?? "")),
14208
+ tools: [...state.tools]
14209
+ };
14210
+ }
14211
+ function usageOf(message) {
14212
+ if (message === null || typeof message !== "object") return null;
14213
+ const usage = message.usage;
14214
+ return usage !== null && typeof usage === "object" ? usage : null;
14215
+ }
14216
+ function backendOf(message) {
14217
+ if (message === null || typeof message !== "object") return null;
14218
+ const backend = message.backendTimings;
14219
+ return backend !== null && typeof backend === "object" ? backend : null;
14220
+ }
14221
+ function prewarmPromptTokens(result) {
14222
+ if (result.backend) return result.backend.promptTokens;
14223
+ const usage = result.usage;
14224
+ if (!usage) return null;
14225
+ const total = (usage.input || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
14226
+ return total > 0 ? total : null;
14227
+ }
14228
+ async function runPrewarmRound(input) {
14229
+ const prefix = prewarmPrefix(input.state);
14230
+ const context = {
14231
+ systemPrompt: prefix.systemPrompt,
14232
+ messages: [
14233
+ ...prefix.messages,
14234
+ { role: "user", content: [{ type: "text", text: PREWARM_USER_TEXT }], timestamp: Date.now() }
14235
+ ],
14236
+ tools: prefix.tools
14237
+ };
14238
+ const options = { maxTokens: PREWARM_MAX_TOKENS };
14239
+ if (input.state.thinkingLevel !== "off") options.reasoning = input.state.thinkingLevel;
14240
+ if (input.apiKey !== void 0) options.apiKey = input.apiKey;
14241
+ if (input.signal !== void 0) options.signal = input.signal;
14242
+ const send = input.streamFn ?? streamSimple;
14243
+ const startedAt = performance.now();
14244
+ let firstDeltaAt = null;
14245
+ const elapsed = () => Math.round(Math.max(0, performance.now() - startedAt));
14246
+ const ttft = () => firstDeltaAt === null ? null : Math.round(Math.max(0, firstDeltaAt - startedAt));
14247
+ try {
14248
+ const events = send(
14249
+ input.model,
14250
+ context,
14251
+ options
14252
+ );
14253
+ for await (const event of events) {
14254
+ if (firstDeltaAt === null && event.type !== "start") firstDeltaAt = performance.now();
14255
+ if (event.type === "done") {
14256
+ return {
14257
+ aborted: false,
14258
+ usage: usageOf(event.message),
14259
+ backend: backendOf(event.message),
14260
+ timing: { ttftMs: ttft(), apiMs: elapsed() },
14261
+ errorMessage: null
14262
+ };
14263
+ }
14264
+ if (event.type === "error") {
14265
+ const failed = event.error;
14266
+ const aborted = event.reason === "aborted" || failed.stopReason === "aborted" || input.signal?.aborted === true;
14267
+ return {
14268
+ aborted,
14269
+ usage: usageOf(event.error),
14270
+ backend: backendOf(event.error),
14271
+ timing: { ttftMs: ttft(), apiMs: elapsed() },
14272
+ errorMessage: aborted ? null : typeof failed.errorMessage === "string" ? failed.errorMessage : "pre-warm failed"
14273
+ };
14274
+ }
14275
+ }
14276
+ } catch (error) {
14277
+ return {
14278
+ aborted: input.signal?.aborted === true,
14279
+ usage: null,
14280
+ backend: null,
14281
+ timing: { ttftMs: ttft(), apiMs: elapsed() },
14282
+ errorMessage: input.signal?.aborted === true ? null : error instanceof Error ? error.message : String(error)
14283
+ };
14284
+ }
14285
+ return {
14286
+ aborted: input.signal?.aborted === true,
14287
+ usage: null,
14288
+ backend: null,
14289
+ timing: { ttftMs: ttft(), apiMs: elapsed() },
14290
+ errorMessage: null
14291
+ };
14292
+ }
14293
+
14294
+ // src/interactive/turn-prewarm.ts
14295
+ var ABORT_ROUND_ON_SUBMIT = false;
14296
+ function createTurnPrewarm(deps) {
14297
+ const round = deps.runPrewarm ?? runPrewarmRound;
14298
+ const abortOnSubmit = deps.abortRoundOnSubmit ?? ABORT_ROUND_ON_SUBMIT;
14299
+ let pendingTrigger = null;
14300
+ let timer = null;
14301
+ let active = null;
14302
+ const detached = /* @__PURE__ */ new WeakSet();
14303
+ let inFlight = Promise.resolve(null);
14304
+ let dispatchesInFlight = 0;
14305
+ const unsubscribeDispatch = [
14306
+ deps.bus?.on(BusChannels.DispatchStarted, () => {
14307
+ dispatchesInFlight += 1;
14308
+ }) ?? null,
14309
+ ...[BusChannels.DispatchCompleted, BusChannels.DispatchFailed].map(
14310
+ (channel) => deps.bus?.on(channel, () => {
14311
+ dispatchesInFlight = Math.max(0, dispatchesInFlight - 1);
14312
+ }) ?? null
14313
+ )
14314
+ ];
14315
+ const dispatchActive = () => dispatchesInFlight > 0 || deps.hasActiveDispatch();
14316
+ const admissionRefusal = () => {
14317
+ if (deps.getSettings().prewarm?.enabled === false) return "disabled";
14318
+ if (!deps.isLatencySurface()) return "surface";
14319
+ if (deps.state.streaming || deps.isTurnActive()) return "turn-active";
14320
+ if (dispatchActive()) return "dispatch-active";
14321
+ const targetId = deps.getSettings().orchestrator?.target?.trim();
14322
+ const runtimeId = targetId ? deps.providers.getTarget(targetId)?.runtime : void 0;
14323
+ if (runtimeId === void 0 || deps.providers.getRuntime(runtimeId)?.tier !== "local-native") return "tier";
14324
+ return null;
14325
+ };
14326
+ const runOnce = async (trigger, controller) => {
14327
+ const refusal2 = admissionRefusal();
14328
+ if (refusal2) return { ran: false, reason: refusal2 };
14329
+ const prepared = await deps.prepareRuntime(controller.signal);
14330
+ if (!prepared.ok) return { ran: false, reason: "unresolved" };
14331
+ const runtime = prepared.runtime;
14332
+ if (deps.providers.getRuntime(runtime.runtimeId)?.tier !== "local-native") {
14333
+ return { ran: false, reason: "tier" };
14334
+ }
14335
+ deps.applySessionTools(runtime);
14336
+ await deps.context.ensureSessionPrompt(runtime);
14337
+ const lateRefusal = admissionRefusal();
14338
+ if (lateRefusal) return { ran: false, reason: lateRefusal };
14339
+ if (detached.has(controller) || controller.signal.aborted) return { ran: false, reason: "superseded" };
14340
+ const releaseEndpointSlot = deps.registerEndpointSlot?.(runtime) ?? null;
14341
+ let result;
14342
+ try {
14343
+ result = await round({
14344
+ model: runtime.agent.state.model,
14345
+ state: {
14346
+ systemPrompt: runtime.agent.state.systemPrompt,
14347
+ messages: runtime.agent.state.messages,
14348
+ tools: runtime.agent.state.tools,
14349
+ thinkingLevel: runtime.agent.state.thinkingLevel ?? "off"
14350
+ },
14351
+ ...prepared.apiKey !== void 0 ? { apiKey: prepared.apiKey } : {},
14352
+ signal: controller.signal
14353
+ });
14354
+ } finally {
14355
+ releaseEndpointSlot?.();
14356
+ }
14357
+ const wasDetached = detached.has(controller) || controller.signal.aborted;
14358
+ const promptTokens = prewarmPromptTokens(result);
14359
+ if (!wasDetached) {
14360
+ deps.context.notePrewarm({
14361
+ tokens: promptTokens,
14362
+ ms: result.timing.apiMs,
14363
+ aborted: result.aborted
14364
+ });
14365
+ }
14366
+ deps.recordUsage(runtime, result.usage);
14367
+ try {
14368
+ deps.session?.appendEntry({
14369
+ kind: "custom",
14370
+ customType: "prewarm",
14371
+ // Never a model message and never a rendered transcript block: a
14372
+ // pre-warm is a fact about the backend, not something the session said.
14373
+ display: false,
14374
+ parentTurnId: deps.state.lastTurnId,
14375
+ data: {
14376
+ trigger,
14377
+ target: runtime.targetId,
14378
+ model: runtime.wireModelId,
14379
+ promptTokens,
14380
+ aborted: result.aborted,
14381
+ detached: wasDetached,
14382
+ timing: result.timing,
14383
+ promptCache: {
14384
+ input: result.usage?.input ?? 0,
14385
+ cacheRead: result.usage?.cacheRead ?? 0,
14386
+ cacheWrite: result.usage?.cacheWrite ?? 0,
14387
+ ...result.backend ? { backend: { ...result.backend } } : {}
14388
+ },
14389
+ ...result.errorMessage ? { error: result.errorMessage } : {}
14390
+ }
14391
+ });
14392
+ } catch {
14393
+ }
14394
+ return { ran: true, trigger };
14395
+ };
14396
+ const releaseActiveRound = () => {
14397
+ const controller = active;
14398
+ if (controller === null) return;
14399
+ detached.add(controller);
14400
+ if (abortOnSubmit) controller.abort();
14401
+ active = null;
14402
+ };
14403
+ const fire = () => {
14404
+ timer = null;
14405
+ const trigger = pendingTrigger;
14406
+ pendingTrigger = null;
14407
+ if (trigger === null) return;
14408
+ const controller = new AbortController();
14409
+ active = controller;
14410
+ const previous = inFlight;
14411
+ inFlight = previous.catch(() => null).then(() => runOnce(trigger, controller)).catch(() => null).finally(() => {
14412
+ if (active === controller) active = null;
14413
+ });
14414
+ };
14415
+ return {
14416
+ schedule(trigger) {
14417
+ releaseActiveRound();
14418
+ pendingTrigger = trigger;
14419
+ if (timer !== null) return;
14420
+ timer = setTimeout(fire, 0);
14421
+ },
14422
+ cancel() {
14423
+ pendingTrigger = null;
14424
+ if (timer !== null) {
14425
+ clearTimeout(timer);
14426
+ timer = null;
14427
+ }
14428
+ releaseActiveRound();
14429
+ },
14430
+ settled() {
14431
+ return new Promise((resolve7) => {
14432
+ setTimeout(() => {
14433
+ resolve7(inFlight.catch(() => null));
14434
+ }, 0);
14435
+ });
14436
+ },
14437
+ dispose() {
14438
+ for (const unsubscribe of unsubscribeDispatch) unsubscribe?.();
14439
+ pendingTrigger = null;
14440
+ if (timer !== null) {
14441
+ clearTimeout(timer);
14442
+ timer = null;
14443
+ }
14444
+ active?.abort();
14445
+ active = null;
14446
+ }
14447
+ };
14448
+ }
14449
+ function subscribePrewarmToCompaction(bus, prewarm) {
14450
+ const unsubscribe = bus?.on(BusChannels.CompactionEnd, () => {
14451
+ prewarm.schedule("compaction");
14452
+ });
14453
+ return () => unsubscribe?.();
14454
+ }
14455
+
13748
14456
  // src/interactive/turn-queues.ts
13749
14457
  init_esm_shims();
13750
14458
  var DEFAULT_STEERING_MODE = "next-slot";
@@ -13756,14 +14464,13 @@ function createTurnQueues(deps) {
13756
14464
  deps.emitQueueUpdateEvent(queuedMirror.map((entry) => ({ ...entry })));
13757
14465
  };
13758
14466
  const enqueue = (text, kind) => {
13759
- const trimmed = text.trim();
13760
- if (trimmed.length === 0 || !state.streaming || !state.runtime) return false;
14467
+ if (text.trim().length === 0 || !state.streaming || !state.runtime) return false;
13761
14468
  const message = {
13762
14469
  role: "user",
13763
- content: trimmed,
14470
+ content: text,
13764
14471
  timestamp: Date.now()
13765
14472
  };
13766
- queuedMirror.push({ text: trimmed, kind });
14473
+ queuedMirror.push({ text, kind });
13767
14474
  if (kind === "steer") {
13768
14475
  state.runtime.agent.steer(message);
13769
14476
  } else {
@@ -14157,7 +14864,11 @@ function createTurnRuntime(deps) {
14157
14864
  requestedThinkingLevel: settings.orchestrator.thinkingLevel ?? "off",
14158
14865
  use: "orchestrator",
14159
14866
  requireTools: false,
14160
- requireOutputBudget: true
14867
+ requireOutputBudget: true,
14868
+ // A resumed session already knows what this backend had open; without
14869
+ // it the first turn budgets against the probed server-wide figure and
14870
+ // corrects only once discovery reports a loaded window (issue #227).
14871
+ knownLoadedContextWindow: context.rememberedLoadedContextWindow(targetId, wireModelId)
14161
14872
  });
14162
14873
  if (!resolved.ok) {
14163
14874
  const failure = resolved.diagnostics.find((entry) => entry.severity === "error") ?? resolved.diagnostics[0];
@@ -14247,7 +14958,8 @@ function createTurnRuntime(deps) {
14247
14958
  requestedThinkingLevel: deps.getSettings().orchestrator.thinkingLevel ?? "off",
14248
14959
  use: "orchestrator",
14249
14960
  requireTools: false,
14250
- requireOutputBudget: true
14961
+ requireOutputBudget: true,
14962
+ knownLoadedContextWindow: context.rememberedLoadedContextWindow(target.target.id, target.wireModelId)
14251
14963
  });
14252
14964
  if (!refreshed.ok) return;
14253
14965
  const liveModel = state.runtime.agent.state.model;
@@ -14608,7 +15320,8 @@ function createTurnRuntime(deps) {
14608
15320
  if (isAssistant && usage && typeof usage === "object" && runFirstCallVerdict === null) {
14609
15321
  const input = typeof usage.input === "number" ? usage.input : 0;
14610
15322
  const cacheRead = typeof usage.cacheRead === "number" ? usage.cacheRead : 0;
14611
- runFirstCallVerdict = backendCacheVerdict(input, cacheRead);
15323
+ const backendTimings = enrichedEvent.message.backendTimings;
15324
+ runFirstCallVerdict = backendCacheVerdict(input, cacheRead, backendTimings);
14612
15325
  }
14613
15326
  if (usage) {
14614
15327
  context.reconcileUsage(usage);
@@ -14652,6 +15365,8 @@ function createTurnState(initialThinkingLevel) {
14652
15365
  runtime: null,
14653
15366
  lastTurnId: null,
14654
15367
  streaming: false,
15368
+ turnPreparation: "idle",
15369
+ turnPreparationSince: 0,
14655
15370
  currentThinkingLevel: initialThinkingLevel,
14656
15371
  replayedContextMessages: [],
14657
15372
  activeUserTurnId: null,
@@ -14769,6 +15484,31 @@ function createChatLoop(deps) {
14769
15484
  const middlewareToolChoice = deps.middlewareToolChoice ?? createMiddlewareToolChoiceControl();
14770
15485
  const state = createTurnState(deps.getSettings().orchestrator.thinkingLevel ?? "off");
14771
15486
  const toolStartTimes = /* @__PURE__ */ new Map();
15487
+ const preparationListeners = /* @__PURE__ */ new Set();
15488
+ const setTurnPreparation = (phase) => {
15489
+ if (state.turnPreparation === phase) return;
15490
+ if (state.turnPreparation === "idle") state.turnPreparationSince = Date.now();
15491
+ if (phase === "idle") state.turnPreparationSince = 0;
15492
+ state.turnPreparation = phase;
15493
+ for (const listener of preparationListeners) {
15494
+ try {
15495
+ listener(phase);
15496
+ } catch {
15497
+ }
15498
+ }
15499
+ };
15500
+ let preparingSubmits = 0;
15501
+ const enterPreparation = () => {
15502
+ preparingSubmits += 1;
15503
+ if (state.turnPreparation === "idle") setTurnPreparation("preparing");
15504
+ };
15505
+ const leavePreparation = () => {
15506
+ preparingSubmits = Math.max(0, preparingSubmits - 1);
15507
+ if (preparingSubmits === 0) setTurnPreparation("idle");
15508
+ };
15509
+ const endPreparationCompaction = () => {
15510
+ setTurnPreparation(preparingSubmits > 0 ? "preparing" : "idle");
15511
+ };
14772
15512
  const emit = (event) => {
14773
15513
  for (const listener of listeners) {
14774
15514
  listener(event);
@@ -14845,7 +15585,7 @@ function createChatLoop(deps) {
14845
15585
  middlewareToolChoice,
14846
15586
  consumePersistedEcho: (text) => queues.consumePersistedEcho(text),
14847
15587
  removeQueuedMirrorEntry: (text) => queues.removeQueuedMirrorEntry(text),
14848
- promptCachePayloadForAssistant: (usage) => context.promptCachePayloadForAssistant(usage),
15588
+ promptCachePayloadForAssistant: (usage, backend) => context.promptCachePayloadForAssistant(usage, backend),
14849
15589
  promptSideTokens: () => context.promptSideTokens(),
14850
15590
  observability: deps.observability
14851
15591
  });
@@ -14916,6 +15656,16 @@ function createChatLoop(deps) {
14916
15656
  return { ok: false, reason: err instanceof Error ? err.message : String(err) };
14917
15657
  }
14918
15658
  };
15659
+ const withEndpointSlot = async (runtime, round) => {
15660
+ const endpointKey = canonicalEndpointKey(runtime.runtimeResolution.target);
15661
+ const release = endpointKey === null ? () => {
15662
+ } : registerForegroundStream(endpointKey);
15663
+ try {
15664
+ return await round();
15665
+ } finally {
15666
+ release();
15667
+ }
15668
+ };
14919
15669
  const writeOutOfTurnUsageRow = deps.recordOutOfTurnUsageRow ?? ((row2) => appendOutOfTurnUsageRow(clioStateDir(), row2));
14920
15670
  const recordOutOfTurnUsage = (runtime, usage, label) => {
14921
15671
  if (!usage) return;
@@ -14960,6 +15710,39 @@ function createChatLoop(deps) {
14960
15710
  }
14961
15711
  });
14962
15712
  };
15713
+ let turnActive = false;
15714
+ const prewarm = createTurnPrewarm({
15715
+ state,
15716
+ getSettings: deps.getSettings,
15717
+ providers: deps.providers,
15718
+ context,
15719
+ bus: deps.bus,
15720
+ ...deps.session ? { session: deps.session } : {},
15721
+ isLatencySurface: () => deps.isLatencySurface?.() !== false,
15722
+ isTurnActive: () => turnActive,
15723
+ hasActiveDispatch: () => (deps.hasActiveDispatch ?? deps.hasAttachedDispatch)?.() === true,
15724
+ prepareRuntime: async (signal) => {
15725
+ await turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {
15726
+ });
15727
+ return prepareOutOfTurnRound("a turn is in flight", signal);
15728
+ },
15729
+ applySessionTools: (runtime) => {
15730
+ runtime.agent.state.tools = resolveSessionTools(
15731
+ runtime,
15732
+ deps.toolRegistry,
15733
+ currentToolInvokeOptions,
15734
+ turnRuntime.toolTelemetry
15735
+ );
15736
+ },
15737
+ recordUsage: (runtime, usage) => {
15738
+ recordOutOfTurnUsage(runtime, sideQuestionUsage(usage), "prewarm");
15739
+ },
15740
+ ...deps.runPrewarm ? { runPrewarm: deps.runPrewarm } : {},
15741
+ ...deps.abortPrewarmOnSubmit === void 0 ? {} : { abortRoundOnSubmit: deps.abortPrewarmOnSubmit },
15742
+ ...deps.registerPrewarmEndpointSlot ? { registerEndpointSlot: deps.registerPrewarmEndpointSlot } : {}
15743
+ });
15744
+ const unsubscribePrewarmCompaction = subscribePrewarmToCompaction(deps.bus, prewarm);
15745
+ prewarm.schedule("session-start");
14963
15746
  const api = {
14964
15747
  steer: (text) => queues.steer(text),
14965
15748
  queueFollowUp: (text) => queues.queueFollowUp(text),
@@ -14972,9 +15755,9 @@ function createChatLoop(deps) {
14972
15755
  let mode = options.steering ?? DEFAULT_STEERING_MODE;
14973
15756
  const trimmed = text.trim();
14974
15757
  if (mode === "interrupt" && trimmed.length > 0) {
14975
- const refusal = interruptRefusalReason();
14976
- if (refusal !== null) {
14977
- emitNotice(`[Clio Coder] interrupt refused: ${refusal}. Queued for the next slot instead.`, "warning");
15758
+ const refusal2 = interruptRefusalReason();
15759
+ if (refusal2 !== null) {
15760
+ emitNotice(`[Clio Coder] interrupt refused: ${refusal2}. Queued for the next slot instead.`, "warning");
14978
15761
  mode = "next-slot";
14979
15762
  } else {
14980
15763
  const prior = priorSubmit;
@@ -14998,15 +15781,15 @@ function createChatLoop(deps) {
14998
15781
  const hasImages = options.images !== void 0 && options.images.length > 0;
14999
15782
  if (!hasImages && trimmed.length > 0 && state.runtime) {
15000
15783
  if (isWorkerShareNote(trimmed)) state.turnSharedWorkerNote = true;
15001
- if (mode === "end-of-turn") queues.queueFollowUp(trimmed);
15002
- else queues.steer(trimmed);
15784
+ if (mode === "end-of-turn") queues.queueFollowUp(text);
15785
+ else queues.steer(text);
15003
15786
  return;
15004
15787
  }
15005
15788
  emitNotice("[Clio Coder] response already in progress. Press Esc to cancel the active run.");
15006
15789
  return;
15007
15790
  }
15008
15791
  }
15009
- state.lastRunSnapshot = null;
15792
+ const previousRunSnapshot = state.lastRunSnapshot;
15010
15793
  let agentRuntime;
15011
15794
  try {
15012
15795
  await turnRuntime.ensureLiveCapabilitiesForSelectedModel();
@@ -15037,6 +15820,7 @@ function createChatLoop(deps) {
15037
15820
  currentToolInvokeOptions,
15038
15821
  turnRuntime.toolTelemetry
15039
15822
  );
15823
+ const toolSignature = toolSignatureFromState(agentRuntime.agent.state.tools);
15040
15824
  const askUserPolicy = createAskUserToolPolicy(agentRuntime.agent.state.tools);
15041
15825
  middleware.fireTurnStart(agentRuntime, text, pendingSkillRequests.length, options.requestContinuation === true);
15042
15826
  const reminderBlock = middleware.flushPendingReminders();
@@ -15051,9 +15835,12 @@ function createChatLoop(deps) {
15051
15835
  const submittedText = [reminderBlock, skillPreamble, taskMemoryHandoffSource, text].filter((part) => part.length > 0).join("\n\n");
15052
15836
  const forceNow = process.env.CLIO_CODER_FORCE_COMPACT === "1";
15053
15837
  try {
15838
+ setTurnPreparation("compacting");
15054
15839
  await context.runAutoCompact(agentRuntime, forceNow, void 0, void 0, submittedText);
15055
15840
  } catch (err) {
15056
15841
  emitNotice(`[Clio Coder] auto-compaction failed: ${err instanceof Error ? err.message : String(err)}`);
15842
+ } finally {
15843
+ endPreparationCompaction();
15057
15844
  }
15058
15845
  const compiledPrompt = await context.ensureSessionPrompt(agentRuntime);
15059
15846
  const compactionThreshold = deps.getSettings().compaction?.threshold ?? null;
@@ -15062,18 +15849,23 @@ function createChatLoop(deps) {
15062
15849
  pendingUserInput: submittedText,
15063
15850
  images,
15064
15851
  promptHash: compiledPrompt?.systemPromptHash,
15065
- toolSignature: toolSignatureFromState(agentRuntime.agent.state.tools)
15852
+ toolSignature
15066
15853
  });
15067
15854
  const reservedOutput = resolveReservedOutputTokens(agentRuntime.runtimeResolution.capabilityDecisions.maxTokens);
15068
15855
  const effectiveWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
15069
15856
  const pendingInputTokens = ceilChars(submittedText.length);
15070
15857
  let turnSnapshot = captureTurnSnapshot("pending");
15071
- const totalEstimate = snapshotInputTokens(turnSnapshot) + pendingInputTokens + reservedOutput;
15858
+ const budgetedPromptTokens = (snapshot) => Math.max(
15859
+ snapshotInputTokens(snapshot) + pendingInputTokens,
15860
+ context.liveContextEstimate(agentRuntime, submittedText).tokens
15861
+ );
15862
+ const totalEstimate = budgetedPromptTokens(turnSnapshot) + reservedOutput;
15072
15863
  if (effectiveWindow > 0 && totalEstimate > effectiveWindow) {
15073
15864
  emitNotice(
15074
- `[Clio Coder] Estimated request size ${totalEstimate} tokens (input ${snapshotInputTokens(turnSnapshot) + pendingInputTokens} + output budget ${reservedOutput}) exceeds the effective context window of ${effectiveWindow} tokens. Running compaction before sending...`
15865
+ `[Clio Coder] Estimated request size ${totalEstimate} tokens (input ${totalEstimate - reservedOutput} + output budget ${reservedOutput}) exceeds the effective context window of ${effectiveWindow} tokens. Running compaction before sending...`
15075
15866
  );
15076
- const compacted = await context.runAutoCompact(agentRuntime, true, void 0, void 0, submittedText);
15867
+ setTurnPreparation("compacting");
15868
+ const compacted = await context.runAutoCompact(agentRuntime, true, void 0, void 0, submittedText).finally(endPreparationCompaction);
15077
15869
  if (!compacted) {
15078
15870
  emitNotice(
15079
15871
  "[Clio Coder] Compaction could not reclaim enough space. Request blocked; trim the prompt, reduce active tools, or start a fresh session."
@@ -15082,7 +15874,7 @@ function createChatLoop(deps) {
15082
15874
  }
15083
15875
  context.refreshAgentMessagesFromSession(agentRuntime);
15084
15876
  turnSnapshot = captureTurnSnapshot("pending");
15085
- const postTotalEstimate = snapshotInputTokens(turnSnapshot) + pendingInputTokens + reservedOutput;
15877
+ const postTotalEstimate = budgetedPromptTokens(turnSnapshot) + reservedOutput;
15086
15878
  if (postTotalEstimate > effectiveWindow) {
15087
15879
  emitNotice(
15088
15880
  `[Clio Coder] Request still exceeds the effective window after compaction (${postTotalEstimate} > ${effectiveWindow}). Request blocked.`
@@ -15102,6 +15894,13 @@ function createChatLoop(deps) {
15102
15894
  );
15103
15895
  if (interrupted) emit({ type: "queued_user_turn", text, kind: "interrupt" });
15104
15896
  context.logPromptCompileIfPending();
15897
+ const previousThinkingLevel = previousRunSnapshot?.runtimeResolution?.effectiveThinkingLevel;
15898
+ if (previousThinkingLevel !== void 0 && previousThinkingLevel !== agentRuntime.runtimeResolution.effectiveThinkingLevel) {
15899
+ context.noteColdReason("thinking_change");
15900
+ }
15901
+ if (typeof previousRunSnapshot?.toolSignature === "string" && previousRunSnapshot.toolSignature !== toolSignature) {
15902
+ context.noteColdReason("tool_surface_change");
15903
+ }
15105
15904
  turnSnapshot = { ...turnSnapshot, turnId: userTurnId ?? "unknown" };
15106
15905
  context.setCurrentSnapshot(turnSnapshot);
15107
15906
  context.persistContextSnapshot(turnSnapshot);
@@ -15116,7 +15915,7 @@ function createChatLoop(deps) {
15116
15915
  compiledPromptHash: promptHash,
15117
15916
  staticCompositionHash: promptHash,
15118
15917
  promptSignature: promptHash,
15119
- toolSignature: toolSignatureFromState(agentRuntime.agent.state.tools),
15918
+ toolSignature,
15120
15919
  runtimeResolution: runtimeTargetSnapshot(agentRuntime.runtimeResolution),
15121
15920
  sessionId: deps.session?.current()?.id ?? null,
15122
15921
  cwd: process.cwd()
@@ -15129,6 +15928,9 @@ function createChatLoop(deps) {
15129
15928
  state.interruptedUsage = null;
15130
15929
  context.consumeExpectedColdReasons(agentRuntime.runtimeId);
15131
15930
  state.streaming = true;
15931
+ const endpointKey = canonicalEndpointKey(agentRuntime.runtimeResolution.target);
15932
+ const releaseForeground = endpointKey === null ? () => {
15933
+ } : registerForegroundStream(endpointKey);
15132
15934
  try {
15133
15935
  options.onAdmitted?.();
15134
15936
  } catch {
@@ -15178,6 +15980,7 @@ function createChatLoop(deps) {
15178
15980
  }
15179
15981
  await recovery.runCompactAndRetry(agentRuntime, runtimePromptText, overflow, images);
15180
15982
  } finally {
15983
+ releaseForeground();
15181
15984
  if (askUserPolicy) {
15182
15985
  try {
15183
15986
  await finalizeAskUserInterviewForHost(
@@ -15247,9 +16050,19 @@ function createChatLoop(deps) {
15247
16050
  isStreaming() {
15248
16051
  return state.streaming;
15249
16052
  },
16053
+ turnPreparation() {
16054
+ return { phase: state.turnPreparation, since: state.turnPreparationSince };
16055
+ },
16056
+ onTurnPreparation(handler) {
16057
+ preparationListeners.add(handler);
16058
+ return () => {
16059
+ preparationListeners.delete(handler);
16060
+ };
16061
+ },
15250
16062
  contextUsage: () => context.contextUsage(),
15251
16063
  contextLedger: () => context.contextLedger(),
15252
16064
  whenSettled: () => activeSubmit,
16065
+ whenPrewarmSettled: () => prewarm.settled(),
15253
16066
  resetForSession(leafTurnId, replayMessages) {
15254
16067
  if (state.runtime) {
15255
16068
  state.runtime.agent.abort();
@@ -15261,6 +16074,7 @@ function createChatLoop(deps) {
15261
16074
  middleware.clearPendingReminders();
15262
16075
  middlewareToolChoice.reset();
15263
16076
  state.lastTurnId = leafTurnId;
16077
+ state.lastRunSnapshot = null;
15264
16078
  context.resetForSession();
15265
16079
  void turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {
15266
16080
  });
@@ -15271,10 +16085,13 @@ function createChatLoop(deps) {
15271
16085
  if (deps.protectedArtifacts) {
15272
16086
  reloadProtectedArtifactsForSession(deps.protectedArtifacts, deps.readSessionEntries);
15273
16087
  }
16088
+ prewarm.schedule(replayMessages && replayMessages.length > 0 ? "resume" : "session-start");
15274
16089
  },
15275
16090
  dispose() {
15276
16091
  unsubscribeConfigReload?.();
15277
16092
  unsubscribeSynthesisLock?.();
16093
+ unsubscribePrewarmCompaction();
16094
+ prewarm.dispose();
15278
16095
  context.dispose();
15279
16096
  if (state.runtime) {
15280
16097
  state.runtime.agent.abort();
@@ -15297,16 +16114,19 @@ function createChatLoop(deps) {
15297
16114
  if (!prepared.ok) return { status: "refused", reason: prepared.reason };
15298
16115
  let result;
15299
16116
  try {
15300
- result = await sideQuestionRound({
15301
- model: prepared.runtime.agent.state.model,
15302
- // Read-only: runSideQuestion copies before appending its own
15303
- // message, so the live agent's history is untouched.
15304
- messages: prepared.runtime.agent.state.messages,
15305
- question: text,
15306
- ...prepared.apiKey !== void 0 ? { apiKey: prepared.apiKey } : {},
15307
- ...options.signal ? { signal: options.signal } : {},
15308
- ...options.onDelta ? { onDelta: options.onDelta } : {}
15309
- });
16117
+ result = await withEndpointSlot(
16118
+ prepared.runtime,
16119
+ () => sideQuestionRound({
16120
+ model: prepared.runtime.agent.state.model,
16121
+ // Read-only: runSideQuestion copies before appending its own
16122
+ // message, so the live agent's history is untouched.
16123
+ messages: prepared.runtime.agent.state.messages,
16124
+ question: text,
16125
+ ...prepared.apiKey !== void 0 ? { apiKey: prepared.apiKey } : {},
16126
+ ...options.signal ? { signal: options.signal } : {},
16127
+ ...options.onDelta ? { onDelta: options.onDelta } : {}
16128
+ })
16129
+ );
15310
16130
  } catch (err) {
15311
16131
  return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
15312
16132
  }
@@ -15325,14 +16145,21 @@ function createChatLoop(deps) {
15325
16145
  if (!prepared.ok) return { status: "refused", reason: prepared.reason };
15326
16146
  let result;
15327
16147
  try {
15328
- result = await handoffRound({
15329
- model: prepared.runtime.agent.state.model,
15330
- // Read-only, exactly as the side-question round treats it.
15331
- messages: prepared.runtime.agent.state.messages,
15332
- goal: text,
15333
- ...prepared.apiKey !== void 0 ? { apiKey: prepared.apiKey } : {},
15334
- ...options.signal ? { signal: options.signal } : {}
15335
- });
16148
+ result = await withEndpointSlot(
16149
+ prepared.runtime,
16150
+ () => handoffRound({
16151
+ model: prepared.runtime.agent.state.model,
16152
+ // Read-only, exactly as the side-question round treats it.
16153
+ messages: prepared.runtime.agent.state.messages,
16154
+ goal: text,
16155
+ // The runtime id is what decides whether the schema can be bound
16156
+ // on the wire rather than only stated in the prompt.
16157
+ runtimeId: prepared.runtime.runtimeResolution.runtime.id,
16158
+ ...prepared.apiKey !== void 0 ? { apiKey: prepared.apiKey } : {},
16159
+ ...options.signal ? { signal: options.signal } : {},
16160
+ ...options.repair ? { repair: options.repair } : {}
16161
+ })
16162
+ );
15336
16163
  } catch (err) {
15337
16164
  return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
15338
16165
  }
@@ -15371,13 +16198,17 @@ function createChatLoop(deps) {
15371
16198
  const submitInner = api.submit.bind(api);
15372
16199
  const submitTracked = (text, options) => {
15373
16200
  priorSubmit = activeSubmit;
15374
- const run = submitInner(text, options);
16201
+ turnActive = true;
16202
+ const run = submitInner(text, options).finally(() => {
16203
+ turnActive = false;
16204
+ });
15375
16205
  activeSubmit = run.catch(() => {
15376
16206
  });
15377
16207
  return run;
15378
16208
  };
15379
16209
  let admissionTail = null;
15380
16210
  api.submit = (text, options = {}) => {
16211
+ prewarm.cancel();
15381
16212
  const previous = admissionTail;
15382
16213
  let release = () => {
15383
16214
  };
@@ -15389,14 +16220,30 @@ function createChatLoop(deps) {
15389
16220
  release();
15390
16221
  if (admissionTail === ticket) admissionTail = null;
15391
16222
  };
15392
- const start = () => submitTracked(text, {
15393
- ...options,
15394
- onAdmitted: () => {
15395
- releaseTicket();
15396
- options.onAdmitted?.();
16223
+ enterPreparation();
16224
+ let leftPreparation = false;
16225
+ const leaveOnce = () => {
16226
+ if (leftPreparation) return;
16227
+ leftPreparation = true;
16228
+ leavePreparation();
16229
+ };
16230
+ const start = async () => {
16231
+ const { onPreparationVisible, ...submitOptions } = options;
16232
+ try {
16233
+ await onPreparationVisible?.();
16234
+ } catch {
15397
16235
  }
15398
- }).finally(releaseTicket);
15399
- return previous ? previous.then(start) : start();
16236
+ return submitTracked(text, {
16237
+ ...submitOptions,
16238
+ onAdmitted: () => {
16239
+ releaseTicket();
16240
+ leaveOnce();
16241
+ options.onAdmitted?.();
16242
+ }
16243
+ }).finally(releaseTicket);
16244
+ };
16245
+ const run = previous ? previous.then(start) : start();
16246
+ return run.finally(leaveOnce);
15400
16247
  };
15401
16248
  return api;
15402
16249
  }
@@ -15716,6 +16563,17 @@ function buildArtifactHeader(artifact, verification, width) {
15716
16563
  }
15717
16564
  return padAnsi(parts.join(" "), width, ELLIPSIS);
15718
16565
  }
16566
+ function buildArtifactHeaderLines(artifact, verification, width) {
16567
+ const metadata = buildArtifactHeader(artifact, void 0, width);
16568
+ const verify = verificationText(verification);
16569
+ if (artifact === void 0 || verify.length === 0) return [metadata];
16570
+ const theme6 = clioTheme();
16571
+ const token = verification?.status === "ok" ? "success" : verification?.status === "fail" ? "error" : verification?.status === "retired" ? "warning" : "info";
16572
+ return [
16573
+ metadata,
16574
+ ...wrapTextWithAnsi(theme6.fg(token, verify), Math.max(1, width)).map((line) => padAnsi(line, width, ELLIPSIS))
16575
+ ];
16576
+ }
15719
16577
  function viewFooterHint(focus, canVerify, innerWidth) {
15720
16578
  if (innerWidth !== void 0 && viewUsesSinglePane(innerWidth - 2)) {
15721
16579
  const budget = innerWidth - 3;
@@ -15905,14 +16763,14 @@ var ViewOverlayView = class {
15905
16763
  this.ensureContentLoaded(artifact);
15906
16764
  this.lastContentWidth = width;
15907
16765
  const verification = artifact ? this.verifications.get(artifactKey(artifact)) : void 0;
15908
- const header = buildArtifactHeader(artifact, verification, width);
15909
- const bodyHeight = Math.max(0, height - 1);
16766
+ const header = buildArtifactHeaderLines(artifact, verification, width);
16767
+ const bodyHeight = Math.max(0, height - header.length);
15910
16768
  this.lastContentBodyHeight = Math.max(1, bodyHeight);
15911
16769
  const body = this.renderedContentLines(width);
15912
16770
  const maxOffset = Math.max(0, body.length - Math.max(1, bodyHeight));
15913
16771
  if (this.contentScrollOffset > maxOffset) this.contentScrollOffset = maxOffset;
15914
16772
  const visible = body.slice(this.contentScrollOffset, this.contentScrollOffset + bodyHeight).map((line) => padAnsi(line, width, ELLIPSIS));
15915
- const lines = [header, ...visible];
16773
+ const lines = [...header, ...visible];
15916
16774
  return this.fixedLines(lines, width, height);
15917
16775
  }
15918
16776
  fixedLines(lines, width, height) {
@@ -16102,7 +16960,8 @@ function createQuestionState(question) {
16102
16960
  selected: /* @__PURE__ */ new Set(),
16103
16961
  customAnswer: "",
16104
16962
  inputValue: "",
16105
- answer: ""
16963
+ answer: "",
16964
+ rawValue: ""
16106
16965
  };
16107
16966
  }
16108
16967
  function isOtherOption(label) {
@@ -16139,12 +16998,16 @@ function optionIndexFromValue(value) {
16139
16998
  const index = Number(value.slice("option:".length));
16140
16999
  return Number.isInteger(index) && index >= 0 ? index : null;
16141
17000
  }
16142
- function answerText(question, selected, customAnswer) {
16143
- const parts = [];
17001
+ function selectedOptionLabels(question, selected) {
17002
+ const labels = [];
16144
17003
  for (const index of [...selected].sort((a, b) => a - b)) {
16145
17004
  const label = question.options?.[index]?.label;
16146
- if (label) parts.push(label);
17005
+ if (label) labels.push(label);
16147
17006
  }
17007
+ return labels;
17008
+ }
17009
+ function answerText(question, selected, customAnswer) {
17010
+ const parts = selectedOptionLabels(question, selected);
16148
17011
  const custom = customAnswer.trim();
16149
17012
  if (custom.length > 0) parts.push(custom);
16150
17013
  return parts.join("; ");
@@ -16261,6 +17124,10 @@ var AskUserOverlayView = class {
16261
17124
  this.commitMultiSelectOrOpenOther(state);
16262
17125
  return;
16263
17126
  }
17127
+ if (this.isAddTextKey(data)) {
17128
+ this.chooseFocusedAndOpenText(question, state);
17129
+ return;
17130
+ }
16264
17131
  this.list?.handleInput(data);
16265
17132
  }
16266
17133
  /**
@@ -16387,20 +17254,16 @@ var AskUserOverlayView = class {
16387
17254
  { key: "Alt+Left/Right", verb: "question" }
16388
17255
  ]) : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
16389
17256
  }
17257
+ const addText = { key: "t", verb: "add text", short: "text" };
16390
17258
  if (question.multi_select === true) {
16391
17259
  return this.questions.length > 1 ? this.withScrollHint([
16392
17260
  { key: "Left/Right", verb: "question" },
16393
17261
  { key: "Space", verb: "toggle" },
17262
+ addText,
16394
17263
  { key: "Enter", verb: recordAnswer }
16395
- ]) : this.withScrollHint([
16396
- { key: "Space", verb: "toggle" },
16397
- { key: "Enter", verb: recordAnswer }
16398
- ]);
17264
+ ]) : this.withScrollHint([{ key: "Space", verb: "toggle" }, addText, { key: "Enter", verb: recordAnswer }]);
16399
17265
  }
16400
- return this.questions.length > 1 ? this.withScrollHint([
16401
- { key: "Left/Right", verb: "question" },
16402
- { key: "Enter", verb: recordAnswer }
16403
- ]) : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
17266
+ return this.questions.length > 1 ? this.withScrollHint([{ key: "Left/Right", verb: "question" }, addText, { key: "Enter", verb: recordAnswer }]) : this.withScrollHint([addText, { key: "Enter", verb: recordAnswer }]);
16404
17267
  }
16405
17268
  renderDecisionContext(width) {
16406
17269
  const theme6 = clioTheme();
@@ -16501,6 +17364,32 @@ var AskUserOverlayView = class {
16501
17364
  isTextModeNextKey(data) {
16502
17365
  return this.questions.length > 1 && (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right"));
16503
17366
  }
17367
+ /**
17368
+ * The key that adds typed text to the option under the cursor.
17369
+ *
17370
+ * Choosing an option used to be the whole answer, so a model that offered
17371
+ * "Exact number - I'll type it" gave the operator a label and nowhere to put
17372
+ * the number; the interview then spent two of its four rounds asking for the
17373
+ * figures again (issue #228). The list itself only reads the arrows, Enter,
17374
+ * and Esc, so a letter is free here.
17375
+ */
17376
+ isAddTextKey(data) {
17377
+ return matchesKey(data, "t");
17378
+ }
17379
+ /**
17380
+ * Take the focused option and open the text field for it, without committing.
17381
+ * Submitting the text records the label and the text together.
17382
+ */
17383
+ chooseFocusedAndOpenText(question, state) {
17384
+ const current = this.list?.getSelectedItem();
17385
+ const optionIndex = current && current.value !== "other" ? optionIndexFromValue(current.value) : null;
17386
+ if (optionIndex !== null) {
17387
+ if (question.multi_select === true) state.selected.add(optionIndex);
17388
+ else state.selected = /* @__PURE__ */ new Set([optionIndex]);
17389
+ state.focusedValue = `option:${optionIndex}`;
17390
+ }
17391
+ this.openTextInput(state.selected.size > 0 ? "Add your answer to the chosen option" : "Other answer");
17392
+ }
16504
17393
  /**
16505
17394
  * The row above the question, or nothing.
16506
17395
  *
@@ -16654,14 +17543,15 @@ var AskUserOverlayView = class {
16654
17543
  return;
16655
17544
  }
16656
17545
  state.inputValue = answer;
17546
+ state.rawValue = value;
16657
17547
  if (questionHasOptions(question) && question.multi_select === true) {
16658
17548
  state.customAnswer = answer;
16659
17549
  this.commitCurrentAnswer();
16660
- } else {
16661
- state.customAnswer = questionHasOptions(question) ? answer : "";
16662
- state.answer = answer;
16663
- this.finishIfCompleteOrAdvance();
17550
+ return;
16664
17551
  }
17552
+ state.customAnswer = questionHasOptions(question) ? answer : "";
17553
+ state.answer = questionHasOptions(question) ? answerText(question, state.selected, state.customAnswer) : answer;
17554
+ this.finishIfCompleteOrAdvance();
16665
17555
  };
16666
17556
  activeInput.onEscape = () => this.cancel();
16667
17557
  this.input = activeInput;
@@ -16690,7 +17580,10 @@ var AskUserOverlayView = class {
16690
17580
  return;
16691
17581
  }
16692
17582
  state.selected = /* @__PURE__ */ new Set([optionIndex]);
16693
- state.answer = question.options?.[optionIndex]?.label ?? item.label;
17583
+ state.customAnswer = "";
17584
+ state.rawValue = "";
17585
+ state.inputValue = "";
17586
+ state.answer = answerText(question, state.selected, state.customAnswer) || item.label;
16694
17587
  state.focusedValue = item.value;
16695
17588
  this.status = "";
16696
17589
  this.finishIfCompleteOrAdvance();
@@ -16802,12 +17695,26 @@ var AskUserOverlayView = class {
16802
17695
  allAnswered() {
16803
17696
  return this.states.length > 0 && this.states.every((state) => state.answer.trim().length > 0);
16804
17697
  }
17698
+ /**
17699
+ * What the round captured, as three separable facts per question: the one-line
17700
+ * answer, the labels chosen, and the text typed. A reader tells a label-only
17701
+ * answer from a label-plus-value one by whether `value` is there, which the
17702
+ * joined string alone could never say.
17703
+ */
16805
17704
  answers() {
16806
17705
  const answers = [];
16807
17706
  for (let index = 0; index < this.questions.length; index += 1) {
16808
17707
  const question = this.questions[index];
16809
- const answer = this.states[index]?.answer.trim();
16810
- if (question && answer && answer.length > 0) answers.push({ question: question.question, answer });
17708
+ const state = this.states[index];
17709
+ const answer = state?.answer.trim();
17710
+ if (!question || !state || !answer || answer.length === 0) continue;
17711
+ const options = selectedOptionLabels(question, state.selected);
17712
+ answers.push({
17713
+ question: question.question,
17714
+ answer,
17715
+ ...options.length > 0 ? { options } : {},
17716
+ ...state.rawValue.length > 0 ? { value: state.rawValue } : {}
17717
+ });
16811
17718
  }
16812
17719
  return answers;
16813
17720
  }
@@ -17416,7 +18323,7 @@ function legendRow2(group, contentWidth) {
17416
18323
  const tokens = formatTokens(group.tokens);
17417
18324
  const percent = formatContextPercent(group.percent);
17418
18325
  const right = `${tokens.padStart(9)} ${percent.padStart(6)}`;
17419
- const labelToken = group.category === "free" || group.category === "reserve" ? "dim" : "muted";
18326
+ const labelToken = group.category === "reserve" ? "frame" : group.category === "free" ? "dim" : "muted";
17420
18327
  const leftWidth = Math.max(0, contentWidth - visibleWidth(right) - 2);
17421
18328
  const labelText = group.label.length > leftWidth ? group.label.slice(0, leftWidth) : group.label.padEnd(leftWidth);
17422
18329
  return `${swatch} ${theme6.fg(labelToken, labelText)} ${theme6.fg("muted", right)}`;
@@ -17438,6 +18345,16 @@ function coldReasonLabel(reason) {
17438
18345
  return "compaction";
17439
18346
  case "dispatch":
17440
18347
  return "dispatch traffic";
18348
+ case "residency":
18349
+ return "residency change";
18350
+ case "thinking_change":
18351
+ return "thinking-level change";
18352
+ case "tool_surface_change":
18353
+ return "tool-surface change";
18354
+ case "prompt_recompiled":
18355
+ return "prompt recompile";
18356
+ case "background_memory":
18357
+ return "background memory step";
17441
18358
  default:
17442
18359
  return reason;
17443
18360
  }
@@ -17509,18 +18426,28 @@ function renderContextLedgerLines(ledger, contentWidth, workingSet, workingSetCo
17509
18426
  if (ledger.promptCache) {
17510
18427
  const cache2 = ledger.promptCache;
17511
18428
  const shell = cache2.shellReused ? "shell reused" : "shell recompiled";
17512
- const backend = cache2.backendVerdict === "hot" || cache2.backendVerdict === "partial" ? "backend reused" : cache2.backendVerdict === "cold" ? "backend cold" : cache2.backendVerdict === "small" ? "backend small" : "backend n/a";
18429
+ const backendCacheReadsUnknown = cache2.backend?.cachedTokens === null;
18430
+ const backend = backendCacheReadsUnknown ? "server does not report cache reads" : cache2.backendVerdict === "hot" || cache2.backendVerdict === "partial" ? "backend reused" : cache2.backendVerdict === "cold" ? "backend cold" : cache2.backendVerdict === "small" ? "backend small" : "backend n/a";
17513
18431
  const read = cache2.cacheReadTokens !== null ? `cache read ${formatTokens(cache2.cacheReadTokens)}` : "cache read n/a";
17514
18432
  const uncached = cache2.uncachedInputTokens !== null ? `uncached input ${formatTokens(cache2.uncachedInputTokens)}` : null;
17515
- const line = ["prompt cache:", shell, "\xB7", backend, "\xB7", read, ...uncached ? ["\xB7", uncached] : []].join(" ");
17516
- const coldReasons = cache2.backendVerdict === "cold" ? cache2.expectedColdReasons ?? [] : [];
17517
- const misleading = cache2.shellReused && cache2.backendVerdict === "cold" && coldReasons.length === 0;
18433
+ const line = backendCacheReadsUnknown ? ["prompt cache:", shell, "\xB7", backend].join(" ") : ["prompt cache:", shell, "\xB7", backend, "\xB7", read, ...uncached ? ["\xB7", uncached] : []].join(" ");
18434
+ const coldReasons = cache2.backendVerdict === "cold" && !backendCacheReadsUnknown ? cache2.expectedColdReasons ?? [] : [];
18435
+ const misleading = cache2.shellReused && cache2.backendVerdict === "cold" && !backendCacheReadsUnknown && coldReasons.length === 0;
17518
18436
  lines.push(theme6.fg(misleading ? "warning" : "dim", line));
18437
+ if (cache2.backend) {
18438
+ const prefill = cache2.backend.cachedTokens !== null && cache2.uncachedPrefillTokens !== null ? `prefill: ${formatTokens(cache2.uncachedPrefillTokens)} uncached \xB7 ${formatTokens(cache2.backend.cachedTokens)} cached \xB7 ${formatTokens(cache2.backend.promptMs)} ms` : `prefill: ${formatTokens(cache2.backend.promptTokens)} prompt \xB7 ${formatTokens(cache2.backend.promptMs)} ms`;
18439
+ lines.push(theme6.fg("dim", prefill));
18440
+ }
17519
18441
  if (coldReasons.length > 0) {
17520
18442
  const reasons = coldReasons.map(coldReasonLabel).join(", ");
17521
18443
  lines.push(theme6.fg("dim", `last cold turn: ${reasons} (expected)`));
17522
18444
  }
17523
18445
  }
18446
+ if (ledger.prewarm) {
18447
+ const tokens = ledger.prewarm.tokens !== null ? `${formatTokens(ledger.prewarm.tokens)} tokens` : "prefix";
18448
+ const aborted = ledger.prewarm.aborted ? " (aborted on submit; prefix stays in the slot)" : "";
18449
+ lines.push(theme6.fg("dim", `prewarmed: ${tokens} in ${formatTokens(ledger.prewarm.ms)} ms${aborted}`));
18450
+ }
17524
18451
  if (ledger.lastCompaction) {
17525
18452
  const pruneInfo = `last compaction: reclaimed ${formatTokens(ledger.lastCompaction.tokensBefore)} -> ${formatTokens(ledger.lastCompaction.tokensAfter)} tokens (${ledger.lastCompaction.stage})`;
17526
18453
  lines.push(theme6.fg("dim", pruneInfo));
@@ -17614,6 +18541,8 @@ function aggregateCostEntries(entries) {
17614
18541
  existing.row.apiCalls += entry.apiCalls ?? 1;
17615
18542
  if (entry.label === "side-question") existing.row.sideQuestions += 1;
17616
18543
  if (entry.label === "handoff") existing.row.handoffs += 1;
18544
+ if (entry.label === "prewarm") existing.row.prewarms += 1;
18545
+ if (entry.label === "background-memory") existing.row.backgroundMemory += 1;
17617
18546
  for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
17618
18547
  addResponseModelIdObservationCounts(
17619
18548
  existing.responseModelIdObservationCounts,
@@ -17635,7 +18564,9 @@ function aggregateCostEntries(entries) {
17635
18564
  reasoningTokens: entry.reasoningTokens,
17636
18565
  apiCalls: entry.apiCalls ?? 1,
17637
18566
  sideQuestions: entry.label === "side-question" ? 1 : 0,
17638
- handoffs: entry.label === "handoff" ? 1 : 0
18567
+ handoffs: entry.label === "handoff" ? 1 : 0,
18568
+ prewarms: entry.label === "prewarm" ? 1 : 0,
18569
+ backgroundMemory: entry.label === "background-memory" ? 1 : 0
17639
18570
  },
17640
18571
  requestedModelIds: new Set(entry.requestedModelIds),
17641
18572
  responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
@@ -17667,7 +18598,9 @@ function sumRows(rows) {
17667
18598
  reasoningTokens: acc.reasoningTokens + row2.reasoningTokens,
17668
18599
  apiCalls: acc.apiCalls + row2.apiCalls,
17669
18600
  sideQuestions: acc.sideQuestions + row2.sideQuestions,
17670
- handoffs: acc.handoffs + row2.handoffs
18601
+ handoffs: acc.handoffs + row2.handoffs,
18602
+ prewarms: acc.prewarms + row2.prewarms,
18603
+ backgroundMemory: acc.backgroundMemory + row2.backgroundMemory
17671
18604
  }),
17672
18605
  {
17673
18606
  runs: 0,
@@ -17679,7 +18612,9 @@ function sumRows(rows) {
17679
18612
  reasoningTokens: 0,
17680
18613
  apiCalls: 0,
17681
18614
  sideQuestions: 0,
17682
- handoffs: 0
18615
+ handoffs: 0,
18616
+ prewarms: 0,
18617
+ backgroundMemory: 0
17683
18618
  }
17684
18619
  );
17685
18620
  }
@@ -17701,21 +18636,37 @@ function kvBlock(entries) {
17701
18636
  return annotation ? `${row2} ${theme6.fg("dim", annotation)}` : row2;
17702
18637
  });
17703
18638
  }
17704
- function summaryBlock(totalCost, totalTokens, rows) {
18639
+ function summaryBlock(totalCost, totalTokens, rows, promptCache) {
17705
18640
  const totals = sumRows(rows);
17706
18641
  const resolvedTotal = totalTokens > 0 ? totalTokens : totals.tokens;
17707
18642
  const cost = formatCostAggregate(totalCost);
18643
+ const cacheRows = promptCache !== null && hasPromptCacheTelemetry(promptCache) ? [
18644
+ [
18645
+ "uncached prefill",
18646
+ promptCache.uncachedPrefillTokens === null ? "not reported" : formatTokens2(promptCache.uncachedPrefillTokens)
18647
+ ],
18648
+ [
18649
+ "cache verdicts",
18650
+ `hot ${promptCache.verdictCounts.hot} \xB7 partial ${promptCache.verdictCounts.partial} \xB7 cold ${promptCache.verdictCounts.cold} \xB7 small ${promptCache.verdictCounts.small}`
18651
+ ]
18652
+ ] : [];
17708
18653
  return kvBlock([
17709
- ["turns", formatTokens2(totals.runs - totals.sideQuestions - totals.handoffs)],
18654
+ [
18655
+ "turns",
18656
+ formatTokens2(totals.runs - totals.sideQuestions - totals.handoffs - totals.prewarms - totals.backgroundMemory)
18657
+ ],
17710
18658
  ["model calls", formatTokens2(totals.apiCalls)],
17711
18659
  ...totals.sideQuestions > 0 ? [["side questions", formatTokens2(totals.sideQuestions)]] : [],
17712
18660
  ...totals.handoffs > 0 ? [["handoffs", formatTokens2(totals.handoffs)]] : [],
18661
+ ...totals.prewarms > 0 ? [["pre-warms", formatTokens2(totals.prewarms)]] : [],
18662
+ ...totals.backgroundMemory > 0 ? [["memory steps", formatTokens2(totals.backgroundMemory)]] : [],
17713
18663
  ...cost === null ? [] : [["cost", cost]],
17714
18664
  ["input", formatTokens2(totals.input)],
17715
18665
  ["output", formatTokens2(totals.output)],
17716
18666
  ["reasoning", ...reasoningValue(totals.reasoningTokens)],
17717
18667
  ["cache read", ...cacheReadValue(totals.cacheRead, totals.apiCalls)],
17718
18668
  ["cache write", formatTokens2(totals.cacheWrite)],
18669
+ ...cacheRows,
17719
18670
  ["processed", `${formatTokens2(resolvedTotal)} tokens`]
17720
18671
  ]);
17721
18672
  }
@@ -17724,10 +18675,12 @@ function modelBlock(row2) {
17724
18675
  return kvBlock([
17725
18676
  ["requested model ids", row2.requestedModelIds.join(", ")],
17726
18677
  ["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
17727
- ["turns", formatTokens2(row2.runs - row2.sideQuestions - row2.handoffs)],
18678
+ ["turns", formatTokens2(row2.runs - row2.sideQuestions - row2.handoffs - row2.prewarms - row2.backgroundMemory)],
17728
18679
  ["model calls", formatTokens2(row2.apiCalls)],
17729
18680
  ...row2.sideQuestions > 0 ? [["side questions", formatTokens2(row2.sideQuestions)]] : [],
17730
18681
  ...row2.handoffs > 0 ? [["handoffs", formatTokens2(row2.handoffs)]] : [],
18682
+ ...row2.prewarms > 0 ? [["pre-warms", formatTokens2(row2.prewarms)]] : [],
18683
+ ...row2.backgroundMemory > 0 ? [["memory steps", formatTokens2(row2.backgroundMemory)]] : [],
17731
18684
  ...cost === null ? [] : [["cost", cost]],
17732
18685
  ["input", formatTokens2(row2.input)],
17733
18686
  ["output", formatTokens2(row2.output)],
@@ -17737,10 +18690,10 @@ function modelBlock(row2) {
17737
18690
  ["processed", `${formatTokens2(row2.tokens)} tokens`]
17738
18691
  ]);
17739
18692
  }
17740
- function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth) {
18693
+ function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth, promptCache = null) {
17741
18694
  const theme6 = clioTheme();
17742
18695
  const lines = [];
17743
- for (const line of summaryBlock(totalCost, totalTokens, rows)) {
18696
+ for (const line of summaryBlock(totalCost, totalTokens, rows, promptCache)) {
17744
18697
  lines.push(line);
17745
18698
  }
17746
18699
  lines.push(rule(theme6, contentWidth));
@@ -17757,7 +18710,7 @@ function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth)
17757
18710
  }
17758
18711
  return lines;
17759
18712
  }
17760
- function buildCostSnapshot(observability, sessionId, snapshot) {
18713
+ function buildCostSnapshot(observability, sessionId, snapshot, getSessionEntries) {
17761
18714
  const entries = observability.costEntries();
17762
18715
  const rows = aggregateCostEntries(entries);
17763
18716
  const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
@@ -17765,7 +18718,8 @@ function buildCostSnapshot(observability, sessionId, snapshot) {
17765
18718
  sessionId,
17766
18719
  totalCost: snapshot?.session.cost ?? observability.sessionCostSummary(),
17767
18720
  totalTokens,
17768
- rows
18721
+ rows,
18722
+ promptCache: getSessionEntries ? foldPromptCacheTelemetry(getSessionEntries()) : null
17769
18723
  };
17770
18724
  }
17771
18725
  var CostOverlayBody = class {
@@ -17776,7 +18730,13 @@ var CostOverlayBody = class {
17776
18730
  render(width) {
17777
18731
  const contentWidth = Math.max(1, Math.floor(width));
17778
18732
  const snapshot = this.getSnapshot();
17779
- return formatCostOverlayBodyLines(snapshot.totalCost, snapshot.totalTokens, snapshot.rows, contentWidth);
18733
+ return formatCostOverlayBodyLines(
18734
+ snapshot.totalCost,
18735
+ snapshot.totalTokens,
18736
+ snapshot.rows,
18737
+ contentWidth,
18738
+ snapshot.promptCache
18739
+ );
17780
18740
  }
17781
18741
  invalidate() {
17782
18742
  }
@@ -17784,7 +18744,9 @@ var CostOverlayBody = class {
17784
18744
  function openCostOverlay(tui, observability, options) {
17785
18745
  const sessionId = options?.sessionId ?? null;
17786
18746
  let latest = observability.snapshot();
17787
- const body = new CostOverlayBody(() => buildCostSnapshot(observability, sessionId, latest));
18747
+ const body = new CostOverlayBody(
18748
+ () => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries)
18749
+ );
17788
18750
  const handle = showClioOverlayFrame(tui, body, {
17789
18751
  anchor: "center",
17790
18752
  width: COST_OVERLAY_WIDTH,
@@ -17871,6 +18833,38 @@ function compileFleetRunPreview(input) {
17871
18833
  diagnostics.push(describeError(error));
17872
18834
  return fail();
17873
18835
  }
18836
+ const routes = /* @__PURE__ */ new Map();
18837
+ if (input.resolveRoute !== void 0) {
18838
+ for (const step of plan.steps) {
18839
+ if (step.kind === "code") continue;
18840
+ try {
18841
+ const route = input.resolveRoute({
18842
+ stepId: step.id,
18843
+ agentId: step.agentId,
18844
+ task: step.task,
18845
+ scope: step.scope,
18846
+ ...step.target !== void 0 ? { target: step.target } : {},
18847
+ ...step.profile !== void 0 ? { profile: step.profile } : {}
18848
+ });
18849
+ if (route !== null) routes.set(step.id, route);
18850
+ } catch (error) {
18851
+ diagnostics.push(`step '${step.id}' route preflight failed: ${describeError(error)}`);
18852
+ }
18853
+ }
18854
+ plan = bindExecutionPlanEndpoints(
18855
+ plan,
18856
+ Object.fromEntries(
18857
+ [...routes].map(([stepId, route]) => [
18858
+ stepId,
18859
+ route.endpoint === void 0 ? void 0 : {
18860
+ key: route.endpoint.key,
18861
+ limit: route.endpoint.limit,
18862
+ ...route.endpoint.foregroundHeld !== void 0 ? { foregroundHeld: route.endpoint.foregroundHeld } : {}
18863
+ }
18864
+ ])
18865
+ )
18866
+ );
18867
+ }
17874
18868
  try {
17875
18869
  preflightWriteBoundaries(plan, input.workspaceRoot);
17876
18870
  } catch (error) {
@@ -17919,19 +18913,7 @@ function compileFleetRunPreview(input) {
17919
18913
  }
17920
18914
  ];
17921
18915
  }
17922
- let route = null;
17923
- try {
17924
- route = input.resolveRoute?.({
17925
- stepId: step.id,
17926
- agentId: step.agentId,
17927
- task: step.task,
17928
- scope: step.scope,
17929
- ...step.target !== void 0 ? { target: step.target } : {},
17930
- ...step.profile !== void 0 ? { profile: step.profile } : {}
17931
- }) ?? null;
17932
- } catch (error) {
17933
- diagnostics.push(`step '${step.id}' route preflight failed: ${describeError(error)}`);
17934
- }
18916
+ const route = routes.get(step.id) ?? null;
17935
18917
  return [
17936
18918
  {
17937
18919
  ...base,
@@ -18549,6 +19531,8 @@ function formatMemoryStatusLine(status, contentWidth) {
18549
19531
  theme6.fg("muted", `last ${status.lastDecision ?? "none"}`)
18550
19532
  ];
18551
19533
  if (status.stepInFlight) units.push(theme6.fg("reason", "step running"));
19534
+ const spend = status.spend === null || status.spend === void 0 ? "" : formatTaskMemorySpend(status.spend);
19535
+ if (spend.length > 0) units.push(theme6.fg("dim", spend));
18552
19536
  return fitUnits(theme6, "", units, width);
18553
19537
  }
18554
19538
  function bankEntries(snapshot) {
@@ -18645,7 +19629,8 @@ function memorySignature(status, records) {
18645
19629
  status.tier,
18646
19630
  String(status.size),
18647
19631
  status.lastDecision ?? "none",
18648
- status.stepInFlight ? "running" : "idle"
19632
+ status.stepInFlight ? "running" : "idle",
19633
+ `${status.spend?.llmSteps ?? 0}:${status.spend?.injections ?? 0}:${status.spend?.totalTokens ?? 0}`
18649
19634
  ];
18650
19635
  for (const record2 of records) parts.push(`r:${record2.id}:${record2.approved}:${record2.rejectedAt ?? ""}`);
18651
19636
  for (const entry of bankEntries(status.bank)) {
@@ -21303,6 +22288,7 @@ function buildSettingItems(settings, options) {
21303
22288
  }),
21304
22289
  fleetGroupHeader("fleet.group.placement", "Placement"),
21305
22290
  ...fleetNodeRows(options?.getFleetNodes?.() ?? []),
22291
+ ...fleetEndpointRows(options?.providers),
21306
22292
  settingItem("targets", "", {
21307
22293
  description: "Live inference target inventory and routing roles.",
21308
22294
  affordance: "column heading",
@@ -21641,6 +22627,32 @@ function fleetNodeRows(nodes) {
21641
22627
  });
21642
22628
  });
21643
22629
  }
22630
+ function fleetEndpointRows(providers) {
22631
+ if (providers === void 0) return [];
22632
+ let usage = {};
22633
+ try {
22634
+ usage = capacityLeaseUsage().endpoints;
22635
+ } catch {
22636
+ }
22637
+ const capacities = endpointCapacitiesForStatuses(providers.list());
22638
+ const targetsByEndpoint = /* @__PURE__ */ new Map();
22639
+ for (const status of providers.list()) {
22640
+ const endpoint = endpointCapacityForStatus(status);
22641
+ if (endpoint === null) continue;
22642
+ targetsByEndpoint.set(endpoint.key, [...targetsByEndpoint.get(endpoint.key) ?? [], status.target.id]);
22643
+ }
22644
+ return Object.entries(capacities).map(([key, endpoint]) => {
22645
+ const slots = `slots ${usage[key] ?? 0}/${endpoint.limit}`;
22646
+ return settingItem(`fleet.endpoints.${key}`, slots, {
22647
+ label: `endpoint ${endpoint.label}`,
22648
+ description: `Targets: ${(targetsByEndpoint.get(key) ?? []).join(", ")} \xB7 Limit source: ${endpoint.source}`,
22649
+ affordance: "read-only inference endpoint capacity",
22650
+ readOnly: true,
22651
+ presentationKind: "status",
22652
+ valueSegments: [{ text: slots, tone: "neutral" }]
22653
+ });
22654
+ });
22655
+ }
21644
22656
  function buildSettingsSections(items) {
21645
22657
  return SETTINGS_SECTIONS.map((section2) => ({
21646
22658
  id: section2.id,
@@ -25080,8 +26092,8 @@ function dispatchSlashCommand(command, ctx) {
25080
26092
  ctx.submitChat(command.text);
25081
26093
  return "accepted";
25082
26094
  }
25083
- const refusal = expansion?.expanded === false ? expansion.refusal : void 0;
25084
- ctx.notice("error", refusal ? refusal.message : `/${command.token} is not a command. Type /help for the list.`);
26095
+ const refusal2 = expansion?.expanded === false ? expansion.refusal : void 0;
26096
+ ctx.notice("error", refusal2 ? refusal2.message : `/${command.token} is not a command. Type /help for the list.`);
25085
26097
  ctx.render();
25086
26098
  return "rejected";
25087
26099
  }
@@ -25532,7 +26544,10 @@ function createOverlayGeneralOpeners(deps) {
25532
26544
  if (deps.transitions.state !== "closed") return;
25533
26545
  deps.transitions.state = "cost";
25534
26546
  deps.transitions.handle = openCostOverlayFactory(deps.tui, deps.observability, {
25535
- sessionId: deps.getSessionId?.() ?? null
26547
+ sessionId: deps.getSessionId?.() ?? null,
26548
+ ...deps.readSessionEntries ? {
26549
+ getSessionEntries: () => deps.readSessionEntries?.() ?? []
26550
+ } : {}
25536
26551
  });
25537
26552
  deps.requestRender();
25538
26553
  };
@@ -25777,10 +26792,17 @@ function createOverlayGeneralOpeners(deps) {
25777
26792
  };
25778
26793
  const resolution = deps.dispatch.preview?.(request);
25779
26794
  if (!resolution) return null;
26795
+ const foregroundHeld = resolution.endpoint === void 0 ? 0 : foregroundStreamUsage()[resolution.endpoint.key] ?? 0;
25780
26796
  return {
25781
26797
  targetId: resolution.targetId,
25782
26798
  wireModelId: resolution.wireModelId,
25783
- nodeId: resolution.node.id
26799
+ nodeId: resolution.node.id,
26800
+ ...resolution.endpoint !== void 0 ? {
26801
+ endpoint: {
26802
+ ...resolution.endpoint,
26803
+ ...foregroundHeld > 0 ? { foregroundHeld } : {}
26804
+ }
26805
+ } : {}
25784
26806
  };
25785
26807
  }
25786
26808
  });
@@ -25967,12 +26989,41 @@ function permissionOverlayPlacement(tui, editor, footer) {
25967
26989
  };
25968
26990
  }
25969
26991
  var PermissionOverlayBody = class {
25970
- constructor(view) {
26992
+ constructor(view, inspect) {
25971
26993
  this.view = view;
26994
+ this.inspect = inspect;
25972
26995
  }
25973
26996
  view;
26997
+ inspect;
26998
+ preview = null;
26999
+ scroll = 0;
27000
+ lastLineCount = 0;
27001
+ canInspect() {
27002
+ return this.inspect !== void 0;
27003
+ }
27004
+ isInspecting() {
27005
+ return this.preview !== null;
27006
+ }
27007
+ toggleInspect() {
27008
+ if (this.inspect === void 0) return;
27009
+ if (this.preview !== null) {
27010
+ this.preview = null;
27011
+ this.scroll = 0;
27012
+ return;
27013
+ }
27014
+ this.preview = this.inspect();
27015
+ this.scroll = 0;
27016
+ }
27017
+ scrollInspect(delta) {
27018
+ if (this.preview === null) return;
27019
+ const maxScroll = Math.max(0, this.lastLineCount - MUTATION_PREVIEW_VISIBLE_ROWS);
27020
+ this.scroll = Math.max(0, Math.min(this.scroll + delta, maxScroll));
27021
+ }
25974
27022
  render(width) {
25975
- return permissionOverlayLines(this.view, width);
27023
+ if (this.preview === null) return permissionOverlayLines(this.view, width);
27024
+ const rendered = permissionInspectionLines(this.view, this.preview, width, this.scroll);
27025
+ this.lastLineCount = rendered.wrappedLineCount;
27026
+ return rendered.lines;
25976
27027
  }
25977
27028
  invalidate() {
25978
27029
  }
@@ -26033,18 +27084,66 @@ function permissionOverlayTitle(view) {
26033
27084
  function permissionOverlayTone(view) {
26034
27085
  return permissionDecisionPresentation(view).semanticToken;
26035
27086
  }
26036
- function permissionOverlayHint(innerWidth, composerHasDraft = false) {
26037
- return fitHintEntries(permissionHintEntries(composerHasDraft), innerWidth - 3);
27087
+ function permissionOverlayHint(innerWidth, composerHasDraft = false, inspection = "none") {
27088
+ return fitHintEntries(permissionHintEntries(composerHasDraft, inspection), innerWidth - 3);
27089
+ }
27090
+ function isFileMutationTool(tool) {
27091
+ return tool === "write" || tool === "edit";
26038
27092
  }
26039
27093
  function actionConsequence(presentation, id) {
26040
27094
  return presentation.requiredActions.find((action) => action.id === id)?.consequence ?? "";
26041
27095
  }
27096
+ function wrapMutationLine(value, max) {
27097
+ if (max <= 1) return [value];
27098
+ if (value.length <= max) return [value];
27099
+ const lines = [];
27100
+ let remaining = value;
27101
+ while (remaining.length > max) {
27102
+ lines.push(`${remaining.slice(0, max - 1)}\u21A9`);
27103
+ remaining = remaining.slice(max - 1);
27104
+ }
27105
+ lines.push(remaining);
27106
+ return lines;
27107
+ }
27108
+ function permissionInspectionLines(view, preview, width, scroll) {
27109
+ const content = Math.max(8, Math.floor(width));
27110
+ const wrapped = preview.body.flatMap((line) => wrapMutationLine(line, content));
27111
+ const pane = mutationPreviewWindow(wrapped, scroll);
27112
+ const notes = [];
27113
+ if (preview.neutralized) notes.push("control characters shown as \xB7");
27114
+ if (preview.tabsExpanded) notes.push("tabs shown as spaces");
27115
+ const lines = [
27116
+ field("Tool: ", `${view.tool} \xB7 Action: ${view.actionClass}`, content),
27117
+ ...view.target !== void 0 && view.target.length > 0 ? [field("Target: ", view.target, content)] : [],
27118
+ ...wrapSentence(`Mutation: ${mutationFactsLine(preview.facts)}`, content),
27119
+ "",
27120
+ ...wrapSentence(preview.heading, content),
27121
+ ...pane.window,
27122
+ ...pane.position === null ? [] : [pane.position],
27123
+ ...notes.length > 0 ? [`Rendered safely: ${notes.join(", ")}.`] : []
27124
+ ];
27125
+ return { lines, wrappedLineCount: wrapped.length };
27126
+ }
26042
27127
  function permissionOverlayLines(view, width) {
26043
27128
  const content = Math.max(8, Math.floor(width));
26044
27129
  const presentation = permissionDecisionPresentation(view);
26045
27130
  const lines = [
26046
27131
  field("Tool: ", `${view.tool} \xB7 Action: ${view.actionClass}`, content),
26047
27132
  ...view.target !== void 0 && view.target.length > 0 ? [field("Target: ", view.target, content)] : [],
27133
+ // Size and digest stay on the collapsed card whether or not the operator
27134
+ // opens the mutation, so the decision always carries the identity of the
27135
+ // bytes it applies to.
27136
+ // Wrapped, never ellipsized: at 40 columns the digest is the tail of the
27137
+ // line, and ellipsizing it would drop the one fact that ties the decision
27138
+ // to the bytes it applies to.
27139
+ ...view.mutation !== void 0 ? [
27140
+ ...wrapSentence(`Mutation: ${mutationFactsLine(view.mutation)}`, content),
27141
+ ...wrapSentence(`Press ${MUTATION_PREVIEW_KEY} to read it before deciding.`, content)
27142
+ ] : [],
27143
+ ...view.mutation === void 0 && view.origin.kind === "worker" && isFileMutationTool(view.tool) ? wrapSentence(
27144
+ "No local preview: this call's arguments stay inside the worker, so you are approving the target above without inspecting the mutation.",
27145
+ content
27146
+ ) : [],
26048
27147
  ...wrapSentence(`Requested by: ${presentation.requestedByCopy}`, content),
26049
27148
  ...view.artifact !== void 0 ? [
26050
27149
  "",
@@ -26064,8 +27163,8 @@ function permissionOverlayLines(view, width) {
26064
27163
  }
26065
27164
  return lines;
26066
27165
  }
26067
- function createPermissionOverlayBody(view) {
26068
- return new PermissionOverlayBody(view);
27166
+ function createPermissionOverlayBody(view, inspect) {
27167
+ return new PermissionOverlayBody(view, inspect);
26069
27168
  }
26070
27169
 
26071
27170
  // src/interactive/bus-notices.ts
@@ -26286,6 +27385,7 @@ function mainApprovalRequestView(call, decision, meta, autonomy, queueDepth) {
26286
27385
  const axisFromDecision = askAxis(decision);
26287
27386
  const axis = axisFromMeta ?? (axisFromDecision.kind === "net" ? { kind: "net", ruleId: axisFromDecision.ruleId } : { kind: "autonomy", level: autonomy });
26288
27387
  const target = describeCallTarget(call.tool, call.args);
27388
+ const mutation = mutationFacts(call.tool, call.args);
26289
27389
  return {
26290
27390
  requestId: meta?.requestId ?? "permission-pending",
26291
27391
  tool: call.tool,
@@ -26296,9 +27396,14 @@ function mainApprovalRequestView(call, decision, meta, autonomy, queueDepth) {
26296
27396
  ...call.tool === ToolNames.Dispatch && decision.kind === "ask" ? { artifact: { kind: "dispatch-plan", text: decision.rejection.detail } } : {},
26297
27397
  ...call.tool === ToolNames.AskUser ? { exposure: askUserExposure(call.args) } : {},
26298
27398
  ...target.length > 0 ? { target } : {},
27399
+ ...mutation !== null ? { mutation } : {},
26299
27400
  ...queueDepth !== void 0 && queueDepth > 1 ? { queueDepth } : {}
26300
27401
  };
26301
27402
  }
27403
+ function mainMutationInspector(call, view) {
27404
+ if (view.mutation === void 0) return void 0;
27405
+ return createMutationInspector(call.tool, call.args, view.mutation);
27406
+ }
26302
27407
  function workerEscalationEntry(payload, autonomy) {
26303
27408
  if (!isLiveWorkerEscalationRequest(payload) || typeof payload.requestId !== "string") return null;
26304
27409
  const origin = typeof payload.origin === "string" ? payload.origin : void 0;
@@ -26372,7 +27477,7 @@ function createOverlayPermissionLifecycle(deps) {
26372
27477
  announceParked();
26373
27478
  return;
26374
27479
  }
26375
- if (!deps.openPermissionOverlay(view)) {
27480
+ if (!deps.openPermissionOverlay(view, mainMutationInspector(call, view))) {
26376
27481
  announceParked();
26377
27482
  return;
26378
27483
  }
@@ -26760,7 +27865,8 @@ function openHelpOverlay(tui, manager, onClose, initialFilter) {
26760
27865
  "**Tool surface**: which tools exist at all through registration, tool profiles, skill narrowing, and dispatch admission. Violations are terminal denials, never approvable.",
26761
27866
  "**Safety net** (always on, level-independent): damage-control rules, path policy for secrets and system paths, command-substitution confirmation, and `git_destructive` blocks. Blocks are final at every level; confirm rails ask at every level.",
26762
27867
  "**Autonomy level** (`/settings`, persisted as `autonomy`): the operator's standing grant per action class for actions the net passed, enforced by the harness at tool admission. read-only denies non-read, suggest parks every non-read call, auto-edit parks unrecognized commands, and full-auto runs them.",
26763
- "**Approvals**: a parked call waits for a one-shot operator decision; approving resumes only that call. Workers resolve asks per `workers.onPermission` (Approvals Routing); headless runs auto-deny."
27868
+ "**Approvals**: a parked call waits for a one-shot operator decision; approving resumes only that call. Workers resolve asks per `workers.onPermission` (Approvals Routing); headless runs auto-deny.",
27869
+ "**Inspecting a mutation**: a parked `write` or `edit` card carries the target, the byte count, and a `sha256` digest of the exact call arguments. Press `v` to read the complete proposed content, or the complete effective diff against the file on disk, and `v` again to put it away; \u2191/\u2193 and PageUp/PageDown scroll it. Enter still allows, `s` still stops, and Esc still denies while it is open. The mutation text is shown locally and never enters the transcript, a notice, a desktop notification, or the render trace. A worker escalation has no preview because the call's arguments never leave the worker, and its card says so."
26764
27870
  ]
26765
27871
  }
26766
27872
  ];
@@ -26893,13 +27999,16 @@ function openPromptsOverlay(tui, ctx, onClose) {
26893
27999
  lines.push(`**Argument Hint:** \`${template.argumentHint}\``);
26894
28000
  }
26895
28001
  lines.push(`**Source:** ${template.sourceInfo.source ?? template.sourceInfo.scope}`);
28002
+ if (template.unavailable !== void 0) {
28003
+ lines.push(`**Unavailable:** ${template.unavailable}`);
28004
+ }
26896
28005
  if (!template.trusted) {
26897
28006
  lines.push("**Untrusted:** set `skills.trustProjectCompatRoots` before this template will expand.");
26898
28007
  }
26899
28008
  return lines;
26900
28009
  }
26901
28010
  };
26902
- const marker = template.trusted ? void 0 : clioTheme().fg("warning", "untrusted");
28011
+ const marker = template.unavailable !== void 0 ? clioTheme().fg("error", "unavailable") : template.trusted ? void 0 : clioTheme().fg("warning", "untrusted");
26903
28012
  if (marker) item.meta = template.argumentHint ? `${template.argumentHint} \xB7 ${marker}` : marker;
26904
28013
  else if (template.argumentHint) item.meta = template.argumentHint;
26905
28014
  return item;
@@ -27059,7 +28168,7 @@ function runCompactWithNotice(onCompact, notice, instructions) {
27059
28168
  init_esm_shims();
27060
28169
  import { spawnSync } from "node:child_process";
27061
28170
  import { randomUUID as randomUUID2 } from "node:crypto";
27062
- import { readFileSync as readFileSync7, unlinkSync, writeFileSync } from "node:fs";
28171
+ import { readFileSync as readFileSync8, unlinkSync, writeFileSync } from "node:fs";
27063
28172
  import { tmpdir } from "node:os";
27064
28173
  import { join as join7 } from "node:path";
27065
28174
  function defaultProbe(candidates) {
@@ -27099,7 +28208,7 @@ function editTextExternally(initialText, command) {
27099
28208
  const result = runEditor(command, tmpFile);
27100
28209
  if (result.error) return { ok: false, error: result.error.message };
27101
28210
  if (result.status !== 0) return { ok: false, error: `external editor exited with code ${result.status ?? "?"}` };
27102
- return { ok: true, text: readFileSync7(tmpFile, "utf8").replace(/\n$/, "") };
28211
+ return { ok: true, text: readFileSync8(tmpFile, "utf8").replace(/\n$/, "") };
27103
28212
  } finally {
27104
28213
  try {
27105
28214
  unlinkSync(tmpFile);
@@ -28356,19 +29465,54 @@ function createOverlaySessionLifecycle(deps) {
28356
29465
  }
28357
29466
  void runHandoffExtraction(session, fromSessionId, verdict.goal);
28358
29467
  }
29468
+ const HANDOFF_REFUSAL_QUOTE_CHARS = 200;
29469
+ function terminalHandoffRefusal(firstReason, secondReason, secondText) {
29470
+ const answered = secondText.replace(/\s+/g, " ").trim().slice(0, HANDOFF_REFUSAL_QUOTE_CHARS);
29471
+ return [
29472
+ "the extraction round could not produce a handoff record after one repair attempt.",
29473
+ `Asked for: one JSON object with decisions, facts, files, commands, and openQuestions.`,
29474
+ `Round 1: ${firstReason}.`,
29475
+ `Round 2: ${secondReason}.`,
29476
+ `Round 2 returned: ${answered.length > 0 ? answered : "(nothing)"}`
29477
+ ].join(" ");
29478
+ }
29479
+ async function extractWithOneRepair(goal) {
29480
+ const first = await deps.chat.extractHandoff(goal);
29481
+ if (first.status !== "answered") {
29482
+ return {
29483
+ ok: false,
29484
+ level: first.status === "failed" ? "error" : "warn",
29485
+ reason: first.status === "aborted" ? "the extraction round was cancelled" : first.reason
29486
+ };
29487
+ }
29488
+ const parsedFirst = parseHandoffExtraction(first.text);
29489
+ if (parsedFirst.ok) return { ok: true, parsed: parsedFirst };
29490
+ const second = await deps.chat.extractHandoff(goal, {
29491
+ repair: { complaint: parsedFirst.reason, previous: first.text }
29492
+ });
29493
+ if (second.status !== "answered") {
29494
+ return {
29495
+ ok: false,
29496
+ level: second.status === "failed" ? "error" : "warn",
29497
+ reason: second.status === "aborted" ? "the repair round was cancelled" : `round 1 could not be read (${parsedFirst.reason}); the repair round then failed: ${second.reason}`
29498
+ };
29499
+ }
29500
+ const parsedSecond = parseHandoffExtraction(second.text);
29501
+ if (parsedSecond.ok) return { ok: true, parsed: parsedSecond };
29502
+ return {
29503
+ ok: false,
29504
+ level: "error",
29505
+ reason: terminalHandoffRefusal(parsedFirst.reason, parsedSecond.reason, second.text)
29506
+ };
29507
+ }
28359
29508
  async function runHandoffExtraction(session, fromSessionId, goal) {
28360
29509
  const notice = deps.getSlashNotice();
28361
- const outcome = await deps.chat.extractHandoff(goal);
28362
- if (outcome.status !== "answered") {
28363
- const reason = outcome.status === "aborted" ? "the extraction round was cancelled" : outcome.reason;
28364
- emitCommandNotice(notice, outcome.status === "failed" ? "error" : "warn", "handoff", reason);
28365
- return;
28366
- }
28367
- const parsed = parseHandoffExtraction(outcome.text);
28368
- if (!parsed.ok) {
28369
- emitCommandNotice(notice, "error", "handoff", parsed.reason);
29510
+ const extraction = await extractWithOneRepair(goal);
29511
+ if (!extraction.ok) {
29512
+ emitCommandNotice(notice, extraction.level, "handoff", extraction.reason);
28370
29513
  return;
28371
29514
  }
29515
+ const parsed = extraction.parsed;
28372
29516
  const meta = session.current();
28373
29517
  const cwd = typeof meta?.cwd === "string" && meta.cwd.length > 0 ? meta.cwd : null;
28374
29518
  const entries = deps.readStructuredEntries(fromSessionId);
@@ -28547,6 +29691,14 @@ var DRAFT_EDIT_KEYS = ["backspace", "delete", "ctrl+u", "ctrl+w", "alt+backspace
28547
29691
  function isDraftEditKey(data) {
28548
29692
  return !isKeyRelease(data) && DRAFT_EDIT_KEYS.some((key) => matchesKey(data, key));
28549
29693
  }
29694
+ var MUTATION_PAGE_ROWS = 16;
29695
+ function mutationScrollDelta(data) {
29696
+ if (matchesKey(data, "up")) return -1;
29697
+ if (matchesKey(data, "down")) return 1;
29698
+ if (matchesKey(data, "pageUp")) return -MUTATION_PAGE_ROWS;
29699
+ if (matchesKey(data, "pageDown")) return MUTATION_PAGE_ROWS;
29700
+ return 0;
29701
+ }
28550
29702
  function routePermissionOverlayKey(data, deps) {
28551
29703
  if (matchesKey(data, "enter") && !isKeyRelease(data)) {
28552
29704
  if (!(deps.composerHasDraft?.() ?? false)) deps.confirmPermission();
@@ -28556,6 +29708,17 @@ function routePermissionOverlayKey(data, deps) {
28556
29708
  deps.editDraft(data);
28557
29709
  return true;
28558
29710
  }
29711
+ if (matchesKey(data, MUTATION_PREVIEW_KEY) && !isKeyRelease(data) && (deps.canInspectMutation?.() ?? false) && deps.toggleMutationInspection) {
29712
+ deps.toggleMutationInspection();
29713
+ return true;
29714
+ }
29715
+ if ((deps.isInspectingMutation?.() ?? false) && deps.scrollMutationInspection && !isKeyRelease(data)) {
29716
+ const delta = mutationScrollDelta(data);
29717
+ if (delta !== 0) {
29718
+ deps.scrollMutationInspection(delta);
29719
+ return true;
29720
+ }
29721
+ }
28559
29722
  if (isEscapeKey(data)) {
28560
29723
  deps.cancelPermission();
28561
29724
  return true;
@@ -28730,6 +29893,11 @@ function createOverlayLifecycle(deps) {
28730
29893
  } = deps;
28731
29894
  let overlayPermission = null;
28732
29895
  let overlayAskUser = null;
29896
+ let permissionBody = null;
29897
+ const inspectionHint = () => {
29898
+ if (permissionBody === null || !permissionBody.canInspect()) return "none";
29899
+ return permissionBody.isInspecting() ? "open" : "closed";
29900
+ };
28733
29901
  const overlayTransitions = createOverlayTransitions({
28734
29902
  stopDispatchBoardTicker: () => interactiveTickers.stopDispatchBoardTicker(),
28735
29903
  renderContextIsland: () => interactiveTickers.renderContextIsland(),
@@ -28737,7 +29905,10 @@ function createOverlayLifecycle(deps) {
28737
29905
  requestRender: () => tui.requestRender(),
28738
29906
  cancelPendingAskUser: () => overlayAskUser?.cancelPending() ?? false,
28739
29907
  finishAuth: (dismiss) => overlayAuth.finish(dismiss),
28740
- onPermissionOverlayClosed: () => overlayPermission?.onPermissionOverlayClosed(),
29908
+ onPermissionOverlayClosed: () => {
29909
+ permissionBody = null;
29910
+ overlayPermission?.onPermissionOverlayClosed();
29911
+ },
28741
29912
  onOverlayClosed: () => overlayPermission?.retryPending()
28742
29913
  });
28743
29914
  const closeOverlay = overlayTransitions.close;
@@ -28766,17 +29937,20 @@ function createOverlayLifecycle(deps) {
28766
29937
  dispatch: deps.app.dispatch,
28767
29938
  getAutonomy: () => deps.app.getSettings?.().autonomy ?? "auto-edit",
28768
29939
  getOverlayState: () => overlayTransitions.state,
28769
- openPermissionOverlay: (view) => {
29940
+ openPermissionOverlay: (view, inspect) => {
28770
29941
  if (overlayTransitions.state !== "closed") return false;
28771
29942
  overlayTransitions.state = "permission-confirm";
28772
- overlayTransitions.handle = showOverlayFrame(tui, createPermissionOverlayBody(view), {
29943
+ const body = createPermissionOverlayBody(view, inspect);
29944
+ permissionBody = body;
29945
+ overlayTransitions.handle = showOverlayFrame(tui, body, {
28773
29946
  ...permissionOverlayPlacement(tui, editor, footer.view),
28774
29947
  width: PERMISSION_OVERLAY_WIDTH,
28775
29948
  title: permissionOverlayTitle(view),
28776
29949
  tone: permissionOverlayTone(view),
28777
29950
  // Read per frame: the footer names what Enter does right now, and
28778
- // that depends on whether the composer holds a draft.
28779
- footerHint: (innerWidth) => permissionOverlayHint(innerWidth, editor.getText().length > 0)
29951
+ // that depends on whether the composer holds a draft and on whether
29952
+ // the mutation is open.
29953
+ footerHint: (innerWidth) => permissionOverlayHint(innerWidth, editor.getText().length > 0, inspectionHint())
28780
29954
  });
28781
29955
  tui.requestRender();
28782
29956
  return true;
@@ -28974,6 +30148,16 @@ function createOverlayLifecycle(deps) {
28974
30148
  footer.refresh();
28975
30149
  tui.requestRender();
28976
30150
  },
30151
+ canInspectMutation: () => permissionBody?.canInspect() ?? false,
30152
+ isInspectingMutation: () => permissionBody?.isInspecting() ?? false,
30153
+ toggleMutationInspection: () => {
30154
+ permissionBody?.toggleInspect();
30155
+ tui.requestRender();
30156
+ },
30157
+ scrollMutationInspection: (delta) => {
30158
+ permissionBody?.scrollInspect(delta);
30159
+ tui.requestRender();
30160
+ },
28977
30161
  cancelAskUser: overlayAskUser.cancel,
28978
30162
  dispose: () => {
28979
30163
  overlayPermission?.dispose();
@@ -30496,6 +31680,10 @@ function createInteractiveInputRuntime(deps) {
30496
31680
  confirmPermission: () => deps.overlay.confirmPermission(),
30497
31681
  stopTurnFromPermission: () => deps.overlay.stopTurnFromPermission(),
30498
31682
  composerHasDraft: () => deps.editor.getText().length > 0,
31683
+ canInspectMutation: () => deps.overlay.canInspectMutation(),
31684
+ isInspectingMutation: () => deps.overlay.isInspectingMutation(),
31685
+ toggleMutationInspection: () => deps.overlay.toggleMutationInspection(),
31686
+ scrollMutationInspection: (delta) => deps.overlay.scrollMutationInspection(delta),
30499
31687
  editDraft: (data2) => {
30500
31688
  deps.editor.handleInput?.(data2);
30501
31689
  deps.requestRender();
@@ -31327,11 +32515,23 @@ function renderErrorSegmentLines(seg, width) {
31327
32515
  var CLIO_PREFIX = `${TEAL}${AGENT_GLYPH}${RESET} `;
31328
32516
  var CLIO_PREFIX_ERROR = `${RED_CRIT}${AGENT_GLYPH}${RESET} `;
31329
32517
  var USER_PREFIX = `${TEAL}${USER_GLYPH}${RESET} `;
32518
+ var USER_PREFIX_PENDING = `${DIM}${USER_GLYPH}${RESET} `;
32519
+ var USER_PENDING_TAIL = " \xB7 preparing";
32520
+ var USER_REFUSED_TAIL = " \xB7 not sent";
31330
32521
  var PROSE_GUTTER = " ";
31331
32522
  var PROSE_GUTTER_WIDTH = 2;
31332
32523
  function hangProseLines(lines, firstPrefix) {
31333
32524
  return lines.map((line, index) => `${index === 0 && firstPrefix !== void 0 ? firstPrefix : PROSE_GUTTER}${line}`);
31334
32525
  }
32526
+ function appendUserRowTail(rendered, tail, width) {
32527
+ const last = rendered.length - 1;
32528
+ const lastLine = rendered[last];
32529
+ if (lastLine !== void 0 && visibleWidth(lastLine) + tail.length <= width) {
32530
+ rendered[last] = `${lastLine}${DIM}${tail}${RESET}`;
32531
+ return;
32532
+ }
32533
+ rendered.push(`${PROSE_GUTTER}${dimLine(tail.trimStart(), Math.max(1, width - PROSE_GUTTER_WIDTH))}`);
32534
+ }
31335
32535
  var THINKING_HIDDEN_LABEL = `Thinking${GLYPH.ellipsis}`;
31336
32536
  var THINKING_LINE_LIMIT = 12;
31337
32537
  function dimLine(text, width) {
@@ -31488,8 +32688,11 @@ function renderEntryLines(entry, width, expandKey, latestHintToolId, latestFolde
31488
32688
  const contentWidth = Math.max(1, width - PROSE_GUTTER_WIDTH);
31489
32689
  const lines2 = [];
31490
32690
  for (const sourceLine of entry.text.split("\n")) lines2.push(...wrapTextWithAnsi(sourceLine, contentWidth));
31491
- const rendered = hangProseLines(lines2, USER_PREFIX);
32691
+ const status = entry.status?.() ?? "committed";
32692
+ const rendered = hangProseLines(lines2, status === "committed" ? USER_PREFIX : USER_PREFIX_PENDING);
31492
32693
  if (rendered[0] !== void 0) rendered[0] = `${OSC133_PROMPT_START}${rendered[0]}`;
32694
+ if (status !== "committed")
32695
+ appendUserRowTail(rendered, status === "pending" ? USER_PENDING_TAIL : USER_REFUSED_TAIL, width);
31493
32696
  return rendered;
31494
32697
  }
31495
32698
  if (entry.role === "retryStatus") {
@@ -31789,8 +32992,8 @@ function createChatPanel(options = {}) {
31789
32992
  (segment) => segment.kind === "tool" && !segment.finished && segment.startedAtMs !== void 0
31790
32993
  // The live reasoning line counts its own elapsed, so a turn that is
31791
32994
  // only thinking still needs the time-keyed render key.
31792
- ) || entry.pending && openThinkingSegment(entry)?.startedAtMs !== void 0) || entry.role === "replayBlock" && entry.isLive?.() === true;
31793
- const entryIsStable = (entry) => entry.role === "user" || entry.role === "replayBlock" && entry.isLive?.() !== true || entry.role === "worker" && !entry.state.pending || entry.role === "assistant" && !entry.pending && !entry.segments.some((segment) => segment.kind === "tool" && !segment.finished);
32995
+ ) || entry.pending && openThinkingSegment(entry)?.startedAtMs !== void 0) || entry.role === "replayBlock" && entry.isLive?.() === true || entry.role === "user" && (entry.status?.() ?? "committed") !== "committed";
32996
+ const entryIsStable = (entry) => entry.role === "user" && (entry.status?.() ?? "committed") === "committed" || entry.role === "replayBlock" && entry.isLive?.() !== true || entry.role === "worker" && !entry.state.pending || entry.role === "assistant" && !entry.pending && !entry.segments.some((segment) => segment.kind === "tool" && !segment.finished);
31794
32997
  const render2 = (width) => {
31795
32998
  const startedAt = performance5.now();
31796
32999
  const expandKey = resolveExpandKey();
@@ -31872,8 +33075,8 @@ function createChatPanel(options = {}) {
31872
33075
  return out;
31873
33076
  };
31874
33077
  return {
31875
- appendUser(text) {
31876
- transcript.push({ role: "user", text });
33078
+ appendUser(text, status) {
33079
+ transcript.push({ role: "user", text, ...status ? { status } : {} });
31877
33080
  markDirty();
31878
33081
  },
31879
33082
  appendReplayBlock(renderBlock, isLive2, fold) {
@@ -32310,6 +33513,7 @@ init_esm_shims();
32310
33513
  var CONTEXT_ISLAND_WIDTH = 52;
32311
33514
  var PHASES = ["scan", "codewiki", "generate", "clio-md", "state", "done"];
32312
33515
  var WIKI_PHASES = ["codewiki", "generate", "state", "done"];
33516
+ var COMPACTION_PHASES = ["compact", "done"];
32313
33517
  var PHASE_LABELS = {
32314
33518
  scan: "scan",
32315
33519
  // "index" rather than "wiki": this phase is the codewiki index build, and
@@ -32318,6 +33522,7 @@ var PHASE_LABELS = {
32318
33522
  generate: "draft",
32319
33523
  "clio-md": "CLIO-CODER.md",
32320
33524
  state: "state",
33525
+ compact: "compact",
32321
33526
  done: "done"
32322
33527
  };
32323
33528
  var WIKI_PHASE_LABELS = {
@@ -32332,8 +33537,9 @@ var KINDS = /* @__PURE__ */ new Set([
32332
33537
  "context-wiki",
32333
33538
  "compaction"
32334
33539
  ]);
32335
- var PHASE_SET = new Set(PHASES);
33540
+ var PHASE_SET = /* @__PURE__ */ new Set([...PHASES, ...COMPACTION_PHASES]);
32336
33541
  function phasesFor(kind) {
33542
+ if (kind === "compaction") return COMPACTION_PHASES;
32337
33543
  return kind === "context-wiki" ? WIKI_PHASES : PHASES;
32338
33544
  }
32339
33545
  function phaseLabel(kind, phase) {
@@ -33638,6 +34844,8 @@ function createInteractivePresentation(deps) {
33638
34844
  },
33639
34845
  isStreaming: () => deps.chat.isStreaming(),
33640
34846
  ...deps.isAwaitingApproval ? { isAwaitingApproval: deps.isAwaitingApproval } : {},
34847
+ ...deps.getPermissionInspection ? { getPermissionInspection: deps.getPermissionInspection } : {},
34848
+ getTurnPreparation: () => deps.chat.turnPreparation().phase,
33641
34849
  willEnterSteer: (text) => willEnterSteerActiveWork(deps, text),
33642
34850
  getSubmitKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.submit")[0], "Enter"),
33643
34851
  getNewlineKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.newLine")[0], "Shift+Enter")
@@ -34150,9 +35358,9 @@ function createInteractiveSlashRuntime(deps) {
34150
35358
  const willQueue = deps.chat.isStreaming();
34151
35359
  deps.recordSubmittedTurn();
34152
35360
  deps.refreshFooter();
34153
- if (!willQueue) deps.chatPanel.appendUser(sub.text);
35361
+ let rowStatus = "pending";
35362
+ if (!willQueue) deps.chatPanel.appendUser(sub.text, () => rowStatus);
34154
35363
  deps.requestRender();
34155
- deps.beforeSemanticSubmit?.();
34156
35364
  let acknowledgeAdmission = () => {
34157
35365
  };
34158
35366
  const admitted = new Promise((resolve7) => {
@@ -34163,13 +35371,26 @@ function createInteractiveSlashRuntime(deps) {
34163
35371
  ...sub.workingContextPaths.length > 0 ? { workingContextPaths: sub.workingContextPaths } : {},
34164
35372
  ...sub.pendingSkillRequests.length > 0 ? { pendingSkillRequests: sub.pendingSkillRequests } : {},
34165
35373
  ...awaitAdmission && willQueue ? { steering: "end-of-turn" } : {},
34166
- ...awaitAdmission ? { onAdmitted: acknowledgeAdmission } : {}
35374
+ ...!willQueue ? {
35375
+ onPreparationVisible: async () => {
35376
+ deps.beforeSemanticSubmit?.();
35377
+ await deps.settleVisibleFrame?.("submit-preparing");
35378
+ }
35379
+ } : {},
35380
+ onAdmitted: () => {
35381
+ rowStatus = "committed";
35382
+ deps.requestRender();
35383
+ if (awaitAdmission) acknowledgeAdmission();
35384
+ }
34167
35385
  });
34168
35386
  const settlement = turn.then(() => deps.settleVisibleFrame?.("submit-return")).catch((err) => {
34169
35387
  const msg = err instanceof Error ? err.message : String(err);
34170
35388
  deps.io.stderr(`[interactive] chat failed: ${msg}
34171
35389
  `);
34172
- }).finally(deps.requestRender);
35390
+ }).finally(() => {
35391
+ if (rowStatus === "pending") rowStatus = willQueue ? "committed" : "refused";
35392
+ deps.requestRender();
35393
+ });
34173
35394
  return awaitAdmission ? Promise.race([admitted, settlement]) : settlement;
34174
35395
  } catch (err) {
34175
35396
  const msg = err instanceof Error ? err.message : String(err);
@@ -34923,6 +36144,13 @@ async function createInteractiveApplication(deps) {
34923
36144
  });
34924
36145
  const { terminal, tui } = shell;
34925
36146
  const renderTrace = getActiveRenderTrace();
36147
+ const dumpInputWedgeOnTerminate = renderTrace ? () => {
36148
+ try {
36149
+ writeInputWedgeDump(clioStateDir(), renderTrace.snapshotInputWedge());
36150
+ } catch {
36151
+ }
36152
+ } : null;
36153
+ if (dumpInputWedgeOnTerminate) process.on("SIGTERM", dumpInputWedgeOnTerminate);
34926
36154
  const visibleEventIngress = /* @__PURE__ */ new WeakMap();
34927
36155
  let visibleEventSequence = 0;
34928
36156
  let visibleEventGeneration = 0;
@@ -34978,6 +36206,11 @@ async function createInteractiveApplication(deps) {
34978
36206
  getLeaderArmed: () => leaderArmed,
34979
36207
  getShutdownArmed: () => shutdownArmed,
34980
36208
  isAwaitingApproval: () => overlayLifecycle?.getState() === "permission-confirm",
36209
+ getPermissionInspection: () => {
36210
+ if (overlayLifecycle === void 0 || overlayLifecycle === null) return "none";
36211
+ if (!overlayLifecycle.canInspectMutation()) return "none";
36212
+ return overlayLifecycle.isInspectingMutation() ? "open" : "closed";
36213
+ },
34981
36214
  resolveVisibleEventSequence: (event) => visibleEventIngress.get(event)?.traceSequence ?? null,
34982
36215
  resolveStreamIngress: (event) => visibleEventIngress.get(event) ?? null,
34983
36216
  commitFrame: (reason) => shell.commitCurrentFrame(reason === "teardown" ? 300 : 3e4),
@@ -35086,7 +36319,7 @@ async function createInteractiveApplication(deps) {
35086
36319
  chat: deps.chat,
35087
36320
  chatPanel: {
35088
36321
  appendReplayBlock: (...args) => chatRenderer.mutate(() => chatPanel.appendReplayBlock(...args), "slash-output"),
35089
- appendUser: (text) => chatRenderer.mutate(() => chatPanel.appendUser(text), "user-submit"),
36322
+ appendUser: (text, status) => chatRenderer.mutate(() => chatPanel.appendUser(text, status), "user-submit"),
35090
36323
  clearFoldOverrides: () => chatRenderer.mutate(() => chatPanel.clearFoldOverrides(), "output-detail")
35091
36324
  },
35092
36325
  beforeSemanticSubmit: () => chatRenderer.flush(),
@@ -35385,6 +36618,7 @@ async function createInteractiveApplication(deps) {
35385
36618
  cancelParkedCalls: (reason) => deps.toolRegistry?.cancelParkedCalls(reason),
35386
36619
  onShutdown: async () => {
35387
36620
  try {
36621
+ if (dumpInputWedgeOnTerminate) process.off("SIGTERM", dumpInputWedgeOnTerminate);
35388
36622
  await deps.onShutdown();
35389
36623
  } finally {
35390
36624
  if (lease) await lease.close();
@@ -35503,7 +36737,7 @@ import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve6 }
35503
36737
 
35504
36738
  // src/tools/dispatch-scout-admission.ts
35505
36739
  init_esm_shims();
35506
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
36740
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
35507
36741
  import { join as join10 } from "node:path";
35508
36742
  var MAX_SCOUT_PLAN_DEADLINE_MS = 36e5;
35509
36743
  function record(value) {
@@ -35555,7 +36789,7 @@ function loadVerifiedScoutSource(input) {
35555
36789
  if (!existsSync9(receiptPath)) throw new Error(`dispatch: Scout source receipt '${input.ref.runId}' is unavailable`);
35556
36790
  let receipt;
35557
36791
  try {
35558
- receipt = JSON.parse(readFileSync8(receiptPath, "utf8"));
36792
+ receipt = JSON.parse(readFileSync9(receiptPath, "utf8"));
35559
36793
  } catch (error) {
35560
36794
  throw new Error(
35561
36795
  `dispatch: Scout source receipt is unreadable: ${error instanceof Error ? error.message : String(error)}`
@@ -36411,7 +37645,7 @@ function createDispatchTool(inputDeps, surfaceOptions = {}) {
36411
37645
  const admission = createDispatchAdmissionController(deps);
36412
37646
  let runnerPromise = null;
36413
37647
  const loadRunner = () => {
36414
- runnerPromise ??= (surfaceOptions.loadRunner ?? (() => import("./dispatch-runner-22ZCNOM3.js")))();
37648
+ runnerPromise ??= (surfaceOptions.loadRunner ?? (() => import("./dispatch-runner-VVA4SRRH.js")))();
36415
37649
  return runnerPromise;
36416
37650
  };
36417
37651
  return {
@@ -36649,7 +37883,7 @@ function registerAllTools(registry, deps = {}) {
36649
37883
  ...builtin(
36650
37884
  lazyTool(
36651
37885
  monitorToolSurface,
36652
- async () => (await import("./monitor-XLDVO7TN.js")).createMonitorTool({ dispatch, runEvents: dispatchRunEvents })
37886
+ async () => (await import("./monitor-MMVTJABD.js")).createMonitorTool({ dispatch, runEvents: dispatchRunEvents })
36653
37887
  ),
36654
37888
  {
36655
37889
  path: "src/tools/monitor.ts",
@@ -36659,7 +37893,7 @@ function registerAllTools(registry, deps = {}) {
36659
37893
  });
36660
37894
  registry.register({
36661
37895
  ...builtin(
36662
- lazyTool(steerToolSurface, async () => (await import("./steer-GGWFUJUD.js")).createSteerTool({ dispatch })),
37896
+ lazyTool(steerToolSurface, async () => (await import("./steer-CF5TDANS.js")).createSteerTool({ dispatch })),
36663
37897
  {
36664
37898
  path: "src/tools/steer.ts",
36665
37899
  scope: "core"
@@ -36806,7 +38040,7 @@ async function resolveApiKeyForTarget(target, providers, signal) {
36806
38040
  const resolved = await providers.auth.resolveForTarget(target, runtime, signal ? { signal } : void 0);
36807
38041
  return resolved.apiKey;
36808
38042
  }
36809
- function createBackgroundMemoryModelClient(providers, settings, timeoutMs) {
38043
+ function createBackgroundMemoryModelClient(providers, settings, timeoutMs, bus) {
36810
38044
  const targetId = settings.background.target?.trim();
36811
38045
  const wireModelId = settings.background.model?.trim();
36812
38046
  if (!targetId || !wireModelId) return null;
@@ -36830,22 +38064,52 @@ function createBackgroundMemoryModelClient(providers, settings, timeoutMs) {
36830
38064
  const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
36831
38065
  const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
36832
38066
  applyModelCapabilityPatch(model, refined.capabilities);
38067
+ const costProvenance = normalizeCostProvenance(refined.costProvenance);
38068
+ const endpointKey = canonicalEndpointKey(refined.target);
36833
38069
  return {
36834
- async complete(request) {
36835
- const apiKey = targetRequiresAuth(refined.target, refined.runtime) ? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey : LOCAL_API_KEY_FALLBACK2;
36836
- return completeEngineText({
36837
- model,
36838
- systemPrompt: request.systemPrompt,
36839
- userPrompt: request.userPrompt,
36840
- maxTokens: request.maxTokens,
36841
- // Always off, never the operator's chat thinking level. A model that
36842
- // reasons anyway still works: `completeEngineText` keeps only text
36843
- // blocks, and the memory output budget leaves room for the preamble.
36844
- thinkingLevel: "off",
36845
- signal: request.signal,
36846
- timeoutMs,
36847
- ...apiKey === void 0 ? {} : { apiKey }
36848
- });
38070
+ targetId,
38071
+ wireModelId: refined.wireModelId,
38072
+ endpointKey,
38073
+ client: {
38074
+ // Wrapped at the layer that knows a request left the process: the step
38075
+ // holds endpoint capacity while it is out and publishes the cache
38076
+ // disturbance even when a timeout means the usage sink never sees it.
38077
+ complete: announceMemoryStepEndpoint({ bus, endpointKey, targetId }, async (request) => {
38078
+ const apiKey = targetRequiresAuth(refined.target, refined.runtime) ? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey : LOCAL_API_KEY_FALLBACK2;
38079
+ const startedAt = Date.now();
38080
+ const completion = await completeEngineText({
38081
+ model,
38082
+ systemPrompt: request.systemPrompt,
38083
+ userPrompt: request.userPrompt,
38084
+ maxTokens: request.maxTokens,
38085
+ // Always off, never the operator's chat thinking level. A model that
38086
+ // reasons anyway still works: `completeEngineText` keeps only text
38087
+ // blocks, and the memory output budget leaves room for the preamble.
38088
+ thinkingLevel: "off",
38089
+ signal: request.signal,
38090
+ timeoutMs,
38091
+ ...apiKey === void 0 ? {} : { apiKey }
38092
+ });
38093
+ return {
38094
+ text: completion.text,
38095
+ inputTokens: completion.inputTokens,
38096
+ outputTokens: completion.outputTokens,
38097
+ usage: {
38098
+ targetId,
38099
+ attributedModelId: refined.wireModelId,
38100
+ input: completion.usage.input,
38101
+ output: completion.usage.output,
38102
+ cacheRead: completion.usage.cacheRead,
38103
+ cacheWrite: completion.usage.cacheWrite,
38104
+ reasoning: completion.usage.reasoning,
38105
+ totalTokens: completion.usage.totalTokens,
38106
+ costUsd: completion.usage.costUsd,
38107
+ costProvenance,
38108
+ durationMs: Date.now() - startedAt,
38109
+ backend: completion.backend
38110
+ }
38111
+ };
38112
+ })
36849
38113
  }
36850
38114
  };
36851
38115
  }
@@ -36916,7 +38180,7 @@ async function resolveCompactionModel(settings, providers) {
36916
38180
  const model = synthesizeOrchestratorModel(providers, target, wireModelId);
36917
38181
  if (!model) return null;
36918
38182
  const apiKey = await resolveApiKeyForTarget(target, providers);
36919
- const resolution = { model, targetId };
38183
+ const resolution = { model, targetId, endpointKey: canonicalEndpointKey(target) };
36920
38184
  if (apiKey !== void 0) resolution.apiKey = apiKey;
36921
38185
  return resolution;
36922
38186
  }
@@ -36988,12 +38252,19 @@ async function runCompactionFlow(session, settings, providers, instructions, tri
36988
38252
  const activeLeafTurnId = session.tree(meta.id).leafId ?? void 0;
36989
38253
  const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
36990
38254
  if (entries.length === 0) return null;
36991
- const result = await compact({
36992
- entries,
36993
- model: resolved.model,
36994
- ...resolved.apiKey !== void 0 ? { apiKey: resolved.apiKey } : {},
36995
- ...instructions !== void 0 ? { instructions } : {}
36996
- });
38255
+ const releaseEndpointSlot = resolved.endpointKey === null ? () => {
38256
+ } : registerForegroundStream(resolved.endpointKey);
38257
+ let result;
38258
+ try {
38259
+ result = await compact({
38260
+ entries,
38261
+ model: resolved.model,
38262
+ ...resolved.apiKey !== void 0 ? { apiKey: resolved.apiKey } : {},
38263
+ ...instructions !== void 0 ? { instructions } : {}
38264
+ });
38265
+ } finally {
38266
+ releaseEndpointSlot();
38267
+ }
36997
38268
  if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
36998
38269
  const entry = {
36999
38270
  kind: "compactionSummary",
@@ -37362,6 +38633,40 @@ async function bootOrchestrator(options = {}) {
37362
38633
  let deferredWatchdogNoticeSink = null;
37363
38634
  const memoryTracePath = taskMemoryTracePath();
37364
38635
  const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
38636
+ let backgroundMemoryRoute = null;
38637
+ const recordBackgroundMemoryUsage = (usage) => {
38638
+ const meta = session?.current() ?? null;
38639
+ try {
38640
+ recordBackgroundMemoryStep({
38641
+ usage,
38642
+ stateDir: clioStateDir(),
38643
+ sessionId: meta?.id ?? null,
38644
+ // The identity the session ledger is filed under, so `usage report
38645
+ // --repo` selects these rows with the same hash it selects ledgers with.
38646
+ repoIdentity: meta ? meta.cwdHash || cwdHash(meta.cwd || process.cwd()) : null,
38647
+ ...observability === void 0 ? {} : { observability }
38648
+ });
38649
+ } catch {
38650
+ }
38651
+ };
38652
+ const proposeInjectedMemoryEntries = (entries) => {
38653
+ const meta = session?.current() ?? null;
38654
+ void proposeInjectedTaskMemory(clioDataDir(), {
38655
+ sessionId: meta?.id ?? null,
38656
+ cwd: meta?.cwd || process.cwd(),
38657
+ entries
38658
+ }).then((result2) => {
38659
+ for (const error of result2.errors) {
38660
+ process.stderr.write(`[clio:memory] proposed record not written for ${error}
38661
+ `);
38662
+ }
38663
+ }).catch((error) => {
38664
+ process.stderr.write(
38665
+ `[clio:memory] proposed records not written: ${error instanceof Error ? error.message : String(error)}
38666
+ `
38667
+ );
38668
+ });
38669
+ };
37365
38670
  const memoryIntervention = createMemoryInterventionRegistration({
37366
38671
  bank: taskMemoryBank,
37367
38672
  telemetry: createTaskMemoryTelemetrySink(),
@@ -37376,8 +38681,21 @@ async function bootOrchestrator(options = {}) {
37376
38681
  },
37377
38682
  getModelClient: () => {
37378
38683
  const settings = effectiveSettingsForDispatch?.();
37379
- return settings === void 0 ? null : createBackgroundMemoryModelClient(providers, settings, settings.memory.intervention.timeoutMs);
37380
- }
38684
+ backgroundMemoryRoute = settings === void 0 ? null : createBackgroundMemoryModelClient(providers, settings, settings.memory.intervention.timeoutMs, bus);
38685
+ return backgroundMemoryRoute?.client ?? null;
38686
+ },
38687
+ // A single-slot local server serves one request at a time, so a memory step
38688
+ // started while the operator's turn is streaming either waits behind it or
38689
+ // makes the server swap the resident model out to answer. The chat loop
38690
+ // registers its stream on the endpoint it streams against, so the two are
38691
+ // compared by the same canonical key dispatch admission counts slots on.
38692
+ backgroundEndpointBusy: () => {
38693
+ const endpointKey = backgroundMemoryRoute?.endpointKey ?? null;
38694
+ if (endpointKey === null) return false;
38695
+ return (foregroundStreamUsage()[endpointKey] ?? 0) > 0;
38696
+ },
38697
+ onStepUsage: (usage) => recordBackgroundMemoryUsage(usage),
38698
+ onInjectedEntries: (entries) => proposeInjectedMemoryEntries(entries)
37381
38699
  });
37382
38700
  middleware.registerHook(memoryIntervention);
37383
38701
  const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
@@ -37705,6 +39023,12 @@ async function bootOrchestrator(options = {}) {
37705
39023
  );
37706
39024
  }
37707
39025
  const chat = createChatLoop({
39026
+ // The pre-warm holds one slot on its endpoint while it runs, so dispatch
39027
+ // admission (#250) sees it exactly as it sees the orchestrator's own turn.
39028
+ registerPrewarmEndpointSlot: (runtime) => {
39029
+ const key = canonicalEndpointKey(runtime.runtimeResolution.target);
39030
+ return key === null ? null : registerForegroundStream(key);
39031
+ },
37708
39032
  getSettings: getCurrentSettings,
37709
39033
  providers,
37710
39034
  middleware,
@@ -37761,7 +39085,12 @@ async function bootOrchestrator(options = {}) {
37761
39085
  autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers)
37762
39086
  } : {},
37763
39087
  toolRegistry,
37764
- hasAttachedDispatch: () => dispatchBackground.size() > 0
39088
+ hasAttachedDispatch: () => dispatchBackground.size() > 0,
39089
+ // The pre-warm buys latency for a person about to type the next turn. A
39090
+ // headless `run` submits its one prompt immediately and an unattended boot
39091
+ // never submits at all, so neither has latency to buy; the ACP surface has
39092
+ // an operator on the other end of the client and keeps it.
39093
+ isLatencySurface: () => interactive || acpMode
37765
39094
  });
37766
39095
  termination.onDrain(async () => {
37767
39096
  chat.dispose();
@@ -37964,7 +39293,11 @@ async function bootOrchestrator(options = {}) {
37964
39293
  lastDecision: memoryIntervention.lastDecision(),
37965
39294
  bank,
37966
39295
  activity: memoryIntervention.recentActivity(),
37967
- stepInFlight: memoryIntervention.stepInFlight()
39296
+ stepInFlight: memoryIntervention.stepInFlight(),
39297
+ // Folded from the telemetry ledger, which is durable across sessions,
39298
+ // so `/memory` answers what the tier has cost since it was turned on
39299
+ // rather than what it cost since this process started.
39300
+ spend: readTaskMemorySpendSummary(clioStateDir())
37968
39301
  };
37969
39302
  },
37970
39303
  getTaskMemorySeedOffer,
@@ -38137,4 +39470,4 @@ export {
38137
39470
  resolveApiKeyForTarget,
38138
39471
  synthesizeOrchestratorModel
38139
39472
  };
38140
- //# sourceMappingURL=orchestrator-6KSPYRHA.js.map
39473
+ //# sourceMappingURL=orchestrator-ZKBPCHW6.js.map