@parhelia/core 0.1.12523 → 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/editor/ai/AgentTerminal.js +216 -53
- 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/ToolCallDisplay.d.ts +2 -1
- package/dist/editor/ai/ToolCallDisplay.js +152 -5
- package/dist/editor/ai/ToolCallDisplay.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/menubar/toolbar-sections/ManualBrowser.js +22 -2
- 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/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 +10 -0
- package/dist/editor/services/agentService.js +20 -0
- package/dist/editor/services/agentService.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/OperationItem.js +1 -0
- package/dist/editor/sidebar/OperationItem.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/types.d.ts +7 -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, assignAgentSkill, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } 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
|
|
@@ -1103,7 +1145,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1103
1145
|
const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
|
|
1104
1146
|
const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
|
|
1105
1147
|
const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
|
|
1106
|
-
const
|
|
1148
|
+
const isPersistedAgent = !!agent?.userId;
|
|
1149
|
+
const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
|
|
1107
1150
|
const hasSpawnedAgents = useMemo(() => {
|
|
1108
1151
|
if (!agentMetadata)
|
|
1109
1152
|
return false;
|
|
@@ -1291,7 +1334,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1291
1334
|
}, [activeProfile?.preloadSkills]);
|
|
1292
1335
|
const autoAssignedSkillIds = useMemo(() => {
|
|
1293
1336
|
const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
|
|
1294
|
-
const all =
|
|
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, preloadedSkillIds]);
|
|
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,
|
|
@@ -1455,8 +1505,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1455
1505
|
const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
|
|
1456
1506
|
const previewAvailableTools = useMemo(() => {
|
|
1457
1507
|
const baseToolNames = mode === "read-only"
|
|
1458
|
-
? activeProfile?.readOnlyToolNames ?? []
|
|
1459
|
-
: activeProfile?.allowedToolNames ?? [];
|
|
1508
|
+
? (activeProfile?.readOnlyToolNames ?? [])
|
|
1509
|
+
: (activeProfile?.allowedToolNames ?? []);
|
|
1460
1510
|
const toolNames = new Set();
|
|
1461
1511
|
for (const toolName of baseToolNames) {
|
|
1462
1512
|
const normalized = String(toolName || "").trim();
|
|
@@ -1479,13 +1529,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1479
1529
|
mode,
|
|
1480
1530
|
selectedSkills,
|
|
1481
1531
|
]);
|
|
1482
|
-
const displayedAvailableTools =
|
|
1532
|
+
const displayedAvailableTools = isLocalOnlyDraftAgent
|
|
1483
1533
|
? previewAvailableTools
|
|
1484
1534
|
: availableTools;
|
|
1485
|
-
const displayedAvailableToolsLoading =
|
|
1535
|
+
const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
|
|
1486
1536
|
? false
|
|
1487
1537
|
: availableToolsLoading;
|
|
1488
|
-
const displayedAvailableToolsError =
|
|
1538
|
+
const displayedAvailableToolsError = isLocalOnlyDraftAgent
|
|
1539
|
+
? null
|
|
1540
|
+
: availableToolsError;
|
|
1489
1541
|
// Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
|
|
1490
1542
|
const sanitizeAgentMetadata = useCallback((meta) => {
|
|
1491
1543
|
try {
|
|
@@ -1525,7 +1577,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1525
1577
|
return;
|
|
1526
1578
|
}
|
|
1527
1579
|
const current = agentMetadata || {};
|
|
1528
|
-
const currentAdditionalData = current.additionalData ||
|
|
1580
|
+
const currentAdditionalData = current.additionalData ||
|
|
1581
|
+
{};
|
|
1529
1582
|
const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
|
|
1530
1583
|
const next = {
|
|
1531
1584
|
...current,
|
|
@@ -1551,6 +1604,86 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1551
1604
|
setAgent,
|
|
1552
1605
|
setAgentMetadata,
|
|
1553
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);
|
|
1646
|
+
}
|
|
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
|
+
};
|
|
1554
1687
|
const handleAddSkill = useCallback(async (skillId) => {
|
|
1555
1688
|
if (selectedSkillSet.has(skillId.toLowerCase()))
|
|
1556
1689
|
return;
|
|
@@ -1558,7 +1691,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1558
1691
|
return;
|
|
1559
1692
|
try {
|
|
1560
1693
|
setSkillActionError(null);
|
|
1561
|
-
|
|
1694
|
+
const persistedAgent = await ensureDraftAgentPersisted();
|
|
1695
|
+
await assignAgentSkill({ agentId: persistedAgent.id, skillId });
|
|
1562
1696
|
setAgent((prev) => {
|
|
1563
1697
|
if (!prev)
|
|
1564
1698
|
return prev;
|
|
@@ -1584,6 +1718,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1584
1718
|
agent?.id,
|
|
1585
1719
|
assignAgentSkill,
|
|
1586
1720
|
clearLegacySelectedSkillsFromMetadata,
|
|
1721
|
+
ensureDraftAgentPersisted,
|
|
1587
1722
|
getSkillActionErrorMessage,
|
|
1588
1723
|
setAgent,
|
|
1589
1724
|
selectedSkillSet,
|
|
@@ -2297,7 +2432,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2297
2432
|
if (toolCallMessageId && message.data && toolCallId) {
|
|
2298
2433
|
const toolCallError = message.data.functionError || message.data.error || "";
|
|
2299
2434
|
const isPruned = !!message.data?.isPruned || /^PRUNED$/i.test(String(toolCallError));
|
|
2300
|
-
const toolCallCreatedDate = message.data.createdDate ||
|
|
2435
|
+
const toolCallCreatedDate = message.data.createdDate ||
|
|
2436
|
+
message.timestamp ||
|
|
2437
|
+
new Date().toISOString();
|
|
2301
2438
|
const toolCall = {
|
|
2302
2439
|
id: toolCallId,
|
|
2303
2440
|
messageId: toolCallMessageId,
|
|
@@ -2306,6 +2443,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2306
2443
|
functionName: extractedToolCall.functionName,
|
|
2307
2444
|
functionArguments: extractedToolCall.functionArguments,
|
|
2308
2445
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2446
|
+
functionResultRichContent: message.data.richContent || undefined,
|
|
2309
2447
|
functionError: toolCallError,
|
|
2310
2448
|
isPruned,
|
|
2311
2449
|
isCompleted: false,
|
|
@@ -2355,12 +2493,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2355
2493
|
newArgsText !== existingArgsText) ||
|
|
2356
2494
|
(existingArgsText === "{}" && newArgsText !== "{}");
|
|
2357
2495
|
const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
|
|
2496
|
+
const hasNewRichContent = toolCall.functionResultRichContent &&
|
|
2497
|
+
!existingToolCall.functionResultRichContent;
|
|
2358
2498
|
const hasNewError = toolCall.functionError && !existingToolCall.functionError;
|
|
2359
2499
|
const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
|
|
2360
2500
|
const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
|
|
2361
2501
|
// Only update if there's meaningful new data
|
|
2362
2502
|
if (hasMoreCompleteArgs ||
|
|
2363
2503
|
hasNewResult ||
|
|
2504
|
+
hasNewRichContent ||
|
|
2364
2505
|
hasNewError ||
|
|
2365
2506
|
hasNewApprovalInfo ||
|
|
2366
2507
|
hasNewDbMessageId) {
|
|
@@ -2380,6 +2521,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2380
2521
|
? newArgsText
|
|
2381
2522
|
: existingArgsText || existing.functionArguments,
|
|
2382
2523
|
functionResult: toolCall.functionResult || existing.functionResult,
|
|
2524
|
+
functionResultRichContent: toolCall.functionResultRichContent ||
|
|
2525
|
+
existing.functionResultRichContent,
|
|
2383
2526
|
functionError: toolCall.functionError || existing.functionError,
|
|
2384
2527
|
requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
|
|
2385
2528
|
};
|
|
@@ -2540,6 +2683,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2540
2683
|
? nextArgsText
|
|
2541
2684
|
: existingToolCall.functionArguments,
|
|
2542
2685
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2686
|
+
functionResultRichContent: message.data.richContent ||
|
|
2687
|
+
existingToolCall.functionResultRichContent,
|
|
2543
2688
|
functionError: message.data.functionError || message.data.error || "",
|
|
2544
2689
|
isCompleted: true,
|
|
2545
2690
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2556,7 +2701,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2556
2701
|
}
|
|
2557
2702
|
else if (message.data && resultToolCallId && resultMessageId) {
|
|
2558
2703
|
// Create new tool call if it doesn't exist
|
|
2559
|
-
const toolCallCreatedDate = message.data.createdDate ||
|
|
2704
|
+
const toolCallCreatedDate = message.data.createdDate ||
|
|
2705
|
+
message.timestamp ||
|
|
2706
|
+
new Date().toISOString();
|
|
2560
2707
|
const toolCall = {
|
|
2561
2708
|
id: resultToolCallId,
|
|
2562
2709
|
messageId: resultMessageId,
|
|
@@ -2564,6 +2711,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2564
2711
|
functionName: extractedToolCall.functionName,
|
|
2565
2712
|
functionArguments: extractedToolCall.functionArguments,
|
|
2566
2713
|
functionResult: message.data.functionResult || message.data.result || "",
|
|
2714
|
+
functionResultRichContent: message.data.richContent || undefined,
|
|
2567
2715
|
functionError: message.data.functionError || message.data.error || "",
|
|
2568
2716
|
isCompleted: true,
|
|
2569
2717
|
responseTimeMs: message.data.responseTimeMs,
|
|
@@ -2658,7 +2806,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2658
2806
|
// The agent might have been persisted after sending a prompt
|
|
2659
2807
|
// Only treat as "new" if backend returns 404
|
|
2660
2808
|
const hasExistingMessages = messagesRef.current.length > 0;
|
|
2661
|
-
if (agentStub.status === "new" &&
|
|
2809
|
+
if (agentStub.status === "new" &&
|
|
2810
|
+
!agentStub.userId &&
|
|
2811
|
+
!hasExistingMessages) {
|
|
2662
2812
|
// Only initialize as new if we have no messages yet (initial mount)
|
|
2663
2813
|
// Derive initial profile from provided metadata if present
|
|
2664
2814
|
const initialProfileIdFromMeta = (() => {
|
|
@@ -4004,14 +4154,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4004
4154
|
return prev;
|
|
4005
4155
|
return candidate;
|
|
4006
4156
|
});
|
|
4007
|
-
}, [
|
|
4157
|
+
}, [
|
|
4158
|
+
profiles,
|
|
4159
|
+
agent?.id,
|
|
4160
|
+
agent?.profileId,
|
|
4161
|
+
agentStub.id,
|
|
4162
|
+
agentStub.profileId,
|
|
4163
|
+
]);
|
|
4008
4164
|
// Clear queued prompts when agent changes or is new;
|
|
4009
4165
|
// initial fetch is handled by loadAgent() for better performance and reliability
|
|
4010
4166
|
useEffect(() => {
|
|
4011
|
-
if (!agent?.id ||
|
|
4167
|
+
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
4012
4168
|
setQueuedPrompts([]);
|
|
4013
4169
|
}
|
|
4014
|
-
}, [agent?.id,
|
|
4170
|
+
}, [agent?.id, isLocalOnlyDraftAgent]);
|
|
4015
4171
|
// Update selected model when the active profile or agent model changes
|
|
4016
4172
|
useEffect(() => {
|
|
4017
4173
|
if (!activeProfile)
|
|
@@ -5289,9 +5445,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5289
5445
|
const renderErrorBanner = () => {
|
|
5290
5446
|
const currentAgent = agent || agentStub;
|
|
5291
5447
|
const isErrorStatus = currentAgent?.status === "error";
|
|
5292
|
-
const
|
|
5293
|
-
if (!
|
|
5448
|
+
const rawErrorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
|
|
5449
|
+
if (!rawErrorMessage)
|
|
5294
5450
|
return null;
|
|
5451
|
+
// Clean the error message (statusMessage from DB may contain raw JSON)
|
|
5452
|
+
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5295
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 })] })] }) }));
|
|
5296
5454
|
};
|
|
5297
5455
|
const renderBrowserClaimBanner = (variant = "inline") => {
|
|
@@ -5326,7 +5484,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5326
5484
|
const inlineBrowserClaimBanner = null;
|
|
5327
5485
|
const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
|
|
5328
5486
|
? {
|
|
5329
|
-
toolNames: [
|
|
5487
|
+
toolNames: [
|
|
5488
|
+
"capture-page-screenshot",
|
|
5489
|
+
"capture-parhelia-ui-screenshot",
|
|
5490
|
+
"capture-page-dom",
|
|
5491
|
+
],
|
|
5330
5492
|
label: isClaimedByAnotherBrowser
|
|
5331
5493
|
? "Attached in another browser"
|
|
5332
5494
|
: "No browser attached",
|
|
@@ -5793,7 +5955,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5793
5955
|
mode: nextMode,
|
|
5794
5956
|
};
|
|
5795
5957
|
try {
|
|
5796
|
-
if (!agent?.id ||
|
|
5958
|
+
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
5797
5959
|
setMode(nextMode);
|
|
5798
5960
|
setAgentMetadata(nextMeta);
|
|
5799
5961
|
pendingSettingsRef.current = {
|
|
@@ -5805,7 +5967,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5805
5967
|
const result = await updateAgentSettings(agent.id, {
|
|
5806
5968
|
mode: nextMode,
|
|
5807
5969
|
});
|
|
5808
|
-
if (result.success === false ||
|
|
5970
|
+
if (result.success === false ||
|
|
5971
|
+
result.updates?.mode === false) {
|
|
5809
5972
|
throw new Error("Mode change was not applied");
|
|
5810
5973
|
}
|
|
5811
5974
|
setMode(nextMode);
|
|
@@ -5832,7 +5995,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5832
5995
|
return;
|
|
5833
5996
|
setActiveProfile(nextProfile);
|
|
5834
5997
|
try {
|
|
5835
|
-
if (agent?.id &&
|
|
5998
|
+
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
5836
5999
|
await updateAgentSettings(agent.id, {
|
|
5837
6000
|
profileId: nextProfile.id,
|
|
5838
6001
|
profileName: nextProfile.name,
|
|
@@ -5890,7 +6053,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5890
6053
|
const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
|
|
5891
6054
|
setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
|
|
5892
6055
|
try {
|
|
5893
|
-
if (agent?.id &&
|
|
6056
|
+
if (agent?.id && !isLocalOnlyDraftAgent) {
|
|
5894
6057
|
await updateAgentSettings(agent.id, {
|
|
5895
6058
|
model: modelName,
|
|
5896
6059
|
});
|
|
@@ -5945,7 +6108,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5945
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: () => {
|
|
5946
6109
|
void handleRemoveSkill(skillId);
|
|
5947
6110
|
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
5948
|
-
}) }))] }), _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
|
|
5949
6112
|
? "No available tools for this profile and mode"
|
|
5950
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) => {
|
|
5951
6114
|
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
@@ -5960,12 +6123,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5960
6123
|
]
|
|
5961
6124
|
.filter(Boolean)
|
|
5962
6125
|
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
5963
|
-
})] }, 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
|
|
5964
6127
|
? "Allowances are shown after the agent is created"
|
|
5965
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) => {
|
|
5966
6129
|
const filterText = (sub.filter || "").trim();
|
|
5967
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));
|
|
5968
|
-
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children:
|
|
6131
|
+
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
5969
6132
|
? "Subscribed triggers are shown after the agent is created"
|
|
5970
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: () => {
|
|
5971
6134
|
setPrompt(p.prompt);
|