@agent-native/core 0.47.1 → 0.48.1

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 (808) hide show
  1. package/bin/agent-native.js +41 -0
  2. package/dist/a2a/handlers.js +2 -2
  3. package/dist/a2a/handlers.js.map +1 -1
  4. package/dist/a2a/server.js +2 -2
  5. package/dist/a2a/server.js.map +1 -1
  6. package/dist/action.d.ts +43 -2
  7. package/dist/action.d.ts.map +1 -1
  8. package/dist/action.js.map +1 -1
  9. package/dist/agent/context-xray/actions/context-evict.d.ts +7 -1
  10. package/dist/agent/context-xray/actions/context-evict.d.ts.map +1 -1
  11. package/dist/agent/context-xray/actions/context-manifest-get.d.ts +4 -1
  12. package/dist/agent/context-xray/actions/context-manifest-get.d.ts.map +1 -1
  13. package/dist/agent/context-xray/actions/context-pin.d.ts +7 -1
  14. package/dist/agent/context-xray/actions/context-pin.d.ts.map +1 -1
  15. package/dist/agent/context-xray/actions/context-report.d.ts +12 -1
  16. package/dist/agent/context-xray/actions/context-report.d.ts.map +1 -1
  17. package/dist/agent/context-xray/actions/context-restore.d.ts +7 -1
  18. package/dist/agent/context-xray/actions/context-restore.d.ts.map +1 -1
  19. package/dist/agent/context-xray/apply-directives.d.ts.map +1 -1
  20. package/dist/agent/context-xray/apply-directives.js.map +1 -1
  21. package/dist/agent/context-xray/schema.d.ts +10 -10
  22. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  23. package/dist/agent/engine/ai-sdk-engine.js +26 -3
  24. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  25. package/dist/agent/engine/anthropic-engine.d.ts +1 -1
  26. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  27. package/dist/agent/engine/builder-engine.d.ts +1 -1
  28. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  29. package/dist/agent/engine/builder-engine.js +47 -8
  30. package/dist/agent/engine/builder-engine.js.map +1 -1
  31. package/dist/agent/engine/builtin.js +1 -1
  32. package/dist/agent/engine/builtin.js.map +1 -1
  33. package/dist/agent/engine/output-tokens.d.ts +1 -1
  34. package/dist/agent/engine/output-tokens.d.ts.map +1 -1
  35. package/dist/agent/engine/output-tokens.js +6 -2
  36. package/dist/agent/engine/output-tokens.js.map +1 -1
  37. package/dist/agent/engine/registry.d.ts.map +1 -1
  38. package/dist/agent/engine/registry.js +7 -4
  39. package/dist/agent/engine/registry.js.map +1 -1
  40. package/dist/agent/engine/types.d.ts +19 -0
  41. package/dist/agent/engine/types.d.ts.map +1 -1
  42. package/dist/agent/engine/types.js +6 -0
  43. package/dist/agent/engine/types.js.map +1 -1
  44. package/dist/agent/model-config.d.ts +22 -14
  45. package/dist/agent/model-config.d.ts.map +1 -1
  46. package/dist/agent/model-config.js +113 -8
  47. package/dist/agent/model-config.js.map +1 -1
  48. package/dist/agent/production-agent.d.ts +19 -1
  49. package/dist/agent/production-agent.d.ts.map +1 -1
  50. package/dist/agent/production-agent.js +253 -39
  51. package/dist/agent/production-agent.js.map +1 -1
  52. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  53. package/dist/agent/run-loop-with-resume.js +10 -0
  54. package/dist/agent/run-loop-with-resume.js.map +1 -1
  55. package/dist/agent/run-manager.d.ts +1 -0
  56. package/dist/agent/run-manager.d.ts.map +1 -1
  57. package/dist/agent/run-manager.js +36 -9
  58. package/dist/agent/run-manager.js.map +1 -1
  59. package/dist/agent/run-store.d.ts +47 -4
  60. package/dist/agent/run-store.d.ts.map +1 -1
  61. package/dist/agent/run-store.js +154 -4
  62. package/dist/agent/run-store.js.map +1 -1
  63. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  64. package/dist/agent/thread-data-builder.js +57 -2
  65. package/dist/agent/thread-data-builder.js.map +1 -1
  66. package/dist/agent/types.d.ts +3 -0
  67. package/dist/agent/types.d.ts.map +1 -1
  68. package/dist/agent/types.js.map +1 -1
  69. package/dist/agent-web/generator.d.ts +3 -3
  70. package/dist/appearance/actions/change-appearance.d.ts +6 -1
  71. package/dist/appearance/actions/change-appearance.d.ts.map +1 -1
  72. package/dist/application-state/handlers.d.ts +2 -2
  73. package/dist/application-state/handlers.d.ts.map +1 -1
  74. package/dist/application-state/store.d.ts.map +1 -1
  75. package/dist/application-state/store.js +17 -0
  76. package/dist/application-state/store.js.map +1 -1
  77. package/dist/catalog.json +2 -1
  78. package/dist/cli/code-agent-commands.d.ts.map +1 -1
  79. package/dist/cli/code-agent-commands.js +2 -0
  80. package/dist/cli/code-agent-commands.js.map +1 -1
  81. package/dist/cli/code-agent-connector.js +7 -13
  82. package/dist/cli/code-agent-connector.js.map +1 -1
  83. package/dist/cli/code-agent-executor.d.ts +54 -2
  84. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  85. package/dist/cli/code-agent-executor.js +504 -48
  86. package/dist/cli/code-agent-executor.js.map +1 -1
  87. package/dist/cli/code-agent-runs.d.ts +13 -0
  88. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  89. package/dist/cli/code-agent-runs.js +36 -0
  90. package/dist/cli/code-agent-runs.js.map +1 -1
  91. package/dist/cli/code.js +59 -5
  92. package/dist/cli/code.js.map +1 -1
  93. package/dist/cli/connect.js +141 -3
  94. package/dist/cli/connect.js.map +1 -1
  95. package/dist/cli/index.js +0 -0
  96. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  97. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  98. package/dist/cli/recap.js +476 -46
  99. package/dist/cli/recap.js.map +1 -1
  100. package/dist/cli/skills.js +298 -179
  101. package/dist/cli/skills.js.map +1 -1
  102. package/dist/client/AgentPanel.d.ts.map +1 -1
  103. package/dist/client/AgentPanel.js +29 -2
  104. package/dist/client/AgentPanel.js.map +1 -1
  105. package/dist/client/AgentTaskCard.d.ts.map +1 -1
  106. package/dist/client/AgentTaskCard.js +17 -2
  107. package/dist/client/AgentTaskCard.js.map +1 -1
  108. package/dist/client/AssistantChat.d.ts +1 -1
  109. package/dist/client/AssistantChat.d.ts.map +1 -1
  110. package/dist/client/AssistantChat.js +310 -1732
  111. package/dist/client/AssistantChat.js.map +1 -1
  112. package/dist/client/CommandMenu.d.ts +1 -1
  113. package/dist/client/CommandMenu.d.ts.map +1 -1
  114. package/dist/client/CommandMenu.js +1 -1
  115. package/dist/client/CommandMenu.js.map +1 -1
  116. package/dist/client/HighlightedCodeBlock.d.ts +40 -0
  117. package/dist/client/HighlightedCodeBlock.d.ts.map +1 -0
  118. package/dist/client/HighlightedCodeBlock.js +110 -0
  119. package/dist/client/HighlightedCodeBlock.js.map +1 -0
  120. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  121. package/dist/client/MultiTabAssistantChat.js +8 -1
  122. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  123. package/dist/client/PoweredByBadge.d.ts +2 -2
  124. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  125. package/dist/client/RunStuckBanner.d.ts +1 -1
  126. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  127. package/dist/client/StarfieldBackground.d.ts.map +1 -1
  128. package/dist/client/StarfieldBackground.js +10 -5
  129. package/dist/client/StarfieldBackground.js.map +1 -1
  130. package/dist/client/Turnstile.d.ts +1 -1
  131. package/dist/client/Turnstile.d.ts.map +1 -1
  132. package/dist/client/agent-chat-adapter.d.ts +3 -2
  133. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  134. package/dist/client/agent-chat-adapter.js +13 -9
  135. package/dist/client/agent-chat-adapter.js.map +1 -1
  136. package/dist/client/app-providers.d.ts +99 -0
  137. package/dist/client/app-providers.d.ts.map +1 -0
  138. package/dist/client/app-providers.js +19 -0
  139. package/dist/client/app-providers.js.map +1 -0
  140. package/dist/client/assistant-ui-recovery.d.ts +1 -1
  141. package/dist/client/auth-redirect-url.d.ts +1 -1
  142. package/dist/client/auth-redirect-url.d.ts.map +1 -1
  143. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +0 -19
  144. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  145. package/dist/client/blocks/library/AnnotatedCodeBlock.js +141 -55
  146. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  147. package/dist/client/blocks/library/DiffBlock.js +1 -1
  148. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  149. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  150. package/dist/client/blocks/library/FileTreeBlock.js +1 -1
  151. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  152. package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
  153. package/dist/client/blocks/library/HighlightedCode.js +5 -3
  154. package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
  155. package/dist/client/blocks/library/annotation-rail.d.ts +5 -4
  156. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  157. package/dist/client/blocks/library/annotation-rail.js +22 -3
  158. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  159. package/dist/client/blocks/library/diagram.js +1 -1
  160. package/dist/client/blocks/library/diagram.js.map +1 -1
  161. package/dist/client/blocks/library/diff.config.d.ts +3 -2
  162. package/dist/client/blocks/library/diff.config.d.ts.map +1 -1
  163. package/dist/client/blocks/library/diff.config.js +4 -3
  164. package/dist/client/blocks/library/diff.config.js.map +1 -1
  165. package/dist/client/blocks/library/question-form.d.ts.map +1 -1
  166. package/dist/client/blocks/library/question-form.js +2 -1
  167. package/dist/client/blocks/library/question-form.js.map +1 -1
  168. package/dist/client/blocks/library/wireframe-kit.d.ts +1 -1
  169. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  170. package/dist/client/blocks/library/wireframe.js +1 -1
  171. package/dist/client/blocks/library/wireframe.js.map +1 -1
  172. package/dist/client/chat/attachment-adapters.d.ts +58 -0
  173. package/dist/client/chat/attachment-adapters.d.ts.map +1 -0
  174. package/dist/client/chat/attachment-adapters.js +331 -0
  175. package/dist/client/chat/attachment-adapters.js.map +1 -0
  176. package/dist/client/chat/index.d.ts +13 -0
  177. package/dist/client/chat/index.d.ts.map +1 -0
  178. package/dist/client/chat/index.js +13 -0
  179. package/dist/client/chat/index.js.map +1 -0
  180. package/dist/client/chat/markdown-renderer.d.ts +49 -0
  181. package/dist/client/chat/markdown-renderer.d.ts.map +1 -0
  182. package/dist/client/chat/markdown-renderer.js +391 -0
  183. package/dist/client/chat/markdown-renderer.js.map +1 -0
  184. package/dist/client/chat/message-components.d.ts +35 -0
  185. package/dist/client/chat/message-components.d.ts.map +1 -0
  186. package/dist/client/chat/message-components.js +452 -0
  187. package/dist/client/chat/message-components.js.map +1 -0
  188. package/dist/client/chat/repo-helpers.d.ts +41 -0
  189. package/dist/client/chat/repo-helpers.d.ts.map +1 -0
  190. package/dist/client/chat/repo-helpers.js +61 -0
  191. package/dist/client/chat/repo-helpers.js.map +1 -0
  192. package/dist/client/chat/run-recovery.d.ts +41 -0
  193. package/dist/client/chat/run-recovery.d.ts.map +1 -0
  194. package/dist/client/chat/run-recovery.js +348 -0
  195. package/dist/client/chat/run-recovery.js.map +1 -0
  196. package/dist/client/chat/tool-call-display.d.ts +34 -0
  197. package/dist/client/chat/tool-call-display.d.ts.map +1 -0
  198. package/dist/client/chat/tool-call-display.js +284 -0
  199. package/dist/client/chat/tool-call-display.js.map +1 -0
  200. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  201. package/dist/client/code-agent-chat-adapter.js +20 -0
  202. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  203. package/dist/client/collab/index.d.ts +10 -0
  204. package/dist/client/collab/index.d.ts.map +1 -0
  205. package/dist/client/collab/index.js +10 -0
  206. package/dist/client/collab/index.js.map +1 -0
  207. package/dist/client/components/AgentPresenceChip.d.ts +1 -1
  208. package/dist/client/components/AgentPresenceChip.d.ts.map +1 -1
  209. package/dist/client/components/ApiKeySettings.d.ts +1 -1
  210. package/dist/client/components/ApiKeySettings.d.ts.map +1 -1
  211. package/dist/client/components/CodeAgentIndicator.d.ts +1 -1
  212. package/dist/client/components/CodeAgentIndicator.d.ts.map +1 -1
  213. package/dist/client/components/CodeRequiredDialog.d.ts +1 -1
  214. package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
  215. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  216. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  217. package/dist/client/components/PresenceBar.d.ts +1 -1
  218. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  219. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  220. package/dist/client/composer/PromptComposer.js +6 -26
  221. package/dist/client/composer/PromptComposer.js.map +1 -1
  222. package/dist/client/composer/TiptapComposer.d.ts +8 -2
  223. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  224. package/dist/client/composer/TiptapComposer.js +21 -9
  225. package/dist/client/composer/TiptapComposer.js.map +1 -1
  226. package/dist/client/composer/VoiceButton.d.ts +2 -2
  227. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  228. package/dist/client/composer/index.d.ts +1 -1
  229. package/dist/client/composer/index.d.ts.map +1 -1
  230. package/dist/client/composer/index.js +1 -1
  231. package/dist/client/composer/index.js.map +1 -1
  232. package/dist/client/composer/use-skills.d.ts +1 -1
  233. package/dist/client/context-xray/ContextMeter.d.ts +1 -1
  234. package/dist/client/context-xray/ContextMeter.d.ts.map +1 -1
  235. package/dist/client/context-xray/ContextMeter.js +3 -3
  236. package/dist/client/context-xray/ContextMeter.js.map +1 -1
  237. package/dist/client/context-xray/ContextXRayPanel.d.ts.map +1 -1
  238. package/dist/client/context-xray/ContextXRayPanel.js +4 -3
  239. package/dist/client/context-xray/ContextXRayPanel.js.map +1 -1
  240. package/dist/client/context-xray/format.d.ts +11 -0
  241. package/dist/client/context-xray/format.d.ts.map +1 -1
  242. package/dist/client/context-xray/format.js +16 -0
  243. package/dist/client/context-xray/format.js.map +1 -1
  244. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  245. package/dist/client/conversation/AgentConversation.js +8 -53
  246. package/dist/client/conversation/AgentConversation.js.map +1 -1
  247. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -1
  248. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
  249. package/dist/client/conversation/use-near-bottom-autoscroll.js +14 -1
  250. package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
  251. package/dist/client/create-query-client.d.ts +28 -0
  252. package/dist/client/create-query-client.d.ts.map +1 -0
  253. package/dist/client/create-query-client.js +78 -0
  254. package/dist/client/create-query-client.js.map +1 -0
  255. package/dist/client/db-admin/DevDatabaseLink.d.ts +1 -1
  256. package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -1
  257. package/dist/client/db-admin/RowSidePanel.d.ts +1 -1
  258. package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -1
  259. package/dist/client/db-admin/RowSidePanel.js +2 -2
  260. package/dist/client/db-admin/RowSidePanel.js.map +1 -1
  261. package/dist/client/db-admin/TableEditor.d.ts +1 -1
  262. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  263. package/dist/client/db-admin/TableEditor.js +1 -1
  264. package/dist/client/db-admin/TableEditor.js.map +1 -1
  265. package/dist/client/db-admin/cell-format.d.ts +1 -1
  266. package/dist/client/db-admin/cell-format.d.ts.map +1 -1
  267. package/dist/client/dev-overlay/DevOverlay.d.ts +1 -1
  268. package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
  269. package/dist/client/editor/index.d.ts +2 -0
  270. package/dist/client/editor/index.d.ts.map +1 -0
  271. package/dist/client/editor/index.js +2 -0
  272. package/dist/client/editor/index.js.map +1 -0
  273. package/dist/client/error-format.d.ts.map +1 -1
  274. package/dist/client/error-format.js +4 -0
  275. package/dist/client/error-format.js.map +1 -1
  276. package/dist/client/extensions/AgentNativeExtensionFrame.d.ts +1 -1
  277. package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
  278. package/dist/client/extensions/EmbeddedExtension.d.ts +1 -1
  279. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  280. package/dist/client/extensions/ExtensionSlot.d.ts +1 -1
  281. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
  282. package/dist/client/extensions/ExtensionViewerPage.d.ts +1 -1
  283. package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -1
  284. package/dist/client/guided-questions.d.ts +6 -6
  285. package/dist/client/host-bridge.d.ts.map +1 -1
  286. package/dist/client/host-bridge.js +2 -0
  287. package/dist/client/host-bridge.js.map +1 -1
  288. package/dist/client/index.d.ts +7 -6
  289. package/dist/client/index.d.ts.map +1 -1
  290. package/dist/client/index.js +5 -3
  291. package/dist/client/index.js.map +1 -1
  292. package/dist/client/onboarding/OnboardingBanner.d.ts +1 -1
  293. package/dist/client/onboarding/OnboardingBanner.d.ts.map +1 -1
  294. package/dist/client/onboarding/OnboardingPanel.d.ts +1 -1
  295. package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
  296. package/dist/client/onboarding/SetupButton.d.ts +1 -1
  297. package/dist/client/onboarding/SetupButton.d.ts.map +1 -1
  298. package/dist/client/org/InvitationBanner.d.ts +1 -1
  299. package/dist/client/org/InvitationBanner.d.ts.map +1 -1
  300. package/dist/client/org/OrgSwitcher.d.ts +1 -1
  301. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  302. package/dist/client/org/RequireActiveOrg.d.ts +1 -1
  303. package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
  304. package/dist/client/org/hooks.d.ts +3 -3
  305. package/dist/client/org/hooks.d.ts.map +1 -1
  306. package/dist/client/progress/RunsTray.d.ts +2 -2
  307. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  308. package/dist/client/progress/RunsTray.js +34 -9
  309. package/dist/client/progress/RunsTray.js.map +1 -1
  310. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  311. package/dist/client/resources/ResourceEditor.js +1 -1
  312. package/dist/client/resources/ResourceEditor.js.map +1 -1
  313. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  314. package/dist/client/resources/ResourcesPanel.js +2 -0
  315. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  316. package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts +1 -1
  317. package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +1 -1
  318. package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts.map +1 -1
  319. package/dist/client/rich-markdown-editor/CodeBlockNode.js +2 -1
  320. package/dist/client/rich-markdown-editor/CodeBlockNode.js.map +1 -1
  321. package/dist/client/rich-markdown-editor/ImageExtension.d.ts.map +1 -1
  322. package/dist/client/rich-markdown-editor/ImageExtension.js +2 -1
  323. package/dist/client/rich-markdown-editor/ImageExtension.js.map +1 -1
  324. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +1 -1
  325. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
  326. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +1 -1
  327. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  328. package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts +1 -1
  329. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  330. package/dist/client/rich-markdown-editor/SharedRichEditor.js +2 -3
  331. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  332. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +1 -1
  333. package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +1 -1
  334. package/dist/client/route-state.d.ts +12 -2
  335. package/dist/client/route-state.d.ts.map +1 -1
  336. package/dist/client/route-state.js +1 -1
  337. package/dist/client/route-state.js.map +1 -1
  338. package/dist/client/route-warmup.d.ts +1 -1
  339. package/dist/client/route-warmup.d.ts.map +1 -1
  340. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  341. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  342. package/dist/client/settings/useBuilderStatus.d.ts +2 -2
  343. package/dist/client/sharing/ShareDialog.d.ts +1 -1
  344. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  345. package/dist/client/sse-event-processor.d.ts +8 -0
  346. package/dist/client/sse-event-processor.d.ts.map +1 -1
  347. package/dist/client/sse-event-processor.js +33 -10
  348. package/dist/client/sse-event-processor.js.map +1 -1
  349. package/dist/client/terminal/AgentTerminal.d.ts +1 -1
  350. package/dist/client/terminal/AgentTerminal.d.ts.map +1 -1
  351. package/dist/client/terminal/AgentTerminal.js +4 -2
  352. package/dist/client/terminal/AgentTerminal.js.map +1 -1
  353. package/dist/client/tool-cells/BashCell.d.ts +25 -0
  354. package/dist/client/tool-cells/BashCell.d.ts.map +1 -0
  355. package/dist/client/tool-cells/BashCell.js +49 -0
  356. package/dist/client/tool-cells/BashCell.js.map +1 -0
  357. package/dist/client/tool-cells/EditCell.d.ts +24 -0
  358. package/dist/client/tool-cells/EditCell.d.ts.map +1 -0
  359. package/dist/client/tool-cells/EditCell.js +126 -0
  360. package/dist/client/tool-cells/EditCell.js.map +1 -0
  361. package/dist/client/tool-cells/FilesChangedSummary.d.ts +13 -0
  362. package/dist/client/tool-cells/FilesChangedSummary.d.ts.map +1 -0
  363. package/dist/client/tool-cells/FilesChangedSummary.js +98 -0
  364. package/dist/client/tool-cells/FilesChangedSummary.js.map +1 -0
  365. package/dist/client/tool-cells/WriteCell.d.ts +17 -0
  366. package/dist/client/tool-cells/WriteCell.d.ts.map +1 -0
  367. package/dist/client/tool-cells/WriteCell.js +26 -0
  368. package/dist/client/tool-cells/WriteCell.js.map +1 -0
  369. package/dist/client/tool-cells/index.d.ts +8 -0
  370. package/dist/client/tool-cells/index.d.ts.map +1 -0
  371. package/dist/client/tool-cells/index.js +5 -0
  372. package/dist/client/tool-cells/index.js.map +1 -0
  373. package/dist/client/transcription/BuilderTranscriptionCta.d.ts +1 -1
  374. package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
  375. package/dist/client/use-chat-threads.d.ts +1 -1
  376. package/dist/client/use-chat-threads.d.ts.map +1 -1
  377. package/dist/client/use-chat-threads.js +11 -8
  378. package/dist/client/use-chat-threads.js.map +1 -1
  379. package/dist/client/use-db-sync.d.ts +2 -0
  380. package/dist/client/use-db-sync.d.ts.map +1 -1
  381. package/dist/client/use-db-sync.js +329 -302
  382. package/dist/client/use-db-sync.js.map +1 -1
  383. package/dist/code-agents/transcript-normalizer.d.ts +15 -1
  384. package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
  385. package/dist/code-agents/transcript-normalizer.js +47 -0
  386. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  387. package/dist/coding-tools/index.d.ts +75 -0
  388. package/dist/coding-tools/index.d.ts.map +1 -1
  389. package/dist/coding-tools/index.js +137 -10
  390. package/dist/coding-tools/index.js.map +1 -1
  391. package/dist/collab/client.d.ts.map +1 -1
  392. package/dist/collab/client.js +15 -9
  393. package/dist/collab/client.js.map +1 -1
  394. package/dist/collab/ydoc-manager.d.ts +1 -1
  395. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  396. package/dist/collab/ydoc-manager.js +1 -1
  397. package/dist/collab/ydoc-manager.js.map +1 -1
  398. package/dist/db/client.d.ts +9 -1
  399. package/dist/db/client.d.ts.map +1 -1
  400. package/dist/db/client.js +204 -48
  401. package/dist/db/client.js.map +1 -1
  402. package/dist/db/create-get-db.d.ts +38 -0
  403. package/dist/db/create-get-db.d.ts.map +1 -1
  404. package/dist/db/create-get-db.js +204 -4
  405. package/dist/db/create-get-db.js.map +1 -1
  406. package/dist/db/migrations.d.ts.map +1 -1
  407. package/dist/db/migrations.js +159 -67
  408. package/dist/db/migrations.js.map +1 -1
  409. package/dist/demo/actions/toggle-demo-mode.d.ts +6 -1
  410. package/dist/demo/actions/toggle-demo-mode.d.ts.map +1 -1
  411. package/dist/deploy/build.d.ts.map +1 -1
  412. package/dist/deploy/build.js +80 -39
  413. package/dist/deploy/build.js.map +1 -1
  414. package/dist/deploy/workspace-deploy.js +20 -10
  415. package/dist/deploy/workspace-deploy.js.map +1 -1
  416. package/dist/extensions/schema.d.ts +51 -51
  417. package/dist/extensions/slots/schema.d.ts +13 -13
  418. package/dist/file-upload/actions/upload-image.d.ts +26 -1
  419. package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
  420. package/dist/file-upload/index.d.ts +1 -1
  421. package/dist/file-upload/index.d.ts.map +1 -1
  422. package/dist/file-upload/index.js +1 -1
  423. package/dist/file-upload/index.js.map +1 -1
  424. package/dist/file-upload/pre-upload-attachments.d.ts +37 -0
  425. package/dist/file-upload/pre-upload-attachments.d.ts.map +1 -1
  426. package/dist/file-upload/pre-upload-attachments.js +79 -19
  427. package/dist/file-upload/pre-upload-attachments.js.map +1 -1
  428. package/dist/index.d.ts +1 -1
  429. package/dist/index.d.ts.map +1 -1
  430. package/dist/index.js.map +1 -1
  431. package/dist/integrations/adapters/slack.js +1 -1
  432. package/dist/integrations/adapters/slack.js.map +1 -1
  433. package/dist/integrations/plugin.js +1 -1
  434. package/dist/integrations/plugin.js.map +1 -1
  435. package/dist/jobs/scheduler.js +70 -21
  436. package/dist/jobs/scheduler.js.map +1 -1
  437. package/dist/mcp/actions/create-org-service-token.d.ts +14 -0
  438. package/dist/mcp/actions/create-org-service-token.d.ts.map +1 -0
  439. package/dist/mcp/actions/create-org-service-token.js +74 -0
  440. package/dist/mcp/actions/create-org-service-token.js.map +1 -0
  441. package/dist/mcp/actions/list-org-service-tokens.d.ts +17 -0
  442. package/dist/mcp/actions/list-org-service-tokens.d.ts.map +1 -0
  443. package/dist/mcp/actions/list-org-service-tokens.js +42 -0
  444. package/dist/mcp/actions/list-org-service-tokens.js.map +1 -0
  445. package/dist/mcp/actions/revoke-org-service-token.d.ts +7 -0
  446. package/dist/mcp/actions/revoke-org-service-token.d.ts.map +1 -0
  447. package/dist/mcp/actions/revoke-org-service-token.js +28 -0
  448. package/dist/mcp/actions/revoke-org-service-token.js.map +1 -0
  449. package/dist/mcp/actions/service-token-access.d.ts +24 -0
  450. package/dist/mcp/actions/service-token-access.d.ts.map +1 -0
  451. package/dist/mcp/actions/service-token-access.js +63 -0
  452. package/dist/mcp/actions/service-token-access.js.map +1 -0
  453. package/dist/mcp/build-server.d.ts +42 -11
  454. package/dist/mcp/build-server.d.ts.map +1 -1
  455. package/dist/mcp/build-server.js +53 -3
  456. package/dist/mcp/build-server.js.map +1 -1
  457. package/dist/mcp/connect-route.d.ts +35 -0
  458. package/dist/mcp/connect-route.d.ts.map +1 -1
  459. package/dist/mcp/connect-route.js +57 -2
  460. package/dist/mcp/connect-route.js.map +1 -1
  461. package/dist/mcp/connect-store.d.ts +43 -0
  462. package/dist/mcp/connect-store.d.ts.map +1 -1
  463. package/dist/mcp/connect-store.js +129 -12
  464. package/dist/mcp/connect-store.js.map +1 -1
  465. package/dist/mcp/oauth-token.d.ts +10 -0
  466. package/dist/mcp/oauth-token.d.ts.map +1 -1
  467. package/dist/mcp/oauth-token.js +2 -0
  468. package/dist/mcp/oauth-token.js.map +1 -1
  469. package/dist/mcp/server.d.ts.map +1 -1
  470. package/dist/mcp/server.js +3 -0
  471. package/dist/mcp/server.js.map +1 -1
  472. package/dist/mcp-client/routes.js +1 -1
  473. package/dist/mcp-client/routes.js.map +1 -1
  474. package/dist/org/context.d.ts +4 -0
  475. package/dist/org/context.d.ts.map +1 -1
  476. package/dist/org/context.js +10 -0
  477. package/dist/org/context.js.map +1 -1
  478. package/dist/org/handlers.d.ts +11 -7
  479. package/dist/org/handlers.d.ts.map +1 -1
  480. package/dist/org/handlers.js +0 -8
  481. package/dist/org/handlers.js.map +1 -1
  482. package/dist/org/migrations.d.ts.map +1 -1
  483. package/dist/org/migrations.js +8 -0
  484. package/dist/org/migrations.js.map +1 -1
  485. package/dist/org/schema.d.ts +15 -15
  486. package/dist/progress/actions.d.ts.map +1 -1
  487. package/dist/progress/actions.js +13 -5
  488. package/dist/progress/actions.js.map +1 -1
  489. package/dist/provider-api/actions/delete-staged-dataset.d.ts +9 -0
  490. package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -0
  491. package/dist/provider-api/actions/delete-staged-dataset.js +35 -0
  492. package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -0
  493. package/dist/provider-api/actions/list-staged-datasets.d.ts +15 -0
  494. package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -0
  495. package/dist/provider-api/actions/list-staged-datasets.js +41 -0
  496. package/dist/provider-api/actions/list-staged-datasets.js.map +1 -0
  497. package/dist/provider-api/actions/query-staged-dataset.d.ts +29 -0
  498. package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -0
  499. package/dist/provider-api/actions/query-staged-dataset.js +116 -0
  500. package/dist/provider-api/actions/query-staged-dataset.js.map +1 -0
  501. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  502. package/dist/provider-api/custom-registry.js.map +1 -1
  503. package/dist/provider-api/index.d.ts +10 -10
  504. package/dist/provider-api/index.js +0 -5
  505. package/dist/provider-api/index.js.map +1 -1
  506. package/dist/provider-api/staged-datasets-aggregate.d.ts +46 -0
  507. package/dist/provider-api/staged-datasets-aggregate.d.ts.map +1 -0
  508. package/dist/provider-api/staged-datasets-aggregate.js +209 -0
  509. package/dist/provider-api/staged-datasets-aggregate.js.map +1 -0
  510. package/dist/provider-api/staged-datasets-store.d.ts +76 -0
  511. package/dist/provider-api/staged-datasets-store.d.ts.map +1 -0
  512. package/dist/provider-api/staged-datasets-store.js +319 -0
  513. package/dist/provider-api/staged-datasets-store.js.map +1 -0
  514. package/dist/provider-api/staging.d.ts +100 -0
  515. package/dist/provider-api/staging.d.ts.map +1 -0
  516. package/dist/provider-api/staging.js +281 -0
  517. package/dist/provider-api/staging.js.map +1 -0
  518. package/dist/resources/handlers.d.ts.map +1 -1
  519. package/dist/resources/handlers.js +13 -1
  520. package/dist/resources/handlers.js.map +1 -1
  521. package/dist/scripts/call-agent.d.ts.map +1 -1
  522. package/dist/scripts/call-agent.js +1 -2
  523. package/dist/scripts/call-agent.js.map +1 -1
  524. package/dist/scripts/resources/migrate-learnings.d.ts +1 -1
  525. package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
  526. package/dist/scripts/resources/migrate-learnings.js +1 -1
  527. package/dist/scripts/resources/migrate-learnings.js.map +1 -1
  528. package/dist/secrets/schema.d.ts +7 -7
  529. package/dist/server/action-discovery.d.ts.map +1 -1
  530. package/dist/server/action-discovery.js +14 -0
  531. package/dist/server/action-discovery.js.map +1 -1
  532. package/dist/server/action-routes.d.ts.map +1 -1
  533. package/dist/server/action-routes.js +3 -2
  534. package/dist/server/action-routes.js.map +1 -1
  535. package/dist/server/agent-chat-plugin.d.ts +33 -0
  536. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  537. package/dist/server/agent-chat-plugin.js +251 -180
  538. package/dist/server/agent-chat-plugin.js.map +1 -1
  539. package/dist/server/agent-discovery.d.ts.map +1 -1
  540. package/dist/server/agent-discovery.js +13 -16
  541. package/dist/server/agent-discovery.js.map +1 -1
  542. package/dist/server/agent-teams-run-queue.d.ts +31 -8
  543. package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
  544. package/dist/server/agent-teams-run-queue.js +61 -18
  545. package/dist/server/agent-teams-run-queue.js.map +1 -1
  546. package/dist/server/agent-teams.d.ts +27 -1
  547. package/dist/server/agent-teams.d.ts.map +1 -1
  548. package/dist/server/agent-teams.js +214 -14
  549. package/dist/server/agent-teams.js.map +1 -1
  550. package/dist/server/app-base-path.d.ts +20 -0
  551. package/dist/server/app-base-path.d.ts.map +1 -1
  552. package/dist/server/app-base-path.js +36 -0
  553. package/dist/server/app-base-path.js.map +1 -1
  554. package/dist/server/attachment-actions.d.ts +43 -0
  555. package/dist/server/attachment-actions.d.ts.map +1 -0
  556. package/dist/server/attachment-actions.js +214 -0
  557. package/dist/server/attachment-actions.js.map +1 -0
  558. package/dist/server/auth.js +1 -1
  559. package/dist/server/auth.js.map +1 -1
  560. package/dist/server/complete-text.d.ts +56 -0
  561. package/dist/server/complete-text.d.ts.map +1 -0
  562. package/dist/server/complete-text.js +147 -0
  563. package/dist/server/complete-text.js.map +1 -0
  564. package/dist/server/core-routes-plugin.d.ts +1 -0
  565. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  566. package/dist/server/core-routes-plugin.js +37 -27
  567. package/dist/server/core-routes-plugin.js.map +1 -1
  568. package/dist/server/cors-origins.d.ts.map +1 -1
  569. package/dist/server/cors-origins.js +6 -1
  570. package/dist/server/cors-origins.js.map +1 -1
  571. package/dist/server/create-server.d.ts.map +1 -1
  572. package/dist/server/create-server.js +2 -1
  573. package/dist/server/create-server.js.map +1 -1
  574. package/dist/server/csrf.d.ts +1 -1
  575. package/dist/server/csrf.d.ts.map +1 -1
  576. package/dist/server/email-actions.d.ts +19 -0
  577. package/dist/server/email-actions.d.ts.map +1 -0
  578. package/dist/server/email-actions.js +191 -0
  579. package/dist/server/email-actions.js.map +1 -0
  580. package/dist/server/embed-route.js +1 -1
  581. package/dist/server/embed-route.js.map +1 -1
  582. package/dist/server/embed-session.d.ts.map +1 -1
  583. package/dist/server/embed-session.js +5 -1
  584. package/dist/server/embed-session.js.map +1 -1
  585. package/dist/server/entry-server.d.ts +24 -0
  586. package/dist/server/entry-server.d.ts.map +1 -0
  587. package/dist/server/entry-server.js +54 -0
  588. package/dist/server/entry-server.js.map +1 -0
  589. package/dist/server/framework-request-handler.d.ts.map +1 -1
  590. package/dist/server/framework-request-handler.js +2 -10
  591. package/dist/server/framework-request-handler.js.map +1 -1
  592. package/dist/server/google-oauth.d.ts.map +1 -1
  593. package/dist/server/google-oauth.js +2 -9
  594. package/dist/server/google-oauth.js.map +1 -1
  595. package/dist/server/google-realtime-session.d.ts.map +1 -1
  596. package/dist/server/google-realtime-session.js +6 -4
  597. package/dist/server/google-realtime-session.js.map +1 -1
  598. package/dist/server/h3-helpers.d.ts +39 -0
  599. package/dist/server/h3-helpers.d.ts.map +1 -1
  600. package/dist/server/h3-helpers.js +104 -1
  601. package/dist/server/h3-helpers.js.map +1 -1
  602. package/dist/server/index.d.ts +2 -1
  603. package/dist/server/index.d.ts.map +1 -1
  604. package/dist/server/index.js +2 -1
  605. package/dist/server/index.js.map +1 -1
  606. package/dist/server/onboarding-html.d.ts.map +1 -1
  607. package/dist/server/onboarding-html.js +1 -8
  608. package/dist/server/onboarding-html.js.map +1 -1
  609. package/dist/server/open-route.d.ts.map +1 -1
  610. package/dist/server/open-route.js +1 -0
  611. package/dist/server/open-route.js.map +1 -1
  612. package/dist/server/prompts/framework-core-compact.d.ts +19 -0
  613. package/dist/server/prompts/framework-core-compact.d.ts.map +1 -0
  614. package/dist/server/prompts/framework-core-compact.js +69 -0
  615. package/dist/server/prompts/framework-core-compact.js.map +1 -0
  616. package/dist/server/prompts/framework-core.d.ts +26 -0
  617. package/dist/server/prompts/framework-core.d.ts.map +1 -0
  618. package/dist/server/prompts/framework-core.js +130 -0
  619. package/dist/server/prompts/framework-core.js.map +1 -0
  620. package/dist/server/prompts/index.d.ts +9 -0
  621. package/dist/server/prompts/index.d.ts.map +1 -0
  622. package/dist/server/prompts/index.js +9 -0
  623. package/dist/server/prompts/index.js.map +1 -0
  624. package/dist/server/prompts/model-overlays.d.ts +18 -0
  625. package/dist/server/prompts/model-overlays.d.ts.map +1 -0
  626. package/dist/server/prompts/model-overlays.js +46 -0
  627. package/dist/server/prompts/model-overlays.js.map +1 -0
  628. package/dist/server/prompts/shared-rules.d.ts +29 -0
  629. package/dist/server/prompts/shared-rules.d.ts.map +1 -0
  630. package/dist/server/prompts/shared-rules.js +54 -0
  631. package/dist/server/prompts/shared-rules.js.map +1 -0
  632. package/dist/server/security-headers.d.ts +7 -1
  633. package/dist/server/security-headers.d.ts.map +1 -1
  634. package/dist/server/security-headers.js +11 -0
  635. package/dist/server/security-headers.js.map +1 -1
  636. package/dist/server/ssr-handler.d.ts.map +1 -1
  637. package/dist/server/ssr-handler.js +135 -46
  638. package/dist/server/ssr-handler.js.map +1 -1
  639. package/dist/server/transcribe-voice.d.ts.map +1 -1
  640. package/dist/server/transcribe-voice.js +7 -4
  641. package/dist/server/transcribe-voice.js.map +1 -1
  642. package/dist/settings/store.d.ts.map +1 -1
  643. package/dist/settings/store.js +9 -0
  644. package/dist/settings/store.js.map +1 -1
  645. package/dist/shared/markdown-block-split.d.ts +39 -0
  646. package/dist/shared/markdown-block-split.d.ts.map +1 -0
  647. package/dist/shared/markdown-block-split.js +97 -0
  648. package/dist/shared/markdown-block-split.js.map +1 -0
  649. package/dist/shared/reasoning-effort.js +13 -1
  650. package/dist/shared/reasoning-effort.js.map +1 -1
  651. package/dist/shared/streaming-text-smoothing.d.ts +18 -0
  652. package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
  653. package/dist/shared/streaming-text-smoothing.js +70 -4
  654. package/dist/shared/streaming-text-smoothing.js.map +1 -1
  655. package/dist/sharing/actions/list-resource-shares.d.ts +24 -1
  656. package/dist/sharing/actions/list-resource-shares.d.ts.map +1 -1
  657. package/dist/sharing/actions/set-resource-visibility.d.ts +8 -1
  658. package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
  659. package/dist/sharing/actions/share-resource.d.ts +12 -1
  660. package/dist/sharing/actions/share-resource.d.ts.map +1 -1
  661. package/dist/sharing/actions/unshare-resource.d.ts +8 -1
  662. package/dist/sharing/actions/unshare-resource.d.ts.map +1 -1
  663. package/dist/sharing/schema.d.ts +10 -10
  664. package/dist/styles/agent-conversation.css +239 -0
  665. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +50 -2
  666. package/dist/templates/default/AGENTS.md +1 -1
  667. package/dist/templates/default/DEVELOPING.md +19 -0
  668. package/dist/templates/default/app/entry.client.tsx +4 -1
  669. package/dist/templates/default/app/entry.server.tsx +4 -56
  670. package/dist/templates/default/app/global.css +3 -2
  671. package/dist/templates/default/app/root.tsx +8 -24
  672. package/dist/templates/default/app/routes/_index.tsx +0 -13
  673. package/dist/templates/default/package.json +6 -5
  674. package/dist/templates/default/tsconfig.json +2 -1
  675. package/dist/templates/starter-shell-sync.spec.ts +118 -0
  676. package/dist/templates/ui-primitives-sync.spec.ts +399 -0
  677. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +50 -2
  678. package/dist/terminal/pty-server.js +1 -1
  679. package/dist/terminal/pty-server.js.map +1 -1
  680. package/dist/triggers/dispatcher.js +1 -1
  681. package/dist/triggers/dispatcher.js.map +1 -1
  682. package/dist/usage/store.d.ts.map +1 -1
  683. package/dist/usage/store.js +60 -7
  684. package/dist/usage/store.js.map +1 -1
  685. package/dist/vite/client.d.ts.map +1 -1
  686. package/dist/vite/client.js +44 -12
  687. package/dist/vite/client.js.map +1 -1
  688. package/dist/workspace-files/schema.d.ts +8 -8
  689. package/dist/workspace-files/tool.d.ts.map +1 -1
  690. package/dist/workspace-files/tool.js +0 -1
  691. package/dist/workspace-files/tool.js.map +1 -1
  692. package/docs/content/a2a-protocol.md +18 -12
  693. package/docs/content/actions.md +42 -10
  694. package/docs/content/agent-mentions.md +7 -8
  695. package/docs/content/agent-teams.md +23 -37
  696. package/docs/content/agent-web-surfaces.md +18 -9
  697. package/docs/content/authentication.md +6 -17
  698. package/docs/content/automations.md +43 -15
  699. package/docs/content/cli-adapters.md +25 -24
  700. package/docs/content/client.md +66 -17
  701. package/docs/content/cloneable-saas.md +19 -23
  702. package/docs/content/code-agents-ui.md +3 -31
  703. package/docs/content/components.md +308 -0
  704. package/docs/content/context-awareness.md +4 -0
  705. package/docs/content/creating-templates.md +4 -2
  706. package/docs/content/cross-app-sso.md +45 -19
  707. package/docs/content/database.md +26 -1
  708. package/docs/content/deployment.md +3 -1
  709. package/docs/content/dispatch.md +9 -37
  710. package/docs/content/drop-in-agent.md +123 -2
  711. package/docs/content/embedding-sdk.md +35 -0
  712. package/docs/content/extensions.md +2 -2
  713. package/docs/content/external-agents.md +86 -171
  714. package/docs/content/faq.md +6 -27
  715. package/docs/content/frames.md +9 -12
  716. package/docs/content/getting-started.md +80 -77
  717. package/docs/content/key-concepts.md +29 -19
  718. package/docs/content/mcp-apps.md +103 -0
  719. package/docs/content/mcp-clients.md +2 -2
  720. package/docs/content/mcp-protocol.md +40 -17
  721. package/docs/content/messaging.md +11 -4
  722. package/docs/content/migration-workbench.md +4 -47
  723. package/docs/content/multi-app-workspace.md +48 -17
  724. package/docs/content/multi-tenancy.md +1 -1
  725. package/docs/content/notifications.md +8 -6
  726. package/docs/content/observability.md +26 -15
  727. package/docs/content/onboarding.md +7 -1
  728. package/docs/content/pr-visual-recap.md +203 -23
  729. package/docs/content/progress.md +5 -5
  730. package/docs/content/pure-agent-apps.md +3 -1
  731. package/docs/content/real-time-collaboration.md +106 -0
  732. package/docs/content/recurring-jobs.md +17 -1
  733. package/docs/content/security.md +17 -3
  734. package/docs/content/server.md +39 -3
  735. package/docs/content/sharing.md +20 -1
  736. package/docs/content/skills-guide.md +151 -125
  737. package/docs/content/template-analytics.md +8 -0
  738. package/docs/content/template-assets.md +2 -0
  739. package/docs/content/template-brain.md +59 -3
  740. package/docs/content/template-calendar.md +8 -0
  741. package/docs/content/template-clips.md +11 -2
  742. package/docs/content/template-content.md +24 -4
  743. package/docs/content/template-design.md +19 -17
  744. package/docs/content/template-dispatch.md +2 -0
  745. package/docs/content/template-forms.md +28 -1
  746. package/docs/content/template-mail.md +17 -0
  747. package/docs/content/template-plan.md +177 -10
  748. package/docs/content/template-slides.md +51 -12
  749. package/docs/content/template-videos.md +17 -0
  750. package/docs/content/tracking.md +17 -13
  751. package/docs/content/using-your-agent.md +15 -5
  752. package/docs/content/voice-input.md +1 -1
  753. package/docs/content/what-is-agent-native.md +5 -6
  754. package/docs/content/workspace-connections.md +138 -424
  755. package/docs/content/workspace-management.md +12 -128
  756. package/docs/content/workspace.md +125 -199
  757. package/docs/content/writing-agent-instructions.md +17 -1
  758. package/package.json +25 -6
  759. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +50 -2
  760. package/src/templates/default/AGENTS.md +1 -1
  761. package/src/templates/default/DEVELOPING.md +19 -0
  762. package/src/templates/default/app/entry.client.tsx +4 -1
  763. package/src/templates/default/app/entry.server.tsx +4 -56
  764. package/src/templates/default/app/global.css +3 -2
  765. package/src/templates/default/app/root.tsx +8 -24
  766. package/src/templates/default/app/routes/_index.tsx +0 -13
  767. package/src/templates/default/package.json +6 -5
  768. package/src/templates/default/tsconfig.json +2 -1
  769. package/src/templates/starter-shell-sync.spec.ts +118 -0
  770. package/src/templates/ui-primitives-sync.spec.ts +399 -0
  771. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +50 -2
  772. package/tsconfig.base.json +2 -10
  773. package/dist/cli/app-skill.d.ts +0 -157
  774. package/dist/cli/app-skill.d.ts.map +0 -1
  775. package/dist/cli/audit-agent-web.d.ts +0 -2
  776. package/dist/cli/audit-agent-web.d.ts.map +0 -1
  777. package/dist/cli/code-agent-connector.d.ts +0 -17
  778. package/dist/cli/code-agent-connector.d.ts.map +0 -1
  779. package/dist/cli/code.d.ts +0 -66
  780. package/dist/cli/code.d.ts.map +0 -1
  781. package/dist/cli/connect.d.ts +0 -140
  782. package/dist/cli/connect.d.ts.map +0 -1
  783. package/dist/cli/context-xray-local.d.ts +0 -16
  784. package/dist/cli/context-xray-local.d.ts.map +0 -1
  785. package/dist/cli/create-workspace.d.ts +0 -8
  786. package/dist/cli/create-workspace.d.ts.map +0 -1
  787. package/dist/cli/index.d.ts +0 -3
  788. package/dist/cli/index.d.ts.map +0 -1
  789. package/dist/cli/info.d.ts +0 -2
  790. package/dist/cli/info.d.ts.map +0 -1
  791. package/dist/cli/mcp-config-writers.d.ts +0 -82
  792. package/dist/cli/mcp-config-writers.d.ts.map +0 -1
  793. package/dist/cli/mcp.d.ts +0 -16
  794. package/dist/cli/mcp.d.ts.map +0 -1
  795. package/dist/cli/migrate.d.ts +0 -38
  796. package/dist/cli/migrate.d.ts.map +0 -1
  797. package/dist/cli/plan-local.d.ts +0 -43
  798. package/dist/cli/plan-local.d.ts.map +0 -1
  799. package/dist/cli/plan-publish-store.d.ts +0 -62
  800. package/dist/cli/plan-publish-store.d.ts.map +0 -1
  801. package/dist/cli/pr-visual-recap-workflow.d.ts +0 -11
  802. package/dist/cli/pr-visual-recap-workflow.d.ts.map +0 -1
  803. package/dist/cli/recap.d.ts +0 -297
  804. package/dist/cli/recap.d.ts.map +0 -1
  805. package/dist/cli/skills.d.ts +0 -162
  806. package/dist/cli/skills.d.ts.map +0 -1
  807. package/dist/cli/workspace-dev.d.ts +0 -96
  808. package/dist/cli/workspace-dev.d.ts.map +0 -1
