@linxiraos/pi-tui 1.1.26 → 1.2.0

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 (423) hide show
  1. package/CHANGELOG.md +51 -491
  2. package/README.md +13 -7
  3. package/dist/types/active-terminal.d.ts +30 -0
  4. package/dist/types/app-keybindings.d.ts +7 -2
  5. package/dist/types/apps/cleanse-board.d.ts +1 -1
  6. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  7. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  8. package/dist/types/apps/git/git-tui.d.ts +5 -4
  9. package/dist/types/apps/git/help.d.ts +3 -1
  10. package/dist/types/apps/if-bench-board.d.ts +2 -2
  11. package/dist/types/apps/setup-model-picker.d.ts +1 -1
  12. package/dist/types/autocomplete.d.ts +5 -2
  13. package/dist/types/charts/chart-plan.d.ts +674 -0
  14. package/dist/types/charts/chart-svg.d.ts +59 -0
  15. package/dist/types/charts/table-data.d.ts +175 -0
  16. package/dist/types/chat/assistant-message.d.ts +11 -1
  17. package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
  18. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  19. package/dist/types/chat/execution-shared.d.ts +6 -5
  20. package/dist/types/chat/fence-figure.d.ts +42 -0
  21. package/dist/types/chat/figure-markdown.d.ts +42 -0
  22. package/dist/types/chat/messages.d.ts +1 -1
  23. package/dist/types/chat/svg-figure.d.ts +33 -0
  24. package/dist/types/chat/svg-source.d.ts +49 -0
  25. package/dist/types/chat/table-chart.d.ts +89 -0
  26. package/dist/types/chat/tool-execution.d.ts +2 -0
  27. package/dist/types/chat/user-message.d.ts +17 -4
  28. package/dist/types/chrome/shared.d.ts +9 -0
  29. package/dist/types/chrome/status-notice.d.ts +15 -3
  30. package/dist/types/chrome/transcript-container.d.ts +6 -0
  31. package/dist/types/components/box.d.ts +1 -0
  32. package/dist/types/components/disclosure.d.ts +1 -0
  33. package/dist/types/components/editor.d.ts +23 -5
  34. package/dist/types/components/form.d.ts +1 -0
  35. package/dist/types/components/image.d.ts +26 -44
  36. package/dist/types/components/input.d.ts +2 -1
  37. package/dist/types/components/key-value-list.d.ts +1 -0
  38. package/dist/types/components/layout/row.d.ts +1 -0
  39. package/dist/types/components/layout/stack.d.ts +1 -0
  40. package/dist/types/components/loader.d.ts +5 -6
  41. package/dist/types/components/markdown.d.ts +47 -5
  42. package/dist/types/components/scroll-view.d.ts +1 -0
  43. package/dist/types/components/section.d.ts +1 -0
  44. package/dist/types/components/table.d.ts +1 -0
  45. package/dist/types/components/text.d.ts +1 -0
  46. package/dist/types/components/tree-view.d.ts +7 -0
  47. package/dist/types/components/truncated-text.d.ts +1 -0
  48. package/dist/types/fuzzy.d.ts +29 -1
  49. package/dist/types/key-hint-format.d.ts +9 -1
  50. package/dist/types/keybinding-matchers.d.ts +3 -0
  51. package/dist/types/keybindings.d.ts +1 -1
  52. package/dist/types/kitty-graphics.d.ts +2 -2
  53. package/dist/types/loop-watchdog.d.ts +8 -7
  54. package/dist/types/multiplexers/cmux.d.ts +16 -0
  55. package/dist/types/multiplexers/herdr.d.ts +26 -0
  56. package/dist/types/multiplexers/notify.d.ts +13 -0
  57. package/dist/types/multiplexers/orca.d.ts +12 -0
  58. package/dist/types/multiplexers/screen.d.ts +9 -0
  59. package/dist/types/multiplexers/tmux.d.ts +29 -0
  60. package/dist/types/multiplexers/types.d.ts +100 -0
  61. package/dist/types/multiplexers/wmux.d.ts +12 -0
  62. package/dist/types/multiplexers/zellij.d.ts +12 -0
  63. package/dist/types/native/apply.d.ts +4 -3
  64. package/dist/types/native/backend.d.ts +5 -4
  65. package/dist/types/native/blobs.d.ts +7 -3
  66. package/dist/types/native/encode.d.ts +8 -6
  67. package/dist/types/native/node.d.ts +36 -5
  68. package/dist/types/native/overlay.d.ts +8 -3
  69. package/dist/types/native/settle.d.ts +4 -3
  70. package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
  71. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  72. package/dist/types/overlays/agent-hub.d.ts +3 -0
  73. package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
  74. package/dist/types/overlays/agents-hub.d.ts +9 -0
  75. package/dist/types/overlays/annotation-overlay.d.ts +19 -2
  76. package/dist/types/overlays/annotation-types.d.ts +2 -0
  77. package/dist/types/overlays/ask-dialog.d.ts +5 -2
  78. package/dist/types/overlays/btw-history-panel.d.ts +13 -1
  79. package/dist/types/overlays/copy-selector.d.ts +1 -0
  80. package/dist/types/overlays/history-search.d.ts +10 -0
  81. package/dist/types/overlays/hook-selector.d.ts +9 -4
  82. package/dist/types/overlays/hub-frame.d.ts +4 -1
  83. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  84. package/dist/types/overlays/model-browser.d.ts +54 -7
  85. package/dist/types/overlays/model-hub.d.ts +37 -0
  86. package/dist/types/overlays/oauth-selector.d.ts +0 -1
  87. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  88. package/dist/types/overlays/report-panel.d.ts +68 -0
  89. package/dist/types/overlays/rewind-selector.d.ts +1 -0
  90. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  91. package/dist/types/overlays/session-selector.d.ts +6 -2
  92. package/dist/types/overlays/settings-defs.d.ts +1 -0
  93. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  94. package/dist/types/overlays/usage-dashboard.d.ts +12 -8
  95. package/dist/types/overlays/usage-display.d.ts +3 -1
  96. package/dist/types/prompt/composer-cache.d.ts +0 -16
  97. package/dist/types/prompt/composer.d.ts +18 -3
  98. package/dist/types/prompt/custom-editor.d.ts +25 -13
  99. package/dist/types/prompt/image-source.d.ts +2 -1
  100. package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
  101. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  102. package/dist/types/prompt/welcome.d.ts +7 -2
  103. package/dist/types/render/composite.d.ts +2 -0
  104. package/dist/types/render/output-pane.d.ts +1 -0
  105. package/dist/types/render/render-utils.d.ts +1 -0
  106. package/dist/types/render/tool-card.d.ts +6 -0
  107. package/dist/types/render/width-aware-text.d.ts +2 -0
  108. package/dist/types/setup/scenes/types.d.ts +1 -0
  109. package/dist/types/space-hold.d.ts +40 -25
  110. package/dist/types/status-line/component.d.ts +3 -0
  111. package/dist/types/status-line/context-usage.d.ts +7 -5
  112. package/dist/types/status-line/host.d.ts +4 -1
  113. package/dist/types/status-line/segments.d.ts +2 -0
  114. package/dist/types/status-line/types.d.ts +14 -3
  115. package/dist/types/terminal-capabilities.d.ts +41 -36
  116. package/dist/types/terminal-multiplexer.d.ts +127 -14
  117. package/dist/types/terminal.d.ts +23 -16
  118. package/dist/types/theme/active-symbols.d.ts +1 -1
  119. package/dist/types/theme/schema.d.ts +11 -2
  120. package/dist/types/theme/symbols.d.ts +1 -1
  121. package/dist/types/theme/theme-class.d.ts +7 -0
  122. package/dist/types/theme/theme.d.ts +1 -1
  123. package/dist/types/theme/tui-adapters.d.ts +3 -0
  124. package/dist/types/thinking.d.ts +1 -1
  125. package/dist/types/tmux.d.ts +11 -4
  126. package/dist/types/tools/bash.d.ts +7 -2
  127. package/dist/types/tools/daemon.d.ts +1 -1
  128. package/dist/types/tools/eval.d.ts +22 -3
  129. package/dist/types/tools/glob.d.ts +4 -0
  130. package/dist/types/tools/native-view.d.ts +47 -0
  131. package/dist/types/tools/output-meta.d.ts +11 -1
  132. package/dist/types/tools/renderer.d.ts +37 -2
  133. package/dist/types/tools/streaming-output.d.ts +20 -1
  134. package/dist/types/tools/task.d.ts +2 -3
  135. package/dist/types/tools/write.d.ts +9 -2
  136. package/dist/types/tools/yield.d.ts +22 -0
  137. package/dist/types/tui.d.ts +36 -0
  138. package/dist/types/utils.d.ts +6 -0
  139. package/dist/types/vim.d.ts +19 -5
  140. package/dist/types/windows-input-mode.d.ts +33 -1
  141. package/package.json +13 -9
  142. package/src/active-terminal.d.ts +30 -0
  143. package/src/active-terminal.ts +80 -0
  144. package/src/app-keybindings.d.ts +7 -2
  145. package/src/app-keybindings.ts +7 -2
  146. package/src/apps/autoresearch-dashboard.ts +7 -3
  147. package/src/apps/cleanse-board.ts +2 -2
  148. package/src/apps/cleanse-picker.ts +1 -1
  149. package/src/apps/debug/log-viewer.ts +188 -72
  150. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  151. package/src/apps/debug/raw-sse.ts +39 -5
  152. package/src/apps/git/diff-pane.ts +96 -23
  153. package/src/apps/git/git-tui.ts +23 -16
  154. package/src/apps/git/help.ts +29 -20
  155. package/src/apps/git/sidebar.ts +8 -3
  156. package/src/apps/if-bench-board.ts +2 -2
  157. package/src/apps/ps-top.ts +2 -2
  158. package/src/apps/setup-model-picker.ts +1 -1
  159. package/src/autocomplete.d.ts +5 -2
  160. package/src/autocomplete.ts +67 -43
  161. package/src/bracketed-paste.ts +10 -3
  162. package/src/charts/chart-plan.d.ts +674 -0
  163. package/src/charts/chart-plan.ts +5081 -0
  164. package/src/charts/chart-svg.d.ts +59 -0
  165. package/src/charts/chart-svg.ts +4657 -0
  166. package/src/charts/table-data.d.ts +175 -0
  167. package/src/charts/table-data.ts +1478 -0
  168. package/src/chat/advisor-message.ts +11 -0
  169. package/src/chat/assistant-message.d.ts +11 -1
  170. package/src/chat/assistant-message.ts +245 -54
  171. package/src/chat/chat-transcript-builder.d.ts +3 -0
  172. package/src/chat/chat-transcript-builder.ts +9 -3
  173. package/src/chat/compaction-summary-message.d.ts +1 -0
  174. package/src/chat/compaction-summary-message.ts +8 -0
  175. package/src/chat/execution-shared.d.ts +6 -5
  176. package/src/chat/execution-shared.ts +42 -25
  177. package/src/chat/fence-figure.d.ts +42 -0
  178. package/src/chat/fence-figure.ts +80 -0
  179. package/src/chat/figure-markdown.d.ts +42 -0
  180. package/src/chat/figure-markdown.ts +174 -0
  181. package/src/chat/messages.d.ts +1 -1
  182. package/src/chat/messages.ts +1 -1
  183. package/src/chat/read-tool-group.ts +6 -4
  184. package/src/chat/skill-message.ts +6 -0
  185. package/src/chat/svg-figure.d.ts +33 -0
  186. package/src/chat/svg-figure.ts +399 -0
  187. package/src/chat/svg-source.d.ts +49 -0
  188. package/src/chat/svg-source.ts +188 -0
  189. package/src/chat/table-chart.d.ts +89 -0
  190. package/src/chat/table-chart.ts +294 -0
  191. package/src/chat/tool-execution.d.ts +2 -0
  192. package/src/chat/tool-execution.ts +86 -7
  193. package/src/chat/user-message.d.ts +17 -4
  194. package/src/chat/user-message.ts +71 -8
  195. package/src/chrome/shared.ts +53 -0
  196. package/src/chrome/status-notice.ts +25 -7
  197. package/src/chrome/transcript-container.d.ts +6 -0
  198. package/src/chrome/transcript-container.ts +162 -19
  199. package/src/components/box.d.ts +1 -0
  200. package/src/components/box.ts +5 -0
  201. package/src/components/disclosure.d.ts +1 -0
  202. package/src/components/disclosure.ts +8 -0
  203. package/src/components/editor.d.ts +23 -5
  204. package/src/components/editor.ts +316 -47
  205. package/src/components/form.d.ts +1 -0
  206. package/src/components/form.ts +8 -0
  207. package/src/components/image.d.ts +26 -44
  208. package/src/components/image.ts +115 -152
  209. package/src/components/input.d.ts +2 -1
  210. package/src/components/input.ts +17 -7
  211. package/src/components/key-value-list.d.ts +1 -0
  212. package/src/components/key-value-list.ts +4 -0
  213. package/src/components/layout/row.d.ts +1 -0
  214. package/src/components/layout/row.ts +7 -0
  215. package/src/components/layout/stack.d.ts +1 -0
  216. package/src/components/layout/stack.ts +7 -0
  217. package/src/components/loader.d.ts +5 -6
  218. package/src/components/loader.ts +10 -17
  219. package/src/components/markdown.d.ts +47 -5
  220. package/src/components/markdown.ts +786 -303
  221. package/src/components/scroll-view.d.ts +1 -0
  222. package/src/components/scroll-view.ts +5 -0
  223. package/src/components/section.d.ts +1 -0
  224. package/src/components/section.ts +5 -0
  225. package/src/components/settings-list.ts +2 -0
  226. package/src/components/table.d.ts +1 -0
  227. package/src/components/table.ts +4 -0
  228. package/src/components/text.d.ts +1 -0
  229. package/src/components/text.ts +7 -0
  230. package/src/components/tree-view.d.ts +7 -0
  231. package/src/components/tree-view.ts +38 -8
  232. package/src/components/truncated-text.d.ts +1 -0
  233. package/src/components/truncated-text.ts +5 -0
  234. package/src/desktop-notify.ts +3 -2
  235. package/src/fuzzy.d.ts +29 -1
  236. package/src/fuzzy.ts +115 -30
  237. package/src/hotkeys-markdown.ts +9 -2
  238. package/src/key-hint-format.d.ts +9 -1
  239. package/src/key-hint-format.ts +26 -11
  240. package/src/keybinding-matchers.d.ts +3 -0
  241. package/src/keybinding-matchers.ts +6 -2
  242. package/src/keybindings.d.ts +1 -1
  243. package/src/keybindings.ts +1 -1
  244. package/src/kitty-graphics.d.ts +2 -2
  245. package/src/kitty-graphics.ts +11 -5
  246. package/src/loop-watchdog.d.ts +8 -7
  247. package/src/loop-watchdog.ts +18 -13
  248. package/src/multiplexers/cmux.d.ts +16 -0
  249. package/src/multiplexers/cmux.ts +29 -0
  250. package/src/multiplexers/herdr.d.ts +26 -0
  251. package/src/multiplexers/herdr.ts +64 -0
  252. package/src/multiplexers/notify.d.ts +13 -0
  253. package/src/multiplexers/notify.ts +24 -0
  254. package/src/multiplexers/orca.d.ts +12 -0
  255. package/src/multiplexers/orca.ts +16 -0
  256. package/src/multiplexers/screen.d.ts +9 -0
  257. package/src/multiplexers/screen.ts +15 -0
  258. package/src/multiplexers/tmux.d.ts +29 -0
  259. package/src/multiplexers/tmux.ts +40 -0
  260. package/src/multiplexers/types.d.ts +100 -0
  261. package/src/multiplexers/types.ts +110 -0
  262. package/src/multiplexers/wmux.d.ts +12 -0
  263. package/src/multiplexers/wmux.ts +16 -0
  264. package/src/multiplexers/zellij.d.ts +12 -0
  265. package/src/multiplexers/zellij.ts +24 -0
  266. package/src/native/apply.d.ts +4 -3
  267. package/src/native/apply.ts +4 -3
  268. package/src/native/backend.d.ts +5 -4
  269. package/src/native/backend.ts +199 -22
  270. package/src/native/blobs.d.ts +7 -3
  271. package/src/native/blobs.ts +47 -12
  272. package/src/native/encode.d.ts +8 -6
  273. package/src/native/encode.ts +76 -34
  274. package/src/native/node.d.ts +36 -5
  275. package/src/native/node.ts +31 -5
  276. package/src/native/overlay.d.ts +8 -3
  277. package/src/native/overlay.ts +17 -4
  278. package/src/native/reconcile.ts +16 -4
  279. package/src/native/settle.d.ts +4 -3
  280. package/src/native/settle.ts +4 -3
  281. package/src/overlays/agent-hub-projection.d.ts +6 -5
  282. package/src/overlays/agent-hub-projection.ts +124 -13
  283. package/src/overlays/agent-hub-types.d.ts +9 -0
  284. package/src/overlays/agent-hub-types.ts +6 -0
  285. package/src/overlays/agent-hub.ts +48 -29
  286. package/src/overlays/agent-transcript-viewer.ts +93 -23
  287. package/src/overlays/agents-hub.ts +28 -8
  288. package/src/overlays/annotation-overlay.ts +293 -90
  289. package/src/overlays/annotation-types.ts +2 -0
  290. package/src/overlays/ask-dialog.ts +10 -17
  291. package/src/overlays/btw-history-panel.ts +136 -39
  292. package/src/overlays/copy-selector.d.ts +1 -0
  293. package/src/overlays/copy-selector.ts +32 -8
  294. package/src/overlays/extensions/extension-dashboard.ts +4 -4
  295. package/src/overlays/extensions/state-manager.ts +52 -12
  296. package/src/overlays/history-search.ts +100 -7
  297. package/src/overlays/hook-selector.ts +12 -5
  298. package/src/overlays/hub-frame.ts +19 -1
  299. package/src/overlays/jobs-panel.ts +33 -52
  300. package/src/overlays/model-browser.ts +367 -122
  301. package/src/overlays/model-hub.ts +435 -59
  302. package/src/overlays/move-overlay.ts +28 -14
  303. package/src/overlays/oauth-selector.ts +54 -70
  304. package/src/overlays/pause-screen.ts +2 -2
  305. package/src/overlays/plan-review-overlay.ts +208 -70
  306. package/src/overlays/plugin-settings.ts +3 -3
  307. package/src/overlays/report-panel.ts +191 -0
  308. package/src/overlays/rewind-selector.ts +88 -17
  309. package/src/overlays/session-observer-registry.d.ts +2 -0
  310. package/src/overlays/session-observer-registry.ts +10 -2
  311. package/src/overlays/session-selector.ts +64 -26
  312. package/src/overlays/settings-defs.ts +5 -4
  313. package/src/overlays/settings-selector.ts +33 -8
  314. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  315. package/src/overlays/stats-notice.ts +1 -1
  316. package/src/overlays/thinking-selector.ts +11 -10
  317. package/src/overlays/tree-selector.ts +41 -20
  318. package/src/overlays/usage-dashboard.ts +117 -58
  319. package/src/overlays/usage-display.ts +10 -4
  320. package/src/overlays/usage-row.ts +22 -8
  321. package/src/prompt/attachment-chips.ts +15 -11
  322. package/src/prompt/composer-attachments.ts +6 -4
  323. package/src/prompt/composer-cache.ts +7 -86
  324. package/src/prompt/composer.ts +37 -26
  325. package/src/prompt/custom-editor.ts +282 -105
  326. package/src/prompt/image-source.ts +2 -1
  327. package/src/prompt/interactive-context-helpers.ts +32 -18
  328. package/src/prompt/macos-spelling.ts +7 -0
  329. package/src/prompt/magic-keywords.ts +11 -2
  330. package/src/prompt/model-mention-autocomplete.ts +14 -14
  331. package/src/prompt/queued-messages.ts +6 -4
  332. package/src/prompt/tips.txt +13 -9
  333. package/src/prompt/welcome.ts +64 -46
  334. package/src/prompt/word-completion.ts +38 -5
  335. package/src/render/composite.d.ts +2 -0
  336. package/src/render/composite.ts +64 -0
  337. package/src/render/output-pane.d.ts +1 -0
  338. package/src/render/output-pane.ts +6 -0
  339. package/src/render/render-utils.d.ts +1 -0
  340. package/src/render/render-utils.ts +16 -1
  341. package/src/render/tool-card.d.ts +6 -0
  342. package/src/render/tool-card.ts +15 -0
  343. package/src/render/width-aware-text.d.ts +2 -0
  344. package/src/render/width-aware-text.ts +5 -0
  345. package/src/setup/scenes/model.ts +2 -1
  346. package/src/setup/scenes/types.ts +1 -0
  347. package/src/space-hold.d.ts +40 -25
  348. package/src/space-hold.ts +156 -52
  349. package/src/status-line/component.ts +292 -78
  350. package/src/status-line/context-usage.ts +21 -46
  351. package/src/status-line/host.ts +4 -1
  352. package/src/status-line/segments.ts +40 -22
  353. package/src/status-line/types.ts +15 -3
  354. package/src/terminal-capabilities.d.ts +41 -36
  355. package/src/terminal-capabilities.ts +137 -213
  356. package/src/terminal-multiplexer.d.ts +127 -14
  357. package/src/terminal-multiplexer.ts +112 -33
  358. package/src/terminal.d.ts +23 -16
  359. package/src/terminal.ts +203 -99
  360. package/src/theme/active-symbols.d.ts +1 -1
  361. package/src/theme/active-symbols.ts +1 -1
  362. package/src/theme/schema-validation.ts +1 -0
  363. package/src/theme/schema.d.ts +11 -2
  364. package/src/theme/schema.ts +14 -1
  365. package/src/theme/session-color.ts +1 -7
  366. package/src/theme/symbols.d.ts +1 -1
  367. package/src/theme/symbols.ts +11 -4
  368. package/src/theme/theme-class.d.ts +7 -0
  369. package/src/theme/theme-class.ts +26 -1
  370. package/src/theme/theme-schema.json +5 -1
  371. package/src/theme/theme.d.ts +1 -1
  372. package/src/theme/theme.ts +8 -4
  373. package/src/theme/tui-adapters.d.ts +3 -0
  374. package/src/theme/tui-adapters.ts +68 -0
  375. package/src/thinking.d.ts +1 -1
  376. package/src/thinking.ts +1 -1
  377. package/src/tmux.d.ts +11 -4
  378. package/src/tmux.ts +47 -11
  379. package/src/tools/ast-edit.ts +59 -35
  380. package/src/tools/ast-grep.ts +35 -16
  381. package/src/tools/bash.d.ts +7 -2
  382. package/src/tools/bash.ts +116 -47
  383. package/src/tools/daemon.d.ts +1 -1
  384. package/src/tools/daemon.ts +1 -1
  385. package/src/tools/default-renderer.ts +35 -19
  386. package/src/tools/edit.ts +142 -120
  387. package/src/tools/eval-format/index.ts +28 -2
  388. package/src/tools/eval-format/javascript.ts +7 -5
  389. package/src/tools/eval.d.ts +22 -3
  390. package/src/tools/eval.ts +364 -208
  391. package/src/tools/fetch.ts +5 -6
  392. package/src/tools/glob.d.ts +4 -0
  393. package/src/tools/glob.ts +33 -65
  394. package/src/tools/grep.ts +81 -48
  395. package/src/tools/index.ts +2 -0
  396. package/src/tools/lsp.ts +81 -59
  397. package/src/tools/mcp.ts +111 -68
  398. package/src/tools/native-view.d.ts +47 -0
  399. package/src/tools/native-view.ts +102 -1
  400. package/src/tools/output-meta.d.ts +11 -1
  401. package/src/tools/output-meta.ts +23 -3
  402. package/src/tools/read.ts +7 -5
  403. package/src/tools/renderer.d.ts +37 -2
  404. package/src/tools/renderer.ts +42 -2
  405. package/src/tools/streaming-output.d.ts +20 -1
  406. package/src/tools/streaming-output.ts +154 -50
  407. package/src/tools/task.d.ts +2 -3
  408. package/src/tools/task.ts +115 -62
  409. package/src/tools/todo.ts +119 -30
  410. package/src/tools/wait.ts +34 -24
  411. package/src/tools/web-search.ts +1 -1
  412. package/src/tools/write.d.ts +9 -2
  413. package/src/tools/write.ts +147 -95
  414. package/src/tools/yield.d.ts +22 -0
  415. package/src/tools/yield.ts +225 -0
  416. package/src/tui.d.ts +36 -0
  417. package/src/tui.ts +666 -217
  418. package/src/utils.d.ts +6 -0
  419. package/src/utils.ts +19 -0
  420. package/src/vim.d.ts +19 -5
  421. package/src/vim.ts +333 -18
  422. package/src/windows-input-mode.d.ts +33 -1
  423. package/src/windows-input-mode.ts +212 -6
