@prestyj/cli 4.6.4 → 4.10.2

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 (316) hide show
  1. package/dist/cli/auth.d.ts.map +1 -1
  2. package/dist/cli/auth.js +11 -10
  3. package/dist/cli/auth.js.map +1 -1
  4. package/dist/cli/mcp.js +1 -1
  5. package/dist/cli/shared.d.ts +12 -0
  6. package/dist/cli/shared.d.ts.map +1 -1
  7. package/dist/cli/shared.js +31 -6
  8. package/dist/cli/shared.js.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +63 -62
  11. package/dist/cli.js.map +1 -1
  12. package/dist/config.d.ts +7 -14
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +13 -17
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session-compaction.test.js +1 -1
  17. package/dist/core/agent-session-compaction.test.js.map +1 -1
  18. package/dist/core/agent-session.d.ts +1 -0
  19. package/dist/core/agent-session.d.ts.map +1 -1
  20. package/dist/core/agent-session.js +6 -2
  21. package/dist/core/agent-session.js.map +1 -1
  22. package/dist/core/auth-storage.d.ts +1 -58
  23. package/dist/core/auth-storage.d.ts.map +1 -1
  24. package/dist/core/auth-storage.js +3 -254
  25. package/dist/core/auth-storage.js.map +1 -1
  26. package/dist/core/auto-update.d.ts +4 -26
  27. package/dist/core/auto-update.d.ts.map +1 -1
  28. package/dist/core/auto-update.js +12 -237
  29. package/dist/core/auto-update.js.map +1 -1
  30. package/dist/core/claude-code-version.d.ts +1 -9
  31. package/dist/core/claude-code-version.d.ts.map +1 -1
  32. package/dist/core/claude-code-version.js +2 -105
  33. package/dist/core/claude-code-version.js.map +1 -1
  34. package/dist/core/compaction/compactor.test.js +2 -2
  35. package/dist/core/compaction/compactor.test.js.map +1 -1
  36. package/dist/core/file-lock.d.ts +1 -5
  37. package/dist/core/file-lock.d.ts.map +1 -1
  38. package/dist/core/file-lock.js +2 -75
  39. package/dist/core/file-lock.js.map +1 -1
  40. package/dist/core/logger.d.ts +4 -17
  41. package/dist/core/logger.d.ts.map +1 -1
  42. package/dist/core/logger.js +21 -110
  43. package/dist/core/logger.js.map +1 -1
  44. package/dist/core/lsp/client.d.ts +63 -0
  45. package/dist/core/lsp/client.d.ts.map +1 -0
  46. package/dist/core/lsp/client.js +215 -0
  47. package/dist/core/lsp/client.js.map +1 -0
  48. package/dist/core/lsp/format.d.ts +12 -0
  49. package/dist/core/lsp/format.d.ts.map +1 -0
  50. package/dist/core/lsp/format.js +30 -0
  51. package/dist/core/lsp/format.js.map +1 -0
  52. package/dist/core/lsp/format.test.d.ts +2 -0
  53. package/dist/core/lsp/format.test.d.ts.map +1 -0
  54. package/dist/core/lsp/format.test.js +56 -0
  55. package/dist/core/lsp/format.test.js.map +1 -0
  56. package/dist/core/lsp/integration.test.d.ts +2 -0
  57. package/dist/core/lsp/integration.test.d.ts.map +1 -0
  58. package/dist/core/lsp/integration.test.js +60 -0
  59. package/dist/core/lsp/integration.test.js.map +1 -0
  60. package/dist/core/lsp/jsonrpc.d.ts +32 -0
  61. package/dist/core/lsp/jsonrpc.d.ts.map +1 -0
  62. package/dist/core/lsp/jsonrpc.js +141 -0
  63. package/dist/core/lsp/jsonrpc.js.map +1 -0
  64. package/dist/core/lsp/jsonrpc.test.d.ts +2 -0
  65. package/dist/core/lsp/jsonrpc.test.d.ts.map +1 -0
  66. package/dist/core/lsp/jsonrpc.test.js +127 -0
  67. package/dist/core/lsp/jsonrpc.test.js.map +1 -0
  68. package/dist/core/lsp/manager.d.ts +40 -0
  69. package/dist/core/lsp/manager.d.ts.map +1 -0
  70. package/dist/core/lsp/manager.js +135 -0
  71. package/dist/core/lsp/manager.js.map +1 -0
  72. package/dist/core/lsp/manager.test.d.ts +2 -0
  73. package/dist/core/lsp/manager.test.d.ts.map +1 -0
  74. package/dist/core/lsp/manager.test.js +119 -0
  75. package/dist/core/lsp/manager.test.js.map +1 -0
  76. package/dist/core/lsp/servers.d.ts +45 -0
  77. package/dist/core/lsp/servers.d.ts.map +1 -0
  78. package/dist/core/lsp/servers.js +247 -0
  79. package/dist/core/lsp/servers.js.map +1 -0
  80. package/dist/core/mcp/defaults.d.ts +1 -1
  81. package/dist/core/mcp/defaults.js +1 -1
  82. package/dist/core/mcp/store.d.ts +1 -1
  83. package/dist/core/mcp/store.test.js +1 -1
  84. package/dist/core/mcp/store.test.js.map +1 -1
  85. package/dist/core/model-registry.d.ts +1 -75
  86. package/dist/core/model-registry.d.ts.map +1 -1
  87. package/dist/core/model-registry.js +4 -344
  88. package/dist/core/model-registry.js.map +1 -1
  89. package/dist/core/oauth/anthropic.d.ts +1 -3
  90. package/dist/core/oauth/anthropic.d.ts.map +1 -1
  91. package/dist/core/oauth/anthropic.js +2 -96
  92. package/dist/core/oauth/anthropic.js.map +1 -1
  93. package/dist/core/oauth/gemini.d.ts +1 -3
  94. package/dist/core/oauth/gemini.d.ts.map +1 -1
  95. package/dist/core/oauth/gemini.js +2 -385
  96. package/dist/core/oauth/gemini.js.map +1 -1
  97. package/dist/core/oauth/kimi.d.ts +1 -45
  98. package/dist/core/oauth/kimi.d.ts.map +1 -1
  99. package/dist/core/oauth/kimi.js +2 -277
  100. package/dist/core/oauth/kimi.js.map +1 -1
  101. package/dist/core/oauth/openai.d.ts +1 -3
  102. package/dist/core/oauth/openai.d.ts.map +1 -1
  103. package/dist/core/oauth/openai.js +2 -187
  104. package/dist/core/oauth/openai.js.map +1 -1
  105. package/dist/core/oauth/pkce.d.ts +1 -4
  106. package/dist/core/oauth/pkce.d.ts.map +1 -1
  107. package/dist/core/oauth/pkce.js +2 -16
  108. package/dist/core/oauth/pkce.js.map +1 -1
  109. package/dist/core/oauth/types.d.ts +1 -13
  110. package/dist/core/oauth/types.d.ts.map +1 -1
  111. package/dist/core/process-manager.d.ts +10 -0
  112. package/dist/core/process-manager.d.ts.map +1 -1
  113. package/dist/core/process-manager.js +45 -1
  114. package/dist/core/process-manager.js.map +1 -1
  115. package/dist/core/prompt-commands.d.ts.map +1 -1
  116. package/dist/core/prompt-commands.js +20 -7
  117. package/dist/core/prompt-commands.js.map +1 -1
  118. package/dist/core/session-manager.d.ts +26 -0
  119. package/dist/core/session-manager.d.ts.map +1 -1
  120. package/dist/core/session-manager.js +99 -2
  121. package/dist/core/session-manager.js.map +1 -1
  122. package/dist/core/session-manager.test.d.ts +2 -0
  123. package/dist/core/session-manager.test.d.ts.map +1 -0
  124. package/dist/core/session-manager.test.js +113 -0
  125. package/dist/core/session-manager.test.js.map +1 -0
  126. package/dist/core/settings-manager.d.ts +2 -0
  127. package/dist/core/settings-manager.d.ts.map +1 -1
  128. package/dist/core/settings-manager.js +6 -0
  129. package/dist/core/settings-manager.js.map +1 -1
  130. package/dist/core/telegram.d.ts +1 -112
  131. package/dist/core/telegram.d.ts.map +1 -1
  132. package/dist/core/telegram.js +2 -251
  133. package/dist/core/telegram.js.map +1 -1
  134. package/dist/core/thinking-level.d.ts +1 -4
  135. package/dist/core/thinking-level.d.ts.map +1 -1
  136. package/dist/core/thinking-level.js +3 -58
  137. package/dist/core/thinking-level.js.map +1 -1
  138. package/dist/core/voice-transcriber.d.ts +1 -32
  139. package/dist/core/voice-transcriber.d.ts.map +1 -1
  140. package/dist/core/voice-transcriber.js +3 -112
  141. package/dist/core/voice-transcriber.js.map +1 -1
  142. package/dist/interactive.d.ts.map +1 -1
  143. package/dist/interactive.js +5 -2
  144. package/dist/interactive.js.map +1 -1
  145. package/dist/modes/agent-home-mode.d.ts.map +1 -1
  146. package/dist/modes/agent-home-mode.js +10 -38
  147. package/dist/modes/agent-home-mode.js.map +1 -1
  148. package/dist/modes/serve-mode.d.ts.map +1 -1
  149. package/dist/modes/serve-mode.js +11 -39
  150. package/dist/modes/serve-mode.js.map +1 -1
  151. package/dist/session.d.ts.map +1 -1
  152. package/dist/session.js +13 -6
  153. package/dist/session.js.map +1 -1
  154. package/dist/tools/bash.d.ts.map +1 -1
  155. package/dist/tools/bash.js +6 -3
  156. package/dist/tools/bash.js.map +1 -1
  157. package/dist/tools/edit.d.ts +3 -1
  158. package/dist/tools/edit.d.ts.map +1 -1
  159. package/dist/tools/edit.js +15 -3
  160. package/dist/tools/edit.js.map +1 -1
  161. package/dist/tools/edit.test.js +70 -0
  162. package/dist/tools/edit.test.js.map +1 -1
  163. package/dist/tools/goals.d.ts +1 -1
  164. package/dist/tools/index.d.ts +15 -0
  165. package/dist/tools/index.d.ts.map +1 -1
  166. package/dist/tools/index.js +16 -3
  167. package/dist/tools/index.js.map +1 -1
  168. package/dist/tools/task-send.d.ts +12 -0
  169. package/dist/tools/task-send.d.ts.map +1 -0
  170. package/dist/tools/task-send.js +34 -0
  171. package/dist/tools/task-send.js.map +1 -0
  172. package/dist/tools/task-send.test.d.ts +2 -0
  173. package/dist/tools/task-send.test.d.ts.map +1 -0
  174. package/dist/tools/task-send.test.js +95 -0
  175. package/dist/tools/task-send.test.js.map +1 -0
  176. package/dist/tools/write.d.ts +3 -1
  177. package/dist/tools/write.d.ts.map +1 -1
  178. package/dist/tools/write.js +13 -2
  179. package/dist/tools/write.js.map +1 -1
  180. package/dist/tools/write.test.js +33 -0
  181. package/dist/tools/write.test.js.map +1 -1
  182. package/dist/types.d.ts +2 -0
  183. package/dist/types.d.ts.map +1 -1
  184. package/dist/ui/App.d.ts +16 -0
  185. package/dist/ui/App.d.ts.map +1 -1
  186. package/dist/ui/App.js +126 -12
  187. package/dist/ui/App.js.map +1 -1
  188. package/dist/ui/anchor-pad-reclaim.test.d.ts +2 -0
  189. package/dist/ui/anchor-pad-reclaim.test.d.ts.map +1 -0
  190. package/dist/ui/anchor-pad-reclaim.test.js +146 -0
  191. package/dist/ui/anchor-pad-reclaim.test.js.map +1 -0
  192. package/dist/ui/app-items.d.ts +14 -0
  193. package/dist/ui/app-items.d.ts.map +1 -1
  194. package/dist/ui/app-items.js +13 -0
  195. package/dist/ui/app-items.js.map +1 -1
  196. package/dist/ui/app-items.test.js +17 -1
  197. package/dist/ui/app-items.test.js.map +1 -1
  198. package/dist/ui/atomic-flush-regression.test.d.ts +2 -0
  199. package/dist/ui/atomic-flush-regression.test.d.ts.map +1 -0
  200. package/dist/ui/atomic-flush-regression.test.js +218 -0
  201. package/dist/ui/atomic-flush-regression.test.js.map +1 -0
  202. package/dist/ui/components/AssistantMessage.test.js +32 -1
  203. package/dist/ui/components/AssistantMessage.test.js.map +1 -1
  204. package/dist/ui/components/Footer.d.ts.map +1 -1
  205. package/dist/ui/components/Footer.js +1 -0
  206. package/dist/ui/components/Footer.js.map +1 -1
  207. package/dist/ui/components/InputArea.test.js +11 -5
  208. package/dist/ui/components/InputArea.test.js.map +1 -1
  209. package/dist/ui/components/Markdown.d.ts.map +1 -1
  210. package/dist/ui/components/Markdown.js +9 -1
  211. package/dist/ui/components/Markdown.js.map +1 -1
  212. package/dist/ui/footer-jump-regression.test.js +56 -9
  213. package/dist/ui/footer-jump-regression.test.js.map +1 -1
  214. package/dist/ui/frame-clip-regression.test.d.ts +2 -0
  215. package/dist/ui/frame-clip-regression.test.d.ts.map +1 -0
  216. package/dist/ui/frame-clip-regression.test.js +215 -0
  217. package/dist/ui/frame-clip-regression.test.js.map +1 -0
  218. package/dist/ui/hooks/useTranscriptHistory.d.ts +9 -1
  219. package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
  220. package/dist/ui/hooks/useTranscriptHistory.js +51 -19
  221. package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
  222. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts +2 -0
  223. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts.map +1 -0
  224. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js +81 -0
  225. package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js.map +1 -0
  226. package/dist/ui/live-area-clamp.test.js +37 -0
  227. package/dist/ui/live-area-clamp.test.js.map +1 -1
  228. package/dist/ui/live-area-height.d.ts.map +1 -1
  229. package/dist/ui/live-area-height.js +13 -6
  230. package/dist/ui/live-area-height.js.map +1 -1
  231. package/dist/ui/live-area-height.test.js +9 -0
  232. package/dist/ui/live-area-height.test.js.map +1 -1
  233. package/dist/ui/live-item-flush.d.ts +28 -0
  234. package/dist/ui/live-item-flush.d.ts.map +1 -1
  235. package/dist/ui/live-item-flush.js +44 -0
  236. package/dist/ui/live-item-flush.js.map +1 -1
  237. package/dist/ui/live-item-flush.test.js +83 -1
  238. package/dist/ui/live-item-flush.test.js.map +1 -1
  239. package/dist/ui/login.d.ts.map +1 -1
  240. package/dist/ui/login.js +12 -30
  241. package/dist/ui/login.js.map +1 -1
  242. package/dist/ui/mcp.d.ts +1 -2
  243. package/dist/ui/mcp.d.ts.map +1 -1
  244. package/dist/ui/mcp.js +8 -43
  245. package/dist/ui/mcp.js.map +1 -1
  246. package/dist/ui/pixel.d.ts.map +1 -1
  247. package/dist/ui/pixel.js +10 -38
  248. package/dist/ui/pixel.js.map +1 -1
  249. package/dist/ui/queued-message.test.js +7 -1
  250. package/dist/ui/queued-message.test.js.map +1 -1
  251. package/dist/ui/render.d.ts.map +1 -1
  252. package/dist/ui/render.js +154 -5
  253. package/dist/ui/render.js.map +1 -1
  254. package/dist/ui/sessions.d.ts.map +1 -1
  255. package/dist/ui/sessions.js +10 -42
  256. package/dist/ui/sessions.js.map +1 -1
  257. package/dist/ui/startup-banner-order.test.d.ts +2 -0
  258. package/dist/ui/startup-banner-order.test.d.ts.map +1 -0
  259. package/dist/ui/startup-banner-order.test.js +105 -0
  260. package/dist/ui/startup-banner-order.test.js.map +1 -0
  261. package/dist/ui/streaming-flush-bounce.test.js +41 -20
  262. package/dist/ui/streaming-flush-bounce.test.js.map +1 -1
  263. package/dist/ui/submit-boundary-gap.test.d.ts +2 -0
  264. package/dist/ui/submit-boundary-gap.test.d.ts.map +1 -0
  265. package/dist/ui/submit-boundary-gap.test.js +180 -0
  266. package/dist/ui/submit-boundary-gap.test.js.map +1 -0
  267. package/dist/ui/terminal-history.d.ts.map +1 -1
  268. package/dist/ui/terminal-history.js +28 -9
  269. package/dist/ui/terminal-history.js.map +1 -1
  270. package/dist/ui/terminal-history.test.js +10 -3
  271. package/dist/ui/terminal-history.test.js.map +1 -1
  272. package/dist/ui/testing/screen-recorder.d.ts.map +1 -1
  273. package/dist/ui/testing/screen-recorder.js +11 -1
  274. package/dist/ui/testing/screen-recorder.js.map +1 -1
  275. package/dist/ui/tui-history-parity.test.js +4 -1
  276. package/dist/ui/tui-history-parity.test.js.map +1 -1
  277. package/dist/ui/tui-simulation.test.js +7 -1
  278. package/dist/ui/tui-simulation.test.js.map +1 -1
  279. package/dist/ui/utils/assistant-stream-split.d.ts +7 -0
  280. package/dist/ui/utils/assistant-stream-split.d.ts.map +1 -1
  281. package/dist/ui/utils/assistant-stream-split.js +22 -1
  282. package/dist/ui/utils/assistant-stream-split.js.map +1 -1
  283. package/dist/ui/utils/assistant-stream-split.test.js +14 -0
  284. package/dist/ui/utils/assistant-stream-split.test.js.map +1 -1
  285. package/dist/ui/utils/markdown-renderer.d.ts.map +1 -1
  286. package/dist/ui/utils/markdown-renderer.js +46 -3
  287. package/dist/ui/utils/markdown-renderer.js.map +1 -1
  288. package/dist/ui/utils/markdown-renderer.test.d.ts +2 -0
  289. package/dist/ui/utils/markdown-renderer.test.d.ts.map +1 -0
  290. package/dist/ui/utils/markdown-renderer.test.js +63 -0
  291. package/dist/ui/utils/markdown-renderer.test.js.map +1 -0
  292. package/dist/ui/utils/terminal-graphics.d.ts +28 -1
  293. package/dist/ui/utils/terminal-graphics.d.ts.map +1 -1
  294. package/dist/ui/utils/terminal-graphics.js +44 -3
  295. package/dist/ui/utils/terminal-graphics.js.map +1 -1
  296. package/dist/ui/utils/terminal-graphics.test.js +40 -1
  297. package/dist/ui/utils/terminal-graphics.test.js.map +1 -1
  298. package/dist/utils/session-title.js +1 -1
  299. package/dist/utils/session-title.js.map +1 -1
  300. package/package.json +7 -6
  301. package/dist/core/model-registry.test.d.ts +0 -2
  302. package/dist/core/model-registry.test.d.ts.map +0 -1
  303. package/dist/core/model-registry.test.js +0 -95
  304. package/dist/core/model-registry.test.js.map +0 -1
  305. package/dist/core/oauth/gemini.test.d.ts +0 -2
  306. package/dist/core/oauth/gemini.test.d.ts.map +0 -1
  307. package/dist/core/oauth/gemini.test.js +0 -154
  308. package/dist/core/oauth/gemini.test.js.map +0 -1
  309. package/dist/core/thinking-level.test.d.ts +0 -2
  310. package/dist/core/thinking-level.test.d.ts.map +0 -1
  311. package/dist/core/thinking-level.test.js +0 -51
  312. package/dist/core/thinking-level.test.js.map +0 -1
  313. package/dist/core/voice-transcriber.test.d.ts +0 -2
  314. package/dist/core/voice-transcriber.test.d.ts.map +0 -1
  315. package/dist/core/voice-transcriber.test.js +0 -88
  316. package/dist/core/voice-transcriber.test.js.map +0 -1
