@parhelia/core 0.1.12565 → 0.1.12570
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 +99 -191
- 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 +496 -2406
- 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 -481
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +113 -161
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -10
- package/dist/editor/ai/AiResponseMessage.js +26 -267
- 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 +150 -542
- 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 +100 -90
- 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 +237 -770
- 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 +15 -73
- 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 -24
- 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 -39
|
@@ -1,11 +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 {
|
|
4
|
-
import {
|
|
5
|
-
import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
|
|
6
|
-
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";
|
|
7
5
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
8
|
-
import { localStorageService } from "../services/localStorageService";
|
|
9
6
|
import { Textarea } from "../../components/ui/textarea";
|
|
10
7
|
import { Button } from "../../components/ui/button";
|
|
11
8
|
import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
|
|
@@ -15,145 +12,21 @@ import { AgentDocumentList, } from "./AgentDocumentList";
|
|
|
15
12
|
import { AgentEditOperationsPanel } from "./EditOperationsPanel";
|
|
16
13
|
import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
|
|
17
14
|
import { getComponentById } from "../componentTreeHelper";
|
|
18
|
-
import { toUserFacingAgentErrorMessage } from "../services/agentErrorMessage";
|
|
19
15
|
import { AgentGreeting } from "./AgentGreeting";
|
|
20
16
|
import { getAgentHistory } from "../services/editService";
|
|
21
|
-
import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
|
|
22
|
-
import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
|
|
23
|
-
import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
|
|
24
17
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
25
18
|
import { SecretAgentIcon } from "../ui/Icons";
|
|
26
19
|
import { formatTime, formatDateTime } from "../utils";
|
|
27
20
|
import { cn } from "../../lib/utils";
|
|
28
|
-
import { sanitizeSvg } from "../../lib/sanitize";
|
|
29
21
|
import { Select } from "../../components/ui/select";
|
|
30
22
|
import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
|
|
23
|
+
import { useMediaQuery } from "../client/hooks/useMediaQuery";
|
|
31
24
|
import { SimpleTabs } from "../ui/SimpleTabs";
|
|
32
|
-
import { Splitter } from "../ui/Splitter";
|
|
33
|
-
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
34
|
-
import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
|
|
35
|
-
const userMessageMarkdownComponents = {
|
|
36
|
-
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
|
|
37
|
-
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
|
|
38
|
-
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
|
|
39
|
-
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
|
|
40
|
-
p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
|
|
41
|
-
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
42
|
-
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
43
|
-
li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
|
|
44
|
-
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" })),
|
|
45
|
-
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 })),
|
|
46
|
-
};
|
|
47
|
-
function buildPlaceholderAgentDetails(agentStub) {
|
|
48
|
-
const now = new Date().toISOString();
|
|
49
|
-
const updated = agentStub.updatedDate || now;
|
|
50
|
-
// AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
|
|
51
|
-
// This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
|
|
52
|
-
return {
|
|
53
|
-
...agentStub,
|
|
54
|
-
name: agentStub.name || "Agent",
|
|
55
|
-
userId: agentStub.userId || "",
|
|
56
|
-
updatedDate: updated,
|
|
57
|
-
profileName: agentStub.profileName || "",
|
|
58
|
-
model: agentStub.model || "",
|
|
59
|
-
createdDate: agentStub.createdDate || updated,
|
|
60
|
-
totalTokensUsed: 0,
|
|
61
|
-
totalInputTokens: 0,
|
|
62
|
-
totalOutputTokens: 0,
|
|
63
|
-
totalCachedInputTokens: 0,
|
|
64
|
-
totalInputTokenCost: 0,
|
|
65
|
-
totalOutputTokenCost: 0,
|
|
66
|
-
totalCachedInputTokenCost: 0,
|
|
67
|
-
totalImageCost: 0,
|
|
68
|
-
totalCost: 0,
|
|
69
|
-
currency: agentStub.currency || "USD",
|
|
70
|
-
messageCount: agentStub.messageCount || 0,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function normalizeDialogAgentId(value) {
|
|
74
|
-
return value?.trim().toLowerCase() || "";
|
|
75
|
-
}
|
|
76
|
-
function formatAllowanceSource(source) {
|
|
77
|
-
const normalized = source?.trim();
|
|
78
|
-
if (!normalized)
|
|
79
|
-
return null;
|
|
80
|
-
if (normalized === "user")
|
|
81
|
-
return "User granted";
|
|
82
|
-
if (normalized.startsWith("preconfigured:profile:"))
|
|
83
|
-
return "Profile";
|
|
84
|
-
if (normalized.startsWith("preconfigured:skill:"))
|
|
85
|
-
return "Skill";
|
|
86
|
-
if (normalized.startsWith("system:")) {
|
|
87
|
-
return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
|
|
88
|
-
}
|
|
89
|
-
return normalized;
|
|
90
|
-
}
|
|
91
|
-
function formatAllowanceLabel(allowance) {
|
|
92
|
-
return `${allowance.operationType || "*"}${"itemPath" in allowance
|
|
93
|
-
? ` ${allowance.itemPath}`
|
|
94
|
-
: ` ${allowance.normalizedPath}`}`;
|
|
95
|
-
}
|
|
96
|
-
function getAgentRunMessageAgentId(payload) {
|
|
97
|
-
const agentId = payload?.agentId;
|
|
98
|
-
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
99
|
-
}
|
|
100
|
-
function getAgentRunMessageSeq(payload) {
|
|
101
|
-
const seq = payload?.seq;
|
|
102
|
-
return typeof seq === "number" ? seq : null;
|
|
103
|
-
}
|
|
104
|
-
function getAgentRunMessageDetail(type, payload) {
|
|
105
|
-
if (type === "agent:run:delta") {
|
|
106
|
-
return payload?.type || null;
|
|
107
|
-
}
|
|
108
|
-
if (type === "agent:run:status") {
|
|
109
|
-
return payload?.data?.state || payload?.data?.status || null;
|
|
110
|
-
}
|
|
111
|
-
if (type === "agent:run:error") {
|
|
112
|
-
return payload?.error || null;
|
|
113
|
-
}
|
|
114
|
-
if (type === "agent:run:complete") {
|
|
115
|
-
return payload?.finalStatus || null;
|
|
116
|
-
}
|
|
117
|
-
if (type === "agent:run:start") {
|
|
118
|
-
return payload?.agentName || null;
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
function isHeartbeatRunEventMessage(message) {
|
|
123
|
-
if (!message)
|
|
124
|
-
return false;
|
|
125
|
-
if (message.type === "agent:run:delta") {
|
|
126
|
-
const deltaType = message.payload?.type;
|
|
127
|
-
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
128
|
-
}
|
|
129
|
-
if (message.type === "agent:run:status") {
|
|
130
|
-
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
131
|
-
return String(state || "").toLowerCase() === "heartbeat";
|
|
132
|
-
}
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
function getVisibleDialogRegistry() {
|
|
136
|
-
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
137
|
-
return registry && typeof registry === "object" ? registry : {};
|
|
138
|
-
}
|
|
139
|
-
function isAgentErrorStatusValue(status) {
|
|
140
|
-
return status === "error";
|
|
141
|
-
}
|
|
142
25
|
// Simple user message component
|
|
143
26
|
const UserMessage = ({ message }) => {
|
|
144
|
-
const content = message.content || "";
|
|
145
|
-
const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
|
|
146
|
-
// Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
|
|
147
|
-
const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
|
|
148
|
-
const triggerMatch = content.match(triggerPattern);
|
|
149
|
-
const triggerName = triggerMatch?.[1]?.trim() || "";
|
|
150
|
-
const triggerContent = triggerMatch?.[2] || "";
|
|
151
|
-
const isTriggerMessage = triggerName.length > 0;
|
|
152
|
-
if (isTriggerMessage) {
|
|
153
|
-
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 }) }))] }) }));
|
|
154
|
-
}
|
|
155
27
|
// Parse source agent name from content if it starts with "[From ...]:"
|
|
156
28
|
// Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
|
|
29
|
+
const content = message.content || "";
|
|
157
30
|
const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
|
|
158
31
|
const match = content.match(fromPattern);
|
|
159
32
|
let sourceAgentName;
|
|
@@ -178,10 +51,7 @@ const UserMessage = ({ message }) => {
|
|
|
178
51
|
message.sourceAgent?.name;
|
|
179
52
|
}
|
|
180
53
|
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
181
|
-
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:
|
|
182
|
-
};
|
|
183
|
-
const HeartbeatMessage = ({ message }) => {
|
|
184
|
-
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 })] })] }));
|
|
185
55
|
};
|
|
186
56
|
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
187
57
|
const extractPartialTodos = (jsonText) => {
|
|
@@ -415,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
|
|
|
415
285
|
todoMap.set(key, todo);
|
|
416
286
|
}
|
|
417
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
|
+
}
|
|
418
296
|
return result;
|
|
419
297
|
};
|
|
420
298
|
// TodoListPanel component
|
|
@@ -422,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
|
422
300
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
423
301
|
// First try to get todos from agent metadata (real-time updates)
|
|
424
302
|
// Server sends additionalData.todoList directly via contextChanged status
|
|
303
|
+
// Also check top-level todoList for backward compatibility with stored contexts
|
|
425
304
|
const metadataTodos = (() => {
|
|
426
305
|
try {
|
|
427
|
-
|
|
306
|
+
// Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
|
|
307
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
308
|
+
agentMetadata?.todoList;
|
|
428
309
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
429
310
|
const rawItems = todoList.items
|
|
430
311
|
.map((item, idx) => ({
|
|
@@ -552,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
552
433
|
// Add user message
|
|
553
434
|
groups.push({ type: "user", messages: [message] });
|
|
554
435
|
}
|
|
555
|
-
else if (message.messageType === "heartbeat" ||
|
|
556
|
-
message.role === "system") {
|
|
557
|
-
if (currentAssistantGroup.length > 0) {
|
|
558
|
-
groups.push({
|
|
559
|
-
type: "assistant-group",
|
|
560
|
-
messages: currentAssistantGroup,
|
|
561
|
-
});
|
|
562
|
-
currentAssistantGroup = [];
|
|
563
|
-
}
|
|
564
|
-
groups.push({ type: "heartbeat", messages: [message] });
|
|
565
|
-
}
|
|
566
436
|
else if (message.role === "assistant") {
|
|
567
437
|
// Add to current assistant group
|
|
568
438
|
currentAssistantGroup.push(message);
|
|
@@ -630,7 +500,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
630
500
|
outputCost: acc.outputCost + (message.outputTokenCost || 0),
|
|
631
501
|
cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
|
|
632
502
|
cacheWriteCost: acc.cacheWriteCost,
|
|
633
|
-
imageCost: acc.imageCost,
|
|
634
503
|
totalCost: acc.totalCost + (message.totalCost || 0),
|
|
635
504
|
};
|
|
636
505
|
}, {
|
|
@@ -642,7 +511,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
642
511
|
outputCost: 0,
|
|
643
512
|
cachedCost: 0,
|
|
644
513
|
cacheWriteCost: 0,
|
|
645
|
-
imageCost: 0,
|
|
646
514
|
totalCost: 0,
|
|
647
515
|
});
|
|
648
516
|
return totals;
|
|
@@ -653,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
|
|
|
653
521
|
const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
|
|
654
522
|
return matched;
|
|
655
523
|
};
|
|
656
|
-
const stringifyToolField = (value) => {
|
|
657
|
-
if (value === undefined || value === null)
|
|
658
|
-
return undefined;
|
|
659
|
-
if (typeof value === "string") {
|
|
660
|
-
return value.trim().length > 0 ? value : undefined;
|
|
661
|
-
}
|
|
662
|
-
try {
|
|
663
|
-
return JSON.stringify(value);
|
|
664
|
-
}
|
|
665
|
-
catch {
|
|
666
|
-
return String(value);
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
const parseToolResultValue = (value) => {
|
|
670
|
-
if (value === undefined || value === null) {
|
|
671
|
-
return undefined;
|
|
672
|
-
}
|
|
673
|
-
if (typeof value === "object") {
|
|
674
|
-
return value;
|
|
675
|
-
}
|
|
676
|
-
if (typeof value !== "string") {
|
|
677
|
-
return String(value);
|
|
678
|
-
}
|
|
679
|
-
const trimmed = value.trim();
|
|
680
|
-
if (!trimmed) {
|
|
681
|
-
return undefined;
|
|
682
|
-
}
|
|
683
|
-
try {
|
|
684
|
-
let parsed = JSON.parse(trimmed);
|
|
685
|
-
if (typeof parsed === "string" &&
|
|
686
|
-
(parsed.startsWith("{") || parsed.startsWith("["))) {
|
|
687
|
-
parsed = JSON.parse(parsed);
|
|
688
|
-
}
|
|
689
|
-
if (parsed && typeof parsed === "object") {
|
|
690
|
-
return parsed;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
catch {
|
|
694
|
-
// Fall back to the original string when the payload is plain text.
|
|
695
|
-
}
|
|
696
|
-
return value;
|
|
697
|
-
};
|
|
698
|
-
const getFirstToolCallEnvelope = (data) => {
|
|
699
|
-
if (!data || typeof data !== "object")
|
|
700
|
-
return undefined;
|
|
701
|
-
const direct = data.toolCall || data.tool_call;
|
|
702
|
-
if (direct)
|
|
703
|
-
return direct;
|
|
704
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
705
|
-
for (const candidate of arrayCandidates) {
|
|
706
|
-
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
707
|
-
return candidate[0];
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
return undefined;
|
|
711
|
-
};
|
|
712
|
-
const extractToolCallFields = (data) => {
|
|
713
|
-
const envelope = getFirstToolCallEnvelope(data);
|
|
714
|
-
const functionPayload = data?.function || envelope?.function;
|
|
715
|
-
const functionName = data?.functionName ||
|
|
716
|
-
data?.name ||
|
|
717
|
-
functionPayload?.name ||
|
|
718
|
-
envelope?.functionName ||
|
|
719
|
-
envelope?.name ||
|
|
720
|
-
"unknown";
|
|
721
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
722
|
-
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
723
|
-
stringifyToolField(data?.arguments) ||
|
|
724
|
-
stringifyToolField(functionPayload?.arguments) ||
|
|
725
|
-
stringifyToolField(envelope?.functionArguments) ||
|
|
726
|
-
stringifyToolField(envelope?.arguments) ||
|
|
727
|
-
"{}";
|
|
728
|
-
return {
|
|
729
|
-
toolCallId,
|
|
730
|
-
functionName,
|
|
731
|
-
functionArguments,
|
|
732
|
-
};
|
|
733
|
-
};
|
|
734
524
|
// Convert agent messages to AI terminal format for a response group
|
|
735
525
|
const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
736
526
|
return agentMessages.map((agentMessage) => {
|
|
@@ -751,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
751
541
|
role: agentMessage.role,
|
|
752
542
|
createdDate: agentMessage.createdDate,
|
|
753
543
|
tool_calls: agentMessage.toolCalls
|
|
754
|
-
? agentMessage.toolCalls.map((toolCall) => {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
!toolCall.isCompleted &&
|
|
777
|
-
!displayResult &&
|
|
778
|
-
!toolCall.functionError &&
|
|
779
|
-
!isPruned,
|
|
780
|
-
// Pass through message IDs for approval/rejection events
|
|
781
|
-
messageId: toolCall.messageId,
|
|
782
|
-
dbMessageId: toolCall.dbMessageId,
|
|
783
|
-
responseTimeMs: toolCall.responseTimeMs,
|
|
784
|
-
createdDate: toolCall.createdDate,
|
|
785
|
-
};
|
|
786
|
-
})
|
|
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
|
+
}))
|
|
787
566
|
: [],
|
|
788
567
|
};
|
|
789
568
|
if (agentMessage.toolCallId) {
|
|
@@ -795,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
795
574
|
// interface AgentTerminalProps {
|
|
796
575
|
// agentStub: Agent;
|
|
797
576
|
// }
|
|
798
|
-
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, }) {
|
|
799
578
|
const editContext = useEditContext();
|
|
800
579
|
const fieldsContext = useFieldsEditContext();
|
|
801
|
-
const [agent, setAgent] = useState(
|
|
580
|
+
const [agent, setAgent] = useState(undefined);
|
|
802
581
|
const [messages, setMessages] = useState([]);
|
|
803
582
|
const [agentOperations, setAgentOperations] = useState([]);
|
|
804
583
|
const [prompt, setPrompt] = useState("");
|
|
@@ -809,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
809
588
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
810
589
|
const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
|
|
811
590
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
812
|
-
const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
|
|
813
|
-
const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
|
|
814
|
-
// Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
|
|
815
|
-
// This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
|
|
816
|
-
useEffect(() => {
|
|
817
|
-
setAgent((prev) => {
|
|
818
|
-
if (prev?.id === agentStub.id)
|
|
819
|
-
return prev;
|
|
820
|
-
return buildPlaceholderAgentDetails(agentStub);
|
|
821
|
-
});
|
|
822
|
-
}, [agentStub.id]);
|
|
823
|
-
const observedMessageIdsRef = useRef(new Set());
|
|
824
|
-
useEffect(() => {
|
|
825
|
-
observedMessageIdsRef.current = new Set();
|
|
826
|
-
}, [agentStub.id]);
|
|
827
|
-
useEffect(() => {
|
|
828
|
-
if (!onMessage)
|
|
829
|
-
return;
|
|
830
|
-
for (const message of messages) {
|
|
831
|
-
if (!message?.id)
|
|
832
|
-
continue;
|
|
833
|
-
if (!message.isCompleted)
|
|
834
|
-
continue;
|
|
835
|
-
if (observedMessageIdsRef.current.has(message.id))
|
|
836
|
-
continue;
|
|
837
|
-
observedMessageIdsRef.current.add(message.id);
|
|
838
|
-
onMessage(message);
|
|
839
|
-
}
|
|
840
|
-
}, [messages, onMessage]);
|
|
841
591
|
// Generate a stable clientSessionId per component instance for stream deduplication
|
|
842
592
|
const clientSessionIdRef = useRef(null);
|
|
843
593
|
if (!clientSessionIdRef.current) {
|
|
@@ -849,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
849
599
|
const [isListening, setIsListening] = useState(false);
|
|
850
600
|
const recognitionRef = useRef(null);
|
|
851
601
|
const prevPlaceholderRef = useRef(null);
|
|
852
|
-
const promptBeforeVoiceRef = useRef("");
|
|
853
602
|
// Voice button press-and-hold tracking
|
|
854
603
|
const voicePressStartRef = useRef(null);
|
|
855
604
|
const voiceHoldTimerRef = useRef(null);
|
|
@@ -859,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
859
608
|
const [showCompressionPopover, setShowCompressionPopover] = useState(false);
|
|
860
609
|
// Agent inline dialog state (for component type selector, etc.)
|
|
861
610
|
const [activeInlineDialog, setActiveInlineDialog] = useState(null);
|
|
862
|
-
const dialogTerminalInstanceIdRef = useRef("");
|
|
863
|
-
if (!dialogTerminalInstanceIdRef.current) {
|
|
864
|
-
dialogTerminalInstanceIdRef.current = crypto.randomUUID();
|
|
865
|
-
}
|
|
866
|
-
const activeInlineDialogRef = useRef(activeInlineDialog);
|
|
867
|
-
const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
|
|
868
|
-
const orphanTimeoutRef = useRef(null);
|
|
869
|
-
useEffect(() => {
|
|
870
|
-
activeInlineDialogRef.current = activeInlineDialog;
|
|
871
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
872
|
-
const callbackId = activeInlineDialog?.request.callbackId || null;
|
|
873
|
-
const terminalInstanceId = dialogTerminalInstanceIdRef.current;
|
|
874
|
-
const agentKeys = [
|
|
875
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
876
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
877
|
-
].filter(Boolean);
|
|
878
|
-
agentKeys.forEach((key) => {
|
|
879
|
-
if (callbackId) {
|
|
880
|
-
visibleRegistry[key] = {
|
|
881
|
-
callbackId,
|
|
882
|
-
terminalInstanceId,
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
else {
|
|
886
|
-
delete visibleRegistry[key];
|
|
887
|
-
}
|
|
888
|
-
});
|
|
889
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
890
|
-
}, [activeInlineDialog]);
|
|
891
|
-
useEffect(() => {
|
|
892
|
-
onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
|
|
893
|
-
}, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
|
|
894
|
-
useLayoutEffect(() => {
|
|
895
|
-
if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
const scrollSummaryTerminalToTop = () => {
|
|
899
|
-
const container = messagesContainerRef.current;
|
|
900
|
-
if (!container) {
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
container.scrollTop = 0;
|
|
904
|
-
};
|
|
905
|
-
scrollSummaryTerminalToTop();
|
|
906
|
-
const frame1 = requestAnimationFrame(() => {
|
|
907
|
-
scrollSummaryTerminalToTop();
|
|
908
|
-
});
|
|
909
|
-
let frame3 = 0;
|
|
910
|
-
const frame2 = requestAnimationFrame(() => {
|
|
911
|
-
frame3 = requestAnimationFrame(() => {
|
|
912
|
-
scrollSummaryTerminalToTop();
|
|
913
|
-
});
|
|
914
|
-
});
|
|
915
|
-
return () => {
|
|
916
|
-
cancelAnimationFrame(frame1);
|
|
917
|
-
cancelAnimationFrame(frame2);
|
|
918
|
-
cancelAnimationFrame(frame3);
|
|
919
|
-
};
|
|
920
|
-
}, [displayMode, isQuestionnaireDialogOpen]);
|
|
921
|
-
useEffect(() => {
|
|
922
|
-
return () => {
|
|
923
|
-
onQuestionnaireOpenChange?.(false);
|
|
924
|
-
};
|
|
925
|
-
}, [onQuestionnaireOpenChange]);
|
|
926
611
|
const isWaitingRef = useRef(false);
|
|
927
612
|
useEffect(() => {
|
|
928
613
|
isWaitingRef.current = isWaitingForResponse;
|
|
@@ -933,64 +618,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
933
618
|
const isStoppingRef = useRef(false);
|
|
934
619
|
// Server-driven state: true when agent is actively processing (set by WebSocket messages)
|
|
935
620
|
const [isAgentThinking, setIsAgentThinking] = useState(false);
|
|
936
|
-
useEffect(() => {
|
|
937
|
-
if (!initialMetadata)
|
|
938
|
-
return;
|
|
939
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata({
|
|
940
|
-
...(prev || {}),
|
|
941
|
-
...initialMetadata,
|
|
942
|
-
additionalData: {
|
|
943
|
-
...(prev?.additionalData || {}),
|
|
944
|
-
...(initialMetadata?.additionalData || {}),
|
|
945
|
-
},
|
|
946
|
-
}));
|
|
947
|
-
}, [initialMetadata]);
|
|
948
621
|
const hasActiveStreaming = useCallback(() => {
|
|
949
622
|
const current = messagesRef.current || [];
|
|
950
623
|
return current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
951
624
|
}, []);
|
|
952
|
-
const currentAgentId = agent?.id || agentStub.id;
|
|
953
|
-
const recentAgentRunEvents = useMemo(() => {
|
|
954
|
-
const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
|
|
955
|
-
if (!normalizedAgentId) {
|
|
956
|
-
return [];
|
|
957
|
-
}
|
|
958
|
-
if (!editContext) {
|
|
959
|
-
return [];
|
|
960
|
-
}
|
|
961
|
-
return (editContext.webSocketMessages || [])
|
|
962
|
-
.filter((message) => {
|
|
963
|
-
if (!message?.type?.startsWith("agent:run:")) {
|
|
964
|
-
return false;
|
|
965
|
-
}
|
|
966
|
-
if (isHeartbeatRunEventMessage(message)) {
|
|
967
|
-
return false;
|
|
968
|
-
}
|
|
969
|
-
return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
|
|
970
|
-
})
|
|
971
|
-
.slice(-8)
|
|
972
|
-
.map((message) => ({
|
|
973
|
-
timestamp: message.timestamp,
|
|
974
|
-
type: message.type,
|
|
975
|
-
seq: getAgentRunMessageSeq(message.payload),
|
|
976
|
-
detail: getAgentRunMessageDetail(message.type, message.payload),
|
|
977
|
-
}));
|
|
978
|
-
}, [currentAgentId, editContext?.webSocketMessages]);
|
|
979
|
-
const appendToolUiEvent = useCallback((type, detail, seq) => {
|
|
980
|
-
const timestamp = new Date().toISOString();
|
|
981
|
-
setRecentToolUiEvents((prev) => {
|
|
982
|
-
const next = [
|
|
983
|
-
...prev,
|
|
984
|
-
{
|
|
985
|
-
timestamp,
|
|
986
|
-
type,
|
|
987
|
-
detail: detail || null,
|
|
988
|
-
seq: seq ?? null,
|
|
989
|
-
},
|
|
990
|
-
];
|
|
991
|
-
return next.slice(-40);
|
|
992
|
-
});
|
|
993
|
-
}, []);
|
|
994
625
|
// Collect all pending tool calls for batch approval functionality
|
|
995
626
|
const allPendingApprovals = useMemo(() => {
|
|
996
627
|
const pending = [];
|
|
@@ -1032,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1032
663
|
}, [allPendingApprovals]);
|
|
1033
664
|
// Handle mode switch to autonomous
|
|
1034
665
|
const handleSwitchToAutonomous = useCallback(() => {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
setAgentMetadata((prev) => ({
|
|
1038
|
-
...(prev ?? {}),
|
|
1039
|
-
mode: nextMode,
|
|
1040
|
-
}));
|
|
1041
|
-
setAgent((prev) => {
|
|
1042
|
-
if (!prev)
|
|
1043
|
-
return prev;
|
|
1044
|
-
return {
|
|
1045
|
-
...prev,
|
|
1046
|
-
mode: nextMode,
|
|
1047
|
-
};
|
|
1048
|
-
});
|
|
1049
|
-
}, [setAgent, setAgentMetadata]);
|
|
666
|
+
setMode("autonomous");
|
|
667
|
+
}, []);
|
|
1050
668
|
const [resolvedPageName, setResolvedPageName] = useState(undefined);
|
|
1051
669
|
const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
|
|
1052
670
|
const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
|
|
1053
671
|
const [promptHistory, setPromptHistory] = useState(() => {
|
|
1054
|
-
|
|
672
|
+
if (typeof window !== "undefined") {
|
|
673
|
+
return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
|
|
674
|
+
}
|
|
675
|
+
return [];
|
|
1055
676
|
});
|
|
1056
677
|
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
|
|
1057
678
|
const [showPredefined, setShowPredefined] = useState(false);
|
|
1058
679
|
const [activeProfile, setActiveProfile] = useState(undefined);
|
|
1059
680
|
const [selectedModelId, setSelectedModelId] = useState(undefined);
|
|
1060
|
-
const normalizeAgentMode = (value) => {
|
|
1061
|
-
if (value === "autonomous" ||
|
|
1062
|
-
value === "read-only" ||
|
|
1063
|
-
value === "supervised") {
|
|
1064
|
-
return value;
|
|
1065
|
-
}
|
|
1066
|
-
return null;
|
|
1067
|
-
};
|
|
1068
681
|
const [mode, setMode] = useState("supervised");
|
|
1069
682
|
const [queuedPrompts, setQueuedPrompts] = useState([]);
|
|
1070
|
-
const
|
|
683
|
+
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
1071
684
|
const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
|
|
1072
685
|
const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
|
|
1073
686
|
const [showCostAndAgent, setShowCostAndAgent] = useState(false);
|
|
1074
|
-
const [showAgentSettings, setShowAgentSettings] = useState(false);
|
|
1075
|
-
const [showSkillPicker, setShowSkillPicker] = useState(false);
|
|
1076
|
-
const [availableSkills, setAvailableSkills] = useState([]);
|
|
1077
|
-
const [skillRootIds, setSkillRootIds] = useState([]);
|
|
1078
|
-
const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
|
|
1079
|
-
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1080
|
-
const [skillsError, setSkillsError] = useState(null);
|
|
1081
|
-
const [skillActionError, setSkillActionError] = useState(null);
|
|
1082
|
-
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1083
|
-
const [availableTools, setAvailableTools] = useState([]);
|
|
1084
|
-
const [operationAllowances, setOperationAllowances] = useState({
|
|
1085
|
-
sitecore: [],
|
|
1086
|
-
filesystem: [],
|
|
1087
|
-
});
|
|
1088
|
-
const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
|
|
1089
|
-
const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
|
|
1090
|
-
const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
|
|
1091
|
-
const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
|
|
1092
|
-
const [availableToolsError, setAvailableToolsError] = useState(null);
|
|
1093
|
-
const [operationAllowancesError, setOperationAllowancesError] = useState(null);
|
|
1094
|
-
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1095
|
-
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1096
|
-
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1097
|
-
const isPersistedAgent = !!agent?.userId;
|
|
1098
|
-
const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
|
|
1099
687
|
const hasSpawnedAgents = useMemo(() => {
|
|
1100
688
|
if (!agentMetadata)
|
|
1101
689
|
return false;
|
|
1102
|
-
const childAgents = agentMetadata?.
|
|
690
|
+
const childAgents = agentMetadata?.ChildAgents ||
|
|
691
|
+
agentMetadata?.childAgents;
|
|
1103
692
|
if (!Array.isArray(childAgents) || childAgents.length === 0)
|
|
1104
693
|
return false;
|
|
1105
|
-
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));
|
|
1106
695
|
}, [agentMetadata]);
|
|
1107
696
|
const hasTodoContent = useMemo(() => {
|
|
1108
697
|
const metadataTodos = (() => {
|
|
1109
698
|
try {
|
|
1110
|
-
const todoList = agentMetadata?.additionalData?.todoList
|
|
699
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
700
|
+
agentMetadata?.todoList;
|
|
1111
701
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
1112
702
|
const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
|
|
1113
703
|
return raw.length > 0;
|
|
@@ -1155,9 +745,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1155
745
|
.then((result) => {
|
|
1156
746
|
if (cancelled)
|
|
1157
747
|
return;
|
|
1158
|
-
if (result.type === "success" &&
|
|
1159
|
-
result.data &&
|
|
1160
|
-
result.data.length > 0) {
|
|
748
|
+
if (result.type === "success" && result.data && result.data.length > 0) {
|
|
1161
749
|
setHasHistoryContent(true);
|
|
1162
750
|
}
|
|
1163
751
|
else {
|
|
@@ -1188,38 +776,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1188
776
|
});
|
|
1189
777
|
return () => unsubscribe();
|
|
1190
778
|
}, [agent?.id, editContext?.addSocketMessageListener]);
|
|
1191
|
-
useEffect(() => {
|
|
1192
|
-
let active = true;
|
|
1193
|
-
const loadSkills = async () => {
|
|
1194
|
-
try {
|
|
1195
|
-
setSkillsLoading(true);
|
|
1196
|
-
setSkillsError(null);
|
|
1197
|
-
const catalog = await getAgentSkillCatalog(false);
|
|
1198
|
-
if (active) {
|
|
1199
|
-
setAvailableSkills(catalog.skills.filter((s) => !s.disabled));
|
|
1200
|
-
setSkillRootIds(catalog.rootIds);
|
|
1201
|
-
setSelectableTemplateIds(catalog.selectableTemplateIds);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
catch (e) {
|
|
1205
|
-
if (active) {
|
|
1206
|
-
setSkillsError(e?.message || "Failed to load skills");
|
|
1207
|
-
setAvailableSkills([]);
|
|
1208
|
-
setSkillRootIds([]);
|
|
1209
|
-
setSelectableTemplateIds([]);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
finally {
|
|
1213
|
-
if (active) {
|
|
1214
|
-
setSkillsLoading(false);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
};
|
|
1218
|
-
void loadSkills();
|
|
1219
|
-
return () => {
|
|
1220
|
-
active = false;
|
|
1221
|
-
};
|
|
1222
|
-
}, []);
|
|
1223
779
|
const modeOptions = useMemo(() => [
|
|
1224
780
|
{
|
|
1225
781
|
value: "supervised",
|
|
@@ -1254,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1254
810
|
value: m.id,
|
|
1255
811
|
label: m.name,
|
|
1256
812
|
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1257
|
-
const metadataSelectedSkillIds = useMemo(() => {
|
|
1258
|
-
const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
|
|
1259
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1260
|
-
return [];
|
|
1261
|
-
}
|
|
1262
|
-
return rawSkillIds
|
|
1263
|
-
.map((x) => String(x || "").trim())
|
|
1264
|
-
.filter((x) => x.length > 0);
|
|
1265
|
-
}, [agentMetadata]);
|
|
1266
|
-
const backendAssignedSkillIds = useMemo(() => {
|
|
1267
|
-
const rawSkillIds = agent?.assignedSkillIds ?? [];
|
|
1268
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1269
|
-
return [];
|
|
1270
|
-
}
|
|
1271
|
-
return rawSkillIds
|
|
1272
|
-
.map((x) => String(x || "").trim())
|
|
1273
|
-
.filter((x) => x.length > 0);
|
|
1274
|
-
}, [agent]);
|
|
1275
|
-
const preloadedSkillIds = useMemo(() => {
|
|
1276
|
-
const rawSkillIds = activeProfile?.preloadSkills ?? [];
|
|
1277
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1278
|
-
return [];
|
|
1279
|
-
}
|
|
1280
|
-
return rawSkillIds
|
|
1281
|
-
.map((skill) => String(skill?.id || "").trim())
|
|
1282
|
-
.filter((id) => id.length > 0);
|
|
1283
|
-
}, [activeProfile?.preloadSkills]);
|
|
1284
|
-
const autoAssignedSkillIds = useMemo(() => {
|
|
1285
|
-
const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
|
|
1286
|
-
const all = isLocalOnlyDraftAgent
|
|
1287
|
-
? preloadedSkillIds
|
|
1288
|
-
: backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
|
|
1289
|
-
const seen = new Set();
|
|
1290
|
-
const unique = [];
|
|
1291
|
-
for (const id of all) {
|
|
1292
|
-
const key = id.toLowerCase();
|
|
1293
|
-
if (seen.has(key))
|
|
1294
|
-
continue;
|
|
1295
|
-
seen.add(key);
|
|
1296
|
-
unique.push(id);
|
|
1297
|
-
}
|
|
1298
|
-
return unique;
|
|
1299
|
-
}, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
|
|
1300
|
-
const selectedSkillIds = useMemo(() => {
|
|
1301
|
-
const all = [
|
|
1302
|
-
...autoAssignedSkillIds,
|
|
1303
|
-
...backendAssignedSkillIds,
|
|
1304
|
-
...metadataSelectedSkillIds,
|
|
1305
|
-
];
|
|
1306
|
-
const seen = new Set();
|
|
1307
|
-
const unique = [];
|
|
1308
|
-
for (const id of all) {
|
|
1309
|
-
const key = id.toLowerCase();
|
|
1310
|
-
if (seen.has(key))
|
|
1311
|
-
continue;
|
|
1312
|
-
seen.add(key);
|
|
1313
|
-
unique.push(id);
|
|
1314
|
-
}
|
|
1315
|
-
return unique;
|
|
1316
|
-
}, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1317
|
-
useEffect(() => {
|
|
1318
|
-
let active = true;
|
|
1319
|
-
if (!showAgentSettings) {
|
|
1320
|
-
return () => {
|
|
1321
|
-
active = false;
|
|
1322
|
-
};
|
|
1323
|
-
}
|
|
1324
|
-
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
1325
|
-
setTriggerSubscriptions([]);
|
|
1326
|
-
setTriggerSubscriptionsLoading(false);
|
|
1327
|
-
setTriggerSubscriptionsError(null);
|
|
1328
|
-
setAvailableTools([]);
|
|
1329
|
-
setAvailableToolsLoading(false);
|
|
1330
|
-
setAvailableToolsError(null);
|
|
1331
|
-
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1332
|
-
setOperationAllowancesLoading(false);
|
|
1333
|
-
setOperationAllowancesError(null);
|
|
1334
|
-
return () => {
|
|
1335
|
-
active = false;
|
|
1336
|
-
};
|
|
1337
|
-
}
|
|
1338
|
-
const loadTriggerSubscriptions = async () => {
|
|
1339
|
-
try {
|
|
1340
|
-
setTriggerSubscriptionsLoading(true);
|
|
1341
|
-
setTriggerSubscriptionsError(null);
|
|
1342
|
-
const subscriptions = await getAgentTriggerSubscriptions(agent.id);
|
|
1343
|
-
if (active) {
|
|
1344
|
-
setTriggerSubscriptions(subscriptions);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
catch (e) {
|
|
1348
|
-
if (active) {
|
|
1349
|
-
setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
finally {
|
|
1353
|
-
if (active) {
|
|
1354
|
-
setTriggerSubscriptionsLoading(false);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
};
|
|
1358
|
-
const loadOperationAllowances = async () => {
|
|
1359
|
-
try {
|
|
1360
|
-
setOperationAllowancesLoading(true);
|
|
1361
|
-
setOperationAllowancesError(null);
|
|
1362
|
-
const allowances = await getAgentOperationAllowances(agent.id);
|
|
1363
|
-
if (active) {
|
|
1364
|
-
setOperationAllowances(allowances);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
catch (e) {
|
|
1368
|
-
if (active) {
|
|
1369
|
-
setOperationAllowancesError(e?.message || "Failed to load allowances");
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
finally {
|
|
1373
|
-
if (active) {
|
|
1374
|
-
setOperationAllowancesLoading(false);
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
};
|
|
1378
|
-
const loadAvailableTools = async () => {
|
|
1379
|
-
try {
|
|
1380
|
-
setAvailableToolsLoading(true);
|
|
1381
|
-
setAvailableToolsError(null);
|
|
1382
|
-
const tools = await getAgentAvailableTools(agent.id);
|
|
1383
|
-
if (active) {
|
|
1384
|
-
setAvailableTools(tools);
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
catch (e) {
|
|
1388
|
-
if (active) {
|
|
1389
|
-
setAvailableToolsError(e?.message || "Failed to load available tools");
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
finally {
|
|
1393
|
-
if (active) {
|
|
1394
|
-
setAvailableToolsLoading(false);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
};
|
|
1398
|
-
void loadTriggerSubscriptions();
|
|
1399
|
-
void loadAvailableTools();
|
|
1400
|
-
void loadOperationAllowances();
|
|
1401
|
-
return () => {
|
|
1402
|
-
active = false;
|
|
1403
|
-
};
|
|
1404
|
-
}, [
|
|
1405
|
-
showAgentSettings,
|
|
1406
|
-
agent?.id,
|
|
1407
|
-
agent?.status,
|
|
1408
|
-
agent?.userId,
|
|
1409
|
-
agent?.profileId,
|
|
1410
|
-
mode,
|
|
1411
|
-
selectedSkillIds,
|
|
1412
|
-
]);
|
|
1413
|
-
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
1414
|
-
const allowanceGroups = useMemo(() => [
|
|
1415
|
-
{
|
|
1416
|
-
key: "sitecore",
|
|
1417
|
-
label: "Sitecore",
|
|
1418
|
-
rows: operationAllowances.sitecore,
|
|
1419
|
-
},
|
|
1420
|
-
{
|
|
1421
|
-
key: "filesystem",
|
|
1422
|
-
label: "Filesystem",
|
|
1423
|
-
rows: operationAllowances.filesystem,
|
|
1424
|
-
},
|
|
1425
|
-
], [operationAllowances]);
|
|
1426
|
-
const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
|
|
1427
|
-
operationAllowances.filesystem.length > 0, [operationAllowances]);
|
|
1428
|
-
const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
|
|
1429
|
-
operationAllowances.filesystem.length, [operationAllowances]);
|
|
1430
|
-
const listedProfileSkillIdSet = useMemo(() => {
|
|
1431
|
-
const ids = [
|
|
1432
|
-
...(activeProfile?.availableSkills ?? []),
|
|
1433
|
-
...(activeProfile?.allowedSkills ?? []),
|
|
1434
|
-
]
|
|
1435
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1436
|
-
.filter((id) => id.length > 0);
|
|
1437
|
-
return new Set(ids);
|
|
1438
|
-
}, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
|
|
1439
|
-
const profileFilteredSkills = useMemo(() => {
|
|
1440
|
-
if (listedProfileSkillIdSet.size === 0) {
|
|
1441
|
-
return [];
|
|
1442
|
-
}
|
|
1443
|
-
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1444
|
-
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1445
|
-
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1446
|
-
const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
|
|
1447
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1448
|
-
.filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
|
|
1449
|
-
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
1450
|
-
const selectedSkills = useMemo(() => selectedSkillIds
|
|
1451
|
-
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
1452
|
-
.filter((s) => !!s), [availableSkills, selectedSkillIds]);
|
|
1453
|
-
const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
|
|
1454
|
-
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1455
|
-
const previewAvailableTools = useMemo(() => {
|
|
1456
|
-
const baseToolNames = mode === "read-only"
|
|
1457
|
-
? (activeProfile?.readOnlyToolNames ?? [])
|
|
1458
|
-
: (activeProfile?.allowedToolNames ?? []);
|
|
1459
|
-
const toolNames = new Set();
|
|
1460
|
-
for (const toolName of baseToolNames) {
|
|
1461
|
-
const normalized = String(toolName || "").trim();
|
|
1462
|
-
if (normalized) {
|
|
1463
|
-
toolNames.add(normalized);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
for (const skill of selectedSkills) {
|
|
1467
|
-
for (const toolName of skill.additionalAllowedTools ?? []) {
|
|
1468
|
-
const normalized = String(toolName || "").trim();
|
|
1469
|
-
if (normalized) {
|
|
1470
|
-
toolNames.add(normalized);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
|
|
1475
|
-
}, [
|
|
1476
|
-
activeProfile?.allowedToolNames,
|
|
1477
|
-
activeProfile?.readOnlyToolNames,
|
|
1478
|
-
mode,
|
|
1479
|
-
selectedSkills,
|
|
1480
|
-
]);
|
|
1481
|
-
const displayedAvailableTools = isLocalOnlyDraftAgent
|
|
1482
|
-
? previewAvailableTools
|
|
1483
|
-
: availableTools;
|
|
1484
|
-
const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
|
|
1485
|
-
? false
|
|
1486
|
-
: availableToolsLoading;
|
|
1487
|
-
const displayedAvailableToolsError = isLocalOnlyDraftAgent
|
|
1488
|
-
? null
|
|
1489
|
-
: availableToolsError;
|
|
1490
813
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1491
814
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1492
815
|
try {
|
|
@@ -1511,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1511
834
|
return meta;
|
|
1512
835
|
}
|
|
1513
836
|
}, []);
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
1528
|
-
const current = agentMetadata || {};
|
|
1529
|
-
const currentAdditionalData = current.additionalData ||
|
|
1530
|
-
{};
|
|
1531
|
-
const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
|
|
1532
|
-
const next = {
|
|
1533
|
-
...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,
|
|
1534
849
|
};
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
}
|
|
1541
|
-
try {
|
|
1542
|
-
await updateAgentContext(agent.id, next);
|
|
1543
|
-
setAgentMetadata(next);
|
|
1544
|
-
setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
|
|
1545
|
-
}
|
|
1546
|
-
catch (e) {
|
|
1547
|
-
console.error("Failed to clear legacy selected skills", e);
|
|
1548
|
-
}
|
|
1549
|
-
}, [
|
|
1550
|
-
agent?.id,
|
|
1551
|
-
agentMetadata,
|
|
1552
|
-
metadataSelectedSkillIds,
|
|
1553
|
-
setAgent,
|
|
1554
|
-
setAgentMetadata,
|
|
1555
|
-
]);
|
|
1556
|
-
const parsePersistedAgentContext = (contextJson) => {
|
|
1557
|
-
try {
|
|
1558
|
-
if (!contextJson)
|
|
1559
|
-
return null;
|
|
1560
|
-
const parsedContext = JSON.parse(contextJson);
|
|
1561
|
-
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1562
|
-
return null;
|
|
1563
|
-
}
|
|
1564
|
-
return sanitizeAgentMetadata(parsedContext);
|
|
1565
|
-
}
|
|
1566
|
-
catch {
|
|
1567
|
-
return null;
|
|
1568
|
-
}
|
|
1569
|
-
};
|
|
1570
|
-
const buildDraftPersistenceContext = () => {
|
|
1571
|
-
let effectiveContext = agentMetadata;
|
|
1572
|
-
try {
|
|
1573
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
1574
|
-
const profile = activeProfile ||
|
|
1575
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
1576
|
-
const isLiveMode = profile?.editorContextMode === "live";
|
|
1577
|
-
if (isLiveMode && typeof buildCurrentContext === "function") {
|
|
1578
|
-
const freshContext = buildCurrentContext();
|
|
1579
|
-
if (freshContext) {
|
|
1580
|
-
effectiveContext = {
|
|
1581
|
-
...(agentMetadata || {}),
|
|
1582
|
-
items: freshContext.items,
|
|
1583
|
-
currentItemId: freshContext.currentItemId,
|
|
1584
|
-
components: freshContext.components,
|
|
1585
|
-
field: freshContext.field,
|
|
1586
|
-
activeWorkspace: freshContext.activeWorkspace,
|
|
1587
|
-
hasPageLoaded: freshContext.hasPageLoaded,
|
|
1588
|
-
openSidebars: freshContext.openSidebars,
|
|
1589
|
-
};
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
catch (e) {
|
|
1594
|
-
console.warn("[AgentTerminal] Failed to compute draft context:", e);
|
|
1595
|
-
}
|
|
1596
|
-
return sanitizeAgentMetadata(effectiveContext || null);
|
|
1597
|
-
};
|
|
1598
|
-
const ensureDraftAgentPersisted = async () => {
|
|
1599
|
-
if (!agent?.id) {
|
|
1600
|
-
throw new Error("Agent not ready. Please try again.");
|
|
1601
|
-
}
|
|
1602
|
-
if (!isLocalOnlyDraftAgent) {
|
|
1603
|
-
return agent;
|
|
1604
|
-
}
|
|
1605
|
-
const requestSettings = getPendingRequestSettings();
|
|
1606
|
-
if (!requestSettings.profileId) {
|
|
1607
|
-
throw new Error("Select an agent profile before adding a skill.");
|
|
1608
|
-
}
|
|
1609
|
-
const effectiveContext = buildDraftPersistenceContext();
|
|
1610
|
-
const persistedAgent = await persistDraftAgent({
|
|
1611
|
-
agentId: agent.id,
|
|
1612
|
-
sessionId: editContext?.sessionId || undefined,
|
|
1613
|
-
profileId: requestSettings.profileId,
|
|
1614
|
-
mode: requestSettings.mode,
|
|
1615
|
-
model: requestSettings.modelId || undefined,
|
|
1616
|
-
name: agent.name,
|
|
1617
|
-
context: effectiveContext,
|
|
1618
|
-
});
|
|
1619
|
-
pendingSettingsRef.current = null;
|
|
1620
|
-
const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
|
|
1621
|
-
effectiveContext;
|
|
1622
|
-
setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
|
|
1623
|
-
setAgent((prev) => {
|
|
1624
|
-
const next = {
|
|
1625
|
-
...(prev || {}),
|
|
1626
|
-
...persistedAgent,
|
|
1627
|
-
};
|
|
1628
|
-
if (prev?.messages?.length && !persistedAgent.messages?.length) {
|
|
1629
|
-
next.messages = prev.messages;
|
|
1630
|
-
}
|
|
1631
|
-
return next;
|
|
1632
|
-
});
|
|
1633
|
-
onAgentUpdate?.(persistedAgent);
|
|
1634
|
-
return persistedAgent;
|
|
1635
|
-
};
|
|
1636
|
-
const handleAddSkill = useCallback(async (skillId) => {
|
|
1637
|
-
if (selectedSkillSet.has(skillId.toLowerCase()))
|
|
1638
|
-
return;
|
|
1639
|
-
if (!agent?.id)
|
|
1640
|
-
return;
|
|
1641
|
-
try {
|
|
1642
|
-
setSkillActionError(null);
|
|
1643
|
-
const persistedAgent = await ensureDraftAgentPersisted();
|
|
1644
|
-
await assignAgentSkill({ agentId: persistedAgent.id, skillId });
|
|
1645
|
-
setAgent((prev) => {
|
|
1646
|
-
if (!prev)
|
|
1647
|
-
return prev;
|
|
1648
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1649
|
-
? prev.assignedSkillIds
|
|
1650
|
-
: [];
|
|
1651
|
-
if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
|
|
1652
|
-
return prev;
|
|
1653
|
-
}
|
|
1654
|
-
return {
|
|
1655
|
-
...prev,
|
|
1656
|
-
assignedSkillIds: [...currentAssigned, skillId],
|
|
1657
|
-
};
|
|
1658
|
-
});
|
|
1659
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1660
|
-
return true;
|
|
1661
|
-
}
|
|
1662
|
-
catch (e) {
|
|
1663
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1664
|
-
return false;
|
|
1665
|
-
}
|
|
1666
|
-
}, [
|
|
1667
|
-
agent?.id,
|
|
1668
|
-
assignAgentSkill,
|
|
1669
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1670
|
-
ensureDraftAgentPersisted,
|
|
1671
|
-
getSkillActionErrorMessage,
|
|
1672
|
-
setAgent,
|
|
1673
|
-
selectedSkillSet,
|
|
1674
|
-
]);
|
|
1675
|
-
const handleRemoveSkill = useCallback(async (skillId) => {
|
|
1676
|
-
if (!agent?.id)
|
|
1677
|
-
return;
|
|
1678
|
-
try {
|
|
1679
|
-
setSkillActionError(null);
|
|
1680
|
-
await revokeAgentSkill({ agentId: agent.id, skillId });
|
|
1681
|
-
setAgent((prev) => {
|
|
1682
|
-
if (!prev)
|
|
1683
|
-
return prev;
|
|
1684
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1685
|
-
? prev.assignedSkillIds
|
|
1686
|
-
: [];
|
|
1687
|
-
return {
|
|
1688
|
-
...prev,
|
|
1689
|
-
assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
|
|
1690
|
-
};
|
|
1691
|
-
});
|
|
1692
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1693
|
-
}
|
|
1694
|
-
catch (e) {
|
|
1695
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1696
|
-
}
|
|
1697
|
-
}, [
|
|
1698
|
-
agent?.id,
|
|
1699
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1700
|
-
getSkillActionErrorMessage,
|
|
1701
|
-
revokeAgentSkill,
|
|
1702
|
-
setAgent,
|
|
1703
|
-
]);
|
|
1704
|
-
const handleOpenProfileSettings = useCallback(async () => {
|
|
1705
|
-
if (!editContext || !activeProfile?.id)
|
|
1706
|
-
return;
|
|
1707
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1708
|
-
await editContext.loadItem({
|
|
1709
|
-
id: activeProfile.id,
|
|
1710
|
-
language: lang,
|
|
1711
|
-
version: 0,
|
|
1712
|
-
});
|
|
1713
|
-
editContext.switchWorkspace("editor");
|
|
1714
|
-
}, [editContext, activeProfile?.id]);
|
|
1715
|
-
const handleEditProfileSideBySide = useCallback(async () => {
|
|
1716
|
-
if (!editContext || !activeProfile?.id)
|
|
1717
|
-
return;
|
|
1718
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1719
|
-
const profileItem = {
|
|
1720
|
-
id: activeProfile.id,
|
|
1721
|
-
language: lang,
|
|
1722
|
-
version: 0,
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
deterministicFlags = {
|
|
853
|
+
deterministic: false,
|
|
854
|
+
seed: undefined,
|
|
1723
855
|
};
|
|
1724
|
-
|
|
1725
|
-
editContext.setShowAgentsWorkspaceEditor(true);
|
|
1726
|
-
await editContext.loadItem(profileItem);
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
await editContext.loadItem(profileItem, {
|
|
1730
|
-
openInNewSlot: true,
|
|
1731
|
-
});
|
|
1732
|
-
editContext.switchWorkspace("editor");
|
|
1733
|
-
}, [editContext, activeProfile?.id]);
|
|
1734
|
-
const handleOpenSkillItem = useCallback(async (skillId) => {
|
|
1735
|
-
if (!editContext || !skillId)
|
|
1736
|
-
return;
|
|
1737
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1738
|
-
await editContext.loadItem({
|
|
1739
|
-
id: skillId,
|
|
1740
|
-
language: lang,
|
|
1741
|
-
version: 0,
|
|
1742
|
-
});
|
|
1743
|
-
editContext.switchWorkspace("editor");
|
|
1744
|
-
}, [editContext]);
|
|
856
|
+
}
|
|
1745
857
|
useEffect(() => {
|
|
1746
|
-
|
|
858
|
+
localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
|
|
1747
859
|
}, [promptHistory]);
|
|
1748
860
|
useEffect(() => {
|
|
1749
861
|
// Keep messagesRef synchronized with messages state
|
|
@@ -1755,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1755
867
|
const [liveTotals, setLiveTotals] = useState(null);
|
|
1756
868
|
// Context window status from backend (extracted from delta cost object)
|
|
1757
869
|
const [contextWindowStatus, setContextWindowStatus] = useState(null);
|
|
1758
|
-
const effectiveModelName = useMemo(() => {
|
|
1759
|
-
const fromContextWindow = contextWindowStatus?.model?.trim();
|
|
1760
|
-
if (fromContextWindow)
|
|
1761
|
-
return fromContextWindow;
|
|
1762
|
-
const fromAgent = agent?.model?.trim();
|
|
1763
|
-
if (fromAgent)
|
|
1764
|
-
return fromAgent;
|
|
1765
|
-
const models = activeProfile?.models || [];
|
|
1766
|
-
const selectedModelName = selectedModelId
|
|
1767
|
-
? models.find((m) => m.id === selectedModelId)?.name?.trim()
|
|
1768
|
-
: undefined;
|
|
1769
|
-
if (selectedModelName)
|
|
1770
|
-
return selectedModelName;
|
|
1771
|
-
const defaultModelName = activeProfile?.defaultModelId
|
|
1772
|
-
? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
|
|
1773
|
-
: undefined;
|
|
1774
|
-
if (defaultModelName)
|
|
1775
|
-
return defaultModelName;
|
|
1776
|
-
return models[0]?.name?.trim() || undefined;
|
|
1777
|
-
}, [
|
|
1778
|
-
contextWindowStatus?.model,
|
|
1779
|
-
agent?.model,
|
|
1780
|
-
activeProfile?.models,
|
|
1781
|
-
activeProfile?.defaultModelId,
|
|
1782
|
-
selectedModelId,
|
|
1783
|
-
]);
|
|
1784
870
|
// Flag to track when we should create a new message
|
|
1785
871
|
const shouldCreateNewMessage = useRef(false);
|
|
1786
872
|
// Keep a ref to the current messages for immediate access
|
|
@@ -1791,16 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1791
877
|
const placeholderInputRef = useRef(null);
|
|
1792
878
|
const promptPlaceholderInputRef = useRef(null);
|
|
1793
879
|
const messagesContainerRef = useRef(null);
|
|
1794
|
-
const inlineDialogContainerRef = useRef(null);
|
|
1795
880
|
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
1796
|
-
const [recentToolUiEvents, setRecentToolUiEvents] = useState([]);
|
|
1797
881
|
// WebSocket subscription state for agent streaming
|
|
1798
882
|
const seenMessageIdsRef = useRef(new Set());
|
|
1799
883
|
const lastSeqRef = useRef(0);
|
|
1800
884
|
const subscribedAgentIdRef = useRef(null);
|
|
1801
|
-
|
|
1802
|
-
const pendingToolCompletionTimersRef = useRef({});
|
|
1803
|
-
// Cache mode/model/profile changes made while the agent is still "new" (not yet persisted)
|
|
885
|
+
// Cache mode/model changes made while the agent is still "new" (not yet persisted)
|
|
1804
886
|
const pendingSettingsRef = useRef(null);
|
|
1805
887
|
// Track whether textarea should maintain focus during re-renders
|
|
1806
888
|
const shouldMaintainFocusRef = useRef(false);
|
|
@@ -1822,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1822
904
|
setIsVoiceSupported(!!SR);
|
|
1823
905
|
if (SR === undefined) {
|
|
1824
906
|
setIsVoiceDisabled(true);
|
|
1825
|
-
|
|
907
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1826
908
|
return;
|
|
1827
909
|
}
|
|
1828
910
|
else {
|
|
1829
|
-
const wasDisabled =
|
|
911
|
+
const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
|
|
1830
912
|
setIsVoiceDisabled(wasDisabled);
|
|
1831
913
|
}
|
|
1832
914
|
}
|
|
@@ -1837,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1837
919
|
// Auto-focus terminal input on mount
|
|
1838
920
|
useEffect(() => {
|
|
1839
921
|
if (textareaRef.current) {
|
|
1840
|
-
|
|
1841
|
-
textareaRef.current.focus({ preventScroll: true });
|
|
1842
|
-
}
|
|
1843
|
-
catch {
|
|
1844
|
-
textareaRef.current.focus();
|
|
1845
|
-
}
|
|
922
|
+
textareaRef.current.focus();
|
|
1846
923
|
}
|
|
1847
924
|
}, []);
|
|
1848
925
|
// Start voice recognition
|
|
@@ -1861,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1861
938
|
r.lang = editContext?.currentItemDescriptor?.language || "en-US";
|
|
1862
939
|
r.continuous = true;
|
|
1863
940
|
r.interimResults = true;
|
|
1864
|
-
promptBeforeVoiceRef.current = prompt;
|
|
1865
941
|
r.onstart = () => {
|
|
1866
942
|
setIsListening(true);
|
|
1867
943
|
prevPlaceholderRef.current = inputPlaceholder;
|
|
1868
944
|
setInputPlaceholder("Listening...");
|
|
1869
945
|
};
|
|
1870
|
-
// Desktop Chrome: single result at index 0, transitions interim→final once.
|
|
1871
|
-
// Mobile Chrome: new result index per event, ALL immediately isFinal,
|
|
1872
|
-
// each containing the full cumulative transcript. We always take the last
|
|
1873
|
-
// non-empty final result and REPLACE the prompt to handle both patterns.
|
|
1874
946
|
r.onresult = (event) => {
|
|
1875
|
-
let
|
|
947
|
+
let finalText = "";
|
|
1876
948
|
let interimText = "";
|
|
1877
|
-
for (let i = event.
|
|
949
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
1878
950
|
const res = event.results[i];
|
|
1879
|
-
if (res.isFinal
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
}
|
|
1884
|
-
if (!res.isFinal) {
|
|
1885
|
-
interimText = (res[0]?.transcript || "") + interimText;
|
|
1886
|
-
}
|
|
951
|
+
if (res.isFinal)
|
|
952
|
+
finalText += res[0]?.transcript || "";
|
|
953
|
+
else
|
|
954
|
+
interimText += res[0]?.transcript || "";
|
|
1887
955
|
}
|
|
1888
956
|
if (interimText) {
|
|
1889
957
|
setInputPlaceholder(`Listening... ${interimText.trim()}`);
|
|
@@ -1891,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1891
959
|
else {
|
|
1892
960
|
setInputPlaceholder("Listening...");
|
|
1893
961
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
962
|
+
if (finalText && finalText.trim()) {
|
|
963
|
+
setPrompt((prev) => {
|
|
964
|
+
const prefix = prev && !prev.endsWith(" ") ? prev + " " : prev;
|
|
965
|
+
return (prefix || "") + finalText.trim() + " ";
|
|
966
|
+
});
|
|
1899
967
|
if (textareaRef.current) {
|
|
1900
968
|
try {
|
|
1901
969
|
const v = textareaRef.current.value || "";
|
|
@@ -1911,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1911
979
|
// network error also comes from incompatible chromium client
|
|
1912
980
|
if (e?.error === "network") {
|
|
1913
981
|
try {
|
|
1914
|
-
|
|
982
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1915
983
|
setIsVoiceDisabled(true);
|
|
1916
984
|
}
|
|
1917
985
|
catch { }
|
|
@@ -2080,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2080
1148
|
// Shared stream message handlers with messageId support
|
|
2081
1149
|
const createNewStreamMessage = useCallback((messageId, agentData) => {
|
|
2082
1150
|
const currentAgent = agentData || agent;
|
|
2083
|
-
|
|
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
|
+
}
|
|
2084
1159
|
// Reduced: avoid verbose logging during streaming
|
|
2085
1160
|
return {
|
|
2086
1161
|
id: messageId,
|
|
2087
|
-
agentId:
|
|
1162
|
+
agentId: currentAgent.id,
|
|
2088
1163
|
messageIndex: -1,
|
|
2089
1164
|
role: "assistant",
|
|
2090
1165
|
content: "",
|
|
2091
1166
|
name: "agent",
|
|
2092
1167
|
messageType: "streaming",
|
|
2093
1168
|
isCompleted: false,
|
|
2094
|
-
model: currentAgent
|
|
1169
|
+
model: currentAgent.model || "",
|
|
2095
1170
|
tokensUsed: 0,
|
|
2096
1171
|
inputTokens: 0,
|
|
2097
1172
|
outputTokens: 0,
|
|
@@ -2100,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2100
1175
|
outputTokenCost: 0,
|
|
2101
1176
|
cachedInputTokenCost: 0,
|
|
2102
1177
|
totalCost: 0,
|
|
2103
|
-
currency: currentAgent
|
|
1178
|
+
currency: currentAgent.currency || "USD",
|
|
2104
1179
|
createdDate: new Date().toISOString(),
|
|
2105
1180
|
toolCalls: [],
|
|
2106
1181
|
};
|
|
2107
|
-
}, [agent
|
|
2108
|
-
const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
|
|
2109
|
-
const handleHeartbeatMessage = useCallback((message) => {
|
|
2110
|
-
const heartbeatText = (message.data?.message ||
|
|
2111
|
-
message.data?.Message ||
|
|
2112
|
-
"Still working on your request. This is taking a little longer than usual.").trim() ||
|
|
2113
|
-
"Still working on your request. This is taking a little longer than usual.";
|
|
2114
|
-
const createdDate = message.timestamp || new Date().toISOString();
|
|
2115
|
-
const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
|
|
2116
|
-
setMessages((prev) => {
|
|
2117
|
-
const withoutHeartbeats = stripHeartbeatMessages(prev);
|
|
2118
|
-
const updated = [
|
|
2119
|
-
...withoutHeartbeats,
|
|
2120
|
-
{
|
|
2121
|
-
id: heartbeatId,
|
|
2122
|
-
agentId: agent?.id || agentStub.id,
|
|
2123
|
-
messageIndex: -1,
|
|
2124
|
-
role: "system",
|
|
2125
|
-
content: heartbeatText,
|
|
2126
|
-
name: "system",
|
|
2127
|
-
messageType: "heartbeat",
|
|
2128
|
-
isCompleted: true,
|
|
2129
|
-
model: "",
|
|
2130
|
-
tokensUsed: 0,
|
|
2131
|
-
inputTokens: 0,
|
|
2132
|
-
outputTokens: 0,
|
|
2133
|
-
cachedInputTokens: 0,
|
|
2134
|
-
inputTokenCost: 0,
|
|
2135
|
-
outputTokenCost: 0,
|
|
2136
|
-
cachedInputTokenCost: 0,
|
|
2137
|
-
totalCost: 0,
|
|
2138
|
-
currency: "USD",
|
|
2139
|
-
createdDate,
|
|
2140
|
-
toolCalls: [],
|
|
2141
|
-
},
|
|
2142
|
-
];
|
|
2143
|
-
messagesRef.current = updated;
|
|
2144
|
-
return updated;
|
|
2145
|
-
});
|
|
2146
|
-
}, [agent?.id, agentStub.id, stripHeartbeatMessages]);
|
|
2147
|
-
const clearHeartbeatMessages = useCallback(() => {
|
|
2148
|
-
setMessages((prev) => {
|
|
2149
|
-
const updated = stripHeartbeatMessages(prev);
|
|
2150
|
-
if (updated.length === prev.length) {
|
|
2151
|
-
return prev;
|
|
2152
|
-
}
|
|
2153
|
-
messagesRef.current = updated;
|
|
2154
|
-
return updated;
|
|
2155
|
-
});
|
|
2156
|
-
}, [stripHeartbeatMessages]);
|
|
1182
|
+
}, [agent]);
|
|
2157
1183
|
const handleContentChunk = useCallback((message, agentData) => {
|
|
2158
1184
|
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
2159
1185
|
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
2160
1186
|
let messageId = message.data?.messageId;
|
|
2161
1187
|
if (!messageId && agentData?.id) {
|
|
2162
|
-
console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
|
|
2163
|
-
agentId: agentData.id,
|
|
2164
|
-
isIncremental: message.data?.isIncremental,
|
|
2165
|
-
previousContentLength: message.data?.previousContentLength,
|
|
2166
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2167
|
-
});
|
|
2168
1188
|
// For backward compatibility: if no messageId, find or create the current streaming message
|
|
2169
1189
|
// This handles cases where the backend doesn't send messageId
|
|
2170
1190
|
const currentMessages = messagesRef.current;
|
|
@@ -2178,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2178
1198
|
// If the agent isn't currently running (e.g., we switched tabs after the run
|
|
2179
1199
|
// completed), skip creating a new streaming message to avoid duplicates.
|
|
2180
1200
|
const currentAgentStatus = (agentData || agent)?.status;
|
|
2181
|
-
const isAgentRunning = currentAgentStatus === "running";
|
|
1201
|
+
const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
|
|
2182
1202
|
if (!isAgentRunning) {
|
|
2183
1203
|
return;
|
|
2184
1204
|
}
|
|
@@ -2211,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2211
1231
|
outputCost: Number(cost.output) || 0,
|
|
2212
1232
|
cachedCost: Number(cost.cached) || 0,
|
|
2213
1233
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2214
|
-
imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
|
|
2215
|
-
0,
|
|
2216
1234
|
totalCost: Number(cost.total) || 0,
|
|
2217
1235
|
currency: "USD",
|
|
2218
1236
|
};
|
|
@@ -2236,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2236
1254
|
setIsWaitingForResponse(false);
|
|
2237
1255
|
shouldCreateNewMessage.current = false;
|
|
2238
1256
|
}
|
|
2239
|
-
// Extract context window info from cost object
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
if (contextWindowRaw !== undefined &&
|
|
2245
|
-
contextWindowRaw !== null &&
|
|
2246
|
-
contextUsedRaw !== undefined &&
|
|
2247
|
-
contextUsedRaw !== null) {
|
|
2248
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2249
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2250
|
-
if (contextWindowValue > 0 &&
|
|
2251
|
-
Number.isFinite(contextUsedValue) &&
|
|
2252
|
-
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) {
|
|
2253
1262
|
setContextWindowStatus({
|
|
2254
1263
|
contextWindowTokens: contextWindowValue,
|
|
2255
1264
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2265,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2265
1274
|
const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
|
|
2266
1275
|
if (existingMessageIndex === -1) {
|
|
2267
1276
|
// Message doesn't exist - create new streaming message
|
|
2268
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2269
|
-
if (message.data?.isIncremental && previousContentLength > 0) {
|
|
2270
|
-
console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
|
|
2271
|
-
messageId,
|
|
2272
|
-
previousContentLength,
|
|
2273
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2274
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2275
|
-
});
|
|
2276
|
-
}
|
|
2277
1277
|
const newStreamMessage = createNewStreamMessage(messageId, agentData);
|
|
2278
1278
|
// Set the content for the new message
|
|
2279
1279
|
const updatedNewMessage = { ...newStreamMessage };
|
|
@@ -2296,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2296
1296
|
return prev;
|
|
2297
1297
|
// Check if existing content is already longer than what we're trying to stream
|
|
2298
1298
|
const currentContentLength = existingMessage.content?.length || 0;
|
|
2299
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2300
1299
|
const totalContentLength = message.data?.totalContentLength || 0;
|
|
2301
|
-
if (
|
|
2302
|
-
previousContentLength !== currentContentLength &&
|
|
2303
|
-
(previousContentLength > 0 || currentContentLength > 0)) {
|
|
2304
|
-
console.warn("[AgentTerminal] Content chunk length mismatch", {
|
|
2305
|
-
messageId,
|
|
2306
|
-
previousContentLength,
|
|
2307
|
-
currentContentLength,
|
|
2308
|
-
totalContentLength,
|
|
2309
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
if (message.data?.isIncremental &&
|
|
2313
|
-
currentContentLength >= totalContentLength &&
|
|
1300
|
+
if (currentContentLength >= totalContentLength &&
|
|
2314
1301
|
totalContentLength > 0) {
|
|
2315
1302
|
return prev;
|
|
2316
1303
|
}
|
|
@@ -2334,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2334
1321
|
});
|
|
2335
1322
|
}, [createNewStreamMessage, agent]);
|
|
2336
1323
|
const handleToolCall = useCallback((message, agentData) => {
|
|
2337
|
-
const
|
|
2338
|
-
const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1324
|
+
const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2339
1325
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2340
1326
|
let toolCallMessageId = message.data?.messageId;
|
|
2341
1327
|
if (!toolCallMessageId) {
|
|
2342
|
-
console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
|
|
2343
|
-
toolCallId,
|
|
2344
|
-
toolName: message.data?.name || message.data?.displayName,
|
|
2345
|
-
});
|
|
2346
1328
|
const current = messagesRef.current;
|
|
2347
1329
|
const lastStreaming = [...current]
|
|
2348
1330
|
.reverse()
|
|
@@ -2350,13 +1332,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2350
1332
|
if (lastStreaming?.id) {
|
|
2351
1333
|
toolCallMessageId = lastStreaming.id;
|
|
2352
1334
|
}
|
|
2353
|
-
else {
|
|
2354
|
-
// Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
|
|
2355
|
-
// Create a synthetic streaming message so the UI can render the tool call immediately.
|
|
2356
|
-
toolCallMessageId = crypto.randomUUID();
|
|
2357
|
-
}
|
|
2358
1335
|
}
|
|
2359
|
-
appendToolUiEvent("ui:tool-call-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId || "none"} providedMessageId=${String(message.data?.messageId || "none")}`);
|
|
2360
1336
|
// Find or create the target message for this tool call
|
|
2361
1337
|
if (toolCallMessageId) {
|
|
2362
1338
|
const currentMessages = messagesRef.current;
|
|
@@ -2383,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2383
1359
|
}
|
|
2384
1360
|
// Add tool call to the message in the array
|
|
2385
1361
|
if (toolCallMessageId && message.data && toolCallId) {
|
|
2386
|
-
const
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
new Date().toISOString();
|
|
1362
|
+
const functionName = message.data.functionName ||
|
|
1363
|
+
message.data.name ||
|
|
1364
|
+
message.data.function?.name ||
|
|
1365
|
+
"unknown";
|
|
2391
1366
|
const toolCall = {
|
|
2392
1367
|
id: toolCallId,
|
|
2393
1368
|
messageId: toolCallMessageId,
|
|
2394
1369
|
dbMessageId: message.data.messageId, // Database message ID for approval/rejection
|
|
2395
1370
|
toolCallId: toolCallId,
|
|
2396
|
-
functionName:
|
|
2397
|
-
functionArguments:
|
|
1371
|
+
functionName: functionName,
|
|
1372
|
+
functionArguments: message.data.functionArguments ||
|
|
1373
|
+
message.data.arguments ||
|
|
1374
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2398
1375
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2399
|
-
|
|
2400
|
-
functionError: toolCallError,
|
|
2401
|
-
isPruned,
|
|
1376
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
2402
1377
|
isCompleted: false,
|
|
2403
1378
|
responseTimeMs: message.data.responseTimeMs,
|
|
2404
|
-
createdDate:
|
|
1379
|
+
createdDate: new Date().toISOString(),
|
|
2405
1380
|
requiresApproval: message.data?.requiresApproval,
|
|
2406
1381
|
};
|
|
2407
1382
|
// Check for existing tool call - search across ALL messages by toolCallId first
|
|
@@ -2440,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2440
1415
|
// Check if the new data has more information than what we have
|
|
2441
1416
|
const newArgs = toolCall.functionArguments;
|
|
2442
1417
|
const existingArgs = existingToolCall.functionArguments;
|
|
2443
|
-
const
|
|
2444
|
-
const existingArgsText = stringifyToolField(existingArgs) || "";
|
|
2445
|
-
const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
|
|
2446
|
-
newArgsText !== existingArgsText) ||
|
|
2447
|
-
(existingArgsText === "{}" && newArgsText !== "{}");
|
|
1418
|
+
const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
|
|
2448
1419
|
const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
|
|
2449
|
-
const hasNewRichContent = toolCall.functionResultRichContent &&
|
|
2450
|
-
!existingToolCall.functionResultRichContent;
|
|
2451
1420
|
const hasNewError = toolCall.functionError && !existingToolCall.functionError;
|
|
2452
1421
|
const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
|
|
2453
1422
|
const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
|
|
2454
1423
|
// Only update if there's meaningful new data
|
|
2455
1424
|
if (hasMoreCompleteArgs ||
|
|
2456
1425
|
hasNewResult ||
|
|
2457
|
-
hasNewRichContent ||
|
|
2458
1426
|
hasNewError ||
|
|
2459
1427
|
hasNewApprovalInfo ||
|
|
2460
1428
|
hasNewDbMessageId) {
|
|
@@ -2471,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2471
1439
|
updatedToolCalls[idx] = {
|
|
2472
1440
|
...existing,
|
|
2473
1441
|
functionArguments: hasMoreCompleteArgs
|
|
2474
|
-
?
|
|
2475
|
-
:
|
|
1442
|
+
? newArgs
|
|
1443
|
+
: existing.functionArguments,
|
|
2476
1444
|
functionResult: toolCall.functionResult || existing.functionResult,
|
|
2477
|
-
functionResultRichContent: toolCall.functionResultRichContent ||
|
|
2478
|
-
existing.functionResultRichContent,
|
|
2479
1445
|
functionError: toolCall.functionError || existing.functionError,
|
|
2480
1446
|
requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
|
|
2481
1447
|
};
|
|
@@ -2493,36 +1459,27 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2493
1459
|
}
|
|
2494
1460
|
return; // Done updating existing tool call
|
|
2495
1461
|
}
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
|
|
2503
|
-
});
|
|
2504
|
-
messagesRef.current = updated;
|
|
2505
|
-
return updated;
|
|
1462
|
+
setMessages((prev) => {
|
|
1463
|
+
const updated = prev.map((msg) => {
|
|
1464
|
+
if (msg.id !== toolCallMessageId)
|
|
1465
|
+
return msg;
|
|
1466
|
+
const existingToolCalls = msg.toolCalls || [];
|
|
1467
|
+
return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
|
|
2506
1468
|
});
|
|
1469
|
+
messagesRef.current = updated;
|
|
1470
|
+
return updated;
|
|
2507
1471
|
});
|
|
2508
|
-
const messageWithToolCall = messagesRef.current.find((msg) => msg.id === toolCallMessageId);
|
|
2509
|
-
appendToolUiEvent("ui:tool-call-attached", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId} messageToolCalls=${messageWithToolCall?.toolCalls?.length || 0} assistantMessages=${messagesRef.current.filter((msg) => msg.role === "assistant").length}`);
|
|
2510
1472
|
// If tool requires approval, agent is now waiting for user action - stop thinking
|
|
2511
1473
|
if (message.data?.requiresApproval) {
|
|
2512
1474
|
setIsAgentThinking(false);
|
|
2513
1475
|
}
|
|
2514
1476
|
}
|
|
2515
|
-
}, [
|
|
1477
|
+
}, [createNewStreamMessage]);
|
|
2516
1478
|
const handleToolResult = useCallback((message, agentData) => {
|
|
2517
|
-
const
|
|
2518
|
-
const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1479
|
+
const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2519
1480
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2520
1481
|
let resultMessageId = message.data?.messageId;
|
|
2521
1482
|
if (!resultMessageId) {
|
|
2522
|
-
console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
|
|
2523
|
-
toolCallId: resultToolCallId,
|
|
2524
|
-
toolName: message.data?.functionName || message.data?.displayName,
|
|
2525
|
-
});
|
|
2526
1483
|
const current = messagesRef.current;
|
|
2527
1484
|
const lastStreaming = [...current]
|
|
2528
1485
|
.reverse()
|
|
@@ -2544,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2544
1501
|
outputCost: Number(cost.output) || 0,
|
|
2545
1502
|
cachedCost: Number(cost.cached) || 0,
|
|
2546
1503
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2547
|
-
imageCost: Number(cost.imageCost) || 0,
|
|
2548
1504
|
totalCost: Number(cost.total) || 0,
|
|
2549
1505
|
currency: "USD",
|
|
2550
1506
|
};
|
|
@@ -2556,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2556
1512
|
if (anyNonZero) {
|
|
2557
1513
|
setLiveTotals(nextTotals);
|
|
2558
1514
|
}
|
|
2559
|
-
// Extract context window info from cost object
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
if (contextWindowRaw !== undefined &&
|
|
2565
|
-
contextWindowRaw !== null &&
|
|
2566
|
-
contextUsedRaw !== undefined &&
|
|
2567
|
-
contextUsedRaw !== null) {
|
|
2568
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2569
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2570
|
-
if (contextWindowValue > 0 &&
|
|
2571
|
-
Number.isFinite(contextUsedValue) &&
|
|
2572
|
-
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) {
|
|
2573
1520
|
setContextWindowStatus({
|
|
2574
1521
|
contextWindowTokens: contextWindowValue,
|
|
2575
1522
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2593,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2593
1540
|
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
2594
1541
|
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
2595
1542
|
cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
|
|
2596
|
-
imageCost: Number(data.totalImageCost) || 0,
|
|
2597
1543
|
totalCost: Number(data.totalCost) || 0,
|
|
2598
1544
|
currency: data.currency || "USD",
|
|
2599
1545
|
};
|
|
@@ -2609,10 +1555,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2609
1555
|
}
|
|
2610
1556
|
// Update tool result directly in the messages array
|
|
2611
1557
|
if (!resultMessageId) {
|
|
2612
|
-
appendToolUiEvent("ui:tool-result-dropped", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} reason=no-result-message-id`);
|
|
2613
1558
|
return;
|
|
2614
1559
|
}
|
|
2615
|
-
appendToolUiEvent("ui:tool-result-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId}`);
|
|
2616
1560
|
// Update the message with tool result
|
|
2617
1561
|
setMessages((prev) => {
|
|
2618
1562
|
const updated = prev.map((msg) => {
|
|
@@ -2628,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2628
1572
|
const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
|
|
2629
1573
|
if (existingToolCall && message.data) {
|
|
2630
1574
|
const updatedToolCalls = [...updatedMessage.toolCalls];
|
|
2631
|
-
const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
|
|
2632
|
-
const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
|
|
2633
|
-
const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
|
|
2634
|
-
nextArgsText !== existingArgsText) ||
|
|
2635
|
-
(existingArgsText === "{}" && nextArgsText !== "{}");
|
|
2636
1575
|
const toolCall = {
|
|
2637
1576
|
id: existingToolCall.id,
|
|
2638
1577
|
messageId: existingToolCall.messageId,
|
|
2639
1578
|
toolCallId: existingToolCall.toolCallId,
|
|
2640
1579
|
functionName: existingToolCall.functionName,
|
|
2641
|
-
functionArguments:
|
|
2642
|
-
? nextArgsText
|
|
2643
|
-
: existingToolCall.functionArguments,
|
|
1580
|
+
functionArguments: existingToolCall.functionArguments,
|
|
2644
1581
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2645
|
-
functionResultRichContent: message.data.richContent ||
|
|
2646
|
-
existingToolCall.functionResultRichContent,
|
|
2647
1582
|
functionError: message.data.functionError || message.data.error || "",
|
|
2648
1583
|
isCompleted: true,
|
|
2649
1584
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2660,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2660
1595
|
}
|
|
2661
1596
|
else if (message.data && resultToolCallId && resultMessageId) {
|
|
2662
1597
|
// Create new tool call if it doesn't exist
|
|
2663
|
-
const
|
|
2664
|
-
message.
|
|
2665
|
-
|
|
1598
|
+
const functionName = message.data.functionName ||
|
|
1599
|
+
message.data.name ||
|
|
1600
|
+
message.data.function?.name ||
|
|
1601
|
+
"unknown";
|
|
2666
1602
|
const toolCall = {
|
|
2667
1603
|
id: resultToolCallId,
|
|
2668
1604
|
messageId: resultMessageId,
|
|
2669
1605
|
toolCallId: resultToolCallId,
|
|
2670
|
-
functionName:
|
|
2671
|
-
functionArguments:
|
|
1606
|
+
functionName: functionName,
|
|
1607
|
+
functionArguments: message.data.functionArguments ||
|
|
1608
|
+
message.data.arguments ||
|
|
1609
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2672
1610
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2673
|
-
functionResultRichContent: message.data.richContent || undefined,
|
|
2674
1611
|
functionError: message.data.functionError || message.data.error || "",
|
|
2675
1612
|
isCompleted: true,
|
|
2676
1613
|
responseTimeMs: message.data.responseTimeMs,
|
|
2677
|
-
createdDate:
|
|
1614
|
+
createdDate: new Date().toISOString(),
|
|
2678
1615
|
};
|
|
2679
1616
|
updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
|
|
2680
1617
|
}
|
|
@@ -2682,12 +1619,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2682
1619
|
return updatedMessage;
|
|
2683
1620
|
});
|
|
2684
1621
|
messagesRef.current = updated;
|
|
2685
|
-
const messageWithToolResult = updated.find((msg) => msg.id === resultMessageId);
|
|
2686
|
-
const matchingToolCall = messageWithToolResult?.toolCalls?.find((tc) => tc.toolCallId === resultToolCallId);
|
|
2687
|
-
appendToolUiEvent("ui:tool-result-applied", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId} completed=${matchingToolCall?.isCompleted ? "yes" : "no"} messageToolCalls=${messageWithToolResult?.toolCalls?.length || 0}`);
|
|
2688
1622
|
return updated;
|
|
2689
1623
|
});
|
|
2690
|
-
}, [
|
|
1624
|
+
}, []);
|
|
2691
1625
|
// Listen for local approval resolution to update UI
|
|
2692
1626
|
useEffect(() => {
|
|
2693
1627
|
const onApprovalResolved = (ev) => {
|
|
@@ -2768,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2768
1702
|
// The agent might have been persisted after sending a prompt
|
|
2769
1703
|
// Only treat as "new" if backend returns 404
|
|
2770
1704
|
const hasExistingMessages = messagesRef.current.length > 0;
|
|
2771
|
-
if (agentStub.status === "new" &&
|
|
2772
|
-
!agentStub.userId &&
|
|
2773
|
-
!hasExistingMessages) {
|
|
1705
|
+
if (agentStub.status === "new" && !hasExistingMessages) {
|
|
2774
1706
|
// Only initialize as new if we have no messages yet (initial mount)
|
|
2775
1707
|
// Derive initial profile from provided metadata if present
|
|
2776
1708
|
const initialProfileIdFromMeta = (() => {
|
|
@@ -2810,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2810
1742
|
totalInputTokenCost: 0,
|
|
2811
1743
|
totalOutputTokenCost: 0,
|
|
2812
1744
|
totalCachedInputTokenCost: 0,
|
|
2813
|
-
totalImageCost: 0,
|
|
2814
1745
|
totalCost: 0,
|
|
2815
1746
|
messageCount: 0,
|
|
2816
1747
|
});
|
|
@@ -2853,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2853
1784
|
}
|
|
2854
1785
|
})();
|
|
2855
1786
|
// Create context with top-level properties (what ContextInfoBar expects)
|
|
2856
|
-
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;
|
|
2857
1829
|
let nextMetadata = null;
|
|
2858
1830
|
if (initialMetadata) {
|
|
2859
1831
|
// Merge initial metadata with local context (using top-level structure)
|
|
@@ -2929,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2929
1901
|
seenMessageIdsRef.current.add(msg.id.toLowerCase());
|
|
2930
1902
|
}
|
|
2931
1903
|
});
|
|
2932
|
-
// Keep local streaming if the agent is still
|
|
2933
|
-
// This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
|
|
2934
|
-
// "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
|
|
2935
1905
|
const isRunning = agentData.status === "running" || agentData.status === 1;
|
|
2936
|
-
const isWaiting = agentData.status === "waitingForApproval" ||
|
|
2937
|
-
agentData.status === 2 ||
|
|
2938
|
-
agentData.status === "waitingForInput" ||
|
|
2939
|
-
agentData.status === "costLimitReached" ||
|
|
2940
|
-
agentData.status === 7;
|
|
2941
|
-
const keepLocalStreaming = isRunning || isWaiting;
|
|
2942
1906
|
// Filter local messages to only include streaming/incomplete messages that aren't in DB
|
|
2943
1907
|
// This prevents duplicates when reloading - DB messages are source of truth for completed messages
|
|
2944
1908
|
const localStreaming = isRunning
|
|
@@ -2956,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2956
1920
|
// Don't keep completed messages from local state - DB is source of truth
|
|
2957
1921
|
return false;
|
|
2958
1922
|
})
|
|
2959
|
-
:
|
|
2960
|
-
? messagesRef.current.filter((localMsg) => {
|
|
2961
|
-
if (!localMsg.id)
|
|
2962
|
-
return false;
|
|
2963
|
-
if (!localMsg.isCompleted &&
|
|
2964
|
-
localMsg.messageType === "streaming") {
|
|
2965
|
-
const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
|
|
2966
|
-
dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
|
|
2967
|
-
return !existsInDb;
|
|
2968
|
-
}
|
|
2969
|
-
return false;
|
|
2970
|
-
})
|
|
2971
|
-
: [];
|
|
1923
|
+
: [];
|
|
2972
1924
|
const merged = mergeMessagesById(dbMessages, localStreaming);
|
|
2973
1925
|
messagesRef.current = merged;
|
|
2974
1926
|
setMessages(merged);
|
|
@@ -2979,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2979
1931
|
const runningNow = agentData.status === "running" || agentData.status === 1;
|
|
2980
1932
|
const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
|
|
2981
1933
|
agentData.status === 2;
|
|
2982
|
-
const waitingForInputNow = agentData.status === "waitingForInput";
|
|
2983
1934
|
const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
2984
1935
|
if (runningNow || hasStreamingNow) {
|
|
2985
1936
|
setIsWaitingForResponse(true);
|
|
@@ -2987,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2987
1938
|
// Agent is actively running, show thinking dots
|
|
2988
1939
|
setIsAgentThinking(true);
|
|
2989
1940
|
}
|
|
2990
|
-
else if (waitingForApprovalNow
|
|
1941
|
+
else if (waitingForApprovalNow) {
|
|
2991
1942
|
setIsWaitingForResponse(false);
|
|
2992
1943
|
isWaitingRef.current = false;
|
|
2993
1944
|
setIsConnecting(false);
|
|
2994
|
-
// Agent is waiting for user
|
|
1945
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
2995
1946
|
setIsAgentThinking(false);
|
|
2996
1947
|
}
|
|
2997
1948
|
else {
|
|
@@ -3040,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3040
1991
|
if (!contextJson)
|
|
3041
1992
|
return null;
|
|
3042
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
|
|
3043
1997
|
if (parsedContext && typeof parsedContext === "object") {
|
|
3044
|
-
|
|
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;
|
|
3045
2007
|
}
|
|
3046
2008
|
return null;
|
|
3047
2009
|
}
|
|
@@ -3127,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3127
2089
|
return;
|
|
3128
2090
|
}
|
|
3129
2091
|
// Check if cost limit exceeded based on status or cost values
|
|
3130
|
-
const statusIndicatesLimit = agent.status === "costLimitReached";
|
|
2092
|
+
const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
|
|
3131
2093
|
// Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
|
|
3132
2094
|
const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
|
|
3133
2095
|
const costExceedsLimit = agent.costLimit &&
|
|
@@ -3212,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3212
2174
|
// Handle agent:profile:switched (profile changed via switch-profile function)
|
|
3213
2175
|
if (messageType === "agent:profile:switched") {
|
|
3214
2176
|
const payload = message.payload || {};
|
|
3215
|
-
const switchedAgentId = payload.agentId;
|
|
3216
|
-
const newProfileId = payload.newProfileId;
|
|
3217
|
-
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;
|
|
3218
2180
|
if (switchedAgentId === agent.id && newProfileId) {
|
|
3219
2181
|
// Update the agent's profile
|
|
3220
2182
|
setAgent((prev) => {
|
|
@@ -3255,10 +2217,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3255
2217
|
return;
|
|
3256
2218
|
}
|
|
3257
2219
|
// For other agent messages, check if this is for our agent
|
|
3258
|
-
const agentId = message.payload?.agentId;
|
|
3259
|
-
if (agentId !== agent.id)
|
|
2220
|
+
const agentId = message.payload?.agentId || message.payload?.AgentId;
|
|
2221
|
+
if (agentId !== agent.id)
|
|
3260
2222
|
return;
|
|
3261
|
-
}
|
|
3262
2223
|
// Handle agent:run:start
|
|
3263
2224
|
if (messageType === "agent:run:start") {
|
|
3264
2225
|
// If a stop operation is in progress, ignore this message to prevent
|
|
@@ -3267,15 +2228,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3267
2228
|
console.log("[AgentTerminal] Ignoring agent:run:start during stop operation");
|
|
3268
2229
|
return;
|
|
3269
2230
|
}
|
|
3270
|
-
const currentStatus = agentRef.current?.status;
|
|
3271
|
-
if (currentStatus === "waitingForInput" ||
|
|
3272
|
-
currentStatus === "waitingForApproval" ||
|
|
3273
|
-
currentStatus === "costLimitReached") {
|
|
3274
|
-
// Replayed start messages arrive before the buffered status payload when
|
|
3275
|
-
// reopening an already-paused agent. Preserve the attention state instead
|
|
3276
|
-
// of flashing "running" until the follow-up status update lands.
|
|
3277
|
-
return;
|
|
3278
|
-
}
|
|
3279
2231
|
// Reset run-scoped deduplication so new run seq values (starting at 1)
|
|
3280
2232
|
// are not discarded due to previous run's lastSeqRef
|
|
3281
2233
|
lastSeqRef.current = 0;
|
|
@@ -3371,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3371
2323
|
if (messageType === "agent:prompt:queued") {
|
|
3372
2324
|
const { queueEntry } = message.payload;
|
|
3373
2325
|
if (queueEntry) {
|
|
3374
|
-
if (shouldSuppressQueuedPrompt(queueEntry)) {
|
|
3375
|
-
return;
|
|
3376
|
-
}
|
|
3377
2326
|
// Add the new prompt to the queued prompts list
|
|
3378
2327
|
setQueuedPrompts((prev) => {
|
|
3379
2328
|
// Check if prompt already exists (deduplication)
|
|
@@ -3402,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3402
2351
|
return;
|
|
3403
2352
|
}
|
|
3404
2353
|
const { seq, type, data, cost } = message.payload;
|
|
3405
|
-
if (type === "ToolCall" || type === "toolCall") {
|
|
3406
|
-
}
|
|
3407
2354
|
// Always allow ContextUpdate messages (metadata updates) regardless of agent status
|
|
3408
2355
|
const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
|
|
3409
|
-
const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
|
|
3410
|
-
const shouldClearHeartbeat = type === "ContentChunk" ||
|
|
3411
|
-
type === "contentChunk" ||
|
|
3412
|
-
type === "ToolCall" ||
|
|
3413
|
-
type === "toolCall" ||
|
|
3414
|
-
type === "ToolResult" ||
|
|
3415
|
-
type === "toolResult";
|
|
3416
2356
|
// Allow deltas if the agent is running OR if we already have an active streaming message
|
|
3417
2357
|
// OR if the server provided a messageId for targeted updates.
|
|
3418
2358
|
// This avoids dropping early deltas that may arrive before the 'running' status update.
|
|
3419
2359
|
// ContextUpdate messages are always allowed as they're metadata updates, not content.
|
|
3420
2360
|
const isRunning = agent.status === "running" || agent.status === 1;
|
|
3421
2361
|
const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3422
|
-
const hasMessageId = !!message?.payload?.data?.messageId
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
!isRunning &&
|
|
3426
|
-
!hasStreaming &&
|
|
3427
|
-
!hasMessageId) {
|
|
2362
|
+
const hasMessageId = !!message?.payload?.data?.messageId ||
|
|
2363
|
+
!!message?.payload?.data?.MessageId;
|
|
2364
|
+
if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
|
|
3428
2365
|
return; // ignore only if we cannot safely target an existing streaming message
|
|
3429
2366
|
}
|
|
3430
2367
|
// Deduplicate by sequence
|
|
@@ -3441,23 +2378,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3441
2378
|
cost,
|
|
3442
2379
|
timestamp: new Date().toISOString(),
|
|
3443
2380
|
};
|
|
3444
|
-
if (shouldClearHeartbeat) {
|
|
3445
|
-
clearHeartbeatMessages();
|
|
3446
|
-
}
|
|
3447
2381
|
if (type === "ContentChunk" || type === "contentChunk") {
|
|
3448
2382
|
handleContentChunk(agentStreamMessage, agent);
|
|
3449
2383
|
}
|
|
3450
2384
|
else if (type === "ToolCall" || type === "toolCall") {
|
|
3451
|
-
appendToolUiEvent("ui:tool-delta-received", `ToolCall:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
|
|
3452
2385
|
handleToolCall(agentStreamMessage, agent);
|
|
3453
2386
|
}
|
|
3454
2387
|
else if (type === "ToolResult" || type === "toolResult") {
|
|
3455
|
-
appendToolUiEvent("ui:tool-delta-received", `ToolResult:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
|
|
3456
2388
|
handleToolResult(agentStreamMessage, agent);
|
|
3457
2389
|
}
|
|
3458
|
-
else if (type === "Heartbeat" || type === "heartbeat") {
|
|
3459
|
-
handleHeartbeatMessage(agentStreamMessage);
|
|
3460
|
-
}
|
|
3461
2390
|
else if (type === "ContextUpdate" || type === "contextUpdate") {
|
|
3462
2391
|
// Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
|
|
3463
2392
|
const contextData = data;
|
|
@@ -3467,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3467
2396
|
const current = (prev || {});
|
|
3468
2397
|
const updated = { ...current };
|
|
3469
2398
|
// Merge additionalData if present (deep merge to preserve existing values)
|
|
3470
|
-
if (contextData.additionalData) {
|
|
3471
|
-
const sourceAdditionalData = contextData.additionalData ||
|
|
2399
|
+
if (contextData.additionalData || contextData.AdditionalData) {
|
|
2400
|
+
const sourceAdditionalData = contextData.additionalData ||
|
|
2401
|
+
contextData.AdditionalData ||
|
|
2402
|
+
{};
|
|
3472
2403
|
updated.additionalData = {
|
|
3473
2404
|
...(current.additionalData || {}),
|
|
3474
2405
|
...sourceAdditionalData,
|
|
@@ -3476,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3476
2407
|
}
|
|
3477
2408
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3478
2409
|
// Backend sends the complete updated list, not just additions
|
|
3479
|
-
if (contextData.childAgents) {
|
|
3480
|
-
const childAgents = contextData.childAgents;
|
|
2410
|
+
if (contextData.ChildAgents || contextData.childAgents) {
|
|
2411
|
+
const childAgents = contextData.ChildAgents || contextData.childAgents;
|
|
3481
2412
|
if (Array.isArray(childAgents)) {
|
|
3482
|
-
updated.
|
|
2413
|
+
updated.ChildAgents = childAgents;
|
|
3483
2414
|
}
|
|
3484
2415
|
}
|
|
3485
2416
|
return updated;
|
|
@@ -3505,18 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3505
2436
|
// Route based on statusData.state
|
|
3506
2437
|
try {
|
|
3507
2438
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3508
|
-
const normalizedStatus =
|
|
3509
|
-
|
|
3510
|
-
|
|
2439
|
+
const normalizedStatus = statusData?.state ||
|
|
2440
|
+
statusData?.Status ||
|
|
2441
|
+
statusData?.status;
|
|
2442
|
+
if (normalizedStatus === "Cancelled" ||
|
|
2443
|
+
normalizedStatus === "canceled") {
|
|
3511
2444
|
// Stop indicators and mark any in-progress streaming messages as completed
|
|
3512
|
-
clearHeartbeatMessages();
|
|
3513
|
-
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3514
2445
|
setIsWaitingForResponse(false);
|
|
3515
2446
|
isWaitingRef.current = false;
|
|
3516
2447
|
setIsConnecting(false);
|
|
3517
|
-
setIsSubmitting(false);
|
|
3518
|
-
shouldCreateNewMessage.current = false;
|
|
3519
|
-
setIsAgentThinking(false);
|
|
3520
2448
|
setMessages((prev) => {
|
|
3521
2449
|
const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
|
|
3522
2450
|
? {
|
|
@@ -3559,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3559
2487
|
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
3560
2488
|
cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
|
|
3561
2489
|
cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
|
|
3562
|
-
imageCost: Number(totals.totalImageCost) || 0,
|
|
3563
2490
|
totalCost: totalCost,
|
|
3564
2491
|
currency: totals.currency,
|
|
3565
2492
|
};
|
|
@@ -3571,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3571
2498
|
if (anyNonZero) {
|
|
3572
2499
|
setLiveTotals(nextTotals);
|
|
3573
2500
|
}
|
|
3574
|
-
// Fallback context usage update for providers that do not include
|
|
3575
|
-
// context usage in every stream delta (for example some OpenAI streams).
|
|
3576
|
-
// Prefer explicit status values when present; otherwise derive from totals.
|
|
3577
|
-
const contextWindowValue = Number(statusData?.contextWindow ??
|
|
3578
|
-
agent?.contextWindowTokens ??
|
|
3579
|
-
0);
|
|
3580
|
-
const contextUsedValue = Number(statusData?.contextUsed ??
|
|
3581
|
-
(Number(totals.totalInputTokens) || 0) +
|
|
3582
|
-
(Number(totals.totalCachedInputTokens) || 0) +
|
|
3583
|
-
(Number(totals.totalCacheWriteTokens) || 0));
|
|
3584
|
-
if (contextWindowValue > 0 &&
|
|
3585
|
-
Number.isFinite(contextUsedValue) &&
|
|
3586
|
-
contextUsedValue >= 0) {
|
|
3587
|
-
setContextWindowStatus({
|
|
3588
|
-
contextWindowTokens: contextWindowValue,
|
|
3589
|
-
estimatedInputTokens: contextUsedValue,
|
|
3590
|
-
contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
|
|
3591
|
-
model: agent?.model || "",
|
|
3592
|
-
});
|
|
3593
|
-
}
|
|
3594
2501
|
// If server provides costLimit along with totals, persist it locally
|
|
3595
2502
|
if (statusCostLimit) {
|
|
3596
2503
|
setAgent((prev) => prev &&
|
|
@@ -3612,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3612
2519
|
return;
|
|
3613
2520
|
}
|
|
3614
2521
|
if (statusData?.state === "ToolApprovalsRequired") {
|
|
3615
|
-
setPendingBrowserCaptureDialogType(null);
|
|
3616
2522
|
const msgId = statusData.messageId;
|
|
3617
2523
|
const ids = statusData.toolCallIds || [];
|
|
3618
2524
|
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
@@ -3644,40 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3644
2550
|
setIsWaitingForResponse(false);
|
|
3645
2551
|
return;
|
|
3646
2552
|
}
|
|
3647
|
-
// Handle
|
|
3648
|
-
if (
|
|
3649
|
-
|
|
2553
|
+
// Handle "WaitingForApproval" state explicitly
|
|
2554
|
+
if (statusData?.state === "WaitingForApproval" ||
|
|
2555
|
+
statusData?.state === "waitingForApproval") {
|
|
3650
2556
|
setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
|
|
3651
2557
|
setIsConnecting(false);
|
|
3652
2558
|
setIsWaitingForResponse(false);
|
|
3653
2559
|
setIsAgentThinking(false);
|
|
3654
2560
|
return;
|
|
3655
2561
|
}
|
|
3656
|
-
if (
|
|
3657
|
-
const dialogType = typeof statusData?.dialogType === "string"
|
|
3658
|
-
? statusData.dialogType
|
|
3659
|
-
: null;
|
|
3660
|
-
const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
3661
|
-
dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
3662
|
-
setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
|
|
3663
|
-
setAgent((prev) => prev
|
|
3664
|
-
? {
|
|
3665
|
-
...prev,
|
|
3666
|
-
status: "waitingForInput",
|
|
3667
|
-
statusMessage: isBrowserCaptureWait &&
|
|
3668
|
-
typeof statusData?.title === "string" &&
|
|
3669
|
-
statusData.title.trim()
|
|
3670
|
-
? statusData.title.trim()
|
|
3671
|
-
: prev.statusMessage,
|
|
3672
|
-
}
|
|
3673
|
-
: prev);
|
|
3674
|
-
setIsConnecting(false);
|
|
3675
|
-
setIsWaitingForResponse(false);
|
|
3676
|
-
setIsAgentThinking(false);
|
|
3677
|
-
return;
|
|
3678
|
-
}
|
|
3679
|
-
if (normalizedStatus === "costLimitReached") {
|
|
3680
|
-
setPendingBrowserCaptureDialogType(null);
|
|
2562
|
+
if (statusData?.state === "CostLimitReached") {
|
|
3681
2563
|
const totalCost = Number(statusData.totalCost) || 0;
|
|
3682
2564
|
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
3683
2565
|
setCostLimitExceeded({
|
|
@@ -3695,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3695
2577
|
// Server sends additionalData directly (with todoList inside)
|
|
3696
2578
|
// Also may include ChildAgents for spawned agents
|
|
3697
2579
|
try {
|
|
3698
|
-
const serverAdditionalData = statusData.additionalData || {};
|
|
2580
|
+
const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
|
|
3699
2581
|
setAgentMetadata((prev) => {
|
|
3700
2582
|
const current = (prev || {});
|
|
3701
2583
|
const updated = { ...current };
|
|
@@ -3709,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3709
2591
|
}
|
|
3710
2592
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3711
2593
|
// Backend sends the complete updated list, not just additions
|
|
3712
|
-
if (statusData.childAgents) {
|
|
3713
|
-
const childAgents = statusData.childAgents;
|
|
2594
|
+
if (statusData.ChildAgents || statusData.childAgents) {
|
|
2595
|
+
const childAgents = statusData.ChildAgents || statusData.childAgents;
|
|
3714
2596
|
if (Array.isArray(childAgents)) {
|
|
3715
|
-
updated.
|
|
2597
|
+
updated.ChildAgents = childAgents;
|
|
3716
2598
|
}
|
|
3717
2599
|
}
|
|
3718
2600
|
return updated;
|
|
@@ -3724,10 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3724
2606
|
return;
|
|
3725
2607
|
}
|
|
3726
2608
|
// Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
|
|
3727
|
-
if (normalizedStatus === "completed"
|
|
2609
|
+
if (normalizedStatus === "completed" ||
|
|
2610
|
+
normalizedStatus === "Completed") {
|
|
3728
2611
|
// Reset deduplication for the next run
|
|
3729
2612
|
lastSeqRef.current = 0;
|
|
3730
|
-
clearHeartbeatMessages();
|
|
3731
2613
|
// Mark the last assistant message as completed
|
|
3732
2614
|
setMessages((prev) => {
|
|
3733
2615
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3750,21 +2632,30 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3750
2632
|
setIsAgentThinking(false);
|
|
3751
2633
|
return;
|
|
3752
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
|
+
}
|
|
3753
2646
|
// Handle "Running" state - agent is actively processing
|
|
3754
|
-
if (normalizedStatus === "running"
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
: prev);
|
|
2647
|
+
if (normalizedStatus === "running" ||
|
|
2648
|
+
normalizedStatus === "Running") {
|
|
2649
|
+
// Update agent status to running
|
|
2650
|
+
setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
|
|
3759
2651
|
setIsWaitingForResponse(true);
|
|
3760
2652
|
isWaitingRef.current = true;
|
|
3761
2653
|
setIsAgentThinking(true);
|
|
3762
2654
|
return;
|
|
3763
2655
|
}
|
|
3764
2656
|
// Handle "Error" state
|
|
3765
|
-
if (normalizedStatus === "error") {
|
|
3766
|
-
const errorMsg =
|
|
3767
|
-
clearHeartbeatMessages();
|
|
2657
|
+
if (normalizedStatus === "error" || normalizedStatus === "Error") {
|
|
2658
|
+
const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
|
|
3768
2659
|
setAgent((prev) => prev
|
|
3769
2660
|
? { ...prev, status: "error", statusMessage: errorMsg }
|
|
3770
2661
|
: prev);
|
|
@@ -3784,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3784
2675
|
if (messageType === "agent:run:complete") {
|
|
3785
2676
|
// Reset deduplication for the next run
|
|
3786
2677
|
lastSeqRef.current = 0;
|
|
3787
|
-
clearHeartbeatMessages();
|
|
3788
2678
|
// Mark the last assistant message as completed
|
|
3789
2679
|
setMessages((prev) => {
|
|
3790
2680
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3809,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3809
2699
|
}
|
|
3810
2700
|
// Lifecycle: agent:run:error
|
|
3811
2701
|
if (messageType === "agent:run:error") {
|
|
3812
|
-
const errorMsg =
|
|
3813
|
-
"AI could not complete this request.";
|
|
3814
|
-
clearHeartbeatMessages();
|
|
2702
|
+
const errorMsg = message.payload?.error || "Unknown error";
|
|
3815
2703
|
// Reset deduplication for the next run after an error
|
|
3816
2704
|
lastSeqRef.current = 0;
|
|
3817
2705
|
setError(errorMsg);
|
|
@@ -3826,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3826
2714
|
}
|
|
3827
2715
|
}, [
|
|
3828
2716
|
agent,
|
|
3829
|
-
clearHeartbeatMessages,
|
|
3830
2717
|
handleContentChunk,
|
|
3831
|
-
handleHeartbeatMessage,
|
|
3832
2718
|
handleToolCall,
|
|
3833
2719
|
handleToolResult,
|
|
3834
2720
|
onAgentUpdate,
|
|
@@ -3883,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3883
2769
|
const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
|
|
3884
2770
|
const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
|
|
3885
2771
|
currentAgent.status === 2;
|
|
3886
|
-
const isWaitingForInput = currentAgent.status === "waitingForInput";
|
|
3887
2772
|
if (isRunning) {
|
|
3888
2773
|
setIsWaitingForResponse(true);
|
|
3889
2774
|
isWaitingRef.current = true;
|
|
@@ -3891,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3891
2776
|
// Agent is currently running, show thinking dots
|
|
3892
2777
|
setIsAgentThinking(true);
|
|
3893
2778
|
}
|
|
3894
|
-
else if (isWaitingForApproval
|
|
2779
|
+
else if (isWaitingForApproval) {
|
|
3895
2780
|
setIsWaitingForResponse(false);
|
|
3896
2781
|
isWaitingRef.current = false;
|
|
3897
|
-
// Agent is waiting for user
|
|
2782
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
3898
2783
|
setIsAgentThinking(false);
|
|
3899
2784
|
}
|
|
3900
2785
|
else {
|
|
@@ -3941,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3941
2826
|
window.addEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3942
2827
|
return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3943
2828
|
}, []);
|
|
3944
|
-
// Keep stable refs so we don't miss window events during effect re-runs.
|
|
3945
|
-
const agentIdRefForDialogs = useRef(null);
|
|
3946
|
-
const agentStubIdRefForDialogs = useRef(agentStub.id);
|
|
3947
|
-
const isActiveRefForDialogs = useRef(isActive);
|
|
3948
|
-
const scrollToBottomRefForDialogs = useRef(scrollToBottom);
|
|
3949
|
-
useEffect(() => {
|
|
3950
|
-
agentIdRefForDialogs.current = agent?.id ?? null;
|
|
3951
|
-
}, [agent?.id]);
|
|
3952
|
-
useEffect(() => {
|
|
3953
|
-
const prevId = agentStubIdRefForDialogs.current;
|
|
3954
|
-
agentStubIdRefForDialogs.current = agentStub.id;
|
|
3955
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
3956
|
-
const normalizedPrevId = normalizeDialogAgentId(prevId);
|
|
3957
|
-
if (normalizedPrevId) {
|
|
3958
|
-
delete visibleRegistry[normalizedPrevId];
|
|
3959
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
3960
|
-
}
|
|
3961
|
-
if (prevId && prevId !== agentStub.id) {
|
|
3962
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
3963
|
-
if (orphanedDialog) {
|
|
3964
|
-
setActiveInlineDialog(null);
|
|
3965
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
3966
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
3967
|
-
}));
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
}, [agentStub.id]);
|
|
3971
|
-
useEffect(() => {
|
|
3972
|
-
isActiveRefForDialogs.current = isActive;
|
|
3973
|
-
}, [isActive]);
|
|
3974
|
-
useEffect(() => {
|
|
3975
|
-
scrollToBottomRefForDialogs.current = scrollToBottom;
|
|
3976
|
-
}, [scrollToBottom]);
|
|
3977
2829
|
// Listen for agent inline dialog requests from AgentDialogHandler
|
|
3978
2830
|
useEffect(() => {
|
|
3979
|
-
if (orphanTimeoutRef.current) {
|
|
3980
|
-
clearTimeout(orphanTimeoutRef.current);
|
|
3981
|
-
orphanTimeoutRef.current = null;
|
|
3982
|
-
}
|
|
3983
|
-
const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
|
|
3984
|
-
const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
3985
|
-
if (normalizedAgentStubId &&
|
|
3986
|
-
!globalListeners.includes(normalizedAgentStubId)) {
|
|
3987
|
-
globalListeners.push(normalizedAgentStubId);
|
|
3988
|
-
}
|
|
3989
|
-
globalThis.__agentDialogMountedAgents = globalListeners;
|
|
3990
2831
|
const handleDialogShow = (event) => {
|
|
3991
|
-
const
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
const onCancel = detail?.onCancel;
|
|
3995
|
-
const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
|
|
3996
|
-
const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
3997
|
-
const isActiveNow = isActiveRefForDialogs.current;
|
|
3998
|
-
if (!request)
|
|
3999
|
-
return;
|
|
4000
|
-
const requestAgentId = normalizeDialogAgentId(request.agentId);
|
|
4001
|
-
const agentMatch = !requestAgentId ||
|
|
4002
|
-
!terminalAgentStubId ||
|
|
4003
|
-
requestAgentId === terminalAgentStubId ||
|
|
4004
|
-
requestAgentId === terminalAgentId;
|
|
4005
|
-
if (!isActiveNow) {
|
|
4006
|
-
return;
|
|
4007
|
-
}
|
|
4008
|
-
if (!request)
|
|
4009
|
-
return;
|
|
4010
|
-
// Only handle dialog requests for this terminal's agent stub
|
|
4011
|
-
if (requestAgentId &&
|
|
4012
|
-
terminalAgentStubId &&
|
|
4013
|
-
requestAgentId !== terminalAgentStubId &&
|
|
4014
|
-
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) {
|
|
4015
2835
|
return;
|
|
4016
2836
|
}
|
|
4017
2837
|
console.log("[AgentTerminal] Received inline dialog request:", request);
|
|
4018
|
-
setActiveInlineDialog({
|
|
4019
|
-
request,
|
|
4020
|
-
onComplete: (result) => {
|
|
4021
|
-
onComplete(result);
|
|
4022
|
-
onInteractionSubmitted?.();
|
|
4023
|
-
},
|
|
4024
|
-
onCancel: () => {
|
|
4025
|
-
onCancel();
|
|
4026
|
-
onInteractionSubmitted?.();
|
|
4027
|
-
},
|
|
4028
|
-
});
|
|
2838
|
+
setActiveInlineDialog({ request, onComplete, onCancel });
|
|
4029
2839
|
// Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
setTimeout(() => {
|
|
4036
|
-
if (request.dialogType === "questionnaire") {
|
|
4037
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4038
|
-
return;
|
|
4039
|
-
}
|
|
4040
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4041
|
-
}, 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);
|
|
4042
2845
|
};
|
|
4043
2846
|
window.addEventListener("agent:dialog:show", handleDialogShow);
|
|
4044
|
-
return () =>
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
const idsToClear = [
|
|
4049
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
4050
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
4051
|
-
].filter(Boolean);
|
|
4052
|
-
idsToClear.forEach((id) => delete visibleRegistry[id]);
|
|
4053
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
4054
|
-
// If unmounting with an active dialog, defer the orphan event so that
|
|
4055
|
-
// React strict mode remounts can cancel it. Only real unmounts fire.
|
|
4056
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
4057
|
-
if (orphanedDialog) {
|
|
4058
|
-
orphanTimeoutRef.current = setTimeout(() => {
|
|
4059
|
-
orphanTimeoutRef.current = null;
|
|
4060
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
4061
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
4062
|
-
}));
|
|
4063
|
-
}, 300);
|
|
4064
|
-
}
|
|
4065
|
-
window.removeEventListener("agent:dialog:show", handleDialogShow);
|
|
4066
|
-
};
|
|
4067
|
-
}, []);
|
|
4068
|
-
// Announce when this terminal is ready to accept dialogs.
|
|
4069
|
-
// Fire immediately on mount using agentStub.id so the AgentDialogHandler
|
|
4070
|
-
// can re-dispatch pending dialogs without waiting for the async agent load.
|
|
4071
|
-
// Also fire when agent?.id becomes available in case it differs from the stub.
|
|
4072
|
-
useEffect(() => {
|
|
4073
|
-
const normalizedStubId = normalizeDialogAgentId(agentStub.id);
|
|
4074
|
-
if (normalizedStubId) {
|
|
4075
|
-
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4076
|
-
detail: {
|
|
4077
|
-
agentId: normalizedStubId,
|
|
4078
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4079
|
-
},
|
|
4080
|
-
}));
|
|
4081
|
-
}
|
|
4082
|
-
}, [agentStub.id]);
|
|
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
|
|
4083
2851
|
useEffect(() => {
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
|
|
2852
|
+
if (agent?.id) {
|
|
2853
|
+
console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
|
|
4087
2854
|
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4088
|
-
detail: {
|
|
4089
|
-
agentId: normalizedAgentId,
|
|
4090
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4091
|
-
},
|
|
2855
|
+
detail: { agentId: agent.id },
|
|
4092
2856
|
}));
|
|
4093
2857
|
}
|
|
4094
|
-
}, [agent?.id
|
|
2858
|
+
}, [agent?.id]);
|
|
4095
2859
|
// Profiles are provided by parent component (Agents). No local loading here.
|
|
4096
2860
|
// Select active profile based on agent.profileId or agentStub.profileId
|
|
4097
2861
|
useEffect(() => {
|
|
@@ -4102,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4102
2866
|
// Use case-insensitive comparison for GUID matching (backend may return different casing)
|
|
4103
2867
|
const normalizedProfileId = profileIdToUse?.toLowerCase();
|
|
4104
2868
|
const candidate = normalizedProfileId
|
|
4105
|
-
? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
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);
|
|
4110
2874
|
}
|
|
4111
|
-
|
|
4112
|
-
// not only when the profile id changes. Otherwise availableSkills (and similar fields)
|
|
4113
|
-
// that are merged in the parent after async loads never update (e.g. Template Builder
|
|
4114
|
-
// settings skills merged into the profile).
|
|
4115
|
-
setActiveProfile((prev) => {
|
|
4116
|
-
if (!prev || prev.id !== candidate.id)
|
|
4117
|
-
return candidate;
|
|
4118
|
-
const skillKey = (p) => JSON.stringify({
|
|
4119
|
-
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
4120
|
-
String(s?.id ?? ""),
|
|
4121
|
-
String(s?.name ?? ""),
|
|
4122
|
-
]),
|
|
4123
|
-
available: (p.availableSkills ?? []).map((s) => [
|
|
4124
|
-
String(s?.id ?? ""),
|
|
4125
|
-
String(s?.name ?? ""),
|
|
4126
|
-
]),
|
|
4127
|
-
});
|
|
4128
|
-
if (skillKey(prev) === skillKey(candidate))
|
|
4129
|
-
return prev;
|
|
4130
|
-
return candidate;
|
|
4131
|
-
});
|
|
4132
|
-
}, [
|
|
4133
|
-
profiles,
|
|
4134
|
-
agent?.id,
|
|
4135
|
-
agent?.profileId,
|
|
4136
|
-
agentStub.id,
|
|
4137
|
-
agentStub.profileId,
|
|
4138
|
-
]);
|
|
2875
|
+
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
4139
2876
|
// Clear queued prompts when agent changes or is new;
|
|
4140
2877
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
4141
2878
|
useEffect(() => {
|
|
4142
|
-
if (!agent?.id ||
|
|
2879
|
+
if (!agent?.id || agent.status === "new") {
|
|
4143
2880
|
setQueuedPrompts([]);
|
|
4144
2881
|
}
|
|
4145
|
-
}, [agent?.id,
|
|
2882
|
+
}, [agent?.id, agent?.status]);
|
|
4146
2883
|
// Update selected model when the active profile or agent model changes
|
|
4147
2884
|
useEffect(() => {
|
|
4148
2885
|
if (!activeProfile)
|
|
@@ -4171,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4171
2908
|
// Initialize mode from metadata; fall back to agent.Mode from server
|
|
4172
2909
|
useEffect(() => {
|
|
4173
2910
|
try {
|
|
4174
|
-
const metaMode =
|
|
4175
|
-
if (metaMode
|
|
2911
|
+
const metaMode = agentMetadata?.mode;
|
|
2912
|
+
if (metaMode === "autonomous" ||
|
|
2913
|
+
metaMode === "read-only" ||
|
|
2914
|
+
metaMode === "supervised") {
|
|
4176
2915
|
setMode(metaMode);
|
|
4177
2916
|
return;
|
|
4178
2917
|
}
|
|
4179
2918
|
}
|
|
4180
2919
|
catch { }
|
|
4181
2920
|
try {
|
|
4182
|
-
const serverMode =
|
|
4183
|
-
if (serverMode
|
|
2921
|
+
const serverMode = agent?.mode;
|
|
2922
|
+
if (serverMode === "autonomous" ||
|
|
2923
|
+
serverMode === "read-only" ||
|
|
2924
|
+
serverMode === "supervised") {
|
|
4184
2925
|
setMode(serverMode);
|
|
4185
2926
|
}
|
|
4186
2927
|
}
|
|
@@ -4200,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4200
2941
|
textareaRef.current.focus();
|
|
4201
2942
|
}
|
|
4202
2943
|
}, [messages, activePlaceholderInput]);
|
|
4203
|
-
// Persist any pending settings (mode/model
|
|
2944
|
+
// Persist any pending settings (mode/model) once an agent exists server-side
|
|
4204
2945
|
const persistPendingSettingsIfNeeded = useCallback(async () => {
|
|
4205
2946
|
try {
|
|
4206
2947
|
if (!agent?.id)
|
|
@@ -4213,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4213
2954
|
payload.model = pending.modelName;
|
|
4214
2955
|
if (pending.mode)
|
|
4215
2956
|
payload.mode = pending.mode;
|
|
4216
|
-
if (pending.profileId)
|
|
4217
|
-
payload.profileId = pending.profileId;
|
|
4218
|
-
if (pending.profileName != null)
|
|
4219
|
-
payload.profileName = pending.profileName;
|
|
4220
2957
|
if (Object.keys(payload).length === 0)
|
|
4221
2958
|
return;
|
|
4222
2959
|
await updateAgentSettings(agent.id, payload);
|
|
@@ -4226,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4226
2963
|
console.error("Failed to persist pending settings", e);
|
|
4227
2964
|
}
|
|
4228
2965
|
}, [agent?.id]);
|
|
4229
|
-
const getPendingRequestSettings = useCallback(() => {
|
|
4230
|
-
const pending = pendingSettingsRef.current;
|
|
4231
|
-
const requestProfile = pending?.profileId
|
|
4232
|
-
? profiles.find((profile) => profile.id === pending.profileId) ||
|
|
4233
|
-
activeProfile ||
|
|
4234
|
-
profiles[0]
|
|
4235
|
-
: activeProfile || profiles[0];
|
|
4236
|
-
let requestModelId = selectedModelId;
|
|
4237
|
-
if (pending?.modelName) {
|
|
4238
|
-
const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
|
|
4239
|
-
pending.modelName?.trim().toLowerCase());
|
|
4240
|
-
requestModelId = requestModel?.id || requestModelId;
|
|
4241
|
-
}
|
|
4242
|
-
return {
|
|
4243
|
-
mode: (pending?.mode || mode),
|
|
4244
|
-
profileId: pending?.profileId || requestProfile?.id || "",
|
|
4245
|
-
profileName: pending?.profileName || requestProfile?.name || "",
|
|
4246
|
-
modelId: requestModelId,
|
|
4247
|
-
};
|
|
4248
|
-
}, [activeProfile, mode, profiles, selectedModelId]);
|
|
4249
|
-
const getSubmitErrorMessage = (error) => {
|
|
4250
|
-
const fallback = "Failed to submit prompt. Please try again.";
|
|
4251
|
-
if (!(error instanceof Error))
|
|
4252
|
-
return fallback;
|
|
4253
|
-
const cleaned = toUserFacingAgentErrorMessage(error.message);
|
|
4254
|
-
return cleaned || fallback;
|
|
4255
|
-
};
|
|
4256
|
-
const suppressedQueuedPromptsRef = useRef([]);
|
|
4257
|
-
const pruneSuppressedQueuedPrompts = useCallback(() => {
|
|
4258
|
-
const cutoff = Date.now() - 15_000;
|
|
4259
|
-
suppressedQueuedPromptsRef.current =
|
|
4260
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
|
|
4261
|
-
}, []);
|
|
4262
|
-
const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
|
|
4263
|
-
const normalizedAgentId = agentId.trim().toLowerCase();
|
|
4264
|
-
const normalizedPrompt = promptText.trim();
|
|
4265
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4266
|
-
return null;
|
|
4267
|
-
}
|
|
4268
|
-
pruneSuppressedQueuedPrompts();
|
|
4269
|
-
const token = crypto.randomUUID();
|
|
4270
|
-
suppressedQueuedPromptsRef.current = [
|
|
4271
|
-
...suppressedQueuedPromptsRef.current,
|
|
4272
|
-
{
|
|
4273
|
-
token,
|
|
4274
|
-
agentId: normalizedAgentId,
|
|
4275
|
-
prompt: normalizedPrompt,
|
|
4276
|
-
createdAt: Date.now(),
|
|
4277
|
-
},
|
|
4278
|
-
];
|
|
4279
|
-
return token;
|
|
4280
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4281
|
-
const clearSuppressedQueuedPrompt = useCallback((token) => {
|
|
4282
|
-
if (!token) {
|
|
4283
|
-
return;
|
|
4284
|
-
}
|
|
4285
|
-
suppressedQueuedPromptsRef.current =
|
|
4286
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
|
|
4287
|
-
}, []);
|
|
4288
|
-
const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
|
|
4289
|
-
const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
|
|
4290
|
-
const normalizedPrompt = queueEntry?.prompt?.trim();
|
|
4291
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4292
|
-
return false;
|
|
4293
|
-
}
|
|
4294
|
-
pruneSuppressedQueuedPrompts();
|
|
4295
|
-
const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
|
|
4296
|
-
entry.prompt === normalizedPrompt);
|
|
4297
|
-
if (!matchedEntry) {
|
|
4298
|
-
return false;
|
|
4299
|
-
}
|
|
4300
|
-
suppressedQueuedPromptsRef.current =
|
|
4301
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
|
|
4302
|
-
return true;
|
|
4303
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4304
|
-
const cancelActiveInlineDialog = useCallback(() => {
|
|
4305
|
-
const activeDialog = activeInlineDialogRef.current;
|
|
4306
|
-
if (!activeDialog)
|
|
4307
|
-
return;
|
|
4308
|
-
try {
|
|
4309
|
-
activeDialog.onCancel();
|
|
4310
|
-
}
|
|
4311
|
-
finally {
|
|
4312
|
-
setActiveInlineDialog(null);
|
|
4313
|
-
}
|
|
4314
|
-
}, []);
|
|
4315
2966
|
const handleSubmit = async () => {
|
|
4316
2967
|
// Guard against double-submit and missing context
|
|
4317
2968
|
if (isSubmitting) {
|
|
@@ -4355,12 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4355
3006
|
setError("Agent not ready. Please try again.");
|
|
4356
3007
|
return;
|
|
4357
3008
|
}
|
|
4358
|
-
const hadQuestionnaireDialogOpen = activeInlineDialogRef.current?.request.dialogType === "questionnaire";
|
|
4359
|
-
const suppressedQueuedPromptToken = hadQuestionnaireDialogOpen && savedPrompt
|
|
4360
|
-
? registerSuppressedQueuedPrompt(agentId, savedPrompt)
|
|
4361
|
-
: null;
|
|
4362
|
-
// A new user prompt supersedes any active questionnaire/inline dialog.
|
|
4363
|
-
cancelActiveInlineDialog();
|
|
4364
3009
|
// Generate a temporary ID for optimistic UI - will be replaced by server ID
|
|
4365
3010
|
const tempMessageId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
4366
3011
|
try {
|
|
@@ -4447,24 +3092,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4447
3092
|
console.warn("[AgentTerminal] Failed to compute live context:", e);
|
|
4448
3093
|
}
|
|
4449
3094
|
// Add visible test IDs to context (only for Help agent)
|
|
4450
|
-
const
|
|
4451
|
-
const currentProfileName = requestSettings.profileName;
|
|
3095
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4452
3096
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4453
3097
|
const request = {
|
|
4454
3098
|
agentId: agentId,
|
|
4455
3099
|
message: savedPrompt,
|
|
4456
3100
|
sessionId: editContext.sessionId,
|
|
4457
|
-
profileId:
|
|
3101
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4458
3102
|
profile: currentProfileName,
|
|
4459
|
-
model:
|
|
4460
|
-
mode:
|
|
3103
|
+
model: selectedModelId,
|
|
3104
|
+
mode: mode,
|
|
4461
3105
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3106
|
+
deterministic: deterministicFlags.deterministic,
|
|
3107
|
+
seed: deterministicFlags.seed,
|
|
4462
3108
|
};
|
|
4463
3109
|
console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
|
|
4464
3110
|
const response = await startAgent(request);
|
|
4465
3111
|
console.log("[AgentTerminal] startAgent response:", response);
|
|
4466
3112
|
// Check if prompt was queued (agent was already running)
|
|
4467
|
-
const wasQueued = response.message?.toLowerCase().includes("queued")
|
|
3113
|
+
const wasQueued = response.message?.toLowerCase().includes("queued") ||
|
|
3114
|
+
response.status === "Queued";
|
|
4468
3115
|
if (wasQueued) {
|
|
4469
3116
|
// Prompt was queued - show a brief notification but don't set waiting state
|
|
4470
3117
|
// The prompt will be processed when the agent becomes idle
|
|
@@ -4474,7 +3121,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4474
3121
|
isWaitingRef.current = false;
|
|
4475
3122
|
}
|
|
4476
3123
|
else {
|
|
4477
|
-
clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
|
|
4478
3124
|
// Normal submission - set waiting state to show dancing dots immediately
|
|
4479
3125
|
setIsWaitingForResponse(true);
|
|
4480
3126
|
isWaitingRef.current = true;
|
|
@@ -4489,13 +3135,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4489
3135
|
...prev.filter((p) => p !== savedPrompt).slice(0, 9),
|
|
4490
3136
|
]);
|
|
4491
3137
|
setCurrentHistoryIndex(-1);
|
|
4492
|
-
await onInteractionSubmitted?.();
|
|
4493
3138
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4494
3139
|
}
|
|
4495
3140
|
catch (err) {
|
|
4496
3141
|
console.error("[AgentTerminal] Failed to submit prompt:", err);
|
|
4497
|
-
|
|
4498
|
-
setError(getSubmitErrorMessage(err));
|
|
3142
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4499
3143
|
setIsWaitingForResponse(false);
|
|
4500
3144
|
isWaitingRef.current = false;
|
|
4501
3145
|
// Remove the optimistic user message on API error
|
|
@@ -4705,29 +3349,29 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4705
3349
|
console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
|
|
4706
3350
|
}
|
|
4707
3351
|
// Add visible test IDs to context (only for Help agent)
|
|
4708
|
-
const
|
|
4709
|
-
const currentProfileName = requestSettings.profileName;
|
|
3352
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4710
3353
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4711
3354
|
const request = {
|
|
4712
3355
|
agentId: agent.id,
|
|
4713
3356
|
message: savedPrompt,
|
|
4714
3357
|
sessionId: editContext.sessionId,
|
|
4715
|
-
profileId:
|
|
3358
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4716
3359
|
profile: currentProfileName,
|
|
4717
|
-
model:
|
|
4718
|
-
mode:
|
|
3360
|
+
model: selectedModelId,
|
|
3361
|
+
mode: mode,
|
|
4719
3362
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3363
|
+
deterministic: deterministicFlags.deterministic,
|
|
3364
|
+
seed: deterministicFlags.seed,
|
|
4720
3365
|
};
|
|
4721
3366
|
console.log("[AgentTerminal] Calling startAgent API for quick message");
|
|
4722
3367
|
await startAgent(request);
|
|
4723
3368
|
// If user changed mode/model while the agent was new, persist them now
|
|
4724
3369
|
await persistPendingSettingsIfNeeded();
|
|
4725
|
-
await onInteractionSubmitted?.();
|
|
4726
3370
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4727
3371
|
}
|
|
4728
3372
|
catch (err) {
|
|
4729
3373
|
console.error("[AgentTerminal] Failed to submit quick message:", err);
|
|
4730
|
-
setError(
|
|
3374
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4731
3375
|
setIsWaitingForResponse(false);
|
|
4732
3376
|
isWaitingRef.current = false;
|
|
4733
3377
|
// Remove the optimistic user message on API error
|
|
@@ -4905,90 +3549,66 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4905
3549
|
}
|
|
4906
3550
|
return context;
|
|
4907
3551
|
}, [collectVisibleTestIds]);
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
name: editContext?.item?.name,
|
|
4913
|
-
path: editContext?.item?.path,
|
|
4914
|
-
}), [editContext?.item?.name, editContext?.item?.path]);
|
|
4915
|
-
const buildComponentContext = useCallback((item) => {
|
|
4916
|
-
if (!editContext?.selection?.length)
|
|
4917
|
-
return undefined;
|
|
4918
|
-
return editContext.selection.map((componentId) => {
|
|
4919
|
-
let componentName;
|
|
4920
|
-
let componentType;
|
|
4921
|
-
let renderingItemId;
|
|
4922
|
-
if (editContext.page) {
|
|
4923
|
-
try {
|
|
4924
|
-
const component = getComponentById(componentId, editContext.page);
|
|
4925
|
-
componentName =
|
|
4926
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4927
|
-
componentType = component?.type || undefined;
|
|
4928
|
-
renderingItemId = component?.rendering?.id || undefined;
|
|
4929
|
-
}
|
|
4930
|
-
catch { }
|
|
4931
|
-
}
|
|
4932
|
-
return {
|
|
4933
|
-
componentId,
|
|
4934
|
-
componentName,
|
|
4935
|
-
componentType,
|
|
4936
|
-
renderingItemId,
|
|
4937
|
-
pageItem: buildPageContextItem(item),
|
|
4938
|
-
};
|
|
4939
|
-
});
|
|
4940
|
-
}, [buildPageContextItem, editContext?.page, editContext?.selection]);
|
|
4941
|
-
const buildFieldContext = useCallback(() => {
|
|
4942
|
-
const focusedField = fieldsContext?.focusedField;
|
|
4943
|
-
if (!focusedField?.fieldId || !focusedField?.item?.id)
|
|
4944
|
-
return undefined;
|
|
4945
|
-
const fieldItem = focusedField.item;
|
|
4946
|
-
const currentItem = editContext?.currentItemDescriptor;
|
|
4947
|
-
let fieldItemName = fieldItem.id === currentItem?.id ? editContext?.item?.name : undefined;
|
|
4948
|
-
if (!fieldItemName && editContext?.page) {
|
|
4949
|
-
try {
|
|
4950
|
-
const component = getComponentById(fieldItem.id, editContext.page);
|
|
4951
|
-
fieldItemName =
|
|
4952
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4953
|
-
}
|
|
4954
|
-
catch { }
|
|
4955
|
-
}
|
|
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;
|
|
4956
3556
|
return {
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
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,
|
|
4965
3602
|
};
|
|
4966
3603
|
}, [
|
|
4967
3604
|
editContext?.currentItemDescriptor,
|
|
3605
|
+
editContext?.selection,
|
|
3606
|
+
editContext?.workspaceId,
|
|
4968
3607
|
editContext?.item?.name,
|
|
4969
|
-
editContext?.
|
|
3608
|
+
editContext?.activeSlotId,
|
|
3609
|
+
editContext?.openSidebars,
|
|
4970
3610
|
fieldsContext?.focusedField,
|
|
4971
3611
|
]);
|
|
4972
|
-
const buildEditorContextPayload = useCallback((item) => ({
|
|
4973
|
-
items: item ? [buildPageContextItem(item)] : undefined,
|
|
4974
|
-
currentItemId: item?.id,
|
|
4975
|
-
components: item ? buildComponentContext(item) : undefined,
|
|
4976
|
-
field: buildFieldContext(),
|
|
4977
|
-
activeWorkspace: editContext?.workspaceId,
|
|
4978
|
-
hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
|
|
4979
|
-
openSidebars: editContext?.openSidebars,
|
|
4980
|
-
}), [
|
|
4981
|
-
buildComponentContext,
|
|
4982
|
-
buildFieldContext,
|
|
4983
|
-
buildPageContextItem,
|
|
4984
|
-
editContext,
|
|
4985
|
-
]);
|
|
4986
|
-
// Helper function to build current context from editor state
|
|
4987
|
-
const buildCurrentContext = useCallback(() => {
|
|
4988
|
-
// Return context even without item - we want view info regardless
|
|
4989
|
-
const item = editContext?.currentItemDescriptor;
|
|
4990
|
-
return buildEditorContextPayload(item);
|
|
4991
|
-
}, [buildEditorContextPayload, editContext?.currentItemDescriptor]);
|
|
4992
3612
|
// Live context updates: watch for changes and update agent context when in "live" mode
|
|
4993
3613
|
const previousContextRef = useRef("");
|
|
4994
3614
|
useEffect(() => {
|
|
@@ -5088,15 +3708,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5088
3708
|
const handleRefreshContext = useCallback(async () => {
|
|
5089
3709
|
if (!agent?.id)
|
|
5090
3710
|
return;
|
|
5091
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5092
|
-
const refreshProfile = activeProfile ||
|
|
5093
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5094
|
-
if (refreshProfile?.editorContextMode === "none") {
|
|
5095
|
-
editContext?.showInfoToast({
|
|
5096
|
-
summary: "This profile excludes editor context.",
|
|
5097
|
-
});
|
|
5098
|
-
return;
|
|
5099
|
-
}
|
|
5100
3711
|
try {
|
|
5101
3712
|
const currentCtx = buildCurrentContext();
|
|
5102
3713
|
if (!currentCtx) {
|
|
@@ -5137,73 +3748,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5137
3748
|
buildCurrentContext,
|
|
5138
3749
|
sanitizeAgentMetadata,
|
|
5139
3750
|
editContext,
|
|
5140
|
-
activeProfile,
|
|
5141
|
-
profiles,
|
|
5142
3751
|
]);
|
|
5143
|
-
const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
|
|
5144
|
-
const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
5145
|
-
pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
5146
|
-
const currentSessionId = editContext?.sessionId?.trim() || "";
|
|
5147
|
-
const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
|
|
5148
|
-
const isClaimedByCurrentSession = !!currentSessionId &&
|
|
5149
|
-
!!claimedSessionId &&
|
|
5150
|
-
currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
|
|
5151
|
-
const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
|
|
5152
|
-
const handleClaimBrowser = useCallback(async (takeOver) => {
|
|
5153
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5154
|
-
return;
|
|
5155
|
-
setIsBrowserClaimMutationPending(true);
|
|
5156
|
-
try {
|
|
5157
|
-
const response = await claimAgentBrowser({
|
|
5158
|
-
agentId: agent.id,
|
|
5159
|
-
sessionId: editContext.sessionId,
|
|
5160
|
-
takeOver,
|
|
5161
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5162
|
-
});
|
|
5163
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5164
|
-
}
|
|
5165
|
-
catch (err) {
|
|
5166
|
-
console.error("[AgentTerminal] Failed to claim browser:", err);
|
|
5167
|
-
editContext.showErrorToast(err);
|
|
5168
|
-
}
|
|
5169
|
-
finally {
|
|
5170
|
-
setIsBrowserClaimMutationPending(false);
|
|
5171
|
-
}
|
|
5172
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5173
|
-
const handleReleaseBrowser = useCallback(async () => {
|
|
5174
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5175
|
-
return;
|
|
5176
|
-
setIsBrowserClaimMutationPending(true);
|
|
5177
|
-
try {
|
|
5178
|
-
const response = await releaseAgentBrowser({
|
|
5179
|
-
agentId: agent.id,
|
|
5180
|
-
sessionId: editContext.sessionId,
|
|
5181
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5182
|
-
});
|
|
5183
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5184
|
-
}
|
|
5185
|
-
catch (err) {
|
|
5186
|
-
console.error("[AgentTerminal] Failed to release browser:", err);
|
|
5187
|
-
editContext.showErrorToast(err);
|
|
5188
|
-
}
|
|
5189
|
-
finally {
|
|
5190
|
-
setIsBrowserClaimMutationPending(false);
|
|
5191
|
-
}
|
|
5192
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5193
|
-
useEffect(() => {
|
|
5194
|
-
return () => {
|
|
5195
|
-
if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
|
|
5196
|
-
return;
|
|
5197
|
-
}
|
|
5198
|
-
void releaseAgentBrowser({
|
|
5199
|
-
agentId: agent.id,
|
|
5200
|
-
sessionId: editContext.sessionId,
|
|
5201
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5202
|
-
}).catch((error) => {
|
|
5203
|
-
console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
|
|
5204
|
-
});
|
|
5205
|
-
};
|
|
5206
|
-
}, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
|
|
5207
3752
|
// Stop current execution/stream safely
|
|
5208
3753
|
const handleStop = useCallback(async () => {
|
|
5209
3754
|
try {
|
|
@@ -5267,81 +3812,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5267
3812
|
if (effectiveCostLimit === undefined) {
|
|
5268
3813
|
effectiveCostLimit = undefined;
|
|
5269
3814
|
}
|
|
5270
|
-
// Calculate total token usage for cost display
|
|
5271
|
-
|
|
5272
|
-
// to the persisted agent aggregate for that single field.
|
|
5273
|
-
const totalTokens = (() => {
|
|
5274
|
-
const totals = calculateTotalTokens(messages);
|
|
5275
|
-
return {
|
|
5276
|
-
...totals,
|
|
5277
|
-
imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
|
|
5278
|
-
};
|
|
5279
|
-
})();
|
|
3815
|
+
// Calculate total token usage for cost display
|
|
3816
|
+
const totalTokens = calculateTotalTokens(messages);
|
|
5280
3817
|
// Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
|
|
5281
3818
|
const isExecuting = isSubmitting ||
|
|
5282
3819
|
isConnecting ||
|
|
5283
3820
|
isWaitingForResponse ||
|
|
5284
3821
|
hasActiveStreaming();
|
|
5285
|
-
const assistantMessageCount = useMemo(() => messages.filter((message) => message.role === "assistant").length, [messages]);
|
|
5286
|
-
const messagesWithToolCallsCount = useMemo(() => messages.filter((message) => (message.toolCalls || []).length > 0).length, [messages]);
|
|
5287
|
-
const totalToolCallCount = useMemo(() => messages.reduce((sum, message) => sum + (message.toolCalls?.length || 0), 0), [messages]);
|
|
5288
|
-
const incompleteToolCallCount = useMemo(() => messages.reduce((sum, message) => sum +
|
|
5289
|
-
(message.toolCalls?.filter((toolCall) => !toolCall.isCompleted).length || 0), 0), [messages]);
|
|
5290
|
-
const assistantGroupsWithRenderableToolCalls = useMemo(() => {
|
|
5291
|
-
const groups = groupConsecutiveMessages(messages);
|
|
5292
|
-
return groups.filter((group) => {
|
|
5293
|
-
if (group.type !== "assistant-group")
|
|
5294
|
-
return false;
|
|
5295
|
-
const convertedMessages = convertAgentMessagesToAiFormat(group.messages);
|
|
5296
|
-
return convertedMessages.some((message) => (message.tool_calls?.length || 0) > 0);
|
|
5297
|
-
}).length;
|
|
5298
|
-
}, [messages]);
|
|
5299
|
-
const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
|
|
5300
|
-
const runDiagnosticsSnapshot = useMemo(() => {
|
|
5301
|
-
const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
|
|
5302
|
-
return {
|
|
5303
|
-
agentId: currentAgentId,
|
|
5304
|
-
isSubmitting,
|
|
5305
|
-
isConnecting,
|
|
5306
|
-
isWaitingForResponse,
|
|
5307
|
-
isAgentThinking,
|
|
5308
|
-
isExecuting,
|
|
5309
|
-
hasActiveStreaming: hasActiveStreaming(),
|
|
5310
|
-
isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
|
|
5311
|
-
normalizeDialogAgentId(currentAgentId),
|
|
5312
|
-
lastSeq: lastSeqRef.current,
|
|
5313
|
-
lastEventType: lastEvent?.type ?? null,
|
|
5314
|
-
lastEventAt: lastEvent?.timestamp ?? null,
|
|
5315
|
-
recentEvents: recentAgentRunEvents,
|
|
5316
|
-
assistantMessageCount,
|
|
5317
|
-
assistantGroupCount,
|
|
5318
|
-
assistantGroupsWithRenderableToolCalls,
|
|
5319
|
-
messagesWithToolCalls: messagesWithToolCallsCount,
|
|
5320
|
-
totalToolCallCount,
|
|
5321
|
-
incompleteToolCallCount,
|
|
5322
|
-
recentToolUiEvents,
|
|
5323
|
-
};
|
|
5324
|
-
}, [
|
|
5325
|
-
assistantGroupCount,
|
|
5326
|
-
assistantGroupsWithRenderableToolCalls,
|
|
5327
|
-
assistantMessageCount,
|
|
5328
|
-
currentAgentId,
|
|
5329
|
-
hasActiveStreaming,
|
|
5330
|
-
incompleteToolCallCount,
|
|
5331
|
-
isAgentThinking,
|
|
5332
|
-
isConnecting,
|
|
5333
|
-
isExecuting,
|
|
5334
|
-
isSubmitting,
|
|
5335
|
-
isWaitingForResponse,
|
|
5336
|
-
messagesWithToolCallsCount,
|
|
5337
|
-
recentAgentRunEvents,
|
|
5338
|
-
recentToolUiEvents,
|
|
5339
|
-
totalToolCallCount,
|
|
5340
|
-
]);
|
|
5341
|
-
const showInitialThinkingSplash = messages.length === 0 &&
|
|
5342
|
-
!error &&
|
|
5343
|
-
hideGreeting &&
|
|
5344
|
-
(isSubmitting || isConnecting);
|
|
5345
3822
|
// Compute dots visibility: only show BEFORE any assistant message exists
|
|
5346
3823
|
// This prevents duplicate headers - the dots indicator has its own header,
|
|
5347
3824
|
// and we don't want to show a second header below existing messages
|
|
@@ -5356,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5356
3833
|
// The message with the pending approval will display its own UI for approval
|
|
5357
3834
|
if (allPendingApprovals.length > 0)
|
|
5358
3835
|
return false;
|
|
5359
|
-
// The hidden-greeting startup splash already renders its own bouncing dots.
|
|
5360
|
-
// Suppress the generic indicator so reopening/running terminals don't show two.
|
|
5361
|
-
if (showInitialThinkingSplash)
|
|
5362
|
-
return false;
|
|
5363
3836
|
// IMPORTANT: If the last message is an assistant message and we're still executing,
|
|
5364
3837
|
// the AiResponseMessage for that message will show its own activity indicator.
|
|
5365
3838
|
// We only want these global thinking dots if the last message was from the user
|
|
5366
3839
|
// or if no messages exist yet (waiting for initial response).
|
|
5367
3840
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
5368
|
-
if (isExecuting &&
|
|
5369
|
-
lastMessage?.role === "assistant" &&
|
|
5370
|
-
!lastMessage.isCompleted) {
|
|
3841
|
+
if (isExecuting && lastMessage?.role === "assistant")
|
|
5371
3842
|
return false;
|
|
5372
|
-
}
|
|
5373
3843
|
// Existing check for uncompleted assistant messages
|
|
5374
3844
|
const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
|
|
5375
3845
|
if (hasActiveStreamingMessage)
|
|
@@ -5385,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5385
3855
|
messages,
|
|
5386
3856
|
activeInlineDialog,
|
|
5387
3857
|
allPendingApprovals,
|
|
5388
|
-
showInitialThinkingSplash,
|
|
5389
3858
|
]);
|
|
5390
3859
|
// Move useMemo hook before early return to comply with Rules of Hooks
|
|
5391
|
-
const
|
|
3860
|
+
const isLiveEditorContextMode = React.useMemo(() => {
|
|
5392
3861
|
try {
|
|
5393
3862
|
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5394
3863
|
const profile = activeProfile ||
|
|
5395
3864
|
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5396
|
-
|
|
3865
|
+
const mode = profile?.editorContextMode;
|
|
3866
|
+
return mode === "live";
|
|
5397
3867
|
}
|
|
5398
3868
|
catch {
|
|
5399
|
-
return
|
|
3869
|
+
return false;
|
|
5400
3870
|
}
|
|
5401
3871
|
}, [activeProfile, profiles, agent?.profileId]);
|
|
5402
|
-
const isLiveEditorContextMode = resolvedEditorContextMode === "live";
|
|
5403
|
-
const omitsEditorContext = resolvedEditorContextMode === "none";
|
|
5404
3872
|
// Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
|
|
5405
3873
|
const parentAgentId = agent?.parentAgentId ||
|
|
5406
3874
|
agent?.ParentAgentId ||
|
|
@@ -5414,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5414
3882
|
detail: { agentId: parentAgentId },
|
|
5415
3883
|
}));
|
|
5416
3884
|
}, [parentAgentId]);
|
|
5417
|
-
|
|
5418
|
-
|
|
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 }));
|
|
5419
3889
|
const renderCostLimitBanner = () => {
|
|
5420
3890
|
if (!costLimitExceeded)
|
|
5421
3891
|
return null;
|
|
@@ -5426,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5426
3896
|
try {
|
|
5427
3897
|
// Extend cost limit - backend will automatically resume the agent
|
|
5428
3898
|
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
5429
|
-
// Update the agent's cost limit
|
|
5430
|
-
// status in local state so the useEffect watcher doesn't
|
|
5431
|
-
// immediately re-show the banner before the backend status
|
|
5432
|
-
// update arrives.
|
|
3899
|
+
// Update the agent's cost limit in local state
|
|
5433
3900
|
if (result.success && result.costLimit !== undefined) {
|
|
5434
|
-
setAgent((prev) => prev
|
|
5435
|
-
? {
|
|
5436
|
-
...prev,
|
|
5437
|
-
costLimit: result.costLimit,
|
|
5438
|
-
status: prev.status === "costLimitReached"
|
|
5439
|
-
? "running"
|
|
5440
|
-
: prev.status,
|
|
5441
|
-
}
|
|
5442
|
-
: prev);
|
|
3901
|
+
setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
|
|
5443
3902
|
}
|
|
5444
3903
|
// Clear the banner and set waiting state
|
|
5445
3904
|
// Agent will resume automatically via backend's ResumeAgentAsync
|
|
@@ -5459,257 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5459
3918
|
};
|
|
5460
3919
|
const renderErrorBanner = () => {
|
|
5461
3920
|
const currentAgent = agent || agentStub;
|
|
5462
|
-
const isErrorStatus = currentAgent?.status === "error";
|
|
5463
|
-
const
|
|
5464
|
-
|
|
5465
|
-
// Show error banner for error status, or for any terminal status that still
|
|
5466
|
-
// carries a statusMessage (e.g. agent closed after an error).
|
|
5467
|
-
const isTerminalWithError = !isErrorStatus &&
|
|
5468
|
-
!!currentAgent?.statusMessage &&
|
|
5469
|
-
currentAgent?.status !== "running" &&
|
|
5470
|
-
currentAgent?.status !== "new" &&
|
|
5471
|
-
!isWaitingForInputStatus &&
|
|
5472
|
-
!isWaitingForApprovalStatus;
|
|
5473
|
-
const rawErrorMessage = (isErrorStatus || isTerminalWithError
|
|
5474
|
-
? currentAgent?.statusMessage
|
|
5475
|
-
: null) || error;
|
|
5476
|
-
if (!rawErrorMessage)
|
|
3921
|
+
const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
|
|
3922
|
+
const errorMessage = currentAgent?.statusMessage;
|
|
3923
|
+
if (!isErrorStatus || !errorMessage)
|
|
5477
3924
|
return null;
|
|
5478
|
-
|
|
5479
|
-
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5480
|
-
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 })] })] }) }));
|
|
5481
3926
|
};
|
|
5482
|
-
|
|
5483
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5484
|
-
return null;
|
|
5485
|
-
if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
|
|
5486
|
-
return null;
|
|
5487
|
-
}
|
|
5488
|
-
if (isPendingBrowserCaptureWait) {
|
|
5489
|
-
return null;
|
|
5490
|
-
}
|
|
5491
|
-
const label = isClaimedByCurrentSession
|
|
5492
|
-
? "Attached to this browser"
|
|
5493
|
-
: isClaimedByAnotherBrowser
|
|
5494
|
-
? "Attached in another browser"
|
|
5495
|
-
: "No browser attached";
|
|
5496
|
-
const description = isClaimedByCurrentSession
|
|
5497
|
-
? "This browser will handle page screenshot and DOM capture requests for the agent."
|
|
5498
|
-
: isClaimedByAnotherBrowser
|
|
5499
|
-
? "Capture requests will stay with the other browser until you take over control here."
|
|
5500
|
-
: "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
|
|
5501
|
-
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");
|
|
5502
|
-
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: () => {
|
|
5503
|
-
void handleReleaseBrowser();
|
|
5504
|
-
}, 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: () => {
|
|
5505
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5506
|
-
}, children: isClaimedByAnotherBrowser
|
|
5507
|
-
? "Take over browser control"
|
|
5508
|
-
: "Attach to this browser" })) })] }) }));
|
|
5509
|
-
};
|
|
5510
|
-
const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
|
|
5511
|
-
const inlineBrowserClaimBanner = null;
|
|
5512
|
-
const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
|
|
5513
|
-
? {
|
|
5514
|
-
toolNames: [
|
|
5515
|
-
"capture-page-screenshot",
|
|
5516
|
-
"capture-parhelia-ui-screenshot",
|
|
5517
|
-
"capture-page-dom",
|
|
5518
|
-
],
|
|
5519
|
-
label: isClaimedByAnotherBrowser
|
|
5520
|
-
? "Attached in another browser"
|
|
5521
|
-
: "No browser attached",
|
|
5522
|
-
description: isClaimedByAnotherBrowser
|
|
5523
|
-
? "This capture request is waiting in another browser. Take over browser control here to continue."
|
|
5524
|
-
: "This capture request is waiting for a browser attachment. Attach this browser to continue.",
|
|
5525
|
-
actionLabel: isClaimedByAnotherBrowser
|
|
5526
|
-
? "Take over browser control"
|
|
5527
|
-
: "Attach to this browser",
|
|
5528
|
-
isPending: isBrowserClaimMutationPending,
|
|
5529
|
-
onAction: () => {
|
|
5530
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5531
|
-
},
|
|
5532
|
-
}
|
|
5533
|
-
: null;
|
|
5534
|
-
useEffect(() => {
|
|
5535
|
-
if (agent?.status !== "waitingForInput") {
|
|
5536
|
-
setPendingBrowserCaptureDialogType(null);
|
|
5537
|
-
}
|
|
5538
|
-
}, [agent?.status]);
|
|
5539
|
-
const renderInlineDialogContent = () => {
|
|
5540
|
-
if (!activeInlineDialog)
|
|
5541
|
-
return null;
|
|
5542
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5543
|
-
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) => {
|
|
5544
|
-
activeInlineDialog.onComplete(result);
|
|
5545
|
-
setActiveInlineDialog(null);
|
|
5546
|
-
void onInteractionSubmitted?.();
|
|
5547
|
-
}, onCancel: () => {
|
|
5548
|
-
activeInlineDialog.onCancel();
|
|
5549
|
-
setActiveInlineDialog(null);
|
|
5550
|
-
} }) }));
|
|
5551
|
-
}
|
|
5552
|
-
const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
|
|
5553
|
-
if (dialogRegistration) {
|
|
5554
|
-
const DialogComponent = dialogRegistration.component;
|
|
5555
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
|
|
5556
|
-
activeInlineDialog.onComplete(result);
|
|
5557
|
-
setActiveInlineDialog(null);
|
|
5558
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5559
|
-
void onInteractionSubmitted?.();
|
|
5560
|
-
}
|
|
5561
|
-
}, onCancel: () => {
|
|
5562
|
-
activeInlineDialog.onCancel();
|
|
5563
|
-
setActiveInlineDialog(null);
|
|
5564
|
-
} }) }));
|
|
5565
|
-
}
|
|
5566
|
-
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] }) }));
|
|
5567
|
-
};
|
|
5568
|
-
const latestSummaryAssistantGroup = useMemo(() => {
|
|
5569
|
-
if (hideSummaryMessages)
|
|
5570
|
-
return null;
|
|
5571
|
-
const groups = groupConsecutiveMessages(messages);
|
|
5572
|
-
for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
|
|
5573
|
-
const group = groups[groupIndex];
|
|
5574
|
-
if (!group || group.type !== "assistant-group")
|
|
5575
|
-
continue;
|
|
5576
|
-
const filteredMessages = group.messages.filter((msg) => {
|
|
5577
|
-
const content = msg.content || "";
|
|
5578
|
-
return !content.startsWith("⚠️") || !content.includes("Cost limit");
|
|
5579
|
-
});
|
|
5580
|
-
if (filteredMessages.length === 0)
|
|
5581
|
-
continue;
|
|
5582
|
-
return {
|
|
5583
|
-
messages: filteredMessages,
|
|
5584
|
-
isLastGroup: groupIndex === groups.length - 1,
|
|
5585
|
-
};
|
|
5586
|
-
}
|
|
5587
|
-
return null;
|
|
5588
|
-
}, [messages, hideSummaryMessages]);
|
|
5589
|
-
const summaryModeContent = displayMode === "summary"
|
|
5590
|
-
? (() => {
|
|
5591
|
-
const inlineDialog = renderInlineDialogContent();
|
|
5592
|
-
const summaryMessages = latestSummaryAssistantGroup
|
|
5593
|
-
? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
|
|
5594
|
-
: [];
|
|
5595
|
-
const summaryOperations = latestSummaryAssistantGroup
|
|
5596
|
-
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
5597
|
-
: [];
|
|
5598
|
-
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 &&
|
|
5599
|
-
!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: {
|
|
5600
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5601
|
-
} })) : (_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 ||
|
|
5602
|
-
activeProfile?.displayTitle ||
|
|
5603
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5604
|
-
const text = (action.prompt ||
|
|
5605
|
-
action.value ||
|
|
5606
|
-
action.label ||
|
|
5607
|
-
"").trim();
|
|
5608
|
-
if (!text)
|
|
5609
|
-
return;
|
|
5610
|
-
if (isExecuting) {
|
|
5611
|
-
try {
|
|
5612
|
-
handleStop();
|
|
5613
|
-
}
|
|
5614
|
-
catch { }
|
|
5615
|
-
}
|
|
5616
|
-
sendQuickMessage(text);
|
|
5617
|
-
} }) })) : 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
|
|
5618
|
-
? "The agent is still working. The next update will appear here automatically."
|
|
5619
|
-
: agent?.statusMessage ||
|
|
5620
|
-
summaryPlaceholderMessage ||
|
|
5621
|
-
"Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
|
|
5622
|
-
shouldShowThinkingDots &&
|
|
5623
|
-
!inlineDialog &&
|
|
5624
|
-
!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: {
|
|
5625
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5626
|
-
} })) : (_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 ||
|
|
5627
|
-
activeProfile?.displayTitle ||
|
|
5628
|
-
activeProfile?.name ||
|
|
5629
|
-
"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) => {
|
|
5630
|
-
setActivePlaceholderInput(null);
|
|
5631
|
-
setAllPlaceholdersFilled(false);
|
|
5632
|
-
if (activePlaceholderInput.behavior === "compose" &&
|
|
5633
|
-
!hideBottomControls) {
|
|
5634
|
-
setPrompt(filledText);
|
|
5635
|
-
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
5636
|
-
if (textareaRef.current) {
|
|
5637
|
-
try {
|
|
5638
|
-
textareaRef.current.focus();
|
|
5639
|
-
const v = textareaRef.current.value || "";
|
|
5640
|
-
textareaRef.current.selectionStart = v.length;
|
|
5641
|
-
textareaRef.current.selectionEnd = v.length;
|
|
5642
|
-
}
|
|
5643
|
-
catch { }
|
|
5644
|
-
}
|
|
5645
|
-
}
|
|
5646
|
-
else {
|
|
5647
|
-
if (isExecuting) {
|
|
5648
|
-
try {
|
|
5649
|
-
handleStop();
|
|
5650
|
-
}
|
|
5651
|
-
catch { }
|
|
5652
|
-
}
|
|
5653
|
-
sendQuickMessage(filledText);
|
|
5654
|
-
}
|
|
5655
|
-
}, onCancel: () => {
|
|
5656
|
-
setActivePlaceholderInput(null);
|
|
5657
|
-
setAllPlaceholdersFilled(false);
|
|
5658
|
-
} })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5659
|
-
setPrompt(filledText);
|
|
5660
|
-
setAllPlaceholdersFilled(false);
|
|
5661
|
-
if (filledText.trim()) {
|
|
5662
|
-
if (isExecuting) {
|
|
5663
|
-
try {
|
|
5664
|
-
handleStop();
|
|
5665
|
-
}
|
|
5666
|
-
catch { }
|
|
5667
|
-
}
|
|
5668
|
-
sendQuickMessage(filledText);
|
|
5669
|
-
}
|
|
5670
|
-
}, onCancel: () => {
|
|
5671
|
-
setPrompt("");
|
|
5672
|
-
setAllPlaceholdersFilled(false);
|
|
5673
|
-
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
5674
|
-
} })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
|
|
5675
|
-
setPrompt(e.target.value);
|
|
5676
|
-
if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
|
|
5677
|
-
setAllPlaceholdersFilled(false);
|
|
5678
|
-
}
|
|
5679
|
-
if (currentHistoryIndex !== -1) {
|
|
5680
|
-
setCurrentHistoryIndex(-1);
|
|
5681
|
-
}
|
|
5682
|
-
}, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
|
|
5683
|
-
shouldMaintainFocusRef.current = true;
|
|
5684
|
-
}, onBlur: () => {
|
|
5685
|
-
shouldMaintainFocusRef.current = false;
|
|
5686
|
-
}, 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 }) })), (() => {
|
|
5687
|
-
const isInPlaceholderMode = activePlaceholderInput ||
|
|
5688
|
-
(prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
|
|
5689
|
-
const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
|
|
5690
|
-
if (placeholderShowsOwnButtons)
|
|
5691
|
-
return null;
|
|
5692
|
-
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
|
|
5693
|
-
simpleMode ||
|
|
5694
|
-
isInPlaceholderMode
|
|
5695
|
-
? "justify-end"
|
|
5696
|
-
: "justify-between"), children: [!hideBottomControls &&
|
|
5697
|
-
!simpleMode &&
|
|
5698
|
-
!isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
|
|
5699
|
-
if (isExecuting) {
|
|
5700
|
-
handleStop();
|
|
5701
|
-
}
|
|
5702
|
-
else {
|
|
5703
|
-
handleSubmit();
|
|
5704
|
-
}
|
|
5705
|
-
}, disabled: !isExecuting &&
|
|
5706
|
-
!activePlaceholderInput &&
|
|
5707
|
-
(!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
|
|
5708
|
-
})()] })) : null] }));
|
|
5709
|
-
})()
|
|
5710
|
-
: null;
|
|
5711
|
-
const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
|
|
5712
|
-
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) => {
|
|
5713
3928
|
setPrompt(p);
|
|
5714
3929
|
// Use setTimeout to ensure state is updated before submission
|
|
5715
3930
|
setTimeout(() => {
|
|
@@ -5722,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5722
3937
|
handleSubmit();
|
|
5723
3938
|
}
|
|
5724
3939
|
}, 0);
|
|
5725
|
-
} })) })),
|
|
5726
|
-
|
|
5727
|
-
|
|
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: [(() => {
|
|
5728
3946
|
const groups = groupConsecutiveMessages(messages);
|
|
5729
3947
|
return groups.map((group, groupIndex) => {
|
|
5730
3948
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -5732,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5732
3950
|
// Render user message
|
|
5733
3951
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
5734
3952
|
}
|
|
5735
|
-
else if (group.type === "heartbeat" && group.messages[0]) {
|
|
5736
|
-
return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
|
|
5737
|
-
}
|
|
5738
3953
|
else {
|
|
5739
3954
|
// Render bundled assistant messages
|
|
5740
3955
|
// Check if this group contains any streaming message
|
|
@@ -5751,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5751
3966
|
}
|
|
5752
3967
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
5753
3968
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5754
|
-
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 ||
|
|
5755
3970
|
activeProfile?.displayTitle ||
|
|
5756
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous,
|
|
3971
|
+
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5757
3972
|
const text = (action.prompt ||
|
|
5758
3973
|
action.value ||
|
|
5759
3974
|
action.label ||
|
|
@@ -5797,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5797
4012
|
}
|
|
5798
4013
|
});
|
|
5799
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: {
|
|
5800
|
-
__html:
|
|
4015
|
+
__html: activeProfile.svgIcon,
|
|
5801
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 ||
|
|
5802
4017
|
activeProfile?.displayTitle ||
|
|
5803
4018
|
activeProfile?.name ||
|
|
5804
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 &&
|
|
5805
4020
|
!simpleMode &&
|
|
5806
|
-
(
|
|
4021
|
+
(isMobile ? (_jsx("div", { className: "border-t border-gray-200 bg-gray-50", "data-testid": "agent-context-panel-tabs", children: _jsx(SimpleTabs, { tabs: [
|
|
5807
4022
|
{
|
|
5808
4023
|
id: "context",
|
|
5809
4024
|
label: "Context",
|
|
@@ -5851,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5851
4066
|
hasTodoContent,
|
|
5852
4067
|
hasSpawnedAgents,
|
|
5853
4068
|
agent?.id && hasHistoryContent,
|
|
5854
|
-
].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-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
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() ===
|
|
5877
|
-
new Date().toDateString()
|
|
5878
|
-
? formatTime(new Date(qp.scheduledFor))
|
|
5879
|
-
: formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
|
|
5880
|
-
}) })] }) }))] }));
|
|
5881
|
-
const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
|
|
5882
|
-
const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
|
|
5883
|
-
{
|
|
5884
|
-
name: "conversation",
|
|
5885
|
-
defaultSize: 65,
|
|
5886
|
-
content: fullModeUpperContent,
|
|
5887
|
-
},
|
|
5888
|
-
{
|
|
5889
|
-
name: "questionnaire",
|
|
5890
|
-
defaultSize: 35,
|
|
5891
|
-
content: fullModeInlineDialog,
|
|
5892
|
-
},
|
|
5893
|
-
], direction: "vertical", localStorageKey: compact
|
|
5894
|
-
? "agent-terminal-compact-questionnaire-splitter"
|
|
5895
|
-
: "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
|
|
5896
|
-
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 ? (
|
|
5897
4091
|
// Placeholder Input (from quick actions)
|
|
5898
4092
|
// Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
|
|
5899
4093
|
_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
@@ -5970,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5970
4164
|
return null;
|
|
5971
4165
|
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
|
|
5972
4166
|
? "justify-end"
|
|
5973
|
-
: "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"
|
|
5974
4168
|
? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
|
|
5975
4169
|
: mode === "supervised"
|
|
5976
4170
|
? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
|
|
5977
4171
|
: "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
|
|
5978
4172
|
const nextMode = val || "supervised";
|
|
4173
|
+
// Optimistic UI update
|
|
4174
|
+
setMode(nextMode);
|
|
5979
4175
|
const current = agentMetadata || {};
|
|
5980
4176
|
const nextMeta = {
|
|
5981
4177
|
...current,
|
|
5982
4178
|
mode: nextMode,
|
|
5983
4179
|
};
|
|
5984
4180
|
try {
|
|
5985
|
-
if (!agent?.id ||
|
|
5986
|
-
setMode(nextMode);
|
|
4181
|
+
if (!agent?.id || agent.status === "new") {
|
|
5987
4182
|
setAgentMetadata(nextMeta);
|
|
4183
|
+
// Cache until first start when agent is persisted
|
|
5988
4184
|
pendingSettingsRef.current = {
|
|
5989
4185
|
...(pendingSettingsRef.current || {}),
|
|
5990
4186
|
mode: nextMode,
|
|
5991
4187
|
};
|
|
5992
4188
|
return;
|
|
5993
4189
|
}
|
|
5994
|
-
|
|
4190
|
+
await updateAgentSettings(agent.id, {
|
|
5995
4191
|
mode: nextMode,
|
|
5996
4192
|
});
|
|
5997
|
-
if (result.success === false ||
|
|
5998
|
-
result.updates?.mode === false) {
|
|
5999
|
-
throw new Error("Mode change was not applied");
|
|
6000
|
-
}
|
|
6001
|
-
setMode(nextMode);
|
|
6002
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
|
|
6003
4231
|
setAgent((prev) => prev
|
|
6004
4232
|
? {
|
|
6005
4233
|
...prev,
|
|
6006
|
-
|
|
6007
|
-
|
|
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
|
+
}),
|
|
6008
4244
|
}
|
|
6009
4245
|
: prev);
|
|
6010
4246
|
}
|
|
6011
|
-
catch (
|
|
6012
|
-
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
|
+
}
|
|
6013
4270
|
}
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
if (!open) {
|
|
6017
|
-
setShowSkillPicker(false);
|
|
4271
|
+
catch (err) {
|
|
4272
|
+
console.error("Failed to persist agent model", err);
|
|
6018
4273
|
}
|
|
6019
|
-
}
|
|
6020
|
-
const target = e.target;
|
|
6021
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6022
|
-
e.preventDefault();
|
|
6023
|
-
}
|
|
6024
|
-
}, onPointerDownOutside: (e) => {
|
|
6025
|
-
const target = e.target;
|
|
6026
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6027
|
-
e.preventDefault();
|
|
6028
|
-
}
|
|
6029
|
-
}, onFocusOutside: (e) => {
|
|
6030
|
-
const target = e.target;
|
|
6031
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6032
|
-
e.preventDefault();
|
|
6033
|
-
}
|
|
6034
|
-
}, 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) => {
|
|
6035
|
-
const nextProfile = profiles.find((x) => x.id === val);
|
|
6036
|
-
if (!nextProfile)
|
|
6037
|
-
return;
|
|
6038
|
-
setActiveProfile(nextProfile);
|
|
6039
|
-
try {
|
|
6040
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6041
|
-
await updateAgentSettings(agent.id, {
|
|
6042
|
-
profileId: nextProfile.id,
|
|
6043
|
-
profileName: nextProfile.name,
|
|
6044
|
-
});
|
|
6045
|
-
}
|
|
6046
|
-
else {
|
|
6047
|
-
pendingSettingsRef.current = {
|
|
6048
|
-
...(pendingSettingsRef.current || {}),
|
|
6049
|
-
profileId: nextProfile.id,
|
|
6050
|
-
profileName: nextProfile.name,
|
|
6051
|
-
};
|
|
6052
|
-
setAgentMetadata((current) => {
|
|
6053
|
-
const next = {
|
|
6054
|
-
...(current || {}),
|
|
6055
|
-
};
|
|
6056
|
-
next.profile = nextProfile.name;
|
|
6057
|
-
next.additionalData = {
|
|
6058
|
-
...(next.additionalData || {}),
|
|
6059
|
-
profileId: nextProfile.id,
|
|
6060
|
-
profileName: nextProfile.name,
|
|
6061
|
-
};
|
|
6062
|
-
return next;
|
|
6063
|
-
});
|
|
6064
|
-
}
|
|
6065
|
-
setAgent((prev) => prev
|
|
6066
|
-
? {
|
|
6067
|
-
...prev,
|
|
6068
|
-
profileId: nextProfile.id,
|
|
6069
|
-
profileName: nextProfile.name,
|
|
6070
|
-
metadata: JSON.stringify({
|
|
6071
|
-
...(agentMetadata || {}),
|
|
6072
|
-
profile: nextProfile.name,
|
|
6073
|
-
additionalData: {
|
|
6074
|
-
...(agentMetadata
|
|
6075
|
-
?.additionalData || {}),
|
|
6076
|
-
profileId: nextProfile.id,
|
|
6077
|
-
profileName: nextProfile.name,
|
|
6078
|
-
},
|
|
6079
|
-
}),
|
|
6080
|
-
}
|
|
6081
|
-
: prev);
|
|
6082
|
-
}
|
|
6083
|
-
catch (err) {
|
|
6084
|
-
console.error("Failed to persist agent profile", err);
|
|
6085
|
-
}
|
|
6086
|
-
} }), 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: () => {
|
|
6087
|
-
void handleEditProfileSideBySide();
|
|
6088
|
-
setShowAgentSettings(false);
|
|
6089
|
-
}, "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: () => {
|
|
6090
|
-
void handleOpenProfileSettings();
|
|
6091
|
-
setShowAgentSettings(false);
|
|
6092
|
-
}, 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) => {
|
|
6093
|
-
const nextId = val;
|
|
6094
|
-
setSelectedModelId(nextId);
|
|
6095
|
-
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
|
|
6096
|
-
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
6097
|
-
try {
|
|
6098
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6099
|
-
await updateAgentSettings(agent.id, {
|
|
6100
|
-
model: modelName,
|
|
6101
|
-
});
|
|
6102
|
-
}
|
|
6103
|
-
else {
|
|
6104
|
-
pendingSettingsRef.current = {
|
|
6105
|
-
...(pendingSettingsRef.current || {}),
|
|
6106
|
-
modelName,
|
|
6107
|
-
};
|
|
6108
|
-
}
|
|
6109
|
-
}
|
|
6110
|
-
catch (err) {
|
|
6111
|
-
console.error("Failed to persist agent model", err);
|
|
6112
|
-
}
|
|
6113
|
-
} })] })) : 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) => {
|
|
6114
|
-
setShowSkillPicker(open);
|
|
6115
|
-
if (open) {
|
|
6116
|
-
setSkillActionError(null);
|
|
6117
|
-
}
|
|
6118
|
-
}, 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) => {
|
|
6119
|
-
const selected = selection[0];
|
|
6120
|
-
if (!selected?.id)
|
|
6121
|
-
return;
|
|
6122
|
-
setSkillActionError(null);
|
|
6123
|
-
if (selectableTemplateIdSet.size > 0 &&
|
|
6124
|
-
(!selected.templateId ||
|
|
6125
|
-
!selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
|
|
6126
|
-
return;
|
|
6127
|
-
}
|
|
6128
|
-
if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
|
|
6129
|
-
setSkillActionError("This skill cannot be added for the current agent profile.");
|
|
6130
|
-
return;
|
|
6131
|
-
}
|
|
6132
|
-
void (async () => {
|
|
6133
|
-
const added = await handleAddSkill(selected.id);
|
|
6134
|
-
if (added) {
|
|
6135
|
-
setShowSkillPicker(false);
|
|
6136
|
-
}
|
|
6137
|
-
})();
|
|
6138
|
-
} }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
|
|
6139
|
-
!skillsError &&
|
|
6140
|
-
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 &&
|
|
6141
|
-
!skillsError &&
|
|
6142
|
-
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
6143
|
-
!skillsError &&
|
|
6144
|
-
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
6145
|
-
? "All addable skills are selected"
|
|
6146
|
-
: "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) => {
|
|
6147
|
-
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
6148
|
-
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: () => {
|
|
6149
|
-
void handleOpenSkillItem(skillId);
|
|
6150
|
-
}, 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: () => {
|
|
6151
|
-
void handleRemoveSkill(skillId);
|
|
6152
|
-
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
6153
|
-
}) }))] }), _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
|
|
6154
|
-
? "No available tools for this profile and mode"
|
|
6155
|
-
: "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) => {
|
|
6156
|
-
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
6157
|
-
const pathLabel = "itemPath" in allowance
|
|
6158
|
-
? allowance.itemPath
|
|
6159
|
-
: allowance.normalizedPath;
|
|
6160
|
-
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 ||
|
|
6161
|
-
"*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
|
|
6162
|
-
allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
|
|
6163
|
-
sourceLabel,
|
|
6164
|
-
allowance.grantedBy,
|
|
6165
|
-
]
|
|
6166
|
-
.filter(Boolean)
|
|
6167
|
-
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
6168
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6169
|
-
? "Allowances are shown after the agent is created"
|
|
6170
|
-
: "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) => {
|
|
6171
|
-
const filterText = (sub.filter || "").trim();
|
|
6172
|
-
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));
|
|
6173
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6174
|
-
? "Subscribed triggers are shown after the agent is created"
|
|
6175
|
-
: "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: () => {
|
|
6176
4275
|
setPrompt(p.prompt);
|
|
6177
4276
|
setShowPredefined(false);
|
|
6178
4277
|
if (textareaRef.current)
|
|
6179
4278
|
textareaRef.current.focus();
|
|
6180
|
-
}, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
|
|
6181
|
-
|
|
6182
|
-
editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
|
|
6183
|
-
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)
|
|
6184
4281
|
setShowCostAndAgent((prev) => !prev);
|
|
6185
|
-
}, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded":
|
|
6186
|
-
? showCostAndAgent
|
|
6187
|
-
: 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
|
|
6188
4283
|
? {
|
|
6189
4284
|
input: liveTotals.input,
|
|
6190
4285
|
output: liveTotals.output,
|
|
@@ -6194,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6194
4289
|
outputCost: liveTotals.outputCost,
|
|
6195
4290
|
cachedCost: liveTotals.cachedCost,
|
|
6196
4291
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6197
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6198
4292
|
totalCost: liveTotals.totalCost,
|
|
6199
4293
|
}
|
|
6200
|
-
: 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
|
|
6201
4295
|
? "Your browser does not support Speech Recognition"
|
|
6202
4296
|
: isListening
|
|
6203
4297
|
? "Stop voice input"
|
|
@@ -6218,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6218
4312
|
: allPendingApprovals.length > 0
|
|
6219
4313
|
? "Approve or reject pending tool calls first"
|
|
6220
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 })) })] })] }));
|
|
6221
|
-
})(), !hideBottomControls &&
|
|
6222
|
-
!simpleMode &&
|
|
6223
|
-
editContext &&
|
|
6224
|
-
!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
|
|
6225
4316
|
? {
|
|
6226
4317
|
input: liveTotals.input,
|
|
6227
4318
|
output: liveTotals.output,
|
|
@@ -6231,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6231
4322
|
outputCost: liveTotals.outputCost,
|
|
6232
4323
|
cachedCost: liveTotals.cachedCost,
|
|
6233
4324
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6234
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6235
4325
|
totalCost: liveTotals.totalCost,
|
|
6236
4326
|
}
|
|
6237
|
-
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
|
|
4327
|
+
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
|
|
6238
4328
|
}
|
|
6239
4329
|
//# sourceMappingURL=AgentTerminal.js.map
|