package/src/tools/mcp.ts CHANGED
@@ -88,30 +88,22 @@ function renderMarkdownMCPResult(
88
88
  const markdown = new Markdown(trimmedOutput, 0, 0, getMarkdownTheme(), {
89
89
  color: text => theme.fg("toolOutput", text),
90
90
  });
91
+ // Args tree and output pane rows are spinner-invariant; rebuild only on
92
+ // expansion or width change instead of every animated frame.
93
+ let bodyMemo: { expanded: boolean; width: number; lines: readonly string[] } | undefined;
91
94
  return plainToolCard(
92
95
  theme,
93
96
  ({ contentWidth }) => {
94
97
  const isError = result.isError ?? result.details?.isError ?? false;
95
98
  const title = result.details ? `${result.details.serverName}/${result.details.mcpToolName}` : "MCP";
96
- const body: string[] = [];
97
- if (options.expanded && args && Object.keys(args).length > 0) {
98
- body.push(...buildMcpArgsSection(args, theme));
99
+ if (bodyMemo === undefined || bodyMemo.expanded !== options.expanded || bodyMemo.width !== contentWidth) {
100
+ bodyMemo = {
101
+ expanded: options.expanded,
102
+ width: contentWidth,
103
+ lines: buildMarkdownMcpBody(markdown, truncationWarning, options.expanded, contentWidth, theme, args),
104
+ };
99
105
  }
100
-
101
- const rendered = markdown.render(Math.max(1, contentWidth));
102
- body.push(
103
- ...formatOutputPaneLines(
104
- {
105
- lines: rendered,
106
- expanded: options.expanded,
107
- collapsedMaxLines: 4,
108
- expandedMaxLines: 12,
109
- showExpandHintWhenUncapped: true,
110
- },
111
- theme,
112
- ).lines,
113
- );
114
- if (truncationWarning) body.push(truncationWarning);
106
+ const body = bodyMemo.lines;
115
107
  const status: StatusLineOptions = options.isPartial
116
108
  ? {
117
109
  icon: options.spinnerFrame !== undefined ? "running" : "pending",
@@ -129,10 +121,47 @@ function renderMarkdownMCPResult(
129
121
  applyBg: false,
130
122
  };
131
123
  },
132
- { paddingX: 0, paddingY: 0 },
124
+ {
125
+ paddingX: 0,
126
+ paddingY: 0,
127
+ onInvalidate: () => (bodyMemo = undefined),
128
+ onReleaseRenderCaches: () => {
129
+ bodyMemo = undefined;
130
+ markdown.releaseRenderCaches();
131
+ },
132
+ },
133
133
  );
134
134
  }
135
135
 
136
+ function buildMarkdownMcpBody(
137
+ markdown: Markdown,
138
+ truncationWarning: string | null,
139
+ expanded: boolean,
140
+ contentWidth: number,
141
+ theme: Theme,
142
+ args: Record<string, unknown> | undefined,
143
+ ): string[] {
144
+ const body: string[] = [];
145
+ if (expanded && args && Object.keys(args).length > 0) {
146
+ body.push(...buildMcpArgsSection(args, theme));
147
+ }
148
+ const rendered = markdown.render(Math.max(1, contentWidth));
149
+ body.push(
150
+ ...formatOutputPaneLines(
151
+ {
152
+ lines: rendered,
153
+ expanded,
154
+ collapsedMaxLines: 4,
155
+ expandedMaxLines: 12,
156
+ showExpandHintWhenUncapped: true,
157
+ },
158
+ theme,
159
+ ).lines,
160
+ );
161
+ if (truncationWarning) body.push(truncationWarning);
162
+ return body;
163
+ }
164
+
136
165
  /**
137
166
  * Render MCP tool result.
138
167
  */
@@ -165,6 +194,8 @@ export function renderMCPResult(
165
194
  if (trimmedOutput && renderMarkdownResults && !isJsonOutput) {
166
195
  return renderMarkdownMCPResult(result, trimmedOutput, truncationWarning, options, theme, args);
167
196
  }
197
+ // `expanded`, args and output are fixed for this card; only width varies the body.
198
+ let bodyMemo: { width: number; lines: readonly string[] } | undefined;
168
199
  return plainToolCard(
169
200
  theme,
170
201
  ({ contentWidth }) => {
@@ -180,61 +211,72 @@ export function renderMCPResult(
180
211
  ? { icon: "error", title }
181
212
  : { iconOverride: theme.styledSymbol("tool.mcp", "accent"), title };
182
213
  const phase: ToolCardPhase = options.isPartial ? "partial" : isError ? "error" : "success";
183
- const body: string[] = [];
184
-
185
- // Args section (when expanded)
186
- if (expanded && args && typeof args === "object" && Object.keys(args).length > 0) {
187
- body.push(...buildMcpArgsSection(args, theme));
214
+ if (bodyMemo === undefined || bodyMemo.width !== contentWidth) {
215
+ bodyMemo = { width: contentWidth, lines: buildMcpResultBody(contentWidth) };
188
216
  }
217
+ return { status, phase, body: bodyMemo.lines, applyBg: false };
218
+ },
219
+ {
220
+ paddingX: 0,
221
+ paddingY: 0,
222
+ onInvalidate: () => (bodyMemo = undefined),
223
+ onReleaseRenderCaches: () => (bodyMemo = undefined),
224
+ },
225
+ );
189
226
 
190
- // Output section. The body and spill metadata are normalized before
191
- // component selection so the opt-in Markdown path can use its own renderer.
227
+ function buildMcpResultBody(contentWidth: number): string[] {
228
+ const body: string[] = [];
229
+ // Args section (when expanded)
230
+ if (expanded && args && typeof args === "object" && Object.keys(args).length > 0) {
231
+ body.push(...buildMcpArgsSection(args, theme));
232
+ }
192
233
 
193
- if (!trimmedOutput) {
194
- body.push(theme.fg("dim", "(no output)"));
195
- return { status, phase, body, applyBg: false };
196
- }
234
+ // Output section. The body and spill metadata are normalized before
235
+ // component selection so the opt-in Markdown path can use its own renderer.
197
236
 
198
- // Preserve the existing structured JSON renderer regardless of the
199
- // Markdown preference; JSON trees remain more useful than styled source.
200
- if (isJsonOutput) {
201
- const maxDepth = expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED;
202
- const maxLines = expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED;
203
- const maxScalarLen = expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED;
204
- const tree = renderJsonTreeLines(parsedOutput, theme, maxDepth, maxLines, maxScalarLen);
205
-
206
- if (tree.lines.length > 0) {
207
- body.push(...tree.lines);
208
- if (!expanded) {
209
- body.push(formatExpandHint(theme, expanded, true));
210
- } else if (tree.truncated) {
211
- body.push(theme.fg("dim", "…"));
212
- }
213
- if (truncationWarning) body.push(truncationWarning);
214
- return { status, phase, body, applyBg: false };
237
+ if (!trimmedOutput) {
238
+ body.push(theme.fg("dim", "(no output)"));
239
+ return body;
240
+ }
241
+
242
+ // Preserve the existing structured JSON renderer regardless of the
243
+ // Markdown preference; JSON trees remain more useful than styled source.
244
+ if (isJsonOutput) {
245
+ const maxDepth = expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED;
246
+ const maxLines = expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED;
247
+ const maxScalarLen = expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED;
248
+ const tree = renderJsonTreeLines(parsedOutput, theme, maxDepth, maxLines, maxScalarLen);
249
+
250
+ if (tree.lines.length > 0) {
251
+ body.push(...tree.lines);
252
+ if (!expanded) {
253
+ body.push(formatExpandHint(theme, expanded, true));
254
+ } else if (tree.truncated) {
255
+ body.push(theme.fg("dim", "…"));
215
256
  }
257
+ if (truncationWarning) body.push(truncationWarning);
258
+ return body;
216
259
  }
260
+ }
217
261
 
218
- // Raw text output, capped to the first rows with an expand hint while collapsed.
219
- body.push(
220
- ...formatOutputPaneLines(
221
- {
222
- lines: trimmedOutput.split("\n"),
223
- expanded,
224
- collapsedMaxLines: 4,
225
- expandedMaxLines: 12,
226
- styleLine: line => truncateToWidth(styleToolOutputLine(line, theme), contentWidth),
227
- showExpandHintWhenUncapped: true,
228
- },
229
- theme,
230
- ).lines,
231
- );
232
-
233
- if (truncationWarning) body.push(truncationWarning);
234
- return { status, phase, body, applyBg: false };
235
- },
236
- { paddingX: 0, paddingY: 0 },
237
- );
262
+ // Raw text output, capped to the first rows with an expand hint while collapsed.
263
+ body.push(
264
+ ...formatOutputPaneLines(
265
+ {
266
+ lines: trimmedOutput.split("\n"),
267
+ expanded,
268
+ collapsedMaxLines: 4,
269
+ expandedMaxLines: 12,
270
+ styleLine: line => truncateToWidth(styleToolOutputLine(line, theme), contentWidth),
271
+ showExpandHintWhenUncapped: true,
272
+ },
273
+ theme,
274
+ ).lines,
275
+ );
276
+
277
+ if (truncationWarning) body.push(truncationWarning);
278
+ return body;
279
+ }
238
280
  }
239
281
 
240
282
  /** Visible MCP argument entries (streaming/intent bookkeeping keys dropped). */
@@ -273,7 +315,8 @@ export function describeMCPResult(
273
315
  _options: RenderResultOptions,
274
316
  args?: Record<string, unknown>,
275
317
  ): NativeToolView | undefined {
276
- return mcpResultMemo.get(result, [renderMarkdownResults, JSON.stringify(args ?? null)], () => {
318
+ // Callers allocate a fresh args object per streamed delta, so identity is a sound dep.
319
+ return mcpResultMemo.get(result, [renderMarkdownResults, args], () => {
277
320
  const textContent = (result.content ?? [])
278
321
  .filter(block => block.type === "text")
279
322
  .map(block => block.text ?? "")
@@ -72,6 +72,53 @@ export declare function truncationNotice(meta: OutputMeta | undefined): NativeNo
72
72
  * truncation / artifact-capture notice from `meta`; undefined when empty.
73
73
  */
74
74
  export declare function footnoteText(parts: readonly string[], meta?: OutputMeta): NativeNode | undefined;
75
+ /** Drawn lines of a run's input (command, cell code) before its "N more lines" button. */
76
+ export declare const RUN_INPUT_PREVIEW_LINES = 6;
77
+ /**
78
+ * What a run's foot names: `running` (live clock), `background` (an async job
79
+ * outlives the call), and the settled outcomes. A failure with an exit code
80
+ * reads `exit N`; without one, `Failed`.
81
+ */
82
+ export type RunState = "running" | "background" | "done" | "failed" | "cancelled" | "timed-out";
83
+ /** Inputs of {@link runFoot}. */
84
+ export interface RunFootInput {
85
+ readonly state: RunState;
86
+ /** Exit code of a `failed` run; a non-zero code names the state `exit N`. */
87
+ readonly exitCode?: number;
88
+ /** Milliseconds run so far (`running`) or in total; the time is omitted when undefined. */
89
+ readonly elapsedMs?: number;
90
+ /** Quiet facts (job id, service state, artifact), muted and ` · `-joined. */
91
+ readonly facts?: readonly string[];
92
+ /** Truncation / artifact-capture notices, appended to the facts in `warning`. */
93
+ readonly meta?: OutputMeta;
94
+ }
95
+ /**
96
+ * The foot line of a run box (`zeta.run.foot`): the state word in its tone,
97
+ * the run time (a live clock while running, frozen once settled) and the
98
+ * quiet facts. Glyphs are the terminal's styling, never text.
99
+ */
100
+ export declare function runFoot(input: RunFootInput): NativeNode;
101
+ /** A run's input section: the command or cell source, clamped to {@link RUN_INPUT_PREVIEW_LINES}. */
102
+ export declare function runInput(source: string, p: {
103
+ role: string;
104
+ lang: string;
105
+ wrap: boolean;
106
+ }): NativeNode;
107
+ /** A run's terminal output: follows its tail, clamped to the last `previewLines` lines while folded. */
108
+ export declare function runOutput(output: string, p: {
109
+ role: string;
110
+ previewLines: number;
111
+ key?: string;
112
+ }): NativeNode;
113
+ /**
114
+ * One run as one box (`zeta.run`): input, output, status lines and foot in
115
+ * order. `tone` tints the box: `error` for a failed run, `warning` for a
116
+ * timed-out one.
117
+ */
118
+ export declare function runBox(children: readonly (NativeChild | undefined | false)[], p: {
119
+ key: string;
120
+ tone?: TspTone;
121
+ }): NativeNode;
75
122
  /** A diagnostic's severity glyph: `x-circle` error, `warn` warning, `info`, `lightbulb` hint, in the severity tone. */
76
123
  export declare function diagnosticGlyph(severity: ParsedDiagnostic["severity"]): NativeNode;
77
124
  /**
@@ -6,7 +6,7 @@
6
6
  import * as path from "node:path";
7
7
  import { pathToFileURL } from "node:url";
8
8
  import type { TspSpan, TspText, TspTone } from "@linxiraos/pi-wire";
9
- import { code, keyed, node, row, span, text } from "../native/describe";
9
+ import { ansi, code, compact, keyed, node, row, span, text } from "../native/describe";
10
10
  import type { NativeChild, NativeNode } from "../native/node";
11
11
  import { type ParsedDiagnostic, parseDiagnosticMessage, shortenPath } from "../render/render-utils";
12
12
  import { plainText } from "../native/spans";
@@ -192,6 +192,107 @@ export function footnoteText(parts: readonly string[], meta?: OutputMeta): Nativ
192
192
  };
193
193
  }
194
194
 
195
+ /** Drawn lines of a run's input (command, cell code) before its "N more lines" button. */
196
+ export const RUN_INPUT_PREVIEW_LINES = 6;
197
+
198
+ /**
199
+ * What a run's foot names: `running` (live clock), `background` (an async job
200
+ * outlives the call), and the settled outcomes. A failure with an exit code
201
+ * reads `exit N`; without one, `Failed`.
202
+ */
203
+ export type RunState = "running" | "background" | "done" | "failed" | "cancelled" | "timed-out";
204
+
205
+ const RUN_STATE: Record<RunState, { readonly word: string; readonly tone: TspTone }> = {
206
+ running: { word: "Running", tone: "pending" },
207
+ background: { word: "In background", tone: "pending" },
208
+ done: { word: "Done", tone: "success" },
209
+ failed: { word: "Failed", tone: "error" },
210
+ cancelled: { word: "Cancelled", tone: "muted" },
211
+ "timed-out": { word: "Timed out", tone: "warning" },
212
+ };
213
+
214
+ /** Inputs of {@link runFoot}. */
215
+ export interface RunFootInput {
216
+ readonly state: RunState;
217
+ /** Exit code of a `failed` run; a non-zero code names the state `exit N`. */
218
+ readonly exitCode?: number;
219
+ /** Milliseconds run so far (`running`) or in total; the time is omitted when undefined. */
220
+ readonly elapsedMs?: number;
221
+ /** Quiet facts (job id, service state, artifact), muted and ` · `-joined. */
222
+ readonly facts?: readonly string[];
223
+ /** Truncation / artifact-capture notices, appended to the facts in `warning`. */
224
+ readonly meta?: OutputMeta;
225
+ }
226
+
227
+ /**
228
+ * The foot line of a run box (`zeta.run.foot`): the state word in its tone,
229
+ * the run time (a live clock while running, frozen once settled) and the
230
+ * quiet facts. Glyphs are the terminal's styling, never text.
231
+ */
232
+ export function runFoot(input: RunFootInput): NativeNode {
233
+ const failedWithCode = input.state === "failed" && input.exitCode !== undefined && input.exitCode !== 0;
234
+ const state = failedWithCode
235
+ ? { word: `exit ${input.exitCode}`, tone: RUN_STATE.failed.tone }
236
+ : RUN_STATE[input.state];
237
+ const facts = (input.facts ?? []).map(fact => plainText(fact).trim()).filter(fact => fact.length > 0);
238
+ const notices: string[] = [];
239
+ if (input.meta?.truncation) notices.push(formatTruncationMetaNotice(input.meta.truncation, input.meta.source));
240
+ if (input.meta?.artifactError) notices.push(formatArtifactErrorNotice(input.meta.artifactError));
241
+ const factSpans: TspSpan[] = [];
242
+ if (facts.length > 0) factSpans.push(span(facts.join(" · "), "muted"));
243
+ for (const notice of notices) {
244
+ if (factSpans.length > 0) factSpans.push(span(" · ", "muted"));
245
+ factSpans.push(span(notice, "warning"));
246
+ }
247
+ const ms = input.elapsedMs === undefined ? undefined : Math.max(0, Math.round(input.elapsedMs));
248
+ const time =
249
+ ms === undefined
250
+ ? undefined
251
+ : node(
252
+ "elapsed",
253
+ input.state === "running"
254
+ ? { role: "zeta.run.time", age: ms, format: "short" }
255
+ : { role: "zeta.run.time", age: ms, stopped: ms, format: "short" },
256
+ undefined,
257
+ "time",
258
+ );
259
+ return node(
260
+ "row",
261
+ { role: "zeta.run.foot", gap: "sm", align: "center" },
262
+ compact<NativeNode>([
263
+ keyed(text([span(state.word)], { role: "zeta.run.state", tone: state.tone }), "state"),
264
+ time,
265
+ factSpans.length > 0 && keyed(text(factSpans, { role: "zeta.run.facts", wrap: "word" }), "facts"),
266
+ ]),
267
+ "foot",
268
+ );
269
+ }
270
+
271
+ /** A run's input section: the command or cell source, clamped to {@link RUN_INPUT_PREVIEW_LINES}. */
272
+ export function runInput(source: string, p: { role: string; lang: string; wrap: boolean }): NativeNode {
273
+ return keyed(
274
+ code(source.trimEnd(), { role: p.role, lang: p.lang, wrap: p.wrap, preview: { lines: RUN_INPUT_PREVIEW_LINES } }),
275
+ "input",
276
+ );
277
+ }
278
+
279
+ /** A run's terminal output: follows its tail, clamped to the last `previewLines` lines while folded. */
280
+ export function runOutput(output: string, p: { role: string; previewLines: number; key?: string }): NativeNode {
281
+ return keyed(ansi(output, { role: p.role, follow: true, preview: { lines: p.previewLines } }), p.key ?? "output");
282
+ }
283
+
284
+ /**
285
+ * One run as one box (`zeta.run`): input, output, status lines and foot in
286
+ * order. `tone` tints the box: `error` for a failed run, `warning` for a
287
+ * timed-out one.
288
+ */
289
+ export function runBox(
290
+ children: readonly (NativeChild | undefined | false)[],
291
+ p: { key: string; tone?: TspTone },
292
+ ): NativeNode {
293
+ return node("col", { role: "zeta.run", gap: "none", tone: p.tone }, compact(children), p.key);
294
+ }
295
+
195
296
  /** Diagnostic rows shown before `+N more`. */
196
297
  const DIAGNOSTIC_ROWS = 5;
197
298
 
@@ -84,12 +84,22 @@ export interface LimitsMeta {
84
84
  reached: number;
85
85
  suggestion: number;
86
86
  };
87
- /** `unit` may be absent in sessions persisted before it was recorded. */
87
+ /**
88
+ * `unit` may be absent in sessions persisted before it was recorded. `lines` is
89
+ * the first-to-last range of cut lines, and `selectorBase` the target a
90
+ * `:raw:<line>-<line>` selector reads one of them whole from (an artifact's
91
+ * `artifact://<id>` stands in when absent).
92
+ */
88
93
  columnTruncated?: {
89
94
  maxColumn: number;
90
95
  unit?: "bytes" | "chars";
91
96
  artifactId?: string;
92
97
  artifactElidedBytes?: number;
98
+ lines?: {
99
+ first: number;
100
+ last: number;
101
+ };
102
+ selectorBase?: string;
93
103
  };
94
104
  }
95
105
  /**
@@ -63,8 +63,20 @@ export interface LimitsMeta {
63
63
  /** `suggestion` is omitted when the tool is already at its hard cap, so no larger usable limit exists to advise. */
64
64
  resultLimit?: { reached: number; suggestion?: number };
65
65
  headLimit?: { reached: number; suggestion: number };
66
- /** `unit` may be absent in sessions persisted before it was recorded. */
67
- columnTruncated?: { maxColumn: number; unit?: "bytes" | "chars"; artifactId?: string; artifactElidedBytes?: number };
66
+ /**
67
+ * `unit` may be absent in sessions persisted before it was recorded. `lines` is
68
+ * the first-to-last range of cut lines, and `selectorBase` the target a
69
+ * `:raw:<line>-<line>` selector reads one of them whole from (an artifact's
70
+ * `artifact://<id>` stands in when absent).
71
+ */
72
+ columnTruncated?: {
73
+ maxColumn: number;
74
+ unit?: "bytes" | "chars";
75
+ artifactId?: string;
76
+ artifactElidedBytes?: number;
77
+ lines?: { first: number; last: number };
78
+ selectorBase?: string;
79
+ };
68
80
  }
69
81
 
70
82
  /**
@@ -329,7 +341,15 @@ export function formatOutputNotice(meta: OutputMeta | undefined): string {
329
341
  // a resumed legacy session renders "… 768 undefined" and stripOutputNotice
330
342
  // stops matching the persisted "… 768 chars" text.
331
343
  let columnNotice = `Some lines truncated to ${c.maxColumn} ${c.unit ?? "chars"}`;
332
- if (c.artifactId != null) {
344
+ // An artifact cut to a head/tail sample renumbers its lines, so only a complete one names a line.
345
+ const selectorBase =
346
+ c.selectorBase ?? (c.artifactId != null && !c.artifactElidedBytes ? `artifact://${c.artifactId}` : undefined);
347
+ if (c.lines && selectorBase !== undefined) {
348
+ // One line per selector: a span of cut lines can exceed what one read returns.
349
+ const { first, last } = c.lines;
350
+ columnNotice += `. Use ${selectorBase}:raw:${first}-${first} to read line ${first} whole`;
351
+ if (last > first) columnNotice += `; cut lines run to line ${last}, each read the same way`;
352
+ } else if (c.artifactId != null) {
333
353
  columnNotice += `. ${formatFullOutputReference(c.artifactId, c.artifactElidedBytes)}`;
334
354
  }
335
355
  parts.push(columnNotice);
package/src/tools/read.ts CHANGED
@@ -600,6 +600,11 @@ export const readToolRenderer = {
600
600
  let cachedWidth: number | undefined;
601
601
  let cachedExpanded: boolean | undefined;
602
602
  let cachedLines: string[] | undefined;
603
+ const dropCache = () => {
604
+ cachedWidth = undefined;
605
+ cachedExpanded = undefined;
606
+ cachedLines = undefined;
607
+ };
603
608
  return markFramedBlockComponent({
604
609
  render: (width: number) => {
605
610
  const expanded = options.expanded;
@@ -634,11 +639,8 @@ export const readToolRenderer = {
634
639
  cachedExpanded = expanded;
635
640
  return cachedLines;
636
641
  },
637
- invalidate: () => {
638
- cachedWidth = undefined;
639
- cachedExpanded = undefined;
640
- cachedLines = undefined;
641
- },
642
+ invalidate: dropCache,
643
+ releaseRenderCaches: dropCache,
642
644
  });
643
645
  },
644
646
  describeCall(args: ReadRenderArgs): NativeToolView {
@@ -25,6 +25,14 @@ export interface RenderResultOptions {
25
25
  * Streamed `xd://` previews stay queued until this is set.
26
26
  */
27
27
  executionStarted?: boolean;
28
+ /**
29
+ * Milliseconds since this call began executing (`tool_execution_start`),
30
+ * or its total once settled; undefined before it starts. Native describe
31
+ * hooks only.
32
+ */
33
+ elapsedMs?: number;
34
+ /** The call was abandoned without a final result (turn aborted, sealed). Native describe hooks only. */
35
+ cancelled?: boolean;
28
36
  }
29
37
  /** Render options for a result, plus the tool-specific context the transcript threads through. */
30
38
  export type RenderResultContextOptions = RenderResultOptions & {
@@ -39,6 +47,18 @@ export interface ToolRenderResult<TDetails = unknown> {
39
47
  details?: TDetails;
40
48
  isError?: boolean;
41
49
  }
50
+ /**
51
+ * A fence a tool call draws under its card, as assistant Markdown draws that
52
+ * fence. Any fence language may be named (`svg`, `mermaid`, `obj`); the
53
+ * transcript draws only those it draws as figures in assistant text.
54
+ */
55
+ export interface ToolFigure {
56
+ readonly lang: string;
57
+ /** The body so far. */
58
+ readonly source: string;
59
+ /** No more of the body arrives. */
60
+ readonly closed: boolean;
61
+ }
42
62
  /**
43
63
  * Per-renderer opt-in for a full viewport replay when the first result
44
64
  * replaces a painted pending-call render. A predicate receives the painted
@@ -73,6 +93,8 @@ export interface NativeToolHead {
73
93
  readonly targetKind?: TspToolProps["targetKind"];
74
94
  /** Language for `command` targets. */
75
95
  readonly lang?: string;
96
+ /** The raw command line the head's Copy command button copies (set implicitly by a `command` target). */
97
+ readonly command?: string;
76
98
  /** `file://` link for path targets. */
77
99
  readonly href?: string;
78
100
  /** Short facts after the target (`+8 −1`, `5 matches · 2 files`). */
@@ -99,10 +121,15 @@ export interface NativeToolView {
99
121
  readonly body?: readonly NativeChild[];
100
122
  /** Tone override; the frame otherwise derives it from the call status. */
101
123
  readonly tone?: TspTone;
102
- /** Body clamp while collapsed; `{tail}` keeps the end (terminal output). Defaults to the transcript's preview size. */
124
+ /**
125
+ * Body clamp while collapsed; `{tail}` keeps the end (terminal output). Defaults to the transcript's preview size.
126
+ * `children`: the body stays whole while collapsed and its own `ansi`/`code` previews clamp instead.
127
+ */
103
128
  readonly preview?: TspPreview | {
104
129
  readonly tail: number;
105
- } | "none";
130
+ } | "none" | "children";
131
+ /** Starts expanded whatever the transcript's expand state (the todo checklist); the user can still fold it. */
132
+ readonly open?: boolean;
106
133
  /** Render frameless (`frame:"inline"`): a head line plus a disclosed body. */
107
134
  readonly inline?: boolean;
108
135
  /** Head actions offered on hover (`copy`, `retry`). */
@@ -125,6 +152,14 @@ export interface ToolRenderer<TArgs = unknown, TDetails = unknown> {
125
152
  /** Semantic result view for TSP terminals; merged with the call view when {@link mergeCallAndResult}. */
126
153
  describeResult?(result: ToolRenderResult<TDetails>, options: RenderResultContextOptions, args?: TArgs): NativeToolView | undefined;
127
154
  mergeCallAndResult?: boolean;
155
+ /**
156
+ * The fence a call draws under its card (a `.svg` being written, as its
157
+ * image): growing while the args stream, closed once they are final; none
158
+ * after an error. `result` is undefined until the call has one. Rendered
159
+ * TUI only, and only fences the terminal draws as figures; native views put
160
+ * their drawing in their describe hooks.
161
+ */
162
+ figure?(args: TArgs, result: ToolRenderResult<TDetails> | undefined, options: RenderResultOptions): ToolFigure | undefined;
128
163
  /** Describes current activity without coupling a renderer to terminal layout. */
129
164
  activitySummary?(args: TArgs, context: ToolActivityContext): ToolActivitySummary;
130
165
  /** Render without background box, inline in the response flow */
@@ -26,6 +26,14 @@ export interface RenderResultOptions {
26
26
  * Streamed `xd://` previews stay queued until this is set.
27
27
  */
28
28
  executionStarted?: boolean;
29
+ /**
30
+ * Milliseconds since this call began executing (`tool_execution_start`),
31
+ * or its total once settled; undefined before it starts. Native describe
32
+ * hooks only.
33
+ */
34
+ elapsedMs?: number;
35
+ /** The call was abandoned without a final result (turn aborted, sealed). Native describe hooks only. */
36
+ cancelled?: boolean;
29
37
  }
30
38
 
31
39
  /** Render options for a result, plus the tool-specific context the transcript threads through. */
@@ -38,6 +46,19 @@ export interface ToolRenderResult<TDetails = unknown> {
38
46
  isError?: boolean;
39
47
  }
40
48
 
49
+ /**
50
+ * A fence a tool call draws under its card, as assistant Markdown draws that
51
+ * fence. Any fence language may be named (`svg`, `mermaid`, `obj`); the
52
+ * transcript draws only those it draws as figures in assistant text.
53
+ */
54
+ export interface ToolFigure {
55
+ readonly lang: string;
56
+ /** The body so far. */
57
+ readonly source: string;
58
+ /** No more of the body arrives. */
59
+ readonly closed: boolean;
60
+ }
61
+
41
62
  /**
42
63
  * Per-renderer opt-in for a full viewport replay when the first result
43
64
  * replaces a painted pending-call render. A predicate receives the painted
@@ -77,6 +98,8 @@ export interface NativeToolHead {
77
98
  readonly targetKind?: TspToolProps["targetKind"];
78
99
  /** Language for `command` targets. */
79
100
  readonly lang?: string;
101
+ /** The raw command line the head's Copy command button copies (set implicitly by a `command` target). */
102
+ readonly command?: string;
80
103
  /** `file://` link for path targets. */
81
104
  readonly href?: string;
82
105
  /** Short facts after the target (`+8 −1`, `5 matches · 2 files`). */
@@ -104,8 +127,13 @@ export interface NativeToolView {
104
127
  readonly body?: readonly NativeChild[];
105
128
  /** Tone override; the frame otherwise derives it from the call status. */
106
129
  readonly tone?: TspTone;
107
- /** Body clamp while collapsed; `{tail}` keeps the end (terminal output). Defaults to the transcript's preview size. */
108
- readonly preview?: TspPreview | { readonly tail: number } | "none";
130
+ /**
131
+ * Body clamp while collapsed; `{tail}` keeps the end (terminal output). Defaults to the transcript's preview size.
132
+ * `children`: the body stays whole while collapsed and its own `ansi`/`code` previews clamp instead.
133
+ */
134
+ readonly preview?: TspPreview | { readonly tail: number } | "none" | "children";
135
+ /** Starts expanded whatever the transcript's expand state (the todo checklist); the user can still fold it. */
136
+ readonly open?: boolean;
109
137
  /** Render frameless (`frame:"inline"`): a head line plus a disclosed body. */
110
138
  readonly inline?: boolean;
111
139
  /** Head actions offered on hover (`copy`, `retry`). */
@@ -138,6 +166,18 @@ export interface ToolRenderer<TArgs = unknown, TDetails = unknown> {
138
166
  args?: TArgs,
139
167
  ): NativeToolView | undefined;
140
168
  mergeCallAndResult?: boolean;
169
+ /**
170
+ * The fence a call draws under its card (a `.svg` being written, as its
171
+ * image): growing while the args stream, closed once they are final; none
172
+ * after an error. `result` is undefined until the call has one. Rendered
173
+ * TUI only, and only fences the terminal draws as figures; native views put
174
+ * their drawing in their describe hooks.
175
+ */
176
+ figure?(
177
+ args: TArgs,
178
+ result: ToolRenderResult<TDetails> | undefined,
179
+ options: RenderResultOptions,
180
+ ): ToolFigure | undefined;
141
181
  /** Describes current activity without coupling a renderer to terminal layout. */
142
182
  activitySummary?(args: TArgs, context: ToolActivityContext): ToolActivitySummary;
143
183
  /** Render without background box, inline in the response flow */