@iowarp/clio-coder 0.3.3 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/CONTRIBUTING.md +7 -7
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-2BEHC4DL.js} +9 -8
  5. package/dist/{agents-72W3BI7I.js → agents-LNNFTM53.js} +29 -24
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-KXXFI2VS.js} +14 -10
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
  10. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  11. package/dist/chunk-3BPUFZDL.js +37 -0
  12. package/dist/chunk-43AOLP7E.js +375 -0
  13. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  14. package/dist/{chunk-6SGHMWE3.js → chunk-5JGRAMKL.js} +5 -5
  15. package/dist/{chunk-V6RTAOC2.js → chunk-6US73PDB.js} +572 -51
  16. package/dist/{chunk-5UFT4SUX.js → chunk-6XXKFVSN.js} +3 -3
  17. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  18. package/dist/chunk-AOCYTWAV.js +449 -0
  19. package/dist/chunk-CFGTUFWB.js +67 -0
  20. package/dist/chunk-CJUB2JJ2.js +1478 -0
  21. package/dist/{chunk-FNTMWMX5.js → chunk-CKXWIANG.js} +14 -12
  22. package/dist/{chunk-PIWWS5BL.js → chunk-CYQKWTG3.js} +63 -78
  23. package/dist/{chunk-LZSJBIVT.js → chunk-DJVECN66.js} +271 -762
  24. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  25. package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
  26. package/dist/{chunk-STBPMHSX.js → chunk-EKY57CSP.js} +51 -84
  27. package/dist/{chunk-DUYJ5IO6.js → chunk-EYPA3EGJ.js} +12 -4
  28. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  29. package/dist/chunk-FYYLNIL5.js +313 -0
  30. package/dist/{chunk-OQ33BKR3.js → chunk-G7MUEIGA.js} +3 -60
  31. package/dist/chunk-GEYXPTRF.js +613 -0
  32. package/dist/chunk-GOXNB3AO.js +261 -0
  33. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  34. package/dist/chunk-HWUFFB6L.js +83 -0
  35. package/dist/{chunk-4XUGQOHA.js → chunk-K7T3E2SR.js} +15 -8
  36. package/dist/chunk-K7VKOLQQ.js +15 -0
  37. package/dist/{chunk-UFIIWP2H.js → chunk-KHSFENX2.js} +8 -8
  38. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  39. package/dist/chunk-LCGCVYZ4.js +57 -0
  40. package/dist/chunk-LL4KHSZI.js +22 -0
  41. package/dist/{chunk-PAJK6MAQ.js → chunk-LYF7OHWH.js} +42 -15
  42. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  43. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  44. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  45. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  46. package/dist/chunk-ODFEOB4F.js +1082 -0
  47. package/dist/{chunk-AGYYIBLL.js → chunk-OH3TOQTB.js} +6 -2
  48. package/dist/chunk-OZNBF4L3.js +23 -0
  49. package/dist/{chunk-DSELYM6W.js → chunk-PBTHKCPN.js} +30 -10
  50. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  51. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  52. package/dist/{chunk-SRDMMSEP.js → chunk-QM3F2GKX.js} +1063 -1645
  53. package/dist/{chunk-X6IAEBZR.js → chunk-QNQHSOLF.js} +7 -7
  54. package/dist/{chunk-OC7FIQPC.js → chunk-R46L2BIR.js} +10 -7
  55. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  56. package/dist/{chunk-6N5PTWMY.js → chunk-RY3LY4J5.js} +50 -13
  57. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  58. package/dist/{chunk-OOJYHWRB.js → chunk-TSHXZTOQ.js} +6 -5
  59. package/dist/chunk-TZSKNMZG.js +434 -0
  60. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  61. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  62. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  63. package/dist/chunk-W6GROXXM.js +69 -0
  64. package/dist/{chunk-OAO4GE4M.js → chunk-WHGPSPT5.js} +2 -2
  65. package/dist/chunk-WHJYKASB.js +677 -0
  66. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  67. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  68. package/dist/{chunk-TZTZS7QK.js → chunk-XE2VEJHX.js} +5 -3
  69. package/dist/{chunk-LM5TQCJZ.js → chunk-XF5N4U5A.js} +8 -7
  70. package/dist/{chunk-LWLEKMDQ.js → chunk-XXQNGV4M.js} +1073 -552
  71. package/dist/{chunk-KZWTDYJF.js → chunk-XYDYPRZI.js} +7 -7
  72. package/dist/chunk-ZGVHUX3M.js +66 -0
  73. package/dist/{chunk-LW6DSM3M.js → chunk-ZRGEBJ4T.js} +1192 -1119
  74. package/dist/{chunk-2DJ2KNFG.js → chunk-ZXF4XRKW.js} +202 -40
  75. package/dist/chunk-ZZMN5OM4.js +122 -0
  76. package/dist/cli/index.js +34 -30
  77. package/dist/{clio-JOU4FXVA.js → clio-M2KGYUFZ.js} +7 -6
  78. package/dist/{code-nav-7AX6FYE6.js → code-nav-GQNL7XA6.js} +8 -6
  79. package/dist/codewiki/build-worker.js +4 -4
  80. package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
  81. package/dist/{config-XCDVKR23.js → config-XUUYQIWO.js} +47 -35
  82. package/dist/{configure-4GAP54ZW.js → configure-IHJ7YOMV.js} +18 -15
  83. package/dist/{context-77FM5DV5.js → context-74JLXAWD.js} +18 -10
  84. package/dist/{context-4UOGGLQ5.js → context-75MIWW3U.js} +41 -29
  85. package/dist/{context-5VKGUVJJ.js → context-ZQ7SIFJV.js} +85 -9
  86. package/dist/{context-clear-XXJRLCJJ.js → context-clear-GYKWNUML.js} +41 -29
  87. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  88. package/dist/context-working-set-UX5KEP4J.js +1553 -0
  89. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-GIJBHNFL.js} +47 -32
  90. package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
  91. package/dist/{doctor-HR46URBJ.js → doctor-SVJ5BZCW.js} +12 -12
  92. package/dist/{eval-XSSNATB4.js → eval-CG6LLBLD.js} +54 -238
  93. package/dist/{evidence-6HG2PY2B.js → evidence-ZYFIEN42.js} +57 -28
  94. package/dist/{evolve-K7YU3NCY.js → evolve-QGEXEMDW.js} +36 -25
  95. package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
  96. package/dist/{fleet-VY3HHKN6.js → fleet-S5R4ZOQY.js} +73 -44
  97. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-BHSNPBMH.js} +3 -3
  98. package/dist/{init-JYGXI3FK.js → init-5DRU55YR.js} +49 -37
  99. package/dist/memory-7YKKR6UC.js +467 -0
  100. package/dist/{models-I5QWSEOM.js → models-ZPOLRU2C.js} +24 -21
  101. package/dist/{monitor-GE4ID3IA.js → monitor-US5F5YGZ.js} +73 -46
  102. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-E2AL4T5N.js} +1624 -1007
  103. package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
  104. package/dist/{reset-L2FQEE3E.js → reset-KZ652EK6.js} +6 -5
  105. package/dist/{run-ZU3QMZPZ.js → run-SRNBKDWD.js} +76 -54
  106. package/dist/{share-S5BZQC5I.js → share-CGZE33UP.js} +7 -6
  107. package/dist/{skills-X5VXCRNQ.js → skills-S2X4DLY5.js} +4 -4
  108. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-W2GGIC4R.js} +40 -29
  109. package/dist/{targets-SNCPI2NR.js → targets-54SWINWB.js} +28 -23
  110. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-SAIF2OGY.js} +6 -4
  111. package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
  112. package/dist/{upgrade-JQHHPQ4K.js → upgrade-JKAR27XC.js} +20 -19
  113. package/dist/{usage-OR4O5SMZ.js → usage-MSAWCLX4.js} +79 -36
  114. package/dist/verifiers-NCBTHHN2.js +1220 -0
  115. package/dist/verify-X5HDROLA.js +25 -0
  116. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-GUSOQ6ZP.js} +50 -37
  117. package/dist/worker/entry.js +90 -70
  118. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  119. package/docs/README.md +8 -7
  120. package/docs/acp.md +1 -1
  121. package/docs/alcf-provider.md +1 -1
  122. package/docs/architecture.md +2 -2
  123. package/docs/artifact-placement.md +1 -2
  124. package/docs/artifact-versions.md +1 -1
  125. package/docs/built-in-agents.md +1 -1
  126. package/docs/capacity-and-scheduling.md +1 -1
  127. package/docs/commands-and-modes.md +60 -26
  128. package/docs/config-knobs-audit.md +1 -2
  129. package/docs/configuration-and-targets.md +26 -1
  130. package/docs/context-engine.md +67 -13
  131. package/docs/context-working-set.md +194 -0
  132. package/docs/development-pipeline.md +1 -1
  133. package/docs/documentation-coverage.md +6 -6
  134. package/docs/documentation-guide.md +7 -6
  135. package/docs/environment-variables.md +2 -1
  136. package/docs/eval-runner.md +1 -1
  137. package/docs/evals-internal.md +4 -32
  138. package/docs/evidence-and-memory.md +139 -7
  139. package/docs/evolution.md +1 -1
  140. package/docs/exit-codes-and-output.md +1 -1
  141. package/docs/extensions-and-sharing.md +2 -2
  142. package/docs/fleet-dispatch.md +49 -8
  143. package/docs/glossary.md +21 -1
  144. package/docs/installation-and-lifecycle.md +2 -2
  145. package/docs/middleware-and-components.md +19 -2
  146. package/docs/model-catalog.md +7 -9
  147. package/docs/observability.md +4 -4
  148. package/docs/proactive-memory.md +26 -16
  149. package/docs/prompt-envelope-and-tools.md +7 -5
  150. package/docs/provider-adapter-cookbook.md +1 -1
  151. package/docs/release-cut-checklist.md +43 -40
  152. package/docs/safety-model.md +49 -8
  153. package/docs/scientific-validation.md +21 -3
  154. package/docs/session-lifecycle.md +3 -3
  155. package/docs/skills-marketplace.md +1 -1
  156. package/docs/tool-usage.md +79 -12
  157. package/docs/trace-store.md +1 -1
  158. package/docs/troubleshooting.md +1 -1
  159. package/docs/tui-design.md +38 -4
  160. package/docs/worker-dispatch-mechanics.md +11 -1
  161. package/package.json +13 -13
  162. package/skills/meta/clio-test/SKILL.md +20 -17
  163. package/skills/meta/clio-test/evals.md +3 -3
  164. package/skills/meta/clio-test/references/harness.md +35 -6
  165. package/skills/meta/clio-test/references/test-map.md +20 -10
  166. package/skills/registry.yaml +2 -2
  167. package/skills/skill-marketplace.json +1 -1
  168. package/src/cli/agents.ts +2 -3
  169. package/src/cli/argv.ts +14 -1
  170. package/src/cli/context-working-set.ts +513 -0
  171. package/src/cli/context.ts +8 -0
  172. package/src/cli/evidence.ts +20 -2
  173. package/src/cli/fleet.ts +15 -0
  174. package/src/cli/index.ts +5 -1
  175. package/src/cli/memory.ts +272 -10
  176. package/src/cli/modes/json-stream.ts +2 -2
  177. package/src/cli/modes/print.ts +12 -1
  178. package/src/cli/run.ts +22 -2
  179. package/src/cli/targets.ts +12 -3
  180. package/src/cli/usage.ts +55 -7
  181. package/src/cli/verifiers.ts +325 -0
  182. package/src/core/bash-exec.ts +39 -14
  183. package/src/core/bus-events.ts +22 -4
  184. package/src/core/config.ts +54 -0
  185. package/src/core/defaults.ts +50 -3
  186. package/src/core/response-model-id.ts +134 -0
  187. package/src/core/toml.ts +62 -0
  188. package/src/core/verification-scripts.ts +6 -0
  189. package/src/core/workspace-files.ts +0 -1
  190. package/src/domains/agents/builtins/architect.md +1 -1
  191. package/src/domains/agents/builtins/verifier.md +3 -0
  192. package/src/domains/agents/catalog.ts +5 -4
  193. package/src/domains/agents/recipe.ts +54 -14
  194. package/src/domains/agents/result-contract.ts +7 -4
  195. package/src/domains/config/classify.ts +1 -0
  196. package/src/domains/context/bootstrap.ts +36 -27
  197. package/src/domains/context/project-metadata.ts +19 -63
  198. package/src/domains/context/prompt-context.ts +8 -0
  199. package/src/domains/context/working-set/contract.ts +161 -0
  200. package/src/domains/context/working-set/defaults.ts +28 -0
  201. package/src/domains/context/working-set/engine.ts +203 -0
  202. package/src/domains/context/working-set/fold.ts +62 -0
  203. package/src/domains/context/working-set/horizon.ts +38 -0
  204. package/src/domains/context/working-set/marker.ts +103 -0
  205. package/src/domains/context/working-set/path-index.ts +436 -0
  206. package/src/domains/context/working-set/payload.ts +152 -0
  207. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  208. package/src/domains/context/working-set/policies/index.ts +20 -0
  209. package/src/domains/context/working-set/policies/structural.ts +160 -0
  210. package/src/domains/context/working-set/project.ts +132 -0
  211. package/src/domains/context/working-set/protect.ts +109 -0
  212. package/src/domains/context/working-set/recall.ts +177 -0
  213. package/src/domains/context/working-set/replay/controls.ts +112 -0
  214. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  215. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  216. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  217. package/src/domains/context/working-set/replay/report.ts +139 -0
  218. package/src/domains/context/working-set/replay/runner.ts +325 -0
  219. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  220. package/src/domains/context/working-set/replay/trace.ts +21 -0
  221. package/src/domains/context/working-set/visible.ts +54 -0
  222. package/src/domains/dispatch/budget-envelope.ts +396 -0
  223. package/src/domains/dispatch/contract.ts +2 -0
  224. package/src/domains/dispatch/extension.ts +81 -27
  225. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  227. package/src/domains/dispatch/state.ts +1 -0
  228. package/src/domains/dispatch/types.ts +10 -3
  229. package/src/domains/dispatch/validation.ts +14 -0
  230. package/src/domains/dispatch/worker-spawn.ts +14 -3
  231. package/src/domains/eval/metrics/evidence.ts +0 -116
  232. package/src/domains/eval/metrics/invariants.ts +1 -1
  233. package/src/domains/eval/runners/clio-run.ts +1 -10
  234. package/src/domains/eval/runners/external-command.ts +2 -29
  235. package/src/domains/eval/schema/suite.ts +0 -7
  236. package/src/domains/eval/suites/run.ts +1 -7
  237. package/src/domains/evidence/build.ts +112 -45
  238. package/src/domains/evidence/eval.ts +24 -7
  239. package/src/domains/evidence/index.ts +53 -0
  240. package/src/domains/evidence/ordering.ts +12 -0
  241. package/src/domains/evidence/run-trust.ts +221 -0
  242. package/src/domains/evidence/store.ts +46 -6
  243. package/src/domains/evidence/trust-status.ts +854 -0
  244. package/src/domains/evidence/types.ts +26 -0
  245. package/src/domains/memory/index.ts +22 -0
  246. package/src/domains/memory/operations.ts +58 -1
  247. package/src/domains/memory/promotion.ts +281 -0
  248. package/src/domains/memory/prompt-section.ts +25 -5
  249. package/src/domains/memory/proposal.ts +51 -7
  250. package/src/domains/memory/task-bank.ts +3 -2
  251. package/src/domains/memory/task-memory-handoff.ts +181 -24
  252. package/src/domains/memory/task-memory-policy.ts +3 -1
  253. package/src/domains/memory/types.ts +37 -0
  254. package/src/domains/memory/validate.ts +178 -0
  255. package/src/domains/middleware/memory-intervention.ts +38 -25
  256. package/src/domains/middleware/runtime.ts +6 -0
  257. package/src/domains/middleware/skills-reminder.ts +19 -4
  258. package/src/domains/middleware/stalled-turn.ts +208 -5
  259. package/src/domains/middleware/types.ts +10 -0
  260. package/src/domains/observability/contract.ts +6 -1
  261. package/src/domains/observability/cost.ts +20 -4
  262. package/src/domains/observability/extension.ts +2 -2
  263. package/src/domains/providers/index.ts +3 -0
  264. package/src/domains/providers/model-discovery.ts +9 -0
  265. package/src/domains/providers/runtime-resolution.ts +38 -1
  266. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  267. package/src/domains/providers/types/context-window-slots.ts +18 -0
  268. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  269. package/src/domains/safety/autonomy.ts +1 -1
  270. package/src/domains/safety/call-target.ts +211 -14
  271. package/src/domains/safety/decision-presentation.ts +268 -0
  272. package/src/domains/safety/default-path-policy.ts +8 -0
  273. package/src/domains/safety/finish-contract.ts +4 -3
  274. package/src/domains/safety/policy-engine.ts +48 -6
  275. package/src/domains/safety/redaction.ts +73 -0
  276. package/src/domains/session/compaction/compact.ts +23 -1
  277. package/src/domains/session/compaction/cut-point.ts +2 -0
  278. package/src/domains/session/compaction/tokens.ts +16 -1
  279. package/src/domains/session/context-ledger.ts +12 -1
  280. package/src/domains/session/decision-board.ts +4 -0
  281. package/src/domains/session/entries.ts +110 -1
  282. package/src/domains/session/history.ts +68 -19
  283. package/src/domains/session/manager.ts +9 -2
  284. package/src/domains/session/migrations/index.ts +22 -3
  285. package/src/domains/session/usage.ts +24 -7
  286. package/src/engine/acp/event-mapper.ts +7 -0
  287. package/src/engine/acp/server.ts +32 -2
  288. package/src/engine/agent.ts +18 -1
  289. package/src/engine/apis/lmstudio.ts +25 -4
  290. package/src/engine/apis/openai-completions.ts +147 -22
  291. package/src/engine/claude/sdk-runtime.ts +8 -2
  292. package/src/engine/claude/tool-safety.ts +13 -0
  293. package/src/engine/loop-guard.ts +27 -3
  294. package/src/engine/session.ts +9 -3
  295. package/src/engine/worker-events.ts +4 -3
  296. package/src/engine/worker-runtime.ts +59 -54
  297. package/src/entry/orchestrator.ts +34 -5
  298. package/src/interactive/chat-loop-messages.ts +40 -6
  299. package/src/interactive/chat-loop.ts +13 -0
  300. package/src/interactive/chat-panel.ts +17 -1
  301. package/src/interactive/chat-renderer.ts +49 -24
  302. package/src/interactive/clio-editor.ts +44 -7
  303. package/src/interactive/context-meter.ts +10 -0
  304. package/src/interactive/context-overlay.ts +120 -7
  305. package/src/interactive/context-recall-command.ts +110 -0
  306. package/src/interactive/cost-overlay.ts +39 -8
  307. package/src/interactive/dispatch-board.ts +212 -35
  308. package/src/interactive/footer/widgets.ts +13 -0
  309. package/src/interactive/interactive-application.ts +6 -1
  310. package/src/interactive/interactive-input-runtime.ts +11 -1
  311. package/src/interactive/interactive-presentation.ts +11 -1
  312. package/src/interactive/interactive-slash-runtime.ts +37 -1
  313. package/src/interactive/memory-overlay.ts +89 -4
  314. package/src/interactive/model-session-replay.ts +21 -0
  315. package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
  316. package/src/interactive/overlay-frame.ts +5 -2
  317. package/src/interactive/overlay-general-openers.ts +46 -1
  318. package/src/interactive/overlay-key-routing.ts +41 -1
  319. package/src/interactive/overlay-lifecycle.ts +11 -4
  320. package/src/interactive/overlay-permission-lifecycle.ts +23 -8
  321. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  322. package/src/interactive/overlay-transitions.ts +11 -0
  323. package/src/interactive/overlays/ask-user.ts +74 -30
  324. package/src/interactive/overlays/decisions.ts +3 -1
  325. package/src/interactive/permission-hint.ts +35 -0
  326. package/src/interactive/permission-overlay.ts +95 -45
  327. package/src/interactive/renderers/tool-execution.ts +37 -51
  328. package/src/interactive/session-last-turn.ts +8 -1
  329. package/src/interactive/session-transcript.ts +2 -2
  330. package/src/interactive/session-usage-reseed.ts +36 -10
  331. package/src/interactive/slash-commands.ts +31 -4
  332. package/src/interactive/status/summary.ts +5 -0
  333. package/src/interactive/status/types.ts +5 -0
  334. package/src/interactive/terminal-lease.ts +1 -0
  335. package/src/interactive/turn-context.ts +333 -110
  336. package/src/interactive/turn-middleware.ts +7 -6
  337. package/src/interactive/turn-runtime.ts +37 -8
  338. package/src/interactive/turn-state.ts +3 -0
  339. package/src/interactive/worker-progress.ts +440 -0
  340. package/src/interactive/worker-stream.ts +51 -110
  341. package/src/tools/agent-tools.ts +39 -7
  342. package/src/tools/ask-user.ts +21 -1
  343. package/src/tools/bash.ts +144 -82
  344. package/src/tools/builtin-tool-catalog.ts +11 -5
  345. package/src/tools/context/index.ts +107 -5
  346. package/src/tools/context/surface.ts +3 -2
  347. package/src/tools/core-bootstrap.ts +21 -0
  348. package/src/tools/dispatch-arguments.ts +8 -0
  349. package/src/tools/dispatch-event-text.ts +19 -0
  350. package/src/tools/dispatch-runner.ts +9 -7
  351. package/src/tools/dispatch.ts +24 -1
  352. package/src/tools/monitor.ts +43 -20
  353. package/src/tools/registry.ts +72 -10
  354. package/src/tools/result-disposition.ts +706 -0
  355. package/src/tools/result-shaping.ts +321 -20
  356. package/src/tools/safe-exec.ts +2 -0
  357. package/src/tools/verify/authoring.ts +1120 -0
  358. package/src/tools/verify/catalog.ts +346 -0
  359. package/src/tools/verify/index.ts +13 -3
  360. package/src/tools/verify/scripts.ts +135 -37
  361. package/src/tools/verify/surface.ts +9 -5
  362. package/src/tools/worker-evidence.ts +54 -12
  363. package/src/worker/spec-contract.ts +43 -3
  364. package/dist/chunk-J7CWMCQD.js +0 -255
  365. package/dist/chunk-T6YILFSB.js +0 -80
  366. package/dist/chunk-VAKQQHWR.js +0 -434
  367. package/dist/chunk-VPAYEGVX.js +0 -184
  368. package/dist/chunk-XBXAASKX.js +0 -18
  369. package/dist/memory-WFZMGYHX.js +0 -236
  370. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -1,10 +1,27 @@
1
1
  import { createRequire as __clioCreateRequire } from "node:module"; const require = __clioCreateRequire(import.meta.url);
2
+ import {
3
+ ShareDomainModule
4
+ } from "./chunk-WR67VIZY.js";
2
5
  import {
3
6
  bootstrapInputFromInitOptions,
4
7
  modelBootstrapGenerate,
5
8
  resolveBootstrapRoute
6
- } from "./chunk-FNTMWMX5.js";
9
+ } from "./chunk-CKXWIANG.js";
7
10
  import "./chunk-J5Q24KAG.js";
11
+ import {
12
+ DEFAULT_KEEP_RECENT_TOKENS,
13
+ DEFAULT_RESERVE_TOKENS,
14
+ buildEvictionFields,
15
+ calculateContextTokens,
16
+ compactionCut,
17
+ estimateTokens,
18
+ findCutPoint,
19
+ getLastAssistantUsage,
20
+ planEviction,
21
+ projectWorkingSet,
22
+ resolveWorkingSetPolicy,
23
+ selectVisibleEntries
24
+ } from "./chunk-WHJYKASB.js";
8
25
  import {
9
26
  assistantSessionPayload,
10
27
  backendCacheVerdict,
@@ -37,37 +54,33 @@ import {
37
54
  toolNamesFromAgentState,
38
55
  toolResultSummary,
39
56
  toolSignatureFromState
40
- } from "./chunk-DSELYM6W.js";
57
+ } from "./chunk-PBTHKCPN.js";
41
58
  import {
42
59
  SchedulingDomainModule,
43
60
  createPromptsDomainModule,
44
61
  renderAgentCatalogSectionsFromSpecs
45
- } from "./chunk-LM5TQCJZ.js";
46
- import {
47
- ShareDomainModule
48
- } from "./chunk-ORBHGJC5.js";
62
+ } from "./chunk-XF5N4U5A.js";
49
63
  import {
50
64
  AgentsDomainModule,
51
65
  SafetyDomainModule
52
- } from "./chunk-PAJK6MAQ.js";
66
+ } from "./chunk-LYF7OHWH.js";
53
67
  import {
54
68
  LifecycleDomainModule,
55
69
  describeUpgradeNotice
56
- } from "./chunk-5UFT4SUX.js";
70
+ } from "./chunk-6XXKFVSN.js";
71
+ import {
72
+ ensureClioState,
73
+ takeUpgradeNotice
74
+ } from "./chunk-KHSFENX2.js";
57
75
  import {
58
76
  ConfigDomainModule,
59
77
  createConfigDomainModule,
60
78
  loadDomains
61
- } from "./chunk-OC7FIQPC.js";
62
- import {
63
- ensureClioState,
64
- takeUpgradeNotice
65
- } from "./chunk-UFIIWP2H.js";
79
+ } from "./chunk-R46L2BIR.js";
66
80
  import {
67
81
  compileScoutTransition,
68
82
  createDispatchRunEventRegistry
69
- } from "./chunk-6SGHMWE3.js";
70
- import "./chunk-XBXAASKX.js";
83
+ } from "./chunk-5JGRAMKL.js";
71
84
  import {
72
85
  monitorToolSurface
73
86
  } from "./chunk-FJ3H4MN5.js";
@@ -78,9 +91,10 @@ import {
78
91
  costAggregateForAmount,
79
92
  formatCostAggregate,
80
93
  readAccountabilitySummary
81
- } from "./chunk-4XUGQOHA.js";
94
+ } from "./chunk-K7T3E2SR.js";
82
95
  import "./chunk-HC4CLZ2Y.js";
83
96
  import "./chunk-HFSBBKSQ.js";
97
+ import "./chunk-3BPUFZDL.js";
84
98
  import {
85
99
  steerToolSurface
86
100
  } from "./chunk-WEH5XRJQ.js";
@@ -88,11 +102,12 @@ import {
88
102
  flushRawStdout,
89
103
  writeRawStdout
90
104
  } from "./chunk-2VTFPG5O.js";
91
- import "./chunk-CBCAPZAA.js";
105
+ import "./chunk-E25LMLRW.js";
92
106
  import {
93
107
  createContextDomainModule,
94
108
  loadProjectClioMd
95
- } from "./chunk-PIWWS5BL.js";
109
+ } from "./chunk-CYQKWTG3.js";
110
+ import "./chunk-LCGCVYZ4.js";
96
111
  import {
97
112
  renderCodewikiDigest
98
113
  } from "./chunk-5WIGXA4T.js";
@@ -102,12 +117,13 @@ import {
102
117
  createTaskMemoryTrace,
103
118
  describeTaskMemoryActivity,
104
119
  loadMemoryRecordsSync,
120
+ proposeMemoryPromotion,
105
121
  renderTaskMemoryHandoffSource,
106
122
  seedTaskMemoryFromNewestHandoff,
107
123
  taskMemoryBankSize,
108
124
  taskMemoryHandoffSeedOffer,
109
125
  taskMemoryTracePath
110
- } from "./chunk-V6RTAOC2.js";
126
+ } from "./chunk-6US73PDB.js";
111
127
  import {
112
128
  ACP_MAX_CHUNK_BYTES,
113
129
  ACP_MAX_RAW_RECORD_BYTES,
@@ -118,31 +134,37 @@ import {
118
134
  ACP_USAGE_META_KEY,
119
135
  AcpRequestError,
120
136
  AcpTimeoutError,
121
- SessionDomainModule,
122
137
  WORKER_OUTPUT_MAX_BYTES,
123
138
  acpErrorMessage,
124
- collectSessionEntries,
125
139
  createDispatchDomainModule,
126
140
  createRunReceiptQuality,
127
141
  createStdioServerTransport,
128
142
  durableAssistantTextFromEvent,
129
143
  evidenceDirectory,
130
- filterEntriesToActivePath,
131
144
  inspectEvidence,
132
145
  isGitRepository,
133
- ledgerUsageCalls,
134
146
  listEvidenceOverviews,
135
147
  openLedger,
136
- protectedArtifactEntryFromArtifact,
137
- protectedArtifactStateFromSessionEntries,
138
148
  protectedResidencyModels,
139
- readAuditRows,
140
149
  readGateDecisionArtifacts,
141
150
  readPendingGateDecisions,
142
151
  recoverCleanupReadyCompeteGroups,
143
152
  requireAgentSteps
144
- } from "./chunk-SRDMMSEP.js";
145
- import "./chunk-4BJ5BYCE.js";
153
+ } from "./chunk-QM3F2GKX.js";
154
+ import {
155
+ SessionDomainModule,
156
+ addResponseModelIdObservationCount,
157
+ addResponseModelIdObservationCounts,
158
+ attributedModelId,
159
+ collectSessionEntries,
160
+ emptyResponseModelIdObservationCounts,
161
+ ledgerUsageCalls,
162
+ protectedArtifactEntryFromArtifact,
163
+ protectedArtifactStateFromSessionEntries,
164
+ readAuditRows,
165
+ responseModelIdObservationCountsLabel,
166
+ responseModelIdObservationFromRecord
167
+ } from "./chunk-CJUB2JJ2.js";
146
168
  import {
147
169
  parseRigorOverride,
148
170
  resolveRigor
@@ -150,8 +172,8 @@ import {
150
172
  import {
151
173
  isReceiptIntegrity,
152
174
  verifyReceiptIntegrity
153
- } from "./chunk-AGYYIBLL.js";
154
- import "./chunk-OOJYHWRB.js";
175
+ } from "./chunk-OH3TOQTB.js";
176
+ import "./chunk-TSHXZTOQ.js";
155
177
  import {
156
178
  shellQuote
157
179
  } from "./chunk-TXOTCRLG.js";
@@ -159,14 +181,24 @@ import {
159
181
  emptyWorkspaceSnapshot,
160
182
  probeGitStatusAsync,
161
183
  probeWorkspaceAsync
162
- } from "./chunk-G4BMMOKF.js";
184
+ } from "./chunk-HVDIIIQW.js";
163
185
  import {
164
186
  listWikiPages,
165
187
  wikiCompleteness,
166
188
  wikiStaleness,
167
189
  wikiStalenessAsync
168
- } from "./chunk-X4RCMKVQ.js";
190
+ } from "./chunk-NDINPTJ4.js";
169
191
  import "./chunk-MVVUPGPW.js";
192
+ import {
193
+ buildRecallFields,
194
+ recallErrorMessage,
195
+ recallParentTurnId,
196
+ recallableRefListing,
197
+ resolveRecall
198
+ } from "./chunk-ZZMN5OM4.js";
199
+ import {
200
+ foldWorkingSet
201
+ } from "./chunk-TZSKNMZG.js";
170
202
  import {
171
203
  createResourcesDomainModule
172
204
  } from "./chunk-MFFY33HR.js";
@@ -182,6 +214,9 @@ import "./chunk-2SFS6XQE.js";
182
214
  import {
183
215
  ExtensionsDomainModule
184
216
  } from "./chunk-ZWLZP4ZT.js";
217
+ import {
218
+ filterEntriesToActivePath
219
+ } from "./chunk-W6GROXXM.js";
185
220
  import {
186
221
  formatBootTrace,
187
222
  traceBoot
@@ -190,13 +225,17 @@ import {
190
225
  ASK_USER_OTHER_LABEL,
191
226
  INTERACTIVE_LOOP_BLOCK_BUDGET,
192
227
  askAxis,
228
+ askUserExposure,
193
229
  assertRegisteredBuiltinTools,
194
230
  cancelledAskUserResult,
231
+ classifyDecisionPresentation,
195
232
  createEngineAgent,
196
233
  createLoopGuardRegistration,
197
234
  createProtectedArtifactsRegistration,
198
235
  createRegistry,
199
- describeCallTarget,
236
+ decisionActionClass,
237
+ decisionFactsForAnswer,
238
+ decisionFactsForPermission,
200
239
  diffWords,
201
240
  finalizeAskUserInterviewForHost,
202
241
  lazyTool,
@@ -207,14 +246,11 @@ import {
207
246
  registerCoreTools,
208
247
  resolveFdBinary,
209
248
  resolveSessionTools,
210
- sanitizeCallTargetText,
211
249
  sanitizeLockedSynthesisMessage
212
- } from "./chunk-LW6DSM3M.js";
250
+ } from "./chunk-ZRGEBJ4T.js";
213
251
  import {
214
- HEADLESS_PERMISSION_DENIED_REASON,
215
- combineBashOutput,
216
- runBashCommand
217
- } from "./chunk-J7CWMCQD.js";
252
+ HEADLESS_PERMISSION_DENIED_REASON
253
+ } from "./chunk-K7VKOLQQ.js";
218
254
  import {
219
255
  MIDDLEWARE_HOOK_TEXT_MAX_CHARS,
220
256
  MiddlewareDomainModule,
@@ -230,7 +266,7 @@ import {
230
266
  installUserHooks,
231
267
  openDetachedBatchViews,
232
268
  writeMiddlewareDiagnosticToStderr
233
- } from "./chunk-2DJ2KNFG.js";
269
+ } from "./chunk-ZXF4XRKW.js";
234
270
  import "./chunk-IR4CFBFN.js";
235
271
  import "./chunk-UOV2BYIW.js";
236
272
  import {
@@ -244,11 +280,11 @@ import {
244
280
  stringArg,
245
281
  timeoutMsArg,
246
282
  withResolvedDispatchPlan
247
- } from "./chunk-DUYJ5IO6.js";
283
+ } from "./chunk-EYPA3EGJ.js";
248
284
  import {
249
285
  TaskMemoryBank,
250
286
  createTaskMemoryTelemetrySink
251
- } from "./chunk-OAO4GE4M.js";
287
+ } from "./chunk-WHGPSPT5.js";
252
288
  import {
253
289
  DISPATCH_BRIEFING_MAX_BYTES,
254
290
  JUDGE_GATE_PROMPT,
@@ -257,53 +293,76 @@ import {
257
293
  approvedRouteCandidates,
258
294
  builtin,
259
295
  classifyResourceRead,
260
- createTaskBoardStore,
261
- foldSessionTaskHistory,
296
+ cloneRunToolBudgetEnvelope,
297
+ combineBashOutput,
298
+ describeCallTarget,
299
+ formatBudgetPolicy,
300
+ formatBudgetReasons,
301
+ formatBudgetRequest,
302
+ formatEffectiveBudget,
262
303
  isToolProfileName,
304
+ redactSecretString,
305
+ redactToolArgs,
306
+ runBashCommand,
263
307
  runKindSupportsLiveSteering,
264
- taskBoardCounts,
308
+ sanitizeCallTargetText,
265
309
  toolPresentationPolicy
266
- } from "./chunk-LWLEKMDQ.js";
310
+ } from "./chunk-XXQNGV4M.js";
311
+ import {
312
+ createTaskBoardStore,
313
+ foldSessionTaskHistory,
314
+ taskBoardCounts
315
+ } from "./chunk-43AOLP7E.js";
267
316
  import {
268
- DEFAULT_AUTONOMY_LEVEL,
269
317
  DEFAULT_RECENT_ENTRY_LIMIT,
270
318
  assessFinishContract,
271
319
  parseScoutResult,
272
320
  resultContractSourceId
273
- } from "./chunk-STBPMHSX.js";
321
+ } from "./chunk-EKY57CSP.js";
274
322
  import {
275
323
  agentRoleFactsResolver,
276
324
  defaultRoutingIntent,
277
325
  gateDeciderAgentId,
278
326
  requestExecutionRole,
279
327
  sameRouteIdentity
280
- } from "./chunk-GGXXDWE4.js";
328
+ } from "./chunk-22NAGB7X.js";
329
+ import "./chunk-AOCYTWAV.js";
281
330
  import {
282
331
  CLIO_PROJECT_DIR,
283
332
  toolMutationPaths
284
- } from "./chunk-5UUP6MWO.js";
333
+ } from "./chunk-MV3K5QF2.js";
285
334
  import "./chunk-CMZWFGD2.js";
