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