@oh-my-pi/pi-tui 18.4.2 → 18.4.4

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 (463) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/thinking-display.d.ts +2 -0
  22. package/dist/types/chat/todo-reminder.d.ts +4 -1
  23. package/dist/types/chat/tool-execution.d.ts +25 -1
  24. package/dist/types/chat/transcript-actions.d.ts +37 -0
  25. package/dist/types/chat/transcript-entry.d.ts +3 -1
  26. package/dist/types/chat/user-message.d.ts +25 -0
  27. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  28. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  29. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  30. package/dist/types/chrome/diff.d.ts +12 -0
  31. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  32. package/dist/types/chrome/format.d.ts +6 -0
  33. package/dist/types/chrome/message-divider.d.ts +13 -0
  34. package/dist/types/chrome/message-frame.d.ts +10 -0
  35. package/dist/types/chrome/message-notice.d.ts +23 -0
  36. package/dist/types/chrome/overlay-box.d.ts +11 -2
  37. package/dist/types/chrome/qrcode.d.ts +7 -0
  38. package/dist/types/chrome/segment-track.d.ts +7 -0
  39. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  40. package/dist/types/chrome/status-notice.d.ts +14 -0
  41. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  42. package/dist/types/chrome/tool-activity.d.ts +3 -0
  43. package/dist/types/chrome/transcript-container.d.ts +13 -0
  44. package/dist/types/chrome/transcript-status.d.ts +15 -3
  45. package/dist/types/components/box.d.ts +7 -0
  46. package/dist/types/components/disclosure.d.ts +9 -0
  47. package/dist/types/components/editor.d.ts +42 -0
  48. package/dist/types/components/form.d.ts +14 -0
  49. package/dist/types/components/image.d.ts +7 -0
  50. package/dist/types/components/input.d.ts +20 -1
  51. package/dist/types/components/key-value-list.d.ts +3 -0
  52. package/dist/types/components/layout/geometry.d.ts +16 -0
  53. package/dist/types/components/layout/row.d.ts +7 -0
  54. package/dist/types/components/layout/split-pane.d.ts +8 -0
  55. package/dist/types/components/layout/stack.d.ts +6 -0
  56. package/dist/types/components/loader.d.ts +48 -0
  57. package/dist/types/components/markdown.d.ts +8 -0
  58. package/dist/types/components/metric.d.ts +7 -0
  59. package/dist/types/components/progress-bar.d.ts +3 -0
  60. package/dist/types/components/scroll-view.d.ts +6 -0
  61. package/dist/types/components/section.d.ts +3 -0
  62. package/dist/types/components/select-list.d.ts +33 -0
  63. package/dist/types/components/settings-list.d.ts +68 -0
  64. package/dist/types/components/spacer.d.ts +4 -0
  65. package/dist/types/components/tab-bar.d.ts +9 -0
  66. package/dist/types/components/table.d.ts +7 -0
  67. package/dist/types/components/text.d.ts +6 -0
  68. package/dist/types/components/tree-view.d.ts +9 -0
  69. package/dist/types/components/truncated-text.d.ts +3 -0
  70. package/dist/types/components/wizard-step.d.ts +6 -0
  71. package/dist/types/hotkeys-markdown.d.ts +24 -0
  72. package/dist/types/index.d.ts +5 -0
  73. package/dist/types/latex-block.d.ts +7 -0
  74. package/dist/types/loop-watchdog.d.ts +9 -0
  75. package/dist/types/native/apply.d.ts +39 -0
  76. package/dist/types/native/backend.d.ts +94 -0
  77. package/dist/types/native/blobs.d.ts +29 -0
  78. package/dist/types/native/describe.d.ts +56 -0
  79. package/dist/types/native/encode.d.ts +50 -0
  80. package/dist/types/native/icons.d.ts +18 -0
  81. package/dist/types/native/memo.d.ts +27 -0
  82. package/dist/types/native/node.d.ts +107 -0
  83. package/dist/types/native/overlay.d.ts +64 -0
  84. package/dist/types/native/picker.d.ts +124 -0
  85. package/dist/types/native/reconcile.d.ts +41 -0
  86. package/dist/types/native/settle.d.ts +11 -0
  87. package/dist/types/native/spans.d.ts +21 -0
  88. package/dist/types/native/state.d.ts +6 -0
  89. package/dist/types/native/tone.d.ts +24 -0
  90. package/dist/types/overlays/advisor-config.d.ts +20 -0
  91. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  92. package/dist/types/overlays/agent-hub.d.ts +10 -0
  93. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  94. package/dist/types/overlays/agents-hub.d.ts +8 -0
  95. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  96. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  97. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  98. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  99. package/dist/types/overlays/btw-panel.d.ts +3 -0
  100. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  101. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  102. package/dist/types/overlays/copy-selector.d.ts +38 -0
  103. package/dist/types/overlays/error-banner.d.ts +9 -0
  104. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  105. package/dist/types/overlays/history-search.d.ts +18 -0
  106. package/dist/types/overlays/hook-editor.d.ts +28 -1
  107. package/dist/types/overlays/hook-input.d.ts +3 -0
  108. package/dist/types/overlays/hook-selector.d.ts +10 -0
  109. package/dist/types/overlays/hub-frame.d.ts +18 -0
  110. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  111. package/dist/types/overlays/login-dialog.d.ts +10 -0
  112. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  113. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  114. package/dist/types/overlays/model-browser.d.ts +69 -0
  115. package/dist/types/overlays/model-hub.d.ts +7 -0
  116. package/dist/types/overlays/model-picker.d.ts +9 -1
  117. package/dist/types/overlays/move-overlay.d.ts +3 -0
  118. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  119. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  120. package/dist/types/overlays/pause-screen.d.ts +12 -0
  121. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  122. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  123. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  124. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  125. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  126. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  127. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  128. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  129. package/dist/types/overlays/session-selector.d.ts +64 -1
  130. package/dist/types/overlays/settings-defs.d.ts +14 -0
  131. package/dist/types/overlays/settings-selector.d.ts +21 -0
  132. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  133. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  134. package/dist/types/overlays/stats-notice.d.ts +8 -0
  135. package/dist/types/overlays/theme-selector.d.ts +11 -0
  136. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  137. package/dist/types/overlays/tree-selector.d.ts +60 -2
  138. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  139. package/dist/types/overlays/usage-row.d.ts +36 -2
  140. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  141. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  142. package/dist/types/prompt/composer.d.ts +18 -1
  143. package/dist/types/prompt/custom-editor.d.ts +48 -2
  144. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  145. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  146. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  147. package/dist/types/prompt/queued-messages.d.ts +23 -0
  148. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  149. package/dist/types/prompt/welcome.d.ts +16 -0
  150. package/dist/types/render/code-cell.d.ts +10 -0
  151. package/dist/types/render/file-list.d.ts +8 -0
  152. package/dist/types/render/hyperlink.d.ts +14 -0
  153. package/dist/types/render/output-block.d.ts +43 -0
  154. package/dist/types/render/output-pane.d.ts +17 -0
  155. package/dist/types/render/tool-card.d.ts +8 -0
  156. package/dist/types/render/tree-list.d.ts +8 -0
  157. package/dist/types/render/width-aware-text.d.ts +7 -0
  158. package/dist/types/setup/scenes/outro.d.ts +7 -0
  159. package/dist/types/setup/scenes/sign-in.d.ts +16 -9
  160. package/dist/types/setup/scenes/splash.d.ts +8 -0
  161. package/dist/types/setup/scenes/types.d.ts +6 -27
  162. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  163. package/dist/types/status-line/component.d.ts +49 -5
  164. package/dist/types/status-line/context-usage.d.ts +20 -0
  165. package/dist/types/status-line/footer.d.ts +8 -0
  166. package/dist/types/status-line/metrics.d.ts +1 -2
  167. package/dist/types/status-line/segments.d.ts +10 -1
  168. package/dist/types/status-line/types.d.ts +40 -1
  169. package/dist/types/stdin-buffer.d.ts +10 -7
  170. package/dist/types/terminal-capabilities.d.ts +1 -1
  171. package/dist/types/terminal.d.ts +36 -0
  172. package/dist/types/theme/defaults/index.d.ts +100 -0
  173. package/dist/types/theme/schema.d.ts +2 -0
  174. package/dist/types/theme/shimmer.d.ts +8 -0
  175. package/dist/types/theme/theme-class.d.ts +2 -0
  176. package/dist/types/theme/theme.d.ts +33 -1
  177. package/dist/types/tools/ask.d.ts +3 -0
  178. package/dist/types/tools/ast-edit.d.ts +10 -1
  179. package/dist/types/tools/ast-grep.d.ts +10 -1
  180. package/dist/types/tools/autoresearch.d.ts +21 -4
  181. package/dist/types/tools/bash.d.ts +30 -1
  182. package/dist/types/tools/cfg-render.d.ts +5 -1
  183. package/dist/types/tools/debug.d.ts +3 -1
  184. package/dist/types/tools/default-renderer.d.ts +6 -1
  185. package/dist/types/tools/edit.d.ts +14 -1
  186. package/dist/types/tools/eval.d.ts +12 -1
  187. package/dist/types/tools/fetch.d.ts +16 -1
  188. package/dist/types/tools/find.d.ts +15 -1
  189. package/dist/types/tools/github.d.ts +3 -0
  190. package/dist/types/tools/glob.d.ts +14 -1
  191. package/dist/types/tools/goal.d.ts +3 -1
  192. package/dist/types/tools/grep.d.ts +14 -1
  193. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  194. package/dist/types/tools/json-tree.d.ts +20 -0
  195. package/dist/types/tools/lsp.d.ts +3 -10
  196. package/dist/types/tools/mcp.d.ts +16 -1
  197. package/dist/types/tools/memory.d.ts +26 -0
  198. package/dist/types/tools/native-view.d.ts +90 -0
  199. package/dist/types/tools/proc-render.d.ts +7 -1
  200. package/dist/types/tools/read.d.ts +23 -1
  201. package/dist/types/tools/renderer.d.ts +57 -0
  202. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  203. package/dist/types/tools/resolve.d.ts +14 -1
  204. package/dist/types/tools/task.d.ts +3 -0
  205. package/dist/types/tools/think.d.ts +3 -0
  206. package/dist/types/tools/todo.d.ts +12 -0
  207. package/dist/types/tools/vibe.d.ts +3 -1
  208. package/dist/types/tools/wait.d.ts +3 -0
  209. package/dist/types/tools/web-search-types.d.ts +90 -0
  210. package/dist/types/tools/web-search.d.ts +9 -168
  211. package/dist/types/tools/write.d.ts +14 -1
  212. package/dist/types/tools/xdev.d.ts +13 -0
  213. package/dist/types/tui.d.ts +49 -0
  214. package/package.json +9 -9
  215. package/src/apps/autoresearch-dashboard.ts +362 -4
  216. package/src/apps/cleanse-board.ts +219 -1
  217. package/src/apps/cleanse-picker.ts +9 -1
  218. package/src/apps/debug/log-viewer.ts +15 -15
  219. package/src/apps/debug/protocol-probe.ts +2 -20
  220. package/src/apps/git/avatar.ts +2 -22
  221. package/src/apps/git/git-tui.ts +406 -59
  222. package/src/apps/git/help.ts +130 -0
  223. package/src/apps/git/sidebar.ts +491 -0
  224. package/src/apps/live-visualizer.ts +65 -0
  225. package/src/apps/ps-top.ts +448 -17
  226. package/src/apps/session-picker.ts +1 -0
  227. package/src/apps/setup-model-picker.ts +5 -1
  228. package/src/apps/standalone-picker.ts +55 -1
  229. package/src/autocomplete.ts +44 -1
  230. package/src/chat/advisor-message.ts +72 -3
  231. package/src/chat/assistant-message.ts +538 -51
  232. package/src/chat/background-tan-message.ts +4 -4
  233. package/src/chat/bash-execution.ts +81 -0
  234. package/src/chat/cache-invalidation-marker.ts +8 -0
  235. package/src/chat/chat-transcript-builder.ts +37 -6
  236. package/src/chat/collab-prompt-message.ts +15 -3
  237. package/src/chat/compaction-summary-message.ts +74 -1
  238. package/src/chat/custom-message.ts +3 -1
  239. package/src/chat/eval-execution.ts +57 -0
  240. package/src/chat/execution-shared.ts +153 -1
  241. package/src/chat/hook-message.ts +1 -0
  242. package/src/chat/late-diagnostics-message.ts +85 -2
  243. package/src/chat/read-tool-group.ts +341 -1
  244. package/src/chat/served-model-marker.ts +8 -0
  245. package/src/chat/skill-message.ts +63 -0
  246. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  247. package/src/chat/thinking-display.ts +73 -11
  248. package/src/chat/todo-reminder.ts +34 -4
  249. package/src/chat/tool-execution.ts +385 -10
  250. package/src/chat/transcript-actions.ts +38 -0
  251. package/src/chat/transcript-browser.ts +2 -9
  252. package/src/chat/transcript-entry.ts +10 -1
  253. package/src/chat/transcript-render-helpers.ts +16 -15
  254. package/src/chat/ttsr-notification.ts +38 -1
  255. package/src/chat/user-message.ts +167 -4
  256. package/src/chrome/collab-qrcode.ts +27 -2
  257. package/src/chrome/context-thresholds.ts +34 -0
  258. package/src/chrome/countdown-timer.ts +24 -1
  259. package/src/chrome/diff.ts +60 -1
  260. package/src/chrome/dynamic-border.ts +7 -0
  261. package/src/chrome/format.ts +12 -0
  262. package/src/chrome/message-divider.ts +33 -1
  263. package/src/chrome/message-frame.ts +53 -10
  264. package/src/chrome/message-notice.ts +71 -0
  265. package/src/chrome/overlay-box.ts +45 -2
  266. package/src/chrome/qrcode.ts +28 -0
  267. package/src/chrome/segment-track.ts +26 -0
  268. package/src/chrome/selector-helpers.ts +1 -17
  269. package/src/chrome/status-notice.ts +46 -0
  270. package/src/chrome/streaming-panel.ts +25 -1
  271. package/src/chrome/tool-activity.ts +20 -0
  272. package/src/chrome/transcript-container.ts +139 -17
  273. package/src/chrome/transcript-status.ts +40 -11
  274. package/src/components/box.ts +51 -0
  275. package/src/components/disclosure.ts +33 -0
  276. package/src/components/editor.ts +239 -36
  277. package/src/components/form.ts +93 -9
  278. package/src/components/image.ts +30 -0
  279. package/src/components/input.ts +77 -26
  280. package/src/components/key-value-list.ts +18 -0
  281. package/src/components/layout/geometry.ts +44 -0
  282. package/src/components/layout/row.ts +30 -0
  283. package/src/components/layout/split-pane.ts +65 -0
  284. package/src/components/layout/stack.ts +27 -0
  285. package/src/components/loader.ts +183 -1
  286. package/src/components/markdown.ts +131 -56
  287. package/src/components/metric.ts +39 -0
  288. package/src/components/progress-bar.ts +28 -0
  289. package/src/components/scroll-view.ts +23 -0
  290. package/src/components/section.ts +16 -0
  291. package/src/components/select-list.ts +174 -25
  292. package/src/components/settings-list.ts +264 -7
  293. package/src/components/spacer.ts +17 -0
  294. package/src/components/tab-bar.ts +42 -0
  295. package/src/components/table.ts +34 -0
  296. package/src/components/text.ts +23 -0
  297. package/src/components/tree-view.ts +65 -0
  298. package/src/components/truncated-text.ts +14 -0
  299. package/src/components/wizard-step.ts +18 -9
  300. package/src/debug-server.ts +14 -7
  301. package/src/hotkeys-markdown.ts +251 -69
  302. package/src/index.ts +6 -0
  303. package/src/latex-block.ts +11 -0
  304. package/src/loop-watchdog.ts +38 -10
  305. package/src/native/apply.ts +275 -0
  306. package/src/native/backend.ts +682 -0
  307. package/src/native/blobs.ts +70 -0
  308. package/src/native/describe.ts +124 -0
  309. package/src/native/encode.ts +233 -0
  310. package/src/native/icons.ts +184 -0
  311. package/src/native/memo.ts +64 -0
  312. package/src/native/node.ts +112 -0
  313. package/src/native/overlay.ts +131 -0
  314. package/src/native/picker.ts +333 -0
  315. package/src/native/reconcile.ts +1063 -0
  316. package/src/native/settle.ts +23 -0
  317. package/src/native/spans.ts +308 -0
  318. package/src/native/state.ts +28 -0
  319. package/src/native/tone.ts +79 -0
  320. package/src/overlays/advisor-config.ts +420 -0
  321. package/src/overlays/agent-hub-renderer.ts +155 -18
  322. package/src/overlays/agent-hub.ts +1037 -44
  323. package/src/overlays/agent-transcript-viewer.ts +129 -0
  324. package/src/overlays/agents-hub.ts +688 -53
  325. package/src/overlays/annotation-overlay.ts +391 -52
  326. package/src/overlays/ask-dialog.ts +435 -24
  327. package/src/overlays/bordered-loader.ts +17 -0
  328. package/src/overlays/btw-history-panel.ts +182 -0
  329. package/src/overlays/btw-panel.ts +68 -2
  330. package/src/overlays/cleanse-panel.ts +105 -15
  331. package/src/overlays/codex-reset-fireworks.ts +65 -5
  332. package/src/overlays/composer-shape-preview.ts +26 -1
  333. package/src/overlays/copy-selector.ts +545 -23
  334. package/src/overlays/error-banner.ts +44 -0
  335. package/src/overlays/extensions/extension-list.ts +23 -17
  336. package/src/overlays/history-search.ts +175 -33
  337. package/src/overlays/hook-editor.ts +113 -6
  338. package/src/overlays/hook-input.ts +22 -1
  339. package/src/overlays/hook-selector.ts +311 -22
  340. package/src/overlays/hub-frame.ts +80 -0
  341. package/src/overlays/jobs-panel.ts +112 -0
  342. package/src/overlays/login-dialog.ts +183 -1
  343. package/src/overlays/logout-account-selector.ts +102 -4
  344. package/src/overlays/mcp-add-wizard.ts +232 -53
  345. package/src/overlays/model-browser.ts +681 -22
  346. package/src/overlays/model-hub.ts +1402 -78
  347. package/src/overlays/model-picker.ts +184 -7
  348. package/src/overlays/move-overlay.ts +45 -0
  349. package/src/overlays/oauth-selector.ts +227 -23
  350. package/src/overlays/omfg-panel.ts +86 -2
  351. package/src/overlays/pause-screen.ts +55 -4
  352. package/src/overlays/plan-review-overlay.ts +352 -0
  353. package/src/overlays/plan-save-overlay.ts +45 -0
  354. package/src/overlays/plugin-selector.ts +21 -1
  355. package/src/overlays/plugin-settings.ts +195 -5
  356. package/src/overlays/queue-mode-selector.ts +19 -1
  357. package/src/overlays/reset-usage-selector.ts +110 -40
  358. package/src/overlays/rewind-selector.ts +458 -44
  359. package/src/overlays/session-account-selector.ts +1 -1
  360. package/src/overlays/session-info-overlay.ts +137 -1
  361. package/src/overlays/session-selector.ts +807 -46
  362. package/src/overlays/settings-defs.ts +41 -0
  363. package/src/overlays/settings-selector.ts +585 -33
  364. package/src/overlays/show-images-selector.ts +18 -1
  365. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  366. package/src/overlays/stats-notice.ts +31 -0
  367. package/src/overlays/theme-selector.ts +31 -1
  368. package/src/overlays/thinking-selector.ts +22 -1
  369. package/src/overlays/tree-selector.ts +570 -110
  370. package/src/overlays/usage-dashboard.ts +587 -12
  371. package/src/overlays/usage-row.ts +161 -25
  372. package/src/prompt/attachment-chips.ts +60 -0
  373. package/src/prompt/composer-attachments.ts +31 -9
  374. package/src/prompt/composer.ts +55 -2
  375. package/src/prompt/custom-editor.ts +302 -4
  376. package/src/prompt/editor-top-gap.ts +8 -0
  377. package/src/prompt/macos-spelling.ts +24 -9
  378. package/src/prompt/magic-keywords.ts +22 -1
  379. package/src/prompt/model-mention-autocomplete.ts +1 -0
  380. package/src/prompt/queued-messages.ts +101 -0
  381. package/src/prompt/skill-tokens.ts +5 -3
  382. package/src/prompt/welcome.ts +213 -6
  383. package/src/render/code-cell.ts +103 -2
  384. package/src/render/file-list.ts +29 -0
  385. package/src/render/hyperlink.ts +32 -0
  386. package/src/render/output-block.ts +110 -0
  387. package/src/render/output-pane.ts +37 -0
  388. package/src/render/tool-card.ts +137 -0
  389. package/src/render/tree-list.ts +39 -0
  390. package/src/render/width-aware-text.ts +20 -0
  391. package/src/setup/scenes/composer.ts +29 -0
  392. package/src/setup/scenes/glyph.ts +15 -0
  393. package/src/setup/scenes/model.ts +36 -8
  394. package/src/setup/scenes/outro.ts +28 -1
  395. package/src/setup/scenes/sign-in.ts +138 -24
  396. package/src/setup/scenes/splash.ts +28 -1
  397. package/src/setup/scenes/theme.ts +88 -3
  398. package/src/setup/scenes/types.ts +7 -28
  399. package/src/setup/startup-splash.ts +46 -9
  400. package/src/setup/wizard-overlay.ts +140 -23
  401. package/src/setup/wizard.ts +1 -1
  402. package/src/status-line/component.ts +414 -18
  403. package/src/status-line/context-usage.ts +279 -50
  404. package/src/status-line/footer.ts +94 -1
  405. package/src/status-line/metrics.ts +3 -6
  406. package/src/status-line/segments.ts +463 -97
  407. package/src/status-line/separators.ts +3 -16
  408. package/src/status-line/types.ts +43 -1
  409. package/src/stdin-buffer.ts +27 -21
  410. package/src/terminal-capabilities.ts +7 -0
  411. package/src/terminal.ts +153 -11
  412. package/src/theme/defaults/dark-neon-noir.json +100 -0
  413. package/src/theme/defaults/index.ts +2 -0
  414. package/src/theme/defaults/light-prism.json +5 -5
  415. package/src/theme/defaults/onyx.json +62 -62
  416. package/src/theme/mermaid-cache.ts +10 -1
  417. package/src/theme/schema.ts +15 -0
  418. package/src/theme/shimmer.ts +33 -0
  419. package/src/theme/theme-class.ts +24 -19
  420. package/src/theme/theme.ts +111 -9
  421. package/src/theme/tui-adapters.ts +14 -1
  422. package/src/tools/ask.ts +157 -1
  423. package/src/tools/ast-edit.ts +174 -1
  424. package/src/tools/ast-grep.ts +82 -2
  425. package/src/tools/autoresearch.ts +149 -1
  426. package/src/tools/bash.ts +233 -62
  427. package/src/tools/cfg-render.ts +66 -1
  428. package/src/tools/debug.ts +78 -1
  429. package/src/tools/default-renderer.ts +35 -1
  430. package/src/tools/edit.ts +357 -31
  431. package/src/tools/eval.ts +257 -1
  432. package/src/tools/fetch.ts +102 -3
  433. package/src/tools/find.ts +127 -1
  434. package/src/tools/github.ts +119 -10
  435. package/src/tools/glob.ts +116 -1
  436. package/src/tools/goal.ts +56 -1
  437. package/src/tools/grep.ts +83 -2
  438. package/src/tools/grouped-file-output.ts +205 -0
  439. package/src/tools/json-tree.ts +116 -1
  440. package/src/tools/lsp.ts +318 -1
  441. package/src/tools/mcp.ts +80 -1
  442. package/src/tools/memory.ts +93 -11
  443. package/src/tools/native-view.ts +292 -0
  444. package/src/tools/proc-render.ts +221 -1
  445. package/src/tools/read.ts +199 -4
  446. package/src/tools/renderer.ts +61 -0
  447. package/src/tools/report-tool-issue.ts +7 -1
  448. package/src/tools/resolve.ts +63 -1
  449. package/src/tools/streaming-output.ts +79 -16
  450. package/src/tools/task.ts +406 -4
  451. package/src/tools/think.ts +19 -1
  452. package/src/tools/todo.ts +89 -0
  453. package/src/tools/vibe.ts +230 -2
  454. package/src/tools/wait.ts +181 -1
  455. package/src/tools/web-search-types.ts +99 -0
  456. package/src/tools/web-search.ts +111 -170
  457. package/src/tools/write.ts +144 -5
  458. package/src/tools/xdev.ts +64 -1
  459. package/src/tui.ts +295 -4
  460. package/dist/types/setup/scenes/providers.d.ts +0 -3
  461. package/dist/types/setup/scenes/web-search.d.ts +0 -22
  462. package/src/setup/scenes/providers.ts +0 -110
  463. package/src/setup/scenes/web-search.ts +0 -165