@@ -1,191 +1,37 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React, { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle, } from "react";
3
- import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime, useMessagePartText, useMessageRuntime, ThreadPrimitive, MessagePrimitive, } from "@assistant-ui/react";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle, } from "react";
3
+ import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime, ThreadPrimitive, } from "@assistant-ui/react";
4
4
  import { CompositeAttachmentAdapter } from "@assistant-ui/react";
5
- import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
6
- import remarkGfm from "remark-gfm";
7
5
  import { createAgentChatAdapter, } from "./agent-chat-adapter.js";
8
6
  import { appendAgentChatContextToMessage, formatAgentChatContextItemsForPrompt, normalizeAgentChatContextItem, publishAgentChatContextItems, refreshAgentChatContext, } from "./agent-chat.js";
9
7
  import { useAgentDynamicSuggestionsResult, } from "./dynamic-suggestions.js";
10
- import { initialSmoothStreamingGraphemeCount, SMOOTH_STREAMING_COMMIT_INTERVAL_MS, smoothStreamingPunctuationDelayMs, smoothStreamingRevealCount, splitStreamingTextGraphemes, } from "../shared/streaming-text-smoothing.js";
8
+ import { PROVIDER_ENV_VARS } from "../agent/engine/provider-env-vars.js";
11
9
  import { getActiveRun } from "./active-run-state.js";
