@alumbwe/anvil 1.0.19 → 1.0.20

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 (494) hide show
  1. package/README.md +28 -30
  2. package/bin/anvil +2 -0
  3. package/package.json +71 -75
  4. package/src/agents/bundled-agents.generated.d.ts +14 -0
  5. package/src/agents/bundled-agents.generated.ts +3760 -0
  6. package/src/app.tsx +399 -0
  7. package/src/chat.tsx +1698 -0
  8. package/src/cli-args.ts +134 -0
  9. package/src/commands/__tests__/anvil-command-aliases.test.ts +56 -0
  10. package/src/commands/__tests__/bash-command.test.ts +445 -0
  11. package/src/commands/__tests__/command-args.test.ts +349 -0
  12. package/src/commands/__tests__/copy-conversation-clipboard.test.ts +108 -0
  13. package/src/commands/__tests__/copy-conversation.test.ts +226 -0
  14. package/src/commands/__tests__/diagnostics-command.test.ts +35 -0
  15. package/src/commands/__tests__/image.test.ts +99 -0
  16. package/src/commands/__tests__/init.test.ts +456 -0
  17. package/src/commands/__tests__/new-command-rotates-chat.test.ts +91 -0
  18. package/src/commands/__tests__/process-diagnostics.test.ts +54 -0
  19. package/src/commands/__tests__/prompt-builders.test.ts +28 -0
  20. package/src/commands/__tests__/router-input.test.ts +298 -0
  21. package/src/commands/ads.ts +44 -0
  22. package/src/commands/command-registry.ts +726 -0
  23. package/src/commands/copy-conversation.ts +392 -0
  24. package/src/commands/help.ts +13 -0
  25. package/src/commands/image.ts +20 -0
  26. package/src/commands/init.ts +121 -0
  27. package/src/commands/process-diagnostics.ts +120 -0
  28. package/src/commands/prompt-builders.ts +100 -0
  29. package/src/commands/publish.ts +203 -0
  30. package/src/commands/router-utils.ts +80 -0
  31. package/src/commands/router.ts +487 -0
  32. package/src/commands/usage.ts +26 -0
  33. package/src/components/__tests__/ad-banner.test.tsx +83 -0
  34. package/src/components/__tests__/anvil-landing-heading-row.test.tsx +91 -0
  35. package/src/components/__tests__/anvil-model-selector.test.tsx +519 -0
  36. package/src/components/__tests__/anvil-offer-invariants.test.ts +54 -0
  37. package/src/components/__tests__/anvil-takeover-prompt.test.tsx +212 -0
  38. package/src/components/__tests__/chat-input-bar.test.tsx +130 -0
  39. package/src/components/__tests__/grid-layout.integration.test.tsx +304 -0
  40. package/src/components/__tests__/grid-layout.test.tsx +1051 -0
  41. package/src/components/__tests__/message-block.completion.test.tsx +104 -0
  42. package/src/components/__tests__/message-block.streaming.test.tsx +78 -0
  43. package/src/components/__tests__/message-with-agents.test.tsx +579 -0
  44. package/src/components/__tests__/multiline-input.test.tsx +1132 -0
  45. package/src/components/__tests__/queue-panel.test.tsx +398 -0
  46. package/src/components/__tests__/selectable-list.test.ts +232 -0
  47. package/src/components/__tests__/status-bar.test.tsx +44 -0
  48. package/src/components/__tests__/status-indicator.test.tsx +204 -0
  49. package/src/components/__tests__/terminal-thinking-orb.test.tsx +111 -0
  50. package/src/components/__tests__/user-error-banner.test.tsx +102 -0
  51. package/src/components/ad-banner.tsx +323 -0
  52. package/src/components/agent-checklist.tsx +350 -0
  53. package/src/components/agent-mode-toggle.tsx +241 -0
  54. package/src/components/anvil-active-session-summary.tsx +63 -0
  55. package/src/components/anvil-landing-screen.tsx +865 -0
  56. package/src/components/anvil-mark.tsx +52 -0
  57. package/src/components/anvil-model-selector.tsx +1087 -0
  58. package/src/components/anvil-referral-banner.tsx +604 -0
  59. package/src/components/anvil-superseded-screen.tsx +62 -0
  60. package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
  61. package/src/components/ask-user/__tests__/validation.test.ts +213 -0
  62. package/src/components/ask-user/components/accordion-question.tsx +153 -0
  63. package/src/components/ask-user/components/custom-answer-input.tsx +66 -0
  64. package/src/components/ask-user/components/options-list.tsx +133 -0
  65. package/src/components/ask-user/components/question-header.tsx +80 -0
  66. package/src/components/ask-user/components/question-option.tsx +89 -0
  67. package/src/components/ask-user/constants.ts +38 -0
  68. package/src/components/ask-user/index.tsx +631 -0
  69. package/src/components/ask-user/utils/validation.ts +85 -0
  70. package/src/components/attachment-card.tsx +65 -0
  71. package/src/components/blocks/agent-block-grid.tsx +58 -0
  72. package/src/components/blocks/agent-branch-item.tsx +311 -0
  73. package/src/components/blocks/agent-branch-wrapper.tsx +493 -0
  74. package/src/components/blocks/agent-list-branch.tsx +77 -0
  75. package/src/components/blocks/ask-user-branch.tsx +81 -0
  76. package/src/components/blocks/block-helpers.ts +11 -0
  77. package/src/components/blocks/blocks-renderer.tsx +289 -0
  78. package/src/components/blocks/content-with-markdown.tsx +33 -0
  79. package/src/components/blocks/image-block.tsx +128 -0
  80. package/src/components/blocks/implementor-row.tsx +429 -0
  81. package/src/components/blocks/single-block.tsx +201 -0
  82. package/src/components/blocks/thinking-block.tsx +67 -0
  83. package/src/components/blocks/tool-block-group.tsx +57 -0
  84. package/src/components/blocks/tool-branch.tsx +206 -0
  85. package/src/components/blocks/user-content-copy.tsx +157 -0
  86. package/src/components/bottom-banner.tsx +136 -0
  87. package/src/components/build-mode-buttons.tsx +110 -0
  88. package/src/components/button.tsx +66 -0
  89. package/src/components/chat-header.tsx +77 -0
  90. package/src/components/chat-history-screen.tsx +413 -0
  91. package/src/components/chat-input-bar.tsx +523 -0
  92. package/src/components/clickable-title-box.tsx +66 -0
  93. package/src/components/clickable.tsx +119 -0
  94. package/src/components/collapse-button.tsx +34 -0
  95. package/src/components/copy-button.tsx +203 -0
  96. package/src/components/elapsed-timer.tsx +55 -0
  97. package/src/components/error-boundary.tsx +55 -0
  98. package/src/components/feedback-container.tsx +181 -0
  99. package/src/components/feedback-icon-button.tsx +89 -0
  100. package/src/components/feedback-input-mode.tsx +344 -0
  101. package/src/components/file-attachment-card.tsx +98 -0
  102. package/src/components/grid-layout.tsx +80 -0
  103. package/src/components/help-banner.tsx +127 -0
  104. package/src/components/highlighted-text.tsx +52 -0
  105. package/src/components/image-card.tsx +167 -0
  106. package/src/components/image-thumbnail.tsx +114 -0
  107. package/src/components/input-cursor.tsx +77 -0
  108. package/src/components/input-mode-banner.tsx +57 -0
  109. package/src/components/load-previous-button.tsx +48 -0
  110. package/src/components/login-modal.tsx +475 -0
  111. package/src/components/message-block.tsx +346 -0
  112. package/src/components/message-footer.tsx +258 -0
  113. package/src/components/message-with-agents.tsx +506 -0
  114. package/src/components/mode-divider.tsx +43 -0
  115. package/src/components/multiline-input.tsx +1241 -0
  116. package/src/components/out-of-credits-banner.tsx +160 -0
  117. package/src/components/pending-attachments-banner.tsx +116 -0
  118. package/src/components/pending-bash-message.tsx +59 -0
  119. package/src/components/progress-bar.tsx +81 -0
  120. package/src/components/project-picker-screen.tsx +485 -0
  121. package/src/components/publish-confirmation.tsx +493 -0
  122. package/src/components/publish-container.tsx +687 -0
  123. package/src/components/queue-panel.tsx +310 -0
  124. package/src/components/raised-pill.tsx +95 -0
  125. package/src/components/renderers/plan-box.tsx +59 -0
  126. package/src/components/review-screen.tsx +112 -0
  127. package/src/components/scroll-to-bottom-button.tsx +34 -0
  128. package/src/components/segmented-control.tsx +187 -0
  129. package/src/components/selectable-list.tsx +248 -0
  130. package/src/components/selected-chips.tsx +80 -0
  131. package/src/components/separator.tsx +40 -0
  132. package/src/components/session-ended-banner.tsx +226 -0
  133. package/src/components/shimmer-text.tsx +248 -0
  134. package/src/components/status-bar.tsx +313 -0
  135. package/src/components/subscription-limit-banner.tsx +206 -0
  136. package/src/components/suggested-prompts.tsx +184 -0
  137. package/src/components/suggestion-menu.tsx +215 -0
  138. package/src/components/terminal-command-display.tsx +151 -0
  139. package/src/components/terminal-link.tsx +97 -0
  140. package/src/components/terminal-thinking-orb.tsx +95 -0
  141. package/src/components/text-attachment-card.tsx +77 -0
  142. package/src/components/thinking.tsx +109 -0
  143. package/src/components/tools/__tests__/apply-patch.test.tsx +84 -0
  144. package/src/components/tools/__tests__/code-search.test.tsx +45 -0
  145. package/src/components/tools/__tests__/gravity-index.test.ts +76 -0
  146. package/src/components/tools/__tests__/render-ui.test.tsx +92 -0
  147. package/src/components/tools/__tests__/run-terminal-command.test.ts +233 -0
  148. package/src/components/tools/apply-patch.tsx +95 -0
  149. package/src/components/tools/code-search.tsx +45 -0
  150. package/src/components/tools/composio.tsx +152 -0
  151. package/src/components/tools/diff-viewer.tsx +76 -0
  152. package/src/components/tools/glob.tsx +61 -0
  153. package/src/components/tools/gravity-index.tsx +77 -0
  154. package/src/components/tools/list-directory.tsx +61 -0
  155. package/src/components/tools/read-docs.tsx +37 -0
  156. package/src/components/tools/read-files.tsx +91 -0
  157. package/src/components/tools/read-subtree.tsx +44 -0
  158. package/src/components/tools/read-url.tsx +33 -0
  159. package/src/components/tools/registry.ts +126 -0
  160. package/src/components/tools/render-ui.tsx +122 -0
  161. package/src/components/tools/run-terminal-command.tsx +79 -0
  162. package/src/components/tools/skill.tsx +29 -0
  163. package/src/components/tools/str-replace.tsx +77 -0
  164. package/src/components/tools/suggest-followups.tsx +369 -0
  165. package/src/components/tools/task-completed.tsx +16 -0
  166. package/src/components/tools/tool-call-item.tsx +273 -0
  167. package/src/components/tools/types.ts +55 -0
  168. package/src/components/tools/web-search.tsx +33 -0
  169. package/src/components/tools/write-file.tsx +16 -0
  170. package/src/components/tools/write-todos.tsx +95 -0
  171. package/src/components/top-banner.tsx +178 -0
  172. package/src/components/usage-banner.tsx +244 -0
  173. package/src/components/user-error-banner.tsx +56 -0
  174. package/src/components/validation-error-popover.tsx +192 -0
  175. package/src/contexts/__tests__/chat-runtime-context.test.tsx +149 -0
  176. package/src/contexts/chat-runtime-context.tsx +240 -0
  177. package/src/data/slash-commands.test.ts +24 -0
  178. package/src/data/slash-commands.ts +249 -0
  179. package/src/entry.ts +12 -0
  180. package/src/hooks/__tests__/holds-live-anvil-slot.test.ts +49 -0
  181. package/src/hooks/__tests__/session-fetch-signal.test.ts +63 -0
  182. package/src/hooks/__tests__/use-activity-query.test.ts +1000 -0
  183. package/src/hooks/__tests__/use-ask-user-bridge.test.ts +176 -0
  184. package/src/hooks/__tests__/use-auth-query.test.ts +21 -0
  185. package/src/hooks/__tests__/use-chat-input-remount.test.tsx +66 -0
  186. package/src/hooks/__tests__/use-clipboard.test.tsx +73 -0
  187. package/src/hooks/__tests__/use-connection-status.test.ts +112 -0
  188. package/src/hooks/__tests__/use-directory-browser.test.ts +203 -0
  189. package/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
  190. package/src/hooks/__tests__/use-grid-layout.test.ts +349 -0
  191. package/src/hooks/__tests__/use-input-history.test.ts +699 -0
  192. package/src/hooks/__tests__/use-message-queue-editing.test.tsx +209 -0
  193. package/src/hooks/__tests__/use-message-queue.test.tsx +71 -0
  194. package/src/hooks/__tests__/use-path-tab-completion.test.ts +315 -0
  195. package/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
  196. package/src/hooks/__tests__/use-queue-ui.test.tsx +83 -0
  197. package/src/hooks/__tests__/use-searchable-list.test.ts +359 -0
  198. package/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
  199. package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +361 -0
  200. package/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
  201. package/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
  202. package/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
  203. package/src/hooks/__tests__/use-timeout.test.ts +330 -0
  204. package/src/hooks/__tests__/use-usage-query.test.ts +502 -0
  205. package/src/hooks/__tests__/use-user-details-query.test.ts +195 -0
  206. package/src/hooks/helpers/__tests__/send-message.test.ts +1918 -0
  207. package/src/hooks/helpers/send-message.ts +654 -0
  208. package/src/hooks/stream-state.ts +83 -0
  209. package/src/hooks/use-activity-query.ts +668 -0
  210. package/src/hooks/use-agent-validation.ts +74 -0
  211. package/src/hooks/use-anvil-ctrl-c-exit.ts +23 -0
  212. package/src/hooks/use-anvil-session-progress.ts +34 -0
  213. package/src/hooks/use-anvil-session.ts +806 -0
  214. package/src/hooks/use-anvil-streak-query.ts +61 -0
  215. package/src/hooks/use-ask-user-bridge.ts +92 -0
  216. package/src/hooks/use-auth-query.ts +262 -0
  217. package/src/hooks/use-auth-state.ts +152 -0
  218. package/src/hooks/use-chat-input.ts +105 -0
  219. package/src/hooks/use-chat-keyboard.ts +321 -0
  220. package/src/hooks/use-chat-messages.ts +254 -0
  221. package/src/hooks/use-chat-state.ts +165 -0
  222. package/src/hooks/use-chat-streaming.ts +180 -0
  223. package/src/hooks/use-chat-ui.ts +131 -0
  224. package/src/hooks/use-clipboard.ts +151 -0
  225. package/src/hooks/use-connection-status.ts +142 -0
  226. package/src/hooks/use-directory-browser.ts +88 -0
  227. package/src/hooks/use-elapsed-time.ts +116 -0
  228. package/src/hooks/use-event.ts +34 -0
  229. package/src/hooks/use-exit-handler.ts +66 -0
  230. package/src/hooks/use-fetch-login-url.ts +65 -0
  231. package/src/hooks/use-fingerprint.ts +61 -0
  232. package/src/hooks/use-gravity-ad.ts +656 -0
  233. package/src/hooks/use-grid-layout.ts +73 -0
  234. package/src/hooks/use-input-history.ts +173 -0
  235. package/src/hooks/use-login-keyboard-handlers.ts +75 -0
  236. package/src/hooks/use-login-polling.ts +114 -0
  237. package/src/hooks/use-logo.tsx +161 -0
  238. package/src/hooks/use-message-queue.ts +387 -0
  239. package/src/hooks/use-now.ts +20 -0
  240. package/src/hooks/use-path-tab-completion.ts +88 -0
  241. package/src/hooks/use-publish-mutation.ts +60 -0
  242. package/src/hooks/use-queue-controls.ts +68 -0
  243. package/src/hooks/use-queue-ui.ts +74 -0
  244. package/src/hooks/use-scroll-management.ts +184 -0
  245. package/src/hooks/use-searchable-list.ts +98 -0
  246. package/src/hooks/use-send-message.ts +773 -0
  247. package/src/hooks/use-sheen-animation.tsx +88 -0
  248. package/src/hooks/use-subscription-query.ts +71 -0
  249. package/src/hooks/use-suggestion-engine.ts +783 -0
  250. package/src/hooks/use-terminal-breakpoints.ts +77 -0
  251. package/src/hooks/use-terminal-dimensions.ts +46 -0
  252. package/src/hooks/use-terminal-focus.ts +29 -0
  253. package/src/hooks/use-terminal-layout.ts +189 -0
  254. package/src/hooks/use-theme.tsx +140 -0
  255. package/src/hooks/use-timeout.ts +85 -0
  256. package/src/hooks/use-update-preference.ts +66 -0
  257. package/src/hooks/use-usage-monitor.ts +55 -0
  258. package/src/hooks/use-usage-query.ts +124 -0
  259. package/src/hooks/use-user-details-query.ts +93 -0
  260. package/src/hooks/use-why-did-you-update.ts +177 -0
  261. package/src/index.tsx +473 -0
  262. package/src/init/__tests__/init-direnv.test.ts +526 -0
  263. package/src/init/init-app.ts +34 -0
  264. package/src/init/init-direnv.ts +133 -0
  265. package/src/login/constants.ts +60 -0
  266. package/src/login/login-flow.ts +204 -0
  267. package/src/login/plain-login.ts +108 -0
  268. package/src/login/utils.ts +183 -0
  269. package/src/native/ripgrep.ts +80 -0
  270. package/src/polyfills/bun-strip-ansi.ts +12 -0
  271. package/src/pre-init/tree-sitter-wasm.ts +96 -0
  272. package/src/project-files.ts +102 -0
  273. package/src/setup/key-setup.ts +295 -0
  274. package/src/setup/load-env.ts +106 -0
  275. package/src/state/__tests__/chat-store-focus.test.ts +22 -0
  276. package/src/state/__tests__/feedback-store.test.ts +257 -0
  277. package/src/state/anvil-model-store.ts +42 -0
  278. package/src/state/anvil-session-store.ts +53 -0
  279. package/src/state/chat-history-store.ts +30 -0
  280. package/src/state/chat-store.ts +544 -0
  281. package/src/state/feedback-store.ts +160 -0
  282. package/src/state/login-store.ts +150 -0
  283. package/src/state/message-block-store.ts +149 -0
  284. package/src/state/publish-store.ts +146 -0
  285. package/src/state/queue-panel-store.ts +26 -0
  286. package/src/state/review-store.ts +24 -0
  287. package/src/types/anvil-session.ts +17 -0
  288. package/src/types/chat-state.ts +15 -0
  289. package/src/types/chat.ts +241 -0
  290. package/src/types/contracts/send-message.ts +12 -0
  291. package/src/types/env.ts +98 -0
  292. package/src/types/function-params.ts +28 -0
  293. package/src/types/react19-compat.d.ts +19 -0
  294. package/src/types/store.ts +111 -0
  295. package/src/types/theme-system.ts +144 -0
  296. package/src/types/utils.ts +3 -0
  297. package/src/utils/__tests__/active-run.test.ts +140 -0
  298. package/src/utils/__tests__/activity-tracker.test.ts +440 -0
  299. package/src/utils/__tests__/agent-display.test.ts +139 -0
  300. package/src/utils/__tests__/analytics-client.test.ts +262 -0
  301. package/src/utils/__tests__/anonymous-id.test.ts +74 -0
  302. package/src/utils/__tests__/anvil-api.test.ts +578 -0
  303. package/src/utils/__tests__/anvil-instance-owner.test.ts +69 -0
  304. package/src/utils/__tests__/anvil-model-navigation.test.ts +102 -0
  305. package/src/utils/__tests__/anvil-premium-reset.test.ts +79 -0
  306. package/src/utils/__tests__/anvil-referral-cache.test.ts +116 -0
  307. package/src/utils/__tests__/anvil-session-api.test.ts +215 -0
  308. package/src/utils/__tests__/anvil-session-display.test.ts +23 -0
  309. package/src/utils/__tests__/anvil-streak-line.test.ts +217 -0
  310. package/src/utils/__tests__/arrays.test.ts +264 -0
  311. package/src/utils/__tests__/bash-context-processor.test.ts +38 -0
  312. package/src/utils/__tests__/block-processor.test.ts +810 -0
  313. package/src/utils/__tests__/chat-history.test.ts +203 -0
  314. package/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
  315. package/src/utils/__tests__/chat-meta.test.ts +130 -0
  316. package/src/utils/__tests__/clipboard-linux.test.ts +359 -0
  317. package/src/utils/__tests__/clipboard.test.ts +910 -0
  318. package/src/utils/__tests__/code-search-summary.test.ts +84 -0
  319. package/src/utils/__tests__/collapse-helpers.test.ts +1109 -0
  320. package/src/utils/__tests__/error-handling.test.ts +658 -0
  321. package/src/utils/__tests__/exit-cleanly.test.ts +110 -0
  322. package/src/utils/__tests__/feedback-helpers.test.ts +444 -0
  323. package/src/utils/__tests__/feedback-submission.test.ts +26 -0
  324. package/src/utils/__tests__/fetch-usage.test.ts +355 -0
  325. package/src/utils/__tests__/fingerprint.test.ts +144 -0
  326. package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +7 -0
  327. package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +3 -0
  328. package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +33 -0
  329. package/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
  330. package/src/utils/__tests__/format-timeout.test.ts +87 -0
  331. package/src/utils/__tests__/image-dimensions.test.ts +249 -0
  332. package/src/utils/__tests__/image-processor.test.ts +209 -0
  333. package/src/utils/__tests__/implementor-helpers.test.ts +1458 -0
  334. package/src/utils/__tests__/keyboard-actions.test.ts +660 -0
  335. package/src/utils/__tests__/layout-helpers.test.ts +36 -0
  336. package/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
  337. package/src/utils/__tests__/log-shipper.test.ts +53 -0
  338. package/src/utils/__tests__/markdown-renderer.test.tsx +421 -0
  339. package/src/utils/__tests__/message-block-helpers.test.ts +1263 -0
  340. package/src/utils/__tests__/message-updater.test.ts +645 -0
  341. package/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
  342. package/src/utils/__tests__/pending-attachments.test.ts +277 -0
  343. package/src/utils/__tests__/polling-backoff.test.ts +51 -0
  344. package/src/utils/__tests__/queue-panel-actions.test.ts +148 -0
  345. package/src/utils/__tests__/run-state-storage.test.ts +919 -0
  346. package/src/utils/__tests__/safe-json.test.ts +77 -0
  347. package/src/utils/__tests__/sdk-event-handlers.test.ts +685 -0
  348. package/src/utils/__tests__/send-message-helpers.test.ts +1898 -0
  349. package/src/utils/__tests__/send-message-timer.test.ts +64 -0
  350. package/src/utils/__tests__/settings.test.ts +99 -0
  351. package/src/utils/__tests__/strings.test.ts +192 -0
  352. package/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
  353. package/src/utils/__tests__/terminal-command-broker.test.ts +528 -0
  354. package/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
  355. package/src/utils/__tests__/terminal-io.test.ts +137 -0
  356. package/src/utils/__tests__/terminal-protocol-controller.test.ts +119 -0
  357. package/src/utils/__tests__/text-layout.test.ts +82 -0
  358. package/src/utils/__tests__/theme-platform-detection.test.ts +125 -0
  359. package/src/utils/__tests__/think-tag-parser.test.ts +159 -0
  360. package/src/utils/__tests__/trace-writer.test.ts +223 -0
  361. package/src/utils/__tests__/trim-chat-logs.test.ts +78 -0
  362. package/src/utils/__tests__/usage-banner-state.test.ts +298 -0
  363. package/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
  364. package/src/utils/__tests__/windows-terminal-health.test.ts +101 -0
  365. package/src/utils/__tests__/write-file-atomic.test.ts +127 -0
  366. package/src/utils/active-run.ts +131 -0
  367. package/src/utils/activity-tracker.ts +66 -0
  368. package/src/utils/agent-display.ts +87 -0
  369. package/src/utils/agent-helpers.ts +27 -0
  370. package/src/utils/agent-id-utils.ts +14 -0
  371. package/src/utils/ai-message-id.ts +12 -0
  372. package/src/utils/analytics.ts +361 -0
  373. package/src/utils/anonymous-id.ts +87 -0
  374. package/src/utils/anvil-agent-selection.ts +33 -0
  375. package/src/utils/anvil-api.ts +612 -0
  376. package/src/utils/anvil-client.ts +217 -0
  377. package/src/utils/anvil-instance-owner.ts +66 -0
  378. package/src/utils/anvil-model-navigation.ts +50 -0
  379. package/src/utils/anvil-premium-reset.ts +55 -0
  380. package/src/utils/anvil-referral-cache.ts +51 -0
  381. package/src/utils/anvil-session-api.ts +243 -0
  382. package/src/utils/anvil-session-display.ts +21 -0
  383. package/src/utils/anvil-streak-line.ts +93 -0
  384. package/src/utils/arrays.ts +117 -0
  385. package/src/utils/auth.ts +258 -0
  386. package/src/utils/bash-context-processor.ts +47 -0
  387. package/src/utils/bash-messages.ts +108 -0
  388. package/src/utils/block-margins.ts +35 -0
  389. package/src/utils/block-operations.ts +523 -0
  390. package/src/utils/block-processor.ts +213 -0
  391. package/src/utils/chat-history.ts +253 -0
  392. package/src/utils/chat-input-key-intercept.ts +52 -0
  393. package/src/utils/chat-meta.ts +90 -0
  394. package/src/utils/chat-scroll-accel.ts +151 -0
  395. package/src/utils/clipboard-image.ts +588 -0
  396. package/src/utils/clipboard.ts +397 -0
  397. package/src/utils/code-search-summary.ts +70 -0
  398. package/src/utils/collapse-helpers.ts +256 -0
  399. package/src/utils/config-dir.ts +24 -0
  400. package/src/utils/constants.ts +190 -0
  401. package/src/utils/create-event-handler-state.ts +73 -0
  402. package/src/utils/create-run-config.ts +121 -0
  403. package/src/utils/detect-shell.ts +116 -0
  404. package/src/utils/directory-browser.ts +72 -0
  405. package/src/utils/engagement.ts +55 -0
  406. package/src/utils/env.ts +92 -0
  407. package/src/utils/error-handling.ts +255 -0
  408. package/src/utils/error-messages.ts +62 -0
  409. package/src/utils/exit-cleanly.ts +86 -0
  410. package/src/utils/feedback-helpers.ts +103 -0
  411. package/src/utils/feedback-submission.ts +22 -0
  412. package/src/utils/fetch-usage.ts +76 -0
  413. package/src/utils/fingerprint.ts +245 -0
  414. package/src/utils/format-elapsed-time.ts +33 -0
  415. package/src/utils/format-session-units.ts +6 -0
  416. package/src/utils/format-timeout.ts +28 -0
  417. package/src/utils/format-validation-errors-for-message.ts +84 -0
  418. package/src/utils/git.ts +17 -0
  419. package/src/utils/helpers.ts +47 -0
  420. package/src/utils/image-display.ts +67 -0
  421. package/src/utils/image-handler.ts +325 -0
  422. package/src/utils/image-processor.ts +133 -0
  423. package/src/utils/image-thumbnail.ts +78 -0
  424. package/src/utils/implementor-helpers.ts +787 -0
  425. package/src/utils/input-modes.ts +174 -0
  426. package/src/utils/keyboard-actions.ts +392 -0
  427. package/src/utils/keypad-keys.ts +47 -0
  428. package/src/utils/layout-helpers.ts +33 -0
  429. package/src/utils/lazy-response-ads.ts +1 -0
  430. package/src/utils/local-agent-registry.ts +474 -0
  431. package/src/utils/log-shipper.ts +126 -0
  432. package/src/utils/logger.ts +327 -0
  433. package/src/utils/markdown-renderer.tsx +1090 -0
  434. package/src/utils/math.ts +3 -0
  435. package/src/utils/message-block-helpers.ts +684 -0
  436. package/src/utils/message-history.ts +143 -0
  437. package/src/utils/message-tree-utils.ts +67 -0
  438. package/src/utils/message-updater.ts +279 -0
  439. package/src/utils/open-file.ts +146 -0
  440. package/src/utils/open-url.ts +63 -0
  441. package/src/utils/path-completion.ts +111 -0
  442. package/src/utils/path-helpers.ts +38 -0
  443. package/src/utils/pending-attachments.ts +360 -0
  444. package/src/utils/polling-backoff.ts +59 -0
  445. package/src/utils/project-picker.ts +12 -0
  446. package/src/utils/queue-panel-actions.ts +70 -0
  447. package/src/utils/recent-projects.ts +156 -0
  448. package/src/utils/renderer-cleanup.ts +233 -0
  449. package/src/utils/response-ad-positions.ts +1 -0
  450. package/src/utils/run-state-storage.ts +603 -0
  451. package/src/utils/safe-json.ts +153 -0
  452. package/src/utils/sdk-event-handlers.ts +571 -0
  453. package/src/utils/send-message-helpers.ts +210 -0
  454. package/src/utils/send-message-timer.ts +110 -0
  455. package/src/utils/settings.ts +226 -0
  456. package/src/utils/skill-registry.ts +94 -0
  457. package/src/utils/spawn-agent-matcher.ts +55 -0
  458. package/src/utils/status-indicator-state.ts +107 -0
  459. package/src/utils/stream-chunk-processor.ts +57 -0
  460. package/src/utils/strings.ts +271 -0
  461. package/src/utils/subscription.ts +31 -0
  462. package/src/utils/syntax-highlighter.tsx +19 -0
  463. package/src/utils/terminal-color-detection.ts +474 -0
  464. package/src/utils/terminal-command-broker.ts +467 -0
  465. package/src/utils/terminal-enter-detection.ts +63 -0
  466. package/src/utils/terminal-images.ts +229 -0
  467. package/src/utils/terminal-io.ts +85 -0
  468. package/src/utils/terminal-protocol-controller.ts +173 -0
  469. package/src/utils/terminal-reset-sequences.ts +33 -0
  470. package/src/utils/terminal-title.ts +84 -0
  471. package/src/utils/terminal-watchdog.ts +366 -0
  472. package/src/utils/text-layout.ts +149 -0
  473. package/src/utils/theme-config.ts +141 -0
  474. package/src/utils/theme-system.ts +1206 -0
  475. package/src/utils/think-tag-parser.ts +95 -0
  476. package/src/utils/time-format.ts +21 -0
  477. package/src/utils/trace-writer.ts +162 -0
  478. package/src/utils/ui-constants.ts +69 -0
  479. package/src/utils/update-checker.ts +110 -0
  480. package/src/utils/usage-banner-state.ts +146 -0
  481. package/src/utils/validation-error-formatting.ts +86 -0
  482. package/src/utils/validation-error-helpers.ts +19 -0
  483. package/src/utils/windows-terminal-health.ts +124 -0
  484. package/src/utils/word-wrap-utils.ts +53 -0
  485. package/src/utils/write-file-atomic.ts +54 -0
  486. package/src/utils/yield-to-event-loop.ts +9 -0
  487. package/bin/anvil.cjs +0 -40
  488. package/dist/bundled.js +0 -123508
  489. package/dist/bundled.js.map +0 -828
  490. package/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
  491. package/dist/vendor/ripgrep/arm64-linux/rg +0 -0
  492. package/dist/vendor/ripgrep/x64-darwin/rg +0 -0
  493. package/dist/vendor/ripgrep/x64-linux/rg +0 -0
  494. package/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