package/src/tools/read.ts CHANGED
@@ -6,20 +6,38 @@ import * as path from "node:path";
6
6
  import { parseArchivePathCandidates } from "@oh-my-pi/pi-utils/ar";
7
7
  import type { Component } from "../tui";
8
8
  import { Text } from "../components/text";
9
- import type { RenderResultOptions, ToolActivityContext, ToolActivitySummary, ToolRenderer } from "./renderer";
9
+ import type {
10
+ NativeToolHead,
11
+ NativeToolView,
12
+ RenderResultOptions,
13
+ ToolActivityContext,
14
+ ToolActivitySummary,
15
+ ToolRenderer,
16
+ } from "./renderer";
17
+ import { ansi, compact, md } from "../native/describe";
18
+ import { base64ImageNode } from "../native/blobs";
19
+ import type { NativeChild, NativeNode } from "../native/node";
20
+ import { fileHref, footnoteText, inlineErrorView, resultText } from "./native-view";
21
+ import { numberedCode } from "./grouped-file-output";
10
22
  import { getLanguageFromPath } from "../lang-from-path";
11
23
  import type { Theme } from "../theme/theme";
12
24
  import { fileHyperlink, renderCodeCell, renderMarkdownCell, renderStatusLine } from "../render";
13
25
  import { markFramedBlockComponent } from "../render/output-block";