12
10
  import { AgentAutoContinueSignal, readSSEStreamRaw, } from "./sse-event-processor.js";
13
11
  import { captureError } from "./analytics.js";
14
12
  import { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, } from "./assistant-ui-recovery.js";
15
13
  import { cn } from "./utils.js";
16
- import { writeClipboardText } from "./clipboard.js";
17
14
  import { useNearBottomAutoscroll } from "./conversation/index.js";
18
15
  import { TextAttachmentAdapter } from "./composer/attachment-accept.js";
19
- import { AgentTaskCard } from "./AgentTaskCard.js";
20
- import { ConnectBuilderCard } from "./ConnectBuilderCard.js";
21
- import { McpAppRenderer } from "./mcp-apps/McpAppRenderer.js";
22
- import { humanizeToolName } from "./tool-display.js";
23
- import { useBuilderConnectFlow } from "./settings/useBuilderStatus.js";
24
16
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
25
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
26
- import { IframeEmbed, parseEmbedBody } from "./IframeEmbed.js";
27
17
  import { GuidedQuestionFlow, useGuidedQuestionFlow, } from "./guided-questions.js";
28
18
  import { useDevMode } from "./use-dev-mode.js";
29
19
  import { agentNativePath } from "./api-path.js";
30
- import { saveAgentEngineApiKey, } from "./agent-engine-key.js";
31
- import { BUILDER_SPACE_SETTINGS_URL, NEW_CHAT_ACTION_HREF, } from "./error-format.js";
32
- import { ThumbsFeedback } from "./observability/ThumbsFeedback.js";
33
20
  import { TiptapComposer, } from "./composer/TiptapComposer.js";
34
21
  import { AgentComposerFrame } from "./composer/AgentComposerFrame.js";
35
22
  import { isPastedTextAttachmentName } from "./composer/pasted-text.js";
36
23
  import { PastedTextChip } from "./composer/PastedTextChip.js";
37
24
  import { ContextMeter } from "./context-xray/ContextMeter.js";
38
- import { IconMessage, IconX, IconPlayerStop, IconCheck, IconChevronDown, IconCopy, IconTerminal, IconLoader2, IconCircleX, IconSquareFilled, IconClock, IconFile, IconFolder, IconFileText, IconCheckbox, IconMail, IconUser, IconPresentation, IconStack2, IconMessageChatbot, IconArrowBackUp, IconExternalLink, IconKey, IconDots, IconGitFork, IconId, IconQuote, IconGauge, IconArrowRight, IconSettings, IconAlertTriangle, IconRefresh, IconPlayerPlay, IconClipboardList, IconSearch, IconArrowsMaximize, IconArrowsMinimize, IconPlus, } from "@tabler/icons-react";
25
+ import { IconMessage, IconX, IconPlayerStop, IconChevronDown, IconTerminal, IconClock, IconAlertTriangle, IconRefresh, } from "@tabler/icons-react";
26
+ // ─── chat/ module imports ─────────────────────────────────────────────────────
27
+ import { DownscalingImageAttachmentAdapter, BinaryDocumentAttachmentAdapter, MAX_ESTIMATED_BODY_BYTES, AGGRESSIVE_MAX_IMAGE_DIMENSION, AGGRESSIVE_JPEG_QUALITY, transcodeImageToDataURL, createAgentImageAttachments, serializeQueuedAttachments, estimateAttachmentBodyBytes, } from "./chat/attachment-adapters.js";
28
+ import { TextStreamingContext } from "./chat/markdown-renderer.js";
29
+ import { ChatRunningContext, ReconnectStreamMessage, } from "./chat/tool-call-display.js";
30
+ import { CheckpointContext, MessageActionsContext, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, } from "./chat/message-components.js";
31
+ import { BuilderSetupCard, LoopLimitContinueCard, RunErrorRecoveryCard, PlanModeCallout, getLoopLimitMetadata, getRunErrorMetadata, getRequestModeMetadata, } from "./chat/run-recovery.js";
32
+ import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, } from "./chat/repo-helpers.js";
39
33
  export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
