@parhelia/core 0.1.12517 → 0.1.12534
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/config/config.js +13 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +9 -0
- package/dist/config/types.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -1
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +345 -87
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +4 -1
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +9 -1
- package/dist/editor/ai/AiResponseMessage.js +2 -2
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +11 -2
- package/dist/editor/ai/ToolCallDisplay.js +161 -7
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/dialogs/browserBoundCapture.js +18 -3
- package/dist/editor/ai/dialogs/browserBoundCapture.js.map +1 -1
- package/dist/editor/ai/types.d.ts +1 -1
- package/dist/editor/client/editContext.d.ts +1 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +18 -0
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/context-menu/InsertMenu.js +46 -23
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/hooks/useNavigationPanelLogic.js +6 -1
- package/dist/editor/hooks/useNavigationPanelLogic.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +102 -15
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +9 -2
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.d.ts +1 -1
- package/dist/editor/page-viewer/MiniMap.js +18 -6
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/RenderingParametersSection.d.ts +6 -0
- package/dist/editor/page-viewer/RenderingParametersSection.js +147 -0
- package/dist/editor/page-viewer/RenderingParametersSection.js.map +1 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +7 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/pageModel.d.ts +4 -0
- package/dist/editor/reviews/SuggestedEdit.js +4 -1
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +24 -0
- package/dist/editor/services/agentService.js +34 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +1 -0
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +17 -0
- package/dist/editor/services/editService.js +12 -0
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.js +1 -36
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/NavigationPanelItem.js +5 -4
- package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
- package/dist/editor/sidebar/OperationItem.js +1 -0
- package/dist/editor/sidebar/OperationItem.js.map +1 -1
- package/dist/editor/sidebar/SidebarPanel.js +10 -3
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +22 -12
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/ui/Splitter.js +2 -2
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/components/TaskAttachmentsSection.js +2 -1
- package/dist/task-board/components/TaskAttachmentsSection.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +10 -7
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/components/WizardTaskDetailsPanel.js +3 -2
- package/dist/task-board/components/WizardTaskDetailsPanel.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/package.json +1 -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, claimAgentBrowser, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, } from "../services/agentService";
|
|
4
|
+
import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
|
|
5
5
|
import { parseAgentStatus } from "../services/agentStatus";
|
|
6
6
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
7
7
|
import { localStorageService } from "../services/localStorageService";
|
|
@@ -31,14 +31,14 @@ import { Splitter } from "../ui/Splitter";
|
|
|
31
31
|
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
32
32
|
import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
|
|
33
33
|
const userMessageMarkdownComponents = {
|
|
34
|
-
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm font-semibold
|
|
35
|
-
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] font-semibold
|
|
36
|
-
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] font-semibold
|
|
37
|
-
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] font-medium
|
|
38
|
-
p: (props) => _jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" }),
|
|
34
|
+
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
|
|
35
|
+
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
|
|
36
|
+
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
|
|
37
|
+
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
|
|
38
|
+
p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
|
|
39
39
|
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
40
40
|
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
41
|
-
li: (props) => _jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" }),
|
|
41
|
+
li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
|
|
42
42
|
pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
|
|
43
43
|
code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
|
|
44
44
|
};
|
|
@@ -149,25 +149,36 @@ function toUserFacingAgentErrorMessage(value) {
|
|
|
149
149
|
if (!normalizedValue)
|
|
150
150
|
return "";
|
|
151
151
|
const trimmed = normalizedValue.trim();
|
|
152
|
-
const maybeJson = trimmed.startsWith("{") ||
|
|
152
|
+
const maybeJson = trimmed.startsWith("{") ||
|
|
153
|
+
trimmed.startsWith("[") ||
|
|
154
|
+
trimmed.startsWith('"');
|
|
153
155
|
if (maybeJson) {
|
|
154
156
|
try {
|
|
155
157
|
const parsed = JSON.parse(trimmed);
|
|
156
158
|
const structuredMessage = typeof parsed === "string"
|
|
157
159
|
? parsed
|
|
158
|
-
: typeof parsed?.error === "
|
|
159
|
-
?
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
160
|
+
: typeof parsed?.error === "object" && parsed?.error
|
|
161
|
+
? (() => {
|
|
162
|
+
const errObj = parsed.error;
|
|
163
|
+
// Extract detailed message from metadata.raw (OpenRouter-style nested errors)
|
|
164
|
+
if (typeof errObj.metadata?.raw ===
|
|
165
|
+
"string") {
|
|
166
|
+
return String(errObj.metadata.raw);
|
|
167
|
+
}
|
|
168
|
+
// Fall back to error.message
|
|
169
|
+
if (typeof errObj.message === "string") {
|
|
170
|
+
return errObj.message;
|
|
171
|
+
}
|
|
172
|
+
return "";
|
|
173
|
+
})()
|
|
174
|
+
: typeof parsed?.error === "string"
|
|
175
|
+
? parsed.error
|
|
176
|
+
: typeof parsed?.message === "string"
|
|
177
|
+
? parsed.message
|
|
178
|
+
: typeof parsed?.detail === "string"
|
|
179
|
+
? parsed.detail
|
|
180
|
+
: typeof parsed?.error_description === "string"
|
|
181
|
+
? parsed.error_description
|
|
171
182
|
: "";
|
|
172
183
|
if (structuredMessage.trim()) {
|
|
173
184
|
value = structuredMessage;
|
|
@@ -607,7 +618,8 @@ const groupConsecutiveMessages = (agentMessages) => {
|
|
|
607
618
|
// Add user message
|
|
608
619
|
groups.push({ type: "user", messages: [message] });
|
|
609
620
|
}
|
|
610
|
-
else if (message.messageType === "heartbeat" ||
|
|
621
|
+
else if (message.messageType === "heartbeat" ||
|
|
622
|
+
message.role === "system") {
|
|
611
623
|
if (currentAssistantGroup.length > 0) {
|
|
612
624
|
groups.push({
|
|
613
625
|
type: "assistant-group",
|
|
@@ -720,6 +732,35 @@ const stringifyToolField = (value) => {
|
|
|
720
732
|
return String(value);
|
|
721
733
|
}
|
|
722
734
|
};
|
|
735
|
+
const parseToolResultValue = (value) => {
|
|
736
|
+
if (value === undefined || value === null) {
|
|
737
|
+
return undefined;
|
|
738
|
+
}
|
|
739
|
+
if (typeof value === "object") {
|
|
740
|
+
return value;
|
|
741
|
+
}
|
|
742
|
+
if (typeof value !== "string") {
|
|
743
|
+
return String(value);
|
|
744
|
+
}
|
|
745
|
+
const trimmed = value.trim();
|
|
746
|
+
if (!trimmed) {
|
|
747
|
+
return undefined;
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
let parsed = JSON.parse(trimmed);
|
|
751
|
+
if (typeof parsed === "string" &&
|
|
752
|
+
(parsed.startsWith("{") || parsed.startsWith("["))) {
|
|
753
|
+
parsed = JSON.parse(parsed);
|
|
754
|
+
}
|
|
755
|
+
if (parsed && typeof parsed === "object") {
|
|
756
|
+
return parsed;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
catch {
|
|
760
|
+
// Fall back to the original string when the payload is plain text.
|
|
761
|
+
}
|
|
762
|
+
return value;
|
|
763
|
+
};
|
|
723
764
|
const getFirstToolCallEnvelope = (data) => {
|
|
724
765
|
if (!data || typeof data !== "object")
|
|
725
766
|
return undefined;
|
|
@@ -779,13 +820,14 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
779
820
|
? agentMessage.toolCalls.map((toolCall) => {
|
|
780
821
|
const isPruned = !!toolCall.isPruned ||
|
|
781
822
|
/^PRUNED$/i.test(toolCall.functionError || "");
|
|
823
|
+
const displayResult = parseToolResultValue(toolCall.functionResultRichContent) ?? toolCall.functionResult;
|
|
782
824
|
return {
|
|
783
825
|
id: toolCall.toolCallId,
|
|
784
826
|
displayName: toolCall.functionName,
|
|
785
827
|
function: {
|
|
786
828
|
name: toolCall.functionName,
|
|
787
829
|
arguments: toolCall.functionArguments,
|
|
788
|
-
result:
|
|
830
|
+
result: displayResult,
|
|
789
831
|
error: toolCall.functionError,
|
|
790
832
|
},
|
|
791
833
|
// Pass through approval info if present on the tool call
|
|
@@ -798,7 +840,7 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
|
798
840
|
// Tool call is streaming if message is not completed and tool call has no result yet
|
|
799
841
|
isStreaming: !agentMessage.isCompleted &&
|
|
800
842
|
!toolCall.isCompleted &&
|
|
801
|
-
!
|
|
843
|
+
!displayResult &&
|
|
802
844
|
!toolCall.functionError &&
|
|
803
845
|
!isPruned,
|
|
804
846
|
// Pass through message IDs for approval/rejection events
|
|
@@ -1087,6 +1129,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1087
1129
|
const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
|
|
1088
1130
|
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
1089
1131
|
const [skillsError, setSkillsError] = useState(null);
|
|
1132
|
+
const [skillActionError, setSkillActionError] = useState(null);
|
|
1090
1133
|
const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
|
|
1091
1134
|
const [availableTools, setAvailableTools] = useState([]);
|
|
1092
1135
|
const [operationAllowances, setOperationAllowances] = useState({
|
|
@@ -1102,7 +1145,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1102
1145
|
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1103
1146
|
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1104
1147
|
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1105
|
-
const
|
|
1148
|
+
const isPersistedAgent = !!agent?.userId;
|
|
1149
|
+
const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
|
|
1106
1150
|
const hasSpawnedAgents = useMemo(() => {
|
|
1107
1151
|
if (!agentMetadata)
|
|
1108
1152
|
return false;
|
|
@@ -1289,9 +1333,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1289
1333
|
.filter((id) => id.length > 0);
|
|
1290
1334
|
}, [activeProfile?.preloadSkills]);
|
|
1291
1335
|
const autoAssignedSkillIds = useMemo(() => {
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1336
|
+
const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
|
|
1337
|
+
const all = isLocalOnlyDraftAgent
|
|
1338
|
+
? preloadedSkillIds
|
|
1339
|
+
: backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
|
|
1295
1340
|
const seen = new Set();
|
|
1296
1341
|
const unique = [];
|
|
1297
1342
|
for (const id of all) {
|
|
@@ -1302,9 +1347,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1302
1347
|
unique.push(id);
|
|
1303
1348
|
}
|
|
1304
1349
|
return unique;
|
|
1305
|
-
}, [backendAssignedSkillIds,
|
|
1350
|
+
}, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
|
|
1306
1351
|
const selectedSkillIds = useMemo(() => {
|
|
1307
|
-
const all = [
|
|
1352
|
+
const all = [
|
|
1353
|
+
...autoAssignedSkillIds,
|
|
1354
|
+
...backendAssignedSkillIds,
|
|
1355
|
+
...metadataSelectedSkillIds,
|
|
1356
|
+
];
|
|
1308
1357
|
const seen = new Set();
|
|
1309
1358
|
const unique = [];
|
|
1310
1359
|
for (const id of all) {
|
|
@@ -1315,7 +1364,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1315
1364
|
unique.push(id);
|
|
1316
1365
|
}
|
|
1317
1366
|
return unique;
|
|
1318
|
-
}, [autoAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1367
|
+
}, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
|
|
1319
1368
|
useEffect(() => {
|
|
1320
1369
|
let active = true;
|
|
1321
1370
|
if (!showAgentSettings) {
|
|
@@ -1323,7 +1372,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1323
1372
|
active = false;
|
|
1324
1373
|
};
|
|
1325
1374
|
}
|
|
1326
|
-
if (!agent?.id ||
|
|
1375
|
+
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
1327
1376
|
setTriggerSubscriptions([]);
|
|
1328
1377
|
setTriggerSubscriptionsLoading(false);
|
|
1329
1378
|
setTriggerSubscriptionsError(null);
|
|
@@ -1407,6 +1456,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1407
1456
|
showAgentSettings,
|
|
1408
1457
|
agent?.id,
|
|
1409
1458
|
agent?.status,
|
|
1459
|
+
agent?.userId,
|
|
1410
1460
|
agent?.profileId,
|
|
1411
1461
|
mode,
|
|
1412
1462
|
selectedSkillIds,
|
|
@@ -1444,7 +1494,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1444
1494
|
return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
|
|
1445
1495
|
}, [availableSkills, listedProfileSkillIdSet]);
|
|
1446
1496
|
const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
|
|
1447
|
-
const
|
|
1497
|
+
const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
|
|
1498
|
+
.map((skill) => String(skill?.id || "").toLowerCase())
|
|
1499
|
+
.filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
|
|
1448
1500
|
const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
|
|
1449
1501
|
const selectedSkills = useMemo(() => selectedSkillIds
|
|
1450
1502
|
.map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
|
|
@@ -1453,8 +1505,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1453
1505
|
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1454
1506
|
const previewAvailableTools = useMemo(() => {
|
|
1455
1507
|
const baseToolNames = mode === "read-only"
|
|
1456
|
-
? activeProfile?.readOnlyToolNames ?? []
|
|
1457
|
-
: activeProfile?.allowedToolNames ?? [];
|
|
1508
|
+
? (activeProfile?.readOnlyToolNames ?? [])
|
|
1509
|
+
: (activeProfile?.allowedToolNames ?? []);
|
|
1458
1510
|
const toolNames = new Set();
|
|
1459
1511
|
for (const toolName of baseToolNames) {
|
|
1460
1512
|
const normalized = String(toolName || "").trim();
|
|
@@ -1477,13 +1529,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1477
1529
|
mode,
|
|
1478
1530
|
selectedSkills,
|
|
1479
1531
|
]);
|
|
1480
|
-
const displayedAvailableTools =
|
|
1532
|
+
const displayedAvailableTools = isLocalOnlyDraftAgent
|
|
1481
1533
|
? previewAvailableTools
|
|
1482
1534
|
: availableTools;
|
|
1483
|
-
const displayedAvailableToolsLoading =
|
|
1535
|
+
const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
|
|
1484
1536
|
? false
|
|
1485
1537
|
: availableToolsLoading;
|
|
1486
|
-
const displayedAvailableToolsError =
|
|
1538
|
+
const displayedAvailableToolsError = isLocalOnlyDraftAgent
|
|
1539
|
+
? null
|
|
1540
|
+
: availableToolsError;
|
|
1487
1541
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1488
1542
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1489
1543
|
try {
|
|
@@ -1508,20 +1562,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1508
1562
|
return meta;
|
|
1509
1563
|
}
|
|
1510
1564
|
}, []);
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
...currentAdditionalData,
|
|
1518
|
-
};
|
|
1519
|
-
if (skillIds.length > 0) {
|
|
1520
|
-
nextAdditionalData.skillIds = skillIds;
|
|
1565
|
+
const getSkillActionErrorMessage = useCallback((error) => {
|
|
1566
|
+
const message = error instanceof Error && error.message.trim()
|
|
1567
|
+
? error.message.trim()
|
|
1568
|
+
: "Failed to update skill";
|
|
1569
|
+
if (message.includes("Skill is not available for this agent profile")) {
|
|
1570
|
+
return "This skill cannot be added for the current agent profile.";
|
|
1521
1571
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1572
|
+
return message;
|
|
1573
|
+
}, []);
|
|
1574
|
+
const clearLegacySelectedSkillsFromMetadata = useCallback(async () => {
|
|
1575
|
+
const legacySkillIds = metadataSelectedSkillIds;
|
|
1576
|
+
if (!agent?.id || legacySkillIds.length === 0) {
|
|
1577
|
+
return;
|
|
1524
1578
|
}
|
|
1579
|
+
const current = agentMetadata || {};
|
|
1580
|
+
const currentAdditionalData = current.additionalData ||
|
|
1581
|
+
{};
|
|
1582
|
+
const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
|
|
1525
1583
|
const next = {
|
|
1526
1584
|
...current,
|
|
1527
1585
|
};
|
|
@@ -1532,26 +1590,168 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1532
1590
|
delete next.additionalData;
|
|
1533
1591
|
}
|
|
1534
1592
|
try {
|
|
1535
|
-
if (agent.status === "new") {
|
|
1536
|
-
setAgentMetadata(next);
|
|
1537
|
-
return;
|
|
1538
|
-
}
|
|
1539
1593
|
await updateAgentContext(agent.id, next);
|
|
1540
1594
|
setAgentMetadata(next);
|
|
1541
1595
|
setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
|
|
1542
1596
|
}
|
|
1543
1597
|
catch (e) {
|
|
1544
|
-
console.error("Failed to
|
|
1598
|
+
console.error("Failed to clear legacy selected skills", e);
|
|
1599
|
+
}
|
|
1600
|
+
}, [
|
|
1601
|
+
agent?.id,
|
|
1602
|
+
agentMetadata,
|
|
1603
|
+
metadataSelectedSkillIds,
|
|
1604
|
+
setAgent,
|
|
1605
|
+
setAgentMetadata,
|
|
1606
|
+
]);
|
|
1607
|
+
const parsePersistedAgentContext = (contextJson) => {
|
|
1608
|
+
try {
|
|
1609
|
+
if (!contextJson)
|
|
1610
|
+
return null;
|
|
1611
|
+
const parsedContext = JSON.parse(contextJson);
|
|
1612
|
+
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1613
|
+
return null;
|
|
1614
|
+
}
|
|
1615
|
+
return sanitizeAgentMetadata(parsedContext);
|
|
1616
|
+
}
|
|
1617
|
+
catch {
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
const buildDraftPersistenceContext = () => {
|
|
1622
|
+
let effectiveContext = agentMetadata;
|
|
1623
|
+
try {
|
|
1624
|
+
const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
|
|
1625
|
+
const profile = activeProfile ||
|
|
1626
|
+
profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
|
|
1627
|
+
const isLiveMode = profile?.editorContextMode === "live";
|
|
1628
|
+
if (isLiveMode && typeof buildCurrentContext === "function") {
|
|
1629
|
+
const freshContext = buildCurrentContext();
|
|
1630
|
+
if (freshContext) {
|
|
1631
|
+
effectiveContext = {
|
|
1632
|
+
...(agentMetadata || {}),
|
|
1633
|
+
items: freshContext.items,
|
|
1634
|
+
currentItemId: freshContext.currentItemId,
|
|
1635
|
+
components: freshContext.components,
|
|
1636
|
+
field: freshContext.field,
|
|
1637
|
+
activeWorkspace: freshContext.activeWorkspace,
|
|
1638
|
+
hasPageLoaded: freshContext.hasPageLoaded,
|
|
1639
|
+
openSidebars: freshContext.openSidebars,
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
catch (e) {
|
|
1645
|
+
console.warn("[AgentTerminal] Failed to compute draft context:", e);
|
|
1545
1646
|
}
|
|
1546
|
-
|
|
1647
|
+
return sanitizeAgentMetadata(effectiveContext || null);
|
|
1648
|
+
};
|
|
1649
|
+
const ensureDraftAgentPersisted = async () => {
|
|
1650
|
+
if (!agent?.id) {
|
|
1651
|
+
throw new Error("Agent not ready. Please try again.");
|
|
1652
|
+
}
|
|
1653
|
+
if (!isLocalOnlyDraftAgent) {
|
|
1654
|
+
return agent;
|
|
1655
|
+
}
|
|
1656
|
+
const requestSettings = getPendingRequestSettings();
|
|
1657
|
+
if (!requestSettings.profileId) {
|
|
1658
|
+
throw new Error("Select an agent profile before adding a skill.");
|
|
1659
|
+
}
|
|
1660
|
+
const effectiveContext = buildDraftPersistenceContext();
|
|
1661
|
+
const persistedAgent = await persistDraftAgent({
|
|
1662
|
+
agentId: agent.id,
|
|
1663
|
+
sessionId: editContext?.sessionId || undefined,
|
|
1664
|
+
profileId: requestSettings.profileId,
|
|
1665
|
+
mode: requestSettings.mode,
|
|
1666
|
+
model: requestSettings.modelId || undefined,
|
|
1667
|
+
name: agent.name,
|
|
1668
|
+
context: effectiveContext,
|
|
1669
|
+
});
|
|
1670
|
+
pendingSettingsRef.current = null;
|
|
1671
|
+
const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
|
|
1672
|
+
effectiveContext;
|
|
1673
|
+
setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
|
|
1674
|
+
setAgent((prev) => {
|
|
1675
|
+
const next = {
|
|
1676
|
+
...(prev || {}),
|
|
1677
|
+
...persistedAgent,
|
|
1678
|
+
};
|
|
1679
|
+
if (prev?.messages?.length && !persistedAgent.messages?.length) {
|
|
1680
|
+
next.messages = prev.messages;
|
|
1681
|
+
}
|
|
1682
|
+
return next;
|
|
1683
|
+
});
|
|
1684
|
+
onAgentUpdate?.(persistedAgent);
|
|
1685
|
+
return persistedAgent;
|
|
1686
|
+
};
|
|
1547
1687
|
const handleAddSkill = useCallback(async (skillId) => {
|
|
1548
1688
|
if (selectedSkillSet.has(skillId.toLowerCase()))
|
|
1549
1689
|
return;
|
|
1550
|
-
|
|
1551
|
-
|
|
1690
|
+
if (!agent?.id)
|
|
1691
|
+
return;
|
|
1692
|
+
try {
|
|
1693
|
+
setSkillActionError(null);
|
|
1694
|
+
const persistedAgent = await ensureDraftAgentPersisted();
|
|
1695
|
+
await assignAgentSkill({ agentId: persistedAgent.id, skillId });
|
|
1696
|
+
setAgent((prev) => {
|
|
1697
|
+
if (!prev)
|
|
1698
|
+
return prev;
|
|
1699
|
+
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1700
|
+
? prev.assignedSkillIds
|
|
1701
|
+
: [];
|
|
1702
|
+
if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
|
|
1703
|
+
return prev;
|
|
1704
|
+
}
|
|
1705
|
+
return {
|
|
1706
|
+
...prev,
|
|
1707
|
+
assignedSkillIds: [...currentAssigned, skillId],
|
|
1708
|
+
};
|
|
1709
|
+
});
|
|
1710
|
+
await clearLegacySelectedSkillsFromMetadata();
|
|
1711
|
+
return true;
|
|
1712
|
+
}
|
|
1713
|
+
catch (e) {
|
|
1714
|
+
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1715
|
+
return false;
|
|
1716
|
+
}
|
|
1717
|
+
}, [
|
|
1718
|
+
agent?.id,
|
|
1719
|
+
assignAgentSkill,
|
|
1720
|
+
clearLegacySelectedSkillsFromMetadata,
|
|
1721
|
+
ensureDraftAgentPersisted,
|
|
1722
|
+
getSkillActionErrorMessage,
|
|
1723
|
+
setAgent,
|
|
1724
|
+
selectedSkillSet,
|
|
1725
|
+
]);
|
|
1552
1726
|
const handleRemoveSkill = useCallback(async (skillId) => {
|
|
1553
|
-
|
|
1554
|
-
|
|
1727
|
+
if (!agent?.id)
|
|
1728
|
+
return;
|
|
1729
|
+
try {
|
|
1730
|
+
setSkillActionError(null);
|
|
1731
|
+
await revokeAgentSkill({ agentId: agent.id, skillId });
|
|
1732
|
+
setAgent((prev) => {
|
|
1733
|
+
if (!prev)
|
|
1734
|
+
return prev;
|
|
1735
|
+
const currentAssigned = Array.isArray(prev.assignedSkillIds)
|
|
1736
|
+
? prev.assignedSkillIds
|
|
1737
|
+
: [];
|
|
1738
|
+
return {
|
|
1739
|
+
...prev,
|
|
1740
|
+
assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
|
|
1741
|
+
};
|
|
1742
|
+
});
|
|
1743
|
+
await clearLegacySelectedSkillsFromMetadata();
|
|
1744
|
+
}
|
|
1745
|
+
catch (e) {
|
|
1746
|
+
setSkillActionError(getSkillActionErrorMessage(e));
|
|
1747
|
+
}
|
|
1748
|
+
}, [
|
|
1749
|
+
agent?.id,
|
|
1750
|
+
clearLegacySelectedSkillsFromMetadata,
|
|
1751
|
+
getSkillActionErrorMessage,
|
|
1752
|
+
revokeAgentSkill,
|
|
1753
|
+
setAgent,
|
|
1754
|
+
]);
|
|
1555
1755
|
const handleOpenProfileSettings = useCallback(async () => {
|
|
1556
1756
|
if (!editContext || !activeProfile?.id)
|
|
1557
1757
|
return;
|
|
@@ -2232,7 +2432,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2232
2432
|
if (toolCallMessageId && message.data && toolCallId) {
|
|
2233
2433
|
const toolCallError = message.data.functionError || message.data.error || "";
|
|
2234
2434
|
const isPruned = !!message.data?.isPruned || /^PRUNED$/i.test(String(toolCallError));
|
|
2235
|
-
const toolCallCreatedDate = message.data.createdDate ||
|
|
2435
|
+
const toolCallCreatedDate = message.data.createdDate ||
|
|
2436
|
+
message.timestamp ||
|
|
2437
|
+
new Date().toISOString();
|
|
2236
2438
|
const toolCall = {
|
|
2237
2439
|
id: toolCallId,
|
|
2238
2440
|
messageId: toolCallMessageId,
|
|
@@ -2241,6 +2443,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2241
2443
|
functionName: extractedToolCall.functionName,
|
|
2242
2444
|
functionArguments: extractedToolCall.functionArguments,
|
|
2243
2445
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2446
|
+
functionResultRichContent: message.data.richContent || undefined,
|
|
2244
2447
|
functionError: toolCallError,
|
|
2245
2448
|
isPruned,
|
|
2246
2449
|
isCompleted: false,
|
|
@@ -2290,12 +2493,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2290
2493
|
newArgsText !== existingArgsText) ||
|
|
2291
2494
|
(existingArgsText === "{}" && newArgsText !== "{}");
|
|
2292
2495
|
const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
|
|
2496
|
+
const hasNewRichContent = toolCall.functionResultRichContent &&
|
|
2497
|
+
!existingToolCall.functionResultRichContent;
|
|
2293
2498
|
const hasNewError = toolCall.functionError && !existingToolCall.functionError;
|
|
2294
2499
|
const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
|
|
2295
2500
|
const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
|
|
2296
2501
|
// Only update if there's meaningful new data
|
|
2297
2502
|
if (hasMoreCompleteArgs ||
|
|
2298
2503
|
hasNewResult ||
|
|
2504
|
+
hasNewRichContent ||
|
|
2299
2505
|
hasNewError ||
|
|
2300
2506
|
hasNewApprovalInfo ||
|
|
2301
2507
|
hasNewDbMessageId) {
|
|
@@ -2315,6 +2521,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2315
2521
|
? newArgsText
|
|
2316
2522
|
: existingArgsText || existing.functionArguments,
|
|
2317
2523
|
functionResult: toolCall.functionResult || existing.functionResult,
|
|
2524
|
+
functionResultRichContent: toolCall.functionResultRichContent ||
|
|
2525
|
+
existing.functionResultRichContent,
|
|
2318
2526
|
functionError: toolCall.functionError || existing.functionError,
|
|
2319
2527
|
requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
|
|
2320
2528
|
};
|
|
@@ -2475,6 +2683,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2475
2683
|
? nextArgsText
|
|
2476
2684
|
: existingToolCall.functionArguments,
|
|
2477
2685
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2686
|
+
functionResultRichContent: message.data.richContent ||
|
|
2687
|
+
existingToolCall.functionResultRichContent,
|
|
2478
2688
|
functionError: message.data.functionError || message.data.error || "",
|
|
2479
2689
|
isCompleted: true,
|
|
2480
2690
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2491,7 +2701,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2491
2701
|
}
|
|
2492
2702
|
else if (message.data && resultToolCallId && resultMessageId) {
|
|
2493
2703
|
// Create new tool call if it doesn't exist
|
|
2494
|
-
const toolCallCreatedDate = message.data.createdDate ||
|
|
2704
|
+
const toolCallCreatedDate = message.data.createdDate ||
|
|
2705
|
+
message.timestamp ||
|
|
2706
|
+
new Date().toISOString();
|
|
2495
2707
|
const toolCall = {
|
|
2496
2708
|
id: resultToolCallId,
|
|
2497
2709
|
messageId: resultMessageId,
|
|
@@ -2499,6 +2711,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2499
2711
|
functionName: extractedToolCall.functionName,
|
|
2500
2712
|
functionArguments: extractedToolCall.functionArguments,
|
|
2501
2713
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2714
|
+
functionResultRichContent: message.data.richContent || undefined,
|
|
2502
2715
|
functionError: message.data.functionError || message.data.error || "",
|
|
2503
2716
|
isCompleted: true,
|
|
2504
2717
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2593,7 +2806,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2593
2806
|
// The agent might have been persisted after sending a prompt
|
|
2594
2807
|
// Only treat as "new" if backend returns 404
|
|
2595
2808
|
const hasExistingMessages = messagesRef.current.length > 0;
|
|
2596
|
-
if (agentStub.status === "new" &&
|
|
2809
|
+
if (agentStub.status === "new" &&
|
|
2810
|
+
!agentStub.userId &&
|
|
2811
|
+
!hasExistingMessages) {
|
|
2597
2812
|
// Only initialize as new if we have no messages yet (initial mount)
|
|
2598
2813
|
// Derive initial profile from provided metadata if present
|
|
2599
2814
|
const initialProfileIdFromMeta = (() => {
|
|
@@ -3939,14 +4154,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3939
4154
|
return prev;
|
|
3940
4155
|
return candidate;
|
|
3941
4156
|
});
|
|
3942
|
-
}, [
|
|
4157
|
+
}, [
|
|
4158
|
+
profiles,
|
|
4159
|
+
agent?.id,
|
|
4160
|
+
agent?.profileId,
|
|
4161
|
+
agentStub.id,
|
|
4162
|
+
agentStub.profileId,
|
|
4163
|
+
]);
|
|
3943
4164
|
// Clear queued prompts when agent changes or is new;
|
|
3944
4165
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
3945
4166
|
useEffect(() => {
|
|
3946
|
-
if (!agent?.id ||
|
|
4167
|
+
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
3947
4168
|
setQueuedPrompts([]);
|
|
3948
4169
|
}
|
|
3949
|
-
}, [agent?.id,
|
|
4170
|
+
}, [agent?.id, isLocalOnlyDraftAgent]);
|
|
3950
4171
|
// Update selected model when the active profile or agent model changes
|
|
3951
4172
|
useEffect(() => {
|
|
3952
4173
|
if (!activeProfile)
|
|
@@ -5224,17 +5445,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5224
5445
|
const renderErrorBanner = () => {
|
|
5225
5446
|
const currentAgent = agent || agentStub;
|
|
5226
5447
|
const isErrorStatus = currentAgent?.status === "error";
|
|
5227
|
-
const
|
|
5228
|
-
if (!
|
|
5448
|
+
const rawErrorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
|
|
5449
|
+
if (!rawErrorMessage)
|
|
5229
5450
|
return null;
|
|
5451
|
+
// Clean the error message (statusMessage from DB may contain raw JSON)
|
|
5452
|
+
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5230
5453
|
return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", "data-testid": "agent-error-banner", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
|
|
5231
5454
|
};
|
|
5232
5455
|
const renderBrowserClaimBanner = (variant = "inline") => {
|
|
5233
5456
|
if (!agent?.id || !editContext?.sessionId)
|
|
5234
5457
|
return null;
|
|
5235
|
-
if (!isClaimedByCurrentSession &&
|
|
5236
|
-
|
|
5237
|
-
|
|
5458
|
+
if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
|
|
5459
|
+
return null;
|
|
5460
|
+
}
|
|
5461
|
+
if (isPendingBrowserCaptureWait) {
|
|
5238
5462
|
return null;
|
|
5239
5463
|
}
|
|
5240
5464
|
const label = isClaimedByCurrentSession
|
|
@@ -5258,6 +5482,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5258
5482
|
};
|
|
5259
5483
|
const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
|
|
5260
5484
|
const inlineBrowserClaimBanner = null;
|
|
5485
|
+
const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
|
|
5486
|
+
? {
|
|
5487
|
+
toolNames: [
|
|
5488
|
+
"capture-page-screenshot",
|
|
5489
|
+
"capture-parhelia-ui-screenshot",
|
|
5490
|
+
"capture-page-dom",
|
|
5491
|
+
],
|
|
5492
|
+
label: isClaimedByAnotherBrowser
|
|
5493
|
+
? "Attached in another browser"
|
|
5494
|
+
: "No browser attached",
|
|
5495
|
+
description: isClaimedByAnotherBrowser
|
|
5496
|
+
? "This capture request is waiting in another browser. Take over browser control here to continue."
|
|
5497
|
+
: "This capture request is waiting for a browser attachment. Attach this browser to continue.",
|
|
5498
|
+
actionLabel: isClaimedByAnotherBrowser
|
|
5499
|
+
? "Take over browser control"
|
|
5500
|
+
: "Attach to this browser",
|
|
5501
|
+
isPending: isBrowserClaimMutationPending,
|
|
5502
|
+
onAction: () => {
|
|
5503
|
+
void handleClaimBrowser(isClaimedByAnotherBrowser);
|
|
5504
|
+
},
|
|
5505
|
+
}
|
|
5506
|
+
: null;
|
|
5261
5507
|
useEffect(() => {
|
|
5262
5508
|
if (agent?.status !== "waitingForInput") {
|
|
5263
5509
|
setPendingBrowserCaptureDialogType(null);
|
|
@@ -5327,7 +5573,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5327
5573
|
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5328
5574
|
} })) : (_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 ||
|
|
5329
5575
|
activeProfile?.displayTitle ||
|
|
5330
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5576
|
+
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5331
5577
|
const text = (action.prompt ||
|
|
5332
5578
|
action.value ||
|
|
5333
5579
|
action.label ||
|
|
@@ -5480,7 +5726,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5480
5726
|
const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
|
|
5481
5727
|
return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
|
|
5482
5728
|
activeProfile?.displayTitle ||
|
|
5483
|
-
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
|
|
5729
|
+
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5484
5730
|
const text = (action.prompt ||
|
|
5485
5731
|
action.value ||
|
|
5486
5732
|
action.label ||
|
|
@@ -5709,7 +5955,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5709
5955
|
mode: nextMode,
|
|
5710
5956
|
};
|
|
5711
5957
|
try {
|
|
5712
|
-
if (!agent?.id ||
|
|
5958
|
+
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
5713
5959
|
setMode(nextMode);
|
|
5714
5960
|
setAgentMetadata(nextMeta);
|
|
5715
5961
|
pendingSettingsRef.current = {
|
|
@@ -5721,7 +5967,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5721
5967
|
const result = await updateAgentSettings(agent.id, {
|
|
5722
5968
|
mode: nextMode,
|
|
5723
5969
|
});
|
|
5724
|
-
if (result.success === false ||
|
|
5970
|
+
if (result.success === false ||
|
|
5971
|
+
result.updates?.mode === false) {
|
|
5725
5972
|
throw new Error("Mode change was not applied");
|
|
5726
5973
|
}
|
|
5727
5974
|
setMode(nextMode);
|
|
@@ -5748,7 +5995,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5748
5995
|
return;
|
|
5749
5996
|
setActiveProfile(nextProfile);
|
|
5750
5997
|
try {
|
|
5751
|
-
if (agent?.id &&
|
|
5998
|
+
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
5752
5999
|
await updateAgentSettings(agent.id, {
|
|
5753
6000
|
profileId: nextProfile.id,
|
|
5754
6001
|
profileName: nextProfile.name,
|
|
@@ -5806,7 +6053,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5806
6053
|
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
|
|
5807
6054
|
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
5808
6055
|
try {
|
|
5809
|
-
if (agent?.id &&
|
|
6056
|
+
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
5810
6057
|
await updateAgentSettings(agent.id, {
|
|
5811
6058
|
model: modelName,
|
|
5812
6059
|
});
|
|
@@ -5821,36 +6068,47 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5821
6068
|
catch (err) {
|
|
5822
6069
|
console.error("Failed to persist agent model", err);
|
|
5823
6070
|
}
|
|
5824
|
-
} })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange:
|
|
6071
|
+
} })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange: (open) => {
|
|
6072
|
+
setShowSkillPicker(open);
|
|
6073
|
+
if (open) {
|
|
6074
|
+
setSkillActionError(null);
|
|
6075
|
+
}
|
|
6076
|
+
}, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[10px] text-gray-600", "data-testid": "agent-skill-picker-trigger", children: [_jsx(Plus, { className: "mr-1 h-3 w-3", strokeWidth: 1.5 }), "Add"] }) }), _jsx(PopoverContent, { className: "w-88 p-2", align: "end", side: "bottom", children: _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-[11px] font-medium text-gray-700", children: "Select a skill" }), _jsxs("div", { className: "relative h-56 rounded border border-gray-200 bg-gray-50", children: [_jsx(ScrollingContentTree, { rootItemIds: skillRootIds, selectedItemId: selectedSkillIds[selectedSkillIds.length - 1] || undefined, expandedItemId: selectedSkillIds[selectedSkillIds.length - 1] || skillRootIds[0], scrollToSelected: true, hideRootNodes: false, onSelectionChange: (selection) => {
|
|
5825
6077
|
const selected = selection[0];
|
|
5826
6078
|
if (!selected?.id)
|
|
5827
6079
|
return;
|
|
6080
|
+
setSkillActionError(null);
|
|
5828
6081
|
if (selectableTemplateIdSet.size > 0 &&
|
|
5829
6082
|
(!selected.templateId ||
|
|
5830
6083
|
!selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
|
|
5831
6084
|
return;
|
|
5832
6085
|
}
|
|
5833
|
-
if (!
|
|
6086
|
+
if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
|
|
6087
|
+
setSkillActionError("This skill cannot be added for the current agent profile.");
|
|
5834
6088
|
return;
|
|
5835
6089
|
}
|
|
5836
|
-
void
|
|
5837
|
-
|
|
6090
|
+
void (async () => {
|
|
6091
|
+
const added = await handleAddSkill(selected.id);
|
|
6092
|
+
if (added) {
|
|
6093
|
+
setShowSkillPicker(false);
|
|
6094
|
+
}
|
|
6095
|
+
})();
|
|
5838
6096
|
} }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
|
|
5839
6097
|
!skillsError &&
|
|
5840
|
-
profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsLoading &&
|
|
6098
|
+
profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsError && skillActionError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillActionError })), !skillsLoading &&
|
|
5841
6099
|
!skillsError &&
|
|
5842
6100
|
skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
|
|
5843
6101
|
!skillsError &&
|
|
5844
6102
|
profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
|
|
5845
|
-
? "All
|
|
5846
|
-
: "No
|
|
6103
|
+
? "All addable skills are selected"
|
|
6104
|
+
: "No skills can be added for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
|
|
5847
6105
|
const skill = selectedSkills.find((s) => s.id === skillId);
|
|
5848
6106
|
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: () => {
|
|
5849
6107
|
void handleOpenSkillItem(skillId);
|
|
5850
6108
|
}, 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: () => {
|
|
5851
6109
|
void handleRemoveSkill(skillId);
|
|
5852
6110
|
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
5853
|
-
}) }))] }), _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: [
|
|
6111
|
+
}) }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setToolsSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": toolsSectionExpanded, "data-testid": "agent-tools-section-toggle", children: [_jsxs("span", { children: ["Available tools (", displayedAvailableTools.length, ")"] }), toolsSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), toolsSectionExpanded && (_jsx("div", { className: "max-h-36 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-tools-section", children: displayedAvailableToolsLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading available tools..." })) : displayedAvailableTools.length > 0 ? (_jsxs("div", { className: "space-y-0.5", children: [isLocalOnlyDraftAgent && (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Preview based on the current profile, mode, and selected skills." })), displayedAvailableTools.map((toolName) => (_jsx("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": "agent-tool-row", title: toolName, children: _jsx("div", { className: "truncate text-[10px] text-gray-700", children: toolName }) }, toolName)))] })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
5854
6112
|
? "No available tools for this profile and mode"
|
|
5855
6113
|
: "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) => {
|
|
5856
6114
|
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
@@ -5865,12 +6123,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5865
6123
|
]
|
|
5866
6124
|
.filter(Boolean)
|
|
5867
6125
|
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
5868
|
-
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
6126
|
+
})] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
5869
6127
|
? "Allowances are shown after the agent is created"
|
|
5870
6128
|
: "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) => {
|
|
5871
6129
|
const filterText = (sub.filter || "").trim();
|
|
5872
6130
|
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));
|
|
5873
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
6131
|
+
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
5874
6132
|
? "Subscribed triggers are shown after the agent is created"
|
|
5875
6133
|
: "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: () => {
|
|
5876
6134
|
setPrompt(p.prompt);
|