14
26
  import { framedToolCard } from "../render/tool-card";
15
- import { type ReadUrlToolDetails, renderReadUrlCall, renderReadUrlResult } from "./fetch";
27
+ import {
28
+ describeReadUrlCall,
29
+ describeReadUrlResult,
30
+ type ReadUrlToolDetails,
31
+ renderReadUrlCall,
32
+ renderReadUrlResult,
33
+ } from "./fetch";
16
34
  import { formatFullOutputReference, formatStyledTruncationWarning, stripOutputNotice } from "./output-meta";
17
35
  import { formatBytes, sanitizeDisplayLines, shortenPath, wrapBrackets } from "../render/render-utils";
18
36
 
19
37
  import type { OutputMeta } from "./output-meta";
20
38
  import type { TruncationResult } from "./streaming-output";
21
- import { renderProcRead, type ProcReadDetails } from "./proc-render";
22
- import { renderCfgRead, type CfgReadDetails } from "./cfg-render";
39
+ import { describeProcRead, renderProcRead, type ProcReadDetails } from "./proc-render";
40
+ import { describeCfgRead, renderCfgRead, type CfgReadDetails } from "./cfg-render";
23
41
  import type { CardToolResult } from "./result-card";
24
42
  import { type InternalUrlSchemeSpec, internalUrlSchemeSpec, splitUrlScheme } from "./url-scheme-host";
