@parhelia/core 0.1.12485 → 0.1.12496
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 +2 -1
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsView.js +2 -1
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.js +12 -5
- 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/input.js +1 -1
- package/dist/components/ui/input.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 +33 -8
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +7 -0
- package/dist/config/types.js.map +1 -1
- package/dist/editor/FieldHistory.js +49 -31
- package/dist/editor/FieldHistory.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/AgentTerminal.js +544 -169
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +26 -26
- 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 +3 -2
- 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 +4 -1
- 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 +11 -0
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/client/EditorShell.js +39 -41
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +5 -1
- package/dist/editor/client/editContext.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/itemsRepository.js +10 -6
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +1 -1
- package/dist/editor/client/pageModelBuilder.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/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/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 -1
- 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 +31 -1
- package/dist/editor/services/agentService.js +107 -72
- package/dist/editor/services/agentService.js.map +1 -1
- 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 +18 -16
- 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 +19 -21
- 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 +9 -9
- 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 +1 -1
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/NavigationPanelItem.js +2 -5
- package/dist/editor/sidebar/NavigationPanelItem.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/types.d.ts +1 -0
- 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 +9 -1
- 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/ItemCollectionEditorDialog.js +5 -12
- package/dist/task-board/components/ItemCollectionEditorDialog.js.map +1 -1
- package/dist/task-board/components/ProjectAgentsPanel.js +1 -1
- 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/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 +2 -0
- 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
|
@@ -1,7 +1,7 @@
|
|
|
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
5
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
6
6
|
import { localStorageService } from "../services/localStorageService";
|
|
7
7
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -16,10 +16,13 @@ import { getComponentById } from "../componentTreeHelper";
|
|
|
16
16
|
import { AgentGreeting } from "./AgentGreeting";
|
|
17
17
|
import { getAgentHistory } from "../services/editService";
|
|
18
18
|
import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
|
|
19
|
+
import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
|
|
20
|
+
import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
|
|
19
21
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
20
22
|
import { SecretAgentIcon } from "../ui/Icons";
|
|
21
23
|
import { formatTime, formatDateTime } from "../utils";
|
|
22
24
|
import { cn } from "../../lib/utils";
|
|
25
|
+
import { sanitizeSvg } from "../../lib/sanitize";
|
|
23
26
|
import { Select } from "../../components/ui/select";
|
|
24
27
|
import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
|
|
25
28
|
import { SimpleTabs } from "../ui/SimpleTabs";
|
|
@@ -75,23 +78,19 @@ function formatAllowanceLabel(allowance) {
|
|
|
75
78
|
: ` ${allowance.normalizedPath}`}`;
|
|
76
79
|
}
|
|
77
80
|
function getAgentRunMessageAgentId(payload) {
|
|
78
|
-
const agentId = payload?.agentId
|
|
81
|
+
const agentId = payload?.agentId;
|
|
79
82
|
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
80
83
|
}
|
|
81
84
|
function getAgentRunMessageSeq(payload) {
|
|
82
|
-
const seq = payload?.seq
|
|
85
|
+
const seq = payload?.seq;
|
|
83
86
|
return typeof seq === "number" ? seq : null;
|
|
84
87
|
}
|
|
85
88
|
function getAgentRunMessageDetail(type, payload) {
|
|
86
89
|
if (type === "agent:run:delta") {
|
|
87
|
-
return payload?.type ||
|
|
90
|
+
return payload?.type || null;
|
|
88
91
|
}
|
|
89
92
|
if (type === "agent:run:status") {
|
|
90
|
-
return
|
|
91
|
-
payload?.data?.State ||
|
|
92
|
-
payload?.data?.status ||
|
|
93
|
-
payload?.data?.Status ||
|
|
94
|
-
null);
|
|
93
|
+
return payload?.data?.state || payload?.data?.status || null;
|
|
95
94
|
}
|
|
96
95
|
if (type === "agent:run:error") {
|
|
97
96
|
return payload?.error || null;
|
|
@@ -104,6 +103,19 @@ function getAgentRunMessageDetail(type, payload) {
|
|
|
104
103
|
}
|
|
105
104
|
return null;
|
|
106
105
|
}
|
|
106
|
+
function isHeartbeatRunEventMessage(message) {
|
|
107
|
+
if (!message)
|
|
108
|
+
return false;
|
|
109
|
+
if (message.type === "agent:run:delta") {
|
|
110
|
+
const deltaType = message.payload?.type;
|
|
111
|
+
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
112
|
+
}
|
|
113
|
+
if (message.type === "agent:run:status") {
|
|
114
|
+
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
115
|
+
return String(state || "").toLowerCase() === "heartbeat";
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
107
119
|
function getVisibleDialogRegistry() {
|
|
108
120
|
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
109
121
|
return registry && typeof registry === "object" ? registry : {};
|
|
@@ -119,6 +131,38 @@ function decodeHtmlEntities(value) {
|
|
|
119
131
|
function toUserFacingAgentErrorMessage(value) {
|
|
120
132
|
if (!value)
|
|
121
133
|
return "";
|
|
134
|
+
const normalizedValue = decodeHtmlEntities(value.replace(/<br\s*\/?>/gi, "\n")).trim();
|
|
135
|
+
if (!normalizedValue)
|
|
136
|
+
return "";
|
|
137
|
+
const trimmed = normalizedValue.trim();
|
|
138
|
+
const maybeJson = trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith('"');
|
|
139
|
+
if (maybeJson) {
|
|
140
|
+
try {
|
|
141
|
+
const parsed = JSON.parse(trimmed);
|
|
142
|
+
const structuredMessage = typeof parsed === "string"
|
|
143
|
+
? parsed
|
|
144
|
+
: typeof parsed?.error === "string"
|
|
145
|
+
? parsed.error
|
|
146
|
+
: typeof parsed?.message === "string"
|
|
147
|
+
? parsed.message
|
|
148
|
+
: typeof parsed?.detail === "string"
|
|
149
|
+
? parsed.detail
|
|
150
|
+
: typeof parsed?.error_description === "string"
|
|
151
|
+
? parsed.error_description
|
|
152
|
+
: typeof parsed?.error === "object" &&
|
|
153
|
+
parsed?.error &&
|
|
154
|
+
typeof parsed.error.message ===
|
|
155
|
+
"string"
|
|
156
|
+
? String(parsed.error.message)
|
|
157
|
+
: "";
|
|
158
|
+
if (structuredMessage.trim()) {
|
|
159
|
+
value = structuredMessage;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Fall through to plain-text cleanup when the provider error isn't valid JSON.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
122
166
|
const firstLine = value
|
|
123
167
|
.replace(/<br\s*\/?>/gi, "\n")
|
|
124
168
|
.split(/\r?\n/)
|
|
@@ -177,6 +221,9 @@ const UserMessage = ({ message }) => {
|
|
|
177
221
|
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
178
222
|
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 })] })] }));
|
|
179
223
|
};
|
|
224
|
+
const HeartbeatMessage = ({ message }) => {
|
|
225
|
+
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)) }))] }) }));
|
|
226
|
+
};
|
|
180
227
|
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
181
228
|
const extractPartialTodos = (jsonText) => {
|
|
182
229
|
// First try to parse complete JSON
|
|
@@ -549,6 +596,16 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
549
596
|
// Add user message
|
|
550
597
|
groups.push({ type: "user", messages: [message] });
|
|
551
598
|
}
|
|
599
|
+
else if (message.messageType === "heartbeat" || message.role === "system") {
|
|
600
|
+
if (currentAssistantGroup.length > 0) {
|
|
601
|
+
groups.push({
|
|
602
|
+
type: "assistant-group",
|
|
603
|
+
messages: currentAssistantGroup,
|
|
604
|
+
});
|
|
605
|
+
currentAssistantGroup = [];
|
|
606
|
+
}
|
|
607
|
+
groups.push({ type: "heartbeat", messages: [message] });
|
|
608
|
+
}
|
|
552
609
|
else if (message.role === "assistant") {
|
|
553
610
|
// Add to current assistant group
|
|
554
611
|
currentAssistantGroup.push(message);
|
|
@@ -655,10 +712,10 @@ const stringifyToolField = (value) => {
|
|
|
655
712
|
const getFirstToolCallEnvelope = (data) => {
|
|
656
713
|
if (!data || typeof data !== "object")
|
|
657
714
|
return undefined;
|
|
658
|
-
const direct = data.toolCall || data.tool_call
|
|
715
|
+
const direct = data.toolCall || data.tool_call;
|
|
659
716
|
if (direct)
|
|
660
717
|
return direct;
|
|
661
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls
|
|
718
|
+
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
662
719
|
for (const candidate of arrayCandidates) {
|
|
663
720
|
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
664
721
|
return candidate[0];
|
|
@@ -668,27 +725,18 @@ const getFirstToolCallEnvelope = (data) => {
|
|
|
668
725
|
};
|
|
669
726
|
const extractToolCallFields = (data) => {
|
|
670
727
|
const envelope = getFirstToolCallEnvelope(data);
|
|
671
|
-
const functionPayload = data?.function ||
|
|
672
|
-
data?.Function ||
|
|
673
|
-
envelope?.function ||
|
|
674
|
-
envelope?.Function;
|
|
728
|
+
const functionPayload = data?.function || envelope?.function;
|
|
675
729
|
const functionName = data?.functionName ||
|
|
676
730
|
data?.name ||
|
|
677
731
|
functionPayload?.name ||
|
|
678
|
-
functionPayload?.Name ||
|
|
679
732
|
envelope?.functionName ||
|
|
680
733
|
envelope?.name ||
|
|
681
|
-
envelope?.FunctionName ||
|
|
682
|
-
envelope?.Name ||
|
|
683
734
|
"unknown";
|
|
684
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id
|
|
735
|
+
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
685
736
|
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
686
|
-
stringifyToolField(data?.Arguments) ||
|
|
687
737
|
stringifyToolField(data?.arguments) ||
|
|
688
738
|
stringifyToolField(functionPayload?.arguments) ||
|
|
689
|
-
stringifyToolField(functionPayload?.Arguments) ||
|
|
690
739
|
stringifyToolField(envelope?.functionArguments) ||
|
|
691
|
-
stringifyToolField(envelope?.Arguments) ||
|
|
692
740
|
stringifyToolField(envelope?.arguments) ||
|
|
693
741
|
"{}";
|
|
694
742
|
return {
|
|
@@ -774,6 +822,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
774
822
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
775
823
|
const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
|
|
776
824
|
const [agentMetadata, setAgentMetadata] = useState(null);
|
|
825
|
+
const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
|
|
826
|
+
const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
|
|
777
827
|
// Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
|
|
778
828
|
// This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
|
|
779
829
|
useEffect(() => {
|
|
@@ -926,6 +976,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
926
976
|
if (!message?.type?.startsWith("agent:run:")) {
|
|
927
977
|
return false;
|
|
928
978
|
}
|
|
979
|
+
if (isHeartbeatRunEventMessage(message)) {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
929
982
|
return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
|
|
930
983
|
})
|
|
931
984
|
.slice(-8)
|
|
@@ -1029,24 +1082,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1029
1082
|
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1030
1083
|
const [skillsError, setSkillsError] = useState(null);
|
|
1031
1084
|
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1085
|
+
const [availableTools, setAvailableTools] = useState([]);
|
|
1032
1086
|
const [operationAllowances, setOperationAllowances] = useState({
|
|
1033
1087
|
sitecore: [],
|
|
1034
1088
|
filesystem: [],
|
|
1035
1089
|
});
|
|
1036
1090
|
const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
|
|
1091
|
+
const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
|
|
1037
1092
|
const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
|
|
1038
1093
|
const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
|
|
1094
|
+
const [availableToolsError, setAvailableToolsError] = useState(null);
|
|
1039
1095
|
const [operationAllowancesError, setOperationAllowancesError] = useState(null);
|
|
1096
|
+
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1040
1097
|
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1041
1098
|
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1099
|
+
const isNewAgent = agent?.status === "new";
|
|
1042
1100
|
const hasSpawnedAgents = useMemo(() => {
|
|
1043
1101
|
if (!agentMetadata)
|
|
1044
1102
|
return false;
|
|
1045
|
-
const childAgents = agentMetadata?.
|
|
1046
|
-
agentMetadata?.childAgents;
|
|
1103
|
+
const childAgents = agentMetadata?.childAgents;
|
|
1047
1104
|
if (!Array.isArray(childAgents) || childAgents.length === 0)
|
|
1048
1105
|
return false;
|
|
1049
|
-
return childAgents.some((a) => a != null && typeof a === "object" &&
|
|
1106
|
+
return childAgents.some((a) => a != null && typeof a === "object" && a.agentId);
|
|
1050
1107
|
}, [agentMetadata]);
|
|
1051
1108
|
const hasTodoContent = useMemo(() => {
|
|
1052
1109
|
const metadataTodos = (() => {
|
|
@@ -1165,70 +1222,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1165
1222
|
active = false;
|
|
1166
1223
|
};
|
|
1167
1224
|
}, []);
|
|
1168
|
-
useEffect(() => {
|
|
1169
|
-
let active = true;
|
|
1170
|
-
if (!showAgentSettings) {
|
|
1171
|
-
return () => {
|
|
1172
|
-
active = false;
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
if (!agent?.id || agent.status === "new") {
|
|
1176
|
-
setTriggerSubscriptions([]);
|
|
1177
|
-
setTriggerSubscriptionsLoading(false);
|
|
1178
|
-
setTriggerSubscriptionsError(null);
|
|
1179
|
-
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1180
|
-
setOperationAllowancesLoading(false);
|
|
1181
|
-
setOperationAllowancesError(null);
|
|
1182
|
-
return () => {
|
|
1183
|
-
active = false;
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
const loadTriggerSubscriptions = async () => {
|
|
1187
|
-
try {
|
|
1188
|
-
setTriggerSubscriptionsLoading(true);
|
|
1189
|
-
setTriggerSubscriptionsError(null);
|
|
1190
|
-
const subscriptions = await getAgentTriggerSubscriptions(agent.id);
|
|
1191
|
-
if (active) {
|
|
1192
|
-
setTriggerSubscriptions(subscriptions);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
catch (e) {
|
|
1196
|
-
if (active) {
|
|
1197
|
-
setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
finally {
|
|
1201
|
-
if (active) {
|
|
1202
|
-
setTriggerSubscriptionsLoading(false);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
};
|
|
1206
|
-
const loadOperationAllowances = async () => {
|
|
1207
|
-
try {
|
|
1208
|
-
setOperationAllowancesLoading(true);
|
|
1209
|
-
setOperationAllowancesError(null);
|
|
1210
|
-
const allowances = await getAgentOperationAllowances(agent.id);
|
|
1211
|
-
if (active) {
|
|
1212
|
-
setOperationAllowances(allowances);
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
catch (e) {
|
|
1216
|
-
if (active) {
|
|
1217
|
-
setOperationAllowancesError(e?.message || "Failed to load allowances");
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
finally {
|
|
1221
|
-
if (active) {
|
|
1222
|
-
setOperationAllowancesLoading(false);
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
void loadTriggerSubscriptions();
|
|
1227
|
-
void loadOperationAllowances();
|
|
1228
|
-
return () => {
|
|
1229
|
-
active = false;
|
|
1230
|
-
};
|
|
1231
|
-
}, [showAgentSettings, agent?.id, agent?.status]);
|
|
1232
1225
|
const modeOptions = useMemo(() => [
|
|
1233
1226
|
{
|
|
1234
1227
|
value: "supervised",
|
|
@@ -1265,9 +1258,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1265
1258
|
})) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
|
|
1266
1259
|
const metadataSelectedSkillIds = useMemo(() => {
|
|
1267
1260
|
const rawSkillIds = agentMetadata?.additionalData?.skillIds ??
|
|
1268
|
-
agentMetadata?.AdditionalData?.skillIds ??
|
|
1269
1261
|
agentMetadata?.skillIds ??
|
|
1270
|
-
agentMetadata?.SkillIds ??
|
|
1271
1262
|
[];
|
|
1272
1263
|
if (!Array.isArray(rawSkillIds)) {
|
|
1273
1264
|
return [];
|
|
@@ -1277,9 +1268,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1277
1268
|
.filter((x) => x.length > 0);
|
|
1278
1269
|
}, [agentMetadata]);
|
|
1279
1270
|
const backendAssignedSkillIds = useMemo(() => {
|
|
1280
|
-
const rawSkillIds = agent?.assignedSkillIds ??
|
|
1281
|
-
agent?.AssignedSkillIds ??
|
|
1282
|
-
[];
|
|
1271
|
+
const rawSkillIds = agent?.assignedSkillIds ?? [];
|
|
1283
1272
|
if (!Array.isArray(rawSkillIds)) {
|
|
1284
1273
|
return [];
|
|
1285
1274
|
}
|
|
@@ -1287,8 +1276,32 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1287
1276
|
.map((x) => String(x || "").trim())
|
|
1288
1277
|
.filter((x) => x.length > 0);
|
|
1289
1278
|
}, [agent]);
|
|
1279
|
+
const preloadedSkillIds = useMemo(() => {
|
|
1280
|
+
const rawSkillIds = activeProfile?.preloadSkills ?? [];
|
|
1281
|
+
if (!Array.isArray(rawSkillIds)) {
|
|
1282
|
+
return [];
|
|
1283
|
+
}
|
|
1284
|
+
return rawSkillIds
|
|
1285
|
+
.map((skill) => String(skill?.id || "").trim())
|
|
1286
|
+
.filter((id) => id.length > 0);
|
|
1287
|
+
}, [activeProfile?.preloadSkills]);
|
|
1288
|
+
const autoAssignedSkillIds = useMemo(() => {
|
|
1289
|
+
const all = isNewAgent
|
|
1290
|
+
? [...preloadedSkillIds, ...backendAssignedSkillIds]
|
|
1291
|
+
: backendAssignedSkillIds;
|
|
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, isNewAgent, preloadedSkillIds]);
|
|
1290
1303
|
const selectedSkillIds = useMemo(() => {
|
|
1291
|
-
const all = [...
|
|
1304
|
+
const all = [...autoAssignedSkillIds, ...metadataSelectedSkillIds];
|
|
1292
1305
|
const seen = new Set();
|
|
1293
1306
|
const unique = [];
|
|
1294
1307
|
for (const id of all) {
|
|
@@ -1299,7 +1312,102 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1299
1312
|
unique.push(id);
|
|
1300
1313
|
}
|
|
1301
1314
|
return unique;
|
|
1302
|
-
}, [
|
|
1315
|
+
}, [autoAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1316
|
+
useEffect(() => {
|
|
1317
|
+
let active = true;
|
|
1318
|
+
if (!showAgentSettings) {
|
|
1319
|
+
return () => {
|
|
1320
|
+
active = false;
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
if (!agent?.id || agent.status === "new") {
|
|
1324
|
+
setTriggerSubscriptions([]);
|
|
1325
|
+
setTriggerSubscriptionsLoading(false);
|
|
1326
|
+
setTriggerSubscriptionsError(null);
|
|
1327
|
+
setAvailableTools([]);
|
|
1328
|
+
setAvailableToolsLoading(false);
|
|
1329
|
+
setAvailableToolsError(null);
|
|
1330
|
+
setOperationAllowances({ sitecore: [], filesystem: [] });
|
|
1331
|
+
setOperationAllowancesLoading(false);
|
|
1332
|
+
setOperationAllowancesError(null);
|
|
1333
|
+
return () => {
|
|
1334
|
+
active = false;
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
const loadTriggerSubscriptions = async () => {
|
|
1338
|
+
try {
|
|
1339
|
+
setTriggerSubscriptionsLoading(true);
|
|
1340
|
+
setTriggerSubscriptionsError(null);
|
|
1341
|
+
const subscriptions = await getAgentTriggerSubscriptions(agent.id);
|
|
1342
|
+
if (active) {
|
|
1343
|
+
setTriggerSubscriptions(subscriptions);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
catch (e) {
|
|
1347
|
+
if (active) {
|
|
1348
|
+
setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
finally {
|
|
1352
|
+
if (active) {
|
|
1353
|
+
setTriggerSubscriptionsLoading(false);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
const loadOperationAllowances = async () => {
|
|
1358
|
+
try {
|
|
1359
|
+
setOperationAllowancesLoading(true);
|
|
1360
|
+
setOperationAllowancesError(null);
|
|
1361
|
+
const allowances = await getAgentOperationAllowances(agent.id);
|
|
1362
|
+
if (active) {
|
|
1363
|
+
setOperationAllowances(allowances);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
catch (e) {
|
|
1367
|
+
if (active) {
|
|
1368
|
+
setOperationAllowancesError(e?.message || "Failed to load allowances");
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
finally {
|
|
1372
|
+
if (active) {
|
|
1373
|
+
setOperationAllowancesLoading(false);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
const loadAvailableTools = async () => {
|
|
1378
|
+
try {
|
|
1379
|
+
setAvailableToolsLoading(true);
|
|
1380
|
+
setAvailableToolsError(null);
|
|
1381
|
+
const tools = await getAgentAvailableTools(agent.id);
|
|
1382
|
+
if (active) {
|
|
1383
|
+
setAvailableTools(tools);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
catch (e) {
|
|
1387
|
+
if (active) {
|
|
1388
|
+
setAvailableToolsError(e?.message || "Failed to load available tools");
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
finally {
|
|
1392
|
+
if (active) {
|
|
1393
|
+
setAvailableToolsLoading(false);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
void loadTriggerSubscriptions();
|
|
1398
|
+
void loadAvailableTools();
|
|
1399
|
+
void loadOperationAllowances();
|
|
1400
|
+
return () => {
|
|
1401
|
+
active = false;
|
|
1402
|
+
};
|
|
1403
|
+
}, [
|
|
1404
|
+
showAgentSettings,
|
|
1405
|
+
agent?.id,
|
|
1406
|
+
agent?.status,
|
|
1407
|
+
agent?.profileId,
|
|
1408
|
+
mode,
|
|
1409
|
+
selectedSkillIds,
|
|
1410
|
+
]);
|
|
1303
1411
|
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
1304
1412
|
const allowanceGroups = useMemo(() => [
|
|
1305
1413
|
{
|
|
@@ -1317,18 +1425,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1317
1425
|
operationAllowances.filesystem.length > 0, [operationAllowances]);
|
|
1318
1426
|
const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
|
|
1319
1427
|
operationAllowances.filesystem.length, [operationAllowances]);
|
|
1320
|
-
const
|
|
1321
|
-
const ids =
|
|
1322
|
-
|
|
1323
|
-
|
|
1428
|
+
const listedProfileSkillIdSet = useMemo(() => {
|
|
1429
|
+
const ids = [
|
|
1430
|
+
...(activeProfile?.availableSkills ?? []),
|
|
1431
|
+
...(activeProfile?.allowedSkills ?? []),
|
|
1432
|
+
]
|
|
1433
|
+
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1434
|
+
.filter((id) => id.length > 0);
|
|
1324
1435
|
return new Set(ids);
|
|
1325
|
-
}, [activeProfile?.allowedSkills]);
|
|
1436
|
+
}, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
|
|
1326
1437
|
const profileFilteredSkills = useMemo(() => {
|
|
1327
|
-
if (
|
|
1328
|
-
return
|
|
1438
|
+
if (listedProfileSkillIdSet.size === 0) {
|
|
1439
|
+
return [];
|
|
1329
1440
|
}
|
|
1330
|
-
return availableSkills.filter((skill) =>
|
|
1331
|
-
}, [availableSkills,
|
|
1441
|
+
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1442
|
+
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1332
1443
|
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1333
1444
|
const availableSkillIdSet = useMemo(() => new Set(availableSkills.map((skill) => skill.id.toLowerCase())), [availableSkills]);
|
|
1334
1445
|
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
@@ -1336,7 +1447,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1336
1447
|
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
1337
1448
|
.filter((s) => !!s), [availableSkills, selectedSkillIds]);
|
|
1338
1449
|
const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
|
|
1339
|
-
const
|
|
1450
|
+
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1451
|
+
const previewAvailableTools = useMemo(() => {
|
|
1452
|
+
const baseToolNames = mode === "read-only"
|
|
1453
|
+
? activeProfile?.readOnlyToolNames ?? []
|
|
1454
|
+
: activeProfile?.allowedToolNames ?? [];
|
|
1455
|
+
const toolNames = new Set();
|
|
1456
|
+
for (const toolName of baseToolNames) {
|
|
1457
|
+
const normalized = String(toolName || "").trim();
|
|
1458
|
+
if (normalized) {
|
|
1459
|
+
toolNames.add(normalized);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
for (const skill of selectedSkills) {
|
|
1463
|
+
for (const toolName of skill.additionalAllowedTools ?? []) {
|
|
1464
|
+
const normalized = String(toolName || "").trim();
|
|
1465
|
+
if (normalized) {
|
|
1466
|
+
toolNames.add(normalized);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
|
|
1471
|
+
}, [
|
|
1472
|
+
activeProfile?.allowedToolNames,
|
|
1473
|
+
activeProfile?.readOnlyToolNames,
|
|
1474
|
+
mode,
|
|
1475
|
+
selectedSkills,
|
|
1476
|
+
]);
|
|
1477
|
+
const displayedAvailableTools = isNewAgent
|
|
1478
|
+
? previewAvailableTools
|
|
1479
|
+
: availableTools;
|
|
1480
|
+
const displayedAvailableToolsLoading = isNewAgent
|
|
1481
|
+
? false
|
|
1482
|
+
: availableToolsLoading;
|
|
1483
|
+
const displayedAvailableToolsError = isNewAgent ? null : availableToolsError;
|
|
1340
1484
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1341
1485
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1342
1486
|
try {
|
|
@@ -1365,9 +1509,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1365
1509
|
if (!agent?.id)
|
|
1366
1510
|
return;
|
|
1367
1511
|
const current = agentMetadata || {};
|
|
1368
|
-
const currentAdditionalData = current.additionalData ||
|
|
1369
|
-
current.AdditionalData ||
|
|
1370
|
-
{};
|
|
1512
|
+
const currentAdditionalData = current.additionalData || {};
|
|
1371
1513
|
const nextAdditionalData = {
|
|
1372
1514
|
...currentAdditionalData,
|
|
1373
1515
|
};
|
|
@@ -1382,11 +1524,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1382
1524
|
};
|
|
1383
1525
|
if (Object.keys(nextAdditionalData).length > 0) {
|
|
1384
1526
|
next.additionalData = nextAdditionalData;
|
|
1385
|
-
delete next.AdditionalData;
|
|
1386
1527
|
}
|
|
1387
1528
|
else {
|
|
1388
1529
|
delete next.additionalData;
|
|
1389
|
-
delete next.AdditionalData;
|
|
1390
1530
|
}
|
|
1391
1531
|
try {
|
|
1392
1532
|
if (agent.status === "new") {
|
|
@@ -1805,6 +1945,55 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1805
1945
|
toolCalls: [],
|
|
1806
1946
|
};
|
|
1807
1947
|
}, [agent, agentStub.id]);
|
|
1948
|
+
const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
|
|
1949
|
+
const handleHeartbeatMessage = useCallback((message) => {
|
|
1950
|
+
const heartbeatText = (message.data?.message ||
|
|
1951
|
+
message.data?.Message ||
|
|
1952
|
+
"Still working on your request. This is taking a little longer than usual.").trim() ||
|
|
1953
|
+
"Still working on your request. This is taking a little longer than usual.";
|
|
1954
|
+
const createdDate = message.timestamp || new Date().toISOString();
|
|
1955
|
+
const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
|
|
1956
|
+
setMessages((prev) => {
|
|
1957
|
+
const withoutHeartbeats = stripHeartbeatMessages(prev);
|
|
1958
|
+
const updated = [
|
|
1959
|
+
...withoutHeartbeats,
|
|
1960
|
+
{
|
|
1961
|
+
id: heartbeatId,
|
|
1962
|
+
agentId: agent?.id || agentStub.id,
|
|
1963
|
+
messageIndex: -1,
|
|
1964
|
+
role: "system",
|
|
1965
|
+
content: heartbeatText,
|
|
1966
|
+
name: "system",
|
|
1967
|
+
messageType: "heartbeat",
|
|
1968
|
+
isCompleted: true,
|
|
1969
|
+
model: "",
|
|
1970
|
+
tokensUsed: 0,
|
|
1971
|
+
inputTokens: 0,
|
|
1972
|
+
outputTokens: 0,
|
|
1973
|
+
cachedInputTokens: 0,
|
|
1974
|
+
inputTokenCost: 0,
|
|
1975
|
+
outputTokenCost: 0,
|
|
1976
|
+
cachedInputTokenCost: 0,
|
|
1977
|
+
totalCost: 0,
|
|
1978
|
+
currency: "USD",
|
|
1979
|
+
createdDate,
|
|
1980
|
+
toolCalls: [],
|
|
1981
|
+
},
|
|
1982
|
+
];
|
|
1983
|
+
messagesRef.current = updated;
|
|
1984
|
+
return updated;
|
|
1985
|
+
});
|
|
1986
|
+
}, [agent?.id, agentStub.id, stripHeartbeatMessages]);
|
|
1987
|
+
const clearHeartbeatMessages = useCallback(() => {
|
|
1988
|
+
setMessages((prev) => {
|
|
1989
|
+
const updated = stripHeartbeatMessages(prev);
|
|
1990
|
+
if (updated.length === prev.length) {
|
|
1991
|
+
return prev;
|
|
1992
|
+
}
|
|
1993
|
+
messagesRef.current = updated;
|
|
1994
|
+
return updated;
|
|
1995
|
+
});
|
|
1996
|
+
}, [stripHeartbeatMessages]);
|
|
1808
1997
|
const handleContentChunk = useCallback((message, agentData) => {
|
|
1809
1998
|
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
1810
1999
|
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
@@ -2823,9 +3012,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2823
3012
|
// Handle agent:profile:switched (profile changed via switch-profile function)
|
|
2824
3013
|
if (messageType === "agent:profile:switched") {
|
|
2825
3014
|
const payload = message.payload || {};
|
|
2826
|
-
const switchedAgentId = payload.agentId
|
|
2827
|
-
const newProfileId = payload.newProfileId
|
|
2828
|
-
const newProfileName = payload.newProfileName
|
|
3015
|
+
const switchedAgentId = payload.agentId;
|
|
3016
|
+
const newProfileId = payload.newProfileId;
|
|
3017
|
+
const newProfileName = payload.newProfileName;
|
|
2829
3018
|
if (switchedAgentId === agent.id && newProfileId) {
|
|
2830
3019
|
// Update the agent's profile
|
|
2831
3020
|
setAgent((prev) => {
|
|
@@ -2866,7 +3055,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2866
3055
|
return;
|
|
2867
3056
|
}
|
|
2868
3057
|
// For other agent messages, check if this is for our agent
|
|
2869
|
-
const agentId = message.payload?.agentId
|
|
3058
|
+
const agentId = message.payload?.agentId;
|
|
2870
3059
|
if (agentId !== agent.id) {
|
|
2871
3060
|
return;
|
|
2872
3061
|
}
|
|
@@ -3008,15 +3197,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3008
3197
|
}
|
|
3009
3198
|
// Always allow ContextUpdate messages (metadata updates) regardless of agent status
|
|
3010
3199
|
const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
|
|
3200
|
+
const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
|
|
3201
|
+
const shouldClearHeartbeat = type === "ContentChunk" ||
|
|
3202
|
+
type === "contentChunk" ||
|
|
3203
|
+
type === "ToolCall" ||
|
|
3204
|
+
type === "toolCall" ||
|
|
3205
|
+
type === "ToolResult" ||
|
|
3206
|
+
type === "toolResult";
|
|
3011
3207
|
// Allow deltas if the agent is running OR if we already have an active streaming message
|
|
3012
3208
|
// OR if the server provided a messageId for targeted updates.
|
|
3013
3209
|
// This avoids dropping early deltas that may arrive before the 'running' status update.
|
|
3014
3210
|
// ContextUpdate messages are always allowed as they're metadata updates, not content.
|
|
3015
3211
|
const isRunning = agent.status === "running" || agent.status === 1;
|
|
3016
3212
|
const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
3017
|
-
const hasMessageId = !!message?.payload?.data?.messageId
|
|
3018
|
-
|
|
3019
|
-
|
|
3213
|
+
const hasMessageId = !!message?.payload?.data?.messageId;
|
|
3214
|
+
if (!isContextUpdate &&
|
|
3215
|
+
!isHeartbeat &&
|
|
3216
|
+
!isRunning &&
|
|
3217
|
+
!hasStreaming &&
|
|
3218
|
+
!hasMessageId) {
|
|
3020
3219
|
return; // ignore only if we cannot safely target an existing streaming message
|
|
3021
3220
|
}
|
|
3022
3221
|
// Deduplicate by sequence
|
|
@@ -3033,6 +3232,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3033
3232
|
cost,
|
|
3034
3233
|
timestamp: new Date().toISOString(),
|
|
3035
3234
|
};
|
|
3235
|
+
if (shouldClearHeartbeat) {
|
|
3236
|
+
clearHeartbeatMessages();
|
|
3237
|
+
}
|
|
3036
3238
|
if (type === "ContentChunk" || type === "contentChunk") {
|
|
3037
3239
|
handleContentChunk(agentStreamMessage, agent);
|
|
3038
3240
|
}
|
|
@@ -3042,6 +3244,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3042
3244
|
else if (type === "ToolResult" || type === "toolResult") {
|
|
3043
3245
|
handleToolResult(agentStreamMessage, agent);
|
|
3044
3246
|
}
|
|
3247
|
+
else if (type === "Heartbeat" || type === "heartbeat") {
|
|
3248
|
+
handleHeartbeatMessage(agentStreamMessage);
|
|
3249
|
+
}
|
|
3045
3250
|
else if (type === "ContextUpdate" || type === "contextUpdate") {
|
|
3046
3251
|
// Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
|
|
3047
3252
|
const contextData = data;
|
|
@@ -3051,10 +3256,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3051
3256
|
const current = (prev || {});
|
|
3052
3257
|
const updated = { ...current };
|
|
3053
3258
|
// Merge additionalData if present (deep merge to preserve existing values)
|
|
3054
|
-
if (contextData.additionalData
|
|
3055
|
-
const sourceAdditionalData = contextData.additionalData ||
|
|
3056
|
-
contextData.AdditionalData ||
|
|
3057
|
-
{};
|
|
3259
|
+
if (contextData.additionalData) {
|
|
3260
|
+
const sourceAdditionalData = contextData.additionalData || {};
|
|
3058
3261
|
updated.additionalData = {
|
|
3059
3262
|
...(current.additionalData || {}),
|
|
3060
3263
|
...sourceAdditionalData,
|
|
@@ -3062,10 +3265,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3062
3265
|
}
|
|
3063
3266
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3064
3267
|
// Backend sends the complete updated list, not just additions
|
|
3065
|
-
if (contextData.
|
|
3066
|
-
const childAgents = contextData.
|
|
3268
|
+
if (contextData.childAgents) {
|
|
3269
|
+
const childAgents = contextData.childAgents;
|
|
3067
3270
|
if (Array.isArray(childAgents)) {
|
|
3068
|
-
updated.
|
|
3271
|
+
updated.childAgents = childAgents;
|
|
3069
3272
|
}
|
|
3070
3273
|
}
|
|
3071
3274
|
return updated;
|
|
@@ -3092,13 +3295,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3092
3295
|
try {
|
|
3093
3296
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3094
3297
|
const normalizedStatus = statusData?.state ||
|
|
3095
|
-
statusData?.Status ||
|
|
3096
3298
|
statusData?.status;
|
|
3097
3299
|
if (normalizedStatus === "Cancelled" ||
|
|
3098
3300
|
normalizedStatus === "canceled" ||
|
|
3099
3301
|
normalizedStatus === "stopped" ||
|
|
3100
3302
|
normalizedStatus === "Stopped") {
|
|
3101
3303
|
// Stop indicators and mark any in-progress streaming messages as completed
|
|
3304
|
+
clearHeartbeatMessages();
|
|
3102
3305
|
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3103
3306
|
setIsWaitingForResponse(false);
|
|
3104
3307
|
isWaitingRef.current = false;
|
|
@@ -3201,6 +3404,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3201
3404
|
return;
|
|
3202
3405
|
}
|
|
3203
3406
|
if (statusData?.state === "ToolApprovalsRequired") {
|
|
3407
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3204
3408
|
const msgId = statusData.messageId;
|
|
3205
3409
|
const ids = statusData.toolCallIds || [];
|
|
3206
3410
|
if (msgId && Array.isArray(ids) && ids.length > 0) {
|
|
@@ -3235,6 +3439,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3235
3439
|
// Handle waiting states explicitly
|
|
3236
3440
|
if (statusData?.state === "WaitingForApproval" ||
|
|
3237
3441
|
statusData?.state === "waitingForApproval") {
|
|
3442
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3238
3443
|
setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
|
|
3239
3444
|
setIsConnecting(false);
|
|
3240
3445
|
setIsWaitingForResponse(false);
|
|
@@ -3243,13 +3448,30 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3243
3448
|
}
|
|
3244
3449
|
if (statusData?.state === "WaitingForInput" ||
|
|
3245
3450
|
statusData?.state === "waitingForInput") {
|
|
3246
|
-
|
|
3451
|
+
const dialogType = typeof statusData?.dialogType === "string"
|
|
3452
|
+
? statusData.dialogType
|
|
3453
|
+
: null;
|
|
3454
|
+
const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
3455
|
+
dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
3456
|
+
setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
|
|
3457
|
+
setAgent((prev) => prev
|
|
3458
|
+
? {
|
|
3459
|
+
...prev,
|
|
3460
|
+
status: "waitingForInput",
|
|
3461
|
+
statusMessage: isBrowserCaptureWait &&
|
|
3462
|
+
typeof statusData?.title === "string" &&
|
|
3463
|
+
statusData.title.trim()
|
|
3464
|
+
? statusData.title.trim()
|
|
3465
|
+
: prev.statusMessage,
|
|
3466
|
+
}
|
|
3467
|
+
: prev);
|
|
3247
3468
|
setIsConnecting(false);
|
|
3248
3469
|
setIsWaitingForResponse(false);
|
|
3249
3470
|
setIsAgentThinking(false);
|
|
3250
3471
|
return;
|
|
3251
3472
|
}
|
|
3252
3473
|
if (statusData?.state === "CostLimitReached") {
|
|
3474
|
+
setPendingBrowserCaptureDialogType(null);
|
|
3253
3475
|
const totalCost = Number(statusData.totalCost) || 0;
|
|
3254
3476
|
const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
|
|
3255
3477
|
setCostLimitExceeded({
|
|
@@ -3267,7 +3489,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3267
3489
|
// Server sends additionalData directly (with todoList inside)
|
|
3268
3490
|
// Also may include ChildAgents for spawned agents
|
|
3269
3491
|
try {
|
|
3270
|
-
const serverAdditionalData = statusData.additionalData ||
|
|
3492
|
+
const serverAdditionalData = statusData.additionalData || {};
|
|
3271
3493
|
setAgentMetadata((prev) => {
|
|
3272
3494
|
const current = (prev || {});
|
|
3273
3495
|
const updated = { ...current };
|
|
@@ -3281,10 +3503,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3281
3503
|
}
|
|
3282
3504
|
// Merge ChildAgents if present (for spawned agents) - replace entire array
|
|
3283
3505
|
// Backend sends the complete updated list, not just additions
|
|
3284
|
-
if (statusData.
|
|
3285
|
-
const childAgents = statusData.
|
|
3506
|
+
if (statusData.childAgents) {
|
|
3507
|
+
const childAgents = statusData.childAgents;
|
|
3286
3508
|
if (Array.isArray(childAgents)) {
|
|
3287
|
-
updated.
|
|
3509
|
+
updated.childAgents = childAgents;
|
|
3288
3510
|
}
|
|
3289
3511
|
}
|
|
3290
3512
|
return updated;
|
|
@@ -3300,6 +3522,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3300
3522
|
normalizedStatus === "Completed") {
|
|
3301
3523
|
// Reset deduplication for the next run
|
|
3302
3524
|
lastSeqRef.current = 0;
|
|
3525
|
+
clearHeartbeatMessages();
|
|
3303
3526
|
// Mark the last assistant message as completed
|
|
3304
3527
|
setMessages((prev) => {
|
|
3305
3528
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3325,6 +3548,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3325
3548
|
// Handle "Idle" state - agent has finished processing
|
|
3326
3549
|
if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
|
|
3327
3550
|
// Update agent status to idle
|
|
3551
|
+
clearHeartbeatMessages();
|
|
3328
3552
|
setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
|
|
3329
3553
|
setIsWaitingForResponse(false);
|
|
3330
3554
|
isWaitingRef.current = false;
|
|
@@ -3346,6 +3570,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3346
3570
|
// Handle "Error" state
|
|
3347
3571
|
if (normalizedStatus === "error" || normalizedStatus === "Error") {
|
|
3348
3572
|
const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
|
|
3573
|
+
clearHeartbeatMessages();
|
|
3349
3574
|
setAgent((prev) => prev
|
|
3350
3575
|
? { ...prev, status: "error", statusMessage: errorMsg }
|
|
3351
3576
|
: prev);
|
|
@@ -3365,6 +3590,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3365
3590
|
if (messageType === "agent:run:complete") {
|
|
3366
3591
|
// Reset deduplication for the next run
|
|
3367
3592
|
lastSeqRef.current = 0;
|
|
3593
|
+
clearHeartbeatMessages();
|
|
3368
3594
|
// Mark the last assistant message as completed
|
|
3369
3595
|
setMessages((prev) => {
|
|
3370
3596
|
const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
|
|
@@ -3391,6 +3617,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3391
3617
|
if (messageType === "agent:run:error") {
|
|
3392
3618
|
const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
|
|
3393
3619
|
"AI could not complete this request.";
|
|
3620
|
+
clearHeartbeatMessages();
|
|
3394
3621
|
// Reset deduplication for the next run after an error
|
|
3395
3622
|
lastSeqRef.current = 0;
|
|
3396
3623
|
setError(errorMsg);
|
|
@@ -3405,7 +3632,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3405
3632
|
}
|
|
3406
3633
|
}, [
|
|
3407
3634
|
agent,
|
|
3635
|
+
clearHeartbeatMessages,
|
|
3408
3636
|
handleContentChunk,
|
|
3637
|
+
handleHeartbeatMessage,
|
|
3409
3638
|
handleToolCall,
|
|
3410
3639
|
handleToolResult,
|
|
3411
3640
|
onAgentUpdate,
|
|
@@ -3687,16 +3916,22 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3687
3916
|
if (!candidate)
|
|
3688
3917
|
return;
|
|
3689
3918
|
// Keep active profile in sync whenever the matching entry in `profiles` changes —
|
|
3690
|
-
// not only when the profile id changes. Otherwise
|
|
3919
|
+
// not only when the profile id changes. Otherwise availableSkills (and similar fields)
|
|
3691
3920
|
// that are merged in the parent after async loads never update (e.g. Template Builder
|
|
3692
3921
|
// settings skills merged into the profile).
|
|
3693
3922
|
setActiveProfile((prev) => {
|
|
3694
3923
|
if (!prev || prev.id !== candidate.id)
|
|
3695
3924
|
return candidate;
|
|
3696
|
-
const skillKey = (p) => JSON.stringify(
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3925
|
+
const skillKey = (p) => JSON.stringify({
|
|
3926
|
+
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
3927
|
+
String(s?.id ?? ""),
|
|
3928
|
+
String(s?.name ?? ""),
|
|
3929
|
+
]),
|
|
3930
|
+
available: (p.availableSkills ?? []).map((s) => [
|
|
3931
|
+
String(s?.id ?? ""),
|
|
3932
|
+
String(s?.name ?? ""),
|
|
3933
|
+
]),
|
|
3934
|
+
});
|
|
3700
3935
|
if (skillKey(prev) === skillKey(candidate))
|
|
3701
3936
|
return prev;
|
|
3702
3937
|
return candidate;
|
|
@@ -3792,6 +4027,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3792
4027
|
console.error("Failed to persist pending settings", e);
|
|
3793
4028
|
}
|
|
3794
4029
|
}, [agent?.id]);
|
|
4030
|
+
const getPendingRequestSettings = useCallback(() => {
|
|
4031
|
+
const pending = pendingSettingsRef.current;
|
|
4032
|
+
const requestProfile = pending?.profileId
|
|
4033
|
+
? profiles.find((profile) => profile.id === pending.profileId) ||
|
|
4034
|
+
activeProfile ||
|
|
4035
|
+
profiles[0]
|
|
4036
|
+
: activeProfile || profiles[0];
|
|
4037
|
+
let requestModelId = selectedModelId;
|
|
4038
|
+
if (pending?.modelName) {
|
|
4039
|
+
const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
|
|
4040
|
+
pending.modelName?.trim().toLowerCase());
|
|
4041
|
+
requestModelId = requestModel?.id || requestModelId;
|
|
4042
|
+
}
|
|
4043
|
+
return {
|
|
4044
|
+
mode: (pending?.mode || mode),
|
|
4045
|
+
profileId: pending?.profileId || requestProfile?.id || "",
|
|
4046
|
+
profileName: pending?.profileName || requestProfile?.name || "",
|
|
4047
|
+
modelId: requestModelId,
|
|
4048
|
+
};
|
|
4049
|
+
}, [activeProfile, mode, profiles, selectedModelId]);
|
|
3795
4050
|
const getSubmitErrorMessage = (error) => {
|
|
3796
4051
|
const fallback = "Failed to submit prompt. Please try again.";
|
|
3797
4052
|
if (!(error instanceof Error))
|
|
@@ -3993,16 +4248,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3993
4248
|
console.warn("[AgentTerminal] Failed to compute live context:", e);
|
|
3994
4249
|
}
|
|
3995
4250
|
// Add visible test IDs to context (only for Help agent)
|
|
3996
|
-
const
|
|
4251
|
+
const requestSettings = getPendingRequestSettings();
|
|
4252
|
+
const currentProfileName = requestSettings.profileName;
|
|
3997
4253
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
3998
4254
|
const request = {
|
|
3999
4255
|
agentId: agentId,
|
|
4000
4256
|
message: savedPrompt,
|
|
4001
4257
|
sessionId: editContext.sessionId,
|
|
4002
|
-
profileId:
|
|
4258
|
+
profileId: requestSettings.profileId,
|
|
4003
4259
|
profile: currentProfileName,
|
|
4004
|
-
model:
|
|
4005
|
-
mode: mode,
|
|
4260
|
+
model: requestSettings.modelId,
|
|
4261
|
+
mode: requestSettings.mode,
|
|
4006
4262
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
4007
4263
|
};
|
|
4008
4264
|
console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
|
|
@@ -4251,16 +4507,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4251
4507
|
console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
|
|
4252
4508
|
}
|
|
4253
4509
|
// Add visible test IDs to context (only for Help agent)
|
|
4254
|
-
const
|
|
4510
|
+
const requestSettings = getPendingRequestSettings();
|
|
4511
|
+
const currentProfileName = requestSettings.profileName;
|
|
4255
4512
|
effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
|
|
4256
4513
|
const request = {
|
|
4257
4514
|
agentId: agent.id,
|
|
4258
4515
|
message: savedPrompt,
|
|
4259
4516
|
sessionId: editContext.sessionId,
|
|
4260
|
-
profileId:
|
|
4517
|
+
profileId: requestSettings.profileId,
|
|
4261
4518
|
profile: currentProfileName,
|
|
4262
|
-
model:
|
|
4263
|
-
mode: mode,
|
|
4519
|
+
model: requestSettings.modelId,
|
|
4520
|
+
mode: requestSettings.mode,
|
|
4264
4521
|
context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
|
|
4265
4522
|
};
|
|
4266
4523
|
console.log("[AgentTerminal] Calling startAgent API for quick message");
|
|
@@ -4685,6 +4942,70 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4685
4942
|
activeProfile,
|
|
4686
4943
|
profiles,
|
|
4687
4944
|
]);
|
|
4945
|
+
const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
|
|
4946
|
+
const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
|
|
4947
|
+
pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
|
|
4948
|
+
const currentSessionId = editContext?.sessionId?.trim() || "";
|
|
4949
|
+
const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
|
|
4950
|
+
const isClaimedByCurrentSession = !!currentSessionId &&
|
|
4951
|
+
!!claimedSessionId &&
|
|
4952
|
+
currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
|
|
4953
|
+
const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
|
|
4954
|
+
const handleClaimBrowser = useCallback(async (takeOver) => {
|
|
4955
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
4956
|
+
return;
|
|
4957
|
+
setIsBrowserClaimMutationPending(true);
|
|
4958
|
+
try {
|
|
4959
|
+
const response = await claimAgentBrowser({
|
|
4960
|
+
agentId: agent.id,
|
|
4961
|
+
sessionId: editContext.sessionId,
|
|
4962
|
+
takeOver,
|
|
4963
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4964
|
+
});
|
|
4965
|
+
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
4966
|
+
}
|
|
4967
|
+
catch (err) {
|
|
4968
|
+
console.error("[AgentTerminal] Failed to claim browser:", err);
|
|
4969
|
+
editContext.showErrorToast(err);
|
|
4970
|
+
}
|
|
4971
|
+
finally {
|
|
4972
|
+
setIsBrowserClaimMutationPending(false);
|
|
4973
|
+
}
|
|
4974
|
+
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
4975
|
+
const handleReleaseBrowser = useCallback(async () => {
|
|
4976
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
4977
|
+
return;
|
|
4978
|
+
setIsBrowserClaimMutationPending(true);
|
|
4979
|
+
try {
|
|
4980
|
+
const response = await releaseAgentBrowser({
|
|
4981
|
+
agentId: agent.id,
|
|
4982
|
+
sessionId: editContext.sessionId,
|
|
4983
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
4984
|
+
});
|
|
4985
|
+
setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
|
|
4986
|
+
}
|
|
4987
|
+
catch (err) {
|
|
4988
|
+
console.error("[AgentTerminal] Failed to release browser:", err);
|
|
4989
|
+
editContext.showErrorToast(err);
|
|
4990
|
+
}
|
|
4991
|
+
finally {
|
|
4992
|
+
setIsBrowserClaimMutationPending(false);
|
|
4993
|
+
}
|
|
4994
|
+
}, [agent?.id, editContext, sanitizeAgentMetadata]);
|
|
4995
|
+
useEffect(() => {
|
|
4996
|
+
return () => {
|
|
4997
|
+
if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
|
|
4998
|
+
return;
|
|
4999
|
+
}
|
|
5000
|
+
void releaseAgentBrowser({
|
|
5001
|
+
agentId: agent.id,
|
|
5002
|
+
sessionId: editContext.sessionId,
|
|
5003
|
+
terminalInstanceId: dialogTerminalInstanceIdRef.current,
|
|
5004
|
+
}).catch((error) => {
|
|
5005
|
+
console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
|
|
5006
|
+
});
|
|
5007
|
+
};
|
|
5008
|
+
}, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
|
|
4688
5009
|
// Stop current execution/stream safely
|
|
4689
5010
|
const handleStop = useCallback(async () => {
|
|
4690
5011
|
try {
|
|
@@ -4817,8 +5138,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4817
5138
|
// We only want these global thinking dots if the last message was from the user
|
|
4818
5139
|
// or if no messages exist yet (waiting for initial response).
|
|
4819
5140
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
4820
|
-
if (isExecuting &&
|
|
5141
|
+
if (isExecuting &&
|
|
5142
|
+
lastMessage?.role === "assistant" &&
|
|
5143
|
+
!lastMessage.isCompleted) {
|
|
4821
5144
|
return false;
|
|
5145
|
+
}
|
|
4822
5146
|
// Existing check for uncompleted assistant messages
|
|
4823
5147
|
const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
|
|
4824
5148
|
if (hasActiveStreamingMessage)
|
|
@@ -4898,11 +5222,49 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4898
5222
|
const renderErrorBanner = () => {
|
|
4899
5223
|
const currentAgent = agent || agentStub;
|
|
4900
5224
|
const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
|
|
4901
|
-
const errorMessage = currentAgent?.statusMessage;
|
|
4902
|
-
if (!
|
|
5225
|
+
const errorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
|
|
5226
|
+
if (!errorMessage)
|
|
4903
5227
|
return null;
|
|
4904
5228
|
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 })] })] }) }));
|
|
4905
5229
|
};
|
|
5230
|
+
const renderBrowserClaimBanner = (variant = "inline") => {
|
|
5231
|
+
if (!agent?.id || !editContext?.sessionId)
|
|
5232
|
+
return null;
|
|
5233
|
+
if (!isClaimedByCurrentSession &&
|
|
5234
|
+
!isClaimedByAnotherBrowser &&
|
|
5235
|
+
!isPendingBrowserCaptureWait) {
|
|
5236
|
+
return null;
|
|
5237
|
+
}
|
|
5238
|
+
const label = isClaimedByCurrentSession
|
|
5239
|
+
? "Attached to this browser"
|
|
5240
|
+
: isClaimedByAnotherBrowser
|
|
5241
|
+
? "Attached in another browser"
|
|
5242
|
+
: "No browser attached";
|
|
5243
|
+
const description = isClaimedByCurrentSession
|
|
5244
|
+
? "This browser will handle page screenshot and DOM capture requests for the agent."
|
|
5245
|
+
: isClaimedByAnotherBrowser
|
|
5246
|
+
? "Capture requests will stay with the other browser until you take over control here."
|
|
5247
|
+
: "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
|
|
5248
|
+
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");
|
|
5249
|
+
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: () => {
|
|
5250
|
+
void handleReleaseBrowser();
|
|
5251
|
+
}, 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: () => {
|
|
5252
|
+
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5253
|
+
}, children: isClaimedByAnotherBrowser
|
|
5254
|
+
? "Take over browser control"
|
|
5255
|
+
: "Attach to this browser" })) })] }) }));
|
|
5256
|
+
};
|
|
5257
|
+
const fixedBrowserClaimBanner = isClaimedByCurrentSession
|
|
5258
|
+
? renderBrowserClaimBanner("fixed")
|
|
5259
|
+
: null;
|
|
5260
|
+
const inlineBrowserClaimBanner = !isClaimedByCurrentSession
|
|
5261
|
+
? renderBrowserClaimBanner("inline")
|
|
5262
|
+
: null;
|
|
5263
|
+
useEffect(() => {
|
|
5264
|
+
if (agent?.status !== "waitingForInput") {
|
|
5265
|
+
setPendingBrowserCaptureDialogType(null);
|
|
5266
|
+
}
|
|
5267
|
+
}, [agent?.status]);
|
|
4906
5268
|
const renderInlineDialogContent = () => {
|
|
4907
5269
|
if (!activeInlineDialog)
|
|
4908
5270
|
return null;
|
|
@@ -4962,10 +5324,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4962
5324
|
const summaryOperations = latestSummaryAssistantGroup
|
|
4963
5325
|
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
4964
5326
|
: [];
|
|
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 &&
|
|
5327
|
+
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
5328
|
!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,
|
|
5329
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5330
|
+
} })) : (_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
5331
|
activeProfile?.displayTitle ||
|
|
4970
5332
|
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
4971
5333
|
const text = (action.prompt ||
|
|
@@ -4989,7 +5351,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4989
5351
|
shouldShowThinkingDots &&
|
|
4990
5352
|
!inlineDialog &&
|
|
4991
5353
|
!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,
|
|
5354
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
4993
5355
|
} })) : (_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
5356
|
activeProfile?.displayTitle ||
|
|
4995
5357
|
activeProfile?.name ||
|
|
@@ -5076,7 +5438,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5076
5438
|
})()
|
|
5077
5439
|
: null;
|
|
5078
5440
|
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: [error && !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 })] })] }) })), 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) => {
|
|
5441
|
+
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: [error && !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 })] })] }) })), 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
5442
|
setPrompt(p);
|
|
5081
5443
|
// Use setTimeout to ensure state is updated before submission
|
|
5082
5444
|
setTimeout(() => {
|
|
@@ -5090,8 +5452,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5090
5452
|
}
|
|
5091
5453
|
}, 0);
|
|
5092
5454
|
} })) })), 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: [(() => {
|
|
5455
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5456
|
+
} })) : (_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
5457
|
const groups = groupConsecutiveMessages(messages);
|
|
5096
5458
|
return groups.map((group, groupIndex) => {
|
|
5097
5459
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -5099,6 +5461,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5099
5461
|
// Render user message
|
|
5100
5462
|
return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
|
|
5101
5463
|
}
|
|
5464
|
+
else if (group.type === "heartbeat" && group.messages[0]) {
|
|
5465
|
+
return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
|
|
5466
|
+
}
|
|
5102
5467
|
else {
|
|
5103
5468
|
// Render bundled assistant messages
|
|
5104
5469
|
// Check if this group contains any streaming message
|
|
@@ -5115,7 +5480,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5115
5480
|
}
|
|
5116
5481
|
const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
|
|
5117
5482
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5118
|
-
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup,
|
|
5483
|
+
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
|
|
5119
5484
|
activeProfile?.displayTitle ||
|
|
5120
5485
|
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5121
5486
|
const text = (action.prompt ||
|
|
@@ -5161,7 +5526,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5161
5526
|
}
|
|
5162
5527
|
});
|
|
5163
5528
|
})(), 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,
|
|
5529
|
+
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5165
5530
|
} })) : (_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
5531
|
activeProfile?.displayTitle ||
|
|
5167
5532
|
activeProfile?.name ||
|
|
@@ -5220,10 +5585,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5220
5585
|
if (qp.data) {
|
|
5221
5586
|
try {
|
|
5222
5587
|
const parsed = JSON.parse(qp.data);
|
|
5223
|
-
triggerName =
|
|
5224
|
-
parsed?.TriggerName?.trim() ||
|
|
5225
|
-
parsed?.triggerName?.trim() ||
|
|
5226
|
-
"";
|
|
5588
|
+
triggerName = parsed?.triggerName?.trim() || "";
|
|
5227
5589
|
}
|
|
5228
5590
|
catch {
|
|
5229
5591
|
// Ignore invalid JSON metadata and render as regular queued prompt.
|
|
@@ -5337,13 +5699,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5337
5699
|
return null;
|
|
5338
5700
|
return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
|
|
5339
5701
|
? "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"
|
|
5702
|
+
: "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
5703
|
? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
|
|
5342
5704
|
: mode === "supervised"
|
|
5343
5705
|
? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
|
|
5344
5706
|
: "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
|
|
5345
5707
|
const nextMode = val || "supervised";
|
|
5346
|
-
setMode(nextMode);
|
|
5347
5708
|
const current = agentMetadata || {};
|
|
5348
5709
|
const nextMeta = {
|
|
5349
5710
|
...current,
|
|
@@ -5351,6 +5712,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5351
5712
|
};
|
|
5352
5713
|
try {
|
|
5353
5714
|
if (!agent?.id || agent.status === "new") {
|
|
5715
|
+
setMode(nextMode);
|
|
5354
5716
|
setAgentMetadata(nextMeta);
|
|
5355
5717
|
pendingSettingsRef.current = {
|
|
5356
5718
|
...(pendingSettingsRef.current || {}),
|
|
@@ -5358,13 +5720,18 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5358
5720
|
};
|
|
5359
5721
|
return;
|
|
5360
5722
|
}
|
|
5361
|
-
await updateAgentSettings(agent.id, {
|
|
5723
|
+
const result = await updateAgentSettings(agent.id, {
|
|
5362
5724
|
mode: nextMode,
|
|
5363
5725
|
});
|
|
5726
|
+
if (result.success === false || result.updates?.mode === false) {
|
|
5727
|
+
throw new Error("Mode change was not applied");
|
|
5728
|
+
}
|
|
5729
|
+
setMode(nextMode);
|
|
5364
5730
|
setAgentMetadata(nextMeta);
|
|
5365
5731
|
setAgent((prev) => prev
|
|
5366
5732
|
? {
|
|
5367
5733
|
...prev,
|
|
5734
|
+
mode: nextMode,
|
|
5368
5735
|
metadata: JSON.stringify(nextMeta),
|
|
5369
5736
|
}
|
|
5370
5737
|
: prev);
|
|
@@ -5411,6 +5778,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5411
5778
|
setAgent((prev) => prev
|
|
5412
5779
|
? {
|
|
5413
5780
|
...prev,
|
|
5781
|
+
profileId: nextProfile.id,
|
|
5782
|
+
profileName: nextProfile.name,
|
|
5414
5783
|
metadata: JSON.stringify({
|
|
5415
5784
|
...(agentMetadata || {}),
|
|
5416
5785
|
profile: nextProfile.name,
|
|
@@ -5475,15 +5844,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5475
5844
|
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
5476
5845
|
!skillsError &&
|
|
5477
5846
|
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
5478
|
-
? "All allowed skills are selected"
|
|
5479
|
-
: "No skills
|
|
5847
|
+
? "All available/allowed skills are selected"
|
|
5848
|
+
: "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
5849
|
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
5481
5850
|
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
5851
|
void handleOpenSkillItem(skillId);
|
|
5483
|
-
}, children: _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }),
|
|
5852
|
+
}, 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
5853
|
void handleRemoveSkill(skillId);
|
|
5485
5854
|
}, 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: () =>
|
|
5855
|
+
}) }))] }), _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
|
|
5856
|
+
? "No available tools for this profile and mode"
|
|
5857
|
+
: "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
5858
|
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
5488
5859
|
const pathLabel = "itemPath" in allowance
|
|
5489
5860
|
? allowance.itemPath
|
|
@@ -5496,10 +5867,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5496
5867
|
]
|
|
5497
5868
|
.filter(Boolean)
|
|
5498
5869
|
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
5499
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
5870
|
+
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
|
|
5871
|
+
? "Allowances are shown after the agent is created"
|
|
5872
|
+
: "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
5873
|
const filterText = (sub.filter || "").trim();
|
|
5501
5874
|
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:
|
|
5875
|
+
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
|
|
5876
|
+
? "Subscribed triggers are shown after the agent is created"
|
|
5877
|
+
: "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
5878
|
setPrompt(p.prompt);
|
|
5504
5879
|
setShowPredefined(false);
|
|
5505
5880
|
if (textareaRef.current)
|