@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
@@ -13,9 +13,11 @@
13
13
  */
14
14
 
15
15
  import { sanitizeCallTargetText } from "../../domains/safety/call-target.js";
16
+ import { formatSize } from "../../engine/truncate.js";
16
17
  import { visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
18
+ import type { ApprovalRequestView } from "../permission-overlay.js";
17
19
  import { clioTheme, formatCompactMs, GLYPH } from "../theme/index.js";
18
- import { type DiffRenderInput, renderUnifiedDiff } from "./diff.js";
20
+ import { renderDiffLines } from "./diff.js";
19
21
  import { tryRenderJson, tryRenderXml } from "./structured.js";
20
22
 
21
23
  const theme = clioTheme();
@@ -34,7 +36,6 @@ const BODY_INDENT_VISIBLE_WIDTH = 2;
34
36
  const HEADER_PREFIX_PLAIN = "▸ ";
35
37
  const ARG_PREVIEW_LIMIT = 60;
36
38
  const WEB_FETCH_ARG_PREVIEW_LIMIT = 140;
37
- const RESULT_PREVIEW_LIMIT = 4000;
38
39
  const FULL_RESULT_PREVIEW_LIMIT = 60_000;
39
40
  const FULL_RESULT_ROW_LIMIT = 120;
40
41
  const STREAMING_RESULT_ROW_LIMIT = 20;
@@ -54,6 +55,8 @@ export interface ToolExecutionStart {
54
55
  args: unknown;
55
56
  /** Live elapsed time supplied by the panel for running segments. */
56
57
  elapsedMs?: number | undefined;
58
+ /** Pi may stream a tool call's arguments before execution starts. */
59
+ phase?: "forming" | "ready" | "running" | undefined;
57
60
  }
58
61
 
59
62
  export interface ToolExecutionFinished {
@@ -73,6 +76,10 @@ export interface ToolExecutionFinished {
73
76
  * model reading the same transcript, to guess at the rule.
74
77
  */
75
78
  blockReason?: string | undefined;
79
+ /** Structured exit status when the caller has one; text parsing is legacy fallback only. */
80
+ exitCode?: number | string | null | undefined;
81
+ /** Local `!!` bash output is visible to the operator but excluded from model context. */
82
+ excludeFromContext?: boolean | undefined;
76
83
  }
77
84
 
78
85
  export interface ToolBodyRenderOptions {
@@ -83,6 +90,8 @@ export interface ToolBodyRenderOptions {
83
90
  * the complete tool output the model actually received.
84
91
  */
85
92
  unbounded?: boolean;
93
+ /** Live rows color mutation diffs; replay and export deliberately use plain text. */
94
+ diffStyle?: "color" | "plain";
86
95
  }
87
96
 
88
97
  /** Row cap for a tool body: unbounded lifts both the row and char limits. */
@@ -218,10 +227,9 @@ function bashExitCodeFromResult(result: unknown): string | null {
218
227
 
219
228
  /**
220
229
  * Map of known tools to their canonical "primary" arg field. When the arg is
221
- * a string the header summarises it directly and the args body is suppressed
222
- * because echoing `{"path": "..."}` next to `tool: read(...)` is duplicate
223
- * noise. Tools not in this map (or with an unexpected arg shape) fall back
224
- * to a JSON-dump summary plus the full args body.
230
+ * a string the header summarises it directly and the expanded argument list
231
+ * omits only that repeated field. Tools not in this map (or with an unexpected
232
+ * arg shape) fall back to a JSON summary plus their complete redacted fields.
225
233
  */
226
234
  const PRIMARY_ARG_FIELD: Record<string, string> = {
227
235
  read: "path",
@@ -244,8 +252,8 @@ const PRIMARY_ARG_FIELD: Record<string, string> = {
244
252
 
245
253
  /**
246
254
  * Returns the captured primary-arg string when the header successfully used a
247
- * known tool's canonical arg, otherwise null. Drives the args-body suppression
248
- * so `tool: read(README.md)` does not get followed by `{"path": "README.md"}`.
255
+ * known tool's canonical arg, otherwise null. The argument renderer uses the
256
+ * same map to omit that one repeated field below `read(README.md)`.
249
257
  */
250
258
  function capturedPrimaryArg(toolName: string, args: unknown): string | null {
251
259
  const field = PRIMARY_ARG_FIELD[toolName];
@@ -318,12 +326,6 @@ function stringField(record: Record<string, unknown> | null, key: string): strin
318
326
  return typeof value === "string" && value.length > 0 ? value : null;
319
327
  }
320
328
 
321
- function formatBytes(bytes: number): string {
322
- if (bytes < 1024) return `${bytes}B`;
323
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
324
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
325
- }
326
-
327
329
  function countSummary(observation: Record<string, unknown>): string | null {
328
330
  const unit = stringField(observation, "unit") ?? "results";
329
331
  const shown = numberField(observation, "shownCount");
@@ -396,9 +398,46 @@ function shownBytesOf(finished: ToolExecutionFinished): number | null {
396
398
  const observation = observationOf(finished);
397
399
  const fromObservation = numberField(observation, "shownBytes");
398
400
  if (fromObservation !== null) return fromObservation;
401
+ const resultSize = detailsOf(finished.result)?.resultSize;
402
+ const fromResultSize = isPlainObject(resultSize) ? numberField(resultSize, "shownBytes") : null;
403
+ if (fromResultSize !== null) return fromResultSize;
399
404
  return numberField(finished.resultSummary ?? null, "bytes");
400
405
  }
401
406
 
407
+ function totalBytesOf(finished: ToolExecutionFinished): number | null {
408
+ const observation = observationOf(finished);
409
+ const fromObservation = numberField(observation, "totalBytes");
410
+ if (fromObservation !== null) return fromObservation;
411
+ const resultSize = detailsOf(finished.result)?.resultSize;
412
+ if (isPlainObject(resultSize)) {
413
+ const total = numberField(resultSize, "bytes");
414
+ if (total !== null) return total;
415
+ }
416
+ return shownBytesOf(finished);
417
+ }
418
+
419
+ function resultSizeOf(finished: ToolExecutionFinished): Record<string, unknown> | null {
420
+ const value = detailsOf(finished.result)?.resultSize;
421
+ return isPlainObject(value) ? value : null;
422
+ }
423
+
424
+ function isTruncatedResult(finished: ToolExecutionFinished): boolean {
425
+ const observation = observationOf(finished);
426
+ if (observation?.truncated === true) return true;
427
+ if (resultSizeOf(finished)?.truncated === true) return true;
428
+ const truncation = detailsOf(finished.result)?.truncation;
429
+ if (isPlainObject(truncation) && truncation.truncated === true) return true;
430
+ return finished.resultSummary?.truncated === true;
431
+ }
432
+
433
+ function structuredExitCode(finished: ToolExecutionFinished): string | null {
434
+ if (isNonExecutedOutcome(finished.outcome)) return null;
435
+ if (finished.exitCode !== undefined && finished.exitCode !== null) return String(finished.exitCode);
436
+ const exitCode = detailsOf(finished.result)?.exitCode;
437
+ if (typeof exitCode === "number" || typeof exitCode === "string") return String(exitCode);
438
+ return finished.toolName === "bash" && finished.isError ? bashExitCodeFromResult(finished.result) : null;
439
+ }
440
+
402
441
  /**
403
442
  * The dim ledger tail appended to a finished collapsed subline: outcome facts,
404
443
  * then byte size (duration rides on the status glyph), then the offload path
@@ -410,7 +449,7 @@ function ledgerTail(finished: ToolExecutionFinished): { facts: string; offload:
410
449
  const outcome = outcomeSummary(finished);
411
450
  if (outcome !== null) parts.push(outcome);
412
451
  const bytes = isNonExecutedOutcome(finished.outcome) ? null : shownBytesOf(finished);
413
- if (bytes !== null && bytes > 0) parts.push(formatBytes(bytes));
452
+ if (bytes !== null && bytes > 0) parts.push(formatSize(bytes));
414
453
  const offloadPath = offloadPathOf(finished);
415
454
  return {
416
455
  facts: parts.length > 0 ? dim(` · ${parts.join(" · ")}`) : "",
@@ -430,24 +469,40 @@ const AWAITING_APPROVAL_TAIL = ` ${yellow(GLYPH.phaseBlocked)}${dim(" awaiting a
430
469
  * No elapsed counter (nothing is running) and no status glyph (nothing has
431
470
  * finished): the awaiting-approval tail is the segment's whole state.
432
471
  */
433
- export function renderToolAwaitingApproval(call: ToolExecutionStart, width: number): string[] {
472
+ export function renderToolAwaitingApproval(
473
+ call: ToolExecutionStart,
474
+ width: number,
475
+ view?: ApprovalRequestView,
476
+ ): string[] {
434
477
  const parts = sublineParts({ toolCallId: call.toolCallId, toolName: call.toolName, args: call.args }, undefined, {});
435
- return wrapSublineWithTail(parts.lead, AWAITING_APPROVAL_TAIL, width);
478
+ const lines = wrapSublineWithTail(parts.lead, AWAITING_APPROVAL_TAIL, width);
479
+ if (view === undefined) return lines;
480
+ const axis = view.axis.kind === "net" ? `safety-net rail ${view.axis.ruleId}` : `autonomy level ${view.axis.level}`;
481
+ const facts = [
482
+ ["action", view.actionClass],
483
+ ["axis", axis],
484
+ ...(view.target !== undefined && view.target.length > 0 ? [["target", view.target]] : []),
485
+ ] as const;
486
+ for (const [label, value] of facts) {
487
+ lines.push(...indentAndWrap(`${dim(`${label} ·`)} ${value}`, width, false));
488
+ }
489
+ return lines;
436
490
  }
437
491
 
438
492
  /**
439
- * Header status: `undefined` when the call is still in flight (no glyph),
440
- * `"ok"` for success (green check), `"error"` for failure (red cross). The
493
+ * Header status follows Pi's call lifecycle before and during execution, then
494
+ * becomes `"ok"` or `"error"` at settlement. The
441
495
  * glyph hangs off the right of the header line so the tool name + args read
442
496
  * left-to-right without extra punctuation.
443
497
  */
444
- type HeaderStatus = "ok" | "error" | undefined;
498
+ type HeaderStatus = "forming" | "ready" | "running" | "ok" | "error" | undefined;
445
499
 
446
500
  /** Keeps a refusal reason to one scannable clause on the status tail. */
447
501
  const BLOCK_REASON_LIMIT = 72;
448
502
 
449
503
  interface StatusMeta {
450
504
  durationMs?: number | undefined;
505
+ elapsedMs?: number | undefined;
451
506
  exitCode?: string | null;
452
507
  outcome?: ToolExecutionFinished["outcome"];
453
508
  /** Refusal reason, rendered only alongside a non-executed outcome. */
@@ -456,6 +511,12 @@ interface StatusMeta {
456
511
 
457
512
  function statusGlyph(status: HeaderStatus, meta: StatusMeta = {}): string {
458
513
  if (status === undefined) return "";
514
+ if (status === "forming") return ` ${dim(GLYPH.queued)}${dim(" forming call")}`;
515
+ if (status === "ready") return ` ${dim(GLYPH.queued)}${dim(" ready")}`;
516
+ if (status === "running") {
517
+ const elapsed = optionalCompactMs(meta.elapsedMs);
518
+ return ` ${cyan(GLYPH.running)}${dim(elapsed === null ? " running" : ` running · ${elapsed}`)}`;
519
+ }
459
520
  const duration = optionalCompactMs(meta.durationMs);
460
521
  const durationSuffix = duration ? dim(` · ${duration}`) : "";
461
522
  if (status === "ok") return ` ${green(STATUS_OK_GLYPH)}${durationSuffix}`;
@@ -615,12 +676,13 @@ function buildSublineBody(
615
676
  outcome?: ToolExecutionFinished["outcome"],
616
677
  ): string {
617
678
  if (isNonExecutedOutcome(outcome)) return buildUnknownToolBody(toolName, args);
679
+ if (status === "forming" || status === "ready") return buildUnknownToolBody(toolName, args);
618
680
  if (toolName === "web_fetch") {
619
681
  const meta = status === undefined ? null : webFetchMeta(result);
620
682
  return `${buildUnknownToolBody(toolName, args)}${meta ? dim(` · ${meta}`) : ""}`;
621
683
  }
622
684
  if (toolName === "bash") {
623
- const lead = status === undefined ? "running " : "ran ";
685
+ const lead = status === "running" || status === undefined ? "running " : "ran ";
624
686
  return (
625
687
  buildFieldSublineBody(args, "command", lead, { wrapInBackticks: true }) ?? buildUnknownToolBody(toolName, args)
626
688
  );
@@ -660,9 +722,7 @@ function sublineParts(
660
722
  tail: `${statusGlyph(status, meta)}${ledger.offload}`,
661
723
  };
662
724
  }
663
- const elapsed = optionalCompactMs("elapsedMs" in call ? call.elapsedMs : undefined);
664
- const running = elapsed !== null ? dim(` · ${elapsed}`) : "";
665
- return { lead: `${dim(HEADER_PREFIX_PLAIN)}${body}${resource}${running}`, tail: "" };
725
+ return { lead: `${dim(HEADER_PREFIX_PLAIN)}${body}${resource}`, tail: statusGlyph(status, meta) };
666
726
  }
667
727
 
668
728
  /**
@@ -708,33 +768,69 @@ function indentAndWrap(line: string, width: number, isError: boolean): string[]
708
768
  return out;
709
769
  }
710
770
 
771
+ function scalarArgValue(value: unknown): string | null {
772
+ if (typeof value === "string") {
773
+ const lines = value.split("\n").length;
774
+ const bytes = Buffer.byteLength(value, "utf8");
775
+ if (lines > 1 || value.length > 160) return dim(`<${lines} lines · ${formatSize(bytes)} text>`);
776
+ return green(JSON.stringify(value));
777
+ }
778
+ if (typeof value === "number") return cyan(String(value));
779
+ if (typeof value === "boolean") return yellow(String(value));
780
+ if (value === null) return dim("null");
781
+ return null;
782
+ }
783
+
711
784
  /**
712
- * Render the pretty-printed JSON args body. Splits on `\n` first and then
713
- * applies a line cap so the cut never lands inside an open string or before
714
- * a closing brace; the previous codepoint-level `truncate` could leave a
715
- * malformed JSON snippet that broke copy-paste. Mirrors the line-cap strategy
716
- * `renderResultBlock` already uses for tool output.
785
+ * Render secondary arguments as a compact typed field list. The primary path,
786
+ * command, pattern, or query already lives in the call signature; keeping the
787
+ * rest means `cwd`, timeout, range, glob, and flags no longer disappear merely
788
+ * because one field was important enough for the header. Nested values retain
789
+ * the structured JSON renderer, while large strings become byte/line facts
790
+ * instead of flooding the transcript.
717
791
  */
718
- function renderArgsBody(args: unknown, width: number, isError: boolean): string[] {
792
+ function renderArgsBody(toolName: string, args: unknown, width: number, isError: boolean): string[] {
719
793
  if (isEmptyArgs(args)) return [];
794
+ const safeArgs = redactToolArgs(args);
795
+ if (!isPlainObject(safeArgs)) {
796
+ const bodyWidth = Math.max(1, width - BODY_INDENT_VISIBLE_WIDTH);
797
+ const lines = tryRenderJson(safeArgs, bodyWidth, { lineLimit: ARGS_BODY_LINE_LIMIT });
798
+ return lines?.flatMap((line) => indentAndWrap(line, width, isError)) ?? [];
799
+ }
800
+ const primary = PRIMARY_ARG_FIELD[toolName];
801
+ const entries = Object.entries(safeArgs).filter(([key]) => key !== primary);
802
+ if (entries.length === 0) return [];
720
803
  const out: string[] = [];
721
804
  const bodyWidth = Math.max(1, width - BODY_INDENT_VISIBLE_WIDTH);
722
- const lines = tryRenderJson(redactToolArgs(args), bodyWidth, { lineLimit: ARGS_BODY_LINE_LIMIT });
723
- if (!lines) return [];
724
- for (const raw of lines) {
725
- out.push(...indentAndWrap(raw, width, isError));
805
+ out.push(...indentAndWrap(`${cyanBold("args")}${dim(` · ${entries.length}`)}`, width, isError));
806
+ const keyWidth = Math.min(18, Math.max(...entries.map(([key]) => visibleWidth(key))));
807
+ for (const [key, value] of entries) {
808
+ const label = `${cyan(key.padEnd(keyWidth))} `;
809
+ const scalar = scalarArgValue(value);
810
+ if (scalar !== null) {
811
+ out.push(...indentAndWrap(`${label}${scalar}`, width, isError));
812
+ continue;
813
+ }
814
+ out.push(...indentAndWrap(cyan(key), width, isError));
815
+ const lines = tryRenderJson(value, Math.max(1, bodyWidth - 2), { lineLimit: ARGS_BODY_LINE_LIMIT });
816
+ for (const line of lines ?? [jsonStringifySafe(value)]) {
817
+ out.push(...indentAndWrap(` ${line}`, width, isError));
818
+ if (out.length >= ARGS_BODY_LINE_LIMIT) break;
819
+ }
820
+ if (out.length >= ARGS_BODY_LINE_LIMIT) break;
726
821
  }
822
+ if (out.length >= ARGS_BODY_LINE_LIMIT) out.push(...indentAndWrap(dim("… more arguments hidden"), width, isError));
727
823
  return out;
728
824
  }
729
825
 
730
826
  /**
731
827
  * pi-agent-core wraps tool results in `{ content: [{ type: "text", text }, ...] }`
732
- * envelopes. Rendering that JSON verbatim hides the actual tool output behind
733
- * a sea of escaped quotes. When the envelope shape matches, concatenate the
734
- * text segments and treat the join as the real result; otherwise return the
735
- * value untouched so callers stringify it normally.
828
+ * envelopes. Rendering that JSON verbatim hides the actual output and, for a
829
+ * mixed result, can dump a base64 image into the terminal. Text blocks are
830
+ * joined, image blocks become compact MIME/size placeholders, and unknown
831
+ * content blocks are named without serializing their payload.
736
832
  */
737
- export function unwrapResultEnvelope(result: unknown): unknown {
833
+ function unwrapResultEnvelope(result: unknown): unknown {
738
834
  if (typeof result === "string" || result === null || result === undefined) return result;
739
835
  const blocks = Array.isArray(result)
740
836
  ? result
@@ -745,16 +841,25 @@ export function unwrapResultEnvelope(result: unknown): unknown {
745
841
  const parts: string[] = [];
746
842
  for (const block of blocks) {
747
843
  if (!isPlainObject(block)) return result;
748
- if (block.type !== "text" || typeof block.text !== "string") return result;
749
- parts.push(block.text);
844
+ if (block.type === "text" && typeof block.text === "string") {
845
+ parts.push(block.text);
846
+ continue;
847
+ }
848
+ if (block.type === "image" && typeof block.data === "string") {
849
+ const mimeType = typeof block.mimeType === "string" ? block.mimeType : "image/unknown";
850
+ const padding = block.data.endsWith("==") ? 2 : block.data.endsWith("=") ? 1 : 0;
851
+ const bytes = Math.max(0, Math.floor((block.data.length * 3) / 4) - padding);
852
+ parts.push(`[image ${mimeType} · ${formatSize(bytes)}]`);
853
+ continue;
854
+ }
855
+ if (typeof block.type === "string") {
856
+ parts.push(`[${block.type} content]`);
857
+ continue;
858
+ }
859
+ return result;
750
860
  }
751
861
  if (parts.length === 0) return result;
752
- return parts.join("");
753
- }
754
-
755
- export function previewResult(result: unknown): string {
756
- if (typeof result === "string") return truncate(result, RESULT_PREVIEW_LIMIT);
757
- return truncate(jsonStringifySafe(result), RESULT_PREVIEW_LIMIT);
862
+ return parts.join("\n");
758
863
  }
759
864
 
760
865
  function isEmptyResult(result: unknown): boolean {
@@ -822,50 +927,14 @@ function highlightBashCommand(command: string): string {
822
927
  .join("");
823
928
  }
824
929
 
825
- interface EditDiffArgs {
826
- path?: string;
827
- old_string: string;
828
- new_string: string;
829
- }
830
-
831
- /**
832
- * Defensive shape check: edit-tool args must carry both `old_string` and
833
- * `new_string` strings before we can render a diff. `path` is optional; the
834
- * diff renderer falls back to `"file"` when absent. Anything else falls
835
- * through to the standard result block so the dispatch is opportunistic and
836
- * never throws.
837
- */
838
- function asEditDiffArgs(args: unknown): EditDiffArgs | null {
839
- if (!isPlainObject(args)) return null;
840
- const oldString = args.old_string;
841
- const newString = args.new_string;
842
- if (typeof oldString !== "string" || typeof newString !== "string") return null;
843
- const out: EditDiffArgs = { old_string: oldString, new_string: newString };
844
- if (typeof args.path === "string") out.path = args.path;
845
- return out;
846
- }
847
-
848
- // The `diff` library appends a `` sentinel row when a
849
- // side of the comparison lacks a trailing newline. It is transcript noise, so
850
- // the edit diff block drops it. The marker text carries no ANSI, so a substring
851
- // check against the styled row is reliable and keeps this module free of raw
852
- // escape literals.
853
- const NO_NEWLINE_MARKER = "\";
854
-
855
- function isNoNewlineMarkerRow(row: string): boolean {
856
- return row.includes(NO_NEWLINE_MARKER);
930
+ function resultDiff(result: unknown): string | null {
931
+ const value = detailsOf(result)?.diff;
932
+ return typeof value === "string" && value.length > 0 ? value : null;
857
933
  }
858
934
 
859
- function renderEditDiffBlock(args: EditDiffArgs, width: number): string[] {
935
+ function renderMutationDiffBlock(diff: string, width: number, color: boolean): string[] {
860
936
  const bodyWidth = Math.max(1, width - BODY_INDENT_VISIBLE_WIDTH);
861
- const input: DiffRenderInput = { oldText: args.old_string, newText: args.new_string };
862
- if (args.path !== undefined) input.filename = args.path;
863
- const out: string[] = [];
864
- for (const line of renderUnifiedDiff(input, bodyWidth)) {
865
- if (isNoNewlineMarkerRow(line)) continue;
866
- out.push(`${RAIL_DIM}${line}`);
867
- }
868
- return out;
937
+ return renderDiffLines(diff, bodyWidth, { color }).map((line) => `${RAIL_DIM}${line}`);
869
938
  }
870
939
 
871
940
  interface BashArgs {
@@ -884,6 +953,79 @@ function asBashArgs(args: unknown): BashArgs | null {
884
953
  return { command: redactSecretString(command) };
885
954
  }
886
955
 
956
+ function resultLineCount(result: unknown): number {
957
+ const unwrapped = unwrapResultEnvelope(result);
958
+ if (typeof unwrapped !== "string" || unwrapped.length === 0) return 0;
959
+ const normalized = unwrapped.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
960
+ const withoutTerminator = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
961
+ return withoutTerminator.split("\n").length;
962
+ }
963
+
964
+ function toolUsageFact(result: unknown): string | null {
965
+ if (!isPlainObject(result) || !isPlainObject(result.usage)) return null;
966
+ const total = numberField(result.usage, "totalTokens");
967
+ if (total === null || total <= 0) return null;
968
+ return `${total} tool token${total === 1 ? "" : "s"}`;
969
+ }
970
+
971
+ function outputFacts(finished: ToolExecutionFinished): string[] {
972
+ const parts: string[] = [];
973
+ if (isNonExecutedOutcome(finished.outcome)) {
974
+ if (finished.excludeFromContext === true) parts.push("excluded from context");
975
+ return parts;
976
+ }
977
+ const exitCode = structuredExitCode(finished) ?? (finished.toolName === "bash" && !finished.isError ? "0" : null);
978
+ if (exitCode !== null) parts.push(`exit ${exitCode}`);
979
+ const observation = observationOf(finished);
980
+ const count = observation === null ? null : countSummary(observation);
981
+ if (count !== null) parts.push(count);
982
+ const lines = resultLineCount(finished.result);
983
+ if (count === null && lines > 0) parts.push(`${lines} line${lines === 1 ? "" : "s"}`);
984
+ const shownBytes = shownBytesOf(finished);
985
+ const totalBytes = totalBytesOf(finished);
986
+ if (shownBytes !== null && shownBytes > 0) {
987
+ parts.push(
988
+ totalBytes !== null && totalBytes > shownBytes
989
+ ? `${formatSize(shownBytes)} shown / ${formatSize(totalBytes)} total`
990
+ : formatSize(shownBytes),
991
+ );
992
+ }
993
+ if (isTruncatedResult(finished)) parts.push("truncated");
994
+ const details = detailsOf(finished.result);
995
+ if (details?.timedOut === true) parts.push("timed out");
996
+ if (details?.outputCapped === true) parts.push("output capped");
997
+ const usage = toolUsageFact(finished.result);
998
+ if (usage !== null) parts.push(usage);
999
+ if (isPlainObject(finished.result) && Array.isArray(finished.result.addedToolNames)) {
1000
+ const added = finished.result.addedToolNames.filter((name): name is string => typeof name === "string");
1001
+ if (added.length > 0) parts.push(`added ${added.join(", ")}`);
1002
+ }
1003
+ if (isPlainObject(finished.result) && finished.result.terminate === true) parts.push("terminal result");
1004
+ if (finished.excludeFromContext === true) parts.push("excluded from context");
1005
+ return parts;
1006
+ }
1007
+
1008
+ function renderOutputMeta(
1009
+ finished: ToolExecutionFinished,
1010
+ width: number,
1011
+ isError: boolean,
1012
+ label = "output",
1013
+ ): string[] {
1014
+ const facts = outputFacts(finished);
1015
+ const suffix = facts.length > 0 ? dim(` · ${facts.join(" · ")}`) : "";
1016
+ return indentAndWrap(`${cyanBold(label)}${suffix}`, width, isError);
1017
+ }
1018
+
1019
+ function renderOutputFooter(finished: ToolExecutionFinished, width: number, isError: boolean): string[] {
1020
+ const out: string[] = [];
1021
+ const offloadPath = isNonExecutedOutcome(finished.outcome) ? null : offloadPathOf(finished);
1022
+ if (offloadPath !== null) out.push(...indentAndWrap(`${yellow("full output")} ${offloadPath}`, width, isError));
1023
+ const hint =
1024
+ stringField(resultSizeOf(finished), "followUpHint") ?? stringField(finished.resultSummary ?? null, "followUpHint");
1025
+ if (hint !== null) out.push(...indentAndWrap(`${dim("next")} ${hint}`, width, isError));
1026
+ return out;
1027
+ }
1028
+
887
1029
  /**
888
1030
  * Bash subrenderer: emits `$ <cmd>` (full command) on its own line under the
889
1031
  * rail, then the unwrapped output via the same chain as `renderResultBlock`.
@@ -927,11 +1069,17 @@ function renderResultBlock(
927
1069
 
928
1070
  /**
929
1071
  * Header-only render for a tool call that has not yet finished. Used by the
930
- * live chat panel between `tool_execution_start` and `tool_execution_end`.
931
- * No status glyph: the absence of the glyph signals "still running".
1072
+ * live chat panel from streamed argument formation through execution. The
1073
+ * lifecycle tail distinguishes a forming call, a ready call, and a running
1074
+ * call without inventing execution time before Pi starts the tool.
932
1075
  */
933
1076
  export function renderToolCallHeader(call: ToolExecutionStart, width: number): string[] {
934
- return wrap(headerLine(call.toolName, call.args, undefined), width);
1077
+ return wrap(
1078
+ headerLine(call.toolName, call.args, call.phase ?? "running", {
1079
+ elapsedMs: call.elapsedMs,
1080
+ }),
1081
+ width,
1082
+ );
935
1083
  }
936
1084
 
937
1085
  /** Running-only footer used when an operator pauses live output; execution continues. */
@@ -939,11 +1087,7 @@ export function renderToolRunningStatus(call: ToolExecutionStart, width: number)
939
1087
  const elapsed = optionalCompactMs(call.elapsedMs);
940
1088
  return [
941
1089
  ...renderToolCallHeader(call, width),
942
- ...indentAndWrap(
943
- dim(elapsed ? `(running... ${elapsed}; live output hidden)` : "(running; live output hidden)"),
944
- width,
945
- false,
946
- ),
1090
+ ...indentAndWrap(dim(elapsed ? `live output paused · ${elapsed}` : "live output paused"), width, false),
947
1091
  ];
948
1092
  }
949
1093
 
@@ -952,7 +1096,7 @@ function sublineStatus(call: ToolExecutionStart | ToolExecutionFinished): Header
952
1096
  // carries a `result` field, so a `ToolExecutionStart` with a stray
953
1097
  // `isError: false` (e.g. from a future event-shape change) cannot trip the
954
1098
  // finished path. `result` is the type's load-bearing field.
955
- if (!("result" in call)) return undefined;
1099
+ if (!("result" in call)) return call.phase ?? "running";
956
1100
  return call.isError ? "error" : "ok";
957
1101
  }
958
1102
 
@@ -982,13 +1126,13 @@ export function renderToolSubline(
982
1126
  "result" in call
983
1127
  ? {
984
1128
  durationMs: call.durationMs,
985
- exitCode: call.toolName === "bash" && call.isError ? bashExitCodeFromResult(call.result) : null,
1129
+ exitCode: structuredExitCode(call),
986
1130
  outcome: call.outcome,
987
1131
  blockReason: call.blockReason,
988
1132
  }
989
- : {};
1133
+ : { elapsedMs: call.elapsedMs };
990
1134
  const parts = sublineParts(call, status, meta);
991
- const showHint = status !== undefined && expandKey !== undefined && expandKey.length > 0;
1135
+ const showHint = "result" in call && expandKey !== undefined && expandKey.length > 0;
992
1136
  const tail = showHint ? `${parts.tail}${dim(` (${expandKey})`)}` : parts.tail;
993
1137
  return wrapSublineWithTail(parts.lead, tail, width);
994
1138
  }
@@ -1008,22 +1152,23 @@ export function renderToolExecution(
1008
1152
  const status: HeaderStatus = finished.isError ? "error" : "ok";
1009
1153
  const statusMeta: StatusMeta = {
1010
1154
  durationMs: finished.durationMs,
1011
- exitCode: finished.toolName === "bash" && finished.isError ? bashExitCodeFromResult(finished.result) : null,
1155
+ exitCode: structuredExitCode(finished),
1012
1156
  outcome: finished.outcome,
1013
1157
  blockReason: finished.blockReason,
1014
1158
  };
1015
1159
  const out: string[] = [];
1016
1160
  out.push(...wrap(headerLine(finished.toolName, finished.args, status, statusMeta), width));
1017
1161
 
1018
- // Edit-tool dispatch: when the tool succeeded and `args` carries the
1019
- // expected `{ old_string, new_string }` strings, swap the args body and
1020
- // result block for a unified diff. The header still renders so the user
1021
- // sees `▸ edit(<path>)`, and the args body is suppressed because echoing
1022
- // both strings would just duplicate what the diff already shows.
1023
- if (finished.toolName === "edit" && finished.isError === false) {
1024
- const editArgs = asEditDiffArgs(redactToolArgs(finished.args));
1025
- if (editArgs !== null) {
1026
- out.push(...renderEditDiffBlock(editArgs, width));
1162
+ // Edit and write tools produce one bounded numbered diff on result.details.
1163
+ // It is the authority because canonical edit args can contain multiple
1164
+ // replacements and fuzzy matching can change the actual base text. Live rows
1165
+ // receive Pi's word-level styling; replay and export request plain rows.
1166
+ if ((finished.toolName === "edit" || finished.toolName === "write") && finished.isError === false) {
1167
+ const diff = resultDiff(finished.result);
1168
+ if (diff !== null) {
1169
+ out.push(...renderOutputMeta(finished, width, false, "change"));
1170
+ out.push(...renderMutationDiffBlock(diff, width, opts.diffStyle !== "plain"));
1171
+ out.push(...renderOutputFooter(finished, width, false));
1027
1172
  return out;
1028
1173
  }
1029
1174
  }
@@ -1035,20 +1180,35 @@ export function renderToolExecution(
1035
1180
  if (finished.toolName === "bash") {
1036
1181
  const bashArgs = asBashArgs(redactToolArgs(finished.args));
1037
1182
  if (bashArgs !== null) {
1183
+ out.push(...renderArgsBody(finished.toolName, finished.args, width, finished.isError));
1184
+ out.push(
1185
+ ...renderOutputMeta(
1186
+ finished,
1187
+ width,
1188
+ finished.isError,
1189
+ isNonExecutedOutcome(finished.outcome) ? "decision" : "output",
1190
+ ),
1191
+ );
1038
1192
  out.push(...renderBashResultBlock(bashArgs, finished.result, width, finished.isError, opts));
1193
+ out.push(...renderOutputFooter(finished, width, finished.isError));
1039
1194
  return out;
1040
1195
  }
1041
1196
  }
1042
1197
 
1043
- // Suppress the args body when the header already encodes the salient arg
1044
- // (e.g. `▸ read(README.md)` already shows the path; rendering
1045
- // `{"path": "README.md"}` underneath is duplicate noise). For tools without
1046
- // a known primary arg the body still renders so users see what the model
1047
- // actually invoked.
1048
- if (capturedPrimaryArg(finished.toolName, finished.args) === null) {
1049
- out.push(...renderArgsBody(finished.args, width, finished.isError));
1050
- }
1198
+ // The primary argument already lives in the signature. Keep the secondary
1199
+ // fields below it so flags, ranges, working directories, and timeouts remain
1200
+ // inspectable without repeating the primary value.
1201
+ out.push(...renderArgsBody(finished.toolName, finished.args, width, finished.isError));
1202
+ out.push(
1203
+ ...renderOutputMeta(
1204
+ finished,
1205
+ width,
1206
+ finished.isError,
1207
+ isNonExecutedOutcome(finished.outcome) ? "decision" : "output",
1208
+ ),
1209
+ );
1051
1210
  out.push(...renderResultBlock(finished.result, finished.isError, width, opts));
1211
+ out.push(...renderOutputFooter(finished, width, finished.isError));
1052
1212
  return out;
1053
1213
  }
1054
1214
 
@@ -1065,13 +1225,15 @@ export function renderToolResultOnly(
1065
1225
  const status: HeaderStatus = finished.isError ? "error" : "ok";
1066
1226
  const statusMeta: StatusMeta = {
1067
1227
  durationMs: finished.durationMs,
1068
- exitCode: finished.toolName === "bash" && finished.isError ? bashExitCodeFromResult(finished.result) : null,
1228
+ exitCode: structuredExitCode(finished),
1069
1229
  outcome: finished.outcome,
1070
1230
  blockReason: finished.blockReason,
1071
1231
  };
1072
1232
  const out: string[] = [];
1073
1233
  out.push(...wrap(headerLine(finished.toolName, undefined, status, statusMeta), width));
1234
+ out.push(...renderOutputMeta(finished, width, finished.isError));
1074
1235
  out.push(...renderResultBlock(finished.result, finished.isError, width, opts));
1236
+ out.push(...renderOutputFooter(finished, width, finished.isError));
1075
1237
  return out;
1076
1238
  }
1077
1239
 
@@ -1079,26 +1241,117 @@ export function renderToolResultOnly(
1079
1241
  * Streaming render for an in-flight tool call whose expanded block should
1080
1242
  * surface the latest partial output. Used by the chat panel between
1081
1243
  * `tool_execution_start` and `tool_execution_end` when the user has expanded
1082
- * the tool segment. The header carries no status glyph (still running), the
1083
- * args body is suppressed when the header captures the primary arg, the
1084
- * partial output renders under the rail capped at `RESULT_LINE_LIMIT` lines,
1085
- * and a dim `(running...)` marker on the trailing line communicates that the
1086
- * block is still being written. An empty `partialOutput` renders
1244
+ * the tool segment. The header carries the running lifecycle tail, secondary
1245
+ * arguments remain visible, and the latest cumulative Pi result renders under
1246
+ * a labeled live-output rail capped at the streaming row limit. An empty result renders
1087
1247
  * `(no output yet)` so the user can distinguish a slow start from a stalled
1088
1248
  * call.
1089
1249
  */
1090
- export function renderToolStreamingExecution(call: ToolExecutionStart, width: number, partialOutput: string): string[] {
1250
+ export function renderToolStreamingExecution(
1251
+ call: ToolExecutionStart,
1252
+ width: number,
1253
+ partialResult: unknown,
1254
+ ): string[] {
1091
1255
  const out: string[] = [];
1092
- out.push(...wrap(headerLine(call.toolName, call.args, undefined), width));
1093
- if (capturedPrimaryArg(call.toolName, call.args) === null) {
1094
- out.push(...renderArgsBody(call.args, width, false));
1095
- }
1096
- if (partialOutput.length === 0) {
1256
+ out.push(...renderToolCallHeader({ ...call, phase: "running" }, width));
1257
+ out.push(...renderArgsBody(call.toolName, call.args, width, false));
1258
+ const partial: ToolExecutionFinished = {
1259
+ toolCallId: call.toolCallId,
1260
+ toolName: call.toolName,
1261
+ args: call.args,
1262
+ result: partialResult,
1263
+ isError: false,
1264
+ };
1265
+ out.push(...renderOutputMeta(partial, width, false, "live output"));
1266
+ const partialOutput = unwrapResultEnvelope(partialResult);
1267
+ if (isEmptyResult(partialOutput)) {
1097
1268
  out.push(...indentAndWrap(dim("(no output yet)"), width, false));
1098
1269
  } else {
1099
- out.push(...renderOutputRows(partialOutput, width, false, STREAMING_RESULT_ROW_LIMIT));
1270
+ if (call.toolName === "bash") {
1271
+ const bashArgs = asBashArgs(redactToolArgs(call.args));
1272
+ if (bashArgs !== null) {
1273
+ const commandLine = `${cyanBold("$")} ${highlightBashCommand(stripShellWrapperForDisplay(bashArgs.command))}`;
1274
+ out.push(...indentAndWrap(commandLine, width, false));
1275
+ }
1276
+ }
1277
+ out.push(
1278
+ ...renderOutputRows(resultText(partialOutput, FULL_RESULT_PREVIEW_LIMIT), width, false, STREAMING_RESULT_ROW_LIMIT),
1279
+ );
1100
1280
  }
1101
- const elapsed = optionalCompactMs(call.elapsedMs);
1102
- out.push(...indentAndWrap(dim(elapsed !== null ? `(running... ${elapsed})` : "(running...)"), width, false));
1103
1281
  return out;
1104
1282
  }
1283
+
1284
+ /** Display-only lifecycle for operator-issued `!` and `!!` bash commands. */
1285
+ export interface BashTranscriptExecution {
1286
+ command: string;
1287
+ output: string;
1288
+ running: boolean;
1289
+ elapsedMs?: number | undefined;
1290
+ totalBytes?: number | undefined;
1291
+ exitCode?: number | null | undefined;
1292
+ cancelled?: boolean | undefined;
1293
+ truncated?: boolean | undefined;
1294
+ fullOutputPath?: string | undefined;
1295
+ excludeFromContext?: boolean | undefined;
1296
+ error?: string | undefined;
1297
+ }
1298
+
1299
+ /**
1300
+ * Render local bash with the same call, argument, output, and settlement grammar
1301
+ * as model-initiated bash. This is a view projection only; callers keep Clio's
1302
+ * existing immutable `bashExecution` ledger entry.
1303
+ */
1304
+ export function renderBashTranscriptExecution(execution: BashTranscriptExecution, width: number): string[] {
1305
+ const shownBytes = Buffer.byteLength(execution.output, "utf8");
1306
+ const totalBytes = execution.totalBytes ?? shownBytes;
1307
+ const args: Record<string, unknown> = { command: execution.command };
1308
+ if (execution.excludeFromContext === true) args.context = "excluded from model context";
1309
+ const details = {
1310
+ resultSize: {
1311
+ bytes: totalBytes,
1312
+ shownBytes,
1313
+ truncated: execution.truncated === true,
1314
+ policy: "tail",
1315
+ ...(execution.fullOutputPath !== undefined ? { offloadPath: execution.fullOutputPath } : {}),
1316
+ },
1317
+ };
1318
+ const result = {
1319
+ content: [{ type: "text", text: execution.output }],
1320
+ details,
1321
+ };
1322
+ if (execution.running) {
1323
+ return renderToolStreamingExecution(
1324
+ {
1325
+ toolCallId: "local-bash",
1326
+ toolName: "bash",
1327
+ args,
1328
+ elapsedMs: execution.elapsedMs,
1329
+ phase: "running",
1330
+ },
1331
+ width,
1332
+ result,
1333
+ );
1334
+ }
1335
+ const message = execution.error?.trim();
1336
+ const output = message
1337
+ ? `${execution.output}${execution.output.length > 0 ? "\n\n" : ""}${message}`
1338
+ : execution.output;
1339
+ return renderToolExecution(
1340
+ {
1341
+ toolCallId: "local-bash",
1342
+ toolName: "bash",
1343
+ args,
1344
+ result: { ...result, content: [{ type: "text", text: output }] },
1345
+ isError: execution.cancelled === true || execution.error !== undefined || (execution.exitCode ?? 0) !== 0,
1346
+ exitCode: execution.exitCode,
1347
+ ...(execution.cancelled === true ? { outcome: "aborted" as const } : {}),
1348
+ excludeFromContext: execution.excludeFromContext,
1349
+ resultSummary: {
1350
+ bytes: shownBytes,
1351
+ truncated: execution.truncated === true,
1352
+ ...(execution.fullOutputPath !== undefined ? { offloadPath: execution.fullOutputPath } : {}),
1353
+ },
1354
+ },
1355
+ width,
1356
+ );
1357
+ }