@vertesia/ui 1.5.0-dev.20260722.120446Z → 1.5.0-dev.20260725.083715Z
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/filters/filterBar.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/filterBar.js +1 -1
- package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
- package/lib/core/components/shadcn/modal/dialog.d.ts +1 -1
- package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
- package/lib/core/components/shadcn/modal/dialog.js +2 -0
- package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
- package/lib/core/components/shadcn/tabs.d.ts +12 -1
- package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
- package/lib/core/components/shadcn/tabs.js +12 -4
- package/lib/core/components/shadcn/tabs.js.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.d.ts +5 -1
- package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.js +2 -2
- package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.d.ts +5 -1
- package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.js +48 -11
- package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
- package/lib/features/agent/chat/DocumentEditingActionCard.d.ts +6 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.d.ts.map +1 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.js +129 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentConversation.d.ts +19 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +125 -27
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts +45 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js +40 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +94 -49
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +9 -5
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +25 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +33 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +22 -24
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts +14 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js +21 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts +30 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +53 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +21 -2
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts +6 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js +29 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts +33 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js +129 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +7 -0
- package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/utils.js +11 -1
- package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.js +24 -9
- package/lib/features/agent/chat/hooks/useAgentStream.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 +3 -0
- package/lib/features/agent/chat/index.js.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.js +2 -2
- package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.d.ts +2 -1
- package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +86 -15
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts +16 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js +116 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts +7 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.js +13 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.js.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.d.ts +48 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.js +720 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.js.map +1 -0
- package/lib/features/store/objects/components/TextEditorPanel.d.ts.map +1 -1
- package/lib/features/store/objects/components/TextEditorPanel.js +22 -9
- package/lib/features/store/objects/components/TextEditorPanel.js.map +1 -1
- package/lib/features/store/objects/components/documentArtifactRefs.d.ts +26 -0
- package/lib/features/store/objects/components/documentArtifactRefs.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentArtifactRefs.js +52 -0
- package/lib/features/store/objects/components/documentArtifactRefs.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts +4 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.js +40 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingPrompts.d.ts +3 -0
- package/lib/features/store/objects/components/documentEditingPrompts.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingPrompts.js +13 -0
- package/lib/features/store/objects/components/documentEditingPrompts.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingRun.d.ts +27 -0
- package/lib/features/store/objects/components/documentEditingRun.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingRun.js +126 -0
- package/lib/features/store/objects/components/documentEditingRun.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingTarget.d.ts +20 -0
- package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingTarget.js +40 -0
- package/lib/features/store/objects/components/documentEditingTarget.js.map +1 -0
- package/lib/features/store/objects/components/index.d.ts +1 -0
- package/lib/features/store/objects/components/index.d.ts.map +1 -1
- package/lib/features/store/objects/components/index.js +1 -0
- package/lib/features/store/objects/components/index.js.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts +5 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.js +53 -29
- package/lib/features/store/objects/components/useContentPanelHooks.js.map +1 -1
- package/lib/features/store/objects/components/useDownloadFile.d.ts +2 -1
- package/lib/features/store/objects/components/useDownloadFile.d.ts.map +1 -1
- package/lib/features/store/objects/components/useDownloadFile.js +3 -0
- package/lib/features/store/objects/components/useDownloadFile.js.map +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -1
- package/lib/i18n/locales/ar.json +118 -1
- package/lib/i18n/locales/de.json +118 -1
- package/lib/i18n/locales/en.json +118 -0
- package/lib/i18n/locales/es.json +118 -1
- package/lib/i18n/locales/fr.json +118 -1
- package/lib/i18n/locales/it.json +118 -1
- package/lib/i18n/locales/ja.json +118 -1
- package/lib/i18n/locales/ko.json +118 -1
- package/lib/i18n/locales/pt.json +118 -1
- package/lib/i18n/locales/ru.json +118 -1
- package/lib/i18n/locales/tr.json +118 -1
- package/lib/i18n/locales/zh-TW.json +118 -1
- package/lib/i18n/locales/zh.json +118 -1
- package/lib/rich-text/EditorToolbar.d.ts +37 -0
- package/lib/rich-text/EditorToolbar.d.ts.map +1 -0
- package/lib/rich-text/EditorToolbar.js +215 -0
- package/lib/rich-text/EditorToolbar.js.map +1 -0
- package/lib/rich-text/VertesiaRichTextEditor.d.ts +26 -0
- package/lib/rich-text/VertesiaRichTextEditor.d.ts.map +1 -0
- package/lib/rich-text/VertesiaRichTextEditor.js +67 -0
- package/lib/rich-text/VertesiaRichTextEditor.js.map +1 -0
- package/lib/rich-text/index.d.ts +2 -0
- package/lib/rich-text/index.d.ts.map +1 -0
- package/lib/rich-text/index.js +2 -0
- package/lib/rich-text/index.js.map +1 -0
- package/lib/vertesia-ui-core.js +2 -2
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +35 -30
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +14 -14
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-rich-text.js +5 -0
- package/lib/vertesia-ui-rich-text.js.map +1 -0
- package/lib/vertesia-ui-widgets.js +25 -7
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/markdown/AgentResourceResolver.d.ts +31 -0
- package/lib/widgets/markdown/AgentResourceResolver.d.ts.map +1 -0
- package/lib/widgets/markdown/AgentResourceResolver.js +39 -0
- package/lib/widgets/markdown/AgentResourceResolver.js.map +1 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.d.ts +63 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.d.ts.map +1 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.js +582 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.js.map +1 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts +69 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts.map +1 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +374 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js.map +1 -0
- package/lib/widgets/markdown/MarkdownLink.d.ts +3 -1
- package/lib/widgets/markdown/MarkdownLink.d.ts.map +1 -1
- package/lib/widgets/markdown/MarkdownLink.js +41 -14
- package/lib/widgets/markdown/MarkdownLink.js.map +1 -1
- package/lib/widgets/markdown/MarkdownRenderer.d.ts +8 -1
- package/lib/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
- package/lib/widgets/markdown/MarkdownRenderer.js +20 -4
- package/lib/widgets/markdown/MarkdownRenderer.js.map +1 -1
- package/lib/widgets/markdown/index.d.ts +5 -1
- package/lib/widgets/markdown/index.d.ts.map +1 -1
- package/lib/widgets/markdown/index.js +5 -1
- package/lib/widgets/markdown/index.js.map +1 -1
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.d.ts.map +1 -1
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.js +14 -2
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.js.map +1 -1
- package/lib/widgets/markdown/textDiff.d.ts +52 -0
- package/lib/widgets/markdown/textDiff.d.ts.map +1 -0
- package/lib/widgets/markdown/textDiff.js +362 -0
- package/lib/widgets/markdown/textDiff.js.map +1 -0
- package/lib/widgets/markdown/useArtifactContent.d.ts +5 -3
- package/lib/widgets/markdown/useArtifactContent.d.ts.map +1 -1
- package/lib/widgets/markdown/useArtifactContent.js +6 -3
- package/lib/widgets/markdown/useArtifactContent.js.map +1 -1
- package/lib/widgets/markdown/useResolvedUrl.d.ts +1 -5
- package/lib/widgets/markdown/useResolvedUrl.d.ts.map +1 -1
- package/lib/widgets/markdown/useResolvedUrl.js +19 -31
- package/lib/widgets/markdown/useResolvedUrl.js.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
- package/package.json +13 -9
- package/src/__tests__/a11y.test.tsx +4 -1
- package/src/__tests__/axe-helper.ts +17 -7
- package/src/__tests__/setup.ts +25 -0
- package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
- package/src/core/components/shadcn/modal/dialog.tsx +3 -1
- package/src/core/components/shadcn/tabs.test.tsx +65 -0
- package/src/core/components/shadcn/tabs.tsx +25 -3
- package/src/features/agent/chat/AgentRightPanel.tsx +16 -1
- package/src/features/agent/chat/ArtifactsTab.test.tsx +55 -0
- package/src/features/agent/chat/ArtifactsTab.tsx +203 -52
- package/src/features/agent/chat/DocumentEditingActionCard.test.tsx +105 -0
- package/src/features/agent/chat/DocumentEditingActionCard.tsx +210 -0
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +237 -40
- package/src/features/agent/chat/ModernAgentConversation.tsx +247 -75
- package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.test.tsx +79 -0
- package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.tsx +94 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +257 -34
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +217 -105
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +17 -0
- package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx +44 -0
- package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +88 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -46
- package/src/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.tsx +40 -0
- package/src/features/agent/chat/ModernAgentOutput/ResourceChip.test.tsx +148 -0
- package/src/features/agent/chat/ModernAgentOutput/ResourceChip.tsx +141 -0
- package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +33 -1
- package/src/features/agent/chat/ModernAgentOutput/resourceLinks.tsx +43 -0
- package/src/features/agent/chat/ModernAgentOutput/resourceSummary.test.ts +229 -0
- package/src/features/agent/chat/ModernAgentOutput/resourceSummary.ts +152 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +17 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.ts +14 -1
- package/src/features/agent/chat/hooks/useAgentStream.test.tsx +188 -0
- package/src/features/agent/chat/hooks/useAgentStream.ts +28 -8
- package/src/features/agent/chat/index.ts +11 -0
- package/src/features/store/objects/DocumentSearchResults.tsx +2 -2
- package/src/features/store/objects/components/ContentOverview.tsx +216 -65
- package/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts +34 -0
- package/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx +211 -0
- package/src/features/store/objects/components/DocumentEditingLockStatus.test.tsx +32 -0
- package/src/features/store/objects/components/DocumentEditingLockStatus.tsx +45 -0
- package/src/features/store/objects/components/DocumentEditingPanel.test.tsx +336 -0
- package/src/features/store/objects/components/DocumentEditingPanel.tsx +1236 -0
- package/src/features/store/objects/components/TextEditorPanel.test.tsx +48 -0
- package/src/features/store/objects/components/TextEditorPanel.tsx +41 -20
- package/src/features/store/objects/components/documentArtifactRefs.test.ts +65 -0
- package/src/features/store/objects/components/documentArtifactRefs.ts +72 -0
- package/src/features/store/objects/components/documentEditingAgentConfig.test.ts +28 -0
- package/src/features/store/objects/components/documentEditingAgentConfig.ts +41 -0
- package/src/features/store/objects/components/documentEditingPrompts.test.ts +26 -0
- package/src/features/store/objects/components/documentEditingPrompts.ts +12 -0
- package/src/features/store/objects/components/documentEditingRun.test.ts +176 -0
- package/src/features/store/objects/components/documentEditingRun.ts +184 -0
- package/src/features/store/objects/components/documentEditingTarget.test.ts +96 -0
- package/src/features/store/objects/components/documentEditingTarget.ts +63 -0
- package/src/features/store/objects/components/index.ts +1 -0
- package/src/features/store/objects/components/useContentPanelHooks.ts +52 -27
- package/src/features/store/objects/components/useDownloadFile.test.ts +20 -0
- package/src/features/store/objects/components/useDownloadFile.ts +5 -1
- package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +2 -2
- package/src/i18n/locales/ar.json +118 -1
- package/src/i18n/locales/de.json +118 -1
- package/src/i18n/locales/en.json +118 -0
- package/src/i18n/locales/es.json +118 -1
- package/src/i18n/locales/fr.json +118 -1
- package/src/i18n/locales/it.json +118 -1
- package/src/i18n/locales/ja.json +118 -1
- package/src/i18n/locales/ko.json +118 -1
- package/src/i18n/locales/pt.json +118 -1
- package/src/i18n/locales/ru.json +118 -1
- package/src/i18n/locales/tr.json +118 -1
- package/src/i18n/locales/zh-TW.json +118 -1
- package/src/i18n/locales/zh.json +118 -1
- package/src/rich-text/EditorToolbar.tsx +708 -0
- package/src/rich-text/VertesiaRichTextEditor.test.tsx +211 -0
- package/src/rich-text/VertesiaRichTextEditor.tsx +241 -0
- package/src/rich-text/index.ts +9 -0
- package/src/session/UserSession.test.ts +11 -11
- package/src/widgets/markdown/AgentResourceResolver.test.ts +24 -0
- package/src/widgets/markdown/AgentResourceResolver.tsx +70 -0
- package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +537 -0
- package/src/widgets/markdown/ArtifactEditingSurface.tsx +878 -0
- package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +484 -0
- package/src/widgets/markdown/CollaborativeMarkdownRenderer.tsx +825 -0
- package/src/widgets/markdown/MarkdownLink.test.tsx +122 -0
- package/src/widgets/markdown/MarkdownLink.tsx +102 -17
- package/src/widgets/markdown/MarkdownRenderer.tsx +30 -3
- package/src/widgets/markdown/index.ts +39 -1
- package/src/widgets/markdown/normalizeCustomSchemeLinks.ts +15 -2
- package/src/widgets/markdown/textDiff.test.ts +156 -0
- package/src/widgets/markdown/textDiff.ts +399 -0
- package/src/widgets/markdown/useArtifactContent.ts +9 -3
- package/src/widgets/markdown/useResolvedUrl.test.ts +29 -40
- package/src/widgets/markdown/useResolvedUrl.ts +33 -35
- package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
- package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type AgentMessage,
|
|
3
|
+
AgentMessageType,
|
|
4
|
+
type AgentResourceReference,
|
|
5
|
+
getResourcesFromMessage,
|
|
6
|
+
} from '@vertesia/common';
|
|
2
7
|
import { Button, cn, onActivateKey, useToast } from '@vertesia/ui/core';
|
|
3
8
|
import { useUITranslation } from '@vertesia/ui/i18n';
|
|
4
9
|
import { useUserSession } from '@vertesia/ui/session';
|
|
@@ -9,6 +14,7 @@ import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
9
14
|
import { PulsatingCircle } from '../AnimatedThinkingDots';
|
|
10
15
|
import { useImageLightbox } from '../ImageLightbox';
|
|
11
16
|
import { getArtifactCacheKey, useArtifactUrlCache } from '../useArtifactUrlCache.js';
|
|
17
|
+
import { ResourceChipList } from './ResourceChip';
|
|
12
18
|
import type { ToolExecutionStatus } from './utils';
|
|
13
19
|
|
|
14
20
|
/** Keys that are internal metadata and not interesting to display */
|
|
@@ -27,8 +33,10 @@ const META_KEYS = new Set([
|
|
|
27
33
|
'files',
|
|
28
34
|
'outputFiles',
|
|
29
35
|
'display_role',
|
|
36
|
+
'duration_ms',
|
|
30
37
|
'observation',
|
|
31
38
|
'progress_messages',
|
|
39
|
+
'resources',
|
|
32
40
|
'browseruse',
|
|
33
41
|
'browser_use',
|
|
34
42
|
]);
|
|
@@ -797,6 +805,23 @@ function ToolCallGroupComponent({
|
|
|
797
805
|
// Extract workflow_run_id from messages (any message in the group should have it)
|
|
798
806
|
const artifactRunId = messages.find((m) => m.workflow_run_id)?.workflow_run_id ?? messages[0]?.workflow_run_id;
|
|
799
807
|
|
|
808
|
+
// Aggregate resource references across the whole group so deep links stay visible even when the
|
|
809
|
+
// group is collapsed (mirrors how group images/files are surfaced at the group level).
|
|
810
|
+
const groupResources = useMemo(() => {
|
|
811
|
+
const seen = new Set<string>();
|
|
812
|
+
const out: AgentResourceReference[] = [];
|
|
813
|
+
for (const m of messages) {
|
|
814
|
+
for (const ref of getResourcesFromMessage(m)) {
|
|
815
|
+
const key = `${ref.type}:${ref.id}:${ref.action}`;
|
|
816
|
+
if (!seen.has(key)) {
|
|
817
|
+
seen.add(key);
|
|
818
|
+
out.push(ref);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return out;
|
|
823
|
+
}, [messages]);
|
|
824
|
+
|
|
800
825
|
// Render status indicator based on tool execution status
|
|
801
826
|
const renderStatusIndicator = () => {
|
|
802
827
|
// Only pulse for the currently active/latest group
|
|
@@ -966,6 +991,13 @@ function ToolCallGroupComponent({
|
|
|
966
991
|
{/* Show all images from the group prominently at the top */}
|
|
967
992
|
<GroupImageDisplay messages={messages} artifactRunId={artifactRunId} />
|
|
968
993
|
|
|
994
|
+
{/* Deep links to resources created/updated/deleted in this group, independent of expansion state. */}
|
|
995
|
+
{groupResources.length > 0 && (
|
|
996
|
+
<div className="px-4 pt-1 pb-2">
|
|
997
|
+
<ResourceChipList resources={groupResources} workflowRunId={artifactRunId} />
|
|
998
|
+
</div>
|
|
999
|
+
)}
|
|
1000
|
+
|
|
969
1001
|
{/* Collapsed summary - show tool calls as single-line rows with expand option */}
|
|
970
1002
|
{isCollapsed && (
|
|
971
1003
|
<div className="px-3 py-0.5 space-y-0">
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentResourceAction, AgentResourceType } from '@vertesia/common';
|
|
2
|
+
import type { LucideIcon } from 'lucide-react';
|
|
3
|
+
import {
|
|
4
|
+
Boxes,
|
|
5
|
+
BrainCircuit,
|
|
6
|
+
Database,
|
|
7
|
+
FileType,
|
|
8
|
+
MessagesSquare,
|
|
9
|
+
PanelsTopLeft,
|
|
10
|
+
PlayCircle,
|
|
11
|
+
SquareTerminal,
|
|
12
|
+
Workflow,
|
|
13
|
+
} from 'lucide-react';
|
|
14
|
+
|
|
15
|
+
const RESOURCE_ICONS: Record<AgentResourceType, LucideIcon> = {
|
|
16
|
+
document: Database,
|
|
17
|
+
collection: Boxes,
|
|
18
|
+
content_type: FileType,
|
|
19
|
+
interaction: MessagesSquare,
|
|
20
|
+
prompt: SquareTerminal,
|
|
21
|
+
agent: BrainCircuit,
|
|
22
|
+
workflow: Workflow,
|
|
23
|
+
process: Workflow,
|
|
24
|
+
process_run: PlayCircle,
|
|
25
|
+
interaction_run: PlayCircle,
|
|
26
|
+
view: PanelsTopLeft,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function getResourceIcon(type: AgentResourceType): LucideIcon {
|
|
30
|
+
return RESOURCE_ICONS[type];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Badge variant for a resource action, using the semantic color system. */
|
|
34
|
+
export function getResourceActionVariant(action: AgentResourceAction): 'success' | 'info' | 'outline' {
|
|
35
|
+
switch (action) {
|
|
36
|
+
case 'created':
|
|
37
|
+
return 'success';
|
|
38
|
+
case 'deleted':
|
|
39
|
+
return 'outline';
|
|
40
|
+
default:
|
|
41
|
+
return 'info';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { type AgentMessage, AgentMessageType, type AgentResourceReference } from '@vertesia/common';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
aggregateLatestTurnResources,
|
|
5
|
+
interleaveTurnSummaries,
|
|
6
|
+
isResourceSummaryReady,
|
|
7
|
+
segmentTurnResources,
|
|
8
|
+
} from './resourceSummary';
|
|
9
|
+
|
|
10
|
+
let seq = 0;
|
|
11
|
+
function msg(type: AgentMessageType, resources?: AgentResourceReference[], workstream_id = 'main'): AgentMessage {
|
|
12
|
+
seq += 1;
|
|
13
|
+
return {
|
|
14
|
+
timestamp: seq,
|
|
15
|
+
workflow_run_id: 'run-1',
|
|
16
|
+
type,
|
|
17
|
+
message: '',
|
|
18
|
+
workstream_id,
|
|
19
|
+
...(resources ? { details: { event_class: 'activity', resources } } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ref(
|
|
24
|
+
type: AgentResourceReference['type'],
|
|
25
|
+
id: string,
|
|
26
|
+
action: AgentResourceReference['action'],
|
|
27
|
+
label = id,
|
|
28
|
+
): AgentResourceReference {
|
|
29
|
+
return { type, id, label, action };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('aggregateLatestTurnResources', () => {
|
|
33
|
+
it('returns resources from tool messages in the latest turn', () => {
|
|
34
|
+
const result = aggregateLatestTurnResources([
|
|
35
|
+
msg(AgentMessageType.QUESTION),
|
|
36
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'created', 'Doc One')]),
|
|
37
|
+
msg(AgentMessageType.THOUGHT, [ref('collection', 'c1', 'created', 'Col One')]),
|
|
38
|
+
msg(AgentMessageType.COMPLETE),
|
|
39
|
+
]);
|
|
40
|
+
expect(result).toEqual([
|
|
41
|
+
ref('document', 'd1', 'created', 'Doc One'),
|
|
42
|
+
ref('collection', 'c1', 'created', 'Col One'),
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('ignores resources from earlier turns', () => {
|
|
47
|
+
const result = aggregateLatestTurnResources([
|
|
48
|
+
msg(AgentMessageType.QUESTION),
|
|
49
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'old', 'created')]),
|
|
50
|
+
msg(AgentMessageType.COMPLETE),
|
|
51
|
+
msg(AgentMessageType.QUESTION),
|
|
52
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'new', 'created')]),
|
|
53
|
+
msg(AgentMessageType.COMPLETE),
|
|
54
|
+
]);
|
|
55
|
+
expect(result).toEqual([ref('document', 'new', 'created')]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('dedupes by (type, id) and lets create win over later updates', () => {
|
|
59
|
+
const result = aggregateLatestTurnResources([
|
|
60
|
+
msg(AgentMessageType.QUESTION),
|
|
61
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'created', 'First')]),
|
|
62
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'updated', 'Renamed')]),
|
|
63
|
+
]);
|
|
64
|
+
// create wins, but the most recent label is retained
|
|
65
|
+
expect(result).toEqual([ref('document', 'd1', 'created', 'Renamed')]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('drops a resource created then deleted in the same turn', () => {
|
|
69
|
+
const result = aggregateLatestTurnResources([
|
|
70
|
+
msg(AgentMessageType.QUESTION),
|
|
71
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'scratch', 'created')]),
|
|
72
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'scratch', 'deleted')]),
|
|
73
|
+
]);
|
|
74
|
+
expect(result).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('keeps a resource deleted then re-created in the same turn (order matters)', () => {
|
|
78
|
+
const result = aggregateLatestTurnResources([
|
|
79
|
+
msg(AgentMessageType.QUESTION),
|
|
80
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'deleted')]),
|
|
81
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'created', 'Rebuilt View')]),
|
|
82
|
+
]);
|
|
83
|
+
expect(result).toEqual([ref('view', 'v1', 'created', 'Rebuilt View')]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('reports a pre-existing resource deleted → recreated → deleted as deleted (not transient)', () => {
|
|
87
|
+
const result = aggregateLatestTurnResources([
|
|
88
|
+
msg(AgentMessageType.QUESTION),
|
|
89
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'deleted')]),
|
|
90
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'created')]),
|
|
91
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'deleted', 'Old View')]),
|
|
92
|
+
]);
|
|
93
|
+
expect(result).toEqual([ref('view', 'v1', 'deleted', 'Old View')]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('omits a resource created → deleted → created → deleted (never existed before the turn)', () => {
|
|
97
|
+
const result = aggregateLatestTurnResources([
|
|
98
|
+
msg(AgentMessageType.QUESTION),
|
|
99
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'created')]),
|
|
100
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'deleted')]),
|
|
101
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'created')]),
|
|
102
|
+
msg(AgentMessageType.THOUGHT, [ref('view', 'v1', 'deleted')]),
|
|
103
|
+
]);
|
|
104
|
+
expect(result).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('keeps a delete of a pre-existing resource', () => {
|
|
108
|
+
const result = aggregateLatestTurnResources([
|
|
109
|
+
msg(AgentMessageType.QUESTION),
|
|
110
|
+
msg(AgentMessageType.THOUGHT, [ref('interaction', 'i1', 'deleted', 'Old Interaction')]),
|
|
111
|
+
]);
|
|
112
|
+
expect(result).toEqual([ref('interaction', 'i1', 'deleted', 'Old Interaction')]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('collapses repeated updates into a single entry', () => {
|
|
116
|
+
const result = aggregateLatestTurnResources([
|
|
117
|
+
msg(AgentMessageType.QUESTION),
|
|
118
|
+
msg(AgentMessageType.THOUGHT, [ref('collection', 'c1', 'updated')]),
|
|
119
|
+
msg(AgentMessageType.THOUGHT, [ref('collection', 'c1', 'updated')]),
|
|
120
|
+
]);
|
|
121
|
+
expect(result).toEqual([ref('collection', 'c1', 'updated')]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('aggregates resources across child workstreams in the turn', () => {
|
|
125
|
+
const result = aggregateLatestTurnResources([
|
|
126
|
+
msg(AgentMessageType.QUESTION),
|
|
127
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'main-doc', 'created')], 'main'),
|
|
128
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'child-doc', 'created')], 'ws-2'),
|
|
129
|
+
]);
|
|
130
|
+
expect(result).toEqual([ref('document', 'main-doc', 'created'), ref('document', 'child-doc', 'created')]);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('segmentTurnResources', () => {
|
|
135
|
+
it('returns one entry per turn with that turn’s resources', () => {
|
|
136
|
+
const turns = segmentTurnResources([
|
|
137
|
+
msg(AgentMessageType.QUESTION),
|
|
138
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'created')]),
|
|
139
|
+
msg(AgentMessageType.COMPLETE),
|
|
140
|
+
msg(AgentMessageType.QUESTION),
|
|
141
|
+
msg(AgentMessageType.THOUGHT, [ref('collection', 'c1', 'created')]),
|
|
142
|
+
msg(AgentMessageType.COMPLETE),
|
|
143
|
+
]);
|
|
144
|
+
expect(turns).toEqual([[ref('document', 'd1', 'created')], [ref('collection', 'c1', 'created')]]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('folds leading non-question messages into the first turn (index alignment)', () => {
|
|
148
|
+
const turns = segmentTurnResources([
|
|
149
|
+
msg(AgentMessageType.SYSTEM),
|
|
150
|
+
msg(AgentMessageType.QUESTION),
|
|
151
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'created')]),
|
|
152
|
+
msg(AgentMessageType.QUESTION),
|
|
153
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd2', 'created')]),
|
|
154
|
+
]);
|
|
155
|
+
expect(turns).toHaveLength(2);
|
|
156
|
+
expect(turns[0]).toEqual([ref('document', 'd1', 'created')]);
|
|
157
|
+
expect(turns[1]).toEqual([ref('document', 'd2', 'created')]);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('isResourceSummaryReady', () => {
|
|
162
|
+
it('treats a final answer as the end of an interactive turn', () => {
|
|
163
|
+
expect(
|
|
164
|
+
isResourceSummaryReady([
|
|
165
|
+
msg(AgentMessageType.QUESTION),
|
|
166
|
+
msg(AgentMessageType.THOUGHT, [ref('document', 'd1', 'created')]),
|
|
167
|
+
msg(AgentMessageType.ANSWER),
|
|
168
|
+
]),
|
|
169
|
+
).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('does not treat an answer as final when later main-workstream activity exists', () => {
|
|
173
|
+
expect(
|
|
174
|
+
isResourceSummaryReady([
|
|
175
|
+
msg(AgentMessageType.QUESTION),
|
|
176
|
+
msg(AgentMessageType.ANSWER),
|
|
177
|
+
msg(AgentMessageType.THOUGHT),
|
|
178
|
+
]),
|
|
179
|
+
).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('does not let a child-workstream answer complete the main turn', () => {
|
|
183
|
+
expect(
|
|
184
|
+
isResourceSummaryReady([
|
|
185
|
+
msg(AgentMessageType.QUESTION),
|
|
186
|
+
msg(AgentMessageType.THOUGHT),
|
|
187
|
+
msg(AgentMessageType.ANSWER, undefined, 'child'),
|
|
188
|
+
]),
|
|
189
|
+
).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('interleaveTurnSummaries', () => {
|
|
194
|
+
type Item = { kind: 'q' | 'x'; id: string };
|
|
195
|
+
const q = (id: string): Item => ({ kind: 'q', id });
|
|
196
|
+
const x = (id: string): Item => ({ kind: 'x', id });
|
|
197
|
+
const isBoundary = (i: Item) => i.kind === 'q';
|
|
198
|
+
const turn = (id: string, action: AgentResourceReference['action'] = 'created'): AgentResourceReference[] => [
|
|
199
|
+
ref('document', id, action),
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
it('inserts a completed turn’s summary before the next turn boundary and at the end', () => {
|
|
203
|
+
const items = [q('q1'), x('a'), q('q2'), x('b')];
|
|
204
|
+
const out = interleaveTurnSummaries(items, isBoundary, [turn('d1'), turn('d2')], true);
|
|
205
|
+
const kinds = out.map((i) => ('type' in i ? i.type : i.kind));
|
|
206
|
+
// summary for turn 0 appears before q2; summary for turn 1 at the end
|
|
207
|
+
expect(kinds).toEqual(['q', 'x', 'resource_summary', 'q', 'x', 'resource_summary']);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('does not render the last (in-progress) turn summary until the stream is complete', () => {
|
|
211
|
+
const items = [q('q1'), x('a')];
|
|
212
|
+
const out = interleaveTurnSummaries(items, isBoundary, [turn('d1')], false);
|
|
213
|
+
expect(out.some((i) => 'type' in i && i.type === 'resource_summary')).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('skips turns with no resources', () => {
|
|
217
|
+
const items = [q('q1'), x('a'), q('q2')];
|
|
218
|
+
const empty: AgentResourceReference[] = [];
|
|
219
|
+
const out = interleaveTurnSummaries(items, isBoundary, [empty, turn('d2')], true);
|
|
220
|
+
// turn 0 empty → no summary before q2; turn 1 has resources → summary at end
|
|
221
|
+
const kinds = out.map((i) => ('type' in i ? i.type : i.kind));
|
|
222
|
+
expect(kinds).toEqual(['q', 'x', 'q', 'resource_summary']);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('returns items unchanged when there are no turn summaries', () => {
|
|
226
|
+
const items = [q('q1'), x('a')];
|
|
227
|
+
expect(interleaveTurnSummaries(items, isBoundary, [], true)).toBe(items);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentMessage,
|
|
3
|
+
AgentMessageType,
|
|
4
|
+
type AgentResourceReference,
|
|
5
|
+
getResourcesFromMessage,
|
|
6
|
+
} from '@vertesia/common';
|
|
7
|
+
import { getWorkstreamId, isInProgress } from './utils';
|
|
8
|
+
|
|
9
|
+
interface ResourceAccumulator {
|
|
10
|
+
ref: AgentResourceReference;
|
|
11
|
+
/** The first action observed for this resource in the turn — indicates whether it pre-existed. */
|
|
12
|
+
firstAction: AgentResourceReference['action'];
|
|
13
|
+
/** The most recent action observed, in message order. */
|
|
14
|
+
lastAction: AgentResourceReference['action'];
|
|
15
|
+
/** True once a `created` action was seen for this resource within the turn. */
|
|
16
|
+
createdInTurn: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Dedupe the resources referenced across a set of messages by (type, id), modeling operations
|
|
21
|
+
* sequentially (in message order):
|
|
22
|
+
* - first created then finally deleted (create → … → delete) → transient scratch work that never
|
|
23
|
+
* existed before the turn, omitted;
|
|
24
|
+
* - a resource that pre-existed (first action was delete/update) and ends deleted → kept as `deleted`
|
|
25
|
+
* (e.g. delete → create → delete reports `deleted`, not transient);
|
|
26
|
+
* - any sequence ending in create, or containing a create when not ending deleted → kept as `created`;
|
|
27
|
+
* - otherwise `updated`.
|
|
28
|
+
* The most recently seen label/revision is retained, and first-mention order is preserved.
|
|
29
|
+
*/
|
|
30
|
+
function dedupeResources(messages: AgentMessage[]): AgentResourceReference[] {
|
|
31
|
+
const order: string[] = [];
|
|
32
|
+
const byKey = new Map<string, ResourceAccumulator>();
|
|
33
|
+
|
|
34
|
+
for (const message of messages) {
|
|
35
|
+
for (const ref of getResourcesFromMessage(message)) {
|
|
36
|
+
const key = `${ref.type}:${ref.id}`;
|
|
37
|
+
let entry = byKey.get(key);
|
|
38
|
+
if (!entry) {
|
|
39
|
+
entry = { ref, firstAction: ref.action, lastAction: ref.action, createdInTurn: false };
|
|
40
|
+
byKey.set(key, entry);
|
|
41
|
+
order.push(key);
|
|
42
|
+
}
|
|
43
|
+
entry.ref = ref;
|
|
44
|
+
entry.lastAction = ref.action;
|
|
45
|
+
if (ref.action === 'created') entry.createdInTurn = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result: AgentResourceReference[] = [];
|
|
50
|
+
for (const key of order) {
|
|
51
|
+
const entry = byKey.get(key);
|
|
52
|
+
if (!entry) continue;
|
|
53
|
+
// Created first and ultimately deleted → never existed before the turn, transient: omit.
|
|
54
|
+
if (entry.firstAction === 'created' && entry.lastAction === 'deleted') continue;
|
|
55
|
+
const action: AgentResourceReference['action'] =
|
|
56
|
+
entry.lastAction === 'deleted'
|
|
57
|
+
? 'deleted'
|
|
58
|
+
: entry.lastAction === 'created' || entry.createdInTurn
|
|
59
|
+
? 'created'
|
|
60
|
+
: 'updated';
|
|
61
|
+
result.push({ ...entry.ref, action });
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Split the conversation into turns at user QUESTION boundaries and return the deduped resources for
|
|
68
|
+
* each turn, in order. A turn spans from one user message up to (but excluding) the next. Turns with
|
|
69
|
+
* no resources still produce an entry (empty list) so callers can align positions with turn indices.
|
|
70
|
+
*/
|
|
71
|
+
export function segmentTurnResources(messages: AgentMessage[]): AgentResourceReference[][] {
|
|
72
|
+
if (messages.length === 0) return [];
|
|
73
|
+
const turns: AgentMessage[][] = [];
|
|
74
|
+
let current: AgentMessage[] = [];
|
|
75
|
+
let currentHasQuestion = false;
|
|
76
|
+
for (const message of messages) {
|
|
77
|
+
// Start a new turn at each user QUESTION, but fold any leading non-question messages (e.g. an
|
|
78
|
+
// initial system prompt) into the first turn so turn indices align with QUESTION order.
|
|
79
|
+
if (message.type === AgentMessageType.QUESTION && currentHasQuestion) {
|
|
80
|
+
turns.push(current);
|
|
81
|
+
current = [];
|
|
82
|
+
currentHasQuestion = false;
|
|
83
|
+
}
|
|
84
|
+
if (message.type === AgentMessageType.QUESTION) currentHasQuestion = true;
|
|
85
|
+
current.push(message);
|
|
86
|
+
}
|
|
87
|
+
if (current.length > 0) turns.push(current);
|
|
88
|
+
|
|
89
|
+
return turns.map(dedupeResources);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Aggregate the resources created/updated/deleted during the most recent turn. Convenience wrapper
|
|
94
|
+
* over {@link segmentTurnResources} for callers that only need the latest turn.
|
|
95
|
+
*/
|
|
96
|
+
export function aggregateLatestTurnResources(messages: AgentMessage[]): AgentResourceReference[] {
|
|
97
|
+
const turns = segmentTurnResources(messages);
|
|
98
|
+
return turns.length > 0 ? turns[turns.length - 1] : [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Whether the latest main-workstream turn has produced its final response and its resource summary
|
|
103
|
+
* can be shown. Interactive conversations commonly remain alive after an ANSWER so they can accept
|
|
104
|
+
* another user message; requiring the entire workflow to become terminal would hide the summary for
|
|
105
|
+
* those otherwise-complete turns.
|
|
106
|
+
*/
|
|
107
|
+
export function isResourceSummaryReady(messages: AgentMessage[]): boolean {
|
|
108
|
+
if (!isInProgress(messages)) return true;
|
|
109
|
+
|
|
110
|
+
const mainMessages = messages.filter((message) => getWorkstreamId(message) === 'main');
|
|
111
|
+
const lastMainMessage = mainMessages[mainMessages.length - 1];
|
|
112
|
+
return lastMainMessage?.type === AgentMessageType.ANSWER;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ResourceSummaryRenderItem {
|
|
116
|
+
type: 'resource_summary';
|
|
117
|
+
key: string;
|
|
118
|
+
resources: AgentResourceReference[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Interleave per-turn resource summaries into a rendered timeline of `items`. A completed turn's
|
|
123
|
+
* summary is emitted right before the next turn begins, and the most recent completed turn's summary
|
|
124
|
+
* at the end. `isTurnBoundary` identifies the user-message item that starts a new turn; the i-th
|
|
125
|
+
* boundary maps to `turnResources[i - 1]`. The final (most recent) turn is only summarized when
|
|
126
|
+
* `isLatestTurnComplete` is true, so an in-progress turn shows no summary. Empty turns are skipped.
|
|
127
|
+
*/
|
|
128
|
+
export function interleaveTurnSummaries<T>(
|
|
129
|
+
items: T[],
|
|
130
|
+
isTurnBoundary: (item: T) => boolean,
|
|
131
|
+
turnResources: AgentResourceReference[][],
|
|
132
|
+
isLatestTurnComplete: boolean,
|
|
133
|
+
): Array<T | ResourceSummaryRenderItem> {
|
|
134
|
+
if (turnResources.length === 0) return items;
|
|
135
|
+
const out: Array<T | ResourceSummaryRenderItem> = [];
|
|
136
|
+
const pushSummary = (turnIndex: number) => {
|
|
137
|
+
const resources = turnResources[turnIndex];
|
|
138
|
+
if (!resources || resources.length === 0) return;
|
|
139
|
+
const isLastTurn = turnIndex >= turnResources.length - 1;
|
|
140
|
+
if (isLastTurn && !isLatestTurnComplete) return;
|
|
141
|
+
out.push({ type: 'resource_summary', key: `resource-summary-${turnIndex}`, resources });
|
|
142
|
+
};
|
|
143
|
+
let boundariesSeen = 0;
|
|
144
|
+
for (const item of items) {
|
|
145
|
+
const boundary = isTurnBoundary(item);
|
|
146
|
+
if (boundary && boundariesSeen > 0) pushSummary(boundariesSeen - 1);
|
|
147
|
+
if (boundary) boundariesSeen += 1;
|
|
148
|
+
out.push(item);
|
|
149
|
+
}
|
|
150
|
+
pushSummary(boundariesSeen > 0 ? boundariesSeen - 1 : turnResources.length - 1);
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
@@ -154,6 +154,23 @@ describe('ModernAgentOutput utils - progress state', () => {
|
|
|
154
154
|
|
|
155
155
|
expect(isInProgress([idle, question])).toBe(true);
|
|
156
156
|
});
|
|
157
|
+
|
|
158
|
+
it('ignores passive artifact working-copy saves after the agent goes idle', () => {
|
|
159
|
+
const idle = makeMessage({
|
|
160
|
+
timestamp: 1000,
|
|
161
|
+
type: AgentMessageType.IDLE,
|
|
162
|
+
message: 'Waiting for your command...',
|
|
163
|
+
});
|
|
164
|
+
const userAutosave = makeMessage({
|
|
165
|
+
timestamp: 2000,
|
|
166
|
+
type: AgentMessageType.UPDATE,
|
|
167
|
+
message: 'Updated working copy files/blog_post_draft.md',
|
|
168
|
+
details: { event_class: 'artifact_updated', source: 'user_edit', generation: '1784609448394436' },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// The user editing their artifact while the agent is idle must not resurrect "Working".
|
|
172
|
+
expect(isInProgress([idle, userAutosave])).toBe(false);
|
|
173
|
+
});
|
|
157
174
|
});
|
|
158
175
|
|
|
159
176
|
describe('ModernAgentOutput utils - sliding view thinking', () => {
|
|
@@ -55,6 +55,19 @@ export const DONE_STATES = [
|
|
|
55
55
|
AgentMessageType.TERMINATED,
|
|
56
56
|
];
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Passive artifact working-copy saves (e.g. the user autosaving their edits in the collaborative
|
|
60
|
+
* editor) arrive as UPDATE messages with an `artifact_updated` event class. They are not agent
|
|
61
|
+
* activity: they must not flip the conversation back to "Working" and must not render as tool-call
|
|
62
|
+
* cards in the timeline.
|
|
63
|
+
*/
|
|
64
|
+
export function isPassiveArtifactUpdate(message: AgentMessage): boolean {
|
|
65
|
+
return (
|
|
66
|
+
message.type === AgentMessageType.UPDATE &&
|
|
67
|
+
(message.details as { event_class?: string } | undefined)?.event_class === 'artifact_updated'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
export function isInProgress(messages: AgentMessage[]) {
|
|
59
72
|
if (!messages.length) return true;
|
|
60
73
|
|
|
@@ -63,7 +76,7 @@ export function isInProgress(messages: AgentMessage[]) {
|
|
|
63
76
|
message.details?._deliveryStatus === 'failed' &&
|
|
64
77
|
DONE_STATES.includes(message.type);
|
|
65
78
|
|
|
66
|
-
const progressMessages = messages.filter((m) => !isTerminalOptimisticFailure(m));
|
|
79
|
+
const progressMessages = messages.filter((m) => !isTerminalOptimisticFailure(m) && !isPassiveArtifactUpdate(m));
|
|
67
80
|
|
|
68
81
|
// Only the main workstream determines whether the conversation is in progress.
|
|
69
82
|
// Child workstream COMPLETE/IDLE messages must not flip this flag.
|