@oh-my-pi/pi-tui 18.4.3 → 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 (447) hide show
  1. package/CHANGELOG.md +36 -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/todo-reminder.d.ts +4 -1
  22. package/dist/types/chat/tool-execution.d.ts +25 -1
  23. package/dist/types/chat/transcript-actions.d.ts +37 -0
  24. package/dist/types/chat/transcript-entry.d.ts +3 -1
  25. package/dist/types/chat/user-message.d.ts +25 -0
  26. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  27. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  28. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  29. package/dist/types/chrome/diff.d.ts +12 -0
  30. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  31. package/dist/types/chrome/format.d.ts +6 -0
  32. package/dist/types/chrome/message-divider.d.ts +13 -0
  33. package/dist/types/chrome/message-frame.d.ts +10 -0
  34. package/dist/types/chrome/message-notice.d.ts +23 -0
  35. package/dist/types/chrome/overlay-box.d.ts +11 -2
  36. package/dist/types/chrome/qrcode.d.ts +7 -0
  37. package/dist/types/chrome/segment-track.d.ts +7 -0
  38. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  39. package/dist/types/chrome/status-notice.d.ts +14 -0
  40. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  41. package/dist/types/chrome/tool-activity.d.ts +3 -0
  42. package/dist/types/chrome/transcript-container.d.ts +13 -0
  43. package/dist/types/chrome/transcript-status.d.ts +15 -3
  44. package/dist/types/components/box.d.ts +7 -0
  45. package/dist/types/components/disclosure.d.ts +9 -0
  46. package/dist/types/components/editor.d.ts +37 -0
  47. package/dist/types/components/form.d.ts +14 -0
  48. package/dist/types/components/image.d.ts +7 -0
  49. package/dist/types/components/input.d.ts +20 -1
  50. package/dist/types/components/key-value-list.d.ts +3 -0
  51. package/dist/types/components/layout/geometry.d.ts +16 -0
  52. package/dist/types/components/layout/row.d.ts +7 -0
  53. package/dist/types/components/layout/split-pane.d.ts +8 -0
  54. package/dist/types/components/layout/stack.d.ts +6 -0
  55. package/dist/types/components/loader.d.ts +48 -0
  56. package/dist/types/components/markdown.d.ts +8 -0
  57. package/dist/types/components/metric.d.ts +7 -0
  58. package/dist/types/components/progress-bar.d.ts +3 -0
  59. package/dist/types/components/scroll-view.d.ts +6 -0
  60. package/dist/types/components/section.d.ts +3 -0
  61. package/dist/types/components/select-list.d.ts +33 -0
  62. package/dist/types/components/settings-list.d.ts +68 -0
  63. package/dist/types/components/spacer.d.ts +4 -0
  64. package/dist/types/components/tab-bar.d.ts +9 -0
  65. package/dist/types/components/table.d.ts +7 -0
  66. package/dist/types/components/text.d.ts +6 -0
  67. package/dist/types/components/tree-view.d.ts +9 -0
  68. package/dist/types/components/truncated-text.d.ts +3 -0
  69. package/dist/types/components/wizard-step.d.ts +6 -0
  70. package/dist/types/hotkeys-markdown.d.ts +24 -0
  71. package/dist/types/index.d.ts +5 -0
  72. package/dist/types/latex-block.d.ts +7 -0
  73. package/dist/types/native/apply.d.ts +39 -0
  74. package/dist/types/native/backend.d.ts +94 -0
  75. package/dist/types/native/blobs.d.ts +29 -0
  76. package/dist/types/native/describe.d.ts +56 -0
  77. package/dist/types/native/encode.d.ts +50 -0
  78. package/dist/types/native/icons.d.ts +18 -0
  79. package/dist/types/native/memo.d.ts +27 -0
  80. package/dist/types/native/node.d.ts +107 -0
  81. package/dist/types/native/overlay.d.ts +64 -0
  82. package/dist/types/native/picker.d.ts +124 -0
  83. package/dist/types/native/reconcile.d.ts +41 -0
  84. package/dist/types/native/settle.d.ts +11 -0
  85. package/dist/types/native/spans.d.ts +21 -0
  86. package/dist/types/native/state.d.ts +6 -0
  87. package/dist/types/native/tone.d.ts +24 -0
  88. package/dist/types/overlays/advisor-config.d.ts +20 -0
  89. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  90. package/dist/types/overlays/agent-hub.d.ts +10 -0
  91. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  92. package/dist/types/overlays/agents-hub.d.ts +8 -0
  93. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  94. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  95. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  96. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  97. package/dist/types/overlays/btw-panel.d.ts +3 -0
  98. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  99. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  100. package/dist/types/overlays/copy-selector.d.ts +38 -0
  101. package/dist/types/overlays/error-banner.d.ts +9 -0
  102. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  103. package/dist/types/overlays/history-search.d.ts +18 -0
  104. package/dist/types/overlays/hook-editor.d.ts +28 -1
  105. package/dist/types/overlays/hook-input.d.ts +3 -0
  106. package/dist/types/overlays/hook-selector.d.ts +10 -0
  107. package/dist/types/overlays/hub-frame.d.ts +18 -0
  108. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  109. package/dist/types/overlays/login-dialog.d.ts +10 -0
  110. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  111. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  112. package/dist/types/overlays/model-browser.d.ts +69 -0
  113. package/dist/types/overlays/model-hub.d.ts +7 -0
  114. package/dist/types/overlays/model-picker.d.ts +7 -0
  115. package/dist/types/overlays/move-overlay.d.ts +3 -0
  116. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  117. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  118. package/dist/types/overlays/pause-screen.d.ts +12 -0
  119. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  120. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  121. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  122. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  123. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  124. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  125. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  126. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  127. package/dist/types/overlays/session-selector.d.ts +64 -1
  128. package/dist/types/overlays/settings-defs.d.ts +14 -0
  129. package/dist/types/overlays/settings-selector.d.ts +21 -0
  130. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  131. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  132. package/dist/types/overlays/stats-notice.d.ts +8 -0
  133. package/dist/types/overlays/theme-selector.d.ts +11 -0
  134. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  135. package/dist/types/overlays/tree-selector.d.ts +60 -2
  136. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  137. package/dist/types/overlays/usage-row.d.ts +36 -2
  138. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  139. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  140. package/dist/types/prompt/composer.d.ts +18 -1
  141. package/dist/types/prompt/custom-editor.d.ts +48 -2
  142. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  143. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  144. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  145. package/dist/types/prompt/queued-messages.d.ts +23 -0
  146. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  147. package/dist/types/prompt/welcome.d.ts +16 -0
  148. package/dist/types/render/code-cell.d.ts +10 -0
  149. package/dist/types/render/file-list.d.ts +8 -0
  150. package/dist/types/render/hyperlink.d.ts +14 -0
  151. package/dist/types/render/output-block.d.ts +43 -0
  152. package/dist/types/render/output-pane.d.ts +17 -0
  153. package/dist/types/render/tool-card.d.ts +8 -0
  154. package/dist/types/render/tree-list.d.ts +8 -0
  155. package/dist/types/render/width-aware-text.d.ts +7 -0
  156. package/dist/types/setup/scenes/outro.d.ts +7 -0
  157. package/dist/types/setup/scenes/sign-in.d.ts +7 -0
  158. package/dist/types/setup/scenes/splash.d.ts +8 -0
  159. package/dist/types/setup/scenes/types.d.ts +6 -1
  160. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  161. package/dist/types/status-line/component.d.ts +49 -5
  162. package/dist/types/status-line/context-usage.d.ts +20 -0
  163. package/dist/types/status-line/footer.d.ts +8 -0
  164. package/dist/types/status-line/metrics.d.ts +1 -2
  165. package/dist/types/status-line/segments.d.ts +10 -1
  166. package/dist/types/status-line/types.d.ts +40 -1
  167. package/dist/types/terminal-capabilities.d.ts +1 -1
  168. package/dist/types/terminal.d.ts +27 -0
  169. package/dist/types/theme/defaults/index.d.ts +100 -0
  170. package/dist/types/theme/schema.d.ts +2 -0
  171. package/dist/types/theme/shimmer.d.ts +8 -0
  172. package/dist/types/theme/theme-class.d.ts +2 -0
  173. package/dist/types/theme/theme.d.ts +33 -1
  174. package/dist/types/tools/ask.d.ts +3 -0
  175. package/dist/types/tools/ast-edit.d.ts +10 -1
  176. package/dist/types/tools/ast-grep.d.ts +10 -1
  177. package/dist/types/tools/autoresearch.d.ts +21 -4
  178. package/dist/types/tools/bash.d.ts +30 -1
  179. package/dist/types/tools/cfg-render.d.ts +5 -1
  180. package/dist/types/tools/debug.d.ts +3 -1
  181. package/dist/types/tools/default-renderer.d.ts +6 -1
  182. package/dist/types/tools/edit.d.ts +14 -1
  183. package/dist/types/tools/eval.d.ts +12 -1
  184. package/dist/types/tools/fetch.d.ts +16 -1
  185. package/dist/types/tools/find.d.ts +15 -1
  186. package/dist/types/tools/github.d.ts +3 -0
  187. package/dist/types/tools/glob.d.ts +14 -1
  188. package/dist/types/tools/goal.d.ts +3 -1
  189. package/dist/types/tools/grep.d.ts +14 -1
  190. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  191. package/dist/types/tools/json-tree.d.ts +20 -0
  192. package/dist/types/tools/lsp.d.ts +3 -10
  193. package/dist/types/tools/mcp.d.ts +16 -1
  194. package/dist/types/tools/memory.d.ts +26 -0
  195. package/dist/types/tools/native-view.d.ts +90 -0
  196. package/dist/types/tools/proc-render.d.ts +7 -1
  197. package/dist/types/tools/read.d.ts +23 -1
  198. package/dist/types/tools/renderer.d.ts +57 -0
  199. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  200. package/dist/types/tools/resolve.d.ts +14 -1
  201. package/dist/types/tools/task.d.ts +3 -0
  202. package/dist/types/tools/think.d.ts +3 -0
  203. package/dist/types/tools/todo.d.ts +12 -0
  204. package/dist/types/tools/vibe.d.ts +3 -1
  205. package/dist/types/tools/wait.d.ts +3 -0
  206. package/dist/types/tools/web-search.d.ts +8 -0
  207. package/dist/types/tools/write.d.ts +14 -1
  208. package/dist/types/tools/xdev.d.ts +13 -0
  209. package/dist/types/tui.d.ts +49 -0
  210. package/package.json +9 -9
  211. package/src/apps/autoresearch-dashboard.ts +362 -4
  212. package/src/apps/cleanse-board.ts +219 -1
  213. package/src/apps/cleanse-picker.ts +9 -1
  214. package/src/apps/debug/log-viewer.ts +15 -15
  215. package/src/apps/debug/protocol-probe.ts +2 -20
  216. package/src/apps/git/avatar.ts +2 -22
  217. package/src/apps/git/git-tui.ts +406 -59
  218. package/src/apps/git/help.ts +130 -0
  219. package/src/apps/git/sidebar.ts +491 -0
  220. package/src/apps/live-visualizer.ts +65 -0
  221. package/src/apps/ps-top.ts +448 -17
  222. package/src/apps/session-picker.ts +1 -0
  223. package/src/apps/setup-model-picker.ts +5 -1
  224. package/src/apps/standalone-picker.ts +55 -1
  225. package/src/autocomplete.ts +44 -1
  226. package/src/chat/advisor-message.ts +72 -3
  227. package/src/chat/assistant-message.ts +346 -11
  228. package/src/chat/background-tan-message.ts +4 -4
  229. package/src/chat/bash-execution.ts +81 -0
  230. package/src/chat/cache-invalidation-marker.ts +8 -0
  231. package/src/chat/chat-transcript-builder.ts +37 -6
  232. package/src/chat/collab-prompt-message.ts +15 -3
  233. package/src/chat/compaction-summary-message.ts +74 -1
  234. package/src/chat/custom-message.ts +3 -1
  235. package/src/chat/eval-execution.ts +57 -0
  236. package/src/chat/execution-shared.ts +153 -1
  237. package/src/chat/hook-message.ts +1 -0
  238. package/src/chat/late-diagnostics-message.ts +85 -2
  239. package/src/chat/read-tool-group.ts +341 -1
  240. package/src/chat/served-model-marker.ts +8 -0
  241. package/src/chat/skill-message.ts +63 -0
  242. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  243. package/src/chat/todo-reminder.ts +34 -4
  244. package/src/chat/tool-execution.ts +385 -10
  245. package/src/chat/transcript-actions.ts +38 -0
  246. package/src/chat/transcript-browser.ts +2 -9
  247. package/src/chat/transcript-entry.ts +10 -1
  248. package/src/chat/transcript-render-helpers.ts +16 -15
  249. package/src/chat/ttsr-notification.ts +38 -1
  250. package/src/chat/user-message.ts +167 -4
  251. package/src/chrome/collab-qrcode.ts +27 -2
  252. package/src/chrome/context-thresholds.ts +34 -0
  253. package/src/chrome/countdown-timer.ts +24 -1
  254. package/src/chrome/diff.ts +60 -1
  255. package/src/chrome/dynamic-border.ts +7 -0
  256. package/src/chrome/format.ts +12 -0
  257. package/src/chrome/message-divider.ts +33 -1
  258. package/src/chrome/message-frame.ts +53 -10
  259. package/src/chrome/message-notice.ts +71 -0
  260. package/src/chrome/overlay-box.ts +45 -2
  261. package/src/chrome/qrcode.ts +28 -0
  262. package/src/chrome/segment-track.ts +26 -0
  263. package/src/chrome/selector-helpers.ts +1 -17
  264. package/src/chrome/status-notice.ts +46 -0
  265. package/src/chrome/streaming-panel.ts +25 -1
  266. package/src/chrome/tool-activity.ts +20 -0
  267. package/src/chrome/transcript-container.ts +37 -0
  268. package/src/chrome/transcript-status.ts +40 -11
  269. package/src/components/box.ts +51 -0
  270. package/src/components/disclosure.ts +33 -0
  271. package/src/components/editor.ts +216 -27
  272. package/src/components/form.ts +93 -9
  273. package/src/components/image.ts +30 -0
  274. package/src/components/input.ts +77 -26
  275. package/src/components/key-value-list.ts +18 -0
  276. package/src/components/layout/geometry.ts +44 -0
  277. package/src/components/layout/row.ts +30 -0
  278. package/src/components/layout/split-pane.ts +65 -0
  279. package/src/components/layout/stack.ts +27 -0
  280. package/src/components/loader.ts +183 -1
  281. package/src/components/markdown.ts +21 -0
  282. package/src/components/metric.ts +39 -0
  283. package/src/components/progress-bar.ts +28 -0
  284. package/src/components/scroll-view.ts +23 -0
  285. package/src/components/section.ts +16 -0
  286. package/src/components/select-list.ts +174 -25
  287. package/src/components/settings-list.ts +264 -7
  288. package/src/components/spacer.ts +17 -0
  289. package/src/components/tab-bar.ts +42 -0
  290. package/src/components/table.ts +34 -0
  291. package/src/components/text.ts +23 -0
  292. package/src/components/tree-view.ts +65 -0
  293. package/src/components/truncated-text.ts +14 -0
  294. package/src/components/wizard-step.ts +18 -9
  295. package/src/debug-server.ts +14 -7
  296. package/src/hotkeys-markdown.ts +251 -69
  297. package/src/index.ts +6 -0
  298. package/src/latex-block.ts +11 -0
  299. package/src/native/apply.ts +275 -0
  300. package/src/native/backend.ts +682 -0
  301. package/src/native/blobs.ts +70 -0
  302. package/src/native/describe.ts +124 -0
  303. package/src/native/encode.ts +233 -0
  304. package/src/native/icons.ts +184 -0
  305. package/src/native/memo.ts +64 -0
  306. package/src/native/node.ts +112 -0
  307. package/src/native/overlay.ts +131 -0
  308. package/src/native/picker.ts +333 -0
  309. package/src/native/reconcile.ts +1063 -0
  310. package/src/native/settle.ts +23 -0
  311. package/src/native/spans.ts +308 -0
  312. package/src/native/state.ts +28 -0
  313. package/src/native/tone.ts +79 -0
  314. package/src/overlays/advisor-config.ts +420 -0
  315. package/src/overlays/agent-hub-renderer.ts +155 -18
  316. package/src/overlays/agent-hub.ts +1037 -44
  317. package/src/overlays/agent-transcript-viewer.ts +129 -0
  318. package/src/overlays/agents-hub.ts +688 -53
  319. package/src/overlays/annotation-overlay.ts +391 -52
  320. package/src/overlays/ask-dialog.ts +435 -24
  321. package/src/overlays/bordered-loader.ts +17 -0
  322. package/src/overlays/btw-history-panel.ts +182 -0
  323. package/src/overlays/btw-panel.ts +68 -2
  324. package/src/overlays/cleanse-panel.ts +105 -15
  325. package/src/overlays/codex-reset-fireworks.ts +65 -5
  326. package/src/overlays/composer-shape-preview.ts +26 -1
  327. package/src/overlays/copy-selector.ts +545 -23
  328. package/src/overlays/error-banner.ts +44 -0
  329. package/src/overlays/extensions/extension-list.ts +23 -17
  330. package/src/overlays/history-search.ts +175 -33
  331. package/src/overlays/hook-editor.ts +113 -6
  332. package/src/overlays/hook-input.ts +22 -1
  333. package/src/overlays/hook-selector.ts +311 -22
  334. package/src/overlays/hub-frame.ts +80 -0
  335. package/src/overlays/jobs-panel.ts +112 -0
  336. package/src/overlays/login-dialog.ts +183 -1
  337. package/src/overlays/logout-account-selector.ts +102 -4
  338. package/src/overlays/mcp-add-wizard.ts +232 -53
  339. package/src/overlays/model-browser.ts +681 -22
  340. package/src/overlays/model-hub.ts +1402 -78
  341. package/src/overlays/model-picker.ts +175 -1
  342. package/src/overlays/move-overlay.ts +45 -0
  343. package/src/overlays/oauth-selector.ts +227 -23
  344. package/src/overlays/omfg-panel.ts +86 -2
  345. package/src/overlays/pause-screen.ts +55 -4
  346. package/src/overlays/plan-review-overlay.ts +352 -0
  347. package/src/overlays/plan-save-overlay.ts +45 -0
  348. package/src/overlays/plugin-selector.ts +21 -1
  349. package/src/overlays/plugin-settings.ts +195 -5
  350. package/src/overlays/queue-mode-selector.ts +19 -1
  351. package/src/overlays/reset-usage-selector.ts +110 -40
  352. package/src/overlays/rewind-selector.ts +458 -44
  353. package/src/overlays/session-account-selector.ts +1 -1
  354. package/src/overlays/session-info-overlay.ts +137 -1
  355. package/src/overlays/session-selector.ts +807 -46
  356. package/src/overlays/settings-defs.ts +41 -0
  357. package/src/overlays/settings-selector.ts +583 -23
  358. package/src/overlays/show-images-selector.ts +18 -1
  359. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  360. package/src/overlays/stats-notice.ts +31 -0
  361. package/src/overlays/theme-selector.ts +31 -1
  362. package/src/overlays/thinking-selector.ts +22 -1
  363. package/src/overlays/tree-selector.ts +570 -110
  364. package/src/overlays/usage-dashboard.ts +587 -12
  365. package/src/overlays/usage-row.ts +161 -25
  366. package/src/prompt/attachment-chips.ts +60 -0
  367. package/src/prompt/composer-attachments.ts +31 -9
  368. package/src/prompt/composer.ts +55 -2
  369. package/src/prompt/custom-editor.ts +302 -4
  370. package/src/prompt/editor-top-gap.ts +8 -0
  371. package/src/prompt/macos-spelling.ts +24 -9
  372. package/src/prompt/magic-keywords.ts +22 -1
  373. package/src/prompt/model-mention-autocomplete.ts +1 -0
  374. package/src/prompt/queued-messages.ts +101 -0
  375. package/src/prompt/skill-tokens.ts +5 -3
  376. package/src/prompt/welcome.ts +213 -6
  377. package/src/render/code-cell.ts +103 -2
  378. package/src/render/file-list.ts +29 -0
  379. package/src/render/hyperlink.ts +32 -0
  380. package/src/render/output-block.ts +110 -0
  381. package/src/render/output-pane.ts +37 -0
  382. package/src/render/tool-card.ts +137 -0
  383. package/src/render/tree-list.ts +39 -0
  384. package/src/render/width-aware-text.ts +20 -0
  385. package/src/setup/scenes/composer.ts +29 -0
  386. package/src/setup/scenes/glyph.ts +15 -0
  387. package/src/setup/scenes/model.ts +36 -8
  388. package/src/setup/scenes/outro.ts +28 -1
  389. package/src/setup/scenes/sign-in.ts +122 -13
  390. package/src/setup/scenes/splash.ts +28 -1
  391. package/src/setup/scenes/theme.ts +88 -3
  392. package/src/setup/scenes/types.ts +7 -1
  393. package/src/setup/startup-splash.ts +46 -9
  394. package/src/setup/wizard-overlay.ts +140 -23
  395. package/src/status-line/component.ts +398 -16
  396. package/src/status-line/context-usage.ts +279 -50
  397. package/src/status-line/footer.ts +94 -1
  398. package/src/status-line/metrics.ts +3 -6
  399. package/src/status-line/segments.ts +463 -97
  400. package/src/status-line/separators.ts +3 -16
  401. package/src/status-line/types.ts +43 -1
  402. package/src/terminal-capabilities.ts +7 -0
  403. package/src/terminal.ts +130 -2
  404. package/src/theme/defaults/dark-neon-noir.json +100 -0
  405. package/src/theme/defaults/index.ts +2 -0
  406. package/src/theme/defaults/light-prism.json +5 -5
  407. package/src/theme/defaults/onyx.json +62 -62
  408. package/src/theme/schema.ts +15 -0
  409. package/src/theme/shimmer.ts +33 -0
  410. package/src/theme/theme-class.ts +24 -19
  411. package/src/theme/theme.ts +111 -9
  412. package/src/tools/ask.ts +157 -1
  413. package/src/tools/ast-edit.ts +174 -1
  414. package/src/tools/ast-grep.ts +82 -2
  415. package/src/tools/autoresearch.ts +149 -1
  416. package/src/tools/bash.ts +180 -49
  417. package/src/tools/cfg-render.ts +66 -1
  418. package/src/tools/debug.ts +78 -1
  419. package/src/tools/default-renderer.ts +35 -1
  420. package/src/tools/edit.ts +247 -2
  421. package/src/tools/eval.ts +257 -1
  422. package/src/tools/fetch.ts +102 -3
  423. package/src/tools/find.ts +127 -1
  424. package/src/tools/github.ts +119 -10
  425. package/src/tools/glob.ts +116 -1
  426. package/src/tools/goal.ts +56 -1
  427. package/src/tools/grep.ts +83 -2
  428. package/src/tools/grouped-file-output.ts +205 -0
  429. package/src/tools/json-tree.ts +116 -1
  430. package/src/tools/lsp.ts +318 -1
  431. package/src/tools/mcp.ts +80 -1
  432. package/src/tools/memory.ts +93 -11
  433. package/src/tools/native-view.ts +292 -0
  434. package/src/tools/proc-render.ts +221 -1
  435. package/src/tools/read.ts +199 -4
  436. package/src/tools/renderer.ts +61 -0
  437. package/src/tools/report-tool-issue.ts +7 -1
  438. package/src/tools/resolve.ts +63 -1
  439. package/src/tools/task.ts +406 -4
  440. package/src/tools/think.ts +19 -1
  441. package/src/tools/todo.ts +89 -0
  442. package/src/tools/vibe.ts +230 -2
  443. package/src/tools/wait.ts +181 -1
  444. package/src/tools/web-search.ts +113 -1
  445. package/src/tools/write.ts +144 -5
  446. package/src/tools/xdev.ts +64 -1
  447. package/src/tui.ts +294 -3