@@ -0,0 +1,523 @@
1
+ import { updateBlocksRecursively } from './message-block-helpers'
2
+ import {
3
+ parseThinkTags,
4
+ getPartialTagLength,
5
+ THINK_OPEN_TAG,
6
+ THINK_CLOSE_TAG,
7
+ } from './think-tag-parser'
8
+
9
+ import type {
10
+ ContentBlock,
11
+ ToolContentBlock,
12
+ TextContentBlock,
13
+ } from '../types/chat'
14
+
15
+ let thinkingIdCounter = 0
16
+ const generateThinkingId = (): string => {
17
+ thinkingIdCounter++
18
+ return `thinking-${thinkingIdCounter}`
19
+ }
20
+
21
+ type AgentTextUpdate =
22
+ | { type: 'text'; mode: 'append'; content: string; textType: 'text' | 'reasoning' }
23
+ | { type: 'text'; mode: 'replace'; content: string }
24
+
25
+ const updateAgentText = (
26
+ blocks: ContentBlock[],
27
+ agentId: string,
28
+ update: AgentTextUpdate,
29
+ ) => {
30
+ return updateBlocksRecursively(blocks, agentId, (block) => {
31
+ if (block.type !== 'agent') {
32
+ return block
33
+ }
34
+
35
+ const agentBlocks = block.blocks ? [...block.blocks] : []
36
+ const text = update.content ?? ''
37
+
38
+ if (update.mode === 'replace') {
39
+ const updatedBlocks = [...agentBlocks]
40
+ let replaced = false
41
+
42
+ for (let i = updatedBlocks.length - 1; i >= 0; i--) {
43
+ const entry = updatedBlocks[i]
44
+ if (entry.type === 'text') {
45
+ replaced = true
46
+ if (entry.content === text && block.content === text) {
47
+ return block
48
+ }
49
+ updatedBlocks[i] = { ...entry, content: text }
50
+ break
51
+ }
52
+ }
53
+
54
+ if (!replaced) {
55
+ updatedBlocks.push({ type: 'text', content: text })
56
+ }
57
+
58
+ return {
59
+ ...block,
60
+ content: text,
61
+ blocks: updatedBlocks,
62
+ }
63
+ }
64
+
65
+ if (!text) {
66
+ return block
67
+ }
68
+
69
+ // Handle native reasoning chunks for agent blocks
70
+ if (update.textType === 'reasoning') {
71
+ const updatedAgentBlocks = appendNativeReasoningToBlocks(agentBlocks, text)
72
+ const updatedContent = (block.content ?? '') + text
73
+ return {
74
+ ...block,
75
+ content: updatedContent,
76
+ blocks: updatedAgentBlocks,
77
+ }
78
+ }
79
+
80
+ // For regular text: first close any open native reasoning block, then use think tag parsing
81
+ const blocksWithClosedReasoning = closeNativeReasoningBlock(agentBlocks)
82
+ const updatedAgentBlocks = appendTextWithThinkParsingToBlocks(
83
+ blocksWithClosedReasoning,
84
+ text,
85
+ )
86
+ const updatedContent = (block.content ?? '') + text
87
+ return {
88
+ ...block,
89
+ content: updatedContent,
90
+ blocks: updatedAgentBlocks,
91
+ }
92
+ })
93
+ }
94
+
95
+ /**
96
+ * Check if a text block represents an open (unclosed) thinking block.
97
+ */
98
+ const isOpenThinkingBlock = (block: ContentBlock | undefined): boolean => {
99
+ if (!block || block.type !== 'text') {
100
+ return false
101
+ }
102
+ return block.textType === 'reasoning' && block.thinkingOpen === true
103
+ }
104
+
105
+ /**
106
+ * Creates a new reasoning (thinking) text block.
107
+ */
108
+ const createReasoningBlock = (
109
+ content: string,
110
+ thinkingOpen: boolean,
111
+ thinkingId: string,
112
+ ): TextContentBlock => ({
113
+ type: 'text',
114
+ content,
115
+ textType: 'reasoning',
116
+ thinkingCollapseState: 'preview',
117
+ thinkingOpen,
118
+ thinkingId,
119
+ })
120
+
121
+ /**
122
+ * Creates a new regular text block.
123
+ */
124
+ const createTextBlock = (content: string): TextContentBlock => ({
125
+ type: 'text',
126
+ content,
127
+ textType: 'text',
128
+ })
129
+
130
+ /**
131
+ * Shared logic for appending text with think tag parsing.
132
+ * Used by both root stream and agent blocks.
133
+ */
134
+ const appendTextWithThinkParsingToBlocks = (
135
+ blocks: ContentBlock[],
136
+ text: string,
137
+ ): ContentBlock[] => {
138
+ if (!text) {
139
+ return blocks
140
+ }
141
+
142
+ const nextBlocks = [...blocks]
143
+ const lastBlock = nextBlocks[nextBlocks.length - 1]
144
+ const wasInsideThinking = isOpenThinkingBlock(lastBlock)
145
+
146
+ let textToParse = text
147
+ let lastBlockContent = ''
148
+
149
+ if (wasInsideThinking && lastBlock?.type === 'text') {
150
+ lastBlockContent = lastBlock.content
151
+
152
+ const partialLen = getPartialTagLength(lastBlockContent)
153
+ if (partialLen > 0) {
154
+ const potentialTag = lastBlockContent.slice(-partialLen) + text
155
+ if (potentialTag.startsWith(THINK_CLOSE_TAG)) {
156
+ const newLastContent = lastBlockContent.slice(0, -partialLen)
157
+ textToParse = lastBlockContent.slice(-partialLen) + text
158
+
159
+ if (newLastContent) {
160
+ nextBlocks[nextBlocks.length - 1] = {
161
+ ...lastBlock,
162
+ content: newLastContent,
163
+ }
164
+ } else {
165
+ nextBlocks.pop()
166
+ }
167
+ }
168
+ }
169
+ } else if (
170
+ !wasInsideThinking &&
171
+ lastBlock?.type === 'text' &&
172
+ lastBlock.textType === 'text'
173
+ ) {
174
+ lastBlockContent = lastBlock.content
175
+ const partialLen = getPartialTagLength(lastBlockContent)
176
+ if (partialLen > 0) {
177
+ const potentialTag = lastBlockContent.slice(-partialLen) + text
178
+ if (potentialTag.startsWith(THINK_OPEN_TAG)) {
179
+ const newLastContent = lastBlockContent.slice(0, -partialLen)
180
+ textToParse = lastBlockContent.slice(-partialLen) + text
181
+
182
+ if (newLastContent) {
183
+ nextBlocks[nextBlocks.length - 1] = {
184
+ ...lastBlock,
185
+ content: newLastContent,
186
+ }
187
+ } else {
188
+ nextBlocks.pop()
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ const currentLastBlock = nextBlocks[nextBlocks.length - 1]
195
+ const insideThinking = isOpenThinkingBlock(currentLastBlock)
196
+
197
+ if (insideThinking && !textToParse.includes('<')) {
198
+ if (currentLastBlock?.type === 'text') {
199
+ nextBlocks[nextBlocks.length - 1] = {
200
+ ...currentLastBlock,
201
+ content: currentLastBlock.content + textToParse,
202
+ }
203
+ return nextBlocks
204
+ }
205
+ }
206
+
207
+ if (!insideThinking && !textToParse.includes('<')) {
208
+ if (
209
+ currentLastBlock?.type === 'text' &&
210
+ currentLastBlock.textType === 'text'
211
+ ) {
212
+ nextBlocks[nextBlocks.length - 1] = {
213
+ ...currentLastBlock,
214
+ content: currentLastBlock.content + textToParse,
215
+ }
216
+ return nextBlocks
217
+ }
218
+ return [...nextBlocks, createTextBlock(textToParse)]
219
+ }
220
+
221
+ const fullText = insideThinking ? THINK_OPEN_TAG + textToParse : textToParse
222
+
223
+ const segments = parseThinkTags(fullText)
224
+
225
+ let segmentStartIdx = 0
226
+ if (
227
+ insideThinking &&
228
+ segments.length > 0 &&
229
+ segments[0].type === 'thinking'
230
+ ) {
231
+ const firstSegment = segments[0]
232
+ if (currentLastBlock?.type === 'text') {
233
+ const hasMoreSegments = segments.length > 1
234
+ const thinkingOpen =
235
+ !hasMoreSegments && !textToParse.includes(THINK_CLOSE_TAG)
236
+
237
+ nextBlocks[nextBlocks.length - 1] = {
238
+ ...currentLastBlock,
239
+ content: currentLastBlock.content + firstSegment.content,
240
+ thinkingOpen,
241
+ }
242
+ }
243
+ segmentStartIdx = 1
244
+ } else if (insideThinking && textToParse.includes(THINK_CLOSE_TAG)) {
245
+ // Handle case where we're inside thinking and receive </think> with no content
246
+ // (e.g., just "</think>" or "</think>text"). In this case parseThinkTags returns
247
+ // empty or starts with text, but we still need to close the thinking block.
248
+ if (currentLastBlock?.type === 'text') {
249
+ nextBlocks[nextBlocks.length - 1] = {
250
+ ...currentLastBlock,
251
+ thinkingOpen: false,
252
+ }
253
+ }
254
+ }
255
+
256
+ for (let i = segmentStartIdx; i < segments.length; i++) {
257
+ const segment = segments[i]
258
+ const isLastSegment = i === segments.length - 1
259
+
260
+ if (segment.type === 'thinking') {
261
+ const thinkingOpen =
262
+ isLastSegment && !textToParse.endsWith(THINK_CLOSE_TAG)
263
+ if (thinkingOpen) {
264
+ nextBlocks.push(createReasoningBlock(segment.content, thinkingOpen, generateThinkingId()))
265
+ }
266
+ } else {
267
+ const prevBlock = nextBlocks[nextBlocks.length - 1]
268
+ if (
269
+ prevBlock?.type === 'text' &&
270
+ prevBlock.textType === 'text' &&
271
+ !prevBlock.thinkingOpen
272
+ ) {
273
+ nextBlocks[nextBlocks.length - 1] = {
274
+ ...prevBlock,
275
+ content: prevBlock.content + segment.content,
276
+ }
277
+ } else {
278
+ nextBlocks.push(createTextBlock(segment.content))
279
+ }
280
+ }
281
+ }
282
+
283
+ return nextBlocks
284
+ }
285
+
286
+ /**
287
+ * Appends native reasoning content to blocks array (for agent blocks).
288
+ * Similar to how appendTextToRootStream handles reasoning for root.
289
+ */
290
+ const appendNativeReasoningToBlocks = (
291
+ blocks: ContentBlock[],
292
+ text: string,
293
+ ): ContentBlock[] => {
294
+ if (!text) {
295
+ return blocks
296
+ }
297
+
298
+ const nextBlocks = [...blocks]
299
+ const lastBlock = nextBlocks[nextBlocks.length - 1]
300
+
301
+ // If last block is already an open native reasoning block, append to it
302
+ // Only append if it's a native reasoning block (thinkingOpen === undefined),
303
+ // not a closed one or a <think> tag block
304
+ if (isNativeReasoningBlock(lastBlock) && lastBlock.type === 'text') {
305
+ const updatedBlock: ContentBlock = {
306
+ ...lastBlock,
307
+ content: lastBlock.content + text,
308
+ }
309
+ nextBlocks[nextBlocks.length - 1] = updatedBlock
310
+ return nextBlocks
311
+ }
312
+
313
+ // Create a new native reasoning block
314
+ const newBlock: ContentBlock = {
315
+ type: 'text',
316
+ content: text,
317
+ textType: 'reasoning',
318
+ thinkingCollapseState: 'preview',
319
+ thinkingId: generateThinkingId(),
320
+ }
321
+
322
+ return [...nextBlocks, newBlock]
323
+ }
324
+
325
+ /**
326
+ * Checks if a block is a native reasoning block (not from <think> tags).
327
+ * Native reasoning blocks have textType === 'reasoning' but thinkingOpen === undefined.
328
+ */
329
+ export const isNativeReasoningBlock = (block: ContentBlock | undefined): boolean => {
330
+ if (!block || block.type !== 'text') {
331
+ return false
332
+ }
333
+ return block.textType === 'reasoning' && block.thinkingOpen === undefined
334
+ }
335
+
336
+ /**
337
+ * Closes native reasoning blocks within a specific agent's blocks.
338
+ * Used when a tool call happens for a subagent.
339
+ */
340
+ export const closeNativeReasoningInAgent = (
341
+ blocks: ContentBlock[],
342
+ agentId: string,
343
+ ): ContentBlock[] => {
344
+ return updateBlocksRecursively(blocks, agentId, (block) => {
345
+ if (block.type !== 'agent') {
346
+ return block
347
+ }
348
+ const closedBlocks = block.blocks ? closeNativeReasoningBlock(block.blocks) : undefined
349
+ if (closedBlocks && closedBlocks !== block.blocks) {
350
+ return { ...block, blocks: closedBlocks }
351
+ }
352
+ return block
353
+ })
354
+ }
355
+
356
+ /**
357
+ * Marks the last native reasoning block as complete by setting thinkingOpen: false.
358
+ * This triggers the UI to collapse the thinking block.
359
+ *
360
+ * Note: We search backwards through all blocks because agent/tool blocks may have
361
+ * been added after the reasoning block but before text output starts.
362
+ */
363
+ export const closeNativeReasoningBlock = (
364
+ blocks: ContentBlock[],
365
+ ): ContentBlock[] => {
366
+ // Find the last native reasoning block (not just the last block)
367
+ let lastReasoningIndex = -1
368
+ for (let i = blocks.length - 1; i >= 0; i--) {
369
+ if (isNativeReasoningBlock(blocks[i])) {
370
+ lastReasoningIndex = i
371
+ break
372
+ }
373
+ }
374
+
375
+ if (lastReasoningIndex === -1) {
376
+ return blocks
377
+ }
378
+
379
+ const reasoningBlock = blocks[lastReasoningIndex]
380
+ if (reasoningBlock.type !== 'text') {
381
+ return blocks
382
+ }
383
+
384
+ const nextBlocks = [...blocks]
385
+ nextBlocks[lastReasoningIndex] = {
386
+ ...reasoningBlock,
387
+ thinkingOpen: false,
388
+ }
389
+ return nextBlocks
390
+ }
391
+
392
+ export const appendTextToRootStream = (
393
+ blocks: ContentBlock[],
394
+ delta: { type: 'text' | 'reasoning'; text: string },
395
+ ) => {
396
+ if (!delta.text) {
397
+ return blocks
398
+ }
399
+
400
+ // For reasoning type (from native reasoning_chunk events), use original behavior
401
+ if (delta.type === 'reasoning') {
402
+ const nextBlocks = [...blocks]
403
+ const lastBlock = nextBlocks[nextBlocks.length - 1]
404
+
405
+ if (
406
+ lastBlock &&
407
+ lastBlock.type === 'text' &&
408
+ lastBlock.textType === 'reasoning'
409
+ ) {
410
+ const updatedBlock: ContentBlock = {
411
+ ...lastBlock,
412
+ content: lastBlock.content + delta.text,
413
+ }
414
+ nextBlocks[nextBlocks.length - 1] = updatedBlock
415
+ return nextBlocks
416
+ }
417
+
418
+ const newBlock: ContentBlock = {
419
+ type: 'text',
420
+ content: delta.text,
421
+ textType: 'reasoning',
422
+ thinkingCollapseState: 'preview',
423
+ thinkingId: generateThinkingId(),
424
+ }
425
+
426
+ return [...nextBlocks, newBlock]
427
+ }
428
+
429
+ // For text type: first close any open native reasoning block, then parse for <think> tags
430
+ const blocksWithClosedReasoning = closeNativeReasoningBlock(blocks)
431
+ return appendTextWithThinkParsingToBlocks(blocksWithClosedReasoning, delta.text)
432
+ }
433
+
434
+ export const appendTextToAgentBlock = (
435
+ blocks: ContentBlock[],
436
+ agentId: string,
437
+ text: string,
438
+ textType: 'text' | 'reasoning' = 'text',
439
+ ) =>
440
+ updateAgentText(blocks, agentId, {
441
+ type: 'text',
442
+ mode: 'append',
443
+ content: text,
444
+ textType,
445
+ })
446
+
447
+ export const replaceTextInAgentBlock = (
448
+ blocks: ContentBlock[],
449
+ agentId: string,
450
+ text: string,
451
+ ) =>
452
+ updateAgentText(blocks, agentId, {
453
+ type: 'text',
454
+ mode: 'replace',
455
+ content: text,
456
+ })
457
+
458
+ export const appendToolToAgentBlock = (
459
+ blocks: ContentBlock[],
460
+ agentId: string,
461
+ toolBlock: ToolContentBlock,
462
+ ) =>
463
+ updateBlocksRecursively(blocks, agentId, (block) => {
464
+ if (block.type !== 'agent') {
465
+ return block
466
+ }
467
+ // Close any open native reasoning blocks before adding the tool
468
+ const agentBlocks = block.blocks ? closeNativeReasoningBlock([...block.blocks]) : []
469
+ return { ...block, blocks: [...agentBlocks, toolBlock] }
470
+ })
471
+
472
+ export const markAgentComplete = (blocks: ContentBlock[], agentId: string) =>
473
+ updateBlocksRecursively(blocks, agentId, (block) => {
474
+ if (block.type !== 'agent') {
475
+ return block
476
+ }
477
+ // Close any open native reasoning blocks when the agent completes
478
+ const closedBlocks = block.blocks ? closeNativeReasoningBlock(block.blocks) : undefined
479
+ return {
480
+ ...block,
481
+ status: 'complete' as const,
482
+ ...(closedBlocks && { blocks: closedBlocks }),
483
+ }
484
+ })
485
+
486
+ /**
487
+ * Recursively marks all agent blocks with status 'running' as 'cancelled'.
488
+ * Used when the user interrupts a response to indicate subagents were stopped.
489
+ * Also closes any open native reasoning blocks so they don't appear "streaming".
490
+ */
491
+ export const markRunningAgentsAsCancelled = (
492
+ blocks: ContentBlock[],
493
+ ): ContentBlock[] => {
494
+ return blocks.map((block) => {
495
+ if (block.type !== 'agent') {
496
+ return block
497
+ }
498
+
499
+ // First recursively process nested agents, then close any reasoning blocks
500
+ let updatedBlocks = block.blocks
501
+ ? markRunningAgentsAsCancelled(block.blocks)
502
+ : undefined
503
+
504
+ // Close any open native reasoning blocks in this agent
505
+ if (updatedBlocks) {
506
+ updatedBlocks = closeNativeReasoningBlock(updatedBlocks)
507
+ }
508
+
509
+ if (block.status === 'running') {
510
+ return {
511
+ ...block,
512
+ status: 'cancelled' as const,
513
+ ...(updatedBlocks && { blocks: updatedBlocks }),
514
+ }
515
+ }
516
+
517
+ if (updatedBlocks && updatedBlocks !== block.blocks) {
518
+ return { ...block, blocks: updatedBlocks }
519
+ }
520
+
521
+ return block
522
+ })
523
+ }