@parhelia/core 0.1.12554 → 0.1.12555
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/dist/agents-view/AgentCard.d.ts +4 -6
- package/dist/agents-view/AgentCard.js +24 -143
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsInbox.d.ts +1 -1
- package/dist/agents-view/AgentsInbox.js +92 -7
- package/dist/agents-view/AgentsInbox.js.map +1 -1
- package/dist/agents-view/AgentsTitlebar.js +2 -3
- package/dist/agents-view/AgentsTitlebar.js.map +1 -1
- package/dist/agents-view/AgentsView.d.ts +7 -6
- package/dist/agents-view/AgentsView.js +98 -187
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.d.ts +6 -2
- package/dist/agents-view/AgentsWorkspaceView.js +113 -266
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.d.ts +1 -2
- package/dist/agents-view/ProfileAgentsGroup.js +3 -4
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ActionButton.d.ts +1 -1
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/FilterInput.d.ts +1 -1
- package/dist/components/FilterInput.js +1 -1
- package/dist/components/FilterInput.js.map +1 -1
- package/dist/components/ui/LanguageSelector.js +4 -2
- package/dist/components/ui/LanguageSelector.js.map +1 -1
- package/dist/components/ui/PlaceholderInput.js +3 -3
- package/dist/components/ui/PlaceholderInput.js.map +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +1 -1
- package/dist/components/ui/alert-dialog.js +10 -6
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/button.d.ts +4 -4
- package/dist/components/ui/button.js +1 -4
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +1 -1
- package/dist/components/ui/context-menu.js +4 -12
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/copy-button.d.ts +1 -2
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/copy-button.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.js +126 -21
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/input.d.ts +1 -1
- package/dist/components/ui/input.js +3 -5
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/paste-button.d.ts +1 -2
- package/dist/components/ui/paste-button.js +2 -2
- package/dist/components/ui/paste-button.js.map +1 -1
- package/dist/components/ui/popover.js +9 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/styled-dialog-title.js +1 -1
- package/dist/components/ui/styled-dialog-title.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -1
- package/dist/components/ui/tabs.js +11 -4
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/config/config.d.ts +2 -4
- package/dist/config/config.js +70 -250
- package/dist/config/config.js.map +1 -1
- package/dist/config/types/workspace.d.ts +0 -6
- package/dist/config/types.d.ts +12 -63
- package/dist/config/types.js.map +1 -1
- 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.js +4 -20
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +1 -2
- package/dist/editor/ContentTree.js +32 -93
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js +22 -87
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldHistory.js +36 -84
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +9 -21
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +2 -23
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/GlobalMenuBar.js +2 -29
- package/dist/editor/GlobalMenuBar.js.map +1 -1
- package/dist/editor/ImageEditor.js +2 -5
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/ItemInfo.js +1 -36
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +0 -3
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +2 -0
- package/dist/editor/MainLayout.js +8 -65
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MigrationsView.js +5 -29
- package/dist/editor/MigrationsView.js.map +1 -1
- package/dist/editor/MobileLayout.js +12 -37
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureCropper.js +45 -54
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +15 -17
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +21 -21
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/SetupWizard.js +12 -52
- package/dist/editor/SetupWizard.js.map +1 -1
- package/dist/editor/Titlebar.js +2 -7
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +0 -1
- package/dist/editor/ai/AgentCostDisplay.js +1 -1
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentDocumentList.js +14 -32
- package/dist/editor/ai/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/AgentGreeting.js +2 -3
- package/dist/editor/ai/AgentGreeting.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +1 -2
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +5 -0
- package/dist/editor/ai/AgentStatusBadge.js +65 -67
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +2 -14
- package/dist/editor/ai/AgentTerminal.js +483 -2377
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +3 -8
- package/dist/editor/ai/AgentTerminalStatusBar.js +56 -460
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +113 -150
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -10
- package/dist/editor/ai/AiResponseMessage.js +23 -238
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.d.ts +3 -2
- package/dist/editor/ai/ContextInfoBar.js +7 -64
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +11 -17
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/HelpTerminal.d.ts +5 -0
- package/dist/editor/ai/HelpTerminal.js +166 -0
- package/dist/editor/ai/HelpTerminal.js.map +1 -0
- package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
- package/dist/editor/ai/InlineAiDialog.js +192 -514
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/InlineAiTrigger.js +12 -115
- package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
- package/dist/editor/ai/MediaImage.js +8 -40
- package/dist/editor/ai/MediaImage.js.map +1 -1
- package/dist/editor/ai/SpawnedAgentsPanel.js +12 -10
- package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +2 -22
- package/dist/editor/ai/ToolCallDisplay.js +147 -518
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +8 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +42 -379
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +1 -5
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +60 -628
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +0 -115
- package/dist/editor/ai/dialogs/agentDialogTypes.js +0 -2
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/ai/types.d.ts +1 -3
- package/dist/editor/ai/useAgentStatus.d.ts +1 -2
- package/dist/editor/ai/useAgentStatus.js +99 -86
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/ai/useInlineAiPosition.js +5 -45
- package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
- package/dist/editor/client/AboutDialog.js +2 -4
- package/dist/editor/client/AboutDialog.js.map +1 -1
- package/dist/editor/client/EditorShell.d.ts +1 -4
- package/dist/editor/client/EditorShell.js +230 -730
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +19 -33
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/helpers.js +0 -6
- package/dist/editor/client/helpers.js.map +1 -1
- package/dist/editor/client/hooks/useEditorUrlSync.js +2 -1
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +0 -10
- package/dist/editor/client/hooks/useEditorWebSocket.js +14 -209
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
- package/dist/editor/client/hooks/useQuota.d.ts +0 -8
- package/dist/editor/client/hooks/useQuota.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +7 -68
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +6 -10
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +3 -6
- package/dist/editor/client/operations.js +30 -208
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +31 -4
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/ui/DevModeIndicator.js +2 -2
- package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.d.ts +6 -0
- package/dist/editor/client/ui/EditorChrome.js +72 -55
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/client/ui/FullscreenControls.js +3 -5
- package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
- package/dist/editor/commands/commands.d.ts +1 -11
- package/dist/editor/commands/commands.js +1 -12
- package/dist/editor/commands/commands.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +55 -109
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/customCommandConverter.d.ts +1 -8
- package/dist/editor/commands/customCommandConverter.js +5 -35
- package/dist/editor/commands/customCommandConverter.js.map +1 -1
- package/dist/editor/commands/handlers/agentHandler.js +1 -2
- package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
- package/dist/editor/commands/itemCommands.d.ts +0 -3
- package/dist/editor/commands/itemCommands.js +10 -93
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/commands/undo.d.ts +15 -9
- package/dist/editor/commands/undo.js +0 -24
- package/dist/editor/commands/undo.js.map +1 -1
- package/dist/editor/context-menu/InsertMenu.js +39 -83
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +1 -1
- 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.js +5 -13
- package/dist/editor/field-types/RichTextEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +3 -37
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +1 -1
- package/dist/editor/field-types/TreeListEditor.js +2 -3
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.css +5 -23
- package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +0 -2
- package/dist/editor/field-types/richtext/components/ReactSlate.js +4 -28
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ToolbarButton.js +2 -4
- package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +0 -13
- package/dist/editor/field-types/richtext/contextMenuFactory.js +24 -181
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/field-types/richtext/types.d.ts +0 -2
- package/dist/editor/field-types/richtext/types.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/plugins.js +0 -4
- package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
- package/dist/editor/field-types/textContextMenuFactory.js +2 -3
- package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -4
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/media-selector/MediaSelector.js +1 -7
- package/dist/editor/media-selector/MediaSelector.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +35 -40
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +1 -1
- package/dist/editor/menubar/ActiveUsers.js.map +1 -1
- package/dist/editor/menubar/GenericToolbar.js +2 -4
- package/dist/editor/menubar/GenericToolbar.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +147 -26
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/Separator.js +1 -1
- package/dist/editor/menubar/VersionSelector.js +4 -2
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.js +12 -39
- package/dist/editor/menubar/WorkflowButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +38 -16
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/HelpButton.js +0 -1
- package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +10 -6
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +220 -597
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +2 -13
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -42
- package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +48 -97
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +17 -38
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +11 -17
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +11 -69
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.d.ts +4 -2
- package/dist/editor/page-viewer/MiniMap.js +28 -91
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.d.ts +1 -3
- package/dist/editor/page-viewer/PageViewer.js +19 -92
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.d.ts +1 -2
- package/dist/editor/page-viewer/PageViewerFrame.js +115 -348
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +49 -114
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +0 -1
- package/dist/editor/page-viewer/pageViewContext.js +14 -51
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/pageModel.d.ts +1 -14
- package/dist/editor/reviews/Comment.js +12 -26
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +5 -7
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +4 -19
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +72 -89
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +177 -281
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/reviews/DecisionsMatrix.js +25 -96
- package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
- package/dist/editor/reviews/DiffView.js +14 -7
- package/dist/editor/reviews/DiffView.js.map +1 -1
- package/dist/editor/reviews/EditReviewSettingsDialog.js +4 -6
- package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
- package/dist/editor/reviews/MultiReviewManager.js +3 -25
- package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
- package/dist/editor/reviews/PagesPanel.js +15 -31
- package/dist/editor/reviews/PagesPanel.js.map +1 -1
- package/dist/editor/reviews/PreviewInfo.js +4 -1
- package/dist/editor/reviews/PreviewInfo.js.map +1 -1
- package/dist/editor/reviews/ReviewCard.js +7 -13
- package/dist/editor/reviews/ReviewCard.js.map +1 -1
- package/dist/editor/reviews/ReviewDetail.js +2 -3
- package/dist/editor/reviews/ReviewDetail.js.map +1 -1
- package/dist/editor/reviews/ReviewsList.js +3 -7
- package/dist/editor/reviews/ReviewsList.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +3 -34
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +5 -31
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +6 -25
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/reviews/reviewCommands.js +1 -4
- package/dist/editor/reviews/reviewCommands.js.map +1 -1
- package/dist/editor/reviews/useMultiReview.js +2 -2
- package/dist/editor/reviews/useMultiReview.js.map +1 -1
- package/dist/editor/reviews/useReviews.d.ts +2 -2
- package/dist/editor/reviews/useReviews.js +30 -12
- package/dist/editor/reviews/useReviews.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +5 -229
- package/dist/editor/services/agentService.js +39 -292
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -57
- package/dist/editor/services/aiService.js +6 -79
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +3 -6
- package/dist/editor/services/contentService.js +12 -13
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +1 -52
- package/dist/editor/services/editService.js +2 -94
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/indexService.js +1 -1
- package/dist/editor/services/indexService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +6 -3
- package/dist/editor/services/reviewsService.js +11 -2
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/services/serviceHelper.d.ts +1 -2
- package/dist/editor/services/serviceHelper.js +20 -112
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +1 -2
- package/dist/editor/services/systemService.js +0 -3
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/services-server/api.d.ts +2 -1
- package/dist/editor/services-server/api.js +6 -11
- package/dist/editor/services-server/api.js.map +1 -1
- 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/settings/About.js +3 -317
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/editor/settings/AllAgentsPanel.d.ts +5 -0
- package/dist/editor/settings/AllAgentsPanel.js +139 -0
- package/dist/editor/settings/AllAgentsPanel.js.map +1 -0
- package/dist/editor/settings/LatestFeedback.d.ts +1 -0
- package/dist/editor/settings/LatestFeedback.js +136 -0
- package/dist/editor/settings/LatestFeedback.js.map +1 -0
- package/dist/editor/settings/QuotaInfo.js +4 -210
- package/dist/editor/settings/QuotaInfo.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +23 -25
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/Setup.d.ts +1 -0
- package/dist/editor/settings/Setup.js +211 -0
- package/dist/editor/settings/Setup.js.map +1 -0
- package/dist/editor/settings/Status.js +6 -7
- package/dist/editor/settings/Status.js.map +1 -1
- package/dist/editor/settings/index/useIndexStatus.js +22 -20
- package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
- package/dist/editor/settings/panels/AgentsPanel.d.ts +4 -0
- package/dist/editor/settings/panels/AgentsPanel.js +121 -95
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/DatabasePanel.d.ts +6 -0
- package/dist/editor/settings/panels/DatabasePanel.js +50 -0
- package/dist/editor/settings/panels/DatabasePanel.js.map +1 -0
- package/dist/editor/settings/panels/ModelsPanel.js +108 -329
- package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.js +59 -86
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/settings/panels/SearchConfigPanel.js +4 -4
- package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/index.d.ts +2 -3
- package/dist/editor/settings/panels/index.js +2 -3
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +2 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js +21 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +233 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js +2 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +1 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +5 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +44 -0
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +1 -0
- package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +2 -0
- package/dist/editor/settings/setup-steps/IndexSetupStep.js +36 -0
- package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +1 -0
- package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +2 -0
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js +111 -0
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +1 -0
- package/dist/editor/settings/setup-steps/SetupOverview.d.ts +14 -0
- package/dist/editor/settings/setup-steps/SetupOverview.js +38 -0
- package/dist/editor/settings/setup-steps/SetupOverview.js.map +1 -0
- package/dist/editor/settings/status/coreStatusChecks.js +19 -124
- package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
- package/dist/editor/settings/status/useStartupChecks.d.ts +1 -3
- package/dist/editor/settings/status/useStartupChecks.js +5 -9
- package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +1 -2
- package/dist/editor/setup-wizard/steps/CompleteStep.js +1 -2
- package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +1 -2
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.d.ts +1 -8
- package/dist/editor/sidebar/ComponentTree.js +69 -216
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/Debug.d.ts +1 -0
- package/dist/editor/sidebar/Debug.js +70 -0
- package/dist/editor/sidebar/Debug.js.map +1 -0
- package/dist/editor/sidebar/EditHistory.js +46 -22
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/Favorites.js +8 -4
- package/dist/editor/sidebar/Favorites.js.map +1 -1
- 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/LeftToolbar.d.ts +1 -0
- package/dist/editor/sidebar/LeftToolbar.js +12 -0
- package/dist/editor/sidebar/LeftToolbar.js.map +1 -0
- package/dist/editor/sidebar/MainContentTree.js +3 -4
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/NavigationSidebar.d.ts +4 -0
- package/dist/editor/sidebar/NavigationSidebar.js +254 -0
- package/dist/editor/sidebar/NavigationSidebar.js.map +1 -0
- package/dist/editor/sidebar/OperationItem.js +7 -21
- package/dist/editor/sidebar/OperationItem.js.map +1 -1
- package/dist/editor/sidebar/SidebarPanel.d.ts +1 -3
- package/dist/editor/sidebar/SidebarPanel.js +12 -44
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/sidebar/SidebarStack.d.ts +1 -2
- package/dist/editor/sidebar/SidebarStack.js +3 -4
- package/dist/editor/sidebar/SidebarStack.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +12 -22
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/sidebar/Workbox.js +3 -53
- package/dist/editor/sidebar/Workbox.js.map +1 -1
- package/dist/editor/sidebar/WorkspaceRail.d.ts +1 -0
- package/dist/editor/sidebar/WorkspaceRail.js +167 -56
- package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.d.ts +1 -3
- package/dist/editor/tree-indicators/GutterColumns.js +5 -26
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/tree-indicators/GutterContext.d.ts +0 -4
- package/dist/editor/tree-indicators/GutterContext.js +0 -23
- package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
- package/dist/editor/tree-indicators/GutterSelector.d.ts +5 -0
- package/dist/editor/tree-indicators/GutterSelector.js +91 -0
- package/dist/editor/tree-indicators/GutterSelector.js.map +1 -0
- package/dist/editor/tree-indicators/index.d.ts +1 -0
- package/dist/editor/tree-indicators/index.js +1 -0
- package/dist/editor/tree-indicators/index.js.map +1 -1
- package/dist/editor/tree-indicators/types.d.ts +1 -12
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.d.ts +0 -2
- package/dist/editor/ui/ItemNameDialogNew.js +17 -33
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/ItemSearch.js +11 -7
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.js +1 -1
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +0 -1
- package/dist/editor/ui/SimpleTabs.js +25 -45
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +0 -1
- package/dist/editor/ui/Splitter.js +86 -102
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
- package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
- package/dist/editor/ui/TreeListSelector.d.ts +1 -6
- package/dist/editor/ui/TreeListSelector.js +2 -2
- package/dist/editor/ui/TreeListSelector.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.d.ts +20 -6
- package/dist/editor/utils/keyboardNavigation.js +140 -48
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/editor/utils.js +9 -19
- package/dist/editor/utils.js.map +1 -1
- package/dist/editor/views/CompareView.d.ts +1 -3
- package/dist/editor/views/CompareView.js +5 -7
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditView.js +1 -1
- package/dist/editor/views/EditView.js.map +1 -1
- package/dist/editor/views/EditorSlot.js +34 -27
- package/dist/editor/views/EditorSlot.js.map +1 -1
- package/dist/editor/views/ItemEditor.js +3 -7
- package/dist/editor/views/ItemEditor.js.map +1 -1
- package/dist/editor/views/MediaFolderEditView.js +1 -1
- package/dist/editor/views/MediaFolderEditView.js.map +1 -1
- package/dist/editor/views/ParheliaView.js +6 -5
- package/dist/editor/views/ParheliaView.js.map +1 -1
- package/dist/editor/views/SingleEditView.d.ts +1 -2
- package/dist/editor/views/SingleEditView.js +8 -10
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/editor/views/editorSlotContext.js +6 -35
- package/dist/editor/views/editorSlotContext.js.map +1 -1
- package/dist/index.d.ts +2 -16
- package/dist/index.js +0 -11
- package/dist/index.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/setup/services/setupWizardService.d.ts +13 -40
- package/dist/setup/services/setupWizardService.js +17 -32
- package/dist/setup/services/setupWizardService.js.map +1 -1
- package/dist/setup/wizard/steps/AddModelDialog.js +3 -12
- package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
- package/dist/setup/wizard/steps/ImportModelDialog.js +22 -39
- package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
- package/dist/splash-screen/ModernSplashScreen.js +32 -112
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
- package/dist/splash-screen/NewPage.js +50 -33
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +6 -2
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/ParheliaAssistantChat.js +29 -12
- package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
- package/dist/splash-screen/ParheliaLogo.js +37 -87
- package/dist/splash-screen/ParheliaLogo.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +3 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/tour/Tour.d.ts +1 -2
- package/dist/tour/Tour.js +75 -256
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +96 -222
- package/dist/tour/default-tour.js.map +1 -1
- package/dist/types.d.ts +29 -63
- package/package.json +15 -19
- package/styles.css +10 -14
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo, } from "react";
|
|
3
|
-
import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign,
|
|
4
|
-
import { getAgent, startAgent,
|
|
5
|
-
import { parseAgentStatus } from "../services/agentStatus";
|
|
3
|
+
import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, } from "lucide-react";
|
|
4
|
+
import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, } from "../services/agentService";
|
|
6
5
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
7
|
-
import { localStorageService } from "../services/localStorageService";
|
|
8
6
|
import { Textarea } from "../../components/ui/textarea";
|
|
9
7
|
import { Button } from "../../components/ui/button";
|
|
10
8
|
import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
|
|
@@ -16,210 +14,19 @@ import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
|
|
|
16
14
|
import { getComponentById } from "../componentTreeHelper";
|
|
17
15
|
import { AgentGreeting } from "./AgentGreeting";
|
|
18
16
|
import { getAgentHistory } from "../services/editService";
|
|
19
|
-
import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
|
|
20
|
-
import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
|
|
21
|
-
import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
|
|
22
17
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
23
18
|
import { SecretAgentIcon } from "../ui/Icons";
|
|
24
19
|
import { formatTime, formatDateTime } from "../utils";
|
|
25
20
|
import { cn } from "../../lib/utils";
|
|
26
|
-
import { sanitizeSvg } from "../../lib/sanitize";
|
|
27
21
|
import { Select } from "../../components/ui/select";
|
|
28
22
|
import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
|
|
23
|
+
import { useMediaQuery } from "../client/hooks/useMediaQuery";
|
|
29
24
|
import { SimpleTabs } from "../ui/SimpleTabs";
|
|
30
|
-
import { Splitter } from "../ui/Splitter";
|
|
31
|
-
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
32
|
-
import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
|
|
33
|
-
const userMessageMarkdownComponents = {
|
|
34
|
-
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
|
|
35
|
-
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
|
|
36
|
-
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
|
|
37
|
-
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
|
|
38
|
-
p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
|
|
39
|
-
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
40
|
-
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
41
|
-
li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
|
|
42
|
-
pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
|
|
43
|
-
code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
|
|
44
|
-
};
|
|
45
|
-
function buildPlaceholderAgentDetails(agentStub) {
|
|
46
|
-
const now = new Date().toISOString();
|
|
47
|
-
const updated = agentStub.updatedDate || now;
|
|
48
|
-
// AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
|
|
49
|
-
// This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
|
|
50
|
-
return {
|
|
51
|
-
...agentStub,
|
|
52
|
-
name: agentStub.name || "Agent",
|
|
53
|
-
userId: agentStub.userId || "",
|
|
54
|
-
updatedDate: updated,
|
|
55
|
-
profileName: agentStub.profileName || "",
|
|
56
|
-
model: agentStub.model || "",
|
|
57
|
-
createdDate: agentStub.createdDate || updated,
|
|
58
|
-
totalTokensUsed: 0,
|
|
59
|
-
totalInputTokens: 0,
|
|
60
|
-
totalOutputTokens: 0,
|
|
61
|
-
totalCachedInputTokens: 0,
|
|
62
|
-
totalInputTokenCost: 0,
|
|
63
|
-
totalOutputTokenCost: 0,
|
|
64
|
-
totalCachedInputTokenCost: 0,
|
|
65
|
-
totalImageCost: 0,
|
|
66
|
-
totalCost: 0,
|
|
67
|
-
currency: agentStub.currency || "USD",
|
|
68
|
-
messageCount: agentStub.messageCount || 0,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function normalizeDialogAgentId(value) {
|
|
72
|
-
return value?.trim().toLowerCase() || "";
|
|
73
|
-
}
|
|
74
|
-
function formatAllowanceSource(source) {
|
|
75
|
-
const normalized = source?.trim();
|
|
76
|
-
if (!normalized)
|
|
77
|
-
return null;
|
|
78
|
-
if (normalized === "user")
|
|
79
|
-
return "User granted";
|
|
80
|
-
if (normalized.startsWith("preconfigured:profile:"))
|
|
81
|
-
return "Profile";
|
|
82
|
-
if (normalized.startsWith("preconfigured:skill:"))
|
|
83
|
-
return "Skill";
|
|
84
|
-
if (normalized.startsWith("system:")) {
|
|
85
|
-
return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
|
|
86
|
-
}
|
|
87
|
-
return normalized;
|
|
88
|
-
}
|
|
89
|
-
function formatAllowanceLabel(allowance) {
|
|
90
|
-
return `${allowance.operationType || "*"}${"itemPath" in allowance
|
|
91
|
-
? ` ${allowance.itemPath}`
|
|
92
|
-
: ` ${allowance.normalizedPath}`}`;
|
|
93
|
-
}
|
|
94
|
-
function getAgentRunMessageAgentId(payload) {
|
|
95
|
-
const agentId = payload?.agentId;
|
|
96
|
-
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
97
|
-
}
|
|
98
|
-
function getAgentRunMessageSeq(payload) {
|
|
99
|
-
const seq = payload?.seq;
|
|
100
|
-
return typeof seq === "number" ? seq : null;
|
|
101
|
-
}
|
|
102
|
-
function getAgentRunMessageDetail(type, payload) {
|
|
103
|
-
if (type === "agent:run:delta") {
|
|
104
|
-
return payload?.type || null;
|
|
105
|
-
}
|
|
106
|
-
if (type === "agent:run:status") {
|
|
107
|
-
return payload?.data?.state || payload?.data?.status || null;
|
|
108
|
-
}
|
|
109
|
-
if (type === "agent:run:error") {
|
|
110
|
-
return payload?.error || null;
|
|
111
|
-
}
|
|
112
|
-
if (type === "agent:run:complete") {
|
|
113
|
-
return payload?.finalStatus || null;
|
|
114
|
-
}
|
|
115
|
-
if (type === "agent:run:start") {
|
|
116
|
-
return payload?.agentName || null;
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
function isHeartbeatRunEventMessage(message) {
|
|
121
|
-
if (!message)
|
|
122
|
-
return false;
|
|
123
|
-
if (message.type === "agent:run:delta") {
|
|
124
|
-
const deltaType = message.payload?.type;
|
|
125
|
-
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
126
|
-
}
|
|
127
|
-
if (message.type === "agent:run:status") {
|
|
128
|
-
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
129
|
-
return String(state || "").toLowerCase() === "heartbeat";
|
|
130
|
-
}
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
function getVisibleDialogRegistry() {
|
|
134
|
-
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
135
|
-
return registry && typeof registry === "object" ? registry : {};
|
|
136
|
-
}
|
|
137
|
-
function decodeHtmlEntities(value) {
|
|
138
|
-
if (typeof document === "undefined") {
|
|
139
|
-
return value;
|
|
140
|
-
}
|
|
141
|
-
const textarea = document.createElement("textarea");
|
|
142
|
-
textarea.innerHTML = value;
|
|
143
|
-
return textarea.value;
|
|
144
|
-
}
|
|
145
|
-
function toUserFacingAgentErrorMessage(value) {
|
|
146
|
-
if (!value)
|
|
147
|
-
return "";
|
|
148
|
-
const normalizedValue = decodeHtmlEntities(value.replace(/<br\s*\/?>/gi, "\n")).trim();
|
|
149
|
-
if (!normalizedValue)
|
|
150
|
-
return "";
|
|
151
|
-
const trimmed = normalizedValue.trim();
|
|
152
|
-
const maybeJson = trimmed.startsWith("{") ||
|
|
153
|
-
trimmed.startsWith("[") ||
|
|
154
|
-
trimmed.startsWith('"');
|
|
155
|
-
if (maybeJson) {
|
|
156
|
-
try {
|
|
157
|
-
const parsed = JSON.parse(trimmed);
|
|
158
|
-
const structuredMessage = typeof parsed === "string"
|
|
159
|
-
? parsed
|
|
160
|
-
: typeof parsed?.error === "object" && parsed?.error
|
|
161
|
-
? (() => {
|
|
162
|
-
const errObj = parsed.error;
|
|
163
|
-
// Extract detailed message from metadata.raw (OpenRouter-style nested errors)
|
|
164
|
-
if (typeof errObj.metadata?.raw ===
|
|
165
|
-
"string") {
|
|
166
|
-
return String(errObj.metadata.raw);
|
|
167
|
-
}
|
|
168
|
-
// Fall back to error.message
|
|
169
|
-
if (typeof errObj.message === "string") {
|
|
170
|
-
return errObj.message;
|
|
171
|
-
}
|
|
172
|
-
return "";
|
|
173
|
-
})()
|
|
174
|
-
: typeof parsed?.error === "string"
|
|
175
|
-
? parsed.error
|
|
176
|
-
: typeof parsed?.message === "string"
|
|
177
|
-
? parsed.message
|
|
178
|
-
: typeof parsed?.detail === "string"
|
|
179
|
-
? parsed.detail
|
|
180
|
-
: typeof parsed?.error_description === "string"
|
|
181
|
-
? parsed.error_description
|
|
182
|
-
: "";
|
|
183
|
-
if (structuredMessage.trim()) {
|
|
184
|
-
value = structuredMessage;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
// Fall through to plain-text cleanup when the provider error isn't valid JSON.
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const firstLine = value
|
|
192
|
-
.replace(/<br\s*\/?>/gi, "\n")
|
|
193
|
-
.split(/\r?\n/)
|
|
194
|
-
.map((line) => line.trim())
|
|
195
|
-
.find(Boolean);
|
|
196
|
-
const cleaned = decodeHtmlEntities(firstLine || value)
|
|
197
|
-
.replace(/^Failed to start agent:\s*/i, "")
|
|
198
|
-
.replace(/^Unknown error\s*/i, "")
|
|
199
|
-
.replace(/^Error:\s*/i, "")
|
|
200
|
-
.replace(/^Error\s+/i, "")
|
|
201
|
-
.replace(/\s+/g, " ")
|
|
202
|
-
.trim();
|
|
203
|
-
return cleaned;
|
|
204
|
-
}
|
|
205
|
-
function isAgentErrorStatusValue(status) {
|
|
206
|
-
return status === "error";
|
|
207
|
-
}
|
|
208
25
|
// Simple user message component
|
|
209
26
|
const UserMessage = ({ message }) => {
|
|
210
|
-
const content = message.content || "";
|
|
211
|
-
const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
|
|
212
|
-
// Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
|
|
213
|
-
const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
|
|
214
|
-
const triggerMatch = content.match(triggerPattern);
|
|
215
|
-
const triggerName = triggerMatch?.[1]?.trim() || "";
|
|
216
|
-
const triggerContent = triggerMatch?.[2] || "";
|
|
217
|
-
const isTriggerMessage = triggerName.length > 0;
|
|
218
|
-
if (isTriggerMessage) {
|
|
219
|
-
return (_jsx("div", { className: "px-4 py-2", children: _jsxs("div", { className: "text-[11px]", children: [_jsxs("button", { type: "button", onClick: () => setIsTriggerExpanded((expanded) => !expanded), className: "text-theme-secondary hover:bg-theme-hover flex w-full items-center gap-2 rounded-md border-l-2 border-cyan-200 px-2 py-1 text-left transition-colors", "data-testid": "trigger-message-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }), _jsxs("span", { className: "truncate font-medium", children: ["Trigger: ", triggerName] }), message.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-gray-400", children: formatTime(new Date(message.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-1 border-l-2 border-cyan-100 pl-[1.35rem] text-[11px] text-gray-600", children: _jsx(MarkdownDisplay, { source: triggerContent, components: userMessageMarkdownComponents }) }))] }) }));
|
|
220
|
-
}
|
|
221
27
|
// Parse source agent name from content if it starts with "[From ...]:"
|
|
222
28
|
// Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
|
|
29
|
+
const content = message.content || "";
|
|
223
30
|
const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
|
|
224
31
|
const match = content.match(fromPattern);
|
|
225
32
|
let sourceAgentName;
|
|
@@ -244,10 +51,7 @@ const UserMessage = ({ message }) => {
|
|
|
244
51
|
message.sourceAgent?.name;
|
|
245
52
|
}
|
|
246
53
|
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
247
|
-
return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "shrink-0", children: _jsx(User, { className: "text-theme-secondary h-5 w-5", 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-[12px] font-medium text-gray-900", children: displayName }), message.createdDate && (_jsx("span", { className: "text-[12px] text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose max-w-none text-[12px] text-gray-700 select-text", children:
|
|
248
|
-
};
|
|
249
|
-
const HeartbeatMessage = ({ message }) => {
|
|
250
|
-
return (_jsx("div", { className: "px-4 py-2", "data-testid": "agent-heartbeat-message", children: _jsxs("div", { className: "flex items-center gap-2 rounded-md border border-sky-100 bg-sky-50/80 px-3 py-2 text-[11px] text-sky-700", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 flex-1", children: message.content }), message.createdDate && (_jsx("span", { className: "shrink-0 text-[10px] text-sky-500", children: formatTime(new Date(message.createdDate)) }))] }) }));
|
|
54
|
+
return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "shrink-0", children: _jsx(User, { className: "text-theme-secondary h-5 w-5", 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-[12px] font-medium text-gray-900", children: displayName }), message.createdDate && (_jsx("span", { className: "text-[12px] text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose max-w-none text-[12px] text-gray-700 select-text", children: displayContent })] })] }));
|
|
251
55
|
};
|
|
252
56
|
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
253
57
|
const extractPartialTodos = (jsonText) => {
|
|
@@ -481,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
|
|
|
481
285
|
todoMap.set(key, todo);
|
|
482
286
|
}
|
|
483
287
|
const result = Array.from(todoMap.values());
|
|
288
|
+
// Only log when duplicates were actually removed (to reduce noise)
|
|
289
|
+
if (todos.length > result.length) {
|
|
290
|
+
console.log("🟡 TODO_DEBUG deduplication:", {
|
|
291
|
+
removed: todos.length - result.length,
|
|
292
|
+
before: todos.length,
|
|
293
|
+
after: result.length,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
484
296
|
return result;
|
|
485
297
|
};
|
|
486
298
|
// TodoListPanel component
|
|
@@ -488,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
|
488
300
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
489
301
|
// First try to get todos from agent metadata (real-time updates)
|
|
490
302
|
// Server sends additionalData.todoList directly via contextChanged status
|
|
303
|
+
// Also check top-level todoList for backward compatibility with stored contexts
|
|
491
304
|
const metadataTodos = (() => {
|
|
492
305
|
try {
|
|
493
|
-
|
|
306
|
+
// Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
|
|
307
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
308
|
+
agentMetadata?.todoList;
|
|
494
309
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
495
310
|
const rawItems = todoList.items
|
|
496
311
|
.map((item, idx) => ({
|
|
@@ -618,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
618
433
|
// Add user message
|
|
619
434
|
groups.push({ type: "user", messages: [message] });
|
|
620
435
|
}
|
|
621
|
-
else if (message.messageType === "heartbeat" ||
|
|
622
|
-
message.role === "system") {
|
|
623
|
-
if (currentAssistantGroup.length > 0) {
|
|
624
|
-
groups.push({
|
|
625
|
-
type: "assistant-group",
|
|
626
|
-
messages: currentAssistantGroup,
|
|
627
|
-
});
|
|
628
|
-
currentAssistantGroup = [];
|
|
629
|
-
}
|
|
630
|
-
groups.push({ type: "heartbeat", messages: [message] });
|
|
631
|
-
}
|
|
632
436
|
else if (message.role === "assistant") {
|
|
633
437
|
// Add to current assistant group
|
|
634
438
|
currentAssistantGroup.push(message);
|
|
@@ -696,7 +500,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
696
500
|
outputCost: acc.outputCost + (message.outputTokenCost || 0),
|
|
697
501
|
cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
|
|
698
502
|
cacheWriteCost: acc.cacheWriteCost,
|
|
699
|
-
imageCost: acc.imageCost,
|
|
700
503
|
totalCost: acc.totalCost + (message.totalCost || 0),
|
|
701
504
|
};
|
|
702
505
|
}, {
|
|
@@ -708,7 +511,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
708
511
|
outputCost: 0,
|
|
709
512
|
cachedCost: 0,
|
|
710
513
|
cacheWriteCost: 0,
|
|
711
|
-
imageCost: 0,
|
|
712
514
|
totalCost: 0,
|
|
713
515
|
});
|
|
714
516
|
return totals;
|
|
@@ -719,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
|
|
|
719
521
|
const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
|
|
720
522
|
return matched;
|
|
721
523
|
};
|
|
722
|
-
const stringifyToolField = (value) => {
|
|
723
|
-
if (value === undefined || value === null)
|
|
724
|
-
return undefined;
|
|
725
|
-
if (typeof value === "string") {
|
|
726
|
-
return value.trim().length > 0 ? value : undefined;
|
|
727
|
-
}
|
|
728
|
-
try {
|
|
729
|
-
return JSON.stringify(value);
|
|
730
|
-
}
|
|
731
|
-
catch {
|
|
732
|
-
return String(value);
|
|
733
|
-
}
|
|
734
|
-
};
|
|
735
|
-
const parseToolResultValue = (value) => {
|
|
736
|
-
if (value === undefined || value === null) {
|
|
737
|
-
return undefined;
|
|
738
|
-
}
|
|
739
|
-
if (typeof value === "object") {
|
|
740
|
-
return value;
|
|
741
|
-
}
|
|
742
|
-
if (typeof value !== "string") {
|
|
743
|
-
return String(value);
|
|
744
|
-
}
|
|
745
|
-
const trimmed = value.trim();
|
|
746
|
-
if (!trimmed) {
|
|
747
|
-
return undefined;
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
let parsed = JSON.parse(trimmed);
|
|
751
|
-
if (typeof parsed === "string" &&
|
|
752
|
-
(parsed.startsWith("{") || parsed.startsWith("["))) {
|
|
753
|
-
parsed = JSON.parse(parsed);
|
|
754
|
-
}
|
|
755
|
-
if (parsed && typeof parsed === "object") {
|
|
756
|
-
return parsed;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
catch {
|
|
760
|
-
// Fall back to the original string when the payload is plain text.
|
|
761
|
-
}
|
|
762
|
-
return value;
|
|
763
|
-
};
|
|
764
|
-
const getFirstToolCallEnvelope = (data) => {
|
|
765
|
-
if (!data || typeof data !== "object")
|
|
766
|
-
return undefined;
|
|
767
|
-
const direct = data.toolCall || data.tool_call;
|
|
768
|
-
if (direct)
|
|
769
|
-
return direct;
|
|
770
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
771
|
-
for (const candidate of arrayCandidates) {
|
|
772
|
-
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
773
|
-
return candidate[0];
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return undefined;
|
|
777
|
-
};
|
|
778
|
-
const extractToolCallFields = (data) => {
|
|
779
|
-
const envelope = getFirstToolCallEnvelope(data);
|
|
780
|
-
const functionPayload = data?.function || envelope?.function;
|
|
781
|
-
const functionName = data?.functionName ||
|
|
782
|
-
data?.name ||
|
|
783
|
-
functionPayload?.name ||
|
|
784
|
-
envelope?.functionName ||
|
|
785
|
-
envelope?.name ||
|
|
786
|
-
"unknown";
|
|
787
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
788
|
-
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
789
|
-
stringifyToolField(data?.arguments) ||
|
|
790
|
-
stringifyToolField(functionPayload?.arguments) ||
|
|
791
|
-
stringifyToolField(envelope?.functionArguments) ||
|
|
792
|
-
stringifyToolField(envelope?.arguments) ||
|
|
793
|
-
"{}";
|
|
794
|
-
return {
|
|
795
|
-
toolCallId,
|
|
796
|
-
functionName,
|
|
797
|
-
functionArguments,
|
|
798
|
-
};
|
|
799
|
-
};
|
|
800
524
|
// Convert agent messages to AI terminal format for a response group
|
|
801
525
|
const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
802
526
|
return agentMessages.map((agentMessage) => {
|
|
@@ -817,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
817
541
|
role: agentMessage.role,
|
|
818
542
|
createdDate: agentMessage.createdDate,
|
|
819
543
|
tool_calls: agentMessage.toolCalls
|
|
820
|
-
? agentMessage.toolCalls.map((toolCall) => {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
!toolCall.isCompleted &&
|
|
843
|
-
!displayResult &&
|
|
844
|
-
!toolCall.functionError &&
|
|
845
|
-
!isPruned,
|
|
846
|
-
// Pass through message IDs for approval/rejection events
|
|
847
|
-
messageId: toolCall.messageId,
|
|
848
|
-
dbMessageId: toolCall.dbMessageId,
|
|
849
|
-
responseTimeMs: toolCall.responseTimeMs,
|
|
850
|
-
createdDate: toolCall.createdDate,
|
|
851
|
-
};
|
|
852
|
-
})
|
|
544
|
+
? agentMessage.toolCalls.map((toolCall) => ({
|
|
545
|
+
id: toolCall.toolCallId,
|
|
546
|
+
displayName: toolCall.functionName,
|
|
547
|
+
function: {
|
|
548
|
+
name: toolCall.functionName,
|
|
549
|
+
arguments: toolCall.functionArguments,
|
|
550
|
+
result: toolCall.functionResult,
|
|
551
|
+
error: toolCall.functionError,
|
|
552
|
+
},
|
|
553
|
+
// Pass through approval info if present on the tool call
|
|
554
|
+
requiresApproval: toolCall.requiresApproval,
|
|
555
|
+
// Pass through isCompleted so ToolCallDisplay knows when to hide spinner
|
|
556
|
+
isCompleted: toolCall.isCompleted,
|
|
557
|
+
// Tool call is streaming if message is not completed and tool call has no result yet
|
|
558
|
+
isStreaming: !agentMessage.isCompleted &&
|
|
559
|
+
!toolCall.isCompleted &&
|
|
560
|
+
!toolCall.functionResult &&
|
|
561
|
+
!toolCall.functionError,
|
|
562
|
+
// Pass through message IDs for approval/rejection events
|
|
563
|
+
messageId: toolCall.messageId,
|
|
564
|
+
dbMessageId: toolCall.dbMessageId,
|
|
565
|
+
}))
|
|
853
566
|
: [],
|
|
854
567
|
};
|
|
855
568
|
if (agentMessage.toolCallId) {
|
|
@@ -861,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
861
574
|
// interface AgentTerminalProps {
|
|
862
575
|
// agentStub: Agent;
|
|
863
576
|
// }
|
|
864
|
-
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false,
|
|
577
|
+
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false, hideContext = false, hideBottomControls = false, hideGreeting = false, simpleMode = false, className, initialPrompt, onAgentUpdate, }) {
|
|
865
578
|
const editContext = useEditContext();
|
|
866
579
|
const fieldsContext = useFieldsEditContext();
|
|
867
|
-
const [agent, setAgent] = useState(
|
|
580
|
+
const [agent, setAgent] = useState(undefined);
|
|
868
581
|
const [messages, setMessages] = useState([]);
|
|
869
582
|
const [agentOperations, setAgentOperations] = useState([]);
|
|
870
583
|
const [prompt, setPrompt] = useState("");
|
|
@@ -875,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
875
588
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
876
589
|
const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
|
|
877
590
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
878
|
-
const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
|
|
879
|
-
const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
|
|
880
|
-
// Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
|
|
881
|
-
// This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
|
|
882
|
-
useEffect(() => {
|
|
883
|
-
setAgent((prev) => {
|
|
884
|
-
if (prev?.id === agentStub.id)
|
|
885
|
-
return prev;
|
|
886
|
-
return buildPlaceholderAgentDetails(agentStub);
|
|
887
|
-
});
|
|
888
|
-
}, [agentStub.id]);
|
|
889
|
-
const observedMessageIdsRef = useRef(new Set());
|
|
890
|
-
useEffect(() => {
|
|
891
|
-
observedMessageIdsRef.current = new Set();
|
|
892
|
-
}, [agentStub.id]);
|
|
893
|
-
useEffect(() => {
|
|
894
|
-
if (!onMessage)
|
|
895
|
-
return;
|
|
896
|
-
for (const message of messages) {
|
|
897
|
-
if (!message?.id)
|
|
898
|
-
continue;
|
|
899
|
-
if (!message.isCompleted)
|
|
900
|
-
continue;
|
|
901
|
-
if (observedMessageIdsRef.current.has(message.id))
|
|
902
|
-
continue;
|
|
903
|
-
observedMessageIdsRef.current.add(message.id);
|
|
904
|
-
onMessage(message);
|
|
905
|
-
}
|
|
906
|
-
}, [messages, onMessage]);
|
|
907
591
|
// Generate a stable clientSessionId per component instance for stream deduplication
|
|
908
592
|
const clientSessionIdRef = useRef(null);
|
|
909
593
|
if (!clientSessionIdRef.current) {
|
|
@@ -915,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
915
599
|
const [isListening, setIsListening] = useState(false);
|
|
916
600
|
const recognitionRef = useRef(null);
|
|
917
601
|
const prevPlaceholderRef = useRef(null);
|
|
918
|
-
const promptBeforeVoiceRef = useRef("");
|
|
919
602
|
// Voice button press-and-hold tracking
|
|
920
603
|
const voicePressStartRef = useRef(null);
|
|
921
604
|
const voiceHoldTimerRef = useRef(null);
|
|
@@ -925,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
925
608
|
const [showCompressionPopover, setShowCompressionPopover] = useState(false);
|
|
926
609
|
// Agent inline dialog state (for component type selector, etc.)
|
|
927
610
|
const [activeInlineDialog, setActiveInlineDialog] = useState(null);
|
|
928
|
-
const dialogTerminalInstanceIdRef = useRef("");
|
|
929
|
-
if (!dialogTerminalInstanceIdRef.current) {
|
|
930
|
-
dialogTerminalInstanceIdRef.current = crypto.randomUUID();
|
|
931
|
-
}
|
|
932
|
-
const activeInlineDialogRef = useRef(activeInlineDialog);
|
|
933
|
-
const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
|
|
934
|
-
const orphanTimeoutRef = useRef(null);
|
|
935
|
-
useEffect(() => {
|
|
936
|
-
activeInlineDialogRef.current = activeInlineDialog;
|
|
937
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
938
|
-
const callbackId = activeInlineDialog?.request.callbackId || null;
|
|
939
|
-
const terminalInstanceId = dialogTerminalInstanceIdRef.current;
|
|
940
|
-
const agentKeys = [
|
|
941
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
942
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
943
|
-
].filter(Boolean);
|
|
944
|
-
agentKeys.forEach((key) => {
|
|
945
|
-
if (callbackId) {
|
|
946
|
-
visibleRegistry[key] = {
|
|
947
|
-
callbackId,
|
|
948
|
-
terminalInstanceId,
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
else {
|
|
952
|
-
delete visibleRegistry[key];
|
|
953
|
-
}
|
|
954
|
-
});
|
|
955
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
956
|
-
}, [activeInlineDialog]);
|
|
957
|
-
useEffect(() => {
|
|
958
|
-
onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
|
|
959
|
-
}, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
|
|
960
|
-
useLayoutEffect(() => {
|
|
961
|
-
if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
const scrollSummaryTerminalToTop = () => {
|
|
965
|
-
const container = messagesContainerRef.current;
|
|
966
|
-
if (!container) {
|
|
967
|
-
return;
|
|
968
|
-
}
|
|
969
|
-
container.scrollTop = 0;
|
|
970
|
-
};
|
|
971
|
-
scrollSummaryTerminalToTop();
|
|
972
|
-
const frame1 = requestAnimationFrame(() => {
|
|
973
|
-
scrollSummaryTerminalToTop();
|
|
974
|
-
});
|
|
975
|
-
let frame3 = 0;
|
|
976
|
-
const frame2 = requestAnimationFrame(() => {
|
|
977
|
-
frame3 = requestAnimationFrame(() => {
|
|
978
|
-
scrollSummaryTerminalToTop();
|
|
979
|
-
});
|
|
980
|
-
});
|
|
981
|
-
return () => {
|
|
982
|
-
cancelAnimationFrame(frame1);
|
|
983
|
-
cancelAnimationFrame(frame2);
|
|
984
|
-
cancelAnimationFrame(frame3);
|
|
985
|
-
};
|
|
986
|
-
}, [displayMode, isQuestionnaireDialogOpen]);
|
|
987
|
-
useEffect(() => {
|
|
988
|
-
return () => {
|
|
989
|
-
onQuestionnaireOpenChange?.(false);
|
|
990
|
-
};
|
|
991
|
-
}, [onQuestionnaireOpenChange]);
|
|
992
611
|
const isWaitingRef = useRef(false);
|
|
993
612
|
useEffect(() => {
|
|
994
613
|
isWaitingRef.current = isWaitingForResponse;
|
|
@@ -999,49 +618,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
999
618
|
const isStoppingRef = useRef(false);
|
|
1000
619
|
// Server-driven state: true when agent is actively processing (set by WebSocket messages)
|
|
1001
620
|
const [isAgentThinking, setIsAgentThinking] = useState(false);
|
|
1002
|
-
useEffect(() => {
|
|
1003
|
-
if (!initialMetadata)
|
|
1004
|
-
return;
|
|
1005
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata({
|
|
1006
|
-
...(prev || {}),
|
|
1007
|
-
...initialMetadata,
|
|
1008
|
-
additionalData: {
|
|
1009
|
-
...(prev?.additionalData || {}),
|
|
1010
|
-
...(initialMetadata?.additionalData || {}),
|
|
1011
|
-
},
|
|
1012
|
-
}));
|
|
1013
|
-
}, [initialMetadata]);
|
|
1014
621
|
const hasActiveStreaming = useCallback(() => {
|
|
1015
622
|
const current = messagesRef.current || [];
|
|
1016
623
|
return current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
1017
624
|
}, []);
|
|
1018
|
-
const currentAgentId = agent?.id || agentStub.id;
|
|
1019
|
-
const recentAgentRunEvents = useMemo(() => {
|
|
1020
|
-
const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
|
|
1021
|
-
if (!normalizedAgentId) {
|
|
1022
|
-
return [];
|
|
1023
|
-
}
|
|
1024
|
-
if (!editContext) {
|
|
1025
|
-
return [];
|
|
1026
|
-
}
|
|
1027
|
-
return (editContext.webSocketMessages || [])
|
|
1028
|
-
.filter((message) => {
|
|
1029
|
-
if (!message?.type?.startsWith("agent:run:")) {
|
|
1030
|
-
return false;
|
|
1031
|
-
}
|
|
1032
|
-
if (isHeartbeatRunEventMessage(message)) {
|
|
1033
|
-
return false;
|
|
1034
|
-
}
|
|
1035
|
-
return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
|
|
1036
|
-
})
|
|
1037
|
-
.slice(-8)
|
|
1038
|
-
.map((message) => ({
|
|
1039
|
-
timestamp: message.timestamp,
|
|
1040
|
-
type: message.type,
|
|
1041
|
-
seq: getAgentRunMessageSeq(message.payload),
|
|
1042
|
-
detail: getAgentRunMessageDetail(message.type, message.payload),
|
|
1043
|
-
}));
|
|
1044
|
-
}, [currentAgentId, editContext?.webSocketMessages]);
|
|
1045
625
|
// Collect all pending tool calls for batch approval functionality
|
|
1046
626
|
const allPendingApprovals = useMemo(() => {
|
|
1047
627
|
const pending = [];
|
|
@@ -1083,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1083
663
|
}, [allPendingApprovals]);
|
|
1084
664
|
// Handle mode switch to autonomous
|
|
1085
665
|
const handleSwitchToAutonomous = useCallback(() => {
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
setAgentMetadata((prev) => ({
|
|
1089
|
-
...(prev ?? {}),
|
|
1090
|
-
mode: nextMode,
|
|
1091
|
-
}));
|
|
1092
|
-
setAgent((prev) => {
|
|
1093
|
-
if (!prev)
|
|
1094
|
-
return prev;
|
|
1095
|
-
return {
|
|
1096
|
-
...prev,
|
|
1097
|
-
mode: nextMode,
|
|
1098
|
-
};
|
|
1099
|
-
});
|
|
1100
|
-
}, [setAgent, setAgentMetadata]);
|
|
666
|
+
setMode("autonomous");
|
|
667
|
+
}, []);
|
|
1101
668
|
const [resolvedPageName, setResolvedPageName] = useState(undefined);
|
|
1102
669
|
const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
|
|
1103
670
|
const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
|
|
1104
671
|
const [promptHistory, setPromptHistory] = useState(() => {
|
|
1105
|
-
|
|
672
|
+
if (typeof window !== "undefined") {
|
|
673
|
+
return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
|
|
674
|
+
}
|
|
675
|
+
return [];
|
|
1106
676
|
});
|
|
1107
677
|
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
|
|
1108
678
|
const [showPredefined, setShowPredefined] = useState(false);
|
|
1109
679
|
const [activeProfile, setActiveProfile] = useState(undefined);
|
|
1110
680
|
const [selectedModelId, setSelectedModelId] = useState(undefined);
|
|
1111
|
-
const normalizeAgentMode = (value) => {
|
|
1112
|
-
if (value === "autonomous" ||
|
|
1113
|
-
value === "read-only" ||
|
|
1114
|
-
value === "supervised") {
|
|
1115
|
-
return value;
|
|
1116
|
-
}
|
|
1117
|
-
return null;
|
|
1118
|
-
};
|
|
1119
681
|
const [mode, setMode] = useState("supervised");
|
|
1120
682
|
const [queuedPrompts, setQueuedPrompts] = useState([]);
|
|
1121
|
-
const
|
|
683
|
+
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
1122
684
|
const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
|
|
1123
685
|
const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
|
|
1124
686
|
const [showCostAndAgent, setShowCostAndAgent] = useState(false);
|
|
1125
|
-
const [showAgentSettings, setShowAgentSettings] = useState(false);
|
|
1126
|
-
const [showSkillPicker, setShowSkillPicker] = useState(false);
|
|
1127
|
-
const [availableSkills, setAvailableSkills] = useState([]);
|
|
1128
|
-
const [skillRootIds, setSkillRootIds] = useState([]);
|
|
1129
|
-
const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
|
|
1130
|
-
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1131
|
-
const [skillsError, setSkillsError] = useState(null);
|
|
1132
|
-
const [skillActionError, setSkillActionError] = useState(null);
|
|
1133
|
-
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1134
|
-
const [availableTools, setAvailableTools] = useState([]);
|
|
1135
|
-
const [operationAllowances, setOperationAllowances] = useState({
|
|
1136
|
-
sitecore: [],
|
|
1137
|
-
filesystem: [],
|
|
1138
|
-
});
|
|
1139
|
-
const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
|
|
1140
|
-
const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
|
|
1141
|
-
const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
|
|
1142
|
-
const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
|
|
1143
|
-
const [availableToolsError, setAvailableToolsError] = useState(null);
|
|
1144
|
-
const [operationAllowancesError, setOperationAllowancesError] = useState(null);
|
|
1145
|
-
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1146
|
-
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1147
|
-
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1148
|
-
const isPersistedAgent = !!agent?.userId;
|
|
1149
|
-
const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
|
|
1150
687
|
const hasSpawnedAgents = useMemo(() => {
|
|
1151
688
|
if (!agentMetadata)
|
|
1152
689
|
return false;
|
|
1153
|
-
const childAgents = agentMetadata?.
|
|
690
|
+
const childAgents = agentMetadata?.ChildAgents ||
|
|
691
|
+
agentMetadata?.childAgents;
|
|
1154
692
|
if (!Array.isArray(childAgents) || childAgents.length === 0)
|
|
1155
693
|
return false;
|
|
1156
|
-
return childAgents.some((a) => a != null && typeof a === "object" && a.agentId);
|
|
694
|
+
return childAgents.some((a) => a != null && typeof a === "object" && (a.AgentId || a.agentId));
|
|
1157
695
|
}, [agentMetadata]);
|
|
1158
696
|
const hasTodoContent = useMemo(() => {
|
|
1159
697
|
const metadataTodos = (() => {
|
|
1160
698
|
try {
|
|
1161
|
-
const todoList = agentMetadata?.additionalData?.todoList
|
|
699
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
700
|
+
agentMetadata?.todoList;
|
|
1162
701
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
1163
702
|
const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
|
|
1164
703
|
return raw.length > 0;
|
|
@@ -1206,9 +745,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1206
745
|
.then((result) => {
|
|
1207
746
|
if (cancelled)
|
|
1208
747
|
return;
|
|
1209
|
-
if (result.type === "success" &&
|
|
1210
|
-
result.data &&
|
|
1211
|
-
result.data.length > 0) {
|
|
748
|
+
if (result.type === "success" && result.data && result.data.length > 0) {
|
|
1212
749
|
setHasHistoryContent(true);
|
|
1213
750
|
}
|
|
1214
751
|
else {
|
|
@@ -1239,38 +776,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1239
776
|
});
|
|
1240
777
|
return () => unsubscribe();
|
|
1241
778
|
}, [agent?.id, editContext?.addSocketMessageListener]);
|
|
1242
|
-
useEffect(() => {
|
|
1243
|
-
let active = true;
|
|
1244
|
-
const loadSkills = async () => {
|
|
1245
|
-
try {
|
|
1246
|
-
setSkillsLoading(true);
|
|
1247
|
-
setSkillsError(null);
|
|
1248
|
-
const catalog = await getAgentSkillCatalog(false);
|
|
1249
|
-
if (active) {
|
|
1250
|
-
setAvailableSkills(catalog.skills.filter((s) => !s.disabled));
|
|
1251
|
-
setSkillRootIds(catalog.rootIds);
|
|
1252
|
-
setSelectableTemplateIds(catalog.selectableTemplateIds);
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
catch (e) {
|
|
1256
|
-
if (active) {
|
|
1257
|
-
setSkillsError(e?.message || "Failed to load skills");
|
|
1258
|
-
setAvailableSkills([]);
|
|
1259
|
-
setSkillRootIds([]);
|
|
1260
|
-
setSelectableTemplateIds([]);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
finally {
|
|
1264
|
-
if (active) {
|
|
1265
|
-
setSkillsLoading(false);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
};
|
|
1269
|
-
void loadSkills();
|
|
1270
|
-
return () => {
|
|
1271
|
-
active = false;
|
|
1272
|
-
};
|
|
1273
|
-
}, []);
|
|
1274
779
|
const modeOptions = useMemo(() => [
|
|
1275
780
|
{
|
|
1276
781
|
value: "supervised",
|
|
@@ -1305,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1305
810
|
value: m.id,
|
|
1306
811
|
label: m.name,
|
|
1307
812
|
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1308
|
-
const metadataSelectedSkillIds = useMemo(() => {
|
|
1309
|
-
const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
|
|
1310
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1311
|
-
return [];
|
|
1312
|
-
}
|
|
1313
|
-
return rawSkillIds
|
|
1314
|
-
.map((x) => String(x || "").trim())
|
|
1315
|
-
.filter((x) => x.length > 0);
|
|
1316
|
-
}, [agentMetadata]);
|
|
1317
|
-
const backendAssignedSkillIds = useMemo(() => {
|
|
1318
|
-
const rawSkillIds = agent?.assignedSkillIds ?? [];
|
|
1319
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1320
|
-
return [];
|
|
1321
|
-
}
|
|
1322
|
-
return rawSkillIds
|
|
1323
|
-
.map((x) => String(x || "").trim())
|
|
1324
|
-
.filter((x) => x.length > 0);
|
|
1325
|
-
}, [agent]);
|
|
1326
|
-
const preloadedSkillIds = useMemo(() => {
|
|
1327
|
-
const rawSkillIds = activeProfile?.preloadSkills ?? [];
|
|
1328
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1329
|
-
return [];
|
|
1330
|
-
}
|
|
1331
|
-
return rawSkillIds
|
|
1332
|
-
.map((skill) => String(skill?.id || "").trim())
|
|
1333
|
-
.filter((id) => id.length > 0);
|
|
1334
|
-
}, [activeProfile?.preloadSkills]);
|
|
1335
|
-
const autoAssignedSkillIds = useMemo(() => {
|
|
1336
|
-
const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
|
|
1337
|
-
const all = isLocalOnlyDraftAgent
|
|
1338
|
-
? preloadedSkillIds
|
|
1339
|
-
: backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
|
|
1340
|
-
const seen = new Set();
|
|
1341
|
-
const unique = [];
|
|
1342
|
-
for (const id of all) {
|
|
1343
|
-
const key = id.toLowerCase();
|
|
1344
|
-
if (seen.has(key))
|
|
1345
|
-
continue;
|
|
1346
|
-
seen.add(key);
|
|
1347
|
-
unique.push(id);
|
|
1348
|
-
}
|
|
1349
|
-
return unique;
|
|
1350
|
-
}, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
|
|
1351
|
-
const selectedSkillIds = useMemo(() => {
|
|
1352
|
-
const all = [
|
|
1353
|
-
...autoAssignedSkillIds,
|
|
1354
|
-
...backendAssignedSkillIds,
|
|
1355
|
-
...metadataSelectedSkillIds,
|
|
1356
|
-
];
|
|
1357
|
-
const seen = new Set();
|
|
1358
|
-
const unique = [];
|
|
1359
|
-
for (const id of all) {
|
|
1360
|
-
const key = id.toLowerCase();
|
|
1361
|
-
if (seen.has(key))
|
|
1362
|
-
continue;
|
|
1363
|
-
seen.add(key);
|
|
1364
|
-
unique.push(id);
|
|
1365
|
-
}
|
|
1366
|
-
return unique;
|
|
1367
|
-
}, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1368
|
-
useEffect(() => {
|
|
1369
|
-
let active = true;
|
|
1370
|
-
if (!showAgentSettings) {
|
|
1371
|
-
return () => {
|
|
1372
|
-
active = false;
|
|
1373
|
-
};
|
|
1374
|
-
}
|
|
1375
|
-
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
1376
|
-
setTriggerSubscriptions([]);
|
|
1377
|
-
setTriggerSubscriptionsLoading(false);
|
|
1378
|
-
setTriggerSubscriptionsError(null);
|
|
1379
|
-
setAvailableTools([]);
|
|
1380
|
-
setAvailableToolsLoading(false);
|
|
1381
|
-
setAvailableToolsError(null);
|
|
1382
|
-
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1383
|
-
setOperationAllowancesLoading(false);
|
|
1384
|
-
setOperationAllowancesError(null);
|
|
1385
|
-
return () => {
|
|
1386
|
-
active = false;
|
|
1387
|
-
};
|
|
1388
|
-
}
|
|
1389
|
-
const loadTriggerSubscriptions = async () => {
|
|
1390
|
-
try {
|
|
1391
|
-
setTriggerSubscriptionsLoading(true);
|
|
1392
|
-
setTriggerSubscriptionsError(null);
|
|
1393
|
-
const subscriptions = await getAgentTriggerSubscriptions(agent.id);
|
|
1394
|
-
if (active) {
|
|
1395
|
-
setTriggerSubscriptions(subscriptions);
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
catch (e) {
|
|
1399
|
-
if (active) {
|
|
1400
|
-
setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
finally {
|
|
1404
|
-
if (active) {
|
|
1405
|
-
setTriggerSubscriptionsLoading(false);
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
const loadOperationAllowances = async () => {
|
|
1410
|
-
try {
|
|
1411
|
-
setOperationAllowancesLoading(true);
|
|
1412
|
-
setOperationAllowancesError(null);
|
|
1413
|
-
const allowances = await getAgentOperationAllowances(agent.id);
|
|
1414
|
-
if (active) {
|
|
1415
|
-
setOperationAllowances(allowances);
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
catch (e) {
|
|
1419
|
-
if (active) {
|
|
1420
|
-
setOperationAllowancesError(e?.message || "Failed to load allowances");
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
finally {
|
|
1424
|
-
if (active) {
|
|
1425
|
-
setOperationAllowancesLoading(false);
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
};
|
|
1429
|
-
const loadAvailableTools = async () => {
|
|
1430
|
-
try {
|
|
1431
|
-
setAvailableToolsLoading(true);
|
|
1432
|
-
setAvailableToolsError(null);
|
|
1433
|
-
const tools = await getAgentAvailableTools(agent.id);
|
|
1434
|
-
if (active) {
|
|
1435
|
-
setAvailableTools(tools);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
catch (e) {
|
|
1439
|
-
if (active) {
|
|
1440
|
-
setAvailableToolsError(e?.message || "Failed to load available tools");
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
finally {
|
|
1444
|
-
if (active) {
|
|
1445
|
-
setAvailableToolsLoading(false);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
};
|
|
1449
|
-
void loadTriggerSubscriptions();
|
|
1450
|
-
void loadAvailableTools();
|
|
1451
|
-
void loadOperationAllowances();
|
|
1452
|
-
return () => {
|
|
1453
|
-
active = false;
|
|
1454
|
-
};
|
|
1455
|
-
}, [
|
|
1456
|
-
showAgentSettings,
|
|
1457
|
-
agent?.id,
|
|
1458
|
-
agent?.status,
|
|
1459
|
-
agent?.userId,
|
|
1460
|
-
agent?.profileId,
|
|
1461
|
-
mode,
|
|
1462
|
-
selectedSkillIds,
|
|
1463
|
-
]);
|
|
1464
|
-
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
1465
|
-
const allowanceGroups = useMemo(() => [
|
|
1466
|
-
{
|
|
1467
|
-
key: "sitecore",
|
|
1468
|
-
label: "Sitecore",
|
|
1469
|
-
rows: operationAllowances.sitecore,
|
|
1470
|
-
},
|
|
1471
|
-
{
|
|
1472
|
-
key: "filesystem",
|
|
1473
|
-
label: "Filesystem",
|
|
1474
|
-
rows: operationAllowances.filesystem,
|
|
1475
|
-
},
|
|
1476
|
-
], [operationAllowances]);
|
|
1477
|
-
const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
|
|
1478
|
-
operationAllowances.filesystem.length > 0, [operationAllowances]);
|
|
1479
|
-
const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
|
|
1480
|
-
operationAllowances.filesystem.length, [operationAllowances]);
|
|
1481
|
-
const listedProfileSkillIdSet = useMemo(() => {
|
|
1482
|
-
const ids = [
|
|
1483
|
-
...(activeProfile?.availableSkills ?? []),
|
|
1484
|
-
...(activeProfile?.allowedSkills ?? []),
|
|
1485
|
-
]
|
|
1486
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1487
|
-
.filter((id) => id.length > 0);
|
|
1488
|
-
return new Set(ids);
|
|
1489
|
-
}, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
|
|
1490
|
-
const profileFilteredSkills = useMemo(() => {
|
|
1491
|
-
if (listedProfileSkillIdSet.size === 0) {
|
|
1492
|
-
return [];
|
|
1493
|
-
}
|
|
1494
|
-
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1495
|
-
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1496
|
-
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1497
|
-
const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
|
|
1498
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1499
|
-
.filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
|
|
1500
|
-
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
1501
|
-
const selectedSkills = useMemo(() => selectedSkillIds
|
|
1502
|
-
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
1503
|
-
.filter((s) => !!s), [availableSkills, selectedSkillIds]);
|
|
1504
|
-
const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
|
|
1505
|
-
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1506
|
-
const previewAvailableTools = useMemo(() => {
|
|
1507
|
-
const baseToolNames = mode === "read-only"
|
|
1508
|
-
? (activeProfile?.readOnlyToolNames ?? [])
|
|
1509
|
-
: (activeProfile?.allowedToolNames ?? []);
|
|
1510
|
-
const toolNames = new Set();
|
|
1511
|
-
for (const toolName of baseToolNames) {
|
|
1512
|
-
const normalized = String(toolName || "").trim();
|
|
1513
|
-
if (normalized) {
|
|
1514
|
-
toolNames.add(normalized);
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
for (const skill of selectedSkills) {
|
|
1518
|
-
for (const toolName of skill.additionalAllowedTools ?? []) {
|
|
1519
|
-
const normalized = String(toolName || "").trim();
|
|
1520
|
-
if (normalized) {
|
|
1521
|
-
toolNames.add(normalized);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
|
|
1526
|
-
}, [
|
|
1527
|
-
activeProfile?.allowedToolNames,
|
|
1528
|
-
activeProfile?.readOnlyToolNames,
|
|
1529
|
-
mode,
|
|
1530
|
-
selectedSkills,
|
|
1531
|
-
]);
|
|
1532
|
-
const displayedAvailableTools = isLocalOnlyDraftAgent
|
|
1533
|
-
? previewAvailableTools
|
|
1534
|
-
: availableTools;
|
|
1535
|
-
const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
|
|
1536
|
-
? false
|
|
1537
|
-
: availableToolsLoading;
|
|
1538
|
-
const displayedAvailableToolsError = isLocalOnlyDraftAgent
|
|
1539
|
-
? null
|
|
1540
|
-
: availableToolsError;
|
|
1541
813
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1542
814
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1543
815
|
try {
|
|
@@ -1562,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1562
834
|
return meta;
|
|
1563
835
|
}
|
|
1564
836
|
}, []);
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
return;
|
|
1578
|
-
}
|
|
1579
|
-
const current = agentMetadata || {};
|
|
1580
|
-
const currentAdditionalData = current.additionalData ||
|
|
1581
|
-
{};
|
|
1582
|
-
const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
|
|
1583
|
-
const next = {
|
|
1584
|
-
...current,
|
|
837
|
+
// Read deterministic flags from query string once
|
|
838
|
+
let deterministicFlags;
|
|
839
|
+
try {
|
|
840
|
+
const params = new URLSearchParams(window.location.search);
|
|
841
|
+
const detParam = params.get("deterministic");
|
|
842
|
+
const deterministic = detParam === "true" ||
|
|
843
|
+
(detParam === null ? params.has("deterministic") : false);
|
|
844
|
+
const seedStr = params.get("seed");
|
|
845
|
+
const seed = seedStr ? Number(seedStr) : undefined;
|
|
846
|
+
deterministicFlags = {
|
|
847
|
+
deterministic,
|
|
848
|
+
seed: Number.isFinite(seed) ? seed : undefined,
|
|
1585
849
|
};
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
}
|
|
1592
|
-
try {
|
|
1593
|
-
await updateAgentContext(agent.id, next);
|
|
1594
|
-
setAgentMetadata(next);
|
|
1595
|
-
setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
|
|
1596
|
-
}
|
|
1597
|
-
catch (e) {
|
|
1598
|
-
console.error("Failed to clear legacy selected skills", e);
|
|
1599
|
-
}
|
|
1600
|
-
}, [
|
|
1601
|
-
agent?.id,
|
|
1602
|
-
agentMetadata,
|
|
1603
|
-
metadataSelectedSkillIds,
|
|
1604
|
-
setAgent,
|
|
1605
|
-
setAgentMetadata,
|
|
1606
|
-
]);
|
|
1607
|
-
const parsePersistedAgentContext = (contextJson) => {
|
|
1608
|
-
try {
|
|
1609
|
-
if (!contextJson)
|
|
1610
|
-
return null;
|
|
1611
|
-
const parsedContext = JSON.parse(contextJson);
|
|
1612
|
-
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1613
|
-
return null;
|
|
1614
|
-
}
|
|
1615
|
-
return sanitizeAgentMetadata(parsedContext);
|
|
1616
|
-
}
|
|
1617
|
-
catch {
|
|
1618
|
-
return null;
|
|
1619
|
-
}
|
|
1620
|
-
};
|
|
1621
|
-
const buildDraftPersistenceContext = () => {
|
|
1622
|
-
let effectiveContext = agentMetadata;
|
|
1623
|
-
try {
|
|
1624
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
1625
|
-
const profile = activeProfile ||
|
|
1626
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
1627
|
-
const isLiveMode = profile?.editorContextMode === "live";
|
|
1628
|
-
if (isLiveMode && typeof buildCurrentContext === "function") {
|
|
1629
|
-
const freshContext = buildCurrentContext();
|
|
1630
|
-
if (freshContext) {
|
|
1631
|
-
effectiveContext = {
|
|
1632
|
-
...(agentMetadata || {}),
|
|
1633
|
-
items: freshContext.items,
|
|
1634
|
-
currentItemId: freshContext.currentItemId,
|
|
1635
|
-
components: freshContext.components,
|
|
1636
|
-
field: freshContext.field,
|
|
1637
|
-
activeWorkspace: freshContext.activeWorkspace,
|
|
1638
|
-
hasPageLoaded: freshContext.hasPageLoaded,
|
|
1639
|
-
openSidebars: freshContext.openSidebars,
|
|
1640
|
-
};
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
catch (e) {
|
|
1645
|
-
console.warn("[AgentTerminal] Failed to compute draft context:", e);
|
|
1646
|
-
}
|
|
1647
|
-
return sanitizeAgentMetadata(effectiveContext || null);
|
|
1648
|
-
};
|
|
1649
|
-
const ensureDraftAgentPersisted = async () => {
|
|
1650
|
-
if (!agent?.id) {
|
|
1651
|
-
throw new Error("Agent not ready. Please try again.");
|
|
1652
|
-
}
|
|
1653
|
-
if (!isLocalOnlyDraftAgent) {
|
|
1654
|
-
return agent;
|
|
1655
|
-
}
|
|
1656
|
-
const requestSettings = getPendingRequestSettings();
|
|
1657
|
-
if (!requestSettings.profileId) {
|
|
1658
|
-
throw new Error("Select an agent profile before adding a skill.");
|
|
1659
|
-
}
|
|
1660
|
-
const effectiveContext = buildDraftPersistenceContext();
|
|
1661
|
-
const persistedAgent = await persistDraftAgent({
|
|
1662
|
-
agentId: agent.id,
|
|
1663
|
-
sessionId: editContext?.sessionId || undefined,
|
|
1664
|
-
profileId: requestSettings.profileId,
|
|
1665
|
-
mode: requestSettings.mode,
|
|
1666
|
-
model: requestSettings.modelId || undefined,
|
|
1667
|
-
name: agent.name,
|
|
1668
|
-
context: effectiveContext,
|
|
1669
|
-
});
|
|
1670
|
-
pendingSettingsRef.current = null;
|
|
1671
|
-
const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
|
|
1672
|
-
effectiveContext;
|
|
1673
|
-
setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
|
|
1674
|
-
setAgent((prev) => {
|
|
1675
|
-
const next = {
|
|
1676
|
-
...(prev || {}),
|
|
1677
|
-
...persistedAgent,
|
|
1678
|
-
};
|
|
1679
|
-
if (prev?.messages?.length && !persistedAgent.messages?.length) {
|
|
1680
|
-
next.messages = prev.messages;
|
|
1681
|
-
}
|
|
1682
|
-
return next;
|
|
1683
|
-
});
|
|
1684
|
-
onAgentUpdate?.(persistedAgent);
|
|
1685
|
-
return persistedAgent;
|
|
1686
|
-
};
|
|
1687
|
-
const handleAddSkill = useCallback(async (skillId) => {
|
|
1688
|
-
if (selectedSkillSet.has(skillId.toLowerCase()))
|
|
1689
|
-
return;
|
|
1690
|
-
if (!agent?.id)
|
|
1691
|
-
return;
|
|
1692
|
-
try {
|
|
1693
|
-
setSkillActionError(null);
|
|
1694
|
-
const persistedAgent = await ensureDraftAgentPersisted();
|
|
1695
|
-
await assignAgentSkill({ agentId: persistedAgent.id, skillId });
|
|
1696
|
-
setAgent((prev) => {
|
|
1697
|
-
if (!prev)
|
|
1698
|
-
return prev;
|
|
1699
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1700
|
-
? prev.assignedSkillIds
|
|
1701
|
-
: [];
|
|
1702
|
-
if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
|
|
1703
|
-
return prev;
|
|
1704
|
-
}
|
|
1705
|
-
return {
|
|
1706
|
-
...prev,
|
|
1707
|
-
assignedSkillIds: [...currentAssigned, skillId],
|
|
1708
|
-
};
|
|
1709
|
-
});
|
|
1710
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1711
|
-
return true;
|
|
1712
|
-
}
|
|
1713
|
-
catch (e) {
|
|
1714
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1715
|
-
return false;
|
|
1716
|
-
}
|
|
1717
|
-
}, [
|
|
1718
|
-
agent?.id,
|
|
1719
|
-
assignAgentSkill,
|
|
1720
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1721
|
-
ensureDraftAgentPersisted,
|
|
1722
|
-
getSkillActionErrorMessage,
|
|
1723
|
-
setAgent,
|
|
1724
|
-
selectedSkillSet,
|
|
1725
|
-
]);
|
|
1726
|
-
const handleRemoveSkill = useCallback(async (skillId) => {
|
|
1727
|
-
if (!agent?.id)
|
|
1728
|
-
return;
|
|
1729
|
-
try {
|
|
1730
|
-
setSkillActionError(null);
|
|
1731
|
-
await revokeAgentSkill({ agentId: agent.id, skillId });
|
|
1732
|
-
setAgent((prev) => {
|
|
1733
|
-
if (!prev)
|
|
1734
|
-
return prev;
|
|
1735
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1736
|
-
? prev.assignedSkillIds
|
|
1737
|
-
: [];
|
|
1738
|
-
return {
|
|
1739
|
-
...prev,
|
|
1740
|
-
assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
|
|
1741
|
-
};
|
|
1742
|
-
});
|
|
1743
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1744
|
-
}
|
|
1745
|
-
catch (e) {
|
|
1746
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1747
|
-
}
|
|
1748
|
-
}, [
|
|
1749
|
-
agent?.id,
|
|
1750
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1751
|
-
getSkillActionErrorMessage,
|
|
1752
|
-
revokeAgentSkill,
|
|
1753
|
-
setAgent,
|
|
1754
|
-
]);
|
|
1755
|
-
const handleOpenProfileSettings = useCallback(async () => {
|
|
1756
|
-
if (!editContext || !activeProfile?.id)
|
|
1757
|
-
return;
|
|
1758
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1759
|
-
await editContext.loadItem({
|
|
1760
|
-
id: activeProfile.id,
|
|
1761
|
-
language: lang,
|
|
1762
|
-
version: 0,
|
|
1763
|
-
});
|
|
1764
|
-
editContext.switchWorkspace("editor");
|
|
1765
|
-
}, [editContext, activeProfile?.id]);
|
|
1766
|
-
const handleEditProfileSideBySide = useCallback(async () => {
|
|
1767
|
-
if (!editContext || !activeProfile?.id)
|
|
1768
|
-
return;
|
|
1769
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1770
|
-
const profileItem = {
|
|
1771
|
-
id: activeProfile.id,
|
|
1772
|
-
language: lang,
|
|
1773
|
-
version: 0,
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
deterministicFlags = {
|
|
853
|
+
deterministic: false,
|
|
854
|
+
seed: undefined,
|
|
1774
855
|
};
|
|
1775
|
-
|
|
1776
|
-
editContext.setShowAgentsWorkspaceEditor(true);
|
|
1777
|
-
await editContext.loadItem(profileItem);
|
|
1778
|
-
return;
|
|
1779
|
-
}
|
|
1780
|
-
await editContext.loadItem(profileItem, {
|
|
1781
|
-
openInNewSlot: true,
|
|
1782
|
-
});
|
|
1783
|
-
editContext.switchWorkspace("editor");
|
|
1784
|
-
}, [editContext, activeProfile?.id]);
|
|
1785
|
-
const handleOpenSkillItem = useCallback(async (skillId) => {
|
|
1786
|
-
if (!editContext || !skillId)
|
|
1787
|
-
return;
|
|
1788
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1789
|
-
await editContext.loadItem({
|
|
1790
|
-
id: skillId,
|
|
1791
|
-
language: lang,
|
|
1792
|
-
version: 0,
|
|
1793
|
-
});
|
|
1794
|
-
editContext.switchWorkspace("editor");
|
|
1795
|
-
}, [editContext]);
|
|
856
|
+
}
|
|
1796
857
|
useEffect(() => {
|
|
1797
|
-
|
|
858
|
+
localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
|
|
1798
859
|
}, [promptHistory]);
|
|
1799
860
|
useEffect(() => {
|
|
1800
861
|
// Keep messagesRef synchronized with messages state
|
|
@@ -1806,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1806
867
|
const [liveTotals, setLiveTotals] = useState(null);
|
|
1807
868
|
// Context window status from backend (extracted from delta cost object)
|
|
1808
869
|
const [contextWindowStatus, setContextWindowStatus] = useState(null);
|
|
1809
|
-
const effectiveModelName = useMemo(() => {
|
|
1810
|
-
const fromContextWindow = contextWindowStatus?.model?.trim();
|
|
1811
|
-
if (fromContextWindow)
|
|
1812
|
-
return fromContextWindow;
|
|
1813
|
-
const fromAgent = agent?.model?.trim();
|
|
1814
|
-
if (fromAgent)
|
|
1815
|
-
return fromAgent;
|
|
1816
|
-
const models = activeProfile?.models || [];
|
|
1817
|
-
const selectedModelName = selectedModelId
|
|
1818
|
-
? models.find((m) => m.id === selectedModelId)?.name?.trim()
|
|
1819
|
-
: undefined;
|
|
1820
|
-
if (selectedModelName)
|
|
1821
|
-
return selectedModelName;
|
|
1822
|
-
const defaultModelName = activeProfile?.defaultModelId
|
|
1823
|
-
? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
|
|
1824
|
-
: undefined;
|
|
1825
|
-
if (defaultModelName)
|
|
1826
|
-
return defaultModelName;
|
|
1827
|
-
return models[0]?.name?.trim() || undefined;
|
|
1828
|
-
}, [
|
|
1829
|
-
contextWindowStatus?.model,
|
|
1830
|
-
agent?.model,
|
|
1831
|
-
activeProfile?.models,
|
|
1832
|
-
activeProfile?.defaultModelId,
|
|
1833
|
-
selectedModelId,
|
|
1834
|
-
]);
|
|
1835
870
|
// Flag to track when we should create a new message
|
|
1836
871
|
const shouldCreateNewMessage = useRef(false);
|
|
1837
872
|
// Keep a ref to the current messages for immediate access
|
|
@@ -1842,13 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1842
877
|
const placeholderInputRef = useRef(null);
|
|
1843
878
|
const promptPlaceholderInputRef = useRef(null);
|
|
1844
879
|
const messagesContainerRef = useRef(null);
|
|
1845
|
-
const inlineDialogContainerRef = useRef(null);
|
|
1846
880
|
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
1847
881
|
// WebSocket subscription state for agent streaming
|
|
1848
882
|
const seenMessageIdsRef = useRef(new Set());
|
|
1849
883
|
const lastSeqRef = useRef(0);
|
|
1850
884
|
const subscribedAgentIdRef = useRef(null);
|
|
1851
|
-
// Cache mode/model
|
|
885
|
+
// Cache mode/model changes made while the agent is still "new" (not yet persisted)
|
|
1852
886
|
const pendingSettingsRef = useRef(null);
|
|
1853
887
|
// Track whether textarea should maintain focus during re-renders
|
|
1854
888
|
const shouldMaintainFocusRef = useRef(false);
|
|
@@ -1870,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1870
904
|
setIsVoiceSupported(!!SR);
|
|
1871
905
|
if (SR === undefined) {
|
|
1872
906
|
setIsVoiceDisabled(true);
|
|
1873
|
-
|
|
907
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1874
908
|
return;
|
|
1875
909
|
}
|
|
1876
910
|
else {
|
|
1877
|
-
const wasDisabled =
|
|
911
|
+
const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
|
|
1878
912
|
setIsVoiceDisabled(wasDisabled);
|
|
1879
913
|
}
|
|
1880
914
|
}
|
|
@@ -1885,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1885
919
|
// Auto-focus terminal input on mount
|
|
1886
920
|
useEffect(() => {
|
|
1887
921
|
if (textareaRef.current) {
|
|
1888
|
-
|
|
1889
|
-
textareaRef.current.focus({ preventScroll: true });
|
|
1890
|
-
}
|
|
1891
|
-
catch {
|
|
1892
|
-
textareaRef.current.focus();
|
|
1893
|
-
}
|
|
922
|
+
textareaRef.current.focus();
|
|
1894
923
|
}
|
|
1895
924
|
}, []);
|
|
1896
925
|
// Start voice recognition
|
|
@@ -1909,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1909
938
|
r.lang = editContext?.currentItemDescriptor?.language || "en-US";
|
|
1910
939
|
r.continuous = true;
|
|
1911
940
|
r.interimResults = true;
|
|
1912
|
-
promptBeforeVoiceRef.current = prompt;
|
|
1913
941
|
r.onstart = () => {
|
|
1914
942
|
setIsListening(true);
|
|
1915
943
|
prevPlaceholderRef.current = inputPlaceholder;
|
|
1916
944
|
setInputPlaceholder("Listening...");
|
|
1917
945
|
};
|
|
1918
|
-
// Desktop Chrome: single result at index 0, transitions interim→final once.
|
|
1919
|
-
// Mobile Chrome: new result index per event, ALL immediately isFinal,
|
|
1920
|
-
// each containing the full cumulative transcript. We always take the last
|
|
1921
|
-
// non-empty final result and REPLACE the prompt to handle both patterns.
|
|
1922
946
|
r.onresult = (event) => {
|
|
1923
|
-
let
|
|
947
|
+
let finalText = "";
|
|
1924
948
|
let interimText = "";
|
|
1925
|
-
for (let i = event.
|
|
949
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
1926
950
|
const res = event.results[i];
|
|
1927
|
-
if (res.isFinal
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
}
|
|
1932
|
-
if (!res.isFinal) {
|
|
1933
|
-
interimText = (res[0]?.transcript || "") + interimText;
|
|
1934
|
-
}
|
|
951
|
+
if (res.isFinal)
|
|
952
|
+
finalText += res[0]?.transcript || "";
|
|
953
|
+
else
|
|
954
|
+
interimText += res[0]?.transcript || "";
|
|
1935
955
|
}
|
|
1936
956
|
if (interimText) {
|
|
1937
957
|
setInputPlaceholder(`Listening... ${interimText.trim()}`);
|
|
@@ -1939,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1939
959
|
else {
|
|
1940
960
|
setInputPlaceholder("Listening...");
|
|
1941
961
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
962
|
+
if (finalText && finalText.trim()) {
|
|
963
|
+
setPrompt((prev) => {
|
|
964
|
+
const prefix = prev && !prev.endsWith(" ") ? prev + " " : prev;
|
|
965
|
+
return (prefix || "") + finalText.trim() + " ";
|
|
966
|
+
});
|
|
1947
967
|
if (textareaRef.current) {
|
|
1948
968
|
try {
|
|
1949
969
|
const v = textareaRef.current.value || "";
|
|
@@ -1959,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1959
979
|
// network error also comes from incompatible chromium client
|
|
1960
980
|
if (e?.error === "network") {
|
|
1961
981
|
try {
|
|
1962
|
-
|
|
982
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1963
983
|
setIsVoiceDisabled(true);
|
|
1964
984
|
}
|
|
1965
985
|
catch { }
|
|
@@ -2128,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2128
1148
|
// Shared stream message handlers with messageId support
|
|
2129
1149
|
const createNewStreamMessage = useCallback((messageId, agentData) => {
|
|
2130
1150
|
const currentAgent = agentData || agent;
|
|
2131
|
-
|
|
1151
|
+
if (!currentAgent) {
|
|
1152
|
+
console.error("❌ createNewStreamMessage: No agent available", {
|
|
1153
|
+
messageId,
|
|
1154
|
+
agentData: !!agentData,
|
|
1155
|
+
agent: !!agent,
|
|
1156
|
+
});
|
|
1157
|
+
throw new Error("No agent available");
|
|
1158
|
+
}
|
|
2132
1159
|
// Reduced: avoid verbose logging during streaming
|
|
2133
1160
|
return {
|
|
2134
1161
|
id: messageId,
|
|
2135
|
-
agentId:
|
|
1162
|
+
agentId: currentAgent.id,
|
|
2136
1163
|
messageIndex: -1,
|
|
2137
1164
|
role: "assistant",
|
|
2138
1165
|
content: "",
|
|
2139
1166
|
name: "agent",
|
|
2140
1167
|
messageType: "streaming",
|
|
2141
1168
|
isCompleted: false,
|
|
2142
|
-
model: currentAgent
|
|
1169
|
+
model: currentAgent.model || "",
|
|
2143
1170
|
tokensUsed: 0,
|
|
2144
1171
|
inputTokens: 0,
|
|
2145
1172
|
outputTokens: 0,
|
|
@@ -2148,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2148
1175
|
outputTokenCost: 0,
|
|
2149
1176
|
cachedInputTokenCost: 0,
|
|
2150
1177
|
totalCost: 0,
|
|
2151
|
-
currency: currentAgent
|
|
1178
|
+
currency: currentAgent.currency || "USD",
|
|
2152
1179
|
createdDate: new Date().toISOString(),
|
|
2153
1180
|
toolCalls: [],
|
|
2154
1181
|
};
|
|
2155
|
-
}, [agent
|
|
2156
|
-
const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
|
|
2157
|
-
const handleHeartbeatMessage = useCallback((message) => {
|
|
2158
|
-
const heartbeatText = (message.data?.message ||
|
|
2159
|
-
message.data?.Message ||
|
|
2160
|
-
"Still working on your request. This is taking a little longer than usual.").trim() ||
|
|
2161
|
-
"Still working on your request. This is taking a little longer than usual.";
|
|
2162
|
-
const createdDate = message.timestamp || new Date().toISOString();
|
|
2163
|
-
const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
|
|
2164
|
-
setMessages((prev) => {
|
|
2165
|
-
const withoutHeartbeats = stripHeartbeatMessages(prev);
|
|
2166
|
-
const updated = [
|
|
2167
|
-
...withoutHeartbeats,
|
|
2168
|
-
{
|
|
2169
|
-
id: heartbeatId,
|
|
2170
|
-
agentId: agent?.id || agentStub.id,
|
|
2171
|
-
messageIndex: -1,
|
|
2172
|
-
role: "system",
|
|
2173
|
-
content: heartbeatText,
|
|
2174
|
-
name: "system",
|
|
2175
|
-
messageType: "heartbeat",
|
|
2176
|
-
isCompleted: true,
|
|
2177
|
-
model: "",
|
|
2178
|
-
tokensUsed: 0,
|
|
2179
|
-
inputTokens: 0,
|
|
2180
|
-
outputTokens: 0,
|
|
2181
|
-
cachedInputTokens: 0,
|
|
2182
|
-
inputTokenCost: 0,
|
|
2183
|
-
outputTokenCost: 0,
|
|
2184
|
-
cachedInputTokenCost: 0,
|
|
2185
|
-
totalCost: 0,
|
|
2186
|
-
currency: "USD",
|
|
2187
|
-
createdDate,
|
|
2188
|
-
toolCalls: [],
|
|
2189
|
-
},
|
|
2190
|
-
];
|
|
2191
|
-
messagesRef.current = updated;
|
|
2192
|
-
return updated;
|
|
2193
|
-
});
|
|
2194
|
-
}, [agent?.id, agentStub.id, stripHeartbeatMessages]);
|
|
2195
|
-
const clearHeartbeatMessages = useCallback(() => {
|
|
2196
|
-
setMessages((prev) => {
|
|
2197
|
-
const updated = stripHeartbeatMessages(prev);
|
|
2198
|
-
if (updated.length === prev.length) {
|
|
2199
|
-
return prev;
|
|
2200
|
-
}
|
|
2201
|
-
messagesRef.current = updated;
|
|
2202
|
-
return updated;
|
|
2203
|
-
});
|
|
2204
|
-
}, [stripHeartbeatMessages]);
|
|
1182
|
+
}, [agent]);
|
|
2205
1183
|
const handleContentChunk = useCallback((message, agentData) => {
|
|
2206
1184
|
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
2207
1185
|
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
2208
1186
|
let messageId = message.data?.messageId;
|
|
2209
1187
|
if (!messageId && agentData?.id) {
|
|
2210
|
-
console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
|
|
2211
|
-
agentId: agentData.id,
|
|
2212
|
-
isIncremental: message.data?.isIncremental,
|
|
2213
|
-
previousContentLength: message.data?.previousContentLength,
|
|
2214
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2215
|
-
});
|
|
2216
1188
|
// For backward compatibility: if no messageId, find or create the current streaming message
|
|
2217
1189
|
// This handles cases where the backend doesn't send messageId
|
|
2218
1190
|
const currentMessages = messagesRef.current;
|
|
@@ -2226,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2226
1198
|
// If the agent isn't currently running (e.g., we switched tabs after the run
|
|
2227
1199
|
// completed), skip creating a new streaming message to avoid duplicates.
|
|
2228
1200
|
const currentAgentStatus = (agentData || agent)?.status;
|
|
2229
|
-
const isAgentRunning = currentAgentStatus === "running";
|
|
1201
|
+
const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
|
|
2230
1202
|
if (!isAgentRunning) {
|
|
2231
1203
|
return;
|
|
2232
1204
|
}
|
|
@@ -2259,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2259
1231
|
outputCost: Number(cost.output) || 0,
|
|
2260
1232
|
cachedCost: Number(cost.cached) || 0,
|
|
2261
1233
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2262
|
-
imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
|
|
2263
|
-
0,
|
|
2264
1234
|
totalCost: Number(cost.total) || 0,
|
|
2265
1235
|
currency: "USD",
|
|
2266
1236
|
};
|
|
@@ -2284,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2284
1254
|
setIsWaitingForResponse(false);
|
|
2285
1255
|
shouldCreateNewMessage.current = false;
|
|
2286
1256
|
}
|
|
2287
|
-
// Extract context window info from cost object
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
if (contextWindowRaw !== undefined &&
|
|
2293
|
-
contextWindowRaw !== null &&
|
|
2294
|
-
contextUsedRaw !== undefined &&
|
|
2295
|
-
contextUsedRaw !== null) {
|
|
2296
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2297
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2298
|
-
if (contextWindowValue > 0 &&
|
|
2299
|
-
Number.isFinite(contextUsedValue) &&
|
|
2300
|
-
contextUsedValue >= 0) {
|
|
1257
|
+
// Extract context window info from cost object
|
|
1258
|
+
if (cost.contextWindow && cost.contextUsed) {
|
|
1259
|
+
const contextWindowValue = Number(cost.contextWindow);
|
|
1260
|
+
const contextUsedValue = Number(cost.contextUsed);
|
|
1261
|
+
if (contextWindowValue > 0 && contextUsedValue >= 0) {
|
|
2301
1262
|
setContextWindowStatus({
|
|
2302
1263
|
contextWindowTokens: contextWindowValue,
|
|
2303
1264
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2313,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2313
1274
|
const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
|
|
2314
1275
|
if (existingMessageIndex === -1) {
|
|
2315
1276
|
// Message doesn't exist - create new streaming message
|
|
2316
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2317
|
-
if (message.data?.isIncremental && previousContentLength > 0) {
|
|
2318
|
-
console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
|
|
2319
|
-
messageId,
|
|
2320
|
-
previousContentLength,
|
|
2321
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2322
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2323
|
-
});
|
|
2324
|
-
}
|
|
2325
1277
|
const newStreamMessage = createNewStreamMessage(messageId, agentData);
|
|
2326
1278
|
// Set the content for the new message
|
|
2327
1279
|
const updatedNewMessage = { ...newStreamMessage };
|
|
@@ -2344,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2344
1296
|
return prev;
|
|
2345
1297
|
// Check if existing content is already longer than what we're trying to stream
|
|
2346
1298
|
const currentContentLength = existingMessage.content?.length || 0;
|
|
2347
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2348
1299
|
const totalContentLength = message.data?.totalContentLength || 0;
|
|
2349
|
-
if (
|
|
2350
|
-
previousContentLength !== currentContentLength &&
|
|
2351
|
-
(previousContentLength > 0 || currentContentLength > 0)) {
|
|
2352
|
-
console.warn("[AgentTerminal] Content chunk length mismatch", {
|
|
2353
|
-
messageId,
|
|
2354
|
-
previousContentLength,
|
|
2355
|
-
currentContentLength,
|
|
2356
|
-
totalContentLength,
|
|
2357
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2358
|
-
});
|
|
2359
|
-
}
|
|
2360
|
-
if (message.data?.isIncremental &&
|
|
2361
|
-
currentContentLength >= totalContentLength &&
|
|
1300
|
+
if (currentContentLength >= totalContentLength &&
|
|
2362
1301
|
totalContentLength > 0) {
|
|
2363
1302
|
return prev;
|
|
2364
1303
|
}
|
|
@@ -2382,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2382
1321
|
});
|
|
2383
1322
|
}, [createNewStreamMessage, agent]);
|
|
2384
1323
|
const handleToolCall = useCallback((message, agentData) => {
|
|
2385
|
-
const
|
|
2386
|
-
const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1324
|
+
const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2387
1325
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2388
1326
|
let toolCallMessageId = message.data?.messageId;
|
|
2389
1327
|
if (!toolCallMessageId) {
|
|
2390
|
-
console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
|
|
2391
|
-
toolCallId,
|
|
2392
|
-
toolName: message.data?.name || message.data?.displayName,
|
|
2393
|
-
});
|
|
2394
1328
|
const current = messagesRef.current;
|
|
2395
1329
|
const lastStreaming = [...current]
|
|
2396
1330
|
.reverse()
|
|
@@ -2398,11 +1332,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2398
1332
|
if (lastStreaming?.id) {
|
|
2399
1333
|
toolCallMessageId = lastStreaming.id;
|
|
2400
1334
|
}
|
|
2401
|
-
else {
|
|
2402
|
-
// Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
|
|
2403
|
-
// Create a synthetic streaming message so the UI can render the tool call immediately.
|
|
2404
|
-
toolCallMessageId = crypto.randomUUID();
|
|
2405
|
-
}
|
|
2406
1335
|
}
|
|
2407
1336
|
// Find or create the target message for this tool call
|
|
2408
1337
|
if (toolCallMessageId) {
|
|
@@ -2430,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2430
1359
|
}
|
|
2431
1360
|
// Add tool call to the message in the array
|
|
2432
1361
|
if (toolCallMessageId && message.data && toolCallId) {
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
new Date().toISOString();
|
|
1362
|
+
const functionName = message.data.functionName ||
|
|
1363
|
+
message.data.name ||
|
|
1364
|
+
message.data.function?.name ||
|
|
1365
|
+
"unknown";
|
|
2438
1366
|
const toolCall = {
|
|
2439
1367
|
id: toolCallId,
|
|
2440
1368
|
messageId: toolCallMessageId,
|
|
2441
1369
|
dbMessageId: message.data.messageId, // Database message ID for approval/rejection
|
|
2442
1370
|
toolCallId: toolCallId,
|
|
2443
|
-
functionName:
|
|
2444
|
-
functionArguments:
|
|
1371
|
+
functionName: functionName,
|
|
1372
|
+
functionArguments: message.data.functionArguments ||
|
|
1373
|
+
message.data.arguments ||
|
|
1374
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2445
1375
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2446
|
-
|
|
2447
|
-
functionError: toolCallError,
|
|
2448
|
-
isPruned,
|
|
1376
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
2449
1377
|
isCompleted: false,
|
|
2450
1378
|
responseTimeMs: message.data.responseTimeMs,
|
|
2451
|
-
createdDate:
|
|
1379
|
+
createdDate: new Date().toISOString(),
|
|
2452
1380
|
requiresApproval: message.data?.requiresApproval,
|
|
2453
1381
|
};
|
|
2454
1382
|
// Check for existing tool call - search across ALL messages by toolCallId first
|
|
@@ -2487,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2487
1415
|
// Check if the new data has more information than what we have
|
|
2488
1416
|
const newArgs = toolCall.functionArguments;
|
|
2489
1417
|
const existingArgs = existingToolCall.functionArguments;
|
|
2490
|
-
const
|
|
2491
|
-
const existingArgsText = stringifyToolField(existingArgs) || "";
|
|
2492
|
-
const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
|
|
2493
|
-
newArgsText !== existingArgsText) ||
|
|
2494
|
-
(existingArgsText === "{}" && newArgsText !== "{}");
|
|
1418
|
+
const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
|
|
2495
1419
|
const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
|
|
2496
|
-
const hasNewRichContent = toolCall.functionResultRichContent &&
|
|
2497
|
-
!existingToolCall.functionResultRichContent;
|
|
2498
1420
|
const hasNewError = toolCall.functionError && !existingToolCall.functionError;
|
|
2499
1421
|
const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
|
|
2500
1422
|
const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
|
|
2501
1423
|
// Only update if there's meaningful new data
|
|
2502
1424
|
if (hasMoreCompleteArgs ||
|
|
2503
1425
|
hasNewResult ||
|
|
2504
|
-
hasNewRichContent ||
|
|
2505
1426
|
hasNewError ||
|
|
2506
1427
|
hasNewApprovalInfo ||
|
|
2507
1428
|
hasNewDbMessageId) {
|
|
@@ -2518,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2518
1439
|
updatedToolCalls[idx] = {
|
|
2519
1440
|
...existing,
|
|
2520
1441
|
functionArguments: hasMoreCompleteArgs
|
|
2521
|
-
?
|
|
2522
|
-
:
|
|
1442
|
+
? newArgs
|
|
1443
|
+
: existing.functionArguments,
|
|
2523
1444
|
functionResult: toolCall.functionResult || existing.functionResult,
|
|
2524
|
-
functionResultRichContent: toolCall.functionResultRichContent ||
|
|
2525
|
-
existing.functionResultRichContent,
|
|
2526
1445
|
functionError: toolCall.functionError || existing.functionError,
|
|
2527
1446
|
requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
|
|
2528
1447
|
};
|
|
@@ -2557,15 +1476,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2557
1476
|
}
|
|
2558
1477
|
}, [createNewStreamMessage]);
|
|
2559
1478
|
const handleToolResult = useCallback((message, agentData) => {
|
|
2560
|
-
const
|
|
2561
|
-
const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1479
|
+
const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2562
1480
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2563
1481
|
let resultMessageId = message.data?.messageId;
|
|
2564
1482
|
if (!resultMessageId) {
|
|
2565
|
-
console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
|
|
2566
|
-
toolCallId: resultToolCallId,
|
|
2567
|
-
toolName: message.data?.functionName || message.data?.displayName,
|
|
2568
|
-
});
|
|
2569
1483
|
const current = messagesRef.current;
|
|
2570
1484
|
const lastStreaming = [...current]
|
|
2571
1485
|
.reverse()
|
|
@@ -2587,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2587
1501
|
outputCost: Number(cost.output) || 0,
|
|
2588
1502
|
cachedCost: Number(cost.cached) || 0,
|
|
2589
1503
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2590
|
-
imageCost: Number(cost.imageCost) || 0,
|
|
2591
1504
|
totalCost: Number(cost.total) || 0,
|
|
2592
1505
|
currency: "USD",
|
|
2593
1506
|
};
|
|
@@ -2599,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2599
1512
|
if (anyNonZero) {
|
|
2600
1513
|
setLiveTotals(nextTotals);
|
|
2601
1514
|
}
|
|
2602
|
-
// Extract context window info from cost object
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
if (contextWindowRaw !== undefined &&
|
|
2608
|
-
contextWindowRaw !== null &&
|
|
2609
|
-
contextUsedRaw !== undefined &&
|
|
2610
|
-
contextUsedRaw !== null) {
|
|
2611
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2612
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2613
|
-
if (contextWindowValue > 0 &&
|
|
2614
|
-
Number.isFinite(contextUsedValue) &&
|
|
2615
|
-
contextUsedValue >= 0) {
|
|
1515
|
+
// Extract context window info from cost object
|
|
1516
|
+
if (cost.contextWindow && cost.contextUsed) {
|
|
1517
|
+
const contextWindowValue = Number(cost.contextWindow);
|
|
1518
|
+
const contextUsedValue = Number(cost.contextUsed);
|
|
1519
|
+
if (contextWindowValue > 0 && contextUsedValue >= 0) {
|
|
2616
1520
|
setContextWindowStatus({
|
|
2617
1521
|
contextWindowTokens: contextWindowValue,
|
|
2618
1522
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2636,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2636
1540
|
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
2637
1541
|
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
2638
1542
|
cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
|
|
2639
|
-
imageCost: Number(data.totalImageCost) || 0,
|
|
2640
1543
|
totalCost: Number(data.totalCost) || 0,
|
|
2641
1544
|
currency: data.currency || "USD",
|
|
2642
1545
|
};
|
|
@@ -2669,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2669
1572
|
const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
|
|
2670
1573
|
if (existingToolCall && message.data) {
|
|
2671
1574
|
const updatedToolCalls = [...updatedMessage.toolCalls];
|
|
2672
|
-
const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
|
|
2673
|
-
const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
|
|
2674
|
-
const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
|
|
2675
|
-
nextArgsText !== existingArgsText) ||
|
|
2676
|
-
(existingArgsText === "{}" && nextArgsText !== "{}");
|
|
2677
1575
|
const toolCall = {
|
|
2678
1576
|
id: existingToolCall.id,
|
|
2679
1577
|
messageId: existingToolCall.messageId,
|
|
2680
1578
|
toolCallId: existingToolCall.toolCallId,
|
|
2681
1579
|
functionName: existingToolCall.functionName,
|
|
2682
|
-
functionArguments:
|
|
2683
|
-
? nextArgsText
|
|
2684
|
-
: existingToolCall.functionArguments,
|
|
1580
|
+
functionArguments: existingToolCall.functionArguments,
|
|
2685
1581
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2686
|
-
functionResultRichContent: message.data.richContent ||
|
|
2687
|
-
existingToolCall.functionResultRichContent,
|
|
2688
1582
|
functionError: message.data.functionError || message.data.error || "",
|
|
2689
1583
|
isCompleted: true,
|
|
2690
1584
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2701,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2701
1595
|
}
|
|
2702
1596
|
else if (message.data && resultToolCallId && resultMessageId) {
|
|
2703
1597
|
// Create new tool call if it doesn't exist
|
|
2704
|
-
const
|
|
2705
|
-
message.
|
|
2706
|
-
|
|
1598
|
+
const functionName = message.data.functionName ||
|
|
1599
|
+
message.data.name ||
|
|
1600
|
+
message.data.function?.name ||
|
|
1601
|
+
"unknown";
|
|
2707
1602
|
const toolCall = {
|
|
2708
1603
|
id: resultToolCallId,
|
|
2709
1604
|
messageId: resultMessageId,
|
|
2710
1605
|
toolCallId: resultToolCallId,
|
|
2711
|
-
functionName:
|
|
2712
|
-
functionArguments:
|
|
1606
|
+
functionName: functionName,
|
|
1607
|
+
functionArguments: message.data.functionArguments ||
|
|
1608
|
+
message.data.arguments ||
|
|
1609
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2713
1610
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2714
|
-
functionResultRichContent: message.data.richContent || undefined,
|
|
2715
1611
|
functionError: message.data.functionError || message.data.error || "",
|
|
2716
1612
|
isCompleted: true,
|
|
2717
1613
|
responseTimeMs: message.data.responseTimeMs,
|
|
2718
|
-
createdDate:
|
|
1614
|
+
createdDate: new Date().toISOString(),
|
|
2719
1615
|
};
|
|
2720
1616
|
updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
|
|
2721
1617
|
}
|
|
@@ -2806,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2806
1702
|
// The agent might have been persisted after sending a prompt
|
|
2807
1703
|
// Only treat as "new" if backend returns 404
|
|
2808
1704
|
const hasExistingMessages = messagesRef.current.length > 0;
|
|
2809
|
-
if (agentStub.status === "new" &&
|
|
2810
|
-
!agentStub.userId &&
|
|
2811
|
-
!hasExistingMessages) {
|
|
1705
|
+
if (agentStub.status === "new" && !hasExistingMessages) {
|
|
2812
1706
|
// Only initialize as new if we have no messages yet (initial mount)
|
|
2813
1707
|
// Derive initial profile from provided metadata if present
|
|
2814
1708
|
const initialProfileIdFromMeta = (() => {
|
|
@@ -2848,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2848
1742
|
totalInputTokenCost: 0,
|
|
2849
1743
|
totalOutputTokenCost: 0,
|
|
2850
1744
|
totalCachedInputTokenCost: 0,
|
|
2851
|
-
totalImageCost: 0,
|
|
2852
1745
|
totalCost: 0,
|
|
2853
1746
|
messageCount: 0,
|
|
2854
1747
|
});
|
|
@@ -2891,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2891
1784
|
}
|
|
2892
1785
|
})();
|
|
2893
1786
|
// Create context with top-level properties (what ContextInfoBar expects)
|
|
2894
|
-
const localCtx = item && shouldSeedContext
|
|
1787
|
+
const localCtx = item && shouldSeedContext
|
|
1788
|
+
? {
|
|
1789
|
+
items: [
|
|
1790
|
+
{
|
|
1791
|
+
id: item.id,
|
|
1792
|
+
language: item.language,
|
|
1793
|
+
version: item.version,
|
|
1794
|
+
name: editContext?.item?.name,
|
|
1795
|
+
path: editContext?.item?.path,
|
|
1796
|
+
},
|
|
1797
|
+
],
|
|
1798
|
+
components: editContext?.selection?.length && item
|
|
1799
|
+
? editContext.selection.map((componentId) => ({
|
|
1800
|
+
componentId,
|
|
1801
|
+
pageItem: {
|
|
1802
|
+
id: item.id,
|
|
1803
|
+
language: item.language,
|
|
1804
|
+
version: item.version,
|
|
1805
|
+
name: editContext?.item?.name,
|
|
1806
|
+
},
|
|
1807
|
+
}))
|
|
1808
|
+
: undefined,
|
|
1809
|
+
field: fieldsContext?.focusedField?.fieldId &&
|
|
1810
|
+
fieldsContext.focusedField?.item?.id
|
|
1811
|
+
? {
|
|
1812
|
+
fieldId: fieldsContext.focusedField.fieldId,
|
|
1813
|
+
fieldName: fieldsContext.focusedField
|
|
1814
|
+
.fieldName,
|
|
1815
|
+
item: {
|
|
1816
|
+
id: fieldsContext.focusedField.item.id,
|
|
1817
|
+
language: fieldsContext.focusedField.item.language ||
|
|
1818
|
+
editContext?.currentItemDescriptor?.language ||
|
|
1819
|
+
"en",
|
|
1820
|
+
version: fieldsContext.focusedField.item.version ??
|
|
1821
|
+
editContext?.currentItemDescriptor?.version ??
|
|
1822
|
+
0,
|
|
1823
|
+
name: editContext?.item?.name,
|
|
1824
|
+
},
|
|
1825
|
+
}
|
|
1826
|
+
: undefined,
|
|
1827
|
+
}
|
|
1828
|
+
: null;
|
|
2895
1829
|
let nextMetadata = null;
|
|
2896
1830
|
if (initialMetadata) {
|
|
2897
1831
|
// Merge initial metadata with local context (using top-level structure)
|
|
@@ -2967,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2967
1901
|
seenMessageIdsRef.current.add(msg.id.toLowerCase());
|
|
2968
1902
|
}
|
|
2969
1903
|
});
|
|
2970
|
-
// Keep local streaming if the agent is still
|
|
2971
|
-
// This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
|
|
2972
|
-
// "waiting" but we still want to keep the in-flight tool call UI visible.
|
|
1904
|
+
// Keep local streaming only if the agent is still running; otherwise discard locals
|
|
2973
1905
|
const isRunning = agentData.status === "running" || agentData.status === 1;
|
|
2974
|
-
const isWaiting = agentData.status === "waitingForApproval" ||
|
|
2975
|
-
agentData.status === 2 ||
|
|
2976
|
-
agentData.status === "waitingForInput" ||
|
|
2977
|
-
agentData.status === "costLimitReached" ||
|
|
2978
|
-
agentData.status === 7;
|
|
2979
|
-
const keepLocalStreaming = isRunning || isWaiting;
|
|
2980
1906
|
// Filter local messages to only include streaming/incomplete messages that aren't in DB
|
|
2981
1907
|
// This prevents duplicates when reloading - DB messages are source of truth for completed messages
|
|
2982
1908
|
const localStreaming = isRunning
|
|
@@ -2994,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2994
1920
|
// Don't keep completed messages from local state - DB is source of truth
|
|
2995
1921
|
return false;
|
|
2996
1922
|
})
|
|
2997
|
-
:
|
|
2998
|
-
? messagesRef.current.filter((localMsg) => {
|
|
2999
|
-
if (!localMsg.id)
|
|
3000
|
-
return false;
|
|
3001
|
-
if (!localMsg.isCompleted &&
|
|
3002
|
-
localMsg.messageType === "streaming") {
|
|
3003
|
-
const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
|
|
3004
|
-
dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
|
|
3005
|
-
return !existsInDb;
|
|
3006
|
-
}
|
|
3007
|
-
return false;
|
|
3008
|
-
})
|
|
3009
|
-
: [];
|
|
1923
|
+
: [];
|
|
3010
1924
|
const merged = mergeMessagesById(dbMessages, localStreaming);
|
|
3011
1925
|
messagesRef.current = merged;
|
|
3012
1926
|
setMessages(merged);
|
|
@@ -3017,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3017
1931
|
const runningNow = agentData.status === "running" || agentData.status === 1;
|
|
3018
1932
|
const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
|
|
3019
1933
|
agentData.status === 2;
|
|
3020
|
-
const waitingForInputNow = agentData.status === "waitingForInput";
|
|
3021
1934
|
const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3022
1935
|
if (runningNow || hasStreamingNow) {
|
|
3023
1936
|
setIsWaitingForResponse(true);
|
|
@@ -3025,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3025
1938
|
// Agent is actively running, show thinking dots
|
|
3026
1939
|
setIsAgentThinking(true);
|
|
3027
1940
|
}
|
|
3028
|
-
else if (waitingForApprovalNow
|
|
1941
|
+
else if (waitingForApprovalNow) {
|
|
3029
1942
|
setIsWaitingForResponse(false);
|
|
3030
1943
|
isWaitingRef.current = false;
|
|
3031
1944
|
setIsConnecting(false);
|
|
3032
|
-
// Agent is waiting for user
|
|
1945
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
3033
1946
|
setIsAgentThinking(false);
|
|
3034
1947
|
}
|
|
3035
1948
|
else {
|
|
@@ -3078,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3078
1991
|
if (!contextJson)
|
|
3079
1992
|
return null;
|
|
3080
1993
|
const parsedContext = JSON.parse(contextJson);
|
|
1994
|
+
// Context is stored as flat structure with top-level properties
|
|
1995
|
+
// Due to C# [JsonExtensionData], AdditionalData entries are serialized at top level
|
|
1996
|
+
// We need to normalize: move todoList from top level into additionalData if present
|
|
3081
1997
|
if (parsedContext && typeof parsedContext === "object") {
|
|
3082
|
-
|
|
1998
|
+
const normalized = { ...parsedContext };
|
|
1999
|
+
// If todoList is at top level but not in additionalData, move it
|
|
2000
|
+
if (normalized.todoList && !normalized.additionalData?.todoList) {
|
|
2001
|
+
normalized.additionalData = {
|
|
2002
|
+
...(normalized.additionalData || {}),
|
|
2003
|
+
todoList: normalized.todoList,
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
return normalized;
|
|
3083
2007
|
}
|
|
3084
2008
|
return null;
|
|
3085
2009
|
}
|
|
@@ -3165,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3165
2089
|
return;
|
|
3166
2090
|
}
|
|
3167
2091
|
// Check if cost limit exceeded based on status or cost values
|
|
3168
|
-
const statusIndicatesLimit = agent.status === "costLimitReached";
|
|
2092
|
+
const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
|
|
3169
2093
|
// Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
|
|
3170
2094
|
const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
|
|
3171
2095
|
const costExceedsLimit = agent.costLimit &&
|
|
@@ -3250,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3250
2174
|
// Handle agent:profile:switched (profile changed via switch-profile function)
|
|
3251
2175
|
if (messageType === "agent:profile:switched") {
|
|
3252
2176
|
const payload = message.payload || {};
|
|
3253
|
-
const switchedAgentId = payload.agentId;
|
|
3254
|
-
const newProfileId = payload.newProfileId;
|
|
3255
|
-
const newProfileName = payload.newProfileName;
|
|
2177
|
+
const switchedAgentId = payload.agentId || payload.AgentId;
|
|
2178
|
+
const newProfileId = payload.newProfileId || payload.NewProfileId;
|
|
2179
|
+
const newProfileName = payload.newProfileName || payload.NewProfileName;
|
|
3256
2180
|
if (switchedAgentId === agent.id && newProfileId) {
|
|
3257
2181
|
// Update the agent's profile
|
|
3258
2182
|
setAgent((prev) => {
|
|
@@ -3293,10 +2217,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3293
2217
|
return;
|
|
3294
2218
|
}
|
|
3295
2219
|
// For other agent messages, check if this is for our agent
|
|
3296
|
-
const agentId = message.payload?.agentId;
|
|
3297
|
-
if (agentId !== agent.id)
|
|
2220
|
+
const agentId = message.payload?.agentId || message.payload?.AgentId;
|
|
2221
|
+
if (agentId !== agent.id)
|
|
3298
2222
|
return;
|
|
3299
|
-
}
|
|
3300
2223
|
// Handle agent:run:start
|
|
3301
2224
|
if (messageType === "agent:run:start") {
|
|
3302
2225
|
// If a stop operation is in progress, ignore this message to prevent
|
|
@@ -3400,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3400
2323
|
if (messageType === "agent:prompt:queued") {
|
|
3401
2324
|
const { queueEntry } = message.payload;
|
|
3402
2325
|
if (queueEntry) {
|
|
3403
|
-
if (shouldSuppressQueuedPrompt(queueEntry)) {
|
|
3404
|
-
return;
|
|
3405
|
-
}
|
|
3406
2326
|
// Add the new prompt to the queued prompts list
|
|
3407
2327
|
setQueuedPrompts((prev) => {
|
|
3408
2328
|
// Check if prompt already exists (deduplication)
|
|
@@ -3431,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3431
2351
|
return;
|
|
3432
2352
|
}
|
|
3433
2353
|
const { seq, type, data, cost } = message.payload;
|
|
3434
|
-
if (type === "ToolCall" || type === "toolCall") {
|
|
3435
|
-
}
|
|
3436
2354
|
// Always allow ContextUpdate messages (metadata updates) regardless of agent status
|
|
3437
2355
|
const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
|
|
3438
|
-
const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
|
|
3439
|
-
const shouldClearHeartbeat = type === "ContentChunk" ||
|
|
3440
|
-
type === "contentChunk" ||
|
|
3441
|
-
type === "ToolCall" ||
|
|
3442
|
-
type === "toolCall" ||
|
|
3443
|
-
type === "ToolResult" ||
|
|
3444
|
-
type === "toolResult";
|
|
3445
2356
|
// Allow deltas if the agent is running OR if we already have an active streaming message
|
|
3446
2357
|
// OR if the server provided a messageId for targeted updates.
|
|
3447
2358
|
// This avoids dropping early deltas that may arrive before the 'running' status update.
|
|
3448
2359
|
// ContextUpdate messages are always allowed as they're metadata updates, not content.
|
|
3449
2360
|
const isRunning = agent.status === "running" || agent.status === 1;
|
|
3450
2361
|
const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3451
|
-
const hasMessageId = !!message?.payload?.data?.messageId
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
!isRunning &&
|
|
3455
|
-
!hasStreaming &&
|
|
3456
|
-
!hasMessageId) {
|
|
2362
|
+
const hasMessageId = !!message?.payload?.data?.messageId ||
|
|
2363
|
+
!!message?.payload?.data?.MessageId;
|
|
2364
|
+
if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
|
|
3457
2365
|
return; // ignore only if we cannot safely target an existing streaming message
|
|
3458
2366
|
}
|
|
3459
2367
|
// Deduplicate by sequence
|
|
@@ -3470,9 +2378,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3470
2378
|
cost,
|
|
3471
2379
|
timestamp: new Date().toISOString(),
|
|
3472
2380
|
};
|
|
3473
|
-
if (shouldClearHeartbeat) {
|
|
3474
|
-
clearHeartbeatMessages();
|
|
3475
|
-
}
|
|
3476
2381
|
if (type === "ContentChunk" || type === "contentChunk") {
|
|
3477
2382
|
handleContentChunk(agentStreamMessage, agent);
|
|
3478
2383
|
}
|
|
@@ -3482,9 +2387,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3482
2387
|
else if (type === "ToolResult" || type === "toolResult") {
|
|
3483
2388
|
handleToolResult(agentStreamMessage, agent);
|
|
3484
2389
|
}
|
|
3485
|
-
else if (type === "Heartbeat" || type === "heartbeat") {
|
|
3486
|
-
handleHeartbeatMessage(agentStreamMessage);
|
|
3487
|
-
}
|
|
3488
2390
|
else if (type === "ContextUpdate" || type === "contextUpdate") {
|
|
3489
2391
|
// Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
|
|
3490
2392
|
const contextData = data;
|
|
@@ -3494,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3494
2396
|
const current = (prev || {});
|
|
3495
2397
|
const updated = { ...current };
|
|
3496
2398
|
// Merge additionalData if present (deep merge to preserve existing values)
|
|
3497
|
-
if (contextData.additionalData) {
|
|
3498
|
-
const sourceAdditionalData = contextData.additionalData ||
|
|
2399
|
+
if (contextData.additionalData || contextData.AdditionalData) {
|
|
2400
|
+
const sourceAdditionalData = contextData.additionalData ||
|
|
2401
|
+
contextData.AdditionalData ||
|
|
2402
|
+
{};
|
|
3499
2403
|
updated.additionalData = {
|
|
3500
2404
|
...(current.additionalData || {}),
|
|
3501
2405
|
...sourceAdditionalData,
|
|
@@ -3503,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3503
2407
|
}
|
|
3504
2408
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3505
2409
|
// Backend sends the complete updated list, not just additions
|
|
3506
|
-
if (contextData.childAgents) {
|
|
3507
|
-
const childAgents = contextData.childAgents;
|
|
2410
|
+
if (contextData.ChildAgents || contextData.childAgents) {
|
|
2411
|
+
const childAgents = contextData.ChildAgents || contextData.childAgents;
|
|
3508
2412
|
if (Array.isArray(childAgents)) {
|
|
3509
|
-
updated.
|
|
2413
|
+
updated.ChildAgents = childAgents;
|
|
3510
2414
|
}
|
|
3511
2415
|
}
|
|
3512
2416
|
return updated;
|
|
@@ -3532,18 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3532
2436
|
// Route based on statusData.state
|
|
3533
2437
|
try {
|
|
3534
2438
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3535
|
-
const normalizedStatus =
|
|
3536
|
-
|
|
3537
|
-
|
|
2439
|
+
const normalizedStatus = statusData?.state ||
|
|
2440
|
+
statusData?.Status ||
|
|
2441
|
+
statusData?.status;
|
|
2442
|
+
if (normalizedStatus === "Cancelled" ||
|
|
2443
|
+
normalizedStatus === "canceled") {
|
|
3538
2444
|
// Stop indicators and mark any in-progress streaming messages as completed
|
|
3539
|
-
clearHeartbeatMessages();
|
|
3540
|
-
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3541
2445
|
setIsWaitingForResponse(false);
|
|
3542
2446
|
isWaitingRef.current = false;
|
|
3543
2447
|
setIsConnecting(false);
|
|
3544
|
-
setIsSubmitting(false);
|
|
3545
|
-
shouldCreateNewMessage.current = false;
|
|
3546
|
-
setIsAgentThinking(false);
|
|
3547
2448
|
setMessages((prev) => {
|
|
3548
2449
|
const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
|
|
3549
2450
|
? {
|
|
@@ -3586,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3586
2487
|
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
3587
2488
|
cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
|
|
3588
2489
|
cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
|
|
3589
|
-
imageCost: Number(totals.totalImageCost) || 0,
|
|
3590
2490
|
totalCost: totalCost,
|
|
3591
2491
|
currency: totals.currency,
|
|
3592
2492
|
};
|
|
@@ -3598,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3598
2498
|
if (anyNonZero) {
|
|
3599
2499
|
setLiveTotals(nextTotals);
|
|
3600
2500
|
}
|
|
3601
|
-
// Fallback context usage update for providers that do not include
|
|
3602
|
-
// context usage in every stream delta (for example some OpenAI streams).
|
|
3603
|
-
// Prefer explicit status values when present; otherwise derive from totals.
|
|
3604
|
-
const contextWindowValue = Number(statusData?.contextWindow ??
|
|
3605
|
-
agent?.contextWindowTokens ??
|
|
3606
|
-
0);
|
|
3607
|
-
const contextUsedValue = Number(statusData?.contextUsed ??
|
|
3608
|
-
(Number(totals.totalInputTokens) || 0) +
|
|
3609
|
-
(Number(totals.totalCachedInputTokens) || 0) +
|
|
3610
|
-
(Number(totals.totalCacheWriteTokens) || 0));
|
|
3611
|
-
if (contextWindowValue > 0 &&
|
|
3612
|
-
Number.isFinite(contextUsedValue) &&
|
|
3613
|
-
contextUsedValue >= 0) {
|
|
3614
|
-
setContextWindowStatus({
|
|
3615
|
-
contextWindowTokens: contextWindowValue,
|
|
3616
|
-
estimatedInputTokens: contextUsedValue,
|
|
3617
|
-
contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
|
|
3618
|
-
model: agent?.model || "",
|
|
3619
|
-
});
|
|
3620
|
-
}
|
|
3621
2501
|
// If server provides costLimit along with totals, persist it locally
|
|
3622
2502
|
if (statusCostLimit) {
|
|
3623
2503
|
setAgent((prev) => prev &&
|
|
@@ -3639,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3639
2519
|
return;
|
|
3640
2520
|
}
|
|
3641
2521
|
if (statusData?.state === "ToolApprovalsRequired") {
|
|
3642
|
-
setPendingBrowserCaptureDialogType(null);
|
|
3643
2522
|
const msgId = statusData.messageId;
|
|
3644
2523
|
const ids = statusData.toolCallIds || [];
|
|
3645
2524
|
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
@@ -3671,40 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3671
2550
|
setIsWaitingForResponse(false);
|
|
3672
2551
|
return;
|
|
3673
2552
|
}
|
|
3674
|
-
// Handle
|
|
3675
|
-
if (
|
|
3676
|
-
|
|
2553
|
+
// Handle "WaitingForApproval" state explicitly
|
|
2554
|
+
if (statusData?.state === "WaitingForApproval" ||
|
|
2555
|
+
statusData?.state === "waitingForApproval") {
|
|
3677
2556
|
setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
|
|
3678
2557
|
setIsConnecting(false);
|
|
3679
2558
|
setIsWaitingForResponse(false);
|
|
3680
2559
|
setIsAgentThinking(false);
|
|
3681
2560
|
return;
|
|
3682
2561
|
}
|
|
3683
|
-
if (
|
|
3684
|
-
const dialogType = typeof statusData?.dialogType === "string"
|
|
3685
|
-
? statusData.dialogType
|
|
3686
|
-
: null;
|
|
3687
|
-
const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
3688
|
-
dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
3689
|
-
setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
|
|
3690
|
-
setAgent((prev) => prev
|
|
3691
|
-
? {
|
|
3692
|
-
...prev,
|
|
3693
|
-
status: "waitingForInput",
|
|
3694
|
-
statusMessage: isBrowserCaptureWait &&
|
|
3695
|
-
typeof statusData?.title === "string" &&
|
|
3696
|
-
statusData.title.trim()
|
|
3697
|
-
? statusData.title.trim()
|
|
3698
|
-
: prev.statusMessage,
|
|
3699
|
-
}
|
|
3700
|
-
: prev);
|
|
3701
|
-
setIsConnecting(false);
|
|
3702
|
-
setIsWaitingForResponse(false);
|
|
3703
|
-
setIsAgentThinking(false);
|
|
3704
|
-
return;
|
|
3705
|
-
}
|
|
3706
|
-
if (normalizedStatus === "costLimitReached") {
|
|
3707
|
-
setPendingBrowserCaptureDialogType(null);
|
|
2562
|
+
if (statusData?.state === "CostLimitReached") {
|
|
3708
2563
|
const totalCost = Number(statusData.totalCost) || 0;
|
|
3709
2564
|
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
3710
2565
|
setCostLimitExceeded({
|
|
@@ -3722,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3722
2577
|
// Server sends additionalData directly (with todoList inside)
|
|
3723
2578
|
// Also may include ChildAgents for spawned agents
|
|
3724
2579
|
try {
|
|
3725
|
-
const serverAdditionalData = statusData.additionalData || {};
|
|
2580
|
+
const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
|
|
3726
2581
|
setAgentMetadata((prev) => {
|
|
3727
2582
|
const current = (prev || {});
|
|
3728
2583
|
const updated = { ...current };
|
|
@@ -3736,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3736
2591
|
}
|
|
3737
2592
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3738
2593
|
// Backend sends the complete updated list, not just additions
|
|
3739
|
-
if (statusData.childAgents) {
|
|
3740
|
-
const childAgents = statusData.childAgents;
|
|
2594
|
+
if (statusData.ChildAgents || statusData.childAgents) {
|
|
2595
|
+
const childAgents = statusData.ChildAgents || statusData.childAgents;
|
|
3741
2596
|
if (Array.isArray(childAgents)) {
|
|
3742
|
-
updated.
|
|
2597
|
+
updated.ChildAgents = childAgents;
|
|
3743
2598
|
}
|
|
3744
2599
|
}
|
|
3745
2600
|
return updated;
|
|
@@ -3751,10 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3751
2606
|
return;
|
|
3752
2607
|
}
|
|
3753
2608
|
// Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
|
|
3754
|
-
if (normalizedStatus === "completed"
|
|
2609
|
+
if (normalizedStatus === "completed" ||
|
|
2610
|
+
normalizedStatus === "Completed") {
|
|
3755
2611
|
// Reset deduplication for the next run
|
|
3756
2612
|
lastSeqRef.current = 0;
|
|
3757
|
-
clearHeartbeatMessages();
|
|
3758
2613
|
// Mark the last assistant message as completed
|
|
3759
2614
|
setMessages((prev) => {
|
|
3760
2615
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3777,8 +2632,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3777
2632
|
setIsAgentThinking(false);
|
|
3778
2633
|
return;
|
|
3779
2634
|
}
|
|
2635
|
+
// Handle "Idle" state - agent has finished processing
|
|
2636
|
+
if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
|
|
2637
|
+
// Update agent status to idle
|
|
2638
|
+
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
2639
|
+
setIsWaitingForResponse(false);
|
|
2640
|
+
isWaitingRef.current = false;
|
|
2641
|
+
setIsConnecting(false);
|
|
2642
|
+
shouldCreateNewMessage.current = false;
|
|
2643
|
+
setIsAgentThinking(false);
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
3780
2646
|
// Handle "Running" state - agent is actively processing
|
|
3781
|
-
if (normalizedStatus === "running"
|
|
2647
|
+
if (normalizedStatus === "running" ||
|
|
2648
|
+
normalizedStatus === "Running") {
|
|
3782
2649
|
// Update agent status to running
|
|
3783
2650
|
setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
|
|
3784
2651
|
setIsWaitingForResponse(true);
|
|
@@ -3787,9 +2654,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3787
2654
|
return;
|
|
3788
2655
|
}
|
|
3789
2656
|
// Handle "Error" state
|
|
3790
|
-
if (normalizedStatus === "error") {
|
|
2657
|
+
if (normalizedStatus === "error" || normalizedStatus === "Error") {
|
|
3791
2658
|
const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
|
|
3792
|
-
clearHeartbeatMessages();
|
|
3793
2659
|
setAgent((prev) => prev
|
|
3794
2660
|
? { ...prev, status: "error", statusMessage: errorMsg }
|
|
3795
2661
|
: prev);
|
|
@@ -3809,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3809
2675
|
if (messageType === "agent:run:complete") {
|
|
3810
2676
|
// Reset deduplication for the next run
|
|
3811
2677
|
lastSeqRef.current = 0;
|
|
3812
|
-
clearHeartbeatMessages();
|
|
3813
2678
|
// Mark the last assistant message as completed
|
|
3814
2679
|
setMessages((prev) => {
|
|
3815
2680
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3834,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3834
2699
|
}
|
|
3835
2700
|
// Lifecycle: agent:run:error
|
|
3836
2701
|
if (messageType === "agent:run:error") {
|
|
3837
|
-
const errorMsg =
|
|
3838
|
-
"AI could not complete this request.";
|
|
3839
|
-
clearHeartbeatMessages();
|
|
2702
|
+
const errorMsg = message.payload?.error || "Unknown error";
|
|
3840
2703
|
// Reset deduplication for the next run after an error
|
|
3841
2704
|
lastSeqRef.current = 0;
|
|
3842
2705
|
setError(errorMsg);
|
|
@@ -3851,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3851
2714
|
}
|
|
3852
2715
|
}, [
|
|
3853
2716
|
agent,
|
|
3854
|
-
clearHeartbeatMessages,
|
|
3855
2717
|
handleContentChunk,
|
|
3856
|
-
handleHeartbeatMessage,
|
|
3857
2718
|
handleToolCall,
|
|
3858
2719
|
handleToolResult,
|
|
3859
2720
|
onAgentUpdate,
|
|
@@ -3908,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3908
2769
|
const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
|
|
3909
2770
|
const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
|
|
3910
2771
|
currentAgent.status === 2;
|
|
3911
|
-
const isWaitingForInput = currentAgent.status === "waitingForInput";
|
|
3912
2772
|
if (isRunning) {
|
|
3913
2773
|
setIsWaitingForResponse(true);
|
|
3914
2774
|
isWaitingRef.current = true;
|
|
@@ -3916,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3916
2776
|
// Agent is currently running, show thinking dots
|
|
3917
2777
|
setIsAgentThinking(true);
|
|
3918
2778
|
}
|
|
3919
|
-
else if (isWaitingForApproval
|
|
2779
|
+
else if (isWaitingForApproval) {
|
|
3920
2780
|
setIsWaitingForResponse(false);
|
|
3921
2781
|
isWaitingRef.current = false;
|
|
3922
|
-
// Agent is waiting for user
|
|
2782
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
3923
2783
|
setIsAgentThinking(false);
|
|
3924
2784
|
}
|
|
3925
2785
|
else {
|
|
@@ -3966,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3966
2826
|
window.addEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3967
2827
|
return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3968
2828
|
}, []);
|
|
3969
|
-
// Keep stable refs so we don't miss window events during effect re-runs.
|
|
3970
|
-
const agentIdRefForDialogs = useRef(null);
|
|
3971
|
-
const agentStubIdRefForDialogs = useRef(agentStub.id);
|
|
3972
|
-
const isActiveRefForDialogs = useRef(isActive);
|
|
3973
|
-
const scrollToBottomRefForDialogs = useRef(scrollToBottom);
|
|
3974
|
-
useEffect(() => {
|
|
3975
|
-
agentIdRefForDialogs.current = agent?.id ?? null;
|
|
3976
|
-
}, [agent?.id]);
|
|
3977
|
-
useEffect(() => {
|
|
3978
|
-
const prevId = agentStubIdRefForDialogs.current;
|
|
3979
|
-
agentStubIdRefForDialogs.current = agentStub.id;
|
|
3980
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
3981
|
-
const normalizedPrevId = normalizeDialogAgentId(prevId);
|
|
3982
|
-
if (normalizedPrevId) {
|
|
3983
|
-
delete visibleRegistry[normalizedPrevId];
|
|
3984
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
3985
|
-
}
|
|
3986
|
-
if (prevId && prevId !== agentStub.id) {
|
|
3987
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
3988
|
-
if (orphanedDialog) {
|
|
3989
|
-
setActiveInlineDialog(null);
|
|
3990
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
3991
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
3992
|
-
}));
|
|
3993
|
-
}
|
|
3994
|
-
}
|
|
3995
|
-
}, [agentStub.id]);
|
|
3996
|
-
useEffect(() => {
|
|
3997
|
-
isActiveRefForDialogs.current = isActive;
|
|
3998
|
-
}, [isActive]);
|
|
3999
|
-
useEffect(() => {
|
|
4000
|
-
scrollToBottomRefForDialogs.current = scrollToBottom;
|
|
4001
|
-
}, [scrollToBottom]);
|
|
4002
2829
|
// Listen for agent inline dialog requests from AgentDialogHandler
|
|
4003
2830
|
useEffect(() => {
|
|
4004
|
-
if (orphanTimeoutRef.current) {
|
|
4005
|
-
clearTimeout(orphanTimeoutRef.current);
|
|
4006
|
-
orphanTimeoutRef.current = null;
|
|
4007
|
-
}
|
|
4008
|
-
const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
|
|
4009
|
-
const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
4010
|
-
if (normalizedAgentStubId &&
|
|
4011
|
-
!globalListeners.includes(normalizedAgentStubId)) {
|
|
4012
|
-
globalListeners.push(normalizedAgentStubId);
|
|
4013
|
-
}
|
|
4014
|
-
globalThis.__agentDialogMountedAgents = globalListeners;
|
|
4015
2831
|
const handleDialogShow = (event) => {
|
|
4016
|
-
const
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
const onCancel = detail?.onCancel;
|
|
4020
|
-
const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
|
|
4021
|
-
const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
4022
|
-
const isActiveNow = isActiveRefForDialogs.current;
|
|
4023
|
-
if (!request)
|
|
4024
|
-
return;
|
|
4025
|
-
const requestAgentId = normalizeDialogAgentId(request.agentId);
|
|
4026
|
-
const agentMatch = !requestAgentId ||
|
|
4027
|
-
!terminalAgentStubId ||
|
|
4028
|
-
requestAgentId === terminalAgentStubId ||
|
|
4029
|
-
requestAgentId === terminalAgentId;
|
|
4030
|
-
if (!isActiveNow) {
|
|
4031
|
-
return;
|
|
4032
|
-
}
|
|
4033
|
-
if (!request)
|
|
4034
|
-
return;
|
|
4035
|
-
// Only handle dialog requests for this terminal's agent stub
|
|
4036
|
-
if (requestAgentId &&
|
|
4037
|
-
terminalAgentStubId &&
|
|
4038
|
-
requestAgentId !== terminalAgentStubId &&
|
|
4039
|
-
requestAgentId !== terminalAgentId) {
|
|
2832
|
+
const { request, onComplete, onCancel } = event.detail;
|
|
2833
|
+
// Only handle dialog requests for this agent
|
|
2834
|
+
if (request.agentId && agent?.id && request.agentId !== agent.id) {
|
|
4040
2835
|
return;
|
|
4041
2836
|
}
|
|
4042
2837
|
console.log("[AgentTerminal] Received inline dialog request:", request);
|
|
4043
|
-
setActiveInlineDialog({
|
|
4044
|
-
request,
|
|
4045
|
-
onComplete: (result) => {
|
|
4046
|
-
onComplete(result);
|
|
4047
|
-
onInteractionSubmitted?.();
|
|
4048
|
-
},
|
|
4049
|
-
onCancel: () => {
|
|
4050
|
-
onCancel();
|
|
4051
|
-
onInteractionSubmitted?.();
|
|
4052
|
-
},
|
|
4053
|
-
});
|
|
2838
|
+
setActiveInlineDialog({ request, onComplete, onCancel });
|
|
4054
2839
|
// Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
setTimeout(() => {
|
|
4061
|
-
if (request.dialogType === "questionnaire") {
|
|
4062
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4063
|
-
return;
|
|
4064
|
-
}
|
|
4065
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4066
|
-
}, 100);
|
|
2840
|
+
window.dispatchEvent(new CustomEvent("agent:dialog:accepted", {
|
|
2841
|
+
detail: { callbackId: request.callbackId },
|
|
2842
|
+
}));
|
|
2843
|
+
// Scroll to bottom to show the dialog
|
|
2844
|
+
setTimeout(scrollToBottom, 100);
|
|
4067
2845
|
};
|
|
4068
2846
|
window.addEventListener("agent:dialog:show", handleDialogShow);
|
|
4069
|
-
return () =>
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
const idsToClear = [
|
|
4074
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
4075
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
4076
|
-
].filter(Boolean);
|
|
4077
|
-
idsToClear.forEach((id) => delete visibleRegistry[id]);
|
|
4078
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
4079
|
-
// If unmounting with an active dialog, defer the orphan event so that
|
|
4080
|
-
// React strict mode remounts can cancel it. Only real unmounts fire.
|
|
4081
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
4082
|
-
if (orphanedDialog) {
|
|
4083
|
-
orphanTimeoutRef.current = setTimeout(() => {
|
|
4084
|
-
orphanTimeoutRef.current = null;
|
|
4085
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
4086
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
4087
|
-
}));
|
|
4088
|
-
}, 300);
|
|
4089
|
-
}
|
|
4090
|
-
window.removeEventListener("agent:dialog:show", handleDialogShow);
|
|
4091
|
-
};
|
|
4092
|
-
}, []);
|
|
4093
|
-
// Announce when this terminal is ready to accept dialogs.
|
|
4094
|
-
// Fire immediately on mount using agentStub.id so the AgentDialogHandler
|
|
4095
|
-
// can re-dispatch pending dialogs without waiting for the async agent load.
|
|
4096
|
-
// Also fire when agent?.id becomes available in case it differs from the stub.
|
|
2847
|
+
return () => window.removeEventListener("agent:dialog:show", handleDialogShow);
|
|
2848
|
+
}, [agent?.id, scrollToBottom]);
|
|
2849
|
+
// Announce when this terminal is ready to accept dialogs
|
|
2850
|
+
// This allows AgentDialogHandler to re-dispatch pending dialogs when switching to an agent
|
|
4097
2851
|
useEffect(() => {
|
|
4098
|
-
|
|
4099
|
-
|
|
2852
|
+
if (agent?.id) {
|
|
2853
|
+
console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
|
|
4100
2854
|
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4101
|
-
detail: {
|
|
4102
|
-
agentId: normalizedStubId,
|
|
4103
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4104
|
-
},
|
|
2855
|
+
detail: { agentId: agent.id },
|
|
4105
2856
|
}));
|
|
4106
2857
|
}
|
|
4107
|
-
}, [
|
|
4108
|
-
useEffect(() => {
|
|
4109
|
-
const normalizedAgentId = normalizeDialogAgentId(agent?.id);
|
|
4110
|
-
const normalizedStubId = normalizeDialogAgentId(agentStub.id);
|
|
4111
|
-
if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
|
|
4112
|
-
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4113
|
-
detail: {
|
|
4114
|
-
agentId: normalizedAgentId,
|
|
4115
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4116
|
-
},
|
|
4117
|
-
}));
|
|
4118
|
-
}
|
|
4119
|
-
}, [agent?.id, agentStub.id]);
|
|
2858
|
+
}, [agent?.id]);
|
|
4120
2859
|
// Profiles are provided by parent component (Agents). No local loading here.
|
|
4121
2860
|
// Select active profile based on agent.profileId or agentStub.profileId
|
|
4122
2861
|
useEffect(() => {
|
|
@@ -4127,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4127
2866
|
// Use case-insensitive comparison for GUID matching (backend may return different casing)
|
|
4128
2867
|
const normalizedProfileId = profileIdToUse?.toLowerCase();
|
|
4129
2868
|
const candidate = normalizedProfileId
|
|
4130
|
-
? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
2869
|
+
? (profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId) ??
|
|
2870
|
+
profiles[0])
|
|
2871
|
+
: profiles[0];
|
|
2872
|
+
if (candidate && (!activeProfile || activeProfile.id !== candidate.id)) {
|
|
2873
|
+
setActiveProfile(candidate);
|
|
4135
2874
|
}
|
|
4136
|
-
|
|
4137
|
-
// not only when the profile id changes. Otherwise availableSkills (and similar fields)
|
|
4138
|
-
// that are merged in the parent after async loads never update (e.g. Template Builder
|
|
4139
|
-
// settings skills merged into the profile).
|
|
4140
|
-
setActiveProfile((prev) => {
|
|
4141
|
-
if (!prev || prev.id !== candidate.id)
|
|
4142
|
-
return candidate;
|
|
4143
|
-
const skillKey = (p) => JSON.stringify({
|
|
4144
|
-
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
4145
|
-
String(s?.id ?? ""),
|
|
4146
|
-
String(s?.name ?? ""),
|
|
4147
|
-
]),
|
|
4148
|
-
available: (p.availableSkills ?? []).map((s) => [
|
|
4149
|
-
String(s?.id ?? ""),
|
|
4150
|
-
String(s?.name ?? ""),
|
|
4151
|
-
]),
|
|
4152
|
-
});
|
|
4153
|
-
if (skillKey(prev) === skillKey(candidate))
|
|
4154
|
-
return prev;
|
|
4155
|
-
return candidate;
|
|
4156
|
-
});
|
|
4157
|
-
}, [
|
|
4158
|
-
profiles,
|
|
4159
|
-
agent?.id,
|
|
4160
|
-
agent?.profileId,
|
|
4161
|
-
agentStub.id,
|
|
4162
|
-
agentStub.profileId,
|
|
4163
|
-
]);
|
|
2875
|
+
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
4164
2876
|
// Clear queued prompts when agent changes or is new;
|
|
4165
2877
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
4166
2878
|
useEffect(() => {
|
|
4167
|
-
if (!agent?.id ||
|
|
2879
|
+
if (!agent?.id || agent.status === "new") {
|
|
4168
2880
|
setQueuedPrompts([]);
|
|
4169
2881
|
}
|
|
4170
|
-
}, [agent?.id,
|
|
2882
|
+
}, [agent?.id, agent?.status]);
|
|
4171
2883
|
// Update selected model when the active profile or agent model changes
|
|
4172
2884
|
useEffect(() => {
|
|
4173
2885
|
if (!activeProfile)
|
|
@@ -4196,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4196
2908
|
// Initialize mode from metadata; fall back to agent.Mode from server
|
|
4197
2909
|
useEffect(() => {
|
|
4198
2910
|
try {
|
|
4199
|
-
const metaMode =
|
|
4200
|
-
if (metaMode
|
|
2911
|
+
const metaMode = agentMetadata?.mode;
|
|
2912
|
+
if (metaMode === "autonomous" ||
|
|
2913
|
+
metaMode === "read-only" ||
|
|
2914
|
+
metaMode === "supervised") {
|
|
4201
2915
|
setMode(metaMode);
|
|
4202
2916
|
return;
|
|
4203
2917
|
}
|
|
4204
2918
|
}
|
|
4205
2919
|
catch { }
|
|
4206
2920
|
try {
|
|
4207
|
-
const serverMode =
|
|
4208
|
-
if (serverMode
|
|
2921
|
+
const serverMode = agent?.mode;
|
|
2922
|
+
if (serverMode === "autonomous" ||
|
|
2923
|
+
serverMode === "read-only" ||
|
|
2924
|
+
serverMode === "supervised") {
|
|
4209
2925
|
setMode(serverMode);
|
|
4210
2926
|
}
|
|
4211
2927
|
}
|
|
@@ -4225,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4225
2941
|
textareaRef.current.focus();
|
|
4226
2942
|
}
|
|
4227
2943
|
}, [messages, activePlaceholderInput]);
|
|
4228
|
-
// Persist any pending settings (mode/model
|
|
2944
|
+
// Persist any pending settings (mode/model) once an agent exists server-side
|
|
4229
2945
|
const persistPendingSettingsIfNeeded = useCallback(async () => {
|
|
4230
2946
|
try {
|
|
4231
2947
|
if (!agent?.id)
|
|
@@ -4238,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4238
2954
|
payload.model = pending.modelName;
|
|
4239
2955
|
if (pending.mode)
|
|
4240
2956
|
payload.mode = pending.mode;
|
|
4241
|
-
if (pending.profileId)
|
|
4242
|
-
payload.profileId = pending.profileId;
|
|
4243
|
-
if (pending.profileName != null)
|
|
4244
|
-
payload.profileName = pending.profileName;
|
|
4245
2957
|
if (Object.keys(payload).length === 0)
|
|
4246
2958
|
return;
|
|
4247
2959
|
await updateAgentSettings(agent.id, payload);
|
|
@@ -4251,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4251
2963
|
console.error("Failed to persist pending settings", e);
|
|
4252
2964
|
}
|
|
4253
2965
|
}, [agent?.id]);
|
|
4254
|
-
const getPendingRequestSettings = useCallback(() => {
|
|
4255
|
-
const pending = pendingSettingsRef.current;
|
|
4256
|
-
const requestProfile = pending?.profileId
|
|
4257
|
-
? profiles.find((profile) => profile.id === pending.profileId) ||
|
|
4258
|
-
activeProfile ||
|
|
4259
|
-
profiles[0]
|
|
4260
|
-
: activeProfile || profiles[0];
|
|
4261
|
-
let requestModelId = selectedModelId;
|
|
4262
|
-
if (pending?.modelName) {
|
|
4263
|
-
const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
|
|
4264
|
-
pending.modelName?.trim().toLowerCase());
|
|
4265
|
-
requestModelId = requestModel?.id || requestModelId;
|
|
4266
|
-
}
|
|
4267
|
-
return {
|
|
4268
|
-
mode: (pending?.mode || mode),
|
|
4269
|
-
profileId: pending?.profileId || requestProfile?.id || "",
|
|
4270
|
-
profileName: pending?.profileName || requestProfile?.name || "",
|
|
4271
|
-
modelId: requestModelId,
|
|
4272
|
-
};
|
|
4273
|
-
}, [activeProfile, mode, profiles, selectedModelId]);
|
|
4274
|
-
const getSubmitErrorMessage = (error) => {
|
|
4275
|
-
const fallback = "Failed to submit prompt. Please try again.";
|
|
4276
|
-
if (!(error instanceof Error))
|
|
4277
|
-
return fallback;
|
|
4278
|
-
const cleaned = toUserFacingAgentErrorMessage(error.message);
|
|
4279
|
-
return cleaned || fallback;
|
|
4280
|
-
};
|
|
4281
|
-
const suppressedQueuedPromptsRef = useRef([]);
|
|
4282
|
-
const pruneSuppressedQueuedPrompts = useCallback(() => {
|
|
4283
|
-
const cutoff = Date.now() - 15_000;
|
|
4284
|
-
suppressedQueuedPromptsRef.current =
|
|
4285
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
|
|
4286
|
-
}, []);
|
|
4287
|
-
const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
|
|
4288
|
-
const normalizedAgentId = agentId.trim().toLowerCase();
|
|
4289
|
-
const normalizedPrompt = promptText.trim();
|
|
4290
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4291
|
-
return null;
|
|
4292
|
-
}
|
|
4293
|
-
pruneSuppressedQueuedPrompts();
|
|
4294
|
-
const token = crypto.randomUUID();
|
|
4295
|
-
suppressedQueuedPromptsRef.current = [
|
|
4296
|
-
...suppressedQueuedPromptsRef.current,
|
|
4297
|
-
{
|
|
4298
|
-
token,
|
|
4299
|
-
agentId: normalizedAgentId,
|
|
4300
|
-
prompt: normalizedPrompt,
|
|
4301
|
-
createdAt: Date.now(),
|
|
4302
|
-
},
|
|
4303
|
-
];
|
|
4304
|
-
return token;
|
|
4305
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4306
|
-
const clearSuppressedQueuedPrompt = useCallback((token) => {
|
|
4307
|
-
if (!token) {
|
|
4308
|
-
return;
|
|
4309
|
-
}
|
|
4310
|
-
suppressedQueuedPromptsRef.current =
|
|
4311
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
|
|
4312
|
-
}, []);
|
|
4313
|
-
const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
|
|
4314
|
-
const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
|
|
4315
|
-
const normalizedPrompt = queueEntry?.prompt?.trim();
|
|
4316
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4317
|
-
return false;
|
|
4318
|
-
}
|
|
4319
|
-
pruneSuppressedQueuedPrompts();
|
|
4320
|
-
const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
|
|
4321
|
-
entry.prompt === normalizedPrompt);
|
|
4322
|
-
if (!matchedEntry) {
|
|
4323
|
-
return false;
|
|
4324
|
-
}
|
|
4325
|
-
suppressedQueuedPromptsRef.current =
|
|
4326
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
|
|
4327
|
-
return true;
|
|
4328
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4329
|
-
const cancelActiveInlineDialog = useCallback(() => {
|
|
4330
|
-
const activeDialog = activeInlineDialogRef.current;
|
|
4331
|
-
if (!activeDialog)
|
|
4332
|
-
return;
|
|
4333
|
-
try {
|
|
4334
|
-
activeDialog.onCancel();
|
|
4335
|
-
}
|
|
4336
|
-
finally {
|
|
4337
|
-
setActiveInlineDialog(null);
|
|
4338
|
-
}
|
|
4339
|
-
}, []);
|
|
4340
2966
|
const handleSubmit = async () => {
|
|
4341
2967
|
// Guard against double-submit and missing context
|
|
4342
2968
|
if (isSubmitting) {
|
|
@@ -4380,12 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4380
3006
|
setError("Agent not ready. Please try again.");
|
|
4381
3007
|
return;
|
|
4382
3008
|
}
|
|
4383
|
-
const hadQuestionnaireDialogOpen = activeInlineDialogRef.current?.request.dialogType === "questionnaire";
|
|
4384
|
-
const suppressedQueuedPromptToken = hadQuestionnaireDialogOpen && savedPrompt
|
|
4385
|
-
? registerSuppressedQueuedPrompt(agentId, savedPrompt)
|
|
4386
|
-
: null;
|
|
4387
|
-
// A new user prompt supersedes any active questionnaire/inline dialog.
|
|
4388
|
-
cancelActiveInlineDialog();
|
|
4389
3009
|
// Generate a temporary ID for optimistic UI - will be replaced by server ID
|
|
4390
3010
|
const tempMessageId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
4391
3011
|
try {
|
|
@@ -4472,24 +3092,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4472
3092
|
console.warn("[AgentTerminal] Failed to compute live context:", e);
|
|
4473
3093
|
}
|
|
4474
3094
|
// Add visible test IDs to context (only for Help agent)
|
|
4475
|
-
const
|
|
4476
|
-
const currentProfileName = requestSettings.profileName;
|
|
3095
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4477
3096
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4478
3097
|
const request = {
|
|
4479
3098
|
agentId: agentId,
|
|
4480
3099
|
message: savedPrompt,
|
|
4481
3100
|
sessionId: editContext.sessionId,
|
|
4482
|
-
profileId:
|
|
3101
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4483
3102
|
profile: currentProfileName,
|
|
4484
|
-
model:
|
|
4485
|
-
mode:
|
|
3103
|
+
model: selectedModelId,
|
|
3104
|
+
mode: mode,
|
|
4486
3105
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3106
|
+
deterministic: deterministicFlags.deterministic,
|
|
3107
|
+
seed: deterministicFlags.seed,
|
|
4487
3108
|
};
|
|
4488
3109
|
console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
|
|
4489
3110
|
const response = await startAgent(request);
|
|
4490
3111
|
console.log("[AgentTerminal] startAgent response:", response);
|
|
4491
3112
|
// Check if prompt was queued (agent was already running)
|
|
4492
|
-
const wasQueued = response.message?.toLowerCase().includes("queued")
|
|
3113
|
+
const wasQueued = response.message?.toLowerCase().includes("queued") ||
|
|
3114
|
+
response.status === "Queued";
|
|
4493
3115
|
if (wasQueued) {
|
|
4494
3116
|
// Prompt was queued - show a brief notification but don't set waiting state
|
|
4495
3117
|
// The prompt will be processed when the agent becomes idle
|
|
@@ -4499,7 +3121,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4499
3121
|
isWaitingRef.current = false;
|
|
4500
3122
|
}
|
|
4501
3123
|
else {
|
|
4502
|
-
clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
|
|
4503
3124
|
// Normal submission - set waiting state to show dancing dots immediately
|
|
4504
3125
|
setIsWaitingForResponse(true);
|
|
4505
3126
|
isWaitingRef.current = true;
|
|
@@ -4514,13 +3135,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4514
3135
|
...prev.filter((p) => p !== savedPrompt).slice(0, 9),
|
|
4515
3136
|
]);
|
|
4516
3137
|
setCurrentHistoryIndex(-1);
|
|
4517
|
-
await onInteractionSubmitted?.();
|
|
4518
3138
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4519
3139
|
}
|
|
4520
3140
|
catch (err) {
|
|
4521
3141
|
console.error("[AgentTerminal] Failed to submit prompt:", err);
|
|
4522
|
-
|
|
4523
|
-
setError(getSubmitErrorMessage(err));
|
|
3142
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4524
3143
|
setIsWaitingForResponse(false);
|
|
4525
3144
|
isWaitingRef.current = false;
|
|
4526
3145
|
// Remove the optimistic user message on API error
|
|
@@ -4730,29 +3349,29 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4730
3349
|
console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
|
|
4731
3350
|
}
|
|
4732
3351
|
// Add visible test IDs to context (only for Help agent)
|
|
4733
|
-
const
|
|
4734
|
-
const currentProfileName = requestSettings.profileName;
|
|
3352
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4735
3353
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4736
3354
|
const request = {
|
|
4737
3355
|
agentId: agent.id,
|
|
4738
3356
|
message: savedPrompt,
|
|
4739
3357
|
sessionId: editContext.sessionId,
|
|
4740
|
-
profileId:
|
|
3358
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4741
3359
|
profile: currentProfileName,
|
|
4742
|
-
model:
|
|
4743
|
-
mode:
|
|
3360
|
+
model: selectedModelId,
|
|
3361
|
+
mode: mode,
|
|
4744
3362
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3363
|
+
deterministic: deterministicFlags.deterministic,
|
|
3364
|
+
seed: deterministicFlags.seed,
|
|
4745
3365
|
};
|
|
4746
3366
|
console.log("[AgentTerminal] Calling startAgent API for quick message");
|
|
4747
3367
|
await startAgent(request);
|
|
4748
3368
|
// If user changed mode/model while the agent was new, persist them now
|
|
4749
3369
|
await persistPendingSettingsIfNeeded();
|
|
4750
|
-
await onInteractionSubmitted?.();
|
|
4751
3370
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4752
3371
|
}
|
|
4753
3372
|
catch (err) {
|
|
4754
3373
|
console.error("[AgentTerminal] Failed to submit quick message:", err);
|
|
4755
|
-
setError(
|
|
3374
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4756
3375
|
setIsWaitingForResponse(false);
|
|
4757
3376
|
isWaitingRef.current = false;
|
|
4758
3377
|
// Remove the optimistic user message on API error
|
|
@@ -4930,90 +3549,66 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4930
3549
|
}
|
|
4931
3550
|
return context;
|
|
4932
3551
|
}, [collectVisibleTestIds]);
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
name: editContext?.item?.name,
|
|
4938
|
-
path: editContext?.item?.path,
|
|
4939
|
-
}), [editContext?.item?.name, editContext?.item?.path]);
|
|
4940
|
-
const buildComponentContext = useCallback((item) => {
|
|
4941
|
-
if (!editContext?.selection?.length)
|
|
4942
|
-
return undefined;
|
|
4943
|
-
return editContext.selection.map((componentId) => {
|
|
4944
|
-
let componentName;
|
|
4945
|
-
let componentType;
|
|
4946
|
-
let renderingItemId;
|
|
4947
|
-
if (editContext.page) {
|
|
4948
|
-
try {
|
|
4949
|
-
const component = getComponentById(componentId, editContext.page);
|
|
4950
|
-
componentName =
|
|
4951
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4952
|
-
componentType = component?.type || undefined;
|
|
4953
|
-
renderingItemId = component?.rendering?.id || undefined;
|
|
4954
|
-
}
|
|
4955
|
-
catch { }
|
|
4956
|
-
}
|
|
4957
|
-
return {
|
|
4958
|
-
componentId,
|
|
4959
|
-
componentName,
|
|
4960
|
-
componentType,
|
|
4961
|
-
renderingItemId,
|
|
4962
|
-
pageItem: buildPageContextItem(item),
|
|
4963
|
-
};
|
|
4964
|
-
});
|
|
4965
|
-
}, [buildPageContextItem, editContext?.page, editContext?.selection]);
|
|
4966
|
-
const buildFieldContext = useCallback(() => {
|
|
4967
|
-
const focusedField = fieldsContext?.focusedField;
|
|
4968
|
-
if (!focusedField?.fieldId || !focusedField?.item?.id)
|
|
4969
|
-
return undefined;
|
|
4970
|
-
const fieldItem = focusedField.item;
|
|
4971
|
-
const currentItem = editContext?.currentItemDescriptor;
|
|
4972
|
-
let fieldItemName = fieldItem.id === currentItem?.id ? editContext?.item?.name : undefined;
|
|
4973
|
-
if (!fieldItemName && editContext?.page) {
|
|
4974
|
-
try {
|
|
4975
|
-
const component = getComponentById(fieldItem.id, editContext.page);
|
|
4976
|
-
fieldItemName =
|
|
4977
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4978
|
-
}
|
|
4979
|
-
catch { }
|
|
4980
|
-
}
|
|
3552
|
+
// Helper function to build current context from editor state
|
|
3553
|
+
const buildCurrentContext = useCallback(() => {
|
|
3554
|
+
// Return context even without item - we want view info regardless
|
|
3555
|
+
const item = editContext?.currentItemDescriptor;
|
|
4981
3556
|
return {
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
3557
|
+
items: item
|
|
3558
|
+
? [
|
|
3559
|
+
{
|
|
3560
|
+
id: item.id,
|
|
3561
|
+
language: item.language,
|
|
3562
|
+
version: item.version,
|
|
3563
|
+
name: editContext?.item?.name,
|
|
3564
|
+
path: editContext?.item?.path,
|
|
3565
|
+
},
|
|
3566
|
+
]
|
|
3567
|
+
: undefined,
|
|
3568
|
+
currentItemId: item?.id, // ID of the currently loaded item user is viewing
|
|
3569
|
+
components: editContext?.selection?.length && item
|
|
3570
|
+
? editContext.selection.map((componentId) => ({
|
|
3571
|
+
componentId,
|
|
3572
|
+
pageItem: {
|
|
3573
|
+
id: item.id,
|
|
3574
|
+
language: item.language,
|
|
3575
|
+
version: item.version,
|
|
3576
|
+
name: editContext?.item?.name,
|
|
3577
|
+
},
|
|
3578
|
+
}))
|
|
3579
|
+
: undefined,
|
|
3580
|
+
field: fieldsContext?.focusedField?.fieldId &&
|
|
3581
|
+
fieldsContext.focusedField?.item?.id
|
|
3582
|
+
? {
|
|
3583
|
+
fieldId: fieldsContext.focusedField.fieldId,
|
|
3584
|
+
fieldName: fieldsContext.focusedField.fieldName,
|
|
3585
|
+
item: {
|
|
3586
|
+
id: fieldsContext.focusedField.item.id,
|
|
3587
|
+
language: fieldsContext.focusedField.item.language ||
|
|
3588
|
+
editContext?.currentItemDescriptor?.language ||
|
|
3589
|
+
"en",
|
|
3590
|
+
version: fieldsContext.focusedField.item.version ??
|
|
3591
|
+
editContext?.currentItemDescriptor?.version ??
|
|
3592
|
+
0,
|
|
3593
|
+
name: editContext?.item?.name,
|
|
3594
|
+
},
|
|
3595
|
+
}
|
|
3596
|
+
: undefined,
|
|
3597
|
+
// View context - always include so agent knows editor state
|
|
3598
|
+
activeWorkspace: editContext?.workspaceId,
|
|
3599
|
+
hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
|
|
3600
|
+
// Open sidebars - helps agent understand what panels are currently visible
|
|
3601
|
+
openSidebars: editContext?.openSidebars,
|
|
4990
3602
|
};
|
|
4991
3603
|
}, [
|
|
4992
3604
|
editContext?.currentItemDescriptor,
|
|
3605
|
+
editContext?.selection,
|
|
3606
|
+
editContext?.workspaceId,
|
|
4993
3607
|
editContext?.item?.name,
|
|
4994
|
-
editContext?.
|
|
3608
|
+
editContext?.activeSlotId,
|
|
3609
|
+
editContext?.openSidebars,
|
|
4995
3610
|
fieldsContext?.focusedField,
|
|
4996
3611
|
]);
|
|
4997
|
-
const buildEditorContextPayload = useCallback((item) => ({
|
|
4998
|
-
items: item ? [buildPageContextItem(item)] : undefined,
|
|
4999
|
-
currentItemId: item?.id,
|
|
5000
|
-
components: item ? buildComponentContext(item) : undefined,
|
|
5001
|
-
field: buildFieldContext(),
|
|
5002
|
-
activeWorkspace: editContext?.workspaceId,
|
|
5003
|
-
hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
|
|
5004
|
-
openSidebars: editContext?.openSidebars,
|
|
5005
|
-
}), [
|
|
5006
|
-
buildComponentContext,
|
|
5007
|
-
buildFieldContext,
|
|
5008
|
-
buildPageContextItem,
|
|
5009
|
-
editContext,
|
|
5010
|
-
]);
|
|
5011
|
-
// Helper function to build current context from editor state
|
|
5012
|
-
const buildCurrentContext = useCallback(() => {
|
|
5013
|
-
// Return context even without item - we want view info regardless
|
|
5014
|
-
const item = editContext?.currentItemDescriptor;
|
|
5015
|
-
return buildEditorContextPayload(item);
|
|
5016
|
-
}, [buildEditorContextPayload, editContext?.currentItemDescriptor]);
|
|
5017
3612
|
// Live context updates: watch for changes and update agent context when in "live" mode
|
|
5018
3613
|
const previousContextRef = useRef("");
|
|
5019
3614
|
useEffect(() => {
|
|
@@ -5113,15 +3708,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5113
3708
|
const handleRefreshContext = useCallback(async () => {
|
|
5114
3709
|
if (!agent?.id)
|
|
5115
3710
|
return;
|
|
5116
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5117
|
-
const refreshProfile = activeProfile ||
|
|
5118
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5119
|
-
if (refreshProfile?.editorContextMode === "none") {
|
|
5120
|
-
editContext?.showInfoToast({
|
|
5121
|
-
summary: "This profile excludes editor context.",
|
|
5122
|
-
});
|
|
5123
|
-
return;
|
|
5124
|
-
}
|
|
5125
3711
|
try {
|
|
5126
3712
|
const currentCtx = buildCurrentContext();
|
|
5127
3713
|
if (!currentCtx) {
|
|
@@ -5162,73 +3748,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5162
3748
|
buildCurrentContext,
|
|
5163
3749
|
sanitizeAgentMetadata,
|
|
5164
3750
|
editContext,
|
|
5165
|
-
activeProfile,
|
|
5166
|
-
profiles,
|
|
5167
3751
|
]);
|
|
5168
|
-
const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
|
|
5169
|
-
const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
5170
|
-
pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
5171
|
-
const currentSessionId = editContext?.sessionId?.trim() || "";
|
|
5172
|
-
const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
|
|
5173
|
-
const isClaimedByCurrentSession = !!currentSessionId &&
|
|
5174
|
-
!!claimedSessionId &&
|
|
5175
|
-
currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
|
|
5176
|
-
const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
|
|
5177
|
-
const handleClaimBrowser = useCallback(async (takeOver) => {
|
|
5178
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5179
|
-
return;
|
|
5180
|
-
setIsBrowserClaimMutationPending(true);
|
|
5181
|
-
try {
|
|
5182
|
-
const response = await claimAgentBrowser({
|
|
5183
|
-
agentId: agent.id,
|
|
5184
|
-
sessionId: editContext.sessionId,
|
|
5185
|
-
takeOver,
|
|
5186
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5187
|
-
});
|
|
5188
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5189
|
-
}
|
|
5190
|
-
catch (err) {
|
|
5191
|
-
console.error("[AgentTerminal] Failed to claim browser:", err);
|
|
5192
|
-
editContext.showErrorToast(err);
|
|
5193
|
-
}
|
|
5194
|
-
finally {
|
|
5195
|
-
setIsBrowserClaimMutationPending(false);
|
|
5196
|
-
}
|
|
5197
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5198
|
-
const handleReleaseBrowser = useCallback(async () => {
|
|
5199
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5200
|
-
return;
|
|
5201
|
-
setIsBrowserClaimMutationPending(true);
|
|
5202
|
-
try {
|
|
5203
|
-
const response = await releaseAgentBrowser({
|
|
5204
|
-
agentId: agent.id,
|
|
5205
|
-
sessionId: editContext.sessionId,
|
|
5206
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5207
|
-
});
|
|
5208
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5209
|
-
}
|
|
5210
|
-
catch (err) {
|
|
5211
|
-
console.error("[AgentTerminal] Failed to release browser:", err);
|
|
5212
|
-
editContext.showErrorToast(err);
|
|
5213
|
-
}
|
|
5214
|
-
finally {
|
|
5215
|
-
setIsBrowserClaimMutationPending(false);
|
|
5216
|
-
}
|
|
5217
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5218
|
-
useEffect(() => {
|
|
5219
|
-
return () => {
|
|
5220
|
-
if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
|
|
5221
|
-
return;
|
|
5222
|
-
}
|
|
5223
|
-
void releaseAgentBrowser({
|
|
5224
|
-
agentId: agent.id,
|
|
5225
|
-
sessionId: editContext.sessionId,
|
|
5226
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5227
|
-
}).catch((error) => {
|
|
5228
|
-
console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
|
|
5229
|
-
});
|
|
5230
|
-
};
|
|
5231
|
-
}, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
|
|
5232
3752
|
// Stop current execution/stream safely
|
|
5233
3753
|
const handleStop = useCallback(async () => {
|
|
5234
3754
|
try {
|
|
@@ -5292,52 +3812,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5292
3812
|
if (effectiveCostLimit === undefined) {
|
|
5293
3813
|
effectiveCostLimit = undefined;
|
|
5294
3814
|
}
|
|
5295
|
-
// Calculate total token usage for cost display
|
|
5296
|
-
|
|
5297
|
-
// to the persisted agent aggregate for that single field.
|
|
5298
|
-
const totalTokens = (() => {
|
|
5299
|
-
const totals = calculateTotalTokens(messages);
|
|
5300
|
-
return {
|
|
5301
|
-
...totals,
|
|
5302
|
-
imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
|
|
5303
|
-
};
|
|
5304
|
-
})();
|
|
3815
|
+
// Calculate total token usage for cost display
|
|
3816
|
+
const totalTokens = calculateTotalTokens(messages);
|
|
5305
3817
|
// Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
|
|
5306
3818
|
const isExecuting = isSubmitting ||
|
|
5307
3819
|
isConnecting ||
|
|
5308
3820
|
isWaitingForResponse ||
|
|
5309
3821
|
hasActiveStreaming();
|
|
5310
|
-
const runDiagnosticsSnapshot = useMemo(() => {
|
|
5311
|
-
const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
|
|
5312
|
-
return {
|
|
5313
|
-
agentId: currentAgentId,
|
|
5314
|
-
isSubmitting,
|
|
5315
|
-
isConnecting,
|
|
5316
|
-
isWaitingForResponse,
|
|
5317
|
-
isAgentThinking,
|
|
5318
|
-
isExecuting,
|
|
5319
|
-
hasActiveStreaming: hasActiveStreaming(),
|
|
5320
|
-
isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
|
|
5321
|
-
normalizeDialogAgentId(currentAgentId),
|
|
5322
|
-
lastSeq: lastSeqRef.current,
|
|
5323
|
-
lastEventType: lastEvent?.type ?? null,
|
|
5324
|
-
lastEventAt: lastEvent?.timestamp ?? null,
|
|
5325
|
-
recentEvents: recentAgentRunEvents,
|
|
5326
|
-
};
|
|
5327
|
-
}, [
|
|
5328
|
-
currentAgentId,
|
|
5329
|
-
hasActiveStreaming,
|
|
5330
|
-
isAgentThinking,
|
|
5331
|
-
isConnecting,
|
|
5332
|
-
isExecuting,
|
|
5333
|
-
isSubmitting,
|
|
5334
|
-
isWaitingForResponse,
|
|
5335
|
-
recentAgentRunEvents,
|
|
5336
|
-
]);
|
|
5337
|
-
const showInitialThinkingSplash = messages.length === 0 &&
|
|
5338
|
-
!error &&
|
|
5339
|
-
hideGreeting &&
|
|
5340
|
-
(isSubmitting || isConnecting);
|
|
5341
3822
|
// Compute dots visibility: only show BEFORE any assistant message exists
|
|
5342
3823
|
// This prevents duplicate headers - the dots indicator has its own header,
|
|
5343
3824
|
// and we don't want to show a second header below existing messages
|
|
@@ -5352,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5352
3833
|
// The message with the pending approval will display its own UI for approval
|
|
5353
3834
|
if (allPendingApprovals.length > 0)
|
|
5354
3835
|
return false;
|
|
5355
|
-
// The hidden-greeting startup splash already renders its own bouncing dots.
|
|
5356
|
-
// Suppress the generic indicator so reopening/running terminals don't show two.
|
|
5357
|
-
if (showInitialThinkingSplash)
|
|
5358
|
-
return false;
|
|
5359
3836
|
// IMPORTANT: If the last message is an assistant message and we're still executing,
|
|
5360
3837
|
// the AiResponseMessage for that message will show its own activity indicator.
|
|
5361
3838
|
// We only want these global thinking dots if the last message was from the user
|
|
5362
3839
|
// or if no messages exist yet (waiting for initial response).
|
|
5363
3840
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
5364
|
-
if (isExecuting &&
|
|
5365
|
-
lastMessage?.role === "assistant" &&
|
|
5366
|
-
!lastMessage.isCompleted) {
|
|
3841
|
+
if (isExecuting && lastMessage?.role === "assistant")
|
|
5367
3842
|
return false;
|
|
5368
|
-
}
|
|
5369
3843
|
// Existing check for uncompleted assistant messages
|
|
5370
3844
|
const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
|
|
5371
3845
|
if (hasActiveStreamingMessage)
|
|
@@ -5381,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5381
3855
|
messages,
|
|
5382
3856
|
activeInlineDialog,
|
|
5383
3857
|
allPendingApprovals,
|
|
5384
|
-
showInitialThinkingSplash,
|
|
5385
3858
|
]);
|
|
5386
3859
|
// Move useMemo hook before early return to comply with Rules of Hooks
|
|
5387
|
-
const
|
|
3860
|
+
const isLiveEditorContextMode = React.useMemo(() => {
|
|
5388
3861
|
try {
|
|
5389
3862
|
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5390
3863
|
const profile = activeProfile ||
|
|
5391
3864
|
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5392
|
-
|
|
3865
|
+
const mode = profile?.editorContextMode;
|
|
3866
|
+
return mode === "live";
|
|
5393
3867
|
}
|
|
5394
3868
|
catch {
|
|
5395
|
-
return
|
|
3869
|
+
return false;
|
|
5396
3870
|
}
|
|
5397
3871
|
}, [activeProfile, profiles, agent?.profileId]);
|
|
5398
|
-
const isLiveEditorContextMode = resolvedEditorContextMode === "live";
|
|
5399
|
-
const omitsEditorContext = resolvedEditorContextMode === "none";
|
|
5400
3872
|
// Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
|
|
5401
3873
|
const parentAgentId = agent?.parentAgentId ||
|
|
5402
3874
|
agent?.ParentAgentId ||
|
|
@@ -5410,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5410
3882
|
detail: { agentId: parentAgentId },
|
|
5411
3883
|
}));
|
|
5412
3884
|
}, [parentAgentId]);
|
|
5413
|
-
|
|
5414
|
-
|
|
3885
|
+
if (isLoading) {
|
|
3886
|
+
return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-gray-500", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1 }), "Loading agent..."] }) }));
|
|
3887
|
+
}
|
|
3888
|
+
const renderContextInfoBar = () => (_jsx(ContextInfoBar, { agent: agent, agentMetadata: agentMetadata, setAgentMetadata: setAgentMetadata, setAgent: setAgent, resolvedPageName: resolvedPageName, resolvedComponentName: resolvedComponentName, resolvedFieldName: resolvedFieldName, isLiveEditorContextMode: isLiveEditorContextMode, activeProfile: activeProfile, onRefreshContext: handleRefreshContext }));
|
|
5415
3889
|
const renderCostLimitBanner = () => {
|
|
5416
3890
|
if (!costLimitExceeded)
|
|
5417
3891
|
return null;
|
|
@@ -5422,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5422
3896
|
try {
|
|
5423
3897
|
// Extend cost limit - backend will automatically resume the agent
|
|
5424
3898
|
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
5425
|
-
// Update the agent's cost limit
|
|
5426
|
-
// status in local state so the useEffect watcher doesn't
|
|
5427
|
-
// immediately re-show the banner before the backend status
|
|
5428
|
-
// update arrives.
|
|
3899
|
+
// Update the agent's cost limit in local state
|
|
5429
3900
|
if (result.success && result.costLimit !== undefined) {
|
|
5430
|
-
setAgent((prev) => prev
|
|
5431
|
-
? {
|
|
5432
|
-
...prev,
|
|
5433
|
-
costLimit: result.costLimit,
|
|
5434
|
-
status: prev.status === "costLimitReached"
|
|
5435
|
-
? "running"
|
|
5436
|
-
: prev.status,
|
|
5437
|
-
}
|
|
5438
|
-
: prev);
|
|
3901
|
+
setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
|
|
5439
3902
|
}
|
|
5440
3903
|
// Clear the banner and set waiting state
|
|
5441
3904
|
// Agent will resume automatically via backend's ResumeAgentAsync
|
|
@@ -5455,245 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5455
3918
|
};
|
|
5456
3919
|
const renderErrorBanner = () => {
|
|
5457
3920
|
const currentAgent = agent || agentStub;
|
|
5458
|
-
const isErrorStatus = currentAgent?.status === "error";
|
|
5459
|
-
const
|
|
5460
|
-
if (!
|
|
3921
|
+
const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
|
|
3922
|
+
const errorMessage = currentAgent?.statusMessage;
|
|
3923
|
+
if (!isErrorStatus || !errorMessage)
|
|
5461
3924
|
return null;
|
|
5462
|
-
|
|
5463
|
-
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5464
|
-
return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", "data-testid": "agent-error-banner", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
|
|
3925
|
+
return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
|
|
5465
3926
|
};
|
|
5466
|
-
|
|
5467
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5468
|
-
return null;
|
|
5469
|
-
if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
|
|
5470
|
-
return null;
|
|
5471
|
-
}
|
|
5472
|
-
if (isPendingBrowserCaptureWait) {
|
|
5473
|
-
return null;
|
|
5474
|
-
}
|
|
5475
|
-
const label = isClaimedByCurrentSession
|
|
5476
|
-
? "Attached to this browser"
|
|
5477
|
-
: isClaimedByAnotherBrowser
|
|
5478
|
-
? "Attached in another browser"
|
|
5479
|
-
: "No browser attached";
|
|
5480
|
-
const description = isClaimedByCurrentSession
|
|
5481
|
-
? "This browser will handle page screenshot and DOM capture requests for the agent."
|
|
5482
|
-
: isClaimedByAnotherBrowser
|
|
5483
|
-
? "Capture requests will stay with the other browser until you take over control here."
|
|
5484
|
-
: "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
|
|
5485
|
-
const bannerClassName = cn("rounded border border-blue-200 bg-blue-50 p-3 text-[11px] text-blue-900", variant === "fixed" ? "mx-3 mt-3 mb-2 shrink-0" : "m-3", isClaimedByCurrentSession && "border-blue-300");
|
|
5486
|
-
return (_jsx("div", { className: bannerClassName, children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold", children: label }), _jsx("div", { className: "mt-1 text-blue-800", children: description })] }), _jsx("div", { className: "flex shrink-0 items-center gap-2", children: isClaimedByCurrentSession ? (_jsx("button", { type: "button", className: "rounded border border-blue-300 bg-white px-2 py-1 text-[11px] font-medium text-blue-900 disabled:cursor-not-allowed disabled:opacity-60", disabled: isBrowserClaimMutationPending, onClick: () => {
|
|
5487
|
-
void handleReleaseBrowser();
|
|
5488
|
-
}, children: "Release" })) : (_jsx("button", { type: "button", className: "rounded border border-blue-300 bg-white px-2 py-1 text-[11px] font-medium text-blue-900 disabled:cursor-not-allowed disabled:opacity-60", disabled: isBrowserClaimMutationPending, onClick: () => {
|
|
5489
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5490
|
-
}, children: isClaimedByAnotherBrowser
|
|
5491
|
-
? "Take over browser control"
|
|
5492
|
-
: "Attach to this browser" })) })] }) }));
|
|
5493
|
-
};
|
|
5494
|
-
const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
|
|
5495
|
-
const inlineBrowserClaimBanner = null;
|
|
5496
|
-
const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
|
|
5497
|
-
? {
|
|
5498
|
-
toolNames: [
|
|
5499
|
-
"capture-page-screenshot",
|
|
5500
|
-
"capture-parhelia-ui-screenshot",
|
|
5501
|
-
"capture-page-dom",
|
|
5502
|
-
],
|
|
5503
|
-
label: isClaimedByAnotherBrowser
|
|
5504
|
-
? "Attached in another browser"
|
|
5505
|
-
: "No browser attached",
|
|
5506
|
-
description: isClaimedByAnotherBrowser
|
|
5507
|
-
? "This capture request is waiting in another browser. Take over browser control here to continue."
|
|
5508
|
-
: "This capture request is waiting for a browser attachment. Attach this browser to continue.",
|
|
5509
|
-
actionLabel: isClaimedByAnotherBrowser
|
|
5510
|
-
? "Take over browser control"
|
|
5511
|
-
: "Attach to this browser",
|
|
5512
|
-
isPending: isBrowserClaimMutationPending,
|
|
5513
|
-
onAction: () => {
|
|
5514
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5515
|
-
},
|
|
5516
|
-
}
|
|
5517
|
-
: null;
|
|
5518
|
-
useEffect(() => {
|
|
5519
|
-
if (agent?.status !== "waitingForInput") {
|
|
5520
|
-
setPendingBrowserCaptureDialogType(null);
|
|
5521
|
-
}
|
|
5522
|
-
}, [agent?.status]);
|
|
5523
|
-
const renderInlineDialogContent = () => {
|
|
5524
|
-
if (!activeInlineDialog)
|
|
5525
|
-
return null;
|
|
5526
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5527
|
-
return (_jsx("div", { ref: inlineDialogContainerRef, className: cn("agent-inline-dialog min-h-0 overflow-hidden", displayMode === "full" && "h-full"), children: _jsx(QuestionnaireInline, { requestId: activeInlineDialog.request.callbackId, agentId: activeInlineDialog.request.agentId, title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, footerActions: questionnaireFooterActions, onClose: (result) => {
|
|
5528
|
-
activeInlineDialog.onComplete(result);
|
|
5529
|
-
setActiveInlineDialog(null);
|
|
5530
|
-
void onInteractionSubmitted?.();
|
|
5531
|
-
}, onCancel: () => {
|
|
5532
|
-
activeInlineDialog.onCancel();
|
|
5533
|
-
setActiveInlineDialog(null);
|
|
5534
|
-
} }) }));
|
|
5535
|
-
}
|
|
5536
|
-
const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
|
|
5537
|
-
if (dialogRegistration) {
|
|
5538
|
-
const DialogComponent = dialogRegistration.component;
|
|
5539
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
|
|
5540
|
-
activeInlineDialog.onComplete(result);
|
|
5541
|
-
setActiveInlineDialog(null);
|
|
5542
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5543
|
-
void onInteractionSubmitted?.();
|
|
5544
|
-
}
|
|
5545
|
-
}, onCancel: () => {
|
|
5546
|
-
activeInlineDialog.onCancel();
|
|
5547
|
-
setActiveInlineDialog(null);
|
|
5548
|
-
} }) }));
|
|
5549
|
-
}
|
|
5550
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsxs("div", { className: "p-4 text-sm text-red-500", children: ["Unknown dialog type: ", activeInlineDialog.request.dialogType] }) }));
|
|
5551
|
-
};
|
|
5552
|
-
const latestSummaryAssistantGroup = useMemo(() => {
|
|
5553
|
-
if (hideSummaryMessages)
|
|
5554
|
-
return null;
|
|
5555
|
-
const groups = groupConsecutiveMessages(messages);
|
|
5556
|
-
for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
|
|
5557
|
-
const group = groups[groupIndex];
|
|
5558
|
-
if (!group || group.type !== "assistant-group")
|
|
5559
|
-
continue;
|
|
5560
|
-
const filteredMessages = group.messages.filter((msg) => {
|
|
5561
|
-
const content = msg.content || "";
|
|
5562
|
-
return !content.startsWith("⚠️") || !content.includes("Cost limit");
|
|
5563
|
-
});
|
|
5564
|
-
if (filteredMessages.length === 0)
|
|
5565
|
-
continue;
|
|
5566
|
-
return {
|
|
5567
|
-
messages: filteredMessages,
|
|
5568
|
-
isLastGroup: groupIndex === groups.length - 1,
|
|
5569
|
-
};
|
|
5570
|
-
}
|
|
5571
|
-
return null;
|
|
5572
|
-
}, [messages, hideSummaryMessages]);
|
|
5573
|
-
const summaryModeContent = displayMode === "summary"
|
|
5574
|
-
? (() => {
|
|
5575
|
-
const inlineDialog = renderInlineDialogContent();
|
|
5576
|
-
const summaryMessages = latestSummaryAssistantGroup
|
|
5577
|
-
? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
|
|
5578
|
-
: [];
|
|
5579
|
-
const summaryOperations = latestSummaryAssistantGroup
|
|
5580
|
-
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
5581
|
-
: [];
|
|
5582
|
-
return (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [fixedBrowserClaimBanner, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error &&
|
|
5583
|
-
!isAgentErrorStatusValue((agent || agentStub)?.status) && (_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-[11px] font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-[11px] text-red-700", children: error })] })] }) })), showInitialThinkingSplash && (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [activeProfile?.svgIcon ? (_jsx("div", { className: "flex h-16 w-16 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
5584
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5585
|
-
} })) : (_jsx(SecretAgentIcon, { size: 64, strokeWidth: 1, className: "text-gray-400" })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400" })] })] }) })), inlineBrowserClaimBanner, renderErrorBanner(), inlineDialog ? (inlineDialog) : latestSummaryAssistantGroup ? (_jsx("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: _jsx(AiResponseMessage, { messages: summaryMessages, finished: !latestSummaryAssistantGroup.isLastGroup || !isExecuting, editOperations: summaryOperations, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
|
|
5586
|
-
activeProfile?.displayTitle ||
|
|
5587
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5588
|
-
const text = (action.prompt ||
|
|
5589
|
-
action.value ||
|
|
5590
|
-
action.label ||
|
|
5591
|
-
"").trim();
|
|
5592
|
-
if (!text)
|
|
5593
|
-
return;
|
|
5594
|
-
if (isExecuting) {
|
|
5595
|
-
try {
|
|
5596
|
-
handleStop();
|
|
5597
|
-
}
|
|
5598
|
-
catch { }
|
|
5599
|
-
}
|
|
5600
|
-
sendQuickMessage(text);
|
|
5601
|
-
} }) })) : hideSummaryWaitingPlaceholder ? (_jsx("div", { className: `flex h-full items-center justify-center ${compact ? "min-h-[100px] p-3" : "min-h-[220px] p-6"}`, children: summaryPlaceholderActions ? (_jsx("div", { className: "flex justify-center", children: summaryPlaceholderActions })) : null })) : (_jsx("div", { className: `flex h-full items-center justify-center ${compact ? "min-h-[100px] p-3" : "min-h-[220px] p-6"}`, children: _jsxs("div", { className: `max-w-md rounded-xl border border-slate-200 bg-slate-50 text-center text-slate-600 ${compact ? "px-3 py-2 text-xs" : "px-5 py-4 text-sm"}`, children: [_jsx("div", { children: shouldShowThinkingDots || isExecuting
|
|
5602
|
-
? "The agent is still working. The next update will appear here automatically."
|
|
5603
|
-
: agent?.statusMessage ||
|
|
5604
|
-
summaryPlaceholderMessage ||
|
|
5605
|
-
"Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
|
|
5606
|
-
shouldShowThinkingDots &&
|
|
5607
|
-
!inlineDialog &&
|
|
5608
|
-
!latestSummaryAssistantGroup && (_jsxs("div", { className: "flex gap-3 px-4 py-3", "data-testid": "agent-thinking-dots", children: [_jsx("div", { className: "shrink-0", children: activeProfile?.svgIcon ? (_jsx("div", { className: "text-gray-2 flex h-6 w-6 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
5609
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5610
|
-
} })) : (_jsx(SecretAgentIcon, { size: 20, strokeWidth: 1, className: "text-gray-2" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-dark text-xs font-medium", children: activeProfile?.agentName ||
|
|
5611
|
-
activeProfile?.displayTitle ||
|
|
5612
|
-
activeProfile?.name ||
|
|
5613
|
-
"Agent" }), _jsx("span", { className: "text-xs text-gray-400", children: formatTime(new Date()) })] }), _jsxs("div", { className: "flex items-center gap-1 pt-2", children: [_jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400" })] })] })] })), _jsx("div", { ref: messagesEndRef })] }), showSummaryInput && !activeInlineDialog ? (_jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5614
|
-
setActivePlaceholderInput(null);
|
|
5615
|
-
setAllPlaceholdersFilled(false);
|
|
5616
|
-
if (activePlaceholderInput.behavior === "compose" &&
|
|
5617
|
-
!hideBottomControls) {
|
|
5618
|
-
setPrompt(filledText);
|
|
5619
|
-
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
5620
|
-
if (textareaRef.current) {
|
|
5621
|
-
try {
|
|
5622
|
-
textareaRef.current.focus();
|
|
5623
|
-
const v = textareaRef.current.value || "";
|
|
5624
|
-
textareaRef.current.selectionStart = v.length;
|
|
5625
|
-
textareaRef.current.selectionEnd = v.length;
|
|
5626
|
-
}
|
|
5627
|
-
catch { }
|
|
5628
|
-
}
|
|
5629
|
-
}
|
|
5630
|
-
else {
|
|
5631
|
-
if (isExecuting) {
|
|
5632
|
-
try {
|
|
5633
|
-
handleStop();
|
|
5634
|
-
}
|
|
5635
|
-
catch { }
|
|
5636
|
-
}
|
|
5637
|
-
sendQuickMessage(filledText);
|
|
5638
|
-
}
|
|
5639
|
-
}, onCancel: () => {
|
|
5640
|
-
setActivePlaceholderInput(null);
|
|
5641
|
-
setAllPlaceholdersFilled(false);
|
|
5642
|
-
} })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5643
|
-
setPrompt(filledText);
|
|
5644
|
-
setAllPlaceholdersFilled(false);
|
|
5645
|
-
if (filledText.trim()) {
|
|
5646
|
-
if (isExecuting) {
|
|
5647
|
-
try {
|
|
5648
|
-
handleStop();
|
|
5649
|
-
}
|
|
5650
|
-
catch { }
|
|
5651
|
-
}
|
|
5652
|
-
sendQuickMessage(filledText);
|
|
5653
|
-
}
|
|
5654
|
-
}, onCancel: () => {
|
|
5655
|
-
setPrompt("");
|
|
5656
|
-
setAllPlaceholdersFilled(false);
|
|
5657
|
-
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
5658
|
-
} })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
|
|
5659
|
-
setPrompt(e.target.value);
|
|
5660
|
-
if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
|
|
5661
|
-
setAllPlaceholdersFilled(false);
|
|
5662
|
-
}
|
|
5663
|
-
if (currentHistoryIndex !== -1) {
|
|
5664
|
-
setCurrentHistoryIndex(-1);
|
|
5665
|
-
}
|
|
5666
|
-
}, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
|
|
5667
|
-
shouldMaintainFocusRef.current = true;
|
|
5668
|
-
}, onBlur: () => {
|
|
5669
|
-
shouldMaintainFocusRef.current = false;
|
|
5670
|
-
}, placeholder: inputPlaceholder, className: "max-h-[250px] min-h-[80px] flex-1 resize-y overflow-y-auto text-[12px] lg:max-h-[450px]", "data-testid": "agent-terminal-prompt", disabled: isSubmitting }) })), (() => {
|
|
5671
|
-
const isInPlaceholderMode = activePlaceholderInput ||
|
|
5672
|
-
(prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
|
|
5673
|
-
const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
|
|
5674
|
-
if (placeholderShowsOwnButtons)
|
|
5675
|
-
return null;
|
|
5676
|
-
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
|
|
5677
|
-
simpleMode ||
|
|
5678
|
-
isInPlaceholderMode
|
|
5679
|
-
? "justify-end"
|
|
5680
|
-
: "justify-between"), children: [!hideBottomControls &&
|
|
5681
|
-
!simpleMode &&
|
|
5682
|
-
!isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
|
|
5683
|
-
if (isExecuting) {
|
|
5684
|
-
handleStop();
|
|
5685
|
-
}
|
|
5686
|
-
else {
|
|
5687
|
-
handleSubmit();
|
|
5688
|
-
}
|
|
5689
|
-
}, disabled: !isExecuting &&
|
|
5690
|
-
!activePlaceholderInput &&
|
|
5691
|
-
(!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
|
|
5692
|
-
})()] })) : null] }));
|
|
5693
|
-
})()
|
|
5694
|
-
: null;
|
|
5695
|
-
const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
|
|
5696
|
-
const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [fixedBrowserClaimBanner, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
|
|
3927
|
+
return (_jsxs("div", { className: `flex h-full flex-col ${className || ""}`, children: [parentAgentId && !simpleMode && (_jsx("div", { className: "border-b border-gray-200 bg-gray-50 px-4 py-2", children: _jsxs("button", { onClick: handleBackToParent, className: "flex items-center gap-2 text-[11px] text-gray-600 transition-colors hover:text-gray-900", title: "Back to parent agent", children: [_jsx(ArrowLeft, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), _jsx("span", { children: "Back to parent agent" })] }) })), _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-[11px] font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-[11px] text-red-700", children: error })] })] }) })), messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
|
|
5697
3928
|
setPrompt(p);
|
|
5698
3929
|
// Use setTimeout to ensure state is updated before submission
|
|
5699
3930
|
setTimeout(() => {
|
|
@@ -5706,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5706
3937
|
handleSubmit();
|
|
5707
3938
|
}
|
|
5708
3939
|
}, 0);
|
|
5709
|
-
} })) })),
|
|
5710
|
-
|
|
5711
|
-
|
|
3940
|
+
} })) })), messages.length === 0 &&
|
|
3941
|
+
!error &&
|
|
3942
|
+
hideGreeting &&
|
|
3943
|
+
(isSubmitting || isConnecting) && (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [activeProfile?.svgIcon ? (_jsx("div", { className: "flex h-16 w-16 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
3944
|
+
__html: activeProfile.svgIcon,
|
|
3945
|
+
} })) : (_jsx(SecretAgentIcon, { size: 64, strokeWidth: 1, className: "text-gray-400" })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400" })] })] }) })), renderErrorBanner(), _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
|
|
5712
3946
|
const groups = groupConsecutiveMessages(messages);
|
|
5713
3947
|
return groups.map((group, groupIndex) => {
|
|
5714
3948
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -5716,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5716
3950
|
// Render user message
|
|
5717
3951
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
5718
3952
|
}
|
|
5719
|
-
else if (group.type === "heartbeat" && group.messages[0]) {
|
|
5720
|
-
return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
|
|
5721
|
-
}
|
|
5722
3953
|
else {
|
|
5723
3954
|
// Render bundled assistant messages
|
|
5724
3955
|
// Check if this group contains any streaming message
|
|
@@ -5735,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5735
3966
|
}
|
|
5736
3967
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
5737
3968
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5738
|
-
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup,
|
|
3969
|
+
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, error: error || undefined, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
|
|
5739
3970
|
activeProfile?.displayTitle ||
|
|
5740
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous,
|
|
3971
|
+
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5741
3972
|
const text = (action.prompt ||
|
|
5742
3973
|
action.value ||
|
|
5743
3974
|
action.label ||
|
|
@@ -5781,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5781
4012
|
}
|
|
5782
4013
|
});
|
|
5783
4014
|
})(), shouldShowThinkingDots && (_jsxs("div", { className: "flex gap-3 px-4 py-3", "data-testid": "agent-thinking-dots", children: [_jsx("div", { className: "shrink-0", children: activeProfile?.svgIcon ? (_jsx("div", { className: "text-gray-2 flex h-6 w-6 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
|
|
5784
|
-
__html:
|
|
4015
|
+
__html: activeProfile.svgIcon,
|
|
5785
4016
|
} })) : (_jsx(SecretAgentIcon, { size: 20, strokeWidth: 1, className: "text-gray-2" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-dark text-xs font-medium", children: activeProfile?.agentName ||
|
|
5786
4017
|
activeProfile?.displayTitle ||
|
|
5787
4018
|
activeProfile?.name ||
|
|
5788
4019
|
"Agent" }), _jsx("span", { className: "text-xs text-gray-400", children: formatTime(new Date()) })] }), _jsxs("div", { className: "flex items-center gap-1 pt-2", children: [_jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400" })] })] })] }))] }), !simpleMode && renderCostLimitBanner(), _jsx("div", { ref: messagesEndRef })] }), !hideContext &&
|
|
5789
4020
|
!simpleMode &&
|
|
5790
|
-
(
|
|
4021
|
+
(isMobile ? (_jsx("div", { className: "border-t border-gray-200 bg-gray-50", "data-testid": "agent-context-panel-tabs", children: _jsx(SimpleTabs, { tabs: [
|
|
5791
4022
|
{
|
|
5792
4023
|
id: "context",
|
|
5793
4024
|
label: "Context",
|
|
@@ -5835,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5835
4066
|
hasTodoContent,
|
|
5836
4067
|
hasSpawnedAgents,
|
|
5837
4068
|
agent?.id && hasHistoryContent,
|
|
5838
|
-
].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && _jsx(AgentEditOperationsPanel, { agentId: agent.id })] }))), queuedPrompts.length > 0 && !simpleMode && (_jsx("div", { className: "border-t border-gray-200 bg-amber-50/50", "data-testid": "queued-prompts-section", children: _jsxs("div", { className: "px-4 pt-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
1000 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-gray-300", children: "\u2022" }), _jsxs("span", { className: "font-medium text-amber-600", children: ["Scheduled for", " ", new Date(qp.scheduledFor).toDateString() ===
|
|
5861
|
-
new Date().toDateString()
|
|
5862
|
-
? formatTime(new Date(qp.scheduledFor))
|
|
5863
|
-
: formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
|
|
5864
|
-
}) })] }) }))] }));
|
|
5865
|
-
const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
|
|
5866
|
-
const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
|
|
5867
|
-
{
|
|
5868
|
-
name: "conversation",
|
|
5869
|
-
defaultSize: 65,
|
|
5870
|
-
content: fullModeUpperContent,
|
|
5871
|
-
},
|
|
5872
|
-
{
|
|
5873
|
-
name: "questionnaire",
|
|
5874
|
-
defaultSize: 35,
|
|
5875
|
-
content: fullModeInlineDialog,
|
|
5876
|
-
},
|
|
5877
|
-
], direction: "vertical", localStorageKey: compact
|
|
5878
|
-
? "agent-terminal-compact-questionnaire-splitter"
|
|
5879
|
-
: "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
|
|
5880
|
-
return loadingContent ? (loadingContent) : displayMode === "summary" && summaryModeContent ? (summaryModeContent) : (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [parentAgentId && !simpleMode && (_jsx("div", { className: "border-b border-gray-200 bg-gray-50 px-4 py-2", children: _jsxs("button", { onClick: handleBackToParent, className: "flex items-center gap-2 text-[11px] text-gray-600 transition-colors hover:text-gray-900", title: "Back to parent agent", children: [_jsx(ArrowLeft, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), _jsx("span", { children: "Back to parent agent" })] }) })), fullModeContent, _jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (
|
|
4069
|
+
].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && _jsx(AgentEditOperationsPanel, { agentId: agent.id })] }))), queuedPrompts.length > 0 && !simpleMode && (_jsx("div", { className: "border-t border-gray-200 bg-amber-50/50", "data-testid": "queued-prompts-section", children: _jsxs("div", { className: "px-4 pt-3 pb-2", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx("div", { className: "h-2 w-2 animate-pulse rounded-full bg-amber-500" }), _jsxs("span", { className: "text-[11px] font-semibold text-amber-900", "data-testid": "queued-prompts-count", children: ["Queued Messages (", queuedPrompts.length, ")"] })] }), _jsx("div", { className: "max-h-64 space-y-2 overflow-y-auto", children: queuedPrompts.map((qp) => (_jsx("div", { className: "rounded-md border border-amber-200 bg-white p-2.5 text-[11px]", "data-testid": "queued-prompt-item", children: _jsx("div", { className: "flex items-start justify-between gap-2", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "mb-1 flex items-center gap-1.5", children: qp.sourceAgentName ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-medium text-gray-700", children: ["From ", qp.sourceAgentName] }), qp.priority > 5 && (_jsx("span", { className: "rounded bg-red-100 px-1.5 py-0.5 text-[11px] font-medium text-red-700", children: "High Priority" }))] })) : (_jsx("span", { className: "font-medium text-gray-700", children: "From User" })) }), _jsx("div", { className: "wrap-break-word whitespace-pre-wrap text-gray-600", "data-testid": "queued-prompt-text", children: qp.prompt }), _jsxs("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px] text-gray-400", children: [qp.createdDate && (_jsxs("span", { children: ["Queued ", formatTime(new Date(qp.createdDate))] })), qp.scheduledFor &&
|
|
4070
|
+
new Date(qp.scheduledFor).getTime() >
|
|
4071
|
+
new Date(qp.createdDate || 0).getTime() + 1000 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-gray-300", children: "\u2022" }), _jsxs("span", { className: "font-medium text-amber-600", children: ["Scheduled for", " ", new Date(qp.scheduledFor).toDateString() ===
|
|
4072
|
+
new Date().toDateString()
|
|
4073
|
+
? formatTime(new Date(qp.scheduledFor))
|
|
4074
|
+
: formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id))) })] }) })), activeInlineDialog &&
|
|
4075
|
+
(() => {
|
|
4076
|
+
// Look up dialog component from config
|
|
4077
|
+
const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
|
|
4078
|
+
if (dialogRegistration) {
|
|
4079
|
+
const DialogComponent = dialogRegistration.component;
|
|
4080
|
+
return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
|
|
4081
|
+
activeInlineDialog.onComplete(result);
|
|
4082
|
+
setActiveInlineDialog(null);
|
|
4083
|
+
}, onCancel: () => {
|
|
4084
|
+
activeInlineDialog.onCancel();
|
|
4085
|
+
setActiveInlineDialog(null);
|
|
4086
|
+
} }) }));
|
|
4087
|
+
}
|
|
4088
|
+
// Fallback for unknown dialog types
|
|
4089
|
+
return (_jsx("div", { className: "agent-inline-dialog", children: _jsxs("div", { className: "p-4 text-sm text-red-500", children: ["Unknown dialog type: ", activeInlineDialog.request.dialogType] }) }));
|
|
4090
|
+
})(), _jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (
|
|
5881
4091
|
// Placeholder Input (from quick actions)
|
|
5882
4092
|
// Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
|
|
5883
4093
|
_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
@@ -5954,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5954
4164
|
return null;
|
|
5955
4165
|
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
|
|
5956
4166
|
? "justify-end"
|
|
5957
|
-
: "justify-between"), children: [!hideBottomControls && !simpleMode && !isInPlaceholderMode && (_jsxs("div", { className: "flex flex-wrap items-center justify-start gap-2", children: [_jsx(Select, {
|
|
4167
|
+
: "justify-between"), children: [!hideBottomControls && !simpleMode && !isInPlaceholderMode && (_jsxs("div", { className: "flex flex-wrap items-center justify-start gap-2", children: [_jsx(Select, { size: "xs", maxWidth: 240, className: cn("h-5 w-auto min-w-[95px] rounded border px-1.5 text-[11px] font-normal", mode === "read-only"
|
|
5958
4168
|
? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
|
|
5959
4169
|
: mode === "supervised"
|
|
5960
4170
|
? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
|
|
5961
4171
|
: "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
|
|
5962
4172
|
const nextMode = val || "supervised";
|
|
4173
|
+
// Optimistic UI update
|
|
4174
|
+
setMode(nextMode);
|
|
5963
4175
|
const current = agentMetadata || {};
|
|
5964
4176
|
const nextMeta = {
|
|
5965
4177
|
...current,
|
|
5966
4178
|
mode: nextMode,
|
|
5967
4179
|
};
|
|
5968
4180
|
try {
|
|
5969
|
-
if (!agent?.id ||
|
|
5970
|
-
setMode(nextMode);
|
|
4181
|
+
if (!agent?.id || agent.status === "new") {
|
|
5971
4182
|
setAgentMetadata(nextMeta);
|
|
4183
|
+
// Cache until first start when agent is persisted
|
|
5972
4184
|
pendingSettingsRef.current = {
|
|
5973
4185
|
...(pendingSettingsRef.current || {}),
|
|
5974
4186
|
mode: nextMode,
|
|
5975
4187
|
};
|
|
5976
4188
|
return;
|
|
5977
4189
|
}
|
|
5978
|
-
|
|
4190
|
+
await updateAgentSettings(agent.id, {
|
|
5979
4191
|
mode: nextMode,
|
|
5980
4192
|
});
|
|
5981
|
-
if (result.success === false ||
|
|
5982
|
-
result.updates?.mode === false) {
|
|
5983
|
-
throw new Error("Mode change was not applied");
|
|
5984
|
-
}
|
|
5985
|
-
setMode(nextMode);
|
|
5986
4193
|
setAgentMetadata(nextMeta);
|
|
4194
|
+
setAgent((prev) => prev
|
|
4195
|
+
? { ...prev, metadata: JSON.stringify(nextMeta) }
|
|
4196
|
+
: prev);
|
|
4197
|
+
}
|
|
4198
|
+
catch (e2) {
|
|
4199
|
+
console.error("Failed to persist mode change", e2);
|
|
4200
|
+
}
|
|
4201
|
+
} }), profiles?.length > 0 && (_jsx(Select, { size: "xs", maxWidth: 200, searchable: profiles.length > 5, searchPlaceholder: "Filter profiles...", className: "h-5 w-auto min-w-[120px] rounded border px-1.5 text-[11px] text-gray-500", value: activeProfile?.id || "", options: profileOptions, "data-testid": "agent-profile-selector", onValueChange: async (val) => {
|
|
4202
|
+
const nextProfile = profiles.find((x) => x.id === val);
|
|
4203
|
+
if (!nextProfile)
|
|
4204
|
+
return;
|
|
4205
|
+
setActiveProfile(nextProfile);
|
|
4206
|
+
try {
|
|
4207
|
+
if (agent?.id && agent.status !== "new") {
|
|
4208
|
+
await updateAgentSettings(agent.id, {
|
|
4209
|
+
profileId: nextProfile.id,
|
|
4210
|
+
profileName: nextProfile.name,
|
|
4211
|
+
});
|
|
4212
|
+
}
|
|
4213
|
+
else {
|
|
4214
|
+
// cache until first start
|
|
4215
|
+
pendingSettingsRef.current = {
|
|
4216
|
+
...(pendingSettingsRef.current || {}),
|
|
4217
|
+
// we cache profile by updating local metadata
|
|
4218
|
+
};
|
|
4219
|
+
setAgentMetadata((current) => {
|
|
4220
|
+
const next = { ...(current || {}) };
|
|
4221
|
+
next.profile = nextProfile.name;
|
|
4222
|
+
next.additionalData = {
|
|
4223
|
+
...(next.additionalData || {}),
|
|
4224
|
+
profileId: nextProfile.id,
|
|
4225
|
+
profileName: nextProfile.name,
|
|
4226
|
+
};
|
|
4227
|
+
return next;
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
4230
|
+
// reflect in local agent stub so tabs and titles can use it if needed
|
|
5987
4231
|
setAgent((prev) => prev
|
|
5988
4232
|
? {
|
|
5989
4233
|
...prev,
|
|
5990
|
-
|
|
5991
|
-
|
|
4234
|
+
metadata: JSON.stringify({
|
|
4235
|
+
...(agentMetadata || {}),
|
|
4236
|
+
profile: nextProfile.name,
|
|
4237
|
+
additionalData: {
|
|
4238
|
+
...(agentMetadata
|
|
4239
|
+
?.additionalData || {}),
|
|
4240
|
+
profileId: nextProfile.id,
|
|
4241
|
+
profileName: nextProfile.name,
|
|
4242
|
+
},
|
|
4243
|
+
}),
|
|
5992
4244
|
}
|
|
5993
4245
|
: prev);
|
|
5994
4246
|
}
|
|
5995
|
-
catch (
|
|
5996
|
-
console.error("Failed to persist
|
|
4247
|
+
catch (err) {
|
|
4248
|
+
console.error("Failed to persist agent profile", err);
|
|
4249
|
+
}
|
|
4250
|
+
} })), activeProfile?.models?.length ? (_jsx(Select, { size: "xs", maxWidth: 300, searchable: activeProfile.models.length > 5, searchPlaceholder: "Filter models...", className: "h-5 w-auto min-w-[120px] rounded border px-1.5 text-[11px] text-gray-500", value: selectedModelId || "", options: modelOptions, onValueChange: async (val) => {
|
|
4251
|
+
const nextId = val;
|
|
4252
|
+
setSelectedModelId(nextId);
|
|
4253
|
+
const modelName = activeProfile?.models?.find((m) => m.id === nextId)
|
|
4254
|
+
?.name || "";
|
|
4255
|
+
// Update local agent state immediately for UX and to reflect in streaming stub
|
|
4256
|
+
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
4257
|
+
// Persist only for existing agents; otherwise cache until first start
|
|
4258
|
+
try {
|
|
4259
|
+
if (agent?.id && agent.status !== "new") {
|
|
4260
|
+
await updateAgentSettings(agent.id, {
|
|
4261
|
+
model: modelName,
|
|
4262
|
+
});
|
|
4263
|
+
}
|
|
4264
|
+
else {
|
|
4265
|
+
pendingSettingsRef.current = {
|
|
4266
|
+
...(pendingSettingsRef.current || {}),
|
|
4267
|
+
modelName,
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
5997
4270
|
}
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
if (!open) {
|
|
6001
|
-
setShowSkillPicker(false);
|
|
4271
|
+
catch (err) {
|
|
4272
|
+
console.error("Failed to persist agent model", err);
|
|
6002
4273
|
}
|
|
6003
|
-
}
|
|
6004
|
-
const target = e.target;
|
|
6005
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6006
|
-
e.preventDefault();
|
|
6007
|
-
}
|
|
6008
|
-
}, onPointerDownOutside: (e) => {
|
|
6009
|
-
const target = e.target;
|
|
6010
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6011
|
-
e.preventDefault();
|
|
6012
|
-
}
|
|
6013
|
-
}, onFocusOutside: (e) => {
|
|
6014
|
-
const target = e.target;
|
|
6015
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6016
|
-
e.preventDefault();
|
|
6017
|
-
}
|
|
6018
|
-
}, children: _jsxs("div", { className: "space-y-3", children: [profiles?.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-[11px] font-medium text-gray-700", children: "Agent profile" }), _jsx(Select, { size: "xs", maxWidth: 300, searchable: profiles.length > 5, searchPlaceholder: "Filter profiles...", className: "h-6 w-full rounded border px-1.5 text-[11px] text-gray-500", value: activeProfile?.id || "", options: profileOptions, "data-testid": "agent-profile-selector", onValueChange: async (val) => {
|
|
6019
|
-
const nextProfile = profiles.find((x) => x.id === val);
|
|
6020
|
-
if (!nextProfile)
|
|
6021
|
-
return;
|
|
6022
|
-
setActiveProfile(nextProfile);
|
|
6023
|
-
try {
|
|
6024
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6025
|
-
await updateAgentSettings(agent.id, {
|
|
6026
|
-
profileId: nextProfile.id,
|
|
6027
|
-
profileName: nextProfile.name,
|
|
6028
|
-
});
|
|
6029
|
-
}
|
|
6030
|
-
else {
|
|
6031
|
-
pendingSettingsRef.current = {
|
|
6032
|
-
...(pendingSettingsRef.current || {}),
|
|
6033
|
-
profileId: nextProfile.id,
|
|
6034
|
-
profileName: nextProfile.name,
|
|
6035
|
-
};
|
|
6036
|
-
setAgentMetadata((current) => {
|
|
6037
|
-
const next = {
|
|
6038
|
-
...(current || {}),
|
|
6039
|
-
};
|
|
6040
|
-
next.profile = nextProfile.name;
|
|
6041
|
-
next.additionalData = {
|
|
6042
|
-
...(next.additionalData || {}),
|
|
6043
|
-
profileId: nextProfile.id,
|
|
6044
|
-
profileName: nextProfile.name,
|
|
6045
|
-
};
|
|
6046
|
-
return next;
|
|
6047
|
-
});
|
|
6048
|
-
}
|
|
6049
|
-
setAgent((prev) => prev
|
|
6050
|
-
? {
|
|
6051
|
-
...prev,
|
|
6052
|
-
profileId: nextProfile.id,
|
|
6053
|
-
profileName: nextProfile.name,
|
|
6054
|
-
metadata: JSON.stringify({
|
|
6055
|
-
...(agentMetadata || {}),
|
|
6056
|
-
profile: nextProfile.name,
|
|
6057
|
-
additionalData: {
|
|
6058
|
-
...(agentMetadata
|
|
6059
|
-
?.additionalData || {}),
|
|
6060
|
-
profileId: nextProfile.id,
|
|
6061
|
-
profileName: nextProfile.name,
|
|
6062
|
-
},
|
|
6063
|
-
}),
|
|
6064
|
-
}
|
|
6065
|
-
: prev);
|
|
6066
|
-
}
|
|
6067
|
-
catch (err) {
|
|
6068
|
-
console.error("Failed to persist agent profile", err);
|
|
6069
|
-
}
|
|
6070
|
-
} }), activeProfile && (_jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2", children: [_jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-6 px-2 text-[10px]", onClick: () => {
|
|
6071
|
-
void handleEditProfileSideBySide();
|
|
6072
|
-
setShowAgentSettings(false);
|
|
6073
|
-
}, "data-testid": "agent-profile-edit-button", children: "Edit" }), _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 text-[10px] text-gray-500 hover:text-gray-700", onClick: () => {
|
|
6074
|
-
void handleOpenProfileSettings();
|
|
6075
|
-
setShowAgentSettings(false);
|
|
6076
|
-
}, children: ["Open profile settings", _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 })] })] }))] })), activeProfile?.models?.length ? (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-[11px] font-medium text-gray-700", children: "Model" }), _jsx(Select, { "data-testid": "agent-model-selector", size: "xs", maxWidth: 300, searchable: activeProfile.models.length > 5, searchPlaceholder: "Filter models...", className: "h-6 w-full rounded border px-1.5 text-[11px] text-gray-500", value: selectedModelId || "", options: modelOptions, onValueChange: async (val) => {
|
|
6077
|
-
const nextId = val;
|
|
6078
|
-
setSelectedModelId(nextId);
|
|
6079
|
-
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
|
|
6080
|
-
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
6081
|
-
try {
|
|
6082
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6083
|
-
await updateAgentSettings(agent.id, {
|
|
6084
|
-
model: modelName,
|
|
6085
|
-
});
|
|
6086
|
-
}
|
|
6087
|
-
else {
|
|
6088
|
-
pendingSettingsRef.current = {
|
|
6089
|
-
...(pendingSettingsRef.current || {}),
|
|
6090
|
-
modelName,
|
|
6091
|
-
};
|
|
6092
|
-
}
|
|
6093
|
-
}
|
|
6094
|
-
catch (err) {
|
|
6095
|
-
console.error("Failed to persist agent model", err);
|
|
6096
|
-
}
|
|
6097
|
-
} })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange: (open) => {
|
|
6098
|
-
setShowSkillPicker(open);
|
|
6099
|
-
if (open) {
|
|
6100
|
-
setSkillActionError(null);
|
|
6101
|
-
}
|
|
6102
|
-
}, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[10px] text-gray-600", "data-testid": "agent-skill-picker-trigger", children: [_jsx(Plus, { className: "mr-1 h-3 w-3", strokeWidth: 1.5 }), "Add"] }) }), _jsx(PopoverContent, { className: "w-88 p-2", align: "end", side: "bottom", children: _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-[11px] font-medium text-gray-700", children: "Select a skill" }), _jsxs("div", { className: "relative h-56 rounded border border-gray-200 bg-gray-50", children: [_jsx(ScrollingContentTree, { rootItemIds: skillRootIds, selectedItemId: selectedSkillIds[selectedSkillIds.length - 1] || undefined, expandedItemId: selectedSkillIds[selectedSkillIds.length - 1] || skillRootIds[0], scrollToSelected: true, hideRootNodes: false, onSelectionChange: (selection) => {
|
|
6103
|
-
const selected = selection[0];
|
|
6104
|
-
if (!selected?.id)
|
|
6105
|
-
return;
|
|
6106
|
-
setSkillActionError(null);
|
|
6107
|
-
if (selectableTemplateIdSet.size > 0 &&
|
|
6108
|
-
(!selected.templateId ||
|
|
6109
|
-
!selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
|
|
6110
|
-
return;
|
|
6111
|
-
}
|
|
6112
|
-
if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
|
|
6113
|
-
setSkillActionError("This skill cannot be added for the current agent profile.");
|
|
6114
|
-
return;
|
|
6115
|
-
}
|
|
6116
|
-
void (async () => {
|
|
6117
|
-
const added = await handleAddSkill(selected.id);
|
|
6118
|
-
if (added) {
|
|
6119
|
-
setShowSkillPicker(false);
|
|
6120
|
-
}
|
|
6121
|
-
})();
|
|
6122
|
-
} }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
|
|
6123
|
-
!skillsError &&
|
|
6124
|
-
profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsError && skillActionError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillActionError })), !skillsLoading &&
|
|
6125
|
-
!skillsError &&
|
|
6126
|
-
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
6127
|
-
!skillsError &&
|
|
6128
|
-
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
6129
|
-
? "All addable skills are selected"
|
|
6130
|
-
: "No skills can be added for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
|
|
6131
|
-
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
6132
|
-
return (_jsxs("div", { className: "inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-100 px-1.5 py-0.5 text-[10px] text-gray-700", children: [_jsx("span", { children: skill?.name || skillId }), _jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", title: "Open skill item", "aria-label": `Open ${skill?.name || skillId}`, onClick: () => {
|
|
6133
|
-
void handleOpenSkillItem(skillId);
|
|
6134
|
-
}, children: _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }), autoAssignedSkillSet.has(skillId.toLowerCase()) ? (_jsx("span", { className: "text-[9px] text-gray-500", children: "auto" })) : (_jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", onClick: () => {
|
|
6135
|
-
void handleRemoveSkill(skillId);
|
|
6136
|
-
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
6137
|
-
}) }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setToolsSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": toolsSectionExpanded, "data-testid": "agent-tools-section-toggle", children: [_jsxs("span", { children: ["Available tools (", displayedAvailableTools.length, ")"] }), toolsSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), toolsSectionExpanded && (_jsx("div", { className: "max-h-36 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-tools-section", children: displayedAvailableToolsLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading available tools..." })) : displayedAvailableTools.length > 0 ? (_jsxs("div", { className: "space-y-0.5", children: [isLocalOnlyDraftAgent && (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Preview based on the current profile, mode, and selected skills." })), displayedAvailableTools.map((toolName) => (_jsx("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": "agent-tool-row", title: toolName, children: _jsx("div", { className: "truncate text-[10px] text-gray-700", children: toolName }) }, toolName)))] })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6138
|
-
? "No available tools for this profile and mode"
|
|
6139
|
-
: "No available tools" })) })), displayedAvailableToolsError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: displayedAvailableToolsError }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setAllowancesSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": allowancesSectionExpanded, "data-testid": "agent-allowances-section-toggle", children: [_jsxs("span", { children: ["Allowances (", allowancesTotalCount, ")"] }), allowancesSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), allowancesSectionExpanded && (_jsx("div", { className: "max-h-36 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-allowances-section", children: operationAllowancesLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading allowances..." })) : hasAnyAllowances ? (_jsx("div", { className: "space-y-1", children: allowanceGroups.map((group) => group.rows.length > 0 ? (_jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "px-1 text-[9px] font-medium tracking-wide text-gray-400 uppercase", children: group.label }), group.rows.map((allowance, index) => {
|
|
6140
|
-
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
6141
|
-
const pathLabel = "itemPath" in allowance
|
|
6142
|
-
? allowance.itemPath
|
|
6143
|
-
: allowance.normalizedPath;
|
|
6144
|
-
return (_jsxs("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": `agent-allowance-row-${group.key}`, children: [_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsx("div", { className: "shrink-0 text-[10px] font-medium text-gray-700", children: allowance.operationType ||
|
|
6145
|
-
"*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
|
|
6146
|
-
allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
|
|
6147
|
-
sourceLabel,
|
|
6148
|
-
allowance.grantedBy,
|
|
6149
|
-
]
|
|
6150
|
-
.filter(Boolean)
|
|
6151
|
-
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
6152
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6153
|
-
? "Allowances are shown after the agent is created"
|
|
6154
|
-
: "No active allowances" })) })), operationAllowancesError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: operationAllowancesError }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setSubscribedTriggersSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": subscribedTriggersSectionExpanded, "data-testid": "agent-subscribed-triggers-section-toggle", children: [_jsx("span", { children: `Subscribed triggers (${activeTriggerSubscriptions.length})` }), subscribedTriggersSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), subscribedTriggersSectionExpanded && (_jsx("div", { className: "max-h-28 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-subscribed-triggers-section", children: triggerSubscriptionsLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading subscribed triggers..." })) : activeTriggerSubscriptions.length > 0 ? (_jsx("div", { className: "space-y-0.5", children: activeTriggerSubscriptions.map((sub) => {
|
|
6155
|
-
const filterText = (sub.filter || "").trim();
|
|
6156
|
-
return (_jsxs("div", { className: "flex items-baseline gap-1.5 rounded px-1 py-0.5 transition-colors hover:bg-white/60", children: [_jsx("div", { className: "shrink-0 text-[10px] font-medium text-gray-700", children: sub.triggerName }), filterText.length > 0 && (_jsx("div", { className: "truncate text-[9px] text-gray-400", title: filterText, children: filterText }))] }, sub.id));
|
|
6157
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6158
|
-
? "Subscribed triggers are shown after the agent is created"
|
|
6159
|
-
: "No active trigger subscriptions" })) })), triggerSubscriptionsError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: triggerSubscriptionsError }))] })] }) })] }), 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", type: "button", 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-[10px] text-gray-700 hover:bg-gray-100", onClick: () => {
|
|
4274
|
+
} })) : 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", type: "button", 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-[10px] text-gray-700 hover:bg-gray-100", onClick: () => {
|
|
6160
4275
|
setPrompt(p.prompt);
|
|
6161
4276
|
setShowPredefined(false);
|
|
6162
4277
|
if (textareaRef.current)
|
|
6163
4278
|
textareaRef.current.focus();
|
|
6164
|
-
}, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
|
|
6165
|
-
|
|
6166
|
-
editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
|
|
6167
|
-
if (editContext?.isMobile)
|
|
4279
|
+
}, children: p.title }, index))) }) })] })) : null, !hideBottomControls && !simpleMode && isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
|
|
4280
|
+
if (isMobile)
|
|
6168
4281
|
setShowCostAndAgent((prev) => !prev);
|
|
6169
|
-
}, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded":
|
|
6170
|
-
? showCostAndAgent
|
|
6171
|
-
: undefined, "aria-label": "Toggle cost and context info", children: _jsx(DollarSign, { className: "size-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: _jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, effectiveModelName: effectiveModelName, socketDiagnostics: editContext.socketDiagnostics, runDiagnosticsSnapshot: runDiagnosticsSnapshot, liveTotals: liveTotals, totalTokens: liveTotals
|
|
4282
|
+
}, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded": isMobile ? showCostAndAgent : undefined, "aria-label": "Toggle cost and context info", children: _jsx(DollarSign, { className: "size-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: _jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, liveTotals: liveTotals, totalTokens: liveTotals
|
|
6172
4283
|
? {
|
|
6173
4284
|
input: liveTotals.input,
|
|
6174
4285
|
output: liveTotals.output,
|
|
@@ -6178,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6178
4289
|
outputCost: liveTotals.outputCost,
|
|
6179
4290
|
cachedCost: liveTotals.cachedCost,
|
|
6180
4291
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6181
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6182
4292
|
totalCost: liveTotals.totalCost,
|
|
6183
4293
|
}
|
|
6184
|
-
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }) }) })] }))] })), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [_jsx("span", { title: isVoiceDisabled
|
|
4294
|
+
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }) }) })] }))] })), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [_jsx("span", { title: isVoiceDisabled
|
|
6185
4295
|
? "Your browser does not support Speech Recognition"
|
|
6186
4296
|
: isListening
|
|
6187
4297
|
? "Stop voice input"
|
|
@@ -6202,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6202
4312
|
: allPendingApprovals.length > 0
|
|
6203
4313
|
? "Approve or reject pending tool calls first"
|
|
6204
4314
|
: "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 })) })] })] }));
|
|
6205
|
-
})(), !hideBottomControls &&
|
|
6206
|
-
!simpleMode &&
|
|
6207
|
-
editContext &&
|
|
6208
|
-
!editContext.isMobile && (_jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, effectiveModelName: effectiveModelName, socketDiagnostics: editContext.socketDiagnostics, runDiagnosticsSnapshot: runDiagnosticsSnapshot, liveTotals: liveTotals, totalTokens: liveTotals
|
|
4315
|
+
})(), !hideBottomControls && !simpleMode && !isMobile && (_jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, liveTotals: liveTotals, totalTokens: liveTotals
|
|
6209
4316
|
? {
|
|
6210
4317
|
input: liveTotals.input,
|
|
6211
4318
|
output: liveTotals.output,
|
|
@@ -6215,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6215
4322
|
outputCost: liveTotals.outputCost,
|
|
6216
4323
|
cachedCost: liveTotals.cachedCost,
|
|
6217
4324
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6218
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6219
4325
|
totalCost: liveTotals.totalCost,
|
|
6220
4326
|
}
|
|
6221
|
-
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
|
|
4327
|
+
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
|
|
6222
4328
|
}
|
|
6223
4329
|
//# sourceMappingURL=AgentTerminal.js.map
|