@parhelia/core 0.1.10745
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/LICENSE +8 -0
- package/README.md +34 -0
- package/dev.css +3 -0
- package/dist/agents-view/AgentCard.d.ts +12 -0
- package/dist/agents-view/AgentCard.js +30 -0
- package/dist/agents-view/AgentCard.js.map +1 -0
- package/dist/agents-view/AgentsView.d.ts +5 -0
- package/dist/agents-view/AgentsView.js +255 -0
- package/dist/agents-view/AgentsView.js.map +1 -0
- package/dist/agents-view/ProfileAgentsGroup.d.ts +17 -0
- package/dist/agents-view/ProfileAgentsGroup.js +13 -0
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -0
- package/dist/client-components/api.d.ts +1 -0
- package/dist/client-components/api.js +4 -0
- package/dist/client-components/api.js.map +1 -0
- package/dist/client-components/index.d.ts +17 -0
- package/dist/client-components/index.js +18 -0
- package/dist/client-components/index.js.map +1 -0
- package/dist/components/ActionButton.d.ts +14 -0
- package/dist/components/ActionButton.js +6 -0
- package/dist/components/ActionButton.js.map +1 -0
- package/dist/components/Error.d.ts +9 -0
- package/dist/components/Error.js +24 -0
- package/dist/components/Error.js.map +1 -0
- package/dist/components/FilterInput.d.ts +22 -0
- package/dist/components/FilterInput.js +29 -0
- package/dist/components/FilterInput.js.map +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.js +5 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/ui/CardConnector.d.ts +4 -0
- package/dist/components/ui/CardConnector.js +6 -0
- package/dist/components/ui/CardConnector.js.map +1 -0
- package/dist/components/ui/LanguageSelector.d.ts +8 -0
- package/dist/components/ui/LanguageSelector.js +47 -0
- package/dist/components/ui/LanguageSelector.js.map +1 -0
- package/dist/components/ui/PlaceholderInput.d.ts +41 -0
- package/dist/components/ui/PlaceholderInput.js +160 -0
- package/dist/components/ui/PlaceholderInput.js.map +1 -0
- package/dist/components/ui/PlaceholderInputTypes.d.ts +41 -0
- package/dist/components/ui/PlaceholderInputTypes.js +48 -0
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -0
- package/dist/components/ui/PlaceholderItemSelector.d.ts +7 -0
- package/dist/components/ui/PlaceholderItemSelector.js +154 -0
- package/dist/components/ui/PlaceholderItemSelector.js.map +1 -0
- package/dist/components/ui/alert.d.ts +7 -0
- package/dist/components/ui/alert.js +21 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +23 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +33 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +7 -0
- package/dist/components/ui/calendar.js +58 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +19 -0
- package/dist/components/ui/card.js +78 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +10 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/command.d.ts +18 -0
- package/dist/components/ui/command.js +35 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +53 -0
- package/dist/components/ui/context-menu.js +290 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/copy-button.d.ts +10 -0
- package/dist/components/ui/copy-button.js +61 -0
- package/dist/components/ui/copy-button.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +16 -0
- package/dist/components/ui/dialog.js +37 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/dropdown-menu.js +52 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +9 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +26 -0
- package/dist/components/ui/menubar.js +55 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/paste-button.d.ts +14 -0
- package/dist/components/ui/paste-button.js +114 -0
- package/dist/components/ui/paste-button.js.map +1 -0
- package/dist/components/ui/popover.d.ts +11 -0
- package/dist/components/ui/popover.js +66 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +7 -0
- package/dist/components/ui/progress.js +9 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/select.d.ts +18 -0
- package/dist/components/ui/select.js +34 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +3 -0
- package/dist/components/ui/sonner.js +16 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.js +9 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +17 -0
- package/dist/components/ui/tabs.js +27 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/textarea.js +11 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +9 -0
- package/dist/components/ui/tooltip.js +18 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/components/ui/upload-button.d.ts +15 -0
- package/dist/components/ui/upload-button.js +56 -0
- package/dist/components/ui/upload-button.js.map +1 -0
- package/dist/config/config.d.ts +24 -0
- package/dist/config/config.js +1087 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/types.d.ts +249 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/editor/AspectRatioSelector.d.ts +13 -0
- package/dist/editor/AspectRatioSelector.js +71 -0
- package/dist/editor/AspectRatioSelector.js.map +1 -0
- package/dist/editor/ComponentInfo.d.ts +4 -0
- package/dist/editor/ComponentInfo.js +41 -0
- package/dist/editor/ComponentInfo.js.map +1 -0
- package/dist/editor/ConfirmationDialog.d.ts +19 -0
- package/dist/editor/ConfirmationDialog.js +31 -0
- package/dist/editor/ConfirmationDialog.js.map +1 -0
- package/dist/editor/ContentTree.d.ts +39 -0
- package/dist/editor/ContentTree.js +586 -0
- package/dist/editor/ContentTree.js.map +1 -0
- package/dist/editor/ContextMenu.d.ts +15 -0
- package/dist/editor/ContextMenu.js +239 -0
- package/dist/editor/ContextMenu.js.map +1 -0
- package/dist/editor/Editor.d.ts +12 -0
- package/dist/editor/Editor.js +59 -0
- package/dist/editor/Editor.js.map +1 -0
- package/dist/editor/EditorWarning.d.ts +5 -0
- package/dist/editor/EditorWarning.js +12 -0
- package/dist/editor/EditorWarning.js.map +1 -0
- package/dist/editor/EditorWarnings.d.ts +9 -0
- package/dist/editor/EditorWarnings.js +19 -0
- package/dist/editor/EditorWarnings.js.map +1 -0
- package/dist/editor/FieldActionsOverlay.d.ts +18 -0
- package/dist/editor/FieldActionsOverlay.js +201 -0
- package/dist/editor/FieldActionsOverlay.js.map +1 -0
- package/dist/editor/FieldEditorPopup.d.ts +10 -0
- package/dist/editor/FieldEditorPopup.js +23 -0
- package/dist/editor/FieldEditorPopup.js.map +1 -0
- package/dist/editor/FieldHistory.d.ts +7 -0
- package/dist/editor/FieldHistory.js +42 -0
- package/dist/editor/FieldHistory.js.map +1 -0
- package/dist/editor/FieldList.d.ts +19 -0
- package/dist/editor/FieldList.js +90 -0
- package/dist/editor/FieldList.js.map +1 -0
- package/dist/editor/FieldListField.d.ts +16 -0
- package/dist/editor/FieldListField.js +231 -0
- package/dist/editor/FieldListField.js.map +1 -0
- package/dist/editor/FieldListFieldWithFallbacks.d.ts +11 -0
- package/dist/editor/FieldListFieldWithFallbacks.js +120 -0
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -0
- package/dist/editor/FloatingToolbar.d.ts +7 -0
- package/dist/editor/FloatingToolbar.js +91 -0
- package/dist/editor/FloatingToolbar.js.map +1 -0
- package/dist/editor/ImageEditButton.d.ts +27 -0
- package/dist/editor/ImageEditButton.js +22 -0
- package/dist/editor/ImageEditButton.js.map +1 -0
- package/dist/editor/ImageEditor.d.ts +5 -0
- package/dist/editor/ImageEditor.js +76 -0
- package/dist/editor/ImageEditor.js.map +1 -0
- package/dist/editor/ItemInfo.d.ts +4 -0
- package/dist/editor/ItemInfo.js +36 -0
- package/dist/editor/ItemInfo.js.map +1 -0
- package/dist/editor/LinkEditorDialog.d.ts +18 -0
- package/dist/editor/LinkEditorDialog.js +101 -0
- package/dist/editor/LinkEditorDialog.js.map +1 -0
- package/dist/editor/MainLayout.d.ts +12 -0
- package/dist/editor/MainLayout.js +55 -0
- package/dist/editor/MainLayout.js.map +1 -0
- package/dist/editor/MobileLayout.d.ts +2 -0
- package/dist/editor/MobileLayout.js +34 -0
- package/dist/editor/MobileLayout.js.map +1 -0
- package/dist/editor/NewEditorClient.d.ts +5 -0
- package/dist/editor/NewEditorClient.js +7 -0
- package/dist/editor/NewEditorClient.js.map +1 -0
- package/dist/editor/PictureCropper.d.ts +6 -0
- package/dist/editor/PictureCropper.js +722 -0
- package/dist/editor/PictureCropper.js.map +1 -0
- package/dist/editor/PictureEditor.d.ts +9 -0
- package/dist/editor/PictureEditor.js +180 -0
- package/dist/editor/PictureEditor.js.map +1 -0
- package/dist/editor/PictureEditorDialog.d.ts +8 -0
- package/dist/editor/PictureEditorDialog.js +195 -0
- package/dist/editor/PictureEditorDialog.js.map +1 -0
- package/dist/editor/QuickItemSwitcher.d.ts +9 -0
- package/dist/editor/QuickItemSwitcher.js +78 -0
- package/dist/editor/QuickItemSwitcher.js.map +1 -0
- package/dist/editor/ScrollingContentTree.d.ts +10 -0
- package/dist/editor/ScrollingContentTree.js +54 -0
- package/dist/editor/ScrollingContentTree.js.map +1 -0
- package/dist/editor/Titlebar.d.ts +1 -0
- package/dist/editor/Titlebar.js +46 -0
- package/dist/editor/Titlebar.js.map +1 -0
- package/dist/editor/ai/AgentCostDisplay.d.ts +32 -0
- package/dist/editor/ai/AgentCostDisplay.js +93 -0
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
- package/dist/editor/ai/AgentHistory.d.ts +11 -0
- package/dist/editor/ai/AgentHistory.js +12 -0
- package/dist/editor/ai/AgentHistory.js.map +1 -0
- package/dist/editor/ai/AgentProfilesOverview.d.ts +9 -0
- package/dist/editor/ai/AgentProfilesOverview.js +16 -0
- package/dist/editor/ai/AgentProfilesOverview.js.map +1 -0
- package/dist/editor/ai/AgentStatusBadge.d.ts +26 -0
- package/dist/editor/ai/AgentStatusBadge.js +126 -0
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -0
- package/dist/editor/ai/AgentTerminal.d.ts +14 -0
- package/dist/editor/ai/AgentTerminal.js +2634 -0
- package/dist/editor/ai/AgentTerminal.js.map +1 -0
- package/dist/editor/ai/Agents.d.ts +4 -0
- package/dist/editor/ai/Agents.js +813 -0
- package/dist/editor/ai/Agents.js.map +1 -0
- package/dist/editor/ai/AiResponseMessage.d.ts +18 -0
- package/dist/editor/ai/AiResponseMessage.js +684 -0
- package/dist/editor/ai/AiResponseMessage.js.map +1 -0
- package/dist/editor/ai/ContextInfoBar.d.ts +11 -0
- package/dist/editor/ai/ContextInfoBar.js +355 -0
- package/dist/editor/ai/ContextInfoBar.js.map +1 -0
- package/dist/editor/ai/DancingDots.d.ts +1 -0
- package/dist/editor/ai/DancingDots.js +6 -0
- package/dist/editor/ai/DancingDots.js.map +1 -0
- package/dist/editor/ai/MediaImage.d.ts +6 -0
- package/dist/editor/ai/MediaImage.js +38 -0
- package/dist/editor/ai/MediaImage.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.d.ts +45 -0
- package/dist/editor/ai/ToolCallDisplay.js +292 -0
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -0
- package/dist/editor/ai/aiPageModel.d.ts +24 -0
- package/dist/editor/ai/aiPageModel.js +97 -0
- package/dist/editor/ai/aiPageModel.js.map +1 -0
- package/dist/editor/ai/editorAiContext.d.ts +10 -0
- package/dist/editor/ai/editorAiContext.js +10 -0
- package/dist/editor/ai/editorAiContext.js.map +1 -0
- package/dist/editor/ai/types.d.ts +30 -0
- package/dist/editor/ai/types.js +2 -0
- package/dist/editor/ai/types.js.map +1 -0
- package/dist/editor/ai/useAgentStatus.d.ts +14 -0
- package/dist/editor/ai/useAgentStatus.js +203 -0
- package/dist/editor/ai/useAgentStatus.js.map +1 -0
- package/dist/editor/client/AboutDialog.d.ts +2 -0
- package/dist/editor/client/AboutDialog.js +23 -0
- package/dist/editor/client/AboutDialog.js.map +1 -0
- package/dist/editor/client/EditorShell.d.ts +37 -0
- package/dist/editor/client/EditorShell.js +2388 -0
- package/dist/editor/client/EditorShell.js.map +1 -0
- package/dist/editor/client/GenericDialog.d.ts +10 -0
- package/dist/editor/client/GenericDialog.js +25 -0
- package/dist/editor/client/GenericDialog.js.map +1 -0
- package/dist/editor/client/editContext.d.ts +260 -0
- package/dist/editor/client/editContext.js +29 -0
- package/dist/editor/client/editContext.js.map +1 -0
- package/dist/editor/client/fieldModificationStore.d.ts +25 -0
- package/dist/editor/client/fieldModificationStore.js +184 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -0
- package/dist/editor/client/helpers.d.ts +12 -0
- package/dist/editor/client/helpers.js +29 -0
- package/dist/editor/client/helpers.js.map +1 -0
- package/dist/editor/client/hooks/useEditorUrlSync.d.ts +18 -0
- package/dist/editor/client/hooks/useEditorUrlSync.js +56 -0
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -0
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +11 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js +86 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -0
- package/dist/editor/client/hooks/useGlobalEditorKeyDown.d.ts +1 -0
- package/dist/editor/client/hooks/useGlobalEditorKeyDown.js +12 -0
- package/dist/editor/client/hooks/useGlobalEditorKeyDown.js.map +1 -0
- package/dist/editor/client/hooks/useMediaQuery.d.ts +1 -0
- package/dist/editor/client/hooks/useMediaQuery.js +19 -0
- package/dist/editor/client/hooks/useMediaQuery.js.map +1 -0
- package/dist/editor/client/hooks/useMediaSelector.d.ts +12 -0
- package/dist/editor/client/hooks/useMediaSelector.js +30 -0
- package/dist/editor/client/hooks/useMediaSelector.js.map +1 -0
- package/dist/editor/client/hooks/useQuota.d.ts +29 -0
- package/dist/editor/client/hooks/useQuota.js +53 -0
- package/dist/editor/client/hooks/useQuota.js.map +1 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.d.ts +35 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js +234 -0
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -0
- package/dist/editor/client/hooks/useWorkbox.d.ts +9 -0
- package/dist/editor/client/hooks/useWorkbox.js +52 -0
- package/dist/editor/client/hooks/useWorkbox.js.map +1 -0
- package/dist/editor/client/itemsRepository.d.ts +38 -0
- package/dist/editor/client/itemsRepository.js +468 -0
- package/dist/editor/client/itemsRepository.js.map +1 -0
- package/dist/editor/client/operations.d.ts +69 -0
- package/dist/editor/client/operations.js +627 -0
- package/dist/editor/client/operations.js.map +1 -0
- package/dist/editor/client/pageModelBuilder.d.ts +6 -0
- package/dist/editor/client/pageModelBuilder.js +155 -0
- package/dist/editor/client/pageModelBuilder.js.map +1 -0
- package/dist/editor/client/ui/EditorChrome.d.ts +12 -0
- package/dist/editor/client/ui/EditorChrome.js +30 -0
- package/dist/editor/client/ui/EditorChrome.js.map +1 -0
- package/dist/editor/client/ui/FullscreenControls.d.ts +7 -0
- package/dist/editor/client/ui/FullscreenControls.js +21 -0
- package/dist/editor/client/ui/FullscreenControls.js.map +1 -0
- package/dist/editor/commands/agentCommands.d.ts +9 -0
- package/dist/editor/commands/agentCommands.js +30 -0
- package/dist/editor/commands/agentCommands.js.map +1 -0
- package/dist/editor/commands/commands.d.ts +19 -0
- package/dist/editor/commands/commands.js +2 -0
- package/dist/editor/commands/commands.js.map +1 -0
- package/dist/editor/commands/componentCommands.d.ts +12 -0
- package/dist/editor/commands/componentCommands.js +378 -0
- package/dist/editor/commands/componentCommands.js.map +1 -0
- package/dist/editor/commands/createVersionCommand.d.ts +4 -0
- package/dist/editor/commands/createVersionCommand.js +24 -0
- package/dist/editor/commands/createVersionCommand.js.map +1 -0
- package/dist/editor/commands/deleteVersionCommand.d.ts +4 -0
- package/dist/editor/commands/deleteVersionCommand.js +53 -0
- package/dist/editor/commands/deleteVersionCommand.js.map +1 -0
- package/dist/editor/commands/itemCommands.d.ts +29 -0
- package/dist/editor/commands/itemCommands.js +428 -0
- package/dist/editor/commands/itemCommands.js.map +1 -0
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.d.ts +8 -0
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js +91 -0
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js.map +1 -0
- package/dist/editor/commands/undo.d.ts +15 -0
- package/dist/editor/commands/undo.js +29 -0
- package/dist/editor/commands/undo.js.map +1 -0
- package/dist/editor/componentTreeHelper.d.ts +18 -0
- package/dist/editor/componentTreeHelper.js +123 -0
- package/dist/editor/componentTreeHelper.js.map +1 -0
- package/dist/editor/context-menu/CopyMoveMenu.d.ts +7 -0
- package/dist/editor/context-menu/CopyMoveMenu.js +58 -0
- package/dist/editor/context-menu/CopyMoveMenu.js.map +1 -0
- package/dist/editor/context-menu/InsertMenu.d.ts +9 -0
- package/dist/editor/context-menu/InsertMenu.js +229 -0
- package/dist/editor/context-menu/InsertMenu.js.map +1 -0
- package/dist/editor/control-center/About.d.ts +1 -0
- package/dist/editor/control-center/About.js +8 -0
- package/dist/editor/control-center/About.js.map +1 -0
- package/dist/editor/control-center/AllAgentsPanel.d.ts +5 -0
- package/dist/editor/control-center/AllAgentsPanel.js +126 -0
- package/dist/editor/control-center/AllAgentsPanel.js.map +1 -0
- package/dist/editor/control-center/ControlCenterMenu.d.ts +1 -0
- package/dist/editor/control-center/ControlCenterMenu.js +66 -0
- package/dist/editor/control-center/ControlCenterMenu.js.map +1 -0
- package/dist/editor/control-center/IndexOverview.d.ts +1 -0
- package/dist/editor/control-center/IndexOverview.js +323 -0
- package/dist/editor/control-center/IndexOverview.js.map +1 -0
- package/dist/editor/control-center/Info.d.ts +1 -0
- package/dist/editor/control-center/Info.js +10 -0
- package/dist/editor/control-center/Info.js.map +1 -0
- package/dist/editor/control-center/LatestFeedback.d.ts +1 -0
- package/dist/editor/control-center/LatestFeedback.js +136 -0
- package/dist/editor/control-center/LatestFeedback.js.map +1 -0
- package/dist/editor/control-center/QuotaInfo.d.ts +1 -0
- package/dist/editor/control-center/QuotaInfo.js +102 -0
- package/dist/editor/control-center/QuotaInfo.js.map +1 -0
- package/dist/editor/control-center/Setup.d.ts +1 -0
- package/dist/editor/control-center/Setup.js +18 -0
- package/dist/editor/control-center/Setup.js.map +1 -0
- package/dist/editor/control-center/Status.d.ts +1 -0
- package/dist/editor/control-center/Status.js +79 -0
- package/dist/editor/control-center/Status.js.map +1 -0
- package/dist/editor/control-center/WebSocketMessages.d.ts +1 -0
- package/dist/editor/control-center/WebSocketMessages.js +71 -0
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -0
- package/dist/editor/control-center/parhelia-setup/Overview.d.ts +1 -0
- package/dist/editor/control-center/parhelia-setup/Overview.js +91 -0
- package/dist/editor/control-center/parhelia-setup/Overview.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.js +22 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js +233 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +367 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.d.ts +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.js +2 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/types.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.d.ts +4 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js +25 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/utils.js.map +1 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.js +46 -0
- package/dist/editor/control-center/setup-steps/DbSetupStep.js.map +1 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js +36 -0
- package/dist/editor/control-center/setup-steps/IndexSetupStep.js.map +1 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.d.ts +2 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.js +107 -0
- package/dist/editor/control-center/setup-steps/SettingsSetupStep.js.map +1 -0
- package/dist/editor/editor-warnings/ItemLocked.d.ts +2 -0
- package/dist/editor/editor-warnings/ItemLocked.js +38 -0
- package/dist/editor/editor-warnings/ItemLocked.js.map +1 -0
- package/dist/editor/editor-warnings/NoLanguageWriteAccess.d.ts +2 -0
- package/dist/editor/editor-warnings/NoLanguageWriteAccess.js +14 -0
- package/dist/editor/editor-warnings/NoLanguageWriteAccess.js.map +1 -0
- package/dist/editor/editor-warnings/NoWorkflowWriteAccess.d.ts +2 -0
- package/dist/editor/editor-warnings/NoWorkflowWriteAccess.js +14 -0
- package/dist/editor/editor-warnings/NoWorkflowWriteAccess.js.map +1 -0
- package/dist/editor/editor-warnings/NoWriteAccess.d.ts +2 -0
- package/dist/editor/editor-warnings/NoWriteAccess.js +14 -0
- package/dist/editor/editor-warnings/NoWriteAccess.js.map +1 -0
- package/dist/editor/editor-warnings/ValidationErrors.d.ts +2 -0
- package/dist/editor/editor-warnings/ValidationErrors.js +28 -0
- package/dist/editor/editor-warnings/ValidationErrors.js.map +1 -0
- package/dist/editor/field-types/AttachmentEditor.d.ts +9 -0
- package/dist/editor/field-types/AttachmentEditor.js +73 -0
- package/dist/editor/field-types/AttachmentEditor.js.map +1 -0
- package/dist/editor/field-types/CheckboxEditor.d.ts +5 -0
- package/dist/editor/field-types/CheckboxEditor.js +30 -0
- package/dist/editor/field-types/CheckboxEditor.js.map +1 -0
- package/dist/editor/field-types/DateFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/DateFieldEditor.js +93 -0
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -0
- package/dist/editor/field-types/DateTimeFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/DateTimeFieldEditor.js +151 -0
- package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -0
- package/dist/editor/field-types/DropLinkEditor.d.ts +5 -0
- package/dist/editor/field-types/DropLinkEditor.js +42 -0
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -0
- package/dist/editor/field-types/DropListEditor.d.ts +5 -0
- package/dist/editor/field-types/DropListEditor.js +46 -0
- package/dist/editor/field-types/DropListEditor.js.map +1 -0
- package/dist/editor/field-types/ImageFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/ImageFieldEditor.js +34 -0
- package/dist/editor/field-types/ImageFieldEditor.js.map +1 -0
- package/dist/editor/field-types/InternalLinkFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/InternalLinkFieldEditor.js +120 -0
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -0
- package/dist/editor/field-types/LinkFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/LinkFieldEditor.js +62 -0
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -0
- package/dist/editor/field-types/MultiLineText.d.ts +7 -0
- package/dist/editor/field-types/MultiLineText.js +48 -0
- package/dist/editor/field-types/MultiLineText.js.map +1 -0
- package/dist/editor/field-types/NameValueListEditor.d.ts +7 -0
- package/dist/editor/field-types/NameValueListEditor.js +101 -0
- package/dist/editor/field-types/NameValueListEditor.js.map +1 -0
- package/dist/editor/field-types/PictureFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/PictureFieldEditor.js +58 -0
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -0
- package/dist/editor/field-types/RawEditor.d.ts +6 -0
- package/dist/editor/field-types/RawEditor.js +31 -0
- package/dist/editor/field-types/RawEditor.js.map +1 -0
- package/dist/editor/field-types/ReactQuill.d.ts +125 -0
- package/dist/editor/field-types/ReactQuill.js +385 -0
- package/dist/editor/field-types/ReactQuill.js.map +1 -0
- package/dist/editor/field-types/RichTextEditor.d.ts +7 -0
- package/dist/editor/field-types/RichTextEditor.js +21 -0
- package/dist/editor/field-types/RichTextEditor.js.map +1 -0
- package/dist/editor/field-types/RichTextEditorComponent.d.ts +8 -0
- package/dist/editor/field-types/RichTextEditorComponent.js +74 -0
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -0
- package/dist/editor/field-types/SingleLineText.d.ts +7 -0
- package/dist/editor/field-types/SingleLineText.js +113 -0
- package/dist/editor/field-types/SingleLineText.js.map +1 -0
- package/dist/editor/field-types/TreeListEditor.d.ts +5 -0
- package/dist/editor/field-types/TreeListEditor.js +316 -0
- package/dist/editor/field-types/TreeListEditor.js.map +1 -0
- package/dist/editor/field-types/richtext/components/EditorDropdown.d.ts +11 -0
- package/dist/editor/field-types/richtext/components/EditorDropdown.js +83 -0
- package/dist/editor/field-types/richtext/components/EditorDropdown.js.map +1 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +5 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.js +607 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -0
- package/dist/editor/field-types/richtext/components/SimpleDropdown.d.ts +18 -0
- package/dist/editor/field-types/richtext/components/SimpleDropdown.js +71 -0
- package/dist/editor/field-types/richtext/components/SimpleDropdown.js.map +1 -0
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.d.ts +5 -0
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js +359 -0
- package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbar.d.ts +16 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js +181 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbarButton.d.ts +9 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js +14 -0
- package/dist/editor/field-types/richtext/components/SimpleToolbarButton.js.map +1 -0
- package/dist/editor/field-types/richtext/components/ToolbarButton.d.ts +3 -0
- package/dist/editor/field-types/richtext/components/ToolbarButton.js +6 -0
- package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -0
- package/dist/editor/field-types/richtext/config/pluginFactory.d.ts +19 -0
- package/dist/editor/field-types/richtext/config/pluginFactory.js +17 -0
- package/dist/editor/field-types/richtext/config/pluginFactory.js.map +1 -0
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +4 -0
- package/dist/editor/field-types/richtext/contextMenuFactory.js +201 -0
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -0
- package/dist/editor/field-types/richtext/hooks/useProfileCache.d.ts +68 -0
- package/dist/editor/field-types/richtext/hooks/useProfileCache.js +214 -0
- package/dist/editor/field-types/richtext/hooks/useProfileCache.js.map +1 -0
- package/dist/editor/field-types/richtext/hooks/useRichTextProfile.d.ts +25 -0
- package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js +64 -0
- package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js.map +1 -0
- package/dist/editor/field-types/richtext/index.d.ts +6 -0
- package/dist/editor/field-types/richtext/index.js +7 -0
- package/dist/editor/field-types/richtext/index.js.map +1 -0
- package/dist/editor/field-types/richtext/types.d.ts +288 -0
- package/dist/editor/field-types/richtext/types.js +107 -0
- package/dist/editor/field-types/richtext/types.js.map +1 -0
- package/dist/editor/field-types/richtext/utils/conversion.d.ts +7 -0
- package/dist/editor/field-types/richtext/utils/conversion.js +762 -0
- package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -0
- package/dist/editor/field-types/richtext/utils/plugins.d.ts +170 -0
- package/dist/editor/field-types/richtext/utils/plugins.js +490 -0
- package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -0
- package/dist/editor/field-types/richtext/utils/profileMapper.d.ts +38 -0
- package/dist/editor/field-types/richtext/utils/profileMapper.js +386 -0
- package/dist/editor/field-types/richtext/utils/profileMapper.js.map +1 -0
- package/dist/editor/field-types/richtext/utils/profileServiceCache.d.ts +37 -0
- package/dist/editor/field-types/richtext/utils/profileServiceCache.js +119 -0
- package/dist/editor/field-types/richtext/utils/profileServiceCache.js.map +1 -0
- package/dist/editor/fieldTypes.d.ts +129 -0
- package/dist/editor/fieldTypes.js +2 -0
- package/dist/editor/fieldTypes.js.map +1 -0
- package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
- package/dist/editor/hooks/useEditorSettings.js +61 -0
- package/dist/editor/hooks/useEditorSettings.js.map +1 -0
- package/dist/editor/media-selector/AiImageSearch.d.ts +4 -0
- package/dist/editor/media-selector/AiImageSearch.js +164 -0
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -0
- package/dist/editor/media-selector/AiImageSearchPrompt.d.ts +3 -0
- package/dist/editor/media-selector/AiImageSearchPrompt.js +57 -0
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -0
- package/dist/editor/media-selector/MediaFolderBrowser.d.ts +5 -0
- package/dist/editor/media-selector/MediaFolderBrowser.js +182 -0
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -0
- package/dist/editor/media-selector/MediaSelector.d.ts +9 -0
- package/dist/editor/media-selector/MediaSelector.js +8 -0
- package/dist/editor/media-selector/MediaSelector.js.map +1 -0
- package/dist/editor/media-selector/Preview.d.ts +4 -0
- package/dist/editor/media-selector/Preview.js +19 -0
- package/dist/editor/media-selector/Preview.js.map +1 -0
- package/dist/editor/media-selector/Thumbnails.d.ts +8 -0
- package/dist/editor/media-selector/Thumbnails.js +10 -0
- package/dist/editor/media-selector/Thumbnails.js.map +1 -0
- package/dist/editor/media-selector/TreeSelector.d.ts +7 -0
- package/dist/editor/media-selector/TreeSelector.js +183 -0
- package/dist/editor/media-selector/TreeSelector.js.map +1 -0
- package/dist/editor/media-selector/UploadZone.d.ts +4 -0
- package/dist/editor/media-selector/UploadZone.js +81 -0
- package/dist/editor/media-selector/UploadZone.js.map +1 -0
- package/dist/editor/media-selector/index.d.ts +8 -0
- package/dist/editor/media-selector/index.js +9 -0
- package/dist/editor/media-selector/index.js.map +1 -0
- package/dist/editor/menubar/ActiveUsers.d.ts +1 -0
- package/dist/editor/menubar/ActiveUsers.js +120 -0
- package/dist/editor/menubar/ActiveUsers.js.map +1 -0
- package/dist/editor/menubar/ApproveAndPublish.d.ts +1 -0
- package/dist/editor/menubar/ApproveAndPublish.js +13 -0
- package/dist/editor/menubar/ApproveAndPublish.js.map +1 -0
- package/dist/editor/menubar/FavoritesControls.d.ts +8 -0
- package/dist/editor/menubar/FavoritesControls.js +124 -0
- package/dist/editor/menubar/FavoritesControls.js.map +1 -0
- package/dist/editor/menubar/GenericToolbar.d.ts +11 -0
- package/dist/editor/menubar/GenericToolbar.js +10 -0
- package/dist/editor/menubar/GenericToolbar.js.map +1 -0
- package/dist/editor/menubar/ItemActionsMenu.d.ts +3 -0
- package/dist/editor/menubar/ItemActionsMenu.js +23 -0
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -0
- package/dist/editor/menubar/ItemLanguageVersion.d.ts +1 -0
- package/dist/editor/menubar/ItemLanguageVersion.js +54 -0
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -0
- package/dist/editor/menubar/ItemToolbar.d.ts +1 -0
- package/dist/editor/menubar/ItemToolbar.js +11 -0
- package/dist/editor/menubar/ItemToolbar.js.map +1 -0
- package/dist/editor/menubar/NavButtons.d.ts +1 -0
- package/dist/editor/menubar/NavButtons.js +40 -0
- package/dist/editor/menubar/NavButtons.js.map +1 -0
- package/dist/editor/menubar/PageSelector.d.ts +4 -0
- package/dist/editor/menubar/PageSelector.js +149 -0
- package/dist/editor/menubar/PageSelector.js.map +1 -0
- package/dist/editor/menubar/Separator.d.ts +3 -0
- package/dist/editor/menubar/Separator.js +6 -0
- package/dist/editor/menubar/Separator.js.map +1 -0
- package/dist/editor/menubar/SiteInfo.d.ts +1 -0
- package/dist/editor/menubar/SiteInfo.js +24 -0
- package/dist/editor/menubar/SiteInfo.js.map +1 -0
- package/dist/editor/menubar/ToolbarFactory.d.ts +2 -0
- package/dist/editor/menubar/ToolbarFactory.js +55 -0
- package/dist/editor/menubar/ToolbarFactory.js.map +1 -0
- package/dist/editor/menubar/User.d.ts +4 -0
- package/dist/editor/menubar/User.js +18 -0
- package/dist/editor/menubar/User.js.map +1 -0
- package/dist/editor/menubar/VersionSelector.d.ts +10 -0
- package/dist/editor/menubar/VersionSelector.js +53 -0
- package/dist/editor/menubar/VersionSelector.js.map +1 -0
- package/dist/editor/menubar/WorkflowButton.d.ts +1 -0
- package/dist/editor/menubar/WorkflowButton.js +93 -0
- package/dist/editor/menubar/WorkflowButton.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/CompareControls.d.ts +1 -0
- package/dist/editor/menubar/toolbar-sections/CompareControls.js +11 -0
- package/dist/editor/menubar/toolbar-sections/CompareControls.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/EditControls.d.ts +7 -0
- package/dist/editor/menubar/toolbar-sections/EditControls.js +15 -0
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/InsertControls.d.ts +1 -0
- package/dist/editor/menubar/toolbar-sections/InsertControls.js +15 -0
- package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/ReviewCommands.d.ts +1 -0
- package/dist/editor/menubar/toolbar-sections/ReviewCommands.js +41 -0
- package/dist/editor/menubar/toolbar-sections/ReviewCommands.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/UtilityControls.d.ts +1 -0
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +17 -0
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/ViewportControls.d.ts +1 -0
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +21 -0
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/index.d.ts +6 -0
- package/dist/editor/menubar/toolbar-sections/index.js +7 -0
- package/dist/editor/menubar/toolbar-sections/index.js.map +1 -0
- package/dist/editor/page-editor-chrome/CommentHighlighting.d.ts +6 -0
- package/dist/editor/page-editor-chrome/CommentHighlighting.js +270 -0
- package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -0
- package/dist/editor/page-editor-chrome/CommentHighlightings.d.ts +4 -0
- package/dist/editor/page-editor-chrome/CommentHighlightings.js +15 -0
- package/dist/editor/page-editor-chrome/CommentHighlightings.js.map +1 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicator.d.ts +4 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +35 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicators.d.ts +1 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicators.js +13 -0
- package/dist/editor/page-editor-chrome/FieldActionIndicators.js.map +1 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.d.ts +8 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +26 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.d.ts +6 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +14 -0
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -0
- package/dist/editor/page-editor-chrome/FrameMenu.d.ts +7 -0
- package/dist/editor/page-editor-chrome/FrameMenu.js +346 -0
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -0
- package/dist/editor/page-editor-chrome/FrameMenus.d.ts +5 -0
- package/dist/editor/page-editor-chrome/FrameMenus.js +22 -0
- package/dist/editor/page-editor-chrome/FrameMenus.js.map +1 -0
- package/dist/editor/page-editor-chrome/InlineEditor.d.ts +5 -0
- package/dist/editor/page-editor-chrome/InlineEditor.js +704 -0
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -0
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.d.ts +1 -0
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js +33 -0
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js.map +1 -0
- package/dist/editor/page-editor-chrome/NoLayout.d.ts +1 -0
- package/dist/editor/page-editor-chrome/NoLayout.js +19 -0
- package/dist/editor/page-editor-chrome/NoLayout.js.map +1 -0
- package/dist/editor/page-editor-chrome/PageEditorChrome.d.ts +6 -0
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +64 -0
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -0
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.d.ts +1 -0
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +152 -0
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +18 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +122 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.d.ts +5 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +289 -0
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.d.ts +6 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js +224 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlighting.js.map +1 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.d.ts +4 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +16 -0
- package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -0
- package/dist/editor/page-editor-chrome/useInlineAICompletion.d.ts +7 -0
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +699 -0
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -0
- package/dist/editor/page-viewer/DeviceToolbar.d.ts +6 -0
- package/dist/editor/page-viewer/DeviceToolbar.js +21 -0
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -0
- package/dist/editor/page-viewer/EditorForm.d.ts +8 -0
- package/dist/editor/page-viewer/EditorForm.js +257 -0
- package/dist/editor/page-viewer/EditorForm.js.map +1 -0
- package/dist/editor/page-viewer/EditorFormPopup.d.ts +11 -0
- package/dist/editor/page-viewer/EditorFormPopup.js +41 -0
- package/dist/editor/page-viewer/EditorFormPopup.js.map +1 -0
- package/dist/editor/page-viewer/MiniMap.d.ts +9 -0
- package/dist/editor/page-viewer/MiniMap.js +233 -0
- package/dist/editor/page-viewer/MiniMap.js.map +1 -0
- package/dist/editor/page-viewer/PageViewer.d.ts +10 -0
- package/dist/editor/page-viewer/PageViewer.js +67 -0
- package/dist/editor/page-viewer/PageViewer.js.map +1 -0
- package/dist/editor/page-viewer/PageViewerFrame.d.ts +11 -0
- package/dist/editor/page-viewer/PageViewerFrame.js +857 -0
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.d.ts +3 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +339 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -0
- package/dist/editor/page-viewer/pageViewContext.d.ts +40 -0
- package/dist/editor/page-viewer/pageViewContext.js +156 -0
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -0
- package/dist/editor/pageModel.d.ts +222 -0
- package/dist/editor/pageModel.js +2 -0
- package/dist/editor/pageModel.js.map +1 -0
- package/dist/editor/picture-shared.d.ts +16 -0
- package/dist/editor/picture-shared.js +25 -0
- package/dist/editor/picture-shared.js.map +1 -0
- package/dist/editor/reviews/Comment.d.ts +8 -0
- package/dist/editor/reviews/Comment.js +162 -0
- package/dist/editor/reviews/Comment.js.map +1 -0
- package/dist/editor/reviews/CommentDisplayPopover.d.ts +9 -0
- package/dist/editor/reviews/CommentDisplayPopover.js +104 -0
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -0
- package/dist/editor/reviews/CommentEditor.d.ts +20 -0
- package/dist/editor/reviews/CommentEditor.js +57 -0
- package/dist/editor/reviews/CommentEditor.js.map +1 -0
- package/dist/editor/reviews/CommentPopover.d.ts +22 -0
- package/dist/editor/reviews/CommentPopover.js +145 -0
- package/dist/editor/reviews/CommentPopover.js.map +1 -0
- package/dist/editor/reviews/CommentView.d.ts +21 -0
- package/dist/editor/reviews/CommentView.js +52 -0
- package/dist/editor/reviews/CommentView.js.map +1 -0
- package/dist/editor/reviews/Comments.d.ts +3 -0
- package/dist/editor/reviews/Comments.js +118 -0
- package/dist/editor/reviews/Comments.js.map +1 -0
- package/dist/editor/reviews/DiffView.d.ts +17 -0
- package/dist/editor/reviews/DiffView.js +57 -0
- package/dist/editor/reviews/DiffView.js.map +1 -0
- package/dist/editor/reviews/PreviewInfo.d.ts +1 -0
- package/dist/editor/reviews/PreviewInfo.js +12 -0
- package/dist/editor/reviews/PreviewInfo.js.map +1 -0
- package/dist/editor/reviews/Reviews.d.ts +1 -0
- package/dist/editor/reviews/Reviews.js +186 -0
- package/dist/editor/reviews/Reviews.js.map +1 -0
- package/dist/editor/reviews/SuggestedEdit.d.ts +4 -0
- package/dist/editor/reviews/SuggestedEdit.js +200 -0
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -0
- package/dist/editor/reviews/SuggestionDisplayPopover.d.ts +9 -0
- package/dist/editor/reviews/SuggestionDisplayPopover.js +204 -0
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -0
- package/dist/editor/reviews/commentAi.d.ts +7 -0
- package/dist/editor/reviews/commentAi.js +100 -0
- package/dist/editor/reviews/commentAi.js.map +1 -0
- package/dist/editor/reviews/reviewCommands.d.ts +3 -0
- package/dist/editor/reviews/reviewCommands.js +38 -0
- package/dist/editor/reviews/reviewCommands.js.map +1 -0
- package/dist/editor/reviews/useReviews.d.ts +12 -0
- package/dist/editor/reviews/useReviews.js +43 -0
- package/dist/editor/reviews/useReviews.js.map +1 -0
- package/dist/editor/services/agentService.d.ts +306 -0
- package/dist/editor/services/agentService.js +627 -0
- package/dist/editor/services/agentService.js.map +1 -0
- package/dist/editor/services/aiService.d.ts +106 -0
- package/dist/editor/services/aiService.js +294 -0
- package/dist/editor/services/aiService.js.map +1 -0
- package/dist/editor/services/componentDesignerService.d.ts +46 -0
- package/dist/editor/services/componentDesignerService.js +72 -0
- package/dist/editor/services/componentDesignerService.js.map +1 -0
- package/dist/editor/services/contentService.d.ts +79 -0
- package/dist/editor/services/contentService.js +119 -0
- package/dist/editor/services/contentService.js.map +1 -0
- package/dist/editor/services/contextService.d.ts +26 -0
- package/dist/editor/services/contextService.js +103 -0
- package/dist/editor/services/contextService.js.map +1 -0
- package/dist/editor/services/editService.d.ts +66 -0
- package/dist/editor/services/editService.js +325 -0
- package/dist/editor/services/editService.js.map +1 -0
- package/dist/editor/services/favouritesService.d.ts +33 -0
- package/dist/editor/services/favouritesService.js +22 -0
- package/dist/editor/services/favouritesService.js.map +1 -0
- package/dist/editor/services/indexService.d.ts +13 -0
- package/dist/editor/services/indexService.js +45 -0
- package/dist/editor/services/indexService.js.map +1 -0
- package/dist/editor/services/reviewsService.d.ts +18 -0
- package/dist/editor/services/reviewsService.js +60 -0
- package/dist/editor/services/reviewsService.js.map +1 -0
- package/dist/editor/services/searchService.d.ts +17 -0
- package/dist/editor/services/searchService.js +25 -0
- package/dist/editor/services/searchService.js.map +1 -0
- package/dist/editor/services/serviceHelper.d.ts +10 -0
- package/dist/editor/services/serviceHelper.js +118 -0
- package/dist/editor/services/serviceHelper.js.map +1 -0
- package/dist/editor/services/setupService.d.ts +21 -0
- package/dist/editor/services/setupService.js +10 -0
- package/dist/editor/services/setupService.js.map +1 -0
- package/dist/editor/services/suggestedEditsService.d.ts +18 -0
- package/dist/editor/services/suggestedEditsService.js +36 -0
- package/dist/editor/services/suggestedEditsService.js.map +1 -0
- package/dist/editor/services/systemService.d.ts +4 -0
- package/dist/editor/services/systemService.js +11 -0
- package/dist/editor/services/systemService.js.map +1 -0
- package/dist/editor/services-server/api.d.ts +19 -0
- package/dist/editor/services-server/api.js +111 -0
- package/dist/editor/services-server/api.js.map +1 -0
- package/dist/editor/services-server/graphQL.d.ts +29 -0
- package/dist/editor/services-server/graphQL.js +53 -0
- package/dist/editor/services-server/graphQL.js.map +1 -0
- package/dist/editor/sidebar/Completions.d.ts +1 -0
- package/dist/editor/sidebar/Completions.js +55 -0
- package/dist/editor/sidebar/Completions.js.map +1 -0
- package/dist/editor/sidebar/ComponentPalette.d.ts +1 -0
- package/dist/editor/sidebar/ComponentPalette.js +86 -0
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -0
- package/dist/editor/sidebar/ComponentTree.d.ts +1 -0
- package/dist/editor/sidebar/ComponentTree.js +691 -0
- package/dist/editor/sidebar/ComponentTree.js.map +1 -0
- package/dist/editor/sidebar/Debug.d.ts +1 -0
- package/dist/editor/sidebar/Debug.js +71 -0
- package/dist/editor/sidebar/Debug.js.map +1 -0
- package/dist/editor/sidebar/DictionaryEditor.d.ts +1 -0
- package/dist/editor/sidebar/DictionaryEditor.js +158 -0
- package/dist/editor/sidebar/DictionaryEditor.js.map +1 -0
- package/dist/editor/sidebar/Divider.d.ts +6 -0
- package/dist/editor/sidebar/Divider.js +6 -0
- package/dist/editor/sidebar/Divider.js.map +1 -0
- package/dist/editor/sidebar/EditHistory.d.ts +1 -0
- package/dist/editor/sidebar/EditHistory.js +78 -0
- package/dist/editor/sidebar/EditHistory.js.map +1 -0
- package/dist/editor/sidebar/GraphQL.d.ts +2 -0
- package/dist/editor/sidebar/GraphQL.js +234 -0
- package/dist/editor/sidebar/GraphQL.js.map +1 -0
- package/dist/editor/sidebar/Insert.d.ts +1 -0
- package/dist/editor/sidebar/Insert.js +22 -0
- package/dist/editor/sidebar/Insert.js.map +1 -0
- package/dist/editor/sidebar/LeftToolbar.d.ts +1 -0
- package/dist/editor/sidebar/LeftToolbar.js +16 -0
- package/dist/editor/sidebar/LeftToolbar.js.map +1 -0
- package/dist/editor/sidebar/MainContentTree.d.ts +5 -0
- package/dist/editor/sidebar/MainContentTree.js +70 -0
- package/dist/editor/sidebar/MainContentTree.js.map +1 -0
- package/dist/editor/sidebar/Performance.d.ts +1 -0
- package/dist/editor/sidebar/Performance.js +32 -0
- package/dist/editor/sidebar/Performance.js.map +1 -0
- package/dist/editor/sidebar/SEOInfo.d.ts +1 -0
- package/dist/editor/sidebar/SEOInfo.js +158 -0
- package/dist/editor/sidebar/SEOInfo.js.map +1 -0
- package/dist/editor/sidebar/Sessions.d.ts +1 -0
- package/dist/editor/sidebar/Sessions.js +29 -0
- package/dist/editor/sidebar/Sessions.js.map +1 -0
- package/dist/editor/sidebar/Sidebar.d.ts +1 -0
- package/dist/editor/sidebar/Sidebar.js +13 -0
- package/dist/editor/sidebar/Sidebar.js.map +1 -0
- package/dist/editor/sidebar/SidebarView.d.ts +10 -0
- package/dist/editor/sidebar/SidebarView.js +40 -0
- package/dist/editor/sidebar/SidebarView.js.map +1 -0
- package/dist/editor/sidebar/Validation.d.ts +1 -0
- package/dist/editor/sidebar/Validation.js +51 -0
- package/dist/editor/sidebar/Validation.js.map +1 -0
- package/dist/editor/sidebar/ViewSelector.d.ts +4 -0
- package/dist/editor/sidebar/ViewSelector.js +191 -0
- package/dist/editor/sidebar/ViewSelector.js.map +1 -0
- package/dist/editor/sidebar/Workbox.d.ts +1 -0
- package/dist/editor/sidebar/Workbox.js +79 -0
- package/dist/editor/sidebar/Workbox.js.map +1 -0
- package/dist/editor/ui/CenteredMessage.d.ts +3 -0
- package/dist/editor/ui/CenteredMessage.js +5 -0
- package/dist/editor/ui/CenteredMessage.js.map +1 -0
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.d.ts +10 -0
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +38 -0
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -0
- package/dist/editor/ui/DialogButtons.d.ts +3 -0
- package/dist/editor/ui/DialogButtons.js +5 -0
- package/dist/editor/ui/DialogButtons.js.map +1 -0
- package/dist/editor/ui/DragPreview.d.ts +14 -0
- package/dist/editor/ui/DragPreview.js +33 -0
- package/dist/editor/ui/DragPreview.js.map +1 -0
- package/dist/editor/ui/Icons.d.ts +74 -0
- package/dist/editor/ui/Icons.js +106 -0
- package/dist/editor/ui/Icons.js.map +1 -0
- package/dist/editor/ui/ItemList.d.ts +16 -0
- package/dist/editor/ui/ItemList.js +19 -0
- package/dist/editor/ui/ItemList.js.map +1 -0
- package/dist/editor/ui/ItemNameDialogNew.d.ts +10 -0
- package/dist/editor/ui/ItemNameDialogNew.js +68 -0
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -0
- package/dist/editor/ui/ItemSearch.d.ts +25 -0
- package/dist/editor/ui/ItemSearch.js +92 -0
- package/dist/editor/ui/ItemSearch.js.map +1 -0
- package/dist/editor/ui/PerfectTree.d.ts +78 -0
- package/dist/editor/ui/PerfectTree.js +858 -0
- package/dist/editor/ui/PerfectTree.js.map +1 -0
- package/dist/editor/ui/Section.d.ts +4 -0
- package/dist/editor/ui/Section.js +12 -0
- package/dist/editor/ui/Section.js.map +1 -0
- package/dist/editor/ui/SimpleIconButton.d.ts +14 -0
- package/dist/editor/ui/SimpleIconButton.js +15 -0
- package/dist/editor/ui/SimpleIconButton.js.map +1 -0
- package/dist/editor/ui/SimpleMenu.d.ts +6 -0
- package/dist/editor/ui/SimpleMenu.js +7 -0
- package/dist/editor/ui/SimpleMenu.js.map +1 -0
- package/dist/editor/ui/SimpleTable.d.ts +14 -0
- package/dist/editor/ui/SimpleTable.js +9 -0
- package/dist/editor/ui/SimpleTable.js.map +1 -0
- package/dist/editor/ui/SimpleTabs.d.ts +15 -0
- package/dist/editor/ui/SimpleTabs.js +20 -0
- package/dist/editor/ui/SimpleTabs.js.map +1 -0
- package/dist/editor/ui/SimpleToolbar.d.ts +3 -0
- package/dist/editor/ui/SimpleToolbar.js +5 -0
- package/dist/editor/ui/SimpleToolbar.js.map +1 -0
- package/dist/editor/ui/Spinner.d.ts +4 -0
- package/dist/editor/ui/Spinner.js +18 -0
- package/dist/editor/ui/Spinner.js.map +1 -0
- package/dist/editor/ui/Splitter.d.ts +22 -0
- package/dist/editor/ui/Splitter.js +443 -0
- package/dist/editor/ui/Splitter.js.map +1 -0
- package/dist/editor/ui/TemplateSelectorDialog.d.ts +8 -0
- package/dist/editor/ui/TemplateSelectorDialog.js +61 -0
- package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -0
- package/dist/editor/ui/Toolbar.d.ts +3 -0
- package/dist/editor/ui/Toolbar.js +5 -0
- package/dist/editor/ui/Toolbar.js.map +1 -0
- package/dist/editor/utils/id-helper.d.ts +1 -0
- package/dist/editor/utils/id-helper.js +5 -0
- package/dist/editor/utils/id-helper.js.map +1 -0
- package/dist/editor/utils/insertOptions.d.ts +3 -0
- package/dist/editor/utils/insertOptions.js +43 -0
- package/dist/editor/utils/insertOptions.js.map +1 -0
- package/dist/editor/utils/itemConverters.d.ts +3 -0
- package/dist/editor/utils/itemConverters.js +53 -0
- package/dist/editor/utils/itemConverters.js.map +1 -0
- package/dist/editor/utils/itemutils.d.ts +3 -0
- package/dist/editor/utils/itemutils.js +24 -0
- package/dist/editor/utils/itemutils.js.map +1 -0
- package/dist/editor/utils/jsonCleaner.d.ts +8 -0
- package/dist/editor/utils/jsonCleaner.js +76 -0
- package/dist/editor/utils/jsonCleaner.js.map +1 -0
- package/dist/editor/utils/keyboardNavigation.d.ts +34 -0
- package/dist/editor/utils/keyboardNavigation.js +237 -0
- package/dist/editor/utils/keyboardNavigation.js.map +1 -0
- package/dist/editor/utils/urlUtils.d.ts +9 -0
- package/dist/editor/utils/urlUtils.js +25 -0
- package/dist/editor/utils/urlUtils.js.map +1 -0
- package/dist/editor/utils/useMemoDebug.d.ts +1 -0
- package/dist/editor/utils/useMemoDebug.js +18 -0
- package/dist/editor/utils/useMemoDebug.js.map +1 -0
- package/dist/editor/utils.d.ts +65 -0
- package/dist/editor/utils.js +468 -0
- package/dist/editor/utils.js.map +1 -0
- package/dist/editor/views/CompareView.d.ts +1 -0
- package/dist/editor/views/CompareView.js +148 -0
- package/dist/editor/views/CompareView.js.map +1 -0
- package/dist/editor/views/EditView.d.ts +1 -0
- package/dist/editor/views/EditView.js +15 -0
- package/dist/editor/views/EditView.js.map +1 -0
- package/dist/editor/views/ItemEditor.d.ts +7 -0
- package/dist/editor/views/ItemEditor.js +29 -0
- package/dist/editor/views/ItemEditor.js.map +1 -0
- package/dist/editor/views/MediaFolderEditView.d.ts +4 -0
- package/dist/editor/views/MediaFolderEditView.js +40 -0
- package/dist/editor/views/MediaFolderEditView.js.map +1 -0
- package/dist/editor/views/ParheliaView.d.ts +5 -0
- package/dist/editor/views/ParheliaView.js +136 -0
- package/dist/editor/views/ParheliaView.js.map +1 -0
- package/dist/editor/views/SingleEditView.d.ts +9 -0
- package/dist/editor/views/SingleEditView.js +33 -0
- package/dist/editor/views/SingleEditView.js.map +1 -0
- package/dist/images/bg-shape-black.webp +0 -0
- package/dist/images/wizard-bg.png +0 -0
- package/dist/images/wizard-tour.png +0 -0
- package/dist/images/wizard.png +0 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/safelist.d.ts +1 -0
- package/dist/lib/safelist.js +5 -0
- package/dist/lib/safelist.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/revision.d.ts +2 -0
- package/dist/revision.js +3 -0
- package/dist/revision.js.map +1 -0
- package/dist/splash-screen/ModernSplashScreen.d.ts +8 -0
- package/dist/splash-screen/ModernSplashScreen.js +92 -0
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -0
- package/dist/splash-screen/NewPage.d.ts +3 -0
- package/dist/splash-screen/NewPage.js +145 -0
- package/dist/splash-screen/NewPage.js.map +1 -0
- package/dist/splash-screen/OpenPage.d.ts +1 -0
- package/dist/splash-screen/OpenPage.js +56 -0
- package/dist/splash-screen/OpenPage.js.map +1 -0
- package/dist/splash-screen/ParheliaAssistantChat.d.ts +8 -0
- package/dist/splash-screen/ParheliaAssistantChat.js +155 -0
- package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -0
- package/dist/splash-screen/RecentAgents.d.ts +7 -0
- package/dist/splash-screen/RecentAgents.js +76 -0
- package/dist/splash-screen/RecentAgents.js.map +1 -0
- package/dist/splash-screen/RecentPages.d.ts +1 -0
- package/dist/splash-screen/RecentPages.js +47 -0
- package/dist/splash-screen/RecentPages.js.map +1 -0
- package/dist/splash-screen/SectionHeadline.d.ts +4 -0
- package/dist/splash-screen/SectionHeadline.js +7 -0
- package/dist/splash-screen/SectionHeadline.js.map +1 -0
- package/dist/styles.css +5914 -0
- package/dist/tour/Tour.d.ts +3 -0
- package/dist/tour/Tour.js +426 -0
- package/dist/tour/Tour.js.map +1 -0
- package/dist/tour/default-tour.d.ts +9 -0
- package/dist/tour/default-tour.js +302 -0
- package/dist/tour/default-tour.js.map +1 -0
- package/dist/tour/preview-tour.d.ts +2 -0
- package/dist/tour/preview-tour.js +93 -0
- package/dist/tour/preview-tour.js.map +1 -0
- package/dist/types.d.ts +396 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/images/bg-shape-black.webp +0 -0
- package/images/wizard-bg.png +0 -0
- package/images/wizard-tour.png +0 -0
- package/images/wizard.png +0 -0
- package/package.json +93 -0
|
@@ -0,0 +1,2634 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo, ViewTransition, } from "react";
|
|
3
|
+
import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, } from "lucide-react";
|
|
4
|
+
import { DancingDots } from "./DancingDots";
|
|
5
|
+
import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, cancelAgent, canonicalizeAgentMetadata, } from "../services/agentService";
|
|
6
|
+
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
7
|
+
import { Textarea } from "../../components/ui/textarea";
|
|
8
|
+
import { Button } from "../../components/ui/button";
|
|
9
|
+
import { PlaceholderInput } from "../../components/ui/PlaceholderInput";
|
|
10
|
+
import { AiResponseMessage } from "./AiResponseMessage";
|
|
11
|
+
import { AgentCostDisplay } from "./AgentCostDisplay";
|
|
12
|
+
import { ContextInfoBar } from "./ContextInfoBar";
|
|
13
|
+
import { getComponentById } from "../componentTreeHelper";
|
|
14
|
+
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
15
|
+
import { SecretAgentIcon } from "../ui/Icons";
|
|
16
|
+
import { formatTime } from "../utils";
|
|
17
|
+
import { Tooltip, TooltipTrigger, TooltipContent, } from "../../components/ui/tooltip";
|
|
18
|
+
// Simple user message component
|
|
19
|
+
const UserMessage = ({ message }) => {
|
|
20
|
+
return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(User, { className: "h-6 w-6 text-blue-600", strokeWidth: 1 }) }), _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-xs font-medium text-gray-900", children: "You" }), message.createdDate && (_jsx("span", { className: "text-xs text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose-sm max-w-none text-xs text-gray-700 select-text", children: message.content })] })] }));
|
|
21
|
+
};
|
|
22
|
+
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
23
|
+
const extractPartialTodos = (jsonText) => {
|
|
24
|
+
// First try to parse complete JSON
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(jsonText);
|
|
27
|
+
return Array.isArray(parsed) ? parsed : parsed?.items || [];
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
// If JSON is incomplete, try to extract whatever todo items we can find
|
|
31
|
+
const items = [];
|
|
32
|
+
// Look for individual todo objects in the partial JSON
|
|
33
|
+
// Match patterns like: { "text": "...", "done": false, "note": "..." }
|
|
34
|
+
// Handle various field orderings (text can be anywhere in the object)
|
|
35
|
+
const textPattern = /"text"\s*:\s*"([^"]+)"/g;
|
|
36
|
+
const textMatches = [];
|
|
37
|
+
let textMatch;
|
|
38
|
+
while ((textMatch = textPattern.exec(jsonText)) !== null) {
|
|
39
|
+
if (textMatch[1]) {
|
|
40
|
+
textMatches.push({
|
|
41
|
+
text: textMatch[1],
|
|
42
|
+
startIdx: textMatch.index,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// For each text field found, try to find the enclosing object
|
|
47
|
+
for (const { text, startIdx } of textMatches) {
|
|
48
|
+
// Find the opening brace before this text field
|
|
49
|
+
let openBrace = -1;
|
|
50
|
+
for (let i = startIdx - 1; i >= 0; i--) {
|
|
51
|
+
if (jsonText[i] === "{") {
|
|
52
|
+
openBrace = i;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (jsonText[i] === "}")
|
|
56
|
+
break; // Hit another object's end
|
|
57
|
+
}
|
|
58
|
+
if (openBrace === -1)
|
|
59
|
+
continue;
|
|
60
|
+
// Find the closing brace after this text field
|
|
61
|
+
let closeBrace = -1;
|
|
62
|
+
let depth = 0;
|
|
63
|
+
for (let i = openBrace; i < jsonText.length; i++) {
|
|
64
|
+
if (jsonText[i] === "{")
|
|
65
|
+
depth++;
|
|
66
|
+
if (jsonText[i] === "}") {
|
|
67
|
+
depth--;
|
|
68
|
+
if (depth === 0) {
|
|
69
|
+
closeBrace = i;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Extract the object and try to parse it
|
|
75
|
+
const objStr = closeBrace !== -1
|
|
76
|
+
? jsonText.substring(openBrace, closeBrace + 1)
|
|
77
|
+
: jsonText.substring(openBrace) + "}"; // Try to close incomplete object
|
|
78
|
+
try {
|
|
79
|
+
const obj = JSON.parse(objStr);
|
|
80
|
+
if (obj.text) {
|
|
81
|
+
items.push({
|
|
82
|
+
text: obj.text,
|
|
83
|
+
done: obj.done === true,
|
|
84
|
+
note: obj.note || undefined,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
// Skip malformed objects
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Also try to extract from partial objects at the end
|
|
93
|
+
// Look for the last opening brace and try to parse up to where we have valid content
|
|
94
|
+
const lines = jsonText.split("\n");
|
|
95
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
96
|
+
const partialJson = lines.slice(0, i + 1).join("\n");
|
|
97
|
+
// Try to close any open braces/brackets
|
|
98
|
+
let testJson = partialJson;
|
|
99
|
+
const openBraces = (testJson.match(/\{/g) || []).length;
|
|
100
|
+
const closeBraces = (testJson.match(/\}/g) || []).length;
|
|
101
|
+
const openBrackets = (testJson.match(/\[/g) || []).length;
|
|
102
|
+
const closeBrackets = (testJson.match(/\]/g) || []).length;
|
|
103
|
+
// Add missing closing characters
|
|
104
|
+
testJson += "]".repeat(openBrackets - closeBrackets);
|
|
105
|
+
testJson += "}".repeat(openBraces - closeBraces);
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(testJson);
|
|
108
|
+
const partialItems = Array.isArray(parsed)
|
|
109
|
+
? parsed
|
|
110
|
+
: parsed?.items || [];
|
|
111
|
+
if (partialItems.length > items.length) {
|
|
112
|
+
return partialItems;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return items;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const extractTodosFromMessages = (messages) => {
|
|
123
|
+
const todos = [];
|
|
124
|
+
const fencedTodoToken = "```todo_list";
|
|
125
|
+
const plainTodoToken = "todo_list";
|
|
126
|
+
for (const message of messages) {
|
|
127
|
+
if (message.role !== "assistant" || !message.content)
|
|
128
|
+
continue;
|
|
129
|
+
const content = message.content;
|
|
130
|
+
let cursor = 0;
|
|
131
|
+
while (cursor < content.length) {
|
|
132
|
+
const nextFenced = content.indexOf(fencedTodoToken, cursor);
|
|
133
|
+
const nextPlain = content.indexOf(plainTodoToken, cursor);
|
|
134
|
+
let todoStart = -1;
|
|
135
|
+
let isFenced = false;
|
|
136
|
+
if (nextFenced !== -1 && (nextPlain === -1 || nextFenced < nextPlain)) {
|
|
137
|
+
todoStart = nextFenced;
|
|
138
|
+
isFenced = true;
|
|
139
|
+
}
|
|
140
|
+
else if (nextPlain !== -1) {
|
|
141
|
+
// Check if it's at line start
|
|
142
|
+
const before = nextPlain > 0 ? content[nextPlain - 1] : "\n";
|
|
143
|
+
if (before === "\n" || before === "\r" || nextPlain === 0) {
|
|
144
|
+
todoStart = nextPlain;
|
|
145
|
+
isFenced = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (todoStart === -1)
|
|
149
|
+
break;
|
|
150
|
+
try {
|
|
151
|
+
let jsonText = "";
|
|
152
|
+
let isComplete = true;
|
|
153
|
+
if (isFenced) {
|
|
154
|
+
const afterToken = todoStart + fencedTodoToken.length;
|
|
155
|
+
const closePos = content.indexOf("```", afterToken);
|
|
156
|
+
if (closePos === -1) {
|
|
157
|
+
// Incomplete fenced block - extract what we have so far
|
|
158
|
+
jsonText = content.slice(afterToken).trim();
|
|
159
|
+
isComplete = false;
|
|
160
|
+
cursor = content.length; // Process till end
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
jsonText = content.slice(afterToken, closePos).trim();
|
|
164
|
+
cursor = closePos + 3;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const afterToken = todoStart + plainTodoToken.length;
|
|
169
|
+
const braceStart = content.indexOf("{", afterToken);
|
|
170
|
+
if (braceStart === -1)
|
|
171
|
+
break;
|
|
172
|
+
let depth = 0;
|
|
173
|
+
let braceEnd = -1;
|
|
174
|
+
for (let i = braceStart; i < content.length; i++) {
|
|
175
|
+
if (content[i] === "{")
|
|
176
|
+
depth++;
|
|
177
|
+
if (content[i] === "}") {
|
|
178
|
+
depth--;
|
|
179
|
+
if (depth === 0) {
|
|
180
|
+
braceEnd = i;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (braceEnd === -1) {
|
|
186
|
+
// Incomplete JSON - extract what we have
|
|
187
|
+
jsonText = content.slice(braceStart).trim();
|
|
188
|
+
isComplete = false;
|
|
189
|
+
cursor = content.length;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
jsonText = content.slice(braceStart, braceEnd + 1).trim();
|
|
193
|
+
cursor = braceEnd + 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Use the partial extraction helper for incomplete JSON
|
|
197
|
+
const todoItems = isComplete
|
|
198
|
+
? (() => {
|
|
199
|
+
try {
|
|
200
|
+
const parsed = JSON.parse(jsonText);
|
|
201
|
+
return Array.isArray(parsed) ? parsed : parsed?.items || [];
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
})()
|
|
207
|
+
: extractPartialTodos(jsonText);
|
|
208
|
+
const title = (() => {
|
|
209
|
+
try {
|
|
210
|
+
const parsed = JSON.parse(jsonText);
|
|
211
|
+
return Array.isArray(parsed) ? undefined : parsed?.title;
|
|
212
|
+
}
|
|
213
|
+
catch (e) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
})();
|
|
217
|
+
todoItems.forEach((item) => {
|
|
218
|
+
if (!item)
|
|
219
|
+
return;
|
|
220
|
+
const text = item.text ||
|
|
221
|
+
item.content ||
|
|
222
|
+
item.label ||
|
|
223
|
+
String(item.task || item.title || "");
|
|
224
|
+
if (!text)
|
|
225
|
+
return;
|
|
226
|
+
todos.push({
|
|
227
|
+
id: item.id,
|
|
228
|
+
text,
|
|
229
|
+
done: !!(item.done ??
|
|
230
|
+
item.completed ??
|
|
231
|
+
item.checked ??
|
|
232
|
+
item.status === "completed"),
|
|
233
|
+
note: item.note || item.description,
|
|
234
|
+
messageId: message.id,
|
|
235
|
+
sourceTitle: title,
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
cursor++;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return todos;
|
|
246
|
+
};
|
|
247
|
+
// TodoListPanel component
|
|
248
|
+
const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
249
|
+
const [isExpanded, setIsExpanded] = useState(true);
|
|
250
|
+
const todos = useMemo(() => {
|
|
251
|
+
// First try to get todos from agent metadata (real-time updates)
|
|
252
|
+
const metadataTodos = (() => {
|
|
253
|
+
try {
|
|
254
|
+
const todoList = agentMetadata?.additionalData?.todoList;
|
|
255
|
+
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
256
|
+
return todoList.items
|
|
257
|
+
.map((item, idx) => ({
|
|
258
|
+
id: item.id || `metadata-${idx}`,
|
|
259
|
+
text: item.text ||
|
|
260
|
+
item.label ||
|
|
261
|
+
String(item.task || item.title || ""),
|
|
262
|
+
done: !!(item.done ?? item.completed ?? item.checked),
|
|
263
|
+
note: item.note || item.description,
|
|
264
|
+
messageId: undefined,
|
|
265
|
+
sourceTitle: todoList.title,
|
|
266
|
+
}))
|
|
267
|
+
.filter((item) => item.text);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (e) {
|
|
271
|
+
console.error("📋 Error extracting todos from metadata:", e);
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
})();
|
|
275
|
+
// If we have metadata todos, use them; otherwise extract from messages
|
|
276
|
+
if (metadataTodos && metadataTodos.length > 0) {
|
|
277
|
+
return metadataTodos;
|
|
278
|
+
}
|
|
279
|
+
return extractTodosFromMessages(messages);
|
|
280
|
+
}, [messages, agentMetadata]);
|
|
281
|
+
// Check if there's an active streaming message with incomplete todo content
|
|
282
|
+
const isUpdating = useMemo(() => {
|
|
283
|
+
return messages.some((msg) => {
|
|
284
|
+
if (msg.role !== "assistant" || msg.isCompleted)
|
|
285
|
+
return false;
|
|
286
|
+
const content = msg.content || "";
|
|
287
|
+
// Check for incomplete fenced todo blocks
|
|
288
|
+
const fencedStart = content.indexOf("```todo_list");
|
|
289
|
+
if (fencedStart !== -1) {
|
|
290
|
+
const afterStart = fencedStart + "```todo_list".length;
|
|
291
|
+
const closePos = content.indexOf("```", afterStart);
|
|
292
|
+
if (closePos === -1) {
|
|
293
|
+
// Incomplete fenced block
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Check for incomplete plain todo blocks
|
|
298
|
+
const plainStart = content.indexOf("todo_list");
|
|
299
|
+
if (plainStart !== -1 && plainStart !== fencedStart) {
|
|
300
|
+
const before = plainStart > 0 ? content[plainStart - 1] : "\n";
|
|
301
|
+
if (before === "\n" || before === "\r" || plainStart === 0) {
|
|
302
|
+
const braceStart = content.indexOf("{", plainStart);
|
|
303
|
+
if (braceStart !== -1) {
|
|
304
|
+
let depth = 0;
|
|
305
|
+
let braceEnd = -1;
|
|
306
|
+
for (let i = braceStart; i < content.length; i++) {
|
|
307
|
+
if (content[i] === "{")
|
|
308
|
+
depth++;
|
|
309
|
+
if (content[i] === "}") {
|
|
310
|
+
depth--;
|
|
311
|
+
if (depth === 0) {
|
|
312
|
+
braceEnd = i;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (braceEnd === -1) {
|
|
318
|
+
// Incomplete plain block
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
});
|
|
326
|
+
}, [messages]);
|
|
327
|
+
if (todos.length === 0 && !isUpdating)
|
|
328
|
+
return null;
|
|
329
|
+
const completedCount = todos.filter((t) => t.done).length;
|
|
330
|
+
const totalCount = todos.length;
|
|
331
|
+
return (_jsxs("div", { className: "border-t border-gray-200 bg-gray-50", children: [_jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: "flex w-full cursor-pointer items-center justify-between px-4 py-2 text-left transition-colors hover:bg-gray-100", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ListTodo, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 }), _jsx("span", { className: "text-xs font-medium text-gray-700", children: "Todo List" }), isUpdating ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-blue-600", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin", strokeWidth: 1 }), "Updating..."] })) : (_jsxs("span", { className: "text-xs text-gray-500", children: [completedCount, "/", totalCount, " completed"] }))] }), isExpanded ? (_jsx(ChevronUp, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 })) : (_jsx(ChevronDown, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 }))] }), isExpanded && (_jsxs("div", { className: "max-h-64 overflow-y-auto px-4 pb-3", children: [todos.length > 0 && (_jsx("div", { className: "space-y-1.5", children: todos.map((todo, idx) => (_jsxs("div", { className: "flex items-start gap-2 rounded bg-white p-2 text-xs", children: [_jsx("div", { className: "flex-shrink-0 pt-0.5", children: todo.done ? (_jsx("div", { className: "flex h-4 w-4 items-center justify-center rounded border-2 border-green-500 bg-green-500", children: _jsx("svg", { className: "h-3 w-3 text-white", fill: "none", strokeWidth: 2, stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }) })) : (_jsx("div", { className: "h-4 w-4 rounded border-2 border-gray-300" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: `${todo.done
|
|
332
|
+
? "text-gray-500 line-through"
|
|
333
|
+
: "text-gray-900"}`, children: todo.text }), todo.note && (_jsx("div", { className: "mt-0.5 text-xs text-gray-500", children: todo.note }))] })] }, todo.id || `${todo.messageId}-${idx}`))) })), isUpdating && (_jsxs("div", { className: `flex items-center gap-2 rounded px-3 py-2 text-xs ${todos.length > 0
|
|
334
|
+
? "mt-2 bg-blue-50 text-blue-700"
|
|
335
|
+
: "justify-center bg-white text-gray-500"}`, children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin", strokeWidth: 1 }), _jsx("span", { children: todos.length > 0
|
|
336
|
+
? "Updating todo list..."
|
|
337
|
+
: "Loading todo list..." })] }))] }))] }));
|
|
338
|
+
};
|
|
339
|
+
const groupConsecutiveMessages = (agentMessages) => {
|
|
340
|
+
// Work directly with the messages array - streaming messages are identified by their properties
|
|
341
|
+
const allMessages = agentMessages;
|
|
342
|
+
const groups = [];
|
|
343
|
+
let currentAssistantGroup = [];
|
|
344
|
+
for (const message of allMessages) {
|
|
345
|
+
if (message.role === "user") {
|
|
346
|
+
// Finish any current assistant group
|
|
347
|
+
if (currentAssistantGroup.length > 0) {
|
|
348
|
+
groups.push({
|
|
349
|
+
type: "assistant-group",
|
|
350
|
+
messages: currentAssistantGroup,
|
|
351
|
+
});
|
|
352
|
+
currentAssistantGroup = [];
|
|
353
|
+
}
|
|
354
|
+
// Add user message
|
|
355
|
+
groups.push({ type: "user", messages: [message] });
|
|
356
|
+
}
|
|
357
|
+
else if (message.role === "assistant") {
|
|
358
|
+
// Add to current assistant group
|
|
359
|
+
currentAssistantGroup.push(message);
|
|
360
|
+
}
|
|
361
|
+
// Skip tool messages as they're handled within assistant messages
|
|
362
|
+
}
|
|
363
|
+
// Add any remaining assistant group
|
|
364
|
+
if (currentAssistantGroup.length > 0) {
|
|
365
|
+
groups.push({ type: "assistant-group", messages: currentAssistantGroup });
|
|
366
|
+
}
|
|
367
|
+
return groups;
|
|
368
|
+
};
|
|
369
|
+
// Merge messages from DB and local state with ID-based deduplication
|
|
370
|
+
const mergeMessagesById = (dbMessages, localMessages) => {
|
|
371
|
+
const messageMap = new Map();
|
|
372
|
+
// Normalize ID key (lowercase) to avoid duplicates caused by casing differences
|
|
373
|
+
const keyOf = (id) => (id ? id.toLowerCase() : "");
|
|
374
|
+
// First, add all DB messages (source of truth for completed messages)
|
|
375
|
+
dbMessages.forEach((msg) => {
|
|
376
|
+
if (msg.id)
|
|
377
|
+
messageMap.set(keyOf(msg.id), msg);
|
|
378
|
+
});
|
|
379
|
+
// Then merge local messages (preserve streaming state, prefer longer content)
|
|
380
|
+
localMessages.forEach((localMsg) => {
|
|
381
|
+
if (!localMsg.id)
|
|
382
|
+
return;
|
|
383
|
+
const key = keyOf(localMsg.id);
|
|
384
|
+
const existingMsg = messageMap.get(key);
|
|
385
|
+
if (!existingMsg) {
|
|
386
|
+
// New message only in local state (e.g., streaming)
|
|
387
|
+
messageMap.set(key, localMsg);
|
|
388
|
+
}
|
|
389
|
+
else if (!localMsg.isCompleted && localMsg.messageType === "streaming") {
|
|
390
|
+
// Keep streaming version if more recent/longer
|
|
391
|
+
if (localMsg.content.length > existingMsg.content.length) {
|
|
392
|
+
messageMap.set(key, localMsg);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Otherwise, keep the DB version (completed messages from DB are authoritative)
|
|
396
|
+
});
|
|
397
|
+
// Sort by messageIndex or createdDate to maintain order
|
|
398
|
+
// Ignore messageIndex if it's -1 (unassigned/streaming)
|
|
399
|
+
return Array.from(messageMap.values()).sort((a, b) => {
|
|
400
|
+
const aIndex = a.messageIndex === -1 ? Infinity : a.messageIndex;
|
|
401
|
+
const bIndex = b.messageIndex === -1 ? Infinity : b.messageIndex;
|
|
402
|
+
if (aIndex !== bIndex) {
|
|
403
|
+
return aIndex - bIndex;
|
|
404
|
+
}
|
|
405
|
+
return (new Date(a.createdDate || 0).getTime() -
|
|
406
|
+
new Date(b.createdDate || 0).getTime());
|
|
407
|
+
});
|
|
408
|
+
};
|
|
409
|
+
// Calculate total token usage and cost data from agent messages
|
|
410
|
+
const calculateTotalTokens = (messages) => {
|
|
411
|
+
const totals = messages.reduce((acc, message) => {
|
|
412
|
+
return {
|
|
413
|
+
input: acc.input + (message.inputTokens || 0),
|
|
414
|
+
output: acc.output + (message.outputTokens || 0),
|
|
415
|
+
cached: acc.cached + (message.cachedInputTokens || 0),
|
|
416
|
+
cacheWrite: acc.cacheWrite,
|
|
417
|
+
inputCost: acc.inputCost + (message.inputTokenCost || 0),
|
|
418
|
+
outputCost: acc.outputCost + (message.outputTokenCost || 0),
|
|
419
|
+
cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
|
|
420
|
+
cacheWriteCost: acc.cacheWriteCost,
|
|
421
|
+
totalCost: acc.totalCost + (message.totalCost || 0),
|
|
422
|
+
};
|
|
423
|
+
}, {
|
|
424
|
+
input: 0,
|
|
425
|
+
output: 0,
|
|
426
|
+
cached: 0,
|
|
427
|
+
cacheWrite: 0,
|
|
428
|
+
inputCost: 0,
|
|
429
|
+
outputCost: 0,
|
|
430
|
+
cachedCost: 0,
|
|
431
|
+
cacheWriteCost: 0,
|
|
432
|
+
totalCost: 0,
|
|
433
|
+
});
|
|
434
|
+
return totals;
|
|
435
|
+
};
|
|
436
|
+
// Convert agent messages to AI terminal format for a response group
|
|
437
|
+
const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
438
|
+
return agentMessages.map((agentMessage) => {
|
|
439
|
+
const message = {
|
|
440
|
+
id: agentMessage.id,
|
|
441
|
+
content: agentMessage.content,
|
|
442
|
+
formattedContent: agentMessage.content
|
|
443
|
+
?.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>")
|
|
444
|
+
?.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>")
|
|
445
|
+
?.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>")
|
|
446
|
+
?.replace(/^###\s+(.+)$/gm, "<h3>$1</h3>")
|
|
447
|
+
?.replace(/^##\s+(.+)$/gm, "<h2>$1</h2>")
|
|
448
|
+
?.replace(/^#\s+(.+)$/gm, "<h1>$1</h1>")
|
|
449
|
+
?.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
450
|
+
?.replace(/\n/g, "<br/>"),
|
|
451
|
+
name: agentMessage.name,
|
|
452
|
+
role: agentMessage.role,
|
|
453
|
+
createdDate: agentMessage.createdDate,
|
|
454
|
+
tool_calls: agentMessage.toolCalls
|
|
455
|
+
? agentMessage.toolCalls.map((toolCall) => ({
|
|
456
|
+
id: toolCall.toolCallId,
|
|
457
|
+
displayName: toolCall.functionName,
|
|
458
|
+
function: {
|
|
459
|
+
name: toolCall.functionName,
|
|
460
|
+
arguments: toolCall.functionArguments,
|
|
461
|
+
result: toolCall.functionResult,
|
|
462
|
+
error: toolCall.functionError,
|
|
463
|
+
},
|
|
464
|
+
// Pass through approval info if present on the tool call
|
|
465
|
+
requiresApproval: toolCall.requiresApproval,
|
|
466
|
+
}))
|
|
467
|
+
: [],
|
|
468
|
+
};
|
|
469
|
+
if (agentMessage.toolCallId) {
|
|
470
|
+
message.tool_call_id = agentMessage.toolCallId;
|
|
471
|
+
}
|
|
472
|
+
return message;
|
|
473
|
+
});
|
|
474
|
+
};
|
|
475
|
+
// interface AgentTerminalProps {
|
|
476
|
+
// agentStub: Agent;
|
|
477
|
+
// }
|
|
478
|
+
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false, hideContext = false, hideBottomControls = false, hideGreeting = false, className, initialPrompt, }) {
|
|
479
|
+
const editContext = useEditContext();
|
|
480
|
+
const fieldsContext = useFieldsEditContext();
|
|
481
|
+
const [agent, setAgent] = useState(undefined);
|
|
482
|
+
const [messages, setMessages] = useState([]);
|
|
483
|
+
const [prompt, setPrompt] = useState("");
|
|
484
|
+
const [inputPlaceholder, setInputPlaceholder] = useState("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
485
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
486
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
487
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
488
|
+
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
489
|
+
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
490
|
+
// Generate a stable clientSessionId per component instance for stream deduplication
|
|
491
|
+
const clientSessionIdRef = useRef(null);
|
|
492
|
+
if (!clientSessionIdRef.current) {
|
|
493
|
+
clientSessionIdRef.current = crypto.randomUUID();
|
|
494
|
+
}
|
|
495
|
+
// Voice input state
|
|
496
|
+
const [isVoiceSupported, setIsVoiceSupported] = useState(false);
|
|
497
|
+
const [isListening, setIsListening] = useState(false);
|
|
498
|
+
const recognitionRef = useRef(null);
|
|
499
|
+
const prevPlaceholderRef = useRef(null);
|
|
500
|
+
const [isWaitingForResponse, setIsWaitingForResponse] = useState(false);
|
|
501
|
+
const isWaitingRef = useRef(false);
|
|
502
|
+
useEffect(() => {
|
|
503
|
+
isWaitingRef.current = isWaitingForResponse;
|
|
504
|
+
}, [isWaitingForResponse]);
|
|
505
|
+
// Dots visibility controlled by 1s idle timer since last incremental update
|
|
506
|
+
const [showDots, setShowDots] = useState(false);
|
|
507
|
+
const dotsTimeoutRef = useRef(null);
|
|
508
|
+
const hasActiveStreaming = useCallback(() => {
|
|
509
|
+
const current = messagesRef.current || [];
|
|
510
|
+
return current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
511
|
+
}, []);
|
|
512
|
+
const hasPendingApprovals = useCallback(() => {
|
|
513
|
+
const current = messagesRef.current || [];
|
|
514
|
+
return current.some((m) => (m.toolCalls || []).some((tc) => (tc.functionName || "").includes("(pending approval)")));
|
|
515
|
+
}, []);
|
|
516
|
+
const resetDotsTimer = useCallback(() => {
|
|
517
|
+
if (dotsTimeoutRef.current) {
|
|
518
|
+
clearTimeout(dotsTimeoutRef.current);
|
|
519
|
+
dotsTimeoutRef.current = null;
|
|
520
|
+
}
|
|
521
|
+
const waiting = isWaitingRef.current;
|
|
522
|
+
const streaming = hasActiveStreaming();
|
|
523
|
+
const pendingApprovals = hasPendingApprovals();
|
|
524
|
+
// Don't show dots if there are pending approvals (waiting for user action)
|
|
525
|
+
if (!waiting && !streaming) {
|
|
526
|
+
setShowDots(false);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (pendingApprovals) {
|
|
530
|
+
setShowDots(false);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
// Show dots immediately when waiting or streaming
|
|
534
|
+
setShowDots(true);
|
|
535
|
+
dotsTimeoutRef.current = setTimeout(() => {
|
|
536
|
+
// Re-check conditions after a brief delay to avoid flicker
|
|
537
|
+
const stillWaiting = isWaitingRef.current;
|
|
538
|
+
const stillStreaming = hasActiveStreaming();
|
|
539
|
+
const stillPendingApprovals = hasPendingApprovals();
|
|
540
|
+
setShowDots((stillWaiting || stillStreaming) && !stillPendingApprovals);
|
|
541
|
+
}, 100);
|
|
542
|
+
}, [hasActiveStreaming, hasPendingApprovals]);
|
|
543
|
+
const [resolvedPageName, setResolvedPageName] = useState(undefined);
|
|
544
|
+
const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
|
|
545
|
+
const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
|
|
546
|
+
const [isContextCollapsed, setIsContextCollapsed] = useState(true);
|
|
547
|
+
const [isContextDragOver, setIsContextDragOver] = useState(false);
|
|
548
|
+
const [promptHistory, setPromptHistory] = useState(() => {
|
|
549
|
+
if (typeof window !== "undefined") {
|
|
550
|
+
return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
|
|
551
|
+
}
|
|
552
|
+
return [];
|
|
553
|
+
});
|
|
554
|
+
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
|
|
555
|
+
const [showPredefined, setShowPredefined] = useState(false);
|
|
556
|
+
const [activeProfile, setActiveProfile] = useState(undefined);
|
|
557
|
+
const [selectedModelId, setSelectedModelId] = useState(undefined);
|
|
558
|
+
const [mode, setMode] = useState("supervised");
|
|
559
|
+
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
560
|
+
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
561
|
+
try {
|
|
562
|
+
if (!meta)
|
|
563
|
+
return meta;
|
|
564
|
+
const clean = { ...meta };
|
|
565
|
+
delete clean.costLimit;
|
|
566
|
+
delete clean.CostLimit;
|
|
567
|
+
delete clean.initialCostLimit;
|
|
568
|
+
delete clean.InitialCostLimit;
|
|
569
|
+
if (clean.additionalData && typeof clean.additionalData === "object") {
|
|
570
|
+
const ad = { ...clean.additionalData };
|
|
571
|
+
delete ad.costLimit;
|
|
572
|
+
delete ad.CostLimit;
|
|
573
|
+
delete ad.initialCostLimit;
|
|
574
|
+
delete ad.InitialCostLimit;
|
|
575
|
+
clean.additionalData = ad;
|
|
576
|
+
}
|
|
577
|
+
return clean;
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
return meta;
|
|
581
|
+
}
|
|
582
|
+
}, []);
|
|
583
|
+
// Read deterministic flags from query string once
|
|
584
|
+
const deterministicFlags = React.useMemo(() => {
|
|
585
|
+
try {
|
|
586
|
+
const params = new URLSearchParams(window.location.search);
|
|
587
|
+
const detParam = params.get("deterministic");
|
|
588
|
+
const deterministic = detParam === "true" ||
|
|
589
|
+
(detParam === null ? params.has("deterministic") : false);
|
|
590
|
+
const seedStr = params.get("seed");
|
|
591
|
+
const seed = seedStr ? Number(seedStr) : undefined;
|
|
592
|
+
return {
|
|
593
|
+
deterministic,
|
|
594
|
+
seed: Number.isFinite(seed) ? seed : undefined,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return { deterministic: false, seed: undefined };
|
|
599
|
+
}
|
|
600
|
+
}, []);
|
|
601
|
+
useEffect(() => {
|
|
602
|
+
localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
|
|
603
|
+
}, [promptHistory]);
|
|
604
|
+
// Clear idle timer on unmount
|
|
605
|
+
useEffect(() => {
|
|
606
|
+
return () => {
|
|
607
|
+
if (dotsTimeoutRef.current) {
|
|
608
|
+
clearTimeout(dotsTimeoutRef.current);
|
|
609
|
+
dotsTimeoutRef.current = null;
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
}, []);
|
|
613
|
+
// Whenever waiting state changes, restart idle timer logic
|
|
614
|
+
useEffect(() => {
|
|
615
|
+
resetDotsTimer();
|
|
616
|
+
}, [isWaitingForResponse, resetDotsTimer]);
|
|
617
|
+
useEffect(() => {
|
|
618
|
+
// Keep messagesRef synchronized with messages state
|
|
619
|
+
messagesRef.current = messages;
|
|
620
|
+
}, [messages]);
|
|
621
|
+
const [error, setError] = useState(null);
|
|
622
|
+
const [costLimitExceeded, setCostLimitExceeded] = useState(null);
|
|
623
|
+
// Live running totals from backend status updates (tokenUsage)
|
|
624
|
+
const [liveTotals, setLiveTotals] = useState(null);
|
|
625
|
+
// Flag to track when we should create a new message
|
|
626
|
+
const shouldCreateNewMessage = useRef(false);
|
|
627
|
+
// Keep a ref to the current messages for immediate access
|
|
628
|
+
const messagesRef = useRef([]);
|
|
629
|
+
const abortControllerRef = useRef(null);
|
|
630
|
+
const messagesEndRef = useRef(null);
|
|
631
|
+
const textareaRef = useRef(null);
|
|
632
|
+
const messagesContainerRef = useRef(null);
|
|
633
|
+
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
634
|
+
// WebSocket subscription state for agent streaming
|
|
635
|
+
const seenMessageIdsRef = useRef(new Set());
|
|
636
|
+
const lastSeqRef = useRef(0);
|
|
637
|
+
const subscribedAgentIdRef = useRef(null);
|
|
638
|
+
// Cache mode/model changes made while the agent is still "new" (not yet persisted)
|
|
639
|
+
const pendingSettingsRef = useRef(null);
|
|
640
|
+
// Auto-scroll to bottom when new messages arrive
|
|
641
|
+
const scrollToBottom = useCallback(() => {
|
|
642
|
+
const container = messagesContainerRef.current;
|
|
643
|
+
if (!container)
|
|
644
|
+
return;
|
|
645
|
+
// Jump precisely to the bottom to avoid ending slightly above due to smooth scrolling animations
|
|
646
|
+
container.scrollTop = container.scrollHeight;
|
|
647
|
+
}, []);
|
|
648
|
+
// Detect speech recognition support (client-only)
|
|
649
|
+
useEffect(() => {
|
|
650
|
+
try {
|
|
651
|
+
if (typeof window === "undefined")
|
|
652
|
+
return;
|
|
653
|
+
const SR = window.SpeechRecognition ||
|
|
654
|
+
window.webkitSpeechRecognition;
|
|
655
|
+
setIsVoiceSupported(!!SR);
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
setIsVoiceSupported(false);
|
|
659
|
+
}
|
|
660
|
+
}, []);
|
|
661
|
+
// Auto-focus terminal input on mount
|
|
662
|
+
useEffect(() => {
|
|
663
|
+
if (textareaRef.current) {
|
|
664
|
+
textareaRef.current.focus();
|
|
665
|
+
}
|
|
666
|
+
}, []);
|
|
667
|
+
// Start voice recognition
|
|
668
|
+
const startVoice = useCallback(() => {
|
|
669
|
+
try {
|
|
670
|
+
if (isListening)
|
|
671
|
+
return;
|
|
672
|
+
if (typeof window === "undefined")
|
|
673
|
+
return;
|
|
674
|
+
const SR = window.SpeechRecognition ||
|
|
675
|
+
window.webkitSpeechRecognition;
|
|
676
|
+
if (!SR) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const r = new SR();
|
|
680
|
+
r.lang = editContext?.currentItemDescriptor?.language || "en-US";
|
|
681
|
+
r.continuous = true;
|
|
682
|
+
r.interimResults = true;
|
|
683
|
+
r.onstart = () => {
|
|
684
|
+
setIsListening(true);
|
|
685
|
+
prevPlaceholderRef.current = inputPlaceholder;
|
|
686
|
+
setInputPlaceholder("Listening...");
|
|
687
|
+
};
|
|
688
|
+
r.onresult = (event) => {
|
|
689
|
+
let finalText = "";
|
|
690
|
+
let interimText = "";
|
|
691
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
692
|
+
const res = event.results[i];
|
|
693
|
+
if (res.isFinal)
|
|
694
|
+
finalText += res[0]?.transcript || "";
|
|
695
|
+
else
|
|
696
|
+
interimText += res[0]?.transcript || "";
|
|
697
|
+
}
|
|
698
|
+
if (interimText) {
|
|
699
|
+
setInputPlaceholder(`Listening... ${interimText.trim()}`);
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
setInputPlaceholder("Listening...");
|
|
703
|
+
}
|
|
704
|
+
if (finalText && finalText.trim()) {
|
|
705
|
+
setPrompt((prev) => {
|
|
706
|
+
const prefix = prev && !prev.endsWith(" ") ? prev + " " : prev;
|
|
707
|
+
return (prefix || "") + finalText.trim() + " ";
|
|
708
|
+
});
|
|
709
|
+
if (textareaRef.current) {
|
|
710
|
+
try {
|
|
711
|
+
const v = textareaRef.current.value || "";
|
|
712
|
+
textareaRef.current.selectionStart = v.length;
|
|
713
|
+
textareaRef.current.selectionEnd = v.length;
|
|
714
|
+
}
|
|
715
|
+
catch { }
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
r.onerror = (e) => {
|
|
720
|
+
console.warn("Speech recognition error", e);
|
|
721
|
+
};
|
|
722
|
+
r.onend = () => {
|
|
723
|
+
setIsListening(false);
|
|
724
|
+
if (prevPlaceholderRef.current !== null) {
|
|
725
|
+
setInputPlaceholder(prevPlaceholderRef.current);
|
|
726
|
+
prevPlaceholderRef.current = null;
|
|
727
|
+
}
|
|
728
|
+
recognitionRef.current = null;
|
|
729
|
+
};
|
|
730
|
+
recognitionRef.current = r;
|
|
731
|
+
r.start();
|
|
732
|
+
}
|
|
733
|
+
catch (e) {
|
|
734
|
+
console.error("Failed to start voice input", e);
|
|
735
|
+
}
|
|
736
|
+
}, [
|
|
737
|
+
editContext?.currentItemDescriptor?.language,
|
|
738
|
+
inputPlaceholder,
|
|
739
|
+
isListening,
|
|
740
|
+
]);
|
|
741
|
+
const stopVoice = useCallback(() => {
|
|
742
|
+
try {
|
|
743
|
+
const r = recognitionRef.current;
|
|
744
|
+
if (r)
|
|
745
|
+
r.stop();
|
|
746
|
+
}
|
|
747
|
+
catch { }
|
|
748
|
+
}, []);
|
|
749
|
+
const toggleVoice = useCallback(() => {
|
|
750
|
+
if (isListening)
|
|
751
|
+
stopVoice();
|
|
752
|
+
else
|
|
753
|
+
startVoice();
|
|
754
|
+
}, [isListening, startVoice, stopVoice]);
|
|
755
|
+
// Cleanup any active recognition on unmount
|
|
756
|
+
useEffect(() => {
|
|
757
|
+
return () => {
|
|
758
|
+
try {
|
|
759
|
+
const r = recognitionRef.current;
|
|
760
|
+
if (r) {
|
|
761
|
+
r.onresult = null;
|
|
762
|
+
r.onerror = null;
|
|
763
|
+
r.onend = null;
|
|
764
|
+
r.stop();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
catch { }
|
|
768
|
+
recognitionRef.current = null;
|
|
769
|
+
};
|
|
770
|
+
}, []);
|
|
771
|
+
// Check if user is at the bottom of the scroll container
|
|
772
|
+
const isAtBottom = useCallback(() => {
|
|
773
|
+
const container = messagesContainerRef.current;
|
|
774
|
+
if (!container)
|
|
775
|
+
return true;
|
|
776
|
+
const threshold = 100; // pixels from bottom to consider "at bottom"
|
|
777
|
+
return (container.scrollHeight - container.scrollTop - container.clientHeight <
|
|
778
|
+
threshold);
|
|
779
|
+
}, []);
|
|
780
|
+
// Handle scroll events to detect manual scrolling
|
|
781
|
+
const handleScroll = useCallback(() => {
|
|
782
|
+
const atBottom = isAtBottom();
|
|
783
|
+
if (atBottom !== shouldAutoScroll) {
|
|
784
|
+
setShouldAutoScroll(atBottom);
|
|
785
|
+
}
|
|
786
|
+
}, [isAtBottom, shouldAutoScroll]);
|
|
787
|
+
// Shared stream message handlers with messageId support
|
|
788
|
+
const createNewStreamMessage = useCallback((messageId, agentData) => {
|
|
789
|
+
const currentAgent = agentData || agent;
|
|
790
|
+
if (!currentAgent) {
|
|
791
|
+
console.error("❌ createNewStreamMessage: No agent available", {
|
|
792
|
+
messageId,
|
|
793
|
+
agentData: !!agentData,
|
|
794
|
+
agent: !!agent,
|
|
795
|
+
});
|
|
796
|
+
throw new Error("No agent available");
|
|
797
|
+
}
|
|
798
|
+
// Reduced: avoid verbose logging during streaming
|
|
799
|
+
return {
|
|
800
|
+
id: messageId,
|
|
801
|
+
agentId: currentAgent.id,
|
|
802
|
+
messageIndex: -1,
|
|
803
|
+
role: "assistant",
|
|
804
|
+
content: "",
|
|
805
|
+
name: "agent",
|
|
806
|
+
messageType: "streaming",
|
|
807
|
+
isCompleted: false,
|
|
808
|
+
model: currentAgent.model || "",
|
|
809
|
+
tokensUsed: 0,
|
|
810
|
+
inputTokens: 0,
|
|
811
|
+
outputTokens: 0,
|
|
812
|
+
cachedInputTokens: 0,
|
|
813
|
+
inputTokenCost: 0,
|
|
814
|
+
outputTokenCost: 0,
|
|
815
|
+
cachedInputTokenCost: 0,
|
|
816
|
+
totalCost: 0,
|
|
817
|
+
currency: currentAgent.currency || "USD",
|
|
818
|
+
createdDate: new Date().toISOString(),
|
|
819
|
+
toolCalls: [],
|
|
820
|
+
};
|
|
821
|
+
}, [agent]);
|
|
822
|
+
const handleContentChunk = useCallback((message, agentData) => {
|
|
823
|
+
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
824
|
+
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
825
|
+
let messageId = message.data?.messageId;
|
|
826
|
+
if (!messageId && agentData?.id) {
|
|
827
|
+
// For backward compatibility: if no messageId, find or create the current streaming message
|
|
828
|
+
// This handles cases where the backend doesn't send messageId
|
|
829
|
+
const currentMessages = messagesRef.current;
|
|
830
|
+
const lastStreamingMessage = [...currentMessages]
|
|
831
|
+
.reverse()
|
|
832
|
+
.find((m) => m.role === "assistant" && !m.isCompleted);
|
|
833
|
+
if (lastStreamingMessage) {
|
|
834
|
+
messageId = lastStreamingMessage.id;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
// Create a new message ID based on timestamp
|
|
838
|
+
messageId = crypto.randomUUID();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (!messageId) {
|
|
842
|
+
console.error("Unable to determine messageId for content chunk!");
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
// Clear waiting state when first content chunk arrives
|
|
846
|
+
setIsWaitingForResponse(false);
|
|
847
|
+
isWaitingRef.current = false;
|
|
848
|
+
// Any content chunk is an incremental update -> reset idle timer
|
|
849
|
+
resetDotsTimer();
|
|
850
|
+
// Extract cost/token data from message.cost (new structure)
|
|
851
|
+
const cost = message.cost;
|
|
852
|
+
if (cost &&
|
|
853
|
+
(cost.total !== undefined || cost.tokens?.total !== undefined)) {
|
|
854
|
+
const nextTotals = {
|
|
855
|
+
input: Number(cost.tokens?.input) || 0,
|
|
856
|
+
output: Number(cost.tokens?.output) || 0,
|
|
857
|
+
cached: Number(cost.tokens?.cached) || 0,
|
|
858
|
+
cacheWrite: Number(cost.tokens?.cacheWrite) || 0,
|
|
859
|
+
inputCost: Number(cost.input) || 0,
|
|
860
|
+
outputCost: Number(cost.output) || 0,
|
|
861
|
+
cachedCost: Number(cost.cached) || 0,
|
|
862
|
+
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
863
|
+
totalCost: Number(cost.total) || 0,
|
|
864
|
+
currency: "USD",
|
|
865
|
+
};
|
|
866
|
+
const anyNonZero = (nextTotals.totalCost || 0) > 0 ||
|
|
867
|
+
(nextTotals.input || 0) > 0 ||
|
|
868
|
+
(nextTotals.output || 0) > 0 ||
|
|
869
|
+
(nextTotals.cached || 0) > 0 ||
|
|
870
|
+
(nextTotals.cacheWrite || 0) > 0;
|
|
871
|
+
if (anyNonZero) {
|
|
872
|
+
setLiveTotals(nextTotals);
|
|
873
|
+
}
|
|
874
|
+
// Check cost limit if available
|
|
875
|
+
if (cost.limit &&
|
|
876
|
+
cost.total &&
|
|
877
|
+
Number(cost.total) > Number(cost.limit)) {
|
|
878
|
+
setCostLimitExceeded({
|
|
879
|
+
totalCost: Number(cost.total),
|
|
880
|
+
costLimit: Number(cost.limit),
|
|
881
|
+
initialCostLimit: Number(cost.limit),
|
|
882
|
+
});
|
|
883
|
+
setIsWaitingForResponse(false);
|
|
884
|
+
shouldCreateNewMessage.current = false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
// Always call setMessages and handle all logic in the callback with latest messages
|
|
888
|
+
setMessages((prev) => {
|
|
889
|
+
// Find existing message by messageId in the latest messages
|
|
890
|
+
const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
|
|
891
|
+
if (existingMessageIndex === -1) {
|
|
892
|
+
// Message doesn't exist - create new streaming message
|
|
893
|
+
const newStreamMessage = createNewStreamMessage(messageId, agentData);
|
|
894
|
+
// Set the content for the new message
|
|
895
|
+
const updatedNewMessage = { ...newStreamMessage };
|
|
896
|
+
if (!message.data.isIncremental) {
|
|
897
|
+
updatedNewMessage.content = message.data?.deltaContent || "";
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
updatedNewMessage.content = message.data?.deltaContent || "";
|
|
901
|
+
}
|
|
902
|
+
const updated = [...prev, updatedNewMessage];
|
|
903
|
+
messagesRef.current = updated;
|
|
904
|
+
return updated;
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
// Message exists - update it
|
|
908
|
+
const existingMessage = prev[existingMessageIndex];
|
|
909
|
+
if (!existingMessage)
|
|
910
|
+
return prev;
|
|
911
|
+
// Check if existing content is already longer than what we're trying to stream
|
|
912
|
+
const currentContentLength = existingMessage.content?.length || 0;
|
|
913
|
+
const totalContentLength = message.data?.totalContentLength || 0;
|
|
914
|
+
if (currentContentLength >= totalContentLength &&
|
|
915
|
+
totalContentLength > 0) {
|
|
916
|
+
return prev;
|
|
917
|
+
}
|
|
918
|
+
const updatedMessage = { ...existingMessage };
|
|
919
|
+
if (!message.data.isIncremental) {
|
|
920
|
+
updatedMessage.content = message.data?.deltaContent || "";
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
updatedMessage.content =
|
|
924
|
+
existingMessage.content + (message.data?.deltaContent || "");
|
|
925
|
+
}
|
|
926
|
+
const updated = prev.map((msg, index) => index === existingMessageIndex ? updatedMessage : msg);
|
|
927
|
+
messagesRef.current = updated;
|
|
928
|
+
return updated;
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
}, [createNewStreamMessage]);
|
|
932
|
+
const handleToolCall = useCallback((message, agentData) => {
|
|
933
|
+
const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
934
|
+
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
935
|
+
let toolCallMessageId = message.data?.messageId;
|
|
936
|
+
if (!toolCallMessageId) {
|
|
937
|
+
const current = messagesRef.current;
|
|
938
|
+
const lastStreaming = [...current]
|
|
939
|
+
.reverse()
|
|
940
|
+
.find((m) => m.role === "assistant" && !m.isCompleted);
|
|
941
|
+
if (lastStreaming?.id) {
|
|
942
|
+
toolCallMessageId = lastStreaming.id;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// Find or create the target message for this tool call
|
|
946
|
+
if (toolCallMessageId) {
|
|
947
|
+
const currentMessages = messagesRef.current;
|
|
948
|
+
const existingMessageIndex = currentMessages.findIndex((msg) => msg.id === toolCallMessageId);
|
|
949
|
+
if (existingMessageIndex === -1) {
|
|
950
|
+
// Double-check with current ref to prevent race conditions
|
|
951
|
+
const currentMessages = messagesRef.current;
|
|
952
|
+
const existsInRef = currentMessages.find((msg) => msg.id === toolCallMessageId);
|
|
953
|
+
if (!existsInRef) {
|
|
954
|
+
// Create new message for this tool call
|
|
955
|
+
const newStreamMessage = createNewStreamMessage(toolCallMessageId, agentData);
|
|
956
|
+
setMessages((prev) => {
|
|
957
|
+
// Final check before adding to prevent duplicates
|
|
958
|
+
const finalCheck = prev.find((msg) => msg.id === toolCallMessageId);
|
|
959
|
+
if (finalCheck) {
|
|
960
|
+
return prev;
|
|
961
|
+
}
|
|
962
|
+
const updated = [...prev, newStreamMessage];
|
|
963
|
+
messagesRef.current = updated;
|
|
964
|
+
return updated;
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// Add tool call to the message in the array
|
|
970
|
+
if (toolCallMessageId && message.data && toolCallId) {
|
|
971
|
+
const functionName = message.data.functionName ||
|
|
972
|
+
message.data.name ||
|
|
973
|
+
message.data.function?.name ||
|
|
974
|
+
"unknown";
|
|
975
|
+
const toolCall = {
|
|
976
|
+
id: toolCallId,
|
|
977
|
+
messageId: toolCallMessageId,
|
|
978
|
+
dbMessageId: message.data.messageId, // Database message ID for approval/rejection
|
|
979
|
+
toolCallId: toolCallId,
|
|
980
|
+
functionName: functionName,
|
|
981
|
+
functionArguments: message.data.functionArguments ||
|
|
982
|
+
message.data.arguments ||
|
|
983
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
984
|
+
functionResult: message.data.functionResult || message.data.result || "",
|
|
985
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
986
|
+
isCompleted: false,
|
|
987
|
+
responseTimeMs: message.data.responseTimeMs,
|
|
988
|
+
createdDate: new Date().toISOString(),
|
|
989
|
+
requiresApproval: message.data?.requiresApproval,
|
|
990
|
+
};
|
|
991
|
+
// Check for duplicates using the current messages ref
|
|
992
|
+
const currentMessages = messagesRef.current;
|
|
993
|
+
const targetMessage = currentMessages.find((msg) => msg.id === toolCallMessageId);
|
|
994
|
+
if (targetMessage) {
|
|
995
|
+
const existingToolCalls = targetMessage.toolCalls || [];
|
|
996
|
+
const existingToolCallIndex = existingToolCalls.findIndex((tc) => tc.toolCallId === toolCallId);
|
|
997
|
+
if (existingToolCallIndex !== -1) {
|
|
998
|
+
return; // Skip adding duplicate
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
setMessages((prev) => {
|
|
1002
|
+
const updated = prev.map((msg) => {
|
|
1003
|
+
if (msg.id !== toolCallMessageId)
|
|
1004
|
+
return msg;
|
|
1005
|
+
const existingToolCalls = msg.toolCalls || [];
|
|
1006
|
+
return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
|
|
1007
|
+
});
|
|
1008
|
+
messagesRef.current = updated;
|
|
1009
|
+
return updated;
|
|
1010
|
+
});
|
|
1011
|
+
// Tool call activity counts as activity; keep dots hidden for 1s
|
|
1012
|
+
resetDotsTimer();
|
|
1013
|
+
}
|
|
1014
|
+
}, [createNewStreamMessage]);
|
|
1015
|
+
const handleToolResult = useCallback((message, agentData) => {
|
|
1016
|
+
const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
1017
|
+
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
1018
|
+
let resultMessageId = message.data?.messageId;
|
|
1019
|
+
if (!resultMessageId) {
|
|
1020
|
+
const current = messagesRef.current;
|
|
1021
|
+
const lastStreaming = [...current]
|
|
1022
|
+
.reverse()
|
|
1023
|
+
.find((m) => m.role === "assistant" && !m.isCompleted);
|
|
1024
|
+
if (lastStreaming?.id) {
|
|
1025
|
+
resultMessageId = lastStreaming.id;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
// Extract cost/token data from tool result if present
|
|
1029
|
+
const cost = message.cost;
|
|
1030
|
+
if (cost &&
|
|
1031
|
+
(cost.total !== undefined || cost.tokens?.total !== undefined)) {
|
|
1032
|
+
const nextTotals = {
|
|
1033
|
+
input: Number(cost.tokens?.input) || 0,
|
|
1034
|
+
output: Number(cost.tokens?.output) || 0,
|
|
1035
|
+
cached: Number(cost.tokens?.cached) || 0,
|
|
1036
|
+
cacheWrite: Number(cost.tokens?.cacheWrite) || 0,
|
|
1037
|
+
inputCost: Number(cost.input) || 0,
|
|
1038
|
+
outputCost: Number(cost.output) || 0,
|
|
1039
|
+
cachedCost: Number(cost.cached) || 0,
|
|
1040
|
+
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
1041
|
+
totalCost: Number(cost.total) || 0,
|
|
1042
|
+
currency: "USD",
|
|
1043
|
+
};
|
|
1044
|
+
const anyNonZero = (nextTotals.totalCost || 0) > 0 ||
|
|
1045
|
+
(nextTotals.input || 0) > 0 ||
|
|
1046
|
+
(nextTotals.output || 0) > 0 ||
|
|
1047
|
+
(nextTotals.cached || 0) > 0 ||
|
|
1048
|
+
(nextTotals.cacheWrite || 0) > 0;
|
|
1049
|
+
if (anyNonZero) {
|
|
1050
|
+
setLiveTotals(nextTotals);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
// Fallback: legacy aggregated totals included in the tool result data
|
|
1055
|
+
const data = message.data;
|
|
1056
|
+
if (data &&
|
|
1057
|
+
(data.totalCost !== undefined || data.totalTokens !== undefined)) {
|
|
1058
|
+
const nextTotals = {
|
|
1059
|
+
input: Number(data.totalInputTokens) || 0,
|
|
1060
|
+
output: Number(data.totalOutputTokens) || 0,
|
|
1061
|
+
cached: Number(data.totalCachedTokens) || 0,
|
|
1062
|
+
cacheWrite: Number(data.totalCacheWriteTokens) || 0,
|
|
1063
|
+
inputCost: Number(data.totalInputTokenCost) || 0,
|
|
1064
|
+
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
1065
|
+
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
1066
|
+
cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
|
|
1067
|
+
totalCost: Number(data.totalCost) || 0,
|
|
1068
|
+
currency: data.currency || "USD",
|
|
1069
|
+
};
|
|
1070
|
+
const anyNonZero = (nextTotals.totalCost || 0) > 0 ||
|
|
1071
|
+
(nextTotals.input || 0) > 0 ||
|
|
1072
|
+
(nextTotals.output || 0) > 0 ||
|
|
1073
|
+
(nextTotals.cached || 0) > 0 ||
|
|
1074
|
+
(nextTotals.cacheWrite || 0) > 0;
|
|
1075
|
+
if (anyNonZero) {
|
|
1076
|
+
setLiveTotals(nextTotals);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// Update tool result directly in the messages array
|
|
1081
|
+
if (!resultMessageId) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
// Update the message with tool result
|
|
1085
|
+
setMessages((prev) => {
|
|
1086
|
+
const updated = prev.map((msg) => {
|
|
1087
|
+
if (msg.id !== resultMessageId)
|
|
1088
|
+
return msg;
|
|
1089
|
+
const updatedMessage = { ...msg };
|
|
1090
|
+
if (!updatedMessage.toolCalls) {
|
|
1091
|
+
updatedMessage.toolCalls = [];
|
|
1092
|
+
}
|
|
1093
|
+
// Find and update the tool call with the result
|
|
1094
|
+
const toolCallIndex = updatedMessage.toolCalls.findIndex((tc) => tc.toolCallId === resultToolCallId);
|
|
1095
|
+
if (toolCallIndex >= 0) {
|
|
1096
|
+
const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
|
|
1097
|
+
if (existingToolCall && message.data) {
|
|
1098
|
+
const updatedToolCalls = [...updatedMessage.toolCalls];
|
|
1099
|
+
const toolCall = {
|
|
1100
|
+
id: existingToolCall.id,
|
|
1101
|
+
messageId: existingToolCall.messageId,
|
|
1102
|
+
toolCallId: existingToolCall.toolCallId,
|
|
1103
|
+
functionName: existingToolCall.functionName,
|
|
1104
|
+
functionArguments: existingToolCall.functionArguments,
|
|
1105
|
+
functionResult: message.data.functionResult || message.data.result || "",
|
|
1106
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
1107
|
+
isCompleted: true,
|
|
1108
|
+
responseTimeMs: message.data.responseTimeMs,
|
|
1109
|
+
createdDate: existingToolCall.createdDate,
|
|
1110
|
+
};
|
|
1111
|
+
updatedToolCalls[toolCallIndex] = toolCall;
|
|
1112
|
+
updatedMessage.toolCalls = updatedToolCalls;
|
|
1113
|
+
}
|
|
1114
|
+
// Check if all tool calls in message are completed
|
|
1115
|
+
const allToolCallsCompleted = updatedMessage.toolCalls.every((tc) => tc.isCompleted);
|
|
1116
|
+
if (allToolCallsCompleted) {
|
|
1117
|
+
shouldCreateNewMessage.current = true;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
else if (message.data && resultToolCallId && resultMessageId) {
|
|
1121
|
+
// Create new tool call if it doesn't exist
|
|
1122
|
+
const functionName = message.data.functionName ||
|
|
1123
|
+
message.data.name ||
|
|
1124
|
+
message.data.function?.name ||
|
|
1125
|
+
"unknown";
|
|
1126
|
+
const toolCall = {
|
|
1127
|
+
id: resultToolCallId,
|
|
1128
|
+
messageId: resultMessageId,
|
|
1129
|
+
toolCallId: resultToolCallId,
|
|
1130
|
+
functionName: functionName,
|
|
1131
|
+
functionArguments: message.data.functionArguments ||
|
|
1132
|
+
message.data.arguments ||
|
|
1133
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
1134
|
+
functionResult: message.data.functionResult || message.data.result || "",
|
|
1135
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
1136
|
+
isCompleted: true,
|
|
1137
|
+
responseTimeMs: message.data.responseTimeMs,
|
|
1138
|
+
createdDate: new Date().toISOString(),
|
|
1139
|
+
};
|
|
1140
|
+
updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
|
|
1141
|
+
}
|
|
1142
|
+
// Updated tool calls count
|
|
1143
|
+
return updatedMessage;
|
|
1144
|
+
});
|
|
1145
|
+
messagesRef.current = updated;
|
|
1146
|
+
return updated;
|
|
1147
|
+
});
|
|
1148
|
+
// Tool result activity; reset idle timer
|
|
1149
|
+
resetDotsTimer();
|
|
1150
|
+
}, [resetDotsTimer]);
|
|
1151
|
+
// Listen for local approval resolution to update UI
|
|
1152
|
+
useEffect(() => {
|
|
1153
|
+
const onApprovalResolved = (ev) => {
|
|
1154
|
+
try {
|
|
1155
|
+
const detail = ev?.detail || {};
|
|
1156
|
+
const messageId = detail.messageId;
|
|
1157
|
+
const toolCallId = detail.toolCallId;
|
|
1158
|
+
const approved = !!detail.approved;
|
|
1159
|
+
if (!messageId || !toolCallId)
|
|
1160
|
+
return;
|
|
1161
|
+
// Update local state to reflect approval status
|
|
1162
|
+
setMessages((prev) => {
|
|
1163
|
+
const updated = prev.map((m) => {
|
|
1164
|
+
if (m.id !== messageId)
|
|
1165
|
+
return m;
|
|
1166
|
+
const updatedToolCalls = (m.toolCalls || []).map((tc) => {
|
|
1167
|
+
if (tc.toolCallId !== toolCallId)
|
|
1168
|
+
return tc;
|
|
1169
|
+
const base = (tc.functionName || "")
|
|
1170
|
+
.replace(" (pending approval)", "")
|
|
1171
|
+
.replace(" (approved)", "")
|
|
1172
|
+
.replace(" (rejected)", "");
|
|
1173
|
+
return {
|
|
1174
|
+
...tc,
|
|
1175
|
+
functionName: base + (approved ? " (approved)" : " (rejected)"),
|
|
1176
|
+
};
|
|
1177
|
+
});
|
|
1178
|
+
return { ...m, toolCalls: updatedToolCalls };
|
|
1179
|
+
});
|
|
1180
|
+
messagesRef.current = updated;
|
|
1181
|
+
return updated;
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
catch (err) {
|
|
1185
|
+
console.error("❌ Error handling approval resolution:", err);
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
window.addEventListener("agent:toolApprovalResolved", onApprovalResolved);
|
|
1189
|
+
return () => window.removeEventListener("agent:toolApprovalResolved", onApprovalResolved);
|
|
1190
|
+
}, []);
|
|
1191
|
+
// Load agent data and messages
|
|
1192
|
+
const loadAgent = useCallback(async () => {
|
|
1193
|
+
try {
|
|
1194
|
+
if (agentStub.status === "new") {
|
|
1195
|
+
// Derive initial profile from provided metadata if present
|
|
1196
|
+
const initialProfileIdFromMeta = (() => {
|
|
1197
|
+
try {
|
|
1198
|
+
return initialMetadata?.additionalData?.profileId;
|
|
1199
|
+
}
|
|
1200
|
+
catch {
|
|
1201
|
+
return undefined;
|
|
1202
|
+
}
|
|
1203
|
+
})();
|
|
1204
|
+
const initialProfileNameFromMeta = (() => {
|
|
1205
|
+
try {
|
|
1206
|
+
return (initialMetadata?.additionalData?.profileName ||
|
|
1207
|
+
initialMetadata?.profile ||
|
|
1208
|
+
undefined);
|
|
1209
|
+
}
|
|
1210
|
+
catch {
|
|
1211
|
+
return undefined;
|
|
1212
|
+
}
|
|
1213
|
+
})();
|
|
1214
|
+
setAgent({
|
|
1215
|
+
...agentStub,
|
|
1216
|
+
messages: [],
|
|
1217
|
+
status: "new",
|
|
1218
|
+
updatedDate: new Date().toISOString(),
|
|
1219
|
+
createdDate: new Date().toISOString(),
|
|
1220
|
+
id: agentStub.id,
|
|
1221
|
+
name: agentStub.name,
|
|
1222
|
+
model: "",
|
|
1223
|
+
currency: "USD",
|
|
1224
|
+
profileId: initialProfileIdFromMeta || "",
|
|
1225
|
+
profileName: initialProfileNameFromMeta || "",
|
|
1226
|
+
totalTokensUsed: 0,
|
|
1227
|
+
totalInputTokens: 0,
|
|
1228
|
+
totalOutputTokens: 0,
|
|
1229
|
+
totalCachedInputTokens: 0,
|
|
1230
|
+
totalInputTokenCost: 0,
|
|
1231
|
+
totalOutputTokenCost: 0,
|
|
1232
|
+
totalCachedInputTokenCost: 0,
|
|
1233
|
+
totalCost: 0,
|
|
1234
|
+
messageCount: 0,
|
|
1235
|
+
});
|
|
1236
|
+
setMessages([]);
|
|
1237
|
+
setError(null);
|
|
1238
|
+
setIsLoading(false);
|
|
1239
|
+
// Initialize local context for a brand-new agent (not yet persisted)
|
|
1240
|
+
// Optionally seed with current page/selection/focused field based on profile setting
|
|
1241
|
+
const item = editContext?.currentItemDescriptor;
|
|
1242
|
+
const profileForSeeding = (() => {
|
|
1243
|
+
try {
|
|
1244
|
+
if (initialProfileIdFromMeta) {
|
|
1245
|
+
const byId = (profiles || []).find((p) => p.id === initialProfileIdFromMeta);
|
|
1246
|
+
if (byId)
|
|
1247
|
+
return byId;
|
|
1248
|
+
}
|
|
1249
|
+
if (initialProfileNameFromMeta) {
|
|
1250
|
+
const byName = (profiles || []).find((p) => p.name === initialProfileNameFromMeta);
|
|
1251
|
+
if (byName)
|
|
1252
|
+
return byName;
|
|
1253
|
+
}
|
|
1254
|
+
return (profiles || [])[0];
|
|
1255
|
+
}
|
|
1256
|
+
catch {
|
|
1257
|
+
return undefined;
|
|
1258
|
+
}
|
|
1259
|
+
})();
|
|
1260
|
+
const shouldSeedContext = (() => {
|
|
1261
|
+
try {
|
|
1262
|
+
// Prefer profile flag if available; default to true when unspecified
|
|
1263
|
+
return profileForSeeding?.includeEditorContextOnCreate !== undefined
|
|
1264
|
+
? !!profileForSeeding.includeEditorContextOnCreate
|
|
1265
|
+
: true;
|
|
1266
|
+
}
|
|
1267
|
+
catch {
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
})();
|
|
1271
|
+
// Create context with top-level properties (what ContextInfoBar expects)
|
|
1272
|
+
const localCtx = item && shouldSeedContext
|
|
1273
|
+
? {
|
|
1274
|
+
items: [
|
|
1275
|
+
{
|
|
1276
|
+
id: item.id,
|
|
1277
|
+
language: item.language,
|
|
1278
|
+
version: item.version,
|
|
1279
|
+
name: editContext?.contentEditorItem?.name,
|
|
1280
|
+
},
|
|
1281
|
+
],
|
|
1282
|
+
components: editContext?.selection?.length && item
|
|
1283
|
+
? editContext.selection.map((componentId) => ({
|
|
1284
|
+
componentId,
|
|
1285
|
+
pageItem: {
|
|
1286
|
+
id: item.id,
|
|
1287
|
+
language: item.language,
|
|
1288
|
+
version: item.version,
|
|
1289
|
+
name: editContext?.contentEditorItem?.name,
|
|
1290
|
+
},
|
|
1291
|
+
}))
|
|
1292
|
+
: undefined,
|
|
1293
|
+
field: fieldsContext?.focusedField?.fieldId &&
|
|
1294
|
+
fieldsContext.focusedField?.item?.id
|
|
1295
|
+
? {
|
|
1296
|
+
fieldId: fieldsContext.focusedField.fieldId,
|
|
1297
|
+
fieldName: fieldsContext.focusedField
|
|
1298
|
+
.fieldName,
|
|
1299
|
+
item: {
|
|
1300
|
+
id: fieldsContext.focusedField.item.id,
|
|
1301
|
+
language: fieldsContext.focusedField.item.language ||
|
|
1302
|
+
editContext?.currentItemDescriptor?.language ||
|
|
1303
|
+
"en",
|
|
1304
|
+
version: fieldsContext.focusedField.item.version ??
|
|
1305
|
+
editContext?.currentItemDescriptor?.version ??
|
|
1306
|
+
0,
|
|
1307
|
+
name: editContext?.contentEditorItem?.name,
|
|
1308
|
+
},
|
|
1309
|
+
}
|
|
1310
|
+
: undefined,
|
|
1311
|
+
}
|
|
1312
|
+
: null;
|
|
1313
|
+
let nextMetadata = null;
|
|
1314
|
+
if (initialMetadata) {
|
|
1315
|
+
// Merge initial metadata with local context (using top-level structure)
|
|
1316
|
+
const base = { ...initialMetadata };
|
|
1317
|
+
if (localCtx) {
|
|
1318
|
+
// Merge items (avoid duplicates)
|
|
1319
|
+
if (localCtx.items && localCtx.items.length) {
|
|
1320
|
+
const targetPages = Array.isArray(base.items)
|
|
1321
|
+
? [...base.items]
|
|
1322
|
+
: [];
|
|
1323
|
+
const existingKeys = new Set(targetPages.map((p) => `${p.id}-${p.language || ""}-${p.version || ""}`));
|
|
1324
|
+
const additions = localCtx.items.filter((p) => !existingKeys.has(`${p.id}-${p.language || ""}-${p.version || ""}`));
|
|
1325
|
+
if (additions.length)
|
|
1326
|
+
base.items = [...targetPages, ...additions];
|
|
1327
|
+
}
|
|
1328
|
+
// Merge components (avoid duplicates)
|
|
1329
|
+
if (localCtx.components && localCtx.components.length) {
|
|
1330
|
+
const currentComponents = Array.isArray(base.components)
|
|
1331
|
+
? base.components
|
|
1332
|
+
: [];
|
|
1333
|
+
const existingIds = new Set(currentComponents.map((c) => c.componentId));
|
|
1334
|
+
const additions = localCtx.components.filter((c) => !!c?.componentId && !existingIds.has(c.componentId));
|
|
1335
|
+
if (additions.length)
|
|
1336
|
+
base.components = [...currentComponents, ...additions];
|
|
1337
|
+
}
|
|
1338
|
+
// Set field if missing
|
|
1339
|
+
if (!base.field && localCtx.field) {
|
|
1340
|
+
base.field = localCtx.field;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
nextMetadata = base;
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
nextMetadata = localCtx;
|
|
1347
|
+
}
|
|
1348
|
+
if (nextMetadata) {
|
|
1349
|
+
setAgentMetadata(sanitizeAgentMetadata(nextMetadata));
|
|
1350
|
+
// If an initial prompt is provided via metadata, seed the input once
|
|
1351
|
+
try {
|
|
1352
|
+
const maybePrompt = nextMetadata?.additionalData
|
|
1353
|
+
?.initialPrompt;
|
|
1354
|
+
if (typeof maybePrompt === "string" && maybePrompt.trim()) {
|
|
1355
|
+
setPrompt(maybePrompt);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
catch { }
|
|
1359
|
+
}
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
setIsLoading(true);
|
|
1363
|
+
setError(null);
|
|
1364
|
+
const agentData = await getAgent(agentStub.id);
|
|
1365
|
+
setAgent(agentData);
|
|
1366
|
+
// Merge database messages with any existing local messages using ID-based deduplication
|
|
1367
|
+
// This prevents both missing messages and duplicates
|
|
1368
|
+
setMessages((prevMessages) => {
|
|
1369
|
+
const dbMessages = agentData.messages || [];
|
|
1370
|
+
// Track all DB message IDs as "seen" to prevent WebSocket duplicates
|
|
1371
|
+
dbMessages.forEach((msg) => {
|
|
1372
|
+
if (msg.id) {
|
|
1373
|
+
seenMessageIdsRef.current.add(msg.id.toLowerCase());
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
const merged = mergeMessagesById(dbMessages, prevMessages);
|
|
1377
|
+
return merged;
|
|
1378
|
+
});
|
|
1379
|
+
// Check if cost limit was exceeded (detect from existing messages)
|
|
1380
|
+
try {
|
|
1381
|
+
const costLimitMessage = (agentData.messages || []).find((msg) => msg.role === "assistant" &&
|
|
1382
|
+
msg.content &&
|
|
1383
|
+
msg.content.startsWith("⚠️") &&
|
|
1384
|
+
msg.content.includes("Cost limit"));
|
|
1385
|
+
if (costLimitMessage &&
|
|
1386
|
+
costLimitMessage.content &&
|
|
1387
|
+
agentData.costLimit) {
|
|
1388
|
+
// Extract cost from the message if possible
|
|
1389
|
+
const match = costLimitMessage.content.match(/Current cost: \$([0-9.]+)/);
|
|
1390
|
+
const totalCost = match && match[1] ? parseFloat(match[1]) : agentData.totalCost || 0;
|
|
1391
|
+
setCostLimitExceeded({
|
|
1392
|
+
totalCost: totalCost,
|
|
1393
|
+
costLimit: agentData.costLimit,
|
|
1394
|
+
initialCostLimit: agentData.costLimit,
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
else if (agentData.costLimit &&
|
|
1398
|
+
agentData.totalCost &&
|
|
1399
|
+
agentData.totalCost > agentData.costLimit) {
|
|
1400
|
+
setCostLimitExceeded({
|
|
1401
|
+
totalCost: agentData.totalCost,
|
|
1402
|
+
costLimit: agentData.costLimit,
|
|
1403
|
+
initialCostLimit: agentData.costLimit,
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
catch (e) {
|
|
1408
|
+
console.error("Failed to check cost limit on load:", e);
|
|
1409
|
+
}
|
|
1410
|
+
// Parse metadata from DB if present (do not seed for existing agents)
|
|
1411
|
+
const parsedMeta = (() => {
|
|
1412
|
+
try {
|
|
1413
|
+
const contextJson = agentData.agentContext;
|
|
1414
|
+
if (!contextJson)
|
|
1415
|
+
return null;
|
|
1416
|
+
const parsedContext = JSON.parse(contextJson);
|
|
1417
|
+
// Context is stored as flat structure with top-level properties
|
|
1418
|
+
if (parsedContext && typeof parsedContext === "object") {
|
|
1419
|
+
return parsedContext;
|
|
1420
|
+
}
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
catch {
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
})();
|
|
1427
|
+
// For existing agents, use database metadata or none
|
|
1428
|
+
setAgentMetadata(sanitizeAgentMetadata(parsedMeta));
|
|
1429
|
+
}
|
|
1430
|
+
catch (err) {
|
|
1431
|
+
console.error("❌ Failed to load agent:", err);
|
|
1432
|
+
// For new agents that don't exist yet, this is expected
|
|
1433
|
+
if (err?.message?.includes("404") ||
|
|
1434
|
+
err?.message?.includes("not found")) {
|
|
1435
|
+
// Agent does not exist, treat as new
|
|
1436
|
+
setAgent(undefined);
|
|
1437
|
+
setMessages([]);
|
|
1438
|
+
setError(null);
|
|
1439
|
+
}
|
|
1440
|
+
else {
|
|
1441
|
+
console.error("❌ Load error:", err.message);
|
|
1442
|
+
setError("Failed to load agent data");
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
finally {
|
|
1446
|
+
setIsLoading(false);
|
|
1447
|
+
}
|
|
1448
|
+
}, [agentStub.id]);
|
|
1449
|
+
// Initial load
|
|
1450
|
+
useEffect(() => {
|
|
1451
|
+
loadAgent();
|
|
1452
|
+
}, [loadAgent]);
|
|
1453
|
+
// Reload agent when tab becomes active to get latest messages
|
|
1454
|
+
const previousIsActiveRef = useRef(isActive);
|
|
1455
|
+
useEffect(() => {
|
|
1456
|
+
const wasInactive = !previousIsActiveRef.current;
|
|
1457
|
+
const isNowActive = isActive;
|
|
1458
|
+
previousIsActiveRef.current = isActive;
|
|
1459
|
+
if (wasInactive && isNowActive && agent) {
|
|
1460
|
+
loadAgent();
|
|
1461
|
+
}
|
|
1462
|
+
}, [isActive, agent?.id, loadAgent]);
|
|
1463
|
+
// Watch for cost limit exceeded based on agent status or cost values
|
|
1464
|
+
useEffect(() => {
|
|
1465
|
+
if (!agent) {
|
|
1466
|
+
setCostLimitExceeded(null);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
// Check if cost limit exceeded based on status or cost values
|
|
1470
|
+
const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
|
|
1471
|
+
const costExceedsLimit = agent.costLimit && agent.totalCost && agent.totalCost > agent.costLimit;
|
|
1472
|
+
if (statusIndicatesLimit || costExceedsLimit) {
|
|
1473
|
+
// Only set if not already set to avoid unnecessary re-renders
|
|
1474
|
+
setCostLimitExceeded((prev) => {
|
|
1475
|
+
const totalCost = agent.totalCost || 0;
|
|
1476
|
+
const costLimit = agent.costLimit || 0;
|
|
1477
|
+
// Check if values actually changed
|
|
1478
|
+
if (prev?.totalCost === totalCost && prev?.costLimit === costLimit) {
|
|
1479
|
+
return prev;
|
|
1480
|
+
}
|
|
1481
|
+
return {
|
|
1482
|
+
totalCost,
|
|
1483
|
+
costLimit,
|
|
1484
|
+
initialCostLimit: costLimit,
|
|
1485
|
+
};
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
else {
|
|
1489
|
+
// Clear cost limit exceeded if status changed back
|
|
1490
|
+
setCostLimitExceeded((prev) => (prev ? null : prev));
|
|
1491
|
+
}
|
|
1492
|
+
}, [agent?.status, agent?.totalCost, agent?.costLimit]);
|
|
1493
|
+
// WebSocket message handler for agent streaming
|
|
1494
|
+
const handleAgentWebSocketMessage = useCallback((message) => {
|
|
1495
|
+
if (!agent)
|
|
1496
|
+
return;
|
|
1497
|
+
const messageType = message.type;
|
|
1498
|
+
// Handle agent:name:updated (payload structure is different)
|
|
1499
|
+
if (messageType === "agent:name:updated") {
|
|
1500
|
+
const { agentId: updatedAgentId, agentName } = message.payload;
|
|
1501
|
+
if (updatedAgentId === agent.id && agentName) {
|
|
1502
|
+
setAgent((prev) => (prev ? { ...prev, name: agentName } : prev));
|
|
1503
|
+
}
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
// For other agent messages, check if this is for our agent
|
|
1507
|
+
const agentId = message.payload?.agentId;
|
|
1508
|
+
if (agentId !== agent.id)
|
|
1509
|
+
return;
|
|
1510
|
+
// Handle agent:run:start
|
|
1511
|
+
if (messageType === "agent:run:start") {
|
|
1512
|
+
// Reset run-scoped deduplication so new run seq values (starting at 1)
|
|
1513
|
+
// are not discarded due to previous run's lastSeqRef
|
|
1514
|
+
lastSeqRef.current = 0;
|
|
1515
|
+
// Prep streaming UI state for the new run
|
|
1516
|
+
setIsConnecting(true);
|
|
1517
|
+
setIsWaitingForResponse(true);
|
|
1518
|
+
isWaitingRef.current = true;
|
|
1519
|
+
shouldCreateNewMessage.current = false;
|
|
1520
|
+
resetDotsTimer();
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
// Handle agent:user:message
|
|
1524
|
+
if (messageType === "agent:user:message") {
|
|
1525
|
+
const { messageId, content, timestamp } = message.payload;
|
|
1526
|
+
// Track in seenMessageIds for deduplication
|
|
1527
|
+
const normalizedId = messageId.toLowerCase();
|
|
1528
|
+
if (seenMessageIdsRef.current.has(normalizedId)) {
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
seenMessageIdsRef.current.add(normalizedId);
|
|
1532
|
+
// Add user message to the messages list
|
|
1533
|
+
setMessages((prev) => {
|
|
1534
|
+
// Double-check if message already exists (deduplication)
|
|
1535
|
+
if (prev.some((m) => m.id && m.id.toLowerCase() === normalizedId)) {
|
|
1536
|
+
return prev;
|
|
1537
|
+
}
|
|
1538
|
+
const userMessage = {
|
|
1539
|
+
id: messageId,
|
|
1540
|
+
agentId: agent.id,
|
|
1541
|
+
messageIndex: prev.length,
|
|
1542
|
+
role: "user",
|
|
1543
|
+
content: content,
|
|
1544
|
+
name: "user",
|
|
1545
|
+
messageType: "user",
|
|
1546
|
+
isCompleted: true,
|
|
1547
|
+
model: "",
|
|
1548
|
+
tokensUsed: 0,
|
|
1549
|
+
inputTokens: 0,
|
|
1550
|
+
outputTokens: 0,
|
|
1551
|
+
cachedInputTokens: 0,
|
|
1552
|
+
inputTokenCost: 0,
|
|
1553
|
+
outputTokenCost: 0,
|
|
1554
|
+
cachedInputTokenCost: 0,
|
|
1555
|
+
totalCost: 0,
|
|
1556
|
+
currency: "USD",
|
|
1557
|
+
createdDate: timestamp || new Date().toISOString(),
|
|
1558
|
+
toolCalls: [],
|
|
1559
|
+
};
|
|
1560
|
+
const updated = [...prev, userMessage];
|
|
1561
|
+
messagesRef.current = updated;
|
|
1562
|
+
return updated;
|
|
1563
|
+
});
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
// Handle agent:run:delta (content, tools, etc.)
|
|
1567
|
+
if (messageType === "agent:run:delta") {
|
|
1568
|
+
const { seq, type, data, cost } = message.payload;
|
|
1569
|
+
// Deduplicate by sequence
|
|
1570
|
+
if (seq && seq <= lastSeqRef.current) {
|
|
1571
|
+
return; // Already processed
|
|
1572
|
+
}
|
|
1573
|
+
if (seq) {
|
|
1574
|
+
lastSeqRef.current = seq;
|
|
1575
|
+
}
|
|
1576
|
+
// Route based on delta type
|
|
1577
|
+
const agentStreamMessage = {
|
|
1578
|
+
type,
|
|
1579
|
+
data,
|
|
1580
|
+
cost,
|
|
1581
|
+
timestamp: new Date().toISOString(),
|
|
1582
|
+
};
|
|
1583
|
+
if (type === "ContentChunk" || type === "contentChunk") {
|
|
1584
|
+
handleContentChunk(agentStreamMessage, agent);
|
|
1585
|
+
}
|
|
1586
|
+
else if (type === "ToolCall" || type === "toolCall") {
|
|
1587
|
+
handleToolCall(agentStreamMessage, agent);
|
|
1588
|
+
}
|
|
1589
|
+
else if (type === "ToolResult" || type === "toolResult") {
|
|
1590
|
+
handleToolResult(agentStreamMessage, agent);
|
|
1591
|
+
}
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
// Unified: agent:run:status (state only)
|
|
1595
|
+
if (messageType === "agent:run:status") {
|
|
1596
|
+
const { seq, data: statusData } = message.payload;
|
|
1597
|
+
// Deduplicate by sequence
|
|
1598
|
+
if (seq && seq <= lastSeqRef.current) {
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
if (seq) {
|
|
1602
|
+
lastSeqRef.current = seq;
|
|
1603
|
+
}
|
|
1604
|
+
// Route based on statusData.state
|
|
1605
|
+
try {
|
|
1606
|
+
if (statusData?.state === "streamOpen") {
|
|
1607
|
+
setIsConnecting(false);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
if (statusData?.state === "tokenUsage") {
|
|
1611
|
+
const totals = statusData?.totals;
|
|
1612
|
+
if (totals) {
|
|
1613
|
+
const totalCost = Number(totals.totalCost) || 0;
|
|
1614
|
+
const statusCostLimit = (() => {
|
|
1615
|
+
try {
|
|
1616
|
+
const v = statusData?.costLimit;
|
|
1617
|
+
const n = v != null ? Number(v) : undefined;
|
|
1618
|
+
return Number.isFinite(n) && n > 0
|
|
1619
|
+
? n
|
|
1620
|
+
: undefined;
|
|
1621
|
+
}
|
|
1622
|
+
catch {
|
|
1623
|
+
return undefined;
|
|
1624
|
+
}
|
|
1625
|
+
})();
|
|
1626
|
+
const nextTotals = {
|
|
1627
|
+
input: Number(totals.totalInputTokens) || 0,
|
|
1628
|
+
output: Number(totals.totalOutputTokens) || 0,
|
|
1629
|
+
cached: Number(totals.totalCachedInputTokens) || 0,
|
|
1630
|
+
cacheWrite: Number(totals.totalCacheWriteTokens) || 0,
|
|
1631
|
+
inputCost: Number(totals.totalInputTokenCost) || 0,
|
|
1632
|
+
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
1633
|
+
cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
|
|
1634
|
+
cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
|
|
1635
|
+
totalCost: totalCost,
|
|
1636
|
+
currency: totals.currency,
|
|
1637
|
+
};
|
|
1638
|
+
const anyNonZero = (nextTotals.totalCost || 0) > 0 ||
|
|
1639
|
+
(nextTotals.input || 0) > 0 ||
|
|
1640
|
+
(nextTotals.output || 0) > 0 ||
|
|
1641
|
+
(nextTotals.cached || 0) > 0 ||
|
|
1642
|
+
(nextTotals.cacheWrite || 0) > 0;
|
|
1643
|
+
if (anyNonZero) {
|
|
1644
|
+
setLiveTotals(nextTotals);
|
|
1645
|
+
}
|
|
1646
|
+
// If server provides costLimit along with totals, persist it locally
|
|
1647
|
+
if (statusCostLimit) {
|
|
1648
|
+
setAgent((prev) => prev &&
|
|
1649
|
+
(!prev.costLimit || prev.costLimit !== statusCostLimit)
|
|
1650
|
+
? { ...prev, costLimit: statusCostLimit }
|
|
1651
|
+
: prev);
|
|
1652
|
+
}
|
|
1653
|
+
if (agent?.costLimit && totalCost > agent.costLimit) {
|
|
1654
|
+
setCostLimitExceeded({
|
|
1655
|
+
totalCost: totalCost,
|
|
1656
|
+
costLimit: agent.costLimit,
|
|
1657
|
+
initialCostLimit: agent.costLimit,
|
|
1658
|
+
});
|
|
1659
|
+
setIsWaitingForResponse(false);
|
|
1660
|
+
shouldCreateNewMessage.current = false;
|
|
1661
|
+
}
|
|
1662
|
+
setMessages((prev) => [...prev]);
|
|
1663
|
+
}
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
if (statusData?.state === "ToolApprovalsRequired") {
|
|
1667
|
+
const msgId = statusData.messageId;
|
|
1668
|
+
const ids = statusData.toolCallIds || [];
|
|
1669
|
+
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
1670
|
+
setMessages((prev) => {
|
|
1671
|
+
const updated = prev.map((m) => {
|
|
1672
|
+
if (m.id !== msgId)
|
|
1673
|
+
return m;
|
|
1674
|
+
const existingToolCalls = m.toolCalls || [];
|
|
1675
|
+
const updatedToolCalls = existingToolCalls.map((tc) => {
|
|
1676
|
+
if (!ids.includes(tc.toolCallId))
|
|
1677
|
+
return tc;
|
|
1678
|
+
const fn = tc.functionName || "";
|
|
1679
|
+
return {
|
|
1680
|
+
...tc,
|
|
1681
|
+
functionName: fn.includes("(pending approval)")
|
|
1682
|
+
? fn
|
|
1683
|
+
: fn + " (pending approval)",
|
|
1684
|
+
};
|
|
1685
|
+
});
|
|
1686
|
+
return { ...m, toolCalls: updatedToolCalls };
|
|
1687
|
+
});
|
|
1688
|
+
messagesRef.current = updated;
|
|
1689
|
+
return updated;
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
setIsConnecting(false);
|
|
1693
|
+
setIsWaitingForResponse(false);
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
if (statusData?.state === "CostLimitReached") {
|
|
1697
|
+
const totalCost = Number(statusData.totalCost) || 0;
|
|
1698
|
+
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
1699
|
+
setCostLimitExceeded({
|
|
1700
|
+
totalCost: totalCost,
|
|
1701
|
+
costLimit: costLimit,
|
|
1702
|
+
initialCostLimit: costLimit,
|
|
1703
|
+
});
|
|
1704
|
+
setIsWaitingForResponse(false);
|
|
1705
|
+
setIsConnecting(false);
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
if (statusData?.state === "contextWindow") {
|
|
1709
|
+
window.__agentContextWindowStatus = {
|
|
1710
|
+
model: statusData.model,
|
|
1711
|
+
normalizedModel: statusData.normalizedModel,
|
|
1712
|
+
contextWindowTokens: statusData.contextWindowTokens,
|
|
1713
|
+
maxCompletionTokens: statusData.maxCompletionTokens,
|
|
1714
|
+
estimatedInputTokens: statusData.estimatedInputTokens,
|
|
1715
|
+
messageCount: statusData.messageCount,
|
|
1716
|
+
contextUsedPercent: statusData.contextUsedPercent,
|
|
1717
|
+
};
|
|
1718
|
+
setMessages((prev) => [...prev]);
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
if (statusData?.state === "contextChanged") {
|
|
1722
|
+
const nextContext = statusData.context || {};
|
|
1723
|
+
setAgentMetadata((prev) => {
|
|
1724
|
+
const current = (prev || {});
|
|
1725
|
+
const currentWithoutContext = { ...current };
|
|
1726
|
+
delete currentWithoutContext.context;
|
|
1727
|
+
const next = {
|
|
1728
|
+
...currentWithoutContext,
|
|
1729
|
+
additionalData: {
|
|
1730
|
+
...(current.additionalData || {}),
|
|
1731
|
+
context: nextContext,
|
|
1732
|
+
},
|
|
1733
|
+
};
|
|
1734
|
+
return next;
|
|
1735
|
+
});
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
catch (err) {
|
|
1740
|
+
console.error("[AgentTerminal] Error handling status update:", err);
|
|
1741
|
+
}
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
// Lifecycle: agent:run:complete
|
|
1745
|
+
if (messageType === "agent:run:complete") {
|
|
1746
|
+
// Reset deduplication for the next run
|
|
1747
|
+
lastSeqRef.current = 0;
|
|
1748
|
+
// Mark the last assistant message as completed
|
|
1749
|
+
setMessages((prev) => {
|
|
1750
|
+
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
1751
|
+
? {
|
|
1752
|
+
...msg,
|
|
1753
|
+
isCompleted: true,
|
|
1754
|
+
messageType: "completed",
|
|
1755
|
+
}
|
|
1756
|
+
: msg);
|
|
1757
|
+
messagesRef.current = updated;
|
|
1758
|
+
return updated;
|
|
1759
|
+
});
|
|
1760
|
+
setIsWaitingForResponse(false);
|
|
1761
|
+
isWaitingRef.current = false;
|
|
1762
|
+
setIsConnecting(false);
|
|
1763
|
+
shouldCreateNewMessage.current = false;
|
|
1764
|
+
resetDotsTimer();
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
// Lifecycle: agent:run:error
|
|
1768
|
+
if (messageType === "agent:run:error") {
|
|
1769
|
+
const errorMsg = message.payload?.error || "Unknown error";
|
|
1770
|
+
// Reset deduplication for the next run after an error
|
|
1771
|
+
lastSeqRef.current = 0;
|
|
1772
|
+
setError(errorMsg);
|
|
1773
|
+
setIsWaitingForResponse(false);
|
|
1774
|
+
isWaitingRef.current = false;
|
|
1775
|
+
setIsConnecting(false);
|
|
1776
|
+
resetDotsTimer();
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
}, [
|
|
1780
|
+
agent,
|
|
1781
|
+
handleContentChunk,
|
|
1782
|
+
handleToolCall,
|
|
1783
|
+
handleToolResult,
|
|
1784
|
+
resetDotsTimer,
|
|
1785
|
+
]);
|
|
1786
|
+
// Keep refs for latest agent and resetDotsTimer to avoid adding them to effect deps
|
|
1787
|
+
const agentRef = useRef(agent);
|
|
1788
|
+
const resetDotsTimerRef = useRef(resetDotsTimer);
|
|
1789
|
+
const handleAgentWebSocketMessageRef = useRef(handleAgentWebSocketMessage);
|
|
1790
|
+
useEffect(() => {
|
|
1791
|
+
agentRef.current = agent;
|
|
1792
|
+
}, [agent]);
|
|
1793
|
+
useEffect(() => {
|
|
1794
|
+
resetDotsTimerRef.current = resetDotsTimer;
|
|
1795
|
+
}, [resetDotsTimer]);
|
|
1796
|
+
useEffect(() => {
|
|
1797
|
+
handleAgentWebSocketMessageRef.current = handleAgentWebSocketMessage;
|
|
1798
|
+
}, [handleAgentWebSocketMessage]);
|
|
1799
|
+
// Subscribe to agent WebSocket messages when active
|
|
1800
|
+
useEffect(() => {
|
|
1801
|
+
const addListener = editContext?.addSocketMessageListener;
|
|
1802
|
+
if (!isActive || !addListener) {
|
|
1803
|
+
// Unsubscribe if we were previously subscribed
|
|
1804
|
+
if (subscribedAgentIdRef.current) {
|
|
1805
|
+
const socket = globalThis.editorSocket;
|
|
1806
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
1807
|
+
socket.send(JSON.stringify({
|
|
1808
|
+
type: "agent:unsubscribe",
|
|
1809
|
+
agentId: subscribedAgentIdRef.current,
|
|
1810
|
+
}));
|
|
1811
|
+
}
|
|
1812
|
+
subscribedAgentIdRef.current = null;
|
|
1813
|
+
}
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
// Send subscription message to server
|
|
1817
|
+
const socket = globalThis.editorSocket;
|
|
1818
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
1819
|
+
socket.send(JSON.stringify({
|
|
1820
|
+
type: "agent:subscribe",
|
|
1821
|
+
agentId: agentStub.id,
|
|
1822
|
+
}));
|
|
1823
|
+
}
|
|
1824
|
+
// Use the addSocketMessageListener helper from editContext
|
|
1825
|
+
// Wrap the handler in a stable function that uses the ref
|
|
1826
|
+
const stableHandler = (message) => {
|
|
1827
|
+
handleAgentWebSocketMessageRef.current(message);
|
|
1828
|
+
};
|
|
1829
|
+
const unsubscribe = addListener(stableHandler);
|
|
1830
|
+
subscribedAgentIdRef.current = agentStub.id;
|
|
1831
|
+
// Reset deduplication state when switching agents
|
|
1832
|
+
seenMessageIdsRef.current.clear();
|
|
1833
|
+
lastSeqRef.current = 0;
|
|
1834
|
+
// Set up streaming state based on agent status (uses latest values via refs)
|
|
1835
|
+
const currentAgent = agentRef.current;
|
|
1836
|
+
if (currentAgent) {
|
|
1837
|
+
const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
|
|
1838
|
+
const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
|
|
1839
|
+
currentAgent.status === 2;
|
|
1840
|
+
if (isRunning) {
|
|
1841
|
+
setIsWaitingForResponse(true);
|
|
1842
|
+
isWaitingRef.current = true;
|
|
1843
|
+
shouldCreateNewMessage.current = false;
|
|
1844
|
+
resetDotsTimerRef.current();
|
|
1845
|
+
}
|
|
1846
|
+
else if (isWaitingForApproval) {
|
|
1847
|
+
setIsWaitingForResponse(false);
|
|
1848
|
+
isWaitingRef.current = false;
|
|
1849
|
+
resetDotsTimerRef.current();
|
|
1850
|
+
}
|
|
1851
|
+
else {
|
|
1852
|
+
setIsWaitingForResponse(false);
|
|
1853
|
+
isWaitingRef.current = false;
|
|
1854
|
+
resetDotsTimerRef.current();
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
return () => {
|
|
1858
|
+
// Send unsubscribe message to server
|
|
1859
|
+
const socket = globalThis.editorSocket;
|
|
1860
|
+
if (socket &&
|
|
1861
|
+
socket.readyState === WebSocket.OPEN &&
|
|
1862
|
+
subscribedAgentIdRef.current) {
|
|
1863
|
+
socket.send(JSON.stringify({
|
|
1864
|
+
type: "agent:unsubscribe",
|
|
1865
|
+
agentId: subscribedAgentIdRef.current,
|
|
1866
|
+
}));
|
|
1867
|
+
}
|
|
1868
|
+
unsubscribe();
|
|
1869
|
+
subscribedAgentIdRef.current = null;
|
|
1870
|
+
};
|
|
1871
|
+
}, [isActive, agentStub.id, editContext?.addSocketMessageListener]);
|
|
1872
|
+
// Focus prompt when requested globally (from AI command)
|
|
1873
|
+
useEffect(() => {
|
|
1874
|
+
const focusHandler = () => {
|
|
1875
|
+
try {
|
|
1876
|
+
if (textareaRef.current) {
|
|
1877
|
+
textareaRef.current.focus();
|
|
1878
|
+
// Move caret to end
|
|
1879
|
+
const value = textareaRef.current.value || "";
|
|
1880
|
+
textareaRef.current.selectionStart = value.length;
|
|
1881
|
+
textareaRef.current.selectionEnd = value.length;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
catch { }
|
|
1885
|
+
};
|
|
1886
|
+
window.addEventListener("editor:focusAgentPrompt", focusHandler);
|
|
1887
|
+
return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
|
|
1888
|
+
}, []);
|
|
1889
|
+
// Profiles are provided by parent component (Agents). No local loading here.
|
|
1890
|
+
// Select active profile based on agent.profileId or agentStub.profileId
|
|
1891
|
+
useEffect(() => {
|
|
1892
|
+
if (!profiles || profiles.length === 0)
|
|
1893
|
+
return;
|
|
1894
|
+
// For new agents, use agentStub.profileId; for loaded agents, use agent.profileId
|
|
1895
|
+
const profileIdToUse = agent?.profileId || agentStub.profileId;
|
|
1896
|
+
const candidate = profileIdToUse
|
|
1897
|
+
? (profiles.find((p) => p.id === profileIdToUse) ?? profiles[0])
|
|
1898
|
+
: profiles[0];
|
|
1899
|
+
if (candidate && (!activeProfile || activeProfile.id !== candidate.id)) {
|
|
1900
|
+
setActiveProfile(candidate);
|
|
1901
|
+
}
|
|
1902
|
+
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
1903
|
+
// Update selected model when the active profile or agent model changes
|
|
1904
|
+
useEffect(() => {
|
|
1905
|
+
if (!activeProfile)
|
|
1906
|
+
return;
|
|
1907
|
+
const agentModelName = agent?.model; // persisted as model NAME on server
|
|
1908
|
+
const models = activeProfile.models || [];
|
|
1909
|
+
let nextModelId = undefined;
|
|
1910
|
+
if (agentModelName) {
|
|
1911
|
+
const match = models.find((m) => (m.name || "").toLowerCase() === agentModelName.toLowerCase());
|
|
1912
|
+
if (match)
|
|
1913
|
+
nextModelId = match.id;
|
|
1914
|
+
}
|
|
1915
|
+
if (!nextModelId) {
|
|
1916
|
+
nextModelId = activeProfile.defaultModelId || models[0]?.id;
|
|
1917
|
+
}
|
|
1918
|
+
setSelectedModelId(nextModelId || undefined);
|
|
1919
|
+
}, [activeProfile?.id, agent?.model]);
|
|
1920
|
+
// Cleanup stream connection when component unmounts or agent changes
|
|
1921
|
+
useEffect(() => {
|
|
1922
|
+
return () => {
|
|
1923
|
+
if (abortControllerRef.current) {
|
|
1924
|
+
abortControllerRef.current.abort();
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
}, [agent?.id]);
|
|
1928
|
+
// Initialize mode from metadata; fall back to agent.Mode from server
|
|
1929
|
+
useEffect(() => {
|
|
1930
|
+
try {
|
|
1931
|
+
const metaMode = agentMetadata?.mode;
|
|
1932
|
+
if (metaMode === "autonomous" ||
|
|
1933
|
+
metaMode === "read-only" ||
|
|
1934
|
+
metaMode === "supervised") {
|
|
1935
|
+
setMode(metaMode);
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
catch { }
|
|
1940
|
+
try {
|
|
1941
|
+
const serverMode = agent?.mode;
|
|
1942
|
+
if (serverMode === "autonomous" ||
|
|
1943
|
+
serverMode === "read-only" ||
|
|
1944
|
+
serverMode === "supervised") {
|
|
1945
|
+
setMode(serverMode);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
catch { }
|
|
1949
|
+
}, [agentMetadata, agent?.mode]);
|
|
1950
|
+
// Auto-scroll when messages change (only if user hasn't manually scrolled up)
|
|
1951
|
+
useLayoutEffect(() => {
|
|
1952
|
+
if (shouldAutoScroll) {
|
|
1953
|
+
scrollToBottom();
|
|
1954
|
+
}
|
|
1955
|
+
}, [messages, scrollToBottom, shouldAutoScroll]);
|
|
1956
|
+
// Persist any pending settings (mode/model) once an agent exists server-side
|
|
1957
|
+
const persistPendingSettingsIfNeeded = useCallback(async () => {
|
|
1958
|
+
try {
|
|
1959
|
+
if (!agent?.id)
|
|
1960
|
+
return;
|
|
1961
|
+
const pending = pendingSettingsRef.current;
|
|
1962
|
+
if (!pending)
|
|
1963
|
+
return;
|
|
1964
|
+
const payload = {};
|
|
1965
|
+
if (pending.modelName)
|
|
1966
|
+
payload.model = pending.modelName;
|
|
1967
|
+
if (pending.mode)
|
|
1968
|
+
payload.mode = pending.mode;
|
|
1969
|
+
if (Object.keys(payload).length === 0)
|
|
1970
|
+
return;
|
|
1971
|
+
await updateAgentSettings(agent.id, payload);
|
|
1972
|
+
pendingSettingsRef.current = null;
|
|
1973
|
+
}
|
|
1974
|
+
catch (e) {
|
|
1975
|
+
console.error("Failed to persist pending settings", e);
|
|
1976
|
+
}
|
|
1977
|
+
}, [agent?.id]);
|
|
1978
|
+
const handleSubmit = async () => {
|
|
1979
|
+
if (isSubmitting || !editContext)
|
|
1980
|
+
return;
|
|
1981
|
+
try {
|
|
1982
|
+
setIsSubmitting(true);
|
|
1983
|
+
setError(null);
|
|
1984
|
+
// For new agents, use agentStub.id; for existing agents, use agent.id
|
|
1985
|
+
const agentId = agent?.id || agentStub.id;
|
|
1986
|
+
if (!agentId)
|
|
1987
|
+
return;
|
|
1988
|
+
// Optional context factory: invoke if configured and available, otherwise continue
|
|
1989
|
+
const factoryName = agentMetadata?.additionalData
|
|
1990
|
+
?.contextFactory;
|
|
1991
|
+
if (factoryName) {
|
|
1992
|
+
const factory = editContext.getContextFactory?.(factoryName);
|
|
1993
|
+
if (factory) {
|
|
1994
|
+
try {
|
|
1995
|
+
await Promise.resolve(factory());
|
|
1996
|
+
}
|
|
1997
|
+
catch (e) {
|
|
1998
|
+
console.warn(`Context factory '${factoryName}' failed: ${e?.message || String(e)}`);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
else {
|
|
2002
|
+
console.warn(`Context factory not found: ${factoryName}. Proceeding without it.`);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
// NOTE: User message is no longer added optimistically here
|
|
2006
|
+
// It will be added when we receive the agent:user:message broadcast from the server
|
|
2007
|
+
// This ensures all tabs (including the sending tab) have the same messageId from the database
|
|
2008
|
+
const request = {
|
|
2009
|
+
agentId: agentId,
|
|
2010
|
+
message: prompt.trim(),
|
|
2011
|
+
sessionId: editContext.sessionId,
|
|
2012
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
2013
|
+
profile: activeProfile?.name || profiles[0]?.name || "",
|
|
2014
|
+
model: selectedModelId,
|
|
2015
|
+
mode: mode,
|
|
2016
|
+
context: canonicalizeAgentMetadata(agentMetadata), // Canonicalize to ensure complete field structure
|
|
2017
|
+
deterministic: deterministicFlags.deterministic,
|
|
2018
|
+
seed: deterministicFlags.seed,
|
|
2019
|
+
};
|
|
2020
|
+
// Starting agent
|
|
2021
|
+
// Set waiting state to show dancing dots immediately
|
|
2022
|
+
setIsWaitingForResponse(true);
|
|
2023
|
+
// Update the ref immediately so resetDotsTimer sees the new state
|
|
2024
|
+
isWaitingRef.current = true;
|
|
2025
|
+
// Start idle timer; dots appear only if no chunks for >1s
|
|
2026
|
+
resetDotsTimer();
|
|
2027
|
+
// Re-enable auto-scroll when user submits a new message
|
|
2028
|
+
setShouldAutoScroll(true);
|
|
2029
|
+
// No need to create a temporary message - the stream will create messages as needed
|
|
2030
|
+
await startAgent(request);
|
|
2031
|
+
// If user changed mode/model while the agent was new, persist them now
|
|
2032
|
+
await persistPendingSettingsIfNeeded();
|
|
2033
|
+
// Save prompt to history
|
|
2034
|
+
if (prompt.trim()) {
|
|
2035
|
+
setPromptHistory((prev) => [
|
|
2036
|
+
prompt.trim(),
|
|
2037
|
+
...prev.filter((p) => p !== prompt.trim()).slice(0, 9),
|
|
2038
|
+
]);
|
|
2039
|
+
setCurrentHistoryIndex(-1);
|
|
2040
|
+
}
|
|
2041
|
+
setPrompt("");
|
|
2042
|
+
// WebSocket connection is already active via subscription - no need for SSE
|
|
2043
|
+
}
|
|
2044
|
+
catch (err) {
|
|
2045
|
+
console.error("Failed to submit prompt:", err);
|
|
2046
|
+
setError("Failed to submit prompt");
|
|
2047
|
+
setIsWaitingForResponse(false);
|
|
2048
|
+
// Remove the optimistically added user message on error
|
|
2049
|
+
setMessages((prev) => prev.slice(0, -1));
|
|
2050
|
+
}
|
|
2051
|
+
finally {
|
|
2052
|
+
setIsSubmitting(false);
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
const handleKeyPress = (e) => {
|
|
2056
|
+
// Submit only on plain Enter (no Ctrl/Meta/Shift/Alt)
|
|
2057
|
+
if (e.key === "Enter" &&
|
|
2058
|
+
!e.ctrlKey &&
|
|
2059
|
+
!e.metaKey &&
|
|
2060
|
+
!e.shiftKey &&
|
|
2061
|
+
!e.altKey) {
|
|
2062
|
+
e.preventDefault();
|
|
2063
|
+
handleSubmit();
|
|
2064
|
+
}
|
|
2065
|
+
if (e.key === "ArrowUp") {
|
|
2066
|
+
// Only navigate history if prompt is empty or we're already navigating history
|
|
2067
|
+
const canNavigateHistory = prompt.trim().length === 0 || currentHistoryIndex !== -1;
|
|
2068
|
+
if (canNavigateHistory) {
|
|
2069
|
+
e.preventDefault();
|
|
2070
|
+
if (promptHistory.length > 0) {
|
|
2071
|
+
const newIndex = currentHistoryIndex < promptHistory.length - 1
|
|
2072
|
+
? currentHistoryIndex + 1
|
|
2073
|
+
: currentHistoryIndex;
|
|
2074
|
+
setCurrentHistoryIndex(newIndex);
|
|
2075
|
+
const historicalPrompt = promptHistory[newIndex];
|
|
2076
|
+
if (textareaRef.current && historicalPrompt) {
|
|
2077
|
+
setPrompt(historicalPrompt);
|
|
2078
|
+
setTimeout(() => {
|
|
2079
|
+
if (textareaRef.current) {
|
|
2080
|
+
textareaRef.current.selectionStart = historicalPrompt.length;
|
|
2081
|
+
textareaRef.current.selectionEnd = historicalPrompt.length;
|
|
2082
|
+
}
|
|
2083
|
+
}, 0);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (e.key === "ArrowDown" && currentHistoryIndex >= 0) {
|
|
2089
|
+
e.preventDefault();
|
|
2090
|
+
const newIndex = currentHistoryIndex - 1;
|
|
2091
|
+
setCurrentHistoryIndex(newIndex);
|
|
2092
|
+
const historicalPrompt = newIndex >= 0 ? promptHistory[newIndex] || "" : "";
|
|
2093
|
+
setPrompt(historicalPrompt);
|
|
2094
|
+
if (textareaRef.current) {
|
|
2095
|
+
setTimeout(() => {
|
|
2096
|
+
if (textareaRef.current) {
|
|
2097
|
+
textareaRef.current.selectionStart = historicalPrompt.length;
|
|
2098
|
+
textareaRef.current.selectionEnd = historicalPrompt.length;
|
|
2099
|
+
}
|
|
2100
|
+
}, 0);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
// Send a message programmatically (used by quick-action buttons)
|
|
2105
|
+
const sendQuickMessage = async (text) => {
|
|
2106
|
+
if (!text.trim() || isSubmitting || !editContext)
|
|
2107
|
+
return;
|
|
2108
|
+
try {
|
|
2109
|
+
setIsSubmitting(true);
|
|
2110
|
+
setError(null);
|
|
2111
|
+
const agentId = agent?.id;
|
|
2112
|
+
if (!agentId)
|
|
2113
|
+
return;
|
|
2114
|
+
// Optional context factory: invoke if configured and available, otherwise continue
|
|
2115
|
+
const factoryName = agentMetadata?.additionalData
|
|
2116
|
+
?.contextFactory;
|
|
2117
|
+
if (factoryName) {
|
|
2118
|
+
const factory = editContext.getContextFactory?.(factoryName);
|
|
2119
|
+
if (factory) {
|
|
2120
|
+
try {
|
|
2121
|
+
await Promise.resolve(factory());
|
|
2122
|
+
}
|
|
2123
|
+
catch (e) {
|
|
2124
|
+
console.warn(`Context factory '${factoryName}' failed: ${e?.message || String(e)}`);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
else {
|
|
2128
|
+
console.warn(`Context factory not found: ${factoryName}. Proceeding without it.`);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
// NOTE: User message is no longer added optimistically here
|
|
2132
|
+
// It will be added when we receive the agent:user:message broadcast from the server
|
|
2133
|
+
// This ensures all tabs (including the sending tab) have the same messageId from the database
|
|
2134
|
+
const request = {
|
|
2135
|
+
agentId: agent.id,
|
|
2136
|
+
message: text.trim(),
|
|
2137
|
+
sessionId: editContext.sessionId,
|
|
2138
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
2139
|
+
profile: activeProfile?.name || profiles[0]?.name || "",
|
|
2140
|
+
model: selectedModelId,
|
|
2141
|
+
mode: mode,
|
|
2142
|
+
context: canonicalizeAgentMetadata(agentMetadata), // Canonicalize to ensure complete field structure
|
|
2143
|
+
deterministic: deterministicFlags.deterministic,
|
|
2144
|
+
seed: deterministicFlags.seed,
|
|
2145
|
+
};
|
|
2146
|
+
setIsWaitingForResponse(true);
|
|
2147
|
+
// Update the ref immediately so resetDotsTimer sees the new state
|
|
2148
|
+
isWaitingRef.current = true;
|
|
2149
|
+
resetDotsTimer();
|
|
2150
|
+
setShouldAutoScroll(true);
|
|
2151
|
+
await startAgent(request);
|
|
2152
|
+
// If user changed mode/model while the agent was new, persist them now
|
|
2153
|
+
await persistPendingSettingsIfNeeded();
|
|
2154
|
+
// WebSocket connection is already active via subscription - no need for SSE
|
|
2155
|
+
}
|
|
2156
|
+
catch (err) {
|
|
2157
|
+
console.error("Failed to submit quick message:", err);
|
|
2158
|
+
setError("Failed to submit prompt");
|
|
2159
|
+
setIsWaitingForResponse(false);
|
|
2160
|
+
setMessages((prev) => prev.slice(0, -1));
|
|
2161
|
+
}
|
|
2162
|
+
finally {
|
|
2163
|
+
setIsSubmitting(false);
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2166
|
+
// Auto-send initial prompt if provided and agent has no messages yet
|
|
2167
|
+
const initialPromptSentRef = useRef(false);
|
|
2168
|
+
useEffect(() => {
|
|
2169
|
+
if (initialPrompt !== undefined &&
|
|
2170
|
+
!isLoading &&
|
|
2171
|
+
!initialPromptSentRef.current &&
|
|
2172
|
+
messages.length === 0 &&
|
|
2173
|
+
agentStub.id &&
|
|
2174
|
+
editContext &&
|
|
2175
|
+
activeProfile && // MUST have activeProfile set, not just profiles.length
|
|
2176
|
+
profiles.length > 0) {
|
|
2177
|
+
initialPromptSentRef.current = true;
|
|
2178
|
+
// Delay slightly to ensure all state is ready
|
|
2179
|
+
setTimeout(() => {
|
|
2180
|
+
setPrompt(initialPrompt);
|
|
2181
|
+
// Trigger submit programmatically
|
|
2182
|
+
handleSubmit();
|
|
2183
|
+
}, 200);
|
|
2184
|
+
}
|
|
2185
|
+
}, [
|
|
2186
|
+
initialPrompt,
|
|
2187
|
+
isLoading,
|
|
2188
|
+
messages.length,
|
|
2189
|
+
agentStub.id,
|
|
2190
|
+
editContext,
|
|
2191
|
+
activeProfile,
|
|
2192
|
+
profiles.length,
|
|
2193
|
+
handleSubmit,
|
|
2194
|
+
]);
|
|
2195
|
+
// Resolve display names when metadata or editor state changes
|
|
2196
|
+
useEffect(() => {
|
|
2197
|
+
const metaCtx = agentMetadata;
|
|
2198
|
+
if (!metaCtx) {
|
|
2199
|
+
setResolvedPageName(undefined);
|
|
2200
|
+
setResolvedComponentName(undefined);
|
|
2201
|
+
setResolvedFieldName(undefined);
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
// Page names (for now, just resolve the first page)
|
|
2205
|
+
if (metaCtx.items?.length) {
|
|
2206
|
+
const firstPage = metaCtx.items[0];
|
|
2207
|
+
if (firstPage) {
|
|
2208
|
+
let name = firstPage.name;
|
|
2209
|
+
if (!name &&
|
|
2210
|
+
editContext?.contentEditorItem?.descriptor.id === firstPage.id &&
|
|
2211
|
+
editContext?.contentEditorItem?.name) {
|
|
2212
|
+
name = editContext.contentEditorItem.name;
|
|
2213
|
+
}
|
|
2214
|
+
else if (!name) {
|
|
2215
|
+
name = undefined;
|
|
2216
|
+
}
|
|
2217
|
+
setResolvedPageName(name);
|
|
2218
|
+
}
|
|
2219
|
+
else {
|
|
2220
|
+
setResolvedPageName(undefined);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
else
|
|
2224
|
+
setResolvedPageName(undefined);
|
|
2225
|
+
// Component names (for now, just resolve the first component)
|
|
2226
|
+
if (metaCtx.components?.length && editContext?.page) {
|
|
2227
|
+
const firstComponent = metaCtx.components[0];
|
|
2228
|
+
if (firstComponent?.componentId) {
|
|
2229
|
+
try {
|
|
2230
|
+
const comp = getComponentById(firstComponent.componentId, editContext.page);
|
|
2231
|
+
setResolvedComponentName(comp?.name);
|
|
2232
|
+
}
|
|
2233
|
+
catch {
|
|
2234
|
+
setResolvedComponentName(undefined);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
else {
|
|
2238
|
+
setResolvedComponentName(undefined);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
else
|
|
2242
|
+
setResolvedComponentName(undefined);
|
|
2243
|
+
// Field name (async)
|
|
2244
|
+
(async () => {
|
|
2245
|
+
if (metaCtx.field && editContext?.itemsRepository) {
|
|
2246
|
+
try {
|
|
2247
|
+
// Use the item descriptor from field context if available, otherwise fallback to current item
|
|
2248
|
+
const itemDescriptor = metaCtx.field.item || {
|
|
2249
|
+
id: editContext.currentItemDescriptor?.id || "",
|
|
2250
|
+
language: editContext.currentItemDescriptor?.language || "en",
|
|
2251
|
+
version: editContext.currentItemDescriptor?.version || 0,
|
|
2252
|
+
};
|
|
2253
|
+
const field = await editContext.itemsRepository.getField({
|
|
2254
|
+
item: itemDescriptor,
|
|
2255
|
+
fieldId: metaCtx.field.fieldId,
|
|
2256
|
+
});
|
|
2257
|
+
setResolvedFieldName(field?.name || metaCtx.field.fieldName);
|
|
2258
|
+
}
|
|
2259
|
+
catch {
|
|
2260
|
+
setResolvedFieldName(metaCtx.field.fieldName);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
else
|
|
2264
|
+
setResolvedFieldName(undefined);
|
|
2265
|
+
})();
|
|
2266
|
+
}, [
|
|
2267
|
+
agentMetadata,
|
|
2268
|
+
editContext?.page,
|
|
2269
|
+
editContext?.contentEditorItem,
|
|
2270
|
+
editContext?.currentItemDescriptor,
|
|
2271
|
+
editContext?.itemsRepository,
|
|
2272
|
+
]);
|
|
2273
|
+
// Stop current execution/stream safely
|
|
2274
|
+
const handleStop = useCallback(async () => {
|
|
2275
|
+
try {
|
|
2276
|
+
setIsWaitingForResponse(false);
|
|
2277
|
+
// **CRITICAL FIX**: Actually cancel the agent execution on the backend
|
|
2278
|
+
// Before we were only disconnecting from the stream, letting the agent run
|
|
2279
|
+
if (agentStub?.id) {
|
|
2280
|
+
try {
|
|
2281
|
+
await cancelAgent(agentStub.id);
|
|
2282
|
+
console.log(`Agent ${agentStub.id} cancellation requested`);
|
|
2283
|
+
}
|
|
2284
|
+
catch (err) {
|
|
2285
|
+
console.error("Failed to cancel agent on backend:", err);
|
|
2286
|
+
// Continue with UI cleanup even if backend call fails
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
// Disconnect from the stream
|
|
2290
|
+
if (abortControllerRef.current) {
|
|
2291
|
+
abortControllerRef.current.abort();
|
|
2292
|
+
abortControllerRef.current = null;
|
|
2293
|
+
}
|
|
2294
|
+
setIsConnecting(false);
|
|
2295
|
+
setIsSubmitting(false);
|
|
2296
|
+
// Mark any in-progress streaming messages as completed in UI
|
|
2297
|
+
setMessages((prev) => {
|
|
2298
|
+
const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
|
|
2299
|
+
? { ...msg, isCompleted: true, messageType: "completed" }
|
|
2300
|
+
: msg);
|
|
2301
|
+
messagesRef.current = updated;
|
|
2302
|
+
return updated;
|
|
2303
|
+
});
|
|
2304
|
+
// Update indicator state
|
|
2305
|
+
resetDotsTimer();
|
|
2306
|
+
}
|
|
2307
|
+
catch (e) {
|
|
2308
|
+
console.error("Failed to stop agent execution", e);
|
|
2309
|
+
}
|
|
2310
|
+
}, [agentStub?.id, resetDotsTimer]);
|
|
2311
|
+
// Determine effective cost limit from agent, profile, or metadata so the cost display
|
|
2312
|
+
// is visible immediately even before any messages or server-side persistence.
|
|
2313
|
+
const effectiveCostLimit = useMemo(() => {
|
|
2314
|
+
try {
|
|
2315
|
+
const candidates = [
|
|
2316
|
+
agent?.costLimit,
|
|
2317
|
+
activeProfile?.costLimit,
|
|
2318
|
+
];
|
|
2319
|
+
for (const c of candidates) {
|
|
2320
|
+
const n = c != null ? Number(c) : 0;
|
|
2321
|
+
if (Number.isFinite(n) && n > 0)
|
|
2322
|
+
return n;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
catch { }
|
|
2326
|
+
return undefined;
|
|
2327
|
+
}, [agent?.costLimit, activeProfile?.costLimit]);
|
|
2328
|
+
if (isLoading) {
|
|
2329
|
+
return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1 }), "Loading agent..."] }) }));
|
|
2330
|
+
}
|
|
2331
|
+
// Calculate total token usage for cost display
|
|
2332
|
+
const totalTokens = calculateTotalTokens(messages);
|
|
2333
|
+
// Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
|
|
2334
|
+
const isExecuting = isSubmitting ||
|
|
2335
|
+
isConnecting ||
|
|
2336
|
+
isWaitingForResponse ||
|
|
2337
|
+
hasActiveStreaming();
|
|
2338
|
+
const renderContextInfoBar = () => (_jsx(ContextInfoBar, { agent: agent, agentMetadata: agentMetadata, setAgentMetadata: setAgentMetadata, setAgent: setAgent, resolvedPageName: resolvedPageName, resolvedComponentName: resolvedComponentName, resolvedFieldName: resolvedFieldName }));
|
|
2339
|
+
const renderCostLimitBanner = () => {
|
|
2340
|
+
if (!costLimitExceeded)
|
|
2341
|
+
return null;
|
|
2342
|
+
const { totalCost, costLimit, initialCostLimit } = costLimitExceeded;
|
|
2343
|
+
return (_jsxs("div", { className: "m-3 rounded border border-amber-300 bg-amber-50 p-3 text-xs text-amber-900", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-amber-500", strokeWidth: 1 }), _jsxs("span", { children: ["Cost limit exceeded. Spent $", totalCost.toFixed(2), " / $", costLimit.toFixed(2), "."] })] }), _jsx("div", { className: "flex gap-2", children: _jsx("button", { className: "rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100", onClick: async () => {
|
|
2344
|
+
if (!agent?.id)
|
|
2345
|
+
return;
|
|
2346
|
+
try {
|
|
2347
|
+
// Extend cost limit - backend will automatically resume the agent
|
|
2348
|
+
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
2349
|
+
// Update the agent's cost limit in local state
|
|
2350
|
+
if (result.success && result.costLimit !== undefined) {
|
|
2351
|
+
setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
|
|
2352
|
+
}
|
|
2353
|
+
// Clear the banner and set waiting state
|
|
2354
|
+
// Agent will resume automatically via backend's ResumeAgentAsync
|
|
2355
|
+
setCostLimitExceeded(null);
|
|
2356
|
+
setIsWaitingForResponse(true);
|
|
2357
|
+
isWaitingRef.current = true;
|
|
2358
|
+
setShouldAutoScroll(true);
|
|
2359
|
+
}
|
|
2360
|
+
catch (e) {
|
|
2361
|
+
console.error("Failed to extend cost limit:", e);
|
|
2362
|
+
setError(e instanceof Error
|
|
2363
|
+
? e.message
|
|
2364
|
+
: "Failed to extend cost limit");
|
|
2365
|
+
}
|
|
2366
|
+
}, children: "Extend limit and continue" }) })] }));
|
|
2367
|
+
};
|
|
2368
|
+
return (_jsxs("div", { className: `flex h-full flex-col ${className || ""}`, children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error && (_jsx("div", { className: "m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3 select-text", children: _jsxs("div", { className: "flex items-start", children: [_jsx(AlertCircle, { className: "mt-0.5 h-5 w-5 text-red-400", strokeWidth: 1 }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-sm font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-sm text-red-700", children: error })] })] }) })), messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsx("div", { className: "max-w-prose text-center", children: !activeProfile ? (_jsx(Loader2, { className: "h-8 w-8 animate-spin text-gray-400 mx-auto" })) : (_jsxs(_Fragment, { children: [activeProfile.svgIcon ? (_jsx("div", { className: "mx-auto mb-4 flex h-24 w-24 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
2369
|
+
__html: activeProfile.svgIcon,
|
|
2370
|
+
} })) : (_jsx(SecretAgentIcon, { size: 96, strokeWidth: 1, className: "mx-auto mb-4 text-gray-400" })), activeProfile.greetingMessage ? (_jsx(ViewTransition, { children: _jsx("div", { className: "prose prose-sm mx-auto text-center", dangerouslySetInnerHTML: {
|
|
2371
|
+
__html: activeProfile.greetingMessage,
|
|
2372
|
+
} }) })) : (_jsxs(_Fragment, { children: [_jsx("h3", { className: "mb-2 text-lg font-medium text-gray-900", children: "Start a conversation" }), _jsx("p", { className: "mb-4 text-sm text-gray-500", children: "Send a message to begin working with your AI agent." }), _jsx("div", { className: "text-xs text-gray-400", children: "Your agent can help with content editing, research, and automation tasks." })] }))] })) }) })), _jsx("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: groupConsecutiveMessages(messages).map((group, groupIndex) => {
|
|
2373
|
+
if (group.type === "user" && group.messages[0]) {
|
|
2374
|
+
// Render user message
|
|
2375
|
+
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
2376
|
+
}
|
|
2377
|
+
else {
|
|
2378
|
+
// Render bundled assistant messages
|
|
2379
|
+
// Check if this group contains any streaming message
|
|
2380
|
+
const hasStreamingMessage = group.messages.some((msg) => !msg.isCompleted && msg.messageType === "streaming");
|
|
2381
|
+
// Filter out cost limit error messages (they're shown in the banner instead)
|
|
2382
|
+
const filteredMessages = group.messages.filter((msg) => {
|
|
2383
|
+
const content = msg.content || "";
|
|
2384
|
+
// Skip messages that are cost limit errors (shown in banner instead)
|
|
2385
|
+
return (!content.startsWith("⚠️") || !content.includes("Cost limit"));
|
|
2386
|
+
});
|
|
2387
|
+
// If all messages were filtered out, don't render this group
|
|
2388
|
+
if (filteredMessages.length === 0) {
|
|
2389
|
+
return null;
|
|
2390
|
+
}
|
|
2391
|
+
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
2392
|
+
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isSubmitting && !isConnecting, editOperations: [], error: error || undefined, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.name, onQuickAction: (action) => {
|
|
2393
|
+
const text = (action.prompt ||
|
|
2394
|
+
action.value ||
|
|
2395
|
+
action.label ||
|
|
2396
|
+
"").trim();
|
|
2397
|
+
if (!text)
|
|
2398
|
+
return;
|
|
2399
|
+
// Check if text contains placeholders ({placeholder} or <placeholder>)
|
|
2400
|
+
const hasPlaceholders = /\{([^}]+)\}|<([^>]+)>/.test(text);
|
|
2401
|
+
if (hasPlaceholders) {
|
|
2402
|
+
// Show placeholder input
|
|
2403
|
+
setActivePlaceholderInput({
|
|
2404
|
+
text,
|
|
2405
|
+
behavior: action.behavior,
|
|
2406
|
+
});
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (action.behavior === "compose") {
|
|
2410
|
+
setPrompt(text);
|
|
2411
|
+
setInputPlaceholder(action.placeholder ||
|
|
2412
|
+
"Review and edit, then press Enter to send");
|
|
2413
|
+
if (textareaRef.current) {
|
|
2414
|
+
try {
|
|
2415
|
+
textareaRef.current.focus();
|
|
2416
|
+
const v = textareaRef.current.value || "";
|
|
2417
|
+
textareaRef.current.selectionStart = v.length;
|
|
2418
|
+
textareaRef.current.selectionEnd = v.length;
|
|
2419
|
+
}
|
|
2420
|
+
catch { }
|
|
2421
|
+
}
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
// Stop any current execution before sending the next message
|
|
2425
|
+
if (isExecuting) {
|
|
2426
|
+
try {
|
|
2427
|
+
handleStop();
|
|
2428
|
+
}
|
|
2429
|
+
catch { }
|
|
2430
|
+
}
|
|
2431
|
+
sendQuickMessage(text);
|
|
2432
|
+
} }, groupIndex));
|
|
2433
|
+
}
|
|
2434
|
+
}) }), _jsx("div", { className: showDots ? "visible" : "invisible", children: _jsx(DancingDots, {}) }), renderCostLimitBanner(), _jsx("div", { ref: messagesEndRef })] }), !hideContext && renderContextInfoBar(), !hideContext && (_jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata })), _jsxs("div", { className: "border-t border-gray-200 p-4", children: [activePlaceholderInput ? (
|
|
2435
|
+
// Placeholder Input (from quick actions)
|
|
2436
|
+
_jsx(PlaceholderInput, { text: activePlaceholderInput.text, showButtons: false, onComplete: (filledText) => {
|
|
2437
|
+
setActivePlaceholderInput(null);
|
|
2438
|
+
if (activePlaceholderInput.behavior === "compose") {
|
|
2439
|
+
setPrompt(filledText);
|
|
2440
|
+
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
2441
|
+
if (textareaRef.current) {
|
|
2442
|
+
try {
|
|
2443
|
+
textareaRef.current.focus();
|
|
2444
|
+
const v = textareaRef.current.value || "";
|
|
2445
|
+
textareaRef.current.selectionStart = v.length;
|
|
2446
|
+
textareaRef.current.selectionEnd = v.length;
|
|
2447
|
+
}
|
|
2448
|
+
catch { }
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
else {
|
|
2452
|
+
// Submit behavior or default
|
|
2453
|
+
if (isExecuting) {
|
|
2454
|
+
try {
|
|
2455
|
+
handleStop();
|
|
2456
|
+
}
|
|
2457
|
+
catch { }
|
|
2458
|
+
}
|
|
2459
|
+
sendQuickMessage(filledText);
|
|
2460
|
+
}
|
|
2461
|
+
}, onCancel: () => {
|
|
2462
|
+
setActivePlaceholderInput(null);
|
|
2463
|
+
} })) : prompt && /\{([^}]+)\}|<([^>]+)>/.test(prompt) ? (
|
|
2464
|
+
// Template mode: show PlaceholderInput when prompt contains placeholders
|
|
2465
|
+
_jsx(PlaceholderInput, { text: prompt, showButtons: false, onComplete: (filledText) => {
|
|
2466
|
+
setPrompt(filledText);
|
|
2467
|
+
// Auto-submit after filling placeholders
|
|
2468
|
+
if (filledText.trim()) {
|
|
2469
|
+
if (isExecuting) {
|
|
2470
|
+
try {
|
|
2471
|
+
handleStop();
|
|
2472
|
+
}
|
|
2473
|
+
catch { }
|
|
2474
|
+
}
|
|
2475
|
+
sendQuickMessage(filledText);
|
|
2476
|
+
}
|
|
2477
|
+
}, onCancel: () => {
|
|
2478
|
+
setPrompt("");
|
|
2479
|
+
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
2480
|
+
} })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, value: prompt, onChange: (e) => {
|
|
2481
|
+
setPrompt(e.target.value);
|
|
2482
|
+
// Reset history index when user starts typing
|
|
2483
|
+
if (currentHistoryIndex !== -1) {
|
|
2484
|
+
setCurrentHistoryIndex(-1);
|
|
2485
|
+
}
|
|
2486
|
+
}, onKeyDown: handleKeyPress, placeholder: inputPlaceholder, className: "h-[80px] flex-1 resize-none overflow-y-auto text-xs", "data-testid": "agent-terminal-prompt", disabled: isSubmitting }) })), !hideBottomControls && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-stretch justify-between gap-2", children: [_jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-start gap-2", children: [_jsxs(Tooltip, { delayDuration: 400, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("select", { className: `h-5 rounded border px-1.5 text-[10px] ${mode === "read-only"
|
|
2487
|
+
? "border-green-300 bg-green-50 text-green-700"
|
|
2488
|
+
: mode === "supervised"
|
|
2489
|
+
? "border-amber-300 bg-amber-50 text-amber-700"
|
|
2490
|
+
: "border-red-300 bg-red-50 text-red-700"}`, value: mode, onChange: async (e) => {
|
|
2491
|
+
const nextMode = e.target.value || "supervised";
|
|
2492
|
+
// Optimistic UI update
|
|
2493
|
+
setMode(nextMode);
|
|
2494
|
+
const current = agentMetadata || {};
|
|
2495
|
+
const nextMeta = {
|
|
2496
|
+
...current,
|
|
2497
|
+
mode: nextMode,
|
|
2498
|
+
};
|
|
2499
|
+
try {
|
|
2500
|
+
if (!agent?.id || agent.status === "new") {
|
|
2501
|
+
setAgentMetadata(nextMeta);
|
|
2502
|
+
// Cache until first start when agent is persisted
|
|
2503
|
+
pendingSettingsRef.current = {
|
|
2504
|
+
...(pendingSettingsRef.current || {}),
|
|
2505
|
+
mode: nextMode,
|
|
2506
|
+
};
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
await updateAgentSettings(agent.id, {
|
|
2510
|
+
mode: nextMode,
|
|
2511
|
+
});
|
|
2512
|
+
setAgentMetadata(nextMeta);
|
|
2513
|
+
setAgent((prev) => prev
|
|
2514
|
+
? { ...prev, metadata: JSON.stringify(nextMeta) }
|
|
2515
|
+
: prev);
|
|
2516
|
+
}
|
|
2517
|
+
catch (e2) {
|
|
2518
|
+
console.error("Failed to persist mode change", e2);
|
|
2519
|
+
}
|
|
2520
|
+
}, title: "Mode", "aria-label": "Mode", "data-testid": "agent-mode-select", children: [_jsx("option", { value: "supervised", children: "Supervised" }), _jsx("option", { value: "autonomous", children: "Autonomous" }), _jsx("option", { value: "read-only", children: "Read-Only" })] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: _jsxs("div", { className: "max-w-[320px] space-y-1", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold text-green-500", children: "Read-Only" }), ": Limited tool access as configured by the profile (Ask Mode Tools)."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-amber-500", children: "Supervised" }), ": Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside the current context requires explicit approval."] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold text-red-500", children: "Autonomous" }), ": Full tool access; can write across the site/project only limited by user permissions."] })] }) })] }), profiles?.length > 0 && (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: activeProfile?.id || "", onChange: async (e) => {
|
|
2521
|
+
const nextProfile = profiles.find((x) => x.id === e.target.value);
|
|
2522
|
+
if (!nextProfile)
|
|
2523
|
+
return;
|
|
2524
|
+
setActiveProfile(nextProfile);
|
|
2525
|
+
try {
|
|
2526
|
+
if (agent?.id && agent.status !== "new") {
|
|
2527
|
+
await updateAgentSettings(agent.id, {
|
|
2528
|
+
profileId: nextProfile.id,
|
|
2529
|
+
profileName: nextProfile.name,
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
else {
|
|
2533
|
+
// cache until first start
|
|
2534
|
+
pendingSettingsRef.current = {
|
|
2535
|
+
...(pendingSettingsRef.current || {}),
|
|
2536
|
+
// we cache profile by updating local metadata
|
|
2537
|
+
};
|
|
2538
|
+
setAgentMetadata((current) => {
|
|
2539
|
+
const next = { ...(current || {}) };
|
|
2540
|
+
next.profile = nextProfile.name;
|
|
2541
|
+
next.additionalData = {
|
|
2542
|
+
...(next.additionalData || {}),
|
|
2543
|
+
profileId: nextProfile.id,
|
|
2544
|
+
profileName: nextProfile.name,
|
|
2545
|
+
};
|
|
2546
|
+
return next;
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
// reflect in local agent stub so tabs and titles can use it if needed
|
|
2550
|
+
setAgent((prev) => prev
|
|
2551
|
+
? {
|
|
2552
|
+
...prev,
|
|
2553
|
+
metadata: JSON.stringify({
|
|
2554
|
+
...(agentMetadata || {}),
|
|
2555
|
+
profile: nextProfile.name,
|
|
2556
|
+
additionalData: {
|
|
2557
|
+
...(agentMetadata
|
|
2558
|
+
?.additionalData || {}),
|
|
2559
|
+
profileId: nextProfile.id,
|
|
2560
|
+
profileName: nextProfile.name,
|
|
2561
|
+
},
|
|
2562
|
+
}),
|
|
2563
|
+
}
|
|
2564
|
+
: prev);
|
|
2565
|
+
}
|
|
2566
|
+
catch (err) {
|
|
2567
|
+
console.error("Failed to persist agent profile", err);
|
|
2568
|
+
}
|
|
2569
|
+
}, title: "Profile", "aria-label": "Profile", "data-testid": "agent-profile-select", children: profiles.map((p) => (_jsx("option", { value: p.id, children: p.name }, p.id))) })), activeProfile?.models?.length ? (_jsx("select", { className: "h-5 rounded border px-1.5 text-[10px] text-gray-500", value: selectedModelId || "", onChange: async (e) => {
|
|
2570
|
+
const nextId = e.target.value;
|
|
2571
|
+
setSelectedModelId(nextId);
|
|
2572
|
+
const modelName = activeProfile?.models?.find((m) => m.id === nextId)
|
|
2573
|
+
?.name || "";
|
|
2574
|
+
// Update local agent state immediately for UX and to reflect in streaming stub
|
|
2575
|
+
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
2576
|
+
// Persist only for existing agents; otherwise cache until first start
|
|
2577
|
+
try {
|
|
2578
|
+
if (agent?.id && agent.status !== "new") {
|
|
2579
|
+
await updateAgentSettings(agent.id, {
|
|
2580
|
+
model: modelName,
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
else {
|
|
2584
|
+
pendingSettingsRef.current = {
|
|
2585
|
+
...(pendingSettingsRef.current || {}),
|
|
2586
|
+
modelName,
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
catch (err) {
|
|
2591
|
+
console.error("Failed to persist agent model", err);
|
|
2592
|
+
}
|
|
2593
|
+
}, title: "Model", "aria-label": "Model", "data-testid": "agent-model-select", children: activeProfile.models.map((m) => (_jsx("option", { value: m.id, children: m.name }, m.id))) })) : null, activeProfile?.prompts?.length ? (_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { className: "rounded p-1 hover:bg-gray-100", onClick: () => { }, title: "Predefined prompts", "aria-label": "Predefined prompts", children: _jsx(Wand2, { className: "h-3 w-3", strokeWidth: 1 }) }) }), _jsx(PopoverContent, { className: "w-64 p-0", align: "start", children: _jsx("div", { className: "max-h-56 overflow-y-auto p-2", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "cursor-pointer rounded p-1.5 text-xs text-gray-700 hover:bg-gray-100", onClick: () => {
|
|
2594
|
+
setPrompt(p.prompt);
|
|
2595
|
+
setShowPredefined(false);
|
|
2596
|
+
if (textareaRef.current)
|
|
2597
|
+
textareaRef.current.focus();
|
|
2598
|
+
}, children: p.title }, index))) }) })] })) : null] }), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [isVoiceSupported ? (_jsx(Button, { onClick: toggleVoice, size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isListening ? "Stop voice input" : "Start voice input", "aria-label": isListening ? "Stop voice input" : "Start voice input", "aria-pressed": isListening, children: isListening ? (_jsx(MicOff, { className: "size-3", strokeWidth: 1 })) : (_jsx(Mic, { className: "size-3", strokeWidth: 1 })) })) : null, _jsx(Button, { onClick: isExecuting ? handleStop : handleSubmit, disabled: !isExecuting && !prompt.trim(), size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", title: isExecuting ? "Stop" : "Send", "aria-label": isExecuting ? "Stop" : "Send", "data-testid": "agent-send-stop-button", "data-executing": isExecuting ? "true" : "false", children: isExecuting ? (_jsx(Square, { className: "size-3", strokeWidth: 1 })) : (_jsx(Send, { className: "size-3", strokeWidth: 1 })) })] })] }), _jsxs("div", { className: "mt-1 flex items-center gap-2 text-[10px] text-gray-500", children: [_jsx(AgentCostDisplay, { totalTokens: liveTotals
|
|
2599
|
+
? {
|
|
2600
|
+
input: liveTotals.input,
|
|
2601
|
+
output: liveTotals.output,
|
|
2602
|
+
cached: liveTotals.cached,
|
|
2603
|
+
cacheWrite: liveTotals.cacheWrite ?? 0,
|
|
2604
|
+
inputCost: liveTotals.inputCost,
|
|
2605
|
+
outputCost: liveTotals.outputCost,
|
|
2606
|
+
cachedCost: liveTotals.cachedCost,
|
|
2607
|
+
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
2608
|
+
totalCost: liveTotals.totalCost,
|
|
2609
|
+
}
|
|
2610
|
+
: totalTokens, costLimit: effectiveCostLimit }), (() => {
|
|
2611
|
+
try {
|
|
2612
|
+
const s = window.__agentContextWindowStatus;
|
|
2613
|
+
if (!s || !s.contextWindowTokens)
|
|
2614
|
+
return null;
|
|
2615
|
+
const pct = typeof s.contextUsedPercent === "number"
|
|
2616
|
+
? `${s.contextUsedPercent.toFixed(1)}%`
|
|
2617
|
+
: undefined;
|
|
2618
|
+
// Helper function to format tokens as "k"
|
|
2619
|
+
const formatTokens = (tokens) => {
|
|
2620
|
+
if (tokens >= 1000) {
|
|
2621
|
+
return `${(tokens / 1000).toFixed(1)}k`;
|
|
2622
|
+
}
|
|
2623
|
+
return tokens.toString();
|
|
2624
|
+
};
|
|
2625
|
+
if (!pct)
|
|
2626
|
+
return null;
|
|
2627
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "cursor-help rounded border border-gray-200 bg-gray-50 px-2 py-0.5", children: ["Context: ", pct] }) }), _jsx(TooltipContent, { side: "top", sideOffset: 6, children: _jsxs("div", { className: "max-w-[320px] space-y-1 text-xs", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Model:" }), " ", s.model, s.normalizedModel && ` (${s.normalizedModel})`] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Context window:" }), " ", formatTokens(s.estimatedInputTokens || 0), " /", " ", formatTokens(s.contextWindowTokens), " tokens"] }), typeof s.maxCompletionTokens === "number" && (_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Max completion:" }), " ", formatTokens(s.maxCompletionTokens), " tokens"] })), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Used:" }), " ", pct] })] }) })] }));
|
|
2628
|
+
}
|
|
2629
|
+
catch {
|
|
2630
|
+
return null;
|
|
2631
|
+
}
|
|
2632
|
+
})()] })] }))] })] }));
|
|
2633
|
+
}
|
|
2634
|
+
//# sourceMappingURL=AgentTerminal.js.map
|