25
43
 
@@ -313,6 +331,102 @@ function formatReadPathLink(
313
331
  return `${linkedPath}${selectorSuffix}`;
314
332
  }
315
333
 
334
+ function readRawPath(args: ReadRenderArgs | undefined): string {
335
+ return typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
336
+ }
337
+
338
+ /**
339
+ * Native read head (§7.3 read): `Read path:13-36` with the path as the
340
+ * target (shortened, plus the selector / legacy offset range), a `file://`
341
+ * link when the fs path is known, and a suffix correction as meta.
342
+ */
343
+ function readNativeHead(rawPath: string, args: ReadRenderArgs | undefined, details?: ReadToolDetails): NativeToolHead {
344
+ const split = splitReadRenderPath(rawPath);
345
+ const suffix = details?.suffixResolution;
346
+ let target = suffix ? shortenPath(suffix.to) : shortenPath(split.path || rawPath);
347
+ if (split.sel) target += `:${split.sel}`;
348
+ if (args?.offset !== undefined || args?.limit !== undefined) {
349
+ const startLine = args.offset ?? 1;
350
+ const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
351
+ target += `:${startLine}${endLine ? `-${endLine}` : ""}`;
352
+ }
353
+ const meta: string[] = [];
354
+ if (suffix) meta.push(`corrected from ${shortenPath(suffix.from)}`);
355
+ if (details?.summary) {
356
+ const n = details.summary.elidedSpans;
357
+ meta.push(`summary: ${n} elided span${n === 1 ? "" : "s"}`);
358
+ }
359
+ const conflicts = details?.conflictCount ?? 0;
360
+ return {
361
+ title: "Read",
362
+ target: target || "…",
363
+ targetKind: "path",
364
+ href: fileHref(details?.resolvedPath ?? details?.displayTarget ?? readSourceFsPath(details)),
365
+ meta,
366
+ badges:
367
+ conflicts > 0 ? [{ text: `${conflicts} conflict${conflicts === 1 ? "" : "s"}`, tone: "warning" }] : undefined,
368
+ };
369
+ }
370
+
371
+ /**
372
+ * The first line a read shows: the selector's (or legacy `offset`'s) range
373
+ * start, so legacy results without `displayContent` still number from there.
374
+ */
375
+ function readRangeStart(rawPath: string, args: ReadRenderArgs | undefined): number {
376
+ const sel = splitReadRenderPath(rawPath).sel;
377
+ return firstReadSelectorLine(sel) ?? args?.offset ?? 1;
378
+ }
379
+
380
+ /**
381
+ * Read content as numbered `code` (no path header: the head names the file).
382
+ * Numbers come from `lineNumbers` (`displayContent`'s per-line numbers,
383
+ * `null` for an elided block; runs split with `…` between) when they cover
384
+ * every line, else count up from `start`.
385
+ */
386
+ export function readContentCode(
387
+ content: string,
388
+ start: number,
389
+ numbers: readonly (number | null)[] | undefined,
390
+ lang: string | undefined,
391
+ ): NativeNode[] {
392
+ const lines = content.split("\n");
393
+ return numberedCode(
394
+ lines.map((text, i) => ({ n: numbers && numbers.length === lines.length ? numbers[i]! : start + i, text })),
395
+ { lang },
396
+ );
397
+ }
398
+
399
+ /** An image content block as the read tool emits it (base64 data + mime type). */
400
+ interface ReadImageBlock {
401
+ type: "image";
402
+ data: string;
403
+ mimeType: string;
404
+ }
405
+
406
+ function isImageBlock(block: { type: string }): block is ReadImageBlock {
407
+ return (
408
+ block.type === "image" &&
409
+ typeof (block as Partial<ReadImageBlock>).data === "string" &&
410
+ typeof (block as Partial<ReadImageBlock>).mimeType === "string"
411
+ );
412
+ }
413
+
414
+ /** Cached `image` node on its content block, so decoding and hashing happen once per block. */
415
+ const kReadImageNode = Symbol("read.imageNode");
416
+
417
+ interface TaggedReadImageBlock extends ReadImageBlock {
418
+ [kReadImageNode]?: NativeNode;
419
+ }
420
+
421
+ /** Native `image` node for a read image: registered blob, alt = displayed path, source pixel size when parseable. */
422
+ function describeReadImage(block: TaggedReadImageBlock, alt: string): NativeNode {
423
+ const cached = block[kReadImageNode];
424
+ if (cached) return cached;
425
+ const described = base64ImageNode(block.data, block.mimeType, { alt: alt || "image", role: "omp.tool.read.image" });
426
+ block[kReadImageNode] = described;
427
+ return described;
428
+ }
429
+
316
430
  /** Render file, image, and URL reads in the transcript. */
