@vertesia/ui 1.5.0-dev.20260910.052912Z → 1.5.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 (320) hide show
  1. package/lib/core/components/Link.js +1 -1
  2. package/lib/core/components/Link.js.map +1 -1
  3. package/lib/core/components/SelectList.d.ts.map +1 -1
  4. package/lib/core/components/SelectList.js +3 -3
  5. package/lib/core/components/SelectList.js.map +1 -1
  6. package/lib/core/components/SidePanel.d.ts.map +1 -1
  7. package/lib/core/components/SidePanel.js +2 -1
  8. package/lib/core/components/SidePanel.js.map +1 -1
  9. package/lib/core/components/Spinner.js +1 -1
  10. package/lib/core/components/Spinner.js.map +1 -1
  11. package/lib/core/components/TagsInput.js +7 -7
  12. package/lib/core/components/TagsInput.js.map +1 -1
  13. package/lib/core/components/shadcn/badge.js +1 -1
  14. package/lib/core/components/shadcn/badge.js.map +1 -1
  15. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  16. package/lib/core/components/shadcn/button.js +2 -2
  17. package/lib/core/components/shadcn/button.js.map +1 -1
  18. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +2 -2
  19. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js.map +1 -1
  20. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
  21. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js.map +1 -1
  22. package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js +1 -1
  23. package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js.map +1 -1
  24. package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js +1 -1
  25. package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js.map +1 -1
  26. package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
  27. package/lib/core/components/shadcn/filters/filter/dateFilter.js.map +1 -1
  28. package/lib/core/components/shadcn/filters/filters.js +1 -1
  29. package/lib/core/components/shadcn/filters/filters.js.map +1 -1
  30. package/lib/core/components/shadcn/overflowTabs.js +2 -2
  31. package/lib/core/components/shadcn/overflowTabs.js.map +1 -1
  32. package/lib/core/components/shadcn/tabs.js +1 -1
  33. package/lib/core/components/shadcn/tabs.js.map +1 -1
  34. package/lib/features/activity-doc/ActivityDoc.js +1 -1
  35. package/lib/features/activity-doc/ActivityDoc.js.map +1 -1
  36. package/lib/features/agent/chat/AskUserWidget.d.ts +1 -0
  37. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  38. package/lib/features/agent/chat/AskUserWidget.js +15 -4
  39. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  40. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  41. package/lib/features/agent/chat/ModernAgentConversation.js +51 -17
  42. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  43. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  44. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +5 -3
  45. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  46. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +5 -1
  47. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  48. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +9 -2
  49. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  50. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  51. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -2
  52. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  53. package/lib/features/agent/chat/hooks/useAgentPlans.d.ts +1 -1
  54. package/lib/features/agent/chat/hooks/useAgentPlans.d.ts.map +1 -1
  55. package/lib/features/agent/chat/hooks/useAgentPlans.js +5 -5
  56. package/lib/features/agent/chat/hooks/useAgentPlans.js.map +1 -1
  57. package/lib/features/layout/GenericPageNavHeader.js +1 -1
  58. package/lib/features/layout/GenericPageNavHeader.js.map +1 -1
  59. package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -1
  60. package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
  61. package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
  62. package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
  63. package/lib/features/oauth/McpConnectionsButton.d.ts +2 -2
  64. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
  65. package/lib/features/oauth/McpConnectionsButton.js +9 -7
  66. package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
  67. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
  68. package/lib/features/oauth/McpConnectionsDialog.js +7 -3
  69. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
  70. package/lib/features/oauth/useMcpConnections.d.ts +4 -2
  71. package/lib/features/oauth/useMcpConnections.d.ts.map +1 -1
  72. package/lib/features/oauth/useMcpConnections.js +15 -9
  73. package/lib/features/oauth/useMcpConnections.js.map +1 -1
  74. package/lib/features/permissions/UserPermissionsProvider.js +1 -1
  75. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  76. package/lib/features/store/objects/DocumentPreviewPanel.js +5 -5
  77. package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
  78. package/lib/features/store/objects/components/DocumentInput.js +1 -1
  79. package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
  80. package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
  81. package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -0
  82. package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
  83. package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -2
  84. package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
  85. package/lib/features/store/objects/selection/SelectionActions.js +6 -3
  86. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  87. package/lib/features/store/objects/selection/actions/RunIntakeAction.d.ts +8 -0
  88. package/lib/features/store/objects/selection/actions/RunIntakeAction.d.ts.map +1 -0
  89. package/lib/features/store/objects/selection/actions/RunIntakeAction.js +65 -0
  90. package/lib/features/store/objects/selection/actions/RunIntakeAction.js.map +1 -0
  91. package/lib/features/store/objects/selection/actions/index.d.ts +1 -0
  92. package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
  93. package/lib/features/store/objects/selection/actions/index.js +1 -0
  94. package/lib/features/store/objects/selection/actions/index.js.map +1 -1
  95. package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
  96. package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
  97. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  98. package/lib/features/store/types/ContentObjectTypesTable.js +11 -3
  99. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  100. package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
  101. package/lib/features/store/types/IntakePolicyEditor.js +14 -14
  102. package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
  103. package/lib/features/store/types/TypeRegistry.d.ts +275 -150
  104. package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
  105. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +275 -150
  106. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
  107. package/lib/features/user/UserInfo.d.ts +2 -0
  108. package/lib/features/user/UserInfo.d.ts.map +1 -1
  109. package/lib/features/user/UserInfo.js +1 -1
  110. package/lib/features/user/UserInfo.js.map +1 -1
  111. package/lib/i18n/locales/ar.json +28 -7
  112. package/lib/i18n/locales/de.json +24 -7
  113. package/lib/i18n/locales/en.json +24 -7
  114. package/lib/i18n/locales/es.json +24 -7
  115. package/lib/i18n/locales/fr.json +25 -7
  116. package/lib/i18n/locales/it.json +24 -7
  117. package/lib/i18n/locales/ja.json +24 -7
  118. package/lib/i18n/locales/ko.json +24 -7
  119. package/lib/i18n/locales/pt.json +24 -7
  120. package/lib/i18n/locales/ru.json +26 -7
  121. package/lib/i18n/locales/tr.json +24 -7
  122. package/lib/i18n/locales/zh-TW.json +24 -7
  123. package/lib/i18n/locales/zh.json +24 -7
  124. package/lib/router/RouteComponent.d.ts.map +1 -1
  125. package/lib/router/RouteComponent.js +15 -12
  126. package/lib/router/RouteComponent.js.map +1 -1
  127. package/lib/session/UserSession.d.ts.map +1 -1
  128. package/lib/session/UserSession.js +4 -0
  129. package/lib/session/UserSession.js.map +1 -1
  130. package/lib/shell/SplashScreen.js +1 -1
  131. package/lib/shell/SplashScreen.js.map +1 -1
  132. package/lib/shell/apps/AppInstallationProvider.d.ts +4 -4
  133. package/lib/shell/login/SignInIcons.d.ts.map +1 -1
  134. package/lib/shell/login/SignInIcons.js +2 -1
  135. package/lib/shell/login/SignInIcons.js.map +1 -1
  136. package/lib/shell/login/SignInPasswordStep.d.ts +9 -0
  137. package/lib/shell/login/SignInPasswordStep.d.ts.map +1 -0
  138. package/lib/shell/login/SignInPasswordStep.js +58 -0
  139. package/lib/shell/login/SignInPasswordStep.js.map +1 -0
  140. package/lib/shell/login/SignInPrimitives.d.ts +13 -0
  141. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  142. package/lib/shell/login/SignInPrimitives.js +11 -1
  143. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  144. package/lib/shell/login/SignInProvidersStep.d.ts +3 -1
  145. package/lib/shell/login/SignInProvidersStep.d.ts.map +1 -1
  146. package/lib/shell/login/SignInProvidersStep.js +4 -2
  147. package/lib/shell/login/SignInProvidersStep.js.map +1 -1
  148. package/lib/shell/login/SignInReturningStep.d.ts +3 -1
  149. package/lib/shell/login/SignInReturningStep.d.ts.map +1 -1
  150. package/lib/shell/login/SignInReturningStep.js +18 -8
  151. package/lib/shell/login/SignInReturningStep.js.map +1 -1
  152. package/lib/shell/login/SignInTenantStep.d.ts +3 -1
  153. package/lib/shell/login/SignInTenantStep.d.ts.map +1 -1
  154. package/lib/shell/login/SignInTenantStep.js +2 -2
  155. package/lib/shell/login/SignInTenantStep.js.map +1 -1
  156. package/lib/shell/login/SignInWithProviderButton.d.ts +5 -3
  157. package/lib/shell/login/SignInWithProviderButton.d.ts.map +1 -1
  158. package/lib/shell/login/SignInWithProviderButton.js +8 -7
  159. package/lib/shell/login/SignInWithProviderButton.js.map +1 -1
  160. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  161. package/lib/shell/login/SigninScreen.js +13 -4
  162. package/lib/shell/login/SigninScreen.js.map +1 -1
  163. package/lib/shell/login/signInUtils.d.ts +20 -4
  164. package/lib/shell/login/signInUtils.d.ts.map +1 -1
  165. package/lib/shell/login/signInUtils.js +47 -1
  166. package/lib/shell/login/signInUtils.js.map +1 -1
  167. package/lib/vertesia-ui-core.js +3 -3
  168. package/lib/vertesia-ui-core.js.map +1 -1
  169. package/lib/vertesia-ui-features.js +33 -33
  170. package/lib/vertesia-ui-features.js.map +1 -1
  171. package/lib/vertesia-ui-i18n.BoNE1APG.chunk.js +5 -0
  172. package/lib/vertesia-ui-i18n.BoNE1APG.chunk.js.map +1 -0
  173. package/lib/vertesia-ui-i18n.BoPtiSPb.chunk.js +5 -0
  174. package/lib/vertesia-ui-i18n.BoPtiSPb.chunk.js.map +1 -0
  175. package/lib/vertesia-ui-i18n.C4BBW5v5.chunk.js +5 -0
  176. package/lib/vertesia-ui-i18n.C4BBW5v5.chunk.js.map +1 -0
  177. package/lib/vertesia-ui-i18n.C81wuNkY.chunk.js +5 -0
  178. package/lib/vertesia-ui-i18n.C81wuNkY.chunk.js.map +1 -0
  179. package/lib/vertesia-ui-i18n.C9GipKIH.chunk.js +5 -0
  180. package/lib/vertesia-ui-i18n.C9GipKIH.chunk.js.map +1 -0
  181. package/lib/vertesia-ui-i18n.CWsoS0W9.chunk.js +5 -0
  182. package/lib/vertesia-ui-i18n.CWsoS0W9.chunk.js.map +1 -0
  183. package/lib/vertesia-ui-i18n.Cr9NtzZB.chunk.js +5 -0
  184. package/lib/vertesia-ui-i18n.Cr9NtzZB.chunk.js.map +1 -0
  185. package/lib/vertesia-ui-i18n.DEpkHOD5.chunk.js +5 -0
  186. package/lib/vertesia-ui-i18n.DEpkHOD5.chunk.js.map +1 -0
  187. package/lib/vertesia-ui-i18n.DPXaRKFO.chunk.js +5 -0
  188. package/lib/vertesia-ui-i18n.DPXaRKFO.chunk.js.map +1 -0
  189. package/lib/vertesia-ui-i18n.D_QNm1Xg.chunk.js +5 -0
  190. package/lib/vertesia-ui-i18n.D_QNm1Xg.chunk.js.map +1 -0
  191. package/lib/vertesia-ui-i18n.F26Cc0f7.chunk.js +5 -0
  192. package/lib/vertesia-ui-i18n.F26Cc0f7.chunk.js.map +1 -0
  193. package/lib/vertesia-ui-i18n.YkXIESV_.chunk.js +5 -0
  194. package/lib/vertesia-ui-i18n.YkXIESV_.chunk.js.map +1 -0
  195. package/lib/vertesia-ui-i18n.js +3 -3
  196. package/lib/vertesia-ui-i18n.js.map +1 -1
  197. package/lib/vertesia-ui-router.js +1 -1
  198. package/lib/vertesia-ui-router.js.map +1 -1
  199. package/lib/vertesia-ui-session.js +1 -1
  200. package/lib/vertesia-ui-session.js.map +1 -1
  201. package/lib/vertesia-ui-shell.js +15 -9
  202. package/lib/vertesia-ui-shell.js.map +1 -1
  203. package/lib/vertesia-ui-widgets.js +2 -2
  204. package/lib/vertesia-ui-widgets.js.map +1 -1
  205. package/lib/widgets/json-view/JSONCode.js +1 -1
  206. package/lib/widgets/json-view/JSONCode.js.map +1 -1
  207. package/lib/widgets/upload/DropZone.d.ts.map +1 -1
  208. package/lib/widgets/upload/DropZone.js +1 -1
  209. package/lib/widgets/upload/DropZone.js.map +1 -1
  210. package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts +1 -1
  211. package/package.json +7 -7
  212. package/src/core/components/Link.tsx +1 -1
  213. package/src/core/components/SelectList.tsx +5 -2
  214. package/src/core/components/SidePanel.tsx +65 -60
  215. package/src/core/components/Spinner.tsx +1 -1
  216. package/src/core/components/TagsInput.tsx +7 -7
  217. package/src/core/components/shadcn/badge.tsx +1 -1
  218. package/src/core/components/shadcn/button.tsx +2 -3
  219. package/src/core/components/shadcn/filters/comboBox/DateCombobox.tsx +2 -2
  220. package/src/core/components/shadcn/filters/comboBox/SelectCombobox.tsx +1 -1
  221. package/src/core/components/shadcn/filters/comboBox/StringListCombobox.tsx +1 -1
  222. package/src/core/components/shadcn/filters/comboBox/TextCombobox.tsx +1 -1
  223. package/src/core/components/shadcn/filters/filter/dateFilter.tsx +1 -1
  224. package/src/core/components/shadcn/filters/filters.tsx +1 -1
  225. package/src/core/components/shadcn/overflowTabs.tsx +2 -2
  226. package/src/core/components/shadcn/tabs.tsx +1 -1
  227. package/src/css/utilities.css +7 -0
  228. package/src/features/activity-doc/ActivityDoc.tsx +1 -1
  229. package/src/features/agent/chat/AskUserWidget.test.tsx +24 -2
  230. package/src/features/agent/chat/AskUserWidget.tsx +19 -4
  231. package/src/features/agent/chat/ModernAgentConversation.test.tsx +150 -1
  232. package/src/features/agent/chat/ModernAgentConversation.tsx +59 -21
  233. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +62 -0
  234. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +8 -7
  235. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +17 -1
  236. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +4 -3
  237. package/src/features/agent/chat/hooks/useAgentPlans.test.ts +52 -0
  238. package/src/features/agent/chat/hooks/useAgentPlans.ts +5 -5
  239. package/src/features/layout/GenericPageNavHeader.test.tsx +36 -0
  240. package/src/features/layout/GenericPageNavHeader.tsx +1 -1
  241. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -1
  242. package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
  243. package/src/features/oauth/McpConnectionsButton.test.tsx +56 -0
  244. package/src/features/oauth/McpConnectionsButton.tsx +9 -6
  245. package/src/features/oauth/McpConnectionsDialog.tsx +14 -5
  246. package/src/features/oauth/useMcpConnections.test.ts +10 -0
  247. package/src/features/oauth/useMcpConnections.ts +18 -10
  248. package/src/features/permissions/UserPermissionsProvider.tsx +1 -1
  249. package/src/features/store/objects/DocumentPreviewPanel.tsx +10 -10
  250. package/src/features/store/objects/components/DocumentInput.tsx +1 -1
  251. package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -0
  252. package/src/features/store/objects/selection/SelectionActions.tsx +13 -6
  253. package/src/features/store/objects/selection/actions/RunIntakeAction.tsx +69 -0
  254. package/src/features/store/objects/selection/actions/index.ts +1 -0
  255. package/src/features/store/objects/upload/DocumentUploadModal.tsx +1 -1
  256. package/src/features/store/types/ContentObjectTypesTable.tsx +20 -3
  257. package/src/features/store/types/IntakePolicyEditor.tsx +33 -28
  258. package/src/features/user/UserInfo.tsx +1 -1
  259. package/src/i18n/locales/ar.json +28 -7
  260. package/src/i18n/locales/de.json +24 -7
  261. package/src/i18n/locales/en.json +24 -7
  262. package/src/i18n/locales/es.json +24 -7
  263. package/src/i18n/locales/fr.json +25 -7
  264. package/src/i18n/locales/it.json +24 -7
  265. package/src/i18n/locales/ja.json +24 -7
  266. package/src/i18n/locales/ko.json +24 -7
  267. package/src/i18n/locales/pt.json +24 -7
  268. package/src/i18n/locales/ru.json +26 -7
  269. package/src/i18n/locales/tr.json +24 -7
  270. package/src/i18n/locales/zh-TW.json +24 -7
  271. package/src/i18n/locales/zh.json +24 -7
  272. package/src/router/RouteComponent.test.tsx +65 -1
  273. package/src/router/RouteComponent.tsx +18 -15
  274. package/src/session/UserSession.test.ts +65 -0
  275. package/src/session/UserSession.ts +4 -0
  276. package/src/shell/SplashScreen.tsx +1 -1
  277. package/src/shell/login/SignInIcons.tsx +2 -1
  278. package/src/shell/login/SignInPasswordRequired.test.tsx +82 -0
  279. package/src/shell/login/SignInPasswordStep.test.tsx +71 -0
  280. package/src/shell/login/SignInPasswordStep.tsx +103 -0
  281. package/src/shell/login/SignInPrimitives.tsx +53 -1
  282. package/src/shell/login/SignInProvidersStep.tsx +8 -4
  283. package/src/shell/login/SignInReturningStep.tsx +20 -8
  284. package/src/shell/login/SignInTenantStep.tsx +6 -2
  285. package/src/shell/login/SignInWithProviderButton.tsx +11 -7
  286. package/src/shell/login/SigninScreen.tsx +32 -3
  287. package/src/shell/login/signInPassword.test.ts +133 -0
  288. package/src/shell/login/signInUtils.ts +60 -7
  289. package/src/widgets/json-view/JSONCode.test.tsx +2 -2
  290. package/src/widgets/json-view/JSONCode.tsx +1 -1
  291. package/src/widgets/upload/DropZone.tsx +2 -4
  292. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +0 -7
  293. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +0 -1
  294. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +0 -51
  295. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +0 -1
  296. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js +0 -5
  297. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js.map +0 -1
  298. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js +0 -5
  299. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js.map +0 -1
  300. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js +0 -5
  301. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js.map +0 -1
  302. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js +0 -5
  303. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js.map +0 -1
  304. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js +0 -5
  305. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js.map +0 -1
  306. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js +0 -5
  307. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js.map +0 -1
  308. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js +0 -5
  309. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js.map +0 -1
  310. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js +0 -5
  311. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js.map +0 -1
  312. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js +0 -5
  313. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js.map +0 -1
  314. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js +0 -5
  315. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js.map +0 -1
  316. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js +0 -5
  317. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js.map +0 -1
  318. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js +0 -5
  319. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js.map +0 -1
  320. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +0 -104
