@iowarp/clio-coder 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +7 -3
  3. package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
  4. package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
  7. package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
  11. package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
  13. package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
  14. package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
  17. package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
  18. package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
  21. package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
  22. package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
  23. package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
  27. package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
  28. package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
  29. package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
  30. package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
  31. package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
  32. package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
  33. package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
  34. package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
  35. package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
  36. package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
  37. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  38. package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
  39. package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
  40. package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
  41. package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
  42. package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
  43. package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
  44. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  45. package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
  46. package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
  47. package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
  48. package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
  49. package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
  50. package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
  51. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  52. package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
  53. package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
  54. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  55. package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
  56. package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
  57. package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
  58. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  59. package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
  60. package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
  61. package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
  62. package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
  63. package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
  64. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  65. package/dist/chunk-RKKLTLYB.js +45 -0
  66. package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
  67. package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
  68. package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
  69. package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
  70. package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
  71. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  72. package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
  73. package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
  74. package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
  75. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  76. package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
  77. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  78. package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
  79. package/dist/chunk-YSEHGPCT.js +127 -0
  80. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  81. package/dist/cli/index.js +27 -27
  82. package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
  83. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
  84. package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
  85. package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
  86. package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
  87. package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
  88. package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
  89. package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
  90. package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
  91. package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
  92. package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
  93. package/dist/doctor-TWBWFK5V.js +165 -0
  94. package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
  95. package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
  96. package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
  97. package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
  98. package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
  99. package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
  100. package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
  101. package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
  102. package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
  103. package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
  104. package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
  105. package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
  106. package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
  107. package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
  108. package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
  109. package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
  110. package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
  111. package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
  112. package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
  113. package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
  114. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  115. package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
  116. package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
  117. package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
  118. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  119. package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
  120. package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
  121. package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
  122. package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
  123. package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
  124. package/dist/worker/entry.js +29 -33
  125. package/docs/alcf-provider.md +1 -1
  126. package/docs/architecture.md +1 -1
  127. package/docs/artifact-versions.md +6 -1
  128. package/docs/built-in-agents.md +1 -1
  129. package/docs/capacity-and-scheduling.md +23 -2
  130. package/docs/commands-and-modes.md +1 -1
  131. package/docs/configuration-and-targets.md +30 -3
  132. package/docs/context-engine.md +63 -4
  133. package/docs/documentation-coverage.md +3 -3
  134. package/docs/documentation-guide.md +1 -1
  135. package/docs/environment-variables.md +2 -0
  136. package/docs/eval-runner.md +262 -11
  137. package/docs/evals-internal.md +72 -2
  138. package/docs/evidence-and-memory.md +11 -10
  139. package/docs/evolution.md +1 -1
  140. package/docs/extensions-and-sharing.md +3 -1
  141. package/docs/fleet-dispatch.md +4 -4
  142. package/docs/installation-and-lifecycle.md +1 -1
  143. package/docs/middleware-and-components.md +1 -1
  144. package/docs/model-catalog.md +1 -1
  145. package/docs/observability.md +53 -2
  146. package/docs/proactive-memory.md +127 -14
  147. package/docs/prompt-envelope-and-tools.md +19 -1
  148. package/docs/provider-adapter-cookbook.md +1 -1
  149. package/docs/release-cut-checklist.md +19 -3
  150. package/docs/safety-model.md +1 -1
  151. package/docs/scientific-validation.md +1 -1
  152. package/docs/skills-marketplace.md +1 -1
  153. package/docs/tool-usage.md +1 -1
  154. package/docs/trace-store.md +1 -1
  155. package/docs/troubleshooting.md +87 -0
  156. package/docs/tui-design.md +1 -1
  157. package/docs/worker-dispatch-mechanics.md +1 -1
  158. package/package.json +2 -1
  159. package/src/cli/agents.ts +1 -1
  160. package/src/cli/config-inspect.ts +33 -6
  161. package/src/cli/config.ts +1 -1
  162. package/src/cli/doctor-state-size.ts +82 -0
  163. package/src/cli/doctor.ts +3 -1
  164. package/src/cli/eval.ts +80 -16
  165. package/src/cli/extensions.ts +5 -1
  166. package/src/cli/fleet.ts +32 -3
  167. package/src/cli/targets.ts +44 -13
  168. package/src/cli/trace.ts +63 -4
  169. package/src/cli/usage.ts +63 -14
  170. package/src/core/bus-events.ts +29 -1
  171. package/src/core/cache-telemetry.ts +42 -0
  172. package/src/core/config.ts +18 -0
  173. package/src/core/defaults.ts +36 -6
  174. package/src/core/endpoint-key.ts +27 -0
  175. package/src/core/residency-target-key.ts +25 -0
  176. package/src/core/response-schema.ts +36 -2
  177. package/src/domains/config/classify.ts +3 -0
  178. package/src/domains/context/codewiki/coordinator.ts +12 -4
  179. package/src/domains/dispatch/admission.ts +40 -3
  180. package/src/domains/dispatch/capacity-lease.ts +98 -9
  181. package/src/domains/dispatch/contract.ts +11 -0
  182. package/src/domains/dispatch/execution-plan.ts +44 -4
  183. package/src/domains/dispatch/extension.ts +166 -42
  184. package/src/domains/dispatch/fleet-run.ts +23 -3
  185. package/src/domains/dispatch/heartbeat.ts +32 -8
  186. package/src/domains/dispatch/index.ts +3 -0
  187. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  188. package/src/domains/dispatch/reservation-store.ts +116 -8
  189. package/src/domains/dispatch/state.ts +4 -0
  190. package/src/domains/dispatch/worker-spawn.ts +25 -11
  191. package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
  192. package/src/domains/dispatch/write-boundary.ts +62 -1
  193. package/src/domains/eval/artifacts/store.ts +62 -0
  194. package/src/domains/eval/compare/behavioral.ts +224 -0
  195. package/src/domains/eval/compare/compare.ts +355 -2
  196. package/src/domains/eval/compare/envelope.ts +128 -0
  197. package/src/domains/eval/compare/gates.ts +24 -6
  198. package/src/domains/eval/compare/thresholds.ts +30 -3
  199. package/src/domains/eval/execution-provenance.ts +240 -0
  200. package/src/domains/eval/metrics/aggregate.ts +136 -0
  201. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  202. package/src/domains/eval/metrics/tracked.ts +413 -0
  203. package/src/domains/eval/provenance.ts +117 -0
  204. package/src/domains/eval/reports/comparison.ts +128 -0
  205. package/src/domains/eval/reports/junit.ts +17 -3
  206. package/src/domains/eval/reports/markdown.ts +3 -3
  207. package/src/domains/eval/reports/text.ts +14 -0
  208. package/src/domains/eval/run-compare.ts +20 -0
  209. package/src/domains/eval/runners/clio-run.ts +127 -0
  210. package/src/domains/eval/runners/external-command.ts +28 -3
  211. package/src/domains/eval/schema/adapter.ts +111 -0
  212. package/src/domains/eval/schema/artifact.ts +20 -0
  213. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  214. package/src/domains/eval/schema/behavioral.ts +520 -0
  215. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  216. package/src/domains/eval/schema/serving.ts +74 -0
  217. package/src/domains/eval/schema/suite.ts +38 -8
  218. package/src/domains/eval/schema/validate.ts +58 -3
  219. package/src/domains/eval/schema/verdict.ts +237 -0
  220. package/src/domains/eval/suites/resolve.ts +2 -0
  221. package/src/domains/eval/suites/run.ts +264 -33
  222. package/src/domains/eval/verifiers/command.ts +2 -1
  223. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  224. package/src/domains/evidence/build.ts +2 -13
  225. package/src/domains/evidence/eval.ts +2 -12
  226. package/src/domains/evidence/findings-markdown.ts +33 -0
  227. package/src/domains/evidence/run-trust.ts +7 -113
  228. package/src/domains/evidence/trust-projection.ts +2 -2
  229. package/src/domains/extensions/compatibility.ts +285 -0
  230. package/src/domains/extensions/discovery.ts +38 -3
  231. package/src/domains/extensions/resources.ts +1 -1
  232. package/src/domains/extensions/state.ts +12 -3
  233. package/src/domains/extensions/types.ts +2 -0
  234. package/src/domains/lifecycle/doctor.ts +69 -1
  235. package/src/domains/memory/index.ts +14 -0
  236. package/src/domains/memory/task-bank-promotion.ts +64 -0
  237. package/src/domains/memory/task-memory-policy.ts +77 -8
  238. package/src/domains/memory/task-memory-spend.ts +131 -0
  239. package/src/domains/memory/task-memory-status.ts +7 -0
  240. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  241. package/src/domains/middleware/index.ts +1 -0
  242. package/src/domains/middleware/memory-intervention.ts +69 -5
  243. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  244. package/src/domains/observability/background-memory-usage.ts +140 -0
  245. package/src/domains/observability/cost.ts +1 -1
  246. package/src/domains/observability/index.ts +7 -0
  247. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  248. package/src/domains/observability/trace-store.ts +192 -2
  249. package/src/domains/prompts/compiler.ts +100 -13
  250. package/src/domains/providers/endpoint-capacity.ts +96 -0
  251. package/src/domains/providers/index.ts +10 -0
  252. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  253. package/src/domains/providers/runtime-resolution.ts +8 -1
  254. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  255. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  256. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  257. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  258. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  259. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  260. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  261. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  262. package/src/domains/providers/types/capability-flags.ts +2 -0
  263. package/src/domains/providers/types/target-descriptor.ts +2 -0
  264. package/src/domains/resources/prompts/loader.ts +95 -33
  265. package/src/domains/safety/call-target.ts +52 -0
  266. package/src/domains/safety/run-effects.ts +35 -4
  267. package/src/domains/session/context-accounting.ts +52 -1
  268. package/src/domains/session/context-ledger.ts +37 -13
  269. package/src/domains/session/index.ts +6 -0
  270. package/src/domains/session/prompt-cache.ts +140 -0
  271. package/src/domains/session/prompt-manifest.ts +42 -0
  272. package/src/engine/acp/adapter.ts +18 -3
  273. package/src/engine/ai.ts +35 -0
  274. package/src/engine/apis/llamacpp-residency.ts +55 -3
  275. package/src/engine/apis/lmstudio.ts +25 -5
  276. package/src/engine/apis/ollama-native.ts +2 -1
  277. package/src/engine/apis/openai-completions.ts +80 -17
  278. package/src/engine/apis/residency-lock.ts +3 -1
  279. package/src/engine/apis/residency.ts +34 -1
  280. package/src/engine/provider-payload.ts +29 -1
  281. package/src/entry/orchestrator.ts +176 -30
  282. package/src/interactive/chat-loop-messages.ts +26 -7
  283. package/src/interactive/chat-loop.ts +318 -41
  284. package/src/interactive/chat-panel.ts +62 -8
  285. package/src/interactive/clio-editor.ts +45 -8
  286. package/src/interactive/context-activity.ts +5 -1
  287. package/src/interactive/context-meter.ts +1 -1
  288. package/src/interactive/context-overlay.ts +40 -10
  289. package/src/interactive/cost-overlay.ts +64 -6
  290. package/src/interactive/dispatch-board.ts +84 -12
  291. package/src/interactive/fleet-run-preview.ts +41 -15
  292. package/src/interactive/handoff-round.ts +41 -2
  293. package/src/interactive/interactive-application.ts +24 -1
  294. package/src/interactive/interactive-input-runtime.ts +8 -0
  295. package/src/interactive/interactive-presentation.ts +4 -0
  296. package/src/interactive/interactive-shell.ts +20 -17
  297. package/src/interactive/interactive-slash-runtime.ts +27 -4
  298. package/src/interactive/memory-overlay.ts +8 -0
  299. package/src/interactive/mutation-preview.ts +295 -0
  300. package/src/interactive/overlay-general-openers.ts +16 -0
  301. package/src/interactive/overlay-key-routing.ts +38 -0
  302. package/src/interactive/overlay-lifecycle.ts +38 -5
  303. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  304. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  305. package/src/interactive/overlays/ask-user.ts +91 -19
  306. package/src/interactive/overlays/help-reference.ts +4 -0
  307. package/src/interactive/overlays/prompts.ts +11 -1
  308. package/src/interactive/overlays/settings.ts +35 -1
  309. package/src/interactive/permission-hint.ts +34 -2
  310. package/src/interactive/permission-overlay.ts +159 -9
  311. package/src/interactive/prewarm.ts +197 -0
  312. package/src/interactive/render-trace.ts +162 -15
  313. package/src/interactive/renderers/tool-execution.ts +4 -0
  314. package/src/interactive/side-question.ts +58 -1
  315. package/src/interactive/status/controller.ts +11 -0
  316. package/src/interactive/status/state-machine.ts +54 -2
  317. package/src/interactive/status/types.ts +7 -0
  318. package/src/interactive/terminal-lease.ts +2 -0
  319. package/src/interactive/turn-context.ts +299 -31
  320. package/src/interactive/turn-persistence.ts +14 -4
  321. package/src/interactive/turn-prewarm.ts +364 -0
  322. package/src/interactive/turn-queues.ts +7 -4
  323. package/src/interactive/turn-runtime.ts +8 -1
  324. package/src/interactive/turn-state.ts +23 -0
  325. package/src/interactive/view/view-overlay.ts +28 -3
  326. package/src/tools/ask-user.ts +43 -2
  327. package/src/tools/dispatch-plan.ts +17 -9
  328. package/src/tools/dispatch-scout.ts +1 -1
  329. package/src/tools/registry.ts +16 -0
  330. package/dist/chunk-AOCYTWAV.js +0 -449
  331. package/dist/chunk-HWUFFB6L.js +0 -83
  332. package/dist/chunk-R346GLFC.js +0 -31
  333. package/dist/doctor-M7YEDGAE.js +0 -91