317
431
  export const readToolRenderer = {
318
432
  activitySummary(args: unknown, _context: ToolActivityContext): ToolActivitySummary {
@@ -527,6 +641,87 @@ export const readToolRenderer = {
527
641
  },
528
642
  });
529
643
  },
644
+ describeCall(args: ReadRenderArgs): NativeToolView {
645
+ const rawPath = readRawPath(args);
646
+ const routed = readUrlCard(rawPath);
647
+ if (routed?.card.detailsKey === "proc") return describeProcRead(routed.target, undefined, undefined);
648
+ if (routed) return describeCfgRead(splitInternalUrlSel(rawPath).path, undefined, undefined);
649
+ if (isReadableUrlPath(rawPath)) return describeReadUrlCall({ path: rawPath, raw: args.raw });
650
+ return { tool: readNativeHead(rawPath, args), inline: true };
651
+ },
652
+
653
+ /**
654
+ * Inline (§7.3 read): the head names the file and range; the content
655
+ * (numbered from the range start, no path header) is the disclosed body.
656
+ * An error shows its message in the head, never behind the disclosure.
657
+ */
658
+ describeResult(
659
+ result: { content: Array<{ type: string; text?: string }>; details?: ReadToolDetails; isError?: boolean },
660
+ _options: RenderResultOptions,
661
+ args?: ReadRenderArgs,
662
+ ): NativeToolView {
663
+ const details = result.details;
664
+ const rawPath = readRawPath(args);
665
+ const routed = readUrlCard(rawPath, details);
666
+ if (routed?.card.detailsKey === "proc") return describeProcRead(routed.target, result, details?.proc);
667
+ if (routed) return describeCfgRead(splitInternalUrlSel(rawPath).path, result, details?.cfg);
668
+ if (details?.kind === "url" || isReadableUrlPath(rawPath)) {
669
+ return describeReadUrlResult({
670
+ content: result.content,
671
+ details: result.details as ReadUrlToolDetails | undefined,
672
+ isError: result.isError,
673
+ });
674
+ }
675
+
676
+ if (result.isError) {
677
+ const message = (resultText(result) || "Unknown error").replace(/^Error:\s*/, "");
678
+ return inlineErrorView(readNativeHead(rawPath, args, details), message);
679
+ }
680
+
681
+ const suffix = details?.suffixResolution;
682
+ const head = readNativeHead(rawPath, args, details);
683
+ const truncation = details?.meta?.truncation;
684
+ const fallback = details?.truncation;
685
+ const firstLineTooLong = truncation !== undefined && fallback?.firstLineExceedsLimit === true;
686
+ const foot = footnoteText(
687
+ compact([
688
+ details?.resolvedPath !== undefined && `resolved ${shortenPath(details.resolvedPath)}`,
689
+ firstLineTooLong &&
690
+ `First line exceeds ${formatBytes(fallback.outputBytes ?? fallback.totalBytes)} limit${truncation.artifactId ? `. ${formatFullOutputReference(truncation.artifactId)}` : ""}`,
691
+ ]),
692
+ firstLineTooLong ? { artifactError: details?.meta?.artifactError } : details?.meta,
693
+ );
694
+
695
+ const contentText = details?.displayContent?.text ?? stripOutputNotice(resultText(result), details?.meta);
696
+ const imageBlock = result.content?.find(isImageBlock);
697
+ if (imageBlock) {
698
+ const detailText = contentText.trim();
699
+ return {
700
+ tool: head,
701
+ tone: suffix ? "warning" : undefined,
702
+ inline: true,
703
+ body: compact<NativeChild>([
704
+ describeReadImage(imageBlock, shortenPath(suffix?.to ?? splitReadRenderPath(rawPath).path)),
705
+ detailText.length > 0 && ansi(detailText),
706
+ foot,
707
+ ]),
708
+ };
709
+ }
710
+
711
+ const renderPath = splitReadRenderPath(rawPath);
712
+ const rawRequested =
713
+ args?.raw === true || renderPath.sel?.split(":").some(chunk => chunk.toLowerCase() === "raw") === true;
714
+ const content: NativeChild[] =
715
+ details?.contentType === "text/markdown" && !rawRequested
716
+ ? [md(contentText)]
717
+ : readContentCode(
718
+ contentText,
719
+ details?.displayContent?.startLine ?? readRangeStart(rawPath, args),
720
+ details?.displayContent?.lineNumbers,
721
+ getLanguageFromPath(renderPath.path),
722
+ );
723
+ return { tool: head, inline: true, body: compact<NativeChild>([...content, foot]) };
724
+ },
530
725
  mergeCallAndResult: true,
