@iowarp/clio-coder 0.3.1 → 0.3.2

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 (595) hide show
  1. package/CHANGELOG.md +90 -2
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-BIYHVZIM.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-YT6SSRIT.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/{chunk-PV4JUBVJ.js → chunk-2EHAIA3X.js} +40 -21
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/chunk-2VTFPG5O.js +48 -0
  12. package/dist/{chunk-MAR7Y6HW.js → chunk-3ZXDFGR5.js} +23 -16
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4KLWL3UC.js +18 -0
  16. package/dist/chunk-4VP4KH3K.js +962 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  21. package/dist/chunk-65DEGPJ6.js +52 -0
  22. package/dist/chunk-6EJMN2Y3.js +17 -0
  23. package/dist/chunk-6EJV5X2W.js +16405 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6XLNIQDB.js +27 -0
  26. package/dist/{chunk-TEKV33Q5.js → chunk-77VKQEHF.js} +65 -33
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/{chunk-M5T5VO65.js → chunk-7EYHLWU7.js} +837 -635
  29. package/dist/chunk-7MNJORFF.js +22 -0
  30. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  31. package/dist/chunk-AGYYIBLL.js +1069 -0
  32. package/dist/chunk-AO4RKG4M.js +277 -0
  33. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  34. package/dist/chunk-ARBGF5F7.js +174 -0
  35. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  36. package/dist/chunk-CBCAPZAA.js +229 -0
  37. package/dist/chunk-CMZWFGD2.js +352 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/{chunk-RPTR2H26.js → chunk-EPVUXGXG.js} +21 -15
  42. package/dist/chunk-FCSXB6T2.js +338 -0
  43. package/dist/chunk-FJ3H4MN5.js +48 -0
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G2DE3C7R.js +644 -0
  46. package/dist/chunk-G4BMMOKF.js +182 -0
  47. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  48. package/dist/chunk-HC4CLZ2Y.js +68 -0
  49. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  50. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  51. package/dist/chunk-I4HZDVNP.js +73 -0
  52. package/dist/{chunk-4QKXUHSR.js → chunk-IGLFWIYI.js} +70 -20
  53. package/dist/chunk-IKCO5N3L.js +162 -0
  54. package/dist/chunk-IR4CFBFN.js +56 -0
  55. package/dist/{chunk-PFEFKVGL.js → chunk-J5HN4RYU.js} +13 -11
  56. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  57. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  58. package/dist/chunk-KJ5LWLOE.js +1077 -0
  59. package/dist/chunk-LBMZMYH2.js +285 -0
  60. package/dist/{chunk-G34LV2PF.js → chunk-LBNRH5WM.js} +84 -170
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/chunk-MAW544W2.js +1882 -0
  64. package/dist/chunk-MBS4V7ZP.js +217 -0
  65. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  66. package/dist/chunk-MNA4JGU4.js +255 -0
  67. package/dist/chunk-MQSRRFWA.js +3428 -0
  68. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  69. package/dist/chunk-OAO4GE4M.js +619 -0
  70. package/dist/chunk-OHHN2SO4.js +5135 -0
  71. package/dist/chunk-OKGUZO2U.js +34 -0
  72. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  73. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  74. package/dist/chunk-OQE5J4C6.js +73 -0
  75. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  76. package/dist/chunk-POHLU5DW.js +1186 -0
  77. package/dist/chunk-QKMUKYO7.js +4961 -0
  78. package/dist/{chunk-ASND7OZK.js → chunk-QTYWRVRA.js} +13 -13
  79. package/dist/{chunk-EYOKLTMF.js → chunk-SRF2PJNW.js} +17 -3
  80. package/dist/chunk-SST6Z5JA.js +80 -0
  81. package/dist/chunk-STBPMHSX.js +2456 -0
  82. package/dist/chunk-T6YILFSB.js +80 -0
  83. package/dist/chunk-TZTZS7QK.js +227 -0
  84. package/dist/chunk-UOV2BYIW.js +107 -0
  85. package/dist/{chunk-Q3RUPKEJ.js → chunk-V4RXGQ5Q.js} +58 -189
  86. package/dist/chunk-VAKQQHWR.js +434 -0
  87. package/dist/chunk-VG7TBQIY.js +128 -0
  88. package/dist/chunk-VJWL6YS5.js +244 -0
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-WMSVI4G2.js +2095 -0
  91. package/dist/chunk-WVO7V2QY.js +797 -0
  92. package/dist/chunk-X4RCMKVQ.js +641 -0
  93. package/dist/chunk-X75S7HFS.js +374 -0
  94. package/dist/chunk-XN3L4EYL.js +46 -0
  95. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  96. package/dist/chunk-YHZX5GEU.js +193 -0
  97. package/dist/chunk-YXLYO42X.js +91 -0
  98. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  99. package/dist/chunk-ZI647VB5.js +37 -0
  100. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-4LY5K2AC.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-GTLUW2PR.js} +92 -55
  108. package/dist/configure-R6A64DHX.js +42 -0
  109. package/dist/context-5VKGUVJJ.js +866 -0
  110. package/dist/{context-3KWFLHJG.js → context-JFZEJ7W5.js} +15 -13
  111. package/dist/{context-5RADCKTR.js → context-RW5HC47S.js} +71 -35
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-6ZHBAZZT.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-KI767GSN.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-UA6AWDQQ.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-QNTFGV6Z.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-Q7UOMUSG.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-WBB65ZHQ.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-MD3O64RI.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-BZU34YWD.js} +39 -25
  126. package/dist/monitor-MEQA5C3I.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-CGFKEP27.js} +11832 -8687
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-IV4Q6RLN.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-LQEKRDTN.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-3DC4HEWS.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-C4SSGQOB.js} +43 -27
  137. package/dist/terminal-lease-IT5JW2NR.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-7TT7SQ3G.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-GV4PKT3M.js} +62 -31
  143. package/dist/verify-G6V4D2G7.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-DQF6Z66B.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +362 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +42 -6
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +173 -47
  444. package/src/interactive/chat-renderer.ts +262 -72
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +228 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +20 -2
  456. package/src/interactive/footer-panel.ts +2 -2
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +201 -17
  459. package/src/interactive/interactive-event-projection.ts +6 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +176 -114
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/decisions.ts +300 -0
  474. package/src/interactive/overlays/help-reference.ts +15 -10
  475. package/src/interactive/overlays/model-selector.ts +34 -16
  476. package/src/interactive/overlays/session-selector.ts +18 -0
  477. package/src/interactive/overlays/settings.ts +105 -17
  478. package/src/interactive/overlays/skills-hub.ts +4 -4
  479. package/src/interactive/overlays/tree-selector.ts +41 -6
  480. package/src/interactive/render-trace.ts +499 -90
  481. package/src/interactive/renderers/compaction-summary.ts +2 -2
  482. package/src/interactive/renderers/diff.ts +115 -104
  483. package/src/interactive/renderers/mermaid.ts +53 -0
  484. package/src/interactive/renderers/tool-execution.ts +386 -133
  485. package/src/interactive/renderers/worker-entry.ts +20 -4
  486. package/src/interactive/session-switch-settlement.ts +10 -0
  487. package/src/interactive/slash-autocomplete.ts +6 -114
  488. package/src/interactive/slash-commands.ts +135 -47
  489. package/src/interactive/slash-spec.ts +9 -38
  490. package/src/interactive/status/controller.ts +5 -1
  491. package/src/interactive/stdout-backpressure.ts +99 -0
  492. package/src/interactive/stream-pacer.ts +530 -0
  493. package/src/interactive/stream-pacing-policy.ts +66 -0
  494. package/src/interactive/tasks-overlay.ts +368 -14
  495. package/src/interactive/terminal-lease.ts +485 -0
  496. package/src/interactive/theme/tokens.ts +1 -1
  497. package/src/interactive/turn-context.ts +4 -3
  498. package/src/interactive/turn-persistence.ts +30 -13
  499. package/src/interactive/turn-queues.ts +12 -0
  500. package/src/interactive/turn-recovery.ts +25 -8
  501. package/src/interactive/turn-runtime.ts +79 -12
  502. package/src/interactive/turn-state.ts +10 -0
  503. package/src/interactive/view/artifacts.ts +114 -4
  504. package/src/interactive/view/view-overlay.ts +3 -0
  505. package/src/interactive/welcome-dashboard.ts +17 -16
  506. package/src/interactive/worker-receipts.ts +52 -3
  507. package/src/interactive/worker-stream.ts +5 -1
  508. package/src/tools/agent-tools.ts +23 -3
  509. package/src/tools/artifact.ts +2 -2
  510. package/src/tools/ask-user.ts +23 -13
  511. package/src/tools/bash.ts +30 -2
  512. package/src/tools/bootstrap.ts +34 -431
  513. package/src/tools/builtin-tool-catalog.ts +265 -0
  514. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  515. package/src/tools/codewiki/code-nav.ts +8 -22
  516. package/src/tools/codewiki/shared.ts +41 -38
  517. package/src/tools/context/docs-engine.ts +14 -3
  518. package/src/tools/context/index.ts +107 -28
  519. package/src/tools/context/surface.ts +19 -0
  520. package/src/tools/core-bootstrap.ts +168 -0
  521. package/src/tools/credential-present.ts +5 -5
  522. package/src/tools/dispatch-admission.ts +533 -0
  523. package/src/tools/dispatch-background.ts +54 -0
  524. package/src/tools/dispatch-event-text.ts +6 -0
  525. package/src/tools/dispatch-plan.ts +9 -4
  526. package/src/tools/dispatch-run-events.ts +238 -0
  527. package/src/tools/dispatch-runner.ts +2370 -0
  528. package/src/tools/dispatch-scout-admission.ts +295 -0
  529. package/src/tools/dispatch-types.ts +77 -0
  530. package/src/tools/dispatch.ts +67 -3161
  531. package/src/tools/find.ts +4 -2
  532. package/src/tools/grep.ts +2 -2
  533. package/src/tools/lazy-tool.ts +60 -0
  534. package/src/tools/ledger.ts +3 -3
  535. package/src/tools/monitor-surface.ts +36 -0
  536. package/src/tools/monitor.ts +2 -32
  537. package/src/tools/observers.ts +2 -2
  538. package/src/tools/registry.ts +39 -27
  539. package/src/tools/safe-exec.ts +2 -2
  540. package/src/tools/steer-surface.ts +17 -0
  541. package/src/tools/steer.ts +2 -13
  542. package/src/tools/tasks.ts +108 -11
  543. package/src/tools/truncate.ts +25 -184
  544. package/src/tools/verify/frontend.ts +3 -1
  545. package/src/tools/verify/index.ts +3 -38
  546. package/src/tools/verify/surface.ts +46 -0
  547. package/src/tools/web-fetch-surface.ts +23 -0
  548. package/src/tools/web-fetch.ts +2 -20
  549. package/src/tools/write.ts +7 -2
  550. package/src/worker/entry.ts +39 -2
  551. package/src/worker/spec-contract.ts +26 -5
  552. package/dist/chunk-7SS2CTV2.js +0 -61361
  553. package/dist/chunk-DKGKUHFA.js +0 -924
  554. package/dist/chunk-GEP36Y4X.js +0 -12796
  555. package/dist/chunk-XYWBQRDM.js +0 -137
  556. package/dist/clio-BZVGEUFJ.js +0 -58
  557. package/dist/configure-S7S6F6CL.js +0 -32
  558. package/docs/html/agents_blueprint.html +0 -936
  559. package/docs/html/alcf_blueprint.html +0 -324
  560. package/docs/html/architecture_blueprint.html +0 -850
  561. package/docs/html/commands_blueprint.html +0 -939
  562. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  563. package/docs/html/configuration_blueprint.html +0 -1080
  564. package/docs/html/context_blueprint.html +0 -603
  565. package/docs/html/documentation_blueprint.html +0 -832
  566. package/docs/html/environment_blueprint.html +0 -404
  567. package/docs/html/eval_blueprint.html +0 -743
  568. package/docs/html/evals_internal_blueprint.html +0 -190
  569. package/docs/html/evolution_blueprint.html +0 -674
  570. package/docs/html/extensions_blueprint.html +0 -2065
  571. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  572. package/docs/html/index.html +0 -919
  573. package/docs/html/lifecycle_blueprint.html +0 -723
  574. package/docs/html/memory_blueprint.html +0 -699
  575. package/docs/html/middleware_blueprint.html +0 -664
  576. package/docs/html/models_blueprint.html +0 -2366
  577. package/docs/html/observability_blueprint.html +0 -683
  578. package/docs/html/provider_adapter_blueprint.html +0 -245
  579. package/docs/html/safety_blueprint.html +0 -1386
  580. package/docs/html/shared.css +0 -571
  581. package/docs/html/shared.js +0 -143
  582. package/docs/html/skills_blueprint.html +0 -671
  583. package/docs/html/soak_blueprint.html +0 -182
  584. package/docs/html/tool_usage_blueprint.html +0 -350
  585. package/docs/html/tools_blueprint.html +0 -2249
  586. package/docs/html/trace_blueprint.html +0 -235
  587. package/docs/html/tui_design_blueprint.html +0 -374
  588. package/docs/html/validation_blueprint.html +0 -961
  589. package/docs/html/worker_dispatch_blueprint.html +0 -231
  590. package/src/core/release.ts +0 -2
  591. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  592. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  593. package/src/engine/apis/lmstudio-native.ts +0 -1438
  594. package/src/engine/apis/thinking-replay.ts +0 -11
  595. package/src/tools/string-enum.ts +0 -15