40
- class DownscalingImageAttachmentAdapter {
41
- accept = "image/*";
42
- async add(state) {
43
- return {
44
- id: state.file.name,
45
- type: "image",
46
- name: state.file.name,
47
- contentType: state.file.type,
48
- file: state.file,
49
- status: { type: "requires-action", reason: "composer-send" },
50
- };
51
- }
52
- async send(attachment) {
53
- return {
54
- ...attachment,
55
- status: { type: "complete" },
56
- content: [
57
- {
58
- type: "image",
59
- image: await getImageFileDataURL(attachment.file),
60
- },
61
- ],
62
- };
63
- }
64
- async remove() {
65
- // noop
66
- }
67
- }
68
- class BinaryDocumentAttachmentAdapter {
69
- accept = "application/pdf,.pdf";
70
- async add(state) {
71
- return {
72
- id: state.file.name,
73
- type: "document",
74
- name: state.file.name,
75
- contentType: inferDocumentContentType(state.file),
76
- file: state.file,
77
- status: { type: "requires-action", reason: "composer-send" },
78
- };
79
- }
80
- async send(attachment) {
81
- return {
82
- ...attachment,
83
- status: { type: "complete" },
84
- content: [
85
- {
86
- type: "file",
87
- filename: attachment.name,
88
- data: await getFileDataURL(attachment.file),
89
- mimeType: inferDocumentContentType(attachment.file),
90
- },
91
- ],
92
- };
93
- }
94
- async remove() {
95
- // noop
96
- }
97
- }
98
- function inferDocumentContentType(file) {
99
- if (file.type)
100
- return file.type;
101
- if (file.name.toLowerCase().endsWith(".pdf"))
102
- return "application/pdf";
103
- return "application/octet-stream";
104
- }
105
- function getFileDataURL(file) {
106
- return new Promise((resolve, reject) => {
107
- const reader = new FileReader();
108
- reader.onload = () => resolve(reader.result);
109
- reader.onerror = (error) => reject(error);
110
- reader.readAsDataURL(file);
111
- });
112
- }
113
- // Anthropic / OpenAI vision inputs choke on multi-megabyte images, and
114
- // base64-encoding a raw screenshot eats enough heap to crash the composer
115
- // (PayloadTooLarge / "Maximum call stack" in serializers). Downscale large
116
- // images on the client before we ever serialize them.
117
- const MAX_IMAGE_BYTES = 4 * 1024 * 1024;
118
- const MAX_IMAGE_DIMENSION = 2048;
119
- function loadImage(url) {
120
- return new Promise((resolve, reject) => {
121
- const img = new Image();
122
- img.onload = () => resolve(img);
123
- img.onerror = () => reject(new Error("Failed to decode pasted image"));
124
- img.src = url;
125
- });
126
- }
127
- async function getImageFileDataURL(file) {
128
- if (file.size <= MAX_IMAGE_BYTES) {
129
- return getFileDataURL(file);
130
- }
131
- if (typeof document === "undefined" || typeof Image === "undefined") {
132
- return getFileDataURL(file);
133
- }
134
- const objectUrl = URL.createObjectURL(file);
135
- try {
136
- const img = await loadImage(objectUrl);
137
- const ratio = Math.min(MAX_IMAGE_DIMENSION / img.naturalWidth, MAX_IMAGE_DIMENSION / img.naturalHeight, 1);
138
- const width = Math.max(1, Math.round(img.naturalWidth * ratio));
139
- const height = Math.max(1, Math.round(img.naturalHeight * ratio));
140
- const canvas = document.createElement("canvas");
141
- canvas.width = width;
142
- canvas.height = height;
143
- const ctx = canvas.getContext("2d");
144
- if (!ctx) {
145
- return getFileDataURL(file);
146
- }
147
- ctx.drawImage(img, 0, 0, width, height);
148
- const useJpeg = file.type !== "image/png" || file.size > MAX_IMAGE_BYTES * 2;
149
- return canvas.toDataURL(useJpeg ? "image/jpeg" : "image/png", 0.85);
150
- }
151
- catch {
152
- return getFileDataURL(file);
153
- }
154
- finally {
155
- URL.revokeObjectURL(objectUrl);
156
- }
157
- }
158
- function imageContentTypeFromDataUrl(dataUrl) {
159
- const match = /^data:([^;,]+)/.exec(dataUrl);
160
- return match?.[1] || "image/jpeg";
161
- }
162
- function imageExtensionFromContentType(contentType) {
163
- if (contentType === "image/png")
164
- return "png";
165
- if (contentType === "image/webp")
166
- return "webp";
167
- if (contentType === "image/gif")
168
- return "gif";
169
- return "jpg";
170
- }
171
- function createAgentImageAttachments(images) {
172
- const validImages = (images ?? []).filter((image) => image.trim().length > 0);
173
- if (validImages.length === 0)
174
- return undefined;
175
- return validImages.map((image, index) => {
176
- const contentType = imageContentTypeFromDataUrl(image);
177
- const extension = imageExtensionFromContentType(contentType);
178
- const name = `image-${index + 1}.${extension}`;
179
- return {
180
- id: `agent-chat-image-${index + 1}`,
181
- type: "image",
182
- name,
183
- contentType,
184
- status: { type: "complete" },
185
- content: [{ type: "image", image }],
186
- };
187
- });
188
- }
34
+ export { displayableUserMessageText } from "./chat/message-components.js";
189
35
  function createUserMessageRunConfig(references, requestMode, recoveryAction, trackInRunsTray) {
190
36
  const custom = {};
191
37
  if (references && references.length > 0) {
@@ -208,156 +54,6 @@ function createUserMessageRunConfig(references, requestMode, recoveryAction, tra
208
54
  }
209
55
  return options;
210
56
  }
211
- function escapeQueuedAttachmentAttribute(value) {
212
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
213
- }
214
- function isTextLikeFile(file) {
215
- if (file.type.startsWith("text/"))
216
- return true;
217
- if (file.type === "application/json")
218
- return true;
219
- return /\.(txt|md|markdown|csv|json|yaml|yml)$/i.test(file.name);
220
- }
221
- function textFileAttachmentEnvelope(file, text) {
222
- const contentType = file.type || "text/plain";
223
- return `<attachment name="${escapeQueuedAttachmentAttribute(file.name)}" contentType="${escapeQueuedAttachmentAttribute(contentType)}">\n${text}\n</attachment>`;
224
- }
225
- function serializeAttachmentContentPart(part) {
226
- if (part.type === "image" && typeof part.image === "string") {
227
- return { type: "image", image: part.image };
228
- }
229
- if (part.type === "text" && typeof part.text === "string") {
230
- return { type: "text", text: part.text };
231
- }
232
- if (part.type === "file" && typeof part.data === "string") {
233
- return {
234
- type: "file",
235
- data: part.data,
236
- mimeType: typeof part.mimeType === "string"
237
- ? part.mimeType
238
- : "application/octet-stream",
239
- ...(typeof part.filename === "string" ? { filename: part.filename } : {}),
240
- };
241
- }
242
- return null;
243
- }
244
- async function serializeQueuedAttachments(attachments) {
245
- const queued = [];
246
- for (const raw of attachments ?? []) {
247
- const attachment = raw;
248
- const name = attachment.name || attachment.file?.name || "attachment";
249
- const id = attachment.id || name;
250
- const type = attachment.type || "file";
251
- const contentType = attachment.contentType || attachment.file?.type;
252
- if (Array.isArray(attachment.content) && attachment.content.length > 0) {
253
- const content = attachment.content
254
- .map((part) => serializeAttachmentContentPart(part))
255
- .filter((part) => !!part);
256
- if (content.length > 0) {
257
- queued.push({
258
- id,
259
- type,
260
- name,
261
- contentType,
262
- status: { type: "complete" },
263
- content,
264
- });
265
- }
266
- continue;
267
- }
268
- if (typeof File !== "undefined" && attachment.file instanceof File) {
269
- const file = attachment.file;
270
- if (file.type.startsWith("image/")) {
271
- queued.push({
272
- id,
273
- type: "image",
274
- name,
275
- contentType: file.type,
276
- status: { type: "complete" },
277
- content: [{ type: "image", image: await getImageFileDataURL(file) }],
278
- });
279
- }
280
- else if (isTextLikeFile(file)) {
281
- queued.push({
282
- id,
283
- type: "file",
284
- name,
285
- contentType: file.type || "text/plain",
286
- status: { type: "complete" },
287
- content: [
288
- {
289
- type: "text",
290
- text: textFileAttachmentEnvelope(file, await file.text()),
291
- },
292
- ],
293
- });
294
- }
295
- else {
296
- queued.push({
297
- id,
298
- type: "document",
299
- name,
300
- contentType: inferDocumentContentType(file),
301
- status: { type: "complete" },
302
- content: [
303
- {
304
- type: "file",
305
- filename: file.name,
306
- data: await getFileDataURL(file),
307
- mimeType: inferDocumentContentType(file),
308
- },
309
- ],
310
- });
311
- }
312
- }
313
- }
314
- return queued.length > 0 ? queued : undefined;
315
- }
316
- // ─── Markdown Text ──────────────────────────────────────────────────────────
317
- const markdownStyles = `
318
- .agent-markdown > :first-child { margin-top: 0; }
319
- .agent-markdown > :last-child { margin-bottom: 0; }
320
- .agent-markdown p { margin: 0.5em 0; }
321
- .agent-markdown ul, .agent-markdown ol { margin: 0.5em 0; padding-left: 1.5em; }
322
- .agent-markdown li { margin: 0.2em 0; }
323
- .agent-markdown li > p { margin: 0; }
324
- .agent-markdown h1 { font-size: 1.25em; font-weight: 600; margin: 0.75em 0 0.25em; }
325
- .agent-markdown h2 { font-size: 1.125em; font-weight: 600; margin: 0.75em 0 0.25em; }
326
- .agent-markdown h3 { font-size: 1em; font-weight: 600; margin: 0.75em 0 0.25em; }
327
- .agent-markdown strong { font-weight: 600; }
328
- .agent-markdown em { font-style: italic; }
329
- .agent-markdown code { font-size: 0.875em; padding: 0.15em 0.35em; border-radius: 0.25em; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); border: 1px solid hsl(var(--border, 0 0% 80%)); }
330
- .agent-markdown pre { margin: 0.5em 0; padding: 0.75em 1em; border-radius: 0.375em; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); overflow-x: auto; border: 1px solid hsl(var(--border, 0 0% 80%)); }
331
- .agent-markdown pre code { padding: 0; background: transparent; font-size: 0.8125em; color: inherit; border: none; }
332
- .agent-markdown-shiki { margin: 0.5em 0; border-radius: 0.375em; overflow: hidden; font-size: 0.8125em; }
333
- .agent-markdown-shiki pre { margin: 0; padding: 0.75em 1em; overflow-x: auto; background: var(--shiki-light-bg); color: var(--shiki-light); }
334
- .agent-markdown-shiki pre code { background: transparent; padding: 0; font-size: inherit; color: inherit; }
335
- .agent-markdown-shiki pre span { color: var(--shiki-light); background: var(--shiki-light-bg); }
336
- .dark .agent-markdown-shiki pre { background: var(--shiki-dark-bg); color: var(--shiki-dark); }
337
- .dark .agent-markdown-shiki pre span { color: var(--shiki-dark); background: var(--shiki-dark-bg); }
338
- @media (prefers-color-scheme: dark) { :root:not(.light) .agent-markdown-shiki pre { background: var(--shiki-dark-bg); color: var(--shiki-dark); } :root:not(.light) .agent-markdown-shiki pre span { color: var(--shiki-dark); background: var(--shiki-dark-bg); } }
339
- .agent-tool-code .agent-markdown-shiki { margin: 0; border-radius: 0; min-width: max-content; }
340
- .agent-tool-code .agent-markdown-shiki pre { padding: 0.75rem; border: 0; background: transparent; }
341
- .agent-tool-code .agent-markdown-shiki pre span { background: transparent; }
342
- .agent-tool-code pre { margin: 0; min-width: max-content; padding: 0.75rem; background: transparent; color: inherit; }
343
- .agent-tool-code mark { border-radius: 0.1875rem; background: rgba(245, 158, 11, 0.25); color: inherit; }
344
- .agent-markdown hr { border: none; border-top: 1px solid hsl(var(--border, 0 0% 20%)); margin: 0.75em 0 1em; }
345
- .agent-markdown a { text-decoration: underline; text-underline-offset: 2px; }
346
- .agent-markdown a.agent-markdown-cta { text-decoration: none; }
347
- .agent-markdown blockquote { border-left: 2px solid hsl(var(--border, 0 0% 20%)); padding-left: 0.75em; margin: 0.5em 0; opacity: 0.8; }
348
- .agent-markdown table { border-collapse: collapse; margin: 0.5em 0; font-size: 0.875em; }
349
- .agent-markdown th, .agent-markdown td { border: 1px solid hsl(var(--border, 0 0% 20%)); padding: 0.35em 0.65em; text-align: left; }
350
- .agent-markdown th { font-weight: 600; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); }
351
- .agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { content: ""; display: inline-block; width: 0.42em; height: 1em; margin-left: 0.12em; border-radius: 999px; background: currentColor; opacity: 0.35; transform: translateY(0.16em); animation: agent-markdown-stream-caret 1.15s ease-in-out infinite; }
352
- @keyframes agent-markdown-stream-caret { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.58; } }
353
- @media (prefers-reduced-motion: reduce) { .agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { animation: none; opacity: 0.28; } }
354
- `;
355
- /**
356
- * Pending selection context — written to application_state when the user
357
- * presses Cmd+I with text selected on the page. The agent's next turn picks
358
- * it up via the `selectionContextPromise` in production-agent. The pill
359
- * below tells the user the context is attached and lets them clear it.
360
- */
361
57
  const PENDING_SELECTION_KEY = "pending-selection-context";
362
58
  const ACTIVE_RUN_CLEAR_TIMEOUT_MS = 5_000;
363
59
  const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
@@ -367,67 +63,6 @@ function activeRunLooksStale(runInfo) {
367
63
  heartbeatAt != null &&
368
64
  Date.now() - heartbeatAt > 5000);
369
65
  }
370
- function repoHasAssistantMessage(repo) {
371
- return repo?.messages?.some((m) => (m.message?.role ?? m.role) === "assistant");
372
- }
373
- function getRepoMessages(repo) {
374
- return Array.isArray(repo?.messages) ? repo.messages : [];
375
- }
376
- function getRepoMessage(entry) {
377
- return entry?.message ?? entry;
378
- }
379
- function isAssistantMessageTerminal(message) {
380
- const statusType = message?.status && typeof message.status === "object"
381
- ? message.status.type
382
- : undefined;
383
- return statusType === "complete" || statusType === "incomplete";
384
- }
385
- function repoTextLength(repo) {
386
- let length = 0;
387
- for (const entry of getRepoMessages(repo)) {
388
- const message = getRepoMessage(entry);
389
- const content = message?.content;
390
- if (typeof content === "string") {
391
- length += content.length;
392
- }
393
- else if (Array.isArray(content)) {
394
- for (const part of content) {
395
- if (part?.type === "text" && typeof part.text === "string") {
396
- length += part.text.length;
397
- }
398
- }
399
- }
400
- }
401
- return length;
402
- }
403
- function repoTerminalAssistantCount(repo) {
404
- return getRepoMessages(repo).filter((entry) => {
405
- const message = getRepoMessage(entry);
406
- return message?.role === "assistant" && isAssistantMessageTerminal(message);
407
- }).length;
408
- }
409
- function shouldImportServerThreadData(currentRepo, incomingRepo) {
410
- const incomingCount = getRepoMessages(incomingRepo).length;
411
- if (incomingCount === 0)
412
- return false;
413
- const currentCount = getRepoMessages(currentRepo).length;
414
- if (currentCount === 0)
415
- return true;
416
- if (incomingCount < currentCount)
417
- return false;
418
- if (incomingCount === currentCount) {
419
- const currentTerminalAssistants = repoTerminalAssistantCount(currentRepo);
420
- const incomingTerminalAssistants = repoTerminalAssistantCount(incomingRepo);
421
- if (incomingTerminalAssistants < currentTerminalAssistants) {
422
- return false;
423
- }
424
- if (incomingTerminalAssistants <= currentTerminalAssistants &&
425
- repoTextLength(incomingRepo) < repoTextLength(currentRepo)) {
426
- return false;
427
- }
428
- }
429
- return true;
430
- }
431
66
  function cloneContentParts(content) {
432
67
  return content.map((part) => part.type === "text"
433
68
  ? { ...part }
@@ -470,452 +105,15 @@ async function waitForThreadRunToClear(apiUrl, threadId) {
470
105
  await new Promise((resolve) => window.setTimeout(resolve, ACTIVE_RUN_POLL_INTERVAL_MS));
471
106
  }
472
107
  }
473
- function coerceMessageDate(value) {
474
- if (value instanceof Date) {
475
- return Number.isNaN(value.getTime()) ? null : value;
476
- }
477
- if (typeof value === "string" || typeof value === "number") {
478
- const date = new Date(value);
479
- return Number.isNaN(date.getTime()) ? null : date;
480
- }
481
- return null;
482
- }
483
- function isSameCalendarDay(a, b) {
484
- return (a.getFullYear() === b.getFullYear() &&
485
- a.getMonth() === b.getMonth() &&
486
- a.getDate() === b.getDate());
487
- }
488
- function formatMessageTimestamp(value) {
489
- const date = coerceMessageDate(value);
490
- if (!date)
491
- return null;
492
- const now = new Date();
493
- const yesterday = new Date(now);
494
- yesterday.setDate(now.getDate() - 1);
495
- const time = new Intl.DateTimeFormat(undefined, {
496
- hour: "numeric",
497
- minute: "2-digit",
498
- }).format(date);
499
- let short;
500
- if (isSameCalendarDay(date, now)) {
501
- short = time;
502
- }
503
- else if (isSameCalendarDay(date, yesterday)) {
504
- short = `Yesterday ${time}`;
505
- }
506
- else if (date.getFullYear() === now.getFullYear()) {
507
- short = `${new Intl.DateTimeFormat(undefined, {
508
- month: "short",
509
- day: "numeric",
510
- }).format(date)}, ${time}`;
511
- }
512
- else {
513
- short = `${new Intl.DateTimeFormat(undefined, {
514
- month: "short",
515
- day: "numeric",
516
- year: "numeric",
517
- }).format(date)}, ${time}`;
518
- }
519
- return {
520
- short,
521
- full: new Intl.DateTimeFormat(undefined, {
522
- weekday: "short",
523
- month: "short",
524
- day: "numeric",
525
- year: "numeric",
526
- hour: "numeric",
527
- minute: "2-digit",
528
- }).format(date),
529
- };
530
- }
531
- function MessageTimestamp({ timestamp, className, }) {
532
- return (_jsx("span", { className: cn("text-[11px] leading-none text-muted-foreground", className), title: timestamp.full, children: timestamp.short }));
533
- }
534
- function SelectionAttachedPill() {
535
- const [length, setLength] = useState(null);
536
- useEffect(() => {
537
- let cancelled = false;
538
- fetch(agentNativePath(`/_agent-native/application-state/${PENDING_SELECTION_KEY}`))
539
- .then((r) => (r.ok && r.status !== 204 ? r.json() : null))
540
- .then((data) => {
541
- if (cancelled)
542
- return;
543
- const text = data?.value?.text ??
544
- data?.text;
545
- if (text)
546
- setLength(text.length);
547
- })
548
- .catch(() => { });
549
- return () => {
550
- cancelled = true;
551
- };
552
- }, []);
553
- useEffect(() => {
554
- function onAttached(e) {
555
- const detail = e.detail;
556
- if (typeof detail?.length === "number")
557
- setLength(detail.length);
558
- }
559
- function onCleared() {
560
- setLength(null);
561
- }
562
- window.addEventListener("agent-panel:selection-attached", onAttached);
563
- window.addEventListener("agent-panel:selection-cleared", onCleared);
564
- return () => {
565
- window.removeEventListener("agent-panel:selection-attached", onAttached);
566
- window.removeEventListener("agent-panel:selection-cleared", onCleared);
567
- };
568
- }, []);
569
- if (length === null || length === 0)
570
- return null;
571
- return (_jsx("div", { className: "shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }), _jsxs("span", { children: [length.toLocaleString(), " chars of selection attached"] }), _jsx("button", { type: "button", "aria-label": "Clear selection context", onClick: () => {
572
- setLength(null);
573
- clearPendingSelection();
574
- }, className: "flex h-4 w-4 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/60", children: _jsx(IconX, { size: 11 }) })] }) }));
575
- }
576
- let stylesInjected = false;
577
- function injectMarkdownStyles() {
578
- if (stylesInjected || typeof document === "undefined")
579
- return;
580
- stylesInjected = true;
581
- const style = document.createElement("style");
582
- style.textContent = markdownStyles;
583
- document.head.appendChild(style);
584
- }
585
- function extractCodeText(child) {
586
- if (typeof child === "string")
587
- return child;
588
- if (Array.isArray(child))
589
- return child.map(extractCodeText).join("");
590
- if (React.isValidElement(child)) {
591
- const props = child.props;
592
- return extractCodeText(props.children);
593
- }
594
- return "";
595
- }
596
- let highlighterLoader = null;
597
- function loadHighlighter() {
598
- if (!highlighterLoader) {
599
- highlighterLoader = (async () => {
600
- const [{ createHighlighterCore }, { createOnigurumaEngine }] = await Promise.all([
601
- import("shiki/core"),
602
- import("shiki/engine/oniguruma"),
603
- ]);
604
- return createHighlighterCore({
605
- themes: [
606
- import("shiki/themes/github-light-default.mjs"),
607
- import("shiki/themes/github-dark-default.mjs"),
608
- ],
609
- langs: [
610
- import("shiki/langs/javascript.mjs"),
611
- import("shiki/langs/typescript.mjs"),
612
- import("shiki/langs/jsx.mjs"),
613
- import("shiki/langs/tsx.mjs"),
614
- import("shiki/langs/json.mjs"),
615
- import("shiki/langs/css.mjs"),
616
- import("shiki/langs/html.mjs"),
617
- import("shiki/langs/markdown.mjs"),
618
- import("shiki/langs/bash.mjs"),
619
- import("shiki/langs/shellscript.mjs"),
620
- import("shiki/langs/python.mjs"),
621
- import("shiki/langs/yaml.mjs"),
622
- import("shiki/langs/sql.mjs"),
623
- ],
624
- engine: createOnigurumaEngine(import("shiki/wasm")),
625
- });
626
- })().catch((error) => {
627
- // Reset on failure so a future code block can retry instead of
628
- // silently failing forever on a stale chunk / network blip.
629
- highlighterLoader = null;
630
- throw error;
631
- });
632
- }
633
- return highlighterLoader;
634
- }
635
- import { PROVIDER_ENV_VARS } from "../agent/engine/provider-env-vars.js";
636
108
  const PROVIDER_ENV_VAR_SET = new Set(PROVIDER_ENV_VARS);
