@iowarp/clio-coder 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +13 -4
  3. package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
  4. package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
  7. package/dist/builtins-AD25UL3C.js +17 -0
  8. package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
  9. package/dist/chunk-3DPEIQKN.js +113 -0
  10. package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
  11. package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
  12. package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
  13. package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
  14. package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
  15. package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
  16. package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
  17. package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
  18. package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
  19. package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
  20. package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
  21. package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
  22. package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
  23. package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
  24. package/dist/chunk-774ILSRL.js +172 -0
  25. package/dist/chunk-7C6RYZGQ.js +391 -0
  26. package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
  27. package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
  28. package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
  29. package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
  30. package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
  31. package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
  32. package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
  33. package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
  34. package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
  35. package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
  36. package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
  37. package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
  38. package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
  39. package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
  40. package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
  41. package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
  42. package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
  43. package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
  44. package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
  45. package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
  46. package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
  47. package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
  48. package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
  49. package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
  50. package/dist/chunk-JEQQR47K.js +3025 -0
  51. package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
  52. package/dist/chunk-LU7P4LHA.js +33 -0
  53. package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
  54. package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
  55. package/dist/chunk-MLOK6ZOS.js +2888 -0
  56. package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
  57. package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
  58. package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
  59. package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
  60. package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
  61. package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
  62. package/dist/chunk-NUGM5KR6.js +165 -0
  63. package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
  64. package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
  65. package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
  66. package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
  67. package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
  68. package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
  69. package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
  70. package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
  71. package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
  72. package/dist/chunk-RKKLTLYB.js +45 -0
  73. package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
  74. package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
  75. package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
  76. package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
  77. package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
  78. package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
  79. package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
  80. package/dist/chunk-VO2LKSTM.js +165 -0
  81. package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
  82. package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
  83. package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
  84. package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
  85. package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
  86. package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
  87. package/dist/chunk-YSEHGPCT.js +127 -0
  88. package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
  89. package/dist/cli/index.js +32 -32
  90. package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
  91. package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
  92. package/dist/codewiki/build-worker.js +4 -4
  93. package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
  94. package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
  95. package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
  96. package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
  97. package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
  98. package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
  99. package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
  100. package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
  101. package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
  102. package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
  103. package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
  104. package/dist/doctor-TWBWFK5V.js +165 -0
  105. package/dist/eval-IJ5VEZDJ.js +4483 -0
  106. package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
  107. package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
  108. package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
  109. package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
  110. package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
  111. package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
  112. package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
  113. package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
  114. package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
  115. package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
  116. package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
  117. package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
  118. package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
  119. package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
  120. package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
  121. package/dist/registry-LG64LTF4.js +11 -0
  122. package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
  123. package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
  124. package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
  125. package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
  126. package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
  127. package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
  128. package/dist/support-I7LOJLIF.js +38 -0
  129. package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
  130. package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
  131. package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
  132. package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
  133. package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
  134. package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
  135. package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
  136. package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
  137. package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
  138. package/dist/worker/entry.js +69 -66
  139. package/docs/alcf-provider.md +1 -1
  140. package/docs/architecture.md +1 -1
  141. package/docs/artifact-versions.md +11 -5
  142. package/docs/built-in-agents.md +1 -1
  143. package/docs/capacity-and-scheduling.md +23 -2
  144. package/docs/commands-and-modes.md +2 -2
  145. package/docs/configuration-and-targets.md +37 -5
  146. package/docs/context-engine.md +63 -4
  147. package/docs/documentation-coverage.md +3 -3
  148. package/docs/documentation-guide.md +1 -1
  149. package/docs/environment-variables.md +2 -0
  150. package/docs/eval-runner.md +262 -11
  151. package/docs/evals-internal.md +72 -2
  152. package/docs/evidence-and-memory.md +77 -12
  153. package/docs/evolution.md +1 -1
  154. package/docs/extensions-and-sharing.md +3 -1
  155. package/docs/fleet-dispatch.md +34 -9
  156. package/docs/glossary.md +21 -1
  157. package/docs/installation-and-lifecycle.md +1 -1
  158. package/docs/middleware-and-components.md +1 -1
  159. package/docs/model-catalog.md +1 -1
  160. package/docs/observability.md +54 -3
  161. package/docs/proactive-memory.md +127 -14
  162. package/docs/prompt-envelope-and-tools.md +22 -2
  163. package/docs/provider-adapter-cookbook.md +1 -1
  164. package/docs/release-cut-checklist.md +60 -41
  165. package/docs/safety-model.md +1 -1
  166. package/docs/scientific-validation.md +1 -1
  167. package/docs/skills-marketplace.md +1 -1
  168. package/docs/tool-usage.md +1 -1
  169. package/docs/trace-store.md +1 -1
  170. package/docs/troubleshooting.md +87 -0
  171. package/docs/tui-design.md +1 -1
  172. package/docs/worker-dispatch-mechanics.md +1 -1
  173. package/package.json +2 -2
  174. package/src/cli/agents.ts +1 -1
  175. package/src/cli/argv.ts +5 -0
  176. package/src/cli/config-inspect.ts +33 -6
  177. package/src/cli/config.ts +1 -1
  178. package/src/cli/configure.ts +107 -23
  179. package/src/cli/doctor-state-size.ts +82 -0
  180. package/src/cli/doctor.ts +7 -1
  181. package/src/cli/eval.ts +80 -16
  182. package/src/cli/evidence.ts +30 -25
  183. package/src/cli/extensions.ts +5 -1
  184. package/src/cli/fleet-preflight.ts +2 -12
  185. package/src/cli/fleet.ts +32 -3
  186. package/src/cli/shared.ts +1 -0
  187. package/src/cli/targets.ts +45 -11
  188. package/src/cli/trace.ts +63 -4
  189. package/src/cli/usage.ts +63 -14
  190. package/src/cli/validate-model.ts +60 -5
  191. package/src/core/bus-events.ts +54 -1
  192. package/src/core/cache-telemetry.ts +42 -0
  193. package/src/core/commit-attribution.ts +4 -4
  194. package/src/core/config.ts +18 -0
  195. package/src/core/defaults.ts +36 -6
  196. package/src/core/endpoint-key.ts +27 -0
  197. package/src/core/path-boundary.ts +100 -0
  198. package/src/core/residency-target-key.ts +25 -0
  199. package/src/core/response-schema.ts +36 -2
  200. package/src/domains/agents/extension.ts +2 -11
  201. package/src/domains/agents/fleet-contract.ts +30 -12
  202. package/src/domains/agents/recipe.ts +7 -1
  203. package/src/domains/agents/registry.ts +73 -5
  204. package/src/domains/agents/result-contract.ts +128 -17
  205. package/src/domains/agents/write-boundary.ts +15 -50
  206. package/src/domains/config/classify.ts +3 -0
  207. package/src/domains/context/codewiki/coordinator.ts +12 -4
  208. package/src/domains/context/project-rules.ts +51 -1
  209. package/src/domains/dispatch/admission.ts +40 -3
  210. package/src/domains/dispatch/assignment-reconcile.ts +22 -5
  211. package/src/domains/dispatch/assignment-store.ts +151 -14
  212. package/src/domains/dispatch/capacity-lease.ts +98 -9
  213. package/src/domains/dispatch/contract.ts +26 -1
  214. package/src/domains/dispatch/delegation-plan.ts +2 -5
  215. package/src/domains/dispatch/execution-plan.ts +44 -4
  216. package/src/domains/dispatch/execution-role.ts +9 -1
  217. package/src/domains/dispatch/extension.ts +309 -96
  218. package/src/domains/dispatch/fleet-run.ts +78 -4
  219. package/src/domains/dispatch/gate-role-prompts.ts +38 -0
  220. package/src/domains/dispatch/heartbeat.ts +32 -8
  221. package/src/domains/dispatch/index.ts +6 -1
  222. package/src/domains/dispatch/intent-requirements.ts +40 -0
  223. package/src/domains/dispatch/intent.ts +84 -8
  224. package/src/domains/dispatch/orphan-recovery.ts +5 -0
  225. package/src/domains/dispatch/path-scope.ts +370 -0
  226. package/src/domains/dispatch/receipt-integrity.ts +2 -1
  227. package/src/domains/dispatch/reservation-store.ts +116 -8
  228. package/src/domains/dispatch/state.ts +4 -0
  229. package/src/domains/dispatch/types.ts +14 -7
  230. package/src/domains/dispatch/validation.ts +6 -3
  231. package/src/domains/dispatch/worker-spawn.ts +25 -11
  232. package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
  233. package/src/domains/dispatch/write-boundary.ts +262 -22
  234. package/src/domains/eval/artifacts/store.ts +62 -0
  235. package/src/domains/eval/compare/behavioral.ts +224 -0
  236. package/src/domains/eval/compare/compare.ts +355 -2
  237. package/src/domains/eval/compare/envelope.ts +128 -0
  238. package/src/domains/eval/compare/gates.ts +24 -6
  239. package/src/domains/eval/compare/thresholds.ts +30 -3
  240. package/src/domains/eval/execution-provenance.ts +240 -0
  241. package/src/domains/eval/metrics/aggregate.ts +136 -0
  242. package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
  243. package/src/domains/eval/metrics/evidence.ts +79 -2
  244. package/src/domains/eval/metrics/tracked.ts +413 -0
  245. package/src/domains/eval/provenance.ts +117 -0
  246. package/src/domains/eval/reports/comparison.ts +128 -0
  247. package/src/domains/eval/reports/junit.ts +17 -3
  248. package/src/domains/eval/reports/markdown.ts +3 -3
  249. package/src/domains/eval/reports/text.ts +14 -0
  250. package/src/domains/eval/run-compare.ts +20 -0
  251. package/src/domains/eval/runners/clio-run.ts +139 -2
  252. package/src/domains/eval/runners/external-command.ts +28 -3
  253. package/src/domains/eval/schema/adapter.ts +111 -0
  254. package/src/domains/eval/schema/artifact.ts +20 -0
  255. package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
  256. package/src/domains/eval/schema/behavioral.ts +520 -0
  257. package/src/domains/eval/schema/execution-envelope.ts +194 -0
  258. package/src/domains/eval/schema/serving.ts +74 -0
  259. package/src/domains/eval/schema/suite.ts +38 -8
  260. package/src/domains/eval/schema/validate.ts +58 -3
  261. package/src/domains/eval/schema/verdict.ts +237 -0
  262. package/src/domains/eval/suites/resolve.ts +2 -0
  263. package/src/domains/eval/suites/run.ts +264 -33
  264. package/src/domains/eval/verifiers/command.ts +2 -1
  265. package/src/domains/eval/workspaces/temp-copy.ts +145 -13
  266. package/src/domains/evidence/build.ts +68 -21
  267. package/src/domains/evidence/eval.ts +2 -12
  268. package/src/domains/evidence/findings-markdown.ts +33 -0
  269. package/src/domains/evidence/index.ts +21 -0
  270. package/src/domains/evidence/provenance.ts +46 -11
  271. package/src/domains/evidence/run-trust.ts +7 -113
  272. package/src/domains/evidence/trust-projection.ts +274 -0
  273. package/src/domains/evidence/trust-status.ts +145 -17
  274. package/src/domains/evidence/types.ts +4 -0
  275. package/src/domains/extensions/compatibility.ts +285 -0
  276. package/src/domains/extensions/discovery.ts +126 -4
  277. package/src/domains/extensions/resources.ts +21 -9
  278. package/src/domains/extensions/state.ts +18 -5
  279. package/src/domains/extensions/types.ts +6 -1
  280. package/src/domains/lifecycle/doctor.ts +209 -2
  281. package/src/domains/memory/index.ts +14 -0
  282. package/src/domains/memory/task-bank-promotion.ts +64 -0
  283. package/src/domains/memory/task-memory-policy.ts +77 -8
  284. package/src/domains/memory/task-memory-spend.ts +131 -0
  285. package/src/domains/memory/task-memory-status.ts +7 -0
  286. package/src/domains/memory/task-memory-telemetry.ts +2 -0
  287. package/src/domains/middleware/index.ts +1 -0
  288. package/src/domains/middleware/memory-intervention.ts +69 -5
  289. package/src/domains/middleware/memory-step-endpoint.ts +71 -0
  290. package/src/domains/observability/background-memory-usage.ts +140 -0
  291. package/src/domains/observability/cost.ts +1 -1
  292. package/src/domains/observability/index.ts +7 -0
  293. package/src/domains/observability/out-of-turn-usage.ts +51 -2
  294. package/src/domains/observability/trace-store.ts +192 -2
  295. package/src/domains/prompts/compiler.ts +100 -13
  296. package/src/domains/prompts/contract.ts +3 -5
  297. package/src/domains/providers/endpoint-capacity.ts +96 -0
  298. package/src/domains/providers/extension.ts +30 -2
  299. package/src/domains/providers/index.ts +10 -0
  300. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
  301. package/src/domains/providers/runtime-resolution.ts +8 -1
  302. package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
  303. package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
  304. package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
  305. package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
  306. package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
  307. package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
  308. package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
  309. package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
  310. package/src/domains/providers/types/capability-flags.ts +2 -0
  311. package/src/domains/providers/types/target-descriptor.ts +2 -0
  312. package/src/domains/resources/common-loader.ts +3 -0
  313. package/src/domains/resources/prompts/loader.ts +184 -17
  314. package/src/domains/safety/call-target.ts +52 -0
  315. package/src/domains/safety/policy-engine.ts +5 -5
  316. package/src/domains/safety/run-effects.ts +96 -2
  317. package/src/domains/safety/scope.ts +7 -12
  318. package/src/domains/session/context-accounting.ts +52 -1
  319. package/src/domains/session/context-ledger.ts +37 -13
  320. package/src/domains/session/index.ts +6 -0
  321. package/src/domains/session/prompt-cache.ts +140 -0
  322. package/src/domains/session/prompt-manifest.ts +42 -0
  323. package/src/engine/acp/adapter.ts +18 -3
  324. package/src/engine/acp/server.ts +4 -1
  325. package/src/engine/ai.ts +35 -0
  326. package/src/engine/apis/llamacpp-residency.ts +55 -3
  327. package/src/engine/apis/lmstudio.ts +25 -5
  328. package/src/engine/apis/ollama-native.ts +2 -1
  329. package/src/engine/apis/openai-completions.ts +80 -17
  330. package/src/engine/apis/residency-lock.ts +3 -1
  331. package/src/engine/apis/residency.ts +34 -1
  332. package/src/engine/prompt-templates.ts +18 -1
  333. package/src/engine/provider-payload.ts +29 -1
  334. package/src/engine/worker-runtime.ts +6 -3
  335. package/src/entry/orchestrator.ts +176 -30
  336. package/src/interactive/chat-loop-messages.ts +26 -7
  337. package/src/interactive/chat-loop.ts +318 -41
  338. package/src/interactive/chat-panel.ts +62 -8
  339. package/src/interactive/clio-editor.ts +45 -8
  340. package/src/interactive/context-activity.ts +5 -1
  341. package/src/interactive/context-meter.ts +1 -1
  342. package/src/interactive/context-overlay.ts +40 -10
  343. package/src/interactive/cost-overlay.ts +64 -6
  344. package/src/interactive/dispatch-board.ts +127 -5
  345. package/src/interactive/fleet-run-preview.ts +41 -15
  346. package/src/interactive/handoff-round.ts +41 -2
  347. package/src/interactive/interactive-application.ts +24 -1
  348. package/src/interactive/interactive-event-projection.ts +14 -0
  349. package/src/interactive/interactive-input-runtime.ts +8 -0
  350. package/src/interactive/interactive-presentation.ts +4 -0
  351. package/src/interactive/interactive-shell.ts +20 -17
  352. package/src/interactive/interactive-slash-runtime.ts +27 -4
  353. package/src/interactive/memory-overlay.ts +8 -0
  354. package/src/interactive/mutation-preview.ts +295 -0
  355. package/src/interactive/overlay-general-openers.ts +16 -0
  356. package/src/interactive/overlay-key-routing.ts +38 -0
  357. package/src/interactive/overlay-lifecycle.ts +38 -5
  358. package/src/interactive/overlay-permission-lifecycle.ts +22 -2
  359. package/src/interactive/overlay-session-lifecycle.ts +73 -9
  360. package/src/interactive/overlays/ask-user.ts +91 -19
  361. package/src/interactive/overlays/help-reference.ts +4 -0
  362. package/src/interactive/overlays/prompts.ts +11 -1
  363. package/src/interactive/overlays/settings.ts +176 -48
  364. package/src/interactive/permission-hint.ts +34 -2
  365. package/src/interactive/permission-overlay.ts +159 -9
  366. package/src/interactive/prewarm.ts +197 -0
  367. package/src/interactive/render-trace.ts +162 -15
  368. package/src/interactive/renderers/tool-execution.ts +4 -0
  369. package/src/interactive/side-question.ts +58 -1
  370. package/src/interactive/slash-commands.ts +7 -2
  371. package/src/interactive/status/controller.ts +11 -0
  372. package/src/interactive/status/state-machine.ts +54 -2
  373. package/src/interactive/status/types.ts +7 -0
  374. package/src/interactive/terminal-lease.ts +2 -0
  375. package/src/interactive/turn-context.ts +299 -31
  376. package/src/interactive/turn-persistence.ts +14 -4
  377. package/src/interactive/turn-prewarm.ts +364 -0
  378. package/src/interactive/turn-queues.ts +7 -4
  379. package/src/interactive/turn-runtime.ts +8 -1
  380. package/src/interactive/turn-state.ts +23 -0
  381. package/src/interactive/view/artifacts.ts +42 -9
  382. package/src/interactive/view/view-overlay.ts +43 -6
  383. package/src/interactive/worker-receipts.ts +14 -2
  384. package/src/interactive/worker-stream.ts +8 -0
  385. package/src/tools/ask-user.ts +43 -2
  386. package/src/tools/dispatch-admission.ts +12 -13
  387. package/src/tools/dispatch-arguments.ts +27 -0
  388. package/src/tools/dispatch-plan.ts +46 -9
  389. package/src/tools/dispatch-runner.ts +48 -13
  390. package/src/tools/dispatch-scout.ts +1 -1
  391. package/src/tools/monitor.ts +13 -0
  392. package/src/tools/registry.ts +16 -0
  393. package/src/tools/worker-evidence.ts +19 -13
  394. package/src/worker/spec-contract.ts +2 -1
  395. package/dist/chunk-AOCYTWAV.js +0 -449
  396. package/dist/chunk-HWUFFB6L.js +0 -83
  397. package/dist/chunk-R346GLFC.js +0 -31
  398. package/dist/chunk-ZGH7FGS5.js +0 -1079
  399. package/dist/doctor-RN4YKO2X.js +0 -87
  400. package/dist/eval-RUBJVSNQ.js +0 -2557