@@ -79,9 +79,10 @@ export interface TurnRecovery {
79
79
  * that landed in the same window wins: `activeInterruptReason` is set only by
80
80
  * `ChatLoop.cancel`, so the stall reason is dropped rather than retried.
81
81
  *
82
- * The failure's assistant message is rewritten in place because it is the same
83
- * object the ledger persists and the transcript renders; without it the turn
84
- * would read "[aborted] Request was aborted." instead of naming the stall.
82
+ * The runtime event seam calls `rewriteStallAbortMessage` before publishing or
83
+ * persisting `message_end`, so the provider's one terminal message becomes the
84
+ * corrected ledger row. This post-settlement pass covers engines that settle
85
+ * without emitting that event and consumes the watchdog reason in either case.
85
86
  */
86
87
  export function reclassifyStallAbort(
87
88
  state: ChatTurnState,
@@ -90,14 +91,30 @@ export function reclassifyStallAbort(
90
91
  const reason = state.streamStallReason;
91
92
  state.streamStallReason = null;
92
93
  if (reason === null || failure.stopReason !== "aborted" || state.activeInterruptReason !== null) return failure;
93
- if (failure.message) {
94
- const message = failure.message as { stopReason?: unknown; errorMessage?: unknown };
95
- message.stopReason = "error";
96
- message.errorMessage = reason;
97
- }
94
+ if (failure.message) rewriteStallAbortMessage(state, failure.message, reason);
98
95
  return { ...failure, stopReason: "error", errorMessage: reason };
99
96
  }
100
97
 
98
+ /** Rewrite the provider's terminal abort object before its event is emitted and persisted. */
99
+ export function rewriteStallAbortMessage(
100
+ state: ChatTurnState,
101
+ message: AgentMessage,
102
+ reason = state.streamStallReason,
103
+ ): boolean {
104
+ if (
105
+ reason === null ||
106
+ state.activeInterruptReason !== null ||
107
+ message.role !== "assistant" ||
108
+ (message as { stopReason?: unknown }).stopReason !== "aborted"
109
+ ) {
110
+ return false;
111
+ }
112
+ const failure = message as { stopReason?: unknown; errorMessage?: unknown };
113
+ failure.stopReason = "error";
114
+ failure.errorMessage = reason;
115
+ return true;
116
+ }
117
+
101
118
  export function createTurnRecovery(deps: TurnRecoveryDeps): TurnRecovery {
102
119
  const { state, persistence, context } = deps;
103
120
  let retryCountdown: RetryCountdownHandle | null = null;
@@ -11,7 +11,6 @@ import type { MiddlewareToolChoiceControl } from "../domains/middleware/index.js
11
11
  import type { ObservabilityContract } from "../domains/observability/contract.js";
12
12
  import {
13
13
  applyModelCapabilityPatch,
14
- firstRuntimeResolutionError,
15
14
  modelResidencyForStatus,
16
15
  type ProvidersContract,
17
16
  refineRuntimeTargetWithModelHints,
@@ -35,6 +34,7 @@ import {
35
34
  type AssistantCallTiming,
36
35
  type BackendCacheVerdict,
37
36
  backendCacheVerdict,
37
+ estimatedUsageForInterruptedTurn,
38
38
  extractText,
39
39
  extractThinking,
40
40
  fallbackIdentityPrompt,
@@ -52,6 +52,22 @@ import type { AgentRuntime, ChatLoopTarget, ChatTurnState } from "./turn-state.j
52
52
 
53
53
  const LOCAL_API_KEY_FALLBACK = "clio-local-target";
54
54
 
55
+ /**
56
+ * A turn that never started, with a machine-readable reason attached. The
57
+ * operator-facing message is unchanged prose; `reason` exists because the ACP
58
+ * server has to tell a client *why* Clio refused to admit the prompt, and
59
+ * parsing that out of the prose would bind a wire contract to a sentence.
60
+ */
61
+ export class TurnAdmissionError extends Error {
62
+ constructor(
63
+ message: string,
64
+ readonly reason: string,
65
+ ) {
66
+ super(message);
67
+ this.name = "TurnAdmissionError";
68
+ }
69
+ }
70
+
55
71
  /**
56
72
  * Say that the first request will pay for a load, before it does.
57
73
  *
@@ -177,8 +193,11 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
177
193
  requireOutputBudget: true,
178
194
  });
179
195
  if (!resolved.ok) {
180
- const message = firstRuntimeResolutionError(resolved.diagnostics) ?? resolved.diagnostics[0]?.message;
181
- throw new Error(`[Clio Coder] ${message ?? "orchestrator target resolution failed"}`);
196
+ const failure = resolved.diagnostics.find((entry) => entry.severity === "error") ?? resolved.diagnostics[0];
197
+ throw new TurnAdmissionError(
198
+ `[Clio Coder] ${failure?.message ?? "orchestrator target resolution failed"}`,
199
+ failure?.code ?? "admission-failed",
200
+ );
182
201
  }
183
202
  for (const message of runtimeResolutionWarnings(resolved.diagnostics)) {
184
203
  const key = `${targetId}|${wireModelId}|${message}`;
@@ -331,8 +350,9 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
331
350
  if (!target) return null;
332
351
  context.emitContextWindowWarningTransition(target.runtimeResolution?.contextWindowDetails?.warning ?? null);
333
352
  if (!deps.knownTargets().has(target.target.id)) {
334
- throw new Error(
353
+ throw new TurnAdmissionError(
335
354
  `[Clio Coder] orchestrator target=${target.target.id} unknown. Run \`clio-coder targets\` to see configured targets.`,
355
+ "target-unknown",
336
356
  );
337
357
  }
338
358
  if (
@@ -400,6 +420,7 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
400
420
  state.runtime.agent.abort();
401
421
  cleanupSessionResources(state.runtime.agent.sessionId);
402
422
  }
423
+ let getCurrentAgentSignal: () => AbortSignal | undefined = () => undefined;
403
424
  const handle = deps.createAgent({
404
425
  initialState: {
405
426
  systemPrompt: fallbackIdentityPrompt(),
@@ -428,10 +449,16 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
428
449
  if (!targetRequiresAuth(target.target, target.runtime)) {
429
450
  return LOCAL_API_KEY_FALLBACK;
430
451
  }
431
- const resolved = await deps.providers.auth.resolveForTarget(target.target, target.runtime);
452
+ const signal = getCurrentAgentSignal();
453
+ const resolved = await deps.providers.auth.resolveForTarget(
454
+ target.target,
455
+ target.runtime,
456
+ signal ? { signal } : undefined,
457
+ );
432
458
  return resolved.apiKey;
433
459
  },
434
460
  });
461
+ getCurrentAgentSignal = () => handle.agent.signal;
435
462
 
436
463
  // Build the runtime object before subscribing so the callback closes
437
464
  // over the same heap object the hot-swap path mutates. Reading
@@ -543,6 +570,11 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
543
570
  // message of the run (prompts, assistant, tool results) via message_end,
544
571
  // so the slice from here is the run's real message window.
545
572
  let runStartMessageCount = 0;
573
+ // Estimated spend of each cancelled call, keyed by the engine's message
574
+ // object: message_end publishes it, agent_end re-reports it on the same
575
+ // object from agent state, and a suppressed hollow abort hands it to the
576
+ // closing turn. Weak so a settled run's messages are not retained.
577
+ const interruptedUsageByMessage = new WeakMap<object, Record<string, unknown>>();
546
578
 
547
579
  handle.agent.subscribe(async (event) => {
548
580
  // Wall anchor for the one field a human reads back (`recordedAt`);
@@ -608,7 +640,36 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
608
640
  enrichedEvent = { ...event, lockedSynthesisSanitized: true } as typeof event;
609
641
  }
610
642
  }
611
- const publicEvent = enrichedEvent;
643
+ let publicEvent = enrichedEvent;
644
+ if (enrichedEvent.type === "message_end" && enrichedEvent.message?.role === "assistant") {
645
+ const interruptedUsage = estimatedUsageForInterruptedTurn(enrichedEvent.message, context.promptSideTokens());
646
+ if (interruptedUsage !== null) {
647
+ // Persistence records the same estimate, but it does so after the
648
+ // public event has already fed status. Copy it onto the public event
649
+ // only: the raw engine message and observability/cost path must remain
650
+ // provider-reported. Remember it by message identity so the public
651
+ // agent_end window below reports the same figure.
652
+ interruptedUsageByMessage.set(enrichedEvent.message as object, interruptedUsage);
653
+ publicEvent = {
654
+ ...enrichedEvent,
655
+ message: { ...enrichedEvent.message, usage: interruptedUsage as unknown as Usage },
656
+ } as typeof enrichedEvent;
657
+ }
658
+ }
659
+ if (enrichedEvent.type === "agent_end") {
660
+ // The aborted message in the run window still carries the provider's
661
+ // untouched zeros. The footer's settled summary and the transcript's
662
+ // `turn · in/out` receipt both rebuild from this window, so give them
663
+ // the estimate the message_end published; the raw window feeds
664
+ // observability and the cache summary below unchanged.
665
+ const reported = enrichedEvent.messages.map((message) => {
666
+ const estimate = interruptedUsageByMessage.get(message as object);
667
+ return estimate === undefined ? message : ({ ...message, usage: estimate as unknown as Usage } as typeof message);
668
+ });
669
+ if (reported.some((message, index) => message !== enrichedEvent.messages[index])) {
670
+ publicEvent = { ...enrichedEvent, messages: reported } as typeof enrichedEvent;
671
+ }
672
+ }
612
673
  if (publicEvent?.type === "agent_start") {
613
674
  runStartMessageCount = localRuntime.agent.state.messages.length;
614
675
  streamStartedAt = eventClock;
@@ -644,11 +705,11 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
644
705
  if (hasDelta && firstAssistantDeltaAt === null) firstAssistantDeltaAt = eventClock;
645
706
  if (hasDelta && apiCallFirstDeltaAt === null) apiCallFirstDeltaAt = eventClock;
646
707
  }
647
- if (publicEvent?.type === "agent_end") {
648
- context.noteRunCacheSummary(publicEvent.messages, runFirstCallVerdict);
708
+ if (enrichedEvent.type === "agent_end") {
709
+ context.noteRunCacheSummary(enrichedEvent.messages, runFirstCallVerdict);
649
710
  }
650
- if (publicEvent?.type === "agent_end" && deps.observability) {
651
- const summary = sumRunUsage(publicEvent.messages);
711
+ if (enrichedEvent.type === "agent_end" && deps.observability) {
712
+ const summary = sumRunUsage(enrichedEvent.messages);
652
713
  if (summary.hadUsage && (summary.tokens > 0 || summary.costUsd > 0)) {
653
714
  deps.observability.recordTokens(
654
715
  localRuntime.targetId,
@@ -687,9 +748,15 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
687
748
  // persistence does.
688
749
  if (
689
750
  state.activeInterruptReason !== null &&
690
- publicEvent?.type === "message_end" &&
691
- isEmptyAbortedAssistantMessage(publicEvent.message)
751
+ enrichedEvent.type === "message_end" &&
752
+ isEmptyAbortedAssistantMessage(enrichedEvent.message)
692
753
  ) {
754
+ // The ledger drops this message too, so the spend the cancelled call
755
+ // streamed as thinking would otherwise vanish from the durable record.
756
+ // Hand it to the closing turn the interrupt persists when the run
757
+ // settles; the public agent_end window above still feeds status.
758
+ const estimate = interruptedUsageByMessage.get(enrichedEvent.message as object);
759
+ if (estimate !== undefined) state.interruptedUsage = estimate;
693
760
  return;
694
761
  }
695
762
  if (publicEvent) deps.emit(publicEvent);
@@ -39,6 +39,7 @@ export interface AgentRuntime {
39
39
 
40
40
  export interface ChatLoopRunSnapshot {
41
41
  targetId: string;
42
+ targetUrl: string | null;
42
43
  runtimeId: string;
43
44
  runtimeKind: RuntimeDescriptor["kind"];
44
45
  wireModelId: string;
@@ -74,6 +75,14 @@ export interface ChatTurnState {
74
75
  * start of each submit.
75
76
  */
76
77
  activeInterruptReason: string | null;
78
+ /**
79
+ * Estimated spend of the cancelled call whose hollow aborted message the
80
+ * interrupt suppressed (thinking streamed, no text). The message never reaches
81
+ * the ledger, so the closing turn persisted in submit's finally carries this
82
+ * instead of an estimate derived from its own notice text. Cleared with
83
+ * `activeInterruptReason`.
84
+ */
85
+ interruptedUsage: Record<string, unknown> | null;
77
86
  /**
78
87
  * Loop-guard synthesis lockout: once the guard locks a turn, the remaining
79
88
  * model rounds are forced text-only at the request level (tool_choice none
@@ -108,6 +117,7 @@ export function createTurnState(initialThinkingLevel: ThinkingLevel): ChatTurnSt
108
117
  replayedContextMessages: [],
109
118
  activeUserTurnId: null,
110
119
  activeInterruptReason: null,
120
+ interruptedUsage: null,
111
121
  synthesisToolLock: false,
112
122
  toolProseAbortReason: null,
113
123
  streamStallReason: null,
@@ -1,6 +1,6 @@
1
1
  import { createReadStream, readFileSync, statSync } from "node:fs";
2
- import { readFile, stat } from "node:fs/promises";
3
- import { basename, isAbsolute, join, resolve } from "node:path";
2
+ import { readFile, realpath, stat } from "node:fs/promises";
3
+ import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  import type { DispatchContract } from "../../domains/dispatch/contract.js";
5
5
  import {
6
6
  isReceiptIntegrity,
@@ -31,6 +31,8 @@ import {
31
31
  readPromptCompileManifest,
32
32
  type SessionPromptCompileRecord,
33
33
  } from "../../domains/session/prompt-manifest.js";
34
+ import { foldSessionArtifacts, resolveSessionArtifactPath } from "../../domains/session/session-artifacts.js";
35
+ import { filterEntriesToActivePath } from "../../domains/session/tree/active-path.js";
34
36
  import { formatUsd } from "../footer/widgets.js";
35
37
  import { formatFooterTokens } from "../footer-panel.js";
36
38
  import { clockLocal } from "../format-time.js";
@@ -42,6 +44,7 @@ export type ViewArtifactCategory =
42
44
  | "receipt"
43
45
  | "dispatch"
44
46
  | "task-ledger"
47
+ | "workspace"
45
48
  | "tool-output"
46
49
  | "protected-artifact"
47
50
  | "compaction"
@@ -91,6 +94,7 @@ export const VIEW_ARTIFACT_CATEGORIES: readonly ViewArtifactCategory[] = [
91
94
  "receipt",
92
95
  "dispatch",
93
96
  "task-ledger",
97
+ "workspace",
94
98
  "tool-output",
95
99
  "protected-artifact",
96
100
  "compaction",
@@ -815,6 +819,8 @@ function renderTaskLedgerGoals(lines: string[], heading: string, goals: Readonly
815
819
  for (const goal of goals) {
816
820
  const parent = goal.parentGoalId ? ` parent ${goal.parentGoalId}` : "";
817
821
  lines.push(`- [${goal.status}] ${goal.id} ${goal.title}${parent}`);
822
+ if (goal.origin) lines.push(` - origin: ${goal.origin}`);
823
+ if (goal.userTaskId) lines.push(` - operator task: ${goal.userTaskId}`);
818
824
  if (goal.description) lines.push(` - description: ${goal.description}`);
819
825
  }
820
826
  }
@@ -842,6 +848,7 @@ function renderTaskLedgerMarkdown(entry: TaskLedgerEntry): string[] {
842
848
  `- timestamp: ${entry.timestamp}`,
843
849
  `- status: ${taskLedgerStatusSummary(entry)}`,
844
850
  ];
851
+ if (entry.boardId) lines.push(`- board id: ${entry.boardId}`);
845
852
  renderTaskLedgerGoals(lines, "## Board Goals", entry.goals);
846
853
  renderTaskLedgerGoals(lines, "## Subgoals", entry.subgoals);
847
854
  lines.push("", "## Active Runs");
@@ -873,9 +880,24 @@ export class TaskLedgerArtifactProvider implements ArtifactProvider {
873
880
  ...(path ? { path } : {}),
874
881
  searchText: [
875
882
  entry.turnId,
883
+ entry.boardId ?? "",
876
884
  ...entry.activeRunIds,
877
- ...entry.goals.flatMap((goal) => [goal.id, goal.title, goal.description ?? "", goal.parentGoalId ?? ""]),
878
- ...entry.subgoals.flatMap((goal) => [goal.id, goal.title, goal.description ?? "", goal.parentGoalId ?? ""]),
885
+ ...entry.goals.flatMap((goal) => [
886
+ goal.id,
887
+ goal.title,
888
+ goal.description ?? "",
889
+ goal.parentGoalId ?? "",
890
+ goal.origin ?? "agent",
891
+ goal.userTaskId ?? "",
892
+ ]),
893
+ ...entry.subgoals.flatMap((goal) => [
894
+ goal.id,
895
+ goal.title,
896
+ goal.description ?? "",
897
+ goal.parentGoalId ?? "",
898
+ goal.origin ?? "agent",
899
+ goal.userTaskId ?? "",
900
+ ]),
879
901
  ...entry.requiredValidationEvidence.flatMap((item) => [
880
902
  item.id,
881
903
  item.description,
@@ -892,6 +914,93 @@ export class TaskLedgerArtifactProvider implements ArtifactProvider {
892
914
  }
893
915
  }
894
916
 
917
+ function workspaceArtifactFormat(path: string): ViewArtifactFormat {
918
+ const lower = path.toLowerCase();
919
+ return lower.endsWith(".md") || lower.endsWith(".markdown") ? "markdown" : "text";
920
+ }
921
+
922
+ class WorkspaceArtifactContainmentError extends Error {}
923
+
924
+ async function canonicalWorkspaceArtifactPath(path: string, workspace: string): Promise<string> {
925
+ // Resolve both sides from the live filesystem for every load. The artifact
926
+ // row is durable, but neither an earlier lexical check nor an earlier realpath
927
+ // result is authority after a symlink has changed.
928
+ const canonicalWorkspace = await realpath(workspace);
929
+ const canonicalTarget = await realpath(path);
930
+ const rel = relative(canonicalWorkspace, canonicalTarget);
931
+ if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
932
+ throw new WorkspaceArtifactContainmentError("canonical target is outside the recorded workspace");
933
+ }
934
+ return canonicalTarget;
935
+ }
936
+
937
+ async function loadWorkspaceArtifact(
938
+ path: string,
939
+ workspace: string,
940
+ recordedAt: string,
941
+ ): Promise<ViewArtifactLoadResult> {
942
+ try {
943
+ const canonicalPath = await canonicalWorkspaceArtifactPath(path, workspace);
944
+ const { lines } = await readTextFileLinesCapped(canonicalPath);
945
+ return { lines, format: workspaceArtifactFormat(path) };
946
+ } catch (error) {
947
+ const err = error as NodeJS.ErrnoException;
948
+ if (err.code === "ENOENT") {
949
+ return { lines: [`file no longer on disk (recorded at ${recordedAt})`], format: "text" };
950
+ }
951
+ if (error instanceof WorkspaceArtifactContainmentError) {
952
+ return { lines: [`refusing to read ${path}: ${error.message}`], format: "text" };
953
+ }
954
+ const message = error instanceof Error ? error.message : String(error);
955
+ return { lines: [`unable to read ${path}: ${message}`], format: "text" };
956
+ }
957
+ }
958
+
959
+ /** Files successfully produced by artifact/write/edit on the active session branch. */
960
+ export class WorkspaceArtifactProvider implements ArtifactProvider {
961
+ readonly category = "workspace" as const;
962
+ private readonly entries: ReadonlyArray<SessionEntry>;
963
+
964
+ constructor(private readonly deps: ArtifactProviderDeps) {
965
+ const entries = [...sessionEntries(deps)];
966
+ this.entries = filterEntriesToActivePath(entries, deps.sessionMeta?.pinnedLeafTurnId ?? undefined);
967
+ }
968
+
969
+ async list(): Promise<ViewArtifact[]> {
970
+ const workspace = this.deps.sessionMeta?.cwd;
971
+ if (!workspace) return [];
972
+ return foldSessionArtifacts(this.entries, { workspace }).flatMap((artifact) => {
973
+ const path = resolveSessionArtifactPath(artifact.path, workspace);
974
+ if (path === null) return [];
975
+ const kind = artifact.artifactKind ? ` · ${artifact.artifactKind}` : "";
976
+ const overwriteLabel = `${artifact.overwrites} overwrite${artifact.overwrites === 1 ? "" : "s"}`;
977
+ return [
978
+ {
979
+ id: `workspace:${path}`,
980
+ category: this.category,
981
+ title: safeTitle(`${artifact.path} · ${artifact.tool}${kind}`, artifact.path),
982
+ timestamp: parseTime(artifact.timestamp),
983
+ sizeBytes: maybeSizeBytes(path),
984
+ path,
985
+ description: `${artifact.tool}${kind} · ${overwriteLabel}`,
986
+ toolName: artifact.tool,
987
+ searchText: [
988
+ artifact.path,
989
+ path,
990
+ basename(path),
991
+ artifact.tool,
992
+ artifact.artifactKind ?? "",
993
+ overwriteLabel,
994
+ String(artifact.overwrites),
995
+ artifact.turnId,
996
+ ].filter(isNonEmptyString),
997
+ load: () => loadWorkspaceArtifact(path, workspace, artifact.timestamp),
998
+ },
999
+ ];
1000
+ });
1001
+ }
1002
+ }
1003
+
895
1004
  export class ToolOutputArtifactProvider implements ArtifactProvider {
896
1005
  readonly category = "tool-output" as const;
897
1006
 
@@ -1310,6 +1419,7 @@ export function createDefaultArtifactProviders(deps: ArtifactProviderDeps): Arti
1310
1419
  new ReceiptArtifactProvider(deps),
1311
1420
  new DispatchArtifactProvider(deps),
1312
1421
  new TaskLedgerArtifactProvider(deps),
1422
+ new WorkspaceArtifactProvider(deps),
1313
1423
  new ToolOutputArtifactProvider(deps),
1314
1424
  new ProtectedArtifactProvider(deps),
1315
1425
  new CompactionArtifactProvider(deps),
@@ -109,6 +109,8 @@ function categoryLabel(category: ViewArtifactCategory): string {
109
109
  return "Dispatch outputs";
110
110
  case "task-ledger":
111
111
  return "Task ledgers";
112
+ case "workspace":
113
+ return "Workspace outputs";
112
114
  case "tool-output":
113
115
  return "Tool outputs";
114
116
  case "protected-artifact":
@@ -125,6 +127,7 @@ function categoryLabel(category: ViewArtifactCategory): string {
125
127
  const VIEW_ARTIFACT_CATEGORY_SET = new Set<ViewArtifactCategory>(VIEW_ARTIFACT_CATEGORIES);
126
128
  const BARE_CATEGORY_FILTERS = new Set<ViewArtifactCategory>([
127
129
  "task-ledger",
130
+ "workspace",
128
131
  "protected-artifact",
129
132
  "compaction",
130
133
  "prompt-manifest",
@@ -2,17 +2,16 @@ import { existsSync, readdirSync, statSync } from "node:fs";
2
2
  import { basename, join } from "node:path";
3
3
  import type { ClioSettings } from "../core/config.js";
4
4
  import { readClioVersion } from "../core/package-root.js";
5
- import { EXPERIMENTAL_RELEASE_WARNING } from "../core/release.js";
5
+ import { loadProjectClioMd } from "../domains/context/clio-md.js";
6
+ import { readCodewiki, readCodewikiAsync } from "../domains/context/codewiki/artifact.js";
7
+ import { renderCodewikiDigest } from "../domains/context/codewiki/digest.js";
8
+ import { listWikiPages } from "../domains/context/wiki/layout.js";
6
9
  import {
7
- listWikiPages,
8
- readCodewiki,
9
- readCodewikiAsync,
10
- renderCodewikiDigest,
11
10
  type WikiStaleness,
12
11
  wikiCompleteness,
13
12
  wikiStaleness,
14
13
  wikiStalenessAsync,
15
- } from "../domains/context/index.js";
14
+ } from "../domains/context/wiki/staleness.js";
16
15
  import type { TaskMemoryOperatorStatus } from "../domains/memory/index.js";
17
16
  import type { ObservabilityContract } from "../domains/observability/index.js";
18
17
  import {
@@ -172,7 +171,9 @@ function entryPointExcerpt(codewikiDigest: string): string[] {
172
171
  const NO_WIKI_STATUS = "no wiki; run clio-coder context wiki";
173
172
 
174
173
  function clioMdStatusFor(cwd: string): string {
175
- return existsSync(join(cwd, "CLIO-CODER.md")) ? "ok" : "none";
174
+ const loaded = loadProjectClioMd(cwd);
175
+ if (loaded.errors.length > 0) return "malformed";
176
+ return loaded.files.length > 0 ? "ok" : "none";
176
177
  }
177
178
 
178
179
  /** Shape the wiki rows from a staleness verdict the caller already paid for. */
@@ -370,7 +371,10 @@ function nextValue(stats: WelcomeDashboardStats): string {
370
371
  /**
371
372
  * The launchpad has one fixed row per decision the operator needs to make.
372
373
  * Repository facts may replace text inside NEXT, never rows. After submission,
373
- * the session header is exactly one line for every width and fact state.
374
+ * the session header is exactly one line for every width and fact state, and
375
+ * it carries the same three facts in order: where Clio is working, which
376
+ * route answers, and what the context is ready for. Maturity caveats belong
377
+ * in the README, not in a line the operator reads on every turn.
374
378
  */
375
379
  export function buildWelcomeDashboardLines(
376
380
  stats: WelcomeDashboardStats,
@@ -380,21 +384,18 @@ export function buildWelcomeDashboardLines(
380
384
  const theme = clioTheme();
381
385
  const safeWidth = Math.max(1, width);
382
386
  const title = `${brandMark(theme)} ${theme.style("title", "Clio Coder", { bold: true })} ${theme.fg("dim", `v${readClioVersion()}`)}`;
383
- const experimental = theme.style("warning", EXPERIMENTAL_RELEASE_WARNING, { bold: true });
384
387
  const tag = (label: string): string => sectionTag(theme, "accentDeep", label, 9);
385
388
  const row = (label: string, value: string): string => ` ${tag(label)} ${value}`;
389
+ const workspace = theme.fg("muted", workspaceValue(stats));
390
+ const route = theme.fg(stats.currentAvailable ? "success" : "warning", routeValue(stats));
391
+ const next = theme.fg(stats.clioMdStatus === "ok" && !stats.factsPending ? "success" : "warning", nextValue(stats));
386
392
 
387
393
  if (mode === "session") {
388
- const header = `${title} · ${experimental} · ${theme.fg("muted", nextValue(stats))}`;
394
+ const header = [title, workspace, route, theme.fg("muted", nextValue(stats))].join(" · ");
389
395
  return [truncateToWidth(header, safeWidth, GLYPH.ellipsis, true)];
390
396
  }
391
397
 
392
- const body = [
393
- ` ${experimental}`,
394
- row("WORKSPACE", theme.fg("muted", workspaceValue(stats))),
395
- row("ROUTE", theme.fg(stats.currentAvailable ? "success" : "warning", routeValue(stats))),
396
- row("NEXT", theme.fg(stats.clioMdStatus === "ok" && !stats.factsPending ? "success" : "warning", nextValue(stats))),
397
- ];
398
+ const body = [row("WORKSPACE", workspace), row("ROUTE", route), row("NEXT", next)];
398
399
  if (safeWidth >= MID_MIN) return frame(theme, title, body, safeWidth);
399
400
  return [title, ...body].map((line) => truncateToWidth(line, safeWidth, GLYPH.ellipsis, true));
400
401
  }
@@ -13,7 +13,8 @@
13
13
  * to take down a render.
14
14
  */
15
15
 
16
- import { readFileSync } from "node:fs";
16
+ import { existsSync, readFileSync } from "node:fs";
17
+ import { join } from "node:path";
17
18
  import { clioStateDir } from "../core/xdg.js";
18
19
  import { receiptFilePath } from "./view/artifacts.js";
19
20
  import type { WorkerReceiptFacts, WorkerResultContract } from "./worker-stream.js";
@@ -83,8 +84,8 @@ export function workerReceiptFacts(receipt: Record<string, unknown>): WorkerRece
83
84
  };
84
85
  }
85
86
 
86
- /** Read and project `<state>/receipts/<runId>.json`; null when it is absent or unreadable. */
87
- export function readWorkerReceiptFacts(runId: string, stateDir = clioStateDir()): WorkerReceiptFacts | null {
87
+ /** Read and project `<state>/receipts/<runId>.json` alone; null when it is absent or unreadable. */
88
+ function readReceiptFileFacts(runId: string, stateDir: string): WorkerReceiptFacts | null {
88
89
  try {
89
90
  const parsed: unknown = JSON.parse(readFileSync(receiptFilePath(stateDir, runId), "utf8"));
90
91
  return isRecord(parsed) ? workerReceiptFacts(parsed) : null;
@@ -92,3 +93,51 @@ export function readWorkerReceiptFacts(runId: string, stateDir = clioStateDir())
92
93
  return null;
93
94
  }
94
95
  }
96
+
97
+ /** Read and project `<state>/receipts/<runId>.json`; null when it is absent or unreadable. */
98
+ export function readWorkerReceiptFacts(runId: string, stateDir = clioStateDir()): WorkerReceiptFacts | null {
99
+ return readReceiptFileFacts(runId, stateDir);
100
+ }
101
+
102
+ /**
103
+ * The subset of a `runs.json` row replay needs once a receipt has failed to
104
+ * read: whether the run is still open, and, if the ledger closed it early
105
+ * (`closeAbandonedRows`), the closing row's own explanation.
106
+ */
107
+ function findRunRow(runId: string, stateDir: string): Record<string, unknown> | null {
108
+ const path = join(stateDir, "runs.json");
109
+ if (!existsSync(path)) return null;
110
+ try {
111
+ const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
112
+ if (!Array.isArray(parsed)) return null;
113
+ for (const entry of parsed) {
114
+ if (isRecord(entry) && entry.id === runId) return entry;
115
+ }
116
+ return null;
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Replay-only receipt reader: falls back to the run's own `runs.json` row
124
+ * when no receipt was sealed for it, so a resumed transcript can tell a run
125
+ * still going in another process, one the ledger closed as dead/stalled
126
+ * before it could seal a receipt, and one whose evidence is genuinely gone
127
+ * apart from each other. Never used by the live subscription: there, a
128
+ * missing receipt at settle time is a flush race the terminal event's own
129
+ * payload already covers (`worker-stream.ts`'s `settle`), not an open run.
130
+ */
131
+ export function readWorkerReceiptFactsForReplay(runId: string, stateDir = clioStateDir()): WorkerReceiptFacts | null {
132
+ const sealed = readReceiptFileFacts(runId, stateDir);
133
+ if (sealed !== null) return sealed;
134
+ const row = findRunRow(runId, stateDir);
135
+ if (row === null) return null;
136
+ if (row.endedAt === null || row.endedAt === undefined) return { outcome: "running", stillRunning: true };
137
+ const outcome = optionalString(row.outcome) ?? optionalString(row.status) ?? "unknown";
138
+ const abandonedDetail = optionalString(row.outcomeDetail);
139
+ return {
140
+ outcome,
141
+ ...(abandonedDetail !== undefined ? { abandonedDetail } : {}),
142
+ };
143
+ }
@@ -64,8 +64,12 @@ export interface WorkerReceiptSummary {
64
64
  contract?: WorkerResultContract;
65
65
  /** From an abort marker rather than a sealed terminal event; a later DispatchCompleted/DispatchFailed replaces it. */
66
66
  provisional?: boolean;
67
- /** The run is over, but no receipt could be read for it. */
67
+ /** The run is over, but no receipt could be read for it, and the ledger row could not explain why either. */
68
68
  receiptUnavailable?: boolean;
69
+ /** Replay only: the ledger row for this run has no `endedAt` yet, so the run is still going in another process. */
70
+ stillRunning?: boolean;
71
+ /** Replay only: no receipt exists because the ledger closed this row early (`closeAbandonedRows`); the row's own explanation, verbatim. */
72
+ abandonedDetail?: string;
69
73
  }
70
74
 
71
75
  /** A receipt's projection: the summary plus the answer it sealed. */
@@ -25,9 +25,9 @@ import type { ResolvedRuntimeTarget } from "../domains/providers/index.js";
25
25
  import type { SafetyDecision } from "../domains/safety/contract.js";
26
26
  import { formatModelRejection } from "../domains/safety/rejection-feedback.js";
27
27
  import { validateEngineToolArguments } from "../engine/ai.js";
28
- import type { AgentTool, AgentToolResult } from "../engine/types.js";
28
+ import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "../engine/types.js";
29
29
  import { applyToolProfile, type ToolProfileName } from "./profiles.js";
30
- import type { ToolInvokeOptions, ToolRegistry, ToolSpec } from "./registry.js";
30
+ import type { ToolInvokeOptions, ToolRegistry, ToolResult, ToolSpec } from "./registry.js";
31
31
 
32
32
  /**
33
33
  * Lightweight per-call observability hook. Default no-op so unused
@@ -96,6 +96,18 @@ export interface InvokeWorkerToolOptions {
96
96
  type WorkerAgentToolResult = AgentToolResult<{ kind: "ok" } | { kind: "error" }>;
97
97
  type WorkerToolOkDetails = { kind: "ok" } & Record<string, unknown>;
98
98
 
99
+ function projectToolResult(result: ToolResult): WorkerAgentToolResult {
100
+ const details = { ...(result.details ?? {}), kind: result.kind } as { kind: "ok" } | { kind: "error" };
101
+ if (result.kind === "error") {
102
+ return { content: [{ type: "text", text: result.message }], details };
103
+ }
104
+ return {
105
+ content: [{ type: "text", text: result.output }],
106
+ details,
107
+ ...(result.terminate === true ? { terminate: true } : {}),
108
+ };
109
+ }
110
+
99
111
  function isRecord(value: unknown): value is Record<string, unknown> {
100
112
  return typeof value === "object" && value !== null && !Array.isArray(value);
101
113
  }
@@ -264,9 +276,17 @@ function toAgentTool(
264
276
  description: spec.description,
265
277
  parameters: spec.parameters,
266
278
  label: spec.metadata?.uiLabel ?? spec.name,
267
- async execute(toolCallId: string, params: unknown, signal?: AbortSignal): Promise<WorkerAgentToolResult> {
279
+ async execute(
280
+ toolCallId: string,
281
+ params: unknown,
282
+ signal?: AbortSignal,
283
+ onUpdate?: AgentToolUpdateCallback,
284
+ ): Promise<WorkerAgentToolResult> {
268
285
  const options = invokeOptions?.() ?? {};
269
286
  if (toolCallId.length > 0) options.toolCallId = toolCallId;
287
+ if (onUpdate !== undefined) {
288
+ options.onUpdate = (partialResult) => onUpdate(projectToolResult(partialResult));
289
+ }
270
290
  const callInput: RunValidatedToolCallInput = {
271
291
  spec,
272
292
  args: params as Record<string, unknown>,