@@ -0,0 +1,146 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Reproduction of the "large white space right before the final response".
3
+ //
4
+ // While the agent runs, the patched ink's bottom anchor converts every frame
5
+ // SHRINK into pad debt — blank lines emitted above the frame so the footer
6
+ // never moves up. That is correct mid-run: the next scrollback insert
7
+ // overwrites the pads (cursor-up consume). But when the RUN ENDS right after
8
+ // a shrink (live tool panel collapse, intermediate rows removed without a
9
+ // compensating insert), the leftover pads have nothing left to consume them:
10
+ // the anchor deactivates 500ms later and the blank block stays on screen,
11
+ // sitting exactly between the flushed transcript and the live final response.
12
+ //
13
+ // This test drives the real patched ink through that sequence and asserts the
14
+ // anchor deactivation reclaims the leftover pads (no blank gap above the
15
+ // frame, footer still bottom-pinned).
16
+ import React from "react";
17
+ import { Box, render, Text } from "ink";
18
+ import { describe, expect, it } from "vitest";
19
+ import stripAnsi from "strip-ansi";
20
+ import { ScreenRecorder, makeRecordingStdout } from "./testing/screen-recorder.js";
21
+ const COLUMNS = 60;
22
+ const ROWS = 24;
23
+ const FOOTER = "SIM_FOOTER_BOTTOM";
24
+ const FRAME_TOP = "SIM_FRAME_TOP";
25
+ function Frame({ liveRows }) {
26
+ return (_jsxs(Box, { flexDirection: "column", width: COLUMNS, children: [_jsx(Text, { children: FRAME_TOP }), Array.from({ length: liveRows }, (_, i) => (_jsxs(Text, { children: ["SIM_LIVE_", i + 1] }, i))), _jsx(Text, { children: "SIM_INPUT" }), _jsx(Text, { children: FOOTER })] }));
27
+ }
28
+ async function tick(ms = 60) {
29
+ await new Promise((resolve) => setTimeout(resolve, ms));
30
+ }
31
+ describe("anchor pad reclaim on run end", () => {
32
+ it("leaves no blank gap above the frame after the anchor deactivates", async () => {
33
+ const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
34
+ const stdout = makeRecordingStdout(recorder);
35
+ const transcriptRows = Array.from({ length: ROWS }, (_, i) => `SIM_HISTORY_${i + 1}`);
36
+ const mounted = render(_jsx(Frame, { liveRows: 2 }), {
37
+ stdout,
38
+ patchConsole: false,
39
+ maxFps: 1000,
40
+ // Mirror renderApp's INK_OPTIONS — the pad/anchor machinery is opt-in.
41
+ anchorFrameToBottom: true,
42
+ clipFrameToTerminalHeight: true,
43
+ });
44
+ // Skip cleanly when running against unpatched ink (the APIs are fork-only).
45
+ if (!mounted.setFrameAnchorActive || !mounted.insertBeforeFrame) {
46
+ mounted.unmount();
47
+ return;
48
+ }
49
+ // Mirrors renderApp's buildShrinkBackfill: last N transcript rows.
50
+ mounted.setFrameShrinkBackfill?.((needRows) => {
51
+ const lines = transcriptRows.slice(-needRows);
52
+ while (lines.length < needRows)
53
+ lines.unshift("");
54
+ return `${lines.join("\n")}\n`;
55
+ });
56
+ await tick();
57
+ // Turn starts: anchor on, transcript rows flushed to scrollback, live
58
+ // frame grows tall (streamed text + tool rows).
59
+ mounted.setFrameAnchorActive(true);
60
+ mounted.insertBeforeFrame(`${transcriptRows.join("\n")}\n`);
61
+ mounted.rerender(_jsx(Frame, { liveRows: 12 }));
62
+ await tick();
63
+ // Finalization shrink: intermediate live rows vanish with NO compensating
64
+ // scrollback insert. The anchor converts the shrink into pad debt — a
65
+ // blank block above the frame (correct mid-run: footer must not move).
66
+ mounted.rerender(_jsx(Frame, { liveRows: 2 }));
67
+ await tick();
68
+ // Run ends: App's 500ms timer deactivates the anchor. The leftover pads
69
+ // must be reclaimed here — nothing else will ever consume them at idle.
70
+ mounted.setFrameAnchorActive(false);
71
+ await tick(150);
72
+ const lines = recorder.viewportLines().map((line) => stripAnsi(line));
73
+ const frameTopIdx = lines.findIndex((line) => line.includes(FRAME_TOP));
74
+ const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
75
+ expect(frameTopIdx, "live frame visible").toBeGreaterThanOrEqual(0);
76
+ expect(footerIdx, "footer visible").toBeGreaterThan(frameTopIdx);
77
+ // Nothing below the footer (footer not stranded above blanks).
78
+ const lastNonBlank = lines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
79
+ expect(lastNonBlank, "nothing below the footer").toBe(footerIdx);
80
+ // THE BUG: no blank block above the frame. Every row between the last
81
+ // transcript row above the frame and the frame top must be non-blank.
82
+ const blanksAboveFrame = lines.slice(0, frameTopIdx).filter((line) => line.trim() === "");
83
+ expect(blanksAboveFrame.length, "no blank gap above the live frame").toBe(0);
84
+ mounted.unmount();
85
+ });
86
+ it("reclaims the leftover pads MID-RUN via an off→on pulse (no blank gap during a quiet stretch)", async () => {
87
+ const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
88
+ const stdout = makeRecordingStdout(recorder);
89
+ const transcriptRows = Array.from({ length: ROWS }, (_, i) => `SIM_HISTORY_${i + 1}`);
90
+ const mounted = render(_jsx(Frame, { liveRows: 2 }), {
91
+ stdout,
92
+ patchConsole: false,
93
+ maxFps: 1000,
94
+ anchorFrameToBottom: true,
95
+ clipFrameToTerminalHeight: true,
96
+ });
97
+ if (!mounted.setFrameAnchorActive || !mounted.insertBeforeFrame) {
98
+ mounted.unmount();
99
+ return;
100
+ }
101
+ mounted.setFrameShrinkBackfill?.((needRows) => {
102
+ const lines = transcriptRows.slice(-needRows);
103
+ while (lines.length < needRows)
104
+ lines.unshift("");
105
+ return `${lines.join("\n")}\n`;
106
+ });
107
+ await tick();
108
+ // Turn starts: anchor on, transcript flushed, frame grows tall.
109
+ mounted.setFrameAnchorActive(true);
110
+ mounted.insertBeforeFrame(`${transcriptRows.join("\n")}\n`);
111
+ mounted.rerender(_jsx(Frame, { liveRows: 12 }));
112
+ await tick();
113
+ // Tool batch finished, frame shrinks with NO compensating insert: pad debt
114
+ // forms (correct mid-run). Then the agent goes quiet (long thinking phase)
115
+ // — nothing grows the frame to consume the pads, so the blank gap lingers.
116
+ mounted.rerender(_jsx(Frame, { liveRows: 2 }));
117
+ await tick();
118
+ // App's mid-run debounce fires while the run is STILL active: an off→on
119
+ // pulse. The off transition reclaims the pad debt (gap closes, footer stays
120
+ // pinned); the on transition immediately restores pad protection.
121
+ mounted.setFrameAnchorActive(false);
122
+ mounted.setFrameAnchorActive(true);
123
+ await tick(150);
124
+ const lines = recorder.viewportLines().map((line) => stripAnsi(line));
125
+ const frameTopIdx = lines.findIndex((line) => line.includes(FRAME_TOP));
126
+ const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
127
+ expect(frameTopIdx, "live frame visible").toBeGreaterThanOrEqual(0);
128
+ expect(footerIdx, "footer visible").toBeGreaterThan(frameTopIdx);
129
+ const lastNonBlank = lines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
130
+ expect(lastNonBlank, "nothing below the footer").toBe(footerIdx);
131
+ const blanksAboveFrame = lines.slice(0, frameTopIdx).filter((line) => line.trim() === "");
132
+ expect(blanksAboveFrame.length, "no blank gap above the live frame after mid-run pulse").toBe(0);
133
+ // The pulse left the anchor ACTIVE: a further shrink must still pad so the
134
+ // footer does not move UP, proving protection was restored, not disabled.
135
+ mounted.rerender(_jsx(Frame, { liveRows: 1 }));
136
+ await tick();
137
+ const afterLines = recorder.viewportLines().map((line) => stripAnsi(line));
138
+ const afterFooterIdx = afterLines.findIndex((line) => line.includes(FOOTER));
139
+ expect(afterFooterIdx, "footer still visible after a post-pulse shrink").toBeGreaterThan(0);
140
+ expect(afterFooterIdx, "footer did not rise after a post-pulse shrink (anchor still active)").toBeGreaterThanOrEqual(footerIdx);
141
+ const afterLastNonBlank = afterLines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
142
+ expect(afterLastNonBlank, "nothing below the footer after a post-pulse shrink").toBe(afterFooterIdx);
143
+ mounted.unmount();
144
+ });
145
+ });
146
+ //# sourceMappingURL=anchor-pad-reclaim.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-pad-reclaim.test.js","sourceRoot":"","sources":["../../src/ui/anchor-pad-reclaim.test.tsx"],"names":[],"mappings":"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"}
@@ -275,4 +275,18 @@ export declare function lastVisibleTranscriptItem<T extends {
275
275
  imagePreviews?: readonly unknown[];
276
276
  }>(items: readonly T[]): T | undefined;
