@parhelia/core 0.1.12485 → 0.1.12515
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.js +22 -20
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsInbox.js +2 -13
- package/dist/agents-view/AgentsInbox.js.map +1 -1
- package/dist/agents-view/AgentsView.js +9 -56
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.js +14 -16
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.js +2 -1
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ui/copy-button.d.ts +2 -1
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/copy-button.js.map +1 -1
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/paste-button.d.ts +2 -1
- package/dist/components/ui/paste-button.js +2 -2
- package/dist/components/ui/paste-button.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -1
- package/dist/components/ui/tabs.js +4 -11
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/config/config.js +73 -8
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +30 -5
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ContentTree.js +36 -4
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/FieldHistory.js +49 -31
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +4 -4
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/GlobalMenuBar.js +1 -1
- package/dist/editor/GlobalMenuBar.js.map +1 -1
- package/dist/editor/ItemInfo.js +36 -1
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +0 -2
- package/dist/editor/MainLayout.js +0 -1
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Titlebar.js +2 -2
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentDocumentList.js +32 -14
- package/dist/editor/ai/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/AgentGreeting.js +3 -2
- package/dist/editor/ai/AgentGreeting.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +2 -1
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
- package/dist/editor/ai/AgentStatusBadge.js +57 -71
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +585 -248
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +33 -88
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +3 -4
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +17 -11
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.js +4 -8
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/MediaImage.js +40 -8
- package/dist/editor/ai/MediaImage.js.map +1 -1
- package/dist/editor/ai/SpawnedAgentsPanel.js +10 -12
- package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.js +156 -64
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/agentDiagnostics.js +1 -3
- package/dist/editor/ai/agentDiagnostics.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +1 -8
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +89 -12
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +62 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js +2 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/ai/dialogs/browserBoundCapture.d.ts +29 -0
- package/dist/editor/ai/dialogs/browserBoundCapture.js +117 -0
- package/dist/editor/ai/dialogs/browserBoundCapture.js.map +1 -0
- package/dist/editor/ai/dialogs/capturePageDom.d.ts +3 -0
- package/dist/editor/ai/dialogs/capturePageDom.js +64 -0
- package/dist/editor/ai/dialogs/capturePageDom.js.map +1 -0
- package/dist/editor/ai/dialogs/capturePageScreenshot.d.ts +3 -0
- package/dist/editor/ai/dialogs/capturePageScreenshot.js +446 -0
- package/dist/editor/ai/dialogs/capturePageScreenshot.js.map +1 -0
- package/dist/editor/ai/useAgentStatus.js +23 -85
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/client/EditorShell.js +88 -100
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +8 -16
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
- package/dist/editor/client/hooks/useEditorWebSocket.js +56 -44
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +19 -6
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +10 -6
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.js +15 -3
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +1 -1
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.d.ts +0 -4
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/client/waitForEditOperationTerminal.d.ts +8 -3
- package/dist/editor/client/waitForEditOperationTerminal.js +5 -1
- package/dist/editor/client/waitForEditOperationTerminal.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +3 -49
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/customCommandConverter.js +2 -1
- package/dist/editor/commands/customCommandConverter.js.map +1 -1
- package/dist/editor/commands/handlers/agentHandler.js +2 -1
- package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
- package/dist/editor/commands/itemCommands.js +5 -0
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/content-tree/IndicatorSettings.d.ts +11 -0
- package/dist/editor/content-tree/IndicatorSettings.js +60 -0
- package/dist/editor/content-tree/IndicatorSettings.js.map +1 -0
- package/dist/editor/content-tree/TreeOptions.d.ts +6 -0
- package/dist/editor/content-tree/TreeOptions.js +8 -0
- package/dist/editor/content-tree/TreeOptions.js.map +1 -0
- package/dist/editor/content-tree/TreeSettingsMenu.d.ts +2 -0
- package/dist/editor/content-tree/TreeSettingsMenu.js +30 -0
- package/dist/editor/content-tree/TreeSettingsMenu.js.map +1 -0
- package/dist/editor/hooks/useNavigationPanelLogic.js +2 -6
- package/dist/editor/hooks/useNavigationPanelLogic.js.map +1 -1
- package/dist/editor/manualActionEvents.d.ts +8 -0
- package/dist/editor/manualActionEvents.js +48 -0
- package/dist/editor/manualActionEvents.js.map +1 -0
- package/dist/editor/menubar/PageSelector.js +9 -12
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.js +23 -23
- package/dist/editor/menubar/WorkflowButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +3 -9
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +225 -71
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/notifications/WatchButton.js +2 -2
- package/dist/editor/notifications/WatchButton.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/PlaceholderDropZone.js +11 -11
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +2 -0
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +8 -2
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +21 -8
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +107 -49
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
- package/dist/editor/page-viewer/pageViewContext.js +51 -14
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +1 -3
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/reviews/DiffView.js +7 -14
- package/dist/editor/reviews/DiffView.js.map +1 -1
- package/dist/editor/reviews/useMultiReview.js +2 -2
- package/dist/editor/reviews/useMultiReview.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +34 -3
- package/dist/editor/services/agentService.js +107 -72
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/agentStatus.d.ts +12 -0
- package/dist/editor/services/agentStatus.js +59 -0
- package/dist/editor/services/agentStatus.js.map +1 -0
- package/dist/editor/services/aiService.d.ts +3 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/indexService.js +1 -1
- package/dist/editor/services/indexService.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +22 -20
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/Status.js +5 -4
- package/dist/editor/settings/Status.js.map +1 -1
- package/dist/editor/settings/index/useIndexStatus.js +20 -22
- package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
- package/dist/editor/settings/panels/CreateJavaScriptToolDialog.d.ts +7 -0
- package/dist/editor/settings/panels/CreateJavaScriptToolDialog.js +48 -0
- package/dist/editor/settings/panels/CreateJavaScriptToolDialog.js.map +1 -0
- package/dist/editor/settings/panels/GroupedFieldConfigPanel.d.ts +2 -1
- package/dist/editor/settings/panels/GroupedFieldConfigPanel.js +2 -2
- package/dist/editor/settings/panels/GroupedFieldConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/JavaScriptToolAgentPanel.d.ts +12 -0
- package/dist/editor/settings/panels/JavaScriptToolAgentPanel.js +46 -0
- package/dist/editor/settings/panels/JavaScriptToolAgentPanel.js.map +1 -0
- package/dist/editor/settings/panels/JavaScriptToolConfigPanel.d.ts +9 -0
- package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js +34 -0
- package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js.map +1 -0
- package/dist/editor/settings/panels/JavaScriptToolsPanel.d.ts +2 -0
- package/dist/editor/settings/panels/JavaScriptToolsPanel.js +285 -0
- package/dist/editor/settings/panels/JavaScriptToolsPanel.js.map +1 -0
- package/dist/editor/settings/panels/ModelConfigPanel.d.ts +2 -1
- package/dist/editor/settings/panels/ModelConfigPanel.js +88 -7
- package/dist/editor/settings/panels/ModelConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/ModelsPanel.js +129 -70
- package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +1 -4
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -3
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +78 -22
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.js +40 -55
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/settings/panels/index.d.ts +1 -0
- package/dist/editor/settings/panels/index.js +1 -0
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/settings/status/coreStatusChecks.js +28 -17
- package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +2 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +210 -49
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +3 -38
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +4 -3
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/MorePanelsButton.js +1 -1
- package/dist/editor/sidebar/MorePanelsButton.js.map +1 -1
- package/dist/editor/sidebar/NavigationPanelItem.js +3 -6
- package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
- package/dist/editor/sidebar/SidebarPanel.js +20 -4
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/sidebar/SidebarStack.js +1 -0
- package/dist/editor/sidebar/SidebarStack.js.map +1 -1
- package/dist/editor/sidebar/Workbox.js +53 -3
- package/dist/editor/sidebar/Workbox.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.d.ts +3 -1
- package/dist/editor/tree-indicators/GutterColumns.js +4 -3
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
- package/dist/editor/tree-indicators/GutterContext.js +23 -0
- package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
- package/dist/editor/tree-indicators/index.d.ts +0 -1
- package/dist/editor/tree-indicators/index.js +0 -1
- package/dist/editor/tree-indicators/index.js.map +1 -1
- package/dist/editor/tree-indicators/types.d.ts +2 -1
- package/dist/editor/ui/HomeButton.js +1 -1
- package/dist/editor/ui/HomeButton.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
- package/dist/editor/ui/ItemNameDialogNew.js +17 -7
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/ItemSearch.js +7 -11
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.js +33 -16
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.js +1 -1
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/views/CompareView.js +3 -1
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditorSlot.js +2 -2
- package/dist/editor/views/EditorSlot.js.map +1 -1
- package/dist/editor/views/ParheliaView.js +5 -6
- package/dist/editor/views/ParheliaView.js.map +1 -1
- package/dist/editor/views/SingleEditView.js +2 -0
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/editor/views/editorSlotContext.js +35 -6
- package/dist/editor/views/editorSlotContext.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/sanitize.d.ts +10 -0
- package/dist/lib/sanitize.js +40 -0
- package/dist/lib/sanitize.js.map +1 -0
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/setup/services/setupWizardService.d.ts +28 -0
- package/dist/setup/services/setupWizardService.js +34 -0
- package/dist/setup/services/setupWizardService.js.map +1 -1
- package/dist/setup/wizard/steps/AddModelDialog.js +12 -3
- package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
- package/dist/setup/wizard/steps/ImportModelDialog.js +3 -1
- package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
- package/dist/splash-screen/NewPage.js +24 -24
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/task-board/TaskBoardWorkspace.js +29 -10
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/AssignAgentDialog.js +0 -8
- package/dist/task-board/components/AssignAgentDialog.js.map +1 -1
- package/dist/task-board/components/ItemCollectionEditorDialog.js +5 -12
- package/dist/task-board/components/ItemCollectionEditorDialog.js.map +1 -1
- package/dist/task-board/components/ProjectAgentsPanel.js +2 -27
- package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -1
- package/dist/task-board/components/ProjectOverviewContent.js +2 -2
- package/dist/task-board/components/ProjectOverviewContent.js.map +1 -1
- package/dist/task-board/components/ProjectPropertiesPanel.js +1 -1
- package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
- package/dist/task-board/components/TaskAgentPanel.js +2 -6
- package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +1 -1
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/components/TaskboardPersistentLogPanel.js +3 -1
- package/dist/task-board/components/TaskboardPersistentLogPanel.js.map +1 -1
- package/dist/task-board/taskAgentLink.js +1 -3
- package/dist/task-board/taskAgentLink.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.js +19 -1
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/dist/tour/Tour.js +10 -4
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +51 -11
- package/dist/tour/default-tour.js.map +1 -1
- package/dist/types.d.ts +4 -13
- package/package.json +4 -1
- package/dist/editor/ComponentInfo.d.ts +0 -4
- package/dist/editor/ComponentInfo.js +0 -41
- package/dist/editor/ComponentInfo.js.map +0 -1
- package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
- package/dist/editor/tree-indicators/GutterSelector.js +0 -91
- package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
|
@@ -1,7 +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
3
|
import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
|
|
4
|
-
import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, } from "../services/agentService";
|
|
4
|
+
import { getAgent, startAgent, claimAgentBrowser, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, } from "../services/agentService";
|
|
5
|
+
import { parseAgentStatus } from "../services/agentStatus";
|
|
5
6
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
6
7
|
import { localStorageService } from "../services/localStorageService";
|
|
7
8
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -16,15 +17,31 @@ import { getComponentById } from "../componentTreeHelper";
|
|
|
16
17
|
import { AgentGreeting } from "./AgentGreeting";
|
|
17
18
|
import { getAgentHistory } from "../services/editService";
|
|
18
19
|
import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
|
|
20
|
+
import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
|
|
21
|
+
import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
|
|
19
22
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
20
23
|
import { SecretAgentIcon } from "../ui/Icons";
|
|
21
24
|
import { formatTime, formatDateTime } from "../utils";
|
|
22
25
|
import { cn } from "../../lib/utils";
|
|
26
|
+
import { sanitizeSvg } from "../../lib/sanitize";
|
|
23
27
|
import { Select } from "../../components/ui/select";
|
|
24
28
|
import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
|
|
25
29
|
import { SimpleTabs } from "../ui/SimpleTabs";
|
|
26
30
|
import { Splitter } from "../ui/Splitter";
|
|
27
31
|
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
32
|
+
import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
|
|
33
|
+
const userMessageMarkdownComponents = {
|
|
34
|
+
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm font-semibold leading-5 text-gray-900" })),
|
|
35
|
+
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] font-semibold leading-5 text-gray-900" })),
|
|
36
|
+
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] font-semibold leading-5 text-gray-900" })),
|
|
37
|
+
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] font-medium leading-5 text-gray-800" })),
|
|
38
|
+
p: (props) => _jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" }),
|
|
39
|
+
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
40
|
+
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
41
|
+
li: (props) => _jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" }),
|
|
42
|
+
pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
|
|
43
|
+
code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
|
|
44
|
+
};
|
|
28
45
|
function buildPlaceholderAgentDetails(agentStub) {
|
|
29
46
|
const now = new Date().toISOString();
|
|
30
47
|
const updated = agentStub.updatedDate || now;
|
|
@@ -75,23 +92,19 @@ function formatAllowanceLabel(allowance) {
|
|
|
75
92
|
: ` ${allowance.normalizedPath}`}`;
|
|
76
93
|
}
|
|
77
94
|
function getAgentRunMessageAgentId(payload) {
|
|
78
|
-
const agentId = payload?.agentId
|
|
95
|
+
const agentId = payload?.agentId;
|
|
79
96
|
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
80
97
|
}
|
|
81
98
|
function getAgentRunMessageSeq(payload) {
|
|
82
|
-
const seq = payload?.seq
|
|
99
|
+
const seq = payload?.seq;
|
|
83
100
|
return typeof seq === "number" ? seq : null;
|
|
84
101
|
}
|
|
85
102
|
function getAgentRunMessageDetail(type, payload) {
|
|
86
103
|
if (type === "agent:run:delta") {
|
|
87
|
-
return payload?.type ||
|
|
104
|
+
return payload?.type || null;
|
|
88
105
|
}
|
|
89
106
|
if (type === "agent:run:status") {
|
|
90
|
-
return
|
|
91
|
-
payload?.data?.State ||
|
|
92
|
-
payload?.data?.status ||
|
|
93
|
-
payload?.data?.Status ||
|
|
94
|
-
null);
|
|
107
|
+
return payload?.data?.state || payload?.data?.status || null;
|
|
95
108
|
}
|
|
96
109
|
if (type === "agent:run:error") {
|
|
97
110
|
return payload?.error || null;
|
|
@@ -104,6 +117,19 @@ function getAgentRunMessageDetail(type, payload) {
|
|
|
104
117
|
}
|
|
105
118
|
return null;
|
|
106
119
|
}
|
|
120
|
+
function isHeartbeatRunEventMessage(message) {
|
|
121
|
+
if (!message)
|
|
122
|
+
return false;
|
|
123
|
+
if (message.type === "agent:run:delta") {
|
|
124
|
+
const deltaType = message.payload?.type;
|
|
125
|
+
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
126
|
+
}
|
|
127
|
+
if (message.type === "agent:run:status") {
|
|
128
|
+
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
129
|
+
return String(state || "").toLowerCase() === "heartbeat";
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
107
133
|
function getVisibleDialogRegistry() {
|
|
108
134
|
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
109
135
|
return registry && typeof registry === "object" ? registry : {};
|
|
@@ -119,6 +145,38 @@ function decodeHtmlEntities(value) {
|
|
|
119
145
|
function toUserFacingAgentErrorMessage(value) {
|
|
120
146
|
if (!value)
|
|
121
147
|
return "";
|
|
148
|
+
const normalizedValue = decodeHtmlEntities(value.replace(/<br\s*\/?>/gi, "\n")).trim();
|
|
149
|
+
if (!normalizedValue)
|
|
150
|
+
return "";
|
|
151
|
+
const trimmed = normalizedValue.trim();
|
|
152
|
+
const maybeJson = trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith('"');
|
|
153
|
+
if (maybeJson) {
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(trimmed);
|
|
156
|
+
const structuredMessage = typeof parsed === "string"
|
|
157
|
+
? parsed
|
|
158
|
+
: typeof parsed?.error === "string"
|
|
159
|
+
? parsed.error
|
|
160
|
+
: typeof parsed?.message === "string"
|
|
161
|
+
? parsed.message
|
|
162
|
+
: typeof parsed?.detail === "string"
|
|
163
|
+
? parsed.detail
|
|
164
|
+
: typeof parsed?.error_description === "string"
|
|
165
|
+
? parsed.error_description
|
|
166
|
+
: typeof parsed?.error === "object" &&
|
|
167
|
+
parsed?.error &&
|
|
168
|
+
typeof parsed.error.message ===
|
|
169
|
+
"string"
|
|
170
|
+
? String(parsed.error.message)
|
|
171
|
+
: "";
|
|
172
|
+
if (structuredMessage.trim()) {
|
|
173
|
+
value = structuredMessage;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Fall through to plain-text cleanup when the provider error isn't valid JSON.
|
|
178
|
+
}
|
|
179
|
+
}
|
|
122
180
|
const firstLine = value
|
|
123
181
|
.replace(/<br\s*\/?>/gi, "\n")
|
|
124
182
|
.split(/\r?\n/)
|
|
@@ -134,7 +192,7 @@ function toUserFacingAgentErrorMessage(value) {
|
|
|
134
192
|
return cleaned;
|
|
135
193
|
}
|
|
136
194
|
function isAgentErrorStatusValue(status) {
|
|
137
|
-
return status === "error"
|
|
195
|
+
return status === "error";
|
|
138
196
|
}
|
|
139
197
|
// Simple user message component
|
|
140
198
|
const UserMessage = ({ message }) => {
|
|
@@ -147,7 +205,7 @@ const UserMessage = ({ message }) => {
|
|
|
147
205
|
const triggerContent = triggerMatch?.[2] || "";
|
|
148
206
|
const isTriggerMessage = triggerName.length > 0;
|
|
149
207
|
if (isTriggerMessage) {
|
|
150
|
-
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: triggerContent }))] }) }));
|
|
208
|
+
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 }) }))] }) }));
|
|
151
209
|
}
|
|
152
210
|
// Parse source agent name from content if it starts with "[From ...]:"
|
|
153
211
|
// Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
|
|
@@ -175,7 +233,10 @@ const UserMessage = ({ message }) => {
|
|
|
175
233
|
message.sourceAgent?.name;
|
|
176
234
|
}
|
|
177
235
|
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
178
|
-
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 })] })] }));
|
|
236
|
+
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: _jsx(MarkdownDisplay, { source: displayContent, components: userMessageMarkdownComponents }) })] })] }));
|
|
237
|
+
};
|
|
238
|
+
const HeartbeatMessage = ({ message }) => {
|
|
239
|
+
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)) }))] }) }));
|
|
179
240
|
};
|
|
180
241
|
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
181
242
|
const extractPartialTodos = (jsonText) => {
|
|
@@ -416,12 +477,9 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
|
416
477
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
417
478
|
// First try to get todos from agent metadata (real-time updates)
|
|
418
479
|
// Server sends additionalData.todoList directly via contextChanged status
|
|
419
|
-
// Also check top-level todoList for backward compatibility with stored contexts
|
|
420
480
|
const metadataTodos = (() => {
|
|
421
481
|
try {
|
|
422
|
-
|
|
423
|
-
const todoList = agentMetadata?.additionalData?.todoList ||
|
|
424
|
-
agentMetadata?.todoList;
|
|
482
|
+
const todoList = agentMetadata?.additionalData?.todoList;
|
|
425
483
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
426
484
|
const rawItems = todoList.items
|
|
427
485
|
.map((item, idx) => ({
|
|
@@ -549,6 +607,16 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
549
607
|
// Add user message
|
|
550
608
|
groups.push({ type: "user", messages: [message] });
|
|
551
609
|
}
|
|
610
|
+
else if (message.messageType === "heartbeat" || message.role === "system") {
|
|
611
|
+
if (currentAssistantGroup.length > 0) {
|
|
612
|
+
groups.push({
|
|
613
|
+
type: "assistant-group",
|
|
614
|
+
messages: currentAssistantGroup,
|
|
615
|
+
});
|
|
616
|
+
currentAssistantGroup = [];
|
|
617
|
+
}
|
|
618
|
+
groups.push({ type: "heartbeat", messages: [message] });
|
|
619
|
+
}
|
|
552
620
|
else if (message.role === "assistant") {
|
|
553
621
|
// Add to current assistant group
|
|
554
622
|
currentAssistantGroup.push(message);
|
|
@@ -655,10 +723,10 @@ const stringifyToolField = (value) => {
|
|
|
655
723
|
const getFirstToolCallEnvelope = (data) => {
|
|
656
724
|
if (!data || typeof data !== "object")
|
|
657
725
|
return undefined;
|
|
658
|
-
const direct = data.toolCall || data.tool_call
|
|
726
|
+
const direct = data.toolCall || data.tool_call;
|
|
659
727
|
if (direct)
|
|
660
728
|
return direct;
|
|
661
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls
|
|
729
|
+
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
662
730
|
for (const candidate of arrayCandidates) {
|
|
663
731
|
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
664
732
|
return candidate[0];
|
|
@@ -668,27 +736,18 @@ const getFirstToolCallEnvelope = (data) => {
|
|
|
668
736
|
};
|
|
669
737
|
const extractToolCallFields = (data) => {
|
|
670
738
|
const envelope = getFirstToolCallEnvelope(data);
|
|
671
|
-
const functionPayload = data?.function ||
|
|
672
|
-
data?.Function ||
|
|
673
|
-
envelope?.function ||
|
|
674
|
-
envelope?.Function;
|
|
739
|
+
const functionPayload = data?.function || envelope?.function;
|
|
675
740
|
const functionName = data?.functionName ||
|
|
676
741
|
data?.name ||
|
|
677
742
|
functionPayload?.name ||
|
|
678
|
-
functionPayload?.Name ||
|
|
679
743
|
envelope?.functionName ||
|
|
680
744
|
envelope?.name ||
|
|
681
|
-
envelope?.FunctionName ||
|
|
682
|
-
envelope?.Name ||
|
|
683
745
|
"unknown";
|
|
684
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id
|
|
746
|
+
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
685
747
|
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
686
|
-
stringifyToolField(data?.Arguments) ||
|
|
687
748
|
stringifyToolField(data?.arguments) ||
|
|
688
749
|
stringifyToolField(functionPayload?.arguments) ||
|
|
689
|
-
stringifyToolField(functionPayload?.Arguments) ||
|
|
690
750
|
stringifyToolField(envelope?.functionArguments) ||
|
|
691
|
-
stringifyToolField(envelope?.Arguments) ||
|
|
692
751
|
stringifyToolField(envelope?.arguments) ||
|
|
693
752
|
"{}";
|
|
694
753
|
return {
|
|
@@ -774,6 +833,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
774
833
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
775
834
|
const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
|
|
776
835
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
836
|
+
const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
|
|
837
|
+
const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
|
|
777
838
|
// Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
|
|
778
839
|
// This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
|
|
779
840
|
useEffect(() => {
|
|
@@ -926,6 +987,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
926
987
|
if (!message?.type?.startsWith("agent:run:")) {
|
|
927
988
|
return false;
|
|
928
989
|
}
|
|
990
|
+
if (isHeartbeatRunEventMessage(message)) {
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
929
993
|
return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
|
|
930
994
|
})
|
|
931
995
|
.slice(-8)
|
|
@@ -1008,11 +1072,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1008
1072
|
value === "supervised") {
|
|
1009
1073
|
return value;
|
|
1010
1074
|
}
|
|
1011
|
-
// Backend task/spawned agents persist raw "agent", which behaves like
|
|
1012
|
-
// autonomous in the frontend's current 3-mode model.
|
|
1013
|
-
if (value === "agent") {
|
|
1014
|
-
return "autonomous";
|
|
1015
|
-
}
|
|
1016
1075
|
return null;
|
|
1017
1076
|
};
|
|
1018
1077
|
const [mode, setMode] = useState("supervised");
|
|
@@ -1029,30 +1088,33 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1029
1088
|
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1030
1089
|
const [skillsError, setSkillsError] = useState(null);
|
|
1031
1090
|
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1091
|
+
const [availableTools, setAvailableTools] = useState([]);
|
|
1032
1092
|
const [operationAllowances, setOperationAllowances] = useState({
|
|
1033
1093
|
sitecore: [],
|
|
1034
1094
|
filesystem: [],
|
|
1035
1095
|
});
|
|
1036
1096
|
const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
|
|
1097
|
+
const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
|
|
1037
1098
|
const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
|
|
1038
1099
|
const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
|
|
1100
|
+
const [availableToolsError, setAvailableToolsError] = useState(null);
|
|
1039
1101
|
const [operationAllowancesError, setOperationAllowancesError] = useState(null);
|
|
1102
|
+
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1040
1103
|
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1041
1104
|
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1105
|
+
const isNewAgent = agent?.status === "new";
|
|
1042
1106
|
const hasSpawnedAgents = useMemo(() => {
|
|
1043
1107
|
if (!agentMetadata)
|
|
1044
1108
|
return false;
|
|
1045
|
-
const childAgents = agentMetadata?.
|
|
1046
|
-
agentMetadata?.childAgents;
|
|
1109
|
+
const childAgents = agentMetadata?.childAgents;
|
|
1047
1110
|
if (!Array.isArray(childAgents) || childAgents.length === 0)
|
|
1048
1111
|
return false;
|
|
1049
|
-
return childAgents.some((a) => a != null && typeof a === "object" &&
|
|
1112
|
+
return childAgents.some((a) => a != null && typeof a === "object" && a.agentId);
|
|
1050
1113
|
}, [agentMetadata]);
|
|
1051
1114
|
const hasTodoContent = useMemo(() => {
|
|
1052
1115
|
const metadataTodos = (() => {
|
|
1053
1116
|
try {
|
|
1054
|
-
const todoList = agentMetadata?.additionalData?.todoList
|
|
1055
|
-
agentMetadata?.todoList;
|
|
1117
|
+
const todoList = agentMetadata?.additionalData?.todoList;
|
|
1056
1118
|
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
1057
1119
|
const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
|
|
1058
1120
|
return raw.length > 0;
|
|
@@ -1165,6 +1227,95 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1165
1227
|
active = false;
|
|
1166
1228
|
};
|
|
1167
1229
|
}, []);
|
|
1230
|
+
const modeOptions = useMemo(() => [
|
|
1231
|
+
{
|
|
1232
|
+
value: "supervised",
|
|
1233
|
+
label: "Supervised",
|
|
1234
|
+
className: "!bg-amber-50 text-amber-900 data-[selected=true]:!bg-amber-200 data-[selected=true]:text-amber-950",
|
|
1235
|
+
description: "Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside context requires approval.",
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
value: "autonomous",
|
|
1239
|
+
label: "Autonomous",
|
|
1240
|
+
className: "!bg-red-50 text-red-900 data-[selected=true]:!bg-red-200 data-[selected=true]:text-red-950",
|
|
1241
|
+
description: "Full tool access; can write across the site/project only limited by user permissions.",
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
value: "read-only",
|
|
1245
|
+
label: "Read-Only",
|
|
1246
|
+
className: "!bg-green-50 text-green-900 data-[selected=true]:!bg-green-200 data-[selected=true]:text-green-950",
|
|
1247
|
+
description: "Limited tool access as configured by the profile (Ask Mode Tools).",
|
|
1248
|
+
},
|
|
1249
|
+
], []);
|
|
1250
|
+
const profileOptions = useMemo(() => {
|
|
1251
|
+
// Get the current agent's profile ID
|
|
1252
|
+
const currentProfileId = (agent?.profileId || agentStub.profileId)?.toLowerCase();
|
|
1253
|
+
// Filter profiles: show non-hidden ones + always include current profile
|
|
1254
|
+
const filteredProfiles = profiles?.filter((p) => !p.hiddenFromOverview || p.id.toLowerCase() === currentProfileId) || [];
|
|
1255
|
+
return filteredProfiles.map((p) => ({
|
|
1256
|
+
value: p.id,
|
|
1257
|
+
label: p.name,
|
|
1258
|
+
}));
|
|
1259
|
+
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
1260
|
+
const modelOptions = useMemo(() => (activeProfile?.models?.map((m) => ({
|
|
1261
|
+
value: m.id,
|
|
1262
|
+
label: m.name,
|
|
1263
|
+
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1264
|
+
const metadataSelectedSkillIds = useMemo(() => {
|
|
1265
|
+
const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
|
|
1266
|
+
if (!Array.isArray(rawSkillIds)) {
|
|
1267
|
+
return [];
|
|
1268
|
+
}
|
|
1269
|
+
return rawSkillIds
|
|
1270
|
+
.map((x) => String(x || "").trim())
|
|
1271
|
+
.filter((x) => x.length > 0);
|
|
1272
|
+
}, [agentMetadata]);
|
|
1273
|
+
const backendAssignedSkillIds = useMemo(() => {
|
|
1274
|
+
const rawSkillIds = agent?.assignedSkillIds ?? [];
|
|
1275
|
+
if (!Array.isArray(rawSkillIds)) {
|
|
1276
|
+
return [];
|
|
1277
|
+
}
|
|
1278
|
+
return rawSkillIds
|
|
1279
|
+
.map((x) => String(x || "").trim())
|
|
1280
|
+
.filter((x) => x.length > 0);
|
|
1281
|
+
}, [agent]);
|
|
1282
|
+
const preloadedSkillIds = useMemo(() => {
|
|
1283
|
+
const rawSkillIds = activeProfile?.preloadSkills ?? [];
|
|
1284
|
+
if (!Array.isArray(rawSkillIds)) {
|
|
1285
|
+
return [];
|
|
1286
|
+
}
|
|
1287
|
+
return rawSkillIds
|
|
1288
|
+
.map((skill) => String(skill?.id || "").trim())
|
|
1289
|
+
.filter((id) => id.length > 0);
|
|
1290
|
+
}, [activeProfile?.preloadSkills]);
|
|
1291
|
+
const autoAssignedSkillIds = useMemo(() => {
|
|
1292
|
+
const all = isNewAgent
|
|
1293
|
+
? [...preloadedSkillIds, ...backendAssignedSkillIds]
|
|
1294
|
+
: backendAssignedSkillIds;
|
|
1295
|
+
const seen = new Set();
|
|
1296
|
+
const unique = [];
|
|
1297
|
+
for (const id of all) {
|
|
1298
|
+
const key = id.toLowerCase();
|
|
1299
|
+
if (seen.has(key))
|
|
1300
|
+
continue;
|
|
1301
|
+
seen.add(key);
|
|
1302
|
+
unique.push(id);
|
|
1303
|
+
}
|
|
1304
|
+
return unique;
|
|
1305
|
+
}, [backendAssignedSkillIds, isNewAgent, preloadedSkillIds]);
|
|
1306
|
+
const selectedSkillIds = useMemo(() => {
|
|
1307
|
+
const all = [...autoAssignedSkillIds, ...metadataSelectedSkillIds];
|
|
1308
|
+
const seen = new Set();
|
|
1309
|
+
const unique = [];
|
|
1310
|
+
for (const id of all) {
|
|
1311
|
+
const key = id.toLowerCase();
|
|
1312
|
+
if (seen.has(key))
|
|
1313
|
+
continue;
|
|
1314
|
+
seen.add(key);
|
|
1315
|
+
unique.push(id);
|
|
1316
|
+
}
|
|
1317
|
+
return unique;
|
|
1318
|
+
}, [autoAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1168
1319
|
useEffect(() => {
|
|
1169
1320
|
let active = true;
|
|
1170
1321
|
if (!showAgentSettings) {
|
|
@@ -1176,6 +1327,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1176
1327
|
setTriggerSubscriptions([]);
|
|
1177
1328
|
setTriggerSubscriptionsLoading(false);
|
|
1178
1329
|
setTriggerSubscriptionsError(null);
|
|
1330
|
+
setAvailableTools([]);
|
|
1331
|
+
setAvailableToolsLoading(false);
|
|
1332
|
+
setAvailableToolsError(null);
|
|
1179
1333
|
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1180
1334
|
setOperationAllowancesLoading(false);
|
|
1181
1335
|
setOperationAllowancesError(null);
|
|
@@ -1223,83 +1377,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1223
1377
|
}
|
|
1224
1378
|
}
|
|
1225
1379
|
};
|
|
1380
|
+
const loadAvailableTools = async () => {
|
|
1381
|
+
try {
|
|
1382
|
+
setAvailableToolsLoading(true);
|
|
1383
|
+
setAvailableToolsError(null);
|
|
1384
|
+
const tools = await getAgentAvailableTools(agent.id);
|
|
1385
|
+
if (active) {
|
|
1386
|
+
setAvailableTools(tools);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
catch (e) {
|
|
1390
|
+
if (active) {
|
|
1391
|
+
setAvailableToolsError(e?.message || "Failed to load available tools");
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
finally {
|
|
1395
|
+
if (active) {
|
|
1396
|
+
setAvailableToolsLoading(false);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1226
1400
|
void loadTriggerSubscriptions();
|
|
1401
|
+
void loadAvailableTools();
|
|
1227
1402
|
void loadOperationAllowances();
|
|
1228
1403
|
return () => {
|
|
1229
1404
|
active = false;
|
|
1230
1405
|
};
|
|
1231
|
-
}, [
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
{
|
|
1240
|
-
value: "autonomous",
|
|
1241
|
-
label: "Autonomous",
|
|
1242
|
-
className: "!bg-red-50 text-red-900 data-[selected=true]:!bg-red-200 data-[selected=true]:text-red-950",
|
|
1243
|
-
description: "Full tool access; can write across the site/project only limited by user permissions.",
|
|
1244
|
-
},
|
|
1245
|
-
{
|
|
1246
|
-
value: "read-only",
|
|
1247
|
-
label: "Read-Only",
|
|
1248
|
-
className: "!bg-green-50 text-green-900 data-[selected=true]:!bg-green-200 data-[selected=true]:text-green-950",
|
|
1249
|
-
description: "Limited tool access as configured by the profile (Ask Mode Tools).",
|
|
1250
|
-
},
|
|
1251
|
-
], []);
|
|
1252
|
-
const profileOptions = useMemo(() => {
|
|
1253
|
-
// Get the current agent's profile ID
|
|
1254
|
-
const currentProfileId = (agent?.profileId || agentStub.profileId)?.toLowerCase();
|
|
1255
|
-
// Filter profiles: show non-hidden ones + always include current profile
|
|
1256
|
-
const filteredProfiles = profiles?.filter((p) => !p.hiddenFromOverview || p.id.toLowerCase() === currentProfileId) || [];
|
|
1257
|
-
return filteredProfiles.map((p) => ({
|
|
1258
|
-
value: p.id,
|
|
1259
|
-
label: p.name,
|
|
1260
|
-
}));
|
|
1261
|
-
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
1262
|
-
const modelOptions = useMemo(() => (activeProfile?.models?.map((m) => ({
|
|
1263
|
-
value: m.id,
|
|
1264
|
-
label: m.name,
|
|
1265
|
-
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1266
|
-
const metadataSelectedSkillIds = useMemo(() => {
|
|
1267
|
-
const rawSkillIds = agentMetadata?.additionalData?.skillIds ??
|
|
1268
|
-
agentMetadata?.AdditionalData?.skillIds ??
|
|
1269
|
-
agentMetadata?.skillIds ??
|
|
1270
|
-
agentMetadata?.SkillIds ??
|
|
1271
|
-
[];
|
|
1272
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1273
|
-
return [];
|
|
1274
|
-
}
|
|
1275
|
-
return rawSkillIds
|
|
1276
|
-
.map((x) => String(x || "").trim())
|
|
1277
|
-
.filter((x) => x.length > 0);
|
|
1278
|
-
}, [agentMetadata]);
|
|
1279
|
-
const backendAssignedSkillIds = useMemo(() => {
|
|
1280
|
-
const rawSkillIds = agent?.assignedSkillIds ??
|
|
1281
|
-
agent?.AssignedSkillIds ??
|
|
1282
|
-
[];
|
|
1283
|
-
if (!Array.isArray(rawSkillIds)) {
|
|
1284
|
-
return [];
|
|
1285
|
-
}
|
|
1286
|
-
return rawSkillIds
|
|
1287
|
-
.map((x) => String(x || "").trim())
|
|
1288
|
-
.filter((x) => x.length > 0);
|
|
1289
|
-
}, [agent]);
|
|
1290
|
-
const selectedSkillIds = useMemo(() => {
|
|
1291
|
-
const all = [...backendAssignedSkillIds, ...metadataSelectedSkillIds];
|
|
1292
|
-
const seen = new Set();
|
|
1293
|
-
const unique = [];
|
|
1294
|
-
for (const id of all) {
|
|
1295
|
-
const key = id.toLowerCase();
|
|
1296
|
-
if (seen.has(key))
|
|
1297
|
-
continue;
|
|
1298
|
-
seen.add(key);
|
|
1299
|
-
unique.push(id);
|
|
1300
|
-
}
|
|
1301
|
-
return unique;
|
|
1302
|
-
}, [backendAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1406
|
+
}, [
|
|
1407
|
+
showAgentSettings,
|
|
1408
|
+
agent?.id,
|
|
1409
|
+
agent?.status,
|
|
1410
|
+
agent?.profileId,
|
|
1411
|
+
mode,
|
|
1412
|
+
selectedSkillIds,
|
|
1413
|
+
]);
|
|
1303
1414
|
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
1304
1415
|
const allowanceGroups = useMemo(() => [
|
|
1305
1416
|
{
|
|
@@ -1317,18 +1428,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1317
1428
|
operationAllowances.filesystem.length > 0, [operationAllowances]);
|
|
1318
1429
|
const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
|
|
1319
1430
|
operationAllowances.filesystem.length, [operationAllowances]);
|
|
1320
|
-
const
|
|
1321
|
-
const ids =
|
|
1322
|
-
|
|
1323
|
-
|
|
1431
|
+
const listedProfileSkillIdSet = useMemo(() => {
|
|
1432
|
+
const ids = [
|
|
1433
|
+
...(activeProfile?.availableSkills ?? []),
|
|
1434
|
+
...(activeProfile?.allowedSkills ?? []),
|
|
1435
|
+
]
|
|
1436
|
+
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1437
|
+
.filter((id) => id.length > 0);
|
|
1324
1438
|
return new Set(ids);
|
|
1325
|
-
}, [activeProfile?.allowedSkills]);
|
|
1439
|
+
}, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
|
|
1326
1440
|
const profileFilteredSkills = useMemo(() => {
|
|
1327
|
-
if (
|
|
1328
|
-
return
|
|
1441
|
+
if (listedProfileSkillIdSet.size === 0) {
|
|
1442
|
+
return [];
|
|
1329
1443
|
}
|
|
1330
|
-
return availableSkills.filter((skill) =>
|
|
1331
|
-
}, [availableSkills,
|
|
1444
|
+
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1445
|
+
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1332
1446
|
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1333
1447
|
const availableSkillIdSet = useMemo(() => new Set(availableSkills.map((skill) => skill.id.toLowerCase())), [availableSkills]);
|
|
1334
1448
|
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
@@ -1336,7 +1450,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1336
1450
|
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
1337
1451
|
.filter((s) => !!s), [availableSkills, selectedSkillIds]);
|
|
1338
1452
|
const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
|
|
1339
|
-
const
|
|
1453
|
+
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1454
|
+
const previewAvailableTools = useMemo(() => {
|
|
1455
|
+
const baseToolNames = mode === "read-only"
|
|
1456
|
+
? activeProfile?.readOnlyToolNames ?? []
|
|
1457
|
+
: activeProfile?.allowedToolNames ?? [];
|
|
1458
|
+
const toolNames = new Set();
|
|
1459
|
+
for (const toolName of baseToolNames) {
|
|
1460
|
+
const normalized = String(toolName || "").trim();
|
|
1461
|
+
if (normalized) {
|
|
1462
|
+
toolNames.add(normalized);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
for (const skill of selectedSkills) {
|
|
1466
|
+
for (const toolName of skill.additionalAllowedTools ?? []) {
|
|
1467
|
+
const normalized = String(toolName || "").trim();
|
|
1468
|
+
if (normalized) {
|
|
1469
|
+
toolNames.add(normalized);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
|
|
1474
|
+
}, [
|
|
1475
|
+
activeProfile?.allowedToolNames,
|
|
1476
|
+
activeProfile?.readOnlyToolNames,
|
|
1477
|
+
mode,
|
|
1478
|
+
selectedSkills,
|
|
1479
|
+
]);
|
|
1480
|
+
const displayedAvailableTools = isNewAgent
|
|
1481
|
+
? previewAvailableTools
|
|
1482
|
+
: availableTools;
|
|
1483
|
+
const displayedAvailableToolsLoading = isNewAgent
|
|
1484
|
+
? false
|
|
1485
|
+
: availableToolsLoading;
|
|
1486
|
+
const displayedAvailableToolsError = isNewAgent ? null : availableToolsError;
|
|
1340
1487
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1341
1488
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1342
1489
|
try {
|
|
@@ -1365,9 +1512,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1365
1512
|
if (!agent?.id)
|
|
1366
1513
|
return;
|
|
1367
1514
|
const current = agentMetadata || {};
|
|
1368
|
-
const currentAdditionalData = current.additionalData ||
|
|
1369
|
-
current.AdditionalData ||
|
|
1370
|
-
{};
|
|
1515
|
+
const currentAdditionalData = current.additionalData || {};
|
|
1371
1516
|
const nextAdditionalData = {
|
|
1372
1517
|
...currentAdditionalData,
|
|
1373
1518
|
};
|
|
@@ -1382,11 +1527,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1382
1527
|
};
|
|
1383
1528
|
if (Object.keys(nextAdditionalData).length > 0) {
|
|
1384
1529
|
next.additionalData = nextAdditionalData;
|
|
1385
|
-
delete next.AdditionalData;
|
|
1386
1530
|
}
|
|
1387
1531
|
else {
|
|
1388
1532
|
delete next.additionalData;
|
|
1389
|
-
delete next.AdditionalData;
|
|
1390
1533
|
}
|
|
1391
1534
|
try {
|
|
1392
1535
|
if (agent.status === "new") {
|
|
@@ -1542,7 +1685,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1542
1685
|
// Auto-focus terminal input on mount
|
|
1543
1686
|
useEffect(() => {
|
|
1544
1687
|
if (textareaRef.current) {
|
|
1545
|
-
|
|
1688
|
+
try {
|
|
1689
|
+
textareaRef.current.focus({ preventScroll: true });
|
|
1690
|
+
}
|
|
1691
|
+
catch {
|
|
1692
|
+
textareaRef.current.focus();
|
|
1693
|
+
}
|
|
1546
1694
|
}
|
|
1547
1695
|
}, []);
|
|
1548
1696
|
// Start voice recognition
|
|
@@ -1805,6 +1953,55 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1805
1953
|
toolCalls: [],
|
|
1806
1954
|
};
|
|
1807
1955
|
}, [agent, agentStub.id]);
|
|
1956
|
+
const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
|
|
1957
|
+
const handleHeartbeatMessage = useCallback((message) => {
|
|
1958
|
+
const heartbeatText = (message.data?.message ||
|
|
1959
|
+
message.data?.Message ||
|
|
1960
|
+
"Still working on your request. This is taking a little longer than usual.").trim() ||
|
|
1961
|
+
"Still working on your request. This is taking a little longer than usual.";
|
|
1962
|
+
const createdDate = message.timestamp || new Date().toISOString();
|
|
1963
|
+
const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
|
|
1964
|
+
setMessages((prev) => {
|
|
1965
|
+
const withoutHeartbeats = stripHeartbeatMessages(prev);
|
|
1966
|
+
const updated = [
|
|
1967
|
+
...withoutHeartbeats,
|
|
1968
|
+
{
|
|
1969
|
+
id: heartbeatId,
|
|
1970
|
+
agentId: agent?.id || agentStub.id,
|
|
1971
|
+
messageIndex: -1,
|
|
1972
|
+
role: "system",
|
|
1973
|
+
content: heartbeatText,
|
|
1974
|
+
name: "system",
|
|
1975
|
+
messageType: "heartbeat",
|
|
1976
|
+
isCompleted: true,
|
|
1977
|
+
model: "",
|
|
1978
|
+
tokensUsed: 0,
|
|
1979
|
+
inputTokens: 0,
|
|
1980
|
+
outputTokens: 0,
|
|
1981
|
+
cachedInputTokens: 0,
|
|
1982
|
+
inputTokenCost: 0,
|
|
1983
|
+
outputTokenCost: 0,
|
|
1984
|
+
cachedInputTokenCost: 0,
|
|
1985
|
+
totalCost: 0,
|
|
1986
|
+
currency: "USD",
|
|
1987
|
+
createdDate,
|
|
1988
|
+
toolCalls: [],
|
|
1989
|
+
},
|
|
1990
|
+
];
|
|
1991
|
+
messagesRef.current = updated;
|
|
1992
|
+
return updated;
|
|
1993
|
+
});
|
|
1994
|
+
}, [agent?.id, agentStub.id, stripHeartbeatMessages]);
|
|
1995
|
+
const clearHeartbeatMessages = useCallback(() => {
|
|
1996
|
+
setMessages((prev) => {
|
|
1997
|
+
const updated = stripHeartbeatMessages(prev);
|
|
1998
|
+
if (updated.length === prev.length) {
|
|
1999
|
+
return prev;
|
|
2000
|
+
}
|
|
2001
|
+
messagesRef.current = updated;
|
|
2002
|
+
return updated;
|
|
2003
|
+
});
|
|
2004
|
+
}, [stripHeartbeatMessages]);
|
|
1808
2005
|
const handleContentChunk = useCallback((message, agentData) => {
|
|
1809
2006
|
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
1810
2007
|
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
@@ -1823,7 +2020,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1823
2020
|
// If the agent isn't currently running (e.g., we switched tabs after the run
|
|
1824
2021
|
// completed), skip creating a new streaming message to avoid duplicates.
|
|
1825
2022
|
const currentAgentStatus = (agentData || agent)?.status;
|
|
1826
|
-
const isAgentRunning = currentAgentStatus === "running"
|
|
2023
|
+
const isAgentRunning = currentAgentStatus === "running";
|
|
1827
2024
|
if (!isAgentRunning) {
|
|
1828
2025
|
return;
|
|
1829
2026
|
}
|
|
@@ -2630,29 +2827,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2630
2827
|
if (!contextJson)
|
|
2631
2828
|
return null;
|
|
2632
2829
|
const parsedContext = JSON.parse(contextJson);
|
|
2633
|
-
// Context is stored as flat structure with top-level properties.
|
|
2634
|
-
// Due to C# [JsonExtensionData], AdditionalData entries can be serialized at top level.
|
|
2635
|
-
// Normalize well-known extension keys back under additionalData for consistent UI behavior.
|
|
2636
2830
|
if (parsedContext && typeof parsedContext === "object") {
|
|
2637
|
-
|
|
2638
|
-
const additionalData = {
|
|
2639
|
-
...(normalized.additionalData || {}),
|
|
2640
|
-
};
|
|
2641
|
-
let additionalDataUpdated = false;
|
|
2642
|
-
// If todoList is at top level but not in additionalData, move it
|
|
2643
|
-
if (normalized.todoList && !normalized.additionalData?.todoList) {
|
|
2644
|
-
additionalData.todoList = normalized.todoList;
|
|
2645
|
-
additionalDataUpdated = true;
|
|
2646
|
-
}
|
|
2647
|
-
// Planner/task skill selections may also be flattened from JsonExtensionData.
|
|
2648
|
-
if (normalized.skillIds && !normalized.additionalData?.skillIds) {
|
|
2649
|
-
additionalData.skillIds = normalized.skillIds;
|
|
2650
|
-
additionalDataUpdated = true;
|
|
2651
|
-
}
|
|
2652
|
-
if (additionalDataUpdated) {
|
|
2653
|
-
normalized.additionalData = additionalData;
|
|
2654
|
-
}
|
|
2655
|
-
return normalized;
|
|
2831
|
+
return parsedContext;
|
|
2656
2832
|
}
|
|
2657
2833
|
return null;
|
|
2658
2834
|
}
|
|
@@ -2738,7 +2914,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2738
2914
|
return;
|
|
2739
2915
|
}
|
|
2740
2916
|
// Check if cost limit exceeded based on status or cost values
|
|
2741
|
-
const statusIndicatesLimit = agent.status === "costLimitReached"
|
|
2917
|
+
const statusIndicatesLimit = agent.status === "costLimitReached";
|
|
2742
2918
|
// Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
|
|
2743
2919
|
const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
|
|
2744
2920
|
const costExceedsLimit = agent.costLimit &&
|
|
@@ -2823,9 +2999,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2823
2999
|
// Handle agent:profile:switched (profile changed via switch-profile function)
|
|
2824
3000
|
if (messageType === "agent:profile:switched") {
|
|
2825
3001
|
const payload = message.payload || {};
|
|
2826
|
-
const switchedAgentId = payload.agentId
|
|
2827
|
-
const newProfileId = payload.newProfileId
|
|
2828
|
-
const newProfileName = payload.newProfileName
|
|
3002
|
+
const switchedAgentId = payload.agentId;
|
|
3003
|
+
const newProfileId = payload.newProfileId;
|
|
3004
|
+
const newProfileName = payload.newProfileName;
|
|
2829
3005
|
if (switchedAgentId === agent.id && newProfileId) {
|
|
2830
3006
|
// Update the agent's profile
|
|
2831
3007
|
setAgent((prev) => {
|
|
@@ -2866,7 +3042,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2866
3042
|
return;
|
|
2867
3043
|
}
|
|
2868
3044
|
// For other agent messages, check if this is for our agent
|
|
2869
|
-
const agentId = message.payload?.agentId
|
|
3045
|
+
const agentId = message.payload?.agentId;
|
|
2870
3046
|
if (agentId !== agent.id) {
|
|
2871
3047
|
return;
|
|
2872
3048
|
}
|
|
@@ -3008,15 +3184,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3008
3184
|
}
|
|
3009
3185
|
// Always allow ContextUpdate messages (metadata updates) regardless of agent status
|
|
3010
3186
|
const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
|
|
3187
|
+
const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
|
|
3188
|
+
const shouldClearHeartbeat = type === "ContentChunk" ||
|
|
3189
|
+
type === "contentChunk" ||
|
|
3190
|
+
type === "ToolCall" ||
|
|
3191
|
+
type === "toolCall" ||
|
|
3192
|
+
type === "ToolResult" ||
|
|
3193
|
+
type === "toolResult";
|
|
3011
3194
|
// Allow deltas if the agent is running OR if we already have an active streaming message
|
|
3012
3195
|
// OR if the server provided a messageId for targeted updates.
|
|
3013
3196
|
// This avoids dropping early deltas that may arrive before the 'running' status update.
|
|
3014
3197
|
// ContextUpdate messages are always allowed as they're metadata updates, not content.
|
|
3015
3198
|
const isRunning = agent.status === "running" || agent.status === 1;
|
|
3016
3199
|
const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3017
|
-
const hasMessageId = !!message?.payload?.data?.messageId
|
|
3018
|
-
|
|
3019
|
-
|
|
3200
|
+
const hasMessageId = !!message?.payload?.data?.messageId;
|
|
3201
|
+
if (!isContextUpdate &&
|
|
3202
|
+
!isHeartbeat &&
|
|
3203
|
+
!isRunning &&
|
|
3204
|
+
!hasStreaming &&
|
|
3205
|
+
!hasMessageId) {
|
|
3020
3206
|
return; // ignore only if we cannot safely target an existing streaming message
|
|
3021
3207
|
}
|
|
3022
3208
|
// Deduplicate by sequence
|
|
@@ -3033,6 +3219,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3033
3219
|
cost,
|
|
3034
3220
|
timestamp: new Date().toISOString(),
|
|
3035
3221
|
};
|
|
3222
|
+
if (shouldClearHeartbeat) {
|
|
3223
|
+
clearHeartbeatMessages();
|
|
3224
|
+
}
|
|
3036
3225
|
if (type === "ContentChunk" || type === "contentChunk") {
|
|
3037
3226
|
handleContentChunk(agentStreamMessage, agent);
|
|
3038
3227
|
}
|
|
@@ -3042,6 +3231,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3042
3231
|
else if (type === "ToolResult" || type === "toolResult") {
|
|
3043
3232
|
handleToolResult(agentStreamMessage, agent);
|
|
3044
3233
|
}
|
|
3234
|
+
else if (type === "Heartbeat" || type === "heartbeat") {
|
|
3235
|
+
handleHeartbeatMessage(agentStreamMessage);
|
|
3236
|
+
}
|
|
3045
3237
|
else if (type === "ContextUpdate" || type === "contextUpdate") {
|
|
3046
3238
|
// Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
|
|
3047
3239
|
const contextData = data;
|
|
@@ -3051,10 +3243,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3051
3243
|
const current = (prev || {});
|
|
3052
3244
|
const updated = { ...current };
|
|
3053
3245
|
// Merge additionalData if present (deep merge to preserve existing values)
|
|
3054
|
-
if (contextData.additionalData
|
|
3055
|
-
const sourceAdditionalData = contextData.additionalData ||
|
|
3056
|
-
contextData.AdditionalData ||
|
|
3057
|
-
{};
|
|
3246
|
+
if (contextData.additionalData) {
|
|
3247
|
+
const sourceAdditionalData = contextData.additionalData || {};
|
|
3058
3248
|
updated.additionalData = {
|
|
3059
3249
|
...(current.additionalData || {}),
|
|
3060
3250
|
...sourceAdditionalData,
|
|
@@ -3062,10 +3252,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3062
3252
|
}
|
|
3063
3253
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3064
3254
|
// Backend sends the complete updated list, not just additions
|
|
3065
|
-
if (contextData.
|
|
3066
|
-
const childAgents = contextData.
|
|
3255
|
+
if (contextData.childAgents) {
|
|
3256
|
+
const childAgents = contextData.childAgents;
|
|
3067
3257
|
if (Array.isArray(childAgents)) {
|
|
3068
|
-
updated.
|
|
3258
|
+
updated.childAgents = childAgents;
|
|
3069
3259
|
}
|
|
3070
3260
|
}
|
|
3071
3261
|
return updated;
|
|
@@ -3091,14 +3281,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3091
3281
|
// Route based on statusData.state
|
|
3092
3282
|
try {
|
|
3093
3283
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3094
|
-
const normalizedStatus = statusData?.state ||
|
|
3095
|
-
statusData?.
|
|
3096
|
-
|
|
3097
|
-
if (normalizedStatus === "Cancelled" ||
|
|
3098
|
-
normalizedStatus === "canceled" ||
|
|
3099
|
-
normalizedStatus === "stopped" ||
|
|
3100
|
-
normalizedStatus === "Stopped") {
|
|
3284
|
+
const normalizedStatus = parseAgentStatus(statusData?.state) ||
|
|
3285
|
+
parseAgentStatus(statusData?.status);
|
|
3286
|
+
if (normalizedStatus === "idle") {
|
|
3101
3287
|
// Stop indicators and mark any in-progress streaming messages as completed
|
|
3288
|
+
clearHeartbeatMessages();
|
|
3102
3289
|
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3103
3290
|
setIsWaitingForResponse(false);
|
|
3104
3291
|
isWaitingRef.current = false;
|
|
@@ -3201,6 +3388,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3201
3388
|
return;
|
|
3202
3389
|
}
|
|
3203
3390
|
if (statusData?.state === "ToolApprovalsRequired") {
|
|
3391
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3204
3392
|
const msgId = statusData.messageId;
|
|
3205
3393
|
const ids = statusData.toolCallIds || [];
|
|
3206
3394
|
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
@@ -3233,23 +3421,39 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3233
3421
|
return;
|
|
3234
3422
|
}
|
|
3235
3423
|
// Handle waiting states explicitly
|
|
3236
|
-
if (
|
|
3237
|
-
|
|
3424
|
+
if (normalizedStatus === "waitingForApproval") {
|
|
3425
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3238
3426
|
setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
|
|
3239
3427
|
setIsConnecting(false);
|
|
3240
3428
|
setIsWaitingForResponse(false);
|
|
3241
3429
|
setIsAgentThinking(false);
|
|
3242
3430
|
return;
|
|
3243
3431
|
}
|
|
3244
|
-
if (
|
|
3245
|
-
statusData?.
|
|
3246
|
-
|
|
3432
|
+
if (normalizedStatus === "waitingForInput") {
|
|
3433
|
+
const dialogType = typeof statusData?.dialogType === "string"
|
|
3434
|
+
? statusData.dialogType
|
|
3435
|
+
: null;
|
|
3436
|
+
const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
3437
|
+
dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
3438
|
+
setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
|
|
3439
|
+
setAgent((prev) => prev
|
|
3440
|
+
? {
|
|
3441
|
+
...prev,
|
|
3442
|
+
status: "waitingForInput",
|
|
3443
|
+
statusMessage: isBrowserCaptureWait &&
|
|
3444
|
+
typeof statusData?.title === "string" &&
|
|
3445
|
+
statusData.title.trim()
|
|
3446
|
+
? statusData.title.trim()
|
|
3447
|
+
: prev.statusMessage,
|
|
3448
|
+
}
|
|
3449
|
+
: prev);
|
|
3247
3450
|
setIsConnecting(false);
|
|
3248
3451
|
setIsWaitingForResponse(false);
|
|
3249
3452
|
setIsAgentThinking(false);
|
|
3250
3453
|
return;
|
|
3251
3454
|
}
|
|
3252
|
-
if (
|
|
3455
|
+
if (normalizedStatus === "costLimitReached") {
|
|
3456
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3253
3457
|
const totalCost = Number(statusData.totalCost) || 0;
|
|
3254
3458
|
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
3255
3459
|
setCostLimitExceeded({
|
|
@@ -3267,7 +3471,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3267
3471
|
// Server sends additionalData directly (with todoList inside)
|
|
3268
3472
|
// Also may include ChildAgents for spawned agents
|
|
3269
3473
|
try {
|
|
3270
|
-
const serverAdditionalData = statusData.additionalData ||
|
|
3474
|
+
const serverAdditionalData = statusData.additionalData || {};
|
|
3271
3475
|
setAgentMetadata((prev) => {
|
|
3272
3476
|
const current = (prev || {});
|
|
3273
3477
|
const updated = { ...current };
|
|
@@ -3281,10 +3485,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3281
3485
|
}
|
|
3282
3486
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3283
3487
|
// Backend sends the complete updated list, not just additions
|
|
3284
|
-
if (statusData.
|
|
3285
|
-
const childAgents = statusData.
|
|
3488
|
+
if (statusData.childAgents) {
|
|
3489
|
+
const childAgents = statusData.childAgents;
|
|
3286
3490
|
if (Array.isArray(childAgents)) {
|
|
3287
|
-
updated.
|
|
3491
|
+
updated.childAgents = childAgents;
|
|
3288
3492
|
}
|
|
3289
3493
|
}
|
|
3290
3494
|
return updated;
|
|
@@ -3296,10 +3500,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3296
3500
|
return;
|
|
3297
3501
|
}
|
|
3298
3502
|
// Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
|
|
3299
|
-
if (normalizedStatus === "completed"
|
|
3300
|
-
normalizedStatus === "Completed") {
|
|
3503
|
+
if (normalizedStatus === "completed") {
|
|
3301
3504
|
// Reset deduplication for the next run
|
|
3302
3505
|
lastSeqRef.current = 0;
|
|
3506
|
+
clearHeartbeatMessages();
|
|
3303
3507
|
// Mark the last assistant message as completed
|
|
3304
3508
|
setMessages((prev) => {
|
|
3305
3509
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3322,20 +3526,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3322
3526
|
setIsAgentThinking(false);
|
|
3323
3527
|
return;
|
|
3324
3528
|
}
|
|
3325
|
-
// Handle "Idle" state - agent has finished processing
|
|
3326
|
-
if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
|
|
3327
|
-
// Update agent status to idle
|
|
3328
|
-
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3329
|
-
setIsWaitingForResponse(false);
|
|
3330
|
-
isWaitingRef.current = false;
|
|
3331
|
-
setIsConnecting(false);
|
|
3332
|
-
shouldCreateNewMessage.current = false;
|
|
3333
|
-
setIsAgentThinking(false);
|
|
3334
|
-
return;
|
|
3335
|
-
}
|
|
3336
3529
|
// Handle "Running" state - agent is actively processing
|
|
3337
|
-
if (normalizedStatus === "running"
|
|
3338
|
-
normalizedStatus === "Running") {
|
|
3530
|
+
if (normalizedStatus === "running") {
|
|
3339
3531
|
// Update agent status to running
|
|
3340
3532
|
setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
|
|
3341
3533
|
setIsWaitingForResponse(true);
|
|
@@ -3344,8 +3536,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3344
3536
|
return;
|
|
3345
3537
|
}
|
|
3346
3538
|
// Handle "Error" state
|
|
3347
|
-
if (normalizedStatus === "error"
|
|
3539
|
+
if (normalizedStatus === "error") {
|
|
3348
3540
|
const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
|
|
3541
|
+
clearHeartbeatMessages();
|
|
3349
3542
|
setAgent((prev) => prev
|
|
3350
3543
|
? { ...prev, status: "error", statusMessage: errorMsg }
|
|
3351
3544
|
: prev);
|
|
@@ -3365,6 +3558,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3365
3558
|
if (messageType === "agent:run:complete") {
|
|
3366
3559
|
// Reset deduplication for the next run
|
|
3367
3560
|
lastSeqRef.current = 0;
|
|
3561
|
+
clearHeartbeatMessages();
|
|
3368
3562
|
// Mark the last assistant message as completed
|
|
3369
3563
|
setMessages((prev) => {
|
|
3370
3564
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3391,6 +3585,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3391
3585
|
if (messageType === "agent:run:error") {
|
|
3392
3586
|
const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
|
|
3393
3587
|
"AI could not complete this request.";
|
|
3588
|
+
clearHeartbeatMessages();
|
|
3394
3589
|
// Reset deduplication for the next run after an error
|
|
3395
3590
|
lastSeqRef.current = 0;
|
|
3396
3591
|
setError(errorMsg);
|
|
@@ -3405,7 +3600,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3405
3600
|
}
|
|
3406
3601
|
}, [
|
|
3407
3602
|
agent,
|
|
3603
|
+
clearHeartbeatMessages,
|
|
3408
3604
|
handleContentChunk,
|
|
3605
|
+
handleHeartbeatMessage,
|
|
3409
3606
|
handleToolCall,
|
|
3410
3607
|
handleToolResult,
|
|
3411
3608
|
onAgentUpdate,
|
|
@@ -3611,9 +3808,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3611
3808
|
}
|
|
3612
3809
|
setTimeout(() => {
|
|
3613
3810
|
if (request.dialogType === "questionnaire") {
|
|
3614
|
-
|
|
3615
|
-
block: "start",
|
|
3616
|
-
});
|
|
3811
|
+
scrollToBottomRefForDialogs.current?.();
|
|
3617
3812
|
return;
|
|
3618
3813
|
}
|
|
3619
3814
|
scrollToBottomRefForDialogs.current?.();
|
|
@@ -3681,27 +3876,34 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3681
3876
|
// Use case-insensitive comparison for GUID matching (backend may return different casing)
|
|
3682
3877
|
const normalizedProfileId = profileIdToUse?.toLowerCase();
|
|
3683
3878
|
const candidate = normalizedProfileId
|
|
3684
|
-
?
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3879
|
+
? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
|
|
3880
|
+
: undefined;
|
|
3881
|
+
if (!candidate) {
|
|
3882
|
+
setActiveProfile(undefined);
|
|
3688
3883
|
return;
|
|
3884
|
+
}
|
|
3689
3885
|
// Keep active profile in sync whenever the matching entry in `profiles` changes —
|
|
3690
|
-
// not only when the profile id changes. Otherwise
|
|
3886
|
+
// not only when the profile id changes. Otherwise availableSkills (and similar fields)
|
|
3691
3887
|
// that are merged in the parent after async loads never update (e.g. Template Builder
|
|
3692
3888
|
// settings skills merged into the profile).
|
|
3693
3889
|
setActiveProfile((prev) => {
|
|
3694
3890
|
if (!prev || prev.id !== candidate.id)
|
|
3695
3891
|
return candidate;
|
|
3696
|
-
const skillKey = (p) => JSON.stringify(
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3892
|
+
const skillKey = (p) => JSON.stringify({
|
|
3893
|
+
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
3894
|
+
String(s?.id ?? ""),
|
|
3895
|
+
String(s?.name ?? ""),
|
|
3896
|
+
]),
|
|
3897
|
+
available: (p.availableSkills ?? []).map((s) => [
|
|
3898
|
+
String(s?.id ?? ""),
|
|
3899
|
+
String(s?.name ?? ""),
|
|
3900
|
+
]),
|
|
3901
|
+
});
|
|
3700
3902
|
if (skillKey(prev) === skillKey(candidate))
|
|
3701
3903
|
return prev;
|
|
3702
3904
|
return candidate;
|
|
3703
3905
|
});
|
|
3704
|
-
}, [profiles, agent?.profileId, agentStub.profileId]);
|
|
3906
|
+
}, [profiles, agent?.id, agent?.profileId, agentStub.id, agentStub.profileId]);
|
|
3705
3907
|
// Clear queued prompts when agent changes or is new;
|
|
3706
3908
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
3707
3909
|
useEffect(() => {
|
|
@@ -3792,6 +3994,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3792
3994
|
console.error("Failed to persist pending settings", e);
|
|
3793
3995
|
}
|
|
3794
3996
|
}, [agent?.id]);
|
|
3997
|
+
const getPendingRequestSettings = useCallback(() => {
|
|
3998
|
+
const pending = pendingSettingsRef.current;
|
|
3999
|
+
const requestProfile = pending?.profileId
|
|
4000
|
+
? profiles.find((profile) => profile.id === pending.profileId) ||
|
|
4001
|
+
activeProfile ||
|
|
4002
|
+
profiles[0]
|
|
4003
|
+
: activeProfile || profiles[0];
|
|
4004
|
+
let requestModelId = selectedModelId;
|
|
4005
|
+
if (pending?.modelName) {
|
|
4006
|
+
const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
|
|
4007
|
+
pending.modelName?.trim().toLowerCase());
|
|
4008
|
+
requestModelId = requestModel?.id || requestModelId;
|
|
4009
|
+
}
|
|
4010
|
+
return {
|
|
4011
|
+
mode: (pending?.mode || mode),
|
|
4012
|
+
profileId: pending?.profileId || requestProfile?.id || "",
|
|
4013
|
+
profileName: pending?.profileName || requestProfile?.name || "",
|
|
4014
|
+
modelId: requestModelId,
|
|
4015
|
+
};
|
|
4016
|
+
}, [activeProfile, mode, profiles, selectedModelId]);
|
|
3795
4017
|
const getSubmitErrorMessage = (error) => {
|
|
3796
4018
|
const fallback = "Failed to submit prompt. Please try again.";
|
|
3797
4019
|
if (!(error instanceof Error))
|
|
@@ -3993,24 +4215,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3993
4215
|
console.warn("[AgentTerminal] Failed to compute live context:", e);
|
|
3994
4216
|
}
|
|
3995
4217
|
// Add visible test IDs to context (only for Help agent)
|
|
3996
|
-
const
|
|
4218
|
+
const requestSettings = getPendingRequestSettings();
|
|
4219
|
+
const currentProfileName = requestSettings.profileName;
|
|
3997
4220
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
3998
4221
|
const request = {
|
|
3999
4222
|
agentId: agentId,
|
|
4000
4223
|
message: savedPrompt,
|
|
4001
4224
|
sessionId: editContext.sessionId,
|
|
4002
|
-
profileId:
|
|
4225
|
+
profileId: requestSettings.profileId,
|
|
4003
4226
|
profile: currentProfileName,
|
|
4004
|
-
model:
|
|
4005
|
-
mode: mode,
|
|
4227
|
+
model: requestSettings.modelId,
|
|
4228
|
+
mode: requestSettings.mode,
|
|
4006
4229
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
4007
4230
|
};
|
|
4008
4231
|
console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
|
|
4009
4232
|
const response = await startAgent(request);
|
|
4010
4233
|
console.log("[AgentTerminal] startAgent response:", response);
|
|
4011
4234
|
// Check if prompt was queued (agent was already running)
|
|
4012
|
-
const wasQueued = response.message?.toLowerCase().includes("queued")
|
|
4013
|
-
response.status === "Queued";
|
|
4235
|
+
const wasQueued = response.message?.toLowerCase().includes("queued");
|
|
4014
4236
|
if (wasQueued) {
|
|
4015
4237
|
// Prompt was queued - show a brief notification but don't set waiting state
|
|
4016
4238
|
// The prompt will be processed when the agent becomes idle
|
|
@@ -4251,16 +4473,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4251
4473
|
console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
|
|
4252
4474
|
}
|
|
4253
4475
|
// Add visible test IDs to context (only for Help agent)
|
|
4254
|
-
const
|
|
4476
|
+
const requestSettings = getPendingRequestSettings();
|
|
4477
|
+
const currentProfileName = requestSettings.profileName;
|
|
4255
4478
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4256
4479
|
const request = {
|
|
4257
4480
|
agentId: agent.id,
|
|
4258
4481
|
message: savedPrompt,
|
|
4259
4482
|
sessionId: editContext.sessionId,
|
|
4260
|
-
profileId:
|
|
4483
|
+
profileId: requestSettings.profileId,
|
|
4261
4484
|
profile: currentProfileName,
|
|
4262
|
-
model:
|
|
4263
|
-
mode: mode,
|
|
4485
|
+
model: requestSettings.modelId,
|
|
4486
|
+
mode: requestSettings.mode,
|
|
4264
4487
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
4265
4488
|
};
|
|
4266
4489
|
console.log("[AgentTerminal] Calling startAgent API for quick message");
|
|
@@ -4685,6 +4908,70 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4685
4908
|
activeProfile,
|
|
4686
4909
|
profiles,
|
|
4687
4910
|
]);
|
|
4911
|
+
const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
|
|
4912
|
+
const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
4913
|
+
pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
4914
|
+
const currentSessionId = editContext?.sessionId?.trim() || "";
|
|
4915
|
+
const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
|
|
4916
|
+
const isClaimedByCurrentSession = !!currentSessionId &&
|
|
4917
|
+
!!claimedSessionId &&
|
|
4918
|
+
currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
|
|
4919
|
+
const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
|
|
4920
|
+
const handleClaimBrowser = useCallback(async (takeOver) => {
|
|
4921
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
4922
|
+
return;
|
|
4923
|
+
setIsBrowserClaimMutationPending(true);
|
|
4924
|
+
try {
|
|
4925
|
+
const response = await claimAgentBrowser({
|
|
4926
|
+
agentId: agent.id,
|
|
4927
|
+
sessionId: editContext.sessionId,
|
|
4928
|
+
takeOver,
|
|
4929
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4930
|
+
});
|
|
4931
|
+
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
4932
|
+
}
|
|
4933
|
+
catch (err) {
|
|
4934
|
+
console.error("[AgentTerminal] Failed to claim browser:", err);
|
|
4935
|
+
editContext.showErrorToast(err);
|
|
4936
|
+
}
|
|
4937
|
+
finally {
|
|
4938
|
+
setIsBrowserClaimMutationPending(false);
|
|
4939
|
+
}
|
|
4940
|
+
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
4941
|
+
const handleReleaseBrowser = useCallback(async () => {
|
|
4942
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
4943
|
+
return;
|
|
4944
|
+
setIsBrowserClaimMutationPending(true);
|
|
4945
|
+
try {
|
|
4946
|
+
const response = await releaseAgentBrowser({
|
|
4947
|
+
agentId: agent.id,
|
|
4948
|
+
sessionId: editContext.sessionId,
|
|
4949
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4950
|
+
});
|
|
4951
|
+
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
4952
|
+
}
|
|
4953
|
+
catch (err) {
|
|
4954
|
+
console.error("[AgentTerminal] Failed to release browser:", err);
|
|
4955
|
+
editContext.showErrorToast(err);
|
|
4956
|
+
}
|
|
4957
|
+
finally {
|
|
4958
|
+
setIsBrowserClaimMutationPending(false);
|
|
4959
|
+
}
|
|
4960
|
+
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
4961
|
+
useEffect(() => {
|
|
4962
|
+
return () => {
|
|
4963
|
+
if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
|
|
4964
|
+
return;
|
|
4965
|
+
}
|
|
4966
|
+
void releaseAgentBrowser({
|
|
4967
|
+
agentId: agent.id,
|
|
4968
|
+
sessionId: editContext.sessionId,
|
|
4969
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4970
|
+
}).catch((error) => {
|
|
4971
|
+
console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
|
|
4972
|
+
});
|
|
4973
|
+
};
|
|
4974
|
+
}, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
|
|
4688
4975
|
// Stop current execution/stream safely
|
|
4689
4976
|
const handleStop = useCallback(async () => {
|
|
4690
4977
|
try {
|
|
@@ -4817,8 +5104,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4817
5104
|
// We only want these global thinking dots if the last message was from the user
|
|
4818
5105
|
// or if no messages exist yet (waiting for initial response).
|
|
4819
5106
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
4820
|
-
if (isExecuting &&
|
|
5107
|
+
if (isExecuting &&
|
|
5108
|
+
lastMessage?.role === "assistant" &&
|
|
5109
|
+
!lastMessage.isCompleted) {
|
|
4821
5110
|
return false;
|
|
5111
|
+
}
|
|
4822
5112
|
// Existing check for uncompleted assistant messages
|
|
4823
5113
|
const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
|
|
4824
5114
|
if (hasActiveStreamingMessage)
|
|
@@ -4897,12 +5187,46 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4897
5187
|
};
|
|
4898
5188
|
const renderErrorBanner = () => {
|
|
4899
5189
|
const currentAgent = agent || agentStub;
|
|
4900
|
-
const isErrorStatus = currentAgent?.status === "error"
|
|
4901
|
-
const errorMessage = currentAgent?.statusMessage;
|
|
4902
|
-
if (!
|
|
5190
|
+
const isErrorStatus = currentAgent?.status === "error";
|
|
5191
|
+
const errorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
|
|
5192
|
+
if (!errorMessage)
|
|
4903
5193
|
return null;
|
|
4904
|
-
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 })] })] }) }));
|
|
5194
|
+
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 })] })] }) }));
|
|
4905
5195
|
};
|
|
5196
|
+
const renderBrowserClaimBanner = (variant = "inline") => {
|
|
5197
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
5198
|
+
return null;
|
|
5199
|
+
if (!isClaimedByCurrentSession &&
|
|
5200
|
+
!isClaimedByAnotherBrowser &&
|
|
5201
|
+
!isPendingBrowserCaptureWait) {
|
|
5202
|
+
return null;
|
|
5203
|
+
}
|
|
5204
|
+
const label = isClaimedByCurrentSession
|
|
5205
|
+
? "Attached to this browser"
|
|
5206
|
+
: isClaimedByAnotherBrowser
|
|
5207
|
+
? "Attached in another browser"
|
|
5208
|
+
: "No browser attached";
|
|
5209
|
+
const description = isClaimedByCurrentSession
|
|
5210
|
+
? "This browser will handle page screenshot and DOM capture requests for the agent."
|
|
5211
|
+
: isClaimedByAnotherBrowser
|
|
5212
|
+
? "Capture requests will stay with the other browser until you take over control here."
|
|
5213
|
+
: "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
|
|
5214
|
+
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");
|
|
5215
|
+
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: () => {
|
|
5216
|
+
void handleReleaseBrowser();
|
|
5217
|
+
}, 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: () => {
|
|
5218
|
+
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5219
|
+
}, children: isClaimedByAnotherBrowser
|
|
5220
|
+
? "Take over browser control"
|
|
5221
|
+
: "Attach to this browser" })) })] }) }));
|
|
5222
|
+
};
|
|
5223
|
+
const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
|
|
5224
|
+
const inlineBrowserClaimBanner = null;
|
|
5225
|
+
useEffect(() => {
|
|
5226
|
+
if (agent?.status !== "waitingForInput") {
|
|
5227
|
+
setPendingBrowserCaptureDialogType(null);
|
|
5228
|
+
}
|
|
5229
|
+
}, [agent?.status]);
|
|
4906
5230
|
const renderInlineDialogContent = () => {
|
|
4907
5231
|
if (!activeInlineDialog)
|
|
4908
5232
|
return null;
|
|
@@ -4962,10 +5286,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4962
5286
|
const summaryOperations = latestSummaryAssistantGroup
|
|
4963
5287
|
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
4964
5288
|
: [];
|
|
4965
|
-
return (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error &&
|
|
5289
|
+
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 &&
|
|
4966
5290
|
!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: {
|
|
4967
|
-
__html: activeProfile.svgIcon,
|
|
4968
|
-
} })) : (_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(), 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,
|
|
5291
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5292
|
+
} })) : (_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 ||
|
|
4969
5293
|
activeProfile?.displayTitle ||
|
|
4970
5294
|
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
4971
5295
|
const text = (action.prompt ||
|
|
@@ -4989,7 +5313,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4989
5313
|
shouldShowThinkingDots &&
|
|
4990
5314
|
!inlineDialog &&
|
|
4991
5315
|
!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: {
|
|
4992
|
-
__html: activeProfile.svgIcon,
|
|
5316
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
4993
5317
|
} })) : (_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 ||
|
|
4994
5318
|
activeProfile?.displayTitle ||
|
|
4995
5319
|
activeProfile?.name ||
|
|
@@ -5076,7 +5400,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5076
5400
|
})()
|
|
5077
5401
|
: null;
|
|
5078
5402
|
const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
|
|
5079
|
-
const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [
|
|
5403
|
+
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) => {
|
|
5080
5404
|
setPrompt(p);
|
|
5081
5405
|
// Use setTimeout to ensure state is updated before submission
|
|
5082
5406
|
setTimeout(() => {
|
|
@@ -5090,8 +5414,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5090
5414
|
}
|
|
5091
5415
|
}, 0);
|
|
5092
5416
|
} })) })), 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: {
|
|
5093
|
-
__html: activeProfile.svgIcon,
|
|
5094
|
-
} })) : (_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: [(() => {
|
|
5417
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5418
|
+
} })) : (_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(), _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
|
|
5095
5419
|
const groups = groupConsecutiveMessages(messages);
|
|
5096
5420
|
return groups.map((group, groupIndex) => {
|
|
5097
5421
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -5099,6 +5423,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5099
5423
|
// Render user message
|
|
5100
5424
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
5101
5425
|
}
|
|
5426
|
+
else if (group.type === "heartbeat" && group.messages[0]) {
|
|
5427
|
+
return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
|
|
5428
|
+
}
|
|
5102
5429
|
else {
|
|
5103
5430
|
// Render bundled assistant messages
|
|
5104
5431
|
// Check if this group contains any streaming message
|
|
@@ -5115,7 +5442,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5115
5442
|
}
|
|
5116
5443
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
5117
5444
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5118
|
-
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup,
|
|
5445
|
+
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
|
|
5119
5446
|
activeProfile?.displayTitle ||
|
|
5120
5447
|
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5121
5448
|
const text = (action.prompt ||
|
|
@@ -5161,7 +5488,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5161
5488
|
}
|
|
5162
5489
|
});
|
|
5163
5490
|
})(), 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: {
|
|
5164
|
-
__html: activeProfile.svgIcon,
|
|
5491
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5165
5492
|
} })) : (_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 ||
|
|
5166
5493
|
activeProfile?.displayTitle ||
|
|
5167
5494
|
activeProfile?.name ||
|
|
@@ -5220,10 +5547,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5220
5547
|
if (qp.data) {
|
|
5221
5548
|
try {
|
|
5222
5549
|
const parsed = JSON.parse(qp.data);
|
|
5223
|
-
triggerName =
|
|
5224
|
-
parsed?.TriggerName?.trim() ||
|
|
5225
|
-
parsed?.triggerName?.trim() ||
|
|
5226
|
-
"";
|
|
5550
|
+
triggerName = parsed?.triggerName?.trim() || "";
|
|
5227
5551
|
}
|
|
5228
5552
|
catch {
|
|
5229
5553
|
// Ignore invalid JSON metadata and render as regular queued prompt.
|
|
@@ -5337,13 +5661,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5337
5661
|
return null;
|
|
5338
5662
|
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
|
|
5339
5663
|
? "justify-end"
|
|
5340
|
-
: "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"
|
|
5664
|
+
: "justify-between"), children: [!hideBottomControls && !simpleMode && !isInPlaceholderMode && (_jsxs("div", { className: "flex flex-wrap items-center justify-start gap-2", children: [_jsx(Select, { "data-testid": "agent-mode-selector", 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"
|
|
5341
5665
|
? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
|
|
5342
5666
|
: mode === "supervised"
|
|
5343
5667
|
? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
|
|
5344
5668
|
: "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
|
|
5345
5669
|
const nextMode = val || "supervised";
|
|
5346
|
-
setMode(nextMode);
|
|
5347
5670
|
const current = agentMetadata || {};
|
|
5348
5671
|
const nextMeta = {
|
|
5349
5672
|
...current,
|
|
@@ -5351,6 +5674,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5351
5674
|
};
|
|
5352
5675
|
try {
|
|
5353
5676
|
if (!agent?.id || agent.status === "new") {
|
|
5677
|
+
setMode(nextMode);
|
|
5354
5678
|
setAgentMetadata(nextMeta);
|
|
5355
5679
|
pendingSettingsRef.current = {
|
|
5356
5680
|
...(pendingSettingsRef.current || {}),
|
|
@@ -5358,13 +5682,18 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5358
5682
|
};
|
|
5359
5683
|
return;
|
|
5360
5684
|
}
|
|
5361
|
-
await updateAgentSettings(agent.id, {
|
|
5685
|
+
const result = await updateAgentSettings(agent.id, {
|
|
5362
5686
|
mode: nextMode,
|
|
5363
5687
|
});
|
|
5688
|
+
if (result.success === false || result.updates?.mode === false) {
|
|
5689
|
+
throw new Error("Mode change was not applied");
|
|
5690
|
+
}
|
|
5691
|
+
setMode(nextMode);
|
|
5364
5692
|
setAgentMetadata(nextMeta);
|
|
5365
5693
|
setAgent((prev) => prev
|
|
5366
5694
|
? {
|
|
5367
5695
|
...prev,
|
|
5696
|
+
mode: nextMode,
|
|
5368
5697
|
metadata: JSON.stringify(nextMeta),
|
|
5369
5698
|
}
|
|
5370
5699
|
: prev);
|
|
@@ -5411,6 +5740,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5411
5740
|
setAgent((prev) => prev
|
|
5412
5741
|
? {
|
|
5413
5742
|
...prev,
|
|
5743
|
+
profileId: nextProfile.id,
|
|
5744
|
+
profileName: nextProfile.name,
|
|
5414
5745
|
metadata: JSON.stringify({
|
|
5415
5746
|
...(agentMetadata || {}),
|
|
5416
5747
|
profile: nextProfile.name,
|
|
@@ -5475,15 +5806,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5475
5806
|
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
5476
5807
|
!skillsError &&
|
|
5477
5808
|
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
5478
|
-
? "All allowed skills are selected"
|
|
5479
|
-
: "No skills
|
|
5809
|
+
? "All available/allowed skills are selected"
|
|
5810
|
+
: "No available or allowed skills for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
|
|
5480
5811
|
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
5481
5812
|
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: () => {
|
|
5482
5813
|
void handleOpenSkillItem(skillId);
|
|
5483
|
-
}, children: _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }),
|
|
5814
|
+
}, 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: () => {
|
|
5484
5815
|
void handleRemoveSkill(skillId);
|
|
5485
5816
|
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
5486
|
-
}) }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () =>
|
|
5817
|
+
}) }))] }), _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: [isNewAgent && (_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: isNewAgent
|
|
5818
|
+
? "No available tools for this profile and mode"
|
|
5819
|
+
: "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) => {
|
|
5487
5820
|
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
5488
5821
|
const pathLabel = "itemPath" in allowance
|
|
5489
5822
|
? allowance.itemPath
|
|
@@ -5496,10 +5829,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5496
5829
|
]
|
|
5497
5830
|
.filter(Boolean)
|
|
5498
5831
|
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
5499
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
5832
|
+
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
|
|
5833
|
+
? "Allowances are shown after the agent is created"
|
|
5834
|
+
: "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) => {
|
|
5500
5835
|
const filterText = (sub.filter || "").trim();
|
|
5501
5836
|
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));
|
|
5502
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
5837
|
+
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
|
|
5838
|
+
? "Subscribed triggers are shown after the agent is created"
|
|
5839
|
+
: "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: () => {
|
|
5503
5840
|
setPrompt(p.prompt);
|
|
5504
5841
|
setShowPredefined(false);
|
|
5505
5842
|
if (textareaRef.current)
|