@@ -24,6 +24,7 @@ import {
24
24
  type RunStatus,
25
25
  runKindSupportsLiveSteering,
26
26
  } from "../domains/dispatch/types.js";
27
+ import { describeWriteBoundaryAttributionDowngrade } from "../domains/dispatch/write-boundary.js";
27
28
  import {
28
29
  summarizeTrustStatus,
29
30
  type TrustSummaryProjection,
@@ -36,7 +37,7 @@ import {
36
37
  type ObservabilityNotice,
37
38
  type ObservabilitySnapshot,
38
39
  } from "../domains/observability/index.js";
39
- import type { CostProvenance } from "../domains/providers/index.js";
40
+ import { type CostProvenance, foregroundStreamUsage } from "../domains/providers/index.js";
40
41
  import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
41
42
  import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
42
43
  import { formatWorkerContextMeter } from "./context-meter.js";
@@ -88,6 +89,12 @@ export interface DispatchSteerAcknowledgement {
88
89
  chars: number;
89
90
  }
90
91
 
92
+ export interface DispatchWriteRecordDowngrade {
93
+ reason: "opaque_tool_succeeded";
94
+ tool: string;
95
+ toolCallId: string;
96
+ }
97
+
91
98
  export interface DispatchBoardRow {
92
99
  runId: string;
93
100
  agentId: string;
@@ -97,6 +104,8 @@ export interface DispatchBoardRow {
97
104
  runtimeId: string;
98
105
  targetId: string;
99
106
  wireModelId: string;
107
+ /** Canonical inference scheduler and its independent request-slot bound. */
108
+ endpoint?: { key: string; label: string; limit: number };
100
109
  /** Sanitized, one-line summary of the task assigned to this run. */
101
110
  taskSummary?: string;
102
111
  /** Immutable budget admission provenance for this run. */
@@ -156,6 +165,8 @@ export interface DispatchBoardRow {
156
165
  retry?: DispatchRetryPresentation;
157
166
  /** Most recent worker delivery acknowledgement for an operator steer. */
158
167
  steerAcknowledgement?: DispatchSteerAcknowledgement;
168
+ /** First successful opaque call that opened this run's write record. */
169
+ writeRecordDowngrade?: DispatchWriteRecordDowngrade;
159
170
  /**
160
171
  * Where this run sits in a fleet plan: the wave the step was scheduled in
161
172
  * and the step id it executes. Present only for runs a fleet plan
@@ -598,11 +609,29 @@ function progressAnswerLines(
598
609
  return body.map((row, index) => `${index === 0 ? cardKvKey(theme, "answer") : " ".repeat(gutter)}${row}`);
599
610
  }
600
611
 
612
+ /** Keep the canonical tier and every summary clause visible by wrapping the value under its card key. */
613
+ function trustCardLines(theme: ClioTheme, row: DispatchBoardRow, contentWidth: number): string[] {
614
+ const gutter = CARD_KV_KEY_WIDTH + 1;
615
+ const valueWidth = Math.max(1, contentWidth - gutter);
616
+ const trust =
617
+ row.trust === undefined
618
+ ? theme.fg("dim", isTerminalStatus(row.status) ? "receipt not read back" : "not sealed yet")
619
+ : theme.fg(
620
+ trustVerdictToken(row.trust.verdict),
621
+ `${trustVerdictGlyph(row.trust.verdict)} ${row.trust.verdict}; ${row.trust.text}`,
622
+ );
623
+ const host =
624
+ row.hostVerification === undefined ? "" : ` · ${theme.fg("muted", `host checks ${row.hostVerification}`)}`;
625
+ return wrapTextWithAnsi(`${trust}${host}`, valueWidth).map(
626
+ (line, index) => `${index === 0 ? cardKvKey(theme, "trust") : " ".repeat(gutter)}${line}`,
627
+ );
628
+ }
629
+
601
630
  export function renderDispatchCard(
602
631
  row: DispatchBoardRow,
603
632
  width: number,
604
633
  evidence?: RunEvidencePresentation,
605
- options: { selected?: boolean; expanded?: boolean } = {},
634
+ options: { selected?: boolean; expanded?: boolean; endpointActive?: number } = {},
606
635
  ): string[] {
607
636
  const theme = clioTheme();
608
637
  const contentWidth = Math.max(0, width - 4);
@@ -653,6 +682,9 @@ export function renderDispatchCard(
653
682
  const statusUnits = [
654
683
  statusStr,
655
684
  theme.fg("muted", `node ${row.node ?? "local"}`),
685
+ ...(row.endpoint !== undefined
686
+ ? [theme.fg("info", `slots ${options.endpointActive ?? 0}/${row.endpoint.limit} ${row.endpoint.label}`)]
687
+ : []),
656
688
  ...(row.gate !== undefined ? [theme.fg("info", `gate ${row.gate.role} c${row.gate.cycle}`)] : []),
657
689
  ...(row.rerouteCount !== undefined && row.rerouteCount > 0
658
690
  ? [theme.fg("warning", `rerouted x${row.rerouteCount}`)]
@@ -663,15 +695,6 @@ export function renderDispatchCard(
663
695
  `${theme.fg("dim", "ttft")} ${theme.fg("muted", ttft)}`,
664
696
  `${theme.fg("dim", "cost")} ${theme.fg("muted", cost)}`,
665
697
  ];
666
- // The trust line is the canonical projection, styled by its verdict tier;
667
- // a host check is folded into that projection as validation grounding, so
668
- // it is named as a secondary fact and never as independent verification.
669
- const trustUnits = [
670
- row.trust === undefined
671
- ? theme.fg("dim", isTerminalStatus(row.status) ? "trust: receipt not read back" : "trust: not sealed yet")
672
- : theme.fg(trustVerdictToken(row.trust.verdict), `${trustVerdictGlyph(row.trust.verdict)} ${row.trust.text}`),
673
- ...(row.hostVerification !== undefined ? [theme.fg("muted", `host checks ${row.hostVerification}`)] : []),
674
- ];
675
698
  const contextUnit = formatWorkerContextMeter(row.lastContextTokens ?? 0, row.contextWindow, theme);
676
699
  // The phase column: a fleet step says which wave it belongs to and which
677
700
  // step it is; every other run leaves the cell empty rather than inventing a
@@ -690,7 +713,7 @@ export function renderDispatchCard(
690
713
  ? [truncateToWidth(`${cardKvKey(theme, "task")}${theme.fg("muted", row.taskSummary)}`, contentWidth, "…", false)]
691
714
  : []),
692
715
  cardUnitsLine(theme, "status", statusUnits, contentWidth),
693
- cardUnitsLine(theme, "trust", trustUnits, contentWidth),
716
+ ...trustCardLines(theme, row, contentWidth),
694
717
  ...(row.budget !== undefined
695
718
  ? [
696
719
  truncateToWidth(
@@ -751,6 +774,16 @@ export function renderDispatchCard(
751
774
  ),
752
775
  );
753
776
  }
777
+ if (row.writeRecordDowngrade) {
778
+ const warning = describeWriteBoundaryAttributionDowngrade({
779
+ ...row.writeRecordDowngrade,
780
+ runId: row.runId,
781
+ stepId: row.phase?.stepId ?? null,
782
+ });
783
+ bodyLines.push(
784
+ ...wrapTextWithAnsi(`${cardKvKey(theme, "record")}${theme.fg("warning", `open: ${warning}`)}`, contentWidth),
785
+ );
786
+ }
754
787
  // The proof row is present only when the observability projection knows an
755
788
  // evidence state for this run; an unknown/none state adds no line, so cards
756
789
  // without evidence keep their existing three-line body.
@@ -969,6 +1002,16 @@ export function formatDispatchBoardLines(
969
1002
  });
970
1003
  }
971
1004
 
1005
+ const endpointActive: Record<string, number> = { ...foregroundStreamUsage() };
1006
+ for (const row of rows) {
1007
+ if (
1008
+ row.endpoint !== undefined &&
1009
+ (row.status === "running" || row.status === "stale" || row.status === "enqueued" || row.status === "cancelling")
1010
+ ) {
1011
+ endpointActive[row.endpoint.key] = (endpointActive[row.endpoint.key] ?? 0) + 1;
1012
+ }
1013
+ }
1014
+
972
1015
  // A council is one question asked of several members, so its rows render as
973
1016
  // one card holding the whole group rather than as unrelated neighbours.
974
1017
  const cards = dispatchBoardItems(rows, selectedRunId).map((item) =>
@@ -977,6 +1020,7 @@ export function formatDispatchBoardLines(
977
1020
  : renderDispatchCard(item.row, width, deriveRunEvidenceState(observability, item.row.runId), {
978
1021
  selected: item.row.runId === selectedRunId,
979
1022
  expanded: detailExpanded && item.row.runId === selectedRunId,
1023
+ ...(item.row.endpoint !== undefined ? { endpointActive: endpointActive[item.row.endpoint.key] ?? 0 } : {}),
980
1024
  }),
981
1025
  );
982
1026
  const body: string[] = [];
@@ -1155,6 +1199,15 @@ function parseGateBadge(value: unknown): { role: string; cycle: number } | undef
1155
1199
  return { role: record.role, cycle };
1156
1200
  }
1157
1201
 
1202
+ function parseEndpointCapacity(value: unknown): DispatchBoardRow["endpoint"] | undefined {
1203
+ if (typeof value !== "object" || value === null) return undefined;
1204
+ const record = value as { key?: unknown; label?: unknown; limit?: unknown };
1205
+ const key = parseNonEmptyString(record.key);
1206
+ const label = parseNonEmptyString(record.label);
1207
+ const limit = parsePositiveInt(record.limit);
1208
+ return key === undefined || label === undefined || limit === undefined ? undefined : { key, label, limit };
1209
+ }
1210
+
1158
1211
  function parseCouncilBadge(value: unknown): DispatchBoardRow["council"] | undefined {
1159
1212
  if (typeof value !== "object" || value === null) return undefined;
1160
1213
  const record = value as { group?: unknown; label?: unknown; color?: unknown; round?: unknown };
@@ -1351,6 +1404,7 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
1351
1404
  runtimeId: entry.runtimeId,
1352
1405
  targetId: entry.targetId,
1353
1406
  wireModelId: entry.wireModelId,
1407
+ ...(entry.endpoint !== undefined ? { endpoint: { ...entry.endpoint } } : {}),
1354
1408
  ...(entry.taskSummary !== undefined ? { taskSummary: entry.taskSummary } : {}),
1355
1409
  ...(entry.budget !== undefined ? { budget: entry.budget } : {}),
1356
1410
  status: retry ? "retrying" : entry.status,
@@ -1379,6 +1433,7 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
1379
1433
  recentTools: progress.recentActions.map((action) => action.tool),
1380
1434
  ...(retry ? { retry: { ...retry } } : {}),
1381
1435
  ...(entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}),
1436
+ ...(entry.writeRecordDowngrade ? { writeRecordDowngrade: { ...entry.writeRecordDowngrade } } : {}),
1382
1437
  };
1383
1438
  }
1384
1439
 
@@ -1461,6 +1516,7 @@ export function createDispatchBoardStore(
1461
1516
  const agentAudience = parseAgentAudience(raw.agentAudience, previous?.agentAudience);
1462
1517
  const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
1463
1518
  const node = parseNonEmptyString(raw.node) ?? previous?.node;
1519
+ const endpoint = parseEndpointCapacity(raw.endpoint) ?? previous?.endpoint;
1464
1520
  const gate = parseGateBadge(raw.gate) ?? previous?.gate;
1465
1521
  const council = parseCouncilBadge(raw.council) ?? previous?.council;
1466
1522
  const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
@@ -1477,6 +1533,7 @@ export function createDispatchBoardStore(
1477
1533
  runtimeId: parseText(raw.runtimeId, previous?.runtimeId ?? "-"),
1478
1534
  targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
1479
1535
  wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
1536
+ ...(endpoint !== undefined ? { endpoint } : {}),
1480
1537
  ...(taskSummary !== undefined ? { taskSummary } : {}),
1481
1538
  ...(budget !== undefined ? { budget } : {}),
1482
1539
  status,
@@ -1505,6 +1562,7 @@ export function createDispatchBoardStore(
1505
1562
  progress: previous?.progress ?? createWorkerProgressFold(),
1506
1563
  ...(previous?.retry ? { retry: { ...previous.retry } } : {}),
1507
1564
  ...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
1565
+ ...(previous?.writeRecordDowngrade ? { writeRecordDowngrade: { ...previous.writeRecordDowngrade } } : {}),
1508
1566
  };
1509
1567
  entries.set(runId, entry);
1510
1568
  pruneEntries(entries, (evicted) => fleetPhases.delete(evicted));
@@ -1654,6 +1712,20 @@ export function createDispatchBoardStore(
1654
1712
  entry.ttftMs = Math.round(performance.now() - entry.startedAtClockMs);
1655
1713
  }
1656
1714
  }
1715
+ if (type === "clio_write_record_downgraded") {
1716
+ const rawPayload = (workerEvent as { payload?: unknown }).payload;
1717
+ const payload =
1718
+ typeof rawPayload === "object" && rawPayload !== null ? (rawPayload as Record<string, unknown>) : null;
1719
+ const tool = payload === null ? undefined : parseNonEmptyString(payload.tool);
1720
+ const toolCallId = payload === null ? undefined : parseNonEmptyString(payload.toolCallId);
1721
+ if (payload?.reason === "opaque_tool_succeeded" && tool !== undefined && toolCallId !== undefined) {
1722
+ entry.writeRecordDowngrade = {
1723
+ reason: "opaque_tool_succeeded",
1724
+ tool: sanitizeCallTargetText(tool),
1725
+ toolCallId: sanitizeCallTargetText(toolCallId),
1726
+ };
1727
+ }
1728
+ }
1657
1729
  // The one fold that reads worker prose and tool activity. Everything the
1658
1730
  // board says about what a worker is saying or touching comes from here,
1659
1731
  // so the board and the transcript block cannot tell two stories.
@@ -26,7 +26,11 @@ import {
26
26
  validateFleetGraph,
27
27
  } from "../domains/agents/index.js";
28
28
  import type { AgentSpec } from "../domains/agents/spec.js";
29
- import type { ExecutionPlan, ExecutionPlanStep } from "../domains/dispatch/execution-plan.js";
29
+ import {
30
+ bindExecutionPlanEndpoints,
31
+ type ExecutionPlan,
32
+ type ExecutionPlanStep,
33
+ } from "../domains/dispatch/execution-plan.js";
30
34
  import {
31
35
  type AgentRoleFactsResolver,
32
36
  requestExecutionRole,
@@ -40,6 +44,7 @@ export interface FleetRunPreviewRoute {
40
44
  targetId: string;
41
45
  wireModelId: string;
42
46
  nodeId: string;
47
+ endpoint?: { key: string; label: string; limit: number; foregroundHeld?: number };
43
48
  }
44
49
 
45
50
  export interface FleetRunPreviewStep {
@@ -207,6 +212,40 @@ export function compileFleetRunPreview(input: FleetRunPreviewInput): FleetRunPre
207
212
  diagnostics.push(describeError(error));
208
213
  return fail();
209
214
  }
215
+ const routes = new Map<string, FleetRunPreviewRoute>();
216
+ if (input.resolveRoute !== undefined) {
217
+ for (const step of plan.steps) {
218
+ if (step.kind === "code") continue;
219
+ try {
220
+ const route = input.resolveRoute({
221
+ stepId: step.id,
222
+ agentId: step.agentId,
223
+ task: step.task,
224
+ scope: step.scope,
225
+ ...(step.target !== undefined ? { target: step.target } : {}),
226
+ ...(step.profile !== undefined ? { profile: step.profile } : {}),
227
+ });
228
+ if (route !== null) routes.set(step.id, route);
229
+ } catch (error) {
230
+ diagnostics.push(`step '${step.id}' route preflight failed: ${describeError(error)}`);
231
+ }
232
+ }
233
+ plan = bindExecutionPlanEndpoints(
234
+ plan,
235
+ Object.fromEntries(
236
+ [...routes].map(([stepId, route]) => [
237
+ stepId,
238
+ route.endpoint === undefined
239
+ ? undefined
240
+ : {
241
+ key: route.endpoint.key,
242
+ limit: route.endpoint.limit,
243
+ ...(route.endpoint.foregroundHeld !== undefined ? { foregroundHeld: route.endpoint.foregroundHeld } : {}),
244
+ },
245
+ ]),
246
+ ),
247
+ );
248
+ }
210
249
 
211
250
  // A declared boundary is verified against the checkout, so the checkout has
212
251
  // to be one this can read. Refused here, before any dispatch, rather than
@@ -261,20 +300,7 @@ export function compileFleetRunPreview(input: FleetRunPreviewInput): FleetRunPre
261
300
  },
262
301
  ];
263
302
  }
264
- let route: FleetRunPreviewRoute | null = null;
265
- try {
266
- route =
267
- input.resolveRoute?.({
268
- stepId: step.id,
269
- agentId: step.agentId,
270
- task: step.task,
271
- scope: step.scope,
272
- ...(step.target !== undefined ? { target: step.target } : {}),
273
- ...(step.profile !== undefined ? { profile: step.profile } : {}),
274
- }) ?? null;
275
- } catch (error) {
276
- diagnostics.push(`step '${step.id}' route preflight failed: ${describeError(error)}`);
277
- }
303
+ const route = routes.get(step.id) ?? null;
278
304
  return [
279
305
  {
280
306
  ...base,
@@ -34,6 +34,22 @@ export const HANDOFF_SYSTEM_PROMPT = [
34
34
  /** A handoff record is a list of short entries, so the budget is modest. */
35
35
  export const HANDOFF_MAX_TOKENS = 4096;
36
36
 
37
+ /** The name the wire dialect gives the schema, where it takes one. */
38
+ export const HANDOFF_SCHEMA_NAME = "handoff_record";
39
+
40
+ /** Characters of the refused answer quoted back to the model in the repair round. */
41
+ export const HANDOFF_REPAIR_QUOTE_CHARS = 600;
42
+
43
+ /**
44
+ * What the first round returned and why the parser refused it. The repair round
45
+ * shows the model both, because "return one JSON object" repeated verbatim is
46
+ * the instruction it already failed (issue #223).
47
+ */
48
+ export interface HandoffRepairInput {
49
+ complaint: string;
50
+ previous: string;
51
+ }
52
+
37
53
  export interface HandoffRoundInput {
38
54
  model: EngineModel;
39
55
  /** Read-only. The round copies before appending its own message. */
@@ -42,15 +58,38 @@ export interface HandoffRoundInput {
42
58
  apiKey?: string;
43
59
  signal?: AbortSignal;
44
60
  maxTokens?: number;
61
+ /** Resolved runtime id, which decides whether the schema can be bound on the wire. */
62
+ runtimeId?: string;
63
+ /** Set on the second and last round, after the parser refused the first. */
64
+ repair?: HandoffRepairInput;
65
+ }
66
+
67
+ function repairText(goal: string, repair: HandoffRepairInput): string {
68
+ const quoted = repair.previous.slice(0, HANDOFF_REPAIR_QUOTE_CHARS);
69
+ return [
70
+ `The next session's goal is: ${goal}`,
71
+ "",
72
+ "Your previous answer could not be read as a handoff record.",
73
+ `The parser said: ${repair.complaint}`,
74
+ "This is what you returned, verbatim:",
75
+ "---",
76
+ quoted.length > 0 ? quoted : "(nothing)",
77
+ "---",
78
+ "Answer again with one JSON object matching the schema and nothing else.",
79
+ "No prose before it, no prose after it, no code fence.",
80
+ ].join("\n");
45
81
  }
46
82
 
47
83
  /** Run the round. Provider failures reject; the caller renders the reason. */
48
84
  export async function runHandoffRound(input: HandoffRoundInput): Promise<SideQuestionResult> {
49
- const { goal, ...rest } = input;
85
+ const { goal, repair, ...rest } = input;
50
86
  return runOutOfTurnRound({
51
87
  ...rest,
52
88
  maxTokens: input.maxTokens ?? HANDOFF_MAX_TOKENS,
53
89
  systemPrompt: HANDOFF_SYSTEM_PROMPT,
54
- userText: `The next session's goal is: ${goal}\n\nExtract the handoff record for that goal now.`,
90
+ responseSchema: { name: HANDOFF_SCHEMA_NAME, schema: HANDOFF_RESPONSE_SCHEMA },
91
+ userText: repair
92
+ ? repairText(goal, repair)
93
+ : `The next session's goal is: ${goal}\n\nExtract the handoff record for that goal now.`,
55
94
  });
56
95
  }
@@ -3,6 +3,7 @@ import type { ClioSettings } from "../core/config.js";
3
3
  import type { SafeEventBus } from "../core/event-bus.js";
4
4
  import { expandInlineFileReferencesAsync } from "../core/file-references.js";
5
5
  import type { PendingSkillRequest } from "../core/skill-activation.js";
6
+ import { clioStateDir } from "../core/xdg.js";
6
7
  import type { AgentsContract } from "../domains/agents/contract.js";
7
8
  import type { ClioKeybinding } from "../domains/config/keybindings.js";
8
9
  import type { ContextState } from "../domains/context/index.js";
@@ -41,6 +42,7 @@ import { createInteractiveSubscriptions } from "./interactive-subscriptions.js";
41
42
  import { createInteractiveTickers } from "./interactive-tickers.js";
42
43
  import { createOverlayLifecycle, type OverlayLifecycleController, type OverlayState } from "./overlay-lifecycle.js";
43
44
  import { interopOverlaySurface } from "./overlays/interop.js";
45
+ import { writeInputWedgeDump } from "./render-trace.js";
44
46
  import { settleChatBeforeSessionSwitch } from "./session-switch-settlement.js";
45
47
  import { createSessionTranscript } from "./session-transcript.js";
46
48
  import type {
@@ -429,6 +431,21 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
429
431
  });
430
432
  const { terminal, tui } = shell;
431
433
  const renderTrace = getActiveRenderTrace();
434
+ // SIGTERM is the kill an operator reaches for when a pane stops answering, so
435
+ // it is where the always-on input-wedge ring has to land (#224). It writes
436
+ // synchronously from its own listener rather than from a termination hook: a
437
+ // session wedged badly enough to be killed may never reach the coordinator's
438
+ // asynchronous drain, and this dump has to survive that.
439
+ const dumpInputWedgeOnTerminate = renderTrace
440
+ ? (): void => {
441
+ try {
442
+ writeInputWedgeDump(clioStateDir(), renderTrace.snapshotInputWedge());
443
+ } catch {
444
+ // Best effort. The process is ending either way.
445
+ }
446
+ }
447
+ : null;
448
+ if (dumpInputWedgeOnTerminate) process.on("SIGTERM", dumpInputWedgeOnTerminate);
432
449
  const visibleEventIngress = new WeakMap<
433
450
  object,
434
451
  { sequence: number; traceSequence: number; generation: number; ingressAt: number }
@@ -498,6 +515,11 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
498
515
  getLeaderArmed: () => leaderArmed,
499
516
  getShutdownArmed: () => shutdownArmed,
500
517
  isAwaitingApproval: () => overlayLifecycle?.getState() === "permission-confirm",
518
+ getPermissionInspection: () => {
519
+ if (overlayLifecycle === undefined || overlayLifecycle === null) return "none";
520
+ if (!overlayLifecycle.canInspectMutation()) return "none";
521
+ return overlayLifecycle.isInspectingMutation() ? "open" : "closed";
522
+ },
501
523
  resolveVisibleEventSequence: (event) => visibleEventIngress.get(event)?.traceSequence ?? null,
502
524
  resolveStreamIngress: (event) => visibleEventIngress.get(event) ?? null,
503
525
  commitFrame: (reason) => shell.commitCurrentFrame(reason === "teardown" ? 300 : 30_000),
@@ -618,7 +640,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
618
640
  chat: deps.chat,
619
641
  chatPanel: {
620
642
  appendReplayBlock: (...args) => chatRenderer.mutate(() => chatPanel.appendReplayBlock(...args), "slash-output"),
621
- appendUser: (text) => chatRenderer.mutate(() => chatPanel.appendUser(text), "user-submit"),
643
+ appendUser: (text, status) => chatRenderer.mutate(() => chatPanel.appendUser(text, status), "user-submit"),
622
644
  clearFoldOverrides: () => chatRenderer.mutate(() => chatPanel.clearFoldOverrides(), "output-detail"),
623
645
  },
624
646
  beforeSemanticSubmit: () => chatRenderer.flush(),
@@ -949,6 +971,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
949
971
  cancelParkedCalls: (reason) => deps.toolRegistry?.cancelParkedCalls(reason),
950
972
  onShutdown: async () => {
951
973
  try {
974
+ if (dumpInputWedgeOnTerminate) process.off("SIGTERM", dumpInputWedgeOnTerminate);
952
975
  await deps.onShutdown();
953
976
  } finally {
954
977
  if (lease) await lease.close();
@@ -60,6 +60,10 @@ export interface InteractiveInputRuntimeDeps {
60
60
  closeOverlay(): void;
61
61
  confirmPermission(): void;
62
62
  stopTurnFromPermission(): void;
63
+ canInspectMutation(): boolean;
64
+ isInspectingMutation(): boolean;
65
+ toggleMutationInspection(): void;
66
+ scrollMutationInspection(delta: number): void;
63
67
  cancelAskUser(): void;
64
68
  toggleFooterDashboardState(): void;
65
69
  toggleDispatchBoardOverlay(): void;
@@ -213,6 +217,10 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
213
217
  confirmPermission: () => deps.overlay.confirmPermission(),
214
218
  stopTurnFromPermission: () => deps.overlay.stopTurnFromPermission(),
215
219
  composerHasDraft: () => deps.editor.getText().length > 0,
220
+ canInspectMutation: () => deps.overlay.canInspectMutation(),
221
+ isInspectingMutation: () => deps.overlay.isInspectingMutation(),
222
+ toggleMutationInspection: () => deps.overlay.toggleMutationInspection(),
223
+ scrollMutationInspection: (delta) => deps.overlay.scrollMutationInspection(delta),
216
224
  editDraft: (data) => {
217
225
  deps.editor.handleInput?.(data);
218
226
  deps.requestRender();
@@ -99,6 +99,8 @@ export interface InteractivePresentationDeps {
99
99
  getShutdownArmed?: () => boolean;
100
100
  /** Whether a permission prompt owns the keyboard, for the composer's CONFIRM rail. */
101
101
  isAwaitingApproval?: () => boolean;
102
+ /** Whether that prompt has a mutation to inspect, and whether it is open (issue #254). */
103
+ getPermissionInspection?: () => import("./permission-hint.js").PermissionInspectionHint;
102
104
  getCwd?: () => string;
103
105
  resolveVisibleEventSequence?: (event: ChatLoopEvent) => number | null;
104
106
  resolveStreamIngress?: (
@@ -418,6 +420,8 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
418
420
  },
419
421
  isStreaming: () => deps.chat.isStreaming(),
420
422
  ...(deps.isAwaitingApproval ? { isAwaitingApproval: deps.isAwaitingApproval } : {}),
423
+ ...(deps.getPermissionInspection ? { getPermissionInspection: deps.getPermissionInspection } : {}),
424
+ getTurnPreparation: () => deps.chat.turnPreparation().phase,
421
425
  willEnterSteer: (text) => willEnterSteerActiveWork(deps, text),
422
426
  getSubmitKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.submit")[0], "Enter"),
423
427
  getNewlineKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.newLine")[0], "Shift+Enter"),
@@ -158,9 +158,11 @@ export function createInteractiveShell<TTerminal extends Terminal, TTui extends
158
158
  }
159
159
 
160
160
  /**
161
- * Trace for the current process, created once when CLIO_CODER_RENDER_TRACE names a
162
- * file. The panel wires its render metrics into the same instance, so the
163
- * frame rows carry both the build cost and the write cost.
161
+ * Trace for the current process, created once per interactive shell. It writes
162
+ * a JSONL file when CLIO_CODER_RENDER_TRACE names one and otherwise keeps only
163
+ * its bounded input-wedge ring in memory. The panel wires its render metrics
164
+ * into the same instance, so the frame rows carry both the build cost and the
165
+ * write cost.
164
166
  */
165
167
  let activeRenderTrace: RenderTrace | null = null;
166
168
 
@@ -203,20 +205,21 @@ export function createProcessInteractiveShell(
203
205
  let restoreRoot: (() => void) | null = null;
204
206
  let backpressure: StdoutBackpressureGate | null = null;
205
207
  let observedBackpressure = false;
206
- if (tracePath) {
207
- let candidate: RenderTrace | null = null;
208
- try {
209
- candidate = createRenderTrace(tracePath);
210
- restoreStdout = traceProcessStdout(candidate);
211
- activeRenderTrace = candidate;
212
- } catch {
213
- // A diagnostics path must never prevent the interactive application from starting.
214
- restoreStdout?.();
215
- restoreStdout = null;
216
- void candidate?.close().catch(() => {});
217
- activeRenderTrace = null;
218
- }
219
- } else activeRenderTrace = null;
208
+ let candidate: RenderTrace | null = null;
209
+ try {
210
+ // A null path is the ring-only mode: no file, but the input-wedge ring still
211
+ // fills, because the operator who hits a wedge did not arm a trace before it
212
+ // happened (#224).
213
+ candidate = createRenderTrace(tracePath);
214
+ restoreStdout = traceProcessStdout(candidate);
215
+ activeRenderTrace = candidate;
216
+ } catch {
217
+ // A diagnostics path must never prevent the interactive application from starting.
218
+ restoreStdout?.();
219
+ restoreStdout = null;
220
+ void candidate?.close().catch(() => {});
221
+ activeRenderTrace = null;
222
+ }
220
223
  const trace = activeRenderTrace;
221
224
  if (trace && options.onFirstFrameCommit) trace.onFirstFrameCommit(options.onFirstFrameCommit);
222
225
  let firstFrameDelivered = false;
@@ -62,6 +62,7 @@ export interface InteractiveSlashSubmitExpansion {
62
62
 
63
63
  type SlashChat = Pick<ChatLoop, "getSessionId" | "isStreaming" | "submit">;
64
64
  type SlashChatPanel = Pick<ChatPanel, "appendReplayBlock" | "appendUser" | "clearFoldOverrides">;
65
+ type UserTurnStatus = import("./chat-panel.js").UserTurnStatus;
65
66
  type SlashResources = Pick<ResourcesContract, "prompts" | "expandPromptTemplate" | "reload">;
66
67
  type SlashExtensions = Pick<ExtensionsContract, "list">;
67
68
  type SlashAgents = Pick<AgentsContract, "getSpec" | "listSpecs">;
@@ -245,9 +246,13 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
245
246
  const willQueue = deps.chat.isStreaming();
246
247
  deps.recordSubmittedTurn();
247
248
  deps.refreshFooter();
248
- if (!willQueue) deps.chatPanel.appendUser(sub.text);
249
+ // The row is painted now, so the operator sees their prompt land, but
250
+ // it is marked pending until the durable append at admission: nothing
251
+ // is in the ledger yet, and a refused preflight must not leave a row
252
+ // that reads like a committed turn (issue #251).
253
+ let rowStatus: UserTurnStatus = "pending";
254
+ if (!willQueue) deps.chatPanel.appendUser(sub.text, () => rowStatus);
249
255
  deps.requestRender();
250
- deps.beforeSemanticSubmit?.();
251
256
  let acknowledgeAdmission = (): void => {};
252
257
  const admitted = new Promise<void>((resolve) => {
253
258
  acknowledgeAdmission = resolve;
@@ -257,7 +262,19 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
257
262
  ...(sub.workingContextPaths.length > 0 ? { workingContextPaths: sub.workingContextPaths } : {}),
258
263
  ...(sub.pendingSkillRequests.length > 0 ? { pendingSkillRequests: sub.pendingSkillRequests } : {}),
259
264
  ...(awaitAdmission && willQueue ? { steering: "end-of-turn" as const } : {}),
260
- ...(awaitAdmission ? { onAdmitted: acknowledgeAdmission } : {}),
265
+ ...(!willQueue
266
+ ? {
267
+ onPreparationVisible: async () => {
268
+ deps.beforeSemanticSubmit?.();
269
+ await deps.settleVisibleFrame?.("submit-preparing");
270
+ },
271
+ }
272
+ : {}),
273
+ onAdmitted: () => {
274
+ rowStatus = "committed";
275
+ deps.requestRender();
276
+ if (awaitAdmission) acknowledgeAdmission();
277
+ },
261
278
  });
262
279
  const settlement = turn
263
280
  .then(() => deps.settleVisibleFrame?.("submit-return"))
@@ -265,7 +282,13 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
265
282
  const msg = err instanceof Error ? err.message : String(err);
266
283
  deps.io.stderr(`[interactive] chat failed: ${msg}\n`);
267
284
  })
268
- .finally(deps.requestRender);
285
+ .finally(() => {
286
+ // Settlement without admission is a refusal: the probe failed, the
287
+ // overflow preflight blocked the request, or the submit became a
288
+ // steer. Say so on the row rather than leaving a phantom turn.
289
+ if (rowStatus === "pending") rowStatus = willQueue ? "committed" : "refused";
290
+ deps.requestRender();
291
+ });
269
292
  return awaitAdmission ? Promise.race([admitted, settlement]) : settlement;
270
293
  } catch (err) {
271
294
  const msg = err instanceof Error ? err.message : String(err);
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  describeTaskMemoryActivity,
3
+ formatTaskMemorySpend,
3
4
  type MemoryProposalResult,
4
5
  type MemoryRecord,
5
6
  type TaskMemoryActivityEvent,
@@ -38,6 +39,12 @@ export function formatMemoryStatusLine(status: TaskMemoryOperatorStatus, content
38
39
  theme.fg("muted", `last ${status.lastDecision ?? "none"}`),
39
40
  ];
40
41
  if (status.stepInFlight) units.push(theme.fg("reason", "step running"));
42
+ // Lifetime cost of the background plane, beside the state of the current
43
+ // session. The tier spent 137,205 tokens over 14 days on the operator's own
44
+ // machine before any surface said so (#229), and a hit rate is the one figure
45
+ // that says whether that spend is buying anything.
46
+ const spend = status.spend === null || status.spend === undefined ? "" : formatTaskMemorySpend(status.spend);
47
+ if (spend.length > 0) units.push(theme.fg("dim", spend));
41
48
  return fitUnits(theme, "", units, width);
42
49
  }
43
50
 
@@ -172,6 +179,7 @@ function memorySignature(status: TaskMemoryOperatorStatus, records: ReadonlyArra
172
179
  String(status.size),
173
180
  status.lastDecision ?? "none",
174
181
  status.stepInFlight ? "running" : "idle",
182
+ `${status.spend?.llmSteps ?? 0}:${status.spend?.injections ?? 0}:${status.spend?.totalTokens ?? 0}`,
175
183
  ];
176
184
  for (const record of records) parts.push(`r:${record.id}:${record.approved}:${record.rejectedAt ?? ""}`);
177
185
  for (const entry of bankEntries(status.bank)) {