277
277
  export type CompletedItem = UserItem | TaskItem | GoalAgentTransitionItem | GoalProgressItem | AssistantItem | IdealHookItem | ToolStartItem | ToolDoneItem | ServerToolStartItem | ServerToolDoneItem | ErrorItem | InfoItem | StylePackItem | SetupHintItem | UpdateNoticeItem | QueuedItem | CompactingItem | CompactedItem | DurationItem | SessionSummaryItem | BannerItem | SubAgentGroupItem | ToolGroupItem | PlanTransitionItem | ModelTransitionItem | ThemeTransitionItem | PlanEventItem | StoppedItem | TombstoneItem | StepDoneItem;
278
+ /**
279
+ * True when a transcript item carries one or more inline-image previews.
280
+ *
281
+ * Used to detect images in a transcript region before the bottom-anchor shrink
282
+ * backfill tries to reconstruct it as text. A graphics escape's base64 payload
283
+ * is not recognized as zero-width by `wrapAnsi` (it hard-wraps into literal
284
+ * base64 text) and its visual row count never matches its newline count, so a
285
+ * text-only repaint of an image region desyncs Ink's erase math and displaces
286
+ * the on-screen image. Callers bail out of the text repaint when this is true.
287
+ */
288
+ export declare function itemHasImagePreviews(item: {
289
+ kind: string;
290
+ imagePreviews?: readonly unknown[];
291
+ }): boolean;
278
292
  //# sourceMappingURL=app-items.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-items.d.ts","sourceRoot":"","sources":["../../src/ui/app-items.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-items.d.ts","sourceRoot":"","sources":["../../src/ui/app-items.ts"],"names":[],"mappings":"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"}
