@parhelia/core 0.1.12585 → 0.1.12602
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 +63 -37
- 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 +5 -6
- 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 +572 -2508
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +4 -9
- 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/EditOperationsPanel.d.ts +2 -3
- package/dist/editor/ai/EditOperationsPanel.js +78 -21
- package/dist/editor/ai/EditOperationsPanel.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 +202 -614
- 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 -117
- 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 +258 -853
- 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.d.ts +1 -2
- package/dist/editor/reviews/Comment.js +15 -92
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +5 -70
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.d.ts +1 -3
- package/dist/editor/reviews/CommentView.js +6 -26
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +75 -140
- 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 +3 -4
- package/dist/editor/reviews/useReviews.js +32 -21
- package/dist/editor/reviews/useReviews.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +5 -240
- package/dist/editor/services/agentService.js +39 -299
- 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/IndexOverview.js +1 -3
- package/dist/editor/settings/IndexOverview.js.map +1 -1
- 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 -23
- 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 -324
- 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 +6 -67
- package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/StatusPanel.js +2 -7
- package/dist/editor/settings/panels/StatusPanel.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 +14 -48
- package/dist/setup/services/setupWizardService.js +17 -52
- 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 -46
- 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 -70
- 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,155 +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 AGENT_HISTORY_LIMIT = 1000;
|
|
36
|
-
function mergeAgentOperationHistory(existing, incoming, limit = AGENT_HISTORY_LIMIT) {
|
|
37
|
-
const merged = new Map(existing.map((operation) => [operation.id, operation]));
|
|
38
|
-
for (const operation of incoming) {
|
|
39
|
-
merged.set(operation.id, operation);
|
|
40
|
-
}
|
|
41
|
-
return Array.from(merged.values())
|
|
42
|
-
.sort((left, right) => new Date(right.date).getTime() - new Date(left.date).getTime())
|
|
43
|
-
.slice(0, limit);
|
|
44
|
-
}
|
|
45
|
-
const userMessageMarkdownComponents = {
|
|
46
|
-
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
|
|
47
|
-
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
|
|
48
|
-
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
|
|
49
|
-
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
|
|
50
|
-
p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
|
|
51
|
-
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
52
|
-
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
53
|
-
li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
|
|
54
|
-
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" })),
|
|
55
|
-
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 })),
|
|
56
|
-
};
|
|
57
|
-
function buildPlaceholderAgentDetails(agentStub) {
|
|
58
|
-
const now = new Date().toISOString();
|
|
59
|
-
const updated = agentStub.updatedDate || now;
|
|
60
|
-
// AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
|
|
61
|
-
// This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
|
|
62
|
-
return {
|
|
63
|
-
...agentStub,
|
|
64
|
-
name: agentStub.name || "Agent",
|
|
65
|
-
userId: agentStub.userId || "",
|
|
66
|
-
updatedDate: updated,
|
|
67
|
-
profileName: agentStub.profileName || "",
|
|
68
|
-
model: agentStub.model || "",
|
|
69
|
-
createdDate: agentStub.createdDate || updated,
|
|
70
|
-
totalTokensUsed: 0,
|
|
71
|
-
totalInputTokens: 0,
|
|
72
|
-
totalOutputTokens: 0,
|
|
73
|
-
totalCachedInputTokens: 0,
|
|
74
|
-
totalInputTokenCost: 0,
|
|
75
|
-
totalOutputTokenCost: 0,
|
|
76
|
-
totalCachedInputTokenCost: 0,
|
|
77
|
-
totalImageCost: 0,
|
|
78
|
-
totalCost: 0,
|
|
79
|
-
currency: agentStub.currency || "USD",
|
|
80
|
-
messageCount: agentStub.messageCount || 0,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function normalizeDialogAgentId(value) {
|
|
84
|
-
return value?.trim().toLowerCase() || "";
|
|
85
|
-
}
|
|
86
|
-
function formatAllowanceSource(source) {
|
|
87
|
-
const normalized = source?.trim();
|
|
88
|
-
if (!normalized)
|
|
89
|
-
return null;
|
|
90
|
-
if (normalized === "user")
|
|
91
|
-
return "User granted";
|
|
92
|
-
if (normalized.startsWith("preconfigured:profile:"))
|
|
93
|
-
return "Profile";
|
|
94
|
-
if (normalized.startsWith("preconfigured:skill:"))
|
|
95
|
-
return "Skill";
|
|
96
|
-
if (normalized.startsWith("system:")) {
|
|
97
|
-
return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
|
|
98
|
-
}
|
|
99
|
-
return normalized;
|
|
100
|
-
}
|
|
101
|
-
function formatAllowanceLabel(allowance) {
|
|
102
|
-
return `${allowance.operationType || "*"}${"itemPath" in allowance
|
|
103
|
-
? ` ${allowance.itemPath}`
|
|
104
|
-
: ` ${allowance.normalizedPath}`}`;
|
|
105
|
-
}
|
|
106
|
-
function getAgentRunMessageAgentId(payload) {
|
|
107
|
-
const agentId = payload?.agentId;
|
|
108
|
-
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
109
|
-
}
|
|
110
|
-
function getAgentRunMessageSeq(payload) {
|
|
111
|
-
const seq = payload?.seq;
|
|
112
|
-
return typeof seq === "number" ? seq : null;
|
|
113
|
-
}
|
|
114
|
-
function getAgentRunMessageDetail(type, payload) {
|
|
115
|
-
if (type === "agent:run:delta") {
|
|
116
|
-
return payload?.type || null;
|
|
117
|
-
}
|
|
118
|
-
if (type === "agent:run:status") {
|
|
119
|
-
return payload?.data?.state || payload?.data?.status || null;
|
|
120
|
-
}
|
|
121
|
-
if (type === "agent:run:error") {
|
|
122
|
-
return payload?.error || null;
|
|
123
|
-
}
|
|
124
|
-
if (type === "agent:run:complete") {
|
|
125
|
-
return payload?.finalStatus || null;
|
|
126
|
-
}
|
|
127
|
-
if (type === "agent:run:start") {
|
|
128
|
-
return payload?.agentName || null;
|
|
129
|
-
}
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
function isHeartbeatRunEventMessage(message) {
|
|
133
|
-
if (!message)
|
|
134
|
-
return false;
|
|
135
|
-
if (message.type === "agent:run:delta") {
|
|
136
|
-
const deltaType = message.payload?.type;
|
|
137
|
-
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
138
|
-
}
|
|
139
|
-
if (message.type === "agent:run:status") {
|
|
140
|
-
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
141
|
-
return String(state || "").toLowerCase() === "heartbeat";
|
|
142
|
-
}
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
function getVisibleDialogRegistry() {
|
|
146
|
-
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
147
|
-
return registry && typeof registry === "object" ? registry : {};
|
|
148
|
-
}
|
|
149
|
-
function isAgentErrorStatusValue(status) {
|
|
150
|
-
return status === "error";
|
|
151
|
-
}
|
|
152
25
|
// Simple user message component
|
|
153
26
|
const UserMessage = ({ message }) => {
|
|
154
|
-
const content = message.content || "";
|
|
155
|
-
const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
|
|
156
|
-
// Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
|
|
157
|
-
const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
|
|
158
|
-
const triggerMatch = content.match(triggerPattern);
|
|
159
|
-
const triggerName = triggerMatch?.[1]?.trim() || "";
|
|
160
|
-
const triggerContent = triggerMatch?.[2] || "";
|
|
161
|
-
const isTriggerMessage = triggerName.length > 0;
|
|
162
|
-
if (isTriggerMessage) {
|
|
163
|
-
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 }) }))] }) }));
|
|
164
|
-
}
|
|
165
27
|
// Parse source agent name from content if it starts with "[From ...]:"
|
|
166
28
|
// Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
|
|
29
|
+
const content = message.content || "";
|
|
167
30
|
const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
|
|
168
31
|
const match = content.match(fromPattern);
|
|
169
32
|
let sourceAgentName;
|
|
@@ -188,10 +51,7 @@ const UserMessage = ({ message }) => {
|
|
|
188
51
|
message.sourceAgent?.name;
|
|
189
52
|
}
|
|
190
53
|
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
191
|
-
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:
|
|
192
|
-
};
|
|
193
|
-
const HeartbeatMessage = ({ message }) => {
|
|
194
|
-
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 })] })] }));
|
|
195
55
|
};
|
|
196
56
|
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
197
57
|
const extractPartialTodos = (jsonText) => {
|
|
@@ -425,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
|
|
|
425
285
|
todoMap.set(key, todo);
|
|
426
286
|
}
|
|
427
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
|
+
}
|
|
428
296
|
return result;
|
|
429
297
|
};
|
|
430
298
|
// TodoListPanel component
|
|
@@ -432,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
|
432
300
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
433
301
|
// First try to get todos from agent metadata (real-time updates)
|
|
434
302
|
// Server sends additionalData.todoList directly via contextChanged status
|
|
303
|
+
// Also check top-level todoList for backward compatibility with stored contexts
|
|
435
304
|
const metadataTodos = (() => {
|
|
436
305
|
try {
|
|
437
|
-
|
|
306
|
+
// Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
|
|
307
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
308
|
+
agentMetadata?.todoList;
|
|
438
309
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
439
310
|
const rawItems = todoList.items
|
|
440
311
|
.map((item, idx) => ({
|
|
@@ -562,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
562
433
|
// Add user message
|
|
563
434
|
groups.push({ type: "user", messages: [message] });
|
|
564
435
|
}
|
|
565
|
-
else if (message.messageType === "heartbeat" ||
|
|
566
|
-
message.role === "system") {
|
|
567
|
-
if (currentAssistantGroup.length > 0) {
|
|
568
|
-
groups.push({
|
|
569
|
-
type: "assistant-group",
|
|
570
|
-
messages: currentAssistantGroup,
|
|
571
|
-
});
|
|
572
|
-
currentAssistantGroup = [];
|
|
573
|
-
}
|
|
574
|
-
groups.push({ type: "heartbeat", messages: [message] });
|
|
575
|
-
}
|
|
576
436
|
else if (message.role === "assistant") {
|
|
577
437
|
// Add to current assistant group
|
|
578
438
|
currentAssistantGroup.push(message);
|
|
@@ -640,7 +500,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
640
500
|
outputCost: acc.outputCost + (message.outputTokenCost || 0),
|
|
641
501
|
cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
|
|
642
502
|
cacheWriteCost: acc.cacheWriteCost,
|
|
643
|
-
imageCost: acc.imageCost,
|
|
644
503
|
totalCost: acc.totalCost + (message.totalCost || 0),
|
|
645
504
|
};
|
|
646
505
|
}, {
|
|
@@ -652,7 +511,6 @@ const calculateTotalTokens = (messages) => {
|
|
|
652
511
|
outputCost: 0,
|
|
653
512
|
cachedCost: 0,
|
|
654
513
|
cacheWriteCost: 0,
|
|
655
|
-
imageCost: 0,
|
|
656
514
|
totalCost: 0,
|
|
657
515
|
});
|
|
658
516
|
return totals;
|
|
@@ -663,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
|
|
|
663
521
|
const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
|
|
664
522
|
return matched;
|
|
665
523
|
};
|
|
666
|
-
const stringifyToolField = (value) => {
|
|
667
|
-
if (value === undefined || value === null)
|
|
668
|
-
return undefined;
|
|
669
|
-
if (typeof value === "string") {
|
|
670
|
-
return value.trim().length > 0 ? value : undefined;
|
|
671
|
-
}
|
|
672
|
-
try {
|
|
673
|
-
return JSON.stringify(value);
|
|
674
|
-
}
|
|
675
|
-
catch {
|
|
676
|
-
return String(value);
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
const parseToolResultValue = (value) => {
|
|
680
|
-
if (value === undefined || value === null) {
|
|
681
|
-
return undefined;
|
|
682
|
-
}
|
|
683
|
-
if (typeof value === "object") {
|
|
684
|
-
return value;
|
|
685
|
-
}
|
|
686
|
-
if (typeof value !== "string") {
|
|
687
|
-
return String(value);
|
|
688
|
-
}
|
|
689
|
-
const trimmed = value.trim();
|
|
690
|
-
if (!trimmed) {
|
|
691
|
-
return undefined;
|
|
692
|
-
}
|
|
693
|
-
try {
|
|
694
|
-
let parsed = JSON.parse(trimmed);
|
|
695
|
-
if (typeof parsed === "string" &&
|
|
696
|
-
(parsed.startsWith("{") || parsed.startsWith("["))) {
|
|
697
|
-
parsed = JSON.parse(parsed);
|
|
698
|
-
}
|
|
699
|
-
if (parsed && typeof parsed === "object") {
|
|
700
|
-
return parsed;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
catch {
|
|
704
|
-
// Fall back to the original string when the payload is plain text.
|
|
705
|
-
}
|
|
706
|
-
return value;
|
|
707
|
-
};
|
|
708
|
-
const getFirstToolCallEnvelope = (data) => {
|
|
709
|
-
if (!data || typeof data !== "object")
|
|
710
|
-
return undefined;
|
|
711
|
-
const direct = data.toolCall || data.tool_call;
|
|
712
|
-
if (direct)
|
|
713
|
-
return direct;
|
|
714
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
715
|
-
for (const candidate of arrayCandidates) {
|
|
716
|
-
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
717
|
-
return candidate[0];
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return undefined;
|
|
721
|
-
};
|
|
722
|
-
const extractToolCallFields = (data) => {
|
|
723
|
-
const envelope = getFirstToolCallEnvelope(data);
|
|
724
|
-
const functionPayload = data?.function || envelope?.function;
|
|
725
|
-
const functionName = data?.functionName ||
|
|
726
|
-
data?.name ||
|
|
727
|
-
functionPayload?.name ||
|
|
728
|
-
envelope?.functionName ||
|
|
729
|
-
envelope?.name ||
|
|
730
|
-
"unknown";
|
|
731
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
732
|
-
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
733
|
-
stringifyToolField(data?.arguments) ||
|
|
734
|
-
stringifyToolField(functionPayload?.arguments) ||
|
|
735
|
-
stringifyToolField(envelope?.functionArguments) ||
|
|
736
|
-
stringifyToolField(envelope?.arguments) ||
|
|
737
|
-
"{}";
|
|
738
|
-
return {
|
|
739
|
-
toolCallId,
|
|
740
|
-
functionName,
|
|
741
|
-
functionArguments,
|
|
742
|
-
};
|
|
743
|
-
};
|
|
744
524
|
// Convert agent messages to AI terminal format for a response group
|
|
745
525
|
const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
746
526
|
return agentMessages.map((agentMessage) => {
|
|
@@ -761,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
761
541
|
role: agentMessage.role,
|
|
762
542
|
createdDate: agentMessage.createdDate,
|
|
763
543
|
tool_calls: agentMessage.toolCalls
|
|
764
|
-
? agentMessage.toolCalls.map((toolCall) => {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
!toolCall.isCompleted &&
|
|
787
|
-
!displayResult &&
|
|
788
|
-
!toolCall.functionError &&
|
|
789
|
-
!isPruned,
|
|
790
|
-
// Pass through message IDs for approval/rejection events
|
|
791
|
-
messageId: toolCall.messageId,
|
|
792
|
-
dbMessageId: toolCall.dbMessageId,
|
|
793
|
-
responseTimeMs: toolCall.responseTimeMs,
|
|
794
|
-
createdDate: toolCall.createdDate,
|
|
795
|
-
};
|
|
796
|
-
})
|
|
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
|
+
}))
|
|
797
566
|
: [],
|
|
798
567
|
};
|
|
799
568
|
if (agentMessage.toolCallId) {
|
|
@@ -805,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
805
574
|
// interface AgentTerminalProps {
|
|
806
575
|
// agentStub: Agent;
|
|
807
576
|
// }
|
|
808
|
-
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, }) {
|
|
809
578
|
const editContext = useEditContext();
|
|
810
579
|
const fieldsContext = useFieldsEditContext();
|
|
811
|
-
const [agent, setAgent] = useState(
|
|
580
|
+
const [agent, setAgent] = useState(undefined);
|
|
812
581
|
const [messages, setMessages] = useState([]);
|
|
813
582
|
const [agentOperations, setAgentOperations] = useState([]);
|
|
814
583
|
const [prompt, setPrompt] = useState("");
|
|
@@ -819,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
819
588
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
820
589
|
const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
|
|
821
590
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
822
|
-
const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
|
|
823
|
-
const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
|
|
824
|
-
// Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
|
|
825
|
-
// This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
|
|
826
|
-
useEffect(() => {
|
|
827
|
-
setAgent((prev) => {
|
|
828
|
-
if (prev?.id === agentStub.id)
|
|
829
|
-
return prev;
|
|
830
|
-
return buildPlaceholderAgentDetails(agentStub);
|
|
831
|
-
});
|
|
832
|
-
}, [agentStub.id]);
|
|
833
|
-
const observedMessageIdsRef = useRef(new Set());
|
|
834
|
-
useEffect(() => {
|
|
835
|
-
observedMessageIdsRef.current = new Set();
|
|
836
|
-
}, [agentStub.id]);
|
|
837
|
-
useEffect(() => {
|
|
838
|
-
if (!onMessage)
|
|
839
|
-
return;
|
|
840
|
-
for (const message of messages) {
|
|
841
|
-
if (!message?.id)
|
|
842
|
-
continue;
|
|
843
|
-
if (!message.isCompleted)
|
|
844
|
-
continue;
|
|
845
|
-
if (observedMessageIdsRef.current.has(message.id))
|
|
846
|
-
continue;
|
|
847
|
-
observedMessageIdsRef.current.add(message.id);
|
|
848
|
-
onMessage(message);
|
|
849
|
-
}
|
|
850
|
-
}, [messages, onMessage]);
|
|
851
591
|
// Generate a stable clientSessionId per component instance for stream deduplication
|
|
852
592
|
const clientSessionIdRef = useRef(null);
|
|
853
593
|
if (!clientSessionIdRef.current) {
|
|
@@ -859,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
859
599
|
const [isListening, setIsListening] = useState(false);
|
|
860
600
|
const recognitionRef = useRef(null);
|
|
861
601
|
const prevPlaceholderRef = useRef(null);
|
|
862
|
-
const promptBeforeVoiceRef = useRef("");
|
|
863
602
|
// Voice button press-and-hold tracking
|
|
864
603
|
const voicePressStartRef = useRef(null);
|
|
865
604
|
const voiceHoldTimerRef = useRef(null);
|
|
@@ -869,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
869
608
|
const [showCompressionPopover, setShowCompressionPopover] = useState(false);
|
|
870
609
|
// Agent inline dialog state (for component type selector, etc.)
|
|
871
610
|
const [activeInlineDialog, setActiveInlineDialog] = useState(null);
|
|
872
|
-
const dialogTerminalInstanceIdRef = useRef("");
|
|
873
|
-
if (!dialogTerminalInstanceIdRef.current) {
|
|
874
|
-
dialogTerminalInstanceIdRef.current = crypto.randomUUID();
|
|
875
|
-
}
|
|
876
|
-
const activeInlineDialogRef = useRef(activeInlineDialog);
|
|
877
|
-
const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
|
|
878
|
-
const orphanTimeoutRef = useRef(null);
|
|
879
|
-
useEffect(() => {
|
|
880
|
-
activeInlineDialogRef.current = activeInlineDialog;
|
|
881
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
882
|
-
const callbackId = activeInlineDialog?.request.callbackId || null;
|
|
883
|
-
const terminalInstanceId = dialogTerminalInstanceIdRef.current;
|
|
884
|
-
const agentKeys = [
|
|
885
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
886
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
887
|
-
].filter(Boolean);
|
|
888
|
-
agentKeys.forEach((key) => {
|
|
889
|
-
if (callbackId) {
|
|
890
|
-
visibleRegistry[key] = {
|
|
891
|
-
callbackId,
|
|
892
|
-
terminalInstanceId,
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
else {
|
|
896
|
-
delete visibleRegistry[key];
|
|
897
|
-
}
|
|
898
|
-
});
|
|
899
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
900
|
-
}, [activeInlineDialog]);
|
|
901
|
-
useEffect(() => {
|
|
902
|
-
onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
|
|
903
|
-
}, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
|
|
904
|
-
useLayoutEffect(() => {
|
|
905
|
-
if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
const scrollSummaryTerminalToTop = () => {
|
|
909
|
-
const container = messagesContainerRef.current;
|
|
910
|
-
if (!container) {
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
container.scrollTop = 0;
|
|
914
|
-
};
|
|
915
|
-
scrollSummaryTerminalToTop();
|
|
916
|
-
const frame1 = requestAnimationFrame(() => {
|
|
917
|
-
scrollSummaryTerminalToTop();
|
|
918
|
-
});
|
|
919
|
-
let frame3 = 0;
|
|
920
|
-
const frame2 = requestAnimationFrame(() => {
|
|
921
|
-
frame3 = requestAnimationFrame(() => {
|
|
922
|
-
scrollSummaryTerminalToTop();
|
|
923
|
-
});
|
|
924
|
-
});
|
|
925
|
-
return () => {
|
|
926
|
-
cancelAnimationFrame(frame1);
|
|
927
|
-
cancelAnimationFrame(frame2);
|
|
928
|
-
cancelAnimationFrame(frame3);
|
|
929
|
-
};
|
|
930
|
-
}, [displayMode, isQuestionnaireDialogOpen]);
|
|
931
|
-
useEffect(() => {
|
|
932
|
-
return () => {
|
|
933
|
-
onQuestionnaireOpenChange?.(false);
|
|
934
|
-
};
|
|
935
|
-
}, [onQuestionnaireOpenChange]);
|
|
936
611
|
const isWaitingRef = useRef(false);
|
|
937
612
|
useEffect(() => {
|
|
938
613
|
isWaitingRef.current = isWaitingForResponse;
|
|
@@ -941,96 +616,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
941
616
|
// while a stop operation is in progress. This prevents race conditions where
|
|
942
617
|
// messages arriving during the stop process could flip the UI back to "executing".
|
|
943
618
|
const isStoppingRef = useRef(false);
|
|
944
|
-
const [isStopGuardActive, setIsStopGuardActive] = useState(false);
|
|
945
|
-
const stopGuardReleaseTimeoutRef = useRef(null);
|
|
946
|
-
function clearStopGuard() {
|
|
947
|
-
isStoppingRef.current = false;
|
|
948
|
-
setIsStopGuardActive(false);
|
|
949
|
-
if (stopGuardReleaseTimeoutRef.current) {
|
|
950
|
-
clearTimeout(stopGuardReleaseTimeoutRef.current);
|
|
951
|
-
stopGuardReleaseTimeoutRef.current = null;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
function armStopGuard() {
|
|
955
|
-
isStoppingRef.current = true;
|
|
956
|
-
setIsStopGuardActive(true);
|
|
957
|
-
if (stopGuardReleaseTimeoutRef.current) {
|
|
958
|
-
clearTimeout(stopGuardReleaseTimeoutRef.current);
|
|
959
|
-
}
|
|
960
|
-
stopGuardReleaseTimeoutRef.current = setTimeout(() => {
|
|
961
|
-
stopGuardReleaseTimeoutRef.current = null;
|
|
962
|
-
isStoppingRef.current = false;
|
|
963
|
-
setIsStopGuardActive(false);
|
|
964
|
-
}, 30000);
|
|
965
|
-
}
|
|
966
619
|
// Server-driven state: true when agent is actively processing (set by WebSocket messages)
|
|
967
620
|
const [isAgentThinking, setIsAgentThinking] = useState(false);
|
|
968
|
-
useEffect(() => {
|
|
969
|
-
return () => {
|
|
970
|
-
if (stopGuardReleaseTimeoutRef.current) {
|
|
971
|
-
clearTimeout(stopGuardReleaseTimeoutRef.current);
|
|
972
|
-
stopGuardReleaseTimeoutRef.current = null;
|
|
973
|
-
}
|
|
974
|
-
};
|
|
975
|
-
}, []);
|
|
976
|
-
useEffect(() => {
|
|
977
|
-
if (!initialMetadata)
|
|
978
|
-
return;
|
|
979
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata({
|
|
980
|
-
...(prev || {}),
|
|
981
|
-
...initialMetadata,
|
|
982
|
-
additionalData: {
|
|
983
|
-
...(prev?.additionalData || {}),
|
|
984
|
-
...(initialMetadata?.additionalData || {}),
|
|
985
|
-
},
|
|
986
|
-
}));
|
|
987
|
-
}, [initialMetadata]);
|
|
988
621
|
const hasActiveStreaming = useCallback(() => {
|
|
989
622
|
const current = messagesRef.current || [];
|
|
990
623
|
return current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
991
624
|
}, []);
|
|
992
|
-
const currentAgentId = agent?.id || agentStub.id;
|
|
993
|
-
const recentAgentRunEvents = useMemo(() => {
|
|
994
|
-
const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
|
|
995
|
-
if (!normalizedAgentId) {
|
|
996
|
-
return [];
|
|
997
|
-
}
|
|
998
|
-
if (!editContext) {
|
|
999
|
-
return [];
|
|
1000
|
-
}
|
|
1001
|
-
return (editContext.webSocketMessages || [])
|
|
1002
|
-
.filter((message) => {
|
|
1003
|
-
if (!message?.type?.startsWith("agent:run:")) {
|
|
1004
|
-
return false;
|
|
1005
|
-
}
|
|
1006
|
-
if (isHeartbeatRunEventMessage(message)) {
|
|
1007
|
-
return false;
|
|
1008
|
-
}
|
|
1009
|
-
return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
|
|
1010
|
-
})
|
|
1011
|
-
.slice(-8)
|
|
1012
|
-
.map((message) => ({
|
|
1013
|
-
timestamp: message.timestamp,
|
|
1014
|
-
type: message.type,
|
|
1015
|
-
seq: getAgentRunMessageSeq(message.payload),
|
|
1016
|
-
detail: getAgentRunMessageDetail(message.type, message.payload),
|
|
1017
|
-
}));
|
|
1018
|
-
}, [currentAgentId, editContext?.webSocketMessages]);
|
|
1019
|
-
const appendToolUiEvent = useCallback((type, detail, seq) => {
|
|
1020
|
-
const timestamp = new Date().toISOString();
|
|
1021
|
-
setRecentToolUiEvents((prev) => {
|
|
1022
|
-
const next = [
|
|
1023
|
-
...prev,
|
|
1024
|
-
{
|
|
1025
|
-
timestamp,
|
|
1026
|
-
type,
|
|
1027
|
-
detail: detail || null,
|
|
1028
|
-
seq: seq ?? null,
|
|
1029
|
-
},
|
|
1030
|
-
];
|
|
1031
|
-
return next.slice(-40);
|
|
1032
|
-
});
|
|
1033
|
-
}, []);
|
|
1034
625
|
// Collect all pending tool calls for batch approval functionality
|
|
1035
626
|
const allPendingApprovals = useMemo(() => {
|
|
1036
627
|
const pending = [];
|
|
@@ -1072,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1072
663
|
}, [allPendingApprovals]);
|
|
1073
664
|
// Handle mode switch to autonomous
|
|
1074
665
|
const handleSwitchToAutonomous = useCallback(() => {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
setAgentMetadata((prev) => ({
|
|
1078
|
-
...(prev ?? {}),
|
|
1079
|
-
mode: nextMode,
|
|
1080
|
-
}));
|
|
1081
|
-
setAgent((prev) => {
|
|
1082
|
-
if (!prev)
|
|
1083
|
-
return prev;
|
|
1084
|
-
return {
|
|
1085
|
-
...prev,
|
|
1086
|
-
mode: nextMode,
|
|
1087
|
-
};
|
|
1088
|
-
});
|
|
1089
|
-
}, [setAgent, setAgentMetadata]);
|
|
666
|
+
setMode("autonomous");
|
|
667
|
+
}, []);
|
|
1090
668
|
const [resolvedPageName, setResolvedPageName] = useState(undefined);
|
|
1091
669
|
const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
|
|
1092
670
|
const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
|
|
1093
671
|
const [promptHistory, setPromptHistory] = useState(() => {
|
|
1094
|
-
|
|
672
|
+
if (typeof window !== "undefined") {
|
|
673
|
+
return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
|
|
674
|
+
}
|
|
675
|
+
return [];
|
|
1095
676
|
});
|
|
1096
677
|
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
|
|
1097
678
|
const [showPredefined, setShowPredefined] = useState(false);
|
|
1098
679
|
const [activeProfile, setActiveProfile] = useState(undefined);
|
|
1099
680
|
const [selectedModelId, setSelectedModelId] = useState(undefined);
|
|
1100
|
-
const normalizeAgentMode = (value) => {
|
|
1101
|
-
if (value === "autonomous" ||
|
|
1102
|
-
value === "read-only" ||
|
|
1103
|
-
value === "supervised") {
|
|
1104
|
-
return value;
|
|
1105
|
-
}
|
|
1106
|
-
return null;
|
|
1107
|
-
};
|
|
1108
681
|
const [mode, setMode] = useState("supervised");
|
|
1109
682
|
const [queuedPrompts, setQueuedPrompts] = useState([]);
|
|
1110
|
-
const
|
|
683
|
+
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
1111
684
|
const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
|
|
1112
685
|
const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
|
|
1113
686
|
const [showCostAndAgent, setShowCostAndAgent] = useState(false);
|
|
1114
|
-
const [showAgentSettings, setShowAgentSettings] = useState(false);
|
|
1115
|
-
const [showSkillPicker, setShowSkillPicker] = useState(false);
|
|
1116
|
-
const [availableSkills, setAvailableSkills] = useState([]);
|
|
1117
|
-
const [skillRootIds, setSkillRootIds] = useState([]);
|
|
1118
|
-
const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
|
|
1119
|
-
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1120
|
-
const [skillsError, setSkillsError] = useState(null);
|
|
1121
|
-
const [skillActionError, setSkillActionError] = useState(null);
|
|
1122
|
-
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1123
|
-
const [availableTools, setAvailableTools] = useState([]);
|
|
1124
|
-
const [operationAllowances, setOperationAllowances] = useState({
|
|
1125
|
-
sitecore: [],
|
|
1126
|
-
filesystem: [],
|
|
1127
|
-
});
|
|
1128
|
-
const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
|
|
1129
|
-
const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
|
|
1130
|
-
const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
|
|
1131
|
-
const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
|
|
1132
|
-
const [availableToolsError, setAvailableToolsError] = useState(null);
|
|
1133
|
-
const [operationAllowancesError, setOperationAllowancesError] = useState(null);
|
|
1134
|
-
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1135
|
-
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1136
|
-
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1137
|
-
const isPersistedAgent = !!agent?.userId;
|
|
1138
|
-
const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
|
|
1139
687
|
const hasSpawnedAgents = useMemo(() => {
|
|
1140
688
|
if (!agentMetadata)
|
|
1141
689
|
return false;
|
|
1142
|
-
const childAgents = agentMetadata?.
|
|
690
|
+
const childAgents = agentMetadata?.ChildAgents ||
|
|
691
|
+
agentMetadata?.childAgents;
|
|
1143
692
|
if (!Array.isArray(childAgents) || childAgents.length === 0)
|
|
1144
693
|
return false;
|
|
1145
|
-
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));
|
|
1146
695
|
}, [agentMetadata]);
|
|
1147
696
|
const hasTodoContent = useMemo(() => {
|
|
1148
697
|
const metadataTodos = (() => {
|
|
1149
698
|
try {
|
|
1150
|
-
const todoList = agentMetadata?.additionalData?.todoList
|
|
699
|
+
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
700
|
+
agentMetadata?.todoList;
|
|
1151
701
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
1152
702
|
const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
|
|
1153
703
|
return raw.length > 0;
|
|
@@ -1169,7 +719,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1169
719
|
return isUpdating;
|
|
1170
720
|
}, [agentMetadata, messages]);
|
|
1171
721
|
const prevAgentIdRef = useRef(undefined);
|
|
1172
|
-
const hasHistoryContent =
|
|
722
|
+
const [hasHistoryContent, setHasHistoryContent] = useState(false);
|
|
1173
723
|
useEffect(() => {
|
|
1174
724
|
const currentId = agent?.id;
|
|
1175
725
|
if (prevAgentIdRef.current !== currentId) {
|
|
@@ -1185,37 +735,47 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1185
735
|
}
|
|
1186
736
|
}, [agent?.id]);
|
|
1187
737
|
useEffect(() => {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
catch (e) {
|
|
1201
|
-
if (active) {
|
|
1202
|
-
setSkillsError(e?.message || "Failed to load skills");
|
|
1203
|
-
setAvailableSkills([]);
|
|
1204
|
-
setSkillRootIds([]);
|
|
1205
|
-
setSelectableTemplateIds([]);
|
|
1206
|
-
}
|
|
738
|
+
if (!agent?.id) {
|
|
739
|
+
setHasHistoryContent(false);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
setHasHistoryContent(false);
|
|
743
|
+
let cancelled = false;
|
|
744
|
+
getAgentHistory(agent.id, 10)
|
|
745
|
+
.then((result) => {
|
|
746
|
+
if (cancelled)
|
|
747
|
+
return;
|
|
748
|
+
if (result.type === "success" && result.data && result.data.length > 0) {
|
|
749
|
+
setHasHistoryContent(true);
|
|
1207
750
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
setSkillsLoading(false);
|
|
1211
|
-
}
|
|
751
|
+
else {
|
|
752
|
+
setHasHistoryContent(false);
|
|
1212
753
|
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
754
|
+
})
|
|
755
|
+
.catch(() => {
|
|
756
|
+
if (!cancelled)
|
|
757
|
+
setHasHistoryContent(false);
|
|
758
|
+
});
|
|
1215
759
|
return () => {
|
|
1216
|
-
|
|
760
|
+
cancelled = true;
|
|
1217
761
|
};
|
|
1218
|
-
}, []);
|
|
762
|
+
}, [agent?.id]);
|
|
763
|
+
const agentIdForHistoryRef = useRef(agent?.id);
|
|
764
|
+
agentIdForHistoryRef.current = agent?.id;
|
|
765
|
+
useEffect(() => {
|
|
766
|
+
if (!agent?.id || !editContext?.addSocketMessageListener)
|
|
767
|
+
return;
|
|
768
|
+
const unsubscribe = editContext.addSocketMessageListener((message) => {
|
|
769
|
+
if (message.type !== "edit-operation")
|
|
770
|
+
return;
|
|
771
|
+
const op = message.payload;
|
|
772
|
+
const operationAgentId = op.agentId || op.user?.agentId;
|
|
773
|
+
if (operationAgentId === agentIdForHistoryRef.current) {
|
|
774
|
+
setHasHistoryContent(true);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
return () => unsubscribe();
|
|
778
|
+
}, [agent?.id, editContext?.addSocketMessageListener]);
|
|
1219
779
|
const modeOptions = useMemo(() => [
|
|
1220
780
|
{
|
|
1221
781
|
value: "supervised",
|
|
@@ -1250,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1250
810
|
value: m.id,
|
|
1251
811
|
label: m.name,
|
|
1252
812
|
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1253
|
-
const metadataSelectedSkillIds = useMemo(() => {
|
|
1254
|
-
const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
|
|
1255
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1256
|
-
return [];
|
|
1257
|
-
}
|
|
1258
|
-
return rawSkillIds
|
|
1259
|
-
.map((x) => String(x || "").trim())
|
|
1260
|
-
.filter((x) => x.length > 0);
|
|
1261
|
-
}, [agentMetadata]);
|
|
1262
|
-
const backendAssignedSkillIds = useMemo(() => {
|
|
1263
|
-
const rawSkillIds = agent?.assignedSkillIds ?? [];
|
|
1264
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1265
|
-
return [];
|
|
1266
|
-
}
|
|
1267
|
-
return rawSkillIds
|
|
1268
|
-
.map((x) => String(x || "").trim())
|
|
1269
|
-
.filter((x) => x.length > 0);
|
|
1270
|
-
}, [agent]);
|
|
1271
|
-
const preloadedSkillIds = useMemo(() => {
|
|
1272
|
-
const rawSkillIds = activeProfile?.preloadSkills ?? [];
|
|
1273
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1274
|
-
return [];
|
|
1275
|
-
}
|
|
1276
|
-
return rawSkillIds
|
|
1277
|
-
.map((skill) => String(skill?.id || "").trim())
|
|
1278
|
-
.filter((id) => id.length > 0);
|
|
1279
|
-
}, [activeProfile?.preloadSkills]);
|
|
1280
|
-
const autoAssignedSkillIds = useMemo(() => {
|
|
1281
|
-
const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
|
|
1282
|
-
const all = isLocalOnlyDraftAgent
|
|
1283
|
-
? preloadedSkillIds
|
|
1284
|
-
: backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
|
|
1285
|
-
const seen = new Set();
|
|
1286
|
-
const unique = [];
|
|
1287
|
-
for (const id of all) {
|
|
1288
|
-
const key = id.toLowerCase();
|
|
1289
|
-
if (seen.has(key))
|
|
1290
|
-
continue;
|
|
1291
|
-
seen.add(key);
|
|
1292
|
-
unique.push(id);
|
|
1293
|
-
}
|
|
1294
|
-
return unique;
|
|
1295
|
-
}, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
|
|
1296
|
-
const selectedSkillIds = useMemo(() => {
|
|
1297
|
-
const all = [
|
|
1298
|
-
...autoAssignedSkillIds,
|
|
1299
|
-
...backendAssignedSkillIds,
|
|
1300
|
-
...metadataSelectedSkillIds,
|
|
1301
|
-
];
|
|
1302
|
-
const seen = new Set();
|
|
1303
|
-
const unique = [];
|
|
1304
|
-
for (const id of all) {
|
|
1305
|
-
const key = id.toLowerCase();
|
|
1306
|
-
if (seen.has(key))
|
|
1307
|
-
continue;
|
|
1308
|
-
seen.add(key);
|
|
1309
|
-
unique.push(id);
|
|
1310
|
-
}
|
|
1311
|
-
return unique;
|
|
1312
|
-
}, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1313
|
-
useEffect(() => {
|
|
1314
|
-
let active = true;
|
|
1315
|
-
if (!showAgentSettings) {
|
|
1316
|
-
return () => {
|
|
1317
|
-
active = false;
|
|
1318
|
-
};
|
|
1319
|
-
}
|
|
1320
|
-
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
1321
|
-
setTriggerSubscriptions([]);
|
|
1322
|
-
setTriggerSubscriptionsLoading(false);
|
|
1323
|
-
setTriggerSubscriptionsError(null);
|
|
1324
|
-
setAvailableTools([]);
|
|
1325
|
-
setAvailableToolsLoading(false);
|
|
1326
|
-
setAvailableToolsError(null);
|
|
1327
|
-
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1328
|
-
setOperationAllowancesLoading(false);
|
|
1329
|
-
setOperationAllowancesError(null);
|
|
1330
|
-
return () => {
|
|
1331
|
-
active = false;
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
const loadTriggerSubscriptions = async () => {
|
|
1335
|
-
try {
|
|
1336
|
-
setTriggerSubscriptionsLoading(true);
|
|
1337
|
-
setTriggerSubscriptionsError(null);
|
|
1338
|
-
const subscriptions = await getAgentTriggerSubscriptions(agent.id);
|
|
1339
|
-
if (active) {
|
|
1340
|
-
setTriggerSubscriptions(subscriptions);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
catch (e) {
|
|
1344
|
-
if (active) {
|
|
1345
|
-
setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
finally {
|
|
1349
|
-
if (active) {
|
|
1350
|
-
setTriggerSubscriptionsLoading(false);
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
|
-
const loadOperationAllowances = async () => {
|
|
1355
|
-
try {
|
|
1356
|
-
setOperationAllowancesLoading(true);
|
|
1357
|
-
setOperationAllowancesError(null);
|
|
1358
|
-
const allowances = await getAgentOperationAllowances(agent.id);
|
|
1359
|
-
if (active) {
|
|
1360
|
-
setOperationAllowances(allowances);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
catch (e) {
|
|
1364
|
-
if (active) {
|
|
1365
|
-
setOperationAllowancesError(e?.message || "Failed to load allowances");
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
finally {
|
|
1369
|
-
if (active) {
|
|
1370
|
-
setOperationAllowancesLoading(false);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
const loadAvailableTools = async () => {
|
|
1375
|
-
try {
|
|
1376
|
-
setAvailableToolsLoading(true);
|
|
1377
|
-
setAvailableToolsError(null);
|
|
1378
|
-
const tools = await getAgentAvailableTools(agent.id);
|
|
1379
|
-
if (active) {
|
|
1380
|
-
setAvailableTools(tools);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
catch (e) {
|
|
1384
|
-
if (active) {
|
|
1385
|
-
setAvailableToolsError(e?.message || "Failed to load available tools");
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
finally {
|
|
1389
|
-
if (active) {
|
|
1390
|
-
setAvailableToolsLoading(false);
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
};
|
|
1394
|
-
void loadTriggerSubscriptions();
|
|
1395
|
-
void loadAvailableTools();
|
|
1396
|
-
void loadOperationAllowances();
|
|
1397
|
-
return () => {
|
|
1398
|
-
active = false;
|
|
1399
|
-
};
|
|
1400
|
-
}, [
|
|
1401
|
-
showAgentSettings,
|
|
1402
|
-
agent?.id,
|
|
1403
|
-
agent?.status,
|
|
1404
|
-
agent?.userId,
|
|
1405
|
-
agent?.profileId,
|
|
1406
|
-
mode,
|
|
1407
|
-
selectedSkillIds,
|
|
1408
|
-
]);
|
|
1409
|
-
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
1410
|
-
const allowanceGroups = useMemo(() => [
|
|
1411
|
-
{
|
|
1412
|
-
key: "sitecore",
|
|
1413
|
-
label: "Sitecore",
|
|
1414
|
-
rows: operationAllowances.sitecore,
|
|
1415
|
-
},
|
|
1416
|
-
{
|
|
1417
|
-
key: "filesystem",
|
|
1418
|
-
label: "Filesystem",
|
|
1419
|
-
rows: operationAllowances.filesystem,
|
|
1420
|
-
},
|
|
1421
|
-
], [operationAllowances]);
|
|
1422
|
-
const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
|
|
1423
|
-
operationAllowances.filesystem.length > 0, [operationAllowances]);
|
|
1424
|
-
const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
|
|
1425
|
-
operationAllowances.filesystem.length, [operationAllowances]);
|
|
1426
|
-
const listedProfileSkillIdSet = useMemo(() => {
|
|
1427
|
-
const ids = [
|
|
1428
|
-
...(activeProfile?.availableSkills ?? []),
|
|
1429
|
-
...(activeProfile?.allowedSkills ?? []),
|
|
1430
|
-
]
|
|
1431
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1432
|
-
.filter((id) => id.length > 0);
|
|
1433
|
-
return new Set(ids);
|
|
1434
|
-
}, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
|
|
1435
|
-
const profileFilteredSkills = useMemo(() => {
|
|
1436
|
-
if (listedProfileSkillIdSet.size === 0) {
|
|
1437
|
-
return [];
|
|
1438
|
-
}
|
|
1439
|
-
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1440
|
-
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1441
|
-
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1442
|
-
const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
|
|
1443
|
-
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1444
|
-
.filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
|
|
1445
|
-
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
1446
|
-
const selectedSkills = useMemo(() => selectedSkillIds
|
|
1447
|
-
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
1448
|
-
.filter((s) => !!s), [availableSkills, selectedSkillIds]);
|
|
1449
|
-
const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
|
|
1450
|
-
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1451
|
-
const previewAvailableTools = useMemo(() => {
|
|
1452
|
-
const baseToolNames = mode === "read-only"
|
|
1453
|
-
? (activeProfile?.readOnlyToolNames ?? [])
|
|
1454
|
-
: (activeProfile?.allowedToolNames ?? []);
|
|
1455
|
-
const toolNames = new Set();
|
|
1456
|
-
for (const toolName of baseToolNames) {
|
|
1457
|
-
const normalized = String(toolName || "").trim();
|
|
1458
|
-
if (normalized) {
|
|
1459
|
-
toolNames.add(normalized);
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
for (const skill of selectedSkills) {
|
|
1463
|
-
for (const toolName of skill.additionalAllowedTools ?? []) {
|
|
1464
|
-
const normalized = String(toolName || "").trim();
|
|
1465
|
-
if (normalized) {
|
|
1466
|
-
toolNames.add(normalized);
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
|
|
1471
|
-
}, [
|
|
1472
|
-
activeProfile?.allowedToolNames,
|
|
1473
|
-
activeProfile?.readOnlyToolNames,
|
|
1474
|
-
mode,
|
|
1475
|
-
selectedSkills,
|
|
1476
|
-
]);
|
|
1477
|
-
const displayedAvailableTools = isLocalOnlyDraftAgent
|
|
1478
|
-
? previewAvailableTools
|
|
1479
|
-
: availableTools;
|
|
1480
|
-
const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
|
|
1481
|
-
? false
|
|
1482
|
-
: availableToolsLoading;
|
|
1483
|
-
const displayedAvailableToolsError = isLocalOnlyDraftAgent
|
|
1484
|
-
? null
|
|
1485
|
-
: availableToolsError;
|
|
1486
813
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1487
814
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1488
815
|
try {
|
|
@@ -1507,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1507
834
|
return meta;
|
|
1508
835
|
}
|
|
1509
836
|
}, []);
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
const current = agentMetadata || {};
|
|
1525
|
-
const currentAdditionalData = current.additionalData ||
|
|
1526
|
-
{};
|
|
1527
|
-
const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
|
|
1528
|
-
const next = {
|
|
1529
|
-
...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,
|
|
1530
849
|
};
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
try {
|
|
1538
|
-
await updateAgentContext(agent.id, next);
|
|
1539
|
-
setAgentMetadata(next);
|
|
1540
|
-
setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
|
|
1541
|
-
}
|
|
1542
|
-
catch (e) {
|
|
1543
|
-
console.error("Failed to clear legacy selected skills", e);
|
|
1544
|
-
}
|
|
1545
|
-
}, [
|
|
1546
|
-
agent?.id,
|
|
1547
|
-
agentMetadata,
|
|
1548
|
-
metadataSelectedSkillIds,
|
|
1549
|
-
setAgent,
|
|
1550
|
-
setAgentMetadata,
|
|
1551
|
-
]);
|
|
1552
|
-
const parsePersistedAgentContext = (contextJson) => {
|
|
1553
|
-
try {
|
|
1554
|
-
if (!contextJson)
|
|
1555
|
-
return null;
|
|
1556
|
-
const parsedContext = JSON.parse(contextJson);
|
|
1557
|
-
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1558
|
-
return null;
|
|
1559
|
-
}
|
|
1560
|
-
return sanitizeAgentMetadata(parsedContext);
|
|
1561
|
-
}
|
|
1562
|
-
catch {
|
|
1563
|
-
return null;
|
|
1564
|
-
}
|
|
1565
|
-
};
|
|
1566
|
-
const buildDraftPersistenceContext = () => {
|
|
1567
|
-
let effectiveContext = agentMetadata;
|
|
1568
|
-
try {
|
|
1569
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
1570
|
-
const profile = activeProfile ||
|
|
1571
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
1572
|
-
const isLiveMode = profile?.editorContextMode === "live";
|
|
1573
|
-
if (isLiveMode && typeof buildCurrentContext === "function") {
|
|
1574
|
-
const freshContext = buildCurrentContext();
|
|
1575
|
-
if (freshContext) {
|
|
1576
|
-
effectiveContext = {
|
|
1577
|
-
...(agentMetadata || {}),
|
|
1578
|
-
items: freshContext.items,
|
|
1579
|
-
currentItemId: freshContext.currentItemId,
|
|
1580
|
-
components: freshContext.components,
|
|
1581
|
-
field: freshContext.field,
|
|
1582
|
-
activeWorkspace: freshContext.activeWorkspace,
|
|
1583
|
-
hasPageLoaded: freshContext.hasPageLoaded,
|
|
1584
|
-
openSidebars: freshContext.openSidebars,
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
catch (e) {
|
|
1590
|
-
console.warn("[AgentTerminal] Failed to compute draft context:", e);
|
|
1591
|
-
}
|
|
1592
|
-
return sanitizeAgentMetadata(effectiveContext || null);
|
|
1593
|
-
};
|
|
1594
|
-
const ensureDraftAgentPersisted = async () => {
|
|
1595
|
-
if (!agent?.id) {
|
|
1596
|
-
throw new Error("Agent not ready. Please try again.");
|
|
1597
|
-
}
|
|
1598
|
-
if (!isLocalOnlyDraftAgent) {
|
|
1599
|
-
return agent;
|
|
1600
|
-
}
|
|
1601
|
-
const requestSettings = getPendingRequestSettings();
|
|
1602
|
-
if (!requestSettings.profileId) {
|
|
1603
|
-
throw new Error("Select an agent profile before adding a skill.");
|
|
1604
|
-
}
|
|
1605
|
-
const effectiveContext = buildDraftPersistenceContext();
|
|
1606
|
-
const persistedAgent = await persistDraftAgent({
|
|
1607
|
-
agentId: agent.id,
|
|
1608
|
-
sessionId: editContext?.sessionId || undefined,
|
|
1609
|
-
profileId: requestSettings.profileId,
|
|
1610
|
-
mode: requestSettings.mode,
|
|
1611
|
-
model: requestSettings.modelId || undefined,
|
|
1612
|
-
name: agent.name,
|
|
1613
|
-
context: effectiveContext,
|
|
1614
|
-
});
|
|
1615
|
-
pendingSettingsRef.current = null;
|
|
1616
|
-
const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
|
|
1617
|
-
effectiveContext;
|
|
1618
|
-
setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
|
|
1619
|
-
setAgent((prev) => {
|
|
1620
|
-
const next = {
|
|
1621
|
-
...(prev || {}),
|
|
1622
|
-
...persistedAgent,
|
|
1623
|
-
};
|
|
1624
|
-
if (prev?.messages?.length && !persistedAgent.messages?.length) {
|
|
1625
|
-
next.messages = prev.messages;
|
|
1626
|
-
}
|
|
1627
|
-
return next;
|
|
1628
|
-
});
|
|
1629
|
-
onAgentUpdate?.(persistedAgent);
|
|
1630
|
-
return persistedAgent;
|
|
1631
|
-
};
|
|
1632
|
-
const handleAddSkill = useCallback(async (skillId) => {
|
|
1633
|
-
if (selectedSkillSet.has(skillId.toLowerCase()))
|
|
1634
|
-
return;
|
|
1635
|
-
if (!agent?.id)
|
|
1636
|
-
return;
|
|
1637
|
-
try {
|
|
1638
|
-
setSkillActionError(null);
|
|
1639
|
-
const persistedAgent = await ensureDraftAgentPersisted();
|
|
1640
|
-
await assignAgentSkill({ agentId: persistedAgent.id, skillId });
|
|
1641
|
-
setAgent((prev) => {
|
|
1642
|
-
if (!prev)
|
|
1643
|
-
return prev;
|
|
1644
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1645
|
-
? prev.assignedSkillIds
|
|
1646
|
-
: [];
|
|
1647
|
-
if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
|
|
1648
|
-
return prev;
|
|
1649
|
-
}
|
|
1650
|
-
return {
|
|
1651
|
-
...prev,
|
|
1652
|
-
assignedSkillIds: [...currentAssigned, skillId],
|
|
1653
|
-
};
|
|
1654
|
-
});
|
|
1655
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1656
|
-
return true;
|
|
1657
|
-
}
|
|
1658
|
-
catch (e) {
|
|
1659
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1660
|
-
return false;
|
|
1661
|
-
}
|
|
1662
|
-
}, [
|
|
1663
|
-
agent?.id,
|
|
1664
|
-
assignAgentSkill,
|
|
1665
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1666
|
-
ensureDraftAgentPersisted,
|
|
1667
|
-
getSkillActionErrorMessage,
|
|
1668
|
-
setAgent,
|
|
1669
|
-
selectedSkillSet,
|
|
1670
|
-
]);
|
|
1671
|
-
const handleRemoveSkill = useCallback(async (skillId) => {
|
|
1672
|
-
if (!agent?.id)
|
|
1673
|
-
return;
|
|
1674
|
-
try {
|
|
1675
|
-
setSkillActionError(null);
|
|
1676
|
-
await revokeAgentSkill({ agentId: agent.id, skillId });
|
|
1677
|
-
setAgent((prev) => {
|
|
1678
|
-
if (!prev)
|
|
1679
|
-
return prev;
|
|
1680
|
-
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1681
|
-
? prev.assignedSkillIds
|
|
1682
|
-
: [];
|
|
1683
|
-
return {
|
|
1684
|
-
...prev,
|
|
1685
|
-
assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
|
|
1686
|
-
};
|
|
1687
|
-
});
|
|
1688
|
-
await clearLegacySelectedSkillsFromMetadata();
|
|
1689
|
-
}
|
|
1690
|
-
catch (e) {
|
|
1691
|
-
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1692
|
-
}
|
|
1693
|
-
}, [
|
|
1694
|
-
agent?.id,
|
|
1695
|
-
clearLegacySelectedSkillsFromMetadata,
|
|
1696
|
-
getSkillActionErrorMessage,
|
|
1697
|
-
revokeAgentSkill,
|
|
1698
|
-
setAgent,
|
|
1699
|
-
]);
|
|
1700
|
-
const handleOpenProfileSettings = useCallback(async () => {
|
|
1701
|
-
if (!editContext || !activeProfile?.id)
|
|
1702
|
-
return;
|
|
1703
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1704
|
-
await editContext.loadItem({
|
|
1705
|
-
id: activeProfile.id,
|
|
1706
|
-
language: lang,
|
|
1707
|
-
version: 0,
|
|
1708
|
-
});
|
|
1709
|
-
editContext.switchWorkspace("editor");
|
|
1710
|
-
}, [editContext, activeProfile?.id]);
|
|
1711
|
-
const handleEditProfileSideBySide = useCallback(async () => {
|
|
1712
|
-
if (!editContext || !activeProfile?.id)
|
|
1713
|
-
return;
|
|
1714
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1715
|
-
const profileItem = {
|
|
1716
|
-
id: activeProfile.id,
|
|
1717
|
-
language: lang,
|
|
1718
|
-
version: 0,
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
deterministicFlags = {
|
|
853
|
+
deterministic: false,
|
|
854
|
+
seed: undefined,
|
|
1719
855
|
};
|
|
1720
|
-
|
|
1721
|
-
editContext.setShowAgentsWorkspaceEditor(true);
|
|
1722
|
-
await editContext.loadItem(profileItem);
|
|
1723
|
-
return;
|
|
1724
|
-
}
|
|
1725
|
-
await editContext.loadItem(profileItem, {
|
|
1726
|
-
openInNewSlot: true,
|
|
1727
|
-
});
|
|
1728
|
-
editContext.switchWorkspace("editor");
|
|
1729
|
-
}, [editContext, activeProfile?.id]);
|
|
1730
|
-
const handleOpenSkillItem = useCallback(async (skillId) => {
|
|
1731
|
-
if (!editContext || !skillId)
|
|
1732
|
-
return;
|
|
1733
|
-
const lang = editContext.currentItemDescriptor?.language || "en";
|
|
1734
|
-
await editContext.loadItem({
|
|
1735
|
-
id: skillId,
|
|
1736
|
-
language: lang,
|
|
1737
|
-
version: 0,
|
|
1738
|
-
});
|
|
1739
|
-
editContext.switchWorkspace("editor");
|
|
1740
|
-
}, [editContext]);
|
|
856
|
+
}
|
|
1741
857
|
useEffect(() => {
|
|
1742
|
-
|
|
858
|
+
localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
|
|
1743
859
|
}, [promptHistory]);
|
|
1744
860
|
useEffect(() => {
|
|
1745
861
|
// Keep messagesRef synchronized with messages state
|
|
@@ -1751,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1751
867
|
const [liveTotals, setLiveTotals] = useState(null);
|
|
1752
868
|
// Context window status from backend (extracted from delta cost object)
|
|
1753
869
|
const [contextWindowStatus, setContextWindowStatus] = useState(null);
|
|
1754
|
-
const effectiveModelName = useMemo(() => {
|
|
1755
|
-
const fromContextWindow = contextWindowStatus?.model?.trim();
|
|
1756
|
-
if (fromContextWindow)
|
|
1757
|
-
return fromContextWindow;
|
|
1758
|
-
const fromAgent = agent?.model?.trim();
|
|
1759
|
-
if (fromAgent)
|
|
1760
|
-
return fromAgent;
|
|
1761
|
-
const models = activeProfile?.models || [];
|
|
1762
|
-
const selectedModelName = selectedModelId
|
|
1763
|
-
? models.find((m) => m.id === selectedModelId)?.name?.trim()
|
|
1764
|
-
: undefined;
|
|
1765
|
-
if (selectedModelName)
|
|
1766
|
-
return selectedModelName;
|
|
1767
|
-
const defaultModelName = activeProfile?.defaultModelId
|
|
1768
|
-
? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
|
|
1769
|
-
: undefined;
|
|
1770
|
-
if (defaultModelName)
|
|
1771
|
-
return defaultModelName;
|
|
1772
|
-
return models[0]?.name?.trim() || undefined;
|
|
1773
|
-
}, [
|
|
1774
|
-
contextWindowStatus?.model,
|
|
1775
|
-
agent?.model,
|
|
1776
|
-
activeProfile?.models,
|
|
1777
|
-
activeProfile?.defaultModelId,
|
|
1778
|
-
selectedModelId,
|
|
1779
|
-
]);
|
|
1780
870
|
// Flag to track when we should create a new message
|
|
1781
871
|
const shouldCreateNewMessage = useRef(false);
|
|
1782
872
|
// Keep a ref to the current messages for immediate access
|
|
@@ -1787,17 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1787
877
|
const placeholderInputRef = useRef(null);
|
|
1788
878
|
const promptPlaceholderInputRef = useRef(null);
|
|
1789
879
|
const messagesContainerRef = useRef(null);
|
|
1790
|
-
const inlineDialogContainerRef = useRef(null);
|
|
1791
880
|
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
1792
|
-
const [recentToolUiEvents, setRecentToolUiEvents] = useState([]);
|
|
1793
881
|
// WebSocket subscription state for agent streaming
|
|
1794
882
|
const seenMessageIdsRef = useRef(new Set());
|
|
1795
883
|
const lastSeqRef = useRef(0);
|
|
1796
884
|
const subscribedAgentIdRef = useRef(null);
|
|
1797
|
-
|
|
1798
|
-
const toolCallFirstSeenAtRef = useRef({});
|
|
1799
|
-
const pendingToolCompletionTimersRef = useRef({});
|
|
1800
|
-
// 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)
|
|
1801
886
|
const pendingSettingsRef = useRef(null);
|
|
1802
887
|
// Track whether textarea should maintain focus during re-renders
|
|
1803
888
|
const shouldMaintainFocusRef = useRef(false);
|
|
@@ -1819,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1819
904
|
setIsVoiceSupported(!!SR);
|
|
1820
905
|
if (SR === undefined) {
|
|
1821
906
|
setIsVoiceDisabled(true);
|
|
1822
|
-
|
|
907
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1823
908
|
return;
|
|
1824
909
|
}
|
|
1825
910
|
else {
|
|
1826
|
-
const wasDisabled =
|
|
911
|
+
const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
|
|
1827
912
|
setIsVoiceDisabled(wasDisabled);
|
|
1828
913
|
}
|
|
1829
914
|
}
|
|
@@ -1834,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1834
919
|
// Auto-focus terminal input on mount
|
|
1835
920
|
useEffect(() => {
|
|
1836
921
|
if (textareaRef.current) {
|
|
1837
|
-
|
|
1838
|
-
textareaRef.current.focus({ preventScroll: true });
|
|
1839
|
-
}
|
|
1840
|
-
catch {
|
|
1841
|
-
textareaRef.current.focus();
|
|
1842
|
-
}
|
|
922
|
+
textareaRef.current.focus();
|
|
1843
923
|
}
|
|
1844
924
|
}, []);
|
|
1845
925
|
// Start voice recognition
|
|
@@ -1858,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1858
938
|
r.lang = editContext?.currentItemDescriptor?.language || "en-US";
|
|
1859
939
|
r.continuous = true;
|
|
1860
940
|
r.interimResults = true;
|
|
1861
|
-
promptBeforeVoiceRef.current = prompt;
|
|
1862
941
|
r.onstart = () => {
|
|
1863
942
|
setIsListening(true);
|
|
1864
943
|
prevPlaceholderRef.current = inputPlaceholder;
|
|
1865
944
|
setInputPlaceholder("Listening...");
|
|
1866
945
|
};
|
|
1867
|
-
// Desktop Chrome: single result at index 0, transitions interim→final once.
|
|
1868
|
-
// Mobile Chrome: new result index per event, ALL immediately isFinal,
|
|
1869
|
-
// each containing the full cumulative transcript. We always take the last
|
|
1870
|
-
// non-empty final result and REPLACE the prompt to handle both patterns.
|
|
1871
946
|
r.onresult = (event) => {
|
|
1872
|
-
let
|
|
947
|
+
let finalText = "";
|
|
1873
948
|
let interimText = "";
|
|
1874
|
-
for (let i = event.
|
|
949
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
1875
950
|
const res = event.results[i];
|
|
1876
|
-
if (res.isFinal
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
}
|
|
1881
|
-
if (!res.isFinal) {
|
|
1882
|
-
interimText = (res[0]?.transcript || "") + interimText;
|
|
1883
|
-
}
|
|
951
|
+
if (res.isFinal)
|
|
952
|
+
finalText += res[0]?.transcript || "";
|
|
953
|
+
else
|
|
954
|
+
interimText += res[0]?.transcript || "";
|
|
1884
955
|
}
|
|
1885
956
|
if (interimText) {
|
|
1886
957
|
setInputPlaceholder(`Listening... ${interimText.trim()}`);
|
|
@@ -1888,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1888
959
|
else {
|
|
1889
960
|
setInputPlaceholder("Listening...");
|
|
1890
961
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
962
|
+
if (finalText && finalText.trim()) {
|
|
963
|
+
setPrompt((prev) => {
|
|
964
|
+
const prefix = prev && !prev.endsWith(" ") ? prev + " " : prev;
|
|
965
|
+
return (prefix || "") + finalText.trim() + " ";
|
|
966
|
+
});
|
|
1896
967
|
if (textareaRef.current) {
|
|
1897
968
|
try {
|
|
1898
969
|
const v = textareaRef.current.value || "";
|
|
@@ -1908,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1908
979
|
// network error also comes from incompatible chromium client
|
|
1909
980
|
if (e?.error === "network") {
|
|
1910
981
|
try {
|
|
1911
|
-
|
|
982
|
+
localStorage.setItem(VOICE_DISABLED_KEY, "true");
|
|
1912
983
|
setIsVoiceDisabled(true);
|
|
1913
984
|
}
|
|
1914
985
|
catch { }
|
|
@@ -2077,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2077
1148
|
// Shared stream message handlers with messageId support
|
|
2078
1149
|
const createNewStreamMessage = useCallback((messageId, agentData) => {
|
|
2079
1150
|
const currentAgent = agentData || agent;
|
|
2080
|
-
|
|
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
|
+
}
|
|
2081
1159
|
// Reduced: avoid verbose logging during streaming
|
|
2082
1160
|
return {
|
|
2083
1161
|
id: messageId,
|
|
2084
|
-
agentId:
|
|
1162
|
+
agentId: currentAgent.id,
|
|
2085
1163
|
messageIndex: -1,
|
|
2086
1164
|
role: "assistant",
|
|
2087
1165
|
content: "",
|
|
2088
1166
|
name: "agent",
|
|
2089
1167
|
messageType: "streaming",
|
|
2090
1168
|
isCompleted: false,
|
|
2091
|
-
model: currentAgent
|
|
1169
|
+
model: currentAgent.model || "",
|
|
2092
1170
|
tokensUsed: 0,
|
|
2093
1171
|
inputTokens: 0,
|
|
2094
1172
|
outputTokens: 0,
|
|
@@ -2097,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2097
1175
|
outputTokenCost: 0,
|
|
2098
1176
|
cachedInputTokenCost: 0,
|
|
2099
1177
|
totalCost: 0,
|
|
2100
|
-
currency: currentAgent
|
|
1178
|
+
currency: currentAgent.currency || "USD",
|
|
2101
1179
|
createdDate: new Date().toISOString(),
|
|
2102
1180
|
toolCalls: [],
|
|
2103
1181
|
};
|
|
2104
|
-
}, [agent
|
|
2105
|
-
const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
|
|
2106
|
-
const handleHeartbeatMessage = useCallback((message) => {
|
|
2107
|
-
const heartbeatText = (message.data?.message ||
|
|
2108
|
-
message.data?.Message ||
|
|
2109
|
-
"Still working on your request. This is taking a little longer than usual.").trim() ||
|
|
2110
|
-
"Still working on your request. This is taking a little longer than usual.";
|
|
2111
|
-
const createdDate = message.timestamp || new Date().toISOString();
|
|
2112
|
-
const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
|
|
2113
|
-
setMessages((prev) => {
|
|
2114
|
-
const withoutHeartbeats = stripHeartbeatMessages(prev);
|
|
2115
|
-
const updated = [
|
|
2116
|
-
...withoutHeartbeats,
|
|
2117
|
-
{
|
|
2118
|
-
id: heartbeatId,
|
|
2119
|
-
agentId: agent?.id || agentStub.id,
|
|
2120
|
-
messageIndex: -1,
|
|
2121
|
-
role: "system",
|
|
2122
|
-
content: heartbeatText,
|
|
2123
|
-
name: "system",
|
|
2124
|
-
messageType: "heartbeat",
|
|
2125
|
-
isCompleted: true,
|
|
2126
|
-
model: "",
|
|
2127
|
-
tokensUsed: 0,
|
|
2128
|
-
inputTokens: 0,
|
|
2129
|
-
outputTokens: 0,
|
|
2130
|
-
cachedInputTokens: 0,
|
|
2131
|
-
inputTokenCost: 0,
|
|
2132
|
-
outputTokenCost: 0,
|
|
2133
|
-
cachedInputTokenCost: 0,
|
|
2134
|
-
totalCost: 0,
|
|
2135
|
-
currency: "USD",
|
|
2136
|
-
createdDate,
|
|
2137
|
-
toolCalls: [],
|
|
2138
|
-
},
|
|
2139
|
-
];
|
|
2140
|
-
messagesRef.current = updated;
|
|
2141
|
-
return updated;
|
|
2142
|
-
});
|
|
2143
|
-
}, [agent?.id, agentStub.id, stripHeartbeatMessages]);
|
|
2144
|
-
const clearHeartbeatMessages = useCallback(() => {
|
|
2145
|
-
setMessages((prev) => {
|
|
2146
|
-
const updated = stripHeartbeatMessages(prev);
|
|
2147
|
-
if (updated.length === prev.length) {
|
|
2148
|
-
return prev;
|
|
2149
|
-
}
|
|
2150
|
-
messagesRef.current = updated;
|
|
2151
|
-
return updated;
|
|
2152
|
-
});
|
|
2153
|
-
}, [stripHeartbeatMessages]);
|
|
1182
|
+
}, [agent]);
|
|
2154
1183
|
const handleContentChunk = useCallback((message, agentData) => {
|
|
2155
1184
|
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
2156
1185
|
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
2157
1186
|
let messageId = message.data?.messageId;
|
|
2158
1187
|
if (!messageId && agentData?.id) {
|
|
2159
|
-
console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
|
|
2160
|
-
agentId: agentData.id,
|
|
2161
|
-
isIncremental: message.data?.isIncremental,
|
|
2162
|
-
previousContentLength: message.data?.previousContentLength,
|
|
2163
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2164
|
-
});
|
|
2165
1188
|
// For backward compatibility: if no messageId, find or create the current streaming message
|
|
2166
1189
|
// This handles cases where the backend doesn't send messageId
|
|
2167
1190
|
const currentMessages = messagesRef.current;
|
|
@@ -2175,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2175
1198
|
// If the agent isn't currently running (e.g., we switched tabs after the run
|
|
2176
1199
|
// completed), skip creating a new streaming message to avoid duplicates.
|
|
2177
1200
|
const currentAgentStatus = (agentData || agent)?.status;
|
|
2178
|
-
const isAgentRunning = currentAgentStatus === "running";
|
|
1201
|
+
const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
|
|
2179
1202
|
if (!isAgentRunning) {
|
|
2180
1203
|
return;
|
|
2181
1204
|
}
|
|
@@ -2208,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2208
1231
|
outputCost: Number(cost.output) || 0,
|
|
2209
1232
|
cachedCost: Number(cost.cached) || 0,
|
|
2210
1233
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2211
|
-
imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
|
|
2212
|
-
0,
|
|
2213
1234
|
totalCost: Number(cost.total) || 0,
|
|
2214
1235
|
currency: "USD",
|
|
2215
1236
|
};
|
|
@@ -2233,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2233
1254
|
setIsWaitingForResponse(false);
|
|
2234
1255
|
shouldCreateNewMessage.current = false;
|
|
2235
1256
|
}
|
|
2236
|
-
// Extract context window info from cost object
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
if (contextWindowRaw !== undefined &&
|
|
2242
|
-
contextWindowRaw !== null &&
|
|
2243
|
-
contextUsedRaw !== undefined &&
|
|
2244
|
-
contextUsedRaw !== null) {
|
|
2245
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2246
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2247
|
-
if (contextWindowValue > 0 &&
|
|
2248
|
-
Number.isFinite(contextUsedValue) &&
|
|
2249
|
-
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) {
|
|
2250
1262
|
setContextWindowStatus({
|
|
2251
1263
|
contextWindowTokens: contextWindowValue,
|
|
2252
1264
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2262,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2262
1274
|
const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
|
|
2263
1275
|
if (existingMessageIndex === -1) {
|
|
2264
1276
|
// Message doesn't exist - create new streaming message
|
|
2265
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2266
|
-
if (message.data?.isIncremental && previousContentLength > 0) {
|
|
2267
|
-
console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
|
|
2268
|
-
messageId,
|
|
2269
|
-
previousContentLength,
|
|
2270
|
-
totalContentLength: message.data?.totalContentLength,
|
|
2271
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2272
|
-
});
|
|
2273
|
-
}
|
|
2274
1277
|
const newStreamMessage = createNewStreamMessage(messageId, agentData);
|
|
2275
1278
|
// Set the content for the new message
|
|
2276
1279
|
const updatedNewMessage = { ...newStreamMessage };
|
|
@@ -2293,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2293
1296
|
return prev;
|
|
2294
1297
|
// Check if existing content is already longer than what we're trying to stream
|
|
2295
1298
|
const currentContentLength = existingMessage.content?.length || 0;
|
|
2296
|
-
const previousContentLength = message.data?.previousContentLength || 0;
|
|
2297
1299
|
const totalContentLength = message.data?.totalContentLength || 0;
|
|
2298
|
-
if (
|
|
2299
|
-
previousContentLength !== currentContentLength &&
|
|
2300
|
-
(previousContentLength > 0 || currentContentLength > 0)) {
|
|
2301
|
-
console.warn("[AgentTerminal] Content chunk length mismatch", {
|
|
2302
|
-
messageId,
|
|
2303
|
-
previousContentLength,
|
|
2304
|
-
currentContentLength,
|
|
2305
|
-
totalContentLength,
|
|
2306
|
-
deltaLength: (message.data?.deltaContent || "").length,
|
|
2307
|
-
});
|
|
2308
|
-
}
|
|
2309
|
-
if (message.data?.isIncremental &&
|
|
2310
|
-
currentContentLength >= totalContentLength &&
|
|
1300
|
+
if (currentContentLength >= totalContentLength &&
|
|
2311
1301
|
totalContentLength > 0) {
|
|
2312
1302
|
return prev;
|
|
2313
1303
|
}
|
|
@@ -2331,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2331
1321
|
});
|
|
2332
1322
|
}, [createNewStreamMessage, agent]);
|
|
2333
1323
|
const handleToolCall = useCallback((message, agentData) => {
|
|
2334
|
-
const
|
|
2335
|
-
const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1324
|
+
const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2336
1325
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2337
1326
|
let toolCallMessageId = message.data?.messageId;
|
|
2338
1327
|
if (!toolCallMessageId) {
|
|
2339
|
-
console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
|
|
2340
|
-
toolCallId,
|
|
2341
|
-
toolName: message.data?.name || message.data?.displayName,
|
|
2342
|
-
});
|
|
2343
1328
|
const current = messagesRef.current;
|
|
2344
1329
|
const lastStreaming = [...current]
|
|
2345
1330
|
.reverse()
|
|
@@ -2347,13 +1332,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2347
1332
|
if (lastStreaming?.id) {
|
|
2348
1333
|
toolCallMessageId = lastStreaming.id;
|
|
2349
1334
|
}
|
|
2350
|
-
else {
|
|
2351
|
-
// Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
|
|
2352
|
-
// Create a synthetic streaming message so the UI can render the tool call immediately.
|
|
2353
|
-
toolCallMessageId = crypto.randomUUID();
|
|
2354
|
-
}
|
|
2355
1335
|
}
|
|
2356
|
-
appendToolUiEvent("ui:tool-call-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId || "none"} providedMessageId=${String(message.data?.messageId || "none")}`);
|
|
2357
1336
|
// Find or create the target message for this tool call
|
|
2358
1337
|
if (toolCallMessageId) {
|
|
2359
1338
|
const currentMessages = messagesRef.current;
|
|
@@ -2380,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2380
1359
|
}
|
|
2381
1360
|
// Add tool call to the message in the array
|
|
2382
1361
|
if (toolCallMessageId && message.data && toolCallId) {
|
|
2383
|
-
const
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
new Date().toISOString();
|
|
1362
|
+
const functionName = message.data.functionName ||
|
|
1363
|
+
message.data.name ||
|
|
1364
|
+
message.data.function?.name ||
|
|
1365
|
+
"unknown";
|
|
2388
1366
|
const toolCall = {
|
|
2389
1367
|
id: toolCallId,
|
|
2390
1368
|
messageId: toolCallMessageId,
|
|
2391
1369
|
dbMessageId: message.data.messageId, // Database message ID for approval/rejection
|
|
2392
1370
|
toolCallId: toolCallId,
|
|
2393
|
-
functionName:
|
|
2394
|
-
functionArguments:
|
|
1371
|
+
functionName: functionName,
|
|
1372
|
+
functionArguments: message.data.functionArguments ||
|
|
1373
|
+
message.data.arguments ||
|
|
1374
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2395
1375
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2396
|
-
|
|
2397
|
-
functionError: toolCallError,
|
|
2398
|
-
isPruned,
|
|
1376
|
+
functionError: message.data.functionError || message.data.error || "",
|
|
2399
1377
|
isCompleted: false,
|
|
2400
1378
|
responseTimeMs: message.data.responseTimeMs,
|
|
2401
|
-
createdDate:
|
|
1379
|
+
createdDate: new Date().toISOString(),
|
|
2402
1380
|
requiresApproval: message.data?.requiresApproval,
|
|
2403
1381
|
};
|
|
2404
1382
|
// Check for existing tool call - search across ALL messages by toolCallId first
|
|
@@ -2437,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2437
1415
|
// Check if the new data has more information than what we have
|
|
2438
1416
|
const newArgs = toolCall.functionArguments;
|
|
2439
1417
|
const existingArgs = existingToolCall.functionArguments;
|
|
2440
|
-
const
|
|
2441
|
-
const existingArgsText = stringifyToolField(existingArgs) || "";
|
|
2442
|
-
const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
|
|
2443
|
-
newArgsText !== existingArgsText) ||
|
|
2444
|
-
(existingArgsText === "{}" && newArgsText !== "{}");
|
|
1418
|
+
const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
|
|
2445
1419
|
const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
|
|
2446
|
-
const hasNewRichContent = toolCall.functionResultRichContent &&
|
|
2447
|
-
!existingToolCall.functionResultRichContent;
|
|
2448
1420
|
const hasNewError = toolCall.functionError && !existingToolCall.functionError;
|
|
2449
1421
|
const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
|
|
2450
1422
|
const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
|
|
2451
1423
|
// Only update if there's meaningful new data
|
|
2452
1424
|
if (hasMoreCompleteArgs ||
|
|
2453
1425
|
hasNewResult ||
|
|
2454
|
-
hasNewRichContent ||
|
|
2455
1426
|
hasNewError ||
|
|
2456
1427
|
hasNewApprovalInfo ||
|
|
2457
1428
|
hasNewDbMessageId) {
|
|
@@ -2468,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2468
1439
|
updatedToolCalls[idx] = {
|
|
2469
1440
|
...existing,
|
|
2470
1441
|
functionArguments: hasMoreCompleteArgs
|
|
2471
|
-
?
|
|
2472
|
-
:
|
|
1442
|
+
? newArgs
|
|
1443
|
+
: existing.functionArguments,
|
|
2473
1444
|
functionResult: toolCall.functionResult || existing.functionResult,
|
|
2474
|
-
functionResultRichContent: toolCall.functionResultRichContent ||
|
|
2475
|
-
existing.functionResultRichContent,
|
|
2476
1445
|
functionError: toolCall.functionError || existing.functionError,
|
|
2477
1446
|
requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
|
|
2478
1447
|
};
|
|
@@ -2490,36 +1459,27 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2490
1459
|
}
|
|
2491
1460
|
return; // Done updating existing tool call
|
|
2492
1461
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
|
|
2500
|
-
});
|
|
2501
|
-
messagesRef.current = updated;
|
|
2502
|
-
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] };
|
|
2503
1468
|
});
|
|
1469
|
+
messagesRef.current = updated;
|
|
1470
|
+
return updated;
|
|
2504
1471
|
});
|
|
2505
|
-
const messageWithToolCall = messagesRef.current.find((msg) => msg.id === toolCallMessageId);
|
|
2506
|
-
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}`);
|
|
2507
1472
|
// If tool requires approval, agent is now waiting for user action - stop thinking
|
|
2508
1473
|
if (message.data?.requiresApproval) {
|
|
2509
1474
|
setIsAgentThinking(false);
|
|
2510
1475
|
}
|
|
2511
1476
|
}
|
|
2512
|
-
}, [
|
|
1477
|
+
}, [createNewStreamMessage]);
|
|
2513
1478
|
const handleToolResult = useCallback((message, agentData) => {
|
|
2514
|
-
const
|
|
2515
|
-
const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
|
|
1479
|
+
const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
|
|
2516
1480
|
// Prefer provided messageId, otherwise fall back to the last streaming assistant message
|
|
2517
1481
|
let resultMessageId = message.data?.messageId;
|
|
2518
1482
|
if (!resultMessageId) {
|
|
2519
|
-
console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
|
|
2520
|
-
toolCallId: resultToolCallId,
|
|
2521
|
-
toolName: message.data?.functionName || message.data?.displayName,
|
|
2522
|
-
});
|
|
2523
1483
|
const current = messagesRef.current;
|
|
2524
1484
|
const lastStreaming = [...current]
|
|
2525
1485
|
.reverse()
|
|
@@ -2541,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2541
1501
|
outputCost: Number(cost.output) || 0,
|
|
2542
1502
|
cachedCost: Number(cost.cached) || 0,
|
|
2543
1503
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
2544
|
-
imageCost: Number(cost.imageCost) || 0,
|
|
2545
1504
|
totalCost: Number(cost.total) || 0,
|
|
2546
1505
|
currency: "USD",
|
|
2547
1506
|
};
|
|
@@ -2553,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2553
1512
|
if (anyNonZero) {
|
|
2554
1513
|
setLiveTotals(nextTotals);
|
|
2555
1514
|
}
|
|
2556
|
-
// Extract context window info from cost object
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
if (contextWindowRaw !== undefined &&
|
|
2562
|
-
contextWindowRaw !== null &&
|
|
2563
|
-
contextUsedRaw !== undefined &&
|
|
2564
|
-
contextUsedRaw !== null) {
|
|
2565
|
-
const contextWindowValue = Number(contextWindowRaw);
|
|
2566
|
-
const contextUsedValue = Number(contextUsedRaw);
|
|
2567
|
-
if (contextWindowValue > 0 &&
|
|
2568
|
-
Number.isFinite(contextUsedValue) &&
|
|
2569
|
-
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) {
|
|
2570
1520
|
setContextWindowStatus({
|
|
2571
1521
|
contextWindowTokens: contextWindowValue,
|
|
2572
1522
|
estimatedInputTokens: contextUsedValue,
|
|
@@ -2590,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2590
1540
|
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
2591
1541
|
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
2592
1542
|
cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
|
|
2593
|
-
imageCost: Number(data.totalImageCost) || 0,
|
|
2594
1543
|
totalCost: Number(data.totalCost) || 0,
|
|
2595
1544
|
currency: data.currency || "USD",
|
|
2596
1545
|
};
|
|
@@ -2606,10 +1555,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2606
1555
|
}
|
|
2607
1556
|
// Update tool result directly in the messages array
|
|
2608
1557
|
if (!resultMessageId) {
|
|
2609
|
-
appendToolUiEvent("ui:tool-result-dropped", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} reason=no-result-message-id`);
|
|
2610
1558
|
return;
|
|
2611
1559
|
}
|
|
2612
|
-
appendToolUiEvent("ui:tool-result-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId}`);
|
|
2613
1560
|
// Update the message with tool result
|
|
2614
1561
|
setMessages((prev) => {
|
|
2615
1562
|
const updated = prev.map((msg) => {
|
|
@@ -2625,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2625
1572
|
const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
|
|
2626
1573
|
if (existingToolCall && message.data) {
|
|
2627
1574
|
const updatedToolCalls = [...updatedMessage.toolCalls];
|
|
2628
|
-
const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
|
|
2629
|
-
const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
|
|
2630
|
-
const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
|
|
2631
|
-
nextArgsText !== existingArgsText) ||
|
|
2632
|
-
(existingArgsText === "{}" && nextArgsText !== "{}");
|
|
2633
1575
|
const toolCall = {
|
|
2634
1576
|
id: existingToolCall.id,
|
|
2635
1577
|
messageId: existingToolCall.messageId,
|
|
2636
1578
|
toolCallId: existingToolCall.toolCallId,
|
|
2637
1579
|
functionName: existingToolCall.functionName,
|
|
2638
|
-
functionArguments:
|
|
2639
|
-
? nextArgsText
|
|
2640
|
-
: existingToolCall.functionArguments,
|
|
1580
|
+
functionArguments: existingToolCall.functionArguments,
|
|
2641
1581
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2642
|
-
functionResultRichContent: message.data.richContent ||
|
|
2643
|
-
existingToolCall.functionResultRichContent,
|
|
2644
1582
|
functionError: message.data.functionError || message.data.error || "",
|
|
2645
1583
|
isCompleted: true,
|
|
2646
1584
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2657,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2657
1595
|
}
|
|
2658
1596
|
else if (message.data && resultToolCallId && resultMessageId) {
|
|
2659
1597
|
// Create new tool call if it doesn't exist
|
|
2660
|
-
const
|
|
2661
|
-
message.
|
|
2662
|
-
|
|
1598
|
+
const functionName = message.data.functionName ||
|
|
1599
|
+
message.data.name ||
|
|
1600
|
+
message.data.function?.name ||
|
|
1601
|
+
"unknown";
|
|
2663
1602
|
const toolCall = {
|
|
2664
1603
|
id: resultToolCallId,
|
|
2665
1604
|
messageId: resultMessageId,
|
|
2666
1605
|
toolCallId: resultToolCallId,
|
|
2667
|
-
functionName:
|
|
2668
|
-
functionArguments:
|
|
1606
|
+
functionName: functionName,
|
|
1607
|
+
functionArguments: message.data.functionArguments ||
|
|
1608
|
+
message.data.arguments ||
|
|
1609
|
+
JSON.stringify(message.data.function?.arguments || {}),
|
|
2669
1610
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2670
|
-
functionResultRichContent: message.data.richContent || undefined,
|
|
2671
1611
|
functionError: message.data.functionError || message.data.error || "",
|
|
2672
1612
|
isCompleted: true,
|
|
2673
1613
|
responseTimeMs: message.data.responseTimeMs,
|
|
2674
|
-
createdDate:
|
|
1614
|
+
createdDate: new Date().toISOString(),
|
|
2675
1615
|
};
|
|
2676
1616
|
updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
|
|
2677
1617
|
}
|
|
@@ -2679,12 +1619,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2679
1619
|
return updatedMessage;
|
|
2680
1620
|
});
|
|
2681
1621
|
messagesRef.current = updated;
|
|
2682
|
-
const messageWithToolResult = updated.find((msg) => msg.id === resultMessageId);
|
|
2683
|
-
const matchingToolCall = messageWithToolResult?.toolCalls?.find((tc) => tc.toolCallId === resultToolCallId);
|
|
2684
|
-
appendToolUiEvent("ui:tool-result-applied", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId} completed=${matchingToolCall?.isCompleted ? "yes" : "no"} messageToolCalls=${messageWithToolResult?.toolCalls?.length || 0}`);
|
|
2685
1622
|
return updated;
|
|
2686
1623
|
});
|
|
2687
|
-
}, [
|
|
1624
|
+
}, []);
|
|
2688
1625
|
// Listen for local approval resolution to update UI
|
|
2689
1626
|
useEffect(() => {
|
|
2690
1627
|
const onApprovalResolved = (ev) => {
|
|
@@ -2701,11 +1638,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2701
1638
|
if (!messageId || !toolCallId)
|
|
2702
1639
|
return;
|
|
2703
1640
|
// Update local state to reflect approval status
|
|
2704
|
-
const currentMessages = messagesRef.current || [];
|
|
2705
|
-
const hasMatchingToolCall = currentMessages.some((m) => (m.toolCalls || []).some((tc) => tc.toolCallId === toolCallId));
|
|
2706
|
-
if (!hasMatchingToolCall) {
|
|
2707
|
-
return;
|
|
2708
|
-
}
|
|
2709
1641
|
setMessages((prev) => {
|
|
2710
1642
|
const updated = prev.map((m) => {
|
|
2711
1643
|
// Update tool calls in ANY message that contains this toolCallId
|
|
@@ -2770,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2770
1702
|
// The agent might have been persisted after sending a prompt
|
|
2771
1703
|
// Only treat as "new" if backend returns 404
|
|
2772
1704
|
const hasExistingMessages = messagesRef.current.length > 0;
|
|
2773
|
-
if (agentStub.status === "new" &&
|
|
2774
|
-
!agentStub.userId &&
|
|
2775
|
-
!hasExistingMessages) {
|
|
1705
|
+
if (agentStub.status === "new" && !hasExistingMessages) {
|
|
2776
1706
|
// Only initialize as new if we have no messages yet (initial mount)
|
|
2777
1707
|
// Derive initial profile from provided metadata if present
|
|
2778
1708
|
const initialProfileIdFromMeta = (() => {
|
|
@@ -2812,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2812
1742
|
totalInputTokenCost: 0,
|
|
2813
1743
|
totalOutputTokenCost: 0,
|
|
2814
1744
|
totalCachedInputTokenCost: 0,
|
|
2815
|
-
totalImageCost: 0,
|
|
2816
1745
|
totalCost: 0,
|
|
2817
1746
|
messageCount: 0,
|
|
2818
1747
|
});
|
|
@@ -2855,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2855
1784
|
}
|
|
2856
1785
|
})();
|
|
2857
1786
|
// Create context with top-level properties (what ContextInfoBar expects)
|
|
2858
|
-
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;
|
|
2859
1829
|
let nextMetadata = null;
|
|
2860
1830
|
if (initialMetadata) {
|
|
2861
1831
|
// Merge initial metadata with local context (using top-level structure)
|
|
@@ -2931,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2931
1901
|
seenMessageIdsRef.current.add(msg.id.toLowerCase());
|
|
2932
1902
|
}
|
|
2933
1903
|
});
|
|
2934
|
-
// Keep local streaming if the agent is still
|
|
2935
|
-
// This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
|
|
2936
|
-
// "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
|
|
2937
1905
|
const isRunning = agentData.status === "running" || agentData.status === 1;
|
|
2938
|
-
const isWaiting = agentData.status === "waitingForApproval" ||
|
|
2939
|
-
agentData.status === 2 ||
|
|
2940
|
-
agentData.status === "waitingForInput" ||
|
|
2941
|
-
agentData.status === "costLimitReached" ||
|
|
2942
|
-
agentData.status === 7;
|
|
2943
|
-
const keepLocalStreaming = isRunning || isWaiting;
|
|
2944
1906
|
// Filter local messages to only include streaming/incomplete messages that aren't in DB
|
|
2945
1907
|
// This prevents duplicates when reloading - DB messages are source of truth for completed messages
|
|
2946
1908
|
const localStreaming = isRunning
|
|
@@ -2958,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2958
1920
|
// Don't keep completed messages from local state - DB is source of truth
|
|
2959
1921
|
return false;
|
|
2960
1922
|
})
|
|
2961
|
-
:
|
|
2962
|
-
? messagesRef.current.filter((localMsg) => {
|
|
2963
|
-
if (!localMsg.id)
|
|
2964
|
-
return false;
|
|
2965
|
-
if (!localMsg.isCompleted &&
|
|
2966
|
-
localMsg.messageType === "streaming") {
|
|
2967
|
-
const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
|
|
2968
|
-
dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
|
|
2969
|
-
return !existsInDb;
|
|
2970
|
-
}
|
|
2971
|
-
return false;
|
|
2972
|
-
})
|
|
2973
|
-
: [];
|
|
1923
|
+
: [];
|
|
2974
1924
|
const merged = mergeMessagesById(dbMessages, localStreaming);
|
|
2975
1925
|
messagesRef.current = merged;
|
|
2976
1926
|
setMessages(merged);
|
|
@@ -2981,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2981
1931
|
const runningNow = agentData.status === "running" || agentData.status === 1;
|
|
2982
1932
|
const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
|
|
2983
1933
|
agentData.status === 2;
|
|
2984
|
-
const waitingForInputNow = agentData.status === "waitingForInput";
|
|
2985
1934
|
const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
2986
1935
|
if (runningNow || hasStreamingNow) {
|
|
2987
1936
|
setIsWaitingForResponse(true);
|
|
@@ -2989,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2989
1938
|
// Agent is actively running, show thinking dots
|
|
2990
1939
|
setIsAgentThinking(true);
|
|
2991
1940
|
}
|
|
2992
|
-
else if (waitingForApprovalNow
|
|
1941
|
+
else if (waitingForApprovalNow) {
|
|
2993
1942
|
setIsWaitingForResponse(false);
|
|
2994
1943
|
isWaitingRef.current = false;
|
|
2995
1944
|
setIsConnecting(false);
|
|
2996
|
-
// Agent is waiting for user
|
|
1945
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
2997
1946
|
setIsAgentThinking(false);
|
|
2998
1947
|
}
|
|
2999
1948
|
else {
|
|
@@ -3042,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3042
1991
|
if (!contextJson)
|
|
3043
1992
|
return null;
|
|
3044
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
|
|
3045
1997
|
if (parsedContext && typeof parsedContext === "object") {
|
|
3046
|
-
|
|
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;
|
|
3047
2007
|
}
|
|
3048
2008
|
return null;
|
|
3049
2009
|
}
|
|
@@ -3089,21 +2049,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3089
2049
|
setIsLoading(false);
|
|
3090
2050
|
}
|
|
3091
2051
|
}, [agentStub.id]);
|
|
3092
|
-
const reconcileServerStateRef = useRef(null);
|
|
3093
|
-
useEffect(() => {
|
|
3094
|
-
reconcileServerStateRef.current = async () => {
|
|
3095
|
-
if (reconcileServerStateInFlightRef.current) {
|
|
3096
|
-
return;
|
|
3097
|
-
}
|
|
3098
|
-
reconcileServerStateInFlightRef.current = true;
|
|
3099
|
-
try {
|
|
3100
|
-
await loadAgent();
|
|
3101
|
-
}
|
|
3102
|
-
finally {
|
|
3103
|
-
reconcileServerStateInFlightRef.current = false;
|
|
3104
|
-
}
|
|
3105
|
-
};
|
|
3106
|
-
}, [loadAgent]);
|
|
3107
2052
|
// Initial load
|
|
3108
2053
|
useEffect(() => {
|
|
3109
2054
|
loadAgent();
|
|
@@ -3125,9 +2070,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3125
2070
|
setAgentOperations([]);
|
|
3126
2071
|
return;
|
|
3127
2072
|
}
|
|
3128
|
-
setAgentOperations([]);
|
|
3129
2073
|
try {
|
|
3130
|
-
const result = await getAgentHistory(agent.id,
|
|
2074
|
+
const result = await getAgentHistory(agent.id, 1000);
|
|
3131
2075
|
if (result.type === "success") {
|
|
3132
2076
|
setAgentOperations(result.data ?? []);
|
|
3133
2077
|
}
|
|
@@ -3145,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3145
2089
|
return;
|
|
3146
2090
|
}
|
|
3147
2091
|
// Check if cost limit exceeded based on status or cost values
|
|
3148
|
-
const statusIndicatesLimit = agent.status === "costLimitReached";
|
|
2092
|
+
const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
|
|
3149
2093
|
// Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
|
|
3150
2094
|
const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
|
|
3151
2095
|
const costExceedsLimit = agent.costLimit &&
|
|
@@ -3230,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3230
2174
|
// Handle agent:profile:switched (profile changed via switch-profile function)
|
|
3231
2175
|
if (messageType === "agent:profile:switched") {
|
|
3232
2176
|
const payload = message.payload || {};
|
|
3233
|
-
const switchedAgentId = payload.agentId;
|
|
3234
|
-
const newProfileId = payload.newProfileId;
|
|
3235
|
-
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;
|
|
3236
2180
|
if (switchedAgentId === agent.id && newProfileId) {
|
|
3237
2181
|
// Update the agent's profile
|
|
3238
2182
|
setAgent((prev) => {
|
|
@@ -3263,15 +2207,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3263
2207
|
const op = message.payload;
|
|
3264
2208
|
const operationAgentId = op.agentId || op.user?.agentId;
|
|
3265
2209
|
if (operationAgentId === agent.id) {
|
|
3266
|
-
|
|
2210
|
+
// Reload operations when this agent performs an operation
|
|
2211
|
+
getAgentHistory(agent.id, 1000).then((result) => {
|
|
2212
|
+
if (result.type === "success") {
|
|
2213
|
+
setAgentOperations(result.data ?? []);
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
3267
2216
|
}
|
|
3268
2217
|
return;
|
|
3269
2218
|
}
|
|
3270
2219
|
// For other agent messages, check if this is for our agent
|
|
3271
|
-
const agentId = message.payload?.agentId;
|
|
3272
|
-
if (agentId !== agent.id)
|
|
2220
|
+
const agentId = message.payload?.agentId || message.payload?.AgentId;
|
|
2221
|
+
if (agentId !== agent.id)
|
|
3273
2222
|
return;
|
|
3274
|
-
}
|
|
3275
2223
|
// Handle agent:run:start
|
|
3276
2224
|
if (messageType === "agent:run:start") {
|
|
3277
2225
|
// If a stop operation is in progress, ignore this message to prevent
|
|
@@ -3280,15 +2228,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3280
2228
|
console.log("[AgentTerminal] Ignoring agent:run:start during stop operation");
|
|
3281
2229
|
return;
|
|
3282
2230
|
}
|
|
3283
|
-
const currentStatus = agentRef.current?.status;
|
|
3284
|
-
if (currentStatus === "waitingForInput" ||
|
|
3285
|
-
currentStatus === "waitingForApproval" ||
|
|
3286
|
-
currentStatus === "costLimitReached") {
|
|
3287
|
-
// Replayed start messages arrive before the buffered status payload when
|
|
3288
|
-
// reopening an already-paused agent. Preserve the attention state instead
|
|
3289
|
-
// of flashing "running" until the follow-up status update lands.
|
|
3290
|
-
return;
|
|
3291
|
-
}
|
|
3292
2231
|
// Reset run-scoped deduplication so new run seq values (starting at 1)
|
|
3293
2232
|
// are not discarded due to previous run's lastSeqRef
|
|
3294
2233
|
lastSeqRef.current = 0;
|
|
@@ -3384,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3384
2323
|
if (messageType === "agent:prompt:queued") {
|
|
3385
2324
|
const { queueEntry } = message.payload;
|
|
3386
2325
|
if (queueEntry) {
|
|
3387
|
-
if (shouldSuppressQueuedPrompt(queueEntry)) {
|
|
3388
|
-
return;
|
|
3389
|
-
}
|
|
3390
2326
|
// Add the new prompt to the queued prompts list
|
|
3391
2327
|
setQueuedPrompts((prev) => {
|
|
3392
2328
|
// Check if prompt already exists (deduplication)
|
|
@@ -3415,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3415
2351
|
return;
|
|
3416
2352
|
}
|
|
3417
2353
|
const { seq, type, data, cost } = message.payload;
|
|
3418
|
-
if (type === "ToolCall" || type === "toolCall") {
|
|
3419
|
-
}
|
|
3420
2354
|
// Always allow ContextUpdate messages (metadata updates) regardless of agent status
|
|
3421
2355
|
const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
|
|
3422
|
-
const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
|
|
3423
|
-
const shouldClearHeartbeat = type === "ContentChunk" ||
|
|
3424
|
-
type === "contentChunk" ||
|
|
3425
|
-
type === "ToolCall" ||
|
|
3426
|
-
type === "toolCall" ||
|
|
3427
|
-
type === "ToolResult" ||
|
|
3428
|
-
type === "toolResult";
|
|
3429
2356
|
// Allow deltas if the agent is running OR if we already have an active streaming message
|
|
3430
2357
|
// OR if the server provided a messageId for targeted updates.
|
|
3431
2358
|
// This avoids dropping early deltas that may arrive before the 'running' status update.
|
|
3432
2359
|
// ContextUpdate messages are always allowed as they're metadata updates, not content.
|
|
3433
2360
|
const isRunning = agent.status === "running" || agent.status === 1;
|
|
3434
2361
|
const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3435
|
-
const hasMessageId = !!message?.payload?.data?.messageId
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
!isRunning &&
|
|
3439
|
-
!hasStreaming &&
|
|
3440
|
-
!hasMessageId) {
|
|
2362
|
+
const hasMessageId = !!message?.payload?.data?.messageId ||
|
|
2363
|
+
!!message?.payload?.data?.MessageId;
|
|
2364
|
+
if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
|
|
3441
2365
|
return; // ignore only if we cannot safely target an existing streaming message
|
|
3442
2366
|
}
|
|
3443
2367
|
// Deduplicate by sequence
|
|
@@ -3454,23 +2378,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3454
2378
|
cost,
|
|
3455
2379
|
timestamp: new Date().toISOString(),
|
|
3456
2380
|
};
|
|
3457
|
-
if (shouldClearHeartbeat) {
|
|
3458
|
-
clearHeartbeatMessages();
|
|
3459
|
-
}
|
|
3460
2381
|
if (type === "ContentChunk" || type === "contentChunk") {
|
|
3461
2382
|
handleContentChunk(agentStreamMessage, agent);
|
|
3462
2383
|
}
|
|
3463
2384
|
else if (type === "ToolCall" || type === "toolCall") {
|
|
3464
|
-
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);
|
|
3465
2385
|
handleToolCall(agentStreamMessage, agent);
|
|
3466
2386
|
}
|
|
3467
2387
|
else if (type === "ToolResult" || type === "toolResult") {
|
|
3468
|
-
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);
|
|
3469
2388
|
handleToolResult(agentStreamMessage, agent);
|
|
3470
2389
|
}
|
|
3471
|
-
else if (type === "Heartbeat" || type === "heartbeat") {
|
|
3472
|
-
handleHeartbeatMessage(agentStreamMessage);
|
|
3473
|
-
}
|
|
3474
2390
|
else if (type === "ContextUpdate" || type === "contextUpdate") {
|
|
3475
2391
|
// Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
|
|
3476
2392
|
const contextData = data;
|
|
@@ -3480,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3480
2396
|
const current = (prev || {});
|
|
3481
2397
|
const updated = { ...current };
|
|
3482
2398
|
// Merge additionalData if present (deep merge to preserve existing values)
|
|
3483
|
-
if (contextData.additionalData) {
|
|
3484
|
-
const sourceAdditionalData = contextData.additionalData ||
|
|
2399
|
+
if (contextData.additionalData || contextData.AdditionalData) {
|
|
2400
|
+
const sourceAdditionalData = contextData.additionalData ||
|
|
2401
|
+
contextData.AdditionalData ||
|
|
2402
|
+
{};
|
|
3485
2403
|
updated.additionalData = {
|
|
3486
2404
|
...(current.additionalData || {}),
|
|
3487
2405
|
...sourceAdditionalData,
|
|
@@ -3489,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3489
2407
|
}
|
|
3490
2408
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3491
2409
|
// Backend sends the complete updated list, not just additions
|
|
3492
|
-
if (contextData.childAgents) {
|
|
3493
|
-
const childAgents = contextData.childAgents;
|
|
2410
|
+
if (contextData.ChildAgents || contextData.childAgents) {
|
|
2411
|
+
const childAgents = contextData.ChildAgents || contextData.childAgents;
|
|
3494
2412
|
if (Array.isArray(childAgents)) {
|
|
3495
|
-
updated.
|
|
2413
|
+
updated.ChildAgents = childAgents;
|
|
3496
2414
|
}
|
|
3497
2415
|
}
|
|
3498
2416
|
return updated;
|
|
@@ -3518,19 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3518
2436
|
// Route based on statusData.state
|
|
3519
2437
|
try {
|
|
3520
2438
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3521
|
-
const normalizedStatus =
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
2439
|
+
const normalizedStatus = statusData?.state ||
|
|
2440
|
+
statusData?.Status ||
|
|
2441
|
+
statusData?.status;
|
|
2442
|
+
if (normalizedStatus === "Cancelled" ||
|
|
2443
|
+
normalizedStatus === "canceled") {
|
|
3525
2444
|
// Stop indicators and mark any in-progress streaming messages as completed
|
|
3526
|
-
clearHeartbeatMessages();
|
|
3527
|
-
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3528
2445
|
setIsWaitingForResponse(false);
|
|
3529
2446
|
isWaitingRef.current = false;
|
|
3530
2447
|
setIsConnecting(false);
|
|
3531
|
-
setIsSubmitting(false);
|
|
3532
|
-
shouldCreateNewMessage.current = false;
|
|
3533
|
-
setIsAgentThinking(false);
|
|
3534
2448
|
setMessages((prev) => {
|
|
3535
2449
|
const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
|
|
3536
2450
|
? {
|
|
@@ -3542,7 +2456,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3542
2456
|
messagesRef.current = updated;
|
|
3543
2457
|
return updated;
|
|
3544
2458
|
});
|
|
3545
|
-
void reconcileServerStateRef.current?.();
|
|
3546
2459
|
return;
|
|
3547
2460
|
}
|
|
3548
2461
|
if (statusData?.state === "streamOpen") {
|
|
@@ -3574,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3574
2487
|
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
3575
2488
|
cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
|
|
3576
2489
|
cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
|
|
3577
|
-
imageCost: Number(totals.totalImageCost) || 0,
|
|
3578
2490
|
totalCost: totalCost,
|
|
3579
2491
|
currency: totals.currency,
|
|
3580
2492
|
};
|
|
@@ -3586,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3586
2498
|
if (anyNonZero) {
|
|
3587
2499
|
setLiveTotals(nextTotals);
|
|
3588
2500
|
}
|
|
3589
|
-
// Fallback context usage update for providers that do not include
|
|
3590
|
-
// context usage in every stream delta (for example some OpenAI streams).
|
|
3591
|
-
// Prefer explicit status values when present; otherwise derive from totals.
|
|
3592
|
-
const contextWindowValue = Number(statusData?.contextWindow ??
|
|
3593
|
-
agent?.contextWindowTokens ??
|
|
3594
|
-
0);
|
|
3595
|
-
const contextUsedValue = Number(statusData?.contextUsed ??
|
|
3596
|
-
(Number(totals.totalInputTokens) || 0) +
|
|
3597
|
-
(Number(totals.totalCachedInputTokens) || 0) +
|
|
3598
|
-
(Number(totals.totalCacheWriteTokens) || 0));
|
|
3599
|
-
if (contextWindowValue > 0 &&
|
|
3600
|
-
Number.isFinite(contextUsedValue) &&
|
|
3601
|
-
contextUsedValue >= 0) {
|
|
3602
|
-
setContextWindowStatus({
|
|
3603
|
-
contextWindowTokens: contextWindowValue,
|
|
3604
|
-
estimatedInputTokens: contextUsedValue,
|
|
3605
|
-
contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
|
|
3606
|
-
model: agent?.model || "",
|
|
3607
|
-
});
|
|
3608
|
-
}
|
|
3609
2501
|
// If server provides costLimit along with totals, persist it locally
|
|
3610
2502
|
if (statusCostLimit) {
|
|
3611
2503
|
setAgent((prev) => prev &&
|
|
@@ -3627,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3627
2519
|
return;
|
|
3628
2520
|
}
|
|
3629
2521
|
if (statusData?.state === "ToolApprovalsRequired") {
|
|
3630
|
-
setPendingBrowserCaptureDialogType(null);
|
|
3631
2522
|
const msgId = statusData.messageId;
|
|
3632
2523
|
const ids = statusData.toolCallIds || [];
|
|
3633
2524
|
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
@@ -3659,44 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3659
2550
|
setIsWaitingForResponse(false);
|
|
3660
2551
|
return;
|
|
3661
2552
|
}
|
|
3662
|
-
// Handle
|
|
3663
|
-
if (
|
|
3664
|
-
|
|
3665
|
-
setPendingBrowserCaptureDialogType(null);
|
|
2553
|
+
// Handle "WaitingForApproval" state explicitly
|
|
2554
|
+
if (statusData?.state === "WaitingForApproval" ||
|
|
2555
|
+
statusData?.state === "waitingForApproval") {
|
|
3666
2556
|
setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
|
|
3667
2557
|
setIsConnecting(false);
|
|
3668
2558
|
setIsWaitingForResponse(false);
|
|
3669
2559
|
setIsAgentThinking(false);
|
|
3670
|
-
void reconcileServerStateRef.current?.();
|
|
3671
|
-
return;
|
|
3672
|
-
}
|
|
3673
|
-
if (normalizedStatus === "waitingForInput") {
|
|
3674
|
-
clearStopGuard();
|
|
3675
|
-
const dialogType = typeof statusData?.dialogType === "string"
|
|
3676
|
-
? statusData.dialogType
|
|
3677
|
-
: null;
|
|
3678
|
-
const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
3679
|
-
dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
3680
|
-
setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
|
|
3681
|
-
setAgent((prev) => prev
|
|
3682
|
-
? {
|
|
3683
|
-
...prev,
|
|
3684
|
-
status: "waitingForInput",
|
|
3685
|
-
statusMessage: isBrowserCaptureWait &&
|
|
3686
|
-
typeof statusData?.title === "string" &&
|
|
3687
|
-
statusData.title.trim()
|
|
3688
|
-
? statusData.title.trim()
|
|
3689
|
-
: prev.statusMessage,
|
|
3690
|
-
}
|
|
3691
|
-
: prev);
|
|
3692
|
-
setIsConnecting(false);
|
|
3693
|
-
setIsWaitingForResponse(false);
|
|
3694
|
-
setIsAgentThinking(false);
|
|
3695
2560
|
return;
|
|
3696
2561
|
}
|
|
3697
|
-
if (
|
|
3698
|
-
clearStopGuard();
|
|
3699
|
-
setPendingBrowserCaptureDialogType(null);
|
|
2562
|
+
if (statusData?.state === "CostLimitReached") {
|
|
3700
2563
|
const totalCost = Number(statusData.totalCost) || 0;
|
|
3701
2564
|
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
3702
2565
|
setCostLimitExceeded({
|
|
@@ -3714,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3714
2577
|
// Server sends additionalData directly (with todoList inside)
|
|
3715
2578
|
// Also may include ChildAgents for spawned agents
|
|
3716
2579
|
try {
|
|
3717
|
-
const serverAdditionalData = statusData.additionalData || {};
|
|
2580
|
+
const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
|
|
3718
2581
|
setAgentMetadata((prev) => {
|
|
3719
2582
|
const current = (prev || {});
|
|
3720
2583
|
const updated = { ...current };
|
|
@@ -3728,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3728
2591
|
}
|
|
3729
2592
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3730
2593
|
// Backend sends the complete updated list, not just additions
|
|
3731
|
-
if (statusData.childAgents) {
|
|
3732
|
-
const childAgents = statusData.childAgents;
|
|
2594
|
+
if (statusData.ChildAgents || statusData.childAgents) {
|
|
2595
|
+
const childAgents = statusData.ChildAgents || statusData.childAgents;
|
|
3733
2596
|
if (Array.isArray(childAgents)) {
|
|
3734
|
-
updated.
|
|
2597
|
+
updated.ChildAgents = childAgents;
|
|
3735
2598
|
}
|
|
3736
2599
|
}
|
|
3737
2600
|
return updated;
|
|
@@ -3743,11 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3743
2606
|
return;
|
|
3744
2607
|
}
|
|
3745
2608
|
// Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
|
|
3746
|
-
if (normalizedStatus === "completed"
|
|
3747
|
-
|
|
2609
|
+
if (normalizedStatus === "completed" ||
|
|
2610
|
+
normalizedStatus === "Completed") {
|
|
3748
2611
|
// Reset deduplication for the next run
|
|
3749
2612
|
lastSeqRef.current = 0;
|
|
3750
|
-
clearHeartbeatMessages();
|
|
3751
2613
|
// Mark the last assistant message as completed
|
|
3752
2614
|
setMessages((prev) => {
|
|
3753
2615
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3766,29 +2628,34 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3766
2628
|
isWaitingRef.current = false;
|
|
3767
2629
|
setIsConnecting(false);
|
|
3768
2630
|
shouldCreateNewMessage.current = false;
|
|
3769
|
-
// Server says agent finished thinking
|
|
2631
|
+
// Server says agent finished thinking
|
|
2632
|
+
setIsAgentThinking(false);
|
|
2633
|
+
return;
|
|
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;
|
|
3770
2643
|
setIsAgentThinking(false);
|
|
3771
2644
|
return;
|
|
3772
2645
|
}
|
|
3773
2646
|
// Handle "Running" state - agent is actively processing
|
|
3774
|
-
if (normalizedStatus === "running"
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
}
|
|
3778
|
-
// Update agent status to running and clear any previous error statusMessage
|
|
3779
|
-
setAgent((prev) => prev
|
|
3780
|
-
? { ...prev, status: "running", statusMessage: undefined }
|
|
3781
|
-
: prev);
|
|
2647
|
+
if (normalizedStatus === "running" ||
|
|
2648
|
+
normalizedStatus === "Running") {
|
|
2649
|
+
// Update agent status to running
|
|
2650
|
+
setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
|
|
3782
2651
|
setIsWaitingForResponse(true);
|
|
3783
2652
|
isWaitingRef.current = true;
|
|
3784
2653
|
setIsAgentThinking(true);
|
|
3785
2654
|
return;
|
|
3786
2655
|
}
|
|
3787
2656
|
// Handle "Error" state
|
|
3788
|
-
if (normalizedStatus === "error") {
|
|
3789
|
-
|
|
3790
|
-
const errorMsg = toUserFacingAgentErrorMessage(statusData?.statusMessage ?? statusData?.error) || "Unknown error";
|
|
3791
|
-
clearHeartbeatMessages();
|
|
2657
|
+
if (normalizedStatus === "error" || normalizedStatus === "Error") {
|
|
2658
|
+
const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
|
|
3792
2659
|
setAgent((prev) => prev
|
|
3793
2660
|
? { ...prev, status: "error", statusMessage: errorMsg }
|
|
3794
2661
|
: prev);
|
|
@@ -3808,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3808
2675
|
if (messageType === "agent:run:complete") {
|
|
3809
2676
|
// Reset deduplication for the next run
|
|
3810
2677
|
lastSeqRef.current = 0;
|
|
3811
|
-
clearHeartbeatMessages();
|
|
3812
2678
|
// Mark the last assistant message as completed
|
|
3813
2679
|
setMessages((prev) => {
|
|
3814
2680
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3833,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3833
2699
|
}
|
|
3834
2700
|
// Lifecycle: agent:run:error
|
|
3835
2701
|
if (messageType === "agent:run:error") {
|
|
3836
|
-
const errorMsg =
|
|
3837
|
-
"AI could not complete this request.";
|
|
3838
|
-
clearHeartbeatMessages();
|
|
2702
|
+
const errorMsg = message.payload?.error || "Unknown error";
|
|
3839
2703
|
// Reset deduplication for the next run after an error
|
|
3840
2704
|
lastSeqRef.current = 0;
|
|
3841
2705
|
setError(errorMsg);
|
|
@@ -3850,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3850
2714
|
}
|
|
3851
2715
|
}, [
|
|
3852
2716
|
agent,
|
|
3853
|
-
clearHeartbeatMessages,
|
|
3854
2717
|
handleContentChunk,
|
|
3855
|
-
handleHeartbeatMessage,
|
|
3856
2718
|
handleToolCall,
|
|
3857
2719
|
handleToolResult,
|
|
3858
2720
|
onAgentUpdate,
|
|
@@ -3907,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3907
2769
|
const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
|
|
3908
2770
|
const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
|
|
3909
2771
|
currentAgent.status === 2;
|
|
3910
|
-
const isWaitingForInput = currentAgent.status === "waitingForInput";
|
|
3911
2772
|
if (isRunning) {
|
|
3912
2773
|
setIsWaitingForResponse(true);
|
|
3913
2774
|
isWaitingRef.current = true;
|
|
@@ -3915,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3915
2776
|
// Agent is currently running, show thinking dots
|
|
3916
2777
|
setIsAgentThinking(true);
|
|
3917
2778
|
}
|
|
3918
|
-
else if (isWaitingForApproval
|
|
2779
|
+
else if (isWaitingForApproval) {
|
|
3919
2780
|
setIsWaitingForResponse(false);
|
|
3920
2781
|
isWaitingRef.current = false;
|
|
3921
|
-
// Agent is waiting for user
|
|
2782
|
+
// Agent is waiting for user approval, hide thinking dots
|
|
3922
2783
|
setIsAgentThinking(false);
|
|
3923
2784
|
}
|
|
3924
2785
|
else {
|
|
@@ -3965,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3965
2826
|
window.addEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3966
2827
|
return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
|
|
3967
2828
|
}, []);
|
|
3968
|
-
// Keep stable refs so we don't miss window events during effect re-runs.
|
|
3969
|
-
const agentIdRefForDialogs = useRef(null);
|
|
3970
|
-
const agentStubIdRefForDialogs = useRef(agentStub.id);
|
|
3971
|
-
const isActiveRefForDialogs = useRef(isActive);
|
|
3972
|
-
const scrollToBottomRefForDialogs = useRef(scrollToBottom);
|
|
3973
|
-
useEffect(() => {
|
|
3974
|
-
agentIdRefForDialogs.current = agent?.id ?? null;
|
|
3975
|
-
}, [agent?.id]);
|
|
3976
|
-
useEffect(() => {
|
|
3977
|
-
const prevId = agentStubIdRefForDialogs.current;
|
|
3978
|
-
agentStubIdRefForDialogs.current = agentStub.id;
|
|
3979
|
-
const visibleRegistry = { ...getVisibleDialogRegistry() };
|
|
3980
|
-
const normalizedPrevId = normalizeDialogAgentId(prevId);
|
|
3981
|
-
if (normalizedPrevId) {
|
|
3982
|
-
delete visibleRegistry[normalizedPrevId];
|
|
3983
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
3984
|
-
}
|
|
3985
|
-
if (prevId && prevId !== agentStub.id) {
|
|
3986
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
3987
|
-
if (orphanedDialog) {
|
|
3988
|
-
setActiveInlineDialog(null);
|
|
3989
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
3990
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
3991
|
-
}));
|
|
3992
|
-
}
|
|
3993
|
-
}
|
|
3994
|
-
}, [agentStub.id]);
|
|
3995
|
-
useEffect(() => {
|
|
3996
|
-
isActiveRefForDialogs.current = isActive;
|
|
3997
|
-
}, [isActive]);
|
|
3998
|
-
useEffect(() => {
|
|
3999
|
-
scrollToBottomRefForDialogs.current = scrollToBottom;
|
|
4000
|
-
}, [scrollToBottom]);
|
|
4001
2829
|
// Listen for agent inline dialog requests from AgentDialogHandler
|
|
4002
2830
|
useEffect(() => {
|
|
4003
|
-
if (orphanTimeoutRef.current) {
|
|
4004
|
-
clearTimeout(orphanTimeoutRef.current);
|
|
4005
|
-
orphanTimeoutRef.current = null;
|
|
4006
|
-
}
|
|
4007
|
-
const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
|
|
4008
|
-
const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
4009
|
-
if (normalizedAgentStubId &&
|
|
4010
|
-
!globalListeners.includes(normalizedAgentStubId)) {
|
|
4011
|
-
globalListeners.push(normalizedAgentStubId);
|
|
4012
|
-
}
|
|
4013
|
-
globalThis.__agentDialogMountedAgents = globalListeners;
|
|
4014
2831
|
const handleDialogShow = (event) => {
|
|
4015
|
-
const
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
const onCancel = detail?.onCancel;
|
|
4019
|
-
const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
|
|
4020
|
-
const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
|
|
4021
|
-
const isActiveNow = isActiveRefForDialogs.current;
|
|
4022
|
-
if (!request)
|
|
4023
|
-
return;
|
|
4024
|
-
const requestAgentId = normalizeDialogAgentId(request.agentId);
|
|
4025
|
-
const agentMatch = !requestAgentId ||
|
|
4026
|
-
!terminalAgentStubId ||
|
|
4027
|
-
requestAgentId === terminalAgentStubId ||
|
|
4028
|
-
requestAgentId === terminalAgentId;
|
|
4029
|
-
if (!isActiveNow) {
|
|
4030
|
-
return;
|
|
4031
|
-
}
|
|
4032
|
-
if (!request)
|
|
4033
|
-
return;
|
|
4034
|
-
// Only handle dialog requests for this terminal's agent stub
|
|
4035
|
-
if (requestAgentId &&
|
|
4036
|
-
terminalAgentStubId &&
|
|
4037
|
-
requestAgentId !== terminalAgentStubId &&
|
|
4038
|
-
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) {
|
|
4039
2835
|
return;
|
|
4040
2836
|
}
|
|
4041
2837
|
console.log("[AgentTerminal] Received inline dialog request:", request);
|
|
4042
|
-
setActiveInlineDialog({
|
|
4043
|
-
request,
|
|
4044
|
-
onComplete: (result) => {
|
|
4045
|
-
onComplete(result);
|
|
4046
|
-
onInteractionSubmitted?.();
|
|
4047
|
-
},
|
|
4048
|
-
onCancel: () => {
|
|
4049
|
-
onCancel();
|
|
4050
|
-
onInteractionSubmitted?.();
|
|
4051
|
-
},
|
|
4052
|
-
});
|
|
2838
|
+
setActiveInlineDialog({ request, onComplete, onCancel });
|
|
4053
2839
|
// Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
setTimeout(() => {
|
|
4060
|
-
if (request.dialogType === "questionnaire") {
|
|
4061
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4062
|
-
return;
|
|
4063
|
-
}
|
|
4064
|
-
scrollToBottomRefForDialogs.current?.();
|
|
4065
|
-
}, 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);
|
|
4066
2845
|
};
|
|
4067
2846
|
window.addEventListener("agent:dialog:show", handleDialogShow);
|
|
4068
|
-
return () =>
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
const idsToClear = [
|
|
4073
|
-
normalizeDialogAgentId(agentStubIdRefForDialogs.current),
|
|
4074
|
-
normalizeDialogAgentId(agentIdRefForDialogs.current),
|
|
4075
|
-
].filter(Boolean);
|
|
4076
|
-
idsToClear.forEach((id) => delete visibleRegistry[id]);
|
|
4077
|
-
globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
|
|
4078
|
-
// If unmounting with an active dialog, defer the orphan event so that
|
|
4079
|
-
// React strict mode remounts can cancel it. Only real unmounts fire.
|
|
4080
|
-
const orphanedDialog = activeInlineDialogRef.current;
|
|
4081
|
-
if (orphanedDialog) {
|
|
4082
|
-
orphanTimeoutRef.current = setTimeout(() => {
|
|
4083
|
-
orphanTimeoutRef.current = null;
|
|
4084
|
-
window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
|
|
4085
|
-
detail: { callbackId: orphanedDialog.request.callbackId },
|
|
4086
|
-
}));
|
|
4087
|
-
}, 300);
|
|
4088
|
-
}
|
|
4089
|
-
window.removeEventListener("agent:dialog:show", handleDialogShow);
|
|
4090
|
-
};
|
|
4091
|
-
}, []);
|
|
4092
|
-
// Announce when this terminal is ready to accept dialogs.
|
|
4093
|
-
// Fire immediately on mount using agentStub.id so the AgentDialogHandler
|
|
4094
|
-
// can re-dispatch pending dialogs without waiting for the async agent load.
|
|
4095
|
-
// Also fire when agent?.id becomes available in case it differs from the stub.
|
|
2847
|
+
return () => window.removeEventListener("agent:dialog:show", handleDialogShow);
|
|
2848
|
+
}, [agent?.id, scrollToBottom]);
|
|
2849
|
+
// Announce when this terminal is ready to accept dialogs
|
|
2850
|
+
// This allows AgentDialogHandler to re-dispatch pending dialogs when switching to an agent
|
|
4096
2851
|
useEffect(() => {
|
|
4097
|
-
|
|
4098
|
-
|
|
2852
|
+
if (agent?.id) {
|
|
2853
|
+
console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
|
|
4099
2854
|
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4100
|
-
detail: {
|
|
4101
|
-
agentId: normalizedStubId,
|
|
4102
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4103
|
-
},
|
|
2855
|
+
detail: { agentId: agent.id },
|
|
4104
2856
|
}));
|
|
4105
2857
|
}
|
|
4106
|
-
}, [
|
|
4107
|
-
useEffect(() => {
|
|
4108
|
-
const normalizedAgentId = normalizeDialogAgentId(agent?.id);
|
|
4109
|
-
const normalizedStubId = normalizeDialogAgentId(agentStub.id);
|
|
4110
|
-
if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
|
|
4111
|
-
window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
|
|
4112
|
-
detail: {
|
|
4113
|
-
agentId: normalizedAgentId,
|
|
4114
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4115
|
-
},
|
|
4116
|
-
}));
|
|
4117
|
-
}
|
|
4118
|
-
}, [agent?.id, agentStub.id]);
|
|
2858
|
+
}, [agent?.id]);
|
|
4119
2859
|
// Profiles are provided by parent component (Agents). No local loading here.
|
|
4120
2860
|
// Select active profile based on agent.profileId or agentStub.profileId
|
|
4121
2861
|
useEffect(() => {
|
|
@@ -4126,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4126
2866
|
// Use case-insensitive comparison for GUID matching (backend may return different casing)
|
|
4127
2867
|
const normalizedProfileId = profileIdToUse?.toLowerCase();
|
|
4128
2868
|
const candidate = normalizedProfileId
|
|
4129
|
-
? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
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);
|
|
4134
2874
|
}
|
|
4135
|
-
|
|
4136
|
-
// not only when the profile id changes. Otherwise availableSkills (and similar fields)
|
|
4137
|
-
// that are merged in the parent after async loads never update (e.g. Template Builder
|
|
4138
|
-
// settings skills merged into the profile).
|
|
4139
|
-
setActiveProfile((prev) => {
|
|
4140
|
-
if (!prev || prev.id !== candidate.id)
|
|
4141
|
-
return candidate;
|
|
4142
|
-
const skillKey = (p) => JSON.stringify({
|
|
4143
|
-
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
4144
|
-
String(s?.id ?? ""),
|
|
4145
|
-
String(s?.name ?? ""),
|
|
4146
|
-
]),
|
|
4147
|
-
available: (p.availableSkills ?? []).map((s) => [
|
|
4148
|
-
String(s?.id ?? ""),
|
|
4149
|
-
String(s?.name ?? ""),
|
|
4150
|
-
]),
|
|
4151
|
-
});
|
|
4152
|
-
if (skillKey(prev) === skillKey(candidate))
|
|
4153
|
-
return prev;
|
|
4154
|
-
return candidate;
|
|
4155
|
-
});
|
|
4156
|
-
}, [
|
|
4157
|
-
profiles,
|
|
4158
|
-
agent?.id,
|
|
4159
|
-
agent?.profileId,
|
|
4160
|
-
agentStub.id,
|
|
4161
|
-
agentStub.profileId,
|
|
4162
|
-
]);
|
|
2875
|
+
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
4163
2876
|
// Clear queued prompts when agent changes or is new;
|
|
4164
2877
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
4165
2878
|
useEffect(() => {
|
|
4166
|
-
if (!agent?.id ||
|
|
2879
|
+
if (!agent?.id || agent.status === "new") {
|
|
4167
2880
|
setQueuedPrompts([]);
|
|
4168
2881
|
}
|
|
4169
|
-
}, [agent?.id,
|
|
2882
|
+
}, [agent?.id, agent?.status]);
|
|
4170
2883
|
// Update selected model when the active profile or agent model changes
|
|
4171
2884
|
useEffect(() => {
|
|
4172
2885
|
if (!activeProfile)
|
|
@@ -4195,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4195
2908
|
// Initialize mode from metadata; fall back to agent.Mode from server
|
|
4196
2909
|
useEffect(() => {
|
|
4197
2910
|
try {
|
|
4198
|
-
const metaMode =
|
|
4199
|
-
if (metaMode
|
|
2911
|
+
const metaMode = agentMetadata?.mode;
|
|
2912
|
+
if (metaMode === "autonomous" ||
|
|
2913
|
+
metaMode === "read-only" ||
|
|
2914
|
+
metaMode === "supervised") {
|
|
4200
2915
|
setMode(metaMode);
|
|
4201
2916
|
return;
|
|
4202
2917
|
}
|
|
4203
2918
|
}
|
|
4204
2919
|
catch { }
|
|
4205
2920
|
try {
|
|
4206
|
-
const serverMode =
|
|
4207
|
-
if (serverMode
|
|
2921
|
+
const serverMode = agent?.mode;
|
|
2922
|
+
if (serverMode === "autonomous" ||
|
|
2923
|
+
serverMode === "read-only" ||
|
|
2924
|
+
serverMode === "supervised") {
|
|
4208
2925
|
setMode(serverMode);
|
|
4209
2926
|
}
|
|
4210
2927
|
}
|
|
@@ -4224,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4224
2941
|
textareaRef.current.focus();
|
|
4225
2942
|
}
|
|
4226
2943
|
}, [messages, activePlaceholderInput]);
|
|
4227
|
-
// Persist any pending settings (mode/model
|
|
2944
|
+
// Persist any pending settings (mode/model) once an agent exists server-side
|
|
4228
2945
|
const persistPendingSettingsIfNeeded = useCallback(async () => {
|
|
4229
2946
|
try {
|
|
4230
2947
|
if (!agent?.id)
|
|
@@ -4237,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4237
2954
|
payload.model = pending.modelName;
|
|
4238
2955
|
if (pending.mode)
|
|
4239
2956
|
payload.mode = pending.mode;
|
|
4240
|
-
if (pending.profileId)
|
|
4241
|
-
payload.profileId = pending.profileId;
|
|
4242
|
-
if (pending.profileName != null)
|
|
4243
|
-
payload.profileName = pending.profileName;
|
|
4244
2957
|
if (Object.keys(payload).length === 0)
|
|
4245
2958
|
return;
|
|
4246
2959
|
await updateAgentSettings(agent.id, payload);
|
|
@@ -4250,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4250
2963
|
console.error("Failed to persist pending settings", e);
|
|
4251
2964
|
}
|
|
4252
2965
|
}, [agent?.id]);
|
|
4253
|
-
const getPendingRequestSettings = useCallback(() => {
|
|
4254
|
-
const pending = pendingSettingsRef.current;
|
|
4255
|
-
const requestProfile = pending?.profileId
|
|
4256
|
-
? profiles.find((profile) => profile.id === pending.profileId) ||
|
|
4257
|
-
activeProfile ||
|
|
4258
|
-
profiles[0]
|
|
4259
|
-
: activeProfile || profiles[0];
|
|
4260
|
-
let requestModelId = selectedModelId;
|
|
4261
|
-
if (pending?.modelName) {
|
|
4262
|
-
const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
|
|
4263
|
-
pending.modelName?.trim().toLowerCase());
|
|
4264
|
-
requestModelId = requestModel?.id || requestModelId;
|
|
4265
|
-
}
|
|
4266
|
-
return {
|
|
4267
|
-
mode: (pending?.mode || mode),
|
|
4268
|
-
profileId: pending?.profileId || requestProfile?.id || "",
|
|
4269
|
-
profileName: pending?.profileName || requestProfile?.name || "",
|
|
4270
|
-
modelId: requestModelId,
|
|
4271
|
-
};
|
|
4272
|
-
}, [activeProfile, mode, profiles, selectedModelId]);
|
|
4273
|
-
const getSubmitErrorMessage = (error) => {
|
|
4274
|
-
const fallback = "Failed to submit prompt. Please try again.";
|
|
4275
|
-
if (!(error instanceof Error))
|
|
4276
|
-
return fallback;
|
|
4277
|
-
const cleaned = toUserFacingAgentErrorMessage(error.message);
|
|
4278
|
-
return cleaned || fallback;
|
|
4279
|
-
};
|
|
4280
|
-
const suppressedQueuedPromptsRef = useRef([]);
|
|
4281
|
-
const pruneSuppressedQueuedPrompts = useCallback(() => {
|
|
4282
|
-
const cutoff = Date.now() - 15_000;
|
|
4283
|
-
suppressedQueuedPromptsRef.current =
|
|
4284
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
|
|
4285
|
-
}, []);
|
|
4286
|
-
const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
|
|
4287
|
-
const normalizedAgentId = agentId.trim().toLowerCase();
|
|
4288
|
-
const normalizedPrompt = promptText.trim();
|
|
4289
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4290
|
-
return null;
|
|
4291
|
-
}
|
|
4292
|
-
pruneSuppressedQueuedPrompts();
|
|
4293
|
-
const token = crypto.randomUUID();
|
|
4294
|
-
suppressedQueuedPromptsRef.current = [
|
|
4295
|
-
...suppressedQueuedPromptsRef.current,
|
|
4296
|
-
{
|
|
4297
|
-
token,
|
|
4298
|
-
agentId: normalizedAgentId,
|
|
4299
|
-
prompt: normalizedPrompt,
|
|
4300
|
-
createdAt: Date.now(),
|
|
4301
|
-
},
|
|
4302
|
-
];
|
|
4303
|
-
return token;
|
|
4304
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4305
|
-
const clearSuppressedQueuedPrompt = useCallback((token) => {
|
|
4306
|
-
if (!token) {
|
|
4307
|
-
return;
|
|
4308
|
-
}
|
|
4309
|
-
suppressedQueuedPromptsRef.current =
|
|
4310
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
|
|
4311
|
-
}, []);
|
|
4312
|
-
const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
|
|
4313
|
-
const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
|
|
4314
|
-
const normalizedPrompt = queueEntry?.prompt?.trim();
|
|
4315
|
-
if (!normalizedAgentId || !normalizedPrompt) {
|
|
4316
|
-
return false;
|
|
4317
|
-
}
|
|
4318
|
-
pruneSuppressedQueuedPrompts();
|
|
4319
|
-
const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
|
|
4320
|
-
entry.prompt === normalizedPrompt);
|
|
4321
|
-
if (!matchedEntry) {
|
|
4322
|
-
return false;
|
|
4323
|
-
}
|
|
4324
|
-
suppressedQueuedPromptsRef.current =
|
|
4325
|
-
suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
|
|
4326
|
-
return true;
|
|
4327
|
-
}, [pruneSuppressedQueuedPrompts]);
|
|
4328
|
-
const cancelActiveInlineDialog = useCallback(() => {
|
|
4329
|
-
const activeDialog = activeInlineDialogRef.current;
|
|
4330
|
-
if (!activeDialog)
|
|
4331
|
-
return;
|
|
4332
|
-
try {
|
|
4333
|
-
activeDialog.onCancel();
|
|
4334
|
-
}
|
|
4335
|
-
finally {
|
|
4336
|
-
setActiveInlineDialog(null);
|
|
4337
|
-
}
|
|
4338
|
-
}, []);
|
|
4339
2966
|
const handleSubmit = async () => {
|
|
4340
2967
|
// Guard against double-submit and missing context
|
|
4341
2968
|
if (isSubmitting) {
|
|
@@ -4379,13 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4379
3006
|
setError("Agent not ready. Please try again.");
|
|
4380
3007
|
return;
|
|
4381
3008
|
}
|
|
4382
|
-
clearStopGuard();
|
|
4383
|
-
const hadQuestionnaireDialogOpen = activeInlineDialogRef.current?.request.dialogType === "questionnaire";
|
|
4384
|
-
const suppressedQueuedPromptToken = hadQuestionnaireDialogOpen && savedPrompt
|
|
4385
|
-
? registerSuppressedQueuedPrompt(agentId, savedPrompt)
|
|
4386
|
-
: null;
|
|
4387
|
-
// A new user prompt supersedes any active questionnaire/inline dialog.
|
|
4388
|
-
cancelActiveInlineDialog();
|
|
4389
3009
|
// Generate a temporary ID for optimistic UI - will be replaced by server ID
|
|
4390
3010
|
const tempMessageId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
4391
3011
|
try {
|
|
@@ -4472,24 +3092,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4472
3092
|
console.warn("[AgentTerminal] Failed to compute live context:", e);
|
|
4473
3093
|
}
|
|
4474
3094
|
// Add visible test IDs to context (only for Help agent)
|
|
4475
|
-
const
|
|
4476
|
-
const currentProfileName = requestSettings.profileName;
|
|
3095
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4477
3096
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4478
3097
|
const request = {
|
|
4479
3098
|
agentId: agentId,
|
|
4480
3099
|
message: savedPrompt,
|
|
4481
3100
|
sessionId: editContext.sessionId,
|
|
4482
|
-
profileId:
|
|
3101
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4483
3102
|
profile: currentProfileName,
|
|
4484
|
-
model:
|
|
4485
|
-
mode:
|
|
3103
|
+
model: selectedModelId,
|
|
3104
|
+
mode: mode,
|
|
4486
3105
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3106
|
+
deterministic: deterministicFlags.deterministic,
|
|
3107
|
+
seed: deterministicFlags.seed,
|
|
4487
3108
|
};
|
|
4488
3109
|
console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
|
|
4489
3110
|
const response = await startAgent(request);
|
|
4490
3111
|
console.log("[AgentTerminal] startAgent response:", response);
|
|
4491
3112
|
// Check if prompt was queued (agent was already running)
|
|
4492
|
-
const wasQueued = response.message?.toLowerCase().includes("queued")
|
|
3113
|
+
const wasQueued = response.message?.toLowerCase().includes("queued") ||
|
|
3114
|
+
response.status === "Queued";
|
|
4493
3115
|
if (wasQueued) {
|
|
4494
3116
|
// Prompt was queued - show a brief notification but don't set waiting state
|
|
4495
3117
|
// The prompt will be processed when the agent becomes idle
|
|
@@ -4499,7 +3121,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4499
3121
|
isWaitingRef.current = false;
|
|
4500
3122
|
}
|
|
4501
3123
|
else {
|
|
4502
|
-
clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
|
|
4503
3124
|
// Normal submission - set waiting state to show dancing dots immediately
|
|
4504
3125
|
setIsWaitingForResponse(true);
|
|
4505
3126
|
isWaitingRef.current = true;
|
|
@@ -4514,13 +3135,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4514
3135
|
...prev.filter((p) => p !== savedPrompt).slice(0, 9),
|
|
4515
3136
|
]);
|
|
4516
3137
|
setCurrentHistoryIndex(-1);
|
|
4517
|
-
await onInteractionSubmitted?.();
|
|
4518
3138
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4519
3139
|
}
|
|
4520
3140
|
catch (err) {
|
|
4521
3141
|
console.error("[AgentTerminal] Failed to submit prompt:", err);
|
|
4522
|
-
|
|
4523
|
-
setError(getSubmitErrorMessage(err));
|
|
3142
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4524
3143
|
setIsWaitingForResponse(false);
|
|
4525
3144
|
isWaitingRef.current = false;
|
|
4526
3145
|
// Remove the optimistic user message on API error
|
|
@@ -4730,29 +3349,29 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4730
3349
|
console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
|
|
4731
3350
|
}
|
|
4732
3351
|
// Add visible test IDs to context (only for Help agent)
|
|
4733
|
-
const
|
|
4734
|
-
const currentProfileName = requestSettings.profileName;
|
|
3352
|
+
const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
|
|
4735
3353
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4736
3354
|
const request = {
|
|
4737
3355
|
agentId: agent.id,
|
|
4738
3356
|
message: savedPrompt,
|
|
4739
3357
|
sessionId: editContext.sessionId,
|
|
4740
|
-
profileId:
|
|
3358
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
4741
3359
|
profile: currentProfileName,
|
|
4742
|
-
model:
|
|
4743
|
-
mode:
|
|
3360
|
+
model: selectedModelId,
|
|
3361
|
+
mode: mode,
|
|
4744
3362
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
3363
|
+
deterministic: deterministicFlags.deterministic,
|
|
3364
|
+
seed: deterministicFlags.seed,
|
|
4745
3365
|
};
|
|
4746
3366
|
console.log("[AgentTerminal] Calling startAgent API for quick message");
|
|
4747
3367
|
await startAgent(request);
|
|
4748
3368
|
// If user changed mode/model while the agent was new, persist them now
|
|
4749
3369
|
await persistPendingSettingsIfNeeded();
|
|
4750
|
-
await onInteractionSubmitted?.();
|
|
4751
3370
|
// WebSocket connection is already active via subscription - no need for SSE
|
|
4752
3371
|
}
|
|
4753
3372
|
catch (err) {
|
|
4754
3373
|
console.error("[AgentTerminal] Failed to submit quick message:", err);
|
|
4755
|
-
setError(
|
|
3374
|
+
setError("Failed to submit prompt. Please try again.");
|
|
4756
3375
|
setIsWaitingForResponse(false);
|
|
4757
3376
|
isWaitingRef.current = false;
|
|
4758
3377
|
// Remove the optimistic user message on API error
|
|
@@ -4930,90 +3549,66 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4930
3549
|
}
|
|
4931
3550
|
return context;
|
|
4932
3551
|
}, [collectVisibleTestIds]);
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
name: editContext?.item?.name,
|
|
4938
|
-
path: editContext?.item?.path,
|
|
4939
|
-
}), [editContext?.item?.name, editContext?.item?.path]);
|
|
4940
|
-
const buildComponentContext = useCallback((item) => {
|
|
4941
|
-
if (!editContext?.selection?.length)
|
|
4942
|
-
return undefined;
|
|
4943
|
-
return editContext.selection.map((componentId) => {
|
|
4944
|
-
let componentName;
|
|
4945
|
-
let componentType;
|
|
4946
|
-
let renderingItemId;
|
|
4947
|
-
if (editContext.page) {
|
|
4948
|
-
try {
|
|
4949
|
-
const component = getComponentById(componentId, editContext.page);
|
|
4950
|
-
componentName =
|
|
4951
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4952
|
-
componentType = component?.type || undefined;
|
|
4953
|
-
renderingItemId = component?.rendering?.id || undefined;
|
|
4954
|
-
}
|
|
4955
|
-
catch { }
|
|
4956
|
-
}
|
|
4957
|
-
return {
|
|
4958
|
-
componentId,
|
|
4959
|
-
componentName,
|
|
4960
|
-
componentType,
|
|
4961
|
-
renderingItemId,
|
|
4962
|
-
pageItem: buildPageContextItem(item),
|
|
4963
|
-
};
|
|
4964
|
-
});
|
|
4965
|
-
}, [buildPageContextItem, editContext?.page, editContext?.selection]);
|
|
4966
|
-
const buildFieldContext = useCallback(() => {
|
|
4967
|
-
const focusedField = fieldsContext?.focusedField;
|
|
4968
|
-
if (!focusedField?.fieldId || !focusedField?.item?.id)
|
|
4969
|
-
return undefined;
|
|
4970
|
-
const fieldItem = focusedField.item;
|
|
4971
|
-
const currentItem = editContext?.currentItemDescriptor;
|
|
4972
|
-
let fieldItemName = fieldItem.id === currentItem?.id ? editContext?.item?.name : undefined;
|
|
4973
|
-
if (!fieldItemName && editContext?.page) {
|
|
4974
|
-
try {
|
|
4975
|
-
const component = getComponentById(fieldItem.id, editContext.page);
|
|
4976
|
-
fieldItemName =
|
|
4977
|
-
component?.datasourceItem?.name || component?.name || undefined;
|
|
4978
|
-
}
|
|
4979
|
-
catch { }
|
|
4980
|
-
}
|
|
3552
|
+
// Helper function to build current context from editor state
|
|
3553
|
+
const buildCurrentContext = useCallback(() => {
|
|
3554
|
+
// Return context even without item - we want view info regardless
|
|
3555
|
+
const item = editContext?.currentItemDescriptor;
|
|
4981
3556
|
return {
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
3557
|
+
items: item
|
|
3558
|
+
? [
|
|
3559
|
+
{
|
|
3560
|
+
id: item.id,
|
|
3561
|
+
language: item.language,
|
|
3562
|
+
version: item.version,
|
|
3563
|
+
name: editContext?.item?.name,
|
|
3564
|
+
path: editContext?.item?.path,
|
|
3565
|
+
},
|
|
3566
|
+
]
|
|
3567
|
+
: undefined,
|
|
3568
|
+
currentItemId: item?.id, // ID of the currently loaded item user is viewing
|
|
3569
|
+
components: editContext?.selection?.length && item
|
|
3570
|
+
? editContext.selection.map((componentId) => ({
|
|
3571
|
+
componentId,
|
|
3572
|
+
pageItem: {
|
|
3573
|
+
id: item.id,
|
|
3574
|
+
language: item.language,
|
|
3575
|
+
version: item.version,
|
|
3576
|
+
name: editContext?.item?.name,
|
|
3577
|
+
},
|
|
3578
|
+
}))
|
|
3579
|
+
: undefined,
|
|
3580
|
+
field: fieldsContext?.focusedField?.fieldId &&
|
|
3581
|
+
fieldsContext.focusedField?.item?.id
|
|
3582
|
+
? {
|
|
3583
|
+
fieldId: fieldsContext.focusedField.fieldId,
|
|
3584
|
+
fieldName: fieldsContext.focusedField.fieldName,
|
|
3585
|
+
item: {
|
|
3586
|
+
id: fieldsContext.focusedField.item.id,
|
|
3587
|
+
language: fieldsContext.focusedField.item.language ||
|
|
3588
|
+
editContext?.currentItemDescriptor?.language ||
|
|
3589
|
+
"en",
|
|
3590
|
+
version: fieldsContext.focusedField.item.version ??
|
|
3591
|
+
editContext?.currentItemDescriptor?.version ??
|
|
3592
|
+
0,
|
|
3593
|
+
name: editContext?.item?.name,
|
|
3594
|
+
},
|
|
3595
|
+
}
|
|
3596
|
+
: undefined,
|
|
3597
|
+
// View context - always include so agent knows editor state
|
|
3598
|
+
activeWorkspace: editContext?.workspaceId,
|
|
3599
|
+
hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
|
|
3600
|
+
// Open sidebars - helps agent understand what panels are currently visible
|
|
3601
|
+
openSidebars: editContext?.openSidebars,
|
|
4990
3602
|
};
|
|
4991
3603
|
}, [
|
|
4992
3604
|
editContext?.currentItemDescriptor,
|
|
3605
|
+
editContext?.selection,
|
|
3606
|
+
editContext?.workspaceId,
|
|
4993
3607
|
editContext?.item?.name,
|
|
4994
|
-
editContext?.
|
|
3608
|
+
editContext?.activeSlotId,
|
|
3609
|
+
editContext?.openSidebars,
|
|
4995
3610
|
fieldsContext?.focusedField,
|
|
4996
3611
|
]);
|
|
4997
|
-
const buildEditorContextPayload = useCallback((item) => ({
|
|
4998
|
-
items: item ? [buildPageContextItem(item)] : undefined,
|
|
4999
|
-
currentItemId: item?.id,
|
|
5000
|
-
components: item ? buildComponentContext(item) : undefined,
|
|
5001
|
-
field: buildFieldContext(),
|
|
5002
|
-
activeWorkspace: editContext?.workspaceId,
|
|
5003
|
-
hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
|
|
5004
|
-
openSidebars: editContext?.openSidebars,
|
|
5005
|
-
}), [
|
|
5006
|
-
buildComponentContext,
|
|
5007
|
-
buildFieldContext,
|
|
5008
|
-
buildPageContextItem,
|
|
5009
|
-
editContext,
|
|
5010
|
-
]);
|
|
5011
|
-
// Helper function to build current context from editor state
|
|
5012
|
-
const buildCurrentContext = useCallback(() => {
|
|
5013
|
-
// Return context even without item - we want view info regardless
|
|
5014
|
-
const item = editContext?.currentItemDescriptor;
|
|
5015
|
-
return buildEditorContextPayload(item);
|
|
5016
|
-
}, [buildEditorContextPayload, editContext?.currentItemDescriptor]);
|
|
5017
3612
|
// Live context updates: watch for changes and update agent context when in "live" mode
|
|
5018
3613
|
const previousContextRef = useRef("");
|
|
5019
3614
|
useEffect(() => {
|
|
@@ -5113,15 +3708,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5113
3708
|
const handleRefreshContext = useCallback(async () => {
|
|
5114
3709
|
if (!agent?.id)
|
|
5115
3710
|
return;
|
|
5116
|
-
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5117
|
-
const refreshProfile = activeProfile ||
|
|
5118
|
-
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5119
|
-
if (refreshProfile?.editorContextMode === "none") {
|
|
5120
|
-
editContext?.showInfoToast({
|
|
5121
|
-
summary: "This profile excludes editor context.",
|
|
5122
|
-
});
|
|
5123
|
-
return;
|
|
5124
|
-
}
|
|
5125
3711
|
try {
|
|
5126
3712
|
const currentCtx = buildCurrentContext();
|
|
5127
3713
|
if (!currentCtx) {
|
|
@@ -5162,90 +3748,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5162
3748
|
buildCurrentContext,
|
|
5163
3749
|
sanitizeAgentMetadata,
|
|
5164
3750
|
editContext,
|
|
5165
|
-
activeProfile,
|
|
5166
|
-
profiles,
|
|
5167
3751
|
]);
|
|
5168
|
-
const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
|
|
5169
|
-
const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
5170
|
-
pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
5171
|
-
const currentSessionId = editContext?.sessionId?.trim() || "";
|
|
5172
|
-
const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
|
|
5173
|
-
const isClaimedByCurrentSession = !!currentSessionId &&
|
|
5174
|
-
!!claimedSessionId &&
|
|
5175
|
-
currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
|
|
5176
|
-
const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
|
|
5177
|
-
const handleClaimBrowser = useCallback(async (takeOver) => {
|
|
5178
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5179
|
-
return;
|
|
5180
|
-
setIsBrowserClaimMutationPending(true);
|
|
5181
|
-
try {
|
|
5182
|
-
const response = await claimAgentBrowser({
|
|
5183
|
-
agentId: agent.id,
|
|
5184
|
-
sessionId: editContext.sessionId,
|
|
5185
|
-
takeOver,
|
|
5186
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5187
|
-
});
|
|
5188
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5189
|
-
}
|
|
5190
|
-
catch (err) {
|
|
5191
|
-
console.error("[AgentTerminal] Failed to claim browser:", err);
|
|
5192
|
-
editContext.showErrorToast(err);
|
|
5193
|
-
}
|
|
5194
|
-
finally {
|
|
5195
|
-
setIsBrowserClaimMutationPending(false);
|
|
5196
|
-
}
|
|
5197
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5198
|
-
const handleReleaseBrowser = useCallback(async () => {
|
|
5199
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5200
|
-
return;
|
|
5201
|
-
setIsBrowserClaimMutationPending(true);
|
|
5202
|
-
try {
|
|
5203
|
-
const response = await releaseAgentBrowser({
|
|
5204
|
-
agentId: agent.id,
|
|
5205
|
-
sessionId: editContext.sessionId,
|
|
5206
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5207
|
-
});
|
|
5208
|
-
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
5209
|
-
}
|
|
5210
|
-
catch (err) {
|
|
5211
|
-
console.error("[AgentTerminal] Failed to release browser:", err);
|
|
5212
|
-
editContext.showErrorToast(err);
|
|
5213
|
-
}
|
|
5214
|
-
finally {
|
|
5215
|
-
setIsBrowserClaimMutationPending(false);
|
|
5216
|
-
}
|
|
5217
|
-
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
5218
|
-
useEffect(() => {
|
|
5219
|
-
return () => {
|
|
5220
|
-
if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
|
|
5221
|
-
return;
|
|
5222
|
-
}
|
|
5223
|
-
void releaseAgentBrowser({
|
|
5224
|
-
agentId: agent.id,
|
|
5225
|
-
sessionId: editContext.sessionId,
|
|
5226
|
-
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5227
|
-
}).catch((error) => {
|
|
5228
|
-
console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
|
|
5229
|
-
});
|
|
5230
|
-
};
|
|
5231
|
-
}, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
|
|
5232
3752
|
// Stop current execution/stream safely
|
|
5233
3753
|
const handleStop = useCallback(async () => {
|
|
5234
3754
|
try {
|
|
5235
3755
|
// 1. Set the stopping guard to prevent WebSocket handlers from re-enabling states
|
|
5236
3756
|
// This must happen FIRST, before any other state changes
|
|
5237
|
-
|
|
3757
|
+
isStoppingRef.current = true;
|
|
5238
3758
|
// 2. Update all UI state to reflect stopped status
|
|
5239
3759
|
setIsWaitingForResponse(false);
|
|
5240
3760
|
isWaitingRef.current = false;
|
|
5241
3761
|
setIsConnecting(false);
|
|
5242
3762
|
setIsSubmitting(false);
|
|
5243
|
-
setAgent((prev) => prev ? { ...prev, status: "idle", statusMessage: undefined } : prev);
|
|
5244
|
-
shouldCreateNewMessage.current = false;
|
|
5245
3763
|
// User stopped the agent, hide thinking dots
|
|
5246
3764
|
setIsAgentThinking(false);
|
|
5247
|
-
// Stopping an agent discards queued follow-up prompts for that run.
|
|
5248
|
-
setQueuedPrompts([]);
|
|
5249
3765
|
// 3. Mark any in-progress streaming messages as completed in UI
|
|
5250
3766
|
setMessages((prev) => {
|
|
5251
3767
|
const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
|
|
@@ -5263,111 +3779,46 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5263
3779
|
catch (err) {
|
|
5264
3780
|
console.error("Failed to cancel agent on backend:", err);
|
|
5265
3781
|
// Continue - UI is already in stopped state, but backend may still be running
|
|
5266
|
-
// The
|
|
3782
|
+
// The isStoppingRef guard will be cleared below so future runs can proceed
|
|
5267
3783
|
}
|
|
5268
3784
|
}
|
|
5269
3785
|
}
|
|
5270
3786
|
catch (e) {
|
|
5271
3787
|
console.error("Failed to stop agent execution", e);
|
|
5272
3788
|
}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
const agentCostLimit = (() => {
|
|
5278
|
-
try {
|
|
5279
|
-
const value = agent?.costLimit;
|
|
5280
|
-
const parsed = value != null ? Number(value) : 0;
|
|
5281
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
3789
|
+
finally {
|
|
3790
|
+
// Clear the stopping guard so future runs can proceed normally
|
|
3791
|
+
// This happens after backend confirmation (or error)
|
|
3792
|
+
isStoppingRef.current = false;
|
|
5282
3793
|
}
|
|
5283
|
-
|
|
5284
|
-
|
|
3794
|
+
}, []);
|
|
3795
|
+
// Determine effective cost limit from agent, profile, or metadata so the cost display
|
|
3796
|
+
// is visible immediately even before any messages or server-side persistence.
|
|
3797
|
+
let effectiveCostLimit;
|
|
3798
|
+
try {
|
|
3799
|
+
const candidates = [
|
|
3800
|
+
agent?.costLimit,
|
|
3801
|
+
activeProfile?.costLimit,
|
|
3802
|
+
];
|
|
3803
|
+
for (const c of candidates) {
|
|
3804
|
+
const n = c != null ? Number(c) : 0;
|
|
3805
|
+
if (Number.isFinite(n) && n > 0) {
|
|
3806
|
+
effectiveCostLimit = n;
|
|
3807
|
+
break;
|
|
3808
|
+
}
|
|
5285
3809
|
}
|
|
5286
|
-
}
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
...totals,
|
|
5294
|
-
imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
|
|
5295
|
-
};
|
|
5296
|
-
})();
|
|
5297
|
-
const normalizedAgentStatus = parseAgentStatus(agent?.status);
|
|
5298
|
-
const hasSettledToNonExecutingStatus = normalizedAgentStatus === "idle" ||
|
|
5299
|
-
normalizedAgentStatus === "completed" ||
|
|
5300
|
-
normalizedAgentStatus === "error" ||
|
|
5301
|
-
normalizedAgentStatus === "closed" ||
|
|
5302
|
-
normalizedAgentStatus === "waitingForApproval" ||
|
|
5303
|
-
normalizedAgentStatus === "waitingForInput" ||
|
|
5304
|
-
normalizedAgentStatus === "costLimitReached";
|
|
3810
|
+
}
|
|
3811
|
+
catch { }
|
|
3812
|
+
if (effectiveCostLimit === undefined) {
|
|
3813
|
+
effectiveCostLimit = undefined;
|
|
3814
|
+
}
|
|
3815
|
+
// Calculate total token usage for cost display
|
|
3816
|
+
const totalTokens = calculateTotalTokens(messages);
|
|
5305
3817
|
// Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
|
|
5306
|
-
const isExecuting =
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
(isWaitingForResponse || hasActiveStreaming())));
|
|
5311
|
-
const assistantMessageCount = useMemo(() => messages.filter((message) => message.role === "assistant").length, [messages]);
|
|
5312
|
-
const messagesWithToolCallsCount = useMemo(() => messages.filter((message) => (message.toolCalls || []).length > 0).length, [messages]);
|
|
5313
|
-
const totalToolCallCount = useMemo(() => messages.reduce((sum, message) => sum + (message.toolCalls?.length || 0), 0), [messages]);
|
|
5314
|
-
const incompleteToolCallCount = useMemo(() => messages.reduce((sum, message) => sum +
|
|
5315
|
-
(message.toolCalls?.filter((toolCall) => !toolCall.isCompleted).length || 0), 0), [messages]);
|
|
5316
|
-
const assistantGroupsWithRenderableToolCalls = useMemo(() => {
|
|
5317
|
-
const groups = groupConsecutiveMessages(messages);
|
|
5318
|
-
return groups.filter((group) => {
|
|
5319
|
-
if (group.type !== "assistant-group")
|
|
5320
|
-
return false;
|
|
5321
|
-
const convertedMessages = convertAgentMessagesToAiFormat(group.messages);
|
|
5322
|
-
return convertedMessages.some((message) => (message.tool_calls?.length || 0) > 0);
|
|
5323
|
-
}).length;
|
|
5324
|
-
}, [messages]);
|
|
5325
|
-
const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
|
|
5326
|
-
const runDiagnosticsSnapshot = useMemo(() => {
|
|
5327
|
-
const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
|
|
5328
|
-
return {
|
|
5329
|
-
agentId: currentAgentId,
|
|
5330
|
-
isSubmitting,
|
|
5331
|
-
isConnecting,
|
|
5332
|
-
isWaitingForResponse,
|
|
5333
|
-
isAgentThinking,
|
|
5334
|
-
isExecuting,
|
|
5335
|
-
hasActiveStreaming: hasActiveStreaming(),
|
|
5336
|
-
isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
|
|
5337
|
-
normalizeDialogAgentId(currentAgentId),
|
|
5338
|
-
lastSeq: lastSeqRef.current,
|
|
5339
|
-
lastEventType: lastEvent?.type ?? null,
|
|
5340
|
-
lastEventAt: lastEvent?.timestamp ?? null,
|
|
5341
|
-
recentEvents: recentAgentRunEvents,
|
|
5342
|
-
assistantMessageCount,
|
|
5343
|
-
assistantGroupCount,
|
|
5344
|
-
assistantGroupsWithRenderableToolCalls,
|
|
5345
|
-
messagesWithToolCalls: messagesWithToolCallsCount,
|
|
5346
|
-
totalToolCallCount,
|
|
5347
|
-
incompleteToolCallCount,
|
|
5348
|
-
recentToolUiEvents,
|
|
5349
|
-
};
|
|
5350
|
-
}, [
|
|
5351
|
-
assistantGroupCount,
|
|
5352
|
-
assistantGroupsWithRenderableToolCalls,
|
|
5353
|
-
assistantMessageCount,
|
|
5354
|
-
currentAgentId,
|
|
5355
|
-
hasActiveStreaming,
|
|
5356
|
-
incompleteToolCallCount,
|
|
5357
|
-
isAgentThinking,
|
|
5358
|
-
isConnecting,
|
|
5359
|
-
isExecuting,
|
|
5360
|
-
isSubmitting,
|
|
5361
|
-
isWaitingForResponse,
|
|
5362
|
-
messagesWithToolCallsCount,
|
|
5363
|
-
recentAgentRunEvents,
|
|
5364
|
-
recentToolUiEvents,
|
|
5365
|
-
totalToolCallCount,
|
|
5366
|
-
]);
|
|
5367
|
-
const showInitialThinkingSplash = messages.length === 0 &&
|
|
5368
|
-
!error &&
|
|
5369
|
-
hideGreeting &&
|
|
5370
|
-
(isSubmitting || isConnecting);
|
|
3818
|
+
const isExecuting = isSubmitting ||
|
|
3819
|
+
isConnecting ||
|
|
3820
|
+
isWaitingForResponse ||
|
|
3821
|
+
hasActiveStreaming();
|
|
5371
3822
|
// Compute dots visibility: only show BEFORE any assistant message exists
|
|
5372
3823
|
// This prevents duplicate headers - the dots indicator has its own header,
|
|
5373
3824
|
// and we don't want to show a second header below existing messages
|
|
@@ -5382,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5382
3833
|
// The message with the pending approval will display its own UI for approval
|
|
5383
3834
|
if (allPendingApprovals.length > 0)
|
|
5384
3835
|
return false;
|
|
5385
|
-
// The hidden-greeting startup splash already renders its own bouncing dots.
|
|
5386
|
-
// Suppress the generic indicator so reopening/running terminals don't show two.
|
|
5387
|
-
if (showInitialThinkingSplash)
|
|
5388
|
-
return false;
|
|
5389
3836
|
// IMPORTANT: If the last message is an assistant message and we're still executing,
|
|
5390
3837
|
// the AiResponseMessage for that message will show its own activity indicator.
|
|
5391
3838
|
// We only want these global thinking dots if the last message was from the user
|
|
5392
3839
|
// or if no messages exist yet (waiting for initial response).
|
|
5393
3840
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
5394
|
-
if (isExecuting &&
|
|
5395
|
-
lastMessage?.role === "assistant" &&
|
|
5396
|
-
!lastMessage.isCompleted) {
|
|
3841
|
+
if (isExecuting && lastMessage?.role === "assistant")
|
|
5397
3842
|
return false;
|
|
5398
|
-
}
|
|
5399
3843
|
// Existing check for uncompleted assistant messages
|
|
5400
3844
|
const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
|
|
5401
3845
|
if (hasActiveStreamingMessage)
|
|
@@ -5411,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5411
3855
|
messages,
|
|
5412
3856
|
activeInlineDialog,
|
|
5413
3857
|
allPendingApprovals,
|
|
5414
|
-
showInitialThinkingSplash,
|
|
5415
3858
|
]);
|
|
5416
3859
|
// Move useMemo hook before early return to comply with Rules of Hooks
|
|
5417
|
-
const
|
|
3860
|
+
const isLiveEditorContextMode = React.useMemo(() => {
|
|
5418
3861
|
try {
|
|
5419
3862
|
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
5420
3863
|
const profile = activeProfile ||
|
|
5421
3864
|
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
5422
|
-
|
|
3865
|
+
const mode = profile?.editorContextMode;
|
|
3866
|
+
return mode === "live";
|
|
5423
3867
|
}
|
|
5424
3868
|
catch {
|
|
5425
|
-
return
|
|
3869
|
+
return false;
|
|
5426
3870
|
}
|
|
5427
3871
|
}, [activeProfile, profiles, agent?.profileId]);
|
|
5428
|
-
const isLiveEditorContextMode = resolvedEditorContextMode === "live";
|
|
5429
|
-
const omitsEditorContext = resolvedEditorContextMode === "none";
|
|
5430
3872
|
// Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
|
|
5431
3873
|
const parentAgentId = agent?.parentAgentId ||
|
|
5432
3874
|
agent?.ParentAgentId ||
|
|
@@ -5440,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5440
3882
|
detail: { agentId: parentAgentId },
|
|
5441
3883
|
}));
|
|
5442
3884
|
}, [parentAgentId]);
|
|
5443
|
-
|
|
5444
|
-
|
|
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 }));
|
|
5445
3889
|
const renderCostLimitBanner = () => {
|
|
5446
3890
|
if (!costLimitExceeded)
|
|
5447
3891
|
return null;
|
|
@@ -5452,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5452
3896
|
try {
|
|
5453
3897
|
// Extend cost limit - backend will automatically resume the agent
|
|
5454
3898
|
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
5455
|
-
// Update the agent's cost limit
|
|
5456
|
-
// status in local state so the useEffect watcher doesn't
|
|
5457
|
-
// immediately re-show the banner before the backend status
|
|
5458
|
-
// update arrives.
|
|
3899
|
+
// Update the agent's cost limit in local state
|
|
5459
3900
|
if (result.success && result.costLimit !== undefined) {
|
|
5460
|
-
setAgent((prev) => prev
|
|
5461
|
-
? {
|
|
5462
|
-
...prev,
|
|
5463
|
-
costLimit: result.costLimit,
|
|
5464
|
-
status: prev.status === "costLimitReached"
|
|
5465
|
-
? "running"
|
|
5466
|
-
: prev.status,
|
|
5467
|
-
}
|
|
5468
|
-
: prev);
|
|
3901
|
+
setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
|
|
5469
3902
|
}
|
|
5470
3903
|
// Clear the banner and set waiting state
|
|
5471
3904
|
// Agent will resume automatically via backend's ResumeAgentAsync
|
|
@@ -5485,257 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5485
3918
|
};
|
|
5486
3919
|
const renderErrorBanner = () => {
|
|
5487
3920
|
const currentAgent = agent || agentStub;
|
|
5488
|
-
const isErrorStatus = currentAgent?.status === "error";
|
|
5489
|
-
const
|
|
5490
|
-
|
|
5491
|
-
// Show error banner for error status, or for any terminal status that still
|
|
5492
|
-
// carries a statusMessage (e.g. agent closed after an error).
|
|
5493
|
-
const isTerminalWithError = !isErrorStatus &&
|
|
5494
|
-
!!currentAgent?.statusMessage &&
|
|
5495
|
-
currentAgent?.status !== "running" &&
|
|
5496
|
-
currentAgent?.status !== "new" &&
|
|
5497
|
-
!isWaitingForInputStatus &&
|
|
5498
|
-
!isWaitingForApprovalStatus;
|
|
5499
|
-
const rawErrorMessage = (isErrorStatus || isTerminalWithError
|
|
5500
|
-
? currentAgent?.statusMessage
|
|
5501
|
-
: null) || error;
|
|
5502
|
-
if (!rawErrorMessage)
|
|
3921
|
+
const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
|
|
3922
|
+
const errorMessage = currentAgent?.statusMessage;
|
|
3923
|
+
if (!isErrorStatus || !errorMessage)
|
|
5503
3924
|
return null;
|
|
5504
|
-
|
|
5505
|
-
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5506
|
-
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 })] })] }) }));
|
|
5507
3926
|
};
|
|
5508
|
-
|
|
5509
|
-
if (!agent?.id || !editContext?.sessionId)
|
|
5510
|
-
return null;
|
|
5511
|
-
if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
|
|
5512
|
-
return null;
|
|
5513
|
-
}
|
|
5514
|
-
if (isPendingBrowserCaptureWait) {
|
|
5515
|
-
return null;
|
|
5516
|
-
}
|
|
5517
|
-
const label = isClaimedByCurrentSession
|
|
5518
|
-
? "Attached to this browser"
|
|
5519
|
-
: isClaimedByAnotherBrowser
|
|
5520
|
-
? "Attached in another browser"
|
|
5521
|
-
: "No browser attached";
|
|
5522
|
-
const description = isClaimedByCurrentSession
|
|
5523
|
-
? "This browser will handle page screenshot and DOM capture requests for the agent."
|
|
5524
|
-
: isClaimedByAnotherBrowser
|
|
5525
|
-
? "Capture requests will stay with the other browser until you take over control here."
|
|
5526
|
-
: "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
|
|
5527
|
-
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");
|
|
5528
|
-
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: () => {
|
|
5529
|
-
void handleReleaseBrowser();
|
|
5530
|
-
}, 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: () => {
|
|
5531
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5532
|
-
}, children: isClaimedByAnotherBrowser
|
|
5533
|
-
? "Take over browser control"
|
|
5534
|
-
: "Attach to this browser" })) })] }) }));
|
|
5535
|
-
};
|
|
5536
|
-
const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
|
|
5537
|
-
const inlineBrowserClaimBanner = null;
|
|
5538
|
-
const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
|
|
5539
|
-
? {
|
|
5540
|
-
toolNames: [
|
|
5541
|
-
"capture-page-screenshot",
|
|
5542
|
-
"capture-parhelia-ui-screenshot",
|
|
5543
|
-
"capture-page-dom",
|
|
5544
|
-
],
|
|
5545
|
-
label: isClaimedByAnotherBrowser
|
|
5546
|
-
? "Attached in another browser"
|
|
5547
|
-
: "No browser attached",
|
|
5548
|
-
description: isClaimedByAnotherBrowser
|
|
5549
|
-
? "This capture request is waiting in another browser. Take over browser control here to continue."
|
|
5550
|
-
: "This capture request is waiting for a browser attachment. Attach this browser to continue.",
|
|
5551
|
-
actionLabel: isClaimedByAnotherBrowser
|
|
5552
|
-
? "Take over browser control"
|
|
5553
|
-
: "Attach to this browser",
|
|
5554
|
-
isPending: isBrowserClaimMutationPending,
|
|
5555
|
-
onAction: () => {
|
|
5556
|
-
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5557
|
-
},
|
|
5558
|
-
}
|
|
5559
|
-
: null;
|
|
5560
|
-
useEffect(() => {
|
|
5561
|
-
if (agent?.status !== "waitingForInput") {
|
|
5562
|
-
setPendingBrowserCaptureDialogType(null);
|
|
5563
|
-
}
|
|
5564
|
-
}, [agent?.status]);
|
|
5565
|
-
const renderInlineDialogContent = () => {
|
|
5566
|
-
if (!activeInlineDialog)
|
|
5567
|
-
return null;
|
|
5568
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5569
|
-
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) => {
|
|
5570
|
-
activeInlineDialog.onComplete(result);
|
|
5571
|
-
setActiveInlineDialog(null);
|
|
5572
|
-
void onInteractionSubmitted?.();
|
|
5573
|
-
}, onCancel: () => {
|
|
5574
|
-
activeInlineDialog.onCancel();
|
|
5575
|
-
setActiveInlineDialog(null);
|
|
5576
|
-
} }) }));
|
|
5577
|
-
}
|
|
5578
|
-
const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
|
|
5579
|
-
if (dialogRegistration) {
|
|
5580
|
-
const DialogComponent = dialogRegistration.component;
|
|
5581
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
|
|
5582
|
-
activeInlineDialog.onComplete(result);
|
|
5583
|
-
setActiveInlineDialog(null);
|
|
5584
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5585
|
-
void onInteractionSubmitted?.();
|
|
5586
|
-
}
|
|
5587
|
-
}, onCancel: () => {
|
|
5588
|
-
activeInlineDialog.onCancel();
|
|
5589
|
-
setActiveInlineDialog(null);
|
|
5590
|
-
} }) }));
|
|
5591
|
-
}
|
|
5592
|
-
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] }) }));
|
|
5593
|
-
};
|
|
5594
|
-
const latestSummaryAssistantGroup = useMemo(() => {
|
|
5595
|
-
if (hideSummaryMessages)
|
|
5596
|
-
return null;
|
|
5597
|
-
const groups = groupConsecutiveMessages(messages);
|
|
5598
|
-
for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
|
|
5599
|
-
const group = groups[groupIndex];
|
|
5600
|
-
if (!group || group.type !== "assistant-group")
|
|
5601
|
-
continue;
|
|
5602
|
-
const filteredMessages = group.messages.filter((msg) => {
|
|
5603
|
-
const content = msg.content || "";
|
|
5604
|
-
return !content.startsWith("⚠️") || !content.includes("Cost limit");
|
|
5605
|
-
});
|
|
5606
|
-
if (filteredMessages.length === 0)
|
|
5607
|
-
continue;
|
|
5608
|
-
return {
|
|
5609
|
-
messages: filteredMessages,
|
|
5610
|
-
isLastGroup: groupIndex === groups.length - 1,
|
|
5611
|
-
};
|
|
5612
|
-
}
|
|
5613
|
-
return null;
|
|
5614
|
-
}, [messages, hideSummaryMessages]);
|
|
5615
|
-
const summaryModeContent = displayMode === "summary"
|
|
5616
|
-
? (() => {
|
|
5617
|
-
const inlineDialog = renderInlineDialogContent();
|
|
5618
|
-
const summaryMessages = latestSummaryAssistantGroup
|
|
5619
|
-
? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
|
|
5620
|
-
: [];
|
|
5621
|
-
const summaryOperations = latestSummaryAssistantGroup
|
|
5622
|
-
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
5623
|
-
: [];
|
|
5624
|
-
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 &&
|
|
5625
|
-
!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: {
|
|
5626
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5627
|
-
} })) : (_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 ||
|
|
5628
|
-
activeProfile?.displayTitle ||
|
|
5629
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5630
|
-
const text = (action.prompt ||
|
|
5631
|
-
action.value ||
|
|
5632
|
-
action.label ||
|
|
5633
|
-
"").trim();
|
|
5634
|
-
if (!text)
|
|
5635
|
-
return;
|
|
5636
|
-
if (isExecuting) {
|
|
5637
|
-
try {
|
|
5638
|
-
handleStop();
|
|
5639
|
-
}
|
|
5640
|
-
catch { }
|
|
5641
|
-
}
|
|
5642
|
-
sendQuickMessage(text);
|
|
5643
|
-
} }) })) : 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
|
|
5644
|
-
? "The agent is still working. The next update will appear here automatically."
|
|
5645
|
-
: agent?.statusMessage ||
|
|
5646
|
-
summaryPlaceholderMessage ||
|
|
5647
|
-
"Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
|
|
5648
|
-
shouldShowThinkingDots &&
|
|
5649
|
-
!inlineDialog &&
|
|
5650
|
-
!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: {
|
|
5651
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5652
|
-
} })) : (_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 ||
|
|
5653
|
-
activeProfile?.displayTitle ||
|
|
5654
|
-
activeProfile?.name ||
|
|
5655
|
-
"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) => {
|
|
5656
|
-
setActivePlaceholderInput(null);
|
|
5657
|
-
setAllPlaceholdersFilled(false);
|
|
5658
|
-
if (activePlaceholderInput.behavior === "compose" &&
|
|
5659
|
-
!hideBottomControls) {
|
|
5660
|
-
setPrompt(filledText);
|
|
5661
|
-
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
5662
|
-
if (textareaRef.current) {
|
|
5663
|
-
try {
|
|
5664
|
-
textareaRef.current.focus();
|
|
5665
|
-
const v = textareaRef.current.value || "";
|
|
5666
|
-
textareaRef.current.selectionStart = v.length;
|
|
5667
|
-
textareaRef.current.selectionEnd = v.length;
|
|
5668
|
-
}
|
|
5669
|
-
catch { }
|
|
5670
|
-
}
|
|
5671
|
-
}
|
|
5672
|
-
else {
|
|
5673
|
-
if (isExecuting) {
|
|
5674
|
-
try {
|
|
5675
|
-
handleStop();
|
|
5676
|
-
}
|
|
5677
|
-
catch { }
|
|
5678
|
-
}
|
|
5679
|
-
sendQuickMessage(filledText);
|
|
5680
|
-
}
|
|
5681
|
-
}, onCancel: () => {
|
|
5682
|
-
setActivePlaceholderInput(null);
|
|
5683
|
-
setAllPlaceholdersFilled(false);
|
|
5684
|
-
} })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5685
|
-
setPrompt(filledText);
|
|
5686
|
-
setAllPlaceholdersFilled(false);
|
|
5687
|
-
if (filledText.trim()) {
|
|
5688
|
-
if (isExecuting) {
|
|
5689
|
-
try {
|
|
5690
|
-
handleStop();
|
|
5691
|
-
}
|
|
5692
|
-
catch { }
|
|
5693
|
-
}
|
|
5694
|
-
sendQuickMessage(filledText);
|
|
5695
|
-
}
|
|
5696
|
-
}, onCancel: () => {
|
|
5697
|
-
setPrompt("");
|
|
5698
|
-
setAllPlaceholdersFilled(false);
|
|
5699
|
-
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
5700
|
-
} })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
|
|
5701
|
-
setPrompt(e.target.value);
|
|
5702
|
-
if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
|
|
5703
|
-
setAllPlaceholdersFilled(false);
|
|
5704
|
-
}
|
|
5705
|
-
if (currentHistoryIndex !== -1) {
|
|
5706
|
-
setCurrentHistoryIndex(-1);
|
|
5707
|
-
}
|
|
5708
|
-
}, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
|
|
5709
|
-
shouldMaintainFocusRef.current = true;
|
|
5710
|
-
}, onBlur: () => {
|
|
5711
|
-
shouldMaintainFocusRef.current = false;
|
|
5712
|
-
}, 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 }) })), (() => {
|
|
5713
|
-
const isInPlaceholderMode = activePlaceholderInput ||
|
|
5714
|
-
(prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
|
|
5715
|
-
const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
|
|
5716
|
-
if (placeholderShowsOwnButtons)
|
|
5717
|
-
return null;
|
|
5718
|
-
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
|
|
5719
|
-
simpleMode ||
|
|
5720
|
-
isInPlaceholderMode
|
|
5721
|
-
? "justify-end"
|
|
5722
|
-
: "justify-between"), children: [!hideBottomControls &&
|
|
5723
|
-
!simpleMode &&
|
|
5724
|
-
!isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
|
|
5725
|
-
if (isExecuting) {
|
|
5726
|
-
handleStop();
|
|
5727
|
-
}
|
|
5728
|
-
else {
|
|
5729
|
-
handleSubmit();
|
|
5730
|
-
}
|
|
5731
|
-
}, disabled: !isExecuting &&
|
|
5732
|
-
!activePlaceholderInput &&
|
|
5733
|
-
(!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
|
|
5734
|
-
})()] })) : null] }));
|
|
5735
|
-
})()
|
|
5736
|
-
: null;
|
|
5737
|
-
const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
|
|
5738
|
-
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) => {
|
|
5739
3928
|
setPrompt(p);
|
|
5740
3929
|
// Use setTimeout to ensure state is updated before submission
|
|
5741
3930
|
setTimeout(() => {
|
|
@@ -5748,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5748
3937
|
handleSubmit();
|
|
5749
3938
|
}
|
|
5750
3939
|
}, 0);
|
|
5751
|
-
} })) })),
|
|
5752
|
-
|
|
5753
|
-
|
|
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: [(() => {
|
|
5754
3946
|
const groups = groupConsecutiveMessages(messages);
|
|
5755
3947
|
return groups.map((group, groupIndex) => {
|
|
5756
3948
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -5758,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5758
3950
|
// Render user message
|
|
5759
3951
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
5760
3952
|
}
|
|
5761
|
-
else if (group.type === "heartbeat" && group.messages[0]) {
|
|
5762
|
-
return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
|
|
5763
|
-
}
|
|
5764
3953
|
else {
|
|
5765
3954
|
// Render bundled assistant messages
|
|
5766
3955
|
// Check if this group contains any streaming message
|
|
@@ -5777,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5777
3966
|
}
|
|
5778
3967
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
5779
3968
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5780
|
-
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 ||
|
|
5781
3970
|
activeProfile?.displayTitle ||
|
|
5782
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous,
|
|
3971
|
+
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5783
3972
|
const text = (action.prompt ||
|
|
5784
3973
|
action.value ||
|
|
5785
3974
|
action.label ||
|
|
@@ -5823,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5823
4012
|
}
|
|
5824
4013
|
});
|
|
5825
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: {
|
|
5826
|
-
__html:
|
|
4015
|
+
__html: activeProfile.svgIcon,
|
|
5827
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 ||
|
|
5828
4017
|
activeProfile?.displayTitle ||
|
|
5829
4018
|
activeProfile?.name ||
|
|
5830
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 &&
|
|
5831
4020
|
!simpleMode &&
|
|
5832
|
-
(
|
|
4021
|
+
(isMobile ? (_jsx("div", { className: "border-t border-gray-200 bg-gray-50", "data-testid": "agent-context-panel-tabs", children: _jsx(SimpleTabs, { tabs: [
|
|
5833
4022
|
{
|
|
5834
4023
|
id: "context",
|
|
5835
4024
|
label: "Context",
|
|
@@ -5867,7 +4056,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5867
4056
|
{
|
|
5868
4057
|
id: "history",
|
|
5869
4058
|
label: "History",
|
|
5870
|
-
content: (_jsx(AgentEditOperationsPanel, {
|
|
4059
|
+
content: (_jsx(AgentEditOperationsPanel, { agentId: agent.id })),
|
|
5871
4060
|
},
|
|
5872
4061
|
]
|
|
5873
4062
|
: []),
|
|
@@ -5877,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5877
4066
|
hasTodoContent,
|
|
5878
4067
|
hasSpawnedAgents,
|
|
5879
4068
|
agent?.id && hasHistoryContent,
|
|
5880
|
-
].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 &&
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
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() ===
|
|
5903
|
-
new Date().toDateString()
|
|
5904
|
-
? formatTime(new Date(qp.scheduledFor))
|
|
5905
|
-
: formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
|
|
5906
|
-
}) })] }) }))] }));
|
|
5907
|
-
const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
|
|
5908
|
-
const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
|
|
5909
|
-
{
|
|
5910
|
-
name: "conversation",
|
|
5911
|
-
defaultSize: 65,
|
|
5912
|
-
content: fullModeUpperContent,
|
|
5913
|
-
},
|
|
5914
|
-
{
|
|
5915
|
-
name: "questionnaire",
|
|
5916
|
-
defaultSize: 35,
|
|
5917
|
-
content: fullModeInlineDialog,
|
|
5918
|
-
},
|
|
5919
|
-
], direction: "vertical", localStorageKey: compact
|
|
5920
|
-
? "agent-terminal-compact-questionnaire-splitter"
|
|
5921
|
-
: "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
|
|
5922
|
-
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 ? (
|
|
5923
4091
|
// Placeholder Input (from quick actions)
|
|
5924
4092
|
// Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
|
|
5925
4093
|
_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
@@ -5996,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5996
4164
|
return null;
|
|
5997
4165
|
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
|
|
5998
4166
|
? "justify-end"
|
|
5999
|
-
: "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"
|
|
6000
4168
|
? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
|
|
6001
4169
|
: mode === "supervised"
|
|
6002
4170
|
? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
|
|
6003
4171
|
: "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
|
|
6004
4172
|
const nextMode = val || "supervised";
|
|
4173
|
+
// Optimistic UI update
|
|
4174
|
+
setMode(nextMode);
|
|
6005
4175
|
const current = agentMetadata || {};
|
|
6006
4176
|
const nextMeta = {
|
|
6007
4177
|
...current,
|
|
6008
4178
|
mode: nextMode,
|
|
6009
4179
|
};
|
|
6010
4180
|
try {
|
|
6011
|
-
if (!agent?.id ||
|
|
6012
|
-
setMode(nextMode);
|
|
4181
|
+
if (!agent?.id || agent.status === "new") {
|
|
6013
4182
|
setAgentMetadata(nextMeta);
|
|
4183
|
+
// Cache until first start when agent is persisted
|
|
6014
4184
|
pendingSettingsRef.current = {
|
|
6015
4185
|
...(pendingSettingsRef.current || {}),
|
|
6016
4186
|
mode: nextMode,
|
|
6017
4187
|
};
|
|
6018
4188
|
return;
|
|
6019
4189
|
}
|
|
6020
|
-
|
|
4190
|
+
await updateAgentSettings(agent.id, {
|
|
6021
4191
|
mode: nextMode,
|
|
6022
4192
|
});
|
|
6023
|
-
if (result.success === false ||
|
|
6024
|
-
result.updates?.mode === false) {
|
|
6025
|
-
throw new Error("Mode change was not applied");
|
|
6026
|
-
}
|
|
6027
|
-
setMode(nextMode);
|
|
6028
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
|
|
6029
4231
|
setAgent((prev) => prev
|
|
6030
4232
|
? {
|
|
6031
4233
|
...prev,
|
|
6032
|
-
|
|
6033
|
-
|
|
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
|
+
}),
|
|
6034
4244
|
}
|
|
6035
4245
|
: prev);
|
|
6036
4246
|
}
|
|
6037
|
-
catch (
|
|
6038
|
-
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
|
+
}
|
|
6039
4270
|
}
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
if (!open) {
|
|
6043
|
-
setShowSkillPicker(false);
|
|
4271
|
+
catch (err) {
|
|
4272
|
+
console.error("Failed to persist agent model", err);
|
|
6044
4273
|
}
|
|
6045
|
-
}
|
|
6046
|
-
const target = e.target;
|
|
6047
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6048
|
-
e.preventDefault();
|
|
6049
|
-
}
|
|
6050
|
-
}, onPointerDownOutside: (e) => {
|
|
6051
|
-
const target = e.target;
|
|
6052
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6053
|
-
e.preventDefault();
|
|
6054
|
-
}
|
|
6055
|
-
}, onFocusOutside: (e) => {
|
|
6056
|
-
const target = e.target;
|
|
6057
|
-
if (target?.closest('[data-help-panel="true"]')) {
|
|
6058
|
-
e.preventDefault();
|
|
6059
|
-
}
|
|
6060
|
-
}, 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) => {
|
|
6061
|
-
const nextProfile = profiles.find((x) => x.id === val);
|
|
6062
|
-
if (!nextProfile)
|
|
6063
|
-
return;
|
|
6064
|
-
setActiveProfile(nextProfile);
|
|
6065
|
-
try {
|
|
6066
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6067
|
-
await updateAgentSettings(agent.id, {
|
|
6068
|
-
profileId: nextProfile.id,
|
|
6069
|
-
profileName: nextProfile.name,
|
|
6070
|
-
});
|
|
6071
|
-
}
|
|
6072
|
-
else {
|
|
6073
|
-
pendingSettingsRef.current = {
|
|
6074
|
-
...(pendingSettingsRef.current || {}),
|
|
6075
|
-
profileId: nextProfile.id,
|
|
6076
|
-
profileName: nextProfile.name,
|
|
6077
|
-
};
|
|
6078
|
-
setAgentMetadata((current) => {
|
|
6079
|
-
const next = {
|
|
6080
|
-
...(current || {}),
|
|
6081
|
-
};
|
|
6082
|
-
next.profile = nextProfile.name;
|
|
6083
|
-
next.additionalData = {
|
|
6084
|
-
...(next.additionalData || {}),
|
|
6085
|
-
profileId: nextProfile.id,
|
|
6086
|
-
profileName: nextProfile.name,
|
|
6087
|
-
};
|
|
6088
|
-
return next;
|
|
6089
|
-
});
|
|
6090
|
-
}
|
|
6091
|
-
setAgent((prev) => prev
|
|
6092
|
-
? {
|
|
6093
|
-
...prev,
|
|
6094
|
-
profileId: nextProfile.id,
|
|
6095
|
-
profileName: nextProfile.name,
|
|
6096
|
-
metadata: JSON.stringify({
|
|
6097
|
-
...(agentMetadata || {}),
|
|
6098
|
-
profile: nextProfile.name,
|
|
6099
|
-
additionalData: {
|
|
6100
|
-
...(agentMetadata
|
|
6101
|
-
?.additionalData || {}),
|
|
6102
|
-
profileId: nextProfile.id,
|
|
6103
|
-
profileName: nextProfile.name,
|
|
6104
|
-
},
|
|
6105
|
-
}),
|
|
6106
|
-
}
|
|
6107
|
-
: prev);
|
|
6108
|
-
}
|
|
6109
|
-
catch (err) {
|
|
6110
|
-
console.error("Failed to persist agent profile", err);
|
|
6111
|
-
}
|
|
6112
|
-
} }), 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: () => {
|
|
6113
|
-
void handleEditProfileSideBySide();
|
|
6114
|
-
setShowAgentSettings(false);
|
|
6115
|
-
}, "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: () => {
|
|
6116
|
-
void handleOpenProfileSettings();
|
|
6117
|
-
setShowAgentSettings(false);
|
|
6118
|
-
}, 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) => {
|
|
6119
|
-
const nextId = val;
|
|
6120
|
-
setSelectedModelId(nextId);
|
|
6121
|
-
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
|
|
6122
|
-
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
6123
|
-
try {
|
|
6124
|
-
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
6125
|
-
await updateAgentSettings(agent.id, {
|
|
6126
|
-
model: modelName,
|
|
6127
|
-
});
|
|
6128
|
-
}
|
|
6129
|
-
else {
|
|
6130
|
-
pendingSettingsRef.current = {
|
|
6131
|
-
...(pendingSettingsRef.current || {}),
|
|
6132
|
-
modelName,
|
|
6133
|
-
};
|
|
6134
|
-
}
|
|
6135
|
-
}
|
|
6136
|
-
catch (err) {
|
|
6137
|
-
console.error("Failed to persist agent model", err);
|
|
6138
|
-
}
|
|
6139
|
-
} })] })) : 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) => {
|
|
6140
|
-
setShowSkillPicker(open);
|
|
6141
|
-
if (open) {
|
|
6142
|
-
setSkillActionError(null);
|
|
6143
|
-
}
|
|
6144
|
-
}, 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) => {
|
|
6145
|
-
const selected = selection[0];
|
|
6146
|
-
if (!selected?.id)
|
|
6147
|
-
return;
|
|
6148
|
-
setSkillActionError(null);
|
|
6149
|
-
if (selectableTemplateIdSet.size > 0 &&
|
|
6150
|
-
(!selected.templateId ||
|
|
6151
|
-
!selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
|
|
6152
|
-
return;
|
|
6153
|
-
}
|
|
6154
|
-
if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
|
|
6155
|
-
setSkillActionError("This skill cannot be added for the current agent profile.");
|
|
6156
|
-
return;
|
|
6157
|
-
}
|
|
6158
|
-
void (async () => {
|
|
6159
|
-
const added = await handleAddSkill(selected.id);
|
|
6160
|
-
if (added) {
|
|
6161
|
-
setShowSkillPicker(false);
|
|
6162
|
-
}
|
|
6163
|
-
})();
|
|
6164
|
-
} }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
|
|
6165
|
-
!skillsError &&
|
|
6166
|
-
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 &&
|
|
6167
|
-
!skillsError &&
|
|
6168
|
-
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
6169
|
-
!skillsError &&
|
|
6170
|
-
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
6171
|
-
? "All addable skills are selected"
|
|
6172
|
-
: "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) => {
|
|
6173
|
-
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
6174
|
-
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: () => {
|
|
6175
|
-
void handleOpenSkillItem(skillId);
|
|
6176
|
-
}, 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: () => {
|
|
6177
|
-
void handleRemoveSkill(skillId);
|
|
6178
|
-
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
6179
|
-
}) }))] }), _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
|
|
6180
|
-
? "No available tools for this profile and mode"
|
|
6181
|
-
: "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) => {
|
|
6182
|
-
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
6183
|
-
const pathLabel = "itemPath" in allowance
|
|
6184
|
-
? allowance.itemPath
|
|
6185
|
-
: allowance.normalizedPath;
|
|
6186
|
-
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 ||
|
|
6187
|
-
"*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
|
|
6188
|
-
allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
|
|
6189
|
-
sourceLabel,
|
|
6190
|
-
allowance.grantedBy,
|
|
6191
|
-
]
|
|
6192
|
-
.filter(Boolean)
|
|
6193
|
-
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
6194
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6195
|
-
? "Allowances are shown after the agent is created"
|
|
6196
|
-
: "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) => {
|
|
6197
|
-
const filterText = (sub.filter || "").trim();
|
|
6198
|
-
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));
|
|
6199
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6200
|
-
? "Subscribed triggers are shown after the agent is created"
|
|
6201
|
-
: "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: () => {
|
|
6202
4275
|
setPrompt(p.prompt);
|
|
6203
4276
|
setShowPredefined(false);
|
|
6204
4277
|
if (textareaRef.current)
|
|
6205
4278
|
textareaRef.current.focus();
|
|
6206
|
-
}, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
|
|
6207
|
-
|
|
6208
|
-
editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
|
|
6209
|
-
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)
|
|
6210
4281
|
setShowCostAndAgent((prev) => !prev);
|
|
6211
|
-
}, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded":
|
|
6212
|
-
? showCostAndAgent
|
|
6213
|
-
: 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
|
|
6214
4283
|
? {
|
|
6215
4284
|
input: liveTotals.input,
|
|
6216
4285
|
output: liveTotals.output,
|
|
@@ -6220,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6220
4289
|
outputCost: liveTotals.outputCost,
|
|
6221
4290
|
cachedCost: liveTotals.cachedCost,
|
|
6222
4291
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6223
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6224
4292
|
totalCost: liveTotals.totalCost,
|
|
6225
4293
|
}
|
|
6226
|
-
: totalTokens,
|
|
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
|
|
6227
4295
|
? "Your browser does not support Speech Recognition"
|
|
6228
4296
|
: isListening
|
|
6229
4297
|
? "Stop voice input"
|
|
@@ -6244,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6244
4312
|
: allPendingApprovals.length > 0
|
|
6245
4313
|
? "Approve or reject pending tool calls first"
|
|
6246
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 })) })] })] }));
|
|
6247
|
-
})(), !hideBottomControls &&
|
|
6248
|
-
!simpleMode &&
|
|
6249
|
-
editContext &&
|
|
6250
|
-
!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
|
|
6251
4316
|
? {
|
|
6252
4317
|
input: liveTotals.input,
|
|
6253
4318
|
output: liveTotals.output,
|
|
@@ -6257,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6257
4322
|
outputCost: liveTotals.outputCost,
|
|
6258
4323
|
cachedCost: liveTotals.cachedCost,
|
|
6259
4324
|
cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
|
|
6260
|
-
imageCost: liveTotals.imageCost ?? 0,
|
|
6261
4325
|
totalCost: liveTotals.totalCost,
|
|
6262
4326
|
}
|
|
6263
|
-
: totalTokens,
|
|
4327
|
+
: totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
|
|
6264
4328
|
}
|
|
6265
4329
|
//# sourceMappingURL=AgentTerminal.js.map
|