@vertesia/ui 1.4.0-dev.20260615.051508Z → 1.4.0-dev.20260629.130134Z

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 (337) hide show
  1. package/lib/core/components/shadcn/dropdown.d.ts.map +1 -1
  2. package/lib/core/components/shadcn/dropdown.js +6 -2
  3. package/lib/core/components/shadcn/dropdown.js.map +1 -1
  4. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  5. package/lib/core/components/shadcn/filters/filterBar.js +9 -2
  6. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  7. package/lib/core/components/shadcn/modal/ConfirmModal.d.ts +16 -1
  8. package/lib/core/components/shadcn/modal/ConfirmModal.d.ts.map +1 -1
  9. package/lib/core/components/shadcn/modal/ConfirmModal.js +25 -3
  10. package/lib/core/components/shadcn/modal/ConfirmModal.js.map +1 -1
  11. package/lib/core/components/shadcn/modal/dialog.js +1 -1
  12. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  13. package/lib/core/components/shadcn/popover.d.ts.map +1 -1
  14. package/lib/core/components/shadcn/popover.js +3 -1
  15. package/lib/core/components/shadcn/popover.js.map +1 -1
  16. package/lib/core/components/shadcn/switch.js +3 -3
  17. package/lib/core/components/shadcn/switch.js.map +1 -1
  18. package/lib/core/components/shadcn/tabs.js +1 -1
  19. package/lib/core/components/shadcn/tabs.js.map +1 -1
  20. package/lib/core/components/shadcn/tooltip.d.ts.map +1 -1
  21. package/lib/core/components/shadcn/tooltip.js +3 -1
  22. package/lib/core/components/shadcn/tooltip.js.map +1 -1
  23. package/lib/env/index.d.ts +0 -19
  24. package/lib/env/index.d.ts.map +1 -1
  25. package/lib/env/index.js +0 -6
  26. package/lib/env/index.js.map +1 -1
  27. package/lib/features/agent/PayloadBuilder.d.ts +4 -0
  28. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  29. package/lib/features/agent/PayloadBuilder.js +16 -0
  30. package/lib/features/agent/PayloadBuilder.js.map +1 -1
  31. package/lib/features/agent/chat/AgentApprovalModeSelector.d.ts +10 -0
  32. package/lib/features/agent/chat/AgentApprovalModeSelector.d.ts.map +1 -0
  33. package/lib/features/agent/chat/AgentApprovalModeSelector.js +50 -0
  34. package/lib/features/agent/chat/AgentApprovalModeSelector.js.map +1 -0
  35. package/lib/features/agent/chat/AgentChatFixtureReplay.d.ts +2 -1
  36. package/lib/features/agent/chat/AgentChatFixtureReplay.d.ts.map +1 -1
  37. package/lib/features/agent/chat/AgentChatFixtureReplay.js +3 -3
  38. package/lib/features/agent/chat/AgentChatFixtureReplay.js.map +1 -1
  39. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts +5 -2
  40. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
  41. package/lib/features/agent/chat/AgentRequestInputOverlay.js +41 -7
  42. package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
  43. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  44. package/lib/features/agent/chat/AgentRightPanel.js +133 -12
  45. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  46. package/lib/features/agent/chat/AnimatedThinkingDots.js +1 -1
  47. package/lib/features/agent/chat/AnimatedThinkingDots.js.map +1 -1
  48. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  49. package/lib/features/agent/chat/ArtifactsTab.js +11 -65
  50. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  51. package/lib/features/agent/chat/AskUserWidget.d.ts +3 -1
  52. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  53. package/lib/features/agent/chat/AskUserWidget.js +4 -3
  54. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  55. package/lib/features/agent/chat/ModernAgentConversation.d.ts +10 -2
  56. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentConversation.js +526 -117
  58. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  59. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts +8 -0
  60. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts.map +1 -0
  61. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +19 -0
  62. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js.map +1 -0
  63. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -2
  64. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  65. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +551 -92
  66. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  67. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +7 -6
  69. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/Header.js +2 -2
  71. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  72. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.d.ts +8 -0
  73. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.d.ts.map +1 -0
  74. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.js +61 -0
  75. package/lib/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.js.map +1 -0
  76. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +15 -2
  77. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  78. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +28 -38
  79. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  80. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +1 -1
  81. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  82. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -2
  83. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  84. package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js +2 -1
  85. package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js.map +1 -1
  86. package/lib/features/agent/chat/ModernAgentOutput/SlidingMessages.js +1 -1
  87. package/lib/features/agent/chat/ModernAgentOutput/SlidingMessages.js.map +1 -1
  88. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts +5 -0
  89. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  90. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +59 -4
  91. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  92. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +2 -2
  93. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
  94. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +9 -0
  95. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
  96. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +180 -0
  97. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
  98. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +6 -0
  99. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  100. package/lib/features/agent/chat/ModernAgentOutput/utils.js +92 -2
  101. package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
  102. package/lib/features/agent/chat/clipboardFiles.d.ts +15 -0
  103. package/lib/features/agent/chat/clipboardFiles.d.ts.map +1 -0
  104. package/lib/features/agent/chat/clipboardFiles.js +35 -0
  105. package/lib/features/agent/chat/clipboardFiles.js.map +1 -0
  106. package/lib/features/agent/chat/hooks/useAgentStream.d.ts +6 -0
  107. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  108. package/lib/features/agent/chat/hooks/useAgentStream.js +218 -37
  109. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  110. package/lib/features/agent/chat/hooks/useArtifacts.d.ts +1 -8
  111. package/lib/features/agent/chat/hooks/useArtifacts.d.ts.map +1 -1
  112. package/lib/features/agent/chat/hooks/useArtifacts.js +10 -28
  113. package/lib/features/agent/chat/hooks/useArtifacts.js.map +1 -1
  114. package/lib/features/agent/chat/index.d.ts +2 -1
  115. package/lib/features/agent/chat/index.d.ts.map +1 -1
  116. package/lib/features/agent/chat/index.js +2 -1
  117. package/lib/features/agent/chat/index.js.map +1 -1
  118. package/lib/features/agent/chat/workstreams.d.ts +1 -0
  119. package/lib/features/agent/chat/workstreams.d.ts.map +1 -1
  120. package/lib/features/agent/chat/workstreams.js +28 -0
  121. package/lib/features/agent/chat/workstreams.js.map +1 -1
  122. package/lib/features/facets/AgentRunnerFacetsNav.d.ts +10 -1
  123. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  124. package/lib/features/facets/AgentRunnerFacetsNav.js +6 -3
  125. package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  126. package/lib/features/facets/CollectionsFacetsNav.d.ts +10 -1
  127. package/lib/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
  128. package/lib/features/facets/CollectionsFacetsNav.js +14 -5
  129. package/lib/features/facets/CollectionsFacetsNav.js.map +1 -1
  130. package/lib/features/facets/PromptsFacetsNav.d.ts +10 -1
  131. package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
  132. package/lib/features/facets/PromptsFacetsNav.js +14 -5
  133. package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
  134. package/lib/features/facets/RunsFacetsNav.d.ts +10 -1
  135. package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
  136. package/lib/features/facets/RunsFacetsNav.js +14 -5
  137. package/lib/features/facets/RunsFacetsNav.js.map +1 -1
  138. package/lib/features/index.d.ts +0 -1
  139. package/lib/features/index.d.ts.map +1 -1
  140. package/lib/features/index.js +0 -1
  141. package/lib/features/index.js.map +1 -1
  142. package/lib/features/oauth/McpConnectionsButton.d.ts +30 -0
  143. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -0
  144. package/lib/features/oauth/McpConnectionsButton.js +70 -0
  145. package/lib/features/oauth/McpConnectionsButton.js.map +1 -0
  146. package/lib/features/oauth/McpConnectionsDialog.d.ts +25 -0
  147. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -0
  148. package/lib/features/oauth/McpConnectionsDialog.js +26 -0
  149. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -0
  150. package/lib/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -1
  151. package/lib/features/oauth/RemoteMcpConnectionButton.js +14 -6
  152. package/lib/features/oauth/RemoteMcpConnectionButton.js.map +1 -1
  153. package/lib/features/oauth/index.d.ts +3 -0
  154. package/lib/features/oauth/index.d.ts.map +1 -1
  155. package/lib/features/oauth/index.js +3 -0
  156. package/lib/features/oauth/index.js.map +1 -1
  157. package/lib/features/oauth/useMcpConnections.d.ts +44 -0
  158. package/lib/features/oauth/useMcpConnections.d.ts.map +1 -0
  159. package/lib/features/oauth/useMcpConnections.js +168 -0
  160. package/lib/features/oauth/useMcpConnections.js.map +1 -0
  161. package/lib/features/permissions/UserPermissionsProvider.d.ts +4 -6
  162. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  163. package/lib/features/permissions/UserPermissionsProvider.js +40 -42
  164. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  165. package/lib/features/store/objects/DocumentPreviewPanel.d.ts.map +1 -1
  166. package/lib/features/store/objects/DocumentPreviewPanel.js +2 -1
  167. package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  168. package/lib/features/store/objects/DocumentQuickFilter.d.ts +23 -0
  169. package/lib/features/store/objects/DocumentQuickFilter.d.ts.map +1 -0
  170. package/lib/features/store/objects/DocumentQuickFilter.js +30 -0
  171. package/lib/features/store/objects/DocumentQuickFilter.js.map +1 -0
  172. package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  173. package/lib/features/store/objects/DocumentSearchResults.js +17 -1
  174. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  175. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  176. package/lib/features/store/objects/components/ContentOverview.js +17 -74
  177. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  178. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts +7 -1
  179. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts.map +1 -1
  180. package/lib/features/store/objects/layout/DocumentTableColumn.js +27 -3
  181. package/lib/features/store/objects/layout/DocumentTableColumn.js.map +1 -1
  182. package/lib/features/store/objects/layout/documentLayout.js +1 -1
  183. package/lib/features/store/objects/layout/renderers.d.ts +2 -1
  184. package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
  185. package/lib/features/store/objects/layout/renderers.js +27 -20
  186. package/lib/features/store/objects/layout/renderers.js.map +1 -1
  187. package/lib/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
  188. package/lib/features/store/types/ContentObjectTypesSearch.js +4 -1
  189. package/lib/features/store/types/ContentObjectTypesSearch.js.map +1 -1
  190. package/lib/features/store/types/ContentObjectTypesTable.d.ts +2 -1
  191. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  192. package/lib/features/store/types/ContentObjectTypesTable.js +8 -3
  193. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  194. package/lib/i18n/instance.d.ts.map +1 -1
  195. package/lib/i18n/instance.js +1 -0
  196. package/lib/i18n/instance.js.map +1 -1
  197. package/lib/i18n/locales/ar.json +72 -45
  198. package/lib/i18n/locales/de.json +65 -34
  199. package/lib/i18n/locales/en.json +63 -47
  200. package/lib/i18n/locales/es.json +65 -35
  201. package/lib/i18n/locales/fr.json +65 -35
  202. package/lib/i18n/locales/it.json +72 -42
  203. package/lib/i18n/locales/ja.json +72 -41
  204. package/lib/i18n/locales/ko.json +72 -41
  205. package/lib/i18n/locales/pt.json +72 -42
  206. package/lib/i18n/locales/ru.json +72 -43
  207. package/lib/i18n/locales/tr.json +72 -41
  208. package/lib/i18n/locales/zh-TW.json +72 -41
  209. package/lib/i18n/locales/zh.json +72 -41
  210. package/lib/session/UserSession.d.ts +0 -6
  211. package/lib/session/UserSession.d.ts.map +1 -1
  212. package/lib/session/UserSession.js +0 -15
  213. package/lib/session/UserSession.js.map +1 -1
  214. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  215. package/lib/session/UserSessionProvider.js +39 -98
  216. package/lib/session/UserSessionProvider.js.map +1 -1
  217. package/lib/session/auth/composable.d.ts.map +1 -1
  218. package/lib/session/auth/composable.js +3 -33
  219. package/lib/session/auth/composable.js.map +1 -1
  220. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  221. package/lib/shell/login/SignInPrimitives.js +3 -2
  222. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  223. package/lib/vertesia-ui-core.js +1 -1
  224. package/lib/vertesia-ui-core.js.map +1 -1
  225. package/lib/vertesia-ui-env.js +1 -1
  226. package/lib/vertesia-ui-env.js.map +1 -1
  227. package/lib/vertesia-ui-features.js +1 -1
  228. package/lib/vertesia-ui-features.js.map +1 -1
  229. package/lib/vertesia-ui-i18n.js +1 -1
  230. package/lib/vertesia-ui-i18n.js.map +1 -1
  231. package/lib/vertesia-ui-session.js +1 -1
  232. package/lib/vertesia-ui-session.js.map +1 -1
  233. package/lib/vertesia-ui-shell.js +1 -1
  234. package/lib/vertesia-ui-shell.js.map +1 -1
  235. package/lib/vertesia-ui-widgets.js +1 -1
  236. package/lib/vertesia-ui-widgets.js.map +1 -1
  237. package/lib/widgets/schema-editor/editor/Editable.js +1 -1
  238. package/lib/widgets/schema-editor/editor/Editable.js.map +1 -1
  239. package/llms.txt +70 -70
  240. package/package.json +35 -32
  241. package/src/core/components/shadcn/dropdown.tsx +2 -0
  242. package/src/core/components/shadcn/filters/filterBar.tsx +9 -2
  243. package/src/core/components/shadcn/modal/ConfirmModal.tsx +65 -3
  244. package/src/core/components/shadcn/modal/dialog.tsx +1 -1
  245. package/src/core/components/shadcn/popover.tsx +1 -0
  246. package/src/core/components/shadcn/switch.tsx +4 -4
  247. package/src/core/components/shadcn/tabs.tsx +1 -1
  248. package/src/core/components/shadcn/tooltip.tsx +1 -0
  249. package/src/env/index.ts +0 -24
  250. package/src/features/agent/PayloadBuilder.tsx +18 -0
  251. package/src/features/agent/chat/AgentApprovalModeSelector.tsx +130 -0
  252. package/src/features/agent/chat/AgentChatFixtureReplay.test.tsx +22 -2
  253. package/src/features/agent/chat/AgentChatFixtureReplay.tsx +33 -27
  254. package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +122 -0
  255. package/src/features/agent/chat/AgentRequestInputOverlay.tsx +91 -17
  256. package/src/features/agent/chat/AgentRightPanel.tsx +238 -50
  257. package/src/features/agent/chat/AnimatedThinkingDots.tsx +1 -1
  258. package/src/features/agent/chat/ArtifactsTab.tsx +21 -155
  259. package/src/features/agent/chat/AskUserWidget.tsx +6 -2
  260. package/src/features/agent/chat/ModernAgentConversation.test.tsx +1157 -47
  261. package/src/features/agent/chat/ModernAgentConversation.tsx +703 -136
  262. package/src/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.tsx +74 -0
  263. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +1420 -126
  264. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +934 -111
  265. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +31 -40
  266. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +28 -28
  267. package/src/features/agent/chat/ModernAgentOutput/MessageDeliveryStatus.tsx +83 -0
  268. package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +38 -2
  269. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +121 -98
  270. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +7 -1
  271. package/src/features/agent/chat/ModernAgentOutput/SlideInPanel.tsx +3 -3
  272. package/src/features/agent/chat/ModernAgentOutput/SlidingMessages.tsx +1 -1
  273. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +82 -2
  274. package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +2 -2
  275. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +216 -0
  276. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +149 -0
  277. package/src/features/agent/chat/ModernAgentOutput/utils.ts +103 -2
  278. package/src/features/agent/chat/clipboardFiles.ts +33 -0
  279. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +355 -4
  280. package/src/features/agent/chat/hooks/useAgentStream.ts +270 -43
  281. package/src/features/agent/chat/hooks/useArtifacts.ts +11 -44
  282. package/src/features/agent/chat/index.ts +10 -1
  283. package/src/features/agent/chat/workstreams.ts +42 -0
  284. package/src/features/facets/AgentRunnerFacetsNav.tsx +23 -3
  285. package/src/features/facets/CollectionsFacetsNav.tsx +30 -5
  286. package/src/features/facets/PromptsFacetsNav.tsx +30 -5
  287. package/src/features/facets/RunsFacetsNav.tsx +32 -5
  288. package/src/features/index.ts +0 -1
  289. package/src/features/oauth/McpConnectionsButton.test.tsx +248 -0
  290. package/src/features/oauth/McpConnectionsButton.tsx +269 -0
  291. package/src/features/oauth/McpConnectionsDialog.tsx +142 -0
  292. package/src/features/oauth/RemoteMcpConnectionButton.tsx +37 -14
  293. package/src/features/oauth/index.ts +3 -0
  294. package/src/features/oauth/useMcpConnections.test.ts +94 -0
  295. package/src/features/oauth/useMcpConnections.ts +210 -0
  296. package/src/features/permissions/UserPermissionsProvider.tsx +41 -59
  297. package/src/features/store/objects/DocumentPreviewPanel.tsx +4 -2
  298. package/src/features/store/objects/DocumentQuickFilter.tsx +72 -0
  299. package/src/features/store/objects/DocumentSearchResults.tsx +30 -11
  300. package/src/features/store/objects/components/ContentOverview.tsx +30 -99
  301. package/src/features/store/objects/layout/DocumentTableColumn.tsx +29 -4
  302. package/src/features/store/objects/layout/documentLayout.tsx +1 -1
  303. package/src/features/store/objects/layout/renderers.tsx +80 -20
  304. package/src/features/store/types/ContentObjectTypesSearch.tsx +7 -1
  305. package/src/features/store/types/ContentObjectTypesTable.tsx +34 -4
  306. package/src/i18n/instance.ts +1 -0
  307. package/src/i18n/locales/ar.json +72 -45
  308. package/src/i18n/locales/de.json +65 -34
  309. package/src/i18n/locales/en.json +63 -47
  310. package/src/i18n/locales/es.json +65 -35
  311. package/src/i18n/locales/fr.json +65 -35
  312. package/src/i18n/locales/it.json +72 -42
  313. package/src/i18n/locales/ja.json +72 -41
  314. package/src/i18n/locales/ko.json +72 -41
  315. package/src/i18n/locales/pt.json +72 -42
  316. package/src/i18n/locales/ru.json +72 -43
  317. package/src/i18n/locales/tr.json +72 -41
  318. package/src/i18n/locales/zh-TW.json +72 -41
  319. package/src/i18n/locales/zh.json +72 -41
  320. package/src/i18n/rtl.test.ts +57 -1
  321. package/src/session/UserSession.ts +0 -16
  322. package/src/session/UserSessionProvider.tsx +51 -123
  323. package/src/session/auth/composable.ts +3 -34
  324. package/src/shell/login/SignInPrimitives.tsx +3 -2
  325. package/src/widgets/schema-editor/editor/Editable.tsx +1 -1
  326. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts +0 -73
  327. package/lib/features/document-viewer/UniversalDocumentViewer.d.ts.map +0 -1
  328. package/lib/features/document-viewer/UniversalDocumentViewer.js +0 -415
  329. package/lib/features/document-viewer/UniversalDocumentViewer.js.map +0 -1
  330. package/lib/features/document-viewer/index.d.ts +0 -2
  331. package/lib/features/document-viewer/index.d.ts.map +0 -1
  332. package/lib/features/document-viewer/index.js +0 -2
  333. package/lib/features/document-viewer/index.js.map +0 -1
  334. package/src/features/document-viewer/UniversalDocumentViewer.test.tsx +0 -123
  335. package/src/features/document-viewer/UniversalDocumentViewer.tsx +0 -746
  336. package/src/features/document-viewer/index.ts +0 -1
  337. package/src/session/auth/composable.test.ts +0 -79