@@ -27,7 +27,12 @@ import {
27
27
  SKILL_PROMPT_MESSAGE_TYPE,
28
28
  type SkillPromptDetails,
29
29
  } from "./messages";
30
- import { textContent, type TranscriptEntryLike as TranscriptEntry, transcriptEntryMessage } from "./transcript-entry";
30
+ import {
31
+ imageContent,
32
+ textContent,
33
+ type TranscriptEntryLike as TranscriptEntry,
34
+ transcriptEntryMessage,
35
+ } from "./transcript-entry";
31
36
  import { theme } from "../theme";
32
37
  import {
33
38
  assistantHasVisibleContent,
@@ -54,12 +59,16 @@ import {
54
59
  } from "./compaction-summary-message";
55
60
  import { CustomMessageComponent } from "./custom-message";
56
61
  import { EvalExecutionComponent } from "./eval-execution";
57
- import { type LateDiagnosticsFile, LateDiagnosticsMessageComponent } from "./late-diagnostics-message";
62
+ import {
63
+ type LateDiagnosticsFile,
64
+ LateDiagnosticsMessageComponent,
65
+ routeLateDiagnostics,
66
+ } from "./late-diagnostics-message";
58
67
  import { groupedReadUsageCallIds, ReadToolGroupComponent, readArgsCollapseIntoGroup } from "./read-tool-group";
59
68
  import { SkillMessageComponent } from "./skill-message";
60
69
  import { ToolExecutionComponent } from "./tool-execution";
61
70
  import { TranscriptContainer } from "../chrome/transcript-container";
62
- import { createUsageRowBlock, turnElapsedMs } from "../overlays/usage-row";
71
+ import { createUsageRowBlock, TurnUsageTally, turnElapsedMs } from "../overlays/usage-row";
63
72
  import { CollapsedSyntheticMessageComponent, UserMessageComponent } from "./user-message";
64
73
 
65
74
  export interface ChatTranscriptBuilderDeps {
@@ -88,6 +97,7 @@ export class ChatTranscriptBuilder {
88
97
  #pendingReadUsageCallIds: string[] | undefined;
89
98
  #pendingUsageElapsedMs: number | undefined;
90
99
  #turnStartedAt: number | undefined;
100
+ readonly #turnUsage = new TurnUsageTally();
91
101
  #lastAssistantUsage: Usage | undefined;
92
102
  #servedModelTracker = new ServedModelTracker();
93
103
  #waitingPoll: ToolExecutionComponent | null = null;
@@ -124,7 +134,12 @@ export class ChatTranscriptBuilder {
124
134
  if (this.#readArgs.size === 0 && this.#pendingTools.size === 0) this.#flushPendingUsage();
125
135
  }
126
136
 
127
- /** Toggle tool-output expansion across every expandable component. */
137
+ /**
138
+ * Toggle tool-output expansion across every expandable component. Cards a
139
+ * TSP terminal collapsed or expanded locally mirror that into their own
140
+ * state, so this re-applies the transcript-wide state to every one of them
141
+ * even when it equals the previous value.
142
+ */
128
143
  setExpanded(expanded: boolean): void {
129
144
  this.#expanded = expanded;
130
145
  for (const component of this.#expandables) component.setExpanded(expanded);
@@ -156,6 +171,7 @@ export class ChatTranscriptBuilder {
156
171
  this.#pendingReadUsageCallIds = undefined;
157
172
  this.#pendingUsageElapsedMs = undefined;
158
173
  this.#turnStartedAt = undefined;
174
+ this.#turnUsage.reset();
159
175
  this.#lastAssistantUsage = undefined;
160
176
  this.#servedModelTracker = new ServedModelTracker();
161
177
  this.#waitingPoll = null;
@@ -282,7 +298,9 @@ export class ChatTranscriptBuilder {
282
298
  // agent-attributed `user` message (advisor tool-loop redirect) must not.
283
299
  if (message.role === "user" && message.attribution !== "agent") {
284
300
  this.#turnStartedAt = message.timestamp;
301
+ this.#turnUsage.reset();
285
302
  } else if (message.role === "developer" && message.synthetic) {
303
+ this.#turnUsage.reset();
286
304
  // A synthetic developer message initiates a fresh run (auto-
287
305
  // continue, /goal, approved plan): replay must not inherit the
288
306
  // preceding user prompt's timestamp, mirroring the live
@@ -313,6 +331,8 @@ export class ChatTranscriptBuilder {
313
331
  this.container.addChild(
314
332
  new UserMessageComponent(userText, {
315
333
  liveSteered: message.role === "user" && message.liveSteered === true,
334
+ timestamp: message.timestamp,
335
+ images: imageContent(message.content),
316
336
  }),
317
337
  );
318
338
  }
@@ -348,6 +368,7 @@ export class ChatTranscriptBuilder {
348
368
  // message does.
349
369
  if (message.role === "custom" && isUserTurnInitiator(message as CustomMessage)) {
350
370
  this.#turnStartedAt = message.timestamp;
371
+ this.#turnUsage.reset();
351
372
  }
352
373
  this.#appendCustomMessage(message);
353
374
  break;
@@ -398,6 +419,7 @@ export class ChatTranscriptBuilder {
398
419
  this.#trackExpandable(assistantComponent);
399
420
  assistantComponent.pickReactionTarget(this.container.children);
400
421
  this.container.addChild(assistantComponent);
422
+ let lastAssistantComponent = assistantComponent;
401
423
 
402
424
  if (displayPreferences.cacheMissMarker) {
403
425
  const invalidation = detectCacheInvalidation(this.#lastAssistantUsage, message.usage);
@@ -433,6 +455,7 @@ export class ChatTranscriptBuilder {
433
455
  component.setToolResultImagesVisible(!displayPreferences.hideToolActivity);
434
456
  this.#trackExpandable(component);
435
457
  this.container.addChild(component);
458
+ lastAssistantComponent = component;
436
459
  };
437
460
 
438
461
  for (const content of message.content) {
@@ -491,6 +514,8 @@ export class ChatTranscriptBuilder {
491
514
  }
492
515
  appendAssistantSegment(afterToolSegment);
493
516
  }
517
+ const turnUsage = this.#turnUsage.add(message, this.#turnStartedAt);
518
+ if (turnUsage) lastAssistantComponent.setTurnUsage(turnUsage);
494
519
 
495
520
  this.#pendingUsage =
496
521
  displayPreferences.showTokenUsage && assistantUsageIsBilled(message.usage) ? message.usage : undefined;
@@ -544,7 +569,10 @@ export class ChatTranscriptBuilder {
544
569
  }
545
570
  if (message.customType === LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE) {
546
571
  const details = (message as CustomMessage<{ files?: LateDiagnosticsFile[] }>).details;
547
- const component = new LateDiagnosticsMessageComponent(details?.files ?? []);
572
+ // Native: into the edit/write frames they belong to; the rest stand alone.
573
+ const files = routeLateDiagnostics(this.container.children, details?.files ?? []);
574
+ if (files.length === 0) return;
575
+ const component = new LateDiagnosticsMessageComponent(files);
548
576
  this.#trackExpandable(component);
549
577
  this.container.addChild(component);
550
578
  return;
@@ -570,7 +598,10 @@ export class ChatTranscriptBuilder {
570
598
  }
571
599
  if (message.customType === "advisor") {
572
600
  const details = (message as CustomMessage<AdvisorMessageDetails>).details;
573
- this.container.addChild(createAdvisorMessageCard(details, () => this.#expanded, theme));
601
+ const card = createAdvisorMessageCard(details, () => this.#expanded, theme);
602
+ // Tracked so a transcript-wide toggle also clears a per-card native toggle.
603
+ this.#trackExpandable(card);
604
+ this.container.addChild(card);
574
605
  return;
575
606
  }
576
607
  if (message.customType === LAUNCH_COMPLETION_MESSAGE_TYPE) {
@@ -5,12 +5,16 @@ import { Text } from "../components/text";
5
5
  import type { CollabPromptDetails } from "./messages";
6
6
  import type { CustomMessage } from "./messages";
7
7
  import { getMarkdownTheme, theme } from "../theme";
8
+ import { card, md, span } from "../native/describe";
9
+ import type { NativeNode } from "../native/node";
8
10
 
9
11
  /**
10
12
  * Renders a collab guest prompt on every participant's transcript: a
11
13
  * user-message-styled bubble prefixed with the author's name.
12
14
  */
13
15
  export class CollabPromptMessageComponent extends Container {
16
+ readonly #native: NativeNode;
17
+
14
18
  constructor(message: CustomMessage<CollabPromptDetails>) {
15
19
  super();
16
20
  const from = message.details?.from?.trim() || "guest";
@@ -24,11 +28,19 @@ export class CollabPromptMessageComponent extends Container {
24
28
  .filter((content): content is TextContent => content.type === "text")
25
29
  .map(content => content.text)
26
30
  .join("");
27
- const md = new Markdown(text, 1, 1, getMarkdownTheme(), {
31
+ const markdown = new Markdown(text, 1, 1, getMarkdownTheme(), {
28
32
  bgColor: (value: string) => theme.bg("userMessageBg", value),
29
33
  color: (value: string) => theme.fgOnBg("userMessageText", "userMessageBg", value),
30
34
  });
31
- md.setIgnoreTight(true);
32
- this.addChild(md);
35
+ markdown.setIgnoreTight(true);
36
+ this.addChild(markdown);
37
+ this.#native = card({ role: "omp.user.collab", tone: "user", head: [span(`«${from}» ›`, "accent strong")] }, [
38
+ md(text),
39
+ ]);
40
+ }
41
+
42
+ /** A user-toned card headed by the guest's name, with the prompt as markdown. */
43
+ override describe(): NativeNode {
44
+ return this.#native;
33
45
  }
34
46
  }
@@ -3,9 +3,13 @@ import { Disclosure } from "../components/disclosure";
3
3
  import { type Component } from "../tui";
4
4
  import { Markdown } from "../components/markdown";
5
5
  import { formatNumber } from "@oh-my-pi/pi-utils";
6
- import { getMarkdownTheme, theme } from "../theme";
6
+ import { getMarkdownTheme, getThemeEpoch, theme } from "../theme";
7
7
  import { expandKeyHint } from "../render/render-utils";
8
8
  import type { BranchSummaryMessage, CompactionSummaryMessage, CustomMessage } from "./messages";
9
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
10
+ import { md, node, span, text } from "../native/describe";
11
+ import { type NativeChild, type NativeNode, type NativeUiEvent, rootToggleExpanded } from "../native/node";
12
+ import { Memo } from "../native/memo";
9
13
 
10
14
  /** Divider labels per compaction method; unknown/legacy methods fall back to "compacted". */
11
15
  const COMPACTION_METHOD_LABELS: Record<string, string> = {
@@ -24,7 +28,13 @@ function compactionAmount(message: CompactionSummaryMessage): string | undefined
24
28
 
25
29
  interface SummaryDividerOptions {
26
30
  label: () => string;
31
+ /** Themed spans of the label for native terminals. */
32
+ nativeLabel: () => TspSpan[];
27
33
  detailMarkdown: () => string;
34
+ /** Native body when it differs from the markdown detail. */
35
+ nativeDetail?: () => NativeChild[];
36
+ /** Semantic role of the native section. */
37
+ role: string;
28
38
  }
29
39
 
30
40
  /**
@@ -83,6 +93,8 @@ class SummaryMessageComponent implements Component {
83
93
  #disclosure: Disclosure;
84
94
  #ignoreTight: boolean | undefined;
85
95
  #disposed = false;
96
+ #expanded = false;
97
+ readonly #native = new Memo();
86
98
 
87
99
  readonly #options: SummaryDividerOptions;
88
100
 
@@ -92,9 +104,35 @@ class SummaryMessageComponent implements Component {
92
104
  }
93
105
 
94
106
  setExpanded(expanded: boolean): void {
107
+ this.#expanded = expanded;
95
108
  this.#disclosure.setExpanded(expanded);
96
109
  }
97
110
 
111
+ /**
112
+ * A collapsible `section` headed by the divider label, with the summary
113
+ * markdown as its body; expanding is terminal-local and mirrored back
114
+ * through {@link handleNativeEvent}.
115
+ */
116
+ describe(): NativeNode {
117
+ return this.#native.get([this.#expanded, getThemeEpoch()], () =>
118
+ node(
119
+ "section",
120
+ {
121
+ role: this.#options.role,
122
+ head: this.#options.nativeLabel(),
123
+ collapsible: true,
124
+ collapsed: !this.#expanded,
125
+ },
126
+ this.#options.nativeDetail?.() ?? [md(this.#options.detailMarkdown())],
127
+ ),
128
+ );
129
+ }
130
+
131
+ handleNativeEvent(event: NativeUiEvent): void {
132
+ const expanded = rootToggleExpanded(event);
133
+ if (expanded !== undefined) this.setExpanded(expanded);
134
+ }
135
+
98
136
  setIgnoreTight(ignore: boolean): this {
99
137
  if (this.#disposed || this.#ignoreTight === ignore) return this;
100
138
  this.#ignoreTight = ignore;
@@ -160,11 +198,42 @@ export class CompactionSummaryMessageComponent extends SummaryMessageComponent {
160
198
  // A dead-end warning stamped by the progress guard badges the bar;
161
199
  // the full text lives in the ctrl+o detail block below.
162
200
  label: () => compactionLabel(message),
201
+ nativeLabel: () => compactionNativeLabel(message),
202
+ nativeDetail: () => compactionNativeDetail(message),
163
203
  detailMarkdown: () => compactionDetailMarkdown(message),
204
+ role: "omp.compaction",
164
205
  });
165
206
  }
166
207
  }
167
208
 
209
+ /** The divider chip: `Soft-compacted · 19K → 6.9K tokens`, the arrow dimmed. */
210
+ function compactionNativeLabel(message: CompactionSummaryMessage): TspSpan[] {
211
+ const name = (message.method && COMPACTION_METHOD_LABELS[message.method]) || "compacted";
212
+ const spans = [span(`${name[0]!.toUpperCase()}${name.slice(1)}`, "muted")];
213
+ if (message.tokensAfter !== undefined && message.tokensBefore > 0) {
214
+ spans.push(
215
+ span(` · ${formatNumber(message.tokensBefore)}`, "muted mono"),
216
+ span(" → ", "dim"),
217
+ span(`${formatNumber(message.tokensAfter)} tokens`, "muted mono"),
218
+ );
219
+ }
220
+ if (message.warning) spans.push(span(` ${theme.icon.warning}`, "warning"));
221
+ return spans;
222
+ }
223
+
224
+ /** The expanded divider: a caption, the warning if any, then the summary itself (the chip already has the amounts). */
225
+ function compactionNativeDetail(message: CompactionSummaryMessage): NativeChild[] {
226
+ const frames = message.images?.length ?? 0;
227
+ const caption =
228
+ frames > 0
229
+ ? `Summary · ${frames} snapcompact frame${frames === 1 ? "" : "s"} attached`
230
+ : "Summary of the earlier conversation";
231
+ const detail: NativeChild[] = [text([span(caption, "dim")], { role: "omp.compaction.caption", key: "caption" })];
232
+ if (message.warning) detail.push(text([span(message.warning, "warning")], { wrap: "word", key: "warning" }));
233
+ detail.push(md(message.summary, { key: "summary" }));
234
+ return detail;
235
+ }
236
+
168
237
  function compactionLabel(message: CompactionSummaryMessage): string {
169
238
  const name = (message.method && COMPACTION_METHOD_LABELS[message.method]) || "compacted";
170
239
  let label = `${theme.icon.camera} ${name}`;
@@ -199,6 +268,8 @@ export class HandoffSummaryMessageComponent extends SummaryMessageComponent {
199
268
  constructor(message: CustomMessage<unknown>) {
200
269
  super({
201
270
  label: () => `${theme.icon.context} handed-off`,
271
+ nativeLabel: () => [span("Handed off", "muted")],
272
+ role: "omp.handoff",
202
273
  detailMarkdown: () => {
203
274
  const document = extractHandoffDocument(getCustomMessageText(message));
204
275
  return `**Handoff context**\n\n${document || "_No handoff content._"}`;
@@ -226,6 +297,8 @@ export class BranchSummaryMessageComponent extends SummaryMessageComponent {
226
297
  constructor(message: BranchSummaryMessage) {
227
298
  super({
228
299
  label: () => `${theme.icon.branch} branch`,
300
+ nativeLabel: () => [span("Branch summarized", "muted")],
301
+ role: "omp.branch",
229
302
  detailMarkdown: () => `**Branch summary**\n\n${message.summary}`,
230
303
  });
231
304
  }
@@ -10,11 +10,13 @@ import { type CustomMessage, LIVE_DELEGATION_MESSAGE_TYPE } from "./messages";
10
10
  export class CustomMessageComponent extends FramedMessageComponent<CustomMessage<unknown>> {
11
11
  constructor(message: CustomMessage<unknown>, customRenderer?: MessageRenderer) {
12
12
  const isLiveDelegation = message.customType === LIVE_DELEGATION_MESSAGE_TYPE;
13
+ const isHook = String(message.role) === "hookMessage";
13
14
  super({
14
15
  message,
16
+ role: isHook ? "omp.hook" : isLiveDelegation ? "omp.custom.delegation" : "omp.custom",
15
17
  // The transcript dispatch routes both `custom` and legacy `hookMessage` roles here:
16
18
  // tag hooks with the hook glyph, other injected messages with a neutral package.
17
- icon: () => (String(message.role) === "hookMessage" ? theme.icon.extensionHook : theme.icon.package),
19
+ icon: () => (isHook ? theme.icon.extensionHook : theme.icon.package),
18
20
  hideHeader: isLiveDelegation,
19
21
  borderColor: isLiveDelegation ? "borderAccent" : undefined,
20
22
  customRenderer,
@@ -15,11 +15,16 @@ import {
15
15
  buildExecutionFrame,
16
16
  buildStatusFooter,
17
17
  clampDisplayLine,
18
+ describeExecutionCard,
19
+ describeExecutionTool,
18
20
  type ExecutionColorKey,
19
21
  type ExecutionStatus,
20
22
  PREVIEW_LINES,
21
23
  resolveExecutionStatus,
22
24
  } from "./execution-shared";
25
+ import { code, span } from "../native/describe";
26
+ import { type DescribeContext, type NativeNode, type NativeUiEvent, rootToggleExpanded } from "../native/node";
27
+ import { Memo } from "../native/memo";
23
28
 
24
29
  export type EvalExecutionLanguage = "python" | "js";
25
30
 
@@ -40,6 +45,11 @@ export class EvalExecutionComponent extends Container {
40
45
  readonly #code: string;
41
46
  readonly #excludeFromContext: boolean;
42
47
  readonly #language: EvalExecutionLanguage;
48
+ readonly #startedAt = performance.now();
49
+ #endedAt: number | undefined;
50
+ // Bumped whenever the output pane's text changes.
51
+ #outputVersion = 0;
52
+ readonly #native = new Memo();
43
53
 
44
54
  #highlightLang(): "python" | "javascript" {
45
55
  return this.#language === "js" ? "javascript" : "python";
@@ -107,9 +117,54 @@ export class EvalExecutionComponent extends Container {
107
117
  this.#updateDisplay();
108
118
  }
109
119
 
120
+ handleNativeEvent(event: NativeUiEvent): void {
121
+ const expanded = rootToggleExpanded(event);
122
+ if (expanded !== undefined) this.setExpanded(expanded);
123
+ }
124
+
125
+ /**
126
+ * The agent's eval `tool` frame (role `omp.eval`) with a `you` badge: the
127
+ * cell source in the head, its output as an `ansi` mini terminal. Terminals
128
+ * without the `tool` kind get a `card` with the cell as `code` over the output.
129
+ */
130
+ override describe(cx?: DescribeContext): NativeNode {
131
+ const dataFirst = cx?.supports("tool") === true;
132
+ const key = [dataFirst, this.#outputVersion, this.#status, this.#expanded];
133
+ return this.#native.get(key, () => {
134
+ const title = this.#language === "js" ? "JavaScript" : "Python";
135
+ const common = {
136
+ role: "omp.eval",
137
+ status: this.#status,
138
+ startedAt: this.#startedAt,
139
+ expanded: this.#expanded,
140
+ output: this.#outputPane.getText(),
141
+ exitCode: this.#exitCode,
142
+ truncation: this.#truncation,
143
+ artifactError: this.#artifactError,
144
+ };
145
+ return dataFirst
146
+ ? describeExecutionTool({
147
+ ...common,
148
+ name: "eval",
149
+ title,
150
+ command: this.#code,
151
+ lang: this.#highlightLang(),
152
+ excluded: this.#excludeFromContext,
153
+ endedAt: this.#endedAt,
154
+ })
155
+ : describeExecutionCard({
156
+ ...common,
157
+ head: [span(title, `${this.#excludeFromContext ? "dim" : "pythonMode"} strong`)],
158
+ muted: this.#excludeFromContext,
159
+ lead: [code(this.#code, { lang: this.#highlightLang(), key: "code" })],
160
+ });
161
+ });
162
+ }
163
+
110
164
  appendOutput(chunk: string): void {
111
165
  // Chunk is pre-sanitized by OutputSink.push() — no need to sanitize again.
112
166
  this.#outputPane.append(chunk);
167
+ this.#outputVersion++;
113
168
  this.#updateDisplay();
114
169
  }
115
170
 
@@ -120,12 +175,14 @@ export class EvalExecutionComponent extends Container {
120
175
  ): void {
121
176
  this.#exitCode = exitCode;
122
177
  this.#status = resolveExecutionStatus(exitCode, cancelled);
178
+ this.#endedAt ??= performance.now();
123
179
  this.#truncation = options?.truncation;
124
180
  this.#artifactError = options?.artifactError;
125
181
  this.#outputPane.finish();
126
182
  if (options?.output !== undefined) {
127
183
  this.#setOutput(options.output);
128
184
  }
185
+ this.#outputVersion++;
129
186
 
130
187
  this.#loader.stop();
131
188
  this.#updateDisplay();
@@ -16,7 +16,10 @@ import { formatArtifactErrorNotice, formatTruncationMetaNotice, type TruncationM
16
16
  import { DynamicBorder } from "../chrome/dynamic-border";
17
17
  import { Ellipsis, truncateToWidth, visibleWidth } from "../utils";
18
18
  import { interruptKey } from "../chrome/keybinding-hints";
19
- import { expandKeyHint } from "../render/render-utils";
19
+ import { DEFAULT_TERMINAL_PREVIEW_LINES, expandKeyHint } from "../render/render-utils";
20
+ import type { TspCardStatus, TspSpan, TspText, TspTone } from "@oh-my-pi/pi-wire";
21
+ import { card, node, span, text } from "../native/describe";
22
+ import type { NativeChild, NativeNode } from "../native/node";
20
23
 
21
24
  /** Output rows shown while an execution is collapsed. */
22
25
  export const PREVIEW_LINES = 20;
@@ -104,6 +107,155 @@ export function buildStatusFooter(opts: {
104
107
  return new Text(`\n${parts.join("\n")}`, 1, 0);
105
108
  }
106
109
 
110
+ const EXECUTION_CARD_STATUS: Record<ExecutionStatus, TspCardStatus> = {
111
+ running: "running",
112
+ complete: "done",
113
+ cancelled: "cancelled",
114
+ error: "error",
115
+ };
116
+
117
+ /** Inputs for {@link describeExecutionCard}. */
118
+ export interface ExecutionCardInput {
119
+ readonly role: string;
120
+ readonly head: TspText;
121
+ /** Excluded from context (`!!`/`$$`): a muted card. */
122
+ readonly muted: boolean;
123
+ readonly status: ExecutionStatus;
124
+ /** `performance.now()` when the run started; drives the live elapsed timer. */
125
+ readonly startedAt: number;
126
+ readonly expanded: boolean;
127
+ /** Body nodes before the output (the eval cell's code). */
128
+ readonly lead?: readonly NativeChild[];
129
+ /** Raw terminal output. */
130
+ readonly output: string;
131
+ readonly images?: readonly NativeChild[];
132
+ readonly exitCode: number | undefined;
133
+ readonly truncation: TruncationMeta | undefined;
134
+ readonly artifactError?: OutputArtifactError;
135
+ }
136
+
137
+ /**
138
+ * Native card shared by user bash/eval runs: the head with a live `elapsed`
139
+ * while running, the output as an `ansi` mini terminal that follows its tail
140
+ * while running, a cancel hint spinner, then exit/truncation notes. The
141
+ * terminal clamps the collapsed body and owns the "N more lines" affordance.
142
+ */
143
+ export function describeExecutionCard(input: ExecutionCardInput): NativeNode {
144
+ const running = input.status === "running";
145
+ const headChildren: NativeChild[] = [text(input.head, { lines: 1, grow: 1 })];
146
+ if (running) {
147
+ headChildren.push(node("elapsed", { age: Math.max(0, Math.round(performance.now() - input.startedAt)) }));
148
+ }
149
+ const children: NativeChild[] = [
150
+ node("row", { gap: "sm", align: "baseline" }, headChildren, "head"),
151
+ ...(input.lead ?? []),
152
+ ];
153
+ if (input.output) {
154
+ children.push(
155
+ node("ansi", { text: input.output, follow: running, preview: { lines: PREVIEW_LINES } }, undefined, "output"),
156
+ );
157
+ }
158
+ if (input.images) children.push(...input.images);
159
+ if (running) {
160
+ children.push(
161
+ node("spinner", { label: [span(`Running… (${interruptKey()} to cancel)`, "muted")] }, undefined, "running"),
162
+ );
163
+ }
164
+ const notes: TspSpan[] = [];
165
+ const note = (value: string, token: string): void => {
166
+ if (notes.length > 0) notes.push(span("\n"));
167
+ notes.push(span(value, token));
168
+ };
169
+ if (input.status === "cancelled") note("(cancelled)", "warning");
170
+ else if (input.status === "error") note(`(exit ${input.exitCode})`, "error");
171
+ if (input.truncation) note(formatTruncationMetaNotice(input.truncation), "warning");
172
+ if (input.artifactError) note(formatArtifactErrorNotice(input.artifactError), "warning");
173
+ if (notes.length > 0) children.push(text(notes, { wrap: "word", key: "notes" }));
174
+ return card(
175
+ {
176
+ role: input.role,
177
+ tone: input.muted ? "muted" : undefined,
178
+ status: EXECUTION_CARD_STATUS[input.status],
179
+ collapsible: true,
180
+ collapsed: !input.expanded,
181
+ preview: { lines: PREVIEW_LINES },
182
+ },
183
+ children,
184
+ );
185
+ }
186
+
187
+ /** Inputs for {@link describeExecutionTool}. */
188
+ export interface ExecutionToolInput {
189
+ readonly role: string;
190
+ /** Tool name for the head icon (`bash`, `eval`). */
191
+ readonly name: string;
192
+ readonly title: string;
193
+ /** The command / cell source, shown once in the head. */
194
+ readonly command: string;
195
+ readonly lang: string;
196
+ /** Excluded from context (`!!`/`$$`). */
197
+ readonly excluded: boolean;
198
+ readonly status: ExecutionStatus;
199
+ /** `performance.now()` when the run started / ended. */
200
+ readonly startedAt: number;
201
+ readonly endedAt: number | undefined;
202
+ readonly expanded: boolean;
203
+ /** Raw terminal output. */
204
+ readonly output: string;
205
+ readonly images?: readonly NativeChild[];
206
+ readonly exitCode: number | undefined;
207
+ readonly truncation: TruncationMeta | undefined;
208
+ readonly artifactError?: OutputArtifactError;
209
+ }
210
+
211
+ /**
212
+ * A user `!`/`$` run as the agent's `tool` frame (§6.6): the command in the
213
+ * head with a `you` badge (plus `not sent` when excluded from context), the
214
+ * timer, exit chip and cancelled note; the body is the output as an `ansi`
215
+ * mini terminal following its tail, then one quiet truncation line.
216
+ */
217
+ export function describeExecutionTool(input: ExecutionToolInput): NativeNode {
218
+ const running = input.status === "running";
219
+ const body: NativeChild[] = [];
220
+ if (input.output) body.push(node("ansi", { text: input.output, follow: running }, undefined, "output"));
221
+ if (input.images) body.push(...input.images);
222
+ const notes: string[] = [];
223
+ if (input.truncation) notes.push(formatTruncationMetaNotice(input.truncation));
224
+ if (input.artifactError) notes.push(formatArtifactErrorNotice(input.artifactError));
225
+ if (notes.length > 0) {
226
+ body.push({
227
+ ...text([span(notes.join(" · "), "muted")], { wrap: "word", role: "omp.tool.notice" }),
228
+ key: "foot",
229
+ });
230
+ }
231
+ const ms = Math.max(0, Math.round((input.endedAt ?? performance.now()) - input.startedAt));
232
+ const badges: { text: string; tone?: TspTone; title?: string }[] = [{ text: "you", tone: "user" }];
233
+ if (input.excluded) badges.push({ text: "not sent", title: "Not sent to the model", tone: "muted" });
234
+ const hasBody = body.length > 0;
235
+ return node(
236
+ "tool",
237
+ {
238
+ role: input.role,
239
+ name: input.name,
240
+ title: input.title,
241
+ target: input.command,
242
+ targetKind: "command",
243
+ lang: input.lang,
244
+ badges,
245
+ status: EXECUTION_CARD_STATUS[input.status],
246
+ age: running ? ms : undefined,
247
+ took: running ? undefined : ms,
248
+ exit: input.status === "error" ? input.exitCode : undefined,
249
+ note: input.status === "cancelled" ? "cancelled" : undefined,
250
+ frame: "card",
251
+ collapsible: hasBody,
252
+ collapsed: hasBody ? !input.expanded : undefined,
253
+ preview: hasBody ? { tail: DEFAULT_TERMINAL_PREVIEW_LINES } : undefined,
254
+ },
255
+ body,
256
+ );
257
+ }
258
+
107
259
  /**
108
260
  * Derive the post-run status from an exit code + cancellation flag using the
109
261
  * same precedence both execution components apply.
@@ -13,6 +13,7 @@ export class HookMessageComponent extends FramedMessageComponent<HookMessage<unk
13
13
  constructor(message: HookMessage<unknown>, customRenderer?: HookMessageRenderer) {
14
14
  super({
15
15
  message,
16
+ role: "omp.hook",
16
17
  collapseAfterLines: HOOK_COLLAPSED_LINES,
17
18
  customRenderer,
18
19
  });