@@ -1,6 +1,6 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { appendFile } from "node:fs/promises";
3
- import { dirname } from "node:path";
3
+ import { dirname, join } from "node:path";
4
4
  import type { TuiRenderObserver, TuiRenderPhase } from "../engine/tui.js";
5
5
 
6
6
  /** The trace format is append-only JSONL so partial sessions remain readable. */
@@ -106,6 +106,106 @@ export type RenderTraceRecord =
106
106
  }
107
107
  | { type: "trace_drop"; version: typeof RENDER_TRACE_VERSION; at: number; records: number };
108
108
 
109
+ /** One byte-level delivery from the stdin reader to the application listener. */
110
+ export type RenderTraceInputIngressRecord = Extract<RenderTraceRecord, { type: "input_ingress" }>;
111
+
112
+ /**
113
+ * How many `input_ingress` records and how many committed frame records the
114
+ * always-on ring keeps, each. 256 keystrokes and 256 committed frames cover the
115
+ * last stretch of interaction an operator would still be describing when they
116
+ * kill a wedged pane, and cost about 200 KB of process memory in the worst case
117
+ * (a frame record carries its terminal-write commits).
118
+ */
119
+ export const INPUT_WEDGE_RING_CAPACITY = 256;
120
+
121
+ /** Subdirectory of the state directory that holds SIGTERM wedge dumps. */
122
+ export const INPUT_WEDGE_DUMP_DIRNAME = "input-wedge";
123
+
124
+ /** Dumps kept in that directory; older ones are removed as new ones land. */
125
+ export const INPUT_WEDGE_DUMP_RETAINED = 5;
126
+
127
+ /**
128
+ * Which half of the input pipeline was still moving when the dump was taken.
129
+ *
130
+ * `input-not-committed` is the consumer's failure: bytes reached the
131
+ * application listener and no frame that carries them ever reached stdout.
132
+ * `no-input-recorded` is the reader's: nothing was delivered at all, so an
133
+ * operator who was typing was typing into a reader that had stopped.
134
+ * `input-committed` says neither half of this pipeline was stuck.
135
+ */
136
+ export type InputWedgeClassification = "no-input-recorded" | "input-not-committed" | "input-committed";
137
+
138
+ export interface InputWedgeSnapshot {
139
+ version: typeof RENDER_TRACE_VERSION;
140
+ capacity: number;
141
+ pid: number;
142
+ /** Wall clock at trace open, so a record's relative `at` maps onto real time. */
143
+ openedAtEpochMs: number;
144
+ atEpochMs: number;
145
+ /** Milliseconds since trace open, the same clock every record's `at` uses. */
146
+ at: number;
147
+ classification: InputWedgeClassification;
148
+ msSinceLastInputIngress: number | null;
149
+ msSinceLastCommittedFrame: number | null;
150
+ inputIngress: RenderTraceInputIngressRecord[];
151
+ frames: RenderTraceFrameRecord[];
152
+ }
153
+
154
+ /** Bounded FIFO that overwrites its oldest entry rather than growing. */
155
+ function createRing<T>(capacity: number): { push(value: T): void; toArray(): T[] } {
156
+ const slots: T[] = [];
157
+ let next = 0;
158
+ return {
159
+ push(value): void {
160
+ if (slots.length < capacity) slots.push(value);
161
+ else slots[next] = value;
162
+ next = (next + 1) % capacity;
163
+ },
164
+ toArray(): T[] {
165
+ if (slots.length < capacity) return [...slots];
166
+ return [...slots.slice(next), ...slots.slice(0, next)];
167
+ },
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Read the two rings the way the ticket asks the wedge to be read: the newest
173
+ * input the operator should have seen, against the newest frame that reached
174
+ * stdout carrying it.
175
+ */
176
+ function classifyInputWedge(
177
+ inputIngress: ReadonlyArray<RenderTraceInputIngressRecord>,
178
+ frames: ReadonlyArray<RenderTraceFrameRecord>,
179
+ ): InputWedgeClassification {
180
+ const newest = [...inputIngress].reverse().find((record) => record.visualExpected) ?? inputIngress.at(-1);
181
+ if (newest === undefined) return "no-input-recorded";
182
+ return frames.some((frame) => frame.inputHighWater >= newest.inputSeq) ? "input-committed" : "input-not-committed";
183
+ }
184
+
185
+ /**
186
+ * Write one snapshot under `<stateDir>/input-wedge/`, newest-first by name, and
187
+ * drop everything past the retention bound. Synchronous on purpose: the caller
188
+ * is a signal handler on a process that is about to end.
189
+ */
190
+ export function writeInputWedgeDump(stateDir: string, snapshot: InputWedgeSnapshot): string {
191
+ const directory = join(stateDir, INPUT_WEDGE_DUMP_DIRNAME);
192
+ mkdirSync(directory, { recursive: true });
193
+ const stamp = new Date(snapshot.atEpochMs).toISOString().replace(/[:.]/gu, "-");
194
+ const path = join(directory, `${stamp}-${snapshot.pid}.json`);
195
+ writeFileSync(path, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
196
+ try {
197
+ const existing = readdirSync(directory)
198
+ .filter((name) => name.endsWith(".json"))
199
+ .sort();
200
+ for (const name of existing.slice(0, Math.max(0, existing.length - INPUT_WEDGE_DUMP_RETAINED))) {
201
+ rmSync(join(directory, name), { force: true });
202
+ }
203
+ } catch {
204
+ // Retention is housekeeping; a dump that landed is worth more than a tidy directory.
205
+ }
206
+ return path;
207
+ }
208
+
109
209
  interface TraceWriter {
110
210
  enqueue(record: RenderTraceRecord): void;
111
211
  close(): Promise<void>;
@@ -236,6 +336,8 @@ export interface RenderTrace extends TuiRenderObserver {
236
336
  recordTerminalDrain(writeId: number): void;
237
337
  currentFrameId(): number | null;
238
338
  onFirstFrameCommit(listener: (frameId: number) => void): void;
339
+ /** The always-on ring as it stands, classified. Allocates; call it once, on the way out. */
340
+ snapshotInputWedge(): InputWedgeSnapshot;
239
341
  close(): Promise<void>;
240
342
  }
241
343
 
@@ -255,17 +357,32 @@ export function renderTracePath(env: NodeJS.ProcessEnv = process.env): string |
255
357
  return raw && raw.length > 0 ? raw : null;
256
358
  }
257
359
 
360
+ /**
361
+ * One tracer, two sinks. The JSONL file is the armed sink: it exists only when
362
+ * `CLIO_CODER_RENDER_TRACE` named a path, and it keeps every record. The
363
+ * bounded in-memory ring is the always-on sink: it keeps the last
364
+ * {@link INPUT_WEDGE_RING_CAPACITY} `input_ingress` records and the same number
365
+ * of committed frames, so the SIGTERM that recovers a wedged pane can still say
366
+ * which half of the input pipeline stopped (#224). A null `path` is the
367
+ * ring-only mode, and there the recorders that only ever fed the file keep
368
+ * their counters and skip building a record nothing would read.
369
+ */
258
370
  export function createRenderTrace(
259
- path: string,
371
+ path: string | null,
260
372
  now: () => number = () => performance.now(),
261
373
  writerOptions: AsyncTraceWriterOptions = {},
262
374
  ): RenderTrace {
263
375
  // Initialization happens before the terminal/TUI starts, never in a frame.
264
- mkdirSync(dirname(path), { recursive: true });
265
- writeFileSync(path, "", "utf8");
376
+ if (path !== null) {
377
+ mkdirSync(dirname(path), { recursive: true });
378
+ writeFileSync(path, "", "utf8");
379
+ }
266
380
  const openedAt = now();
381
+ const openedAtEpochMs = Date.now();
267
382
  const at = (): number => rounded(now() - openedAt);
268
- const writer = createAsyncTraceWriter(path, { ...writerOptions, recordTime: at });
383
+ const writer = path === null ? null : createAsyncTraceWriter(path, { ...writerOptions, recordTime: at });
384
+ const inputRing = createRing<RenderTraceInputIngressRecord>(INPUT_WEDGE_RING_CAPACITY);
385
+ const frameRing = createRing<RenderTraceFrameRecord>(INPUT_WEDGE_RING_CAPACITY);
269
386
  let frameSeq = 0;
270
387
  let writeSeq = 0;
271
388
  let eventSeq = 0;
@@ -282,7 +399,7 @@ export function createRenderTrace(
282
399
  const ranges = new Map<string, { codeUnits: number; graphemes: number }>();
283
400
  const pendingDrains = new Map<number, { frameId: number | null; at: number }>();
284
401
 
285
- writer.enqueue({ type: "trace_start", version: RENDER_TRACE_VERSION, at: 0 });
402
+ writer?.enqueue({ type: "trace_start", version: RENDER_TRACE_VERSION, at: 0 });
286
403
 
287
404
  const trace: RenderTrace = {
288
405
  recordPanelRender(metrics): void {
@@ -294,12 +411,15 @@ export function createRenderTrace(
294
411
  },
295
412
  recordVisibleEvent(fields): number {
296
413
  eventSeq += 1;
414
+ // The ring keeps no provider deltas, so ring-only mode does not pay for
415
+ // grapheme segmentation on every token of every stream.
416
+ if (writer === null) return eventSeq;
297
417
  const key = `${generation}:${fields.kind}:${fields.contentIndex}`;
298
418
  const previous = ranges.get(key) ?? { codeUnits: 0, graphemes: 0 };
299
419
  const graphemes = graphemeCount(fields.delta);
300
420
  const next = { codeUnits: previous.codeUnits + fields.delta.length, graphemes: previous.graphemes + graphemes };
301
421
  ranges.set(key, next);
302
- writer.enqueue({
422
+ writer?.enqueue({
303
423
  type: "event_ingress",
304
424
  version: RENDER_TRACE_VERSION,
305
425
  eventSeq,
@@ -317,7 +437,7 @@ export function createRenderTrace(
317
437
  },
318
438
  recordQueue(sequence, action): void {
319
439
  queueDepth = Math.max(0, queueDepth + (action === "admit" ? 1 : -1));
320
- writer.enqueue({
440
+ writer?.enqueue({
321
441
  type: "queue",
322
442
  version: RENDER_TRACE_VERSION,
323
443
  eventSeq: sequence,
@@ -328,7 +448,7 @@ export function createRenderTrace(
328
448
  },
329
449
  recordPanelApplied(sequence): void {
330
450
  panelHighWater = Math.max(panelHighWater, sequence);
331
- writer.enqueue({
451
+ writer?.enqueue({
332
452
  type: "panel",
333
453
  version: RENDER_TRACE_VERSION,
334
454
  eventSeq: sequence,
@@ -339,7 +459,7 @@ export function createRenderTrace(
339
459
  recordInputIngress(action, bytes, visualExpected = action !== "no-visual-change"): number {
340
460
  inputSeq += 1;
341
461
  if (visualExpected) inputHighWater = inputSeq;
342
- writer.enqueue({
462
+ const record: RenderTraceInputIngressRecord = {
343
463
  type: "input_ingress",
344
464
  version: RENDER_TRACE_VERSION,
345
465
  inputSeq,
@@ -347,7 +467,9 @@ export function createRenderTrace(
347
467
  at: at(),
348
468
  bytes,
349
469
  visualExpected,
350
- });
470
+ };
471
+ inputRing.push(record);
472
+ writer?.enqueue(record);
351
473
  return inputSeq;
352
474
  },
353
475
  beginFrame(fields): FrameState {
@@ -403,8 +525,11 @@ export function createRenderTrace(
403
525
  },
404
526
  commits: frame.commits,
405
527
  };
406
- writer.enqueue(record);
528
+ writer?.enqueue(record);
407
529
  if (frame.commits.length > 0) {
530
+ // Only a frame that reached stdout answers the question the ring exists
531
+ // for, which is whether the consumer half is still painting.
532
+ frameRing.push(record);
408
533
  lastCommitAt = frame.commits.at(-1)?.at ?? endAt;
409
534
  if (!firstCommitDelivered) {
410
535
  firstCommitDelivered = true;
@@ -439,6 +564,7 @@ export function createRenderTrace(
439
564
  backpressured: !fields.returned,
440
565
  };
441
566
  currentFrame?.commits.push(commit);
567
+ if (writer === null) return writeSeq;
442
568
  writer.enqueue({ type: "terminal_write", version: RENDER_TRACE_VERSION, frameId, ...commit });
443
569
  if (!fields.returned) pendingDrains.set(writeSeq, { frameId, at: commit.at });
444
570
  return writeSeq;
@@ -448,7 +574,7 @@ export function createRenderTrace(
448
574
  if (!pending) return;
449
575
  pendingDrains.delete(writeId);
450
576
  const drainAt = at();
451
- writer.enqueue({
577
+ writer?.enqueue({
452
578
  type: "terminal_drain",
453
579
  version: RENDER_TRACE_VERSION,
454
580
  writeId,
@@ -461,7 +587,28 @@ export function createRenderTrace(
461
587
  onFirstFrameCommit(listener): void {
462
588
  firstCommitListener = listener;
463
589
  },
464
- close: () => writer.close(),
590
+ snapshotInputWedge(): InputWedgeSnapshot {
591
+ const inputIngress = inputRing.toArray();
592
+ const frames = frameRing.toArray();
593
+ const nowAt = at();
594
+ const lastIngressAt = inputIngress.at(-1)?.at;
595
+ const lastFrame = frames.at(-1);
596
+ const lastFrameCommitAt = lastFrame?.commits.at(-1)?.at ?? lastFrame?.endAt;
597
+ return {
598
+ version: RENDER_TRACE_VERSION,
599
+ capacity: INPUT_WEDGE_RING_CAPACITY,
600
+ pid: process.pid,
601
+ openedAtEpochMs,
602
+ atEpochMs: Date.now(),
603
+ at: nowAt,
604
+ classification: classifyInputWedge(inputIngress, frames),
605
+ msSinceLastInputIngress: lastIngressAt === undefined ? null : rounded(nowAt - lastIngressAt),
606
+ msSinceLastCommittedFrame: lastFrameCommitAt === undefined ? null : rounded(nowAt - lastFrameCommitAt),
607
+ inputIngress,
608
+ frames,
609
+ };
610
+ },
611
+ close: () => writer?.close() ?? Promise.resolve(),
465
612
  };
466
613
  return trace;
467
614
  }
@@ -18,6 +18,7 @@ import { formatSize } from "../../engine/truncate.js";
18
18
  import { visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
19
19
  import { classifyResourceRead, toolPresentationPolicy } from "../../tools/presentation.js";
20
20
  import { toolResultPresentationPolicy, toolResultPresentationText } from "../../tools/result-disposition.js";
21
+ import { mutationFactsLine } from "../mutation-preview.js";
21
22
  import type { ApprovalRequestView } from "../permission-overlay.js";
22
23
  import { clioTheme, formatCompactMs, GLYPH } from "../theme/index.js";
23
24
  import { renderDiffLines } from "./diff.js";
@@ -490,6 +491,9 @@ export function renderToolAwaitingApproval(
490
491
  ["action", view.actionClass],
491
492
  ["axis", axis],
492
493
  ...(view.target !== undefined && view.target.length > 0 ? [["target", view.target]] : []),
494
+ // Size and digest, never the mutation text: this row is the transcript,
495
+ // which is written, replayed, and shared (issue #254).
496
+ ...(view.mutation !== undefined ? [["mutation", mutationFactsLine(view.mutation)]] : []),
493
497
  ] as const;
494
498
  for (const [label, value] of facts) {
495
499
  lines.push(...indentAndWrap(`${dim(`${label} ·`)} ${value}`, width, false));
@@ -13,7 +13,13 @@
13
13
  * machine without adding an engine seam of its own. Tools are never sent.
14
14
  */
15
15
 
16
+ import {
17
+ isResponseSchemaRejection,
18
+ type ResponseSchemaDialect,
19
+ responseSchemaDialectFor,
20
+ } from "../core/response-schema.js";
16
21
  import { stream } from "../engine/ai.js";
22
+ import { patchResponseSchemaPayloadForDialect } from "../engine/provider-payload.js";
17
23
  import type { AgentMessage, EngineModel, Usage } from "../engine/types.js";
18
24
 
19
25
  /**
@@ -67,6 +73,18 @@ export interface OutOfTurnRoundInput extends Omit<SideQuestionInput, "question">
67
73
  systemPrompt: string;
68
74
  /** The one message appended to the read-only copy of the history. */
69
75
  userText: string;
76
+ /**
77
+ * Bind the answer to a JSON schema on the wire, where the runtime takes one.
78
+ *
79
+ * A round that asked for a schema in prose and bound nothing is what made
80
+ * `/handoff` refuse on local targets: the model answered with prose around
81
+ * the object, or with no object at all, and the parser had nothing to work
82
+ * with (issue #223). Absent, or on a runtime with no dialect, the request
83
+ * goes out exactly as before and the prompt-level instruction carries it.
84
+ */
85
+ responseSchema?: { name: string; schema: Record<string, unknown> };
86
+ /** Resolved runtime id, which is what decides the wire dialect. */
87
+ runtimeId?: string;
70
88
  }
71
89
 
72
90
  export interface SideQuestionResult {
@@ -80,7 +98,12 @@ function positive(value: unknown): number {
80
98
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
81
99
  }
82
100
 
83
- function sideQuestionUsage(raw: unknown): SideQuestionUsage | null {
101
+ /**
102
+ * Normalize one provider usage object into the shape `/cost` and the
103
+ * out-of-turn usage store take. Shared with the session pre-warm, which is
104
+ * another call billed beside the session rather than inside it.
105
+ */
106
+ export function sideQuestionUsage(raw: unknown): SideQuestionUsage | null {
84
107
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
85
108
  const usage = raw as Partial<Usage> & { reasoning?: number };
86
109
  const input = positive(usage.input);
@@ -128,11 +151,45 @@ export async function runSideQuestion(input: SideQuestionInput): Promise<SideQue
128
151
  return runOutOfTurnRound({ ...rest, systemPrompt: SIDE_QUESTION_SYSTEM_PROMPT, userText: question });
129
152
  }
130
153
 
154
+ interface SchemaBinding {
155
+ dialect: ResponseSchemaDialect;
156
+ name: string;
157
+ schema: Record<string, unknown>;
158
+ }
159
+
160
+ /** The schema binding for this round, or null when nothing can be bound on the wire. */
161
+ function schemaBindingFor(input: OutOfTurnRoundInput): SchemaBinding | null {
162
+ if (input.responseSchema === undefined || input.runtimeId === undefined) return null;
163
+ const dialect = responseSchemaDialectFor(input.runtimeId);
164
+ return dialect === null ? null : { dialect, ...input.responseSchema };
165
+ }
166
+
131
167
  /** The shared round. See {@link OutOfTurnRoundInput}. */
132
168
  export async function runOutOfTurnRound(input: OutOfTurnRoundInput): Promise<SideQuestionResult> {
169
+ const binding = schemaBindingFor(input);
170
+ if (binding === null) return runRound(input, null);
171
+ try {
172
+ return await runRound(input, binding);
173
+ } catch (error) {
174
+ // Native enforcement is an optimization here, never a precondition. A
175
+ // server that refuses the constrained request answers with the same 400
176
+ // the worker seam already recognizes, and the round is worth more
177
+ // unconstrained than not at all. Anything else is a real failure and
178
+ // rejects, so a broken target still costs exactly one round.
179
+ if (input.signal?.aborted === true) throw error;
180
+ if (!isResponseSchemaRejection(error instanceof Error ? error.message : String(error))) throw error;
181
+ return runRound(input, null);
182
+ }
183
+ }
184
+
185
+ async function runRound(input: OutOfTurnRoundInput, binding: SchemaBinding | null): Promise<SideQuestionResult> {
133
186
  const options: Record<string, unknown> = { maxTokens: input.maxTokens ?? SIDE_QUESTION_MAX_TOKENS };
134
187
  if (input.apiKey !== undefined) options.apiKey = input.apiKey;
135
188
  if (input.signal !== undefined) options.signal = input.signal;
189
+ if (binding !== null) {
190
+ options.onPayload = (payload: unknown): unknown | undefined =>
191
+ patchResponseSchemaPayloadForDialect(payload, binding.dialect, binding.schema, binding.name);
192
+ }
136
193
 
137
194
  const context = {
138
195
  systemPrompt: input.systemPrompt,
@@ -6,9 +6,9 @@ import type { AgentSpec } from "../domains/agents/spec.js";
6
6
  import type { ContextInitOptions } from "../domains/context/init-options.js";
7
7
  import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
8
8
  import { type AgentRoleFactsResolver, requestExecutionRole } from "../domains/dispatch/execution-role.js";
9
- import type { ReceiptIntegrityResult } from "../domains/dispatch/receipt-integrity.js";
10
9
  import type { RunReceipt } from "../domains/dispatch/types.js";
11
10
  import type { JobThinkingLevel } from "../domains/dispatch/validation.js";
11
+ import type { ReceiptIntegrityOutcome } from "../domains/evidence/trust-status.js";
12
12
  import type { InstalledExtension } from "../domains/extensions/index.js";
13
13
  import type { InteropAgentId, InteropProposal, InteropReport } from "../domains/interop/index.js";
14
14
  import type { ProvidersContract, ResolvedModelRef } from "../domains/providers/index.js";
@@ -665,7 +665,7 @@ export interface SlashCommandContext {
665
665
  * and emit a single status line. Kept on the context so the registry does
666
666
  * not import the overlay module.
667
667
  */
668
- verifyReceipt: (runId: string) => ReceiptIntegrityResult;
668
+ verifyReceipt: (runId: string) => ReceiptIntegrityOutcome;
669
669
  /**
670
670
  * Handles the "unknown" case: append the text to the chat panel as a user
671
671
  * turn, submit to the chat loop, and schedule a re-render. Handlers for
@@ -1507,6 +1507,11 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
1507
1507
  const result = ctx.verifyReceipt(command.runId);
1508
1508
  if (result.ok) {
1509
1509
  ctx.notice("success", `verify ok ${command.runId}`);
1510
+ } else if (result.retired !== undefined) {
1511
+ // A retired seal is the expected state of a run from the previous
1512
+ // release, so it is a warning that names the versions, never the
1513
+ // failure a tampered receipt gets.
1514
+ ctx.notice("warn", `verify retired ${command.runId} ${result.reason}`);
1510
1515
  } else {
1511
1516
  ctx.notice("error", `verify fail ${command.runId} ${result.reason}`);
1512
1517
  }
@@ -178,6 +178,17 @@ export function createStatusController(deps: StatusControllerDeps): StatusContro
178
178
  notify(prev, status, force);
179
179
  };
180
180
 
181
+ // The window between the editor and the stream. `preparing` and `idle`
182
+ // bracket it; `compacting` inside it is already carried by the compaction
183
+ // bus overlay, which now lands because `preparing` is an active phase.
184
+ if (deps.chat.onTurnPreparation) {
185
+ unsubscribes.push(
186
+ deps.chat.onTurnPreparation((phase) => {
187
+ apply({ type: phase === "idle" ? "submit_settled" : "submit_accepted" }, true);
188
+ }),
189
+ );
190
+ }
191
+
181
192
  unsubscribes.push(
182
193
  deps.chat.onEvent((event: ChatLoopEvent) => {
183
194
  if (event.type === "agent_status") return;
@@ -53,6 +53,21 @@ export interface DispatchProgressStatusEvent {
53
53
  type: "dispatch_progress";
54
54
  }
55
55
 
56
+ /**
57
+ * A prompt has left the editor and Clio is preparing the turn: capability
58
+ * probe, pre-submit compaction, prompt compile, overflow preflight. None of
59
+ * that produces an engine event, so without this the footer kept reporting the
60
+ * previous turn as done for the whole window (issue #251).
61
+ */
62
+ export interface SubmitAcceptedStatusEvent {
63
+ type: "submit_accepted";
64
+ }
65
+
66
+ /** The prepared turn was admitted, refused, or failed. Either way the window is over. */
67
+ export interface SubmitSettledStatusEvent {
68
+ type: "submit_settled";
69
+ }
70
+
56
71
  export type StatusInputEvent =
57
72
  | ChatLoopEvent
58
73
  | WatchdogTickEvent
@@ -60,7 +75,9 @@ export type StatusInputEvent =
60
75
  | OverlayPopEvent
61
76
  | RunAbortedStatusEvent
62
77
  | ForceCancelledStatusEvent
63
- | DispatchProgressStatusEvent;
78
+ | DispatchProgressStatusEvent
79
+ | SubmitAcceptedStatusEvent
80
+ | SubmitSettledStatusEvent;
64
81
 
65
82
  const OVERLAY_PHASES = new Set<StatusPhase>(["tool_blocked", "retrying", "compacting", "dispatching", "stuck"]);
66
83
  const CORE_ACTIVE_PHASES = new Set<StatusPhase>(["preparing", "waiting_model", "thinking", "writing", "tool_running"]);
@@ -292,8 +309,43 @@ function retryOverlay(status: {
292
309
 
293
310
  export function reduceStatus(prev: AgentStatus, event: StatusInputEvent, ctx: ReduceContext): AgentStatus {
294
311
  switch (event.type) {
295
- case "agent_start":
312
+ // A consumed prompt occupies `preparing` before any engine event exists,
313
+ // and the previous turn's summary rides along untouched: a refused
314
+ // admission puts the footer back on the last turn that actually ran,
315
+ // which is still the truth about what happened.
316
+ case "submit_accepted":
296
317
  if (isActive(prev.phase)) return prev;
318
+ return {
319
+ ...prev,
320
+ phase: "preparing",
321
+ since: ctx.now,
322
+ lastMeaningfulAt: ctx.now,
323
+ localRuntime: ctx.localRuntime,
324
+ preparingSubmission: true,
325
+ watchdogTier: 0,
326
+ resumePhase: undefined,
327
+ activePhases: undefined,
328
+ overlayStack: [],
329
+ tool: undefined,
330
+ toolStartedAt: undefined,
331
+ retry: undefined,
332
+ dispatch: undefined,
333
+ };
334
+ case "submit_settled":
335
+ if (prev.preparingSubmission !== true) return prev;
336
+ return {
337
+ ...prev,
338
+ phase: prev.summary ? "ended" : "idle",
339
+ since: ctx.now,
340
+ preparingSubmission: undefined,
341
+ resumePhase: undefined,
342
+ activePhases: undefined,
343
+ overlayStack: [],
344
+ };
345
+ case "agent_start":
346
+ // A window this opened is the same turn arriving, not a duplicate
347
+ // start, so the run resets through it rather than being ignored.
348
+ if (isActive(prev.phase) && prev.preparingSubmission !== true) return prev;
297
349
  return {
298
350
  ...INITIAL_STATUS,
299
351
  phase: "preparing",
@@ -106,6 +106,13 @@ export interface AgentStatus {
106
106
  toolStartedAt?: number | undefined;
107
107
  retry?: RetryOverlay | undefined;
108
108
  dispatch?: DispatchOverlay | undefined;
109
+ /**
110
+ * Set while the `preparing` phase belongs to a consumed prompt that has not
111
+ * yet been admitted, rather than to a run the engine has started. It is what
112
+ * lets `agent_start` take over a window this opened instead of treating an
113
+ * already-active phase as a duplicate start (issue #251).
114
+ */
115
+ preparingSubmission?: boolean | undefined;
109
116
  summary?: TurnSummary | undefined;
110
117
  /** Usage settled so far in the active run. Reset by `agent_start`. */
111
118
  runTally?: RunTally | undefined;
@@ -190,6 +190,8 @@ export function createProcessTerminalLease(options: CreateProcessTerminalLeaseOp
190
190
  getThinkingLabel: () => editorChrome.getThinkingLabel(),
191
191
  isStreaming: () => editorChrome.isStreaming?.() ?? false,
192
192
  isAwaitingApproval: () => editorChrome.isAwaitingApproval?.() ?? false,
193
+ getPermissionInspection: () => editorChrome.getPermissionInspection?.() ?? "none",
194
+ getTurnPreparation: () => editorChrome.getTurnPreparation?.() ?? "idle",
193
195
  willEnterSteer: (text) => editorChrome.willEnterSteer?.(text) ?? false,
194
196
  getSubmitKeyLabel: () => editorChrome.getSubmitKeyLabel?.() ?? "Enter",
195
197
  getNewlineKeyLabel: () => editorChrome.getNewlineKeyLabel?.() ?? "Shift+Enter",