637
- // Map a few common aliases to languages we bundled above.
638
- const LANG_ALIASES = {
639
- js: "javascript",
640
- ts: "typescript",
641
- sh: "bash",
642
- shell: "bash",
643
- zsh: "bash",
644
- py: "python",
645
- yml: "yaml",
646
- md: "markdown",
647
- bq: "sql",
648
- bigquery: "sql",
649
- };
650
- function HighlightedCodeBlock({ code, lang }) {
651
- const [html, setHtml] = useState(null);
652
- useEffect(() => {
653
- let cancelled = false;
654
- loadHighlighter()
655
- .then((highlighter) => {
656
- const requested = (lang || "text").toLowerCase();
657
- const resolved = LANG_ALIASES[requested] ?? requested;
658
- const loaded = highlighter.getLoadedLanguages();
659
- const finalLang = loaded.includes(resolved) ? resolved : "text";
660
- return highlighter.codeToHtml(code, {
661
- lang: finalLang,
662
- themes: {
663
- light: "github-light-default",
664
- dark: "github-dark-default",
665
- },
666
- defaultColor: false,
667
- });
668
- })
669
- .then((out) => {
670
- if (!cancelled)
671
- setHtml(out);
672
- })
673
- .catch(() => {
674
- // Unknown language or other shiki failure — fall back to plain pre.
675
- if (!cancelled)
676
- setHtml(null);
677
- });
678
- return () => {
679
- cancelled = true;
680
- };
681
- }, [code, lang]);
682
- if (html) {
683
- return (_jsx("div", { className: "agent-markdown-shiki", dangerouslySetInnerHTML: { __html: html } }));
684
- }
685
- return (_jsx("pre", { children: _jsx("code", { className: lang ? `language-${lang}` : undefined, children: code }) }));
686
- }
687
- const CTA_BUTTON_CLASSES = "agent-markdown-cta mt-1 inline-flex items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background no-underline shadow-sm transition-colors hover:bg-foreground/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background cursor-pointer";
688
- const markdownComponents = {
689
- a(props) {
690
- const { href, children, className, rel: _rel, target: _target, ...rest } = props;
691
- if (href === NEW_CHAT_ACTION_HREF) {
692
- // In-app action: dispatch a CustomEvent that MultiTabAssistantChat
693
- // listens for and opens a new chat tab. Not an external navigation.
694
- return (_jsxs("button", { type: "button", onClick: (e) => {
695
- e.preventDefault();
696
- window.dispatchEvent(new CustomEvent("agent-chat:new-chat"));
697
- }, className: cn(CTA_BUTTON_CLASSES, className), children: [_jsx(IconPlus, { size: 13, strokeWidth: 2, "aria-hidden": "true" }), _jsx("span", { children: children })] }));
698
- }
699
- const isBuilderCta = isBuilderErrorCtaHref(href);
700
- if (!isBuilderCta) {
701
- return (_jsx("a", { href: href, className: className, ...rest, children: children }));
702
- }
703
- return (_jsxs("a", { href: href, target: "_blank", rel: "noreferrer", className: cn(CTA_BUTTON_CLASSES, className), ...rest, children: [_jsx("span", { children: children }), _jsx(IconExternalLink, { size: 13, strokeWidth: 2, "aria-hidden": "true" })] }));
704
- },
705
- pre(props) {
706
- const { children, ...rest } = props;
707
- if (React.isValidElement(children)) {
708
- const childProps = children.props;
709
- const className = childProps.className || "";
710
- if (/\blanguage-embed\b/.test(className)) {
711
- const body = extractCodeText(childProps.children);
712
- const parsed = parseEmbedBody(body);
713
- return (_jsx(IframeEmbed, { ...parsed }));
714
- }
715
- const langMatch = className.match(/\blanguage-([\w+-]+)\b/);
716
- if (langMatch) {
717
- const code = extractCodeText(childProps.children).replace(/\n$/, "");
718
- return _jsx(HighlightedCodeBlock, { code: code, lang: langMatch[1] });
719
- }
720
- }
721
- return _jsx("pre", { ...rest, children: children });
722
- },
723
- };
724
- function isBuilderErrorCtaHref(href) {
725
- if (!href)
726
- return false;
727
- try {
728
- const url = new URL(href);
729
- if (url.protocol !== "https:" || url.hostname !== "builder.io") {
730
- return false;
731
- }
732
- return (url.href === BUILDER_SPACE_SETTINGS_URL ||
733
- url.pathname === "/account/billing" ||
734
- /^\/app\/organizations\/[^/]+\/billing$/.test(url.pathname));
735
- }
736
- catch {
737
- return false;
738
- }
739
- }
740
- // react-markdown's defaultUrlTransform strips href values whose protocol
741
- // isn't on its safe list (https, mailto, etc.). Our in-app pseudo-href
742
- // `agent-native:new-chat` would be blanked out by that, so let it through
743
- // while delegating every other URL to the default transform for sanitization.
744
- function markdownUrlTransform(value) {
745
- if (value === NEW_CHAT_ACTION_HREF)
746
- return value;
747
- return defaultUrlTransform(value);
748
- }
749
- const TextStreamingContext = React.createContext(false);
750
- function usePrefersReducedMotion() {
751
- const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => typeof window !== "undefined" && window.matchMedia
752
- ? window.matchMedia("(prefers-reduced-motion: reduce)").matches
753
- : false);
754
- useEffect(() => {
755
- if (typeof window === "undefined" || !window.matchMedia)
756
- return undefined;
757
- const media = window.matchMedia("(prefers-reduced-motion: reduce)");
758
- const handleChange = () => setPrefersReducedMotion(media.matches);
759
- handleChange();
760
- if (typeof media.addEventListener === "function") {
761
- media.addEventListener("change", handleChange);
762
- return () => media.removeEventListener("change", handleChange);
763
- }
764
- media.addListener(handleChange);
765
- return () => media.removeListener(handleChange);
766
- }, []);
767
- return prefersReducedMotion;
768
- }
769
- function sliceGraphemes(targetText, graphemes, count) {
770
- if (count >= graphemes.length)
771
- return targetText;
772
- if (count <= 0)
773
- return "";
774
- return graphemes.slice(0, count).join("");
775
- }
776
- function useSmoothStreamingText(targetText, streaming, resetKey) {
777
- const prefersReducedMotion = usePrefersReducedMotion();
778
- const [visibleText, setVisibleText] = useState(() => {
779
- if (!streaming || prefersReducedMotion)
780
- return targetText;
781
- const graphemes = splitStreamingTextGraphemes(targetText);
782
- return sliceGraphemes(targetText, graphemes, initialSmoothStreamingGraphemeCount(graphemes));
783
- });
784
- const visibleTextRef = useRef(visibleText);
785
- const visibleCountRef = useRef(splitStreamingTextGraphemes(visibleText).length);
786
- const targetTextRef = useRef(targetText);
787
- const targetGraphemesRef = useRef(splitStreamingTextGraphemes(targetText));
788
- const frameRef = useRef(null);
789
- const lastCommitAtRef = useRef(0);
790
- const pauseUntilRef = useRef(0);
791
- const resetKeyRef = useRef(resetKey);
792
- const stepRef = useRef(() => { });
793
- const commitVisibleCount = useCallback((nextCount) => {
794
- const graphemes = targetGraphemesRef.current;
795
- const boundedCount = Math.max(0, Math.min(nextCount, graphemes.length));
796
- const nextText = sliceGraphemes(targetTextRef.current, graphemes, boundedCount);
797
- visibleCountRef.current = boundedCount;
798
- if (visibleTextRef.current !== nextText) {
799
- visibleTextRef.current = nextText;
800
- setVisibleText(nextText);
801
- }
802
- }, []);
803
- const cancelFrame = useCallback(() => {
804
- if (frameRef.current != null &&
805
- typeof window !== "undefined" &&
806
- typeof window.cancelAnimationFrame === "function") {
807
- window.cancelAnimationFrame(frameRef.current);
808
- }
809
- frameRef.current = null;
810
- pauseUntilRef.current = 0;
811
- }, []);
812
- const scheduleFrame = useCallback(() => {
813
- if (frameRef.current != null)
814
- return;
815
- if (typeof window === "undefined" ||
816
- typeof window.requestAnimationFrame !== "function") {
817
- commitVisibleCount(targetGraphemesRef.current.length);
818
- return;
819
- }
820
- frameRef.current = window.requestAnimationFrame((time) => {
821
- frameRef.current = null;
822
- stepRef.current(time);
823
- });
824
- }, [commitVisibleCount]);
825
- stepRef.current = (time) => {
826
- const targetGraphemes = targetGraphemesRef.current;
827
- const backlog = targetGraphemes.length - visibleCountRef.current;
828
- if (backlog <= 0) {
829
- pauseUntilRef.current = 0;
830
- return;
831
- }
832
- if (pauseUntilRef.current > time) {
833
- scheduleFrame();
834
- return;
835
- }
836
- const lastCommitAt = lastCommitAtRef.current || time - SMOOTH_STREAMING_COMMIT_INTERVAL_MS;
837
- if (time - lastCommitAt < SMOOTH_STREAMING_COMMIT_INTERVAL_MS &&
838
- backlog > 1) {
839
- scheduleFrame();
840
- return;
841
- }
842
- const revealCount = smoothStreamingRevealCount({
843
- backlog,
844
- elapsedMs: Math.min(120, Math.max(8, time - lastCommitAt)),
845
- });
846
- if (revealCount > 0) {
847
- const nextCount = visibleCountRef.current + revealCount;
848
- commitVisibleCount(nextCount);
849
- lastCommitAtRef.current = time;
850
- const nextBacklog = targetGraphemes.length - visibleCountRef.current;
851
- const pauseMs = smoothStreamingPunctuationDelayMs(targetGraphemes[visibleCountRef.current - 1], nextBacklog);
852
- pauseUntilRef.current = pauseMs > 0 ? time + pauseMs : 0;
853
- }
854
- if (visibleCountRef.current < targetGraphemes.length) {
855
- scheduleFrame();
856
- }
857
- else {
858
- pauseUntilRef.current = 0;
859
- }
860
- };
861
- useEffect(() => {
862
- const targetGraphemes = splitStreamingTextGraphemes(targetText);
863
- targetTextRef.current = targetText;
864
- targetGraphemesRef.current = targetGraphemes;
865
- const keyChanged = resetKeyRef.current !== resetKey;
866
- resetKeyRef.current = resetKey;
867
- if (!streaming || prefersReducedMotion) {
868
- cancelFrame();
869
- commitVisibleCount(targetGraphemes.length);
870
- return;
871
- }
872
- const visibleNoLongerMatchesTarget = visibleTextRef.current.length > 0 &&
873
- !targetText.startsWith(visibleTextRef.current);
874
- if (visibleNoLongerMatchesTarget ||
875
- visibleCountRef.current > targetGraphemes.length ||
876
- (keyChanged && visibleTextRef.current.length === 0)) {
877
- commitVisibleCount(initialSmoothStreamingGraphemeCount(targetGraphemes));
878
- lastCommitAtRef.current = 0;
879
- pauseUntilRef.current = 0;
880
- }
881
- if (visibleCountRef.current < targetGraphemes.length) {
882
- scheduleFrame();
883
- }
884
- }, [
885
- targetText,
886
- streaming,
887
- prefersReducedMotion,
888
- resetKey,
889
- cancelFrame,
890
- commitVisibleCount,
891
- scheduleFrame,
892
- ]);
893
- useEffect(() => cancelFrame, [cancelFrame]);
894
- return visibleText;
895
- }
896
- function SmoothMarkdownText({ text, streaming, resetKey, statusType = "complete", }) {
897
- useEffect(() => {
898
- injectMarkdownStyles();
899
- }, []);
900
- const visibleText = useSmoothStreamingText(text, streaming, resetKey);
901
- const isVisuallyStreaming = streaming && visibleText !== text;
902
- return (_jsx("div", { className: "agent-markdown break-words", "data-status": statusType, "data-streaming": isVisuallyStreaming ? "true" : undefined, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: visibleText }) }));
903
- }
904
- function MarkdownText() {
905
- const textPart = useMessagePartText();
906
- const messageRuntime = useMessageRuntime();
907
- const message = messageRuntime.getState();
908
- const thread = useThread();
909
- const textStreaming = React.useContext(TextStreamingContext);
910
- const lastMessage = thread.messages[thread.messages.length - 1];
911
- const isLastAssistantMessage = message.role === "assistant" && lastMessage?.id === message.id;
912
- const statusType = textPart.status?.type ?? message.status?.type ?? "complete";
913
- return (_jsx(SmoothMarkdownText, { text: textPart.text, streaming: textStreaming && isLastAssistantMessage, resetKey: `${message.id}:${statusType}`, statusType: statusType }));
914
- }
915
109
  // ─── Composer Attachment Preview ─────────────────────────────────────────────