@@ -13,6 +13,21 @@ export interface AskUserOption {
13
13
  icon?: React.ReactNode;
14
14
  }
15
15
 
16
+ export function isAskUserOptions(value: unknown): value is AskUserOption[] {
17
+ return (
18
+ Array.isArray(value) &&
19
+ value.every((option: unknown) => {
20
+ if (!option || typeof option !== 'object' || Array.isArray(option)) return false;
21
+ const { id, label, description } = option as Partial<AskUserOption>;
22
+ return (
23
+ typeof id === 'string' &&
24
+ typeof label === 'string' &&
25
+ (description == null || typeof description === 'string')
26
+ );
27
+ })
28
+ );
29
+ }
30
+
16
31
  /** Props for the AskUserWidget component */
17
32
  export interface AskUserWidgetProps {
18
33
  /** The question or prompt to display */
@@ -153,11 +168,12 @@ export function AskUserWidget({
153
168
 
154
169
  const styles = VARIANT_STYLES[variant];
155
170
  const DefaultIcon = VARIANT_ICONS[variant];
156
- const safeOptions = Array.isArray(options) ? options : [];
171
+ const validOptions = isAskUserOptions(options);
172
+ const safeOptions = validOptions ? options : [];
157
173
  const invalidOptionsReported = React.useRef(false);
158
174
 
159
175
  React.useEffect(() => {
160
- if (options === undefined || Array.isArray(options) || invalidOptionsReported.current) return;
176
+ if (options === undefined || validOptions || invalidOptionsReported.current) return;
161
177
 
162
178
  invalidOptionsReported.current = true;
163
179
  Env.logger.warn('AskUserWidget received invalid options; rendering without options', {
@@ -166,8 +182,7 @@ export function AskUserWidget({
166
182
  received_type: typeof options,
167
183
  },
168
184
  });
169
- console.warn('[AskUserWidget] Invalid options received; rendering without options.');
170
- }, [options]);
185
+ }, [options, validOptions]);
171
186
 
172
187
  const toggleOption = (optionId: string) => {
173
188
  setSelectedOptions((prev) => {
@@ -61,6 +61,8 @@ vi.mock('./ModernAgentOutput/Header', () => ({
61
61
  vi.mock('./ModernAgentOutput/MessageInput', () => ({
62
62
  default: (props: {
63
63
  onSend: (message: string) => void;
64
+ value?: string;
65
+ onValueChange?: (value: string) => void;
64
66
  activeTaskCount?: number;
65
67
  activeWorkstreams?: Array<{ workstream_id: string; status: string }>;
66
68
  onSelectWorkstream?: (workstreamId: string) => void;
@@ -81,6 +83,11 @@ vi.mock('./ModernAgentOutput/MessageInput', () => ({
81
83
  return (
82
84
  <div>
83
85
  {props.approvalModeSlot}
86
+ <textarea
87
+ aria-label="composer draft"
88
+ value={props.value}
89
+ onChange={(event) => props.onValueChange?.(event.target.value)}
90
+ />
84
91
  <button type="button" onClick={() => props.onSend('follow up')}>
85
92
  composer send
86
93
  </button>
@@ -1278,7 +1285,7 @@ describe('ModernAgentConversation send handling', () => {
1278
1285
  expect.objectContaining({ workstream_id: 'beta', status: 'running' }),
1279
1286
  expect.objectContaining({ workstream_id: 'gamma', status: 'completed' }),
1280
1287
  ]);
1281
- expect(latestRightPanelProps.activeTab).toBe('plan');
1288
+ expect(latestRightPanelProps.activeTab).toBe('workstreams');
1282
1289
  expect(latestMessageInputProps.activeTaskCount).toBe(2);
1283
1290
  expect(latestMessageInputProps.activeWorkstreams).toEqual([
1284
1291
  expect.objectContaining({ workstream_id: 'alpha', status: 'running' }),
@@ -1706,6 +1713,80 @@ describe('ModernAgentConversation send handling', () => {
1706
1713
  );
1707
1714
  });
1708
1715
 
1716
+ it('restores the composer draft after an option response without sending it as part of that response', async () => {
1717
+ const requestMessage = {
1718
+ ...createMessage(AgentMessageType.REQUEST_INPUT, 'What is your favorite color?'),
1719
+ details: {
1720
+ request_id: 'ask-user-1',
1721
+ ux: {
1722
+ options: [
1723
+ { id: 'red', label: 'Red' },
1724
+ { id: 'blue', label: 'Blue' },
1725
+ ],
1726
+ },
1727
+ },
1728
+ };
1729
+ mockStreamState({ messages: [], isCompleted: false, agentRunStatus: 'RUNNING' });
1730
+
1731
+ const view = renderConversation({ hideMessageInput: false });
1732
+ fireEvent.change(screen.getByRole('textbox', { name: 'composer draft' }), {
1733
+ target: { value: 'My pending message' },
1734
+ });
1735
+
1736
+ mockStreamState({ messages: [requestMessage], isCompleted: false, agentRunStatus: 'RUNNING' });
1737
+ view.rerender(
1738
+ <ModernAgentConversation
1739
+ agentRunId="agent-run-1"
1740
+ title="Agent"
1741
+ hideHeader
1742
+ hideMessageInput={false}
1743
+ showRightPanel={false}
1744
+ />,
1745
+ );
1746
+
1747
+ await act(async () => {
1748
+ fireEvent.click(screen.getByRole('button', { name: 'Blue' }));
1749
+ await Promise.resolve();
1750
+ });
1751
+ await waitFor(() => {
1752
+ expect(mocks.sendSignal).toHaveBeenCalledWith(
1753
+ 'agent-run-1',
1754
+ 'UserInput',
1755
+ expect.objectContaining({ message: 'blue' }),
1756
+ );
1757
+ });
1758
+
1759
+ mockStreamState({
1760
+ messages: [
1761
+ requestMessage,
1762
+ {
1763
+ ...createMessage(AgentMessageType.QUESTION, 'blue'),
1764
+ details: { request_input_response: { request_id: 'ask-user-1' } },
1765
+ },
1766
+ ],
1767
+ isCompleted: false,
1768
+ agentRunStatus: 'RUNNING',
1769
+ });
1770
+ view.rerender(
1771
+ <ModernAgentConversation
1772
+ agentRunId="agent-run-1"
1773
+ title="Agent"
1774
+ hideHeader
1775
+ hideMessageInput={false}
1776
+ showRightPanel={false}
1777
+ />,
1778
+ );
1779
+
1780
+ expect((screen.getByRole('textbox', { name: 'composer draft' }) as HTMLTextAreaElement).value).toBe(
1781
+ 'My pending message',
1782
+ );
1783
+ expect(mocks.sendSignal).not.toHaveBeenCalledWith(
1784
+ 'agent-run-1',
1785
+ 'UserInput',
1786
+ expect.objectContaining({ message: 'My pending message' }),
1787
+ );
1788
+ });
1789
+
1709
1790
  it('does not resurrect an answered request overlay after remounting before the persisted echo arrives', async () => {
1710
1791
  const requestMessage = {
1711
1792
  ...createMessage(AgentMessageType.REQUEST_INPUT, 'What is your favorite color?'),
@@ -1960,6 +2041,74 @@ describe('ModernAgentConversation send handling', () => {
1960
2041
  );
1961
2042
  });
1962
2043
 
2044
+ it('continues a run that ended while shown as running when the send is rejected with 409', async () => {
2045
+ mockStreamState({
2046
+ messages: [createMessage(AgentMessageType.ANSWER, 'still running')],
2047
+ agentRunStatus: 'RUNNING',
2048
+ });
2049
+ mocks.sendSignal
2050
+ .mockRejectedValueOnce(Object.assign(new Error('This agent run has ended.'), { status: 409 }))
2051
+ .mockResolvedValueOnce({});
2052
+
2053
+ renderConversation({ onRestart: vi.fn() });
2054
+ mocks.retrieve.mockResolvedValue({ status: 'completed' });
2055
+
2056
+ fireEvent.click(screen.getByRole('button', { name: 'inline send' }));
2057
+
2058
+ await waitFor(() => {
2059
+ expect(mocks.sendSignal).toHaveBeenCalledTimes(2);
2060
+ });
2061
+
2062
+ expect(mocks.restart).toHaveBeenCalledWith('agent-run-1');
2063
+ expect(mocks.reconnect).toHaveBeenCalledTimes(1);
2064
+ expect(mocks.restart.mock.invocationCallOrder[0]).toBeLessThan(mocks.sendSignal.mock.invocationCallOrder[1]);
2065
+ await waitFor(() => {
2066
+ expect(mocks.updateOptimisticMessageStatus).toHaveBeenCalledWith(expect.any(String), 'received');
2067
+ });
2068
+ expect(mocks.updateOptimisticMessageStatus).not.toHaveBeenCalledWith(expect.any(String), 'failed');
2069
+ });
2070
+
2071
+ it('shows how the run ended instead of continuing it when it failed', async () => {
2072
+ mockStreamState({
2073
+ messages: [createMessage(AgentMessageType.ANSWER, 'still running')],
2074
+ agentRunStatus: 'RUNNING',
2075
+ });
2076
+ mocks.sendSignal.mockRejectedValue(Object.assign(new Error('This agent run has ended.'), { status: 409 }));
2077
+
2078
+ renderConversation({ onRestart: vi.fn() });
2079
+ mocks.retrieve.mockResolvedValue({ status: 'failed' });
2080
+
2081
+ fireEvent.click(screen.getByRole('button', { name: 'inline send' }));
2082
+
2083
+ await waitFor(() => {
2084
+ expect(mocks.updateOptimisticMessageStatus).toHaveBeenCalledWith(expect.any(String), 'failed');
2085
+ });
2086
+ expect(mocks.restart).not.toHaveBeenCalled();
2087
+ expect(mocks.reconnect).toHaveBeenCalledTimes(1);
2088
+ expect(mocks.sendSignal).toHaveBeenCalledTimes(1);
2089
+ });
2090
+
2091
+ it('does not restart a run that answers 409 while still running', async () => {
2092
+ mockStreamState({
2093
+ messages: [createMessage(AgentMessageType.ANSWER, 'still running')],
2094
+ agentRunStatus: 'RUNNING',
2095
+ });
2096
+ mocks.sendSignal.mockRejectedValue(
2097
+ Object.assign(new Error('Agent run workflow is not ready yet; retry shortly.'), { status: 409 }),
2098
+ );
2099
+
2100
+ renderConversation({ onRestart: vi.fn() });
2101
+ mocks.retrieve.mockResolvedValue({ status: 'running' });
2102
+
2103
+ fireEvent.click(screen.getByRole('button', { name: 'inline send' }));
2104
+
2105
+ await waitFor(() => {
2106
+ expect(mocks.updateOptimisticMessageStatus).toHaveBeenCalledWith(expect.any(String), 'failed');
2107
+ });
2108
+ expect(mocks.restart).not.toHaveBeenCalled();
2109
+ expect(mocks.reconnect).not.toHaveBeenCalled();
2110
+ });
2111
+
1963
2112
  it('marks a sent message as sending and then received', async () => {
1964
2113
  mockStreamState({
1965
2114
  messages: [createMessage(AgentMessageType.ANSWER, 'still running')],
@@ -150,6 +150,24 @@ async function closeStagedFileBatch(
150
150
  }
151
151
  }
152
152
 
153
+ const TERMINAL_WORKFLOW_STATUSES = new Set(['COMPLETED', 'FAILED', 'CANCELED', 'CANCELLED', 'TERMINATED', 'TIMED_OUT']);
154
+
155
+ function isTerminalWorkflowStatus(status: string | null | undefined): boolean {
156
+ return !!status && TERMINAL_WORKFLOW_STATUSES.has(status.toUpperCase());
157
+ }
158
+
159
+ /**
160
+ * Whether the next message can continue an ended run by restarting it. FAILED runs are excluded: a
161
+ * failed run is a dead end, so the failed box and its explicit Restart action are shown instead.
162
+ */
163
+ function canContinueWorkflowStatus(status: string | null | undefined, canRestart: boolean): boolean {
164
+ return canRestart && isTerminalWorkflowStatus(status) && status?.toUpperCase() !== 'FAILED';
165
+ }
166
+
167
+ function isConflictError(err: unknown): boolean {
168
+ return typeof err === 'object' && err !== null && 'status' in err && err.status === 409;
169
+ }
170
+
153
171
  function getTimestampMs(timestamp: number | string | undefined): number {
154
172
  if (typeof timestamp === 'number') return timestamp;
155
173
  if (!timestamp) return Date.now();
@@ -1553,7 +1571,7 @@ function ModernAgentConversationInner({
1553
1571
  showInput,
1554
1572
  showSlidingPanel,
1555
1573
  setShowSlidingPanel,
1556
- } = useAgentPlans(messages, interactive, isModal);
1574
+ } = useAgentPlans(messages, interactive);
1557
1575
 
1558
1576
  const {
1559
1577
  openDocuments,
@@ -1585,6 +1603,9 @@ function ModernAgentConversationInner({
1585
1603
  const conversationRef = useRef<HTMLDivElement | null>(null);
1586
1604
  const conversationLayoutRef = useRef<HTMLDivElement | null>(null);
1587
1605
  const [isSending, setIsSending] = useState(false);
1606
+ // Request-input overlays replace the composer while the user chooses a response. Keep the
1607
+ // ordinary composer draft here so that temporary unmount does not discard it.
1608
+ const [composerValue, setComposerValue] = useState('');
1588
1609
  const [isCompactingContext, setIsCompactingContext] = useState(false);
1589
1610
  const [internalViewMode, setInternalViewMode] = useState<AgentConversationViewMode>('sliding');
1590
1611
  const viewMode = controlledViewMode ?? internalViewMode;
@@ -1659,17 +1680,10 @@ function ModernAgentConversationInner({
1659
1680
  return agentRunStatus;
1660
1681
  }, [lastMainMessage, agentRunStatus]);
1661
1682
 
1662
- const isWorkflowTerminal = useMemo(() => {
1663
- const normalizedStatus = effectiveWorkflowStatus?.toUpperCase();
1664
- return (
1665
- normalizedStatus === 'COMPLETED' ||
1666
- normalizedStatus === 'FAILED' ||
1667
- normalizedStatus === 'CANCELED' ||
1668
- normalizedStatus === 'CANCELLED' ||
1669
- normalizedStatus === 'TERMINATED' ||
1670
- normalizedStatus === 'TIMED_OUT'
1671
- );
1672
- }, [effectiveWorkflowStatus]);
1683
+ const isWorkflowTerminal = useMemo(
1684
+ () => isTerminalWorkflowStatus(effectiveWorkflowStatus),
1685
+ [effectiveWorkflowStatus],
1686
+ );
1673
1687
 
1674
1688
  // When a terminal conversation can be restarted (host provided a restart handler),
1675
1689
  // we keep the composer visible and seamlessly resume the agent on the next message
@@ -1677,8 +1691,8 @@ function ModernAgentConversationInner({
1677
1691
  // FAILED runs are excluded: a failed run is a dead end, so we surface the failed box /
1678
1692
  // `failedAction` (e.g. an explicit Restart button) instead of silently resuming.
1679
1693
  const canContinueConversation = useMemo(
1680
- () => isWorkflowTerminal && effectiveWorkflowStatus?.toUpperCase() !== 'FAILED' && !!onRestart,
1681
- [isWorkflowTerminal, effectiveWorkflowStatus, onRestart],
1694
+ () => canContinueWorkflowStatus(effectiveWorkflowStatus, !!onRestart),
1695
+ [effectiveWorkflowStatus, onRestart],
1682
1696
  );
1683
1697
  const shouldRenderMessageInputArea = !hideMessageInput || canContinueConversation;
1684
1698
 
@@ -1706,6 +1720,8 @@ function ModernAgentConversationInner({
1706
1720
  isWorkflowTerminalRef.current = isWorkflowTerminal;
1707
1721
  const canContinueConversationRef = useRef(canContinueConversation);
1708
1722
  canContinueConversationRef.current = canContinueConversation;
1723
+ const canRestartRef = useRef(!!onRestart);
1724
+ canRestartRef.current = !!onRestart;
1709
1725
 
1710
1726
  // ────────────────────────────────────────────
1711
1727
  // Computed values
@@ -1937,9 +1953,10 @@ function ModernAgentConversationInner({
1937
1953
  // Unified right panel state
1938
1954
  // ────────────────────────────────────────────
1939
1955
  type RightPanelTab = 'plan' | 'workstreams' | 'documents' | 'uploads' | 'artifacts' | 'payload' | 'conversation';
1940
- const [rightPanelTab, setRightPanelTab] = useState<RightPanelTab>(
1941
- conversationContent || conversationTab ? 'conversation' : 'plan',
1956
+ const [rightPanelTab, setRightPanelTab] = useState<RightPanelTab | undefined>(
1957
+ conversationContent || conversationTab ? 'conversation' : undefined,
1942
1958
  );
1959
+ const defaultRightPanelTab = plans.length > 0 || panelWorkstreams.length === 0 ? 'plan' : 'workstreams';
1943
1960
  const [selectedArtifactPath, setSelectedArtifactPath] = useState<string | null>(null);
1944
1961
  const [rightPanelWidth, setRightPanelWidth] = useState(400);
1945
1962
  const [isRightPanelResizing, setIsRightPanelResizing] = useState(false);
@@ -2328,13 +2345,32 @@ function ModernAgentConversationInner({
2328
2345
  if (requestInputId) {
2329
2346
  markRequestInputIdAnsweredForSession(agentRunId, requestInputId);
2330
2347
  }
2348
+ const restartAndReconnect = async () => {
2349
+ await client.agents.restart(agentRunId);
2350
+ reconnectStream();
2351
+ };
2331
2352
  const deliver = (async () => {
2332
2353
  await waitForPendingToolApprovalModeChange();
2333
2354
  if (isWorkflowTerminalRef.current) {
2334
- await client.agents.restart(agentRunId);
2335
- reconnectStream();
2355
+ await restartAndReconnect();
2356
+ }
2357
+ try {
2358
+ await sendUserInput();
2359
+ } catch (err: unknown) {
2360
+ // The run can end while this view still shows it running — for example when it
2361
+ // reaches its maximum duration while idle — and the signal then answers 409.
2362
+ // Re-read its status and, if it ended and can be continued, continue it the way
2363
+ // a run already known to be ended is continued above.
2364
+ if (!isConflictError(err)) throw err;
2365
+ const run = await client.agents.retrieve(agentRunId);
2366
+ if (!canContinueWorkflowStatus(run.status, canRestartRef.current)) {
2367
+ // Reconnecting re-reads the status, so the view shows how the run ended.
2368
+ if (isTerminalWorkflowStatus(run.status)) reconnectStream();
2369
+ throw err;
2370
+ }
2371
+ await restartAndReconnect();
2372
+ await sendUserInput();
2336
2373
  }
2337
- await sendUserInput();
2338
2374
  markReceived();
2339
2375
  })();
2340
2376
 
@@ -2903,6 +2939,8 @@ function ModernAgentConversationInner({
2903
2939
  {composerContext}
2904
2940
  <MessageInput
2905
2941
  onSend={handleSendMessage}
2942
+ value={composerValue}
2943
+ onValueChange={setComposerValue}
2906
2944
  onStop={allowWorkflowControl ? handleStopWorkflow : undefined}
2907
2945
  approvalModeSlot={
2908
2946
  interactive && toolApprovalMode ? (
@@ -3081,8 +3119,8 @@ function ModernAgentConversationInner({
3081
3119
  conversationContent={conversationTab ? conversationAreaJsx : conversationContent}
3082
3120
  // Panel control
3083
3121
  onClose={handleCloseRightPanel}
3084
- defaultTab={rightPanelTab}
3085
- activeTab={rightPanelTab}
3122
+ defaultTab={rightPanelTab ?? defaultRightPanelTab}
3123
+ activeTab={rightPanelTab ?? defaultRightPanelTab}
3086
3124
  onTabChange={setRightPanelTab}
3087
3125
  />
3088
3126
  </div>
@@ -1599,6 +1599,68 @@ describe('AllMessagesMixed summary view', () => {
1599
1599
  expect(screen.getAllByText(/quotes\.md/).length).toBeGreaterThan(0);
1600
1600
  });
1601
1601
 
1602
+ describe.each(['summary', 'stacked', 'activity'] as const)('request input in %s view', (mode) => {
1603
+ function renderPrompt(options: unknown) {
1604
+ const onSendMessage = vi.fn();
1605
+ const prompt = makeMessage({
1606
+ timestamp: 2_000,
1607
+ type: AgentMessageType.REQUEST_INPUT,
1608
+ message: 'Choose a response',
1609
+ details: { request_id: 'robust-ask', ux: { options, variant: 'default', multiSelect: false } },
1610
+ });
1611
+ if (mode === 'stacked') {
1612
+ renderStacked([prompt], false, { onSendMessage });
1613
+ } else if (mode === 'activity') {
1614
+ renderSummary(
1615
+ [
1616
+ makeMessage({
1617
+ timestamp: 1_000,
1618
+ message: 'Waiting for input',
1619
+ details: {
1620
+ event_class: 'activity',
1621
+ tool: 'ask_user',
1622
+ tool_run_id: 'ask-run',
1623
+ tool_status: 'running',
1624
+ activity_group_id: 'ask-group',
1625
+ },
1626
+ }),
1627
+ { ...prompt, details: { ...prompt.details, activity_group_id: 'ask-group', tool: 'ask_user' } },
1628
+ makeMessage({ timestamp: 3_000, message: 'Finished', type: AgentMessageType.COMPLETE }),
1629
+ ],
1630
+ true,
1631
+ new Map(),
1632
+ { onSendMessage },
1633
+ );
1634
+ fireEvent.click(screen.getByRole('button', { name: /Worked\s*for/ }));
1635
+ } else {
1636
+ renderSummary([prompt], false, new Map(), { onSendMessage });
1637
+ }
1638
+ return onSendMessage;
1639
+ }
1640
+
1641
+ it.each([
1642
+ { name: 'a string', options: 'null' },
1643
+ { name: 'null', options: null },
1644
+ { name: 'a null entry', options: [null] },
1645
+ { name: 'an object label', options: [{ id: 'a', label: {} }] },
1646
+ { name: 'mixed valid and invalid entries', options: [{ id: 'a', label: 'A' }, null] },
1647
+ ])('allows text submission for $name', ({ options }) => {
1648
+ const onSendMessage = renderPrompt(options);
1649
+ fireEvent.change(screen.getByRole('textbox'), { target: { value: 'Custom response' } });
1650
+ fireEvent.click(screen.getByRole('button', { name: 'Send' }));
1651
+ expect(onSendMessage).toHaveBeenCalledWith('Custom response', {
1652
+ request_input_response: { request_id: 'robust-ask' },
1653
+ });
1654
+ });
1655
+
1656
+ it('preserves valid choices and their response IDs', () => {
1657
+ const onSendMessage = renderPrompt([{ id: 'a', label: 'Choice A' }]);
1658
+ fireEvent.click(screen.getByRole('button', { name: 'Choice A' }));
1659
+ expect(onSendMessage).toHaveBeenCalledWith('a', { request_input_response: { request_id: 'robust-ask' } });
1660
+ expect(screen.queryByRole('textbox')).toBeNull();
1661
+ });
1662
+ });
1663
+
1602
1664
  it('renders pending ask options compactly in summary view', () => {
1603
1665
  renderSummary([
1604
1666
  makeMessage({
@@ -30,7 +30,7 @@ import {
30
30
  } from 'lucide-react';
31
31
  import React, { Component, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
32
32
  import { AnimatedThinkingDots, PulsatingCircle } from '../AnimatedThinkingDots';
33
- import { AskUserWidget } from '../AskUserWidget';
33
+ import { AskUserWidget, isAskUserOptions } from '../AskUserWidget';
34
34
  import { DocumentEditingActionCard, parseMarkdownEditingAction } from '../DocumentEditingActionCard.js';
35
35
  import { ThinkingMessages } from '../WaitingMessages';
36
36
  import {
@@ -758,11 +758,12 @@ function SummaryMessage({
758
758
  const requestInputDetails = message.details as AskUserMessageDetails | undefined;
759
759
  if (message.type === AgentMessageType.REQUEST_INPUT && requestInputDetails?.ux) {
760
760
  const uxConfig = requestInputDetails.ux;
761
+ const hasSelectableOptions = isAskUserOptions(uxConfig.options) && uxConfig.options.length > 0;
761
762
  return (
762
763
  <div className="mx-auto w-full max-w-3xl px-1">
763
764
  <AskUserWidget
764
765
  question={content}
765
- options={uxConfig.options}
766
+ options={hasSelectableOptions ? uxConfig.options : undefined}
766
767
  variant={uxConfig.variant}
767
768
  multiSelect={uxConfig.multiSelect}
768
769
  onSelect={(optionId) =>
@@ -776,7 +777,7 @@ function SummaryMessage({
776
777
  onMultiSelect={(optionIds) =>
777
778
  sendRequestInputResponse(onSendMessage, message, optionIds.join(', '))
778
779
  }
779
- allowFreeResponse={!uxConfig.options?.length || !!uxConfig.free_response}
780
+ allowFreeResponse={!hasSelectableOptions || !!uxConfig.free_response}
780
781
  placeholder={uxConfig.free_response?.placeholder}
781
782
  submitLabel={uxConfig.free_response?.submit_label}
782
783
  onSubmit={(value) =>
@@ -2300,11 +2301,13 @@ function SummaryActivityRow({
2300
2301
  <div className="mt-3 space-y-3">
2301
2302
  {requestInputMessages.map((message) => {
2302
2303
  const uxConfig = message.details.ux;
2304
+ const hasSelectableOptions =
2305
+ isAskUserOptions(uxConfig.options) && uxConfig.options.length > 0;
2303
2306
  return (
2304
2307
  <AskUserWidget
2305
2308
  key={getAgentMessageRenderKey(message, 'work-request-input')}
2306
2309
  question={getRequestInputDisplayText(message)}
2307
- options={uxConfig.options}
2310
+ options={hasSelectableOptions ? uxConfig.options : undefined}
2308
2311
  variant={uxConfig.variant}
2309
2312
  multiSelect={uxConfig.multiSelect}
2310
2313
  onSelect={(optionId) =>
@@ -2322,9 +2325,7 @@ function SummaryActivityRow({
2322
2325
  optionIds.join(', '),
2323
2326
  )
2324
2327
  }
2325
- allowFreeResponse={
2326
- !uxConfig.options?.length || !!uxConfig.free_response
2327
- }
2328
+ allowFreeResponse={!hasSelectableOptions || !!uxConfig.free_response}
2328
2329
  placeholder={uxConfig.free_response?.placeholder}
2329
2330
  submitLabel={uxConfig.free_response?.submit_label}
2330
2331
  onSubmit={(value) =>
@@ -55,6 +55,10 @@ function formatTokenCountInK(tokens: number): string {
55
55
 
56
56
  interface MessageInputProps {
57
57
  onSend: (message: string) => void;
58
+ /** Controlled composer draft. When omitted, the composer manages its own draft. */
59
+ value?: string;
60
+ /** Called whenever the composer draft changes. */
61
+ onValueChange?: (value: string) => void;
58
62
  onStop?: () => void;
59
63
  disabled?: boolean;
60
64
  isSending?: boolean;
@@ -121,6 +125,8 @@ interface MessageInputProps {
121
125
 
122
126
  export default function MessageInput({
123
127
  onSend,
128
+ value: controlledValue,
129
+ onValueChange,
124
130
  onStop,
125
131
  approvalModeSlot,
126
132
  mcpSlot,
@@ -163,7 +169,17 @@ export default function MessageInput({
163
169
  const resolvedPlaceholder = placeholder ?? t('agent.typeYourMessage');
164
170
  const ref = useRef<HTMLTextAreaElement | null>(null);
165
171
  const fileInputRef = useRef<HTMLInputElement | null>(null);
166
- const [value, setValue] = useState('');
172
+ const [uncontrolledValue, setUncontrolledValue] = useState('');
173
+ const value = controlledValue ?? uncontrolledValue;
174
+ const setValue = useCallback(
175
+ (nextValue: string) => {
176
+ if (controlledValue === undefined) {
177
+ setUncontrolledValue(nextValue);
178
+ }
179
+ onValueChange?.(nextValue);
180
+ },
181
+ [controlledValue, onValueChange],
182
+ );
167
183
  const [isObjectModalOpen, setIsObjectModalOpen] = useState(false);
168
184
  const [isDocSearchOpen, setIsDocSearchOpen] = useState(false);
169
185
  const [isDragOver, setIsDragOver] = useState(false);
@@ -28,7 +28,7 @@ import { memo, useEffect, useMemo, useRef, useState } from 'react';
28
28
  import dayjs from '../../../../core/utils/dayjs.js';
29
29
  import { useDownloadFile } from '../../../store/objects/components/useDownloadFile.js';
30
30
  import { PulsatingCircle } from '../AnimatedThinkingDots';
31
- import { AskUserWidget } from '../AskUserWidget';
31
+ import { AskUserWidget, isAskUserOptions } from '../AskUserWidget';
32
32
  import { DocumentEditingActionCard, parseMarkdownEditingAction } from '../DocumentEditingActionCard.js';
33
33
  import { useImageLightbox } from '../ImageLightbox';
34
34
  import { getArtifactCacheKey, useArtifactUrlCache } from '../useArtifactUrlCache.js';
@@ -375,6 +375,7 @@ function MessageItemComponent({
375
375
  // UX config for REQUEST_INPUT messages (narrowed const so it stays typed inside the JSX closures below)
376
376
  const askUserUx =
377
377
  message.type === AgentMessageType.REQUEST_INPUT ? (message.details as AskUserMessageDetails)?.ux : undefined;
378
+ const hasSelectableAskUserOptions = isAskUserOptions(askUserUx?.options) && askUserUx.options.length > 0;
378
379
 
379
380
  // PERFORMANCE: Memoize markdown components to prevent MarkdownRenderer remounts
380
381
  const markdownComponents = useMemo(
@@ -616,7 +617,7 @@ function MessageItemComponent({
616
617
  {askUserUx ? (
617
618
  <AskUserWidget
618
619
  question={typeof messageContent === 'string' ? messageContent : ''}
619
- options={askUserUx.options}
620
+ options={hasSelectableAskUserOptions ? askUserUx.options : undefined}
620
621
  variant={askUserUx.variant}
621
622
  multiSelect={askUserUx.multiSelect}
622
623
  onSelect={(optionId) =>
@@ -630,7 +631,7 @@ function MessageItemComponent({
630
631
  onMultiSelect={(optionIds) =>
631
632
  sendRequestInputResponse(onSendMessage, message, optionIds.join(', '))
632
633
  }
633
- allowFreeResponse={!askUserUx.options?.length || !!askUserUx.free_response}
634
+ allowFreeResponse={!hasSelectableAskUserOptions || !!askUserUx.free_response}
634
635
  placeholder={askUserUx.free_response?.placeholder}
635
636
  submitLabel={askUserUx.free_response?.submit_label}
636
637
  onSubmit={(value) =>
@@ -0,0 +1,52 @@
1
+ import { act, cleanup, renderHook } from '@testing-library/react';
2
+ import { type AgentMessage, AgentMessageType } from '@vertesia/common';
3
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4
+ import { useAgentPlans } from './useAgentPlans.js';
5
+
6
+ const question: AgentMessage = {
7
+ timestamp: 1,
8
+ workflow_run_id: 'run-1',
9
+ workstream_id: 'main',
10
+ type: AgentMessageType.QUESTION,
11
+ message: 'Help with a task',
12
+ };
13
+ const plan: AgentMessage = {
14
+ ...question,
15
+ timestamp: 2,
16
+ type: AgentMessageType.PLAN,
17
+ details: { plan: [{ id: 1, goal: 'Review the task', status: 'pending' }] },
18
+ };
19
+
20
+ describe('useAgentPlans sidebar visibility', () => {
21
+ beforeEach(() => sessionStorage.clear());
22
+ afterEach(cleanup);
23
+
24
+ it('stays closed while history loads and when history has no plan', () => {
25
+ const { result, rerender } = renderHook(({ messages }) => useAgentPlans(messages, true), {
26
+ initialProps: { messages: [] as AgentMessage[] },
27
+ });
28
+ expect(result.current.showSlidingPanel).toBe(false);
29
+ rerender({ messages: [question] });
30
+ expect(result.current.showSlidingPanel).toBe(false);
31
+ });
32
+
33
+ it('opens when a plan arrives and resets for a new empty run', () => {
34
+ const { result, rerender } = renderHook(({ messages }) => useAgentPlans(messages, true), {
35
+ initialProps: { messages: [question] },
36
+ });
37
+ rerender({ messages: [question, plan] });
38
+ expect(result.current.showSlidingPanel).toBe(true);
39
+ expect(result.current.plans).toHaveLength(1);
40
+ rerender({ messages: [] });
41
+ expect(result.current.showSlidingPanel).toBe(false);
42
+ });
43
+
44
+ it('preserves manual opening when subsequent messages have no plan', () => {
45
+ const { result, rerender } = renderHook(({ messages }) => useAgentPlans(messages, true), {
46
+ initialProps: { messages: [question] },
47
+ });
48
+ act(() => result.current.setShowSlidingPanel(true));
49
+ rerender({ messages: [question, { ...question, timestamp: 3, type: AgentMessageType.ANSWER }] });
50
+ expect(result.current.showSlidingPanel).toBe(true);
51
+ });
52
+ });