@bubblebrain-ai/bubble 0.0.52 → 0.0.54

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 (483) hide show
  1. package/README.md +4 -12
  2. package/dist/agent/routing-catalog.js +3 -2
  3. package/dist/agent/subagent/router.js +5 -0
  4. package/dist/agent/subagent/runtime.d.ts +1 -0
  5. package/dist/agent/subagent/runtime.js +13 -1
  6. package/dist/agent/subagent-control.d.ts +4 -0
  7. package/dist/agent/subagent-control.js +1 -0
  8. package/dist/agent/subagent-store.js +2 -0
  9. package/dist/agent/workflow/control.d.ts +2 -0
  10. package/dist/agent/workflow/runs.d.ts +1 -0
  11. package/dist/agent/workflow/runs.js +3 -0
  12. package/dist/agent.d.ts +11 -1
  13. package/dist/agent.js +158 -57
  14. package/dist/bin.js +2 -44
  15. package/dist/config.d.ts +14 -1
  16. package/dist/config.js +45 -4
  17. package/dist/context/budget.d.ts +7 -1
  18. package/dist/context/budget.js +25 -2
  19. package/dist/context/compact-llm.d.ts +1 -1
  20. package/dist/context/compact-llm.js +5 -2
  21. package/dist/context/overflow.js +2 -0
  22. package/dist/context/projector.d.ts +8 -0
  23. package/dist/context/projector.js +55 -6
  24. package/dist/context/tool-output-truncate.d.ts +17 -1
  25. package/dist/context/tool-output-truncate.js +171 -49
  26. package/dist/feishu/agent-host/run-driver.js +3 -0
  27. package/dist/feishu/serve.js +2 -0
  28. package/dist/goal/command.d.ts +1 -1
  29. package/dist/goal/command.js +7 -2
  30. package/dist/main.d.ts +1 -1
  31. package/dist/main.js +38 -8
  32. package/dist/memory/db.js +0 -7
  33. package/dist/model-catalog.js +25 -6
  34. package/dist/model-config.js +3 -2
  35. package/dist/model-pricing.d.ts +24 -5
  36. package/dist/model-pricing.js +0 -0
  37. package/dist/model-selection.js +13 -4
  38. package/dist/network/chatgpt-transport.js +0 -34
  39. package/dist/network/errors.d.ts +19 -0
  40. package/dist/network/errors.js +24 -0
  41. package/dist/network/provider-transport.d.ts +0 -4
  42. package/dist/network/provider-transport.js +3 -35
  43. package/dist/network/retry.d.ts +6 -1
  44. package/dist/network/retry.js +9 -0
  45. package/dist/provider-model-policy.d.ts +16 -0
  46. package/dist/provider-model-policy.js +33 -0
  47. package/dist/provider-registry.d.ts +1 -1
  48. package/dist/provider-registry.js +73 -10
  49. package/dist/provider-transform.js +32 -4
  50. package/dist/provider.d.ts +6 -2
  51. package/dist/provider.js +95 -6
  52. package/dist/question/controller.js +5 -4
  53. package/dist/question/normalize.d.ts +6 -0
  54. package/dist/question/normalize.js +11 -0
  55. package/dist/rewind.d.ts +20 -0
  56. package/dist/rewind.js +29 -0
  57. package/dist/sdk/index.d.ts +1 -1
  58. package/dist/sdk/index.js +11 -3
  59. package/dist/session.d.ts +7 -0
  60. package/dist/session.js +20 -0
  61. package/dist/skills/discovery.js +18 -0
  62. package/dist/skills/registry.d.ts +12 -2
  63. package/dist/skills/registry.js +40 -5
  64. package/dist/skills/types.d.ts +2 -0
  65. package/dist/slash-commands/commands.js +120 -81
  66. package/dist/slash-commands/registry.d.ts +2 -1
  67. package/dist/slash-commands/registry.js +3 -0
  68. package/dist/slash-commands/types.d.ts +22 -4
  69. package/dist/stats/usage.d.ts +3 -1
  70. package/dist/stats/usage.js +22 -13
  71. package/dist/tasks/manager.d.ts +4 -2
  72. package/dist/tasks/manager.js +34 -9
  73. package/dist/tasks/promotion.d.ts +2 -2
  74. package/dist/tasks/promotion.js +2 -2
  75. package/dist/tools/agent-lifecycle.js +4 -0
  76. package/dist/tools/bash.d.ts +3 -1
  77. package/dist/tools/bash.js +13 -2
  78. package/dist/tools/grep.d.ts +5 -1
  79. package/dist/tools/grep.js +318 -69
  80. package/dist/tools/index.d.ts +3 -1
  81. package/dist/tools/index.js +1 -0
  82. package/dist/tools/question.js +5 -4
  83. package/dist/tools/read.js +40 -21
  84. package/dist/tui/animation-clock.d.ts +14 -0
  85. package/dist/tui/animation-clock.js +34 -0
  86. package/dist/tui/app.d.ts +176 -0
  87. package/dist/tui/app.js +1647 -0
  88. package/dist/tui/components/approval-dialog.d.ts +43 -0
  89. package/dist/tui/components/approval-dialog.js +247 -0
  90. package/dist/tui/components/bottom-sheet.d.ts +9 -0
  91. package/dist/tui/components/bottom-sheet.js +32 -0
  92. package/dist/tui/components/context-info.d.ts +54 -0
  93. package/dist/tui/components/context-info.js +388 -0
  94. package/dist/tui/components/feedback-dialog.d.ts +56 -0
  95. package/dist/tui/components/feedback-dialog.js +262 -0
  96. package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
  97. package/dist/tui/components/feishu-setup-dialog.js +332 -0
  98. package/dist/tui/components/image-preview.d.ts +32 -0
  99. package/dist/tui/components/image-preview.js +130 -0
  100. package/dist/tui/components/plan-dialog.d.ts +34 -0
  101. package/dist/tui/components/plan-dialog.js +196 -0
  102. package/dist/tui/components/provider-key-input.d.ts +18 -0
  103. package/dist/tui/components/provider-key-input.js +49 -0
  104. package/dist/tui/components/question-dialog.d.ts +25 -0
  105. package/dist/tui/components/question-dialog.js +263 -0
  106. package/dist/tui/components/responsive-transcript.d.ts +31 -0
  107. package/dist/tui/components/responsive-transcript.js +77 -0
  108. package/dist/tui/components/rewind-picker.d.ts +62 -0
  109. package/dist/tui/components/rewind-picker.js +317 -0
  110. package/dist/tui/components/session-picker.d.ts +49 -0
  111. package/dist/tui/components/session-picker.js +303 -0
  112. package/dist/tui/components/skills-panel.d.ts +59 -0
  113. package/dist/tui/components/skills-panel.js +568 -0
  114. package/dist/tui/components/stats-panel.d.ts +40 -0
  115. package/dist/tui/components/stats-panel.js +301 -0
  116. package/dist/tui/components/streaming-message.d.ts +69 -0
  117. package/dist/tui/components/streaming-message.js +393 -0
  118. package/dist/tui/components/subagent-inspector.d.ts +28 -0
  119. package/dist/tui/components/subagent-inspector.js +127 -0
  120. package/dist/tui/components/task-inspector.d.ts +26 -0
  121. package/dist/tui/components/task-inspector.js +70 -0
  122. package/dist/tui/components/tasks-pane.d.ts +88 -0
  123. package/dist/tui/components/tasks-pane.js +416 -0
  124. package/dist/tui/components/transcript.d.ts +96 -0
  125. package/dist/tui/components/transcript.js +771 -0
  126. package/dist/tui/components/welcome.d.ts +20 -0
  127. package/dist/tui/components/welcome.js +133 -0
  128. package/dist/tui/components/workflow-inspector.d.ts +30 -0
  129. package/dist/tui/components/workflow-inspector.js +136 -0
  130. package/dist/tui/composer-autocomplete.d.ts +61 -0
  131. package/dist/tui/composer-autocomplete.js +383 -0
  132. package/dist/tui/composer-style.d.ts +7 -0
  133. package/dist/tui/composer-style.js +29 -0
  134. package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
  135. package/dist/tui/controller/agent-event-reducer.js +238 -0
  136. package/dist/tui/controller/composer-controller.d.ts +95 -0
  137. package/dist/tui/controller/composer-controller.js +502 -0
  138. package/dist/tui/controller/controller.d.ts +194 -0
  139. package/dist/tui/controller/controller.js +1035 -0
  140. package/dist/tui/controller/effects.d.ts +71 -0
  141. package/dist/tui/controller/effects.js +1 -0
  142. package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
  143. package/dist/tui/controller/goal-runtime-controller.js +202 -0
  144. package/dist/tui/controller/input-queue-machine.d.ts +71 -0
  145. package/dist/tui/controller/input-queue-machine.js +121 -0
  146. package/dist/tui/controller/intents.d.ts +56 -0
  147. package/dist/tui/controller/intents.js +6 -0
  148. package/dist/tui/controller/overlay-controller.d.ts +64 -0
  149. package/dist/tui/controller/overlay-controller.js +129 -0
  150. package/dist/tui/controller/ports.d.ts +57 -0
  151. package/dist/tui/controller/ports.js +9 -0
  152. package/dist/tui/controller/session-transition.d.ts +54 -0
  153. package/dist/tui/controller/session-transition.js +68 -0
  154. package/dist/tui/controller/state.d.ts +14 -0
  155. package/dist/tui/controller/state.js +36 -0
  156. package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
  157. package/dist/tui/controller/task-runtime-controller.js +158 -0
  158. package/dist/tui/footer.d.ts +37 -0
  159. package/dist/tui/footer.js +80 -0
  160. package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
  161. package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
  162. package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
  163. package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
  164. package/dist/tui/fullscreen.d.ts +48 -0
  165. package/dist/tui/fullscreen.js +255 -0
  166. package/dist/tui/image-display.d.ts +4 -0
  167. package/dist/tui/image-display.js +30 -12
  168. package/dist/tui/markdown-style.d.ts +5 -0
  169. package/dist/tui/markdown-style.js +23 -0
  170. package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
  171. package/dist/tui/model/composer-types.d.ts +17 -0
  172. package/dist/tui/model/composer-types.js +1 -0
  173. package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
  174. package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
  175. package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
  176. package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
  177. package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
  178. package/dist/tui/model/image-attachment.d.ts +15 -0
  179. package/dist/tui/model/image-attachment.js +1 -0
  180. package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
  181. package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
  182. package/dist/tui/model/input-history.d.ts +1 -0
  183. package/dist/tui/model/input-history.js +1 -0
  184. package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
  185. package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
  186. package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
  187. package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
  188. package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
  189. package/dist/tui/model/theme-style.d.ts +10 -0
  190. package/dist/tui/model/theme-style.js +79 -0
  191. package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
  192. package/dist/{tui-ink → tui/model}/theme.js +23 -38
  193. package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
  194. package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
  195. package/dist/tui/model/trace-interaction.d.ts +63 -0
  196. package/dist/tui/model/trace-interaction.js +87 -0
  197. package/dist/tui/model-picker-data.d.ts +1 -2
  198. package/dist/tui/model-picker-data.js +8 -1
  199. package/dist/tui/model-switch.js +2 -0
  200. package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
  201. package/dist/tui/run-session-picker.js +64 -0
  202. package/dist/tui/run.d.ts +72 -0
  203. package/dist/tui/run.js +139 -0
  204. package/dist/tui/terminal/multiplexer.d.ts +5 -0
  205. package/dist/tui/terminal/multiplexer.js +14 -0
  206. package/dist/tui/testing/fake-agent.d.ts +24 -0
  207. package/dist/tui/testing/fake-agent.js +22 -0
  208. package/dist/tui/testing/spy-host.d.ts +21 -0
  209. package/dist/tui/testing/spy-host.js +55 -0
  210. package/dist/tui/trace-groups.js +27 -6
  211. package/dist/types.d.ts +7 -0
  212. package/dist/update/index.d.ts +1 -0
  213. package/dist/update/index.js +22 -17
  214. package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
  215. package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
  216. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
  217. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
  218. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
  219. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
  220. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
  221. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
  222. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
  223. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
  224. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
  225. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
  226. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
  227. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
  228. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
  229. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
  230. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
  231. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
  232. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
  233. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
  234. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
  235. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
  236. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
  237. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
  238. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
  239. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
  240. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
  241. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
  242. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
  243. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
  244. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
  245. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
  246. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
  247. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
  248. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
  249. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
  250. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
  251. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
  252. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
  253. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
  254. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
  255. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
  256. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
  257. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
  258. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
  259. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
  260. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
  261. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
  262. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
  263. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
  264. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
  265. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
  266. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
  267. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
  268. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
  269. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
  270. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
  271. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
  272. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
  273. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
  274. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
  275. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
  276. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
  277. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
  278. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
  279. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
  280. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
  281. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
  282. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
  283. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
  284. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
  285. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
  286. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
  287. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
  288. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
  289. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
  290. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
  291. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
  292. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
  293. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
  294. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
  295. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
  296. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
  297. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
  298. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
  299. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
  300. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
  301. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
  302. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
  303. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
  304. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
  305. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
  306. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
  307. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
  308. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
  309. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
  310. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
  311. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
  312. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
  313. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
  314. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
  315. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
  316. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
  317. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
  318. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
  319. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
  320. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
  321. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
  322. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
  323. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
  324. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
  325. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
  326. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
  327. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
  328. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
  329. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
  330. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
  331. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
  332. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
  333. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
  334. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
  335. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
  336. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
  337. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
  338. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
  339. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
  340. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
  341. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
  342. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
  343. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
  344. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
  345. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
  346. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
  347. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
  348. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
  349. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
  350. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
  351. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
  352. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
  353. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
  354. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
  355. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
  356. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
  357. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
  358. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
  359. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
  360. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
  361. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
  362. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
  363. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
  364. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
  365. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
  366. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
  367. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
  368. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
  369. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
  370. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
  371. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
  372. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
  373. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
  374. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
  375. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
  376. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
  377. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
  378. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
  379. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
  380. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
  381. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
  382. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  383. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  384. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
  385. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
  386. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
  387. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  388. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  389. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
  390. package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
  391. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  392. package/node_modules/get-east-asian-width/index.js +30 -0
  393. package/node_modules/get-east-asian-width/license +9 -0
  394. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  395. package/node_modules/get-east-asian-width/lookup.js +138 -0
  396. package/node_modules/get-east-asian-width/package.json +71 -0
  397. package/node_modules/get-east-asian-width/readme.md +65 -0
  398. package/node_modules/get-east-asian-width/utilities.js +24 -0
  399. package/node_modules/marked/LICENSE +44 -0
  400. package/node_modules/marked/README.md +115 -0
  401. package/node_modules/marked/bin/main.js +284 -0
  402. package/node_modules/marked/bin/marked.js +16 -0
  403. package/node_modules/marked/lib/marked.d.ts +760 -0
  404. package/node_modules/marked/lib/marked.esm.js +77 -0
  405. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  406. package/node_modules/marked/lib/marked.umd.js +79 -0
  407. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  408. package/node_modules/marked/man/marked.1 +113 -0
  409. package/node_modules/marked/man/marked.1.md +93 -0
  410. package/node_modules/marked/package.json +103 -0
  411. package/package.json +25 -10
  412. package/dist/tui-ink/app.d.ts +0 -76
  413. package/dist/tui-ink/app.js +0 -2377
  414. package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
  415. package/dist/tui-ink/approval/approval-dialog.js +0 -184
  416. package/dist/tui-ink/approval/diff-view.d.ts +0 -7
  417. package/dist/tui-ink/approval/diff-view.js +0 -44
  418. package/dist/tui-ink/approval/select.d.ts +0 -35
  419. package/dist/tui-ink/approval/select.js +0 -98
  420. package/dist/tui-ink/code-highlight.d.ts +0 -8
  421. package/dist/tui-ink/code-highlight.js +0 -121
  422. package/dist/tui-ink/command-palette.d.ts +0 -32
  423. package/dist/tui-ink/command-palette.js +0 -158
  424. package/dist/tui-ink/compaction-progress.d.ts +0 -19
  425. package/dist/tui-ink/compaction-progress.js +0 -74
  426. package/dist/tui-ink/detect-theme.d.ts +0 -1
  427. package/dist/tui-ink/detect-theme.js +0 -1
  428. package/dist/tui-ink/feedback-dialog.d.ts +0 -19
  429. package/dist/tui-ink/feedback-dialog.js +0 -133
  430. package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
  431. package/dist/tui-ink/feishu-setup-picker.js +0 -271
  432. package/dist/tui-ink/footer.d.ts +0 -26
  433. package/dist/tui-ink/footer.js +0 -51
  434. package/dist/tui-ink/input-box.d.ts +0 -189
  435. package/dist/tui-ink/input-box.js +0 -1279
  436. package/dist/tui-ink/input-history.d.ts +0 -1
  437. package/dist/tui-ink/input-history.js +0 -1
  438. package/dist/tui-ink/markdown.d.ts +0 -103
  439. package/dist/tui-ink/markdown.js +0 -747
  440. package/dist/tui-ink/message-list.d.ts +0 -49
  441. package/dist/tui-ink/message-list.js +0 -841
  442. package/dist/tui-ink/model-picker.d.ts +0 -143
  443. package/dist/tui-ink/model-picker.js +0 -966
  444. package/dist/tui-ink/plan-confirm.d.ts +0 -7
  445. package/dist/tui-ink/plan-confirm.js +0 -115
  446. package/dist/tui-ink/question-dialog.d.ts +0 -8
  447. package/dist/tui-ink/question-dialog.js +0 -135
  448. package/dist/tui-ink/rewind-picker.d.ts +0 -8
  449. package/dist/tui-ink/rewind-picker.js +0 -102
  450. package/dist/tui-ink/run-session-picker.js +0 -22
  451. package/dist/tui-ink/run.d.ts +0 -62
  452. package/dist/tui-ink/run.js +0 -103
  453. package/dist/tui-ink/session-picker.d.ts +0 -10
  454. package/dist/tui-ink/session-picker.js +0 -113
  455. package/dist/tui-ink/stats-panel.d.ts +0 -11
  456. package/dist/tui-ink/stats-panel.js +0 -65
  457. package/dist/tui-ink/subagent-inspector.d.ts +0 -21
  458. package/dist/tui-ink/subagent-inspector.js +0 -205
  459. package/dist/tui-ink/use-terminal-size.d.ts +0 -4
  460. package/dist/tui-ink/use-terminal-size.js +0 -48
  461. package/dist/tui-ink/waiting-indicator.d.ts +0 -12
  462. package/dist/tui-ink/waiting-indicator.js +0 -129
  463. package/dist/tui-ink/welcome.d.ts +0 -25
  464. package/dist/tui-ink/welcome.js +0 -88
  465. package/dist/tui-ink/wordmark.d.ts +0 -8
  466. package/dist/tui-ink/wordmark.js +0 -103
  467. /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
  468. /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
  469. /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
  470. /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
  471. /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
  472. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
  473. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
  474. /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
  475. /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
  476. /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
  477. /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
  478. /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
  479. /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
  480. /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
  481. /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
  482. /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
  483. /package/dist/{tui-ink → tui/model}/width.js +0 -0