@@ -58,4 +58,17 @@ export function lastVisibleTranscriptItem(items) {
58
58
  }
59
59
  return undefined;
60
60
  }
61
+ /**
62
+ * True when a transcript item carries one or more inline-image previews.
63
+ *
64
+ * Used to detect images in a transcript region before the bottom-anchor shrink
65
+ * backfill tries to reconstruct it as text. A graphics escape's base64 payload
66
+ * is not recognized as zero-width by `wrapAnsi` (it hard-wraps into literal
67
+ * base64 text) and its visual row count never matches its newline count, so a
68
+ * text-only repaint of an image region desyncs Ink's erase math and displaces
69
+ * the on-screen image. Callers bail out of the text repaint when this is true.
70
+ */
71
+ export function itemHasImagePreviews(item) {
72
+ return !!(item.imagePreviews && item.imagePreviews.length > 0);
73
+ }
61
74
  //# sourceMappingURL=app-items.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-items.js","sourceRoot":"","sources":["../../src/ui/app-items.ts"],"names":[],"mappings":"AAiIA,MAAM,CAAC,MAAM,kBAAkB,GAAG,wCAAwC,CAAC;AAE3E,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,2DAA2D,CAAC;AAElG,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GACjC,mEAAmE,CAAC;AAEtE,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAClC,uEAAuE,CAAC;AAY1E;gEACgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAA0D;IACpF,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;CAChB,CAAC;AAsJF;;;;;;;;;;;GAWG;AACH,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS;IAChD,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,mBAAmB;IACnB,kBAAkB;CACnB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAGvC;IACC,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAEvC,KAAmB;IACnB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"app-items.js","sourceRoot":"","sources":["../../src/ui/app-items.ts"],"names":[],"mappings":"AAiIA,MAAM,CAAC,MAAM,kBAAkB,GAAG,wCAAwC,CAAC;AAE3E,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,2DAA2D,CAAC;AAElG,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GACjC,mEAAmE,CAAC;AAEtE,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAClC,uEAAuE,CAAC;AAY1E;gEACgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAA0D;IACpF,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;CAChB,CAAC;AAsJF;;;;;;;;;;;GAWG;AACH,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS;IAChD,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,mBAAmB;IACnB,kBAAkB;CACnB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAGvC;IACC,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAEvC,KAAmB;IACnB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAkCD;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAGpC;IACC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjE,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { lastVisibleTranscriptItem } from "./app-items.js";