531
726
  } satisfies ToolRenderer<ReadRenderArgs, ReadToolDetails>;
532
727
 
@@ -3,6 +3,8 @@
3
3
  * transcript components. Built-in renderers live beside this file; the
4
4
  * coding-agent tools implement the matching `*Details` payloads.
5
5
  */
6
+ import type { TspPreview, TspText, TspTone, TspToolProps } from "@oh-my-pi/pi-wire";
7
+ import type { NativeChild } from "../native/node";
6
8
  import type { Component } from "../tui";
7
9
  import type { Theme } from "../theme/theme";
8
10
 
@@ -62,6 +64,54 @@ export interface ToolActivityContext {
62
64
  readonly renderContext?: Record<string, unknown>;
63
65
  }
64
66
 
67
+ /**
68
+ * The data head of a tool call (NATIVE_REDESIGN §7.2): what the terminal
69
+ * draws in the `tool` kind's head. Each fact appears once — a target named
70
+ * here is never repeated in the body.
71
+ */
72
+ export interface NativeToolHead {
73
+ /** The verb ("Bash", "Edit"); defaults to the tool's label. */
74
+ readonly title?: TspText;
75
+ /** The primary argument, shown once: command, path, pattern, query. */
76
+ readonly target?: TspText;
77
+ readonly targetKind?: TspToolProps["targetKind"];
78
+ /** Language for `command` targets. */
79
+ readonly lang?: string;
80
+ /** `file://` link for path targets. */
81
+ readonly href?: string;
82
+ /** Short facts after the target (`+8 −1`, `5 matches · 2 files`). */
83
+ readonly meta?: readonly TspText[];
84
+ readonly badges?: TspToolProps["badges"];
85
+ /** A one-word state note ("timed out", "partial"). */
86
+ readonly note?: TspText;
87
+ /** A non-zero exit code (error chip). */
88
+ readonly exit?: number | null;
89
+ }
90
+
91
+ /**
92
+ * A tool's semantic presentation for Tern Surface Protocol terminals.
93
+ * `ToolExecutionComponent` wraps it in a `tool` node (terminals that list
94
+ * the kind) or, as the fallback, a `card` (role `omp.tool.<name>`, status,
95
+ * elapsed timer, collapse): the renderer supplies only the head data and the
96
+ * body nodes, never frames, padding or width math.
97
+ */
98
+ export interface NativeToolView {
99
+ /** The data head; the fallback card's head spans derive from it when {@link head} is absent. */
100
+ readonly tool?: NativeToolHead;
101
+ /** Header spans for the fallback card (and the `tool` title when {@link tool} is absent). */
102
+ readonly head?: TspText;
103
+ /** Body: sections, never frames. */
104
+ readonly body?: readonly NativeChild[];
105
+ /** Tone override; the frame otherwise derives it from the call status. */
106
+ 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";
109
+ /** Render frameless (`frame:"inline"`): a head line plus a disclosed body. */
110
+ readonly inline?: boolean;
111
+ /** Head actions offered on hover (`copy`, `retry`). */
112
+ readonly tools?: TspToolProps["tools"];
113
+ }
114
+
65
115
  /**
66
116
  * A tool's transcript presentation: call preview, result view, and repaint/animation hints.
67
117
  * Method signatures are intentionally bivariant so a renderer typed over its own
@@ -76,6 +126,17 @@ export interface ToolRenderer<TArgs = unknown, TDetails = unknown> {
76
126
  theme: Theme,
77
127
  args?: TArgs,
78
128
  ): Component | undefined;
129
+ /**
130
+ * Semantic call preview for TSP terminals. When a renderer implements the
131
+ * describe hooks, the native backend never calls `renderCall`/`renderResult`.
132
+ */
133
+ describeCall?(args: TArgs, options: RenderResultOptions): NativeToolView | undefined;
134
+ /** Semantic result view for TSP terminals; merged with the call view when {@link mergeCallAndResult}. */
135
+ describeResult?(
136
+ result: ToolRenderResult<TDetails>,
137
+ options: RenderResultContextOptions,
138
+ args?: TArgs,
139
+ ): NativeToolView | undefined;
79
140
  mergeCallAndResult?: boolean;