916
110
  function getImageAttachmentSrc(attachment) {
917
111
  if (attachment.type !== "image")
918
112
  return null;
113
+ // Prefer the hosted URL when the server already uploaded this attachment.
114
+ const uploadUrl = attachment.metadata?.uploadUrl;
115
+ if (uploadUrl)
116
+ return uploadUrl;
919
117
  if ("file" in attachment && attachment.file) {
920
118
  return URL.createObjectURL(attachment.file);
921
119
  }
@@ -953,716 +151,6 @@ function ComposerAttachmentPreviewStrip() {
953
151
  return null;
954
152
  return (_jsx("div", { className: "flex flex-wrap gap-2 px-2 pt-2", children: attachments.map((attachment) => (_jsx(ComposerAttachmentPreviewCard, { attachment: attachment, onRemove: handleRemove }, attachment.id))) }));
955
153
  }
956
- // Provides the parent's combined running state to tool-call renderers so they
957
- // can stop spinning when the user clicks stop. `thread.isRunning` alone misses
958
- // the force-stopped case; `part.result === undefined` alone ignores stop.
959
- const ChatRunningContext = React.createContext(false);
960
- function stringifyToolValue(value, pretty = false) {
961
- if (typeof value === "string")
962
- return value;
963
- try {
964
- return JSON.stringify(value, null, pretty ? 2 : 0);
965
- }
966
- catch {
967
- return String(value ?? "");
968
- }
969
- }
970
- function looksLikeSql(text) {
971
- return /^\s*(select|with|insert|update|delete|merge|create|alter|drop|explain|declare|begin)\b/i.test(text);
972
- }
973
- function parseJsonText(text) {
974
- const trimmed = text.trim();
975
- if (!trimmed || !/^[{[]/.test(trimmed))
976
- return null;
977
- try {
978
- return JSON.parse(trimmed);
979
- }
980
- catch {
981
- return null;
982
- }
983
- }
984
- function inferToolTextLanguage(text, key, toolName) {
985
- const keyName = (key ?? "").toLowerCase();
986
- const tool = (toolName ?? "").toLowerCase();
987
- if (keyName === "sql" ||
988
- keyName.endsWith("sql") ||
989
- keyName === "query" ||
990
- tool.includes("bigquery") ||
991
- tool.includes("db-query") ||
992
- looksLikeSql(text)) {
993
- return "sql";
994
- }
995
- return parseJsonText(text) ? "json" : "text";
996
- }
997
- function formatToolTextValue(value, key, toolName) {
998
- if (typeof value === "string") {
999
- const parsed = parseJsonText(value);
1000
- if (parsed) {
1001
- return { text: JSON.stringify(parsed, null, 2), lang: "json" };
1002
- }
1003
- return {
1004
- text: value,
1005
- lang: inferToolTextLanguage(value, key, toolName),
1006
- };
1007
- }
1008
- return { text: stringifyToolValue(value, true), lang: "json" };
1009
- }
1010
- function toolInputPayload(toolName, args) {
1011
- const entries = Object.entries(args);
1012
- if (entries.length === 0)
1013
- return null;
1014
- if (entries.length === 1) {
1015
- const [key, value] = entries[0];
1016
- const formatted = formatToolTextValue(value, key, toolName);
1017
- const normalizedKey = key.toLowerCase();
1018
- const keyLabel = normalizedKey === "sql" || normalizedKey.endsWith("sql") ? "SQL" : key;
1019
- return {
1020
- section: "input",
1021
- title: `Input - ${keyLabel}`,
1022
- text: formatted.text,
1023
- copyText: typeof value === "string" ? value : stringifyToolValue(value, true),
1024
- lang: formatted.lang,
1025
- };
1026
- }
1027
- return {
1028
- section: "input",
1029
- title: "Input",
1030
- text: JSON.stringify(args, null, 2),
1031
- copyText: JSON.stringify(args, null, 2),
1032
- lang: "json",
1033
- };
1034
- }
1035
- function toolResultPayload(result) {
1036
- if (result === undefined)
1037
- return null;
1038
- const formatted = formatToolTextValue(result);
1039
- return {
1040
- section: "result",
1041
- title: "Result",
1042
- text: formatted.text,
1043
- copyText: result,
1044
- lang: formatted.lang,
1045
- };
1046
- }
1047
- function escapeRegExp(value) {
1048
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1049
- }
1050
- function countTextMatches(text, query) {
1051
- const needle = query.trim();
1052
- if (!needle)
1053
- return 0;
1054
- return Array.from(text.matchAll(new RegExp(escapeRegExp(needle), "gi")))
1055
- .length;
1056
- }
1057
- function renderHighlightedSearchText(text, query) {
1058
- const needle = query.trim();
1059
- if (!needle)
1060
- return text;
1061
- const regex = new RegExp(escapeRegExp(needle), "gi");
1062
- const parts = [];
1063
- let lastIndex = 0;
1064
- let match;
1065
- while ((match = regex.exec(text))) {
1066
- if (match.index > lastIndex) {
1067
- parts.push(text.slice(lastIndex, match.index));
1068
- }
1069
- parts.push(_jsx("mark", { children: match[0] }, `${match.index}-${match[0]}`));
1070
- lastIndex = match.index + match[0].length;
1071
- if (match[0].length === 0)
1072
- regex.lastIndex += 1;
1073
- }
1074
- if (lastIndex < text.length)
1075
- parts.push(text.slice(lastIndex));
1076
- return parts;
1077
- }
1078
- function ToolDetailViewer({ payload }) {
1079
- const [expanded, setExpanded] = useState(false);
1080
- const [searchOpen, setSearchOpen] = useState(false);
1081
- const [search, setSearch] = useState("");
1082
- const [copied, setCopied] = useState(false);
1083
- const copyResetRef = useRef(null);
1084
- const matchCount = useMemo(() => countTextMatches(payload.text, search), [payload.text, search]);
1085
- useEffect(() => {
1086
- return () => {
1087
- if (copyResetRef.current)
1088
- clearTimeout(copyResetRef.current);
1089
- };
1090
- }, []);
1091
- const copyValue = useCallback(async () => {
1092
- try {
1093
- if (await writeClipboardText(payload.copyText)) {
1094
- setCopied(true);
1095
- if (copyResetRef.current)
1096
- clearTimeout(copyResetRef.current);
1097
- copyResetRef.current = setTimeout(() => setCopied(false), 1200);
1098
- }
1099
- }
1100
- catch {
1101
- // Clipboard failures should not interrupt chat rendering.
1102
- }
1103
- }, [payload.copyText]);
1104
- return (_jsxs("div", { className: "rounded-md border border-border/50 bg-background/60", children: [_jsxs("div", { className: "flex min-h-9 flex-wrap items-center gap-2 border-b border-border/50 px-2.5 py-1.5", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "truncate text-[11px] font-medium text-foreground/85", children: payload.title }), payload.lang !== "text" && (_jsx("span", { className: "shrink-0 rounded border border-border/60 px-1 py-0.5 font-mono text-[9px] uppercase leading-none text-muted-foreground", children: payload.lang }))] }) }), _jsx("button", { type: "button", onClick: () => setSearchOpen((v) => !v), "aria-label": `Search ${payload.title.toLowerCase()}`, "aria-pressed": searchOpen, className: cn("inline-flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", searchOpen && "bg-accent text-foreground"), children: _jsx(IconSearch, { size: 12 }) }), _jsx("button", { type: "button", onClick: () => setExpanded((v) => !v), "aria-label": expanded ? "Shrink code viewer" : "Expand code viewer", "aria-pressed": expanded, className: "inline-flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: expanded ? (_jsx(IconArrowsMinimize, { size: 12 })) : (_jsx(IconArrowsMaximize, { size: 12 })) }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-6 items-center gap-1 rounded-md px-1.5 font-sans text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 12 }) : _jsx(IconCopy, { size: 12 }), copied ? "Copied" : "Copy"] })] }), searchOpen && (_jsxs("div", { className: "flex items-center gap-2 border-b border-border/50 px-2.5 py-2", children: [_jsx("input", { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Find", className: "h-7 min-w-0 flex-1 rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:ring-1 focus:ring-ring" }), _jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: search.trim() ? matchCount : "" })] })), _jsx("div", { className: cn("agent-tool-code overflow-auto font-mono text-[11px] leading-relaxed text-foreground", expanded ? "max-h-[70vh]" : "max-h-72"), children: search.trim() ? (_jsx("pre", { children: _jsx("code", { children: renderHighlightedSearchText(payload.text, search) }) })) : (_jsx(HighlightedCodeBlock, { code: payload.text, lang: payload.lang })) })] }));
1105
- }
1106
- function ToolCallDisplay({ toolName, argsText, args, result, mcpApp, isRunning, }) {
1107
- const streamRef = useRef(null);
1108
- const isAgentCall = toolName.startsWith("agent:");
1109
- const [expanded, setExpanded] = useState(isAgentCall);
1110
- const agentName = isAgentCall ? toolName.slice(6) : null;
1111
- const isAgentError = isAgentCall && result === "Error calling agent";
1112
- const agentStreamText = isAgentCall ? (argsText ?? "") : "";
1113
- const hasStreamText = agentStreamText.length > 0;
1114
- const hasArgs = !isAgentCall && Object.keys(args).length > 0;
1115
- // NOTE: All hooks must be above any conditional returns
1116
- useEffect(() => {
1117
- if (isAgentCall && isRunning && streamRef.current) {
1118
- streamRef.current.scrollTop = streamRef.current.scrollHeight;
1119
- }
1120
- }, [agentStreamText, isAgentCall, isRunning]);
1121
- // Render connect-builder as ConnectBuilderCard once the result is available
1122
- if (toolName === "connect-builder" && result) {
1123
- try {
1124
- const parsed = JSON.parse(result);
1125
- if (parsed?.kind === "connect-builder-card") {
1126
- return (_jsx(ConnectBuilderCard, { configured: !!parsed.configured, builderEnabled: parsed.builderEnabled !== false,
1127
- // Ignore saved cliAuthUrl values from older tool results. They
1128
- // contain signed callback state and can expire while a chat sits
1129
- // open; the card's hook fetches a fresh signed URL on mount/click.
1130
- connectUrl: parsed.connectUrl || "", orgName: parsed.orgName ?? null, prompt: typeof parsed.prompt === "string" ? parsed.prompt : "" }));
1131
- }
1132
- }
1133
- catch {
1134
- // fall through to default pill rendering
1135
- }
1136
- }
1137
- // Render agent-teams spawn as AgentTaskCard once the result is available
1138
- if (toolName === "agent-teams" &&
1139
- args?.action === "spawn" &&
1140
- result) {
1141
- try {
1142
- const parsed = JSON.parse(result);
1143
- if (parsed.taskId && parsed.threadId) {
1144
- return (_jsx(AgentTaskCard, { taskId: parsed.taskId, threadId: parsed.threadId, description: parsed.description ||
1145
- args?.task ||
1146
- "Sub-agent task", onOpen: (tid) => {
1147
- window.dispatchEvent(new CustomEvent("agent-task-open", {
1148
- detail: {
1149
- threadId: tid,
1150
- description: parsed.description ||
1151
- args?.task ||
1152
- "",
1153
- name: parsed.name || "",
1154
- },
1155
- }));
1156
- } }));
1157
- }
1158
- }
1159
- catch {
1160
- // Fall through to default pill rendering
1161
- }
1162
- }
1163
- const inputPayload = hasArgs ? toolInputPayload(toolName, args) : null;
1164
- const resultPayload = toolResultPayload(result);
1165
- const displayName = isAgentCall
1166
- ? isRunning
1167
- ? `Asking ${agentName}...`
1168
- : isAgentError
1169
- ? `Error asking ${agentName}`
1170
- : `Asked ${agentName}`
1171
- : humanizeToolName(toolName);
1172
- const canExpand = isAgentCall
1173
- ? hasStreamText
1174
- : hasArgs || result !== undefined;
1175
- const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
1176
- return (_jsxs("div", { className: "my-1 overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex items-center gap-2 rounded-md px-2.5 py-1.5 text-xs font-mono w-full text-left overflow-hidden", isRunning
1177
- ? "bg-muted text-muted-foreground"
1178
- : "bg-muted text-muted-foreground hover:bg-accent"), children: [_jsx("span", { className: "shrink-0", children: isRunning ? (_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "h-3 w-3 text-destructive" })) : result !== undefined ? (_jsx(IconCheck, { className: "h-3 w-3 text-emerald-500" })) : (_jsx(IconSquareFilled, { className: "h-3 w-3 text-muted-foreground" })) }), _jsx("span", { className: "truncate min-w-0", children: _jsx("span", { className: "font-medium", children: displayName }) }), canExpand && (_jsx(IconChevronDown, { className: cn("ml-auto h-3 w-3 shrink-0 opacity-40", isExpanded && "rotate-180") }))] }), isExpanded && isAgentCall && hasStreamText && (_jsx("div", { ref: streamRef, className: "mt-1 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground break-words max-h-48 overflow-y-auto agent-markdown prose prose-sm prose-invert max-w-none", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: agentStreamText }) })), isExpanded && !isAgentCall && (hasArgs || result !== undefined) && (_jsxs("div", { className: "mt-1 space-y-2 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground", children: [inputPayload && _jsx(ToolDetailViewer, { payload: inputPayload }), resultPayload && _jsx(ToolDetailViewer, { payload: resultPayload })] }))] }));
1179
- }
1180
- function ToolCallFallback({ toolName, args, argsText, result, ...rest }) {
1181
- const chatRunning = React.useContext(ChatRunningContext);
1182
- const isRunning = result === undefined && chatRunning;
1183
- return (_jsx(ToolCallDisplay, { toolName: toolName, args: args, argsText: argsText, result: typeof result === "string"
1184
- ? result
1185
- : result !== undefined
1186
- ? JSON.stringify(result)
1187
- : undefined, mcpApp: rest.mcpApp, isRunning: isRunning }));
1188
- }
1189
- // ─── Reconnect Stream Message ───────────────────────────────────────────────
1190
- // Renders the agent's in-progress response during reconnection (outside
1191
- // assistant-ui's runtime). Uses the same visual styling as normal messages.
1192
- function ReconnectStreamMessage({ content }) {
1193
- const chatRunning = React.useContext(ChatRunningContext);
1194
- return (_jsx("div", { className: "flex justify-start", children: _jsx("div", { className: "max-w-[95%] text-sm leading-relaxed text-foreground space-y-1", children: content.map((part, i) => {
1195
- if (part.type === "text") {
1196
- return (_jsx(SmoothMarkdownText, { text: part.text, streaming: chatRunning, resetKey: `reconnect-text-${i}`, statusType: chatRunning ? "running" : "complete" }, `reconnect-text-${i}`));
1197
- }
1198
- if (part.type === "tool-call") {
1199
- return (_jsx(ToolCallDisplay, { toolName: part.toolName, argsText: part.argsText, args: part.args, result: part.result, mcpApp: part.mcpApp, isRunning: part.result === undefined && chatRunning }, `reconnect-tool-${i}`));
1200
- }
1201
- return null;
1202
- }) }) }));
1203
- }
1204
- // ─── Message Components ─────────────────────────────────────────────────────
1205
- const mentionIconProps = {
1206
- size: 14,
1207
- className: "shrink-0 text-muted-foreground",
1208
- };
1209
- function MentionChipIcon({ icon }) {
1210
- switch (icon) {
1211
- case "folder":
1212
- return _jsx(IconFolder, { ...mentionIconProps });
1213
- case "document":
1214
- return _jsx(IconFileText, { ...mentionIconProps });
1215
- case "form":
1216
- return _jsx(IconCheckbox, { ...mentionIconProps });
1217
- case "email":
1218
- return _jsx(IconMail, { ...mentionIconProps });
1219
- case "user":
1220
- return _jsx(IconUser, { ...mentionIconProps });
1221
- case "deck":
1222
- return _jsx(IconPresentation, { ...mentionIconProps });
1223
- case "agent":
1224
- return _jsx(IconMessageChatbot, { ...mentionIconProps });
1225
- case "file":
1226
- return _jsx(IconFile, { ...mentionIconProps });
1227
- default:
1228
- return _jsx(IconStack2, { ...mentionIconProps });
1229
- }
1230
- }
1231
- // Matches rich mention format: @[label|icon] or plain @word
1232
- const richMentionPattern = /@\[([^\]|]+)\|([^\]]+)\]/g;
1233
- const plainMentionPattern = /((?:^|(?<=\s))@(\w+))/g;
1234
- function UserMessageText({ text }) {
1235
- // Strip injected <context>...</context> blocks before display
1236
- const displayText = displayableUserMessageText(text);
1237
- const parts = [];
1238
- let lastIndex = 0;
1239
- let match;
1240
- let hasRichMentions = false;
1241
- // First try rich mentions (@[label|icon])
1242
- richMentionPattern.lastIndex = 0;
1243
- while ((match = richMentionPattern.exec(displayText)) !== null) {
1244
- hasRichMentions = true;
1245
- const matchStart = match.index;
1246
- if (matchStart > lastIndex) {
1247
- parts.push(displayText.slice(lastIndex, matchStart));
1248
- }
1249
- const label = match[1];
1250
- const icon = match[2];
1251
- parts.push(_jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-input bg-muted/50 px-1.5 py-0.5 text-xs font-medium text-foreground align-middle mx-0.5 max-w-[200px] select-all", "data-mention-label": label, children: [_jsx(MentionChipIcon, { icon: icon }), _jsx("span", { className: "truncate", children: label })] }, matchStart));
1252
- lastIndex = matchStart + match[0].length;
1253
- }
1254
- if (hasRichMentions) {
1255
- if (lastIndex < displayText.length) {
1256
- parts.push(displayText.slice(lastIndex));
1257
- }
1258
- return _jsx(_Fragment, { children: parts });
1259
- }
1260
- // Fallback: plain @word mentions (for older messages)
1261
- plainMentionPattern.lastIndex = 0;
1262
- while ((match = plainMentionPattern.exec(displayText)) !== null) {
1263
- const matchStart = match.index;
1264
- if (matchStart > lastIndex) {
1265
- parts.push(displayText.slice(lastIndex, matchStart));
1266
- }
1267
- const mentionName = match[2];
1268
- parts.push(_jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-input bg-muted/50 px-1.5 py-0.5 text-xs font-medium text-foreground align-middle mx-0.5 select-all", "data-mention-label": mentionName, children: ["@", mentionName] }, matchStart));
1269
- lastIndex = matchStart + match[0].length;
1270
- }
1271
- if (lastIndex < displayText.length) {
1272
- parts.push(displayText.slice(lastIndex));
1273
- }
1274
- return _jsx(_Fragment, { children: parts.length > 0 ? parts : displayText });
1275
- }
1276
- export function displayableUserMessageText(text) {
1277
- return text.replace(/<context>[\s\S]*?<\/context>\n?/g, "").trim();
1278
- }
1279
- function UserMessageAttachments() {
1280
- const messageRuntime = useMessageRuntime();
1281
- const msg = messageRuntime.getState();
1282
- // assistant-ui stores user attachments on msg.attachments (separate from content).
1283
- // Each attachment has: { id, type, name, contentType?, content: MessagePart[] }.
1284
- // Image adapters put a {type:"image", image:"data:..."} part in content; text
1285
- // adapters put a {type:"text", text:"<attachment>..."} part. Fall back to a
1286
- // file chip when there's no inline image.
1287
- const attachments = msg
1288
- .attachments;
1289
- if (!attachments || attachments.length === 0)
1290
- return null;
1291
- return (_jsx("div", { className: "flex flex-wrap justify-end gap-1.5 mb-1.5", children: attachments.map((att) => {
1292
- if (isPastedTextAttachmentName(att.name)) {
1293
- return _jsx(PastedTextChip, { attachment: att, compact: true }, att.id);
1294
- }
1295
- const imagePart = att.content?.find((p) => p.type === "image" && "image" in p && !!p.image);
1296
- if (imagePart) {
1297
- return (_jsx("div", { className: "h-16 w-16 overflow-hidden rounded-lg border border-border/70 bg-muted/50", title: att.name, children: _jsx("img", { src: imagePart.image, alt: att.name, className: "h-full w-full object-cover" }) }, att.id));
1298
- }
1299
- return (_jsxs("div", { className: "flex items-center gap-1.5 rounded-lg border border-border/70 bg-muted/50 px-2 py-1.5 text-xs text-muted-foreground", title: att.name, children: [_jsx(IconFile, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate max-w-[120px]", children: att.name || "file" })] }, att.id));
1300
- }) }));
1301
- }
1302
- function UserMessage() {
1303
- const [expanded, setExpanded] = useState(false);
1304
- const [isExpandable, setIsExpandable] = useState(false);
1305
- const contentRef = useRef(null);
1306
- const messageRuntime = useMessageRuntime();
1307
- const message = messageRuntime.getState();
1308
- const timestamp = formatMessageTimestamp(message.createdAt);
1309
- const hasDisplayableText = message.content
1310
- ?.filter((part) => {
1311
- return part.type === "text" && typeof part.text === "string";
1312
- })
1313
- .some((part) => displayableUserMessageText(part.text).length > 0) ??
1314
- false;
1315
- useEffect(() => {
1316
- const el = contentRef.current;
1317
- if (!el || !hasDisplayableText)
1318
- return;
1319
- const measure = () => {
1320
- setIsExpandable(el.scrollHeight > 200);
1321
- };
1322
- measure();
1323
- const observer = new ResizeObserver(measure);
1324
- observer.observe(el);
1325
- return () => observer.disconnect();
1326
- }, [hasDisplayableText]);
1327
- return (_jsx("div", { className: "group flex justify-end", style: { contentVisibility: "auto" }, children: _jsxs("div", { className: "max-w-[85%]", children: [_jsx(UserMessageAttachments, {}), hasDisplayableText && (_jsxs("div", { className: "relative rounded-lg bg-accent px-3 py-2 text-sm leading-relaxed text-foreground", onCopy: (e) => {
1328
- const selection = window.getSelection();
1329
- if (!selection || selection.rangeCount === 0)
1330
- return;
1331
- const fragment = selection.getRangeAt(0).cloneContents();
1332
- const mentions = fragment.querySelectorAll("[data-mention-label]");
1333
- if (mentions.length === 0)
1334
- return;
1335
- e.preventDefault();
1336
- mentions.forEach((el) => {
1337
- el.textContent = `@${el.getAttribute("data-mention-label")}`;
1338
- });
1339
- const div = document.createElement("div");
1340
- div.appendChild(fragment);
1341
- e.clipboardData.setData("text/plain", div.textContent || "");
1342
- }, children: [_jsx("div", { ref: contentRef, className: cn("whitespace-pre-wrap break-words", !expanded && isExpandable && "max-h-[200px] overflow-hidden"), children: _jsx(MessagePrimitive.Parts, { components: {
1343
- Text: UserMessageText,
1344
- } }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" }))] })), hasDisplayableText && isExpandable && (_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", expanded && "rotate-180") }), expanded ? "Collapse" : "Expand"] })), timestamp && (_jsx("div", { className: "mt-1 flex justify-end", children: _jsx(MessageTimestamp, { timestamp: timestamp, className: "opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100" }) }))] }) }));
1345
- }
1346
- const CheckpointContext = React.createContext(null);
1347
- const MessageActionsContext = React.createContext(null);
1348
- function MessageActionsMenu({ showRevert, onRevert, } = {}) {
1349
- const [open, setOpen] = useState(false);
1350
- const [copied, setCopied] = useState(null);
1351
- const messageRuntime = useMessageRuntime();
1352
- const actionsCtx = React.useContext(MessageActionsContext);
1353
- const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt);
1354
- const handleCopyMessage = useCallback(() => {
1355
- const m = messageRuntime.getState();
1356
- const text = m.content
1357
- .filter((p) => p.type === "text")
1358
- .map((p) => p.text)
1359
- .join("\n");
1360
- void writeClipboardText(text).then((ok) => {
1361
- if (!ok)
1362
- return;
1363
- setCopied("message");
1364
- setTimeout(() => {
1365
- setCopied(null);
1366
- setOpen(false);
1367
- }, 1000);
1368
- });
1369
- }, [messageRuntime]);
1370
- const handleCopyRequestId = useCallback(() => {
1371
- const m = messageRuntime.getState();
1372
- const meta = m.metadata;
1373
- // Live yields put the trace ID at metadata.custom.runId; server-persisted
1374
- // messages put it at metadata.runId. If neither is present (e.g. the run
1375
- // is still in flight and this is the first message), fall back to the
1376
- // active-run state so a hung / mid-stream chat still surfaces a usable
1377
- // trace ID. Last resort is the assistant-ui local message id.
1378
- const runId = (typeof meta?.custom?.runId === "string" && meta.custom.runId) ||
1379
- (typeof meta?.runId === "string" && meta.runId) ||
1380
- (typeof window !== "undefined" ? getActiveRun()?.runId : null) ||
1381
- m.id ||
1382
- "";
1383
- void writeClipboardText(runId).then((ok) => {
1384
- if (!ok)
1385
- return;
1386
- setCopied("id");
1387
- setTimeout(() => {
1388
- setCopied(null);
1389
- setOpen(false);
1390
- }, 1000);
1391
- });
1392
- }, [messageRuntime]);
1393
- const handleForkChat = useCallback(() => {
1394
- setOpen(false);
1395
- actionsCtx?.onForkChat?.();
1396
- }, [actionsCtx]);
1397
- const handleRevert = useCallback(() => {
1398
- setOpen(false);
1399
- onRevert?.();
1400
- }, [onRevert]);
1401
- return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Message actions", className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), "Fork Chat"] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
1402
- e.preventDefault();
1403
- handleCopyMessage();
1404
- }, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message" ? "Copied!" : "Copy Message"] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
1405
- e.preventDefault();
1406
- handleCopyRequestId();
1407
- }, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id" ? "Copied!" : "Copy Request ID"] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), "Revert to here"] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: ["Sent ", timestamp.short] })] }))] })] }));
1408
- }
1409
- function AssistantMessage() {
1410
- const [restoreState, setRestoreState] = useState("idle");
1411
- const messageRuntime = useMessageRuntime();
1412
- const thread = useThread();
1413
- const chatRunning = React.useContext(ChatRunningContext);
1414
- const msg = messageRuntime.getState();
1415
- const timestamp = formatMessageTimestamp(msg.createdAt);
1416
- const isLast = thread.messages.length > 0 &&
1417
- thread.messages[thread.messages.length - 1].id === msg.id;
1418
- const isComplete = !isLast || !chatRunning;
1419
- const cpCtx = React.useContext(CheckpointContext);
1420
- const handleRestore = useCallback(async () => {
1421
- if (restoreState === "idle") {
1422
- setRestoreState("confirming");
1423
- return;
1424
- }
1425
- if (restoreState !== "confirming" || !cpCtx)
1426
- return;
1427
- setRestoreState("restoring");
1428
- try {
1429
- const m = messageRuntime.getState();
1430
- const meta = m.metadata;
1431
- const runId = (typeof meta?.custom?.runId === "string" && meta.custom.runId) ||
1432
- (typeof meta?.runId === "string" && meta.runId) ||
1433
- null;
1434
- if (!runId) {
1435
- setRestoreState("idle");
1436
- return;
1437
- }
1438
- const tid = cpCtx.threadId || "";
1439
- const res = await fetch(`${cpCtx.apiUrl}/checkpoints?threadId=${encodeURIComponent(tid)}`);
1440
- const checkpoints = res.ok ? await res.json() : [];
1441
- const checkpoint = checkpoints.find((cp) => cp.runId === runId);
1442
- if (!checkpoint) {
1443
- setRestoreState("idle");
1444
- return;
1445
- }
1446
- const restoreRes = await fetch(`${cpCtx.apiUrl}/checkpoints/restore`, {
1447
- method: "POST",
1448
- headers: { "Content-Type": "application/json" },
1449
- body: JSON.stringify({ checkpointId: checkpoint.id }),
1450
- });
1451
- if (restoreRes.ok) {
1452
- window.location.reload();
1453
- }
1454
- else {
1455
- setRestoreState("idle");
1456
- }
1457
- }
1458
- catch {
1459
- setRestoreState("idle");
1460
- }
1461
- }, [restoreState, cpCtx, messageRuntime]);
1462
- const cancelRestore = useCallback(() => {
1463
- setRestoreState("idle");
1464
- }, []);
1465
- const showRestore = cpCtx?.devMode && isComplete && !isLast;
1466
- return (_jsxs("div", { className: "group relative", style: { contentVisibility: isComplete ? "auto" : "visible" }, children: [_jsx("div", { className: "max-w-[95%] text-sm leading-relaxed text-foreground", children: _jsx(MessagePrimitive.Parts, { components: {
1467
- Text: MarkdownText,
1468
- tools: {
1469
- Fallback: ToolCallFallback,
1470
- },
1471
- } }) }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore }), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: "opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100" }))] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: "Restore to here?" }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Cancel" })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), "Restoring..."] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: (() => {
1472
- const meta = messageRuntime.getState().metadata;
1473
- return ((typeof meta?.custom?.runId === "string" &&
1474
- meta.custom.runId) ||
1475
- (typeof meta?.runId === "string" && meta.runId) ||
1476
- "");
1477
- })(), messageSeq: thread.messages.findIndex((m) => m.id === msg.id) }))] }))] }));
1478
- }
1479
- // ─── Thinking Indicator ─────────────────────────────────────────────────────
1480
- function RunningActivityStatus({ label }) {
1481
- return (_jsx("div", { className: "agent-running-activity shrink-0 px-4 pb-2", children: _jsx(ThinkingIndicator, { label: label }) }));
1482
- }
1483
- function ThinkingIndicator({ label = "Thinking" } = {}) {
1484
- const [dots, setDots] = useState(0);
1485
- useEffect(() => {
1486
- const interval = setInterval(() => {
1487
- setDots((d) => (d + 1) % 4);
1488
- }, 400);
1489
- return () => clearInterval(interval);
1490
- }, []);
1491
- return (_jsx("div", { className: "flex items-center text-muted-foreground", children: _jsxs("span", { className: "text-xs", children: [label, ".".repeat(dots)] }) }));
1492
- }
1493
- // ─── Builder.io Connect CTA (shared by setup + usage-limit cards) ───────────
1494
- //
1495
- // Renders a single row with left-aligned copy and a right-aligned action.
1496
- // Click opens the Builder CLI-auth popup via the shared
1497
- // `useBuilderConnectFlow` hook (which owns the synchronous window.open,
1498
- // the 2s status poll, and the focus-refresh). On success the hook broadcasts
1499
- // a config-change event and this card clears its local `missingApiKey` gate
1500
- // so the user can start chatting without a full-page reload.
1501
- //
1502
- // Desktop note: when this component runs inside the Electron shell, the
1503
- // window.open call is intercepted by the main process's webview popup handler,
1504
- // which opens the flow in an Electron BrowserWindow that shares the webview's
1505
- // session. See packages/desktop-app/src/main/index.ts.
1506
- function BuilderConnectCta({ variant = "primary", onConnected, }) {
1507
- const { configured, orgName, connecting, error, start } = useBuilderConnectFlow({
1508
- trackingSource: "assistant_chat_builder_cta",
1509
- onConnected,
1510
- });
1511
- const containerClass = variant === "compact"
1512
- ? "rounded-md border border-border px-3 py-2.5"
1513
- : "flex items-center gap-3 rounded-md border border-border px-3 py-3";
1514
- if (configured) {
1515
- return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5", children: orgName ? `Connected — ${orgName}` : "Connected" })] }), _jsxs("span", { className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-emerald-500/10 px-2 py-0.5 text-[10px] font-medium text-emerald-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })] }));
1516
- }
1517
- return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Connect Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 max-w-[220px]", children: "Free credits for LLM, hosting, and more \u2014 no API key needed" }), error && _jsx("p", { className: "mt-1 text-[10px] text-destructive", children: error })] }), _jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-foreground px-3 py-1.5 text-[11px] font-medium no-underline text-background hover:opacity-90 disabled:opacity-60 disabled:cursor-wait", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Waiting\u2026"] })) : (_jsxs(_Fragment, { children: ["Connect", _jsx(IconExternalLink, { size: 10 })] })) })] }));
1518
- }
1519
- // ─── Builder Setup Card ─────────────────────────────────────────────────────
1520
- function BuilderSetupCard({ onConnected, bouncePulse, }) {
1521
- // Progressive disclosure: the card leads with the one-click Builder connect.
1522
- // The bring-your-own-key path stays tucked behind a single link so the chat
1523
- // stays clean for people who connect Builder or never use the side chat at
1524
- // all (they can keep driving the plan from their own coding agent).
1525
- const [keyOpen, setKeyOpen] = useState(false);
1526
- const cardRef = useRef(null);
1527
- // Replay the bounce keyframe each time bouncePulse increments. Toggling the
1528
- // class off-then-on (with a forced reflow) restarts the animation even when
1529
- // the value changes back-to-back.
1530
- useEffect(() => {
1531
- if (!bouncePulse)
1532
- return;
1533
- const el = cardRef.current;
1534
- if (!el)
1535
- return;
1536
- el.classList.remove("animate-bounce-once");
1537
- void el.offsetWidth;
1538
- el.classList.add("animate-bounce-once");
1539
- }, [bouncePulse]);
1540
- return (_jsxs("div", { ref: cardRef, className: "mx-4 my-6 rounded-lg border border-border bg-card p-5", children: [_jsxs("div", { className: "flex items-center gap-3 mb-3", children: [_jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted", children: _jsx(IconMessage, { className: "h-4.5 w-4.5 text-muted-foreground" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-foreground", children: "Turn on the side chat" }), _jsx("p", { className: "mt-0.5 text-[11px] text-muted-foreground", children: "One click to connect Builder for free hosted access \u2014 no API key or account needed." })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx(BuilderConnectCta, { onConnected: onConnected }), keyOpen ? (_jsx(ApiKeyConnect, { onConnected: onConnected })) : (_jsx("div", { className: "text-center", children: _jsx("button", { type: "button", onClick: () => setKeyOpen(true), className: "text-[11px] text-muted-foreground underline-offset-2 hover:text-foreground hover:underline", children: "Or paste your own Anthropic or OpenAI key" }) })), _jsx("p", { className: "text-center text-[11px] leading-relaxed text-muted-foreground", children: "You can skip this and keep editing the plan with your own coding agent." })] })] }));
1541
- }
1542
- // ─── Inline BYOK (Anthropic / OpenAI) ───────────────────────────────────────
1543
- const API_KEY_PROVIDERS = [
1544
- { value: "anthropic", label: "Anthropic", placeholder: "sk-ant-…" },
1545
- { value: "openai", label: "OpenAI", placeholder: "sk-…" },
1546
- ];
1547
- function ApiKeyConnect({ onConnected }) {
1548
- const [provider, setProvider] = useState("anthropic");
1549
- const [apiKey, setApiKey] = useState("");
1550
- const [saving, setSaving] = useState(false);
1551
- const [error, setError] = useState(null);
1552
- const active = API_KEY_PROVIDERS.find((p) => p.value === provider);
1553
- const handleSave = useCallback(async () => {
1554
- if (!apiKey.trim() || saving)
1555
- return;
1556
- setSaving(true);
1557
- setError(null);
1558
- try {
1559
- await saveAgentEngineApiKey({ provider, apiKey });
1560
- setApiKey("");
1561
- onConnected?.();
1562
- }
1563
- catch (err) {
1564
- setError(err instanceof Error ? err.message : "Could not save the key.");
1565
- }
1566
- finally {
1567
- setSaving(false);
1568
- }
1569
- }, [apiKey, onConnected, provider, saving]);
1570
- return (_jsxs("div", { className: "rounded-md border border-border bg-background/60 p-3", children: [_jsxs("div", { className: "mb-2 flex items-center gap-1.5 text-[11px] font-medium text-foreground", children: [_jsx(IconKey, { size: 12, strokeWidth: 1.9 }), "Use your own API key"] }), _jsx("p", { className: "mb-2.5 text-[11px] leading-relaxed text-muted-foreground", children: "Stored locally for this app only \u2014 no account required." }), _jsx("div", { role: "tablist", "aria-label": "API key provider", className: "mb-2 inline-flex rounded-md border border-border bg-muted/40 p-0.5", children: API_KEY_PROVIDERS.map((option) => {
1571
- const selected = option.value === provider;
1572
- return (_jsx("button", { type: "button", role: "tab", "aria-selected": selected, onClick: () => {
1573
- setProvider(option.value);
1574
- setError(null);
1575
- }, className: cn("rounded px-2.5 py-1 text-[11px] font-medium transition-colors", selected
1576
- ? "bg-background text-foreground shadow-sm"
1577
- : "text-muted-foreground hover:text-foreground"), children: option.label }, option.value));
1578
- }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "password", value: apiKey, autoComplete: "off", spellCheck: false, placeholder: active.placeholder, onChange: (e) => {
1579
- setApiKey(e.target.value);
1580
- if (error)
1581
- setError(null);
1582
- }, onKeyDown: (e) => {
1583
- if (e.key === "Enter") {
1584
- e.preventDefault();
1585
- void handleSave();
1586
- }
1587
- }, className: "h-8 min-w-0 flex-1 rounded-md border border-input bg-background px-2.5 text-[12px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background" }), _jsx("button", { type: "button", onClick: handleSave, disabled: !apiKey.trim() || saving, className: "inline-flex h-8 shrink-0 items-center gap-1 rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60", children: saving ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 11, className: "animate-spin" }), "Saving\u2026"] })) : ("Save") })] }), error ? (_jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })) : null] }));
1588
- }
1589
- function getLoopLimitMetadata(message) {
1590
- const meta = message?.metadata;
1591
- const loopLimit = meta?.custom?.loopLimit ?? meta?.loopLimit;
1592
- if (!loopLimit || typeof loopLimit !== "object")
1593
- return null;
1594
- return {
1595
- ...(typeof loopLimit.maxIterations === "number"
1596
- ? { maxIterations: loopLimit.maxIterations }
1597
- : {}),
1598
- };
1599
- }
1600
- function getRunErrorMetadata(message) {
1601
- const meta = message?.metadata;
1602
- const runError = meta?.custom?.runError ?? meta?.runError;
1603
- if (!runError || typeof runError !== "object")
1604
- return null;
1605
- const messageText = typeof runError.message === "string" ? runError.message : "";
1606
- if (!messageText)
1607
- return null;
1608
- const runId = typeof runError.runId === "string"
1609
- ? runError.runId
1610
- : typeof meta?.custom?.runId === "string"
1611
- ? meta.custom.runId
1612
- : typeof meta?.runId === "string"
1613
- ? meta.runId
1614
- : undefined;
1615
- return {
1616
- message: messageText,
1617
- ...(typeof runError.details === "string"
1618
- ? { details: runError.details }
1619
- : {}),
1620
- ...(typeof runError.errorCode === "string"
1621
- ? { errorCode: runError.errorCode }
1622
- : {}),
1623
- ...(runId ? { runId } : {}),
1624
- ...(runError.recoverable ? { recoverable: true } : {}),
1625
- };
1626
- }
1627
- function getRequestModeMetadata(message) {
1628
- const meta = message?.metadata;
1629
- const requestMode = meta?.custom?.requestMode ?? meta?.requestMode;
1630
- return requestMode === "act" || requestMode === "plan" ? requestMode : null;
1631
- }
1632
- function isBuilderReconnectRunError(info) {
1633
- const code = (info.errorCode ?? "").toLowerCase();
1634
- const message = info.message.toLowerCase();
1635
- const isAuthCode = code === "authentication_error" ||
1636
- code === "unauthorized" ||
1637
- code === "http_401" ||
1638
- code === "http_403";
1639
- return (code === "builder_auth_error" ||
1640
- message.includes("builder authentication failed") ||
1641
- (isAuthCode &&
1642
- (message.includes("invalid token") ||
1643
- message.includes("personal access token"))));
1644
- }
1645
- function isProviderQueryRunError(info) {
1646
- const text = [info.errorCode, info.message, info.details]
1647
- .filter(Boolean)
1648
- .join("\n")
1649
- .toLowerCase();
1650
- return (text.includes("bigquery") ||
1651
- text.includes("sql") ||
1652
- text.includes("query") ||
1653
- text.includes("schema") ||
1654
- text.includes("syntax") ||
1655
- text.includes("unknown column") ||
1656
- text.includes("unknown table") ||
1657
- text.includes("type mismatch"));
1658
- }
1659
- function isConnectionRecoveryRunError(info) {
1660
- const code = (info.errorCode ?? "").toLowerCase();
1661
- const message = info.message.toLowerCase();
1662
- return (code === "connection_error" ||
1663
- message.includes("connection kept failing") ||
1664
- message.includes("automatic recovery attempts"));
1665
- }
1666
154
  function getMessageText(message) {
1667
155
  const msg = message?.message ?? message;
1668
156
  const content = msg?.content;
@@ -1754,165 +242,6 @@ export function latestNonRecoveryUserMessageText(messages) {
1754
242
  }
1755
243
  return "";
1756
244
  }