@@ -3,9 +3,14 @@ import {
3
3
  type AgentMessage,
4
4
  AgentMessageType,
5
5
  type AgentRun,
6
+ type AgentToolApprovalMode,
7
+ type CompletedWorkstreamEntry,
6
8
  type ConversationFile,
7
9
  type ConversationFileRef,
10
+ type McpConnectUxConfig,
11
+ normalizeAgentToolApprovalMode,
8
12
  type Plan,
13
+ type StopSignal,
9
14
  type UserInputSignal,
10
15
  } from '@vertesia/common';
11
16
  import { FusionFragmentProvider } from '@vertesia/fusion-ux';
@@ -27,15 +32,19 @@ import { useUserSession } from '@vertesia/ui/session';
27
32
  import { ArrowUpIcon, Bot, CheckCircle, Cpu, FileTextIcon, UploadIcon, XIcon } from 'lucide-react';
28
33
  import type React from 'react';
29
34
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
35
+ import { McpConnectionsActionMenu } from '../../oauth/McpConnectionsButton.js';
36
+ import { AgentApprovalModeSelector } from './AgentApprovalModeSelector';
30
37
  import { AgentChatPlaybackControls } from './AgentChatPlaybackControls';
31
38
  import { AgentRequestInputOverlay } from './AgentRequestInputOverlay';