80
141
  /** Describes current activity without coupling a renderer to terminal layout. */
81
142
  activitySummary?(args: TArgs, context: ToolActivityContext): ToolActivitySummary;
@@ -1,12 +1,18 @@
1
1
  import type { Component } from "../index";
2
2
  import type { Theme } from "../theme/theme";
3
- import { renderDeviceCallPreview } from "./resolve";
3
+ import type { NativeToolView } from "./renderer";
4
+ import { describeDeviceCallPreview, renderDeviceCallPreview } from "./resolve";
4
5
 
5
6
  /** Call preview for an `xd://report_issue` write. */
6
7
  export function renderReportIssueDeviceCall(content: unknown, uiTheme: Theme): Component {
7
8
  return renderDeviceCallPreview("Report Tool Issue", content, uiTheme);
8
9
  }
9
10
 
11
+ /** Native call preview for an `xd://report_issue` write. */
12
+ export function describeReportIssueDeviceCall(content: unknown): NativeToolView {
13
+ return describeDeviceCallPreview("Report Tool Issue", content);
14
+ }
15
+
10
16
  /** Device name for automatic tool issue reports. */
11
17
  export const REPORT_ISSUE_DEVICE_NAME = "report_issue";
12
18
  /** Internal device URL for automatic tool issue reports. */
@@ -3,7 +3,13 @@ import type { ToolRenderer } from "./renderer";
3
3
  import type { Component } from "../index";
4
4
  import { Text } from "../index";
5
5
 
6
- import type { RenderResultOptions } from "./renderer";
6
+ import type { NativeToolView, RenderResultOptions } from "./renderer";
7
+
8
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
9
+ import { span, text } from "../native/describe";
10
+ import { OwnerMemo } from "../native/memo";
11
+ import { plainText } from "../native/spans";
12
+ import { noteText, toolHead } from "./native-view";
7
13
 
8
14
  import type { Theme } from "../theme/theme";
9
15
 
@@ -47,6 +53,20 @@ export function renderResolutionDeviceCall(device: ResolutionDeviceName, content
47
53
  return renderDeviceCallPreview(title, content, uiTheme, Ellipsis.Omit);
48
54
  }
49
55
 
56
+ /** Native form of {@link renderResolutionDeviceCall}: `Resolve/Reject/Propose` head with the first content line. */
57
+ export function describeResolutionDeviceCall(device: ResolutionDeviceName, content: unknown): NativeToolView {
58
+ const title = device === PROPOSE_DEVICE_NAME ? "Propose" : device === REJECT_DEVICE_NAME ? "Reject" : "Resolve";
59
+ return describeDeviceCallPreview(title, content);
60
+ }
61
+
62
+ /** Native form of {@link renderDeviceCallPreview}: title head plus the first content line of a pending device write. */
63
+ export function describeDeviceCallPreview(title: string, content: unknown): NativeToolView {
64
+ const body = typeof content === "string" ? (plainText(content).trim().split("\n")[0] ?? "") : "";
65
+ return { head: toolHead(title, body || undefined) };
66
+ }
67
+
68
+ const resolveResultMemo = new OwnerMemo<NativeToolView | undefined>();
69
+
50
70
  /** Render the first content line of a pending device write. */
51
71
  export function renderDeviceCallPreview(
52
72
  title: string,
@@ -129,6 +149,48 @@ export const resolveRenderer = {
129
149
  };
130
150
  },
131
151
 
152
+ describeCall(args: Partial<ResolveInvocation>): NativeToolView {
153
+ const head: TspSpan[] = toolHead("Resolve", args.action);
154
+ if (args.action) {
155
+ head.push(
156
+ span(" "),
157
+ span(
158
+ args.action === "apply" ? "proposed → resolved" : "proposed → rejected",
159
+ args.action === "apply" ? "success" : "warning",
160
+ ),
161
+ );
162
+ }
163
+ const reason = args.reason?.trim();
164
+ return { head, inline: true, body: reason ? [noteText(reason, "muted", 1)] : undefined };
165
+ },
166
+
167
+ describeResult(result: {
168
+ content: Array<{ type: string; text?: string }>;
169
+ details?: ResolveDetails;
170
+ isError?: boolean;
171
+ }): NativeToolView | undefined {
172
+ return resolveResultMemo.get(result, [], () => {
173
+ const details = result.details;
174
+ const label = plainText(details?.label ?? "pending action");
175
+ const reason = plainText(details?.reason?.trim() || "No reason provided");
176
+ const action = details?.action ?? "apply";
177
+ const isApply = action === "apply" && !result.isError;
178
+ const verb = isApply ? "Accept" : action === "apply" ? "Failed" : "Discard";
179
+ const tone = result.isError ? "error" : isApply ? "success" : "warning";
180
+ const separatorIndex = label.indexOf(": ");
181
+ const sourceLabel = separatorIndex > 0 ? label.slice(0, separatorIndex).trim() : undefined;
182
+ const summaryLabel = separatorIndex > 0 ? label.slice(separatorIndex + 2).trim() : label;
183
+ const head: TspSpan[] = [span(`${verb}:`, `strong ${tone}`), span(" "), span(summaryLabel)];
184
+ if (sourceLabel) head.push(span(" "), span(`[${sourceLabel}]`, "strong muted"));
185
+ return {
186
+ head,
187
+ tone,
188
+ inline: true,
189
+ body: [text([span(reason, "muted")], { wrap: "word" })],
190
+ };
191
+ });
192
+ },
193
+
132
194
  inline: true,