1757
- function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, }) {
1758
- const [detailsOpen, setDetailsOpen] = useState(false);
1759
- const [copied, setCopied] = useState(false);
1760
- const [forking, setForking] = useState(false);
1761
- const [forkError, setForkError] = useState(null);
1762
- const builderReconnect = useBuilderConnectFlow({
1763
- trackingSource: "assistant_chat_reconnect_error",
1764
- });
1765
- const canRecover = info.recoverable === true;
1766
- const shouldShowBuilderReconnect = isBuilderReconnectRunError(info);
1767
- const builderReconnectResolved = shouldShowBuilderReconnect &&
1768
- builderReconnect.hasFetchedStatus &&
1769
- builderReconnect.configured;
1770
- const isQueryError = isProviderQueryRunError(info);
1771
- const isConnectionRecoveryError = isConnectionRecoveryRunError(info);
1772
- const copyLabel = info.runId || info.errorCode || info.details ? "Copy debug" : "Copy";
1773
- const copyDetails = useCallback(() => {
1774
- const text = [
1775
- info.message,
1776
- info.errorCode ? `Code: ${info.errorCode}` : "",
1777
- info.runId ? `Run: ${info.runId}` : "",
1778
- info.details ? `Details:\n${info.details}` : "",
1779
- ]
1780
- .filter(Boolean)
1781
- .join("\n\n");
1782
- void writeClipboardText(text).then((ok) => {
1783
- if (!ok)
1784
- return;
1785
- setCopied(true);
1786
- setTimeout(() => setCopied(false), 1200);
1787
- });
1788
- }, [info]);
1789
- const startNewChat = useCallback(() => {
1790
- window.dispatchEvent(new CustomEvent("agent-chat:new-chat"));
1791
- onDismiss();
1792
- }, [onDismiss]);
1793
- const handleFork = useCallback(async () => {
1794
- if (!onFork || forking)
1795
- return;
1796
- setForking(true);
1797
- setForkError(null);
1798
- try {
1799
- const result = await onFork();
1800
- if (result === false) {
1801
- setForkError("Could not fork this chat. Try starting a new chat.");
1802
- }
1803
- }
1804
- catch {
1805
- setForkError("Could not fork this chat. Try starting a new chat.");
1806
- }
1807
- finally {
1808
- setForking(false);
1809
- }
1810
- }, [forking, onFork]);
1811
- useEffect(() => {
1812
- if (builderReconnectResolved) {
1813
- onDismiss();
1814
- }
1815
- }, [builderReconnectResolved, onDismiss]);
1816
- return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: canRecover
1817
- ? "The agent stopped before finishing"
1818
- : "The agent hit an error" }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: info.message }), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "The current Builder.io or model-provider credential was rejected. Reconnect Builder.io, then retry this message." })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "If retry lands on the same error, start a new chat session and continue from what already changed." })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), "Details"] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconExternalLink, { size: 13 })), builderReconnect.connecting
1819
- ? "Connecting Builder.io"
1820
- : "Reconnect Builder.io"] })), canRecover && (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Continue"] }), _jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError ? "Diagnose and retry" : "Retry"] })] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), "New chat"] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: "Fork this conversation into a separate chat thread.", "aria-label": "Fork this conversation into a separate chat thread", className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking ? "Forking..." : "Fork chat"] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ml-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? "Copied" : copyLabel] })] }), shouldShowBuilderReconnect && builderReconnect.error && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: builderReconnect.error })), forkError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: forkError }))] }));
1821
- }
1822
- function LoopLimitContinueCard({ info, onContinue, }) {
1823
- const [settings, setSettings] = useState(null);
1824
- const [value, setValue] = useState("");
1825
- const [saving, setSaving] = useState(false);
1826
- const [saved, setSaved] = useState(false);
1827
- const [error, setError] = useState(null);
1828
- const load = useCallback(() => {
1829
- let cancelled = false;
1830
- fetch(agentNativePath("/_agent-native/agent-loop-settings"))
1831
- .then((r) => (r.ok ? r.json() : null))
1832
- .then((data) => {
1833
- if (cancelled || !data)
1834
- return;
1835
- setSettings(data);
1836
- setValue(String(data.maxIterations));
1837
- })
1838
- .catch(() => {
1839
- if (!cancelled)
1840
- setValue(String(info.maxIterations ?? ""));
1841
- });
1842
- return () => {
1843
- cancelled = true;
1844
- };
1845
- }, [info.maxIterations]);
1846
- useEffect(() => load(), [load]);
1847
- const currentLimit = settings?.maxIterations ?? info.maxIterations;
1848
- const numericValue = Number(value);
1849
- const hasPendingChange = !!settings &&
1850
- settings.canUpdate &&
1851
- Number.isInteger(numericValue) &&
1852
- numericValue !== settings.maxIterations;
1853
- const scopeLabel = settings?.scope === "org"
1854
- ? settings.orgName
1855
- ? `${settings.orgName} org`
1856
- : "org"
1857
- : "your account";
1858
- const saveLimit = useCallback(async () => {
1859
- if (!settings?.canUpdate)
1860
- return false;
1861
- setSaving(true);
1862
- setSaved(false);
1863
- setError(null);
1864
- try {
1865
- const res = await fetch(agentNativePath("/_agent-native/agent-loop-settings"), {
1866
- method: "PUT",
1867
- headers: { "Content-Type": "application/json" },
1868
- body: JSON.stringify({ maxIterations: numericValue }),
1869
- });
1870
- const body = await res.json().catch(() => ({}));
1871
- if (!res.ok) {
1872
- throw new Error(body?.error ?? `Save failed (${res.status})`);
1873
- }
1874
- setSettings(body);
1875
- setValue(String(body.maxIterations));
1876
- setSaved(true);
1877
- window.dispatchEvent(new CustomEvent("agent-loop-settings:changed", { detail: body }));
1878
- setTimeout(() => setSaved(false), 2000);
1879
- return true;
1880
- }
1881
- catch (err) {
1882
- setError(err instanceof Error ? err.message : "Save failed");
1883
- return false;
1884
- }
1885
- finally {
1886
- setSaving(false);
1887
- }
1888
- }, [numericValue, settings?.canUpdate]);
1889
- const handleContinue = useCallback(async () => {
1890
- if (hasPendingChange) {
1891
- const ok = await saveLimit();
1892
- if (!ok)
1893
- return;
1894
- }
1895
- onContinue();
1896
- }, [hasPendingChange, onContinue, saveLimit]);
1897
- const openSettings = useCallback(() => {
1898
- try {
1899
- window.location.hash = "agent-limits";
1900
- }
1901
- catch { }
1902
- window.dispatchEvent(new CustomEvent("agent-panel:open-settings"));
1903
- }, []);
1904
- return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] px-3 py-3 shadow-sm", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-600 dark:text-amber-400", children: _jsx(IconGauge, { size: 14 }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: "Step limit reached" }), _jsxs("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: ["The agent used", " ", currentLimit
1905
- ? `${currentLimit.toLocaleString()} steps`
1906
- : "all available steps", ". Keep going in a fresh turn, or raise the ", scopeLabel, " limit first."] })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "min-w-[116px] flex-1 space-y-1", children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Max steps" }), _jsx("input", { type: "number", min: settings?.minMaxIterations ?? 1, max: settings?.maxMaxIterations ?? 1000, value: value, disabled: !settings?.canUpdate || saving, onChange: (e) => {
1907
- setValue(e.target.value);
1908
- setError(null);
1909
- }, className: "h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring disabled:opacity-60" })] }), _jsx("button", { type: "button", onClick: saveLimit, disabled: !hasPendingChange || saving, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50", children: saving ? (_jsx(IconLoader2, { size: 12, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: 12 })) : ("Save") }), _jsxs("button", { type: "button", onClick: openSettings, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: [_jsx(IconSettings, { size: 12 }), "Settings"] }), _jsxs("button", { type: "button", onClick: handleContinue, disabled: saving, className: "ml-auto inline-flex h-8 items-center gap-1 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:opacity-60", children: [hasPendingChange ? "Save and keep going" : "Keep going", _jsx(IconArrowRight, { size: 12 })] })] }), settings && !settings.canUpdate && (_jsx("p", { className: "mt-2 text-[11px] text-muted-foreground", children: "Only organization owners and admins can change this limit." })), error && _jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })] }));
1910
- }
1911
- function PlanModeCallout({ canImplementPlan, onImplementPlan, onSwitchToAct, }) {
1912
- return (_jsx("div", { className: "shrink-0 px-3 pt-2", children: _jsx("div", { className: "rounded-lg border border-blue-500/25 bg-blue-500/[0.06] px-3 py-2.5 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-2.5", children: [_jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-blue-500/10 text-blue-600 dark:text-blue-300", children: _jsx(IconClipboardList, { size: 15 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: canImplementPlan ? "Plan ready" : "Plan mode is on" }), _jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: canImplementPlan
1913
- ? "Switch to Act and run the proposed plan."
1914
- : "The next turn will stay read-only until you switch to Act." })] }), canImplementPlan ? (_jsxs("button", { type: "button", onClick: onImplementPlan, className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Implement Plan"] })) : (_jsxs("button", { type: "button", onClick: onSwitchToAct, className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: ["Act", _jsx(IconArrowRight, { size: 13 })] }))] }) }) }));
1915
- }
1916
245
  export const CHAT_STORAGE_PREFIX = "agent-chat:";
1917
246
  /** Remove persisted chat for a given tabId (or "default"). */
1918
247
  export function clearChatStorage(tabId) {
@@ -1956,7 +285,73 @@ function ensureMessageMetadata(repo) {
1956
285
  // Re-export for backwards compatibility
1957
286
  import { extractThreadMeta, normalizeThreadRepository, } from "../agent/thread-data-builder.js";
1958
287
  export { extractThreadMeta };
1959
- const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, composerAreaClassName, composerPlaceholder, composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, onSlashCommand, execMode, onExecModeChange, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, onModelChange, onEffortChange, onForkChat, onConnectProvider, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, }, ref) {
288
+ /**
289
+ * Strip raw base64 payload from attachment content parts when a hosted URL
290
+ * already exists in the same content entry. This keeps the periodic thread
291
+ * save payload compact — the server already stored the URL reference when it
292
+ * processed the POST, and re-shipping multi-megabyte base64 strings on every
293
+ * 5-second poll save balloons the SQL thread_data column unnecessarily.
294
+ *
295
+ * Only strips the raw base64 data-URL string from `content[].image` / `content[].data`
296
+ * when a `metadata.uploadUrl` reference is present on the same attachment object,
297
+ * so the transcript can still render from the hosted URL after hydration.
298
+ */
299
+ function stripBase64FromRepo(repo) {
300
+ if (!repo || typeof repo !== "object")
301
+ return repo;
302
+ const r = repo;
303
+ if (!Array.isArray(r.messages))
304
+ return repo;
305
+ const messages = r.messages.map((entry) => {
306
+ if (!entry || typeof entry !== "object")
307
+ return entry;
308
+ const e = entry;
309
+ const msg = (e.message ?? e);
310
+ if (!msg || typeof msg !== "object")
311
+ return entry;
312
+ const attachments = msg.attachments;
313
+ if (!Array.isArray(attachments))
314
+ return entry;
315
+ const strippedAttachments = attachments.map((att) => {
316
+ if (!att || typeof att !== "object")
317
+ return att;
318
+ const a = att;
319
+ const meta = a.metadata;
320
+ // Only strip when we have a hosted upload URL confirmed by the server.
321
+ if (!meta?.uploadUrl)
322
+ return att;
323
+ if (!Array.isArray(a.content))
324
+ return att;
325
+ const strippedContent = a.content.map((part) => {
326
+ if (!part || typeof part !== "object")
327
+ return part;
328
+ const p = part;
329
+ // Replace the raw base64 image data-URL with the hosted URL.
330
+ if (p.type === "image" &&
331
+ typeof p.image === "string" &&
332
+ p.image.startsWith("data:")) {
333
+ return { ...p, image: meta.uploadUrl };
334
+ }
335
+ // Replace the raw base64 file data with a stripped marker.
336
+ if (p.type === "file" &&
337
+ typeof p.data === "string" &&
338
+ p.data.startsWith("data:")) {
339
+ const { data: _d, ...rest } = p;
340
+ return { ...rest, url: meta.uploadUrl };
341
+ }
342
+ return part;
343
+ });
344
+ return { ...a, content: strippedContent };
345
+ });
346
+ const strippedMsg = { ...msg, attachments: strippedAttachments };
347
+ if (e.message !== undefined) {
348
+ return { ...e, message: strippedMsg };
349
+ }
350
+ return strippedMsg;
351
+ });
352
+ return { ...r, messages };
353
+ }
354
+ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, composerAreaClassName, composerPlaceholder, composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, onSlashCommand, execMode, onExecModeChange, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEffort, availableModels, onModelChange, onEffortChange, onForkChat, onConnectProvider, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, }, ref) {
1960
355
  const thread = useThread();
1961
356
  const threadRuntime = useThreadRuntime();
1962
357
  const composerRuntime = useComposerRuntime();
@@ -1977,6 +372,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1977
372
  // attachment strip otherwise navigate to the file (browser default), which
1978
373
  // is why "upload does nothing" — the chat refreshes to the dropped image.
1979
374
  const [dropActive, setDropActive] = useState(false);
375
+ // Inline error shown just above the composer for attachment failures
376
+ // (unsupported format, size cap, body-size rejection, drop errors).
377
+ // Cleared on the next message send.
378
+ const [composerError, setComposerError] = useState(null);
1980
379
  const dropDepthRef = useRef(0);
1981
380
  const handleChatDragEnter = useCallback((e) => {
1982
381
  if (!Array.from(e.dataTransfer?.types ?? []).includes("Files"))
@@ -2025,9 +424,12 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2025
424
  return new File([file], uniqueName, { type: file.type });
2026
425
  });
2027
426
  void Promise.all(attachments.map((file) => composerRuntime.addAttachment(file))).catch((error) => {
2028
- console.error("Error adding dropped chat attachment:", error);
427
+ const msg = error instanceof Error
428
+ ? error.message
429
+ : "Could not add the dropped file. Try a different format.";
430
+ setComposerError(msg);
2029
431
  });
2030
- }, [composerRuntime]);
432
+ }, [composerRuntime, setComposerError]);
2031
433
  // Patch the underlying assistant-ui MessageRepository so addOrUpdateMessage
2032
434
  // can't throw "Parent message not found" mid-run. assistant-ui calls
2033
435
  // `repository.clear()` from `runtime.import()` and from `resetHead(null)`,
@@ -2153,6 +555,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2153
555
  const [dismissedRunErrorKey, setDismissedRunErrorKey] = useState(null);
2154
556
  const userStoppedRunRef = useRef(null);
2155
557
  const [isReconnecting, setIsReconnecting] = useState(false);
558
+ // Last activity label emitted by agent-chat:activity events (tool name / step label).
559
+ const [activityLabel, setActivityLabel] = useState(null);
560
+ // True during the 250ms continuation window and startup of the next chunk
561
+ // (adapter's auto-continue delay before POSTing the next chunk).
562
+ const [isAutoResuming, setIsAutoResuming] = useState(false);
2156
563
  const [reconnectContent, setReconnectContent] = useState([]);
2157
564
  // When stop is clicked during reconnect, keep content visible (don't wipe it)
2158
565
  const [reconnectFrozen, setReconnectFrozen] = useState(false);
@@ -2173,6 +580,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2173
580
  const wasRunningRef = useRef(false);
2174
581
  const lastBroadcastRunningRef = useRef(isRunning);
2175
582
  const tiptapRef = useRef(null);
583
+ // Stable ref to the "stop active run" action so addToQueue can abort
584
+ // a running turn without adding many unstable closure deps to its dep list.
585
+ const stopActiveRunRef = useRef(() => { });
2176
586
  useEffect(() => {
2177
587
  if (lastBroadcastRunningRef.current === isRunning)
2178
588
  return;
@@ -2656,7 +1066,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2656
1066
  lastSaveTimeRef.current = now;
2657
1067
  savedTitleRef.current = title;
2658
1068
  onSaveThreadRef.current(threadId, {
2659
- threadData: JSON.stringify(repo),
1069
+ threadData: JSON.stringify(stripBase64FromRepo(repo)),
2660
1070
  title,
2661
1071
  preview,
2662
1072
  messageCount: messages.length,
@@ -2676,7 +1086,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2676
1086
  const { title, preview } = extractThreadMeta(repo);
2677
1087
  savedTitleRef.current = title;
2678
1088
  onSaveThreadRef.current(threadId, {
2679
- threadData: JSON.stringify(repo),
1089
+ threadData: JSON.stringify(stripBase64FromRepo(repo)),
2680
1090
  title,
2681
1091
  preview,
2682
1092
  messageCount: messages.length,
@@ -2884,6 +1294,52 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2884
1294
  window.addEventListener("agent-chat:run-error", handler);
2885
1295
  return () => window.removeEventListener("agent-chat:run-error", handler);
2886
1296
  }, [tabId]);
1297
+ // Track the most recent activity label for the running indicator.
1298
+ useEffect(() => {
1299
+ const handler = (e) => {
1300
+ const detail = e.detail;
1301
+ if (tabId && detail?.tabId && detail.tabId !== tabId)
1302
+ return;
1303
+ if (typeof detail?.label === "string" && detail.label.trim()) {
1304
+ setActivityLabel(detail.label.trim());
1305
+ setIsAutoResuming(false);
1306
+ }
1307
+ };
1308
+ window.addEventListener("agent-chat:activity", handler);
1309
+ return () => window.removeEventListener("agent-chat:activity", handler);
1310
+ }, [tabId]);
1311
+ // Clear the activity label when the server clears a corrective draft.
1312
+ useEffect(() => {
1313
+ const handler = (e) => {
1314
+ const detail = e.detail;
1315
+ if (tabId && detail?.tabId && detail.tabId !== tabId)
1316
+ return;
1317
+ setActivityLabel(null);
1318
+ };
1319
+ window.addEventListener("agent-chat:activity-clear", handler);
1320
+ return () => window.removeEventListener("agent-chat:activity-clear", handler);
1321
+ }, [tabId]);
1322
+ // Show "Resuming…" during the adapter's auto-continuation window (the
1323
+ // ~250ms gap between the end of one serverless chunk and the POST for the
1324
+ // next). The adapter dispatches `agent-chat:auto-continue` at that moment.
1325
+ useEffect(() => {
1326
+ const handler = (e) => {
1327
+ const detail = e.detail;
1328
+ if (tabId && detail?.tabId && detail.tabId !== tabId)
1329
+ return;
1330
+ setIsAutoResuming(true);
1331
+ setActivityLabel(null);
1332
+ };
1333
+ window.addEventListener("agent-chat:auto-continue", handler);
1334
+ return () => window.removeEventListener("agent-chat:auto-continue", handler);
1335
+ }, [tabId]);
1336
+ // Clear auto-resume / activity label when the run stops.
1337
+ useEffect(() => {
1338
+ if (!isRunning) {
1339
+ setIsAutoResuming(false);
1340
+ setActivityLabel(null);
1341
+ }
1342
+ }, [isRunning]);
2887
1343
  // Auto-dequeue: when agent finishes running, send the next queued message
2888
1344
  useEffect(() => {
2889
1345
  if (wasRunningRef.current && !isRunning && queuedMessages.length > 0) {
@@ -3003,12 +1459,55 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3003
1459
  threadId,
3004
1460
  threadRuntime,
3005
1461
  ]);
1462
+ // Abort the active server run (identical to what the Stop button does) so
1463
+ // an immediate-while-running send can proceed cleanly without a 409 race.
1464
+ // Captured in a stable ref so addToQueue can call it without listing
1465
+ // all the stop-related state in its own dep array.
1466
+ const stopActiveRun = useCallback(() => {
1467
+ setForceStopped(true);
1468
+ const activeRun = getActiveRun();
1469
+ const runIdToAbort = reconnectRunIdRef.current ?? activeRun?.runId;
1470
+ userStoppedRunRef.current = {
1471
+ at: Date.now(),
1472
+ ...(runIdToAbort ? { runId: runIdToAbort } : {}),
1473
+ };
1474
+ setRunErrorInfo(null);
1475
+ setDismissedRunErrorKey(null);
1476
+ if (runIdToAbort) {
1477
+ fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
1478
+ method: "POST",
1479
+ }).catch(() => { });
1480
+ }
1481
+ if (isReconnecting) {
1482
+ reconnectAbortRef.current?.abort();
1483
+ reconnectAbortRef.current = null;
1484
+ reconnectRunIdRef.current = null;
1485
+ setIsReconnecting(false);
1486
+ setReconnectFrozen(reconnectContent.length > 0);
1487
+ }
1488
+ threadRuntime.cancelRun();
1489
+ if (typeof window !== "undefined") {
1490
+ window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
1491
+ detail: { isRunning: false, tabId: tabId || threadId },
1492
+ }));
1493
+ }
1494
+ }, [
1495
+ apiUrl,
1496
+ isReconnecting,
1497
+ reconnectContent.length,
1498
+ tabId,
1499
+ threadId,
1500
+ threadRuntime,
1501
+ ]);
1502
+ // Keep the ref current so addToQueue can call it without a stale closure.
1503
+ stopActiveRunRef.current = stopActiveRun;
3006
1504
  const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false) => {
3007
1505
  materializeFrozenReconnectContent();
3008
1506
  setShowContinue(false);
3009
1507
  setLoopLimitInfo(null);
3010
1508
  setRunErrorInfo(null);
3011
1509
  setDismissedRunErrorKey(null);
1510
+ setComposerError(null);
3012
1511
  userStoppedRunRef.current = null;
3013
1512
  // Selection context attached via Cmd+I is one-shot — clear it as soon
3014
1513
  // as the user actually sends a message so it can't be re-used.
@@ -3023,12 +1522,92 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3023
1522
  ? buildComposerContextSubmission(text)
3024
1523
  : { text, includesContext: false };
3025
1524
  const submittedText = submitted.text;
3026
- const queuedAttachments = await serializeQueuedAttachments(attachments);
1525
+ let queuedAttachments;
1526
+ try {
1527
+ queuedAttachments = await serializeQueuedAttachments(attachments);
1528
+ }
1529
+ catch (err) {
1530
+ const msg = err instanceof Error
1531
+ ? err.message
1532
+ : "Attachment could not be processed.";
1533
+ setComposerError(msg);
1534
+ return;
1535
+ }
3027
1536
  const imageAttachments = createAgentImageAttachments(images);
3028
- const messageAttachments = [
1537
+ const allAttachments = [
3029
1538
  ...(queuedAttachments ?? []),
3030
1539
  ...(imageAttachments ?? []),
3031
1540
  ];
1541
+ // ── Body-size guard (Fix 3) ─────────────────────────────────────
1542
+ // Estimate the total serialized attachment payload. If it exceeds the
1543
+ // Vercel/Netlify body limit, progressively re-compress images until
1544
+ // the payload fits, then reject the largest remaining file if still over.
1545
+ let messageAttachments = allAttachments;
1546
+ {
1547
+ const allDataUrls = allAttachments.flatMap((a) => a.content
1548
+ .filter((c) => c.type === "image")
1549
+ .map((c) => c.image));
1550
+ if (estimateAttachmentBodyBytes(allDataUrls) > MAX_ESTIMATED_BODY_BYTES) {
1551
+ // Re-compress image attachments more aggressively.
1552
+ const recompressed = [];
1553
+ let stillOver = false;
1554
+ for (const att of allAttachments) {
1555
+ if (att.type === "image" &&
1556
+ att.content.length === 1 &&
1557
+ att.content[0].type === "image") {
1558
+ // Find the original File from the queued attachments input.
1559
+ const rawAtt = (attachments ?? []).find((r) => r.id === att.id);
1560
+ const rawFile = rawAtt?.file;
1561
+ if (rawFile && typeof document !== "undefined") {
1562
+ try {
1563
+ const recompressedUrl = await transcodeImageToDataURL(rawFile, {
1564
+ maxDimension: AGGRESSIVE_MAX_IMAGE_DIMENSION,
1565
+ jpegQuality: AGGRESSIVE_JPEG_QUALITY,
1566
+ });
1567
+ recompressed.push({
1568
+ ...att,
1569
+ content: [{ type: "image", image: recompressedUrl }],
1570
+ });
1571
+ continue;
1572
+ }
1573
+ catch {
1574
+ // Could not recompress — keep original and flag overflow
1575
+ stillOver = true;
1576
+ }
1577
+ }
1578
+ else {
1579
+ stillOver = true;
1580
+ }
1581
+ }
1582
+ recompressed.push(att);
1583
+ }
1584
+ // Re-estimate after recompression.
1585
+ const recompressedUrls = recompressed.flatMap((a) => a.content
1586
+ .filter((c) => c.type === "image")
1587
+ .map((c) => c.image));
1588
+ if (stillOver ||
1589
+ estimateAttachmentBodyBytes(recompressedUrls) >
1590
+ MAX_ESTIMATED_BODY_BYTES) {
1591
+ // Find the largest attachment and reject it.
1592
+ let largestIdx = -1;
1593
+ let largestSize = 0;
1594
+ for (let i = 0; i < recompressed.length; i++) {
1595
+ const url = recompressed[i].content.find((c) => c.type === "image")?.image ?? "";
1596
+ if (url.length > largestSize) {
1597
+ largestSize = url.length;
1598
+ largestIdx = i;
1599
+ }
1600
+ }
1601
+ if (largestIdx >= 0) {
1602
+ const rejected = recompressed[largestIdx];
1603
+ setComposerError(`"${rejected.name}" makes the message too large to send (combined attachments must be under ${Math.round(MAX_ESTIMATED_BODY_BYTES / 1024 / 1024)} MB). Remove it or use a smaller image.`);
1604
+ return;
1605
+ }
1606
+ }
1607
+ messageAttachments = recompressed;
1608
+ }
1609
+ }
1610
+ // ── End body-size guard ──────────────────────────────────────────
3032
1611
  // Snapshot the exec mode at enqueue time when the caller didn't
3033
1612
  // pass an explicit override. Without this, a plan-mode message that
3034
1613
  // sits in the queue runs as 'act' if the user flips the global toggle
@@ -3039,7 +1618,35 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3039
1618
  : execMode === "build"
3040
1619
  ? "act"
3041
1620
  : undefined);
3042
- if (isRunning && intent === "queued") {
1621
+ if (isRunning && intent === "immediate") {
1622
+ // Mid-run Enter race fix: immediately abort the active server run,
1623
+ // wait for it to clear, then send — mirroring what the auto-dequeue
1624
+ // path already does safely. Without this, assistant-ui's append()
1625
+ // would cancel the adapter run locally but the server run would keep
1626
+ // going; the new POST would then 409, reconnect to the OLD run, and
1627
+ // replay the old answer under the new prompt.
1628
+ setQueuedMessages((prev) => [
1629
+ ...prev,
1630
+ {
1631
+ id: typeof crypto !== "undefined" && crypto.randomUUID
1632
+ ? crypto.randomUUID()
1633
+ : `q-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1634
+ text: submittedText,
1635
+ images,
1636
+ attachments: messageAttachments.length > 0 ? messageAttachments : undefined,
1637
+ references,
1638
+ requestMode: effectiveRequestMode,
1639
+ recoveryAction,
1640
+ trackInRunsTray,
1641
+ },
1642
+ ]);
1643
+ // Abort the server run (same as Stop button). This flips forceStopped
1644
+ // → isRunning=false → auto-dequeue fires → waitForThreadRunToClear →
1645
+ // append. The abort is fire-and-forget; waitForThreadRunToClear does
1646
+ // the actual wait.
1647
+ stopActiveRunRef.current();
1648
+ }
1649
+ else if (isRunning && intent === "queued") {
3043
1650
  setQueuedMessages((prev) => [
3044
1651
  ...prev,
3045
1652
  {
@@ -3305,7 +1912,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3305
1912
  ? { skipLabel: guidedQuestionsSkipLabel }
3306
1913
  : {}), ...(guidedQuestionsSubmitLabel
3307
1914
  ? { submitLabel: guidedQuestionsSubmitLabel }
3308
- : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { label: isReconnecting ? "Reconnecting" : "Thinking" })), _jsxs(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, missingApiKey && "cursor-pointer", isComposerDisabled && "opacity-70"), onClick: missingApiKey
1915
+ : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { label: isReconnecting
1916
+ ? "Reconnecting"
1917
+ : isAutoResuming
1918
+ ? "Resuming"
1919
+ : (activityLabel ?? "Thinking") })), composerError && (_jsxs("div", { role: "alert", className: "shrink-0 mx-3 mb-1.5 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), _jsx("span", { className: "flex-1 leading-snug", children: composerError }), _jsx("button", { type: "button", "aria-label": "Dismiss error", onClick: () => setComposerError(null), className: "shrink-0 opacity-70 hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) })] })), _jsxs(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, missingApiKey && "cursor-pointer", isComposerDisabled && "opacity-70"), onClick: missingApiKey
3309
1920
  ? () => setMissingKeyBouncePulse((p) => p + 1)
3310
1921
  : undefined, children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, disabled: isComposerDisabled, placeholder: missingApiKey
3311
1922
  ? "Connect an AI engine above to start chatting…"
@@ -3318,42 +1929,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3318
1929
  : "Send a follow-up..."
3319
1930
  : composerPlaceholder, onSubmit: isRunning || composerContextItems.length > 0
3320
1931
  ? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0 ? references : undefined, attachments, undefined, options?.intent ?? "immediate", undefined, true)
3321
- : undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, extraActionButton: contextXRayEnabled ||
1932
+ : undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: contextXRayEnabled ||
3322
1933
  composerExtraActionButton ||
3323
- showRunningInUI ? (_jsxs(_Fragment, { children: [contextXRayEnabled && (_jsx(ContextMeter, { threadId: threadId })), composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => {
3324
- // Nuclear stop: flip forceStopped so isRunning is false
3325
- // immediately. This unblocks submission even if the
3326
- // runtime or reconnect state is stuck.
3327
- setForceStopped(true);
3328
- const activeRun = getActiveRun();
3329
- const runIdToAbort = reconnectRunIdRef.current ??
3330
- activeRun?.runId;
3331
- userStoppedRunRef.current = {
3332
- at: Date.now(),
3333
- ...(runIdToAbort
3334
- ? { runId: runIdToAbort }
3335
- : {}),
3336
- };
3337
- setRunErrorInfo(null);
3338
- setDismissedRunErrorKey(null);
3339
- if (runIdToAbort) {
3340
- fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, { method: "POST" }).catch(() => { });
3341
- }
3342
- if (isReconnecting) {
3343
- reconnectAbortRef.current?.abort();
3344
- reconnectAbortRef.current = null;
3345
- reconnectRunIdRef.current = null;
3346
- setIsReconnecting(false);
3347
- setReconnectFrozen(reconnectContent.length > 0);
3348
- }
3349
- threadRuntime.cancelRun();
3350
- window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
3351
- detail: {
3352
- isRunning: false,
3353
- tabId: tabId || threadId,
3354
- },
3355
- }));
3356
- }, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }) }));
1934
+ showRunningInUI ? (_jsxs(_Fragment, { children: [contextXRayEnabled && (_jsx(ContextMeter, { threadId: threadId })), composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: stopActiveRun, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }) }));
3357
1935
  });
3358
1936
  export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentNativePath("/_agent-native/agent-chat"), tabId, browserTabId, threadId, contextScope, isActiveComposer, ...props }, ref) {
3359
1937
  const modelRef = useRef(props.selectedModel);