@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.
- package/lib/core/components/Link.js +1 -1
- package/lib/core/components/Link.js.map +1 -1
- package/lib/core/components/SelectList.d.ts.map +1 -1
- package/lib/core/components/SelectList.js +3 -3
- package/lib/core/components/SelectList.js.map +1 -1
- package/lib/core/components/SidePanel.d.ts.map +1 -1
- package/lib/core/components/SidePanel.js +2 -1
- package/lib/core/components/SidePanel.js.map +1 -1
- package/lib/core/components/Spinner.js +1 -1
- package/lib/core/components/Spinner.js.map +1 -1
- package/lib/core/components/TagsInput.js +7 -7
- package/lib/core/components/TagsInput.js.map +1 -1
- package/lib/core/components/shadcn/badge.js +1 -1
- package/lib/core/components/shadcn/badge.js.map +1 -1
- package/lib/core/components/shadcn/button.d.ts.map +1 -1
- package/lib/core/components/shadcn/button.js +2 -2
- package/lib/core/components/shadcn/button.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +2 -2
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
- package/lib/core/components/shadcn/filters/filter/dateFilter.js.map +1 -1
- package/lib/core/components/shadcn/filters/filters.js +1 -1
- package/lib/core/components/shadcn/filters/filters.js.map +1 -1
- package/lib/core/components/shadcn/overflowTabs.js +2 -2
- package/lib/core/components/shadcn/overflowTabs.js.map +1 -1
- package/lib/core/components/shadcn/tabs.js +1 -1
- package/lib/core/components/shadcn/tabs.js.map +1 -1
- package/lib/features/activity-doc/ActivityDoc.js +1 -1
- package/lib/features/activity-doc/ActivityDoc.js.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.d.ts +1 -0
- package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.js +15 -4
- package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +51 -17
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +5 -3
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +5 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +9 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentPlans.d.ts +1 -1
- package/lib/features/agent/chat/hooks/useAgentPlans.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentPlans.js +5 -5
- package/lib/features/agent/chat/hooks/useAgentPlans.js.map +1 -1
- package/lib/features/layout/GenericPageNavHeader.js +1 -1
- package/lib/features/layout/GenericPageNavHeader.js.map +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
- package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
- package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.d.ts +2 -2
- package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.js +9 -7
- package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.js +7 -3
- package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
- package/lib/features/oauth/useMcpConnections.d.ts +4 -2
- package/lib/features/oauth/useMcpConnections.d.ts.map +1 -1
- package/lib/features/oauth/useMcpConnections.js +15 -9
- package/lib/features/oauth/useMcpConnections.js.map +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
- package/lib/features/store/objects/DocumentPreviewPanel.js +5 -5
- package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
- package/lib/features/store/objects/components/DocumentInput.js +1 -1
- package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -0
- package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -2
- package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js +6 -3
- package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
- package/lib/features/store/objects/selection/actions/RunIntakeAction.d.ts +8 -0
- package/lib/features/store/objects/selection/actions/RunIntakeAction.d.ts.map +1 -0
- package/lib/features/store/objects/selection/actions/RunIntakeAction.js +65 -0
- package/lib/features/store/objects/selection/actions/RunIntakeAction.js.map +1 -0
- package/lib/features/store/objects/selection/actions/index.d.ts +1 -0
- package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
- package/lib/features/store/objects/selection/actions/index.js +1 -0
- package/lib/features/store/objects/selection/actions/index.js.map +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.js +11 -3
- package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js +14 -14
- package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +275 -150
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +275 -150
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/features/user/UserInfo.d.ts +2 -0
- package/lib/features/user/UserInfo.d.ts.map +1 -1
- package/lib/features/user/UserInfo.js +1 -1
- package/lib/features/user/UserInfo.js.map +1 -1
- package/lib/i18n/locales/ar.json +28 -7
- package/lib/i18n/locales/de.json +24 -7
- package/lib/i18n/locales/en.json +24 -7
- package/lib/i18n/locales/es.json +24 -7
- package/lib/i18n/locales/fr.json +25 -7
- package/lib/i18n/locales/it.json +24 -7
- package/lib/i18n/locales/ja.json +24 -7
- package/lib/i18n/locales/ko.json +24 -7
- package/lib/i18n/locales/pt.json +24 -7
- package/lib/i18n/locales/ru.json +26 -7
- package/lib/i18n/locales/tr.json +24 -7
- package/lib/i18n/locales/zh-TW.json +24 -7
- package/lib/i18n/locales/zh.json +24 -7
- package/lib/router/RouteComponent.d.ts.map +1 -1
- package/lib/router/RouteComponent.js +15 -12
- package/lib/router/RouteComponent.js.map +1 -1
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/session/UserSession.js +4 -0
- package/lib/session/UserSession.js.map +1 -1
- package/lib/shell/SplashScreen.js +1 -1
- package/lib/shell/SplashScreen.js.map +1 -1
- package/lib/shell/apps/AppInstallationProvider.d.ts +4 -4
- package/lib/shell/login/SignInIcons.d.ts.map +1 -1
- package/lib/shell/login/SignInIcons.js +2 -1
- package/lib/shell/login/SignInIcons.js.map +1 -1
- package/lib/shell/login/SignInPasswordStep.d.ts +9 -0
- package/lib/shell/login/SignInPasswordStep.d.ts.map +1 -0
- package/lib/shell/login/SignInPasswordStep.js +58 -0
- package/lib/shell/login/SignInPasswordStep.js.map +1 -0
- package/lib/shell/login/SignInPrimitives.d.ts +13 -0
- package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
- package/lib/shell/login/SignInPrimitives.js +11 -1
- package/lib/shell/login/SignInPrimitives.js.map +1 -1
- package/lib/shell/login/SignInProvidersStep.d.ts +3 -1
- package/lib/shell/login/SignInProvidersStep.d.ts.map +1 -1
- package/lib/shell/login/SignInProvidersStep.js +4 -2
- package/lib/shell/login/SignInProvidersStep.js.map +1 -1
- package/lib/shell/login/SignInReturningStep.d.ts +3 -1
- package/lib/shell/login/SignInReturningStep.d.ts.map +1 -1
- package/lib/shell/login/SignInReturningStep.js +18 -8
- package/lib/shell/login/SignInReturningStep.js.map +1 -1
- package/lib/shell/login/SignInTenantStep.d.ts +3 -1
- package/lib/shell/login/SignInTenantStep.d.ts.map +1 -1
- package/lib/shell/login/SignInTenantStep.js +2 -2
- package/lib/shell/login/SignInTenantStep.js.map +1 -1
- package/lib/shell/login/SignInWithProviderButton.d.ts +5 -3
- package/lib/shell/login/SignInWithProviderButton.d.ts.map +1 -1
- package/lib/shell/login/SignInWithProviderButton.js +8 -7
- package/lib/shell/login/SignInWithProviderButton.js.map +1 -1
- package/lib/shell/login/SigninScreen.d.ts.map +1 -1
- package/lib/shell/login/SigninScreen.js +13 -4
- package/lib/shell/login/SigninScreen.js.map +1 -1
- package/lib/shell/login/signInUtils.d.ts +20 -4
- package/lib/shell/login/signInUtils.d.ts.map +1 -1
- package/lib/shell/login/signInUtils.js +47 -1
- package/lib/shell/login/signInUtils.js.map +1 -1
- package/lib/vertesia-ui-core.js +3 -3
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +33 -33
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.BoNE1APG.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.BoNE1APG.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.BoPtiSPb.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.BoPtiSPb.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.C4BBW5v5.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.C4BBW5v5.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.C81wuNkY.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.C81wuNkY.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.C9GipKIH.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.C9GipKIH.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.CWsoS0W9.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.CWsoS0W9.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.Cr9NtzZB.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Cr9NtzZB.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DEpkHOD5.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DEpkHOD5.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DPXaRKFO.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DPXaRKFO.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.D_QNm1Xg.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.D_QNm1Xg.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.F26Cc0f7.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.F26Cc0f7.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.YkXIESV_.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.YkXIESV_.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.js +3 -3
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-router.js +1 -1
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +15 -9
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +2 -2
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/json-view/JSONCode.js +1 -1
- package/lib/widgets/json-view/JSONCode.js.map +1 -1
- package/lib/widgets/upload/DropZone.d.ts.map +1 -1
- package/lib/widgets/upload/DropZone.js +1 -1
- package/lib/widgets/upload/DropZone.js.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts +1 -1
- package/package.json +7 -7
- package/src/core/components/Link.tsx +1 -1
- package/src/core/components/SelectList.tsx +5 -2
- package/src/core/components/SidePanel.tsx +65 -60
- package/src/core/components/Spinner.tsx +1 -1
- package/src/core/components/TagsInput.tsx +7 -7
- package/src/core/components/shadcn/badge.tsx +1 -1
- package/src/core/components/shadcn/button.tsx +2 -3
- package/src/core/components/shadcn/filters/comboBox/DateCombobox.tsx +2 -2
- package/src/core/components/shadcn/filters/comboBox/SelectCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/comboBox/StringListCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/comboBox/TextCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/filter/dateFilter.tsx +1 -1
- package/src/core/components/shadcn/filters/filters.tsx +1 -1
- package/src/core/components/shadcn/overflowTabs.tsx +2 -2
- package/src/core/components/shadcn/tabs.tsx +1 -1
- package/src/css/utilities.css +7 -0
- package/src/features/activity-doc/ActivityDoc.tsx +1 -1
- package/src/features/agent/chat/AskUserWidget.test.tsx +24 -2
- package/src/features/agent/chat/AskUserWidget.tsx +19 -4
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +150 -1
- package/src/features/agent/chat/ModernAgentConversation.tsx +59 -21
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +62 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +8 -7
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +17 -1
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +4 -3
- package/src/features/agent/chat/hooks/useAgentPlans.test.ts +52 -0
- package/src/features/agent/chat/hooks/useAgentPlans.ts +5 -5
- package/src/features/layout/GenericPageNavHeader.test.tsx +36 -0
- package/src/features/layout/GenericPageNavHeader.tsx +1 -1
- package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -1
- package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
- package/src/features/oauth/McpConnectionsButton.test.tsx +56 -0
- package/src/features/oauth/McpConnectionsButton.tsx +9 -6
- package/src/features/oauth/McpConnectionsDialog.tsx +14 -5
- package/src/features/oauth/useMcpConnections.test.ts +10 -0
- package/src/features/oauth/useMcpConnections.ts +18 -10
- package/src/features/permissions/UserPermissionsProvider.tsx +1 -1
- package/src/features/store/objects/DocumentPreviewPanel.tsx +10 -10
- package/src/features/store/objects/components/DocumentInput.tsx +1 -1
- package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -0
- package/src/features/store/objects/selection/SelectionActions.tsx +13 -6
- package/src/features/store/objects/selection/actions/RunIntakeAction.tsx +69 -0
- package/src/features/store/objects/selection/actions/index.ts +1 -0
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +1 -1
- package/src/features/store/types/ContentObjectTypesTable.tsx +20 -3
- package/src/features/store/types/IntakePolicyEditor.tsx +33 -28
- package/src/features/user/UserInfo.tsx +1 -1
- package/src/i18n/locales/ar.json +28 -7
- package/src/i18n/locales/de.json +24 -7
- package/src/i18n/locales/en.json +24 -7
- package/src/i18n/locales/es.json +24 -7
- package/src/i18n/locales/fr.json +25 -7
- package/src/i18n/locales/it.json +24 -7
- package/src/i18n/locales/ja.json +24 -7
- package/src/i18n/locales/ko.json +24 -7
- package/src/i18n/locales/pt.json +24 -7
- package/src/i18n/locales/ru.json +26 -7
- package/src/i18n/locales/tr.json +24 -7
- package/src/i18n/locales/zh-TW.json +24 -7
- package/src/i18n/locales/zh.json +24 -7
- package/src/router/RouteComponent.test.tsx +65 -1
- package/src/router/RouteComponent.tsx +18 -15
- package/src/session/UserSession.test.ts +65 -0
- package/src/session/UserSession.ts +4 -0
- package/src/shell/SplashScreen.tsx +1 -1
- package/src/shell/login/SignInIcons.tsx +2 -1
- package/src/shell/login/SignInPasswordRequired.test.tsx +82 -0
- package/src/shell/login/SignInPasswordStep.test.tsx +71 -0
- package/src/shell/login/SignInPasswordStep.tsx +103 -0
- package/src/shell/login/SignInPrimitives.tsx +53 -1
- package/src/shell/login/SignInProvidersStep.tsx +8 -4
- package/src/shell/login/SignInReturningStep.tsx +20 -8
- package/src/shell/login/SignInTenantStep.tsx +6 -2
- package/src/shell/login/SignInWithProviderButton.tsx +11 -7
- package/src/shell/login/SigninScreen.tsx +32 -3
- package/src/shell/login/signInPassword.test.ts +133 -0
- package/src/shell/login/signInUtils.ts +60 -7
- package/src/widgets/json-view/JSONCode.test.tsx +2 -2
- package/src/widgets/json-view/JSONCode.tsx +1 -1
- package/src/widgets/upload/DropZone.tsx +2 -4
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +0 -7
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +0 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +0 -51
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +0 -1
- package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.DN79y06J.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.DN79y06J.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js.map +0 -1
- package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js +0 -5
- package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js.map +0 -1
- 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
|
|
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 ||
|
|
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
|
-
|
|
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('
|
|
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
|
|
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
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
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
|
-
() =>
|
|
1681
|
-
[
|
|
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' :
|
|
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
|
|
2335
|
-
|
|
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={!
|
|
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 [
|
|
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={!
|
|
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
|
+
});
|