@oh-my-pi/pi-tui 18.2.4 → 18.2.6

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 (748) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +43 -0
  3. package/dist/types/app-keybindings.d.ts +395 -0
  4. package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
  5. package/dist/types/apps/autoresearch-data.d.ts +15 -0
  6. package/dist/types/apps/cleanse-board.d.ts +66 -0
  7. package/dist/types/apps/cleanse-picker.d.ts +23 -0
  8. package/dist/types/apps/debug/log-formatting.d.ts +6 -0
  9. package/dist/types/apps/debug/log-viewer.d.ts +81 -0
  10. package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
  11. package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
  12. package/dist/types/apps/debug/raw-sse.d.ts +26 -0
  13. package/dist/types/apps/debug/terminal-info.d.ts +34 -0
  14. package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
  15. package/dist/types/apps/git/avatar.d.ts +9 -0
  16. package/dist/types/apps/git/colors.d.ts +49 -0
  17. package/dist/types/apps/git/diff-pane.d.ts +188 -0
  18. package/dist/types/apps/git/git-tui.d.ts +66 -0
  19. package/dist/types/apps/git/sidebar.d.ts +149 -0
  20. package/dist/types/apps/git/state.d.ts +117 -0
  21. package/dist/types/apps/if-bench-board.d.ts +75 -0
  22. package/dist/types/apps/live-visualizer.d.ts +33 -0
  23. package/dist/types/apps/ps-data.d.ts +51 -0
  24. package/dist/types/apps/ps-top.d.ts +33 -0
  25. package/dist/types/apps/session-picker.d.ts +26 -0
  26. package/dist/types/apps/setup-model-picker.d.ts +13 -0
  27. package/dist/types/apps/standalone-picker.d.ts +42 -0
  28. package/dist/types/autocomplete.d.ts +4 -0
  29. package/dist/types/chat/advisor-message.d.ts +11 -0
  30. package/dist/types/chat/assistant-message.d.ts +106 -0
  31. package/dist/types/chat/background-tan-message.d.ts +9 -0
  32. package/dist/types/chat/bash-execution.d.ts +47 -0
  33. package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
  34. package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
  35. package/dist/types/chat/collab-prompt-message.d.ts +10 -0
  36. package/dist/types/chat/compaction-summary-message.d.ts +54 -0
  37. package/dist/types/chat/custom-message.d.ts +10 -0
  38. package/dist/types/chat/display-preferences.d.ts +13 -0
  39. package/dist/types/chat/eval-execution.d.ts +29 -0
  40. package/dist/types/chat/execution-shared.d.ts +51 -0
  41. package/dist/types/chat/extension-types.d.ts +28 -0
  42. package/dist/types/chat/hook-message.d.ts +10 -0
  43. package/dist/types/chat/image-loading.d.ts +46 -0
  44. package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
  45. package/dist/types/chat/messages.d.ts +189 -0
  46. package/dist/types/chat/reaction.d.ts +30 -0
  47. package/dist/types/chat/read-target.d.ts +4 -0
  48. package/dist/types/chat/read-tool-group.d.ts +67 -0
  49. package/dist/types/chat/served-model-marker.d.ts +47 -0
  50. package/dist/types/chat/skill-message.d.ts +33 -0
  51. package/dist/types/chat/skill-title-input.d.ts +14 -0
  52. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
  53. package/dist/types/chat/thinking-display.d.ts +12 -0
  54. package/dist/types/chat/todo-reminder.d.ts +13 -0
  55. package/dist/types/chat/tool-execution.d.ts +138 -0
  56. package/dist/types/chat/transcript-browser.d.ts +93 -0
  57. package/dist/types/chat/transcript-entry.d.ts +45 -0
  58. package/dist/types/chat/transcript-outline.d.ts +86 -0
  59. package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
  60. package/dist/types/chat/ttsr-notification.d.ts +23 -0
  61. package/dist/types/chat/user-message.d.ts +52 -0
  62. package/dist/types/chrome/chat-block.d.ts +64 -0
  63. package/dist/types/chrome/collab-qrcode.d.ts +28 -0
  64. package/dist/types/chrome/context-thresholds.d.ts +10 -0
  65. package/dist/types/chrome/countdown-timer.d.ts +14 -0
  66. package/dist/types/chrome/diff.d.ts +14 -0
  67. package/dist/types/chrome/dynamic-border.d.ts +16 -0
  68. package/dist/types/chrome/error-block.d.ts +17 -0
  69. package/dist/types/chrome/form-theme.d.ts +3 -0
  70. package/dist/types/chrome/format.d.ts +12 -0
  71. package/dist/types/chrome/index.d.ts +20 -0
  72. package/dist/types/chrome/keybinding-hints.d.ts +40 -0
  73. package/dist/types/chrome/live-board.d.ts +23 -0
  74. package/dist/types/chrome/local-date.d.ts +4 -0
  75. package/dist/types/chrome/message-divider.d.ts +22 -0
  76. package/dist/types/chrome/message-frame.d.ts +52 -0
  77. package/dist/types/chrome/message-notice.d.ts +35 -0
  78. package/dist/types/chrome/overlay-box.d.ts +74 -0
  79. package/dist/types/chrome/qrcode.d.ts +48 -0
  80. package/dist/types/chrome/segment-track.d.ts +27 -0
  81. package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
  82. package/dist/types/chrome/selector-helpers.d.ts +52 -0
  83. package/dist/types/chrome/shared.d.ts +5 -0
  84. package/dist/types/chrome/streaming-panel.d.ts +19 -0
  85. package/dist/types/chrome/tool-activity.d.ts +17 -0
  86. package/dist/types/chrome/transcript-container.d.ts +129 -0
  87. package/dist/types/chrome/transcript-status.d.ts +12 -0
  88. package/dist/types/chrome/visual-truncate.d.ts +19 -0
  89. package/dist/types/components/disclosure.d.ts +42 -0
  90. package/dist/types/components/form.d.ts +130 -0
  91. package/dist/types/components/key-value-list.d.ts +24 -0
  92. package/dist/types/components/layout/geometry.d.ts +73 -0
  93. package/dist/types/components/layout/row.d.ts +55 -0
  94. package/dist/types/components/layout/split-pane.d.ts +80 -0
  95. package/dist/types/components/layout/stack.d.ts +50 -0
  96. package/dist/types/components/menu-selection.d.ts +71 -0
  97. package/dist/types/components/metric.d.ts +44 -0
  98. package/dist/types/components/progress-bar.d.ts +36 -0
  99. package/dist/types/components/scroll-view.d.ts +53 -19
  100. package/dist/types/components/scroll-viewport.d.ts +46 -0
  101. package/dist/types/components/section.d.ts +19 -0
  102. package/dist/types/components/select-list.d.ts +46 -2
  103. package/dist/types/components/settings-list.d.ts +20 -0
  104. package/dist/types/components/table.d.ts +32 -0
  105. package/dist/types/components/tree-view.d.ts +130 -0
  106. package/dist/types/components/wizard-step.d.ts +60 -0
  107. package/dist/types/hotkeys-markdown.d.ts +7 -0
  108. package/dist/types/index.d.ts +17 -0
  109. package/dist/types/keybinding-matchers.d.ts +35 -0
  110. package/dist/types/keys.d.ts +2 -2
  111. package/dist/types/lang-from-path.d.ts +9 -0
  112. package/dist/types/overlays/advisor-config.d.ts +114 -0
  113. package/dist/types/overlays/agent-activity.d.ts +41 -0
  114. package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
  115. package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
  116. package/dist/types/overlays/agent-hub-types.d.ts +99 -0
  117. package/dist/types/overlays/agent-hub.d.ts +126 -0
  118. package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
  119. package/dist/types/overlays/agents-hub.d.ts +72 -0
  120. package/dist/types/overlays/ask-dialog.d.ts +88 -0
  121. package/dist/types/overlays/bordered-loader.d.ts +11 -0
  122. package/dist/types/overlays/btw-history-panel.d.ts +29 -0
  123. package/dist/types/overlays/btw-history.d.ts +17 -0
  124. package/dist/types/overlays/btw-panel.d.ts +26 -0
  125. package/dist/types/overlays/cleanse-panel.d.ts +36 -0
  126. package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
  127. package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
  128. package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
  129. package/dist/types/overlays/copy-selector.d.ts +48 -0
  130. package/dist/types/overlays/copy-targets.d.ts +63 -0
  131. package/dist/types/overlays/error-banner.d.ts +13 -0
  132. package/dist/types/overlays/extensions/display-text.d.ts +10 -0
  133. package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
  134. package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
  135. package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
  136. package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
  137. package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
  138. package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
  139. package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
  140. package/dist/types/overlays/extensions/types.d.ts +165 -0
  141. package/dist/types/overlays/history-search.d.ts +16 -0
  142. package/dist/types/overlays/hook-editor.d.ts +44 -0
  143. package/dist/types/overlays/hook-input.d.ts +20 -0
  144. package/dist/types/overlays/hook-selector.d.ts +71 -0
  145. package/dist/types/overlays/hub-frame.d.ts +77 -0
  146. package/dist/types/overlays/login-dialog.d.ts +43 -0
  147. package/dist/types/overlays/logout-account-selector.d.ts +15 -0
  148. package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
  149. package/dist/types/overlays/model-browser.d.ts +213 -0
  150. package/dist/types/overlays/model-hub.d.ts +72 -0
  151. package/dist/types/overlays/model-picker.d.ts +74 -0
  152. package/dist/types/overlays/model-selector.d.ts +52 -0
  153. package/dist/types/overlays/move-overlay.d.ts +39 -0
  154. package/dist/types/overlays/oauth-selector.d.ts +41 -0
  155. package/dist/types/overlays/omfg-panel.d.ts +20 -0
  156. package/dist/types/overlays/pause-screen.d.ts +43 -0
  157. package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
  158. package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
  159. package/dist/types/overlays/plan-toc.d.ts +41 -0
  160. package/dist/types/overlays/plugin-selector.d.ts +28 -0
  161. package/dist/types/overlays/plugin-settings.d.ts +180 -0
  162. package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
  163. package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
  164. package/dist/types/overlays/rewind-selector.d.ts +61 -0
  165. package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
  166. package/dist/types/overlays/session-account-selector.d.ts +24 -0
  167. package/dist/types/overlays/session-info-overlay.d.ts +17 -0
  168. package/dist/types/overlays/session-observer-registry.d.ts +77 -0
  169. package/dist/types/overlays/session-selector.d.ts +131 -0
  170. package/dist/types/overlays/settings-defs.d.ts +119 -0
  171. package/dist/types/overlays/settings-selector.d.ts +73 -0
  172. package/dist/types/overlays/show-images-selector.d.ts +11 -0
  173. package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
  174. package/dist/types/overlays/theme-selector.d.ts +12 -0
  175. package/dist/types/overlays/thinking-selector.d.ts +12 -0
  176. package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
  177. package/dist/types/overlays/tree-selector.d.ts +92 -0
  178. package/dist/types/overlays/usage-dashboard.d.ts +94 -0
  179. package/dist/types/overlays/usage-display.d.ts +5 -0
  180. package/dist/types/overlays/usage-row.d.ts +17 -0
  181. package/dist/types/prompt/attachment-chips.d.ts +19 -0
  182. package/dist/types/prompt/composer-attachments.d.ts +71 -0
  183. package/dist/types/prompt/composer-cache.d.ts +37 -0
  184. package/dist/types/prompt/composer.d.ts +159 -0
  185. package/dist/types/prompt/custom-editor.d.ts +261 -0
  186. package/dist/types/prompt/editor-top-gap.d.ts +18 -0
  187. package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
  188. package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
  189. package/dist/types/prompt/gradient-highlight.d.ts +32 -0
  190. package/dist/types/prompt/image-format.d.ts +4 -0
  191. package/dist/types/prompt/image-references.d.ts +26 -0
  192. package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
  193. package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
  194. package/dist/types/prompt/macos-spelling.d.ts +44 -0
  195. package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
  196. package/dist/types/prompt/magic-keywords.d.ts +26 -0
  197. package/dist/types/prompt/markdown-prose.d.ts +15 -0
  198. package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
  199. package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
  200. package/dist/types/prompt/orchestrate.d.ts +13 -0
  201. package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
  202. package/dist/types/prompt/queue-input.d.ts +8 -0
  203. package/dist/types/prompt/skill-tokens.d.ts +13 -0
  204. package/dist/types/prompt/tools-markdown.d.ts +12 -0
  205. package/dist/types/prompt/ultrathink.d.ts +13 -0
  206. package/dist/types/prompt/usage-amounts.d.ts +26 -0
  207. package/dist/types/prompt/video.d.ts +18 -0
  208. package/dist/types/prompt/welcome.d.ts +83 -0
  209. package/dist/types/prompt/workflow.d.ts +13 -0
  210. package/dist/types/render/code-cell.d.ts +51 -0
  211. package/dist/types/render/file-list.d.ts +22 -0
  212. package/dist/types/render/hyperlink.d.ts +80 -0
  213. package/dist/types/render/index.d.ts +16 -0
  214. package/dist/types/render/output-block.d.ts +54 -0
  215. package/dist/types/render/output-pane.d.ts +80 -0
  216. package/dist/types/render/render-utils.d.ts +312 -0
  217. package/dist/types/render/sixel.d.ts +21 -0
  218. package/dist/types/render/status-line.d.ts +23 -0
  219. package/dist/types/render/tool-card.d.ts +71 -0
  220. package/dist/types/render/tree-list.d.ts +29 -0
  221. package/dist/types/render/types.d.ts +17 -0
  222. package/dist/types/render/utils.d.ts +43 -0
  223. package/dist/types/render/width-aware-text.d.ts +23 -0
  224. package/dist/types/setup/lazy.d.ts +3 -0
  225. package/dist/types/setup/scenes/composer.d.ts +3 -0
  226. package/dist/types/setup/scenes/glyph.d.ts +3 -0
  227. package/dist/types/setup/scenes/model.d.ts +3 -0
  228. package/dist/types/setup/scenes/outro.d.ts +2 -0
  229. package/dist/types/setup/scenes/providers.d.ts +3 -0
  230. package/dist/types/setup/scenes/sign-in.d.ts +21 -0
  231. package/dist/types/setup/scenes/splash.d.ts +11 -0
  232. package/dist/types/setup/scenes/theme.d.ts +3 -0
  233. package/dist/types/setup/scenes/types.d.ts +110 -0
  234. package/dist/types/setup/scenes/web-search.d.ts +22 -0
  235. package/dist/types/setup/setup-version.d.ts +11 -0
  236. package/dist/types/setup/startup-splash.d.ts +9 -0
  237. package/dist/types/setup/wizard-overlay.d.ts +16 -0
  238. package/dist/types/setup/wizard.d.ts +25 -0
  239. package/dist/types/status-line/component.d.ts +182 -0
  240. package/dist/types/status-line/context-usage.d.ts +156 -0
  241. package/dist/types/status-line/footer.d.ts +30 -0
  242. package/dist/types/status-line/git-utils.d.ts +22 -0
  243. package/dist/types/status-line/host.d.ts +129 -0
  244. package/dist/types/status-line/index.d.ts +6 -0
  245. package/dist/types/status-line/loop.d.ts +18 -0
  246. package/dist/types/status-line/metrics.d.ts +21 -0
  247. package/dist/types/status-line/presets.d.ts +3 -0
  248. package/dist/types/status-line/schema.d.ts +15 -0
  249. package/dist/types/status-line/segments.d.ts +5 -0
  250. package/dist/types/status-line/separators.d.ts +3 -0
  251. package/dist/types/status-line/types.d.ts +236 -0
  252. package/dist/types/stdin-buffer.d.ts +4 -0
  253. package/dist/types/theme/color.d.ts +24 -0
  254. package/dist/types/theme/defaults/index.d.ts +9489 -0
  255. package/dist/types/theme/index.d.ts +4 -0
  256. package/dist/types/theme/loader.d.ts +23 -0
  257. package/dist/types/theme/mermaid-cache.d.ts +26 -0
  258. package/dist/types/theme/schema-validation.d.ts +3 -0
  259. package/dist/types/theme/schema.d.ts +25 -0
  260. package/dist/types/theme/session-color.d.ts +43 -0
  261. package/dist/types/theme/shimmer.d.ts +44 -0
  262. package/dist/types/theme/symbols.d.ts +34 -0
  263. package/dist/types/theme/theme-class.d.ts +299 -0
  264. package/dist/types/theme/theme.d.ts +111 -0
  265. package/dist/types/theme/tui-adapters.d.ts +30 -0
  266. package/dist/types/thinking.d.ts +165 -0
  267. package/dist/types/tools/agent-tree.d.ts +36 -0
  268. package/dist/types/tools/ask.d.ts +67 -0
  269. package/dist/types/tools/ast-edit.d.ts +53 -0
  270. package/dist/types/tools/ast-grep.d.ts +52 -0
  271. package/dist/types/tools/autoresearch.d.ts +156 -0
  272. package/dist/types/tools/bash-interactive.d.ts +28 -0
  273. package/dist/types/tools/bash.d.ts +103 -0
  274. package/dist/types/tools/conflict-detect.d.ts +43 -0
  275. package/dist/types/tools/debug.d.ts +60 -0
  276. package/dist/types/tools/default-renderer.d.ts +24 -0
  277. package/dist/types/tools/edit.d.ts +158 -0
  278. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
  279. package/dist/types/tools/eval-format/index.d.ts +5 -0
  280. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  281. package/dist/types/tools/eval-format/python.d.ts +2 -0
  282. package/dist/types/tools/eval.d.ts +94 -0
  283. package/dist/types/tools/fetch.d.ts +42 -0
  284. package/dist/types/tools/gh-format.d.ts +8 -0
  285. package/dist/types/tools/github.d.ts +144 -0
  286. package/dist/types/tools/glob.d.ts +44 -0
  287. package/dist/types/tools/goal.d.ts +42 -0
  288. package/dist/types/tools/grep.d.ts +64 -0
  289. package/dist/types/tools/grouped-file-output.d.ts +71 -0
  290. package/dist/types/tools/hashline-format.d.ts +28 -0
  291. package/dist/types/tools/hub.d.ts +340 -0
  292. package/dist/types/tools/index.d.ts +4 -0
  293. package/dist/types/tools/json-tree.d.ts +59 -0
  294. package/dist/types/tools/line-ranges.d.ts +29 -0
  295. package/dist/types/tools/list-limit.d.ts +15 -0
  296. package/dist/types/tools/lsp.d.ts +72 -0
  297. package/dist/types/tools/match-line-format.d.ts +11 -0
  298. package/dist/types/tools/mcp.d.ts +81 -0
  299. package/dist/types/tools/memory.d.ts +67 -0
  300. package/dist/types/tools/output-meta.d.ts +151 -0
  301. package/dist/types/tools/read.d.ts +139 -0
  302. package/dist/types/tools/renderer.d.ts +98 -0
  303. package/dist/types/tools/report-tool-issue.d.ts +8 -0
  304. package/dist/types/tools/resolve.d.ts +47 -0
  305. package/dist/types/tools/streaming-output.d.ts +282 -0
  306. package/dist/types/tools/subprocess.d.ts +24 -0
  307. package/dist/types/tools/task-repair-args.d.ts +53 -0
  308. package/dist/types/tools/task-yield-assembly.d.ts +20 -0
  309. package/dist/types/tools/task.d.ts +425 -0
  310. package/dist/types/tools/terminal-output.d.ts +8 -0
  311. package/dist/types/tools/think.d.ts +13 -0
  312. package/dist/types/tools/todo.d.ts +118 -0
  313. package/dist/types/tools/tool-errors.d.ts +7 -0
  314. package/dist/types/tools/vibe.d.ts +120 -0
  315. package/dist/types/tools/web-search.d.ts +195 -0
  316. package/dist/types/tools/write.d.ts +76 -0
  317. package/dist/types/tools/xd-url.d.ts +13 -0
  318. package/dist/types/tools/xdev.d.ts +52 -0
  319. package/dist/types/tui.d.ts +18 -0
  320. package/dist/types/utils.d.ts +2 -0
  321. package/package.json +37 -3
  322. package/src/app-keybindings.ts +760 -0
  323. package/src/apps/autoresearch-dashboard.ts +500 -0
  324. package/src/apps/autoresearch-data.ts +64 -0
  325. package/src/apps/cleanse-board.ts +341 -0
  326. package/src/apps/cleanse-picker.ts +57 -0
  327. package/src/apps/debug/log-formatting.ts +53 -0
  328. package/src/apps/debug/log-viewer.ts +868 -0
  329. package/src/apps/debug/protocol-probe.ts +267 -0
  330. package/src/apps/debug/raw-sse-buffer.ts +449 -0
  331. package/src/apps/debug/raw-sse.ts +282 -0
  332. package/src/apps/debug/terminal-info.ts +115 -0
  333. package/src/apps/debug/viewer-frame.ts +174 -0
  334. package/src/apps/git/avatar.ts +52 -0
  335. package/src/apps/git/colors.ts +109 -0
  336. package/src/apps/git/diff-pane.ts +1512 -0
  337. package/src/apps/git/git-tui.ts +889 -0
  338. package/src/apps/git/sidebar.ts +1204 -0
  339. package/src/apps/git/state.ts +113 -0
  340. package/src/apps/if-bench-board.ts +324 -0
  341. package/src/apps/live-visualizer.ts +238 -0
  342. package/src/apps/ps-data.ts +116 -0
  343. package/src/apps/ps-top.ts +422 -0
  344. package/src/apps/session-picker.ts +95 -0
  345. package/src/apps/setup-model-picker.ts +21 -0
  346. package/src/apps/standalone-picker.ts +111 -0
  347. package/src/autocomplete.ts +9 -13
  348. package/src/chat/advisor-message.ts +175 -0
  349. package/src/chat/assistant-message.ts +1214 -0
  350. package/src/chat/background-tan-message.ts +35 -0
  351. package/src/chat/bash-execution.ts +394 -0
  352. package/src/chat/cache-invalidation-marker.ts +94 -0
  353. package/src/chat/chat-transcript-builder.ts +593 -0
  354. package/src/chat/collab-prompt-message.ts +34 -0
  355. package/src/chat/compaction-summary-message.ts +261 -0
  356. package/src/chat/custom-message.ts +23 -0
  357. package/src/chat/display-preferences.ts +24 -0
  358. package/src/chat/eval-execution.ts +172 -0
  359. package/src/chat/execution-shared.ts +113 -0
  360. package/src/chat/extension-types.ts +46 -0
  361. package/src/chat/hook-message.ts +20 -0
  362. package/src/chat/image-loading.ts +122 -0
  363. package/src/chat/late-diagnostics-message.ts +92 -0
  364. package/src/chat/messages.ts +305 -0
  365. package/src/chat/reaction.ts +65 -0
  366. package/src/chat/read-target.ts +7 -0
  367. package/src/chat/read-tool-group.ts +873 -0
  368. package/src/chat/served-model-marker.ts +91 -0
  369. package/src/chat/skill-message.ts +220 -0
  370. package/src/chat/skill-title-input.ts +41 -0
  371. package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
  372. package/src/chat/thinking-display.ts +312 -0
  373. package/src/chat/todo-reminder.ts +33 -0
  374. package/src/chat/tool-execution.ts +1395 -0
  375. package/src/chat/transcript-browser.ts +268 -0
  376. package/src/chat/transcript-entry.ts +96 -0
  377. package/src/chat/transcript-outline.ts +260 -0
  378. package/src/chat/transcript-render-helpers.ts +315 -0
  379. package/src/chat/ttsr-notification.ts +123 -0
  380. package/src/chat/user-message.ts +287 -0
  381. package/src/chrome/chat-block.ts +110 -0
  382. package/src/chrome/collab-qrcode.ts +74 -0
  383. package/src/chrome/context-thresholds.ts +85 -0
  384. package/src/chrome/countdown-timer.ts +74 -0
  385. package/src/chrome/diff.ts +266 -0
  386. package/src/chrome/dynamic-border.ts +36 -0
  387. package/src/chrome/error-block.ts +63 -0
  388. package/src/chrome/form-theme.ts +10 -0
  389. package/src/chrome/format.ts +66 -0
  390. package/src/chrome/index.ts +21 -0
  391. package/src/chrome/keybinding-hints.ts +55 -0
  392. package/src/chrome/live-board.ts +123 -0
  393. package/src/chrome/local-date.ts +20 -0
  394. package/src/chrome/message-divider.ts +51 -0
  395. package/src/chrome/message-frame.ts +164 -0
  396. package/src/chrome/message-notice.ts +103 -0
  397. package/src/chrome/overlay-box.ts +301 -0
  398. package/src/chrome/qrcode.ts +539 -0
  399. package/src/chrome/segment-track.ts +86 -0
  400. package/src/chrome/select-list-mouse-routing.ts +11 -0
  401. package/src/chrome/selector-helpers.ts +119 -0
  402. package/src/chrome/shared.ts +27 -0
  403. package/src/chrome/streaming-panel.ts +72 -0
  404. package/src/chrome/tool-activity.ts +44 -0
  405. package/src/chrome/transcript-container.ts +902 -0
  406. package/src/chrome/transcript-status.ts +27 -0
  407. package/src/chrome/visual-truncate.ts +70 -0
  408. package/src/components/disclosure.ts +235 -0
  409. package/src/components/editor.ts +10 -12
  410. package/src/components/form.ts +566 -0
  411. package/src/components/input.ts +6 -27
  412. package/src/components/key-value-list.ts +88 -0
  413. package/src/components/layout/geometry.ts +202 -0
  414. package/src/components/layout/row.ts +283 -0
  415. package/src/components/layout/split-pane.ts +349 -0
  416. package/src/components/layout/stack.ts +261 -0
  417. package/src/components/markdown.ts +16 -12
  418. package/src/components/menu-selection.ts +248 -0
  419. package/src/components/metric.ts +132 -0
  420. package/src/components/progress-bar.ts +140 -0
  421. package/src/components/scroll-view.ts +372 -70
  422. package/src/components/scroll-viewport.ts +154 -0
  423. package/src/components/section.ts +77 -0
  424. package/src/components/select-list.ts +238 -159
  425. package/src/components/settings-list.ts +103 -80
  426. package/src/components/table.ts +127 -0
  427. package/src/components/tree-view.ts +481 -0
  428. package/src/components/wizard-step.ts +229 -0
  429. package/src/deccara.ts +1 -3
  430. package/src/hotkeys-markdown.ts +100 -0
  431. package/src/index.ts +18 -0
  432. package/src/keybinding-matchers.ts +99 -0
  433. package/src/keys.ts +2 -2
  434. package/src/lang-from-path.ts +252 -0
  435. package/src/overlays/advisor-config.ts +761 -0
  436. package/src/overlays/agent-activity.ts +113 -0
  437. package/src/overlays/agent-hub-projection.ts +250 -0
  438. package/src/overlays/agent-hub-renderer.ts +281 -0
  439. package/src/overlays/agent-hub-types.ts +74 -0
  440. package/src/overlays/agent-hub.ts +1562 -0
  441. package/src/overlays/agent-transcript-viewer.ts +669 -0
  442. package/src/overlays/agents-hub.ts +1203 -0
  443. package/src/overlays/ask-dialog.ts +1202 -0
  444. package/src/overlays/bordered-loader.ts +41 -0
  445. package/src/overlays/btw-history-panel.ts +591 -0
  446. package/src/overlays/btw-history.ts +33 -0
  447. package/src/overlays/btw-panel.ts +167 -0
  448. package/src/overlays/cleanse-panel.ts +167 -0
  449. package/src/overlays/codex-reset-fireworks.ts +375 -0
  450. package/src/overlays/composer-shape-preview.ts +137 -0
  451. package/src/overlays/composer-shape-registry.ts +82 -0
  452. package/src/overlays/copy-selector.ts +618 -0
  453. package/src/overlays/copy-targets.ts +210 -0
  454. package/src/overlays/error-banner.ts +39 -0
  455. package/src/overlays/extensions/display-text.ts +39 -0
  456. package/src/overlays/extensions/extension-dashboard.ts +645 -0
  457. package/src/overlays/extensions/extension-list.ts +667 -0
  458. package/src/overlays/extensions/inspector-model.ts +484 -0
  459. package/src/overlays/extensions/inspector-panel.ts +669 -0
  460. package/src/overlays/extensions/live-tool-session.ts +67 -0
  461. package/src/overlays/extensions/mcp-runtime.ts +289 -0
  462. package/src/overlays/extensions/state-manager.ts +337 -0
  463. package/src/overlays/extensions/types.ts +208 -0
  464. package/src/overlays/history-search.ts +268 -0
  465. package/src/overlays/hook-editor.ts +272 -0
  466. package/src/overlays/hook-input.ts +84 -0
  467. package/src/overlays/hook-selector.ts +635 -0
  468. package/src/overlays/hub-frame.ts +262 -0
  469. package/src/overlays/login-dialog.ts +231 -0
  470. package/src/overlays/logout-account-selector.ts +115 -0
  471. package/src/overlays/mcp-add-wizard.ts +1436 -0
  472. package/src/overlays/model-browser.ts +1176 -0
  473. package/src/overlays/model-hub.ts +2279 -0
  474. package/src/overlays/model-picker.ts +285 -0
  475. package/src/overlays/model-selector.ts +107 -0
  476. package/src/overlays/move-overlay.ts +197 -0
  477. package/src/overlays/oauth-selector.ts +446 -0
  478. package/src/overlays/omfg-panel.ts +136 -0
  479. package/src/overlays/pause-screen.ts +198 -0
  480. package/src/overlays/plan-review-overlay.ts +1227 -0
  481. package/src/overlays/plan-save-overlay.ts +74 -0
  482. package/src/overlays/plan-toc.ts +139 -0
  483. package/src/overlays/plugin-selector.ts +97 -0
  484. package/src/overlays/plugin-settings.ts +839 -0
  485. package/src/overlays/queue-mode-selector.ts +55 -0
  486. package/src/overlays/reset-usage-selector.ts +170 -0
  487. package/src/overlays/rewind-selector.ts +499 -0
  488. package/src/overlays/running-subagent-badge.ts +19 -0
  489. package/src/overlays/session-account-selector.ts +68 -0
  490. package/src/overlays/session-info-overlay.ts +96 -0
  491. package/src/overlays/session-observer-registry.ts +289 -0
  492. package/src/overlays/session-selector.ts +1137 -0
  493. package/src/overlays/settings-defs.ts +315 -0
  494. package/src/overlays/settings-selector.ts +1415 -0
  495. package/src/overlays/show-images-selector.ts +44 -0
  496. package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
  497. package/src/overlays/snapcompact-shape-preview.ts +192 -0
  498. package/src/overlays/theme-selector.ts +62 -0
  499. package/src/overlays/thinking-selector.ts +51 -0
  500. package/src/overlays/tiny-title-download-progress.ts +102 -0
  501. package/src/overlays/tree-selector.ts +1121 -0
  502. package/src/overlays/usage-dashboard.ts +629 -0
  503. package/src/overlays/usage-display.ts +45 -0
  504. package/src/overlays/usage-row.ts +118 -0
  505. package/src/prompt/attachment-chips.ts +201 -0
  506. package/src/prompt/composer-attachments.ts +280 -0
  507. package/src/prompt/composer-cache.ts +304 -0
  508. package/src/prompt/composer.ts +934 -0
  509. package/src/prompt/custom-editor.ts +1392 -0
  510. package/src/prompt/data/emojis.json +1 -0
  511. package/src/prompt/editor-top-gap.ts +28 -0
  512. package/src/prompt/emoji-autocomplete.ts +285 -0
  513. package/src/prompt/github-ref-autocomplete.ts +75 -0
  514. package/src/prompt/gradient-highlight.ts +101 -0
  515. package/src/prompt/image-format.ts +28 -0
  516. package/src/prompt/image-references.ts +102 -0
  517. package/src/prompt/interactive-context-helpers.ts +126 -0
  518. package/src/prompt/internal-url-autocomplete.ts +155 -0
  519. package/src/prompt/macos-spelling.ts +456 -0
  520. package/src/prompt/magic-keyword-boundary.ts +23 -0
  521. package/src/prompt/magic-keywords.ts +42 -0
  522. package/src/prompt/markdown-prose.ts +245 -0
  523. package/src/prompt/model-mention-autocomplete.ts +94 -0
  524. package/src/prompt/model-mention-syntax.ts +41 -0
  525. package/src/prompt/orchestrate.ts +26 -0
  526. package/src/prompt/prompt-action-autocomplete.ts +337 -0
  527. package/src/prompt/queue-input.ts +132 -0
  528. package/src/prompt/skill-tokens.ts +36 -0
  529. package/src/prompt/tips.txt +27 -0
  530. package/src/prompt/tools-markdown.ts +29 -0
  531. package/src/prompt/ultrathink.ts +26 -0
  532. package/src/prompt/usage-amounts.ts +99 -0
  533. package/src/prompt/video.ts +47 -0
  534. package/src/prompt/welcome.ts +605 -0
  535. package/src/prompt/workflow.ts +26 -0
  536. package/src/render/code-cell.ts +244 -0
  537. package/src/render/file-list.ts +58 -0
  538. package/src/render/hyperlink.ts +192 -0
  539. package/src/render/index.ts +17 -0
  540. package/src/render/output-block.ts +267 -0
  541. package/src/render/output-pane.ts +299 -0
  542. package/src/render/render-utils.ts +1182 -0
  543. package/src/render/sixel.ts +69 -0
  544. package/src/render/status-line.ts +55 -0
  545. package/src/render/tool-card.ts +272 -0
  546. package/src/render/tree-list.ts +188 -0
  547. package/src/render/types.ts +19 -0
  548. package/src/render/utils.ts +109 -0
  549. package/src/render/width-aware-text.ts +60 -0
  550. package/src/setup/lazy.ts +17 -0
  551. package/src/setup/scenes/composer.ts +127 -0
  552. package/src/setup/scenes/glyph.ts +120 -0
  553. package/src/setup/scenes/model.ts +138 -0
  554. package/src/setup/scenes/outro.ts +24 -0
  555. package/src/setup/scenes/providers.ts +107 -0
  556. package/src/setup/scenes/sign-in.ts +373 -0
  557. package/src/setup/scenes/splash.ts +192 -0
  558. package/src/setup/scenes/theme.ts +343 -0
  559. package/src/setup/scenes/types.ts +111 -0
  560. package/src/setup/scenes/web-search.ts +165 -0
  561. package/src/setup/setup-version.ts +11 -0
  562. package/src/setup/startup-splash.ts +107 -0
  563. package/src/setup/wizard-overlay.ts +319 -0
  564. package/src/setup/wizard.ts +101 -0
  565. package/src/status-line/component.ts +3003 -0
  566. package/src/status-line/context-usage.ts +688 -0
  567. package/src/status-line/footer.ts +323 -0
  568. package/src/status-line/git-utils.ts +42 -0
  569. package/src/status-line/host.ts +97 -0
  570. package/src/status-line/index.ts +6 -0
  571. package/src/status-line/loop.ts +28 -0
  572. package/src/status-line/metrics.ts +94 -0
  573. package/src/status-line/presets.ts +107 -0
  574. package/src/status-line/schema.ts +57 -0
  575. package/src/status-line/segments.ts +934 -0
  576. package/src/status-line/separators.ts +55 -0
  577. package/src/status-line/types.ts +227 -0
  578. package/src/stdin-buffer.ts +4 -2
  579. package/src/theme/color.ts +95 -0
  580. package/src/theme/dark.json +95 -0
  581. package/src/theme/defaults/alabaster.json +93 -0
  582. package/src/theme/defaults/amethyst.json +96 -0
  583. package/src/theme/defaults/anthracite.json +93 -0
  584. package/src/theme/defaults/basalt.json +91 -0
  585. package/src/theme/defaults/birch.json +95 -0
  586. package/src/theme/defaults/dark-abyss.json +91 -0
  587. package/src/theme/defaults/dark-arctic.json +104 -0
  588. package/src/theme/defaults/dark-aurora.json +95 -0
  589. package/src/theme/defaults/dark-catppuccin.json +107 -0
  590. package/src/theme/defaults/dark-cavern.json +91 -0
  591. package/src/theme/defaults/dark-celestial.json +106 -0
  592. package/src/theme/defaults/dark-copper.json +95 -0
  593. package/src/theme/defaults/dark-cosmos.json +90 -0
  594. package/src/theme/defaults/dark-cyberpunk.json +102 -0
  595. package/src/theme/defaults/dark-dracula.json +98 -0
  596. package/src/theme/defaults/dark-eclipse.json +91 -0
  597. package/src/theme/defaults/dark-ember.json +95 -0
  598. package/src/theme/defaults/dark-equinox.json +90 -0
  599. package/src/theme/defaults/dark-forest.json +96 -0
  600. package/src/theme/defaults/dark-github.json +105 -0
  601. package/src/theme/defaults/dark-gruvbox.json +112 -0
  602. package/src/theme/defaults/dark-lavender.json +95 -0
  603. package/src/theme/defaults/dark-lunar.json +89 -0
  604. package/src/theme/defaults/dark-midnight.json +95 -0
  605. package/src/theme/defaults/dark-monochrome.json +94 -0
  606. package/src/theme/defaults/dark-monokai.json +98 -0
  607. package/src/theme/defaults/dark-nebula.json +90 -0
  608. package/src/theme/defaults/dark-nord.json +98 -0
  609. package/src/theme/defaults/dark-ocean.json +101 -0
  610. package/src/theme/defaults/dark-one.json +100 -0
  611. package/src/theme/defaults/dark-poimandres.json +115 -0
  612. package/src/theme/defaults/dark-rainforest.json +91 -0
  613. package/src/theme/defaults/dark-reef.json +91 -0
  614. package/src/theme/defaults/dark-retro.json +92 -0
  615. package/src/theme/defaults/dark-rose-pine.json +96 -0
  616. package/src/theme/defaults/dark-sakura.json +95 -0
  617. package/src/theme/defaults/dark-slate.json +95 -0
  618. package/src/theme/defaults/dark-solarized.json +97 -0
  619. package/src/theme/defaults/dark-solstice.json +90 -0
  620. package/src/theme/defaults/dark-starfall.json +91 -0
  621. package/src/theme/defaults/dark-sunset.json +99 -0
  622. package/src/theme/defaults/dark-swamp.json +90 -0
  623. package/src/theme/defaults/dark-synthwave.json +103 -0
  624. package/src/theme/defaults/dark-taiga.json +91 -0
  625. package/src/theme/defaults/dark-terminal.json +95 -0
  626. package/src/theme/defaults/dark-tokyo-night.json +101 -0
  627. package/src/theme/defaults/dark-tundra.json +91 -0
  628. package/src/theme/defaults/dark-twilight.json +91 -0
  629. package/src/theme/defaults/dark-volcanic.json +91 -0
  630. package/src/theme/defaults/graphite.json +92 -0
  631. package/src/theme/defaults/index.ts +201 -0
  632. package/src/theme/defaults/light-arctic.json +107 -0
  633. package/src/theme/defaults/light-aurora-day.json +91 -0
  634. package/src/theme/defaults/light-canyon.json +91 -0
  635. package/src/theme/defaults/light-catppuccin.json +106 -0
  636. package/src/theme/defaults/light-cirrus.json +90 -0
  637. package/src/theme/defaults/light-coral.json +95 -0
  638. package/src/theme/defaults/light-cyberpunk.json +96 -0
  639. package/src/theme/defaults/light-dawn.json +90 -0
  640. package/src/theme/defaults/light-dunes.json +91 -0
  641. package/src/theme/defaults/light-eucalyptus.json +95 -0
  642. package/src/theme/defaults/light-forest.json +100 -0
  643. package/src/theme/defaults/light-frost.json +95 -0
  644. package/src/theme/defaults/light-github.json +115 -0
  645. package/src/theme/defaults/light-glacier.json +91 -0
  646. package/src/theme/defaults/light-gruvbox.json +108 -0
  647. package/src/theme/defaults/light-haze.json +90 -0
  648. package/src/theme/defaults/light-honeycomb.json +95 -0
  649. package/src/theme/defaults/light-lagoon.json +91 -0
  650. package/src/theme/defaults/light-lavender.json +95 -0
  651. package/src/theme/defaults/light-meadow.json +91 -0
  652. package/src/theme/defaults/light-mint.json +95 -0
  653. package/src/theme/defaults/light-monochrome.json +101 -0
  654. package/src/theme/defaults/light-ocean.json +99 -0
  655. package/src/theme/defaults/light-one.json +99 -0
  656. package/src/theme/defaults/light-opal.json +91 -0
  657. package/src/theme/defaults/light-orchard.json +91 -0
  658. package/src/theme/defaults/light-paper.json +95 -0
  659. package/src/theme/defaults/light-poimandres.json +115 -0
  660. package/src/theme/defaults/light-prism.json +90 -0
  661. package/src/theme/defaults/light-retro.json +98 -0
  662. package/src/theme/defaults/light-sand.json +95 -0
  663. package/src/theme/defaults/light-savanna.json +91 -0
  664. package/src/theme/defaults/light-solarized.json +102 -0
  665. package/src/theme/defaults/light-soleil.json +90 -0
  666. package/src/theme/defaults/light-sunset.json +99 -0
  667. package/src/theme/defaults/light-synthwave.json +98 -0
  668. package/src/theme/defaults/light-tokyo-night.json +111 -0
  669. package/src/theme/defaults/light-wetland.json +91 -0
  670. package/src/theme/defaults/light-zenith.json +89 -0
  671. package/src/theme/defaults/limestone.json +94 -0
  672. package/src/theme/defaults/mahogany.json +97 -0
  673. package/src/theme/defaults/marble.json +93 -0
  674. package/src/theme/defaults/obsidian.json +91 -0
  675. package/src/theme/defaults/onyx.json +91 -0
  676. package/src/theme/defaults/pearl.json +93 -0
  677. package/src/theme/defaults/porcelain.json +91 -0
  678. package/src/theme/defaults/quartz.json +96 -0
  679. package/src/theme/defaults/sandstone.json +95 -0
  680. package/src/theme/defaults/titanium.json +90 -0
  681. package/src/theme/index.ts +5 -0
  682. package/src/theme/light.json +93 -0
  683. package/src/theme/loader.ts +200 -0
  684. package/src/theme/mermaid-cache.ts +92 -0
  685. package/src/theme/schema-validation.ts +119 -0
  686. package/src/theme/schema.ts +168 -0
  687. package/src/theme/session-color.ts +253 -0
  688. package/src/theme/shimmer.ts +307 -0
  689. package/src/theme/symbols.ts +1433 -0
  690. package/src/theme/theme-class.ts +787 -0
  691. package/src/theme/theme-schema.json +463 -0
  692. package/src/theme/theme.ts +810 -0
  693. package/src/theme/tui-adapters.ts +339 -0
  694. package/src/thinking.ts +380 -0
  695. package/src/tools/agent-tree.ts +128 -0
  696. package/src/tools/ask.ts +482 -0
  697. package/src/tools/ast-edit.ts +210 -0
  698. package/src/tools/ast-grep.ts +171 -0
  699. package/src/tools/autoresearch.ts +287 -0
  700. package/src/tools/bash-interactive.ts +295 -0
  701. package/src/tools/bash.ts +468 -0
  702. package/src/tools/conflict-detect.ts +77 -0
  703. package/src/tools/debug.ts +167 -0
  704. package/src/tools/default-renderer.ts +172 -0
  705. package/src/tools/edit.ts +1258 -0
  706. package/src/tools/eval-format/code-mode-declarations.ts +61 -0
  707. package/src/tools/eval-format/index.ts +16 -0
  708. package/src/tools/eval-format/javascript.ts +952 -0
  709. package/src/tools/eval-format/python.ts +544 -0
  710. package/src/tools/eval.ts +835 -0
  711. package/src/tools/fetch.ts +226 -0
  712. package/src/tools/gh-format.ts +18 -0
  713. package/src/tools/github.ts +686 -0
  714. package/src/tools/glob.ts +213 -0
  715. package/src/tools/goal.ts +153 -0
  716. package/src/tools/grep.ts +385 -0
  717. package/src/tools/grouped-file-output.ts +211 -0
  718. package/src/tools/hashline-format.ts +59 -0
  719. package/src/tools/hub.ts +1488 -0
  720. package/src/tools/index.ts +71 -0
  721. package/src/tools/json-tree.ts +388 -0
  722. package/src/tools/line-ranges.ts +98 -0
  723. package/src/tools/list-limit.ts +43 -0
  724. package/src/tools/lsp.ts +702 -0
  725. package/src/tools/match-line-format.ts +20 -0
  726. package/src/tools/mcp.ts +309 -0
  727. package/src/tools/memory.ts +220 -0
  728. package/src/tools/output-meta.ts +367 -0
  729. package/src/tools/read.ts +628 -0
  730. package/src/tools/renderer.ts +107 -0
  731. package/src/tools/report-tool-issue.ts +13 -0
  732. package/src/tools/resolve.ts +139 -0
  733. package/src/tools/streaming-output.ts +1576 -0
  734. package/src/tools/subprocess.ts +39 -0
  735. package/src/tools/task-repair-args.ts +118 -0
  736. package/src/tools/task-yield-assembly.ts +147 -0
  737. package/src/tools/task.ts +2110 -0
  738. package/src/tools/terminal-output.ts +153 -0
  739. package/src/tools/think.ts +28 -0
  740. package/src/tools/todo.ts +580 -0
  741. package/src/tools/tool-errors.ts +15 -0
  742. package/src/tools/vibe.ts +420 -0
  743. package/src/tools/web-search.ts +409 -0
  744. package/src/tools/write.ts +511 -0
  745. package/src/tools/xd-url.ts +28 -0
  746. package/src/tools/xdev.ts +201 -0
  747. package/src/tui.ts +57 -4
  748. package/src/utils.ts +8 -0
