@vertesia/ui 1.5.0-dev.20260804.124748Z → 1.5.0-dev.20260807.073259Z
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/ComboBox.d.ts.map +1 -1
- package/lib/core/components/ComboBox.js +4 -4
- package/lib/core/components/ComboBox.js.map +1 -1
- package/lib/core/components/FileUpload.js.map +1 -1
- package/lib/core/components/MenuList.d.ts +2 -1
- package/lib/core/components/MenuList.d.ts.map +1 -1
- package/lib/core/components/TagsInput.js +1 -1
- package/lib/core/components/popup/PopupController.d.ts.map +1 -1
- package/lib/core/components/popup/PopupController.js.map +1 -1
- package/lib/core/components/popup/position.js.map +1 -1
- package/lib/core/components/popup/utils.js.map +1 -1
- package/lib/core/components/shadcn/LanguageSwitcher.js.map +1 -1
- package/lib/core/components/shadcn/MessageBox.js.map +1 -1
- package/lib/core/components/shadcn/Panel.js +1 -1
- package/lib/core/components/shadcn/badge.d.ts +2 -2
- package/lib/core/components/shadcn/button.d.ts +2 -2
- package/lib/core/components/shadcn/button.js +1 -1
- package/lib/core/components/shadcn/command.d.ts +4 -4
- package/lib/core/components/shadcn/command.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/DynamicLabel.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/TextCombobox.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
- package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/filters.js.map +1 -1
- package/lib/core/components/shadcn/input.d.ts +2 -2
- package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
- package/lib/core/components/shadcn/popover.d.ts +1 -1
- package/lib/core/components/shadcn/popover.d.ts.map +1 -1
- package/lib/core/components/shadcn/popover.js.map +1 -1
- package/lib/core/components/shadcn/radioGroup.d.ts.map +1 -1
- package/lib/core/components/shadcn/selectBox.js +1 -1
- package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
- package/lib/core/components/shadcn/tabs.js +1 -1
- package/lib/core/components/shadcn/text.d.ts +2 -2
- package/lib/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
- package/lib/core/components/toast/ToastProvider.js +1 -1
- package/lib/core/hooks/CompositeState.d.ts +1 -1
- package/lib/core/hooks/CompositeState.d.ts.map +1 -1
- package/lib/core/hooks/CompositeState.js.map +1 -1
- package/lib/core/hooks/SharedState.d.ts.map +1 -1
- package/lib/core/hooks/useFetch.d.ts.map +1 -1
- package/lib/core/hooks/useFetch.js +2 -1
- package/lib/core/hooks/useFetch.js.map +1 -1
- package/lib/core/hooks/useFlag.d.ts.map +1 -1
- package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
- package/lib/core/hooks/useSafeLayoutEffect.d.ts +2 -2
- package/lib/core/hooks/useSafeLayoutEffect.d.ts.map +1 -1
- package/lib/core/hooks/useScrollableSearch.js.map +1 -1
- package/lib/core/hooks/useSharedValue.d.ts.map +1 -1
- package/lib/core/hooks/useSharedValue.js.map +1 -1
- package/lib/core/utils/a11y.d.ts.map +1 -1
- package/lib/env/index.d.ts.map +1 -1
- package/lib/env/index.js.map +1 -1
- package/lib/features/agent/PayloadBuilder.d.ts +1 -1
- package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
- package/lib/features/agent/PayloadBuilder.js.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.js +1 -1
- package/lib/features/agent/chat/AskUserWidget.js +1 -1
- package/lib/features/agent/chat/DocumentEditingActionCard.js +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +26 -3
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +7 -0
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +25 -0
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/BatchProgressPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/StreamingMessage.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +8 -0
- package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
- package/lib/features/agent/chat/SlidingThinkingIndicator.js +1 -1
- package/lib/features/agent/chat/VegaLiteChart.js +1 -1
- package/lib/features/agent/chat/VegaLiteChart.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.js +26 -0
- package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +8 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.js +31 -2
- package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
- package/lib/features/agent/chat/useArtifactUrlCache.js.map +1 -1
- package/lib/features/agent/chat/workstreams.d.ts +1 -1
- package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/DocumentsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/InteractionsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/WorkflowExecutionsFacetsNav.d.ts.map +1 -1
- package/lib/features/magic-pdf/GroundedExtractionView.js +1 -1
- package/lib/features/magic-pdf/MagicPdfProvider.d.ts.map +1 -1
- package/lib/features/magic-pdf/MagicPdfProvider.js.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.js +1 -1
- package/lib/features/oauth/OAuthProviderConnectButton.js +1 -1
- package/lib/features/oauth/RemoteMcpConnectionButton.js +1 -1
- package/lib/features/pdf-viewer/PdfPageRenderer.js +1 -1
- package/lib/features/pdf-viewer/PdfPageSlider.js +1 -1
- package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
- package/lib/features/store/collections/CollectionsTable.js +2 -2
- package/lib/features/store/collections/CollectionsTable.js.map +1 -1
- package/lib/features/store/collections/CreateCollection.js +1 -1
- package/lib/features/store/collections/EditCollectionView.js +1 -1
- package/lib/features/store/objects/DocumentQuickFilter.js +1 -1
- package/lib/features/store/objects/DocumentSelectionProvider.d.ts.map +1 -1
- package/lib/features/store/objects/DocumentSelectionProvider.js.map +1 -1
- package/lib/features/store/objects/components/ContentDispositionButton.d.ts +2 -1
- package/lib/features/store/objects/components/ContentDispositionButton.d.ts.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +1 -1
- package/lib/features/store/objects/components/DocumentEditingPanel.js +1 -1
- package/lib/features/store/objects/components/PropertiesEditorModal.js +1 -1
- package/lib/features/store/objects/components/TextEditorPanel.js +1 -1
- package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts +2 -2
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
- package/lib/features/store/objects/layout/DocumentTableColumn.d.ts +1 -1
- package/lib/features/store/objects/layout/DocumentTableColumn.d.ts.map +1 -1
- 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.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts +25 -25
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContextClass.js.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js +1 -1
- package/lib/features/store/objects/selection/actions/ConfirmAction.js +1 -1
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
- package/lib/features/store/objects/upload/useSmartFileUploadProcessing.d.ts.map +1 -1
- package/lib/features/store/objects/upload/useUploadHandler.d.ts.map +1 -1
- package/lib/features/store/types/ContentObjectTypesSearch.js +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +240 -180
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/TypeRegistry.js.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +239 -179
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.js.map +1 -1
- package/lib/features/user/UserInfo.d.ts +14 -14
- package/lib/features/user/UserInfo.js +1 -1
- package/lib/features/user/UserInfo.js.map +1 -1
- package/lib/features/utils/mimeType.d.ts.map +1 -1
- package/lib/features/utils/rendition.js.map +1 -1
- package/lib/features/utils/text.js.map +1 -1
- package/lib/i18n/locales/ar.json +25 -0
- package/lib/i18n/locales/de.json +25 -0
- package/lib/i18n/locales/en.json +25 -0
- package/lib/i18n/locales/es.json +25 -0
- package/lib/i18n/locales/fr.json +25 -0
- package/lib/i18n/locales/it.json +25 -0
- package/lib/i18n/locales/ja.json +25 -0
- package/lib/i18n/locales/ko.json +25 -0
- package/lib/i18n/locales/pt.json +25 -0
- package/lib/i18n/locales/ru.json +25 -0
- package/lib/i18n/locales/tr.json +25 -0
- package/lib/i18n/locales/zh-TW.json +25 -0
- package/lib/i18n/locales/zh.json +25 -0
- package/lib/i18n/rtl.d.ts +1 -1
- package/lib/i18n/rtl.d.ts.map +1 -1
- package/lib/layout/FullHeightLayout.d.ts +7 -6
- package/lib/layout/FullHeightLayout.d.ts.map +1 -1
- package/lib/layout/Sidebar.d.ts +1 -1
- package/lib/layout/Sidebar.js +1 -1
- package/lib/rich-text/EditorToolbar.js +1 -1
- package/lib/router/HistoryNavigator.d.ts +2 -2
- package/lib/router/HistoryNavigator.d.ts.map +1 -1
- package/lib/router/HistoryNavigator.js.map +1 -1
- package/lib/router/PathMatcher.d.ts.map +1 -1
- package/lib/router/PathMatcher.js.map +1 -1
- package/lib/router/PathWithParams.d.ts.map +1 -1
- package/lib/router/Redirect.js.map +1 -1
- package/lib/router/Route404.js +1 -1
- package/lib/router/Router.d.ts.map +1 -1
- package/lib/router/Router.js.map +1 -1
- package/lib/session/UserSession.d.ts +1 -1
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/session/UserSession.js.map +1 -1
- package/lib/session/UserSessionProvider.d.ts +2 -0
- package/lib/session/UserSessionProvider.d.ts.map +1 -1
- package/lib/session/UserSessionProvider.js +81 -53
- package/lib/session/UserSessionProvider.js.map +1 -1
- package/lib/session/auth/composable.d.ts +49 -1
- package/lib/session/auth/composable.d.ts.map +1 -1
- package/lib/session/auth/composable.js +179 -53
- package/lib/session/auth/composable.js.map +1 -1
- package/lib/shell/VertesiaShell.js +1 -1
- package/lib/shell/apps/AppInstallationProvider.d.ts +58 -50
- package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
- package/lib/shell/apps/AppProjectSelector.js +1 -1
- package/lib/shell/login/SignInEmailStep.js +1 -1
- package/lib/shell/login/SignInPrimitives.d.ts +21 -20
- package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
- package/lib/shell/login/SignInPrimitives.js.map +1 -1
- package/lib/shell/login/SignInRecoveryStep.d.ts +22 -0
- package/lib/shell/login/SignInRecoveryStep.d.ts.map +1 -0
- package/lib/shell/login/SignInRecoveryStep.js +59 -0
- package/lib/shell/login/SignInRecoveryStep.js.map +1 -0
- package/lib/shell/login/SigninScreen.d.ts.map +1 -1
- package/lib/shell/login/SigninScreen.js +84 -11
- package/lib/shell/login/SigninScreen.js.map +1 -1
- package/lib/shell/login/SignupForm.js +1 -1
- package/lib/shell/login/TerminalLogin.js +1 -1
- package/lib/shell/login/UserSessionMenu.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +26 -26
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +13 -13
- package/lib/vertesia-ui-layout.js.map +1 -1
- package/lib/vertesia-ui-rich-text.js.map +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 +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/form/FormContext.d.ts.map +1 -1
- package/lib/widgets/form/FormContext.js.map +1 -1
- package/lib/widgets/form/ManagedObject.d.ts +2 -2
- package/lib/widgets/form/ManagedObject.d.ts.map +1 -1
- package/lib/widgets/form/ManagedObject.js.map +1 -1
- package/lib/widgets/form/schema.d.ts.map +1 -1
- package/lib/widgets/form/schema.js.map +1 -1
- package/lib/widgets/json-view/JSONCode.js +1 -1
- package/lib/widgets/json-view/JSONTable.js +1 -1
- package/lib/widgets/markdown/ArtifactContentRenderer.js.map +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts.map +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.js +1 -1
- package/lib/widgets/markdown/CodeBlockRendering.d.ts.map +1 -1
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +1 -1
- package/lib/widgets/markdown/remarkDirectiveHandler.d.ts.map +1 -1
- package/lib/widgets/markdown/textDiff.js.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
- package/lib/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
- package/lib/widgets/schema-editor/editor/PropertyEditor.js +1 -1
- package/lib/widgets/schema-editor/json-schema4-utils.js.map +1 -1
- package/lib/widgets/xml-viewer/components/Elements/index.js +1 -1
- package/lib/widgets/xml-viewer/components/Tag/index.js +1 -1
- package/lib/widgets/xml-viewer/constants/index.d.ts.map +1 -1
- package/lib/widgets/xml-viewer/helpers/index.d.ts +1 -1
- package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts +1 -1
- package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/core/hooks/useFetch.ts +2 -1
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +13 -0
- package/src/features/agent/chat/ModernAgentConversation.tsx +24 -3
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.test.ts +38 -0
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +26 -0
- package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +9 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +21 -0
- package/src/features/agent/chat/hooks/useAgentStream.test.tsx +116 -1
- package/src/features/agent/chat/hooks/useAgentStream.ts +26 -0
- package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +87 -0
- package/src/features/agent/chat/hooks/useFileProcessing.ts +32 -1
- package/src/features/store/collections/CollectionsTable.tsx +1 -1
- package/src/i18n/locales/ar.json +25 -0
- package/src/i18n/locales/de.json +25 -0
- package/src/i18n/locales/en.json +25 -0
- package/src/i18n/locales/es.json +25 -0
- package/src/i18n/locales/fr.json +25 -0
- package/src/i18n/locales/it.json +25 -0
- package/src/i18n/locales/ja.json +25 -0
- package/src/i18n/locales/ko.json +25 -0
- package/src/i18n/locales/pt.json +25 -0
- package/src/i18n/locales/ru.json +25 -0
- package/src/i18n/locales/tr.json +25 -0
- package/src/i18n/locales/zh-TW.json +25 -0
- package/src/i18n/locales/zh.json +25 -0
- package/src/session/UserSessionProvider.test.ts +69 -0
- package/src/session/UserSessionProvider.tsx +96 -60
- package/src/session/auth/composable.scope.test.ts +88 -0
- package/src/session/auth/composable.test.ts +205 -0
- package/src/session/auth/composable.ts +227 -58
- package/src/shell/login/SignInPrimitives.tsx +1 -0
- package/src/shell/login/SignInRecoveryStep.test.tsx +112 -0
- package/src/shell/login/SignInRecoveryStep.tsx +177 -0
- package/src/shell/login/SigninScreen.tsx +145 -27
|
@@ -57,6 +57,7 @@ import type {
|
|
|
57
57
|
AgentInitialRequestTemplateContext,
|
|
58
58
|
} from './ModernAgentOutput/AllMessagesMixed';
|
|
59
59
|
import AllMessagesMixed from './ModernAgentOutput/AllMessagesMixed';
|
|
60
|
+
import { collectDeliveredArtifactRefs } from './ModernAgentOutput/AttachmentPreview';
|
|
60
61
|
import type { BatchProgressPanelClassNames } from './ModernAgentOutput/BatchProgressPanel';
|
|
61
62
|
import Header from './ModernAgentOutput/Header';
|
|
62
63
|
import MessageInput, {
|
|
@@ -236,12 +237,21 @@ function isWorkstreamActivityFailureMessage(message: AgentMessage): boolean {
|
|
|
236
237
|
tool_event?: unknown;
|
|
237
238
|
tool_run_id?: unknown;
|
|
238
239
|
tool_status?: unknown;
|
|
240
|
+
workstream_id?: unknown;
|
|
239
241
|
workstream_event?: unknown;
|
|
240
242
|
}
|
|
241
243
|
| undefined;
|
|
242
244
|
|
|
243
245
|
if (details?.event_class !== 'activity') return false;
|
|
244
246
|
if (details.workstream_event) return false;
|
|
247
|
+
const workstreamId =
|
|
248
|
+
typeof message.workstream_id === 'string' && message.workstream_id.trim()
|
|
249
|
+
? message.workstream_id.trim()
|
|
250
|
+
: typeof details.workstream_id === 'string'
|
|
251
|
+
? details.workstream_id.trim()
|
|
252
|
+
: '';
|
|
253
|
+
const normalizedWorkstreamId = workstreamId.toLowerCase();
|
|
254
|
+
if (!workstreamId || normalizedWorkstreamId === 'main' || normalizedWorkstreamId === 'all') return false;
|
|
245
255
|
|
|
246
256
|
return !(
|
|
247
257
|
details.tool ||
|
|
@@ -1487,8 +1497,9 @@ function ModernAgentConversationInner({
|
|
|
1487
1497
|
updateDocumentTitle,
|
|
1488
1498
|
} = useDocumentPanel(messages);
|
|
1489
1499
|
|
|
1500
|
+
const deliveredArtifactRefs = useMemo(() => collectDeliveredArtifactRefs(messages), [messages]);
|
|
1490
1501
|
const { processingFiles, hasProcessingFiles, handleFileUpload, removeProcessingFile, clearProcessingFiles } =
|
|
1491
|
-
useFileProcessing(client, agentRunId, serverFileUpdates, toast);
|
|
1502
|
+
useFileProcessing(client, agentRunId, serverFileUpdates, toast, deliveredArtifactRefs);
|
|
1492
1503
|
const canUploadFiles = interactive && !hideFileUpload;
|
|
1493
1504
|
|
|
1494
1505
|
const handleRemoveProcessingFile = useCallback(
|
|
@@ -1600,6 +1611,16 @@ function ModernAgentConversationInner({
|
|
|
1600
1611
|
() => effectiveWorkflowStatus?.toUpperCase() === 'FAILED' || agentRunStatus?.toUpperCase() === 'FAILED',
|
|
1601
1612
|
[effectiveWorkflowStatus, agentRunStatus],
|
|
1602
1613
|
);
|
|
1614
|
+
const workflowFailureReason = useMemo(() => {
|
|
1615
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1616
|
+
const message = messages[index];
|
|
1617
|
+
if (message.type !== AgentMessageType.ERROR) continue;
|
|
1618
|
+
if (getWorkstreamId(message) !== 'main') continue;
|
|
1619
|
+
const text = typeof message.message === 'string' ? message.message.trim() : '';
|
|
1620
|
+
if (text) return text;
|
|
1621
|
+
}
|
|
1622
|
+
return undefined;
|
|
1623
|
+
}, [messages]);
|
|
1603
1624
|
|
|
1604
1625
|
// Read inside handleSendMessage (a stable callback) without widening its deps.
|
|
1605
1626
|
const isWorkflowTerminalRef = useRef(isWorkflowTerminal);
|
|
@@ -2774,8 +2795,8 @@ function ModernAgentConversationInner({
|
|
|
2774
2795
|
// never render for a failed run. Use the caller's action when provided,
|
|
2775
2796
|
// otherwise fall back to the default failed message box.
|
|
2776
2797
|
(failedAction ?? (
|
|
2777
|
-
<MessageBox status="error"
|
|
2778
|
-
|
|
2798
|
+
<MessageBox status="error" title={t('agent.failed')} className="m-2">
|
|
2799
|
+
{workflowFailureReason ?? t('agent.error')}
|
|
2779
2800
|
</MessageBox>
|
|
2780
2801
|
))
|
|
2781
2802
|
) : effectiveWorkflowStatus &&
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type AgentMessage, AgentMessageType } from '@vertesia/common';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { collectDeliveredArtifactRefs } from './AttachmentPreview';
|
|
4
|
+
|
|
5
|
+
function question(message: string, details?: AgentMessage['details']): AgentMessage {
|
|
6
|
+
return {
|
|
7
|
+
timestamp: 1_000,
|
|
8
|
+
workflow_run_id: 'run-1',
|
|
9
|
+
type: AgentMessageType.QUESTION,
|
|
10
|
+
message,
|
|
11
|
+
workstream_id: 'main',
|
|
12
|
+
details,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('collectDeliveredArtifactRefs', () => {
|
|
17
|
+
it('collects attachment references from persisted user messages', () => {
|
|
18
|
+
const refs = collectDeliveredArtifactRefs([
|
|
19
|
+
question('Review this.\n\nUploaded artifacts:\n[report.pdf](artifact:files/report.pdf)'),
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
expect(refs).toEqual(new Set(['artifact:files/report.pdf', 'files/report.pdf']));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it.each(['sending', 'received', 'consumed', 'failed'] as const)(
|
|
26
|
+
'ignores optimistic messages with %s delivery status',
|
|
27
|
+
(_deliveryStatus) => {
|
|
28
|
+
const refs = collectDeliveredArtifactRefs([
|
|
29
|
+
question('Review this.\n\nUploaded artifacts:\n[report.pdf](artifact:files/report.pdf)', {
|
|
30
|
+
_optimistic: true,
|
|
31
|
+
_deliveryStatus,
|
|
32
|
+
}),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
expect(refs).toEqual(new Set());
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AgentMessage, AgentMessageType } from '@vertesia/common';
|
|
1
2
|
import { Button, cn } from '@vertesia/ui/core';
|
|
2
3
|
import { useUITranslation } from '@vertesia/ui/i18n';
|
|
3
4
|
import { UserSessionContext } from '@vertesia/ui/session';
|
|
@@ -475,3 +476,28 @@ export function parseUserMessageAttachments(content: string): ParsedUserAttachme
|
|
|
475
476
|
attachments,
|
|
476
477
|
};
|
|
477
478
|
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Collect the artifact references (`artifact:...` hrefs and bare artifact paths) embedded in
|
|
482
|
+
* sent user messages. A file whose reference appears here has already been delivered to the
|
|
483
|
+
* agent, so the composer must not offer it as a pending attachment again (e.g. after a reload).
|
|
484
|
+
*/
|
|
485
|
+
export function collectDeliveredArtifactRefs(messages: AgentMessage[]): Set<string> {
|
|
486
|
+
const refs = new Set<string>();
|
|
487
|
+
for (const message of messages) {
|
|
488
|
+
// Only persisted server echoes prove delivery. Optimistic messages can still be sending
|
|
489
|
+
// or can fail, in which case suppressing their files would prevent a retry.
|
|
490
|
+
if (
|
|
491
|
+
message.type !== AgentMessageType.QUESTION ||
|
|
492
|
+
message.details?._optimistic ||
|
|
493
|
+
typeof message.message !== 'string'
|
|
494
|
+
) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
for (const attachment of parseUserMessageAttachments(message.message).attachments) {
|
|
498
|
+
if (attachment.href) refs.add(attachment.href);
|
|
499
|
+
if (attachment.artifactPath) refs.add(attachment.artifactPath);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return refs;
|
|
503
|
+
}
|
|
@@ -181,12 +181,21 @@ function isWorkstreamActivityFailureMessage(message: AgentMessage): boolean {
|
|
|
181
181
|
tool_event?: unknown;
|
|
182
182
|
tool_run_id?: unknown;
|
|
183
183
|
tool_status?: unknown;
|
|
184
|
+
workstream_id?: unknown;
|
|
184
185
|
workstream_event?: unknown;
|
|
185
186
|
}
|
|
186
187
|
| undefined;
|
|
187
188
|
|
|
188
189
|
if (details?.event_class !== 'activity') return false;
|
|
189
190
|
if (details.workstream_event) return false;
|
|
191
|
+
const workstreamId =
|
|
192
|
+
typeof message.workstream_id === 'string' && message.workstream_id.trim()
|
|
193
|
+
? message.workstream_id.trim()
|
|
194
|
+
: typeof details.workstream_id === 'string'
|
|
195
|
+
? details.workstream_id.trim()
|
|
196
|
+
: '';
|
|
197
|
+
const normalizedWorkstreamId = workstreamId.toLowerCase();
|
|
198
|
+
if (!workstreamId || normalizedWorkstreamId === 'main' || normalizedWorkstreamId === 'all') return false;
|
|
190
199
|
|
|
191
200
|
return !(
|
|
192
201
|
details.tool ||
|
|
@@ -576,6 +576,27 @@ describe('ModernAgentOutput summary conversation items', () => {
|
|
|
576
576
|
]);
|
|
577
577
|
});
|
|
578
578
|
|
|
579
|
+
it('keeps a main-workstream activity error visible as a primary message', () => {
|
|
580
|
+
const question = makeMessage({
|
|
581
|
+
timestamp: 1000,
|
|
582
|
+
type: AgentMessageType.QUESTION,
|
|
583
|
+
message: 'do the thing',
|
|
584
|
+
});
|
|
585
|
+
const terminationError = makeMessage({
|
|
586
|
+
timestamp: 1100,
|
|
587
|
+
type: AgentMessageType.ERROR,
|
|
588
|
+
message: 'Stopped: the agent called create_document with the same input 4 times in a row.',
|
|
589
|
+
details: { event_class: 'activity' },
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
const items = buildSummaryConversationItems([question, terminationError], true);
|
|
593
|
+
|
|
594
|
+
expect(items).toEqual([
|
|
595
|
+
{ type: 'message', message: question },
|
|
596
|
+
{ type: 'message', message: terminationError },
|
|
597
|
+
]);
|
|
598
|
+
});
|
|
599
|
+
|
|
579
600
|
it('hides transient thinking once the work segment completes', () => {
|
|
580
601
|
const tool = makeMessage({
|
|
581
602
|
timestamp: 1000,
|
|
@@ -362,7 +362,12 @@ describe('useAgentStream', () => {
|
|
|
362
362
|
const { result } = renderHook(() => useAgentStream(client, 'agent-run-1', vi.fn()));
|
|
363
363
|
|
|
364
364
|
await waitFor(() => expect(deliver).toBeDefined());
|
|
365
|
-
|
|
365
|
+
// History hydrates the latest archived inventory once so an unconsumed staged file
|
|
366
|
+
// survives a reload; the live replay of that same snapshot is ignored, leaving the
|
|
367
|
+
// hydrated entry untouched. Already-delivered files are dropped downstream by
|
|
368
|
+
// useFileProcessing (consumed_at + delivered artifact refs), not by refusing to hydrate.
|
|
369
|
+
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
370
|
+
expect(result.current.serverFileUpdates.has('old-upload')).toBe(true);
|
|
366
371
|
|
|
367
372
|
act(() => deliver?.(fileSnapshot(1_100, 'new-upload')));
|
|
368
373
|
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
@@ -843,4 +848,114 @@ describe('useAgentStream', () => {
|
|
|
843
848
|
|
|
844
849
|
expect(result.current.serverFileUpdates.has('file-1')).toBe(false);
|
|
845
850
|
});
|
|
851
|
+
|
|
852
|
+
it('hydrates the latest historical file snapshot once and ignores its live replay', async () => {
|
|
853
|
+
let deliver: ((message: AgentMessage) => void) | undefined;
|
|
854
|
+
const fileSnapshot = (timestamp: number, id: string): AgentMessage => ({
|
|
855
|
+
timestamp,
|
|
856
|
+
workflow_run_id: 'run-1',
|
|
857
|
+
type: AgentMessageType.SYSTEM,
|
|
858
|
+
message: '',
|
|
859
|
+
workstream_id: 'main',
|
|
860
|
+
details: {
|
|
861
|
+
system_type: 'file_processing',
|
|
862
|
+
batch_id: 'batch-1',
|
|
863
|
+
files: [
|
|
864
|
+
{
|
|
865
|
+
id,
|
|
866
|
+
name: `${id}.pdf`,
|
|
867
|
+
content_type: 'application/pdf',
|
|
868
|
+
size: 1,
|
|
869
|
+
status: FileProcessingStatus.READY,
|
|
870
|
+
artifact_path: `files/${id}.pdf`,
|
|
871
|
+
reference: `artifact:files/${id}.pdf`,
|
|
872
|
+
started_at: timestamp,
|
|
873
|
+
},
|
|
874
|
+
],
|
|
875
|
+
pending_count: 0,
|
|
876
|
+
ready_count: 1,
|
|
877
|
+
error_count: 0,
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
const olderHistorical = fileSnapshot(1_000, 'older-upload');
|
|
881
|
+
const latestHistorical = fileSnapshot(1_100, 'staged-upload');
|
|
882
|
+
const streamMessages = vi.fn<
|
|
883
|
+
(
|
|
884
|
+
id: string,
|
|
885
|
+
onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void,
|
|
886
|
+
since?: number,
|
|
887
|
+
signal?: AbortSignal,
|
|
888
|
+
options?: AgentRunStreamMessagesOptions,
|
|
889
|
+
) => Promise<unknown>
|
|
890
|
+
>(async (_id, onMessage, _since, _signal, options) => {
|
|
891
|
+
deliver = onMessage;
|
|
892
|
+
options?.onHistoryLoaded?.([olderHistorical, latestHistorical]);
|
|
893
|
+
// Completed runs replay archived file_processing snapshots through the
|
|
894
|
+
// live callback. Replaying the older snapshot must not replace the latest
|
|
895
|
+
// inventory selected from history.
|
|
896
|
+
onMessage?.(olderHistorical);
|
|
897
|
+
return null;
|
|
898
|
+
});
|
|
899
|
+
const client = createClient(streamMessages);
|
|
900
|
+
const { result } = renderHook(() => useAgentStream(client, 'agent-run-1'));
|
|
901
|
+
|
|
902
|
+
await waitFor(() => expect(deliver).toBeDefined());
|
|
903
|
+
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
904
|
+
expect(result.current.serverFileUpdates.has('staged-upload')).toBe(true);
|
|
905
|
+
|
|
906
|
+
act(() => deliver?.(fileSnapshot(1_200, 'new-upload')));
|
|
907
|
+
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
908
|
+
expect(result.current.serverFileUpdates.has('new-upload')).toBe(true);
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
it('selects the newest historical file snapshot even when history arrives out of order', async () => {
|
|
912
|
+
let deliver: ((message: AgentMessage) => void) | undefined;
|
|
913
|
+
const fileSnapshot = (timestamp: number, id: string): AgentMessage => ({
|
|
914
|
+
timestamp,
|
|
915
|
+
workflow_run_id: 'run-1',
|
|
916
|
+
type: AgentMessageType.SYSTEM,
|
|
917
|
+
message: '',
|
|
918
|
+
workstream_id: 'main',
|
|
919
|
+
details: {
|
|
920
|
+
system_type: 'file_processing',
|
|
921
|
+
batch_id: 'batch-1',
|
|
922
|
+
files: [
|
|
923
|
+
{
|
|
924
|
+
id,
|
|
925
|
+
name: `${id}.pdf`,
|
|
926
|
+
content_type: 'application/pdf',
|
|
927
|
+
size: 1,
|
|
928
|
+
status: FileProcessingStatus.READY,
|
|
929
|
+
artifact_path: `files/${id}.pdf`,
|
|
930
|
+
reference: `artifact:files/${id}.pdf`,
|
|
931
|
+
started_at: timestamp,
|
|
932
|
+
},
|
|
933
|
+
],
|
|
934
|
+
pending_count: 0,
|
|
935
|
+
ready_count: 1,
|
|
936
|
+
error_count: 0,
|
|
937
|
+
},
|
|
938
|
+
});
|
|
939
|
+
const streamMessages = vi.fn<
|
|
940
|
+
(
|
|
941
|
+
id: string,
|
|
942
|
+
onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void,
|
|
943
|
+
since?: number,
|
|
944
|
+
signal?: AbortSignal,
|
|
945
|
+
options?: AgentRunStreamMessagesOptions,
|
|
946
|
+
) => Promise<unknown>
|
|
947
|
+
>(async (_id, onMessage, _since, _signal, options) => {
|
|
948
|
+
deliver = onMessage;
|
|
949
|
+
// retrieveMessages returns GET /updates unsorted, so the newest snapshot is not
|
|
950
|
+
// necessarily last. Selection must be by timestamp, not arrival order.
|
|
951
|
+
options?.onHistoryLoaded?.([fileSnapshot(1_100, 'staged-upload'), fileSnapshot(1_000, 'older-upload')]);
|
|
952
|
+
return null;
|
|
953
|
+
});
|
|
954
|
+
const client = createClient(streamMessages);
|
|
955
|
+
const { result } = renderHook(() => useAgentStream(client, 'agent-run-1'));
|
|
956
|
+
|
|
957
|
+
await waitFor(() => expect(deliver).toBeDefined());
|
|
958
|
+
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
959
|
+
expect(result.current.serverFileUpdates.has('staged-upload')).toBe(true);
|
|
960
|
+
});
|
|
846
961
|
});
|
|
@@ -476,13 +476,39 @@ export function useAgentStream(
|
|
|
476
476
|
onHistoryLoaded: (historical) => {
|
|
477
477
|
if (abortController.signal.aborted) return;
|
|
478
478
|
const timelineMessages = historical.filter(shouldStoreTimelineMessage);
|
|
479
|
+
let latestFileSnapshot: FileProcessingDetails | undefined;
|
|
480
|
+
let latestFileSnapshotTs = Number.NEGATIVE_INFINITY;
|
|
479
481
|
// Advance the watermark synchronously before React processes the
|
|
480
482
|
// history state update. Some completed streams replay history via
|
|
481
483
|
// the live callback immediately after onHistoryLoaded returns.
|
|
484
|
+
// Must cover every historical message, not just the timeline subset:
|
|
485
|
+
// file_processing snapshots are excluded from timelineMessages, so
|
|
486
|
+
// scanning only that subset would leave the cursor behind them and
|
|
487
|
+
// the replay guard would never fire for a file-only history.
|
|
482
488
|
for (const message of historical) {
|
|
483
489
|
if (message.timestamp && message.timestamp > lastDeliveredTsRef.current) {
|
|
484
490
|
lastDeliveredTsRef.current = message.timestamp;
|
|
485
491
|
}
|
|
492
|
+
if (message.type === AgentMessageType.SYSTEM) {
|
|
493
|
+
const details = message.details as FileProcessingDetails | undefined;
|
|
494
|
+
if (details?.system_type === 'file_processing' && details.files) {
|
|
495
|
+
// GET /updates is returned unsorted (the client only ever
|
|
496
|
+
// takes a Math.max over timestamps), so select the newest
|
|
497
|
+
// snapshot by timestamp instead of trusting arrival order.
|
|
498
|
+
// >= keeps last-wins for ties and for untimestamped messages.
|
|
499
|
+
const timestamp = message.timestamp ?? Number.NEGATIVE_INFINITY;
|
|
500
|
+
if (!latestFileSnapshot || timestamp >= latestFileSnapshotTs) {
|
|
501
|
+
latestFileSnapshot = details;
|
|
502
|
+
latestFileSnapshotTs = timestamp;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
// Hydrate the latest archived inventory once so an unconsumed staged file
|
|
508
|
+
// remains visible after reload. Subsequent live-callback replay is ignored
|
|
509
|
+
// by isReplay above, while useFileProcessing filters consumed/delivered files.
|
|
510
|
+
if (latestFileSnapshot) {
|
|
511
|
+
setServerFileUpdates(new Map(latestFileSnapshot.files.map((file) => [file.id, file])));
|
|
486
512
|
}
|
|
487
513
|
debugAgentChat('history loaded', {
|
|
488
514
|
agentRunId,
|
|
@@ -149,6 +149,11 @@ describe('useFileProcessing', () => {
|
|
|
149
149
|
|
|
150
150
|
await act(async () => {
|
|
151
151
|
resolveUpload?.({ url: 'https://example.test/upload', path: 'files/wrong.png' });
|
|
152
|
+
// biome 2.5.6 regression: the nursery rule mis-types `await` on a `Promise | undefined`
|
|
153
|
+
// variable assigned inside a callback, and its own fix suggests `await await`. It only
|
|
154
|
+
// fires on Linux CI, not on macOS, so the suppression may read as unused locally.
|
|
155
|
+
// Drop it once the rule is fixed upstream.
|
|
156
|
+
// biome-ignore lint/nursery/noFloatingPromises: false positive — already awaited
|
|
152
157
|
await uploadPromise;
|
|
153
158
|
});
|
|
154
159
|
|
|
@@ -188,4 +193,86 @@ describe('useFileProcessing', () => {
|
|
|
188
193
|
vi.unstubAllGlobals();
|
|
189
194
|
}
|
|
190
195
|
});
|
|
196
|
+
|
|
197
|
+
it('excludes files already delivered via a sent message (survives reload)', () => {
|
|
198
|
+
const client = createClient();
|
|
199
|
+
const toast = vi.fn();
|
|
200
|
+
const delivered: ConversationFile = {
|
|
201
|
+
...createReadyFile('file-1'),
|
|
202
|
+
artifact_path: 'files/wrong.png',
|
|
203
|
+
reference: 'artifact:files/wrong.png',
|
|
204
|
+
};
|
|
205
|
+
const serverFileUpdates = new Map([['file-1', delivered]]);
|
|
206
|
+
|
|
207
|
+
// No delivered refs → the file shows as a pending composer attachment.
|
|
208
|
+
const { result, rerender } = renderHook(
|
|
209
|
+
({ refs }: { refs: Set<string> | undefined }) =>
|
|
210
|
+
useFileProcessing(client, 'agent-run-1', serverFileUpdates, toast, refs),
|
|
211
|
+
{ initialProps: { refs: undefined as Set<string> | undefined } },
|
|
212
|
+
);
|
|
213
|
+
expect(result.current.processingFiles.has('file-1')).toBe(true);
|
|
214
|
+
|
|
215
|
+
// Once its artifact reference appears in message history, it is excluded — matching by
|
|
216
|
+
// either the full `artifact:` reference or the bare path.
|
|
217
|
+
rerender({ refs: new Set(['artifact:files/wrong.png']) });
|
|
218
|
+
expect(result.current.processingFiles.has('file-1')).toBe(false);
|
|
219
|
+
|
|
220
|
+
rerender({ refs: new Set(['files/wrong.png']) });
|
|
221
|
+
expect(result.current.processingFiles.has('file-1')).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('excludes files marked consumed by the workflow without relying on message history', () => {
|
|
225
|
+
const client = createClient();
|
|
226
|
+
const toast = vi.fn();
|
|
227
|
+
const consumed: ConversationFile = {
|
|
228
|
+
...createReadyFile('file-1'),
|
|
229
|
+
artifact_path: 'files/wrong.png',
|
|
230
|
+
reference: 'artifact:files/wrong.png',
|
|
231
|
+
consumed_at: 1_100,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const { result } = renderHook(() =>
|
|
235
|
+
useFileProcessing(client, 'agent-run-1', new Map([['file-1', consumed]]), toast),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(result.current.processingFiles.has('file-1')).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('drops a staged local entry when the server marks that file consumed', async () => {
|
|
242
|
+
const client = createClient();
|
|
243
|
+
const toast = vi.fn();
|
|
244
|
+
const { result, rerender } = renderHook(
|
|
245
|
+
({ updates }) => useFileProcessing(client, 'agent-run-1', updates, toast),
|
|
246
|
+
{ initialProps: { updates: new Map<string, ConversationFile>() } },
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const file = new File(['pdf'], 'report.pdf', { type: 'application/pdf' });
|
|
250
|
+
await act(async () => {
|
|
251
|
+
await result.current.handleFileUpload([file]);
|
|
252
|
+
});
|
|
253
|
+
const [fileId] = Array.from(result.current.processingFiles.entries())[0];
|
|
254
|
+
expect(result.current.processingFiles.has(fileId)).toBe(true);
|
|
255
|
+
|
|
256
|
+
// The turn was consumed elsewhere (another tab, or an agent-initiated turn), so
|
|
257
|
+
// clearProcessingFiles() never ran here and the optimistic entry is still staged.
|
|
258
|
+
// Merging local first means skipping the server update is not enough to hide it.
|
|
259
|
+
rerender({
|
|
260
|
+
updates: new Map<string, ConversationFile>([
|
|
261
|
+
[
|
|
262
|
+
fileId,
|
|
263
|
+
{
|
|
264
|
+
id: fileId,
|
|
265
|
+
name: 'report.pdf',
|
|
266
|
+
content_type: 'application/pdf',
|
|
267
|
+
size: 0,
|
|
268
|
+
status: FileProcessingStatus.READY,
|
|
269
|
+
started_at: 1_000,
|
|
270
|
+
consumed_at: 1_100,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
]),
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(result.current.processingFiles.has(fileId)).toBe(false);
|
|
277
|
+
});
|
|
191
278
|
});
|
|
@@ -51,6 +51,13 @@ export function useFileProcessing(
|
|
|
51
51
|
agentRunId: string,
|
|
52
52
|
serverFileUpdates: Map<string, ConversationFile>,
|
|
53
53
|
toast: ToastFn,
|
|
54
|
+
/**
|
|
55
|
+
* Artifact references (`artifact:...` and bare paths) that already appear in sent user
|
|
56
|
+
* messages. Files matching these have been delivered to the agent, so they must not resurface
|
|
57
|
+
* as pending composer attachments after a reload — the durable, server-agnostic counterpart to
|
|
58
|
+
* the in-session `removedFileIds` suppression.
|
|
59
|
+
*/
|
|
60
|
+
deliveredArtifactRefs?: Set<string>,
|
|
54
61
|
): UseFileProcessingResult {
|
|
55
62
|
const t = i18nInstance.getFixedT(null, NAMESPACE);
|
|
56
63
|
// Local optimistic file state (uploads initiated from the UI)
|
|
@@ -103,6 +110,17 @@ export function useFileProcessing(
|
|
|
103
110
|
}
|
|
104
111
|
});
|
|
105
112
|
serverFileUpdates.forEach((file, id) => {
|
|
113
|
+
// consumed_at is the workflow's authoritative delivered marker. Filter it here in
|
|
114
|
+
// addition to message-history refs: older runs may lack the marker, while some
|
|
115
|
+
// server-originated messages may not preserve the rendered attachment block.
|
|
116
|
+
if (file.consumed_at) {
|
|
117
|
+
// Drop any optimistic local entry as well. clearProcessingFiles() normally
|
|
118
|
+
// retires it when this client sends the message, but a turn consumed
|
|
119
|
+
// elsewhere (another tab, or an agent-initiated turn) leaves this session's
|
|
120
|
+
// chip staged. Skipping the server update alone would keep it visible.
|
|
121
|
+
merged.delete(id);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
106
124
|
if (!removedFileIds.has(id)) {
|
|
107
125
|
// Server updates are authoritative for status, but may omit fields the local
|
|
108
126
|
// optimistic entry already knows: preview_url is never sent by the server, and
|
|
@@ -117,8 +135,21 @@ export function useFileProcessing(
|
|
|
117
135
|
} as LocalConversationFile);
|
|
118
136
|
}
|
|
119
137
|
});
|
|
138
|
+
// Drop files already delivered via a sent message. Unlike removedFileIds (session-only),
|
|
139
|
+
// this is reconstructed from message history on every mount. It also covers runs created
|
|
140
|
+
// before the workflow began stamping consumed_at.
|
|
141
|
+
if (deliveredArtifactRefs && deliveredArtifactRefs.size > 0) {
|
|
142
|
+
for (const [id, file] of merged) {
|
|
143
|
+
if (
|
|
144
|
+
(file.reference && deliveredArtifactRefs.has(file.reference)) ||
|
|
145
|
+
(file.artifact_path && deliveredArtifactRefs.has(file.artifact_path))
|
|
146
|
+
) {
|
|
147
|
+
merged.delete(id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
120
151
|
return merged;
|
|
121
|
-
}, [localFiles, removedFileIds, serverFileUpdates]);
|
|
152
|
+
}, [localFiles, removedFileIds, serverFileUpdates, deliveredArtifactRefs]);
|
|
122
153
|
|
|
123
154
|
const hasProcessingFiles = useMemo(
|
|
124
155
|
() =>
|
package/src/i18n/locales/ar.json
CHANGED
|
@@ -252,6 +252,31 @@
|
|
|
252
252
|
"auth.providers.bodyConsumer": "اختر أحد المزوّدين أدناه للمتابعة.",
|
|
253
253
|
"auth.providers.eyebrow": "اختر كيفية تسجيل الدخول",
|
|
254
254
|
"auth.providers.title": "خطوة أخرى",
|
|
255
|
+
"auth.recovery.continue": "المتابعة إلى Vertesia",
|
|
256
|
+
"auth.recovery.credential.body": "تعذّر التحقق من تسجيل دخولك. سجّل الدخول مرة أخرى للمتابعة.",
|
|
257
|
+
"auth.recovery.credential.eyebrow": "تسجيل الدخول مطلوب",
|
|
258
|
+
"auth.recovery.credential.title": "تعذّر التحقق من تسجيل دخولك",
|
|
259
|
+
"auth.recovery.errorCode": "رمز الخطأ",
|
|
260
|
+
"auth.recovery.errorMessage": "رسالة الخطأ",
|
|
261
|
+
"auth.recovery.httpStatus": "حالة HTTP",
|
|
262
|
+
"auth.recovery.noAccessibleAccount.body": "لقد سجّلت الدخول، ولكن لا يتوفر حساب Vertesia لهذه الهوية.",
|
|
263
|
+
"auth.recovery.noAccessibleAccount.eyebrow": "الحساب مطلوب",
|
|
264
|
+
"auth.recovery.noAccessibleAccount.title": "لا يتوفر حساب",
|
|
265
|
+
"auth.recovery.requestedAccount": "الحساب المطلوب",
|
|
266
|
+
"auth.recovery.requestedProject": "المشروع المطلوب",
|
|
267
|
+
"auth.recovery.scopeAccount.body": "لقد سجّلت الدخول، ولكن الحساب الموجود في هذا الرابط غير متاح. قد يكون الرابط قديمًا أو قد لا يكون لديك حق الوصول.",
|
|
268
|
+
"auth.recovery.scopeAccount.eyebrow": "الرابط غير متاح",
|
|
269
|
+
"auth.recovery.scopeAccount.title": "الحساب غير متاح",
|
|
270
|
+
"auth.recovery.scopeProject.body": "لقد سجّلت الدخول، ولكن المشروع الموجود في هذا الرابط غير متاح. قد يكون الرابط قديمًا أو قد لا يكون لديك حق الوصول.",
|
|
271
|
+
"auth.recovery.scopeProject.eyebrow": "الرابط غير متاح",
|
|
272
|
+
"auth.recovery.scopeProject.title": "المشروع غير متاح",
|
|
273
|
+
"auth.recovery.service.body": "خدمة المصادقة غير متاحة مؤقتًا. حاول مرة أخرى أو استخدم حسابًا مختلفًا.",
|
|
274
|
+
"auth.recovery.service.eyebrow": "الخدمة غير متاحة",
|
|
275
|
+
"auth.recovery.service.title": "تعذّر إكمال تسجيل الدخول",
|
|
276
|
+
"auth.recovery.supportPrefix": "إذا كنت بحاجة إلى مساعدة، فتواصل مع",
|
|
277
|
+
"auth.recovery.technicalDetails": "التفاصيل التقنية",
|
|
278
|
+
"auth.recovery.tryAgain": "المحاولة مرة أخرى",
|
|
279
|
+
"auth.recovery.useDifferentAccount": "استخدام حساب مختلف",
|
|
255
280
|
"auth.restricted.autoRedirect": "إعادة التوجيه إلى الموقع الرئيسي خلال {{seconds}} ثانية…",
|
|
256
281
|
"auth.restricted.body": "هذا الموقع مقتصر على المستخدمين الذين لديهم وصول مبكر إلى الميزات والإصلاحات الجديدة. يُرجى المتابعة إلى الموقع الرئيسي، أو التواصل مع Vertesia بشأن الوصول المبكر إلى هذا الموقع.",
|
|
257
282
|
"auth.restricted.calloutMeta": "تواصل مع Vertesia للحصول على وصول مبكر",
|
package/src/i18n/locales/de.json
CHANGED
|
@@ -244,6 +244,31 @@
|
|
|
244
244
|
"auth.providers.bodyConsumer": "Wähle einen der folgenden Anbieter aus, um fortzufahren.",
|
|
245
245
|
"auth.providers.eyebrow": "Anmeldeart wählen",
|
|
246
246
|
"auth.providers.title": "Noch ein Schritt",
|
|
247
|
+
"auth.recovery.continue": "Weiter zu Vertesia",
|
|
248
|
+
"auth.recovery.credential.body": "Ihre Anmeldung konnte nicht überprüft werden. Melden Sie sich erneut an, um fortzufahren.",
|
|
249
|
+
"auth.recovery.credential.eyebrow": "Anmeldung erforderlich",
|
|
250
|
+
"auth.recovery.credential.title": "Ihre Anmeldung konnte nicht überprüft werden",
|
|
251
|
+
"auth.recovery.errorCode": "Fehlercode",
|
|
252
|
+
"auth.recovery.errorMessage": "Fehlermeldung",
|
|
253
|
+
"auth.recovery.httpStatus": "HTTP-Status",
|
|
254
|
+
"auth.recovery.noAccessibleAccount.body": "Sie sind angemeldet, aber für diese Identität ist kein Vertesia-Konto verfügbar.",
|
|
255
|
+
"auth.recovery.noAccessibleAccount.eyebrow": "Konto erforderlich",
|
|
256
|
+
"auth.recovery.noAccessibleAccount.title": "Kein Konto verfügbar",
|
|
257
|
+
"auth.recovery.requestedAccount": "Angefordertes Konto",
|
|
258
|
+
"auth.recovery.requestedProject": "Angefordertes Projekt",
|
|
259
|
+
"auth.recovery.scopeAccount.body": "Sie sind angemeldet, aber das Konto in diesem Link ist nicht verfügbar. Der Link ist möglicherweise veraltet oder Sie haben keinen Zugriff.",
|
|
260
|
+
"auth.recovery.scopeAccount.eyebrow": "Link nicht verfügbar",
|
|
261
|
+
"auth.recovery.scopeAccount.title": "Konto nicht verfügbar",
|
|
262
|
+
"auth.recovery.scopeProject.body": "Sie sind angemeldet, aber das Projekt in diesem Link ist nicht verfügbar. Der Link ist möglicherweise veraltet oder Sie haben keinen Zugriff.",
|
|
263
|
+
"auth.recovery.scopeProject.eyebrow": "Link nicht verfügbar",
|
|
264
|
+
"auth.recovery.scopeProject.title": "Projekt nicht verfügbar",
|
|
265
|
+
"auth.recovery.service.body": "Der Authentifizierungsdienst ist vorübergehend nicht verfügbar. Versuchen Sie es erneut oder verwenden Sie ein anderes Konto.",
|
|
266
|
+
"auth.recovery.service.eyebrow": "Dienst nicht verfügbar",
|
|
267
|
+
"auth.recovery.service.title": "Die Anmeldung konnte nicht abgeschlossen werden",
|
|
268
|
+
"auth.recovery.supportPrefix": "Wenn Sie Hilfe benötigen, kontaktieren Sie",
|
|
269
|
+
"auth.recovery.technicalDetails": "Technische Details",
|
|
270
|
+
"auth.recovery.tryAgain": "Erneut versuchen",
|
|
271
|
+
"auth.recovery.useDifferentAccount": "Anderes Konto verwenden",
|
|
247
272
|
"auth.restricted.autoRedirect": "Weiterleitung zur Hauptwebsite in {{seconds}} s…",
|
|
248
273
|
"auth.restricted.body": "Diese Website ist auf Nutzer mit Early Access zu neuen Funktionen und Fixes beschränkt. Bitte fahre zur Hauptwebsite fort oder kontaktiere Vertesia bezüglich Early Access zu dieser Website.",
|
|
249
274
|
"auth.restricted.calloutMeta": "Kontaktiere Vertesia für Early Access",
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -319,6 +319,31 @@
|
|
|
319
319
|
"auth.providers.bodyConsumer": "Pick from one of the providers below to continue.",
|
|
320
320
|
"auth.providers.eyebrow": "Choose How to Sign In",
|
|
321
321
|
"auth.providers.title": "One more step",
|
|
322
|
+
"auth.recovery.continue": "Continue to Vertesia",
|
|
323
|
+
"auth.recovery.credential.body": "We couldn't verify your sign-in. Sign in again to continue.",
|
|
324
|
+
"auth.recovery.credential.eyebrow": "Sign-In Required",
|
|
325
|
+
"auth.recovery.credential.title": "Your sign-in could not be verified",
|
|
326
|
+
"auth.recovery.errorCode": "Error code",
|
|
327
|
+
"auth.recovery.errorMessage": "Error message",
|
|
328
|
+
"auth.recovery.httpStatus": "HTTP status",
|
|
329
|
+
"auth.recovery.noAccessibleAccount.body": "You're signed in, but no Vertesia account is available for this identity.",
|
|
330
|
+
"auth.recovery.noAccessibleAccount.eyebrow": "Account Required",
|
|
331
|
+
"auth.recovery.noAccessibleAccount.title": "No account is available",
|
|
332
|
+
"auth.recovery.requestedAccount": "Requested account",
|
|
333
|
+
"auth.recovery.requestedProject": "Requested project",
|
|
334
|
+
"auth.recovery.scopeAccount.body": "You’re signed in, but the account in this link isn’t available. The link may be outdated, or you may not have access.",
|
|
335
|
+
"auth.recovery.scopeAccount.eyebrow": "Link Unavailable",
|
|
336
|
+
"auth.recovery.scopeAccount.title": "Account not available",
|
|
337
|
+
"auth.recovery.scopeProject.body": "You’re signed in, but the project in this link isn’t available. The link may be outdated, or you may not have access.",
|
|
338
|
+
"auth.recovery.scopeProject.eyebrow": "Link Unavailable",
|
|
339
|
+
"auth.recovery.scopeProject.title": "Project not available",
|
|
340
|
+
"auth.recovery.service.body": "The authentication service is temporarily unavailable. Try again, or use a different account.",
|
|
341
|
+
"auth.recovery.service.eyebrow": "Service Unavailable",
|
|
342
|
+
"auth.recovery.service.title": "We couldn't complete sign-in",
|
|
343
|
+
"auth.recovery.supportPrefix": "If you need help, contact",
|
|
344
|
+
"auth.recovery.technicalDetails": "Technical details",
|
|
345
|
+
"auth.recovery.tryAgain": "Try again",
|
|
346
|
+
"auth.recovery.useDifferentAccount": "Use a different account",
|
|
322
347
|
"auth.restricted.autoRedirect": "Redirecting to the main site in {{seconds}}s…",
|
|
323
348
|
"auth.restricted.body": "This site is restricted to users with early access to new features and fixes. Please continue to the main site, or contact Vertesia about early access to this site.",
|
|
324
349
|
"auth.restricted.calloutMeta": "Contact Vertesia for early access",
|