286
335
  import "./chunk-I4HZDVNP.js";
287
336
  import "./chunk-ZI647VB5.js";
288
- import "./chunk-TZK7PACC.js";
337
+ import "./chunk-NILBFAPG.js";
338
+ import "./chunk-FYYLNIL5.js";
289
339
  import {
290
340
  VERIFICATION_SCRIPT_FAMILY_HINT
291
- } from "./chunk-7MNJORFF.js";
341
+ } from "./chunk-UL3WSD3F.js";
292
342
  import "./chunk-XN3L4EYL.js";
293
343
  import {
294
344
  escapeRegExp
295
345
  } from "./chunk-VG7TBQIY.js";
296
346
  import "./chunk-OQE5J4C6.js";
297
347
  import "./chunk-ECH6PKUQ.js";
298
- import "./chunk-OKGUZO2U.js";
348
+ import "./chunk-SPULKLCF.js";
299
349
  import {
300
350
  SKILL_SUGGESTION_PREFIX,
301
351
  isSkillActivation,
302
352
  skillActivationFromToolDetails
303
- } from "./chunk-T6YILFSB.js";
353
+ } from "./chunk-GOXNB3AO.js";
354
+ import {
355
+ DEFAULT_AUTONOMY_LEVEL
356
+ } from "./chunk-HWUFFB6L.js";
357
+ import {
358
+ sweepExpiredToolOffloads,
359
+ toolResultPresentationPolicy,
360
+ toolResultPresentationText
361
+ } from "./chunk-ODFEOB4F.js";
304
362
  import {
305
363
  formatSize
306
- } from "./chunk-VAKQQHWR.js";
364
+ } from "./chunk-OZNBF4L3.js";
365
+ import "./chunk-4BJ5BYCE.js";
307
366
  import {
308
367
  truncateUtf8
309
368
  } from "./chunk-6XLNIQDB.js";
@@ -316,14 +375,14 @@ import {
316
375
  import {
317
376
  incompleteInstallationAdvice
318
377
  } from "./chunk-65DEGPJ6.js";
319
- import "./chunk-POHLU5DW.js";
320
- import "./chunk-YHZX5GEU.js";
321
- import "./chunk-VJWL6YS5.js";
378
+ import "./chunk-M6L6IDJG.js";
379
+ import "./chunk-XAKHZX5N.js";
380
+ import "./chunk-UUVG37B4.js";
322
381
  import {
323
382
  readCodewiki,
324
383
  readCodewikiAsync
325
384
  } from "./chunk-LBMZMYH2.js";
326
- import "./chunk-EKMEHE4H.js";
385
+ import "./chunk-33YXPOE3.js";
327
386
  import {
328
387
  getVersionInfo
329
388
  } from "./chunk-XPLRXC72.js";
@@ -332,38 +391,24 @@ import {
332
391
  InteropDomainModule,
333
392
  delegationEntryForKind,
334
393
  renderProposalEntry
335
- } from "./chunk-2TLUCQVG.js";
336
- import "./chunk-BMEMKKIT.js";
394
+ } from "./chunk-RD5U66HV.js";
395
+ import "./chunk-KOHPCX4K.js";
337
396
  import {
338
397
  source_default
339
398
  } from "./chunk-5TSRNF4G.js";
340
399
  import {
341
400
  AGENT_ROLE_TOOLS_REQUIRED_REASON,
342
- AutoCompactionTrigger,
343
- DEFAULT_COMPACTION_THRESHOLD,
344
401
  ProvidersDomainModule,
345
402
  VALID_THINKING_LEVELS,
346
- appendContextSnapshot,
347
403
  applyModelCapabilityPatch,
348
- buildContextLedger,
349
- captureContextSnapshot,
350
- ceilChars,
351
- contentChars,
352
- contextUsageSnapshot,
353
- estimateAgentContextBreakdown,
354
- estimateAgentContextTokens,
355
- estimateAgentMessageTokens,
356
- estimateReasoningTextTokens,
357
- extractReasoningTokens,
358
404
  firstRuntimeResolutionError,
359
- getLatestContextSnapshot,
405
+ formatContextWindowSlots,
360
406
  getRuntimeRegistry,
361
407
  isDispatchEligibleRuntime,
362
408
  isOrchestratorEligibleRuntime,
363
409
  modelCandidatesForStatus,
364
410
  modelIdsForStatus,
365
411
  modelResidencyForStatus,
366
- reconcileSnapshot,
367
412
  refineRuntimeTargetWithModelHints,
368
413
  registerBuiltinRuntimes,
369
414
  resolveModelCapabilities,
@@ -372,16 +417,15 @@ import {
372
417
  resolveProviderReference,
373
418
  resolveReservedOutputTokens,
374
419
  resolveRuntimeTarget,
375
- runtimeResolutionWarnings,
420
+ runtimeResolutionWarningsBesideThinkingNotice,
376
421
  runtimeTargetSnapshot,
377
422
  setProtectedModelsProvider,
378
- shouldCompact,
379
- snapshotInputTokens,
380
423
  supportsAgentRoleTools,
381
424
  thinkingLevelChoiceLabel,
382
425
  thinkingLevelFromChoiceLabel,
383
426
  withRunOverrides
384
- } from "./chunk-LZSJBIVT.js";
427
+ } from "./chunk-DJVECN66.js";
428
+ import "./chunk-CFGTUFWB.js";
385
429
  import {
386
430
  bindAgentProfileInSettings,
387
431
  readSettings,
@@ -390,28 +434,19 @@ import {
390
434
  setFleetProfileInSettings,
391
435
  updateSettings,
392
436
  useTargetInSettings
393
- } from "./chunk-A3CYT5EX.js";
437
+ } from "./chunk-AD2SYQYC.js";
394
438
  import {
395
439
  DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS,
396
440
  DEFAULT_SETTINGS,
397
441
  THINKING_LEVELS
398
- } from "./chunk-ZDOOVTXZ.js";
442
+ } from "./chunk-4OC57DA6.js";
443
+ import "./chunk-LL4KHSZI.js";
399
444
  import {
400
445
  targetRequiresAuth
401
446
  } from "./chunk-4VP4KH3K.js";