@@ -0,0 +1,902 @@
1
+ import { type Component, Container, type HistoryBatch } from "../tui";
2
+ import * as logger from "@oh-my-pi/pi-utils/logger";
3
+ import { isToolActivityComponent } from "./tool-activity";
4
+
5
+ /** Shared animation time supplied by the constrained transcript root. */
6
+ export interface AnimationFrame {
7
+ readonly tick: number;
8
+ readonly now: number;
9
+ }
10
+
11
+ /** Lets an active block adapt its presentation to its allocated viewport rows. */
12
+ export interface TranscriptPresentationTarget {
13
+ setTranscriptAllocation?(rows: number, frame: AnimationFrame): void;
14
+ }
15
+
16
+ /** Presentation declaration captured permanently when a block is added. */
17
+ export type TranscriptBlockMode = "mutable" | "appendOnly";
18
+
19
+ /** Immutable width-independent identity for one stable semantic row. */
20
+ export interface TranscriptStableRow {
21
+ readonly key: string;
22
+ }
23
+
24
+ /**
25
+ * Explicit semantic-row contract for a block whose stable head may enter native
26
+ * history before finalization. Every later array must extend the prior keys
27
+ * exactly; each row renderer is deterministic for its width.
28
+ * A publication that breaks these invariants (e.g. a mid-stream theme change
29
+ * re-coloring already-emitted bytes) freezes further stable-row emission for
30
+ * that block instead of failing the render — see {@link TranscriptContainer}.
31
+ */
32
+ export interface AppendOnlyTranscriptBlock {
33
+ readonly transcriptBlockMode: "appendOnly";
34
+ getTranscriptStableRows(): readonly TranscriptStableRow[];
35
+ /**
36
+ * Render the first `count` semantic rows at the requested current width.
37
+ * Counts are monotonic identities, not physical row counts; this output must
38
+ * prefix the block's full render at the same width.
39
+ */
40
+ renderTranscriptStableRows(count: number, width: number): readonly string[];
41
+ /**
42
+ * Discard every published stable row so the block re-renders its head from
43
+ * scratch. Called only alongside a destructive display reset (e.g. a
44
+ * thinking-visibility toggle) that clears the native scrollback those rows
45
+ * occupied — the sole context in which the append-only "published bytes never
46
+ * change" contract may be retracted. Optional: blocks whose stable-row
47
+ * presentation never changes may omit it.
48
+ */
49
+ resetTranscriptStableRows?(): void;
50
+ }
51
+
52
+ interface FinalizableBlock {
53
+ isTranscriptBlockFinalized?(): boolean;
54
+ /** Render the row that must remain represented under emergency viewport pressure. */
55
+ renderTranscriptBlockEmergencyRow?(width: number): string | undefined;
56
+ }
57
+
58
+ /**
59
+ * Block lifecycle:
60
+ * - `active`: still mutating; renders live and counts against tool admission.
61
+ * - `settled`: finalized but retained in the mutable viewport until pressure.
62
+ * - `committed`: logically retired; replay never rewinds this state.
63
+ */
64
+ type BlockState = "active" | "settled" | "committed";
65
+
66
+ interface TranscriptEntry {
67
+ component: Component;
68
+ state: BlockState;
69
+ mode: TranscriptBlockMode;
70
+ stableRows: readonly TranscriptStableRow[];
71
+ renderedStableByWidth: Map<number, readonly string[]>;
72
+ /**
73
+ * Rendered row counts per `(width, snapshot count)`: lets the projected
74
+ * length skip the re-render when the same prefix was already rendered.
75
+ * Keyed on both dimensions because one snapshot commonly renders to
76
+ * multiple physical rows (Markdown wrap).
77
+ */
78
+ stableRowCountByWidth: Map<number, Map<number, number>>;
79
+ emitted: number;
80
+ /**
81
+ * Set when a published stable row drifted (retraction, byte change within a
82
+ * width epoch, or no longer a render prefix). Rows already in native
83
+ * scrollback cannot be retracted, so the entry keeps its last good stable
84
+ * state for emitted-row slicing but never emits another mid-stream row.
85
+ */
86
+ stableFrozen: boolean;
87
+ }
88
+
89
+ type RetirementPolicy = "pressure" | "flush";
90
+ type Offered =
91
+ | { batch: HistoryBatch; kind: "append"; entry: number; emittedEnd: number }
92
+ | { batch: HistoryBatch; kind: "commit"; end: number }
93
+ | { batch: HistoryBatch; kind: "replay" };
94
+
95
+ const MAX_LIVE_BLOCKS = 256;
96
+ /** Grace before a pressure-blocked frontier is reported; a streaming block may legitimately hold it briefly. */
97
+ const PINNED_FRONTIER_WARN_MS = 30_000;
98
+ const EMPTY_ROWS: readonly string[] = [];
99
+ const EMPTY_STABLE_ROWS: readonly TranscriptStableRow[] = [];
100
+
101
+ function isFinalized(component: Component): boolean {
102
+ const block = component as Component & FinalizableBlock;
103
+ return block.isTranscriptBlockFinalized?.() ?? true;
104
+ }
105
+
106
+ function blockMode(component: Component): TranscriptBlockMode {
107
+ return (component as Component & Partial<AppendOnlyTranscriptBlock>).transcriptBlockMode === "appendOnly"
108
+ ? "appendOnly"
109
+ : "mutable";
110
+ }
111
+
112
+ function isPlainBlank(line: string): boolean {
113
+ return !/\S/.test(line);
114
+ }
115
+
116
+ /** Whether `prefix` matches `rows` byte-for-byte from the top. */
117
+ export function isRowPrefix(prefix: readonly string[], rows: readonly string[]): boolean {
118
+ if (prefix.length > rows.length) return false;
119
+ for (let index = 0; index < prefix.length; index++) {
120
+ if (prefix[index] !== rows[index]) return false;
121
+ }
122
+ return true;
123
+ }
124
+
125
+ function isStablePrefix(prefix: readonly TranscriptStableRow[], rows: readonly TranscriptStableRow[]): boolean {
126
+ if (prefix.length > rows.length) return false;
127
+ for (let index = 0; index < prefix.length; index++) {
128
+ if (prefix[index]!.key !== rows[index]!.key) return false;
129
+ }
130
+ return true;
131
+ }
132
+
133
+ /** Strip leading/trailing all-blank rows; the viewport allocator measures blocks by this trimmed height. */
134
+ export function trimBlankEdges(rows: readonly string[]): readonly string[] {
135
+ let start = 0;
136
+ let end = rows.length;
137
+ while (start < end && isPlainBlank(rows[start]!)) start++;
138
+ while (end > start && isPlainBlank(rows[end - 1]!)) end--;
139
+ return start === 0 && end === rows.length ? rows : rows.slice(start, end);
140
+ }
141
+
142
+ /** One live block's row span in the last `renderViewport` output (half-open `[start, end)`). */
143
+ export interface TranscriptViewportSpan {
144
+ component: Component;
145
+ start: number;
146
+ end: number;
147
+ }
148
+
149
+ /** Owns transcript order, live capacity, and ordered immutable retirement. */
150
+ export class TranscriptContainer extends Container {
151
+ #entries: TranscriptEntry[] = [];
152
+ #frontier = 0;
153
+ #nextBatchId = 1;
154
+ #offered: Offered | undefined;
155
+ #replayPending = false;
156
+ #replayRequested = false;
157
+ #toolActivityVisible = true;
158
+ #lastFrame: AnimationFrame = { tick: 0, now: 0 };
159
+ // Start rows from the last full render(), keyed by child component (transcript deep-links).
160
+ #childStartRows = new Map<Component, number>();
161
+ // Watchdog for the wedge where an unfinalized frontier block pins pressure
162
+ // retirement: everything behind it stays live and degrades to one-line
163
+ // allocations. Logs once per pinned episode after a grace period.
164
+ #pinnedFrontier: { index: number; since: number; logged: boolean } | undefined;
165
+ /** Block spans of the last `renderViewport` output, for click hit-testing. */
166
+ #lastViewportSpans: TranscriptViewportSpan[] = [];
167
+ override addChild(component: Component): void {
168
+ if (isToolActivityComponent(component)) component.setToolActivityVisible(this.#toolActivityVisible);
169
+ super.addChild(component);
170
+ this.#entries.push({
171
+ component,
172
+ state: "active",
173
+ mode: blockMode(component),
174
+ stableRows: EMPTY_STABLE_ROWS,
175
+ renderedStableByWidth: new Map(),
176
+ stableRowCountByWidth: new Map(),
177
+ emitted: 0,
178
+ stableFrozen: false,
179
+ });
180
+ }
181
+
182
+ override removeChild(component: Component): void {
183
+ if (this.children.indexOf(component) < 0 || !this.canRemoveBlock(component)) return;
184
+ super.removeChild(component);
185
+ this.#entries = this.#entries.filter(candidate => candidate.component !== component);
186
+ this.#frontier = Math.min(this.#frontier, this.#entries.length);
187
+ this.#childStartRows.delete(component);
188
+ }
189
+
190
+ override clear(): void {
191
+ super.clear();
192
+ this.#entries = [];
193
+ this.#frontier = 0;
194
+ this.#offered = undefined;
195
+ this.#childStartRows.clear();
196
+ this.#pinnedFrontier = undefined;
197
+ this.#replayPending = false;
198
+ this.#replayRequested = false;
199
+ this.#lastViewportSpans = [];
200
+ }
201
+
202
+ setToolActivityVisible(visible: boolean): void {
203
+ if (this.#toolActivityVisible === visible) return;
204
+ this.#toolActivityVisible = visible;
205
+ for (const child of this.children) {
206
+ if (isToolActivityComponent(child)) child.setToolActivityVisible(visible);
207
+ }
208
+ this.invalidate();
209
+ }
210
+
211
+ /**
212
+ * Forget the append-only emission ledger — emitted counts, published stable
213
+ * rows, per-width render cache, and freeze state — for every block, and ask
214
+ * each append-only block to drop its own published rows. The next replay then
215
+ * re-renders each block from its current {@link Component.render}, applying a
216
+ * changed presentation (e.g. a thinking-visibility toggle) to rows that were
217
+ * already emitted as stable heads while streaming (#10177).
218
+ *
219
+ * Callers MUST pair this with a scrollback-clearing {@link resetDisplay}: the
220
+ * emitted rows it forgets still sit in native history until that clear
221
+ * rewrites them, so unpaired use would duplicate them on the next retirement.
222
+ */
223
+ resetStableEmission(): void {
224
+ this.#syncEntries();
225
+ if (this.#offered?.kind === "append") this.#offered = undefined;
226
+ for (const entry of this.#entries) {
227
+ entry.emitted = 0;
228
+ entry.stableRows = EMPTY_STABLE_ROWS;
229
+ entry.renderedStableByWidth = new Map();
230
+ entry.stableRowCountByWidth = new Map();
231
+ entry.stableFrozen = false;
232
+ if (entry.mode === "appendOnly") {
233
+ (entry.component as Component & AppendOnlyTranscriptBlock).resetTranscriptStableRows?.();
234
+ }
235
+ }
236
+ }
237
+
238
+ /** Whether a transient block may be discarded without leaving tape history. */
239
+ canRemoveBlock(component: Component): boolean {
240
+ this.#syncEntries();
241
+ const index = this.#entries.findIndex(entry => entry.component === component);
242
+ if (index < 0) return false;
243
+ const entry = this.#entries[index]!;
244
+ if (entry.state === "committed" || entry.emitted > 0) return false;
245
+ if (this.#offered?.kind === "commit" && index < this.#offered.end) return false;
246
+ if (this.#offered?.kind === "append" && index === this.#offered.entry) return false;
247
+ return true;
248
+ }
249
+
250
+ /** Lifecycle state per block in transcript order (diagnostics and tests). */
251
+ blockStates(): readonly BlockState[] {
252
+ this.#syncEntries();
253
+ return this.#entries.map(entry => entry.state);
254
+ }
255
+
256
+ /** Permanently captured presentation mode per block (diagnostics and tests). */
257
+ blockModes(): readonly TranscriptBlockMode[] {
258
+ this.#syncEntries();
259
+ return this.#entries.map(entry => entry.mode);
260
+ }
261
+
262
+ /** Emitted stable semantic-row counts in transcript order. */
263
+ emittedStableRows(): readonly number[] {
264
+ this.#syncEntries();
265
+ return this.#entries.map(entry => entry.emitted);
266
+ }
267
+
268
+ /** Whether visible active capacity and live-block memory permit another admission. */
269
+ canAdmit(rows: number): boolean {
270
+ const active = this.#entries.filter(entry => entry.state === "active").length;
271
+ return Math.max(0, Math.trunc(rows)) > active && this.#liveCount() < MAX_LIVE_BLOCKS;
272
+ }
273
+
274
+ /** Prepares one atomic replay of the committed ledger and an emitted active-head prefix. */
275
+ beginReplay(): void {
276
+ this.#syncEntries();
277
+ if (this.#offered !== undefined) {
278
+ this.#replayRequested = true;
279
+ return;
280
+ }
281
+ this.#startReplay();
282
+ }
283
+ /**
284
+ * Drop a not-yet-offered replay so a shutdown flush emits only un-retired
285
+ * rows. The terminal already holds the committed ledger; re-streaming it at
286
+ * quit is pure write volume. An already offered replay batch stays valid.
287
+ */
288
+ cancelReplay(): void {
289
+ this.#replayPending = false;
290
+ this.#replayRequested = false;
291
+ }
292
+
293
+ /** Total rows the live, un-emitted tail occupies at `width`. */
294
+ liveRowCount(width: number): number {
295
+ this.#syncEntries();
296
+ this.#settleFinalized();
297
+ let total = 0;
298
+ for (const { entry, index } of this.#liveEntries()) {
299
+ this.#setAllocation(entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
300
+ const rendered = this.#renderEntry(entry, width);
301
+ const block = rendered.slice(this.#projectedEmittedRowCount(entry, index, width));
302
+ if (block.length > 0) total += block.length + (total > 0 ? 1 : 0);
303
+ }
304
+ return total;
305
+ }
306
+
307
+ /** Block spans of the last `renderViewport` output, in output coordinates. Empty when the tail is empty. */
308
+ getLastViewportSpans(): readonly TranscriptViewportSpan[] {
309
+ return this.#lastViewportSpans;
310
+ }
311
+
312
+ /** Collapse a per-line owner list into run-length block spans, clamped to `length`. */
313
+ #commitViewportSpans(owners: readonly (Component | undefined)[], length: number = owners.length): void {
314
+ const spans: TranscriptViewportSpan[] = [];
315
+ let index = 0;
316
+ while (index < length) {
317
+ const component = owners[index];
318
+ if (component === undefined) {
319
+ index++;
320
+ continue;
321
+ }
322
+ let end = index + 1;
323
+ while (end < length && owners[end] === component) end++;
324
+ spans.push({ component, start: index, end });
325
+ index = end;
326
+ }
327
+ this.#lastViewportSpans = spans;
328
+ }
329
+
330
+ /** Render the live tail, constrained to the supplied transcript height. */
331
+ renderViewport(width: number, rows: number, frame: AnimationFrame): readonly string[] {
332
+ this.#lastFrame = frame;
333
+ this.#syncEntries();
334
+ this.#settleFinalized();
335
+ const live = this.#liveEntries();
336
+ const capacity = Math.max(0, Math.trunc(rows));
337
+ if (live.length === 0 || capacity === 0) {
338
+ this.#lastViewportSpans = [];
339
+ return EMPTY_ROWS;
340
+ }
341
+
342
+ const shown: Array<{ entry: TranscriptEntry; index: number }> = [];
343
+ const blocks: (readonly string[])[] = [];
344
+ let total = 0;
345
+ for (const candidate of live) {
346
+ this.#setAllocation(candidate.entry.component, Number.MAX_SAFE_INTEGER, frame);
347
+ const rendered = this.#renderEntry(candidate.entry, width);
348
+ const block = rendered.slice(this.#projectedEmittedRowCount(candidate.entry, candidate.index, width));
349
+ if (block.length === 0) continue;
350
+ total += block.length + (shown.length > 0 ? 1 : 0);
351
+ shown.push(candidate);
352
+ blocks.push(block);
353
+ }
354
+ if (shown.length === 0) {
355
+ this.#lastViewportSpans = [];
356
+ return EMPTY_ROWS;
357
+ }
358
+ if (shown.length > capacity) return this.#renderEmergency(shown, width, capacity, frame);
359
+ if (total <= capacity) {
360
+ const output: string[] = [];
361
+ const owners: (Component | undefined)[] = [];
362
+ for (let blockIndex = 0; blockIndex < blocks.length; blockIndex++) {
363
+ if (output.length > 0) {
364
+ output.push("");
365
+ owners.push(undefined);
366
+ }
367
+ const component = shown[blockIndex]!.entry.component;
368
+ for (const line of blocks[blockIndex]!) {
369
+ output.push(line);
370
+ owners.push(component);
371
+ }
372
+ }
373
+ this.#commitViewportSpans(owners, output.length);
374
+ return output;
375
+ }
376
+
377
+ // oxlint-disable-next-line unicorn/no-new-array -- length preallocation
378
+ const allocation: number[] = new Array(shown.length).fill(1);
379
+ let surplus = capacity - shown.length;
380
+ // Surplus rows favor ordinary transcript blocks over dynamic tool-activity
381
+ // cards (newest-first within each class), so a growing tool card collapses to
382
+ // its compact form instead of clipping already-visible assistant text (#9718).
383
+ const order: number[] = [];
384
+ for (let index = shown.length - 1; index >= 0; index--) {
385
+ if (!isToolActivityComponent(shown[index]!.entry.component)) order.push(index);
386
+ }
387
+ for (let index = shown.length - 1; index >= 0; index--) {
388
+ if (isToolActivityComponent(shown[index]!.entry.component)) order.push(index);
389
+ }
390
+ for (const index of order) {
391
+ if (surplus <= 0) break;
392
+ const extra = Math.min(Math.max(0, blocks[index]!.length - 1), surplus);
393
+ allocation[index] += extra;
394
+ surplus -= extra;
395
+ }
396
+ const output: string[] = [];
397
+ const owners: (Component | undefined)[] = [];
398
+ for (let index = 0; index < shown.length; index++) {
399
+ const candidate = shown[index]!;
400
+ const allocated = allocation[index]!;
401
+ this.#setAllocation(candidate.entry.component, allocated, frame);
402
+ const rendered = this.#renderEntry(candidate.entry, width).slice(
403
+ this.#projectedEmittedRowCount(candidate.entry, candidate.index, width),
404
+ );
405
+ const visible = rendered.length <= allocated ? rendered : rendered.slice(rendered.length - allocated);
406
+ for (const line of visible) {
407
+ output.push(line);
408
+ owners.push(candidate.entry.component);
409
+ }
410
+ }
411
+ const drop = Math.max(0, output.length - capacity);
412
+ this.#commitViewportSpans(owners.slice(drop), output.length - drop);
413
+ return drop > 0 ? output.slice(drop) : output;
414
+ }
415
+
416
+ /** Offers stable-head emission or the shortest finalized prefix needed under pressure. */
417
+ peekFinalizedBatch(width: number, capacity: number): HistoryBatch | undefined {
418
+ return this.#peekBatch(width, capacity, "pressure");
419
+ }
420
+
421
+ /** Returns only a prepared complete replay, never a normal retirement offer. */
422
+ peekReplayBatch(width: number): HistoryBatch | undefined {
423
+ this.#syncEntries();
424
+ this.#settleFinalized();
425
+ return this.#peekReplayBatch(width);
426
+ }
427
+
428
+ #peekReplayBatch(width: number): HistoryBatch | undefined {
429
+ if (this.#offered !== undefined) {
430
+ return this.#offered.kind === "replay" ? this.#offered.batch : undefined;
431
+ }
432
+ if (!this.#replayPending) return undefined;
433
+ const rows = this.#renderReplay(width);
434
+ this.#replayPending = false;
435
+ if (rows.length === 0) return undefined;
436
+ const batch: HistoryBatch = { id: this.#nextBatchId++, rows, kind: "replay" };
437
+ this.#offered = { batch, kind: "replay" };
438
+ return batch;
439
+ }
440
+
441
+ /** Offers the complete currently eligible prefix for graceful shutdown. */
442
+ peekFlushBatch(width: number): HistoryBatch | undefined {
443
+ return this.#peekBatch(width, 0, "flush");
444
+ }
445
+
446
+ /** Recompose the unacknowledged batch so a discarded TUI frame can be rendered again. */
447
+ rerenderOfferedBatch(width: number): HistoryBatch | undefined {
448
+ const offered = this.#offered;
449
+ if (offered === undefined) return undefined;
450
+ let rows: readonly string[];
451
+ if (offered.kind === "append") {
452
+ const entry = this.#entries[offered.entry];
453
+ if (entry === undefined) return undefined;
454
+ const before = this.#renderStablePrefix(entry, entry.emitted, width);
455
+ const after = this.#renderStablePrefix(entry, offered.emittedEnd, width);
456
+ rows = after.slice(before.length);
457
+ } else if (offered.kind === "commit") {
458
+ rows = this.#renderRange(this.#frontier, offered.end, width, true);
459
+ } else {
460
+ rows = this.#renderReplay(width);
461
+ }
462
+ offered.batch = { id: offered.batch.id, rows, kind: offered.batch.kind };
463
+ return offered.batch;
464
+ }
465
+
466
+ #peekBatch(width: number, capacity: number, policy: RetirementPolicy): HistoryBatch | undefined {
467
+ this.#syncEntries();
468
+ this.#settleFinalized();
469
+ if (this.#offered !== undefined) return this.#offered.batch;
470
+ const replay = this.#peekReplayBatch(width);
471
+ if (replay !== undefined) return replay;
472
+
473
+ this.#completeFullyEmittedHeads(width);
474
+ const room = Math.max(0, Math.trunc(capacity));
475
+ const live = this.#liveEntries();
476
+ if (live.length === 0) return undefined;
477
+ // oxlint-disable-next-line unicorn/no-new-array -- length preallocation
478
+ const rendered: (readonly string[])[] = new Array(live.length);
479
+ // oxlint-disable-next-line unicorn/no-new-array -- length preallocation
480
+ const heights: number[] = new Array(live.length);
481
+ let total = 0;
482
+ let visible = 0;
483
+ for (let index = 0; index < live.length; index++) {
484
+ const candidate = live[index]!;
485
+ this.#setAllocation(candidate.entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
486
+ const renderedEntry = this.#renderEntry(candidate.entry, width);
487
+ const rows = renderedEntry.slice(
488
+ this.#renderStablePrefix(candidate.entry, candidate.entry.emitted, width).length,
489
+ );
490
+ rendered[index] = rows;
491
+ heights[index] = rows.length;
492
+ if (rows.length > 0) total += rows.length + (visible++ > 0 ? 1 : 0);
493
+ }
494
+ const overflowing = total > room || this.#liveCount() >= MAX_LIVE_BLOCKS;
495
+ if (policy === "pressure" && !overflowing) {
496
+ this.#pinnedFrontier = undefined;
497
+ return undefined;
498
+ }
499
+
500
+ const head = this.#entries[this.#frontier];
501
+ if (
502
+ policy === "pressure" &&
503
+ total > room &&
504
+ head?.mode === "appendOnly" &&
505
+ !head.stableFrozen &&
506
+ head.state !== "committed" &&
507
+ head.emitted < head.stableRows.length
508
+ ) {
509
+ // Emit as many finished rows as the overflow needs, in one batch. A
510
+ // fast stream adds finished rows quicker than one per pressure cycle,
511
+ // and the live region has to fall back under `room` to stay readable:
512
+ // rows left behind here are rows dropped from the top of the viewport.
513
+ const overflow = total - room;
514
+ const before = this.#renderStablePrefix(head, head.emitted, width);
515
+ let emittedEnd = head.emitted;
516
+ let rows: readonly string[] = EMPTY_ROWS;
517
+ while (emittedEnd < head.stableRows.length && rows.length < overflow) {
518
+ const after = this.#renderStablePrefix(head, emittedEnd + 1, width);
519
+ if (!isRowPrefix(before, after) || after.length === before.length) {
520
+ if (emittedEnd === head.emitted) {
521
+ this.#freezeStableRows(head, EMPTY_ROWS, "semantic row render added no suffix");
522
+ }
523
+ break;
524
+ }
525
+ rows = after.slice(before.length);
526
+ emittedEnd += 1;
527
+ }
528
+ if (emittedEnd > head.emitted) {
529
+ const batch: HistoryBatch = {
530
+ id: this.#nextBatchId++,
531
+ rows,
532
+ kind: "append",
533
+ };
534
+ this.#offered = { batch, kind: "append", entry: this.#frontier, emittedEnd };
535
+ this.#pinnedFrontier = undefined;
536
+ return batch;
537
+ }
538
+ }
539
+
540
+ let end = this.#frontier;
541
+ let freed = 0;
542
+ let index = 0;
543
+ while (end < this.#entries.length && this.#entries[end]!.state === "settled") {
544
+ if (
545
+ policy === "pressure" &&
546
+ total - freed <= room &&
547
+ this.#liveCount() - (end - this.#frontier) < MAX_LIVE_BLOCKS
548
+ )
549
+ break;
550
+ freed += heights[index]! > 0 ? heights[index]! + 1 : 0;
551
+ end++;
552
+ index++;
553
+ }
554
+ if (end === this.#frontier) {
555
+ if (policy === "pressure") this.#notePinnedFrontier();
556
+ return undefined;
557
+ }
558
+ this.#pinnedFrontier = undefined;
559
+ const batch: HistoryBatch = {
560
+ id: this.#nextBatchId++,
561
+ rows: this.#renderRange(this.#frontier, end, width, true),
562
+ kind: "append",
563
+ };
564
+ this.#offered = { batch, end, kind: "commit" };
565
+ return batch;
566
+ }
567
+
568
+ /** Acknowledges exactly the most recently offered append, commit, or replay transaction. */
569
+ acknowledgeFinalizedBatch(id: number): void {
570
+ const offered = this.#offered;
571
+ if (offered === undefined || offered.batch.id !== id) return;
572
+ if (offered.kind === "append") {
573
+ const entry = this.#entries[offered.entry];
574
+ // The offered end must still extend this entry's emitted prefix: a
575
+ // stale offer (already-advanced entry) or a retraction (entry reset to
576
+ // zero with the offer still live) must not move it backwards.
577
+ if (entry === undefined || offered.entry !== this.#frontier || offered.emittedEnd <= entry.emitted) return;
578
+ entry.emitted = offered.emittedEnd;
579
+ } else if (offered.kind === "commit") {
580
+ for (let index = this.#frontier; index < offered.end; index++) {
581
+ this.#entries[index]!.state = "committed";
582
+ this.#entries[index]!.emitted = 0;
583
+ }
584
+ this.#frontier = offered.end;
585
+ }
586
+ this.#offered = undefined;
587
+ if (this.#replayRequested) this.#startReplay();
588
+ }
589
+
590
+ /**
591
+ * Render only the trailing `maxRows` semantic rows, walking blocks bottom-up.
592
+ * Used by the transient resize-buffer repaint, which needs one viewport of
593
+ * tail rows per resize event — never the full committed ledger.
594
+ */
595
+ renderTail(width: number, maxRows: number): readonly string[] {
596
+ this.#syncEntries();
597
+ const cap = Math.max(0, Math.trunc(maxRows));
598
+ if (cap === 0) return EMPTY_ROWS;
599
+ const rows: string[] = [];
600
+ for (let index = this.#entries.length - 1; index >= 0; index--) {
601
+ const entry = this.#entries[index]!;
602
+ this.#setAllocation(entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
603
+ const block = trimBlankEdges(entry.component.render(width));
604
+ if (block.length === 0) continue;
605
+ if (rows.length > 0) rows.unshift("");
606
+ rows.unshift(...block);
607
+ if (rows.length >= cap) break;
608
+ }
609
+ return rows.length > cap ? rows.slice(rows.length - cap) : rows;
610
+ }
611
+
612
+ /** Full semantic render used by exports and non-terminal commands. */
613
+ override render(width: number): readonly string[] {
614
+ this.#syncEntries();
615
+ this.#childStartRows.clear();
616
+ const rows: string[] = [];
617
+ for (const entry of this.#entries) {
618
+ this.#setAllocation(entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
619
+ const block = this.#renderEntry(entry, width);
620
+ if (block.length === 0) continue;
621
+ if (rows.length > 0) rows.push("");
622
+ this.#childStartRows.set(entry.component, rows.length);
623
+ rows.push(...block);
624
+ }
625
+ return rows;
626
+ }
627
+
628
+ /** Rendered row where a child's block begins in the last full render() (transcript deep-links). */
629
+ getChildStartRow(child: Component): number | undefined {
630
+ return this.#childStartRows.get(child);
631
+ }
632
+
633
+ #renderEntry(entry: TranscriptEntry, width: number): readonly string[] {
634
+ const rendered = trimBlankEdges(entry.component.render(width));
635
+ if (entry.mode === "mutable" || entry.stableFrozen) return rendered;
636
+ const appendOnly = entry.component as Component & AppendOnlyTranscriptBlock;
637
+ const stable = appendOnly.getTranscriptStableRows();
638
+ if (!isStablePrefix(entry.stableRows, stable)) {
639
+ return this.#freezeStableRows(entry, rendered, "publication retracted the published prefix");
640
+ }
641
+ if (entry.emitted > stable.length) {
642
+ return this.#freezeStableRows(entry, rendered, "publication retracted emitted history");
643
+ }
644
+ const published =
645
+ stable.length > entry.stableRows.length
646
+ ? [...entry.stableRows, ...stable.slice(entry.stableRows.length)]
647
+ : entry.stableRows;
648
+ const stableRendered = appendOnly.renderTranscriptStableRows(published.length, width);
649
+ if (!isRowPrefix(stableRendered, rendered)) {
650
+ return this.#freezeStableRows(entry, rendered, "stable rows no longer render as a prefix of the block");
651
+ }
652
+ const priorRender = entry.renderedStableByWidth.get(width);
653
+ if (priorRender && !isRowPrefix(priorRender, stableRendered)) {
654
+ return this.#freezeStableRows(entry, rendered, "stable rows changed within a width epoch");
655
+ }
656
+ entry.stableRows = published;
657
+ // Slice only when the rendered rows actually changed: same length
658
+ // plus prefix-equality in both directions means byte-identical, so
659
+ // the stored array can be reused (callers only slice/read it).
660
+ const priorRows = entry.renderedStableByWidth.get(width);
661
+ if (
662
+ priorRows === undefined ||
663
+ priorRows.length !== stableRendered.length ||
664
+ !isRowPrefix(priorRows, stableRendered)
665
+ ) {
666
+ entry.renderedStableByWidth.set(width, stableRendered.slice());
667
+ }
668
+ let perCount = entry.stableRowCountByWidth.get(width);
669
+ if (perCount === undefined) {
670
+ perCount = new Map();
671
+ entry.stableRowCountByWidth.set(width, perCount);
672
+ }
673
+ perCount.set(published.length, stableRendered.length);
674
+ return rendered;
675
+ }
676
+
677
+ /**
678
+ * Demote a drifting append-only publication: rows already written to native
679
+ * scrollback cannot be retracted, so keep the last good stable state for
680
+ * emitted-row slicing and stop mid-stream emission for this block. The block
681
+ * still renders and retires whole on finalization; worst case is the old
682
+ * finalize-time behavior plus a possible stale-byte seam in scrollback.
683
+ */
684
+ #freezeStableRows(entry: TranscriptEntry, rendered: readonly string[], reason: string): readonly string[] {
685
+ entry.stableFrozen = true;
686
+ logger.warn("Append-only transcript block frozen", { reason, emitted: entry.emitted });
687
+ return rendered;
688
+ }
689
+
690
+ #renderStablePrefix(entry: TranscriptEntry, count: number, width: number): readonly string[] {
691
+ if (count === 0) return EMPTY_ROWS;
692
+ const appendOnly = entry.component as Component & AppendOnlyTranscriptBlock;
693
+ return appendOnly.renderTranscriptStableRows(Math.min(count, entry.stableRows.length), width);
694
+ }
695
+
696
+ /**
697
+ * Length-only variant of `#renderStablePrefix`: answers the projected
698
+ * emitted row count without re-rendering the prefix. The container only
699
+ * needs the length for slicing; the render call it replaced existed
700
+ * purely to read `.length` off the result.
701
+ */
702
+ #projectedEmittedRowCount(entry: TranscriptEntry, index: number, width: number): number {
703
+ const offered = this.#offered;
704
+ const count = offered?.kind === "append" && offered.entry === index ? offered.emittedEnd : entry.emitted;
705
+ if (count === 0) return 0;
706
+ const perCount = entry.stableRowCountByWidth.get(width);
707
+ const memo = perCount?.get(Math.min(count, entry.stableRows.length));
708
+ if (memo !== undefined) return memo;
709
+ return this.#renderStablePrefix(entry, count, width).length;
710
+ }
711
+ /**
712
+ * Record that pressure retirement is blocked behind a not-yet-settled
713
+ * frontier block, and log its identity once the episode outlives the grace
714
+ * period. A block that never finalizes (a dropped terminal event) pins the
715
+ * whole live region here with no visible symptom other than degraded
716
+ * one-line layout, so the log line is the only forensic trail.
717
+ */
718
+ #notePinnedFrontier(): void {
719
+ const entry = this.#entries[this.#frontier];
720
+ if (entry === undefined) return;
721
+ const now = Date.now();
722
+ if (this.#pinnedFrontier?.index !== this.#frontier) {
723
+ this.#pinnedFrontier = { index: this.#frontier, since: now, logged: false };
724
+ return;
725
+ }
726
+ if (this.#pinnedFrontier.logged || now - this.#pinnedFrontier.since < PINNED_FRONTIER_WARN_MS) return;
727
+ this.#pinnedFrontier.logged = true;
728
+ logger.warn("Transcript retirement pinned by unfinalized frontier block", {
729
+ component: entry.component.constructor.name,
730
+ state: entry.state,
731
+ mode: entry.mode,
732
+ liveBlocks: this.#liveCount(),
733
+ });
734
+ }
735
+
736
+ #renderRange(start: number, end: number, width: number, trailingBlank: boolean): readonly string[] {
737
+ const rows: string[] = [];
738
+ for (let index = start; index < end; index++) {
739
+ const entry = this.#entries[index]!;
740
+ this.#setAllocation(entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
741
+ // Only the range head is sliced by its emitted stable prefix; every other
742
+ // entry renders whole, so the append-only verification pass (a second
743
+ // full render of the block's stable prefix) is skipped for them. This
744
+ // keeps a complete-ledger replay at one render per block.
745
+ const rendered =
746
+ index === start ? this.#renderEntry(entry, width) : trimBlankEdges(entry.component.render(width));
747
+ const emittedRows = index === start ? this.#renderStablePrefix(entry, entry.emitted, width).length : 0;
748
+ const block = rendered.slice(emittedRows);
749
+ if (block.length === 0) continue;
750
+ if (rows.length > 0) rows.push("");
751
+ rows.push(...block);
752
+ }
753
+ if (trailingBlank && rows.length > 0) rows.push("");
754
+ return rows;
755
+ }
756
+
757
+ #renderReplay(width: number): readonly string[] {
758
+ const rows = Array.from(this.#renderRange(0, this.#frontier, width, true));
759
+ const head = this.#entries[this.#frontier];
760
+ if (head?.mode === "appendOnly" && head.emitted > 0) {
761
+ this.#setAllocation(head.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
762
+ this.#renderEntry(head, width);
763
+ rows.push(...this.#renderStablePrefix(head, head.emitted, width));
764
+ }
765
+ return rows;
766
+ }
767
+
768
+ #completeFullyEmittedHeads(width: number): void {
769
+ while (this.#frontier < this.#entries.length) {
770
+ const entry = this.#entries[this.#frontier]!;
771
+ if (entry.mode !== "appendOnly" || entry.state !== "settled") return;
772
+ this.#setAllocation(entry.component, Number.MAX_SAFE_INTEGER, this.#lastFrame);
773
+ const rendered = this.#renderEntry(entry, width);
774
+ if (entry.emitted !== entry.stableRows.length) return;
775
+ if (this.#renderStablePrefix(entry, entry.emitted, width).length !== rendered.length) return;
776
+ entry.state = "committed";
777
+ entry.emitted = 0;
778
+ this.#frontier++;
779
+ }
780
+ }
781
+
782
+ #startReplay(): void {
783
+ const head = this.#entries[this.#frontier];
784
+ this.#replayPending = this.#frontier > 0 || (head?.mode === "appendOnly" && head.emitted > 0);
785
+ this.#replayRequested = false;
786
+ }
787
+
788
+ #renderEmergency(
789
+ shown: readonly { entry: TranscriptEntry; index: number }[],
790
+ width: number,
791
+ rows: number,
792
+ frame: AnimationFrame,
793
+ ): readonly string[] {
794
+ let visibleRows = rows;
795
+ let visible: { entry: TranscriptEntry; index: number }[] = [];
796
+ let emergencyCandidate: { entry: TranscriptEntry; index: number } | undefined;
797
+ let emergencyRow: string | undefined;
798
+ let hiddenActive = 0;
799
+ for (let attempt = 0; attempt < 2; attempt++) {
800
+ visible = visibleRows > 0 ? shown.slice(-visibleRows) : [];
801
+ emergencyCandidate = undefined;
802
+ emergencyRow = undefined;
803
+ const visibleStart = shown.length - visibleRows;
804
+ for (let index = visibleStart - 1; index >= 0; index--) {
805
+ const candidate = shown[index]!;
806
+ const block = candidate.entry.component as Component & FinalizableBlock;
807
+ const row =
808
+ candidate.entry.state === "settled" ? block.renderTranscriptBlockEmergencyRow?.(width) : undefined;
809
+ if (row === undefined) continue;
810
+ emergencyCandidate = candidate;
811
+ emergencyRow = row;
812
+ visible = [candidate, ...visible.slice(1)];
813
+ break;
814
+ }
815
+
816
+ let activeTotal = 0;
817
+ for (const candidate of shown) {
818
+ if (candidate.entry.state === "active") activeTotal++;
819
+ }
820
+ hiddenActive = activeTotal;
821
+ for (const candidate of visible) {
822
+ if (candidate.entry.state === "active") hiddenActive--;
823
+ }
824
+ // The summary row itself represents the newest active block when no
825
+ // active row fits beside it; report only the additional backlog.
826
+ if (hiddenActive === activeTotal && hiddenActive > 0) hiddenActive--;
827
+ if (attempt === 0 && hiddenActive > 0) {
828
+ visibleRows = Math.max(0, rows - 1);
829
+ continue;
830
+ }
831
+ break;
832
+ }
833
+
834
+ const output = hiddenActive > 0 ? [`${hiddenActive} more transcript blocks active`] : [];
835
+ const owners: (Component | undefined)[] = hiddenActive > 0 ? [undefined] : [];
836
+ for (const candidate of visible) {
837
+ if (candidate === emergencyCandidate) {
838
+ output.push(emergencyRow ?? "");
839
+ owners.push(candidate.entry.component);
840
+ continue;
841
+ }
842
+ this.#setAllocation(candidate.entry.component, 1, frame);
843
+ const rendered = this.#renderEntry(candidate.entry, width).slice(
844
+ this.#projectedEmittedRowCount(candidate.entry, candidate.index, width),
845
+ );
846
+ output.push(rendered[0] ?? "");
847
+ owners.push(candidate.entry.component);
848
+ }
849
+ const visibleOutput = output.slice(0, rows);
850
+ this.#commitViewportSpans(owners, visibleOutput.length);
851
+ return visibleOutput;
852
+ }
853
+
854
+ #setAllocation(component: Component, rows: number, frame: AnimationFrame): void {
855
+ (component as Component & TranscriptPresentationTarget).setTranscriptAllocation?.(rows, frame);
856
+ }
857
+
858
+ #settleFinalized(): void {
859
+ for (let index = this.#frontier; index < this.#entries.length; index++) {
860
+ const entry = this.#entries[index]!;
861
+ if (entry.state === "active" && isFinalized(entry.component)) entry.state = "settled";
862
+ }
863
+ }
864
+
865
+ #liveEntries(): Array<{ entry: TranscriptEntry; index: number }> {
866
+ const start = this.#offered?.kind === "commit" ? this.#offered.end : this.#frontier;
867
+ const live: Array<{ entry: TranscriptEntry; index: number }> = [];
868
+ for (let index = start; index < this.#entries.length; index++) live.push({ entry: this.#entries[index]!, index });
869
+ return live;
870
+ }
871
+
872
+ #liveCount(): number {
873
+ return this.#entries.length - this.#frontier;
874
+ }
875
+
876
+ #syncEntries(): void {
877
+ if (
878
+ this.#entries.length === this.children.length &&
879
+ this.#entries.every((entry, index) => entry.component === this.children[index])
880
+ )
881
+ return;
882
+ const existing = new Map(this.#entries.map(entry => [entry.component, entry]));
883
+ this.#entries = this.children.map(
884
+ component =>
885
+ existing.get(component) ?? {
886
+ component,
887
+ state: "active",
888
+ mode: blockMode(component),
889
+ stableRows: EMPTY_STABLE_ROWS,
890
+ renderedStableByWidth: new Map(),
891
+ stableRowCountByWidth: new Map(),
892
+ emitted: 0,
893
+ stableFrozen: false,
894
+ },
895
+ );
896
+ this.#frontier = this.#entries.findIndex(entry => entry.state !== "committed");
897
+ if (this.#frontier < 0) this.#frontier = this.#entries.length;
898
+ }
899
+ }
900
+
901
+ /** Groups sibling rows into one conservative mutable semantic transcript block. */
902
+ export class TranscriptBlock extends Container {}