133
195
  mergeCallAndResult: true,
134
196
  } satisfies ToolRenderer<Partial<ResolveInvocation>, ResolveDetails>;
@@ -718,6 +718,12 @@ const MAX_PENDING = 10;
718
718
  export class TailBuffer {
719
719
  #pending: string[] = [];
720
720
  #pos = 0; // byte count of the currently-held tail (after trims)
721
+ // `#pending[0]` and its byte count. Clean = well-formed and `#headBytes` is its exact
722
+ // UTF-8 length (a head compacted from chunks that split a surrogate pair is neither).
723
+ #headBytes = 0;
724
+ #headClean = true;
725
+ // Every chunk after `#pending[0]` is well-formed (no lone surrogates).
726
+ #restWellFormed = true;
721
727
 
722
728
  constructor(readonly maxBytes: number) {}
723
729
 
@@ -726,8 +732,7 @@ export class TailBuffer {
726
732
 
727
733
  const max = this.maxBytes;
728
734
  if (max === 0) {
729
- this.#pending.length = 0;
730
- this.#pos = 0;
735
+ this.#setHead("", 0, true);
731
736
  return;
732
737
  }
733
738
 
@@ -736,19 +741,17 @@ export class TailBuffer {
736
741
  // If the incoming chunk alone is >= budget, it fully dominates the tail.
737
742
  if (n >= max) {
738
743
  const { text: t, bytes } = truncateTailBytes(text, max);
739
- this.#pending[0] = t;
740
- this.#pending.length = 1;
741
- this.#pos = bytes;
744
+ // A chunk of exactly `max` bytes comes back verbatim, lone surrogates included.
745
+ this.#setHead(t, bytes, t.isWellFormed());
742
746
  return;
743
747
  }
744
748
 
745
- this.#pos += n;
746
-
747
749
  if (this.#pending.length === 0) {
748
- this.#pending[0] = text;
749
- this.#pending.length = 1;
750
+ this.#setHead(text, n, text.isWellFormed());
750
751
  } else {
752
+ this.#pos += n;
751
753
  this.#pending.push(text);
754
+ if (this.#restWellFormed && !text.isWellFormed()) this.#restWellFormed = false;
752
755
  if (this.#pending.length > MAX_PENDING) this.#compact();
753
756
  }
754
757
 
@@ -770,9 +773,22 @@ export class TailBuffer {
770
773
 
771
774
  // -- private ---------------------------------------------------------------
772
775
 
776
+ /** Replace the whole buffer with a single chunk. */
777
+ #setHead(text: string, bytes: number, clean: boolean): void {
778
+ this.#pending[0] = text;
779
+ this.#pending.length = 1;
780
+ this.#pos = bytes;
781
+ this.#headBytes = bytes;
782
+ this.#headClean = clean;
783
+ this.#restWellFormed = true;
784
+ }
785
+
773
786
  #compact(): void {
774
787
  this.#pending[0] = this.#pending.join("");
775
788
  this.#pending.length = 1;
789
+ this.#headBytes = this.#pos;
790
+ this.#headClean = this.#headClean && this.#restWellFormed;
791
+ this.#restWellFormed = true;
776
792
  }
777
793
 
778
794
  #flush(): string {
@@ -783,17 +799,64 @@ export class TailBuffer {
783
799
 
784
800
  #trimTo(max: number): void {
785
801
  if (max === 0) {
786
- this.#pending.length = 0;
787
- this.#pos = 0;
802
+ this.#setHead("", 0, true);
788
803
  return;
789
804
  }
790
805
  if (this.#pos <= max) return;
791
806
 
792
- const joined = this.#flush();
793
- const { text, bytes } = truncateTailBytes(joined, max);
794
- this.#pos = bytes;
795
- this.#pending[0] = text;
796
- this.#pending.length = 1;
807
+ // Steady state (one `text()` per appended chunk): the overflow lies inside the
808
+ // already-trimmed head, so only the head's front is cut and the chunks after it
809
+ // are kept verbatim instead of re-encoding the whole window. Those chunks must be
810
+ // well-formed: the full path round-trips them through UTF-8, which rewrites lone
811
+ // surrogates (and heals pairs split across chunk boundaries).
812
+ const restBytes = this.#pos - this.#headBytes;
813
+ if (restBytes < max && this.#restWellFormed) {
814
+ const head = this.#pending[0];
815
+ const keep = max - restBytes;
816
+ const drop = this.#headBytes - keep;
817
+ if (head.length === this.#headBytes) {
818
+ // All-ASCII head: byte offsets are char offsets and every byte is a boundary.
819
+ this.#pending[0] = head.slice(drop);
820
+ this.#headBytes = keep;
821
+ } else if (this.#headClean && drop <= keep) {
822
+ // Walk code points to the first boundary at or after `drop` bytes.
823
+ let dropped = 0;
824
+ let index = 0;
825
+ while (dropped < drop) {
826
+ const code = head.charCodeAt(index);
827
+ if (code < 0x80) {
828
+ dropped += 1;
829
+ index += 1;
830
+ } else if (code < 0x800) {
831
+ dropped += 2;
832
+ index += 1;
833
+ } else if (code >= 0xd800 && code <= 0xdbff) {
834
+ // Well-formed, so a high surrogate always starts a pair.
835
+ dropped += 4;
836
+ index += 2;
837
+ } else {
838
+ dropped += 3;
839
+ index += 1;
840
+ }
841
+ }
842
+ this.#pending[0] = head.slice(index);
843
+ this.#headBytes -= dropped;
844
+ } else {
845
+ // A head that already fits comes back verbatim, lone surrogates included.
846
+ const { text, bytes } = truncateTailBytes(head, keep);
847
+ this.#pending[0] = text;
848
+ this.#headBytes = bytes;
849
+ this.#headClean = text.isWellFormed();
850
+ this.#pos = this.#headBytes + restBytes;
851
+ return;
852
+ }
853
+ this.#headClean = true;
854
+ this.#pos = this.#headBytes + restBytes;
855
+ return;
856
+ }
857
+
858
+ const { text, bytes } = truncateTailBytes(this.#flush(), max);
859
+ this.#setHead(text, bytes, text.isWellFormed());
797
860
  }
798
861
  }
799
862