32
39
  import { AgentRightPanel, type WorkstreamInfo } from './AgentRightPanel.js';
33
40
  import { AnimatedThinkingDots, PulsatingCircle } from './AnimatedThinkingDots';
41
+ import { extractFilesFromClipboard } from './clipboardFiles.js';
34
42
  import { useAgentPlans } from './hooks/useAgentPlans.js';
35
43
  import { useAgentStream } from './hooks/useAgentStream.js';
36
44
  import { useDocumentPanel } from './hooks/useDocumentPanel.js';
37
45
  import { useFileProcessing } from './hooks/useFileProcessing.js';
38
46
  import { ImageLightboxProvider } from './ImageLightbox';
47
+ import { ActiveWorkstreamsSummary } from './ModernAgentOutput/ActiveWorkstreamsSummary';
39
48
  import type {
40
49
  AgentConversationViewMode,
41
50
  AgentInitialRequestTemplate,
@@ -44,12 +53,22 @@ import type {
44
53
  import AllMessagesMixed from './ModernAgentOutput/AllMessagesMixed';
45
54
  import type { BatchProgressPanelClassNames } from './ModernAgentOutput/BatchProgressPanel';
46
55
  import Header from './ModernAgentOutput/Header';
47
- import MessageInput, { type SelectedDocument, type UploadedFile } from './ModernAgentOutput/MessageInput';
56
+ import MessageInput, {
57
+ type ContextWindowUsage,
58
+ type SelectedDocument,
59
+ type UploadedFile,
60
+ } from './ModernAgentOutput/MessageInput';
48
61
  import type { MessageItemClassNames } from './ModernAgentOutput/MessageItem';
49
62
  import { getPendingRequestInputMessage } from './ModernAgentOutput/requestInputMessages';
50
63
  import type { StreamingMessageClassNames } from './ModernAgentOutput/StreamingMessage';
51
64
  import type { ToolCallGroupClassNames } from './ModernAgentOutput/ToolCallGroup';
52
- import { getConversationUrl, getWorkstreamId, isInProgress } from './ModernAgentOutput/utils';
65
+ import {
66
+ debugAgentChat,
67
+ filterMessagesForActiveWorkstream,
68
+ getConversationUrl,
69
+ getWorkstreamId,
70
+ isInProgress,
71
+ } from './ModernAgentOutput/utils';
53
72
  import {
54
73
  type AgentChatPlaybackCursor,
55
74
  createPlaybackState,
@@ -62,13 +81,20 @@ import { ArtifactUrlCacheProvider } from './useArtifactUrlCache.js';
62
81
  import { VegaLiteChart } from './VegaLiteChart';
63
82
  import { ThinkingMessages } from './WaitingMessages';
64
83
  import {
84
+ getWorkstreamDisplayName,
65
85
  getWorkstreamLaunchDetails,
66
86
  getWorkstreamLifecycleStatus,
67
87
  isWorkstreamInternalResultMessage,
68
- isWorkstreamTerminalMessage,
69
88
  } from './workstreams.js';
70
89
 
71
- export type StartWorkflowFn = (initialMessage?: string) => Promise<{ agent_run_id: string } | undefined>;
90
+ export interface StartWorkflowOptions {
91
+ tool_approval_mode?: AgentToolApprovalMode;
92
+ }
93
+
94
+ export type StartWorkflowFn = (
95
+ initialMessage?: string,
96
+ options?: StartWorkflowOptions,
97
+ ) => Promise<{ agent_run_id: string } | undefined>;
72
98
 
73
99
  const EMPTY_STREAMING_MESSAGES = new Map<string, never>();
74
100
 
@@ -79,50 +105,279 @@ function getTimestampMs(timestamp: number | string | undefined): number {
79
105
  return Number.isFinite(parsed) ? parsed : Date.now();
80
106
  }
81
107
 
82
- function deriveActiveWorkstreamsFromMessages(messages: AgentMessage[]): WorkstreamInfo[] {
83
- const latestByWorkstream = new Map<string, AgentMessage>();
84
- const launchesByWorkstream = new Map<
85
- string,
86
- { launch_id?: string; interaction?: string; child_workflow_id?: string; child_workflow_run_id?: string }
87
- >();
108
+ type DerivedWorkstreamInfo = WorkstreamInfo & {
109
+ started_at: number;
110
+ updated_at: number;
111
+ order: number;
112
+ };
113
+
114
+ function isActiveWorkstreamStatus(status: WorkstreamInfo['status']) {
115
+ return status === 'running' || status === 'canceling';
116
+ }
117
+
118
+ function isTerminalWorkstreamStatus(status: WorkstreamInfo['status']) {
119
+ return !isActiveWorkstreamStatus(status);
120
+ }
121
+
122
+ function getNumberDetail(value: unknown): number | undefined {
123
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
124
+ }
125
+
126
+ function toContextWindowUsage(messages: AgentMessage[]): ContextWindowUsage | undefined {
127
+ for (let index = messages.length - 1; index >= 0; index--) {
128
+ const details = messages[index].details;
129
+ if (!details) continue;
130
+
131
+ const tokenUsage = details.token_usage as { total?: unknown } | undefined;
132
+ const usedTokens = getNumberDetail(tokenUsage?.total);
133
+ const checkpointTokens =
134
+ getNumberDetail(details.checkpoint_threshold) ?? getNumberDetail(details.checkpoint_at);
135
+
136
+ if (typeof usedTokens !== 'number' || typeof checkpointTokens !== 'number' || checkpointTokens <= 0) {
137
+ continue;
138
+ }
139
+
140
+ const usedPercent = Math.max(0, Math.min(100, Math.round((usedTokens / checkpointTokens) * 100)));
141
+ return {
142
+ usedTokens,
143
+ checkpointTokens,
144
+ usedPercent,
145
+ remainingPercent: Math.max(0, 100 - usedPercent),
146
+ };
147
+ }
148
+
149
+ return undefined;
150
+ }
151
+
152
+ function mergePreservingTerminalStatus(existing: WorkstreamInfo, next: WorkstreamInfo): WorkstreamInfo {
153
+ if (!isTerminalWorkstreamStatus(existing.status) || !isActiveWorkstreamStatus(next.status)) {
154
+ return { ...existing, ...next };
155
+ }
156
+
157
+ return {
158
+ ...existing,
159
+ interaction: existing.interaction ?? next.interaction,
160
+ elapsed_ms: Math.max(existing.elapsed_ms, next.elapsed_ms),
161
+ deadline_ms: Math.max(existing.deadline_ms, next.deadline_ms),
162
+ remaining_ms: 0,
163
+ phase: existing.phase ?? next.phase,
164
+ child_workflow_id: existing.child_workflow_id ?? next.child_workflow_id,
165
+ child_workflow_run_id: existing.child_workflow_run_id ?? next.child_workflow_run_id,
166
+ };
167
+ }
168
+
169
+ function getWorkstreamMessageDetails(message: AgentMessage): {
170
+ workstreamId: string;
171
+ launchId?: string;
172
+ interaction?: string;
173
+ childWorkflowId?: string;
174
+ childWorkflowRunId?: string;
175
+ } | null {
176
+ const details = message.details as
177
+ | {
178
+ workstream_id?: unknown;
179
+ launch_id?: unknown;
180
+ interaction?: unknown;
181
+ child_workflow_id?: unknown;
182
+ child_workflow_run_id?: unknown;
183
+ }
184
+ | undefined;
185
+
186
+ const workstreamId =
187
+ typeof details?.workstream_id === 'string' && details.workstream_id.trim()
188
+ ? details.workstream_id
189
+ : getWorkstreamId(message);
190
+
191
+ if (workstreamId === 'main' || workstreamId === 'all') return null;
192
+
193
+ return {
194
+ workstreamId,
195
+ launchId: typeof details?.launch_id === 'string' ? details.launch_id : undefined,
196
+ interaction: typeof details?.interaction === 'string' ? details.interaction : undefined,
197
+ childWorkflowId: typeof details?.child_workflow_id === 'string' ? details.child_workflow_id : undefined,
198
+ childWorkflowRunId:
199
+ typeof details?.child_workflow_run_id === 'string' ? details.child_workflow_run_id : undefined,
200
+ };
201
+ }
202
+
203
+ function isWorkstreamActivityFailureMessage(message: AgentMessage): boolean {
204
+ if (message.type !== AgentMessageType.ERROR) return false;
205
+
206
+ const details = message.details as
207
+ | {
208
+ activity_group_id?: unknown;
209
+ event_class?: unknown;
210
+ tool?: unknown;
211
+ tool_event?: unknown;
212
+ tool_run_id?: unknown;
213
+ tool_status?: unknown;
214
+ workstream_event?: unknown;
215
+ }
216
+ | undefined;
217
+
218
+ if (details?.event_class !== 'activity') return false;
219
+ if (details.workstream_event) return false;
220
+
221
+ return !(
222
+ details.tool ||
223
+ details.tool_status ||
224
+ details.tool_run_id ||
225
+ details.activity_group_id ||
226
+ details.tool_event
227
+ );
228
+ }
88
229
 
89
- for (const message of messages) {
230
+ function ensureWorkstreamRecord(
231
+ records: Map<string, DerivedWorkstreamInfo>,
232
+ latestKeyByWorkstream: Map<string, string>,
233
+ workstreamId: string,
234
+ launchId: string | undefined,
235
+ timestamp: number,
236
+ order: number,
237
+ ): DerivedWorkstreamInfo {
238
+ if (launchId) {
239
+ const previousKey = latestKeyByWorkstream.get(workstreamId);
240
+ if (previousKey?.startsWith('message-derived:')) {
241
+ const previous = records.get(previousKey);
242
+ if (previous) {
243
+ records.delete(previousKey);
244
+ records.set(launchId, {
245
+ ...previous,
246
+ launch_id: launchId,
247
+ updated_at: Math.max(previous.updated_at, timestamp),
248
+ });
249
+ }
250
+ }
251
+ latestKeyByWorkstream.set(workstreamId, launchId);
252
+ }
253
+
254
+ const key = launchId ?? latestKeyByWorkstream.get(workstreamId) ?? `message-derived:${workstreamId}`;
255
+ const existing = records.get(key);
256
+ if (existing) return existing;
257
+
258
+ const record: DerivedWorkstreamInfo = {
259
+ workstream_id: workstreamId,
260
+ launch_id: key,
261
+ elapsed_ms: 0,
262
+ deadline_ms: 0,
263
+ remaining_ms: 0,
264
+ status: 'running',
265
+ started_at: timestamp,
266
+ updated_at: timestamp,
267
+ order,
268
+ };
269
+ records.set(key, record);
270
+ if (!latestKeyByWorkstream.has(workstreamId)) latestKeyByWorkstream.set(workstreamId, key);
271
+ return record;
272
+ }
273
+
274
+ function deriveWorkstreamsFromMessages(messages: AgentMessage[]): WorkstreamInfo[] {
275
+ const records = new Map<string, DerivedWorkstreamInfo>();
276
+ const latestKeyByWorkstream = new Map<string, string>();
277
+
278
+ messages.forEach((message, order) => {
279
+ const timestamp = getTimestampMs(message.timestamp);
280
+ const details = getWorkstreamMessageDetails(message);
90
281
  const launchDetails = getWorkstreamLaunchDetails(message);
282
+ const workstreamId = launchDetails?.workstreamId ?? details?.workstreamId;
283
+ if (!workstreamId) return;
284
+
285
+ const launchId = launchDetails?.launchId ?? details?.launchId;
286
+ const isInternalResult = isWorkstreamInternalResultMessage(message);
287
+ if (!launchDetails && !launchId && isInternalResult && !latestKeyByWorkstream.has(workstreamId)) return;
288
+
289
+ const record = ensureWorkstreamRecord(records, latestKeyByWorkstream, workstreamId, launchId, timestamp, order);
290
+
91
291
  if (launchDetails) {
92
- launchesByWorkstream.set(launchDetails.workstreamId, {
93
- launch_id: launchDetails.launchId,
94
- interaction: launchDetails.interaction,
95
- child_workflow_id: launchDetails.childWorkflowId,
96
- child_workflow_run_id: launchDetails.childWorkflowRunId,
97
- });
292
+ record.interaction = launchDetails.interaction ?? record.interaction;
293
+ record.child_workflow_id = launchDetails.childWorkflowId ?? record.child_workflow_id;
294
+ record.child_workflow_run_id = launchDetails.childWorkflowRunId ?? record.child_workflow_run_id;
295
+ record.status = 'running';
296
+ } else {
297
+ record.interaction = details?.interaction ?? record.interaction;
298
+ record.child_workflow_id = details?.childWorkflowId ?? record.child_workflow_id;
299
+ record.child_workflow_run_id = details?.childWorkflowRunId ?? record.child_workflow_run_id;
300
+ }
301
+
302
+ const lifecycleStatus = getWorkstreamLifecycleStatus(message);
303
+ if (lifecycleStatus) {
304
+ record.status = lifecycleStatus;
305
+ } else if (!isInternalResult) {
306
+ if (isWorkstreamActivityFailureMessage(message)) {
307
+ record.status = 'failed';
308
+ } else if (message.type === AgentMessageType.COMPLETE || message.type === AgentMessageType.IDLE) {
309
+ record.status = 'completed';
310
+ }
98
311
  }
99
312
 
100
- if (isWorkstreamInternalResultMessage(message)) continue;
313
+ if (isInternalResult) return;
101
314
 
102
- const workstreamId = getWorkstreamId(message);
103
- if (workstreamId === 'main' || workstreamId === 'all') continue;
104
- latestByWorkstream.set(workstreamId, message);
105
- }
315
+ record.updated_at = Math.max(record.updated_at, timestamp);
316
+ if (isActiveWorkstreamStatus(record.status)) {
317
+ record.elapsed_ms = Math.max(record.elapsed_ms, timestamp - record.started_at);
318
+ } else {
319
+ record.elapsed_ms = Math.max(record.elapsed_ms, timestamp - record.started_at);
320
+ record.remaining_ms = 0;
321
+ }
322
+ });
106
323
 
107
- return Array.from(latestByWorkstream.entries())
108
- .filter(([, message]) => {
109
- if ([AgentMessageType.COMPLETE, AgentMessageType.IDLE].includes(message.type)) return false;
110
- return !isWorkstreamTerminalMessage(message);
324
+ return Array.from(records.values())
325
+ .sort((a, b) => {
326
+ const activeDelta =
327
+ Number(!isActiveWorkstreamStatus(a.status)) - Number(!isActiveWorkstreamStatus(b.status));
328
+ if (activeDelta !== 0) return activeDelta;
329
+ if (isActiveWorkstreamStatus(a.status)) return a.order - b.order;
330
+ return b.updated_at - a.updated_at || a.order - b.order;
111
331
  })
112
- .map(([workstreamId, message]) => {
113
- const launch = launchesByWorkstream.get(workstreamId);
114
- return {
115
- workstream_id: workstreamId,
116
- launch_id: launch?.launch_id ?? `message-derived:${workstreamId}`,
117
- interaction: launch?.interaction,
118
- elapsed_ms: 0,
119
- deadline_ms: 0,
120
- remaining_ms: 0,
121
- status: getWorkstreamLifecycleStatus(message) ?? 'running',
122
- child_workflow_id: launch?.child_workflow_id,
123
- child_workflow_run_id: launch?.child_workflow_run_id,
124
- };
125
- });
332
+ .map(({ started_at, updated_at, order, ...workstream }) => workstream);
333
+ }
334
+
335
+ function activeWorkstreamEntryToInfo(ws: ActiveWorkstreamEntry): WorkstreamInfo {
336
+ return {
337
+ workstream_id: ws.workstream_id,
338
+ launch_id: ws.launch_id,
339
+ interaction: ws.interaction,
340
+ elapsed_ms: ws.elapsed_ms,
341
+ deadline_ms: ws.deadline_ms,
342
+ remaining_ms: Math.max(0, ws.deadline_ms - ws.elapsed_ms),
343
+ status: ws.status,
344
+ phase: ws.latest_progress?.phase,
345
+ child_workflow_id: ws.child_workflow_id,
346
+ child_workflow_run_id: ws.child_workflow_run_id,
347
+ };
348
+ }
349
+
350
+ function completedWorkstreamEntryToInfo(ws: CompletedWorkstreamEntry): WorkstreamInfo {
351
+ return {
352
+ workstream_id: ws.workstream_id,
353
+ launch_id: ws.launch_id,
354
+ interaction: ws.interaction,
355
+ elapsed_ms: ws.duration_ms ?? 0,
356
+ deadline_ms: 0,
357
+ remaining_ms: 0,
358
+ status: ws.status,
359
+ phase: ws.last_progress?.phase,
360
+ child_workflow_id: ws.child_workflow_id,
361
+ child_workflow_run_id: ws.child_workflow_run_id,
362
+ };
363
+ }
364
+
365
+ function mergeWorkstreamInfo(workstreams: WorkstreamInfo[], next: WorkstreamInfo) {
366
+ const existingIndex = workstreams.findIndex((ws) => ws.launch_id === next.launch_id);
367
+ if (existingIndex >= 0) {
368
+ workstreams[existingIndex] = mergePreservingTerminalStatus(workstreams[existingIndex], next);
369
+ return;
370
+ }
371
+
372
+ const fallbackIndex = workstreams.findIndex(
373
+ (ws) => ws.workstream_id === next.workstream_id && ws.launch_id.startsWith('message-derived:'),
374
+ );
375
+ if (fallbackIndex >= 0 && !next.launch_id.startsWith('message-derived:')) {
376
+ workstreams[fallbackIndex] = mergePreservingTerminalStatus(workstreams[fallbackIndex], next);
377
+ return;
378
+ }
379
+
380
+ workstreams.push(next);
126
381
  }
127
382
 
128
383
  function formatCompactDuration(seconds: number): string {
@@ -440,6 +695,11 @@ export interface ModernAgentConversationProps {
440
695
  pendingStartMessage?: string;
441
696
  /** Timestamp for the internal optimistic first-message waiting state. */
442
697
  pendingStartTimestamp?: number;
698
+ /**
699
+ * Initial approval mode: seeds the start screen for a new interactive run, and is shown
700
+ * while an existing run's metadata loads. Defaults to `full_control`.
701
+ */
702
+ initialToolApprovalMode?: AgentToolApprovalMode;
443
703
  /** Force display playback controls on or off. When omitted, local playback can be toggled from the header. */
444
704
  enablePlayback?: boolean;
445
705
  /** Show a local toggle for display playback controls in the conversation action rail. */
@@ -479,6 +739,7 @@ function EmptyState() {
479
739
  // Files can be staged locally before workflow starts, then uploaded when the workflow is created
480
740
  function StartWorkflowView({
481
741
  initialMessage,
742
+ interactive = true,
482
743
  startWorkflow,
483
744
  onClose,
484
745
  isModal = false,
@@ -500,8 +761,10 @@ function StartWorkflowView({
500
761
  inputClassName,
501
762
  className,
502
763
  allowWorkflowControl,
764
+ initialToolApprovalMode,
503
765
  }: ModernAgentConversationProps) {
504
766
  const { t } = useUITranslation();
767
+ const canStageFiles = !hideFileUpload;
505
768
  const resolvedPlaceholder = placeholder ?? t('agent.typeYourMessage');
506
769
  const resolvedStartButtonText = startButtonText ?? t('agent.startAgent');
507
770
  const resolvedTitle = title ?? t('agent.startNewConversation');
@@ -511,13 +774,15 @@ function StartWorkflowView({
511
774
  const [startedAgentRunId, setStartedAgentRunId] = useState<string | null>(null);
512
775
  const [pendingStartMessage, setPendingStartMessage] = useState<string | null>(null);
513
776
  const [pendingStartTimestamp, setPendingStartTimestamp] = useState<number | null>(null);
777
+ const [toolApprovalMode, setToolApprovalMode] = useState<AgentToolApprovalMode>(() =>
778
+ normalizeAgentToolApprovalMode(initialToolApprovalMode, interactive),
779
+ );
514
780
  const toast = useToast();
515
781
  const inputRef = useRef<HTMLTextAreaElement>(null);
516
782
  const fileInputRef = useRef<HTMLInputElement>(null);
517
783
 
518
784
  // Staged files - stored locally until workflow starts
519
785
  const [stagedFiles, setStagedFiles] = useState<File[]>([]);
520
- const canStageFiles = !hideFileUpload;
521
786
 
522
787
  // Drag and drop state
523
788
  const [isDragOver, setIsDragOver] = useState(false);
@@ -578,6 +843,20 @@ function StartWorkflowView({
578
843
  [maxFiles, canStageFiles],
579
844
  );
580
845
 
846
+ // Paste handler for files — mirrors MessageInput.handlePaste so the start
847
+ // screen and the live chat both accept pasted clipboard images/files. Files
848
+ // are staged locally here; they get uploaded when the workflow starts.
849
+ const handlePaste = useCallback(
850
+ (e: React.ClipboardEvent) => {
851
+ if (!canStageFiles) return;
852
+ const files = extractFilesFromClipboard(e.clipboardData?.items);
853
+ if (files.length > 0) {
854
+ setStagedFiles((prev) => [...prev, ...files].slice(0, maxFiles));
855
+ }
856
+ },
857
+ [canStageFiles, maxFiles],
858
+ );
859
+
581
860
  const handleFileInputChange = useCallback(
582
861
  (e: React.ChangeEvent<HTMLInputElement>) => {
583
862
  if (e.target.files && e.target.files.length > 0) {
@@ -648,7 +927,7 @@ function StartWorkflowView({
648
927
  setPendingStartMessage(messageContent);
649
928
  setPendingStartTimestamp(Date.now());
650
929
 
651
- const newRun = await startWorkflow(messageContent);
930
+ const newRun = await startWorkflow(messageContent, { tool_approval_mode: toolApprovalMode });
652
931
  if (newRun) {
653
932
  const agentId = newRun.agent_run_id;
654
933
 
@@ -770,6 +1049,7 @@ function StartWorkflowView({
770
1049
  title={title}
771
1050
  pendingStartMessage={pendingStartMessage ?? undefined}
772
1051
  pendingStartTimestamp={pendingStartTimestamp ?? undefined}
1052
+ initialToolApprovalMode={toolApprovalMode}
773
1053
  />
774
1054
  );
775
1055
  }
@@ -863,6 +1143,7 @@ function StartWorkflowView({
863
1143
  <div
864
1144
  key={`${file.name}-${file.size}-${file.lastModified}`}
865
1145
  className="flex items-center gap-1.5 rounded-md bg-attention/10 px-2 py-1 text-sm text-attention"
1146
+ title={t('agent.fileStagedTooltip')}
866
1147
  >
867
1148
  <FileTextIcon className="size-3.5" />
868
1149
  <span className="max-w-[120px] truncate">{file.name}</span>
@@ -885,6 +1166,7 @@ function StartWorkflowView({
885
1166
  value={inputValue}
886
1167
  onChange={(e) => setInputValue(e.target.value)}
887
1168
  onKeyDown={handleKeyDown}
1169
+ onPaste={handlePaste}
888
1170
  placeholder={resolvedPlaceholder}
889
1171
  disabled={isSending}
890
1172
  rows={2}
@@ -910,6 +1192,13 @@ function StartWorkflowView({
910
1192
  <UploadIcon className="size-4" />
911
1193
  </Button>
912
1194
  )}
1195
+ {interactive && (
1196
+ <AgentApprovalModeSelector
1197
+ mode={toolApprovalMode}
1198
+ onChange={setToolApprovalMode}
1199
+ disabled={isSending}
1200
+ />
1201
+ )}
913
1202
  </div>
914
1203
  <Button
915
1204
  onClick={startWorkflowWithMessage}
@@ -928,11 +1217,6 @@ function StartWorkflowView({
928
1217
  </Button>
929
1218
  </div>
930
1219
  </div>
931
- <div className="mx-auto mt-2 max-w-3xl text-center text-xs text-muted">
932
- {canStageFiles && stagedFiles.length > 0
933
- ? t('agent.filesStagedCount', { count: stagedFiles.length })
934
- : t('agent.enterToSend')}
935
- </div>
936
1220
  </div>
937
1221
  </div>
938
1222
  </div>
@@ -1028,6 +1312,7 @@ function ModernAgentConversationInner({
1028
1312
  conversationTab = false,
1029
1313
  pendingStartMessage,
1030
1314
  pendingStartTimestamp,
1315
+ initialToolApprovalMode,
1031
1316
  enablePlayback,
1032
1317
  showPlaybackToggle = true,
1033
1318
  }: ModernAgentConversationProps & { agentRunId: string }) {
@@ -1042,9 +1327,10 @@ function ModernAgentConversationInner({
1042
1327
  messages,
1043
1328
  streamingMessages,
1044
1329
  isCompleted,
1330
+ initialHistoryStatus,
1045
1331
  debugChunkFlash,
1046
1332
  addOptimisticMessage,
1047
- removeOptimisticMessages,
1333
+ updateOptimisticMessageStatus,
1048
1334
  reconnect: reconnectStream,
1049
1335
  agentRunStatus,
1050
1336
  workflowRunId,
@@ -1095,6 +1381,7 @@ function ModernAgentConversationInner({
1095
1381
  const conversationRef = useRef<HTMLDivElement | null>(null);
1096
1382
  const conversationLayoutRef = useRef<HTMLDivElement | null>(null);
1097
1383
  const [isSending, setIsSending] = useState(false);
1384
+ const [isCompactingContext, setIsCompactingContext] = useState(false);
1098
1385
  const [internalViewMode, setInternalViewMode] = useState<AgentConversationViewMode>('sliding');
1099
1386
  const viewMode = controlledViewMode ?? internalViewMode;
1100
1387
  const handleViewModeChange = useCallback(
@@ -1109,17 +1396,31 @@ function ModernAgentConversationInner({
1109
1396
  );
1110
1397
  const [isStopping, setIsStopping] = useState(false);
1111
1398
  const [isDragOver, setIsDragOver] = useState(false);
1399
+ const [activeWorkstream, setActiveWorkstream] = useState('all');
1112
1400
  const [playbackCursor, setPlaybackCursor] = useState<AgentChatPlaybackCursor>('live');
1113
1401
  const [isPlaybackToggleEnabled, setIsPlaybackToggleEnabled] = useState(false);
1114
1402
  const [playbackScrollRequestId, setPlaybackScrollRequestId] = useState(0);
1115
- const [activeWorkstreams, setActiveWorkstreams] = useState<ActiveWorkstreamEntry[]>([]);
1116
- const [completedWorkstreams, setCompletedWorkstreams] = useState<
1117
- Array<{ launch_id: string; workstream_id: string; status: WorkstreamInfo['status'] }>
1118
- >([]);
1403
+ const [queriedActiveWorkstreams, setQueriedActiveWorkstreams] = useState<ActiveWorkstreamEntry[]>([]);
1404
+ const [queriedCompletedWorkstreams, setQueriedCompletedWorkstreams] = useState<CompletedWorkstreamEntry[]>([]);
1405
+ const [isWorkstreamQueryUnavailable, setIsWorkstreamQueryUnavailable] = useState(false);
1406
+ const initialResolvedToolApprovalMode = useMemo<AgentToolApprovalMode | undefined>(
1407
+ () =>
1408
+ initialToolApprovalMode === undefined && interactive
1409
+ ? undefined
1410
+ : normalizeAgentToolApprovalMode(initialToolApprovalMode, interactive),
1411
+ [initialToolApprovalMode, interactive],
1412
+ );
1413
+ const [toolApprovalMode, setToolApprovalMode] = useState<AgentToolApprovalMode | undefined>(
1414
+ initialResolvedToolApprovalMode,
1415
+ );
1119
1416
  const workstreamFetchFailedRef = useRef(false);
1120
1417
  const dragCounterRef = useRef(0);
1121
1418
  const pendingPlaybackScrollRef = useRef(false);
1122
1419
 
1420
+ useEffect(() => {
1421
+ setToolApprovalMode(initialResolvedToolApprovalMode);
1422
+ }, [initialResolvedToolApprovalMode]);
1423
+
1123
1424
  // PERFORMANCE: Refs for values used inside useCallback to avoid re-creating the callback
1124
1425
  const isSendingRef = useRef(isSending);
1125
1426
  isSendingRef.current = isSending;
@@ -1193,63 +1494,139 @@ function ModernAgentConversationInner({
1193
1494
  };
1194
1495
  }, [plans, activePlanIndex, workstreamStatusMap]);
1195
1496
 
1196
- const messageDerivedActiveWorkstreams = useMemo(() => deriveActiveWorkstreamsFromMessages(messages), [messages]);
1497
+ const messageDerivedWorkstreams = useMemo(() => deriveWorkstreamsFromMessages(messages), [messages]);
1197
1498
 
1198
1499
  const panelWorkstreams = useMemo<WorkstreamInfo[]>(() => {
1199
- const running: WorkstreamInfo[] =
1200
- activeWorkstreams.length > 0
1201
- ? activeWorkstreams.map((ws) => ({
1202
- workstream_id: ws.workstream_id,
1203
- launch_id: ws.launch_id,
1204
- interaction: ws.interaction,
1205
- elapsed_ms: ws.elapsed_ms,
1206
- deadline_ms: ws.deadline_ms,
1207
- remaining_ms: Math.max(0, ws.deadline_ms - ws.elapsed_ms),
1208
- status: ws.status,
1209
- phase: ws.latest_progress?.phase,
1210
- child_workflow_id: ws.child_workflow_id,
1211
- child_workflow_run_id: ws.child_workflow_run_id,
1212
- }))
1213
- : messageDerivedActiveWorkstreams;
1214
- const runningWorkstreamIds = new Set(running.map((ws) => ws.workstream_id));
1215
- const completed: WorkstreamInfo[] = completedWorkstreams
1216
- .filter((ws) => !runningWorkstreamIds.has(ws.workstream_id))
1217
- .map((ws) => ({
1218
- workstream_id: ws.workstream_id,
1219
- launch_id: ws.launch_id,
1220
- elapsed_ms: 0,
1221
- deadline_ms: 0,
1222
- remaining_ms: 0,
1223
- status: ws.status,
1224
- }));
1225
- return [...running, ...completed];
1226
- }, [activeWorkstreams, completedWorkstreams, messageDerivedActiveWorkstreams]);
1500
+ const merged = [...messageDerivedWorkstreams];
1501
+ for (const ws of queriedCompletedWorkstreams) {
1502
+ mergeWorkstreamInfo(merged, completedWorkstreamEntryToInfo(ws));
1503
+ }
1504
+ for (const ws of queriedActiveWorkstreams) {
1505
+ mergeWorkstreamInfo(merged, activeWorkstreamEntryToInfo(ws));
1506
+ }
1507
+ return merged
1508
+ .map((workstream, index) => ({ workstream, index }))
1509
+ .sort((a, b) => {
1510
+ const activeDelta =
1511
+ Number(!isActiveWorkstreamStatus(a.workstream.status)) -
1512
+ Number(!isActiveWorkstreamStatus(b.workstream.status));
1513
+ return activeDelta || a.index - b.index;
1514
+ })
1515
+ .map(({ workstream }) => workstream);
1516
+ }, [messageDerivedWorkstreams, queriedActiveWorkstreams, queriedCompletedWorkstreams]);
1517
+
1518
+ const composerActiveWorkstreams = useMemo(
1519
+ () => panelWorkstreams.filter((ws) => isActiveWorkstreamStatus(ws.status)),
1520
+ [panelWorkstreams],
1521
+ );
1227
1522
 
1228
1523
  const activeTaskCount = useMemo(
1229
- () => panelWorkstreams.filter((ws) => ws.status === 'running').length,
1230
- [panelWorkstreams],
1524
+ () => composerActiveWorkstreams.filter((ws) => ws.status === 'running').length,
1525
+ [composerActiveWorkstreams],
1231
1526
  );
1232
1527
 
1528
+ const activeWorkstreamDisplayName = useMemo(() => {
1529
+ if (activeWorkstream === 'all') return undefined;
1530
+ const workstream = panelWorkstreams.find(
1531
+ (ws) => ws.workstream_id === activeWorkstream || ws.launch_id === activeWorkstream,
1532
+ );
1533
+ if (!workstream) return getWorkstreamDisplayName(activeWorkstream);
1534
+ return getWorkstreamDisplayName(workstream.workstream_id, workstream.interaction);
1535
+ }, [activeWorkstream, panelWorkstreams]);
1536
+
1537
+ const composerPlaceholder = useMemo(() => {
1538
+ if (placeholder) return placeholder;
1539
+ if (activeWorkstreamDisplayName) {
1540
+ return t('agent.messageMainAgentWhileViewingWorkstream', { workstream: activeWorkstreamDisplayName });
1541
+ }
1542
+ return undefined;
1543
+ }, [activeWorkstreamDisplayName, placeholder, t]);
1544
+
1233
1545
  const canShowPlaybackToggle = showPlaybackToggle && enablePlayback === undefined && isAgentChatPlaybackAvailable();
1234
1546
  const isPlaybackEnabled = enablePlayback ?? (isAgentChatPlaybackEnabled() || isPlaybackToggleEnabled);
1547
+ const playbackSourceMessages = useMemo(() => {
1548
+ const visibleMessages = hiddenMessageTypes?.length
1549
+ ? messages.filter((message) => !hiddenMessageTypes.includes(message.type))
1550
+ : messages;
1551
+ return filterMessagesForActiveWorkstream(visibleMessages, activeWorkstream);
1552
+ }, [activeWorkstream, hiddenMessageTypes, messages]);
1235
1553
  const playbackState = useMemo(
1236
- () => createPlaybackState(messages, playbackCursor, isPlaybackEnabled),
1237
- [isPlaybackEnabled, messages, playbackCursor],
1554
+ () => createPlaybackState(playbackSourceMessages, playbackCursor, isPlaybackEnabled),
1555
+ [isPlaybackEnabled, playbackCursor, playbackSourceMessages],
1238
1556
  );
1239
1557
  const clampedPlaybackCursor = playbackState.cursor;
1240
1558
  const isPlaybackLive = playbackState.isLive;
1559
+ const isPlaybackAtLatest =
1560
+ isPlaybackEnabled && !isPlaybackLive && playbackState.cursorIndex === playbackSourceMessages.length - 1;
1241
1561
  const displayedMessages = playbackState.displayedMessages;
1242
1562
  const displayedStreamingMessages = isPlaybackLive ? streamingMessages : EMPTY_STREAMING_MESSAGES;
1563
+ const playbackDerivedWorkstreams = useMemo(
1564
+ () => deriveWorkstreamsFromMessages(displayedMessages),
1565
+ [displayedMessages],
1566
+ );
1567
+ const playbackActiveWorkstreams = useMemo(
1568
+ () => playbackDerivedWorkstreams.filter((ws) => isActiveWorkstreamStatus(ws.status)),
1569
+ [playbackDerivedWorkstreams],
1570
+ );
1243
1571
  const effectiveIsCompleted = useMemo(() => isCompleted || !isInProgress(messages), [isCompleted, messages]);
1244
- const displayedIsCompleted = isPlaybackLive ? effectiveIsCompleted : false;
1572
+ const displayedIsCompleted = isPlaybackLive || isPlaybackAtLatest ? effectiveIsCompleted : false;
1245
1573
  const pendingRequestInputMessage = useMemo(
1246
1574
  () => getPendingRequestInputMessage(displayedMessages),
1247
1575
  [displayedMessages],
1248
1576
  );
1249
1577
  const shouldShowRequestInputOverlay = Boolean(pendingRequestInputMessage) && !isFailed;
1250
1578
  const isViewingPlaybackHistory = isPlaybackEnabled && !isPlaybackLive;
1251
- const shouldShowLiveRequestInputOverlay = shouldShowRequestInputOverlay && !isViewingPlaybackHistory;
1252
1579
  const shouldRenderLiveMessageInputArea = shouldRenderMessageInputArea && !isViewingPlaybackHistory;
1580
+ const contextWindowUsage = useMemo(() => toContextWindowUsage(messages), [messages]);
1581
+ // The run is still "alive" while it waits for user input (idle on ask_user), so keep the
1582
+ // context-usage indicator visible then — it's only truly hidden once the run terminates.
1583
+ const canCompactContext =
1584
+ allowWorkflowControl && !isWorkflowTerminal && isPlaybackLive && Boolean(contextWindowUsage);
1585
+
1586
+ useEffect(() => {
1587
+ debugAgentChat('conversation render state', {
1588
+ agentRunId,
1589
+ messageCount: messages.length,
1590
+ playbackMessageCount: playbackSourceMessages.length,
1591
+ displayedMessageCount: displayedMessages.length,
1592
+ streamingCount: streamingMessages.size,
1593
+ displayedStreamingCount: displayedStreamingMessages.size,
1594
+ initialHistoryStatus,
1595
+ isCompleted,
1596
+ effectiveIsCompleted,
1597
+ displayedIsCompleted,
1598
+ agentRunStatus,
1599
+ effectiveWorkflowStatus,
1600
+ isWorkflowTerminal,
1601
+ playbackCursor: clampedPlaybackCursor,
1602
+ isPlaybackEnabled,
1603
+ isPlaybackLive,
1604
+ isPlaybackAtLatest,
1605
+ activeWorkstream,
1606
+ showInitialRequest: initialHistoryStatus === 'empty' && messages.length === 0,
1607
+ pendingStartVisible: messages.length === 0 && !effectiveIsCompleted && Boolean(pendingStartMessage),
1608
+ });
1609
+ }, [
1610
+ activeWorkstream,
1611
+ agentRunId,
1612
+ agentRunStatus,
1613
+ clampedPlaybackCursor,
1614
+ displayedIsCompleted,
1615
+ displayedMessages.length,
1616
+ displayedStreamingMessages.size,
1617
+ effectiveIsCompleted,
1618
+ effectiveWorkflowStatus,
1619
+ initialHistoryStatus,
1620
+ isCompleted,
1621
+ isPlaybackAtLatest,
1622
+ isPlaybackEnabled,
1623
+ isPlaybackLive,
1624
+ isWorkflowTerminal,
1625
+ messages.length,
1626
+ pendingStartMessage,
1627
+ playbackSourceMessages.length,
1628
+ streamingMessages.size,
1629
+ ]);
1253
1630
 
1254
1631
  const handleTogglePlayback = useCallback(() => {
1255
1632
  setIsPlaybackToggleEnabled((prev) => !prev);
@@ -1257,8 +1634,8 @@ function ModernAgentConversationInner({
1257
1634
 
1258
1635
  const handleChangePlaybackCursor = useCallback(
1259
1636
  (nextCursor: AgentChatPlaybackCursor) => {
1260
- const currentIndex = getPlaybackCursorIndex(clampedPlaybackCursor, messages.length);
1261
- const nextIndex = getPlaybackCursorIndex(nextCursor, messages.length);
1637
+ const currentIndex = getPlaybackCursorIndex(clampedPlaybackCursor, playbackSourceMessages.length);
1638
+ const nextIndex = getPlaybackCursorIndex(nextCursor, playbackSourceMessages.length);
1262
1639
  const returningToLive = nextCursor === 'live' && clampedPlaybackCursor !== 'live';
1263
1640
  if (isPlaybackEnabled && (nextIndex > currentIndex || returningToLive)) {
1264
1641
  pendingPlaybackScrollRef.current = true;
@@ -1266,7 +1643,7 @@ function ModernAgentConversationInner({
1266
1643
  }
1267
1644
  setPlaybackCursor(nextCursor);
1268
1645
  },
1269
- [clampedPlaybackCursor, isPlaybackEnabled, messages.length],
1646
+ [clampedPlaybackCursor, isPlaybackEnabled, playbackSourceMessages.length],
1270
1647
  );
1271
1648
 
1272
1649
  useEffect(() => {
@@ -1419,45 +1796,76 @@ function ModernAgentConversationInner({
1419
1796
  onWorkstreamStatusChange?.(workstreamStatusMap);
1420
1797
  }, [workstreamStatusMap, onWorkstreamStatusChange]);
1421
1798
 
1422
- // Poll active workstreams from backend query for right-panel visibility and details.
1423
1799
  useEffect(() => {
1424
- const shouldPoll = !effectiveIsCompleted || activeWorkstreams.length > 0;
1800
+ void agentRunId;
1801
+ workstreamFetchFailedRef.current = false;
1802
+ setIsWorkstreamQueryUnavailable(false);
1803
+ setQueriedActiveWorkstreams([]);
1804
+ setQueriedCompletedWorkstreams([]);
1805
+ }, [agentRunId]);
1806
+
1807
+ // Poll the backend query only as live enrichment. Persisted messages remain the
1808
+ // source of truth for the right-panel history once a workflow can no longer be queried.
1809
+ useEffect(() => {
1810
+ const shouldPoll =
1811
+ initialHistoryStatus !== 'loading' &&
1812
+ !effectiveIsCompleted &&
1813
+ !isWorkflowTerminal &&
1814
+ !isWorkstreamQueryUnavailable;
1815
+ debugAgentChat('active workstreams poll state', {
1816
+ agentRunId,
1817
+ shouldPoll,
1818
+ initialHistoryStatus,
1819
+ effectiveIsCompleted,
1820
+ isWorkflowTerminal,
1821
+ isWorkstreamQueryUnavailable,
1822
+ });
1425
1823
  if (!shouldPoll) {
1426
- setActiveWorkstreams((prev) => (prev.length === 0 ? prev : []));
1824
+ setQueriedActiveWorkstreams((prev) => (prev.length === 0 ? prev : []));
1427
1825
  return;
1428
1826
  }
1429
1827
 
1430
1828
  let isCancelled = false;
1829
+ let isFetchInFlight = false;
1431
1830
 
1432
1831
  const fetchActiveWorkstreams = async () => {
1832
+ if (isFetchInFlight) {
1833
+ debugAgentChat('active workstreams fetch skipped while previous request is pending', { agentRunId });
1834
+ return;
1835
+ }
1836
+
1837
+ isFetchInFlight = true;
1433
1838
  try {
1839
+ debugAgentChat('active workstreams fetch start', { agentRunId });
1434
1840
  const result = await client.agents.getActiveWorkstreams(agentRunId);
1435
1841
  if (isCancelled) return;
1436
- setActiveWorkstreams(result.running ?? []);
1437
- const completed =
1438
- (
1439
- result as unknown as {
1440
- completed?: Array<{ launch_id: string; workstream_id: string; status: string }>;
1441
- }
1442
- ).completed ?? [];
1443
- setCompletedWorkstreams(
1444
- completed.map((c) => ({
1445
- launch_id: c.launch_id,
1446
- workstream_id: c.workstream_id,
1447
- status:
1448
- c.status === 'canceled' || c.status === 'failed' || c.status === 'timeout'
1449
- ? c.status
1450
- : 'completed',
1451
- })),
1452
- );
1842
+ debugAgentChat('active workstreams fetch success', {
1843
+ agentRunId,
1844
+ runningCount: result.running?.length ?? 0,
1845
+ completedCount: result.completed?.length ?? 0,
1846
+ unavailable: result.unavailable === true,
1847
+ });
1848
+ setQueriedActiveWorkstreams(result.running ?? []);
1849
+ setQueriedCompletedWorkstreams(result.completed ?? []);
1850
+ if (result.unavailable) {
1851
+ setIsWorkstreamQueryUnavailable(true);
1852
+ return;
1853
+ }
1453
1854
  workstreamFetchFailedRef.current = false;
1454
1855
  } catch (error) {
1455
1856
  if (isCancelled) return;
1456
- setActiveWorkstreams([]);
1857
+ setQueriedActiveWorkstreams((prev) => (prev.length === 0 ? prev : []));
1858
+ setIsWorkstreamQueryUnavailable(true);
1859
+ debugAgentChat('active workstreams fetch failed', {
1860
+ agentRunId,
1861
+ error: error instanceof Error ? error.message : String(error),
1862
+ });
1457
1863
  if (!workstreamFetchFailedRef.current) {
1458
1864
  console.warn('Failed to fetch active workstreams:', error);
1459
1865
  workstreamFetchFailedRef.current = true;
1460
1866
  }
1867
+ } finally {
1868
+ isFetchInFlight = false;
1461
1869
  }
1462
1870
  };
1463
1871
 
@@ -1468,7 +1876,14 @@ function ModernAgentConversationInner({
1468
1876
  isCancelled = true;
1469
1877
  window.clearInterval(pollHandle);
1470
1878
  };
1471
- }, [client.agents, agentRunId, effectiveIsCompleted, activeWorkstreams.length]);
1879
+ }, [
1880
+ client.agents,
1881
+ agentRunId,
1882
+ effectiveIsCompleted,
1883
+ initialHistoryStatus,
1884
+ isWorkflowTerminal,
1885
+ isWorkstreamQueryUnavailable,
1886
+ ]);
1472
1887
 
1473
1888
  // Notify parent when input availability is determined
1474
1889
  useEffect(() => {
@@ -1496,7 +1911,7 @@ function ModernAgentConversationInner({
1496
1911
 
1497
1912
  // Send a message to the agent
1498
1913
  const handleSendMessage = useCallback(
1499
- (message: string) => {
1914
+ (message: string, inputMetadata?: Record<string, unknown>) => {
1500
1915
  const trimmed = message.trim();
1501
1916
  if (!trimmed || isSendingRef.current) return;
1502
1917
 
@@ -1535,7 +1950,7 @@ function ModernAgentConversationInner({
1535
1950
  type: AgentMessageType.QUESTION,
1536
1951
  message: messageContent,
1537
1952
  workstream_id: 'main',
1538
- details: { _optimistic: true, _messageId: messageId },
1953
+ details: { _optimistic: true, _messageId: messageId, _deliveryStatus: 'sending' },
1539
1954
  };
1540
1955
 
1541
1956
  addOptimisticMessage(optimisticMessage);
@@ -1543,15 +1958,23 @@ function ModernAgentConversationInner({
1543
1958
  const metadata = {
1544
1959
  ...(attachedDocs.length > 0 ? { attached_docs: attachedDocs.map((d) => d.id) } : {}),
1545
1960
  ...contextMetadata,
1961
+ ...inputMetadata,
1962
+ id: messageId,
1546
1963
  _messageId: messageId,
1547
1964
  };
1548
1965
 
1549
1966
  const sendUserInput = () =>
1550
1967
  client.agents.sendSignal(agentRunId, 'UserInput', {
1551
1968
  message: messageContent,
1969
+ client_message_id: messageId,
1552
1970
  metadata,
1553
1971
  } as UserInputSignal);
1554
1972
 
1973
+ const markReceived = () => {
1974
+ updateOptimisticMessageStatus(messageId, 'received');
1975
+ onAttachmentsSent?.();
1976
+ };
1977
+
1555
1978
  // When the workflow has already completed, restart it first so it resumes
1556
1979
  // from the existing conversation history, then deliver the message. Temporal
1557
1980
  // buffers the signal until the new run is ready to receive it. We reconnect
@@ -1560,17 +1983,13 @@ function ModernAgentConversationInner({
1560
1983
  const deliver = isWorkflowTerminalRef.current
1561
1984
  ? client.agents.restart(agentRunId).then(() => {
1562
1985
  reconnectStream();
1563
- return sendUserInput().then(() => {
1564
- onAttachmentsSent?.();
1565
- });
1986
+ return sendUserInput().then(markReceived);
1566
1987
  })
1567
- : sendUserInput().then(() => {
1568
- onAttachmentsSent?.();
1569
- });
1988
+ : sendUserInput().then(markReceived);
1570
1989
 
1571
1990
  deliver
1572
1991
  .catch((err) => {
1573
- removeOptimisticMessages((m) => m.details?._messageId === messageId);
1992
+ updateOptimisticMessageStatus(messageId, 'failed');
1574
1993
  toast({
1575
1994
  status: 'error',
1576
1995
  title: t('agent.failedToSend'),
@@ -1591,11 +2010,92 @@ function ModernAgentConversationInner({
1591
2010
  onAttachmentsSent,
1592
2011
  reconnectStream,
1593
2012
  addOptimisticMessage,
1594
- removeOptimisticMessages,
2013
+ updateOptimisticMessageStatus,
1595
2014
  t,
1596
2015
  ],
1597
2016
  );
1598
2017
 
2018
+ // After the user connects an MCP server requested via request_mcp_connection, flag the
2019
+ // conversation for tool re-discovery and resume it with a confirmation message so the agent
2020
+ // continues automatically with the newly-available tools.
2021
+ const handleMcpConnected = useCallback(
2022
+ async (cfg: McpConnectUxConfig) => {
2023
+ // Await the dirty-flag signal BEFORE sending the follow-up message so Temporal records
2024
+ // McpConfigChanged ahead of the UserInput. Otherwise the resume turn could run before
2025
+ // the flag is set and use the stale tool catalog. An omitted disabled list preserves
2026
+ // the user's current deactivation set.
2027
+ try {
2028
+ await client.agents.sendSignal(agentRunId, 'McpConfigChanged', {});
2029
+ } catch (err: unknown) {
2030
+ console.error('Failed to signal MCP config change', err);
2031
+ }
2032
+ handleSendMessage(t('agent.mcpConnectedMessage', { name: cfg.name }));
2033
+ },
2034
+ [client, agentRunId, handleSendMessage, t],
2035
+ );
2036
+
2037
+ // Per-conversation MCP deactivation set, seeded from the run and updated live via the
2038
+ // MCP action menu in the composer toolbar. Changes are pushed to the running workflow
2039
+ // (which re-discovers tools at the next turn).
2040
+ const [mcpDisabled, setMcpDisabled] = useState<string[] | undefined>(undefined);
2041
+ useEffect(() => {
2042
+ let cancelled = false;
2043
+ client.agents
2044
+ .retrieve(agentRunId)
2045
+ .then((run) => {
2046
+ if (!cancelled) {
2047
+ setMcpDisabled(run.disabled_mcp_collections);
2048
+ if (run.tool_approval_mode !== undefined || run.interactive !== undefined) {
2049
+ setToolApprovalMode(normalizeAgentToolApprovalMode(run.tool_approval_mode, run.interactive));
2050
+ }
2051
+ }
2052
+ })
2053
+ .catch(() => {
2054
+ /* best-effort: toggles default to all-active */
2055
+ });
2056
+ return () => {
2057
+ cancelled = true;
2058
+ };
2059
+ }, [client, agentRunId]);
2060
+
2061
+ const handleMcpDisabledChange = useCallback(
2062
+ (disabled: string[]) => {
2063
+ const next = disabled.length > 0 ? disabled : undefined;
2064
+ setMcpDisabled(next);
2065
+ client.agents
2066
+ .sendSignal(agentRunId, 'McpConfigChanged', { disabled_mcp_collections: disabled })
2067
+ .catch((err: unknown) => console.error('Failed to signal MCP config change', err));
2068
+ },
2069
+ [client, agentRunId],
2070
+ );
2071
+
2072
+ const handleMcpConnectionChange = useCallback(() => {
2073
+ // A connect/disconnect doesn't change the activation set — omit disabled_mcp_collections so
2074
+ // the workflow preserves the current denylist (rather than overwriting it with possibly
2075
+ // stale UI state) and just re-discovers tools.
2076
+ client.agents
2077
+ .sendSignal(agentRunId, 'McpConfigChanged', {})
2078
+ .catch((err: unknown) => console.error('Failed to signal MCP config change', err));
2079
+ }, [client, agentRunId]);
2080
+
2081
+ const handleToolApprovalModeChange = useCallback(
2082
+ (mode: AgentToolApprovalMode) => {
2083
+ const nextMode = normalizeAgentToolApprovalMode(mode, interactive);
2084
+ setToolApprovalMode(nextMode);
2085
+ client.agents
2086
+ .sendSignal(agentRunId, 'ToolApprovalModeChanged', { mode: nextMode })
2087
+ .catch((err: unknown) => {
2088
+ toast({
2089
+ status: 'error',
2090
+ title: t('agent.approvalMode.changeFailed'),
2091
+ description: err instanceof Error ? err.message : t('agent.unknownError'),
2092
+ duration: 3000,
2093
+ });
2094
+ });
2095
+ },
2096
+ [agentRunId, client, interactive, t, toast],
2097
+ );
2098
+
1599
2099
  // Drag and drop handlers for full-panel file upload
1600
2100
  const handleDragEnter = useCallback(
1601
2101
  (e: React.DragEvent) => {
@@ -1652,29 +2152,67 @@ function ModernAgentConversationInner({
1652
2152
  const handleStopWorkflow = useCallback(async () => {
1653
2153
  if (isStopping) return;
1654
2154
 
2155
+ const messageId = `stop-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2156
+ const optimisticMessage: AgentMessage = {
2157
+ timestamp: Date.now(),
2158
+ workflow_run_id: agentRunId,
2159
+ type: AgentMessageType.IDLE,
2160
+ message: 'Stopped. Waiting for your command...',
2161
+ workstream_id: 'main',
2162
+ details: {
2163
+ _optimistic: true,
2164
+ _messageId: messageId,
2165
+ _deliveryStatus: 'sending',
2166
+ display_role: 'user_stopped',
2167
+ status_reason: 'user_stopped',
2168
+ },
2169
+ };
2170
+
2171
+ addOptimisticMessage(optimisticMessage);
1655
2172
  setIsStopping(true);
1656
2173
  try {
1657
2174
  await client.agents.sendSignal(agentRunId, 'Stop', {
1658
2175
  message: 'User requested stop',
1659
- });
1660
-
2176
+ client_message_id: messageId,
2177
+ metadata: {
2178
+ id: messageId,
2179
+ _messageId: messageId,
2180
+ },
2181
+ } as StopSignal);
2182
+
2183
+ updateOptimisticMessageStatus(messageId, 'received');
2184
+ } catch (err) {
1661
2185
  toast({
1662
- status: 'info',
1663
- title: t('agent.agentInterrupted'),
1664
- description: t('agent.typeNewInstructions'),
2186
+ status: 'error',
2187
+ title: t('agent.failedToInterrupt'),
2188
+ description: err instanceof Error ? err.message : t('agent.unknownError'),
1665
2189
  duration: 3000,
1666
2190
  });
2191
+ updateOptimisticMessageStatus(messageId, 'failed');
2192
+ } finally {
2193
+ setIsStopping(false);
2194
+ }
2195
+ }, [isStopping, agentRunId, addOptimisticMessage, client.agents, toast, t, updateOptimisticMessageStatus]);
2196
+
2197
+ const handleCompactContext = useCallback(async () => {
2198
+ if (isCompactingContext || effectiveIsCompleted || !isPlaybackLive) return;
2199
+
2200
+ setIsCompactingContext(true);
2201
+ try {
2202
+ await client.agents.sendSignal(agentRunId, 'TriggerCheckpoint', {
2203
+ reason: 'manual user request',
2204
+ });
1667
2205
  } catch (err) {
1668
2206
  toast({
1669
2207
  status: 'error',
1670
- title: t('agent.failedToInterrupt'),
2208
+ title: t('agent.failedToCompactContext'),
1671
2209
  description: err instanceof Error ? err.message : t('agent.unknownError'),
1672
2210
  duration: 3000,
1673
2211
  });
1674
2212
  } finally {
1675
- setIsStopping(false);
2213
+ setIsCompactingContext(false);
1676
2214
  }
1677
- }, [isStopping, client, agentRunId, toast, t]);
2215
+ }, [agentRunId, client.agents, effectiveIsCompleted, isCompactingContext, isPlaybackLive, t, toast]);
1678
2216
 
1679
2217
  // Expose stop handler to external callers via ref
1680
2218
  useEffect(() => {
@@ -1869,7 +2407,7 @@ function ModernAgentConversationInner({
1869
2407
  <div className="flex flex-shrink-0 justify-end px-2 py-1.5">
1870
2408
  <AgentChatPlaybackControls
1871
2409
  cursor={clampedPlaybackCursor}
1872
- messages={messages}
2410
+ messages={playbackSourceMessages}
1873
2411
  onChangeCursor={handleChangePlaybackCursor}
1874
2412
  />
1875
2413
  </div>
@@ -1910,19 +2448,27 @@ function ModernAgentConversationInner({
1910
2448
  initialRequestSchema={initialRequestSchema}
1911
2449
  initialRequestTitle={initialRequestTitle}
1912
2450
  initialRequestTemplate={initialRequestTemplate}
2451
+ showInitialRequest={initialHistoryStatus === 'empty' && messages.length === 0}
1913
2452
  hiddenMessageTypes={hiddenMessageTypes}
1914
2453
  disableAutoScroll={!isPlaybackLive}
1915
- renderRequestInputControls={isPlaybackLive && !shouldShowLiveRequestInputOverlay}
2454
+ renderRequestInputControls={!shouldShowRequestInputOverlay}
2455
+ activeWorkstream={activeWorkstream}
2456
+ onActiveWorkstreamChange={setActiveWorkstream}
1916
2457
  />
1917
2458
  )}
1918
2459
 
1919
- {shouldShowLiveRequestInputOverlay ? (
2460
+ {shouldShowRequestInputOverlay ? (
1920
2461
  <AgentRequestInputOverlay
1921
2462
  message={pendingRequestInputMessage}
1922
- onSendMessage={handleSendMessage}
2463
+ onSendMessage={isPlaybackLive ? handleSendMessage : undefined}
2464
+ onMcpConnected={isPlaybackLive ? handleMcpConnected : undefined}
1923
2465
  disabled={isUploading || !isPlaybackLive}
1924
2466
  isLoading={isSending || isUploading}
1925
2467
  />
2468
+ ) : isViewingPlaybackHistory && playbackActiveWorkstreams.length > 0 ? (
2469
+ <div className="flex-shrink-0 pb-safe-area">
2470
+ <ActiveWorkstreamsSummary activeWorkstreams={playbackActiveWorkstreams} />
2471
+ </div>
1926
2472
  ) : (
1927
2473
  shouldRenderLiveMessageInputArea && (
1928
2474
  <div className="flex-shrink-0 pb-safe-area">
@@ -1959,14 +2505,35 @@ function ModernAgentConversationInner({
1959
2505
  <MessageInput
1960
2506
  onSend={handleSendMessage}
1961
2507
  onStop={allowWorkflowControl ? handleStopWorkflow : undefined}
2508
+ approvalModeSlot={
2509
+ interactive && toolApprovalMode ? (
2510
+ <AgentApprovalModeSelector
2511
+ mode={toolApprovalMode}
2512
+ onChange={handleToolApprovalModeChange}
2513
+ disabled={
2514
+ !isPlaybackLive || isWorkflowTerminal || !allowWorkflowControl
2515
+ }
2516
+ />
2517
+ ) : undefined
2518
+ }
2519
+ mcpSlot={
2520
+ <McpConnectionsActionMenu
2521
+ disabledCollections={mcpDisabled}
2522
+ onChange={handleMcpDisabledChange}
2523
+ onConnectionChange={handleMcpConnectionChange}
2524
+ />
2525
+ }
1962
2526
  disabled={isUploading || !isPlaybackLive}
1963
2527
  isSending={isSending || isUploading}
1964
2528
  isStopping={isStopping}
1965
2529
  isStreaming={!effectiveIsCompleted}
1966
2530
  isCompleted={effectiveIsCompleted}
2531
+ contextWindowUsage={canCompactContext ? contextWindowUsage : undefined}
2532
+ onCompactContext={canCompactContext ? handleCompactContext : undefined}
2533
+ isCompactingContext={isCompactingContext}
1967
2534
  activeTaskCount={activeTaskCount}
1968
- activeWorkstreams={panelWorkstreams}
1969
- placeholder={placeholder ?? 'Type your message...'}
2535
+ activeWorkstreams={composerActiveWorkstreams}
2536
+ placeholder={composerPlaceholder}
1970
2537
  onFilesSelected={canUploadFiles ? handleFileUpload : undefined}
1971
2538
  uploadedFiles={uploadedFiles}
1972
2539
  onRemoveFile={onRemoveFile}