@@ -0,0 +1,771 @@
1
+ /**
2
+ * Transcript row renderer (Phase 5): DisplayMessage[] -> styled terminal
3
+ * rows. Pure — no pi-tui/Ink imports; the pi app feeds rows into Text
4
+ * components and unit tests assert the strings directly.
5
+ *
6
+ * Visual parity targets from the legacy renderer (message-list.tsx):
7
+ * - user message: highlighted card, '›' marker, vertical padding
8
+ * - assistant: plain wrapped text, blank-line separated
9
+ * - reasoning: dim, collapsed to one line unless expanded
10
+ * - tool calls: single-line trace with status glyph, name, arg preview,
11
+ * error result preview
12
+ * - synthetic rows (interrupt, compaction, notices) rendered as notices
13
+ */
14
+ import chalk from "chalk";
15
+ import stringWidth from "string-width";
16
+ import { truncateVisual } from "../../text-display.js";
17
+ import { darkTheme } from "../model/theme.js";
18
+ import { themeBackground, themeDim, themeForeground } from "../model/theme-style.js";
19
+ import { buildTraceGroups, formatTracePath } from "../model/trace-groups.js";
20
+ import { splitImageDisplayContent } from "../image-display.js";
21
+ function toolTraceLabel(tool) {
22
+ return tool.name;
23
+ }
24
+ function plainProjection(rows) {
25
+ return {
26
+ rows: [...rows],
27
+ traceTargets: rows.map(() => undefined),
28
+ leadingSpacer: rows.length > 0 && stringWidth(rows[0] ?? "") === 0,
29
+ trailingSpacer: rows.length > 0 && stringWidth(rows.at(-1) ?? "") === 0,
30
+ };
31
+ }
32
+ export function joinTranscriptProjections(blocks) {
33
+ const rows = [];
34
+ const traceTargets = [];
35
+ let leadingSpacer = false;
36
+ let trailingSpacer = false;
37
+ for (const block of blocks) {
38
+ let start = 0;
39
+ let end = block.rows.length;
40
+ while (start < end && block.rows[start] === "")
41
+ start += 1;
42
+ while (end > start && block.rows[end - 1] === "")
43
+ end -= 1;
44
+ if (start === end)
45
+ continue;
46
+ const blockHasLeadingSpacer = start === 0
47
+ ? (block.leadingSpacer ?? stringWidth(block.rows[start] ?? "") === 0)
48
+ : stringWidth(block.rows[start] ?? "") === 0;
49
+ if (rows.length === 0)
50
+ leadingSpacer = blockHasLeadingSpacer;
51
+ const previousHasSpacer = rows.length > 0 && trailingSpacer;
52
+ if (rows.length > 0 && !previousHasSpacer && !blockHasLeadingSpacer) {
53
+ rows.push("");
54
+ traceTargets.push(undefined);
55
+ }
56
+ rows.push(...block.rows.slice(start, end));
57
+ traceTargets.push(...block.traceTargets.slice(start, end));
58
+ trailingSpacer = end === block.rows.length
59
+ ? (block.trailingSpacer ?? stringWidth(block.rows[end - 1] ?? "") === 0)
60
+ : stringWidth(block.rows[end - 1] ?? "") === 0;
61
+ }
62
+ return { rows, traceTargets, leadingSpacer, trailingSpacer };
63
+ }
64
+ export function createTranscriptTheme(getTheme) {
65
+ return {
66
+ userBg: (text) => themeBackground(getTheme().userMessageBg, text),
67
+ userText: (text) => themeForeground(getTheme().userMessageText, text),
68
+ accent: (text) => themeForeground(getTheme().accent, text),
69
+ dim: (text) => themeDim(getTheme().dim, text),
70
+ error: (text) => themeForeground(getTheme().error, text),
71
+ success: (text) => themeForeground(getTheme().success, text),
72
+ hoverBorder: (text) => themeDim(getTheme().border, text),
73
+ selectionBorder: (text) => themeForeground(getTheme().border, text),
74
+ hoverBackground: (text) => themeBackground(getTheme().traceHoverBg, text),
75
+ selectionBackground: (text) => themeBackground(getTheme().traceSelectedBg, text),
76
+ imageChip: (text, active) => themeForeground(getTheme().inputText, themeBackground(active ? getTheme().traceSelectedBg : getTheme().backgroundElement, text)),
77
+ };
78
+ }
79
+ export const defaultTranscriptTheme = createTranscriptTheme(() => darkTheme);
80
+ const TRACE_BORDER_MIN_COLUMNS = 4;
81
+ const IMAGE_LABEL_SPACE_SENTINEL = "\uE000";
82
+ const TRACE_CONTENT_LEFT_PAD = 2;
83
+ const TRACE_RESERVED_COLUMNS = 4; // left border + content pad + right border
84
+ // Keep a zero-width row in projection joins without painting a terminal cell.
85
+ // The row exists in layout at rest, so hover can add corners without reflow.
86
+ const TRACE_IDLE_VPAD = "\x1b[0m";
87
+ /** Visible-width-preserving truncation (input is unstyled at this point). */
88
+ function truncateVisible(text, maxColumns) {
89
+ return truncateVisual(text, maxColumns);
90
+ }
91
+ export function renderUserCard(content, options, images = [], messageKey = "user") {
92
+ const theme = options.theme ?? defaultTranscriptTheme;
93
+ const terminalWidth = Math.max(1, Math.floor(options.columns));
94
+ // Leave two terminal cells unpainted. Painting the physical last column can
95
+ // trigger an automatic wrap in several terminals and leaves a broken bg
96
+ // patch at the right edge. At one or two columns there is no room to reserve
97
+ // that margin, so use every available cell.
98
+ const width = terminalWidth > 2 ? terminalWidth - 2 : terminalWidth;
99
+ // The normal card gutter is four cells (marker + trailing pad). On an
100
+ // extremely narrow terminal, degrade to a plain painted row; forcing the
101
+ // historical 20-column minimum is what made settled rows overflow after a
102
+ // 20x5 resize even though the live trace itself was width-safe.
103
+ if (width < 5) {
104
+ return wrapPlain(content, width).map((line) => theme.userBg(truncateVisible(line, width)));
105
+ }
106
+ const textWidth = width - 4; // " › " + trailing pad
107
+ const split = images.length > 0 ? splitImageDisplayContent(content) : undefined;
108
+ const body = split?.bodyLines.join("\n") ?? content;
109
+ // Preserve the label's visible width while preventing the generic word
110
+ // wrapper from splitting `[Image #1]` at its internal ASCII space.
111
+ const protectedBody = images.reduce((value, image) => value.replaceAll(image.label, image.label.replaceAll(" ", IMAGE_LABEL_SPACE_SENTINEL)), body);
112
+ const lines = body.length > 0
113
+ ? wrapPlain(protectedBody, textWidth).map((line) => line.replaceAll(IMAGE_LABEL_SPACE_SENTINEL, " "))
114
+ : [];
115
+ if (split) {
116
+ for (const reference of split.referenceLines) {
117
+ if (images.some((image) => reference === `└ ${image.label}`))
118
+ lines.push(reference);
119
+ else
120
+ lines.push(...wrapPlain(reference, textWidth));
121
+ }
122
+ }
123
+ const rows = [];
124
+ const pad = " ".repeat(width);
125
+ rows.push(theme.userBg(pad));
126
+ lines.forEach((line, index) => {
127
+ // JS length/padEnd counts code units, not terminal cells: CJK characters
128
+ // occupy two columns. Visual padding prevents the line from overflowing,
129
+ // hard-wrapping, and resetting the painted background before the edge.
130
+ let renderedLine = line;
131
+ for (const image of images) {
132
+ if (!renderedLine.includes(image.label))
133
+ continue;
134
+ const groupKey = `image:${messageKey}:${image.label}`;
135
+ const chip = (theme.imageChip ?? ((text) => text))(image.label, options.traceInteraction?.isGroupHovered(groupKey) === true
136
+ || options.traceInteraction?.isGroupSelected(groupKey) === true);
137
+ renderedLine = renderedLine.replace(image.label, chip);
138
+ }
139
+ const padded = `${renderedLine}${" ".repeat(Math.max(0, textWidth - stringWidth(renderedLine)))}`;
140
+ const filled = (index === 0 ? theme.accent(" › ") : " ") + theme.userText(padded) + " ";
141
+ rows.push(theme.userBg(filled));
142
+ });
143
+ // Keep the painted padding symmetric so the message body is vertically
144
+ // centered. Separation from the following trace belongs outside the card;
145
+ // removing only the bottom pad shifts every message down by half a row.
146
+ rows.push(theme.userBg(pad));
147
+ return rows;
148
+ }
149
+ function projectUserCard(message, options) {
150
+ const rows = renderUserCard(message.content, options, message.images ?? [], message.key ?? "user");
151
+ const targets = rows.map(() => undefined);
152
+ if (!message.images?.length)
153
+ return { rows, traceTargets: targets };
154
+ // User-card geometry is top padding + wrapped source lines + bottom padding.
155
+ // Inline chips retain their composer row; legacy reconstructed attachments
156
+ // may still occupy standalone reference rows.
157
+ for (let index = 0; index < message.images.length; index += 1) {
158
+ const image = message.images[index];
159
+ const row = rows.findIndex((line) => line.includes(image.label));
160
+ // At widths too narrow to show the complete atomic label, do not attach an
161
+ // action to an unrelated wrapped fragment.
162
+ if (row <= 0 || row >= rows.length - 1)
163
+ continue;
164
+ const messageKey = message.key ?? "user";
165
+ const groupKey = `image:${messageKey}:${image.label}`;
166
+ // The current interaction model is row-oriented. If multiple chips share
167
+ // one row, keep the first stable target rather than changing action order.
168
+ targets[row] ??= {
169
+ kind: "image",
170
+ key: groupKey,
171
+ groupKey,
172
+ imageLabel: image.label,
173
+ foldable: false,
174
+ action: { kind: "open-image", messageKey, imageLabel: image.label },
175
+ };
176
+ }
177
+ return { rows, traceTargets: targets };
178
+ }
179
+ export function wrapPlain(text, columns) {
180
+ const out = [];
181
+ const splitToken = (token) => {
182
+ const chunks = [];
183
+ let chunk = "";
184
+ let width = 0;
185
+ for (const char of token) {
186
+ const charWidth = stringWidth(char);
187
+ if (chunk && width + charWidth > columns) {
188
+ chunks.push(chunk);
189
+ chunk = "";
190
+ width = 0;
191
+ }
192
+ chunk += char;
193
+ width += charWidth;
194
+ }
195
+ if (chunk)
196
+ chunks.push(chunk);
197
+ return chunks;
198
+ };
199
+ for (const paragraph of text.split("\n")) {
200
+ if (paragraph === "") {
201
+ out.push("");
202
+ continue;
203
+ }
204
+ let line = "";
205
+ for (const word of paragraph.split(/\s+/)) {
206
+ for (const chunk of splitToken(word)) {
207
+ const candidate = line ? `${line} ${chunk}` : chunk;
208
+ if (line && stringWidth(candidate) > columns) {
209
+ out.push(line);
210
+ line = chunk;
211
+ }
212
+ else {
213
+ line = candidate;
214
+ }
215
+ }
216
+ }
217
+ if (line)
218
+ out.push(line);
219
+ }
220
+ return out;
221
+ }
222
+ /**
223
+ * Project assistant prose without transcript spacing. Live streaming and
224
+ * settled history both call this function, so completing a turn cannot change
225
+ * the answer gutter or wrapping width.
226
+ */
227
+ export function projectAssistantRows(content, options) {
228
+ const terminalWidth = Math.max(1, Math.floor(options.columns));
229
+ const width = Math.max(1, terminalWidth - 2);
230
+ if (options.markdownRenderer) {
231
+ return options.markdownRenderer(content, width)
232
+ .map((row) => truncateVisible(row, terminalWidth));
233
+ }
234
+ return wrapPlain(content, width)
235
+ .map((row) => truncateVisible(row, terminalWidth));
236
+ }
237
+ export function renderAssistant(content, options) {
238
+ return [...projectAssistantRows(content, options), ""];
239
+ }
240
+ /**
241
+ * Join reasoning, tool, and prose surfaces with one semantic separator.
242
+ * Individual projectors own only their visible rows; this composition layer
243
+ * owns vertical rhythm, so live and settled timelines cannot drift apart.
244
+ */
245
+ export function joinTranscriptBlocks(blocks) {
246
+ const rows = [];
247
+ for (const block of blocks) {
248
+ let start = 0;
249
+ let end = block.length;
250
+ while (start < end && block[start] === "")
251
+ start += 1;
252
+ while (end > start && block[end - 1] === "")
253
+ end -= 1;
254
+ if (start === end)
255
+ continue;
256
+ if (rows.length > 0 && rows.at(-1) !== "")
257
+ rows.push("");
258
+ rows.push(...block.slice(start, end));
259
+ }
260
+ return rows;
261
+ }
262
+ /** Grok minimal mode keeps the same rolling reasoning surface live/settled. */
263
+ export const MINIMAL_REASONING_BODY_ROWS = 5;
264
+ /**
265
+ * Grok-style reasoning surface. Reasoning alone owns a one-cell rail; normal
266
+ * assistant prose never inherits this gutter. The same projection is used by
267
+ * the live row pool and committed transcript.
268
+ */
269
+ export function projectReasoningRows(content, options, projection = {}) {
270
+ const theme = options.theme ?? defaultTranscriptTheme;
271
+ const columns = Math.max(1, Math.floor(options.columns));
272
+ const bodyWidth = Math.max(1, columns - 1);
273
+ const body = content
274
+ .split("\n")
275
+ .filter((line) => line.trim() !== "")
276
+ .flatMap((line) => wrapPlain(line, bodyWidth));
277
+ if (body.length === 0)
278
+ return [];
279
+ const limit = Math.max(0, projection.maxBodyRows ?? (options.showReasoning ? body.length : MINIMAL_REASONING_BODY_ROWS));
280
+ const fromEnd = projection.fromEnd ?? !options.showReasoning;
281
+ const visible = limit === 0
282
+ ? []
283
+ : fromEnd
284
+ ? body.slice(-limit)
285
+ : body.slice(0, limit);
286
+ const style = (text) => theme.dim(chalk.italic(text));
287
+ const rows = [style(truncateVisible(`┃◆ Thinking${projection.running ? "…" : ""}`, columns))];
288
+ for (const line of visible) {
289
+ rows.push(style(truncateVisible(`┃${line}`, columns)));
290
+ }
291
+ if (body.length > visible.length) {
292
+ const suffix = options.showReasoning ? "┃…" : "┃… (Ctrl+T to expand)";
293
+ rows.push(style(truncateVisible(suffix, columns)));
294
+ }
295
+ return rows;
296
+ }
297
+ export function renderReasoning(content, options) {
298
+ const rows = projectReasoningRows(content, options);
299
+ if (rows.length === 0)
300
+ return [];
301
+ rows.push("");
302
+ return rows;
303
+ }
304
+ function argPreview(tool) {
305
+ const args = tool.args;
306
+ const firstString = ["command", "path", "pattern", "file_path", "url", "query"].find((key) => typeof args?.[key] === "string");
307
+ if (firstString)
308
+ return String(args[firstString]);
309
+ const raw = Object.keys(args ?? {})[0];
310
+ return raw ? String(args[raw]).slice(0, 40) : "";
311
+ }
312
+ export function renderToolTrace(tool, options) {
313
+ const theme = options.theme ?? defaultTranscriptTheme;
314
+ const label = toolTraceLabel(tool) || tool.name;
315
+ const preview = argPreview(tool);
316
+ const columns = Math.max(1, Math.floor(options.columns));
317
+ const text = truncateVisible(`◆ ${label}${preview ? ` ${preview}` : ""}`, columns);
318
+ const parts = [tool.isError ? theme.error(text) : `${theme.accent("◆")}${text.slice(1)}`];
319
+ if (tool.isError && tool.result !== undefined && options.verboseTrace) {
320
+ const errPreview = String(tool.result).split("\n")[0] ?? "";
321
+ parts.push(`\n${theme.error(truncateVisible(` ↳ ${errPreview}`, columns))}`);
322
+ }
323
+ else if (!tool.isError && options.verboseTrace && tool.result !== undefined) {
324
+ const okPreview = String(tool.result).split("\n")[0] ?? "";
325
+ parts.push(`\n${theme.dim(truncateVisible(` ↳ ${okPreview}`, columns))}`);
326
+ }
327
+ return parts.join("");
328
+ }
329
+ /**
330
+ * Render the same semantic trace groups used by the Ink TUI. This is shared
331
+ * by the live row pool and settled transcript so tool_start/tool_update/
332
+ * tool_end cannot change from a rich trace into an unrelated one-line row at
333
+ * the commit boundary.
334
+ */
335
+ export function renderToolTraceGroups(toolCalls, options, renderOptions = {}) {
336
+ return projectToolTraceGroups(toolCalls, options, renderOptions).rows;
337
+ }
338
+ export function projectToolTraceGroups(toolCalls, options, renderOptions = {}) {
339
+ const groups = buildTraceGroups([...toolCalls]);
340
+ const rows = [];
341
+ const traceTargets = [];
342
+ // `showActivity` remains part of the call contract, but Grok expresses
343
+ // activity by mutating the tool entry itself (`running`) instead of adding
344
+ // a second Working heading with a different marker.
345
+ void renderOptions.showActivity;
346
+ for (const group of groups) {
347
+ const projected = projectTraceGroup(group, options);
348
+ rows.push(...projected.rows);
349
+ traceTargets.push(...projected.traceTargets);
350
+ }
351
+ return {
352
+ rows,
353
+ traceTargets,
354
+ leadingSpacer: groups.length > 0,
355
+ trailingSpacer: groups.length > 0,
356
+ };
357
+ }
358
+ function projectTraceGroup(group, options) {
359
+ const theme = options.theme ?? defaultTranscriptTheme;
360
+ const columns = Math.max(1, options.columns);
361
+ const frameColumns = columns > 2 ? columns - 2 : columns;
362
+ const contentColumns = frameColumns >= TRACE_BORDER_MIN_COLUMNS
363
+ ? Math.max(1, frameColumns - TRACE_RESERVED_COLUMNS)
364
+ : frameColumns;
365
+ const interaction = options.traceInteraction;
366
+ const groupKey = interaction?.groupKey(group);
367
+ const readInteraction = group.kind === "read" ? interaction : undefined;
368
+ const singleRead = readInteraction && groupKey && group.raw.length === 1 ? group.raw[0] : undefined;
369
+ const singleItemKey = singleRead && interaction ? interaction.itemKey(group, singleRead.id) : undefined;
370
+ const interactive = interaction !== undefined && groupKey !== undefined;
371
+ const groupFoldable = isTraceGroupFoldable(group);
372
+ const singleItemFoldable = singleRead ? isTraceItemFoldable(singleRead) : false;
373
+ const expanded = interactive
374
+ ? singleItemKey
375
+ ? interaction.isItemExpanded(singleItemKey)
376
+ : interaction.isGroupExpanded(groupKey)
377
+ : false;
378
+ const selected = interaction?.isSelected(singleItemKey ?? groupKey ?? "") ?? false;
379
+ const marker = expanded ? "⌄" : selected ? "›" : "◆";
380
+ const status = group.pending
381
+ ? " running"
382
+ : group.statusLabel
383
+ ? ` ${group.statusLabel}`
384
+ : group.hasError
385
+ ? ` ${group.errorCount || 1} error${(group.errorCount || 1) === 1 ? "" : "s"}`
386
+ : "";
387
+ const detail = singleRead
388
+ ? ` ${readTraceLabel(singleRead)}`
389
+ : group.description
390
+ ? ` ${group.description}`
391
+ : group.command
392
+ ? ` ${group.command.replace(/\s+/g, " ")}`
393
+ : group.count !== undefined && group.noun
394
+ ? ` ${group.count} ${group.noun}`
395
+ : "";
396
+ const header = `${marker} ${group.title}${detail}${status}`;
397
+ const rows = [group.hasError
398
+ ? theme.error(truncateVisible(header, contentColumns))
399
+ : `${theme.accent(marker)}${truncateVisible(header.slice(marker.length), Math.max(0, contentColumns - stringWidth(marker)))}`];
400
+ const subagentIds = group.kind === "subagent"
401
+ ? group.raw[0]?.metadata?.subagents
402
+ ?.map((member) => typeof member?.subAgentId === "string" ? member.subAgentId : "")
403
+ .filter(Boolean) ?? []
404
+ : [];
405
+ const groupAction = subagentIds.length === 1
406
+ ? { kind: "open-subagent", subAgentId: subagentIds[0] }
407
+ : typeof group.raw[0]?.metadata?.taskId === "string"
408
+ ? { kind: "open-task", taskId: group.raw[0].metadata.taskId }
409
+ : undefined;
410
+ const groupTarget = groupKey
411
+ ? { kind: "group", key: groupKey, groupKey, foldable: groupFoldable, action: groupAction }
412
+ : undefined;
413
+ const singleTarget = singleItemKey && groupKey && singleRead
414
+ ? { kind: "item", key: singleItemKey, groupKey, toolId: singleRead.id, foldable: singleItemFoldable }
415
+ : undefined;
416
+ const traceTargets = [singleTarget ?? groupTarget];
417
+ const finish = () => decorateTraceGroup({ rows, traceTargets }, singleTarget ?? groupTarget, groupKey, frameColumns, theme, interaction);
418
+ // Interactive Read groups follow Grok's scrollback model: the summary row
419
+ // is collapsed by default; double-click reveals member rows. Individual
420
+ // members then own their file preview fold without changing the whole group.
421
+ if (readInteraction && groupKey) {
422
+ if (singleRead && singleTarget) {
423
+ if (expanded)
424
+ appendReadPreview(rows, traceTargets, singleRead, singleTarget, contentColumns, theme, " ");
425
+ return finish();
426
+ }
427
+ if (!expanded)
428
+ return finish();
429
+ for (const tool of group.raw) {
430
+ const itemKey = readInteraction.itemKey(group, tool.id);
431
+ const itemExpanded = readInteraction.isItemExpanded(itemKey);
432
+ const itemSelected = readInteraction.isSelected(itemKey);
433
+ const itemMarker = itemExpanded ? "⌄" : itemSelected ? "›" : "◆";
434
+ const label = readTraceLabel(tool);
435
+ const itemText = truncateVisible(` ${itemMarker} ${label}`, contentColumns);
436
+ rows.push(tool.isError ? theme.error(itemText) : theme.dim(itemText));
437
+ const itemTarget = {
438
+ kind: "item",
439
+ key: itemKey,
440
+ groupKey,
441
+ toolId: tool.id,
442
+ foldable: isTraceItemFoldable(tool),
443
+ };
444
+ traceTargets.push(itemTarget);
445
+ if (itemExpanded) {
446
+ appendReadPreview(rows, traceTargets, tool, itemTarget, contentColumns, theme, " ");
447
+ }
448
+ }
449
+ return finish();
450
+ }
451
+ // Grok tool blocks are collapsed by default. A single click selects the
452
+ // entry; a double-click reveals the detail surface only when the block has
453
+ // something meaningful to reveal. Failed List/Search blocks are deliberately
454
+ // non-foldable and keep their error detail visible in-place.
455
+ if (interactive && groupFoldable && !expanded)
456
+ return finish();
457
+ // While Execute is running, retain the command's own lines just like Ink.
458
+ // The header is only a summary and may be truncated on a narrow terminal.
459
+ const commandLines = group.commandLines ?? [];
460
+ const commandNeedsBlock = group.kind === "execute"
461
+ && (!!group.description
462
+ || commandLines.length > 1
463
+ || stringWidth(`◆ ${group.title} ${group.command ?? ""}`) > contentColumns);
464
+ if (commandNeedsBlock) {
465
+ const wrappedCommand = commandLines.flatMap((line) => wrapPlain(line || " ", Math.max(1, contentColumns - 2)));
466
+ for (const line of wrappedCommand.slice(0, 4)) {
467
+ rows.push(theme.dim(truncateVisible(` ${line}`, contentColumns)));
468
+ traceTargets.push(groupTarget);
469
+ }
470
+ if (wrappedCommand.length > 4) {
471
+ rows.push(theme.dim(truncateVisible(` … ${wrappedCommand.length - 4} more lines`, contentColumns)));
472
+ traceTargets.push(groupTarget);
473
+ }
474
+ }
475
+ const collapseSuccessfulExecute = !interactive
476
+ && group.kind === "execute"
477
+ && !group.pending
478
+ && !group.hasError
479
+ && !options.verboseTrace;
480
+ const details = group.kind === "search"
481
+ ? [...group.items, ...group.previewLines]
482
+ : group.previewLines.length > 0
483
+ ? group.previewLines
484
+ : group.items;
485
+ if (collapseSuccessfulExecute) {
486
+ const outputLines = group.previewLines.length + group.omitted;
487
+ rows.push(theme.dim(truncateVisible(` ⎿ ${outputLines > 0
488
+ ? `${outputLines} line${outputLines === 1 ? "" : "s"} output · Ctrl+O to view`
489
+ : "no output"}`, contentColumns)));
490
+ traceTargets.push(groupTarget);
491
+ }
492
+ else {
493
+ for (let index = 0; index < details.length; index += 1) {
494
+ const prefix = index === 0 ? " ↳ " : " ";
495
+ const line = truncateVisible(`${prefix}${details[index] ?? ""}`, contentColumns);
496
+ rows.push(group.hasError ? theme.error(line) : theme.dim(line));
497
+ traceTargets.push(groupTarget);
498
+ }
499
+ }
500
+ for (let index = 0; index < group.errorLines.length; index += 1) {
501
+ const prefix = index === 0 ? " ↳ " : " ";
502
+ rows.push(theme.error(truncateVisible(`${prefix}${group.errorLines[index] ?? ""}`, contentColumns)));
503
+ traceTargets.push(groupTarget);
504
+ }
505
+ if (group.omitted > 0) {
506
+ rows.push(theme.dim(truncateVisible(` … ${group.omitted} more`, contentColumns)));
507
+ traceTargets.push(groupTarget);
508
+ }
509
+ return finish();
510
+ }
511
+ function decorateTraceGroup(projection, target, groupKey, columns, theme, interaction) {
512
+ const selected = groupKey !== undefined && interaction?.isGroupSelected(groupKey) === true;
513
+ const hovered = groupKey !== undefined
514
+ && interaction?.isGroupHovered(groupKey) === true
515
+ && !selected;
516
+ const active = selected || hovered;
517
+ const border = selected
518
+ ? (theme.selectionBorder ?? theme.hoverBorder ?? theme.dim)
519
+ : (theme.hoverBorder ?? theme.dim);
520
+ const background = selected
521
+ ? (theme.selectionBackground ?? theme.hoverBackground ?? ((text) => text))
522
+ : (theme.hoverBackground ?? ((text) => text));
523
+ if (columns < TRACE_BORDER_MIN_COLUMNS) {
524
+ return {
525
+ rows: [
526
+ TRACE_IDLE_VPAD,
527
+ ...projection.rows.map((row) => {
528
+ if (!active)
529
+ return row;
530
+ const clipped = truncateVisible(row, columns);
531
+ return background(`${clipped}${" ".repeat(Math.max(0, columns - stringWidth(clipped)))}`);
532
+ }),
533
+ TRACE_IDLE_VPAD,
534
+ ],
535
+ traceTargets: [target, ...projection.traceTargets, target],
536
+ leadingSpacer: true,
537
+ trailingSpacer: true,
538
+ };
539
+ }
540
+ const top = active
541
+ ? border(`┌${" ".repeat(columns - 2)}┐`)
542
+ : TRACE_IDLE_VPAD;
543
+ const bottom = active
544
+ ? border(`└${" ".repeat(columns - 2)}┘`)
545
+ : TRACE_IDLE_VPAD;
546
+ const rows = projection.rows.map((row) => {
547
+ if (!active)
548
+ return `${" ".repeat(TRACE_CONTENT_LEFT_PAD + 1)}${row}`;
549
+ const interior = `${" ".repeat(TRACE_CONTENT_LEFT_PAD)}${row}`;
550
+ const padding = " ".repeat(Math.max(0, columns - 2 - stringWidth(interior)));
551
+ return `${border("│")}${background(`${interior}${padding}`)}${border("│")}`;
552
+ });
553
+ return {
554
+ rows: [top, ...rows, bottom],
555
+ traceTargets: [target, ...projection.traceTargets, target],
556
+ leadingSpacer: true,
557
+ trailingSpacer: true,
558
+ };
559
+ }
560
+ function isTraceItemFoldable(tool) {
561
+ return typeof tool.result === "string" && tool.result.trim().length > 0;
562
+ }
563
+ function isTraceGroupFoldable(group) {
564
+ switch (group.kind) {
565
+ case "read":
566
+ return group.raw.length > 1 || group.raw.some(isTraceItemFoldable);
567
+ case "list":
568
+ return !group.hasError && (group.items.length > 0 || group.omitted > 0);
569
+ case "search":
570
+ // Grok keeps successful Search entries foldable even for zero results so
571
+ // the expanded view can still reveal the query metadata / no-result state.
572
+ return !group.hasError;
573
+ case "execute":
574
+ return !!group.description
575
+ || (group.commandLines?.length ?? 0) > 1
576
+ || group.previewLines.length > 0
577
+ || group.errorLines.length > 0
578
+ || group.omitted > 0;
579
+ default:
580
+ return false;
581
+ }
582
+ }
583
+ function readTraceLabel(tool) {
584
+ const value = tool.args.path ?? tool.args.file ?? tool.metadata?.path ?? argPreview(tool);
585
+ return formatTracePath(value || tool.name);
586
+ }
587
+ function appendReadPreview(rows, traceTargets, tool, target, columns, theme, indent) {
588
+ if (tool.result === undefined)
589
+ return;
590
+ const normalized = tool.result.replace(/\r\n/g, "\n");
591
+ const source = tool.name === "bash" && normalized.startsWith("stdout:\n")
592
+ ? normalized.slice("stdout:\n".length)
593
+ : normalized;
594
+ const preview = source
595
+ .split("\n")
596
+ .filter((line) => line.trim() !== "")
597
+ .flatMap((line) => wrapPlain(line, Math.max(1, columns - indent.length)));
598
+ for (const line of preview.slice(0, 8)) {
599
+ rows.push((tool.isError ? theme.error : theme.dim)(truncateVisible(`${indent}${line}`, columns)));
600
+ traceTargets.push(target);
601
+ }
602
+ if (preview.length > 8) {
603
+ rows.push(theme.dim(truncateVisible(`${indent}… ${preview.length - 8} more lines`, columns)));
604
+ traceTargets.push(target);
605
+ }
606
+ }
607
+ /**
608
+ * Manual Compact keeps its terminal event deliberately terse, but the summary
609
+ * that replaced the old context remains inspectable. Treat the event as the
610
+ * same scrollback surface as a settled tool trace: hover/select paint the
611
+ * complete row and double-click folds the real summary in place.
612
+ */
613
+ function projectCompactionSummary(message, options) {
614
+ const theme = options.theme ?? defaultTranscriptTheme;
615
+ const columns = Math.max(1, options.columns);
616
+ const frameColumns = columns > 2 ? columns - 2 : columns;
617
+ const contentColumns = frameColumns >= TRACE_BORDER_MIN_COLUMNS
618
+ ? Math.max(1, frameColumns - TRACE_RESERVED_COLUMNS)
619
+ : frameColumns;
620
+ const interaction = options.traceInteraction;
621
+ const groupKey = `compact:${message.key ?? `${message.content}:${message.compactionSummary ?? ""}`}`;
622
+ const target = {
623
+ kind: "group",
624
+ key: groupKey,
625
+ groupKey,
626
+ foldable: true,
627
+ };
628
+ const expanded = interaction?.isGroupExpanded(groupKey) ?? false;
629
+ const selected = interaction?.isSelected(groupKey) ?? false;
630
+ const marker = expanded ? "⌄" : selected ? "›" : "◆";
631
+ const label = message.content.trim() || "Compaction completed.";
632
+ const header = `${marker} ${label}`;
633
+ const rows = [
634
+ `${theme.accent(marker)}${truncateVisible(header.slice(marker.length), Math.max(0, contentColumns - stringWidth(marker)))}`,
635
+ ];
636
+ const traceTargets = [target];
637
+ if (expanded) {
638
+ const summary = message.compactionSummary?.trim() ?? "";
639
+ const bodyWidth = Math.max(1, contentColumns - 2);
640
+ const bodyRows = options.markdownRenderer
641
+ ? options.markdownRenderer(summary, bodyWidth)
642
+ : wrapPlain(summary, bodyWidth);
643
+ for (const row of bodyRows) {
644
+ rows.push(theme.dim(truncateVisible(` ${row}`, contentColumns)));
645
+ traceTargets.push(target);
646
+ }
647
+ }
648
+ return decorateTraceGroup({ rows, traceTargets }, target, groupKey, frameColumns, theme, interaction);
649
+ }
650
+ export function renderMessage(message, options) {
651
+ return projectMessage(message, options).rows;
652
+ }
653
+ export function projectMessage(message, options) {
654
+ const theme = options.theme ?? defaultTranscriptTheme;
655
+ if (message.role === "user") {
656
+ return projectUserCard(message, options);
657
+ }
658
+ if (message.role === "error") {
659
+ return plainProjection([...projectAssistantRows(message.content, options).map(theme.error), ""]);
660
+ }
661
+ if (message.syntheticKind === "ui_compact_summary" && message.compactionSummary?.trim()) {
662
+ return projectCompactionSummary(message, options);
663
+ }
664
+ if (message.syntheticKind) {
665
+ return plainProjection([
666
+ ...projectAssistantRows(message.content, { ...options, markdownRenderer: undefined }).map(theme.dim),
667
+ "",
668
+ ]);
669
+ }
670
+ const blocks = [];
671
+ if (message.reasoning)
672
+ blocks.push(plainProjection(projectReasoningRows(message.reasoning, options)));
673
+ if (message.toolCalls?.length)
674
+ blocks.push(projectToolTraceGroups(message.toolCalls, options));
675
+ if (message.content?.trim())
676
+ blocks.push(plainProjection(projectAssistantRows(message.content, options)));
677
+ const projection = joinTranscriptProjections(blocks);
678
+ if (projection.rows.length === 0)
679
+ return plainProjection([""]);
680
+ projection.rows.push("");
681
+ projection.traceTargets.push(undefined);
682
+ projection.trailingSpacer = true;
683
+ return projection;
684
+ }
685
+ /**
686
+ * Render one assistant provider-turn as part of the surrounding user request.
687
+ *
688
+ * One agent run may contain many provider turns (`turn_end.willContinue`) —
689
+ * normally one before every tool call. Rendering every turn independently
690
+ * exposes the implementation boundary as `thinking → tool → thinking → tool`.
691
+ * The user-facing tool trace is grouped into one request lifecycle, while
692
+ * every provider turn's reasoning remains visible. Live rendering exposes
693
+ * each new reasoning segment, so suppressing later segments only after settle
694
+ * would create another disappear-at-finalize jump.
695
+ *
696
+ * Thinking
697
+ * ◆ tool
698
+ * Thinking (a later provider turn, if present)
699
+ * tool
700
+ * final answer
701
+ *
702
+ * Expanded mode still retains every reasoning segment for diagnostics.
703
+ */
704
+ function projectAssistantInRequest(message, options) {
705
+ const blocks = [];
706
+ if (message.reasoning) {
707
+ blocks.push(plainProjection(projectReasoningRows(message.reasoning, options)));
708
+ }
709
+ const appendTools = (tools) => {
710
+ blocks.push(projectToolTraceGroups(tools, options));
711
+ };
712
+ // Preserve the model's real commentary/tool timeline. `parts` is the
713
+ // canonical ordered stream; the top-level fields are only the fallback for
714
+ // restored/legacy messages. Rendering toolCalls first and content second
715
+ // inverted commentary that was emitted before a tool call.
716
+ if (message.parts?.length) {
717
+ for (const part of message.parts) {
718
+ if (part.type === "tools") {
719
+ appendTools(part.toolCalls);
720
+ }
721
+ else if (part.content.trim()) {
722
+ blocks.push(plainProjection(projectAssistantRows(part.content, options)));
723
+ }
724
+ }
725
+ }
726
+ else {
727
+ if (message.toolCalls?.length)
728
+ appendTools(message.toolCalls);
729
+ if (message.content?.trim())
730
+ blocks.push(plainProjection(projectAssistantRows(message.content, options)));
731
+ }
732
+ return joinTranscriptProjections(blocks);
733
+ }
734
+ export function renderTranscript(messages, options) {
735
+ return projectTranscript(messages, options).rows;
736
+ }
737
+ export function projectTranscript(messages, options) {
738
+ const messageBlocks = [];
739
+ for (let messageIndex = 0; messageIndex < messages.length; messageIndex += 1) {
740
+ const message = messages[messageIndex];
741
+ let block;
742
+ if (message.role === "user") {
743
+ block = projectUserCard(message, options);
744
+ }
745
+ else if (message.role === "assistant" && !message.syntheticKind) {
746
+ block = projectAssistantInRequest(message, options);
747
+ }
748
+ else {
749
+ block = projectMessage(message, options);
750
+ }
751
+ if (options.dimFromMessageIndex !== undefined && messageIndex >= options.dimFromMessageIndex) {
752
+ block = {
753
+ ...block,
754
+ rows: block.rows.map((row) => row ? chalk.dim(row) : row),
755
+ };
756
+ }
757
+ messageBlocks.push(block);
758
+ }
759
+ const projection = joinTranscriptProjections(messageBlocks);
760
+ // The transcript owns the sole separator before a live assistant surface.
761
+ // When that surface settles, the same row becomes the transcript's trailing
762
+ // separator, so user -> Thinking geometry cannot change at commit time.
763
+ if (projection.rows.length === 0)
764
+ return projection;
765
+ if (options.trailingSpacer !== false) {
766
+ projection.rows.push("");
767
+ projection.traceTargets.push(undefined);
768
+ projection.trailingSpacer = true;
769
+ }
770
+ return projection;
771
+ }