402
- import {
403
- atomicWrite,
404
- cwdHash,
405
- openSession,
406
- readSessionTailTurns,
407
- sessionCurrentPath,
408
- sessionPaths
409
- } from "./chunk-M6SHUN7Q.js";
410
- import "./chunk-R346GLFC.js";
411
- import "./chunk-VPAYEGVX.js";
412
447
  import {
413
448
  setGitCommitAttributionEnabled
414
- } from "./chunk-COU2UHX6.js";
449
+ } from "./chunk-VEZEGCGW.js";
415
450
  import {
416
451
  MAX_TIMER_DELAY_MS,
417
452
  rawDurationMs
@@ -421,11 +456,37 @@ import "./chunk-3I7MS7N2.js";
421
456
  import {
422
457
  interopAgentKind
423
458
  } from "./chunk-IHXBNWMM.js";
459
+ import {
460
+ AutoCompactionTrigger,
461
+ DEFAULT_COMPACTION_THRESHOLD,
462
+ appendContextSnapshot,
463
+ buildContextLedger,
464
+ captureContextSnapshot,
465
+ ceilChars,
466
+ contentChars,
467
+ contextUsageSnapshot,
468
+ estimateAgentContextBreakdown,
469
+ estimateAgentContextTokens,
470
+ estimateReasoningTextTokens,
471
+ extractReasoningTokens,
472
+ getLatestContextSnapshot,
473
+ reconcileSnapshot,
474
+ shouldCompact,
475
+ snapshotInputTokens
476
+ } from "./chunk-GEYXPTRF.js";
424
477
  import "./chunk-SST6Z5JA.js";
478
+ import {
479
+ atomicWrite,
480
+ cwdHash,
481
+ openSession,
482
+ readSessionTailTurns,
483
+ sessionCurrentPath,
484
+ sessionPaths
485
+ } from "./chunk-FO5ZOVUY.js";
486
+ import "./chunk-R346GLFC.js";
425
487
  import {
426
488
  configureGuardrails
427
489
  } from "./chunk-4ZG3XFUR.js";
428
- import "./chunk-APJ265NV.js";
429
490
  import {
430
491
  StringEnum,
431
492
  cleanupEngineSessionResources,
@@ -433,6 +494,7 @@ import {
433
494
  isEngineRetryableAssistantError,
434
495
  stream
435
496
  } from "./chunk-FCSXB6T2.js";
497
+ import "./chunk-APJ265NV.js";
436
498
  import "./chunk-YXLYO42X.js";
437
499
  import {
438
500
  clioConfigDir,
@@ -445,10 +507,12 @@ import {
445
507
  safeResourceWrite
446
508
  } from "./chunk-WEPFGWHJ.js";
447
509
  import {
448
- Box,
449
510
  ClioEditor,
450
511
  CombinedAutocompleteProvider,
451
512
  Container,
513
+ DEFAULT_SELECT_THEME,
514
+ FILTER_HINT,
515
+ FocusBox,
452
516
  GLYPH,
453
517
  Input,
454
518
  Markdown,
@@ -462,6 +526,8 @@ import {
462
526
  abbreviateModelId,
463
527
  barSep,
464
528
  brandMark,
529
+ buildHint,
530
+ clioError,
465
531
  clioTheme,
466
532
  collapseHomePath,
467
533
  createAgentProgress,
@@ -471,11 +537,13 @@ import {
471
537
  detectTerminalKeySupport,
472
538
  dotSep,
473
539
  fgSequence,
540
+ fitHintEntries,
474
541
  fitUnits,
475
542
  formatCompactMs,
476
543
  formatContextPercent,
477
544
  formatInvalidKeybindingNotice,
478
545
  formatPlatformKeybindingNotice,
546
+ formatRuntimeResolutionDiagnostic,
479
547
  formatTargetLabel,
480
548
  frame,
481
549
  fuzzyFilter,
@@ -489,26 +557,30 @@ import {
489
557
  markdownTheme,
490
558
  matchesKey,
491
559
  padAnsi,
560
+ permissionHintEntries,
492
561
  rule,
562
+ runtimeResolutionDiagnosticLine,
493
563
  screenTitle,
494
564
  sectionTag,
495
565
  selectListTheme,
496
- settingsListTheme,
566
+ showClioOverlayFrame,
497
567
  spinnerFrame,
498
568
  stripTerminalSequences,
499
569
  truncateToWidth,
500
570
  validateKeybindings,
501
571
  visibleWidth,
502
572
  wrapTextWithAnsi
503
- } from "./chunk-ZWMF7253.js";
573
+ } from "./chunk-E2ER4LJF.js";
504
574
  import {
505
575
  getTerminationCoordinator
506
- } from "./chunk-TZTZS7QK.js";
576
+ } from "./chunk-XE2VEJHX.js";
577
+ import {
578
+ getSharedBus
579
+ } from "./chunk-ZGVHUX3M.js";
507
580
  import {
508
581
  BusChannels,
509
- getSharedBus,
510
582
  isRunAbortedPayload
511
- } from "./chunk-OQ33BKR3.js";
583
+ } from "./chunk-G7MUEIGA.js";
512
584
  import "./chunk-JVCV3ICN.js";
513
585
  import "./chunk-7CR24IG7.js";
514
586
  import {
@@ -606,6 +678,10 @@ function recordToolEnd(stats, event) {
606
678
  }
607
679
  }
608
680
  function addRunUsage(left, right) {
681
+ const responseModelIdObservationCounts = emptyResponseModelIdObservationCounts();
682
+ addResponseModelIdObservationCounts(responseModelIdObservationCounts, left.responseModelIdObservationCounts);
683
+ addResponseModelIdObservationCounts(responseModelIdObservationCounts, right.responseModelIdObservationCounts);
684
+ const last = right.hadUsage ? right : left;
609
685
  return {
610
686
  tokens: left.tokens + right.tokens,
611
687
  costUsd: left.costUsd + right.costUsd,
@@ -616,7 +692,10 @@ function addRunUsage(left, right) {
616
692
  reasoning: left.reasoning + right.reasoning,
617
693
  apiCalls: left.apiCalls + right.apiCalls,
618
694
  hadReasoning: left.hadReasoning || right.hadReasoning,
619
- hadUsage: true
695
+ hadUsage: left.hadUsage || right.hadUsage,
696
+ responseModelIdObservationCounts,
697
+ lastResponseModelIdObservation: last.lastResponseModelIdObservation,
698
+ lastDifferingResponseModelId: last.lastDifferingResponseModelId
620
699
  };
621
700
  }
622
701
  function sortedToolStats(stats) {
@@ -1509,190 +1588,6 @@ ${truncate(entry.output, TOOL_RESULT_MAX_CHARS)}`);
1509
1588
  return parts.join("\n\n");
1510
1589
  }
1511
1590
 
1512
- // src/domains/session/compaction/cut-point.ts
1513
- init_esm_shims();
1514
-
1515
- // src/domains/session/compaction/tokens.ts
1516
- init_esm_shims();
1517
- function estimateMessage(entry) {
1518
- return estimateAgentMessageTokens(entry);
1519
- }
1520
- function estimateBashExecution(entry) {
1521
- return ceilChars(entry.command.length + entry.output.length);
1522
- }
1523
- function estimateCustom(entry) {
1524
- if (entry.data === void 0) return 0;
1525
- return ceilChars(contentChars(entry.data));
1526
- }
1527
- function estimateSummary(entry) {
1528
- return ceilChars(entry.summary.length);
1529
- }
1530
- function estimateTokens(entry) {
1531
- switch (entry.kind) {
1532
- case "message":
1533
- return estimateMessage(entry);
1534
- case "bashExecution":
1535
- return estimateBashExecution(entry);
1536
- case "custom":
1537
- return estimateCustom(entry);
1538
- case "skillActivation":
1539
- return ceilChars(JSON.stringify(entry.activation).length);
1540
- case "branchSummary":
1541
- case "compactionSummary":
1542
- return estimateSummary(entry);
1543
- case "modelChange":
1544
- case "thinkingLevelChange":
1545
- case "fileEntry":
1546
- case "sessionInfo":
1547
- case "label":
1548
- case "protectedArtifact":
1549
- case "taskLedger":
1550
- case "decisionLedger":
1551
- case "workerRun":
1552
- return 0;
1553
- }
1554
- }
1555
- function usageTotalTokens(usage) {
1556
- return usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
1557
- }
1558
- function extractUsage(payload) {
1559
- if (!payload || typeof payload !== "object") return void 0;
1560
- const p = payload;
1561
- if (p.contextUsageInvalidated === true) return void 0;
1562
- if (p.stopReason === "aborted" || p.stopReason === "error") return void 0;
1563
- const u = p.usage;
1564
- if (!u || typeof u !== "object") return void 0;
1565
- const usage = u;
1566
- if (typeof usage.input !== "number" || typeof usage.output !== "number") return void 0;
1567
- return usage;
1568
- }
1569
- function getLastAssistantUsage(entries) {
1570
- for (let i = entries.length - 1; i >= 0; i--) {
1571
- const entry = entries[i];
1572
- if (entry?.kind !== "message" || entry?.role !== "assistant") continue;
1573
- const usage = extractUsage(entry.payload);
1574
- if (usage) return usage;
1575
- }
1576
- return void 0;
1577
- }
1578
- function findLastAssistantUsageIndex(entries) {
1579
- for (let i = entries.length - 1; i >= 0; i--) {
1580
- const entry = entries[i];
1581
- if (entry?.kind !== "message" || entry?.role !== "assistant") continue;
1582
- if (extractUsage(entry.payload)) return i;
1583
- }
1584
- return -1;
1585
- }
1586
- function calculateContextTokens(entries, lastUsage) {
1587
- const usage = lastUsage ?? getLastAssistantUsage(entries);
1588
- if (usage) {
1589
- const anchorTokens = usageTotalTokens(usage);
1590
- const anchorIndex = findLastAssistantUsageIndex(entries);
1591
- let trailing = 0;
1592
- for (let i = anchorIndex + 1; i < entries.length; i++) {
1593
- const entry = entries[i];
1594
- if (entry) trailing += estimateTokens(entry);
1595
- }
1596
- return anchorTokens + trailing;
1597
- }
1598
- let total = 0;
1599
- for (const entry of entries) total += estimateTokens(entry);
1600
- return total;
1601
- }
1602
-
1603
- // src/domains/session/compaction/cut-point.ts
1604
- function findTurnStartIndex(entries, entryIndex, startIndex = 0) {
1605
- for (let i = entryIndex; i >= startIndex; i--) {
1606
- const entry = entries[i];
1607
- if (!entry) continue;
1608
- if (entry.kind === "branchSummary") return i;
1609
- if (entry.kind === "bashExecution") return i;
1610
- if (entry.kind === "message" && entry.role === "user") return i;
1611
- }
1612
- return -1;
1613
- }
1614
- function isValidCutPoint(entry) {
1615
- switch (entry.kind) {
1616
- case "message":
1617
- return entry.role !== "tool_result";
1618
- case "bashExecution":
1619
- case "custom":
1620
- case "skillActivation":
1621
- case "branchSummary":
1622
- case "compactionSummary":
1623
- return true;
1624
- case "modelChange":
1625
- case "thinkingLevelChange":
1626
- case "fileEntry":
1627
- case "sessionInfo":
1628
- case "label":
1629
- case "protectedArtifact":
1630
- case "taskLedger":
1631
- case "decisionLedger":
1632
- case "workerRun":
1633
- return false;
1634
- }
1635
- }
1636
- function findValidCutPoints(entries, startIndex, endIndex) {
1637
- const cuts = [];
1638
- for (let i = startIndex; i < endIndex; i++) {
1639
- const entry = entries[i];
1640
- if (!entry) continue;
1641
- if (isValidCutPoint(entry)) cuts.push(i);
1642
- }
1643
- return cuts;
1644
- }
1645
- function findCutPoint(entries, keepRecentTokens, opts = {}) {
1646
- const startIndex = opts.startIndex ?? 0;
1647
- const endIndex = opts.endIndex ?? entries.length;
1648
- const cutPoints = findValidCutPoints(entries, startIndex, endIndex);
1649
- if (cutPoints.length === 0) {
1650
- return { firstKeptEntryIndex: startIndex, turnStartIndex: -1, isSplitTurn: false };
1651
- }
1652
- let accumulated = 0;
1653
- let cutIndex = -1;
1654
- for (let i = endIndex - 1; i >= startIndex; i--) {
1655
- const entry = entries[i];
1656
- if (!entry) continue;
1657
- accumulated += estimateTokens(entry);
1658
- if (accumulated >= keepRecentTokens) {
1659
- for (const candidate of cutPoints) {
1660
- if (candidate >= i) {
1661
- cutIndex = candidate;
1662
- break;
1663
- }
1664
- }
1665
- break;
1666
- }
1667
- }
1668
- if (cutIndex === -1) {
1669
- const lastTurnStart = findTurnStartIndex(entries, endIndex - 1, startIndex);
1670
- cutIndex = lastTurnStart !== -1 ? lastTurnStart : cutPoints[0] ?? startIndex;
1671
- }
1672
- while (cutIndex > startIndex) {
1673
- const prev = entries[cutIndex - 1];
1674
- if (!prev) break;
1675
- if (prev.kind === "compactionSummary") break;
1676
- if (prev.kind === "message" || prev.kind === "bashExecution" || prev.kind === "custom" || prev.kind === "skillActivation" || prev.kind === "branchSummary") {
1677
- break;
1678
- }
1679
- cutIndex--;
1680
- }
1681
- const cutEntry = entries[cutIndex];
1682
- const cutIsUserTurn = !!cutEntry && cutEntry.kind === "message" && cutEntry.role === "user";
1683
- const turnStartIndex = cutIsUserTurn ? -1 : findTurnStartIndex(entries, cutIndex, startIndex);
1684
- return {
1685
- firstKeptEntryIndex: cutIndex,
1686
- turnStartIndex,
1687
- isSplitTurn: !cutIsUserTurn && turnStartIndex !== -1
1688
- };
1689
- }
1690
-
1691
- // src/domains/session/compaction/defaults.ts
1692
- init_esm_shims();
1693
- var DEFAULT_RESERVE_TOKENS = 16384;
1694
- var DEFAULT_KEEP_RECENT_TOKENS = 2e4;
1695
-
1696
1591
  // src/domains/session/compaction/compact.ts
1697
1592
  var COMPACTION_SYSTEM_PROMPT = [
1698
1593
  "You are a context summarization assistant.",
@@ -1903,6 +1798,26 @@ ${modified.join("\n")}
1903
1798
 
1904
1799
  ${sections.join("\n\n")}` : "";
1905
1800
  }
1801
+ function formatRecallableRefs(entries, firstKeptEntryIndex) {
1802
+ const entryIndexes = /* @__PURE__ */ new Map();
1803
+ for (let index = 0; index < entries.length; index += 1) {
1804
+ const entry = entries[index];
1805
+ if (entry) entryIndexes.set(entry.turnId, index);
1806
+ }
1807
+ const view = foldWorkingSet(entries);
1808
+ const evictedBeforeCut = new Map(
1809
+ [...view.evicted].filter(([ref]) => (entryIndexes.get(ref) ?? Number.POSITIVE_INFINITY) < firstKeptEntryIndex)
1810
+ );
1811
+ const listing = recallableRefListing(entries, { ...view, evicted: evictedBeforeCut });
1812
+ if (listing.refs.length === 0) return "";
1813
+ const rows = [...listing.refs];
1814
+ if (listing.remaining > 0) rows.push(`and ${listing.remaining} more`);
1815
+ return `
1816
+
1817
+ <recallable-refs>
1818
+ ${rows.join("\n")}
1819
+ </recallable-refs>`;
1820
+ }
1906
1821
  function numberOrZero(value) {
1907
1822
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1908
1823
  }
@@ -2016,7 +1931,10 @@ async function compact(input) {
2016
1931
  ${prefixSummary.text}`);
2017
1932
  }
2018
1933
  }
2019
- const summary = `${summaryParts.join("\n\n---\n\n").trim()}${formatFileOperations(fileOps)}`.trim();
1934
+ const summary = `${summaryParts.join("\n\n---\n\n").trim()}${formatFileOperations(fileOps)}${formatRecallableRefs(
1935
+ input.entries,
1936
+ cut.firstKeptEntryIndex
1937
+ )}`.trim();
2020
1938
  return {
2021
1939
  summary,
2022
1940
  firstKeptEntryIndex: cut.firstKeptEntryIndex,
@@ -2077,6 +1995,7 @@ function finalizedInterviewEntryFields(policy) {
2077
1995
  roundCount: policy.rounds.length,
2078
1996
  ...policy.summary ? { summary: policy.summary } : {},
2079
1997
  ...policy.transcriptPath ? { transcriptPath: policy.transcriptPath } : {},
1998
+ exposure: policy.exposure ?? "local",
2080
1999
  decisions: [...decisionsByKey.values()]
2081
2000
  };
2082
2001
  }
@@ -2154,6 +2073,7 @@ function createDecisionBoardStore(deps = {}) {
2154
2073
  roundCount: interview.roundCount,
2155
2074
  ...interview.summary ? { summary: interview.summary } : {},
2156
2075
  ...interview.transcriptPath ? { transcriptPath: interview.transcriptPath } : {},
2076
+ ...interview.exposure ? { exposure: interview.exposure } : {},
2157
2077
  decisions: interview.decisions.map((decision) => {
2158
2078
  if (decision.key !== selected.key) return { ...decision };
2159
2079
  const next = { ...decision, status: "superseded", revisedAt };
@@ -2684,8 +2604,8 @@ function boundRawRecord(value) {
2684
2604
  return { truncated: true, bytes: originalRawRecordBytes(value) ?? boundedBytes };
2685
2605
  }
2686
2606
  var PATH_BEARING_TOOLS = /* @__PURE__ */ new Set(["read", "write", "edit", "ls", "grep", "find"]);
2687
- function toolLocations(toolName2, args, cwd) {
2688
- if (toolName2 === void 0 || !PATH_BEARING_TOOLS.has(toolName2)) return null;
2607
+ function toolLocations(toolName, args, cwd) {
2608
+ if (toolName === void 0 || !PATH_BEARING_TOOLS.has(toolName)) return null;
2689
2609
  if (!isRecord3(args)) return null;
2690
2610
  const path = args.path;
2691
2611
  if (typeof path !== "string" || path.length === 0) return null;
@@ -2790,6 +2710,8 @@ function applyStopReason(active, message) {
2790
2710
  function outputText(value) {
2791
2711
  if (typeof value === "string") return value;
2792
2712
  if (!isRecord3(value)) return "";
2713
+ const presentationText = toolResultPresentationText(value);
2714
+ if (presentationText !== null) return presentationText;
2793
2715
  if (Array.isArray(value.content)) return contentText(value.content);
2794
2716
  if (typeof value.output === "string") return value.output;
2795
2717
  if (typeof value.text === "string") return value.text;
@@ -2919,11 +2841,11 @@ function handleChatEvent(rawEvent, transport, sessionId, active, cwd, diagnostic
2919
2841
  onToolCallLimitReached();
2920
2842
  return;
2921
2843
  }
2922
- const toolName2 = eventString(event, "toolName");
2844
+ const toolName = eventString(event, "toolName");
2923
2845
  const toolCallId = startToolCallId(active, eventString(event, "toolCallId"));
2924
2846
  active.openToolCalls.add(toolCallId);
2925
2847
  active.lastEmittedToolCallId = toolCallId;
2926
- const locations = toolLocations(toolName2, event.args, cwd);
2848
+ const locations = toolLocations(toolName, event.args, cwd);
2927
2849
  const snapshot = {
2928
2850
  rawInput: boundRawRecord(event.args),
2929
2851
  ...locations !== null ? { locations } : {}
@@ -2932,8 +2854,8 @@ function handleChatEvent(rawEvent, transport, sessionId, active, cwd, diagnostic
2932
2854
  sendUpdate(transport, sessionId, active, {
2933
2855
  sessionUpdate: "tool_call",
2934
2856
  toolCallId,
2935
- title: boundString(toolName2 ?? "tool", ACP_MAX_TOOL_TITLE_BYTES),
2936
- kind: toolKind(toolName2),
2857
+ title: boundString(toolName ?? "tool", ACP_MAX_TOOL_TITLE_BYTES),
2858
+ kind: toolKind(toolName),
2937
2859
  status: "in_progress",
2938
2860
  rawInput: snapshot.rawInput,
2939
2861
  ...snapshot.locations !== void 0 ? { locations: snapshot.locations } : {}
@@ -2941,7 +2863,7 @@ function handleChatEvent(rawEvent, transport, sessionId, active, cwd, diagnostic
2941
2863
  return;
2942
2864
  }
2943
2865
  if (event.type === "tool_execution_end") {
2944
- const toolName2 = eventString(event, "toolName");
2866
+ const toolName = eventString(event, "toolName");
2945
2867
  const toolCallId = endToolCallId(active, eventString(event, "toolCallId"));
2946
2868
  if (toolCallId === null) {
2947
2869
  diagnostics?.("dropped tool_execution_end with no tool call to update");
@@ -2957,8 +2879,8 @@ function handleChatEvent(rawEvent, transport, sessionId, active, cwd, diagnostic
2957
2879
  sendUpdate(transport, sessionId, active, {
2958
2880
  sessionUpdate: "tool_call_update",
2959
2881
  toolCallId,
2960
- title: boundString(toolName2 ?? "tool", ACP_MAX_TOOL_TITLE_BYTES),
2961
- kind: toolKind(toolName2),
2882
+ title: boundString(toolName ?? "tool", ACP_MAX_TOOL_TITLE_BYTES),
2883
+ kind: toolKind(toolName),
2962
2884
  status: toolStatus(event),
2963
2885
  ...output.length > 0 ? { content: toolCallContent(output) } : {},
2964
2886
  rawOutput: boundRawRecord({ result: event.result, isError: event.isError === true })
@@ -3280,6 +3202,17 @@ function installPermissionBridge(input) {
3280
3202
  const queuedRequestDetails = /* @__PURE__ */ new Map();
3281
3203
  const unregister = input.toolRegistry.onPermissionRequired((call, decision, meta) => {
3282
3204
  if (queuedRequestIds.has(meta.requestId)) return;
3205
+ const presentation = classifyDecisionPresentation(
3206
+ decisionFactsForPermission({
3207
+ tool: call.tool,
3208
+ actionClass: decision.classification.actionClass,
3209
+ axis: meta.axis.startsWith("net:") ? { kind: "safety-net", ruleId: meta.axis.slice("net:".length) || "unknown" } : { kind: "autonomy", level: meta.axis.slice("autonomy:".length) || DEFAULT_AUTONOMY_LEVEL },
3210
+ origin: { kind: "main" },
3211
+ ...call.tool === ToolNames.AskUser ? { exposure: askUserExposure(call.args) } : {}
3212
+ })
3213
+ );
3214
+ const approveAction = presentation.requiredActions.find((action) => action.id === "approve-once");
3215
+ const denyAction = presentation.requiredActions.find((action) => action.id === "deny");
3283
3216
  queuedRequestIds.add(meta.requestId);
3284
3217
  queuedRequestDetails.set(meta.requestId, {
3285
3218
  tool: call.tool,
@@ -3369,8 +3302,16 @@ function installPermissionBridge(input) {
3369
3302
  ...snapshot.locations !== void 0 ? { locations: snapshot.locations } : {}
3370
3303
  },
3371
3304
  options: [
3372
- { optionId: "allow-once", name: "Allow once", kind: "allow_once" },
3373
- { optionId: "reject-once", name: "Reject", kind: "reject_once" }
3305
+ {
3306
+ optionId: "allow-once",
3307
+ name: approveAction?.label ?? "Approve once",
3308
+ kind: "allow_once"
3309
+ },
3310
+ {
3311
+ optionId: "reject-once",
3312
+ name: denyAction?.label ?? "Deny this request",
3313
+ kind: "reject_once"
3314
+ }
3374
3315
  ]
3375
3316
  },
3376
3317
  input.permissionTimeoutMs
@@ -4237,17 +4178,17 @@ function resultText(result) {
4237
4178
  function extractToolResultPayload(payload) {
4238
4179
  const obj = isRecord4(payload) ? payload : { result: payload };
4239
4180
  const result = obj.result ?? obj.output ?? obj.out ?? obj.content ?? payload;
4240
- const toolName2 = typeof obj.toolName === "string" && obj.toolName || typeof obj.name === "string" && obj.name || typeof obj.tool === "string" && obj.tool || "tool";
4241
- return { obj, result, toolName: toolName2 };
4181
+ const toolName = typeof obj.toolName === "string" && obj.toolName || typeof obj.name === "string" && obj.name || typeof obj.tool === "string" && obj.tool || "tool";
4182
+ return { obj, result, toolName };
4242
4183
  }
4243
4184
  function lineCount(text) {
4244
4185
  if (text.length === 0) return 0;
4245
4186
  return text.split(/\r\n|\r|\n/).length;
4246
4187
  }
4247
- function observationMarker(toolName2, text) {
4188
+ function observationMarker(toolName, text) {
4248
4189
  const preview = text.trim().replace(/\s+/g, " ").slice(0, 160);
4249
4190
  const suffix = preview.length > 0 ? ` Preview: ${preview}` : "";
4250
- return `[Observation masked: ${toolName2} output was ${lineCount(text)} lines, ${text.length} chars - contents masked to save context. Re-run the tool for current content.]${suffix}`;
4191
+ return `[Observation masked: ${toolName} output was ${lineCount(text)} lines, ${text.length} chars - contents masked to save context. Re-run the tool for current content.]${suffix}`;
4251
4192
  }
4252
4193
  function maskedToolResult(original, marker) {
4253
4194
  const details = isRecord4(original) && isRecord4(original.details) ? { ...original.details } : {};
@@ -4270,9 +4211,9 @@ function alreadyCompacted(payload) {
4270
4211
  }
4271
4212
  function maskObservation(entry) {
4272
4213
  const next = cloneEntry(entry);
4273
- const { obj, result, toolName: toolName2 } = extractToolResultPayload(next.payload);
4214
+ const { obj, result, toolName } = extractToolResultPayload(next.payload);
4274
4215
  const text = resultText(result);
4275
- const marker = observationMarker(toolName2, text);
4216
+ const marker = observationMarker(toolName, text);
4276
4217
  next.payload = {
4277
4218
  ...obj,
4278
4219
  result: maskedToolResult(result, marker),
@@ -4469,8 +4410,12 @@ function readPromptCompileManifest(meta, stateDir) {
4469
4410
  }
4470
4411
  }
4471
4412
 
4413
+ // src/interactive/model-session-replay.ts
4414
+ init_esm_shims();
4415
+
4472
4416
  // src/interactive/chat-renderer.ts
4473
4417
  init_esm_shims();
4418
+ import { existsSync as existsSync6 } from "node:fs";
4474
4419
 
4475
4420
  // src/engine/messages.ts
4476
4421
  init_esm_shims();
@@ -4799,39 +4744,6 @@ function truncate2(value, limit) {
4799
4744
  function isPlainObject(value) {
4800
4745
  return value !== null && typeof value === "object" && !Array.isArray(value);
4801
4746
  }
4802
- var SECRET_KEY_RE = /(?:api[-_]?key|access[-_]?key|auth(?:orization)?|credential|password|private[-_]?key|secret|token)/iu;
4803
- var ENV_KEY_RE = /^env(?:ironment)?$/iu;
4804
- var SECRET_STRING_RE = /(https?:\/\/[^\s/@]+:)[^\s/@]+@/giu;
4805
- var SECRET_URL_PARAM_RE = /([?&](?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[^=\s]*=)[^&#\s]+/giu;
4806
- var SECRET_ASSIGNMENT_RE = /(?<![A-Z0-9_])((?:[A-Z0-9_]*(?:ACCESS|API|AUTH|CREDENTIAL|KEY|PASS(?:WORD)?|PRIVATE|SECRET|TOKEN)[A-Z0-9_]*)=)[^\s;&|]+/giu;
4807
- var SECRET_FLAG_RE = /(--?(?:[a-z0-9-]*(?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[a-z0-9-]*))(=|\s+)([^\s;&|]+)/giu;
4808
- function redactSecretString(value) {
4809
- return value.replace(SECRET_STRING_RE, "$1[redacted]@").replace(SECRET_URL_PARAM_RE, "$1[redacted]").replace(SECRET_ASSIGNMENT_RE, "$1[redacted]").replace(SECRET_FLAG_RE, "$1$2[redacted]");
4810
- }
4811
- function redactEnvironmentValue(value, depth) {
4812
- if (depth > 8) return "[redacted nested values]";
4813
- if (typeof value === "string") return "[redacted]";
4814
- if (Array.isArray(value)) return value.map((item) => redactEnvironmentValue(item, depth + 1));
4815
- if (!isPlainObject(value)) return value;
4816
- const out = {};
4817
- for (const [childKey, childValue] of Object.entries(value)) {
4818
- out[childKey] = redactEnvironmentValue(childValue, depth + 1);
4819
- }
4820
- return out;
4821
- }
4822
- function redactToolArgs(value, key = "", depth = 0) {
4823
- if (SECRET_KEY_RE.test(key)) return "[redacted]";
4824
- if (ENV_KEY_RE.test(key)) return redactEnvironmentValue(value, depth);
4825
- if (typeof value === "string") return redactSecretString(value);
4826
- if (depth > 8) return "[redacted nested values]";
4827
- if (Array.isArray(value)) return value.map((item) => redactToolArgs(item, key, depth + 1));
4828
- if (!isPlainObject(value)) return value;
4829
- const out = {};
4830
- for (const [childKey, childValue] of Object.entries(value)) {
4831
- out[childKey] = redactToolArgs(childValue, childKey, depth + 1);
4832
- }
4833
- return out;
4834
- }
4835
4747
  function readStringField(args, key) {
4836
4748
  if (!isPlainObject(args)) return null;
4837
4749
  const value = args[key];
@@ -4865,8 +4777,8 @@ function stripShellWrapperForDisplay(command) {
4865
4777
  if (!match?.[1]) return command;
4866
4778
  return unquoteShellScript(match[1]);
4867
4779
  }
4868
- function displayArg(toolName2, value) {
4869
- return toolName2 === "bash" ? stripShellWrapperForDisplay(value) : value;
4780
+ function displayArg(toolName, value) {
4781
+ return toolName === "bash" ? stripShellWrapperForDisplay(value) : value;
4870
4782
  }
4871
4783
  function optionalCompactMs(durationMs) {
4872
4784
  if (durationMs === void 0 || !Number.isFinite(durationMs) || durationMs < 0) return null;
@@ -4896,8 +4808,8 @@ var PRIMARY_ARG_FIELD = {
4896
4808
  steer: "run_id",
4897
4809
  tasks: "action"
4898
4810
  };
4899
- function capturedPrimaryArg(toolName2, args) {
4900
- const field2 = PRIMARY_ARG_FIELD[toolName2];
4811
+ function capturedPrimaryArg(toolName, args) {
4812
+ const field2 = PRIMARY_ARG_FIELD[toolName];
4901
4813
  if (field2 === void 0) return null;
4902
4814
  return readStringField(args, field2);
4903
4815
  }
@@ -4909,12 +4821,12 @@ function summarizeWebFetchArgs(args) {
4909
4821
  }
4910
4822
  return truncate2(jsonStringifySafe(Object.keys(compact2).length > 0 ? compact2 : args), WEB_FETCH_ARG_PREVIEW_LIMIT);
4911
4823
  }
4912
- function summarizeArgs(toolName2, args) {
4824
+ function summarizeArgs(toolName, args) {
4913
4825
  if (isEmptyArgs(args)) return "";
4914
4826
  const safeArgs = redactToolArgs(args);
4915
- if (toolName2 === "web_fetch") return summarizeWebFetchArgs(safeArgs);
4916
- const primary = capturedPrimaryArg(toolName2, args);
4917
- if (primary !== null) return truncate2(displayArg(toolName2, primary), ARG_PREVIEW_LIMIT);
4827
+ if (toolName === "web_fetch") return summarizeWebFetchArgs(safeArgs);
4828
+ const primary = capturedPrimaryArg(toolName, args);
4829
+ if (primary !== null) return truncate2(displayArg(toolName, primary), ARG_PREVIEW_LIMIT);
4918
4830
  return truncate2(jsonStringifySafe(safeArgs), ARG_PREVIEW_LIMIT);
4919
4831
  }
4920
4832
  function detailsOf(result) {
@@ -4995,6 +4907,9 @@ function offloadPathOf(finished) {
4995
4907
  const resultSize = detailsOf(finished.result)?.resultSize;
4996
4908
  return isPlainObject(resultSize) ? stringField(resultSize, "offloadPath") : null;
4997
4909
  }
4910
+ function offloadFileMissing(finished) {
4911
+ return finished.resultSummary?.offloadFileMissing === true;
4912
+ }
4998
4913
  function shownBytesOf(finished) {
4999
4914
  const observation = observationOf(finished);
5000
4915
  const fromObservation = numberField(observation, "shownBytes");
@@ -5056,15 +4971,17 @@ function ledgerTail(finished) {
5056
4971
  if (details?.outputCapped === true) parts.push("output capped");
5057
4972
  }
5058
4973
  if (finished.excludeFromContext === true) parts.push("excluded from context");
4974
+ if (finished.evictedReason !== void 0) parts.push("evicted", finished.evictedReason);
5059
4975
  const offloadPath = executed ? offloadPathOf(finished) : null;
5060
4976
  return {
5061
4977
  facts: parts.length > 0 ? dim(` \xB7 ${parts.join(" \xB7 ")}`) : "",
5062
- offload: offloadPath !== null ? dim(` \xB7 full: ${offloadPath}`) : ""
4978
+ offload: offloadPath === null ? "" : offloadFileMissing(finished) ? dim(" \xB7 full: gone after the 14-day retention sweep") : dim(` \xB7 full: ${offloadPath}`)
5063
4979
  };
5064
4980
  }
5065
4981
  var FAILURE_EXCERPT_LIMIT = 80;
5066
4982
  function failureExcerpt(finished, width) {
5067
- if (!finished.isError || !toolPresentationPolicy(finished.toolName, finished.args).failureExcerpt) return "";
4983
+ const presentation = toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
4984
+ if (!finished.isError || !presentation.failureExcerpt) return "";
5068
4985
  if (isNonExecutedOutcome(finished.outcome)) return "";
5069
4986
  const text = unwrapResultEnvelope(finished.result);
5070
4987
  if (typeof text !== "string") return "";
@@ -5111,8 +5028,8 @@ function statusGlyph(status, meta = {}) {
5111
5028
  const reasonSuffix = reason ? dim(` \xB7 ${truncate2(reason, BLOCK_REASON_LIMIT)}`) : "";
5112
5029
  return ` ${red(STATUS_ERROR_GLYPH)}${outcomeSuffix}${reasonSuffix}${exitSuffix}${durationSuffix}`;
5113
5030
  }
5114
- function headerLine(toolName2, args, status, meta = {}) {
5115
- const body = styleSublineBody(buildSublineBody(toolName2, args, status, void 0, meta.outcome));
5031
+ function headerLine(toolName, args, status, meta = {}) {
5032
+ const body = styleSublineBody(buildSublineBody(toolName, args, status, void 0, meta.outcome));
5116
5033
  const head = `${dim(HEADER_PREFIX_PLAIN)}${body}`;
5117
5034
  return `${head}${statusGlyph(status, meta)}`;
5118
5035
  }
@@ -5124,8 +5041,8 @@ function styleSublineBody(body) {
5124
5041
  if (match?.groups?.lead === void 0 || match.groups.rest === void 0) return body;
5125
5042
  return sublineLead(match.groups.lead, match.groups.rest);
5126
5043
  }
5127
- function buildGenericToolBody(toolName2, args) {
5128
- const summary = summarizeArgs(toolName2, args);
5044
+ function buildGenericToolBody(toolName, args) {
5045
+ const summary = summarizeArgs(toolName, args);
5129
5046
  return summary.length > 0 ? `tool action ${summary}` : "tool action";
5130
5047
  }
5131
5048
  function buildFieldSublineBody(args, key, lead, options = {}) {
@@ -5217,25 +5134,25 @@ function webFetchMeta(result) {
5217
5134
  function isNonExecutedOutcome(outcome) {
5218
5135
  return outcome === "blocked";
5219
5136
  }
5220
- function buildSublineBody(toolName2, args, status, result, outcome) {
5137
+ function buildSublineBody(toolName, args, status, result, outcome) {
5221
5138
  if (isNonExecutedOutcome(outcome)) {
5222
- if (toolName2 === "bash") {
5223
- return buildFieldSublineBody(args, "command", "command ", { wrapInBackticks: true }) ?? buildGenericToolBody(toolName2, args);
5139
+ if (toolName === "bash") {
5140
+ return buildFieldSublineBody(args, "command", "command ", { wrapInBackticks: true }) ?? buildGenericToolBody(toolName, args);
5224
5141
  }
5225
- return SUBLINE_BODY_BUILDERS[toolName2]?.(args) ?? buildGenericToolBody(toolName2, args);
5142
+ return SUBLINE_BODY_BUILDERS[toolName]?.(args) ?? buildGenericToolBody(toolName, args);
5226
5143
  }
5227
- if (toolName2 === "web_fetch") {
5144
+ if (toolName === "web_fetch") {
5228
5145
  const meta = status === void 0 ? null : webFetchMeta(result);
5229
- const body2 = SUBLINE_BODY_BUILDERS.web_fetch?.(args) ?? buildGenericToolBody(toolName2, args);
5146
+ const body2 = SUBLINE_BODY_BUILDERS.web_fetch?.(args) ?? buildGenericToolBody(toolName, args);
5230
5147
  return `${body2}${meta ? dim(` \xB7 ${meta}`) : ""}`;
5231
5148
  }
5232
- if (toolName2 === "bash") {
5149
+ if (toolName === "bash") {
5233
5150
  const lead = status === "ok" || status === "error" ? "ran " : "running ";
5234
- return buildFieldSublineBody(args, "command", lead, { wrapInBackticks: true }) ?? buildGenericToolBody(toolName2, args);
5151
+ return buildFieldSublineBody(args, "command", lead, { wrapInBackticks: true }) ?? buildGenericToolBody(toolName, args);
5235
5152
  }
5236
- const body = SUBLINE_BODY_BUILDERS[toolName2]?.(args);
5153
+ const body = SUBLINE_BODY_BUILDERS[toolName]?.(args);
5237
5154
  if (body !== null && body !== void 0) return body;
5238
- return buildGenericToolBody(toolName2, args);
5155
+ return buildGenericToolBody(toolName, args);
5239
5156
  }
5240
5157
  function sublineParts(call, status, meta) {
5241
5158
  const finished = "result" in call ? call : null;
@@ -5286,7 +5203,7 @@ function scalarArgValue(value) {
5286
5203
  if (value === null) return dim("null");
5287
5204
  return null;
5288
5205
  }
5289
- function renderArgsBody(toolName2, args, width, isError) {
5206
+ function renderArgsBody(toolName, args, width, isError) {
5290
5207
  if (isEmptyArgs(args)) return [];
5291
5208
  const safeArgs = redactToolArgs(args);
5292
5209
  if (!isPlainObject(safeArgs)) {
@@ -5294,7 +5211,7 @@ function renderArgsBody(toolName2, args, width, isError) {
5294
5211
  const lines = tryRenderJson(safeArgs, bodyWidth2, { lineLimit: ARGS_BODY_LINE_LIMIT });
5295
5212
  return lines?.flatMap((line) => indentAndWrap(line, width, isError)) ?? [];
5296
5213
  }
5297
- const primary = PRIMARY_ARG_FIELD[toolName2];
5214
+ const primary = PRIMARY_ARG_FIELD[toolName];
5298
5215
  const entries = Object.entries(safeArgs).filter(([key]) => key !== primary);
5299
5216
  if (entries.length === 0) return [];
5300
5217
  const out = [];
@@ -5321,6 +5238,8 @@ function renderArgsBody(toolName2, args, width, isError) {
5321
5238
  }
5322
5239
  function unwrapResultEnvelope(result) {
5323
5240
  if (typeof result === "string" || result === null || result === void 0) return result;
5241
+ const presentationText = toolResultPresentationText(result);
5242
+ if (presentationText !== null) return presentationText;
5324
5243
  const blocks = Array.isArray(result) ? result : isPlainObject(result) && Array.isArray(result.content) ? result.content : null;
5325
5244
  if (blocks === null) return result;
5326
5245
  const parts = [];
@@ -5453,6 +5372,7 @@ function outputFacts(finished) {
5453
5372
  }
5454
5373
  if (isPlainObject(finished.result) && finished.result.terminate === true) parts.push("terminal result");
5455
5374
  if (finished.excludeFromContext === true) parts.push("excluded from context");
5375
+ if (finished.evictedReason !== void 0) parts.push("evicted", finished.evictedReason);
5456
5376
  return parts;
5457
5377
  }
5458
5378
  function renderOutputMeta(finished, width, isError, label = "output") {
@@ -5463,7 +5383,10 @@ function renderOutputMeta(finished, width, isError, label = "output") {
5463
5383
  function renderOutputFooter(finished, width, isError) {
5464
5384
  const out = [];
5465
5385
  const offloadPath = isNonExecutedOutcome(finished.outcome) ? null : offloadPathOf(finished);
5466
- if (offloadPath !== null) out.push(...indentAndWrap(`${yellow("full output")} ${offloadPath}`, width, isError));
5386
+ if (offloadPath !== null) {
5387
+ const pointer = offloadFileMissing(finished) ? "gone after the 14-day retention sweep" : offloadPath;
5388
+ out.push(...indentAndWrap(`${yellow("full output")} ${pointer}`, width, isError));
5389
+ }
5467
5390
  const hint = stringField(resultSizeOf(finished), "followUpHint") ?? stringField(finished.resultSummary ?? null, "followUpHint");
5468
5391
  if (hint !== null) out.push(...indentAndWrap(`${dim("next")} ${hint}`, width, isError));
5469
5392
  return out;
@@ -5511,7 +5434,8 @@ function sublineStatus(call) {
5511
5434
  function foldedDiffRows(finished, width, opts) {
5512
5435
  if (opts.foldedExtras === "none") return [];
5513
5436
  if (finished.isError || isNonExecutedOutcome(finished.outcome)) return [];
5514
- if (!toolPresentationPolicy(finished.toolName, finished.args).showDiffWhenFolded) return [];
5437
+ const presentation = toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
5438
+ if (!presentation.showDiffWhenFolded) return [];
5515
5439
  const diff = resultDiff(finished.result);
5516
5440
  if (diff === null) return [];
5517
5441
  return truncateRowsMiddle(
@@ -6288,6 +6212,10 @@ function renderContextMeterGrid(ledger, cols, rows, theme6 = clioTheme()) {
6288
6212
  }
6289
6213
  return lines;
6290
6214
  }
6215
+ function renderEvictedTokensLine(evictedTokens2, theme6 = clioTheme()) {
6216
+ const tokens = Math.round(Math.max(0, evictedTokens2)).toLocaleString("en-US");
6217
+ return `${theme6.fg("dim", GLYPH.contextReserve)} ${theme6.fg("muted", "evicted (outside window)")} ${theme6.fg("dim", `${tokens} tokens`)}`;
6218
+ }
6291
6219
  function contextCategorySwatch(category, theme6 = clioTheme()) {
6292
6220
  return theme6.fg(CONTEXT_CATEGORY_TOKEN[category], contextCategoryGlyph(category));
6293
6221
  }
@@ -6499,6 +6427,267 @@ function buildSegmentedContextBar(theme6, barWidth, contextWindow, breakdown) {
6499
6427
  return `${systemPart}${toolPart}${conversationPart}${freePart}${contextPercentLabel(percent)}`;
6500
6428
  }
6501
6429
 
6430
+ // src/interactive/worker-progress.ts
6431
+ init_esm_shims();
6432
+ var WORKER_LIVE_TAIL_LINES = 40;
6433
+ var WORKER_LIVE_TAIL_MAX_BYTES = 4096;
6434
+ var WORKER_TOOL_NAME_LIMIT = 8;
6435
+ var WORKER_ACTION_TRAIL_LIMIT = 4;
6436
+ var WORKER_PROGRESS_WINDOW_MS = 250;
6437
+ var WORKER_PROGRESS_WINDOW_BYTES = 16384;
6438
+ var TAIL_TRUNCATION_MARKER = "";
6439
+ function isRecord7(value) {
6440
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6441
+ }
6442
+ function nonEmptyString(value) {
6443
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
6444
+ }
6445
+ function workerTextDelta(event) {
6446
+ if (!isRecord7(event)) return "";
6447
+ if (event.type === "message_update") {
6448
+ const assistantEvent = isRecord7(event.assistantMessageEvent) ? event.assistantMessageEvent : null;
6449
+ if (assistantEvent?.type !== "text_delta") return "";
6450
+ return typeof assistantEvent.delta === "string" ? assistantEvent.delta : "";
6451
+ }
6452
+ if (event.type !== "text_delta") return "";
6453
+ if (typeof event.delta === "string") return event.delta;
6454
+ return typeof event.text === "string" ? event.text : "";
6455
+ }
6456
+ function isThinkingEvent(event) {
6457
+ if (event.type === "thinking_delta") return true;
6458
+ if (event.type !== "message_update") return false;
6459
+ const assistantEvent = isRecord7(event.assistantMessageEvent) ? event.assistantMessageEvent : null;
6460
+ return assistantEvent?.type === "thinking_delta";
6461
+ }
6462
+ function toolEventAction(event) {
6463
+ if (event.type !== "clio_tool_start" && event.type !== "clio_tool_finish") return null;
6464
+ const payload = isRecord7(event.payload) ? event.payload : null;
6465
+ const tool = nonEmptyString(payload?.tool);
6466
+ if (tool === void 0) return null;
6467
+ const toolCallId = nonEmptyString(payload?.toolCallId);
6468
+ const raw = isRecord7(payload?.action) ? payload.action : void 0;
6469
+ const verb = nonEmptyString(raw?.verb);
6470
+ if (verb === void 0) return { tool, ...toolCallId !== void 0 ? { toolCallId } : {} };
6471
+ const object = nonEmptyString(raw?.object);
6472
+ return {
6473
+ tool,
6474
+ ...toolCallId !== void 0 ? { toolCallId } : {},
6475
+ descriptor: {
6476
+ verb,
6477
+ ...object !== void 0 ? { object } : {},
6478
+ ...raw?.truncated === true ? { truncated: true } : {}
6479
+ }
6480
+ };
6481
+ }
6482
+ function lineCount2(text) {
6483
+ if (text.length === 0) return 0;
6484
+ let lines = 1;
6485
+ for (let index = 0; index < text.length; index += 1) {
6486
+ if (text[index] === "\n") lines += 1;
6487
+ }
6488
+ return lines;
6489
+ }
6490
+ function keepTailBytes(text, maxBytes) {
6491
+ const buffer = Buffer.from(text, "utf8");
6492
+ if (buffer.byteLength <= maxBytes) return text;
6493
+ let cut = buffer.byteLength - maxBytes;
6494
+ while (cut < buffer.byteLength) {
6495
+ const byte = buffer[cut];
6496
+ if (byte === void 0 || (byte & 192) !== 128) break;
6497
+ cut += 1;
6498
+ }
6499
+ return buffer.subarray(cut).toString("utf8");
6500
+ }
6501
+ function boundLiveTail(text) {
6502
+ const lines = text.split("\n");
6503
+ let droppedLines = 0;
6504
+ if (lines.length > WORKER_LIVE_TAIL_LINES) {
6505
+ droppedLines = lines.length - WORKER_LIVE_TAIL_LINES;
6506
+ lines.splice(0, droppedLines);
6507
+ }
6508
+ while (lines.length > 1 && Buffer.byteLength(lines.join("\n"), "utf8") > WORKER_LIVE_TAIL_MAX_BYTES) {
6509
+ lines.shift();
6510
+ droppedLines += 1;
6511
+ }
6512
+ const joined = lines.join("\n");
6513
+ const bytes = Buffer.byteLength(joined, "utf8");
6514
+ if (bytes <= WORKER_LIVE_TAIL_MAX_BYTES) return { text: joined, droppedLines, droppedBytes: 0 };
6515
+ const kept = keepTailBytes(joined, WORKER_LIVE_TAIL_MAX_BYTES);
6516
+ return { text: kept, droppedLines, droppedBytes: bytes - Buffer.byteLength(kept, "utf8") };
6517
+ }
6518
+ function boundSettledText(text) {
6519
+ const bounded = truncateUtf8(text, WORKER_OUTPUT_MAX_BYTES, TAIL_TRUNCATION_MARKER);
6520
+ if (bounded === text) return { text, dropped: 0 };
6521
+ return { text: bounded, dropped: Math.max(0, lineCount2(text) - lineCount2(bounded)) };
6522
+ }
6523
+ var EMPTY_SNAPSHOT = {
6524
+ revision: 0,
6525
+ phase: "starting",
6526
+ tailText: "",
6527
+ droppedLines: 0,
6528
+ droppedBytes: 0,
6529
+ currentAction: null,
6530
+ recentActions: [],
6531
+ toolNames: [],
6532
+ settled: false
6533
+ };
6534
+ function createWorkerProgressFold() {
6535
+ let revision = 0;
6536
+ let phase = "starting";
6537
+ let tailText = "";
6538
+ let droppedLines = 0;
6539
+ let droppedBytes = 0;
6540
+ let currentAction = null;
6541
+ const recentActions = [];
6542
+ const toolNames = [];
6543
+ let settled = false;
6544
+ let durable = "";
6545
+ const pendingActions = [];
6546
+ const pendingActionsById = /* @__PURE__ */ new Map();
6547
+ let windowStartMs = 0;
6548
+ let windowBytes = 0;
6549
+ let cached = EMPTY_SNAPSHOT;
6550
+ const touch = () => {
6551
+ revision += 1;
6552
+ cached = null;
6553
+ return true;
6554
+ };
6555
+ const setPhase = (next) => {
6556
+ if (phase === next) return false;
6557
+ phase = next;
6558
+ return touch();
6559
+ };
6560
+ const admitDelta = (delta, nowMs) => {
6561
+ if (nowMs - windowStartMs >= WORKER_PROGRESS_WINDOW_MS) {
6562
+ windowStartMs = nowMs;
6563
+ windowBytes = 0;
6564
+ }
6565
+ const bytes = Buffer.byteLength(delta, "utf8");
6566
+ if (windowBytes + bytes <= WORKER_PROGRESS_WINDOW_BYTES) {
6567
+ windowBytes += bytes;
6568
+ return delta;
6569
+ }
6570
+ windowBytes = WORKER_PROGRESS_WINDOW_BYTES;
6571
+ droppedBytes += bytes;
6572
+ return "";
6573
+ };
6574
+ const appendTail = (text) => {
6575
+ if (text.length === 0) return false;
6576
+ const bounded = boundLiveTail(tailText + text);
6577
+ tailText = bounded.text;
6578
+ droppedLines += bounded.droppedLines;
6579
+ droppedBytes += bounded.droppedBytes;
6580
+ return touch();
6581
+ };
6582
+ const noteToolName = (tool) => {
6583
+ if (toolNames.includes(tool) || toolNames.length >= WORKER_TOOL_NAME_LIMIT) return false;
6584
+ toolNames.push(tool);
6585
+ return touch();
6586
+ };
6587
+ const removePendingAction = (action) => {
6588
+ const index = pendingActions.lastIndexOf(action);
6589
+ if (index >= 0) pendingActions.splice(index, 1);
6590
+ if (action.toolCallId !== void 0 && pendingActionsById.get(action.toolCallId) === action) {
6591
+ pendingActionsById.delete(action.toolCallId);
6592
+ }
6593
+ };
6594
+ const lastPendingActionForTool = (tool) => {
6595
+ for (let index = pendingActions.length - 1; index >= 0; index -= 1) {
6596
+ const candidate = pendingActions[index];
6597
+ if (candidate?.tool === tool) return candidate;
6598
+ }
6599
+ return void 0;
6600
+ };
6601
+ return {
6602
+ observe(event, nowMs = Date.now()) {
6603
+ if (!isRecord7(event) || settled) return false;
6604
+ let changed = false;
6605
+ const delta = workerTextDelta(event);
6606
+ if (delta.length > 0) {
6607
+ changed = appendTail(admitDelta(delta, nowMs)) || changed;
6608
+ changed = setPhase("writing") || changed;
6609
+ } else if (isThinkingEvent(event)) {
6610
+ changed = setPhase("thinking") || changed;
6611
+ }
6612
+ const durableText = durableAssistantTextFromEvent(event);
6613
+ if (durableText.trim().length > 0) {
6614
+ durable = durableText;
6615
+ if (delta.length === 0 && tailText.trim().length === 0) {
6616
+ changed = appendTail(durableText) || changed;
6617
+ }
6618
+ }
6619
+ const action = toolEventAction(event);
6620
+ if (action !== null && event.type === "clio_tool_start") {
6621
+ if (action.toolCallId !== void 0) {
6622
+ const duplicate = pendingActionsById.get(action.toolCallId);
6623
+ if (duplicate !== void 0) removePendingAction(duplicate);
6624
+ pendingActionsById.set(action.toolCallId, action);
6625
+ }
6626
+ pendingActions.push(action);
6627
+ currentAction = action;
6628
+ noteToolName(action.tool);
6629
+ touch();
6630
+ setPhase("tool");
6631
+ changed = true;
6632
+ } else if (action !== null && event.type === "clio_tool_finish") {
6633
+ const finished = (action.toolCallId !== void 0 ? pendingActionsById.get(action.toolCallId) : void 0) ?? lastPendingActionForTool(action.tool) ?? action;
6634
+ removePendingAction(finished);
6635
+ currentAction = pendingActions[pendingActions.length - 1] ?? null;
6636
+ recentActions.unshift(finished);
6637
+ if (recentActions.length > WORKER_ACTION_TRAIL_LIMIT) recentActions.length = WORKER_ACTION_TRAIL_LIMIT;
6638
+ noteToolName(finished.tool);
6639
+ touch();
6640
+ setPhase(currentAction === null ? "waiting" : "tool");
6641
+ changed = true;
6642
+ }
6643
+ if (event.type === "agent_start") changed = setPhase("starting") || changed;
6644
+ return changed;
6645
+ },
6646
+ settle(text) {
6647
+ const sealed = nonEmptyString(text) ?? nonEmptyString(durable);
6648
+ if (sealed !== void 0) {
6649
+ const bounded = boundSettledText(sealed);
6650
+ tailText = bounded.text;
6651
+ droppedLines = bounded.dropped;
6652
+ }
6653
+ currentAction = null;
6654
+ pendingActions.length = 0;
6655
+ pendingActionsById.clear();
6656
+ settled = true;
6657
+ phase = "settled";
6658
+ touch();
6659
+ return true;
6660
+ },
6661
+ restart() {
6662
+ currentAction = null;
6663
+ pendingActions.length = 0;
6664
+ pendingActionsById.clear();
6665
+ durable = "";
6666
+ settled = false;
6667
+ phase = "starting";
6668
+ touch();
6669
+ },
6670
+ snapshot() {
6671
+ if (cached !== null) return cached;
6672
+ cached = {
6673
+ revision,
6674
+ phase,
6675
+ tailText,
6676
+ droppedLines,
6677
+ droppedBytes,
6678
+ currentAction: currentAction === null ? null : { ...currentAction },
6679
+ recentActions: recentActions.map((entry) => ({ ...entry })),
6680
+ toolNames: [...toolNames],
6681
+ settled
6682
+ };
6683
+ return cached;
6684
+ },
6685
+ durableText() {
6686
+ return durable;
6687
+ }
6688
+ };
6689
+ }
6690
+
6502
6691
  // src/interactive/dispatch-board.ts
6503
6692
  function isDispatchBoardRowSteerable(row2) {
6504
6693
  return runKindSupportsLiveSteering(row2.runtimeKind) && (row2.status === "running" || row2.status === "stale" || row2.status === "enqueued");
@@ -6506,8 +6695,8 @@ function isDispatchBoardRowSteerable(row2) {
6506
6695
  function isDispatchBoardRowCancellable(row2) {
6507
6696
  return row2.status === "running" || row2.status === "stale" || row2.status === "enqueued" || row2.status === "retrying";
6508
6697
  }
6509
- var RECENT_TOOL_TRAIL_LIMIT = 4;
6510
6698
  var TASK_SUMMARY_MAX_WIDTH = 240;
6699
+ var WORKER_PROGRESS_CARD_ROWS = 6;
6511
6700
  var TASK_ISLAND_WIDTH = 44;
6512
6701
  var STATUS_ORDER = {
6513
6702
  running: 0,
@@ -6632,6 +6821,61 @@ function retryCountdown(retry, now = Date.now()) {
6632
6821
  const remainingMs = Math.max(0, retry.dueAtMs - now);
6633
6822
  return remainingMs === 0 ? "now" : `in ${formatCompactMs(remainingMs)}`;
6634
6823
  }
6824
+ function actionPhrase(action) {
6825
+ const descriptor = action.descriptor;
6826
+ if (descriptor === void 0) return action.tool;
6827
+ const object = descriptor.object === void 0 ? "" : ` ${descriptor.object}${descriptor.truncated ? "\u2026" : ""}`;
6828
+ return `${action.tool} ${descriptor.verb}${object}`;
6829
+ }
6830
+ function progressPhaseUnit(theme6, progress) {
6831
+ switch (progress.phase) {
6832
+ case "thinking":
6833
+ return theme6.fg("reason", `${GLYPH.phaseThinking} thinking`);
6834
+ case "writing":
6835
+ return theme6.fg("accent", `${GLYPH.phaseWriting} writing`);
6836
+ case "tool":
6837
+ return theme6.fg("action", `${GLYPH.phaseTool} tool`);
6838
+ case "waiting":
6839
+ return theme6.fg("info", `${GLYPH.phaseWaiting} waiting`);
6840
+ case "settled":
6841
+ case "starting":
6842
+ return null;
6843
+ }
6844
+ }
6845
+ function progressActionLine(theme6, progress, contentWidth) {
6846
+ const phase = progressPhaseUnit(theme6, progress);
6847
+ const current = progress.currentAction;
6848
+ const recent = progress.recentActions[0];
6849
+ const units = [];
6850
+ if (phase !== null) units.push(phase);
6851
+ if (current !== null) units.push(theme6.fg("muted", actionPhrase(current)));
6852
+ else if (recent !== void 0) units.push(theme6.fg("dim", `last ${actionPhrase(recent)}`));
6853
+ if (units.length === 0) return null;
6854
+ return cardUnitsLine(theme6, "doing", units, contentWidth);
6855
+ }
6856
+ function progressAnswerLines(theme6, progress, runId, contentWidth) {
6857
+ if (progress.tailText.length === 0) return [];
6858
+ const gutter = CARD_KV_KEY_WIDTH + 1;
6859
+ const railWidth = Math.max(1, contentWidth - gutter - 2);
6860
+ const wrapped = [];
6861
+ for (const line of progress.tailText.split("\n")) {
6862
+ for (const row2 of wrapTextWithAnsi(sanitizeCallTargetText(line), railWidth)) wrapped.push(row2);
6863
+ }
6864
+ const shown = wrapped.slice(Math.max(0, wrapped.length - WORKER_PROGRESS_CARD_ROWS));
6865
+ const hiddenRows = wrapped.length - shown.length;
6866
+ const rail = theme6.fg("dim", `${GLYPH.rail} `);
6867
+ const body = shown.map((row2) => `${rail}${theme6.fg("muted", row2)}`);
6868
+ const hiddenLines = progress.droppedLines + hiddenRows;
6869
+ if (hiddenLines > 0 || progress.droppedBytes > 0) {
6870
+ const facts = [
6871
+ ...hiddenLines > 0 ? [`${hiddenLines} more line${hiddenLines === 1 ? "" : "s"}`] : [],
6872
+ ...progress.droppedBytes > 0 ? [`${progress.droppedBytes} bytes outran the view`] : [],
6873
+ `/view dispatch:${runId}`
6874
+ ];
6875
+ body.push(`${rail}${theme6.fg("dim", truncateToWidth(facts.join(" \xB7 "), railWidth, "\u2026", false))}`);
6876
+ }
6877
+ return body.map((row2, index) => `${index === 0 ? cardKvKey(theme6, "answer") : " ".repeat(gutter)}${row2}`);
6878
+ }
6635
6879
  function renderDispatchCard(row2, width, evidence, options = {}) {
6636
6880
  const theme6 = clioTheme();
6637
6881
  const contentWidth = Math.max(0, width - 4);
@@ -6675,6 +6919,20 @@ function renderDispatchCard(row2, width, evidence, options = {}) {
6675
6919
  targetLine,
6676
6920
  ...row2.taskSummary ? [truncateToWidth(`${cardKvKey(theme6, "task")}${theme6.fg("muted", row2.taskSummary)}`, contentWidth, "\u2026", false)] : [],
6677
6921
  cardUnitsLine(theme6, "status", statusUnits, contentWidth),
6922
+ ...row2.budget !== void 0 ? [
6923
+ truncateToWidth(
6924
+ `${cardKvKey(theme6, "policy")}${theme6.fg("muted", `${formatBudgetPolicy(row2.budget)}; requested ${formatBudgetRequest(row2.budget)}`)}`,
6925
+ contentWidth,
6926
+ "\u2026",
6927
+ false
6928
+ ),
6929
+ truncateToWidth(
6930
+ `${cardKvKey(theme6, "budget")}${theme6.fg("muted", `${formatEffectiveBudget(row2.budget)}; reason ${formatBudgetReasons(row2.budget)}`)}`,
6931
+ contentWidth,
6932
+ "\u2026",
6933
+ false
6934
+ )
6935
+ ] : [],
6678
6936
  cardUnitsLine(theme6, "telemetry", [up, down, total, ...contextUnit !== null ? [contextUnit] : []], contentWidth)
6679
6937
  ];
6680
6938
  if (row2.retry) {
@@ -6700,6 +6958,11 @@ function renderDispatchCard(row2, width, evidence, options = {}) {
6700
6958
  ];
6701
6959
  bodyLines2.push(cardUnitsLine(theme6, "tools", toolUnits, contentWidth));
6702
6960
  }
6961
+ if (options.expanded === true && row2.progress) {
6962
+ const doing = progressActionLine(theme6, row2.progress, contentWidth);
6963
+ if (doing !== null) bodyLines2.push(doing);
6964
+ bodyLines2.push(...progressAnswerLines(theme6, row2.progress, row2.runId, contentWidth));
6965
+ }
6703
6966
  if (row2.steerAcknowledgement) {
6704
6967
  bodyLines2.push(
6705
6968
  cardUnitsLine(
@@ -6745,7 +7008,7 @@ function renderTaskIslandRow(row2, width) {
6745
7008
  const task = row2.taskSummary ? ` ${theme6.fg("muted", truncateToWidth(row2.taskSummary, Math.max(1, width - 2), "\u2026", false))}` : null;
6746
7009
  return [padAnsi(line1, width), ...task ? [padAnsi(task, width)] : [], padAnsi(telemetry, width)];
6747
7010
  }
6748
- function formatDispatchBoardLines(rows, width = 76, observability, selectedRunId) {
7011
+ function formatDispatchBoardLines(rows, width = 76, observability, selectedRunId, detailExpanded = false) {
6749
7012
  if (rows.length === 0) {
6750
7013
  const theme6 = clioTheme();
6751
7014
  const lines = ["", "No fleet runs yet", "Delegated runs appear here with task, status, and telemetry.", ""];
@@ -6756,7 +7019,8 @@ function formatDispatchBoardLines(rows, width = 76, observability, selectedRunId
6756
7019
  }
6757
7020
  const cards = rows.map(
6758
7021
  (row2) => renderDispatchCard(row2, width, deriveRunEvidenceState(observability, row2.runId), {
6759
- selected: row2.runId === selectedRunId
7022
+ selected: row2.runId === selectedRunId,
7023
+ expanded: detailExpanded && row2.runId === selectedRunId
6760
7024
  })
6761
7025
  );
6762
7026
  const body = [];
@@ -6768,6 +7032,7 @@ function formatDispatchBoardLines(rows, width = 76, observability, selectedRunId
6768
7032
  }
6769
7033
  function createDispatchBoardView(rows, observability) {
6770
7034
  let selectedRunId = null;
7035
+ let expanded = false;
6771
7036
  const normalizeSelection = (currentRows) => {
6772
7037
  if (currentRows.length === 0) {
6773
7038
  selectedRunId = null;
@@ -6789,7 +7054,7 @@ function createDispatchBoardView(rows, observability) {
6789
7054
  render(width) {
6790
7055
  const currentRows = rows();
6791
7056
  normalizeSelection(currentRows);
6792
- return formatDispatchBoardLines(currentRows, Math.max(1, width), observability(), selectedRunId);
7057
+ return formatDispatchBoardLines(currentRows, Math.max(1, width), observability(), selectedRunId, expanded);
6793
7058
  },
6794
7059
  selectedRow() {
6795
7060
  return normalizeSelection(rows());
@@ -6802,8 +7067,15 @@ function createDispatchBoardView(rows, observability) {
6802
7067
  },
6803
7068
  resetSelection() {
6804
7069
  selectedRunId = null;
7070
+ expanded = false;
6805
7071
  normalizeSelection(rows());
6806
7072
  },
7073
+ toggleDetail() {
7074
+ expanded = !expanded;
7075
+ },
7076
+ detailExpanded() {
7077
+ return expanded;
7078
+ },
6807
7079
  invalidate() {
6808
7080
  }
6809
7081
  };
@@ -6948,13 +7220,15 @@ function readRunningSnapshot(snapshot) {
6948
7220
  for (const value of snapshot.running) {
6949
7221
  const runId = parseRunId(value.runId);
6950
7222
  if (!runId) continue;
7223
+ const budget = cloneRunToolBudgetEnvelope(value.budget);
6951
7224
  running.set(runId, {
6952
7225
  inputTokens: parseFiniteNumberOrZero(value.tokens.input),
6953
7226
  outputTokens: parseFiniteNumberOrZero(value.tokens.output),
6954
7227
  tokenCount: parseFiniteNumberOrZero(value.tokens.total),
6955
7228
  costUsd: parseFiniteNumberOrZero(value.costUsd),
6956
7229
  costProvenance: value.costProvenance ?? "unknown",
6957
- outcomePhase: value.outcomePhase
7230
+ outcomePhase: value.outcomePhase,
7231
+ ...budget !== void 0 ? { budget } : {}
6958
7232
  });
6959
7233
  }
6960
7234
  } catch {
@@ -6969,7 +7243,9 @@ function resolveElapsedMs(entry, now) {
6969
7243
  }
6970
7244
  function toRow(entry, now) {
6971
7245
  const retry = entry.retry;
7246
+ const progress = entry.progress.snapshot();
6972
7247
  return {
7248
+ progress,
6973
7249
  runId: entry.runId,
6974
7250
  agentId: entry.agentId,
6975
7251
  ...entry.agentAudience !== void 0 ? { agentAudience: entry.agentAudience } : {},
@@ -6979,6 +7255,7 @@ function toRow(entry, now) {
6979
7255
  targetId: entry.targetId,
6980
7256
  wireModelId: entry.wireModelId,
6981
7257
  ...entry.taskSummary !== void 0 ? { taskSummary: entry.taskSummary } : {},
7258
+ ...entry.budget !== void 0 ? { budget: entry.budget } : {},
6982
7259
  status: retry ? "retrying" : entry.status,
6983
7260
  elapsedMs: resolveElapsedMs(entry, now),
6984
7261
  tokenCount: entry.tokenCount,
@@ -6995,8 +7272,10 @@ function toRow(entry, now) {
6995
7272
  ...entry.contextWindow !== void 0 ? { contextWindow: entry.contextWindow } : {},
6996
7273
  lastContextTokens: entry.lastContextTokens,
6997
7274
  ...entry.receiptId !== void 0 ? { receiptId: entry.receiptId } : {},
6998
- currentTool: retry ? null : entry.currentTool,
6999
- recentTools: [...entry.recentTools],
7275
+ // Both tool fields are views of the one projection, so the compact card and
7276
+ // the expanded detail can never disagree about what is running.
7277
+ currentTool: retry ? null : progress.currentAction?.tool ?? null,
7278
+ recentTools: progress.recentActions.map((action) => action.tool),
7000
7279
  ...retry ? { retry: { ...retry } } : {},
7001
7280
  ...entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}
7002
7281
  };
@@ -7020,10 +7299,14 @@ function pruneEntries(entries) {
7020
7299
  entries.delete(entry.runId);
7021
7300
  }
7022
7301
  }
7023
- function createDispatchBoardStore(bus, snapshot) {
7302
+ function createDispatchBoardStore(bus, snapshot, readReceipt) {
7024
7303
  const entries = /* @__PURE__ */ new Map();
7025
7304
  let nextSequence = 0;
7026
7305
  let reconciledAtMs = Date.now();
7306
+ const settleFromReceipt = (entry) => {
7307
+ const text = readReceipt?.(entry.runId)?.text;
7308
+ entry.progress.settle(typeof text === "string" && text.trim().length > 0 ? text : void 0);
7309
+ };
7027
7310
  const upsertBase = (raw, status, now) => {
7028
7311
  const runId = parseRunId(raw.runId);
7029
7312
  if (!runId) return null;
@@ -7035,6 +7318,7 @@ function createDispatchBoardStore(bus, snapshot) {
7035
7318
  const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
7036
7319
  const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
7037
7320
  const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
7321
+ const budget = cloneRunToolBudgetEnvelope(raw.budget) ?? previous?.budget;
7038
7322
  const entry = {
7039
7323
  runId,
7040
7324
  agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
@@ -7045,6 +7329,7 @@ function createDispatchBoardStore(bus, snapshot) {
7045
7329
  targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
7046
7330
  wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
7047
7331
  ...taskSummary !== void 0 ? { taskSummary } : {},
7332
+ ...budget !== void 0 ? { budget } : {},
7048
7333
  status,
7049
7334
  tokenCount: previous?.tokenCount ?? 0,
7050
7335
  costUsd: previous?.costUsd ?? 0,
@@ -7065,8 +7350,7 @@ function createDispatchBoardStore(bus, snapshot) {
7065
7350
  ...contextWindow !== void 0 ? { contextWindow } : {},
7066
7351
  lastContextTokens: previous?.lastContextTokens ?? 0,
7067
7352
  ...previous?.receiptId !== void 0 ? { receiptId: previous.receiptId } : {},
7068
- currentTool: previous?.currentTool ?? null,
7069
- recentTools: previous?.recentTools ?? [],
7353
+ progress: previous?.progress ?? createWorkerProgressFold(),
7070
7354
  ...previous?.retry ? { retry: { ...previous.retry } } : {},
7071
7355
  ...previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}
7072
7356
  };
@@ -7101,7 +7385,7 @@ function createDispatchBoardStore(bus, snapshot) {
7101
7385
  entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
7102
7386
  entry.costProvenance = payload.costProvenance ?? "unknown";
7103
7387
  entry.outcomeDetail = null;
7104
- entry.currentTool = null;
7388
+ settleFromReceipt(entry);
7105
7389
  entry.receiptId = entry.runId;
7106
7390
  delete entry.retry;
7107
7391
  if (typeof payload.inputTokenCount === "number") {
@@ -7127,7 +7411,7 @@ function createDispatchBoardStore(bus, snapshot) {
7127
7411
  entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
7128
7412
  entry.costProvenance = payload.costProvenance ?? "unknown";
7129
7413
  entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
7130
- entry.currentTool = null;
7414
+ settleFromReceipt(entry);
7131
7415
  if (payload.reason !== "retry_denied") entry.receiptId = entry.runId;
7132
7416
  delete entry.retry;
7133
7417
  if (typeof payload.inputTokenCount === "number") {
@@ -7147,7 +7431,7 @@ function createDispatchBoardStore(bus, snapshot) {
7147
7431
  if (wasRetrying && raw?.startedAt === null) {
7148
7432
  entry.status = "aborted";
7149
7433
  entry.finishedAtMs = Date.now();
7150
- entry.currentTool = null;
7434
+ entry.progress.settle();
7151
7435
  } else {
7152
7436
  if (isTerminalStatus(entry.status) && !wasRetrying) return;
7153
7437
  entry.status = "cancelling";
@@ -7169,7 +7453,7 @@ function createDispatchBoardStore(bus, snapshot) {
7169
7453
  entry.status = status;
7170
7454
  if (status === "dead") {
7171
7455
  entry.finishedAtMs ??= Date.now();
7172
- entry.currentTool = null;
7456
+ entry.progress.settle();
7173
7457
  delete entry.retry;
7174
7458
  }
7175
7459
  return;
@@ -7178,7 +7462,7 @@ function createDispatchBoardStore(bus, snapshot) {
7178
7462
  entry.failoverHops = (entry.failoverHops ?? 0) + 1;
7179
7463
  entry.status = "running";
7180
7464
  entry.finishedAtMs = null;
7181
- entry.currentTool = null;
7465
+ entry.progress.restart();
7182
7466
  delete entry.retry;
7183
7467
  return;
7184
7468
  }
@@ -7193,20 +7477,7 @@ function createDispatchBoardStore(bus, snapshot) {
7193
7477
  entry.ttftMs = Math.round(performance3.now() - entry.startedAtClockMs);
7194
7478
  }
7195
7479
  }
7196
- if (type === "clio_tool_start") {
7197
- const payload2 = workerEvent.payload;
7198
- const tool = parseNonEmptyString(payload2?.tool);
7199
- if (tool !== void 0) entry.currentTool = tool;
7200
- }
7201
- if (type === "clio_tool_finish") {
7202
- const payload2 = workerEvent.payload;
7203
- const tool = parseNonEmptyString(payload2?.tool);
7204
- entry.currentTool = null;
7205
- if (tool !== void 0) {
7206
- entry.recentTools.unshift(tool);
7207
- if (entry.recentTools.length > RECENT_TOOL_TRAIL_LIMIT) entry.recentTools.length = RECENT_TOOL_TRAIL_LIMIT;
7208
- }
7209
- }
7480
+ entry.progress.observe(payload.event);
7210
7481
  if (type === "clio_steer_received") {
7211
7482
  const steerPayload = workerEvent.payload;
7212
7483
  entry.steerAcknowledgement = {
@@ -7229,7 +7500,7 @@ function createDispatchBoardStore(bus, snapshot) {
7229
7500
  if (!status) return;
7230
7501
  entry.status = status;
7231
7502
  entry.finishedAtMs ??= Date.now();
7232
- entry.currentTool = null;
7503
+ entry.progress.settle();
7233
7504
  delete entry.retry;
7234
7505
  }
7235
7506
  })
@@ -7271,7 +7542,6 @@ function createDispatchBoardStore(bus, snapshot) {
7271
7542
  now
7272
7543
  );
7273
7544
  if (entry) {
7274
- entry.currentTool = null;
7275
7545
  entry.retry = { ...projection.retry };
7276
7546
  if (entry.taskSummary === void 0 && projection.taskSummary !== void 0) {
7277
7547
  entry.taskSummary = projection.taskSummary;
@@ -7287,6 +7557,7 @@ function createDispatchBoardStore(bus, snapshot) {
7287
7557
  entry.tokenCount = live.tokenCount;
7288
7558
  entry.costUsd = live.costUsd;
7289
7559
  entry.costProvenance = live.costProvenance;
7560
+ if (live.budget !== void 0) entry.budget = live.budget;
7290
7561
  if (live.outcomePhase === "aborting") {
7291
7562
  if (entry.status !== "completed" && entry.status !== "aborted") {
7292
7563
  entry.status = "cancelling";
@@ -7351,7 +7622,8 @@ function emptyRunTally() {
7351
7622
  hadProviderReasoning: false,
7352
7623
  hadEstimatedReasoning: false,
7353
7624
  toolCount: 0,
7354
- toolErrorCount: 0
7625
+ toolErrorCount: 0,
7626
+ responseModelIdObservation: { state: "not-observed" }
7355
7627
  };
7356
7628
  }
7357
7629
  function foldMessageIntoRunTally(tally, message) {
@@ -7364,8 +7636,10 @@ function foldMessageIntoRunTally(tally, message) {
7364
7636
  }
7365
7637
  if (message.role !== "assistant") return tally;
7366
7638
  const usage = message.usage;
7639
+ const record2 = message;
7367
7640
  const next = {
7368
7641
  ...tally,
7642
+ responseModelIdObservation: responseModelIdObservationFromRecord(record2, "not-observed"),
7369
7643
  inputTokens: tally.inputTokens + (usage ? finite2(usage.input) : 0),
7370
7644
  outputTokens: tally.outputTokens + (usage ? finite2(usage.output) : 0),
7371
7645
  cacheReadTokens: tally.cacheReadTokens + (usage ? finite2(usage.cacheRead) : 0),
@@ -7399,7 +7673,8 @@ function summaryFromRunTally(tally, input) {
7399
7673
  stopReason: input.stopReason,
7400
7674
  ...input.stopDetail !== void 0 ? { stopDetail: input.stopDetail } : {},
7401
7675
  watchdogPeak: input.watchdogPeak,
7402
- truncated: input.truncated === true
7676
+ truncated: input.truncated === true,
7677
+ responseModelIdObservation: tally.responseModelIdObservation
7403
7678
  };
7404
7679
  if (tally.hadProviderReasoning || tally.hadEstimatedReasoning) {
7405
7680
  summary.reasoningTokens = tally.reasoningTokens;
@@ -8979,7 +9254,7 @@ var ISO_8601_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{
8979
9254
  function isIso8601(value) {
8980
9255
  return typeof value === "string" && ISO_8601_REGEX.test(value) && !Number.isNaN(Date.parse(value));
8981
9256
  }
8982
- function isRecord7(value) {
9257
+ function isRecord8(value) {
8983
9258
  return !!value && typeof value === "object" && !Array.isArray(value);
8984
9259
  }
8985
9260
  function isNonEmptyString(value) {
@@ -9058,7 +9333,7 @@ function readRunLedger(stateDir) {
9058
9333
  const raw = readFileSync5(runLedgerPath(stateDir), "utf8").trim();
9059
9334
  if (raw.length === 0) return [];
9060
9335
  const parsed = JSON.parse(raw);
9061
- return Array.isArray(parsed) ? parsed.filter(isRecord7) : [];
9336
+ return Array.isArray(parsed) ? parsed.filter(isRecord8) : [];
9062
9337
  } catch {
9063
9338
  return [];
9064
9339
  }
@@ -9080,7 +9355,7 @@ function validateToolStats(value) {
9080
9355
  }
9081
9356
  for (let i = 0; i < value.length; i++) {
9082
9357
  const entry = value[i];
9083
- if (!isRecord7(entry)) {
9358
+ if (!isRecord8(entry)) {
9084
9359
  return { ok: false, reason: `toolStats[${i}] not an object` };
9085
9360
  }
9086
9361
  if (!isNonEmptyString(entry.tool)) {
@@ -9120,7 +9395,7 @@ function verifyReceiptFile(stateDir, runId) {
9120
9395
  } catch (err) {
9121
9396
  return { ok: false, reason: `invalid json: ${err.message}` };
9122
9397
  }
9123
- if (!isRecord7(parsed)) {
9398
+ if (!isRecord8(parsed)) {
9124
9399
  return { ok: false, reason: "receipt is not an object" };
9125
9400
  }
9126
9401
  const r = parsed;
@@ -9466,17 +9741,17 @@ var ReceiptArtifactProvider = class {
9466
9741
  }
9467
9742
  };
9468
9743
  function resultText3(result) {
9469
- if (!isRecord7(result)) return "";
9744
+ if (!isRecord8(result)) return "";
9470
9745
  if (result.kind === "ok" && typeof result.output === "string") return result.output;
9471
9746
  if (result.kind === "error" && typeof result.message === "string") return result.message;
9472
9747
  return "";
9473
9748
  }
9474
9749
  function resultDetails(result) {
9475
- return isRecord7(result) && isRecord7(result.details) ? result.details : null;
9750
+ return isRecord8(result) && isRecord8(result.details) ? result.details : null;
9476
9751
  }
9477
9752
  function messagePayload(entry) {
9478
9753
  if (entry.kind !== "message") return null;
9479
- return isRecord7(entry.payload) ? entry.payload : null;
9754
+ return isRecord8(entry.payload) ? entry.payload : null;
9480
9755
  }
9481
9756
  function toolNameFor(entry) {
9482
9757
  const payload = messagePayload(entry);
@@ -9497,12 +9772,12 @@ function dispatchResultForRun(entries, runId) {
9497
9772
  for (const entry of entries) {
9498
9773
  const payload = messagePayload(entry);
9499
9774
  if (!payload) continue;
9500
- const toolName2 = typeof payload.toolName === "string" ? payload.toolName : "";
9501
- if (toolName2 !== "dispatch") continue;
9775
+ const toolName = typeof payload.toolName === "string" ? payload.toolName : "";
9776
+ if (toolName !== "dispatch") continue;
9502
9777
  const details = resultDetails(payload.result);
9503
9778
  const detailRunId = details?.runId;
9504
9779
  const runIds = details?.runIds;
9505
- const matches = detailRunId === runId || Array.isArray(runIds) && runIds.some((item) => item === runId) || Array.isArray(details?.runs) && details.runs.some((item) => isRecord7(item) && item.runId === runId);
9780
+ const matches = detailRunId === runId || Array.isArray(runIds) && runIds.some((item) => item === runId) || Array.isArray(details?.runs) && details.runs.some((item) => isRecord8(item) && item.runId === runId);
9506
9781
  if (!matches) continue;
9507
9782
  const text = resultText3(payload.result);
9508
9783
  if (text.length > 0) return text;
@@ -9798,16 +10073,16 @@ var ToolOutputArtifactProvider = class {
9798
10073
  };
9799
10074
  }
9800
10075
  toolResultArtifact(entry, path) {
9801
- const toolName2 = toolNameFor(entry);
10076
+ const toolName = toolNameFor(entry);
9802
10077
  const artifact = {
9803
10078
  id: `tool:${entry.turnId}`,
9804
10079
  category: this.category,
9805
- title: safeTitle(`${toolName2} \xB7 ${basename(path)}`, `${toolName2} output`),
10080
+ title: safeTitle(`${toolName} \xB7 ${basename(path)}`, `${toolName} output`),
9806
10081
  timestamp: parseTime(entry.timestamp),
9807
10082
  sizeBytes: maybeSizeBytes(path),
9808
10083
  path,
9809
- toolName: toolName2,
9810
- searchText: [toolName2, entry.turnId, path, basename(path)],
10084
+ toolName,
10085
+ searchText: [toolName, entry.turnId, path, basename(path)],
9811
10086
  load: () => loadTextPath(path)
9812
10087
  };
9813
10088
  const payload = messagePayload(entry);
@@ -10179,7 +10454,7 @@ async function listViewArtifacts(providers) {
10179
10454
  }
10180
10455
 
10181
10456
  // src/interactive/worker-receipts.ts
10182
- function isRecord8(value) {
10457
+ function isRecord9(value) {
10183
10458
  return typeof value === "object" && value !== null && !Array.isArray(value);
10184
10459
  }
10185
10460
  function optionalString(value) {
@@ -10195,19 +10470,19 @@ function elapsedMsFrom(receipt) {
10195
10470
  return Math.max(0, endedAt - startedAt);
10196
10471
  }
10197
10472
  function contractFrom(receipt) {
10198
- const quality = isRecord8(receipt.quality) ? receipt.quality : null;
10473
+ const quality = isRecord9(receipt.quality) ? receipt.quality : null;
10199
10474
  if (quality === null) return void 0;
10200
10475
  if (!Object.hasOwn(quality, "resultContract")) return void 0;
10201
10476
  const fact = quality.resultContract;
10202
10477
  if (fact === null) return "unmeasured";
10203
- if (!isRecord8(fact)) return void 0;
10478
+ if (!isRecord9(fact)) return void 0;
10204
10479
  const conformance = fact.conformance;
10205
10480
  return conformance === "pass" || conformance === "fail" || conformance === "not-reached" ? conformance : void 0;
10206
10481
  }
10207
10482
  function workerReceiptFacts(receipt) {
10208
10483
  const outcome = optionalString(receipt.outcome);
10209
10484
  if (outcome === void 0) return null;
10210
- const output = isRecord8(receipt.output) ? receipt.output : null;
10485
+ const output = isRecord9(receipt.output) ? receipt.output : null;
10211
10486
  const contract = contractFrom(receipt);
10212
10487
  const text = optionalString(output?.text);
10213
10488
  const failureMessage = optionalString(receipt.failureMessage);
@@ -10231,7 +10506,7 @@ function workerReceiptFacts(receipt) {
10231
10506
  function readReceiptFileFacts(runId, stateDir) {
10232
10507
  try {
10233
10508
  const parsed = JSON.parse(readFileSync6(receiptFilePath(stateDir, runId), "utf8"));
10234
- return isRecord8(parsed) ? workerReceiptFacts(parsed) : null;
10509
+ return isRecord9(parsed) ? workerReceiptFacts(parsed) : null;
10235
10510
  } catch {
10236
10511
  return null;
10237
10512
  }
@@ -10246,7 +10521,7 @@ function findRunRow(runId, stateDir) {
10246
10521
  const parsed = JSON.parse(readFileSync6(path, "utf8"));
10247
10522
  if (!Array.isArray(parsed)) return null;
10248
10523
  for (const entry of parsed) {
10249
- if (isRecord8(entry) && entry.id === runId) return entry;
10524
+ if (isRecord9(entry) && entry.id === runId) return entry;
10250
10525
  }
10251
10526
  return null;
10252
10527
  } catch {
@@ -10275,13 +10550,7 @@ init_esm_shims();
10275
10550
  function workerAskedByModel(state) {
10276
10551
  return state.parentToolCallId !== void 0 || state.origin === "agent";
10277
10552
  }
10278
- var WORKER_LIVE_TAIL_LINES = 40;
10279
- var WORKER_TOOL_NAME_LIMIT = 8;
10280
- var WORKER_TEXT_TRUNCATION_MARKER = "";
10281
- function isRecord9(value) {
10282
- return typeof value === "object" && value !== null && !Array.isArray(value);
10283
- }
10284
- function nonEmptyString(value) {
10553
+ function nonEmptyString2(value) {
10285
10554
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
10286
10555
  }
10287
10556
  function finiteNumber(value) {
@@ -10296,11 +10565,11 @@ function workerRuntimeKind(runtimeKind, runtimeId) {
10296
10565
  function runtimeIdentity(payload) {
10297
10566
  const kind = workerRuntimeKind(payload.runtimeKind, payload.runtimeId);
10298
10567
  if (kind === "acp") {
10299
- const peerId = nonEmptyString(payload.agentId);
10568
+ const peerId = nonEmptyString2(payload.agentId);
10300
10569
  return { kind, ...peerId !== void 0 ? { peerId } : {} };
10301
10570
  }
10302
- const targetId = nonEmptyString(payload.targetId);
10303
- const wireModelId = nonEmptyString(payload.wireModelId);
10571
+ const targetId = nonEmptyString2(payload.targetId);
10572
+ const wireModelId = nonEmptyString2(payload.wireModelId);
10304
10573
  return {
10305
10574
  kind,
10306
10575
  ...targetId !== void 0 ? { targetId } : {},
@@ -10314,44 +10583,8 @@ function workerTargetLabel(runtime) {
10314
10583
  }
10315
10584
  return runtime.targetId ?? runtime.wireModelId ?? "unknown";
10316
10585
  }
10317
- function textDelta(event) {
10318
- if (!isRecord9(event)) return "";
10319
- if (event.type === "message_update") {
10320
- const assistantEvent = isRecord9(event.assistantMessageEvent) ? event.assistantMessageEvent : null;
10321
- if (assistantEvent?.type !== "text_delta") return "";
10322
- return typeof assistantEvent.delta === "string" ? assistantEvent.delta : "";
10323
- }
10324
- if (event.type !== "text_delta") return "";
10325
- if (typeof event.delta === "string") return event.delta;
10326
- return typeof event.text === "string" ? event.text : "";
10327
- }
10328
- function toolName(event) {
10329
- if (!isRecord9(event)) return void 0;
10330
- if (event.type !== "clio_tool_start" && event.type !== "clio_tool_finish") return void 0;
10331
- const payload = isRecord9(event.payload) ? event.payload : null;
10332
- return nonEmptyString(payload?.tool);
10333
- }
10334
- function lineCount2(text) {
10335
- if (text.length === 0) return 0;
10336
- let lines = 1;
10337
- for (let index = 0; index < text.length; index += 1) {
10338
- if (text[index] === "\n") lines += 1;
10339
- }
10340
- return lines;
10341
- }
10342
- function boundLiveText(text) {
10343
- const lines = text.split("\n");
10344
- if (lines.length <= WORKER_LIVE_TAIL_LINES) return { text, dropped: 0 };
10345
- const dropped = lines.length - WORKER_LIVE_TAIL_LINES;
10346
- return { text: lines.slice(dropped).join("\n"), dropped };
10347
- }
10348
- function boundSettledText(text) {
10349
- const bounded = truncateUtf8(text, WORKER_OUTPUT_MAX_BYTES, WORKER_TEXT_TRUNCATION_MARKER);
10350
- if (bounded === text) return { text, dropped: 0 };
10351
- return { text: bounded, dropped: Math.max(0, lineCount2(text) - lineCount2(bounded)) };
10352
- }
10353
10586
  function terminalFacts(payload) {
10354
- const detail = nonEmptyString(payload.outcomeDetail);
10587
+ const detail = nonEmptyString2(payload.outcomeDetail);
10355
10588
  const tokenCount = finiteNumber(payload.tokenCount);
10356
10589
  const durationMs = finiteNumber(payload.durationMs);
10357
10590
  const exitCode = finiteNumber(payload.exitCode);
@@ -10374,9 +10607,16 @@ function createWorkerStream(options = {}) {
10374
10607
  const byAssignment = /* @__PURE__ */ new Map();
10375
10608
  const attemptByAssignment = /* @__PURE__ */ new Map();
10376
10609
  const assignmentByRun = /* @__PURE__ */ new Map();
10377
- const durableTextByRun = /* @__PURE__ */ new Map();
10610
+ const progressByAssignment = /* @__PURE__ */ new Map();
10611
+ const applyProgress = (entry, fold) => {
10612
+ const snapshot = fold.snapshot();
10613
+ entry.text = snapshot.tailText;
10614
+ entry.droppedLines = snapshot.droppedLines;
10615
+ entry.tools = [...snapshot.toolNames];
10616
+ entry.progress = snapshot;
10617
+ };
10378
10618
  const currentEntryForRun = (runId) => {
10379
- const id = nonEmptyString(runId);
10619
+ const id = nonEmptyString2(runId);
10380
10620
  if (id === void 0) return void 0;
10381
10621
  const assignmentId = assignmentByRun.get(id);
10382
10622
  const entry = assignmentId === void 0 ? void 0 : byAssignment.get(assignmentId);
@@ -10388,18 +10628,17 @@ function createWorkerStream(options = {}) {
10388
10628
  entry.pending = false;
10389
10629
  const attempt = entry.attempts[entry.attempts.length - 1];
10390
10630
  if (attempt !== void 0) attempt.outcome = facts.outcome;
10391
- const settled = nonEmptyString(facts.text) ?? nonEmptyString(durableTextByRun.get(entry.runId));
10392
- if (settled !== void 0) {
10393
- const bounded = boundSettledText(settled);
10394
- entry.text = bounded.text;
10395
- entry.droppedLines = bounded.dropped;
10631
+ const fold = progressByAssignment.get(entry.assignmentId);
10632
+ if (fold !== void 0) {
10633
+ fold.settle(nonEmptyString2(facts.text));
10634
+ applyProgress(entry, fold);
10396
10635
  }
10397
10636
  return { kind: "updated", entry };
10398
10637
  };
10399
10638
  return {
10400
10639
  started(payload) {
10401
- const runId = nonEmptyString(payload.runId);
10402
- const assignmentId = nonEmptyString(payload.assignmentId) ?? runId;
10640
+ const runId = nonEmptyString2(payload.runId);
10641
+ const assignmentId = nonEmptyString2(payload.assignmentId) ?? runId;
10403
10642
  if (runId === void 0 || assignmentId === void 0) return null;
10404
10643
  const runtime = runtimeIdentity(payload);
10405
10644
  const existing = byAssignment.get(assignmentId);
@@ -10412,10 +10651,16 @@ function createWorkerStream(options = {}) {
10412
10651
  delete existing.receipt;
10413
10652
  existing.attempts.push({ runId, targetLabel: workerTargetLabel(runtime) });
10414
10653
  assignmentByRun.set(runId, assignmentId);
10654
+ const progress2 = progressByAssignment.get(assignmentId);
10655
+ if (progress2 !== void 0) {
10656
+ progress2.restart();
10657
+ applyProgress(existing, progress2);
10658
+ }
10415
10659
  return { kind: "updated", entry: existing };
10416
10660
  }
10417
10661
  if (payload.requestOrigin !== "user" && payload.requestOrigin !== "agent") return null;
10418
10662
  attemptByAssignment.set(assignmentId, payload.attempt);
10663
+ const progress = createWorkerProgressFold();
10419
10664
  const entry = {
10420
10665
  assignmentId,
10421
10666
  runId,
@@ -10425,42 +10670,23 @@ function createWorkerStream(options = {}) {
10425
10670
  text: "",
10426
10671
  droppedLines: 0,
10427
10672
  tools: [],
10673
+ progress: progress.snapshot(),
10428
10674
  attempts: [{ runId, targetLabel: workerTargetLabel(runtime) }],
10429
10675
  pending: true,
10430
10676
  ...payload.parentToolCallId !== void 0 ? { parentToolCallId: payload.parentToolCallId } : {}
10431
10677
  };
10432
10678
  byAssignment.set(assignmentId, entry);
10433
10679
  assignmentByRun.set(runId, assignmentId);
10680
+ progressByAssignment.set(assignmentId, progress);
10434
10681
  return { kind: "created", entry };
10435
10682
  },
10436
10683
  progress(payload) {
10437
10684
  const entry = currentEntryForRun(payload.runId);
10438
10685
  if (entry === void 0 || !entry.pending) return null;
10439
- const event = payload.event;
10440
- let changed = false;
10441
- const delta = textDelta(event);
10442
- if (delta.length > 0) {
10443
- const bounded = boundLiveText(entry.text + delta);
10444
- entry.text = bounded.text;
10445
- entry.droppedLines += bounded.dropped;
10446
- changed = true;
10447
- }
10448
- const durable = durableAssistantTextFromEvent(event);
10449
- if (durable.trim().length > 0) {
10450
- durableTextByRun.set(entry.runId, durable);
10451
- if (delta.length === 0 && entry.text.trim().length === 0) {
10452
- const bounded = boundLiveText(durable);
10453
- entry.text = bounded.text;
10454
- entry.droppedLines += bounded.dropped;
10455
- }
10456
- changed = true;
10457
- }
10458
- const tool = toolName(event);
10459
- if (tool !== void 0 && !entry.tools.includes(tool) && entry.tools.length < WORKER_TOOL_NAME_LIMIT) {
10460
- entry.tools.push(tool);
10461
- changed = true;
10462
- }
10463
- return changed ? { kind: "updated", entry } : null;
10686
+ const fold = progressByAssignment.get(entry.assignmentId);
10687
+ if (fold === void 0 || !fold.observe(payload.event)) return null;
10688
+ applyProgress(entry, fold);
10689
+ return { kind: "updated", entry };
10464
10690
  },
10465
10691
  completed(payload) {
10466
10692
  const entry = currentEntryForRun(payload.runId);
@@ -10476,7 +10702,7 @@ function createWorkerStream(options = {}) {
10476
10702
  const entry = currentEntryForRun(payload.runId);
10477
10703
  if (entry === void 0 || !entry.pending) return null;
10478
10704
  const durationMs = finiteNumber(payload.elapsedMs);
10479
- const failureMessage = nonEmptyString(payload.reason);
10705
+ const failureMessage = nonEmptyString2(payload.reason);
10480
10706
  entry.pending = false;
10481
10707
  entry.receipt = {
10482
10708
  outcome: "canceled",
@@ -10495,7 +10721,7 @@ function createWorkerStream(options = {}) {
10495
10721
  byAssignment.clear();
10496
10722
  attemptByAssignment.clear();
10497
10723
  assignmentByRun.clear();
10498
- durableTextByRun.clear();
10724
+ progressByAssignment.clear();
10499
10725
  }
10500
10726
  };
10501
10727
  }
@@ -10818,6 +11044,7 @@ function richMessageFromEntry(entry, maxTextChars) {
10818
11044
  "api",
10819
11045
  "provider",
10820
11046
  "model",
11047
+ "responseModelIdObservation",
10821
11048
  "responseModel",
10822
11049
  "responseId",
10823
11050
  "diagnostics",
@@ -10859,7 +11086,8 @@ function toolResultContent(result, unbounded = false) {
10859
11086
  return [{ type: "text", text: stringifyPreview3(result, unbounded ? Number.POSITIVE_INFINITY : 1e4) }];
10860
11087
  }
10861
11088
  function displayReplayToolResult(result, unbounded = false) {
10862
- const content = toolResultContent(result, unbounded);
11089
+ const presentationText = toolResultPresentationText(result);
11090
+ const content = presentationText === null ? toolResultContent(result, unbounded) : [{ type: "text", text: unbounded ? presentationText : truncateReplayText(presentationText) }];
10863
11091
  const details = payloadObject3(result)?.details;
10864
11092
  return details !== null && details !== void 0 && typeof details === "object" ? { content, details } : content;
10865
11093
  }
@@ -10972,6 +11200,12 @@ function extractToolCall(entry) {
10972
11200
  const args = obj?.arguments ?? obj?.args ?? obj?.input ?? parseMaybeJson(fn?.arguments) ?? block?.arguments ?? block?.args ?? void 0;
10973
11201
  return { id, name, args };
10974
11202
  }
11203
+ function replayResultSummary(result) {
11204
+ const summary = result.resultSummary ?? toolResultSummary(result.result);
11205
+ const offloadPath = typeof summary.offloadPath === "string" && summary.offloadPath.length > 0 ? summary.offloadPath : null;
11206
+ if (offloadPath === null) return result.resultSummary;
11207
+ return { ...summary, offloadFileMissing: !existsSync6(offloadPath) };
11208
+ }
10975
11209
  function extractToolResult(entry) {
10976
11210
  const payload = entry.payload;
10977
11211
  const obj = payloadObject3(payload);
@@ -11091,12 +11325,6 @@ function truncateAtTurn(entries, uptoTurnId) {
11091
11325
  if (index < 0) return [...entries];
11092
11326
  return entries.slice(0, index + 1);
11093
11327
  }
11094
- function latestCompactionIndex(entries) {
11095
- for (let i = entries.length - 1; i >= 0; i--) {
11096
- if (entries[i]?.kind === "compactionSummary") return i;
11097
- }
11098
- return -1;
11099
- }
11100
11328
  function toolCallIdsInEntry(entry) {
11101
11329
  if (entry.kind !== "message") return [];
11102
11330
  if (entry.role === "tool_call") return [extractToolCall(entry).id];
@@ -11149,16 +11377,11 @@ function repairToolResultOrphans(allEntries, selected, compactionIndex) {
11149
11377
  function selectReplayEntries(turns, options = {}) {
11150
11378
  const active = filterEntriesToActivePath(turns, options.activeLeafTurnId ?? options.uptoTurnId);
11151
11379
  const entries = truncateAtTurn(active, options.uptoTurnId);
11152
- const compactionIndex = latestCompactionIndex(entries);
11153
- if (compactionIndex < 0) return dropLegacyToolResultAssistantDuplicates(entries);
11154
- const compaction = entries[compactionIndex];
11155
- const selected = [compaction];
11156
- const firstKeptIndex = compaction.firstKeptTurnId ? entries.findIndex((entry) => entry.turnId === compaction.firstKeptTurnId) : -1;
11157
- if (firstKeptIndex >= 0 && firstKeptIndex < compactionIndex) {
11158
- selected.push(...entries.slice(firstKeptIndex, compactionIndex));
11159
- }
11160
- selected.push(...entries.slice(compactionIndex + 1));
11161
- return dropLegacyToolResultAssistantDuplicates(repairToolResultOrphans(entries, selected, compactionIndex));
11380
+ const cut = compactionCut(entries);
11381
+ if (cut.compactionIndex < 0) return dropLegacyToolResultAssistantDuplicates(entries);
11382
+ const compaction = entries[cut.compactionIndex];
11383
+ const selected = [compaction, ...cut.visible];
11384
+ return dropLegacyToolResultAssistantDuplicates(repairToolResultOrphans(entries, selected, cut.compactionIndex));
11162
11385
  }
11163
11386
  function dropLegacyToolResultAssistantDuplicates(entries) {
11164
11387
  const out = [];
@@ -11253,6 +11476,11 @@ function buildReplayAgentMessagesFromTurns(turns, options = {}) {
11253
11476
  // It reaches the model only when an operator shares it, and a share
11254
11477
  // is already a user message by the time it lands in the ledger.
11255
11478
  case "workerRun":
11479
+ // Working-set entries are folded into a view by the context domain
11480
+ // and applied as a projection before this builder runs; the
11481
+ // entries themselves never become messages.
11482
+ case "contextEviction":
11483
+ case "contextRecall":
11256
11484
  break;
11257
11485
  }
11258
11486
  }
@@ -11262,6 +11490,7 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11262
11490
  const pendingToolIds = [];
11263
11491
  let runAssistantMessages = [];
11264
11492
  const selected = selectReplayEntries(turns, options);
11493
+ const workingSet = foldWorkingSet(turns, options.activeLeafTurnId ?? options.uptoTurnId);
11265
11494
  const workerStates = workerEntriesFromRunEntries(
11266
11495
  selected.filter((entry) => entry.kind === "workerRun"),
11267
11496
  options.readWorkerReceipt ?? readWorkerReceiptFactsForReplay
@@ -11306,6 +11535,8 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11306
11535
  }
11307
11536
  if (entry.role === "tool_result") {
11308
11537
  const result = extractToolResult(entry);
11538
+ const resultSummary = replayResultSummary(result);
11539
+ const evictedReason = workingSet.evicted.get(entry.turnId)?.reason;
11309
11540
  const fallbackId = result.id ?? pendingToolIds.pop() ?? null;
11310
11541
  if (fallbackId) {
11311
11542
  const pendingIndex = pendingToolIds.indexOf(fallbackId);
@@ -11317,9 +11548,10 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11317
11548
  result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
11318
11549
  isError: result.isError,
11319
11550
  ...result.durationMs !== void 0 ? { durationMs: result.durationMs } : {},
11320
- ...result.resultSummary !== void 0 ? { resultSummary: result.resultSummary } : {},
11551
+ ...resultSummary !== void 0 ? { resultSummary } : {},
11321
11552
  ...result.outcome !== void 0 ? { outcome: result.outcome } : {},
11322
- ...result.blockReason !== void 0 ? { blockReason: result.blockReason } : {}
11553
+ ...result.blockReason !== void 0 ? { blockReason: result.blockReason } : {},
11554
+ ...evictedReason !== void 0 ? { evictedReason } : {}
11323
11555
  });
11324
11556
  } else {
11325
11557
  chatPanel.appendReplayBlock(
@@ -11330,9 +11562,10 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11330
11562
  result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
11331
11563
  isError: result.isError,
11332
11564
  ...result.durationMs !== void 0 ? { durationMs: result.durationMs } : {},
11333
- ...result.resultSummary !== void 0 ? { resultSummary: result.resultSummary } : {},
11565
+ ...resultSummary !== void 0 ? { resultSummary } : {},
11334
11566
  ...result.outcome === "blocked" ? { outcome: "blocked" } : {},
11335
- ...result.blockReason !== void 0 ? { blockReason: result.blockReason } : {}
11567
+ ...result.blockReason !== void 0 ? { blockReason: result.blockReason } : {},
11568
+ ...evictedReason !== void 0 ? { evictedReason } : {}
11336
11569
  },
11337
11570
  width,
11338
11571
  { unbounded: options.unboundedToolBodies === true }
@@ -11417,6 +11650,8 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11417
11650
  case "label":
11418
11651
  case "taskLedger":
11419
11652
  case "decisionLedger":
11653
+ case "contextEviction":
11654
+ case "contextRecall":
11420
11655
  break;
11421
11656
  }
11422
11657
  }
@@ -11432,6 +11667,13 @@ function rehydrateChatPanelFromTurns(chatPanel, turns, options = {}) {
11432
11667
  chatPanel.clearFoldOverrides();
11433
11668
  }
11434
11669
 
11670
+ // src/interactive/model-session-replay.ts
11671
+ function buildModelReplayAgentMessagesFromTurns(entries, options = {}) {
11672
+ const activeLeafTurnId = options.activeLeafTurnId ?? options.uptoTurnId;
11673
+ const projected = projectWorkingSet(entries, foldWorkingSet(entries, activeLeafTurnId));
11674
+ return buildReplayAgentMessagesFromTurns(projected, options);
11675
+ }
11676
+
11435
11677
  // src/interactive/turn-context.ts
11436
11678
  function createTurnContext(deps) {
11437
11679
  const { state, middleware } = deps;
@@ -11444,20 +11686,37 @@ function createTurnContext(deps) {
11444
11686
  let sessionPromptKey = null;
11445
11687
  const sessionWorkingContextPaths = /* @__PURE__ */ new Set();
11446
11688
  let pendingPromptLogEntry = null;
11689
+ let emptyAutoCompactTurnId = null;
11447
11690
  const pendingColdReasons = /* @__PURE__ */ new Set();
11448
11691
  let runExpectedColdReasons = [];
11449
- let stampColdReasonsPending = false;
11692
+ let nextAssistantColdReasons = [];
11693
+ const TIER_INDEPENDENT_COLD_REASONS = /* @__PURE__ */ new Set(["working_set_evict"]);
11694
+ const stampsOnTier = (reason, runtimeId) => TIER_INDEPENDENT_COLD_REASONS.has(reason) || runtimeId !== void 0 && deps.providers.getRuntime(runtimeId)?.tier === "local-native";
11695
+ const noteColdReason = (reason) => {
11696
+ if (!state.streaming) {
11697
+ pendingColdReasons.add(reason);
11698
+ return;
11699
+ }
11700
+ if (!stampsOnTier(reason, state.runtime?.runtimeId)) return;
11701
+ if (!runExpectedColdReasons.includes(reason)) runExpectedColdReasons.push(reason);
11702
+ if (nextAssistantColdReasons.includes(reason)) return;
11703
+ nextAssistantColdReasons.push(reason);
11704
+ deps.emitNotice(`[context engine] backend prefix cache likely cold this turn: ${reason}`);
11705
+ };
11450
11706
  const unsubscribeColdReasonSources = [
11451
11707
  ...[BusChannels.DispatchStarted, BusChannels.DispatchCompleted, BusChannels.DispatchFailed].map(
11452
11708
  (channel) => deps.bus?.on(channel, () => {
11453
- pendingColdReasons.add("dispatch");
11709
+ noteColdReason("dispatch");
11454
11710
  }) ?? null
11455
11711
  ),
11456
11712
  ...[BusChannels.CompactionBegin, BusChannels.CompactionEnd].map(
11457
11713
  (channel) => deps.bus?.on(channel, () => {
11458
- pendingColdReasons.add("compaction");
11714
+ noteColdReason("compaction");
11459
11715
  }) ?? null
11460
- )
11716
+ ),
11717
+ deps.bus?.on(BusChannels.ContextRecalled, (payload) => {
11718
+ middleware.fireCompactionHook("working_set_recall", payload.trigger);
11719
+ }) ?? null
11461
11720
  ];
11462
11721
  let snapshotPersistPending = false;
11463
11722
  let lastContextWindowWarning = null;
@@ -11509,6 +11768,50 @@ function createTurnContext(deps) {
11509
11768
  }
11510
11769
  return ceilChars(snapshot.pendingUserInput.length);
11511
11770
  };
11771
+ const resolveWindowWithoutRuntime = () => {
11772
+ const settings = deps.getSettings();
11773
+ const targetId = settings.orchestrator?.target?.trim();
11774
+ const wireModelId = settings.orchestrator?.model?.trim();
11775
+ if (!targetId || !wireModelId) return null;
11776
+ const resolved = resolveRuntimeTarget(deps.providers, {
11777
+ targetId,
11778
+ wireModelId,
11779
+ requestedThinkingLevel: settings.orchestrator?.thinkingLevel ?? "off",
11780
+ use: "orchestrator",
11781
+ requireTools: false,
11782
+ requireOutputBudget: true
11783
+ });
11784
+ return resolved.ok ? resolved.target.contextWindowDetails : null;
11785
+ };
11786
+ const CONTEXT_WINDOW_SOURCES = /* @__PURE__ */ new Set([
11787
+ "catalog",
11788
+ "probe",
11789
+ "loaded",
11790
+ "target-override",
11791
+ "model-hint",
11792
+ "descriptor-default",
11793
+ "unknown"
11794
+ ]);
11795
+ const snapshotWindowSource = (snapshot) => CONTEXT_WINDOW_SOURCES.has(snapshot.contextWindowSource) ? snapshot.contextWindowSource : null;
11796
+ const windowWithoutRuntime = () => {
11797
+ const details = resolveWindowWithoutRuntime();
11798
+ if (details) {
11799
+ return {
11800
+ contextWindow: details.effectiveContextWindow,
11801
+ contextWindowSource: details.contextWindowSource,
11802
+ contextWindowSlots: details.contextWindowSlots
11803
+ };
11804
+ }
11805
+ const snapshot = currentContextSnapshot;
11806
+ if (snapshot && snapshot.effectiveContextWindow > 0) {
11807
+ return {
11808
+ contextWindow: snapshot.effectiveContextWindow,
11809
+ contextWindowSource: snapshotWindowSource(snapshot),
11810
+ contextWindowSlots: null
11811
+ };
11812
+ }
11813
+ return { contextWindow: 0, contextWindowSource: null, contextWindowSlots: null };
11814
+ };
11512
11815
  const liveContextEstimate = (agentRuntime, pendingUserText) => {
11513
11816
  const contextWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
11514
11817
  const estimateInput = {
@@ -11525,7 +11828,7 @@ function createTurnContext(deps) {
11525
11828
  };
11526
11829
  const refreshAgentMessagesFromSession = (agentRuntime) => {
11527
11830
  const refreshedEntries = deps.readSessionEntries?.() ?? [];
11528
- agentRuntime.agent.state.messages = buildReplayAgentMessagesFromTurns(refreshedEntries, {
11831
+ agentRuntime.agent.state.messages = buildModelReplayAgentMessagesFromTurns(refreshedEntries, {
11529
11832
  ...state.lastTurnId ? { activeLeafTurnId: state.lastTurnId } : {}
11530
11833
  });
11531
11834
  state.replayedContextMessages = [];
@@ -11563,11 +11866,17 @@ function createTurnContext(deps) {
11563
11866
  );
11564
11867
  };
11565
11868
  const runAutoCompact = async (agentRuntime, force, instructions, triggerOverride, pendingUserText) => {
11566
- if (!deps.autoCompact || !deps.readSessionEntries) return false;
11869
+ if (!deps.readSessionEntries) return false;
11870
+ const activeAutoTurnId = force ? null : state.activeUserTurnId;
11871
+ if (activeAutoTurnId && emptyAutoCompactTurnId === activeAutoTurnId) return false;
11567
11872
  const settings = deps.getSettings();
11568
11873
  const cfg = settings.compaction;
11569
11874
  const autoEnabled = cfg?.auto !== false;
11570
11875
  if (!force && !autoEnabled) return false;
11876
+ let preSummaryStageActed = false;
11877
+ const rememberEmptyAutomaticAttempt = () => {
11878
+ if (!force && !preSummaryStageActed && activeAutoTurnId) emptyAutoCompactTurnId = activeAutoTurnId;
11879
+ };
11571
11880
  const compactionThreshold = cfg?.threshold ?? DEFAULT_COMPACTION_THRESHOLD;
11572
11881
  const trigger = triggerOverride ?? (force ? "force" : "auto");
11573
11882
  if (!force) {
@@ -11576,78 +11885,176 @@ function createTurnContext(deps) {
11576
11885
  if (!verdict.shouldCompact) return false;
11577
11886
  if (deps.session?.current()) {
11578
11887
  const beforeSnapshotId2 = currentContextSnapshot?.snapshotId ?? null;
11579
- let masked;
11580
- try {
11581
- masked = maskStaleObservations(deps.readSessionEntries() ?? [], cfg?.excludeLastTurns ?? 6);
11582
- } catch (error) {
11583
- middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
11584
- deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
11585
- emitCompactionActivity("started", "compacting context (mask stage)");
11586
- emitCompactionActivity("failed", compactionFailureMessage(error));
11587
- deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
11588
- throw error;
11589
- }
11590
- if (masked.changed) {
11591
- middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
11592
- deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
11593
- emitCompactionActivity("started", "compacting context (mask stage)");
11594
- deps.session.replaceEntries(masked.entries);
11595
- refreshAgentMessagesFromSession(agentRuntime);
11596
- deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
11597
- const postMaskSnapshot = captureRuntimeContextSnapshot(
11598
- agentRuntime,
11599
- state.activeUserTurnId || "compaction",
11600
- compactionThreshold
11601
- );
11602
- currentContextSnapshot = postMaskSnapshot;
11603
- persistContextSnapshot(postMaskSnapshot);
11604
- const tokensAfterMask = snapshotInputTokens(postMaskSnapshot);
11605
- lastCompactionEvent = {
11606
- stage: "mask_observations",
11607
- tokensBefore: estimate.tokens,
11608
- tokensAfter: tokensAfterMask,
11609
- trigger
11610
- };
11611
- deps.bus?.emit(BusChannels.ContextPruned, {
11612
- stage: "mask_observations",
11613
- pressure: verdict.pressure,
11614
- tokensBefore: estimate.tokens,
11615
- tokensAfter: tokensAfterMask,
11616
- maskedObservations: masked.maskedObservations,
11617
- maskedThinkingBlocks: masked.maskedThinkingBlocks,
11618
- maskedThinkingChars: masked.maskedThinkingChars,
11619
- trigger,
11620
- snapshotIdBefore: beforeSnapshotId2,
11621
- snapshotIdAfter: postMaskSnapshot.snapshotId,
11622
- at: Date.now()
11623
- });
11624
- emitCompactionActivity("completed", `${masked.maskedObservations} observations masked`);
11625
- const thinkingNote = masked.maskedThinkingBlocks > 0 ? `, ${masked.maskedThinkingBlocks} thinking blocks dropped (~${masked.maskedThinkingChars} chars)` : "";
11626
- deps.emitNotice(
11627
- `[context engine] mask_observations: ${masked.maskedObservations} observations masked${thinkingNote}; ~${estimate.tokens} tokens -> ~${tokensAfterMask} tokens`
11628
- );
11629
- const after = liveContextEstimate(agentRuntime, pendingUserText);
11630
- if (!shouldCompact(after.tokens, compactionThreshold, after.contextWindow).shouldCompact) return true;
11888
+ if (process.env.CLIO_CODER_LEGACY_MASK === "1") {
11889
+ let masked;
11890
+ try {
11891
+ masked = maskStaleObservations(deps.readSessionEntries() ?? [], cfg?.excludeLastTurns ?? 6);
11892
+ } catch (error) {
11893
+ middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
11894
+ deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
11895
+ emitCompactionActivity("started", "compacting context (mask stage)");
11896
+ emitCompactionActivity("failed", compactionFailureMessage(error));
11897
+ deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
11898
+ throw error;
11899
+ }
11900
+ if (masked.changed) {
11901
+ preSummaryStageActed = true;
11902
+ middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
11903
+ deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
11904
+ emitCompactionActivity("started", "compacting context (mask stage)");
11905
+ deps.session.replaceEntries(masked.entries);
11906
+ refreshAgentMessagesFromSession(agentRuntime);
11907
+ deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
11908
+ const postMaskSnapshot = captureRuntimeContextSnapshot(
11909
+ agentRuntime,
11910
+ state.activeUserTurnId || "compaction",
11911
+ compactionThreshold
11912
+ );
11913
+ currentContextSnapshot = postMaskSnapshot;
11914
+ persistContextSnapshot(postMaskSnapshot);
11915
+ const tokensAfterMask = snapshotInputTokens(postMaskSnapshot);
11916
+ lastCompactionEvent = {
11917
+ stage: "mask_observations",
11918
+ tokensBefore: estimate.tokens,
11919
+ tokensAfter: tokensAfterMask,
11920
+ trigger
11921
+ };
11922
+ deps.bus?.emit(BusChannels.ContextPruned, {
11923
+ stage: "mask_observations",
11924
+ pressure: verdict.pressure,
11925
+ tokensBefore: estimate.tokens,
11926
+ tokensAfter: tokensAfterMask,
11927
+ maskedObservations: masked.maskedObservations,
11928
+ maskedThinkingBlocks: masked.maskedThinkingBlocks,
11929
+ maskedThinkingChars: masked.maskedThinkingChars,
11930
+ trigger,
11931
+ snapshotIdBefore: beforeSnapshotId2,
11932
+ snapshotIdAfter: postMaskSnapshot.snapshotId,
11933
+ at: Date.now()
11934
+ });
11935
+ emitCompactionActivity("completed", `${masked.maskedObservations} observations masked`);
11936
+ const thinkingNote = masked.maskedThinkingBlocks > 0 ? `, ${masked.maskedThinkingBlocks} thinking blocks dropped (~${masked.maskedThinkingChars} chars)` : "";
11937
+ deps.emitNotice(
11938
+ `[context engine] mask_observations: ${masked.maskedObservations} observations masked${thinkingNote}; ~${estimate.tokens} tokens -> ~${tokensAfterMask} tokens`
11939
+ );
11940
+ const after = liveContextEstimate(agentRuntime, pendingUserText);
11941
+ if (!shouldCompact(after.tokens, compactionThreshold, after.contextWindow).shouldCompact) return true;
11942
+ }
11943
+ } else if (settings.context.workingSet.enabled) {
11944
+ let planned;
11945
+ try {
11946
+ const entries = deps.readSessionEntries() ?? [];
11947
+ const view = foldWorkingSet(entries, state.lastTurnId ?? void 0);
11948
+ const policy = resolveWorkingSetPolicy(settings.context.workingSet.policy);
11949
+ planned = (deps.planEviction ?? planEviction)(policy, {
11950
+ entries: selectVisibleEntries(entries, state.lastTurnId ?? void 0),
11951
+ view,
11952
+ cwd: deps.session.current()?.cwd ?? null,
11953
+ settings: settings.context.workingSet,
11954
+ pressure: {
11955
+ tokens: estimate.tokens,
11956
+ contextWindow: estimate.contextWindow,
11957
+ threshold: compactionThreshold,
11958
+ target: settings.context.workingSet.target
11959
+ },
11960
+ estimateTokens
11961
+ });
11962
+ } catch (error) {
11963
+ middleware.fireCompactionHook("working_set_evict", "pressure", estimate.tokens);
11964
+ emitCompactionActivity("started", "compacting context (working-set eviction)");
11965
+ emitCompactionActivity("failed", compactionFailureMessage(error));
11966
+ throw error;
11967
+ }
11968
+ if (planned) {
11969
+ preSummaryStageActed = true;
11970
+ middleware.fireCompactionHook("working_set_evict", "pressure", estimate.tokens);
11971
+ emitCompactionActivity("started", "compacting context (working-set eviction)");
11972
+ try {
11973
+ deps.session.appendEntry({
11974
+ ...buildEvictionFields(planned, {
11975
+ trigger: "pressure",
11976
+ pressureBefore: verdict.pressure,
11977
+ snapshotIdBefore: beforeSnapshotId2
11978
+ }),
11979
+ // appendEntry does not infer this anchor; the interactive
11980
+ // cursor is the leaf the next message will extend.
11981
+ parentTurnId: state.lastTurnId
11982
+ });
11983
+ noteColdReason("working_set_evict");
11984
+ refreshAgentMessagesFromSession(agentRuntime);
11985
+ const postEvictionSnapshot = captureRuntimeContextSnapshot(
11986
+ agentRuntime,
11987
+ state.activeUserTurnId || "compaction",
11988
+ compactionThreshold
11989
+ );
11990
+ currentContextSnapshot = postEvictionSnapshot;
11991
+ persistContextSnapshot(postEvictionSnapshot);
11992
+ lastCompactionEvent = {
11993
+ stage: "working_set",
11994
+ tokensBefore: planned.tokensBefore,
11995
+ tokensAfter: planned.tokensAfter,
11996
+ trigger
11997
+ };
11998
+ deps.bus?.emit(BusChannels.ContextPruned, {
11999
+ stage: "working_set",
12000
+ pressure: verdict.pressure,
12001
+ tokensBefore: planned.tokensBefore,
12002
+ tokensAfter: planned.tokensAfter,
12003
+ trigger,
12004
+ snapshotIdBefore: beforeSnapshotId2,
12005
+ snapshotIdAfter: postEvictionSnapshot.snapshotId,
12006
+ policyId: planned.policyId,
12007
+ evictedItems: planned.items.length,
12008
+ at: Date.now()
12009
+ });
12010
+ const itemsWord = planned.items.length === 1 ? "item" : "items";
12011
+ emitCompactionActivity("completed", `${planned.items.length} working-set ${itemsWord} evicted`);
12012
+ deps.emitNotice(
12013
+ `[context engine] working set: ${planned.items.length} ${itemsWord} evicted by ${planned.policyId}; ~${planned.tokensBefore} -> ~${planned.tokensAfter} tokens, recall by ref with context(scope="recall")`
12014
+ );
12015
+ const after = liveContextEstimate(agentRuntime, pendingUserText);
12016
+ if (!shouldCompact(after.tokens, compactionThreshold, after.contextWindow).shouldCompact) return true;
12017
+ } catch (error) {
12018
+ emitCompactionActivity("failed", compactionFailureMessage(error));
12019
+ throw error;
12020
+ }
12021
+ }
11631
12022
  }
11632
12023
  }
11633
12024
  }
11634
- middleware.fireCompactionHook("llm_summary", trigger);
11635
- deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
11636
- emitCompactionActivity("started", "compacting context (summary)");
12025
+ if (!deps.autoCompact) {
12026
+ rememberEmptyAutomaticAttempt();
12027
+ return false;
12028
+ }
12029
+ let summaryLifecycleStarted = false;
12030
+ const startSummaryLifecycle = () => {
12031
+ middleware.fireCompactionHook("llm_summary", trigger);
12032
+ deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
12033
+ emitCompactionActivity("started", "compacting context (summary)");
12034
+ summaryLifecycleStarted = true;
12035
+ };
12036
+ if (force) startSummaryLifecycle();
11637
12037
  let result = null;
11638
12038
  const beforeSnapshotId = currentContextSnapshot?.snapshotId ?? null;
11639
12039
  try {
11640
12040
  result = await compactionTrigger.fire(() => (deps.autoCompact ?? (async () => null))(instructions, trigger));
11641
12041
  } catch (error) {
12042
+ if (!summaryLifecycleStarted) startSummaryLifecycle();
11642
12043
  emitCompactionActivity("failed", compactionFailureMessage(error));
11643
- throw error;
11644
- } finally {
11645
12044
  deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
12045
+ throw error;
11646
12046
  }
11647
12047
  if (!result || result.summary.length === 0) {
11648
- emitCompactionActivity("completed", "nothing to compact");
12048
+ if (summaryLifecycleStarted) {
12049
+ deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
12050
+ emitCompactionActivity("completed", "nothing to compact");
12051
+ } else {
12052
+ rememberEmptyAutomaticAttempt();
12053
+ }
11649
12054
  return false;
11650
12055
  }
12056
+ if (!summaryLifecycleStarted) startSummaryLifecycle();
12057
+ deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
11651
12058
  recordCompactionUsage(agentRuntime, result);
11652
12059
  refreshAgentMessagesFromSession(agentRuntime);
11653
12060
  const postCompactSnapshot = captureRuntimeContextSnapshot(
@@ -11861,8 +12268,7 @@ function createTurnContext(deps) {
11861
12268
  return compacted ? continuationContextUpdate(agentRuntime) : void 0;
11862
12269
  },
11863
12270
  contextUsage() {
11864
- if (!state.runtime) return contextUsageSnapshot(null, 0);
11865
- const effectiveWindow = state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
12271
+ const effectiveWindow = state.runtime ? state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow : windowWithoutRuntime().contextWindow;
11866
12272
  if (!currentContextSnapshot) {
11867
12273
  return contextUsageSnapshot(null, effectiveWindow);
11868
12274
  }
@@ -11880,26 +12286,20 @@ function createTurnContext(deps) {
11880
12286
  const settings = deps.getSettings();
11881
12287
  const compactionThreshold = settings.compaction?.threshold ?? null;
11882
12288
  const compactionAuto = settings.compaction?.auto !== false;
11883
- if (!state.runtime) {
11884
- return buildContextLedger({
11885
- provider: settings.orchestrator?.target ?? null,
11886
- model: settings.orchestrator?.model ?? null,
11887
- contextWindow: 0,
11888
- compactionThreshold,
11889
- compactionAuto
11890
- });
11891
- }
11892
- const windowDetails = state.runtime.runtimeResolution.contextWindowDetails;
11893
- const effectiveWindow = windowDetails.effectiveContextWindow;
11894
- const provider = state.runtime.targetId ?? settings.orchestrator?.target ?? null;
11895
- const model = state.runtime.wireModelId ?? settings.orchestrator?.model ?? null;
12289
+ const window = state.runtime ? {
12290
+ contextWindow: state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow,
12291
+ contextWindowSource: state.runtime.runtimeResolution.contextWindowDetails.contextWindowSource,
12292
+ contextWindowSlots: state.runtime.runtimeResolution.contextWindowDetails.contextWindowSlots
12293
+ } : windowWithoutRuntime();
12294
+ const provider = state.runtime?.targetId ?? settings.orchestrator?.target ?? null;
12295
+ const model = state.runtime?.wireModelId ?? settings.orchestrator?.model ?? null;
12296
+ const liveToolCount = state.runtime?.agent.state.tools.length ?? 0;
11896
12297
  if (!currentContextSnapshot) {
11897
12298
  return buildContextLedger({
11898
12299
  provider,
11899
12300
  model,
11900
- contextWindow: effectiveWindow,
11901
- contextWindowSource: windowDetails.contextWindowSource,
11902
- toolCount: state.runtime.agent.state.tools.length,
12301
+ ...window,
12302
+ toolCount: liveToolCount,
11903
12303
  compactionThreshold,
11904
12304
  compactionAuto,
11905
12305
  promptCache: lastPromptCache
@@ -11912,15 +12312,14 @@ function createTurnContext(deps) {
11912
12312
  return buildContextLedger({
11913
12313
  provider,
11914
12314
  model,
11915
- contextWindow: effectiveWindow,
11916
- contextWindowSource: windowDetails.contextWindowSource,
12315
+ ...window,
11917
12316
  compactionThreshold,
11918
12317
  compactionAuto,
11919
12318
  systemPromptTokens: currentContextSnapshot.categories.system,
11920
12319
  toolSchemaTokens: currentContextSnapshot.categories.tools,
11921
12320
  // Persisted snapshots strip the captured schemas; fall back to
11922
12321
  // the live agent state after a session resume.
11923
- toolCount: currentContextSnapshot.activeToolSchemas?.length ?? state.runtime.agent.state.tools.length,
12322
+ toolCount: currentContextSnapshot.activeToolSchemas?.length ?? liveToolCount,
11924
12323
  messageTokens: currentContextSnapshot.categories.messages,
11925
12324
  agentsTokens: currentContextSnapshot.categories.agents,
11926
12325
  skillsTokens: currentContextSnapshot.categories.skills,
@@ -11943,13 +12342,13 @@ function createTurnContext(deps) {
11943
12342
  },
11944
12343
  consumeExpectedColdReasons(runtimeId) {
11945
12344
  runExpectedColdReasons = [];
11946
- stampColdReasonsPending = false;
12345
+ nextAssistantColdReasons = [];
11947
12346
  if (pendingColdReasons.size > 0) {
11948
- const reasons = [...pendingColdReasons];
12347
+ const reasons = [...pendingColdReasons].filter((reason) => stampsOnTier(reason, runtimeId));
11949
12348
  pendingColdReasons.clear();
11950
- if (deps.providers.getRuntime(runtimeId)?.tier === "local-native") {
12349
+ if (reasons.length > 0) {
11951
12350
  runExpectedColdReasons = reasons;
11952
- stampColdReasonsPending = true;
12351
+ nextAssistantColdReasons = reasons;
11953
12352
  deps.emitNotice(`[context engine] backend prefix cache likely cold this turn: ${reasons.join(", ")}`);
11954
12353
  }
11955
12354
  }
@@ -11964,9 +12363,9 @@ function createTurnContext(deps) {
11964
12363
  cacheWrite,
11965
12364
  backendVerdict: backendCacheVerdict(input, cacheRead)
11966
12365
  };
11967
- if (stampColdReasonsPending && runExpectedColdReasons.length > 0) {
11968
- promptCache.expectedColdReasons = [...runExpectedColdReasons];
11969
- stampColdReasonsPending = false;
12366
+ if (nextAssistantColdReasons.length > 0) {
12367
+ promptCache.expectedColdReasons = [...nextAssistantColdReasons];
12368
+ nextAssistantColdReasons = [];
11970
12369
  }
11971
12370
  return promptCache;
11972
12371
  },
@@ -11978,7 +12377,8 @@ function createTurnContext(deps) {
11978
12377
  cacheReadTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheRead : null,
11979
12378
  cacheWriteTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheWrite : null,
11980
12379
  uncachedInputTokens: cacheSummary.input,
11981
- backendVerdict: runFirstCallVerdict
12380
+ backendVerdict: runFirstCallVerdict,
12381
+ ...runExpectedColdReasons.length > 0 ? { expectedColdReasons: [...runExpectedColdReasons] } : {}
11982
12382
  };
11983
12383
  }
11984
12384
  },
@@ -11988,6 +12388,7 @@ function createTurnContext(deps) {
11988
12388
  sessionPromptKey = null;
11989
12389
  sessionWorkingContextPaths.clear();
11990
12390
  pendingPromptLogEntry = null;
12391
+ emptyAutoCompactTurnId = null;
11991
12392
  const session = deps.session?.current();
11992
12393
  currentContextSnapshot = session ? getLatestContextSnapshot(session) : null;
11993
12394
  },
@@ -12110,6 +12511,7 @@ function createTurnMiddleware(deps) {
12110
12511
  runtimeTier: agentRuntime.runtimeResolution.runtimeTier ?? "",
12111
12512
  activeToolNames: toolNamesFromAgentState(agentRuntime.agent.state.tools).join(","),
12112
12513
  turnToolCalls: state.turnToolCalls,
12514
+ turnToolNames: state.turnToolNames.join(","),
12113
12515
  sharedWorkerNote: state.turnSharedWorkerNote
12114
12516
  };
12115
12517
  if (terminalToolResult !== void 0) {
@@ -12141,9 +12543,9 @@ function createTurnMiddleware(deps) {
12141
12543
  }
12142
12544
  },
12143
12545
  /**
12144
- * Observe-only lifecycle point fired before each compaction stage, at the
12145
- * existing CompactionBegin emit sites. Consumers record telemetry or state
12146
- * ahead of context loss; returned effects are discarded by design.
12546
+ * Observe-only lifecycle point fired before each compaction or working-set
12547
+ * stage. Consumers record telemetry or state around projected context
12548
+ * changes; returned effects are discarded by design.
12147
12549
  */
12148
12550
  fireCompactionHook(stage, trigger, tokensBefore) {
12149
12551
  if (!deps.middleware) return;
@@ -12794,8 +13196,8 @@ function count(pattern, text) {
12794
13196
  while (pattern.exec(text)) matches += 1;
12795
13197
  return matches;
12796
13198
  }
12797
- function toolPattern(toolName2) {
12798
- const escaped = escapeRegExp(toolName2.trim().toLowerCase());
13199
+ function toolPattern(toolName) {
13200
+ const escaped = escapeRegExp(toolName.trim().toLowerCase());
12799
13201
  return escaped.replace(/[_-]+/g, "[ _-]+");
12800
13202
  }
12801
13203
  function normalizedToolNames(names) {
@@ -12817,8 +13219,8 @@ function assessToolProseLoop(input) {
12817
13219
  const text = input.text.toLowerCase().replace(/\s+/g, " ");
12818
13220
  let maxToolMatches = 0;
12819
13221
  let matchedTool = "";
12820
- for (const toolName2 of tools) {
12821
- const name = toolPattern(toolName2);
13222
+ for (const toolName of tools) {
13223
+ const name = toolPattern(toolName);
12822
13224
  const patterns = [
12823
13225
  new RegExp(`\\b(?:execute|call|make|use)\\s+the\\s+${name}\\s+tool\\s+call\\b`, "g"),
12824
13226
  new RegExp(`\\b(?:execute|call|use)\\s+the\\s+${name}\\s+tool\\b`, "g"),
@@ -12827,7 +13229,7 @@ function assessToolProseLoop(input) {
12827
13229
  const matches = patterns.reduce((sum, pattern) => sum + count(pattern, text), 0);
12828
13230
  if (matches > maxToolMatches) {
12829
13231
  maxToolMatches = matches;
12830
- matchedTool = toolName2;
13232
+ matchedTool = toolName;
12831
13233
  }
12832
13234
  }
12833
13235
  const genericMatches = count(
@@ -12902,7 +13304,10 @@ function createTurnRuntime(deps) {
12902
13304
  failure?.code ?? "admission-failed"
12903
13305
  );
12904
13306
  }
12905
- for (const message of runtimeResolutionWarnings(resolved.diagnostics)) {
13307
+ for (const message of runtimeResolutionWarningsBesideThinkingNotice(
13308
+ resolved.diagnostics,
13309
+ resolved.target.modelRuntime.thinking.notice
13310
+ )) {
12906
13311
  const key = `${targetId}|${wireModelId}|${message}`;
12907
13312
  if (announcedResolutionWarnings.has(key)) continue;
12908
13313
  announcedResolutionWarnings.add(key);
@@ -12917,6 +13322,7 @@ function createTurnRuntime(deps) {
12917
13322
  };
12918
13323
  const TARGET_PROBE_TTL_MS = 5 * 60 * 1e3;
12919
13324
  let lastTargetProbe = null;
13325
+ const targetProbesInFlight = /* @__PURE__ */ new Map();
12920
13326
  const ensureLiveCapabilitiesForSelectedModel = async () => {
12921
13327
  const settings = deps.getSettings();
12922
13328
  const targetId = settings.orchestrator.target?.trim();
@@ -12927,15 +13333,28 @@ function createTurnRuntime(deps) {
12927
13333
  const runtimeDesc = deps.providers.getRuntime(target.runtime);
12928
13334
  if (!runtimeDesc || runtimeDesc.tier === "cloud") return;
12929
13335
  const key = `${targetId}|${wireModelId}`;
13336
+ const inFlight = targetProbesInFlight.get(key);
13337
+ if (inFlight) {
13338
+ await inFlight;
13339
+ return;
13340
+ }
12930
13341
  const now = Date.now();
12931
13342
  if (lastTargetProbe?.key === key && now - lastTargetProbe.at < TARGET_PROBE_TTL_MS) return;
12932
13343
  lastTargetProbe = { key, at: now };
12933
- let status = null;
13344
+ const probe = (async () => {
13345
+ let status = null;
13346
+ try {
13347
+ status = await deps.providers.probeTarget(targetId);
13348
+ } catch {
13349
+ }
13350
+ announceColdModel(status, targetId, wireModelId);
13351
+ })();
13352
+ targetProbesInFlight.set(key, probe);
12934
13353
  try {
12935
- status = await deps.providers.probeTarget(targetId);
12936
- } catch {
13354
+ await probe;
13355
+ } finally {
13356
+ if (targetProbesInFlight.get(key) === probe) targetProbesInFlight.delete(key);
12937
13357
  }
12938
- announceColdModel(status, targetId, wireModelId);
12939
13358
  };
12940
13359
  const announcedColdModels = /* @__PURE__ */ new Set();
12941
13360
  const announceColdModel = (status, targetId, wireModelId) => {
@@ -13154,6 +13573,7 @@ function createTurnRuntime(deps) {
13154
13573
  middlewareToolChoice.toolStarted(event.toolName);
13155
13574
  deps.toolStartTimes.set(event.toolCallId, eventClock);
13156
13575
  state.turnToolCalls += 1;
13576
+ state.turnToolNames.push(event.toolName);
13157
13577
  } else if (event.type === "tool_execution_end") {
13158
13578
  toolsInFlight = Math.max(0, toolsInFlight - 1);
13159
13579
  const startedAt = deps.toolStartTimes.get(event.toolCallId);
@@ -13235,7 +13655,11 @@ function createTurnRuntime(deps) {
13235
13655
  if (summary.hadUsage && (summary.tokens > 0 || summary.costUsd > 0)) {
13236
13656
  deps.observability.recordTokens(
13237
13657
  localRuntime.targetId,
13238
- localRuntime.wireModelId,
13658
+ attributedModelId(
13659
+ summary.lastResponseModelIdObservation,
13660
+ localRuntime.wireModelId,
13661
+ summary.lastDifferingResponseModelId
13662
+ ),
13239
13663
  summary.tokens,
13240
13664
  summary.costUsd,
13241
13665
  {
@@ -13247,7 +13671,11 @@ function createTurnRuntime(deps) {
13247
13671
  totalTokens: summary.tokens,
13248
13672
  apiCalls: summary.apiCalls
13249
13673
  },
13250
- localRuntime.runtimeResolution.costProvenance
13674
+ localRuntime.runtimeResolution.costProvenance,
13675
+ {
13676
+ requestedModelIds: [localRuntime.wireModelId],
13677
+ responseModelIdObservationCounts: summary.responseModelIdObservationCounts
13678
+ }
13251
13679
  );
13252
13680
  }
13253
13681
  if (summary.output > 0 && firstAssistantDeltaAt !== null) {
@@ -13372,6 +13800,7 @@ function createTurnState(initialThinkingLevel) {
13372
13800
  streamStallReason: null,
13373
13801
  toolProseAssessedChars: 0,
13374
13802
  turnToolCalls: 0,
13803
+ turnToolNames: [],
13375
13804
  turnSharedWorkerNote: false,
13376
13805
  stalledTurnNudgeSpent: false,
13377
13806
  pendingRequestContinuation: false,
@@ -13550,6 +13979,8 @@ function createChatLoop(deps) {
13550
13979
  emitNotice,
13551
13980
  toolStartTimes
13552
13981
  });
13982
+ void turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {
13983
+ });
13553
13984
  const unsubscribeConfigReload = deps.bus?.on(BusChannels.ConfigHotReload, () => {
13554
13985
  context.invalidateSessionPromptCache();
13555
13986
  }) ?? null;
@@ -13631,6 +14062,7 @@ function createChatLoop(deps) {
13631
14062
  return;
13632
14063
  }
13633
14064
  state.turnToolCalls = 0;
14065
+ state.turnToolNames = [];
13634
14066
  state.turnSharedWorkerNote = isWorkerShareNote(text);
13635
14067
  middlewareToolChoice.reset();
13636
14068
  if (options.requestContinuation !== true) state.stalledTurnNudgeSpent = false;
@@ -13869,6 +14301,8 @@ function createChatLoop(deps) {
13869
14301
  middlewareToolChoice.reset();
13870
14302
  state.lastTurnId = leafTurnId;
13871
14303
  context.resetForSession();
14304
+ void turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {
14305
+ });
13872
14306
  state.replayedContextMessages = replayMessages ? [...replayMessages] : [];
13873
14307
  if (state.runtime) {
13874
14308
  state.runtime.agent.state.messages = [...state.replayedContextMessages];
@@ -14031,264 +14465,6 @@ init_esm_shims();
14031
14465
  // src/interactive/overlays/ask-user.ts
14032
14466
  init_esm_shims();
14033
14467
 
14034
- // src/interactive/overlay-frame.ts
14035
- init_esm_shims();
14036
- var DEFAULT_SELECT_THEME = selectListTheme(clioTheme());
14037
- var DEFAULT_SETTINGS_THEME = settingsListTheme(clioTheme());
14038
- var FocusBox = class extends Box {
14039
- inputTarget;
14040
- onInput;
14041
- constructor(children, options) {
14042
- super(options?.x ?? 1, options?.y ?? 0);
14043
- const childList = Array.isArray(children) ? children : [children];
14044
- for (const child of childList) this.addChild(child);
14045
- this.inputTarget = options?.inputTarget === void 0 ? childList[0] ?? null : options.inputTarget;
14046
- this.onInput = options?.onInput;
14047
- }
14048
- handleInput(data) {
14049
- if (this.onInput) {
14050
- this.onInput(data);
14051
- return;
14052
- }
14053
- this.inputTarget?.handleInput?.(data);
14054
- }
14055
- };
14056
- function resolveTone(tone) {
14057
- return typeof tone === "function" ? tone() : tone;
14058
- }
14059
- function clioFrame(text, token = "frame") {
14060
- return clioTheme().fg(token, text);
14061
- }
14062
- function clioTitle(text, token) {
14063
- return token === void 0 ? screenTitle(clioTheme(), text) : clioTheme().style(token, text, { bold: true });
14064
- }
14065
- function clioError(text) {
14066
- return clioTheme().fg("error", text);
14067
- }
14068
- function brandedTopBorder(label, innerWidth, tone) {
14069
- const frame2 = (text) => clioFrame(text, tone ?? "frame");
14070
- const clean = label.replace(/^[┌┐└┘├┤─│\s]+/, "").replace(/[┌┐└┘├┤─│\s]+$/, "");
14071
- const formatted = clean.length > 0 ? `\u2500 ${clean} ` : "\u2500";
14072
- const clipped = visibleWidth(formatted) > innerWidth ? truncateToWidth(formatted, innerWidth, "\u2026", true) : formatted;
14073
- const fill = "\u2500".repeat(Math.max(0, innerWidth - visibleWidth(clipped)));
14074
- const cleanIndex = clipped.indexOf(clean);
14075
- if (cleanIndex !== -1) {
14076
- const prefix = clipped.slice(0, cleanIndex);
14077
- const suffix = clipped.slice(cleanIndex + clean.length);
14078
- return `${frame2("\u250C")}${frame2(prefix)}${clioTitle(clean, tone)}${frame2(suffix)}${frame2(fill)}${frame2("\u2510")}`;
14079
- }
14080
- return `${frame2("\u250C")}${frame2(clipped)}${frame2(fill)}${frame2("\u2510")}`;
14081
- }
14082
- function isCriticalHintKey(key) {
14083
- const canonical = canonicalizeKey(key);
14084
- return canonical === "Esc" || canonical === "Enter" || canonical === "Enter/Space" || canonical === "type";
14085
- }
14086
- function isCriticalEntry(entry) {
14087
- return entry.critical ?? isCriticalHintKey(entry.key);
14088
- }
14089
- function renderHintEntries(entries, short) {
14090
- return entries.map((entry) => `[${canonicalizeKey(entry.key)}] ${short ? entry.short ?? entry.verb : entry.verb}`).join(" \xB7 ");
14091
- }
14092
- function fitHintEntries(entries, maxWidth) {
14093
- if (entries.length === 0) return "";
14094
- for (const short of [false, true]) {
14095
- const rendered = renderHintEntries(entries, short);
14096
- if (visibleWidth(rendered) <= maxWidth) return rendered;
14097
- }
14098
- const kept = [...entries];
14099
- for (const criticalPass of [false, true]) {
14100
- while (kept.length > 1) {
14101
- const index = kept.findIndex((entry) => criticalPass || !isCriticalEntry(entry));
14102
- if (index === -1) break;
14103
- kept.splice(index, 1);
14104
- const rendered = renderHintEntries(kept, true);
14105
- if (visibleWidth(rendered) <= maxWidth) return rendered;
14106
- }
14107
- }
14108
- return renderHintEntries(kept, true);
14109
- }
14110
- var FILTER_HINT = { key: "type", verb: "filter" };
14111
- function canonicalizeKey(key) {
14112
- const trimmed = key.trim();
14113
- if (trimmed === "R") return "R";
14114
- const lower = trimmed.toLowerCase();
14115
- if (lower === "up/down" || lower === "updown" || lower === "\u2191/\u2193" || lower === "up/down/j/k" || lower === "\u2191\u2193")
14116
- return "\u2191\u2193";
14117
- if (lower === "enter/space") return "Enter/Space";
14118
- if (lower === "enter") return "Enter";
14119
- if (lower === "esc" || lower === "escape") return "Esc";
14120
- if (lower === "space") return "Space";
14121
- if (lower === "tab") return "Tab";
14122
- if (lower === "r") return "r";
14123
- if (lower === "type") return "type";
14124
- return trimmed;
14125
- }
14126
- function buildHint(entries, esc = "close") {
14127
- const finalEntries = [
14128
- ...entries.map((e) => ({ key: canonicalizeKey(e.key), verb: e.verb })),
14129
- { key: "Esc", verb: esc }
14130
- ];
14131
- return finalEntries.map((e) => `[${e.key}] ${e.verb}`).join(" \xB7 ");
14132
- }
14133
- function buildResponsiveHint(entries, esc = "close") {
14134
- const escEntry = esc === null ? null : typeof esc === "string" ? { key: "Esc", verb: esc } : esc;
14135
- const all = escEntry ? [...entries, escEntry] : [...entries];
14136
- return (innerWidth) => fitHintEntries(all, innerWidth - 3);
14137
- }
14138
- function elideHint(hint, maxCleanWidth) {
14139
- const parts = hint.split(" \xB7 ");
14140
- if (parts.length <= 2) return hint;
14141
- const entries = parts.map((part) => {
14142
- const match = /^\[([^\]]+)\] ([\s\S]+)$/u.exec(part);
14143
- if (!match) return { key: "", verb: part, critical: false };
14144
- return { key: match[1] ?? "", verb: match[2] ?? "" };
14145
- });
14146
- const rendered = fitHintEntries(entries, maxCleanWidth);
14147
- return rendered.replace(/\[\] /gu, "");
14148
- }
14149
- function brandedBottomBorder(innerWidth, hint, tone) {
14150
- const frame2 = (text) => clioFrame(text, tone ?? "frame");
14151
- if (!hint || hint.trim().length === 0) {
14152
- return `${frame2("\u2514")}${frame2("\u2500".repeat(innerWidth))}${frame2("\u2518")}`;
14153
- }
14154
- let clean = hint.trim();
14155
- const maxCleanWidth = innerWidth - 3;
14156
- if (clean.includes(" \xB7 ") && visibleWidth(`\u2500 ${clean} `) > innerWidth) {
14157
- clean = elideHint(clean, maxCleanWidth);
14158
- }
14159
- const formatted = `\u2500 ${clean} `;
14160
- const clipped = visibleWidth(formatted) > innerWidth ? truncateToWidth(formatted, innerWidth, "\u2026", true) : formatted;
14161
- const fill = "\u2500".repeat(Math.max(0, innerWidth - visibleWidth(clipped)));
14162
- const cleanIndex = clipped.indexOf(clean);
14163
- if (cleanIndex !== -1) {
14164
- const prefix = clipped.slice(0, cleanIndex);
14165
- const suffix = clipped.slice(cleanIndex + clean.length);
14166
- return `${frame2("\u2514")}${frame2(prefix)}${clioTheme().fg("dim", clean)}${frame2(suffix)}${frame2(fill)}${frame2("\u2518")}`;
14167
- }
14168
- return `${frame2("\u2514")}${frame2(clipped)}${frame2(fill)}${frame2("\u2518")}`;
14169
- }
14170
- function formatRuntimeResolutionDiagnostic(diagnostic) {
14171
- return `${diagnostic.severity}: ${diagnostic.code}: ${diagnostic.message}`;
14172
- }
14173
- function diagnosticSeverityToken(severity) {
14174
- return severity === "error" ? "error" : severity === "warning" ? "warning" : "muted";
14175
- }
14176
- function runtimeResolutionDiagnosticLine(diagnostic, width) {
14177
- return clioTheme().fg(diagnosticSeverityToken(diagnostic.severity), fitDiagnosticLine(diagnostic, width));
14178
- }
14179
- function fitDiagnosticLine(diagnostic, width) {
14180
- return padAnsi(formatRuntimeResolutionDiagnostic(diagnostic), Math.max(1, width));
14181
- }
14182
- function frameAlignForAnchor(anchor) {
14183
- if (anchor === void 0) return "center";
14184
- if (anchor === "left-center" || anchor.endsWith("-left")) return "left";
14185
- if (anchor === "right-center" || anchor.endsWith("-right")) return "right";
14186
- return "center";
14187
- }
14188
- function fitBody(lines, rowBudget, contentWidth) {
14189
- if (rowBudget <= 0) return lines;
14190
- const bodyBudget = Math.max(1, rowBudget - 2);
14191
- if (lines.length <= bodyBudget) return lines;
14192
- const kept = lines.slice(0, bodyBudget - 1);
14193
- const hidden = lines.length - kept.length;
14194
- return [...kept, clioTheme().fg("dim", truncateToWidth(`\u2026 ${hidden} more rows`, contentWidth, "", true))];
14195
- }
14196
- var ClioOverlayFrame = class {
14197
- constructor(child, title, footerHint, boxWidth = 0, align = "center", tone) {
14198
- this.child = child;
14199
- this.title = title;
14200
- this.footerHint = footerHint;
14201
- this.boxWidth = boxWidth;
14202
- this.align = align;
14203
- this.tone = tone;
14204
- }
14205
- child;
14206
- title;
14207
- footerHint;
14208
- boxWidth;
14209
- align;
14210
- tone;
14211
- /**
14212
- * Rows the box may occupy, or zero while unknown.
14213
- *
14214
- * `Component.render` is handed a width and nothing else, so the frame learns
14215
- * its height budget from the engine's `visible` predicate, which is called
14216
- * with the live terminal size immediately before the render pass that
14217
- * composites this overlay.
14218
- */
14219
- rowBudget = 0;
14220
- /**
14221
- * Last rendered box, keyed by everything that shapes it. `childLines` is
14222
- * compared by array identity: a child that rebuilds its rows returns a fresh
14223
- * array and correctly misses, and one that serves a cache returns the same
14224
- * array and correctly hits.
14225
- */
14226
- cachedRender;
14227
- setRowBudget(rows) {
14228
- this.rowBudget = Math.max(0, Math.floor(rows));
14229
- }
14230
- render(width) {
14231
- const boxWidth = Math.max(5, Math.min(this.boxWidth > 0 ? this.boxWidth : width, width));
14232
- const contentWidth = Math.max(1, boxWidth - 4);
14233
- const childLines = this.child.render(contentWidth);
14234
- const titleText = typeof this.title === "function" ? this.title() : this.title;
14235
- const hint = typeof this.footerHint === "function" ? this.footerHint(contentWidth + 2) : this.footerHint;
14236
- const tone = resolveTone(this.tone);
14237
- const cached = this.cachedRender;
14238
- if (cached !== void 0 && cached.width === width && cached.rowBudget === this.rowBudget && cached.childLines === childLines && cached.title === titleText && cached.hint === hint && cached.tone === tone) {
14239
- return cached.lines;
14240
- }
14241
- const label = titleText.length > 0 ? `\u2500 ${titleText} ` : "\u2500 ";
14242
- const side = clioFrame("\u2502", tone ?? "frame");
14243
- const boxLines = [
14244
- brandedTopBorder(label, contentWidth + 2, tone),
14245
- ...fitBody(childLines, this.rowBudget, contentWidth).map((line) => `${side} ${padAnsi(line, contentWidth)} ${side}`),
14246
- brandedBottomBorder(contentWidth + 2, hint, tone)
14247
- ];
14248
- const slack = Math.max(0, width - boxWidth);
14249
- const lines = (() => {
14250
- if (slack === 0) return boxLines;
14251
- const leading = this.align === "left" ? 0 : this.align === "right" ? slack : Math.floor(slack / 2);
14252
- const lead = " ".repeat(leading);
14253
- const trail = " ".repeat(slack - leading);
14254
- return boxLines.map((line) => `${lead}${line}${trail}`);
14255
- })();
14256
- this.cachedRender = { width, rowBudget: this.rowBudget, childLines, title: titleText, hint, tone, lines };
14257
- return lines;
14258
- }
14259
- handleInput(data) {
14260
- this.child.handleInput?.(data);
14261
- }
14262
- invalidate() {
14263
- this.cachedRender = void 0;
14264
- this.child.invalidate?.();
14265
- }
14266
- };
14267
- function showClioOverlayFrame(tui, child, options) {
14268
- const { title, footerHint, tone, width, visible, maxHeight, margin, ...overlayOptions } = options;
14269
- const boxWidth = typeof width === "number" ? width : 0;
14270
- const frame2 = new ClioOverlayFrame(child, title, footerHint, boxWidth, frameAlignForAnchor(options.anchor), tone);
14271
- const marginRows = typeof margin === "number" ? margin * 2 : (margin?.top ?? 0) + (margin?.bottom ?? 0);
14272
- return tui.showOverlay(frame2, {
14273
- ...overlayOptions,
14274
- ...margin !== void 0 ? { margin } : {},
14275
- width: "100%",
14276
- visible: (termWidth, termHeight) => {
14277
- const available = Math.max(1, termHeight - marginRows);
14278
- const requested = resolveRowSize(maxHeight, termHeight);
14279
- frame2.setRowBudget(requested === null ? available : Math.min(requested, available));
14280
- return visible ? visible(termWidth, termHeight) : true;
14281
- }
14282
- });
14283
- }
14284
- function resolveRowSize(value, termHeight) {
14285
- if (typeof value === "number") return Math.max(1, Math.floor(value));
14286
- if (typeof value !== "string") return null;
14287
- const percent = /^(\d+(?:\.\d+)?)%$/u.exec(value.trim());
14288
- if (!percent?.[1]) return null;
14289
- return Math.max(1, Math.floor(termHeight * Number.parseFloat(percent[1]) / 100));
14290
- }
14291
-
14292
14468
  // src/interactive/view/view-overlay.ts
14293
14469
  init_esm_shims();
14294
14470
  var VIEW_OVERLAY_WIDTH = "100%";
@@ -14871,13 +15047,14 @@ function openViewOverlay(tui, options) {
14871
15047
  }
14872
15048
 
14873
15049
  // src/interactive/overlays/ask-user.ts
14874
- var ASK_USER_DECISION_TONE = "action";
14875
- var ASK_USER_DECISION_TITLE = "Decision required";
15050
+ var DEFAULT_ASK_USER_PRESENTATION = classifyDecisionPresentation(decisionFactsForAnswer("local"));
15051
+ var ASK_USER_DECISION_TONE = DEFAULT_ASK_USER_PRESENTATION.semanticToken;
15052
+ var ASK_USER_DECISION_TITLE = DEFAULT_ASK_USER_PRESENTATION.title;
14876
15053
  var ASK_USER_WAITING_TITLE = "Ask User";
14877
15054
  var ASK_USER_COMPACT_MAX_OPTIONS = 3;
14878
15055
  var ASK_USER_DEFAULT_SURFACE = "panel";
14879
15056
  var ASK_USER_SURFACE_GEOMETRY = {
14880
- compact: { width: 68, maxInnerRows: 10, anchor: "bottom-center", margin: { top: 1, right: 2, bottom: 1, left: 2 } },
15057
+ compact: { width: 68, maxInnerRows: 14, anchor: "bottom-center", margin: { top: 1, right: 2, bottom: 1, left: 2 } },
14881
15058
  panel: { width: 88, maxInnerRows: 18, anchor: "center", margin: 1 },
14882
15059
  // The interview is a workspace, not a modal. A cap of 42 turned a 66-row
14883
15060
  // terminal into a centered box with twenty blank rows around it, so the
@@ -14987,13 +15164,15 @@ var AskUserOverlayView = class {
14987
15164
  contentRegionHeight = 1;
14988
15165
  /** Rows the scroll region wanted on the last render. */
14989
15166
  contentTotalRows = 0;
14990
- begin(questions) {
15167
+ presentation = DEFAULT_ASK_USER_PRESENTATION;
15168
+ begin(questions, presentation = DEFAULT_ASK_USER_PRESENTATION) {
14991
15169
  if (this.phase === "closed") return Promise.resolve(cancelledAskUserResult());
14992
15170
  if (this.resolveCurrent) return Promise.resolve(cancelledAskUserResult());
14993
15171
  this.phase = "asking";
14994
15172
  this.index = 0;
14995
15173
  this.status = "";
14996
15174
  this.questions = [...questions];
15175
+ this.presentation = presentation;
14997
15176
  this.states = this.questions.map((question) => createQuestionState(question));
14998
15177
  this.list = null;
14999
15178
  this.input = null;
@@ -15022,6 +15201,12 @@ var AskUserOverlayView = class {
15022
15201
  currentSurface() {
15023
15202
  return this.surface;
15024
15203
  }
15204
+ decisionTitle() {
15205
+ return this.presentation.title;
15206
+ }
15207
+ decisionTone() {
15208
+ return this.presentation.semanticToken;
15209
+ }
15025
15210
  invalidate() {
15026
15211
  this.list?.invalidate();
15027
15212
  this.input?.invalidate();
@@ -15083,9 +15268,16 @@ var AskUserOverlayView = class {
15083
15268
  const statusLines = this.status.length > 0 ? ["", fitLine(clioTheme().fg("dim", this.status), safeWidth)] : [];
15084
15269
  const headerLine4 = this.renderQuestionHeader(question, safeWidth);
15085
15270
  const headerLines = headerLine4.length > 0 ? [headerLine4] : [];
15086
- const spent = headerLines.length + statusLines.length + 1 + controlLines.length;
15087
- const questionLines = wrapTextWithAnsi(question.question, safeWidth).slice(0, Math.max(1, maxRows - spent));
15088
- return [...headerLines, ...questionLines, ...statusLines, "", ...controlLines].slice(0, maxRows);
15271
+ const bottom = [...statusLines, "", ...controlLines];
15272
+ const topBudget = Math.max(1, maxRows - bottom.length);
15273
+ const contextLines = this.renderDecisionContext(safeWidth);
15274
+ const top = [
15275
+ contextLines[0] ?? "",
15276
+ ...headerLines,
15277
+ ...wrapTextWithAnsi(question.question, safeWidth),
15278
+ ...contextLines.slice(1)
15279
+ ].slice(0, topBudget);
15280
+ return [...top, ...bottom].slice(0, maxRows);
15089
15281
  }
15090
15282
  /**
15091
15283
  * The interview, as a workspace with fixed chrome and a scrolling middle.
@@ -15103,7 +15295,12 @@ var AskUserOverlayView = class {
15103
15295
  const headerLine4 = this.renderQuestionHeader(question, width);
15104
15296
  const headerLines = headerLine4.length > 0 ? [headerLine4] : [];
15105
15297
  const stripLines = [this.renderQuestionStrip(width), ""];
15106
- const content = [...wrapTextWithAnsi(question.question, width), ...this.renderAnswerLedger(width)];
15298
+ const content = [
15299
+ ...wrapTextWithAnsi(question.question, width),
15300
+ "",
15301
+ ...this.renderDecisionContext(width),
15302
+ ...this.renderAnswerLedger(width)
15303
+ ];
15107
15304
  let top = [...stripLines, ...headerLines];
15108
15305
  let bottom = [...statusLines, "", ...controlLines];
15109
15306
  const region = () => maxRows - top.length - bottom.length;
@@ -15167,26 +15364,40 @@ var AskUserOverlayView = class {
15167
15364
  const question = this.currentQuestion();
15168
15365
  const state = this.currentState();
15169
15366
  if (!question || !state) return buildHint([]);
15367
+ const recordAnswer = this.presentation.requiredActions.find((action) => action.id === "record-answer")?.label.toLowerCase() ?? "record answer";
15170
15368
  if (state.mode === "text") {
15171
15369
  return this.questions.length > 1 ? this.withScrollHint([
15172
- { key: "Enter", verb: "submit" },
15370
+ { key: "Enter", verb: recordAnswer },
15173
15371
  { key: "Alt+Left/Right", verb: "question" }
15174
- ]) : this.withScrollHint([{ key: "Enter", verb: "submit" }]);
15372
+ ]) : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
15175
15373
  }
15176
15374
  if (question.multi_select === true) {
15177
15375
  return this.questions.length > 1 ? this.withScrollHint([
15178
15376
  { key: "Left/Right", verb: "question" },
15179
15377
  { key: "Space", verb: "toggle" },
15180
- { key: "Enter", verb: "accept" }
15378
+ { key: "Enter", verb: recordAnswer }
15181
15379
  ]) : this.withScrollHint([
15182
15380
  { key: "Space", verb: "toggle" },
15183
- { key: "Enter", verb: "accept" }
15381
+ { key: "Enter", verb: recordAnswer }
15184
15382
  ]);
15185
15383
  }
15186
15384
  return this.questions.length > 1 ? this.withScrollHint([
15187
15385
  { key: "Left/Right", verb: "question" },
15188
- { key: "Enter", verb: "accept" }
15189
- ]) : this.withScrollHint([{ key: "Enter", verb: "accept" }]);
15386
+ { key: "Enter", verb: recordAnswer }
15387
+ ]) : this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
15388
+ }
15389
+ renderDecisionContext(width) {
15390
+ const theme6 = clioTheme();
15391
+ const tier = theme6.style(this.presentation.semanticToken, this.presentation.tierLabel, { bold: true });
15392
+ const requested = theme6.fg("dim", `requested by ${this.presentation.requestedByCopy}`);
15393
+ const effect = `${theme6.fg("dim", "Effect:")} ${theme6.fg("muted", this.presentation.authorizationCopy)}`;
15394
+ return [
15395
+ fitLine(`${tier}${dotSep(theme6)}${requested}`, width),
15396
+ ...wrapTextWithAnsi(effect, width).map((line) => fitLine(line, width)),
15397
+ ...wrapTextWithAnsi(theme6.fg("muted", this.presentation.reversibilityCopy), width).map(
15398
+ (line) => fitLine(line, width)
15399
+ )
15400
+ ];
15190
15401
  }
15191
15402
  /**
15192
15403
  * The keys for this question, plus the scroll keys when there is something to
@@ -15624,8 +15835,8 @@ function openAskUserOverlay(tui, deps) {
15624
15835
  // the terminal has left after the margins.
15625
15836
  ...geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {},
15626
15837
  margin: geometry.margin,
15627
- title: () => view.isDecisionPending() ? ASK_USER_DECISION_TITLE : ASK_USER_WAITING_TITLE,
15628
- tone: () => view.isDecisionPending() ? ASK_USER_DECISION_TONE : void 0,
15838
+ title: () => view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE,
15839
+ tone: () => view.isDecisionPending() ? view.decisionTone() : void 0,
15629
15840
  footerHint: () => view.footerHint()
15630
15841
  });
15631
15842
  mounted = surface;
@@ -15644,7 +15855,7 @@ function openAskUserOverlay(tui, deps) {
15644
15855
  focus: () => handle?.focus(),
15645
15856
  unfocus: (options) => options ? handle?.unfocus(options) : handle?.unfocus(),
15646
15857
  isFocused: () => handle?.isFocused() ?? false,
15647
- ask: (questions) => view.begin(questions),
15858
+ ask: (questions, presentation) => view.begin(questions, presentation),
15648
15859
  cancel: () => view.cancel(),
15649
15860
  close,
15650
15861
  hide: close,
@@ -15699,7 +15910,7 @@ function createOverlayAskUserLifecycle(deps) {
15699
15910
  const activeSession = ensureSession();
15700
15911
  if (!activeSession) return cancelledAskUserResult();
15701
15912
  pendingCancel = cancel;
15702
- const result = await activeSession.ask(questions);
15913
+ const result = await activeSession.ask(questions, invokeOptions?.decisionPresentation);
15703
15914
  if (result.cancelled === true || !toolBacked) {
15704
15915
  if (result.cancelled === true) cancelledForTurn = true;
15705
15916
  close();
@@ -16192,7 +16403,50 @@ function legendRow2(group, contentWidth) {
16192
16403
  const labelText = group.label.length > leftWidth ? group.label.slice(0, leftWidth) : group.label.padEnd(leftWidth);
16193
16404
  return `${swatch} ${theme6.fg(labelToken, labelText)} ${theme6.fg("muted", right)}`;
16194
16405
  }
16195
- function renderContextLedgerLines(ledger, contentWidth) {
16406
+ function evictedTokens(view) {
16407
+ let total = 0;
16408
+ for (const state of view.evicted.values()) total += state.tokensFreed;
16409
+ return total;
16410
+ }
16411
+ function formatChurn(view) {
16412
+ if (view.itemsEvicted === 0) return "n/a";
16413
+ return (view.recalls / view.itemsEvicted).toFixed(2);
16414
+ }
16415
+ function coldReasonLabel(reason) {
16416
+ switch (reason) {
16417
+ case "working_set_evict":
16418
+ return "working-set eviction";
16419
+ case "compaction":
16420
+ return "compaction";
16421
+ case "dispatch":
16422
+ return "dispatch traffic";
16423
+ default:
16424
+ return reason;
16425
+ }
16426
+ }
16427
+ function workingSetPolicyLabel(view, config) {
16428
+ if (config === null || config === void 0) return `policy ${view.lastPolicyId ?? "none"}`;
16429
+ if (!config.enabled) return "disabled";
16430
+ const state = view.evictionEvents === 0 ? " \xB7 no events yet" : "";
16431
+ const lastRan = view.lastPolicyId !== null && view.lastPolicyId !== config.policy ? ` (last event by ${view.lastPolicyId})` : "";
16432
+ return `policy ${config.policy}${lastRan}${state}`;
16433
+ }
16434
+ function renderWorkingSetLines(view, config) {
16435
+ const theme6 = clioTheme();
16436
+ const items = view.evicted.size;
16437
+ const summary = [
16438
+ `${items} evicted item${items === 1 ? "" : "s"}`,
16439
+ `${formatTokens(evictedTokens(view))} tokens`,
16440
+ `${view.evictionEvents} event${view.evictionEvents === 1 ? "" : "s"}`,
16441
+ `${view.recalls} recall${view.recalls === 1 ? "" : "s"}`,
16442
+ `churn ${formatChurn(view)}`
16443
+ ].join(" \xB7 ");
16444
+ return [
16445
+ `${theme6.fg("muted", "working set")} ${theme6.fg("dim", "\xB7")} ${theme6.fg("accent", workingSetPolicyLabel(view, config))}`,
16446
+ theme6.fg("dim", summary)
16447
+ ];
16448
+ }
16449
+ function renderContextLedgerLines(ledger, contentWidth, workingSet, workingSetConfig) {
16196
16450
  const theme6 = clioTheme();
16197
16451
  const lines = [];
16198
16452
  const provider = ledger.provider ?? "no target";
@@ -16206,7 +16460,8 @@ function renderContextLedgerLines(ledger, contentWidth) {
16206
16460
  lines.push("");
16207
16461
  if (ledger.contextWindow > 0) {
16208
16462
  const source = ledger.measured ? "measured" : "\u2248 estimated";
16209
- const summary = `${formatTokens(ledger.usedTokens)} / ${formatTokens(ledger.contextWindow)} tokens (${formatContextPercent(ledger.percent)})`;
16463
+ const window = ledger.contextWindowSlots ? formatContextWindowSlots(ledger.contextWindow, ledger.contextWindowSlots) : formatTokens(ledger.contextWindow);
16464
+ const summary = `${formatTokens(ledger.usedTokens)} / ${window} tokens (${formatContextPercent(ledger.percent)})`;
16210
16465
  const provenance = contextWindowProvenanceLabel(ledger.contextWindowSource);
16211
16466
  const trailer = provenance ? `${provenance} window \xB7 ${source}` : source;
16212
16467
  lines.push(`${theme6.fg("title", summary)} ${theme6.fg("dim", "\xB7")} ${theme6.fg("muted", trailer)}`);
@@ -16217,7 +16472,12 @@ function renderContextLedgerLines(ledger, contentWidth) {
16217
16472
  }
16218
16473
  lines.push("");
16219
16474
  for (const group of ledger.meter) lines.push(legendRow2(group, contentWidth));
16475
+ if (workingSet && workingSet.evicted.size > 0) lines.push(renderEvictedTokensLine(evictedTokens(workingSet), theme6));
16220
16476
  lines.push("");
16477
+ if (workingSet) {
16478
+ for (const line of renderWorkingSetLines(workingSet, workingSetConfig)) lines.push(line);
16479
+ lines.push("");
16480
+ }
16221
16481
  if (ledger.projectPreload && ledger.groups.some((group) => group.category === "project")) {
16222
16482
  lines.push(theme6.fg("dim", `project preload: ${ledger.projectPreload}`));
16223
16483
  }
@@ -16235,8 +16495,13 @@ function renderContextLedgerLines(ledger, contentWidth) {
16235
16495
  const read = cache2.cacheReadTokens !== null ? `cache read ${formatTokens(cache2.cacheReadTokens)}` : "cache read n/a";
16236
16496
  const uncached = cache2.uncachedInputTokens !== null ? `uncached input ${formatTokens(cache2.uncachedInputTokens)}` : null;
16237
16497
  const line = ["prompt cache:", shell, "\xB7", backend, "\xB7", read, ...uncached ? ["\xB7", uncached] : []].join(" ");
16238
- const misleading = cache2.shellReused && cache2.backendVerdict === "cold";
16498
+ const coldReasons = cache2.backendVerdict === "cold" ? cache2.expectedColdReasons ?? [] : [];
16499
+ const misleading = cache2.shellReused && cache2.backendVerdict === "cold" && coldReasons.length === 0;
16239
16500
  lines.push(theme6.fg(misleading ? "warning" : "dim", line));
16501
+ if (coldReasons.length > 0) {
16502
+ const reasons = coldReasons.map(coldReasonLabel).join(", ");
16503
+ lines.push(theme6.fg("dim", `last cold turn: ${reasons} (expected)`));
16504
+ }
16240
16505
  }
16241
16506
  if (ledger.lastCompaction) {
16242
16507
  const pruneInfo = `last compaction: reclaimed ${formatTokens(ledger.lastCompaction.tokensBefore)} -> ${formatTokens(ledger.lastCompaction.tokensAfter)} tokens (${ledger.lastCompaction.stage})`;
@@ -16246,7 +16511,13 @@ function renderContextLedgerLines(ledger, contentWidth) {
16246
16511
  }
16247
16512
  var STREAMING_FALLBACK_TICK_MS = 1e3;
16248
16513
  function openContextOverlay(tui, getLedger, options) {
16249
- const text = new Text(renderContextLedgerLines(getLedger(), DEFAULT_CONTENT_WIDTH).join("\n"), 0, 0);
16514
+ const render2 = () => renderContextLedgerLines(
16515
+ getLedger(),
16516
+ DEFAULT_CONTENT_WIDTH,
16517
+ options?.getWorkingSet?.() ?? null,
16518
+ options?.getWorkingSetConfig?.() ?? null
16519
+ ).join("\n");
16520
+ const text = new Text(render2(), 0, 0);
16250
16521
  const handle = showClioOverlayFrame(tui, text, {
16251
16522
  anchor: "center",
16252
16523
  width: CONTEXT_OVERLAY_WIDTH,
@@ -16254,7 +16525,7 @@ function openContextOverlay(tui, getLedger, options) {
16254
16525
  footerHint: buildHint([])
16255
16526
  });
16256
16527
  const refresh = () => {
16257
- text.setText(renderContextLedgerLines(getLedger(), DEFAULT_CONTENT_WIDTH).join("\n"));
16528
+ text.setText(render2());
16258
16529
  text.invalidate();
16259
16530
  tui.requestRender();
16260
16531
  };
@@ -16312,7 +16583,7 @@ function formatTokens2(n) {
16312
16583
  function aggregateCostEntries(entries) {
16313
16584
  const grouped = /* @__PURE__ */ new Map();
16314
16585
  for (const entry of entries) {
16315
- const key = `${entry.providerId}::${entry.modelId}`;
16586
+ const key = `${entry.providerId}::${entry.attributedModelId}`;
16316
16587
  const existing = grouped.get(key);
16317
16588
  if (existing) {
16318
16589
  existing.row.runs += 1;
@@ -16323,13 +16594,18 @@ function aggregateCostEntries(entries) {
16323
16594
  existing.row.cacheWrite += entry.cacheWrite;
16324
16595
  existing.row.reasoningTokens += entry.reasoningTokens;
16325
16596
  existing.row.apiCalls += entry.apiCalls ?? 1;
16597
+ for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
16598
+ addResponseModelIdObservationCounts(
16599
+ existing.responseModelIdObservationCounts,
16600
+ entry.responseModelIdObservationCounts
16601
+ );
16326
16602
  existing.entries.push(entry);
16327
16603
  continue;
16328
16604
  }
16329
16605
  grouped.set(key, {
16330
16606
  row: {
16331
16607
  providerId: entry.providerId,
16332
- modelId: entry.modelId,
16608
+ attributedModelId: entry.attributedModelId,
16333
16609
  runs: 1,
16334
16610
  tokens: entry.tokens,
16335
16611
  input: entry.input,
@@ -16339,16 +16615,20 @@ function aggregateCostEntries(entries) {
16339
16615
  reasoningTokens: entry.reasoningTokens,
16340
16616
  apiCalls: entry.apiCalls ?? 1
16341
16617
  },
16618
+ requestedModelIds: new Set(entry.requestedModelIds),
16619
+ responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
16342
16620
  entries: [entry]
16343
16621
  });
16344
16622
  }
16345
- const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2 }) => ({
16623
+ const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2, requestedModelIds, responseModelIdObservationCounts }) => ({
16346
16624
  ...row2,
16625
+ requestedModelIds: [...requestedModelIds].sort(),
16626
+ responseModelIdObservationCounts,
16347
16627
  cost: aggregateCostAmounts(entries2.map((entry) => ({ usd: entry.usd, provenance: entry.provenance })))
16348
16628
  }));
16349
16629
  rows.sort((a, b) => {
16350
16630
  if (a.providerId !== b.providerId) return a.providerId < b.providerId ? -1 : 1;
16351
- if (a.modelId !== b.modelId) return a.modelId < b.modelId ? -1 : 1;
16631
+ if (a.attributedModelId !== b.attributedModelId) return a.attributedModelId < b.attributedModelId ? -1 : 1;
16352
16632
  return 0;
16353
16633
  });
16354
16634
  return rows;
@@ -16405,6 +16685,8 @@ function summaryBlock(totalCost, totalTokens, rows) {
16405
16685
  function modelBlock(row2) {
16406
16686
  const cost = formatCostAggregate(row2.cost);
16407
16687
  return kvBlock([
16688
+ ["requested model ids", row2.requestedModelIds.join(", ")],
16689
+ ["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
16408
16690
  ["turns", formatTokens2(row2.runs)],
16409
16691
  ["model calls", formatTokens2(row2.apiCalls)],
16410
16692
  ...cost === null ? [] : [["cost", cost]],
@@ -16428,7 +16710,7 @@ function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth)
16428
16710
  } else {
16429
16711
  for (const [index, row2] of rows.entries()) {
16430
16712
  if (index > 0) lines.push("");
16431
- lines.push(theme6.style("accent", `${row2.providerId} \xB7 ${row2.modelId}`, { bold: true }));
16713
+ lines.push(theme6.style("accent", `${row2.providerId} \xB7 attributed model ${row2.attributedModelId}`, { bold: true }));
16432
16714
  for (const line of modelBlock(row2)) {
16433
16715
  lines.push(line);
16434
16716
  }
@@ -17063,9 +17345,10 @@ function memorySignature(status, records) {
17063
17345
  return parts.join("|");
17064
17346
  }
17065
17347
  var MemoryOverlayView = class {
17066
- constructor(getStatus, getRecords, onClose, onChange) {
17348
+ constructor(getStatus, getRecords, onClose, onChange, promotion) {
17067
17349
  this.getStatus = getStatus;
17068
17350
  this.getRecords = getRecords;
17351
+ this.promotion = promotion;
17069
17352
  this.list = new ListOverlayView(
17070
17353
  {
17071
17354
  title: "Memory",
@@ -17073,6 +17356,16 @@ var MemoryOverlayView = class {
17073
17356
  filterable: true,
17074
17357
  layout: "split",
17075
17358
  emptyMessage: EMPTY_MESSAGE,
17359
+ ...promotion ? {
17360
+ hints: [
17361
+ { key: "p", verb: "propose repo" },
17362
+ { key: "g", verb: "propose global" }
17363
+ ],
17364
+ actions: {
17365
+ p: (item) => this.promote(item, "repo", onChange),
17366
+ g: (item) => this.promote(item, "global", onChange)
17367
+ }
17368
+ } : {},
17076
17369
  onClose
17077
17370
  },
17078
17371
  onChange
@@ -17080,9 +17373,13 @@ var MemoryOverlayView = class {
17080
17373
  }
17081
17374
  getStatus;
17082
17375
  getRecords;
17376
+ promotion;
17083
17377
  list;
17084
17378
  signature = null;
17085
17379
  renderMemo = null;
17380
+ pendingGlobalEntryId = null;
17381
+ promotionMessage = null;
17382
+ promotionInFlight = false;
17086
17383
  getHint() {
17087
17384
  this.sync();
17088
17385
  return this.list.getHint();
@@ -17090,11 +17387,19 @@ var MemoryOverlayView = class {
17090
17387
  render(width) {
17091
17388
  const status = this.sync();
17092
17389
  const statusLine = formatMemoryStatusLine(status, width);
17390
+ const promotionLine = this.promotionMessage === null ? null : fitUnits(clioTheme(), "", [clioTheme().fg(this.promotionMessage.token, this.promotionMessage.text)], width);
17391
+ const statusKey = promotionLine === null ? statusLine : `${statusLine}
17392
+ ${promotionLine}`;
17093
17393
  const listLines = this.list.render(width);
17094
17394
  const memo = this.renderMemo;
17095
- if (memo && memo.width === width && memo.status === statusLine && memo.listLines === listLines) return memo.lines;
17096
- const lines = [statusLine, rule(clioTheme(), width), ...listLines];
17097
- this.renderMemo = { width, status: statusLine, listLines, lines };
17395
+ if (memo && memo.width === width && memo.status === statusKey && memo.listLines === listLines) return memo.lines;
17396
+ const lines = [
17397
+ statusLine,
17398
+ ...promotionLine === null ? [] : [promotionLine],
17399
+ rule(clioTheme(), width),
17400
+ ...listLines
17401
+ ];
17402
+ this.renderMemo = { width, status: statusKey, listLines, lines };
17098
17403
  return lines;
17099
17404
  }
17100
17405
  handleInput(data) {
@@ -17115,13 +17420,62 @@ var MemoryOverlayView = class {
17115
17420
  }
17116
17421
  return status;
17117
17422
  }
17423
+ promote(item, scope, onChange) {
17424
+ if (this.promotion === void 0 || this.promotionInFlight) return;
17425
+ const entry = this.selectedBankEntry(item);
17426
+ if (entry === null) {
17427
+ this.pendingGlobalEntryId = null;
17428
+ this.promotionMessage = { token: "warning", text: "select a knowledge or procedural task-bank entry" };
17429
+ this.invalidate();
17430
+ onChange();
17431
+ return;
17432
+ }
17433
+ if (scope === "global" && this.pendingGlobalEntryId !== entry.id) {
17434
+ this.pendingGlobalEntryId = entry.id;
17435
+ this.promotionMessage = {
17436
+ token: "warning",
17437
+ text: `global scope broadens applicability for ${entry.id}; press g again to acknowledge`
17438
+ };
17439
+ this.invalidate();
17440
+ onChange();
17441
+ return;
17442
+ }
17443
+ this.pendingGlobalEntryId = null;
17444
+ this.promotionInFlight = true;
17445
+ this.promotionMessage = { token: "muted", text: `proposing ${entry.id} with ${scope} scope` };
17446
+ this.invalidate();
17447
+ onChange();
17448
+ void this.promotion.onPromote(entry, scope).then((result) => {
17449
+ this.promotionMessage = {
17450
+ token: "success",
17451
+ text: `${result.created ? "proposed" : "found existing"} ${result.record.id}; review, then run clio-coder memory approve ${result.record.id}`
17452
+ };
17453
+ }).catch((error) => {
17454
+ this.promotionMessage = {
17455
+ token: "error",
17456
+ text: `promotion failed: ${error instanceof Error ? error.message : String(error)}`
17457
+ };
17458
+ }).finally(() => {
17459
+ this.promotionInFlight = false;
17460
+ this.invalidate();
17461
+ onChange();
17462
+ });
17463
+ }
17464
+ selectedBankEntry(item) {
17465
+ if (!item.id.startsWith("bank:")) return null;
17466
+ const entryId = item.id.slice("bank:".length);
17467
+ const entry = bankEntries(this.getStatus().bank).find((candidate) => candidate.id === entryId);
17468
+ if (entry === void 0 || entry.kind === "status") return null;
17469
+ return entry;
17470
+ }
17118
17471
  };
17119
17472
  function openMemoryOverlay(tui, getStatus, getRecords, options = {}) {
17120
17473
  const view = new MemoryOverlayView(
17121
17474
  getStatus,
17122
17475
  getRecords,
17123
17476
  () => options.onClose?.(),
17124
- () => tui.requestRender()
17477
+ () => tui.requestRender(),
17478
+ options.onPromote ? { onPromote: options.onPromote } : void 0
17125
17479
  );
17126
17480
  const handle = showClioOverlayFrame(tui, view, {
17127
17481
  anchor: "center",
@@ -17250,9 +17604,10 @@ function selectableRows(interviews) {
17250
17604
  }
17251
17605
  function interviewHeader(interview, width, now) {
17252
17606
  const theme6 = clioTheme();
17607
+ const presentation = classifyDecisionPresentation(decisionFactsForAnswer(interview.exposure ?? "local"));
17253
17608
  const rounds = `${interview.roundCount} round${interview.roundCount === 1 ? "" : "s"}`;
17254
17609
  const status = interview.interviewStatus === "complete" ? theme6.fg("success", "complete") : theme6.fg("warning", "cancelled");
17255
- const heading = `${theme6.fg("accent", relativeTime(interview.endedAt, now))}${theme6.fg("dim", " \xB7 ")}${theme6.fg("muted", rounds)}${theme6.fg("dim", " \xB7 ")}${status}`;
17610
+ const heading = `${theme6.style(presentation.semanticToken, presentation.tierLabel, { bold: true })}${theme6.fg("dim", " \xB7 ")}${theme6.fg("accent", relativeTime(interview.endedAt, now))}${theme6.fg("dim", " \xB7 ")}${theme6.fg("muted", rounds)}${theme6.fg("dim", " \xB7 ")}${status}`;
17256
17611
  const lines = [fitLine3(heading, width)];
17257
17612
  if (interview.summary) {
17258
17613
  lines.push(...wrapTextWithAnsi(theme6.fg("dim", interview.summary), width).map((line) => fitLine3(line, width)));
@@ -21366,6 +21721,7 @@ function isSettingsSectionId(value) {
21366
21721
  var COMPACT_POSITIONALS = [
21367
21722
  { name: "instructions", required: false, rest: true }
21368
21723
  ];
21724
+ var RECALL_POSITIONALS = [{ name: "ref", required: true }];
21369
21725
  function fromArgsOrUsage(name, command) {
21370
21726
  return (parsed) => parsed.error ? { kind: "usage-error", command: name, reason: parsed.error } : command;
21371
21727
  }
@@ -21711,11 +22067,12 @@ ${usage}` : usage);
21711
22067
  },
21712
22068
  {
21713
22069
  name: "context",
21714
- description: "Context hub: window overlay plus compact, init, refresh, and reset",
22070
+ description: "Context hub: window overlay plus compact, recall, init, refresh, and reset",
21715
22071
  group: "Inspect",
21716
- kinds: ["context-view", "compact", "init", "context-clear", "context-refresh"],
22072
+ kinds: ["context-view", "compact", "context-recall", "init", "context-clear", "context-refresh"],
21717
22073
  subcommandDescriptions: {
21718
22074
  compact: "Compact session context",
22075
+ recall: "Recall an evicted result",
21719
22076
  init: "Initialize project context",
21720
22077
  refresh: "Refresh project context",
21721
22078
  reset: "Reset project context"
@@ -21723,6 +22080,7 @@ ${usage}` : usage);
21723
22080
  args: {
21724
22081
  subcommands: {
21725
22082
  compact: { positionals: [...COMPACT_POSITIONALS] },
22083
+ recall: { positionals: [...RECALL_POSITIONALS] },
21726
22084
  init: {},
21727
22085
  refresh: {},
21728
22086
  reset: {}
@@ -21733,6 +22091,8 @@ ${usage}` : usage);
21733
22091
  switch (parsed.subcommand) {
21734
22092
  case "compact":
21735
22093
  return { kind: "compact", instructions: parsed.rest };
22094
+ case "recall":
22095
+ return { kind: "context-recall", ref: parsed.positionals[0] ?? "" };
21736
22096
  case "init":
21737
22097
  return { kind: "init", options: {} };
21738
22098
  case "refresh":
@@ -21751,6 +22111,13 @@ ${usage}` : usage);
21751
22111
  case "compact":
21752
22112
  ctx.runCompact(command.instructions);
21753
22113
  return;
22114
+ case "context-recall":
22115
+ if (ctx.runContextRecall) {
22116
+ ctx.runContextRecall(command.ref);
22117
+ } else {
22118
+ ctx.notice("error", "context recall is not wired; no session is bound to this process");
22119
+ }
22120
+ return;
21754
22121
  case "init":
21755
22122
  ctx.runInit(command.options);
21756
22123
  return;
@@ -21859,7 +22226,7 @@ ${usage}` : usage);
21859
22226
  },
21860
22227
  {
21861
22228
  name: "memory",
21862
- description: "Inspect task memory or seed it from the newest handoff",
22229
+ description: "Inspect, promote, or seed task memory",
21863
22230
  group: "Inspect",
21864
22231
  kinds: ["memory", "memory-seed"],
21865
22232
  subcommandDescriptions: { seed: "Seed the task bank from the newest handoff" },
@@ -22611,7 +22978,16 @@ function createOverlayGeneralOpeners(deps) {
22611
22978
  deps.transitions.state = "context-view";
22612
22979
  deps.transitions.handle = openContextOverlayFactory(deps.tui, deps.getContextLedger, {
22613
22980
  bus: deps.bus,
22614
- chat: deps.contextChat
22981
+ chat: deps.contextChat,
22982
+ getWorkingSet: () => {
22983
+ const readSessionEntries = deps.readSessionEntries;
22984
+ if (!readSessionEntries) return null;
22985
+ return foldWorkingSet(readSessionEntries(), deps.getSessionMeta()?.pinnedLeafTurnId ?? void 0);
22986
+ },
22987
+ getWorkingSetConfig: () => {
22988
+ const workingSet = deps.getSettings?.().context.workingSet;
22989
+ return workingSet ? { enabled: workingSet.enabled, policy: workingSet.policy } : null;
22990
+ }
22615
22991
  });
22616
22992
  deps.requestRender();
22617
22993
  };
@@ -22706,7 +23082,30 @@ function createOverlayGeneralOpeners(deps) {
22706
23082
  }
22707
23083
  deps.transitions.state = "memory";
22708
23084
  deps.transitions.handle = openMemoryOverlayFactory(deps.tui, deps.getTaskMemoryStatus, () => records, {
22709
- onClose: deps.closeOverlay
23085
+ onClose: deps.closeOverlay,
23086
+ onPromote: async (entry, scope) => {
23087
+ const meta = deps.getSessionMeta();
23088
+ if (!meta) throw new Error("memory promotion requires an active session");
23089
+ const selection = scope === "global" ? { scope: "global", acknowledgeGlobal: true } : (() => {
23090
+ const repository = canonicalMemoryRepositoryIdentity(meta.cwd);
23091
+ if (repository === null) {
23092
+ throw new Error("repo promotion requires a canonical active repository identity");
23093
+ }
23094
+ return { scope: "repo", repository };
23095
+ })();
23096
+ const result = await proposeMemoryPromotion(
23097
+ deps.dataDir,
23098
+ {
23099
+ kind: "task-bank-entry",
23100
+ sessionId: meta.id,
23101
+ evidenceRefs: [`session-${meta.id}`],
23102
+ entry
23103
+ },
23104
+ selection
23105
+ );
23106
+ records = loadMemoryRecordsSync(deps.dataDir);
23107
+ return result;
23108
+ }
22710
23109
  });
22711
23110
  deps.requestRender();
22712
23111
  };
@@ -22747,6 +23146,7 @@ function createOverlayGeneralOpeners(deps) {
22747
23146
  const dispatchBoardHint = () => {
22748
23147
  const row2 = deps.dispatchBoard.selectedRow();
22749
23148
  const entries = [{ key: "\u2191\u2193", verb: "select" }];
23149
+ if (row2) entries.push({ key: "Enter", verb: deps.dispatchBoard.detailExpanded() ? "hide detail" : "detail" });
22750
23150
  if (row2 && isDispatchBoardRowSteerable(row2)) entries.push({ key: "s", verb: "steer" });
22751
23151
  if (row2 && isDispatchBoardRowCancellable(row2)) entries.push({ key: "x", verb: "cancel" });
22752
23152
  return buildHint(entries);
@@ -22859,6 +23259,26 @@ init_esm_shims();
22859
23259
  init_esm_shims();
22860
23260
  var PERMISSION_OVERLAY_CONTENT_WIDTH = 78;
22861
23261
  var PERMISSION_OVERLAY_WIDTH = PERMISSION_OVERLAY_CONTENT_WIDTH + 4;
23262
+ function permissionOverlayPlacement(tui, editor, footer) {
23263
+ const margin = { bottom: 0 };
23264
+ return {
23265
+ anchor: "bottom-center",
23266
+ margin,
23267
+ visible: (termWidth, termHeight) => {
23268
+ const dockHeight = editor.render(termWidth).length + footer.render(termWidth).length;
23269
+ if (tui.mode === "fullscreen") {
23270
+ margin.bottom = dockHeight;
23271
+ return true;
23272
+ }
23273
+ const baseHeight = tui.render(termWidth).length;
23274
+ const composerTop = Math.max(0, baseHeight - dockHeight);
23275
+ const viewportStart = Math.max(0, baseHeight - termHeight);
23276
+ const composerViewportRow = composerTop - viewportStart;
23277
+ margin.bottom = composerViewportRow >= 0 && composerViewportRow < termHeight ? Math.max(0, termHeight - composerViewportRow) : dockHeight;
23278
+ return true;
23279
+ }
23280
+ };
23281
+ }
22862
23282
  var PermissionOverlayBody = class {
22863
23283
  constructor(view) {
22864
23284
  this.view = view;
@@ -22909,45 +23329,48 @@ function wrapArtifactLine(value, max) {
22909
23329
  lines.push(remaining);
22910
23330
  return lines;
22911
23331
  }
22912
- function axisLabel(axis, style) {
22913
- if (axis.kind === "net") return `safety-net rail ${axis.ruleId}`;
22914
- return style === "worker" ? `autonomy level ${axis.level}` : `autonomy level (${axis.level})`;
23332
+ function permissionDecisionPresentation(view) {
23333
+ return classifyDecisionPresentation(
23334
+ decisionFactsForPermission({
23335
+ tool: view.tool,
23336
+ actionClass: view.actionClass,
23337
+ axis: view.axis.kind === "net" ? { kind: "safety-net", ruleId: view.axis.ruleId } : { kind: "autonomy", level: view.axis.level },
23338
+ origin: view.origin,
23339
+ ...view.exposure !== void 0 ? { exposure: view.exposure } : {}
23340
+ })
23341
+ );
22915
23342
  }
22916
- function askedBy(view) {
22917
- if (view.origin.kind === "worker") {
22918
- return `worker ${view.origin.agentId} (run ${view.origin.runId}), ${axisLabel(view.axis, "worker")}`;
22919
- }
22920
- return axisLabel(view.axis, "main");
23343
+ function permissionOverlayTitle(view) {
23344
+ return permissionDecisionPresentation(view).title;
22921
23345
  }
22922
- function permissionOverlayTitle() {
22923
- return "Allow this action once?";
23346
+ function permissionOverlayTone(view) {
23347
+ return permissionDecisionPresentation(view).semanticToken;
23348
+ }
23349
+ function permissionOverlayHint(innerWidth, composerHasDraft = false) {
23350
+ return fitHintEntries(permissionHintEntries(composerHasDraft), innerWidth - 3);
23351
+ }
23352
+ function actionConsequence(presentation, id) {
23353
+ return presentation.requiredActions.find((action) => action.id === id)?.consequence ?? "";
22924
23354
  }
22925
- var permissionOverlayHint = buildResponsiveHint(
22926
- [
22927
- { key: "Enter", verb: "allow once", short: "allow", critical: true },
22928
- { key: "s", verb: "stop turn", short: "stop", critical: true }
22929
- ],
22930
- { key: "Esc", verb: "close", critical: false }
22931
- );
22932
- var SAFETY_SENTENCES = [
22933
- "Parked until you decide; allow or deny applies to this call only.",
22934
- "Stopping the turn denies it and ends the run, so nothing asks again.",
22935
- "Hard-blocked actions remain blocked."
22936
- ];
22937
23355
  function permissionOverlayLines(view, width) {
22938
23356
  const content = Math.max(8, Math.floor(width));
23357
+ const presentation = permissionDecisionPresentation(view);
22939
23358
  const lines = [
22940
- field("Tool: ", view.tool, content),
23359
+ field("Tool: ", `${view.tool} \xB7 Action: ${view.actionClass}`, content),
22941
23360
  ...view.target !== void 0 && view.target.length > 0 ? [field("Target: ", view.target, content)] : [],
22942
- field("Action: ", view.actionClass, content),
22943
- field("Asked by: ", askedBy(view), content),
23361
+ ...wrapSentence(`Requested by: ${presentation.requestedByCopy}`, content),
22944
23362
  ...view.artifact !== void 0 ? [
22945
23363
  "",
22946
23364
  "Resolved dispatch plan:",
22947
23365
  ...view.artifact.text.split(/\r?\n/u).flatMap((line) => wrapArtifactLine(line, content))
22948
23366
  ] : [],
22949
23367
  "",
22950
- ...SAFETY_SENTENCES.flatMap((sentence) => wrapSentence(sentence, content))
23368
+ ...wrapSentence(`Approval: ${presentation.authorizationCopy}`, content),
23369
+ ...wrapSentence(`Consequence: ${presentation.consequenceCopy}`, content),
23370
+ ...wrapSentence(presentation.reversibilityCopy, content),
23371
+ ...wrapSentence(`Deny: ${actionConsequence(presentation, "deny")}`, content),
23372
+ ...wrapSentence(`Stop: ${actionConsequence(presentation, "stop")}`, content),
23373
+ ...wrapSentence("Hard-blocked actions remain blocked.", content)
22951
23374
  ];
22952
23375
  if (view.queueDepth !== void 0 && view.queueDepth > 1) {
22953
23376
  lines.splice(lines.indexOf(""), 0, `1 of ${view.queueDepth} parked`);
@@ -23162,7 +23585,7 @@ function mainApprovalRequestView(call, decision, meta, autonomy, queueDepth) {
23162
23585
  const axisFromMeta = axisViewFromId(meta?.axis, autonomy);
23163
23586
  const axisFromDecision = askAxis(decision);
23164
23587
  const axis = axisFromMeta ?? (axisFromDecision.kind === "net" ? { kind: "net", ruleId: axisFromDecision.ruleId } : { kind: "autonomy", level: autonomy });
23165
- const target = describeCallTarget(call.args);
23588
+ const target = describeCallTarget(call.tool, call.args);
23166
23589
  return {
23167
23590
  requestId: meta?.requestId ?? "permission-pending",
23168
23591
  tool: call.tool,
@@ -23171,6 +23594,7 @@ function mainApprovalRequestView(call, decision, meta, autonomy, queueDepth) {
23171
23594
  origin: { kind: "main" },
23172
23595
  reason: decision.kind === "ask" ? decision.rejection.short : `${call.tool} requests ${decision.classification.actionClass}`,
23173
23596
  ...call.tool === ToolNames.Dispatch && decision.kind === "ask" ? { artifact: { kind: "dispatch-plan", text: decision.rejection.detail } } : {},
23597
+ ...call.tool === ToolNames.AskUser ? { exposure: askUserExposure(call.args) } : {},
23174
23598
  ...target.length > 0 ? { target } : {},
23175
23599
  ...queueDepth !== void 0 && queueDepth > 1 ? { queueDepth } : {}
23176
23600
  };
@@ -23185,7 +23609,7 @@ function workerEscalationEntry(payload, autonomy) {
23185
23609
  requestId: payload.requestId,
23186
23610
  agentId: typeof payload.agentId === "string" ? payload.agentId : "worker",
23187
23611
  tool: typeof payload.tool === "string" ? payload.tool : "unknown",
23188
- actionClass: typeof payload.actionClass === "string" ? payload.actionClass : "unknown",
23612
+ actionClass: decisionActionClass(payload.actionClass),
23189
23613
  axis: axisViewFromId(typeof payload.axis === "string" ? payload.axis : void 0, autonomy) ?? (typeof payload.ruleId === "string" ? { kind: "net", ruleId: payload.ruleId } : { kind: "autonomy", level: autonomy }),
23190
23614
  reason: payload.rejection?.short ?? (typeof payload.summary === "string" ? payload.summary : `${payload.tool ?? "worker"} requires approval`),
23191
23615
  ...typeof payload.target === "string" && payload.target.length > 0 ? { target: sanitizeCallTargetText(payload.target).slice(0, 200) } : {}
@@ -23221,6 +23645,12 @@ function createOverlayPermissionLifecycle(deps) {
23221
23645
  const next = workerQueue.shift();
23222
23646
  if (next) openWorker(next);
23223
23647
  };
23648
+ const retryPending = () => {
23649
+ maybeOpenWorker();
23650
+ if (deps.getOverlayState() === "closed" && deps.toolRegistry?.hasParkedCalls()) {
23651
+ deps.toolRegistry.renotifyHead();
23652
+ }
23653
+ };
23224
23654
  const unsubscribePermission = deps.toolRegistry?.onPermissionRequired((call, decision, meta) => {
23225
23655
  const autonomy = deps.getAutonomy();
23226
23656
  const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
@@ -23326,10 +23756,7 @@ function createOverlayPermissionLifecycle(deps) {
23326
23756
  workerQueue.length = 0;
23327
23757
  return;
23328
23758
  }
23329
- maybeOpenWorker();
23330
- if (deps.getOverlayState() === "closed" && deps.toolRegistry?.hasParkedCalls()) {
23331
- deps.toolRegistry.renotifyHead();
23332
- }
23759
+ retryPending();
23333
23760
  };
23334
23761
  return {
23335
23762
  confirm: () => {
@@ -23346,6 +23773,7 @@ function createOverlayPermissionLifecycle(deps) {
23346
23773
  );
23347
23774
  },
23348
23775
  onPermissionOverlayClosed,
23776
+ retryPending,
23349
23777
  dispose: () => {
23350
23778
  unsubscribePermission();
23351
23779
  unsubscribeWorker();
@@ -24050,9 +24478,9 @@ init_esm_shims();
24050
24478
 
24051
24479
  // src/domains/session/cwd-fallback.ts
24052
24480
  init_esm_shims();
24053
- import { existsSync as existsSync6, statSync as statSync2 } from "node:fs";
24481
+ import { existsSync as existsSync7, statSync as statSync2 } from "node:fs";
24054
24482
  var defaultFsProbe = {
24055
- exists: (p) => existsSync6(p),
24483
+ exists: (p) => existsSync7(p),
24056
24484
  isDirectory: (p) => {
24057
24485
  try {
24058
24486
  return statSync2(p).isDirectory();
@@ -24800,7 +25228,7 @@ function payloadRecord2(value) {
24800
25228
  function positiveNumber(value) {
24801
25229
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
24802
25230
  }
24803
- function nonEmptyString2(source, ...keys) {
25231
+ function nonEmptyString3(source, ...keys) {
24804
25232
  for (const key of keys) {
24805
25233
  const value = source[key];
24806
25234
  if (typeof value === "string" && value.trim().length > 0) return value;
@@ -24869,6 +25297,10 @@ function lastTurnSummaryFromLedger(entries, defaults = {}, activeLeafTurnId) {
24869
25297
  let toolErrorCount = 0;
24870
25298
  let targetId = turn.targetId;
24871
25299
  let modelId = turn.modelId;
25300
+ let responseModelIdObservation = {
25301
+ state: "legacy-difference-only",
25302
+ differingModelId: null
25303
+ };
24872
25304
  for (const row2 of turn.rows) {
24873
25305
  const record2 = payloadRecord2(row2.payload);
24874
25306
  if (row2.role === "tool_result") {
@@ -24880,8 +25312,9 @@ function lastTurnSummaryFromLedger(entries, defaults = {}, activeLeafTurnId) {
24880
25312
  sawAssistant = true;
24881
25313
  const reason = stopReasonOf(record2.stopReason);
24882
25314
  if (reason && reason !== "stop") stopReason = reason;
24883
- targetId = targetId ?? nonEmptyString2(record2, "provider", "api");
24884
- modelId = modelId ?? nonEmptyString2(record2, "responseModel", "model");
25315
+ targetId = targetId ?? nonEmptyString3(record2, "provider", "api");
25316
+ modelId = modelId ?? nonEmptyString3(record2, "model");
25317
+ responseModelIdObservation = responseModelIdObservationFromRecord(record2, "legacy-difference-only");
24885
25318
  const usage = payloadRecord2(record2.usage);
24886
25319
  if (!usage) continue;
24887
25320
  inputTokens += positiveNumber(usage.input);
@@ -24908,7 +25341,8 @@ function lastTurnSummaryFromLedger(entries, defaults = {}, activeLeafTurnId) {
24908
25341
  toolErrorCount,
24909
25342
  stopReason,
24910
25343
  watchdogPeak: 0,
24911
- truncated: false
25344
+ truncated: false,
25345
+ responseModelIdObservation
24912
25346
  };
24913
25347
  if (sawReasoning) {
24914
25348
  summary.reasoningTokens = reasoningTokens;
@@ -24931,15 +25365,32 @@ function reseedSessionUsageFromLedger(sink, entries, defaults = {}, activeLeafTu
24931
25365
  sink.resetSession();
24932
25366
  const scoped = filterEntriesToActivePath(entries, activeLeafTurnId ?? void 0);
24933
25367
  for (const call of ledgerUsageCalls(scoped, defaults)) {
24934
- sink.recordTokens(call.providerId, call.modelId, call.totalTokens, call.costUsd, {
24935
- input: call.input,
24936
- output: call.output,
24937
- cacheRead: call.cacheRead,
24938
- cacheWrite: call.cacheWrite,
24939
- reasoningTokens: call.reasoningTokens,
24940
- totalTokens: call.totalTokens,
24941
- apiCalls: call.apiCalls ?? 1
24942
- });
25368
+ const responseModelIdObservationCounts = emptyResponseModelIdObservationCounts();
25369
+ addResponseModelIdObservationCount(
25370
+ responseModelIdObservationCounts,
25371
+ call.responseModelIdObservation,
25372
+ call.apiCalls ?? 1
25373
+ );
25374
+ sink.recordTokens(
25375
+ call.providerId,
25376
+ call.attributedModelId,
25377
+ call.totalTokens,
25378
+ call.costUsd,
25379
+ {
25380
+ input: call.input,
25381
+ output: call.output,
25382
+ cacheRead: call.cacheRead,
25383
+ cacheWrite: call.cacheWrite,
25384
+ reasoningTokens: call.reasoningTokens,
25385
+ totalTokens: call.totalTokens,
25386
+ apiCalls: call.apiCalls ?? 1
25387
+ },
25388
+ void 0,
25389
+ {
25390
+ requestedModelIds: [call.requestedModelId],
25391
+ responseModelIdObservationCounts
25392
+ }
25393
+ );
24943
25394
  }
24944
25395
  }
24945
25396
 
@@ -25005,7 +25456,7 @@ function createOverlaySessionLifecycle(deps) {
25005
25456
  const replayOptions = leafTurnId ? { activeLeafTurnId: leafTurnId } : {};
25006
25457
  deps.resetTranscript();
25007
25458
  rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
25008
- const replayMessages = buildReplayAgentMessagesFromTurns(turns, replayOptions);
25459
+ const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, replayOptions);
25009
25460
  deps.chat.resetForSession(leafTurnId, replayMessages);
25010
25461
  rescopeToBranch(session, turns, leafTurnId);
25011
25462
  } catch (error) {
@@ -25055,7 +25506,7 @@ function createOverlaySessionLifecycle(deps) {
25055
25506
  const turns = deps.readStructuredEntries(sessionId);
25056
25507
  deps.resetTranscript();
25057
25508
  rehydrateChatPanelFromTurns(deps.chatPanel, turns, { uptoTurnId: turnId });
25058
- const replayMessages = buildReplayAgentMessagesFromTurns(turns, { uptoTurnId: turnId });
25509
+ const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, { uptoTurnId: turnId });
25059
25510
  deps.chat.resetForSession(turnId, replayMessages);
25060
25511
  rescopeToBranch(session, turns, turnId);
25061
25512
  } catch (error) {
@@ -25121,8 +25572,11 @@ function createOverlaySessionLifecycle(deps) {
25121
25572
  try {
25122
25573
  const turns = deps.readStructuredEntries(forkedSessionId);
25123
25574
  rehydrateChatPanelFromTurns(deps.chatPanel, turns);
25124
- const replayMessages = buildReplayAgentMessagesFromTurns(turns);
25125
25575
  const leafTurnId = session.tree(forkedSessionId).leafId ?? parentTurnId;
25576
+ const replayMessages = buildModelReplayAgentMessagesFromTurns(
25577
+ turns,
25578
+ leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
25579
+ );
25126
25580
  deps.chat.resetForSession(leafTurnId, replayMessages);
25127
25581
  rescopeToBranch(session, turns, leafTurnId);
25128
25582
  } catch (error) {
@@ -25165,7 +25619,9 @@ function createOverlayTransitions(deps) {
25165
25619
  return state;
25166
25620
  },
25167
25621
  set state(next) {
25622
+ const closing = state !== "closed" && next === "closed";
25168
25623
  state = next;
25624
+ if (closing) deps.onOverlayClosed?.();
25169
25625
  },
25170
25626
  get handle() {
25171
25627
  return handle;
@@ -25188,6 +25644,7 @@ function createOverlayTransitions(deps) {
25188
25644
  handle?.hide();
25189
25645
  handle = null;
25190
25646
  if (leaving === "permission-confirm") deps.onPermissionOverlayClosed();
25647
+ else deps.onOverlayClosed?.();
25191
25648
  deps.renderContextIsland();
25192
25649
  deps.renderTaskIsland();
25193
25650
  deps.requestRender();
@@ -25199,9 +25656,17 @@ init_esm_shims();
25199
25656
  function isEscapeKey(data) {
25200
25657
  return matchesKey(data, "escape") && !isKeyRelease(data);
25201
25658
  }
25659
+ var DRAFT_EDIT_KEYS = ["backspace", "delete", "ctrl+u", "ctrl+w", "alt+backspace", "ctrl+k"];
25660
+ function isDraftEditKey(data) {
25661
+ return !isKeyRelease(data) && DRAFT_EDIT_KEYS.some((key) => matchesKey(data, key));
25662
+ }
25202
25663
  function routePermissionOverlayKey(data, deps) {
25203
25664
  if (matchesKey(data, "enter") && !isKeyRelease(data)) {
25204
- deps.confirmPermission();
25665
+ if (!(deps.composerHasDraft?.() ?? false)) deps.confirmPermission();
25666
+ return true;
25667
+ }
25668
+ if (deps.editDraft && isDraftEditKey(data)) {
25669
+ deps.editDraft(data);
25205
25670
  return true;
25206
25671
  }
25207
25672
  if (isEscapeKey(data)) {
@@ -25236,6 +25701,10 @@ function routeDispatchBoardOverlayKey(data, deps) {
25236
25701
  deps.cancelSelectedDispatch();
25237
25702
  return true;
25238
25703
  }
25704
+ if (matchesKey(data, "enter")) {
25705
+ deps.toggleSelectedDispatchDetail();
25706
+ return true;
25707
+ }
25239
25708
  return false;
25240
25709
  }
25241
25710
  function routeAuthOverlayKey(data, deps) {
@@ -25375,7 +25844,8 @@ function createOverlayLifecycle(deps) {
25375
25844
  requestRender: () => tui.requestRender(),
25376
25845
  cancelPendingAskUser: () => overlayAskUser?.cancelPending() ?? false,
25377
25846
  finishAuth: (dismiss) => overlayAuth.finish(dismiss),
25378
- onPermissionOverlayClosed: () => overlayPermission?.onPermissionOverlayClosed()
25847
+ onPermissionOverlayClosed: () => overlayPermission?.onPermissionOverlayClosed(),
25848
+ onOverlayClosed: () => overlayPermission?.retryPending()
25379
25849
  });
25380
25850
  const closeOverlay = overlayTransitions.close;
25381
25851
  const overlayAuth = createOverlayAuthLifecycle({
@@ -25407,10 +25877,13 @@ function createOverlayLifecycle(deps) {
25407
25877
  if (overlayTransitions.state !== "closed") return false;
25408
25878
  overlayTransitions.state = "permission-confirm";
25409
25879
  overlayTransitions.handle = showOverlayFrame(tui, createPermissionOverlayBody(view), {
25410
- anchor: "center",
25880
+ ...permissionOverlayPlacement(tui, editor, footer.view),
25411
25881
  width: PERMISSION_OVERLAY_WIDTH,
25412
- title: permissionOverlayTitle(),
25413
- footerHint: (innerWidth) => permissionOverlayHint(innerWidth)
25882
+ title: permissionOverlayTitle(view),
25883
+ tone: permissionOverlayTone(view),
25884
+ // Read per frame: the footer names what Enter does right now, and
25885
+ // that depends on whether the composer holds a draft.
25886
+ footerHint: (innerWidth) => permissionOverlayHint(innerWidth, editor.getText().length > 0)
25414
25887
  });
25415
25888
  tui.requestRender();
25416
25889
  return true;
@@ -25525,6 +25998,7 @@ function createOverlayLifecycle(deps) {
25525
25998
  stateDir: deps.app.stateDir,
25526
25999
  getSessionMeta: () => deps.app.session?.current() ?? null,
25527
26000
  ...deps.app.readSessionEntries ? { readSessionEntries: deps.app.readSessionEntries } : {},
26001
+ ...deps.app.getSettings ? { getSettings: deps.app.getSettings } : {},
25528
26002
  terminal,
25529
26003
  dispatchBoard,
25530
26004
  startDispatchBoardTicker: () => interactiveTickers.startDispatchBoardTicker(),
@@ -27047,6 +27521,11 @@ function createInteractiveInputRuntime(deps) {
27047
27521
  cancelPermission: () => deps.overlay.closeOverlay(),
27048
27522
  confirmPermission: () => deps.overlay.confirmPermission(),
27049
27523
  stopTurnFromPermission: () => deps.overlay.stopTurnFromPermission(),
27524
+ composerHasDraft: () => deps.editor.getText().length > 0,
27525
+ editDraft: (data2) => {
27526
+ deps.editor.handleInput?.(data2);
27527
+ deps.requestRender();
27528
+ },
27050
27529
  closeOverlay: () => deps.overlay.closeOverlay(),
27051
27530
  selectPreviousDispatch: () => {
27052
27531
  deps.dispatchBoard.selectPrevious();
@@ -27056,6 +27535,10 @@ function createInteractiveInputRuntime(deps) {
27056
27535
  deps.dispatchBoard.selectNext();
27057
27536
  deps.requestRender();
27058
27537
  },
27538
+ toggleSelectedDispatchDetail: () => {
27539
+ deps.dispatchBoard.toggleDetail();
27540
+ deps.requestRender();
27541
+ },
27059
27542
  steerSelectedDispatch: deps.steerSelectedDispatch,
27060
27543
  cancelSelectedDispatch: deps.cancelSelectedDispatch,
27061
27544
  cancelAskUser: () => deps.overlay.cancelAskUser()
@@ -27950,7 +28433,7 @@ function styleStatusVerb(text, toneHint) {
27950
28433
  function policySegmentFold(seg, detail) {
27951
28434
  if (!seg.finished) return policyRunningToolFold(detail);
27952
28435
  if (seg.isError && detail.errors === "body") return "expanded";
27953
- return policyToolFold(detail, toolPresentationPolicy(seg.name, seg.args));
28436
+ return policyToolFold(detail, toolResultPresentationPolicy(seg.result) ?? toolPresentationPolicy(seg.name, seg.args));
27954
28437
  }
27955
28438
  function toolSegmentExpanded(seg, detail) {
27956
28439
  return resolveFold(seg.fold, policySegmentFold(seg, detail)) === "expanded";
@@ -27978,7 +28461,8 @@ function renderToolSegmentLines(seg, width, expandKey, latestHintToolId, nowMs,
27978
28461
  durationMs: seg.durationMs,
27979
28462
  resultSummary: seg.resultSummary,
27980
28463
  outcome: seg.settlement,
27981
- blockReason: seg.blockReason
28464
+ blockReason: seg.blockReason,
28465
+ evictedReason: seg.evictedReason
27982
28466
  } : { toolCallId: seg.id, toolName: seg.name, args: seg.args, elapsedMs, phase },
27983
28467
  width,
27984
28468
  hintKey,
@@ -28009,7 +28493,8 @@ function renderToolSegmentLines(seg, width, expandKey, latestHintToolId, nowMs,
28009
28493
  durationMs: seg.durationMs,
28010
28494
  resultSummary: seg.resultSummary,
28011
28495
  outcome: seg.settlement,
28012
- blockReason: seg.blockReason
28496
+ blockReason: seg.blockReason,
28497
+ evictedReason: seg.evictedReason
28013
28498
  },
28014
28499
  width,
28015
28500
  { unbounded: unboundedToolBodies, diffStyle: seg.replayed === true ? "plain" : "color" }
@@ -28706,6 +29191,7 @@ function createChatPanel(options = {}) {
28706
29191
  tool.result = event.result;
28707
29192
  tool.isError = event.isError;
28708
29193
  const enriched = event;
29194
+ tool.evictedReason = typeof enriched.evictedReason === "string" && enriched.evictedReason.length > 0 ? enriched.evictedReason : void 0;
28709
29195
  const settled = toolSettlement(enriched);
28710
29196
  tool.settlement = settled?.settlement;
28711
29197
  tool.blockReason = settled?.reason;
@@ -29626,7 +30112,7 @@ function processAutoPacingAllowed(backpressureObserved, env = process.env) {
29626
30112
 
29627
30113
  // src/interactive/welcome-dashboard.ts
29628
30114
  init_esm_shims();
29629
- import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
30115
+ import { existsSync as existsSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
29630
30116
  import { basename as basename3, join as join8 } from "node:path";
29631
30117
  function activeStatus(status) {
29632
30118
  return status.available && status.health.status !== "down";
@@ -29694,7 +30180,7 @@ function wikiFactsFrom(cwd, staleness) {
29694
30180
  }
29695
30181
  function handoffFacts(cwd) {
29696
30182
  const handoffsDir = join8(cwd, ".clio-coder", "handoffs");
29697
- if (!existsSync7(handoffsDir)) return { handoffCount: 0, handoffFreshness: "none" };
30183
+ if (!existsSync8(handoffsDir)) return { handoffCount: 0, handoffFreshness: "none" };
29698
30184
  try {
29699
30185
  const files = readdirSync2(handoffsDir).filter((f) => f.startsWith("handoff-") && f.endsWith(".md"));
29700
30186
  if (files.length === 0) return { handoffCount: 0, handoffFreshness: "none" };
@@ -29738,7 +30224,7 @@ async function readWelcomeRepositoryFactsAsync(cwd) {
29738
30224
  function placeholderRepositoryFacts(cwd) {
29739
30225
  return {
29740
30226
  clioMdStatus: clioMdStatusFor(cwd),
29741
- hasCodewiki: existsSync7(join8(cwd, ".clio-coder", "codewiki.json")),
30227
+ hasCodewiki: existsSync8(join8(cwd, ".clio-coder", "codewiki.json")),
29742
30228
  codewikiCount: 0,
29743
30229
  wikiPageCount: 0,
29744
30230
  wikiStatus: NO_WIKI_STATUS,
@@ -30028,7 +30514,11 @@ function createInteractivePresentation(deps) {
30028
30514
  bus: deps.bus,
30029
30515
  ...deps.getSettings ? { getSettings: deps.getSettings } : {}
30030
30516
  });
30031
- const dispatchBoardStore = factories.createDispatchBoardStore(deps.bus, () => deps.dispatch.snapshot());
30517
+ const dispatchBoardStore = factories.createDispatchBoardStore(
30518
+ deps.bus,
30519
+ () => deps.dispatch.snapshot(),
30520
+ (runId) => readWorkerReceiptFacts(runId)
30521
+ );
30032
30522
  const contextActivityStore = factories.createContextActivityStore(deps.bus);
30033
30523
  const footerToolCounts = /* @__PURE__ */ new Map();
30034
30524
  const footerActiveTools = /* @__PURE__ */ new Set();
@@ -30173,6 +30663,7 @@ function createInteractivePresentation(deps) {
30173
30663
  )?.thinking.display ?? current?.orchestrator?.thinkingLevel ?? "off";
30174
30664
  },
30175
30665
  isStreaming: () => deps.chat.isStreaming(),
30666
+ ...deps.isAwaitingApproval ? { isAwaitingApproval: deps.isAwaitingApproval } : {},
30176
30667
  willEnterSteer: (text) => willEnterSteerActiveWork(deps, text),
30177
30668
  getSubmitKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.submit")[0], "Enter"),
30178
30669
  getNewlineKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.newLine")[0], "Shift+Enter")
@@ -30304,9 +30795,9 @@ function createInteractivePresentation(deps) {
30304
30795
  root,
30305
30796
  getObservabilitySnapshot: () => observabilitySnapshot,
30306
30797
  recordChatEvent,
30307
- recordToolStart: (toolCallId, toolName2) => {
30798
+ recordToolStart: (toolCallId, toolName) => {
30308
30799
  footerActiveTools.add(toolCallId);
30309
- footerToolCounts.set(toolName2, (footerToolCounts.get(toolName2) ?? 0) + 1);
30800
+ footerToolCounts.set(toolName, (footerToolCounts.get(toolName) ?? 0) + 1);
30310
30801
  },
30311
30802
  recordToolEnd: ({ toolCallId, isError, truncated }) => {
30312
30803
  footerActiveTools.delete(toolCallId);
@@ -30342,6 +30833,57 @@ init_esm_shims();
30342
30833
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
30343
30834
  import { join as join9, resolve as resolve4 } from "node:path";
30344
30835
 
30836
+ // src/interactive/context-recall-command.ts
30837
+ init_esm_shims();
30838
+ function formatTokens3(tokens) {
30839
+ return tokens.toLocaleString("en-US");
30840
+ }
30841
+ function headlineFor(ref, tokens, state, offloadPath) {
30842
+ const parts = [ref];
30843
+ if (state !== void 0) {
30844
+ parts.push(state.by === void 0 ? `evicted: ${state.reason}` : `evicted: ${state.reason} by ${state.by}`);
30845
+ }
30846
+ parts.push(`${formatTokens3(tokens)} tokens`);
30847
+ if (offloadPath !== void 0) parts.push(`offload: ${offloadPath}`);
30848
+ return `[/context recall] ${parts.join(" \xB7 ")}`;
30849
+ }
30850
+ function runOperatorRecall(ref, deps) {
30851
+ if (!deps.hasSession()) {
30852
+ return { ok: false, message: "[/context recall] no active session; start one with /new or /resume first" };
30853
+ }
30854
+ const trimmed = ref.trim();
30855
+ if (trimmed.length === 0) {
30856
+ return { ok: false, message: "[/context recall] needs a ref: the turnId named in an [evicted ...] marker" };
30857
+ }
30858
+ const entries = deps.readEntries();
30859
+ const leaf = deps.activeLeafTurnId();
30860
+ const view = foldWorkingSet(entries, leaf);
30861
+ const resolved = resolveRecall(entries, view, trimmed, leaf);
30862
+ if (!resolved.ok)
30863
+ return { ok: false, message: `[/context recall] ${recallErrorMessage(resolved.error, entries, view)}` };
30864
+ const { result } = resolved;
30865
+ const fields = buildRecallFields(result, { trigger: "operator" });
30866
+ try {
30867
+ deps.appendEntry({ ...fields, parentTurnId: recallParentTurnId(entries, leaf) });
30868
+ } catch (err) {
30869
+ return {
30870
+ ok: false,
30871
+ message: `[/context recall] recall of ${result.ref.entry} could not be recorded: ${err instanceof Error ? err.message : String(err)}`
30872
+ };
30873
+ }
30874
+ deps.onRecalled?.({
30875
+ ref: result.ref.entry,
30876
+ trigger: "operator",
30877
+ tokensReadmitted: result.tokens,
30878
+ at: deps.now?.() ?? Date.now()
30879
+ });
30880
+ return {
30881
+ ok: true,
30882
+ headline: headlineFor(result.ref.entry, result.tokens, view.evicted.get(result.ref.entry), result.offloadPath),
30883
+ body: result.body
30884
+ };
30885
+ }
30886
+
30345
30887
  // src/interactive/export-html/index.ts
30346
30888
  init_esm_shims();
30347
30889
 
@@ -30899,6 +31441,34 @@ function createInteractiveSlashRuntime(deps) {
30899
31441
  }
30900
31442
  deps.openContextReset();
30901
31443
  },
31444
+ ...deps.session ? {
31445
+ runContextRecall: (ref) => {
31446
+ const session = deps.session;
31447
+ if (!session) return;
31448
+ const outcome = runOperatorRecall(ref, {
31449
+ hasSession: () => session.current() !== null,
31450
+ readEntries: () => {
31451
+ const sessionId = deps.chat.getSessionId();
31452
+ return sessionId ? deps.readStructuredEntries(sessionId) : [];
31453
+ },
31454
+ activeLeafTurnId: () => {
31455
+ const meta = session.current();
31456
+ return meta ? session.tree(meta.id).leafId ?? void 0 : void 0;
31457
+ },
31458
+ appendEntry: (entry) => session.appendEntry(entry),
31459
+ onRecalled: (payload) => deps.bus.emit(BusChannels.ContextRecalled, payload),
31460
+ ...deps.now ? { now: () => (deps.now?.() ?? /* @__PURE__ */ new Date()).getTime() } : {}
31461
+ });
31462
+ if (!outcome.ok) {
31463
+ appendCommandNotice("error", outcome.message);
31464
+ return;
31465
+ }
31466
+ appendCommandNotice("success", outcome.headline);
31467
+ deps.io.stdout(`${outcome.body}
31468
+ `);
31469
+ deps.requestRender();
31470
+ }
31471
+ } : {},
30902
31472
  runContextRefresh: () => {
30903
31473
  if (!deps.onContextRefresh) {
30904
31474
  deps.io.stderr("[/context refresh] context refresh not wired; pass onContextRefresh to startInteractive\n");
@@ -31160,7 +31730,7 @@ function createSessionTranscript(deps) {
31160
31730
  const turns = deps.readSessionEntries();
31161
31731
  deps.chat.resetForSession(
31162
31732
  leafTurnId,
31163
- buildReplayAgentMessagesFromTurns(turns, {
31733
+ buildModelReplayAgentMessagesFromTurns(turns, {
31164
31734
  ...leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
31165
31735
  })
31166
31736
  );
@@ -31398,10 +31968,12 @@ async function createInteractiveApplication(deps) {
31398
31968
  const { readStructuredEntries, recordSubmittedTurn } = sessionTranscript;
31399
31969
  let leaderArmed = false;
31400
31970
  let shutdownArmed = false;
31971
+ let overlayLifecycle;
31401
31972
  const presentation = createInteractivePresentation({
31402
31973
  bus: deps.bus,
31403
31974
  getLeaderArmed: () => leaderArmed,
31404
31975
  getShutdownArmed: () => shutdownArmed,
31976
+ isAwaitingApproval: () => overlayLifecycle?.getState() === "permission-confirm",
31405
31977
  resolveVisibleEventSequence: (event) => visibleEventIngress.get(event)?.traceSequence ?? null,
31406
31978
  resolveStreamIngress: (event) => visibleEventIngress.get(event) ?? null,
31407
31979
  commitFrame: (reason) => shell.commitCurrentFrame(reason === "teardown" ? 300 : 3e4),
@@ -31470,7 +32042,7 @@ async function createInteractiveApplication(deps) {
31470
32042
  isAskUserWaiting: () => overlayLifecycle.isAskUserWaiting(),
31471
32043
  closeAskUserSession: () => overlayLifecycle.closeAskUserSession(),
31472
32044
  resetAskUserCancellation: () => overlayLifecycle.resetAskUserCancellation(),
31473
- recordToolStart: (toolName2, toolCallId) => presentation.recordToolStart(toolCallId, toolName2),
32045
+ recordToolStart: (toolName, toolCallId) => presentation.recordToolStart(toolCallId, toolName),
31474
32046
  recordToolEnd: (_toolName, toolCallId, isError, truncated) => presentation.recordToolEnd({ toolCallId, isError, truncated }),
31475
32047
  setStatusLine: (line) => chatPanel.setStatusLine(line),
31476
32048
  setLiveReasoning: (view) => chatPanel.setLiveReasoning(view),
@@ -31576,7 +32148,6 @@ async function createInteractiveApplication(deps) {
31576
32148
  notify
31577
32149
  });
31578
32150
  editor.onSubmit = editorSubmit.submitEditorText;
31579
- let overlayLifecycle;
31580
32151
  const interactiveTickers = createInteractiveTickers({
31581
32152
  tui,
31582
32153
  dispatchBoardStore,
@@ -31862,7 +32433,7 @@ import { resolve as resolve5 } from "node:path";
31862
32433
 
31863
32434
  // src/tools/dispatch-scout-admission.ts
31864
32435
  init_esm_shims();
31865
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
32436
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
31866
32437
  import { join as join10 } from "node:path";
31867
32438
  var MAX_SCOUT_PLAN_DEADLINE_MS = 36e5;
31868
32439
  function record(value) {
@@ -31911,7 +32482,7 @@ function loadVerifiedScoutSource(input) {
31911
32482
  const envelope = input.dispatch.getRun(input.ref.runId);
31912
32483
  if (envelope === null) throw new Error(`dispatch: Scout source run '${input.ref.runId}' is unavailable`);
31913
32484
  const receiptPath = envelope.receiptPath ?? join10(clioStateDir(), "receipts", `${input.ref.runId}.json`);
31914
- if (!existsSync8(receiptPath)) throw new Error(`dispatch: Scout source receipt '${input.ref.runId}' is unavailable`);
32485
+ if (!existsSync9(receiptPath)) throw new Error(`dispatch: Scout source receipt '${input.ref.runId}' is unavailable`);
31915
32486
  let receipt;
31916
32487
  try {
31917
32488
  receipt = JSON.parse(readFileSync8(receiptPath, "utf8"));
@@ -32504,6 +33075,24 @@ function createDispatchAdmissionController(deps) {
32504
33075
 
32505
33076
  // src/tools/dispatch.ts
32506
33077
  var THINKING_LEVELS2 = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
33078
+ var DispatchBudgetPhaseSchema = typebox_exports.Object(
33079
+ {
33080
+ toolCalls: typebox_exports.Integer({ minimum: 1 }),
33081
+ readReserve: typebox_exports.Integer({ minimum: 0 })
33082
+ },
33083
+ { additionalProperties: false }
33084
+ );
33085
+ var DispatchBudgetSchema = typebox_exports.Object(
33086
+ {
33087
+ toolCalls: typebox_exports.Integer({ minimum: 1, description: "Requested tool-call phase boundary." }),
33088
+ readReserve: typebox_exports.Integer({ minimum: 0, description: "Requested tail reserve for canonical read calls." }),
33089
+ retryRevision: typebox_exports.Optional(DispatchBudgetPhaseSchema)
33090
+ },
33091
+ {
33092
+ additionalProperties: false,
33093
+ description: "Invocation budget inside the recipe policy. retryRevision preauthorizes one ceiling for retry, result-contract revision, or review revision phases."
33094
+ }
33095
+ );
32507
33096
  function admissionFailureResult(message) {
32508
33097
  return {
32509
33098
  kind: "error",
@@ -32518,12 +33107,12 @@ function createDispatchTool(inputDeps, surfaceOptions = {}) {
32518
33107
  const admission = createDispatchAdmissionController(deps);
32519
33108
  let runnerPromise = null;
32520
33109
  const loadRunner = () => {
32521
- runnerPromise ??= (surfaceOptions.loadRunner ?? (() => import("./dispatch-runner-QPRDDBDX.js")))();
33110
+ runnerPromise ??= (surfaceOptions.loadRunner ?? (() => import("./dispatch-runner-GIJBHNFL.js")))();
32522
33111
  return runnerPromise;
32523
33112
  };
32524
33113
  return {
32525
33114
  name: ToolNames.Dispatch,
32526
- description: 'Dispatch one bounded task with task, or a batch with tasks (never both). Singular example: {agent:"debugger", task:"Verify the receipt boundary", briefing:"Prior receipt evidence...", detach:true}. task is the worker assignment; briefing is separate bounded parent context/data and cannot replace task. Ordinary calls auto-wait; detach:true returns ids for monitoring/steering, and collect is the authoritative terminal batch operation before final synthesis. Batch modes are parallel (default), sequential, pipeline, or compete. Task objects may include persona and tool_profile. Sealed receipts are durable evidence; report receipt integrity, evidence verification, briefing provenance, and project-context provenance separately. Call with list:true to see agents. Do not repeat an identical successful dispatch in the same user turn. Prefer this tool over inline exploration whenever work is read-only fan-out, parallel investigation, or the operator asked for a worker by name; if you cannot dispatch, say so plainly and name the reason, and never narrate or summarize a worker you did not actually dispatch.',
33115
+ description: 'Dispatch one bounded task with task, or a batch with tasks (never both). Singular example: {agent:"debugger", task:"Verify the receipt boundary", briefing:"Prior receipt evidence...", detach:true}. task is the worker assignment; briefing is separate bounded parent context/data and cannot replace task. Ordinary calls auto-wait; detach:true returns ids for monitoring/steering, and collect is the authoritative terminal batch operation before final synthesis. Batch modes are parallel (default), sequential, pipeline, or compete. Task objects may include persona, tool_profile, and a recipe-admitted budget envelope. Sealed receipts are durable evidence; report receipt integrity, evidence verification, briefing provenance, and project-context provenance separately. Call with list:true to see agents. Do not repeat an identical successful dispatch in the same user turn. Prefer this tool over inline exploration whenever work is read-only fan-out, parallel investigation, or the operator asked for a worker by name; if you cannot dispatch, say so plainly and name the reason, and never narrate or summarize a worker you did not actually dispatch.',
32527
33116
  parameters: typebox_exports.Object({
32528
33117
  list: typebox_exports.Optional(typebox_exports.Boolean({ description: "List available agents instead of dispatching." })),
32529
33118
  from_scout: typebox_exports.Optional(
@@ -32563,6 +33152,7 @@ function createDispatchTool(inputDeps, surfaceOptions = {}) {
32563
33152
  tool_profile: typebox_exports.Optional(
32564
33153
  StringEnum(TOOL_PROFILE_NAMES, { description: "Narrow this worker's available tools." })
32565
33154
  ),
33155
+ budget: typebox_exports.Optional(DispatchBudgetSchema),
32566
33156
  target: typebox_exports.Optional(typebox_exports.String()),
32567
33157
  model: typebox_exports.Optional(typebox_exports.String()),
32568
33158
  node: typebox_exports.Optional(typebox_exports.String({ description: "Fleet node pin: local or a fleet.nodes id." })),
@@ -32644,6 +33234,7 @@ function createDispatchTool(inputDeps, surfaceOptions = {}) {
32644
33234
  typebox_exports.String({ description: "Default ad-hoc specialist persona for dispatched tasks, max 8000 chars." })
32645
33235
  ),
32646
33236
  tool_profile: typebox_exports.Optional(StringEnum(TOOL_PROFILE_NAMES, { description: "Default worker tool profile." })),
33237
+ budget: typebox_exports.Optional(DispatchBudgetSchema),
32647
33238
  target: typebox_exports.Optional(typebox_exports.String({ description: "Default configured target id (omit for fleet default)." })),
32648
33239
  model: typebox_exports.Optional(typebox_exports.String({ description: "Default model override." })),
32649
33240
  node: typebox_exports.Optional(
@@ -32722,7 +33313,7 @@ function registerAllTools(registry, deps = {}) {
32722
33313
  ...builtin(
32723
33314
  lazyTool(
32724
33315
  monitorToolSurface,
32725
- async () => (await import("./monitor-GE4ID3IA.js")).createMonitorTool({ dispatch, runEvents: dispatchRunEvents })
33316
+ async () => (await import("./monitor-US5F5YGZ.js")).createMonitorTool({ dispatch, runEvents: dispatchRunEvents })
32726
33317
  ),
32727
33318
  {
32728
33319
  path: "src/tools/monitor.ts",
@@ -32796,11 +33387,11 @@ function createFileMutationObserver(sink) {
32796
33387
  hooks: ["after_tool"],
32797
33388
  evaluate(input) {
32798
33389
  if (input.metadata?.resultKind !== "ok") return NO_EFFECTS;
32799
- const toolName2 = input.toolName ?? "";
32800
- const paths = toolMutationPaths(toolName2, input.toolArgs !== void 0 ? { ...input.toolArgs } : void 0);
33390
+ const toolName = input.toolName ?? "";
33391
+ const paths = toolMutationPaths(toolName, input.toolArgs !== void 0 ? { ...input.toolArgs } : void 0);
32801
33392
  if (paths.length === 0) return NO_EFFECTS;
32802
33393
  try {
32803
- sink({ paths, toolName: toolName2 });
33394
+ sink({ paths, toolName });
32804
33395
  } catch {
32805
33396
  }
32806
33397
  return NO_EFFECTS;
@@ -33149,6 +33740,7 @@ async function bootOrchestrator(options = {}) {
33149
33740
  installBusTracer();
33150
33741
  termination.installSignalHandlers();
33151
33742
  ensureClioState();
33743
+ sweepExpiredToolOffloads();
33152
33744
  timer.mark("install check");
33153
33745
  if (isGitRepository(process.cwd())) {
33154
33746
  try {
@@ -33552,7 +34144,14 @@ async function bootOrchestrator(options = {}) {
33552
34144
  });
33553
34145
  const dispatchBackground = createDispatchBackgroundRegistry();
33554
34146
  registerAllTools(toolRegistry, {
33555
- ...session ? { session } : {},
34147
+ ...session ? {
34148
+ session,
34149
+ readSessionEntries: () => {
34150
+ const meta = session.current();
34151
+ return meta ? readSessionEntriesForCompact(meta.id) : [];
34152
+ },
34153
+ onContextRecalled: (payload) => bus.emit(BusChannels.ContextRecalled, payload)
34154
+ } : {},
33556
34155
  taskBoard,
33557
34156
  userTasks,
33558
34157
  dispatch,
@@ -33765,8 +34364,13 @@ async function bootOrchestrator(options = {}) {
33765
34364
  getMemorySection: () => {
33766
34365
  try {
33767
34366
  const records = loadMemoryRecordsSync(clioDataDir());
34367
+ const settings = getCurrentSettings();
34368
+ const targetId = session?.current()?.target ?? settings.orchestrator?.target;
34369
+ const runtimeId = targetId ? providers.getTarget(targetId)?.runtime : void 0;
33768
34370
  return buildMemoryPromptSection(records, {
33769
- activeRepository: canonicalMemoryRepositoryIdentity(process.cwd())
34371
+ scopes: ["global", "repo", "runtime"],
34372
+ activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
34373
+ activeRuntime: runtimeId === void 0 ? null : { kind: "runtime", key: runtimeId }
33770
34374
  }).section;
33771
34375
  } catch {
33772
34376
  return "";
@@ -33774,7 +34378,17 @@ async function bootOrchestrator(options = {}) {
33774
34378
  },
33775
34379
  getTaskMemoryHandoffSource: () => {
33776
34380
  ensureTaskMemorySession();
33777
- return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot());
34381
+ const meta = session?.current();
34382
+ if (!meta) throw new Error("task memory handoff requires an active session");
34383
+ const settings = getCurrentSettings();
34384
+ const targetId = meta.target ?? settings.orchestrator?.target;
34385
+ const runtimeId = targetId ? providers.getTarget(targetId)?.runtime : void 0;
34386
+ return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot(), {
34387
+ sessionId: meta.id,
34388
+ evidenceRefs: [`session-${meta.id}`],
34389
+ runtimeIds: runtimeId === void 0 ? [] : [runtimeId],
34390
+ agentIds: []
34391
+ });
33778
34392
  },
33779
34393
  registerDeferredReminderSink: (sink) => {
33780
34394
  deferredMemoryReminderSink = sink;
@@ -33808,7 +34422,10 @@ async function bootOrchestrator(options = {}) {
33808
34422
  try {
33809
34423
  chat.resetForSession(
33810
34424
  leafTurnId,
33811
- buildReplayAgentMessagesFromTurns(readCurrentSessionEntries(), leafTurnId ? { activeLeafTurnId: leafTurnId } : {})
34425
+ buildModelReplayAgentMessagesFromTurns(
34426
+ readCurrentSessionEntries(),
34427
+ leafTurnId ? { activeLeafTurnId: leafTurnId } : {}
34428
+ )
33812
34429
  );
33813
34430
  } catch (err) {
33814
34431
  chat.resetForSession(leafTurnId);
@@ -33846,7 +34463,7 @@ async function bootOrchestrator(options = {}) {
33846
34463
  ...session ? { session } : {},
33847
34464
  ...session ? {
33848
34465
  readSessionEntries: readSessionEntriesForCompact,
33849
- buildReplayMessages: (entries, leafTurnId) => buildReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId })
34466
+ buildReplayMessages: (entries, leafTurnId) => buildModelReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId })
33850
34467
  } : {},
33851
34468
  providers,
33852
34469
  settings: {
@@ -34156,4 +34773,4 @@ export {
34156
34773
  resolveApiKeyForTarget,
34157
34774
  synthesizeOrchestratorModel
34158
34775
  };
34159
- //# sourceMappingURL=orchestrator-EM5MC3HM.js.map
34776
+ //# sourceMappingURL=orchestrator-E2AL4T5N.js.map