2
+ import { lastVisibleTranscriptItem, itemHasImagePreviews } from "./app-items.js";
3
3
  describe("lastVisibleTranscriptItem", () => {
4
4
  it("returns the last item when nothing is panel-replaced", () => {
5
5
  const items = [
@@ -35,4 +35,20 @@ describe("lastVisibleTranscriptItem", () => {
35
35
  expect(lastVisibleTranscriptItem([])).toBeUndefined();
36
36
  });
37
37
  });
38
+ describe("itemHasImagePreviews", () => {
39
+ it("is true for items carrying image previews", () => {
40
+ expect(itemHasImagePreviews({ kind: "user", imagePreviews: [{ base64: "AAAA" }] })).toBe(true);
41
+ });
42
+ it("is false for items with no previews or an empty list", () => {
43
+ expect(itemHasImagePreviews({ kind: "assistant" })).toBe(false);
44
+ expect(itemHasImagePreviews({ kind: "tool_done", imagePreviews: [] })).toBe(false);
45
+ });
46
+ it("detects an image anywhere in a transcript via some()", () => {
47
+ const history = [
48
+ { kind: "assistant", imagePreviews: undefined },
49
+ { kind: "user", imagePreviews: [{ base64: "x" }] },
50
+ ];
51
+ expect(history.some(itemHasImagePreviews)).toBe(true);
52
+ });
53
+ });
38
54
  //# sourceMappingURL=app-items.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-items.test.js","sourceRoot":"","sources":["../../src/ui/app-items.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE;YAC1B,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE;SAChC,CAAC;QACF,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE;YAC1B,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE;YAChC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE;YAC/B,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE;SACjC,CAAC;QACF,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE;YAC1B,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;SAClE,CAAC;QACF,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE;YAChC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE;SAChC,CAAC;QACF,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"app-items.test.js","sourceRoot":"","sources":["../../src/ui/app-items.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=atomic-flush-regression.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-flush-regression.test.d.ts","sourceRoot":"","sources":["../../src/ui/atomic-flush-regression.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,218 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Full-lifecycle regression for the atomic transcript flush.
3
+ //
4
+ // Models a complete turn on the native-scrollback default: idle → tall
5
+ // streaming response → live tool panel grows under the controls → done. The
6
+ // done transition finalizes the turn the way App does: queueFlush renders the
7
+ // transcript to ANSI and enqueues it through the patched Ink
8
+ // `insertBeforeFrame` while — in the SAME React batch — the live frame shrinks
9
+ // (streamed text flushed, tool panel hidden, activity → done swap). The
10
+ // resulting single frame write is `erase tall frame + scrollback bytes +
11
+ // shorter frame`, so:
12
+ // (a) Ink's full-screen repaint (eraseScreen + cursorTo top) never fires,
13
+ // (b) the footer never moves UP in the terminal buffer, and
14
+ // (c) the controls sit directly under the last transcript row after done —
15
+ // no stranded blank gap.
16
+ import React, { useEffect, useRef, useState } from "react";
17
+ import { render, Text, useStdout } from "ink";
18
+ import { describe, expect, it } from "vitest";
19
+ import stripAnsi from "strip-ansi";
20
+ import { ChatControls, ChatLayout } from "./components/ChatLayout.js";
21
+ import { ChatLivePane } from "./components/ChatLivePane.js";
22
+ import { TerminalSizeProvider } from "./hooks/useTerminalSize.js";
23
+ import { renderTranscriptItem } from "./transcript/TranscriptRenderer.js";
24
+ import { loadTheme, ThemeContext } from "./theme/theme.js";
25
+ import { createTerminalHistoryPrinter } from "./terminal-history.js";
26
+ import { useTranscriptHistory } from "./hooks/useTranscriptHistory.js";
27
+ import { ScreenRecorder, makeRecordingStdout } from "./testing/screen-recorder.js";
28
+ const COLUMNS = 80;
29
+ const ROWS = 24;
30
+ const theme = loadTheme("dark");
31
+ const terminalContext = {
32
+ theme,
33
+ columns: COLUMNS,
34
+ version: "sim",
35
+ model: "sim-model",
36
+ provider: "anthropic",
37
+ cwd: "/tmp/sim-project",
38
+ };
39
+ const FOOTER_BOTTOM = "SIM_ACTIVITY_BAR";
40
+ const TOOL_PANEL_ROWS = 4;
41
+ // Live-area budget: terminal rows minus the worst-case controls block (tool
42
+ // panel + status + input + footer rows). Mirrors App's layout measurement —
43
+ // the clamp is what keeps Ink out of its full-screen tall-frame repaint path.
44
+ const LIVE_AREA_BUDGET = ROWS - (TOOL_PANEL_ROWS + 4) - 2;
45
+ // Taller than the live-area budget, so streaming engages the clamp and the
46
+ // final flush writes more scrollback rows than the live region ever showed.
47
+ const longResponse = Array.from({ length: ROWS - 6 }, (_, i) => `SIM_LINE_${String(i + 1).padStart(2, "0")}`).join("\n");
48
+ function SimulatedControls({ running, toolRows }) {
49
+ return (_jsxs(ChatControls, { controlsRef: () => { }, children: [Array.from({ length: toolRows }, (_, i) => (_jsx(Text, { children: `SIM_TOOL_${i + 1}` }, `tool-${i}`))), _jsx(Text, { children: running ? "SIM_STREAMING" : "SIM_DONE" }), _jsx(Text, { children: "SIM_INPUT" }), _jsx(Text, { children: "SIM_FOOTER" }), _jsx(Text, { children: FOOTER_BOTTOM })] }));
50
+ }
51
+ function Driver({ phase, enqueueStdout }) {
52
+ const { write: writeStdout } = useStdout();
53
+ const [history, setHistory] = useState([]);
54
+ const [liveItems, setLiveItems] = useState([]);
55
+ // All visuals derive from internal state so the done transition can swap
56
+ // EVERYTHING (flush + streamed text removal + panel hide + activity → done)
57
+ // in one effect — i.e. one React batch — exactly like useAgentLoop's onDone.
58
+ const [view, setView] = useState({ running: false, streamingText: "", toolRows: 0 });
59
+ const committedRef = useRef(false);
60
+ // Stable printer across renders — mirrors renderApp, where one printer
61
+ // instance (and its printed-id dedup set) lives for the whole app lifetime.
62
+ const printerRef = useRef(null);
63
+ printerRef.current ??= createTerminalHistoryPrinter();
64
+ const { queueFlush } = useTranscriptHistory({
65
+ terminalHistoryPrinter: printerRef.current,
66
+ terminalHistoryContext: terminalContext,
67
+ writeStdout,
68
+ enqueueStdout,
69
+ sessionPathRef: { current: undefined },
70
+ sessionManagerRef: { current: null },
71
+ history,
72
+ setHistory,
73
+ setLiveItems,
74
+ });
75
+ // Idempotent setter: queueFlush is referentially unstable across renders
76
+ // (it lives in the hook's render scope), so this effect re-runs every
77
+ // commit. Returning the previous object when nothing changed keeps React
78
+ // from looping ("Maximum update depth exceeded") on the stream/tools
79
+ // phases, which set the same view on every run.
80
+ const applyView = (next) => {
81
+ setView((prev) => prev.running === next.running &&
82
+ prev.streamingText === next.streamingText &&
83
+ prev.toolRows === next.toolRows
84
+ ? prev
85
+ : next);
86
+ };
87
+ const applyViewRef = useRef(applyView);
88
+ applyViewRef.current = applyView;
89
+ useEffect(() => {
90
+ if (phase === "idle")
91
+ return;
92
+ if (phase === "stream") {
93
+ applyViewRef.current({ running: true, streamingText: longResponse, toolRows: 0 });
94
+ return;
95
+ }
96
+ if (phase === "tools") {
97
+ applyViewRef.current({
98
+ running: true,
99
+ streamingText: longResponse,
100
+ toolRows: TOOL_PANEL_ROWS,
101
+ });
102
+ return;
103
+ }
104
+ if (committedRef.current)
105
+ return;
106
+ committedRef.current = true;
107
+ // Finalize the turn: enqueue the transcript bytes (passive) and shrink the
108
+ // live frame in the same batch. The next frame write carries both.
109
+ queueFlush([{ kind: "assistant", id: "final", text: longResponse }]);
110
+ setLiveItems([]);
111
+ applyViewRef.current({ running: false, streamingText: "", toolRows: 0 });
112
+ }, [phase, queueFlush]);
113
+ const renderItem = (item, index, items) => renderTranscriptItem({
114
+ item,
115
+ index,
116
+ items,
117
+ version: "sim",
118
+ currentModel: "sim-model",
119
+ currentProvider: "anthropic",
120
+ displayedCwd: "/tmp/sim-project",
121
+ columns: COLUMNS,
122
+ theme,
123
+ renderMarkdown: true,
124
+ measuredLiveAreaRows: LIVE_AREA_BUDGET,
125
+ });
126
+ return (_jsx(ThemeContext.Provider, { value: theme, children: _jsx(TerminalSizeProvider, { children: _jsxs(ChatLayout, { columns: COLUMNS, children: [_jsx(ChatLivePane, { liveItems: liveItems, renderItem: renderItem, isRunning: view.running, visibleStreamingText: view.streamingText, streamingThinking: "", thinkingMs: 0, reserveStreamingSpacing: false, renderMarkdown: true, measuredLiveAreaRows: LIVE_AREA_BUDGET, assistantMarginTop: 0, streamingContinuation: false }), _jsx(SimulatedControls, { running: view.running, toolRows: view.toolRows })] }) }) }));
127
+ }
128
+ const ERASE_SCREEN = "\u001B[2J";
129
+ async function tick() {
130
+ await new Promise((resolve) => setTimeout(resolve, 45));
131
+ }
132
+ describe("atomic transcript flush lifecycle", () => {
133
+ it("finalizes a tool-heavy turn without repaints, footer rises, or gaps", async () => {
134
+ const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
135
+ const baseStdout = makeRecordingStdout(recorder);
136
+ const sink = { raw: "" };
137
+ const stdout = new Proxy(baseStdout, {
138
+ get(target, prop, receiver) {
139
+ if (prop === "write") {
140
+ return (chunk, cb) => {
141
+ sink.raw += chunk;
142
+ return target.write(chunk, cb);
143
+ };
144
+ }
145
+ return Reflect.get(target, prop, receiver);
146
+ },
147
+ });
148
+ // Mirrors renderApp's enqueueHistoryWrite: read the (patched) instance at
149
+ // call time, fall back to a raw write when the API is unavailable.
150
+ const instanceRef = {
151
+ current: null,
152
+ };
153
+ const enqueueStdout = (data) => {
154
+ if (instanceRef.current?.insertBeforeFrame) {
155
+ instanceRef.current.insertBeforeFrame(data);
156
+ }
157
+ else {
158
+ stdout.write(data);
159
+ }
160
+ };
161
+ const frames = [];
162
+ const capture = (label) => {
163
+ frames.push({ label, full: stripAnsi(recorder.fullText()) });
164
+ };
165
+ const mounted = render(_jsx(Driver, { phase: "idle", enqueueStdout: enqueueStdout }), {
166
+ stdout,
167
+ columns: COLUMNS,
168
+ rows: ROWS,
169
+ patchConsole: false,
170
+ maxFps: 1000,
171
+ });
172
+ instanceRef.current = mounted;
173
+ await tick();
174
+ capture("idle");
175
+ mounted.rerender(_jsx(Driver, { phase: "stream", enqueueStdout: enqueueStdout }));
176
+ await tick();
177
+ capture("tall streaming");
178
+ mounted.rerender(_jsx(Driver, { phase: "tools", enqueueStdout: enqueueStdout }));
179
+ await tick();
180
+ capture("tool panel grows");
181
+ mounted.rerender(_jsx(Driver, { phase: "done", enqueueStdout: enqueueStdout }));
182
+ await tick();
183
+ capture("done, first settle");
184
+ await tick();
185
+ capture("done, settled");
186
+ mounted.unmount();
187
+ // (a) Zero full-screen repaints (eraseScreen + cursorTo top) across the
188
+ // whole lifecycle — the clamp keeps Ink out of its tall-frame path, and
189
+ // the atomic insert keeps the shrink from ever needing a repaint.
190
+ expect(sink.raw.split(ERASE_SCREEN).length - 1, "full-screen repaints").toBe(0);
191
+ // (b) The footer's absolute row in the terminal buffer never moves UP.
192
+ // Scrollback growth pushes it down; an upward move means a shrink-write
193
+ // landed without its compensating scrollback bytes.
194
+ let previousFooterRow = -1;
195
+ for (const f of frames) {
196
+ const footerRow = f.full.split("\n").findIndex((line) => line.includes(FOOTER_BOTTOM));
197
+ expect(footerRow, `${f.label}: footer present`).toBeGreaterThanOrEqual(0);
198
+ expect(footerRow, `${f.label}: footer buffer row never decreases (was ${previousFooterRow})`).toBeGreaterThanOrEqual(previousFooterRow);
199
+ previousFooterRow = footerRow;
200
+ }
201
+ // (c) After done: the controls sit directly under the last transcript row —
202
+ // no stranded blank rows between the flushed response and the controls.
203
+ const settled = frames.at(-1);
204
+ const lines = settled.full.split("\n");
205
+ const lastTranscriptRow = lines.reduce((acc, line, i) => (line.includes("SIM_LINE_") ? i : acc), -1);
206
+ const controlsRow = lines.findIndex((line) => line.includes("SIM_DONE"));
207
+ expect(lastTranscriptRow, "flushed transcript present after done").toBeGreaterThanOrEqual(0);
208
+ expect(controlsRow, "controls present after done").toBeGreaterThan(lastTranscriptRow);
209
+ const between = lines.slice(lastTranscriptRow + 1, controlsRow);
210
+ expect(between.filter((line) => line.trim().length === 0).length, "no blank gap between transcript and controls").toBe(0);
211
+ // And the flushed response must never be duplicated (scrollback + live).
212
+ for (const f of frames) {
213
+ const occurrences = f.full.split("SIM_LINE_01").length - 1;
214
+ expect(occurrences, `${f.label}: flushed content not duplicated`).toBeLessThanOrEqual(1);
215
+ }
216
+ });
217
+ });
218
+ //# sourceMappingURL=atomic-flush-regression.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-flush-regression.test.js","sourceRoot":"","sources":["../../src/ui/atomic-flush-regression.test.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { Box, render, renderToString } from "ink";
4
- import { describe, expect, it } from "vitest";
4
+ import { describe, expect, it, vi } from "vitest";
5
5
  import stringWidth from "string-width";
6
6
  import stripAnsi from "strip-ansi";
7
7
  import { AssistantMessage } from "./AssistantMessage.js";
@@ -10,6 +10,12 @@ import { ToolExecution } from "./ToolExecution.js";
10
10
  import { ToolGroupExecution } from "./ToolGroupExecution.js";
11
11
  import { ServerToolExecution } from "./ServerToolExecution.js";
12
12
  import { TerminalSizeProvider } from "../hooks/useTerminalSize.js";
13
+ // BLACK_CIRCLE is platform-dependent (⏺ on macOS, ● elsewhere); pin it so
14
+ // the hardcoded frame expectations pass on Linux/Windows CI too.
15
+ vi.mock("../constants/figures.js", async (importOriginal) => ({
16
+ ...(await importOriginal()),
17
+ BLACK_CIRCLE: "\u23FA",
18
+ }));
13
19
  import { createTerminalHistoryPrinter, serializeCompletedItemToTerminalHistory, } from "../terminal-history.js";
14
20
  import { loadTheme } from "../theme/theme.js";
15
21
  import { pinStreamingTextBeforeToolBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "../App.js";
@@ -61,6 +67,22 @@ function renderWithTerminal(element) {
61
67
  function renderAssistantFrame(streaming, text = LONG_STREAMING_TEXT) {
62
68
  return renderWithTerminal(_jsx(AssistantMessage, { text: text, streaming: streaming }));
63
69
  }
70
+ /** Like renderWithTerminal but keeps blank rows — needed for alignment checks. */
71
+ function renderWithTerminalRaw(element) {
72
+ const originalColumns = process.stdout.columns;
73
+ const originalRows = process.stdout.rows;
74
+ process.stdout.columns = TERMINAL_COLUMNS;
75
+ process.stdout.rows = 20;
76
+ try {
77
+ return rawLinesOf(renderToString(_jsx(TerminalSizeProvider, { children: element }), {
78
+ columns: TERMINAL_COLUMNS,
79
+ }));
80
+ }
81
+ finally {
82
+ process.stdout.columns = originalColumns;
83
+ process.stdout.rows = originalRows;
84
+ }
85
+ }
64
86
  function renderTerminalHistoryAssistantText(text = LONG_STREAMING_TEXT) {
65
87
  return serializeCompletedItemToTerminalHistory({ kind: "assistant", text, id: "assistant-1" }, {
66
88
  theme: loadTheme("dark"),
@@ -218,6 +240,15 @@ describe("AssistantMessage live layout", () => {
218
240
  const lines = renderWithTerminal(_jsx(AssistantMessage, { text: text, streaming: true, availableTerminalHeight: 5 }));
219
241
  expect(lines.length).toBeLessThanOrEqual(5);
220
242
  });
243
+ it("REGRESSION: dot stays inline when the height clamp slices at a blank line", () => {
244
+ // 3 paragraphs render as [p1, "", p2, "", p3]. availableTerminalHeight=4
245
+ // tail-slices to ["", p2, "", p3] — the ⏺ prefix sits in a sibling box
246
+ // aligned to the first row, so a leading blank row left the dot floating
247
+ // one row above the text (alignment varied with the slice boundary).
248
+ const text = "First paragraph here.\n\nSecond paragraph here.\n\nThird paragraph here.";
249
+ const lines = renderWithTerminalRaw(_jsx(AssistantMessage, { text: text, availableTerminalHeight: 4 }));
250
+ expect(lines[0]).toBe(" \u23FA Second paragraph here.");
251
+ });
221
252
  it("renders the streaming frame with the same line structure as finalized history", () => {
222
253
  const streamingLines = renderAssistantFrame(true);
223
254
  const finalLines = renderAssistantFrame(false);