@alpaca-editor/core 1.0.4135 → 1.0.4140
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 +7 -0
- package/dist/config/config.js.map +1 -1
- package/dist/editor/FieldListField.js +3 -4
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +3 -1
- package/dist/editor/ai/AgentCostDisplay.js +26 -2
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +26 -0
- package/dist/editor/ai/AgentStatusBadge.js +110 -0
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -0
- package/dist/editor/ai/AgentTerminal.js +289 -198
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.d.ts +2 -2
- package/dist/editor/ai/Agents.js +115 -19
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +259 -45
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +124 -113
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +1 -0
- package/dist/editor/ai/ToolCallDisplay.js +70 -58
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/useAgentStatus.d.ts +13 -0
- package/dist/editor/ai/useAgentStatus.js +101 -0
- package/dist/editor/ai/useAgentStatus.js.map +1 -0
- package/dist/editor/client/EditorShell.js +23 -8
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js +5 -5
- package/dist/editor/commands/localizeItem/LocalizeItemDialog.js.map +1 -1
- package/dist/editor/control-center/About.js +1 -1
- package/dist/editor/control-center/About.js.map +1 -1
- package/dist/editor/control-center/AllAgentsPanel.d.ts +5 -0
- package/dist/editor/control-center/AllAgentsPanel.js +126 -0
- package/dist/editor/control-center/AllAgentsPanel.js.map +1 -0
- package/dist/editor/control-center/WebSocketMessages.js +1 -0
- package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +42 -7
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearch.d.ts +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +162 -103
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +20 -4
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +5 -2
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +7 -5
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +2 -2
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +18 -11
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +53 -48
- package/dist/editor/services/agentService.js +137 -79
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -1
- package/dist/editor/services/editService.js +1 -0
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +20 -7
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -2
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/Translations.js +10 -7
- package/dist/editor/sidebar/Translations.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +1 -1
- package/dist/editor/ui/ItemSearch.js +10 -4
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +2 -2
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/FieldEditor.js +2 -2
- package/dist/page-wizard/steps/FieldEditor.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/NewPage.js +2 -2
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +1 -1
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/styles.css +167 -22
- package/dist/tour/Tour.js +15 -11
- package/dist/tour/Tour.js.map +1 -1
- package/package.json +1 -1
- package/src/config/config.tsx +7 -0
- package/src/editor/FieldListField.tsx +13 -13
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -1
- package/src/editor/ai/AgentCostDisplay.tsx +57 -1
- package/src/editor/ai/AgentStatusBadge.tsx +144 -0
- package/src/editor/ai/AgentTerminal.tsx +345 -219
- package/src/editor/ai/Agents.tsx +179 -30
- package/src/editor/ai/AiResponseMessage.tsx +411 -114
- package/src/editor/ai/ContextInfoBar.tsx +134 -131
- package/src/editor/ai/ToolCallDisplay.tsx +217 -176
- package/src/editor/ai/useAgentStatus.ts +123 -0
- package/src/editor/client/EditorShell.tsx +34 -8
- package/src/editor/client/itemsRepository.ts +1 -2
- package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +5 -5
- package/src/editor/control-center/About.tsx +0 -14
- package/src/editor/control-center/AllAgentsPanel.tsx +300 -0
- package/src/editor/control-center/WebSocketMessages.tsx +1 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +49 -8
- package/src/editor/media-selector/AiImageSearch.tsx +162 -172
- package/src/editor/media-selector/TreeSelector.tsx +137 -116
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +9 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +7 -4
- package/src/editor/page-viewer/DeviceToolbar.tsx +15 -11
- package/src/editor/page-viewer/PageViewerFrame.tsx +20 -14
- package/src/editor/services/agentService.ts +217 -129
- package/src/editor/services/aiService.ts +2 -2
- package/src/editor/services/editService.ts +1 -0
- package/src/editor/sidebar/GraphQL.tsx +143 -117
- package/src/editor/sidebar/SEOInfo.tsx +1 -2
- package/src/editor/sidebar/Translations.tsx +14 -12
- package/src/editor/ui/ItemNameDialogNew.tsx +1 -1
- package/src/editor/ui/ItemSearch.tsx +11 -4
- package/src/editor/ui/SimpleTabs.tsx +1 -1
- package/src/page-wizard/steps/CollectStep.tsx +2 -2
- package/src/page-wizard/steps/FieldEditor.tsx +13 -15
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +2 -2
- package/src/splash-screen/RecentPages.tsx +1 -1
- package/src/tour/Tour.tsx +61 -48
|
@@ -29,10 +29,11 @@ import {
|
|
|
29
29
|
StartAgentRequest,
|
|
30
30
|
Agent,
|
|
31
31
|
AgentDetails,
|
|
32
|
-
|
|
32
|
+
updateAgentContext,
|
|
33
33
|
AgentMetadata,
|
|
34
34
|
updateAgentSettings,
|
|
35
35
|
updateAgentCostLimit,
|
|
36
|
+
cancelAgent,
|
|
36
37
|
} from "../services/agentService";
|
|
37
38
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
38
39
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -678,6 +679,14 @@ export function AgentTerminal({
|
|
|
678
679
|
const current = messagesRef.current || [];
|
|
679
680
|
return current.some((m) => !m.isCompleted && m.messageType === "streaming");
|
|
680
681
|
}, []);
|
|
682
|
+
const hasPendingApprovals = useCallback(() => {
|
|
683
|
+
const current = messagesRef.current || [];
|
|
684
|
+
return current.some((m) =>
|
|
685
|
+
(m.toolCalls || []).some((tc) =>
|
|
686
|
+
(tc.functionName || "").includes("(pending approval)"),
|
|
687
|
+
),
|
|
688
|
+
);
|
|
689
|
+
}, []);
|
|
681
690
|
const resetDotsTimer = useCallback(() => {
|
|
682
691
|
if (dotsTimeoutRef.current) {
|
|
683
692
|
clearTimeout(dotsTimeoutRef.current);
|
|
@@ -685,18 +694,26 @@ export function AgentTerminal({
|
|
|
685
694
|
}
|
|
686
695
|
const waiting = isWaitingRef.current;
|
|
687
696
|
const streaming = hasActiveStreaming();
|
|
697
|
+
const pendingApprovals = hasPendingApprovals();
|
|
698
|
+
// Don't show dots if there are pending approvals (waiting for user action)
|
|
688
699
|
if (!waiting && !streaming) {
|
|
689
700
|
setShowDots(false);
|
|
690
701
|
return;
|
|
691
702
|
}
|
|
692
|
-
|
|
703
|
+
if (pendingApprovals) {
|
|
704
|
+
setShowDots(false);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
// Show dots immediately when waiting or streaming
|
|
708
|
+
setShowDots(true);
|
|
693
709
|
dotsTimeoutRef.current = setTimeout(() => {
|
|
694
|
-
// Re-check conditions after
|
|
710
|
+
// Re-check conditions after a brief delay to avoid flicker
|
|
695
711
|
const stillWaiting = isWaitingRef.current;
|
|
696
712
|
const stillStreaming = hasActiveStreaming();
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
713
|
+
const stillPendingApprovals = hasPendingApprovals();
|
|
714
|
+
setShowDots((stillWaiting || stillStreaming) && !stillPendingApprovals);
|
|
715
|
+
}, 100);
|
|
716
|
+
}, [hasActiveStreaming, hasPendingApprovals]);
|
|
700
717
|
const [resolvedPageName, setResolvedPageName] = useState<string | undefined>(
|
|
701
718
|
undefined,
|
|
702
719
|
);
|
|
@@ -724,8 +741,8 @@ export function AgentTerminal({
|
|
|
724
741
|
const [selectedModelId, setSelectedModelId] = useState<string | undefined>(
|
|
725
742
|
undefined,
|
|
726
743
|
);
|
|
727
|
-
type AgentMode = "
|
|
728
|
-
const [mode, setMode] = useState<AgentMode>("
|
|
744
|
+
type AgentMode = "autonomous" | "read-only" | "supervised";
|
|
745
|
+
const [mode, setMode] = useState<AgentMode>("supervised");
|
|
729
746
|
|
|
730
747
|
// Read deterministic flags from query string once
|
|
731
748
|
const deterministicFlags = React.useMemo(() => {
|
|
@@ -807,7 +824,7 @@ export function AgentTerminal({
|
|
|
807
824
|
// Cache mode/model changes made while the agent is still "new" (not yet persisted)
|
|
808
825
|
const pendingSettingsRef = useRef<{
|
|
809
826
|
modelName?: string | null;
|
|
810
|
-
mode?: "
|
|
827
|
+
mode?: "autonomous" | "read-only" | "supervised";
|
|
811
828
|
} | null>(null);
|
|
812
829
|
|
|
813
830
|
// Auto-scroll to bottom when new messages arrive
|
|
@@ -993,10 +1010,28 @@ export function AgentTerminal({
|
|
|
993
1010
|
|
|
994
1011
|
const handleContentChunk = useCallback(
|
|
995
1012
|
(message: AgentStreamMessage, agentData?: AgentDetails) => {
|
|
996
|
-
|
|
1013
|
+
// Get messageId from data, or generate one from agent ID (for backward compatibility)
|
|
1014
|
+
// If no messageId is provided, we'll use the last assistant message or create a new one
|
|
1015
|
+
let messageId = message.data?.messageId;
|
|
1016
|
+
|
|
1017
|
+
if (!messageId && agentData?.id) {
|
|
1018
|
+
// For backward compatibility: if no messageId, find or create the current streaming message
|
|
1019
|
+
// This handles cases where the backend doesn't send messageId
|
|
1020
|
+
const currentMessages = messagesRef.current;
|
|
1021
|
+
const lastStreamingMessage = [...currentMessages]
|
|
1022
|
+
.reverse()
|
|
1023
|
+
.find((m) => m.role === "assistant" && !m.isCompleted);
|
|
1024
|
+
|
|
1025
|
+
if (lastStreamingMessage) {
|
|
1026
|
+
messageId = lastStreamingMessage.id;
|
|
1027
|
+
} else {
|
|
1028
|
+
// Create a new message ID based on timestamp
|
|
1029
|
+
messageId = crypto.randomUUID();
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
997
1032
|
|
|
998
1033
|
if (!messageId) {
|
|
999
|
-
console.error("
|
|
1034
|
+
console.error("Unable to determine messageId for content chunk!");
|
|
1000
1035
|
return;
|
|
1001
1036
|
}
|
|
1002
1037
|
|
|
@@ -1005,6 +1040,24 @@ export function AgentTerminal({
|
|
|
1005
1040
|
// Any content chunk is an incremental update -> reset idle timer
|
|
1006
1041
|
resetDotsTimer();
|
|
1007
1042
|
|
|
1043
|
+
// Extract cost/token data from content chunk if present
|
|
1044
|
+
const data = message.data as any;
|
|
1045
|
+
if (
|
|
1046
|
+
data &&
|
|
1047
|
+
(data.totalCost !== undefined || data.totalTokens !== undefined)
|
|
1048
|
+
) {
|
|
1049
|
+
setLiveTotals({
|
|
1050
|
+
input: Number(data.totalInputTokens) || 0,
|
|
1051
|
+
output: Number(data.totalOutputTokens) || 0,
|
|
1052
|
+
cached: Number(data.totalCachedTokens) || 0,
|
|
1053
|
+
inputCost: Number(data.totalInputTokenCost) || 0,
|
|
1054
|
+
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
1055
|
+
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
1056
|
+
totalCost: Number(data.totalCost) || 0,
|
|
1057
|
+
currency: data.currency || "USD",
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1008
1061
|
// Always call setMessages and handle all logic in the callback with latest messages
|
|
1009
1062
|
setMessages((prev) => {
|
|
1010
1063
|
// Find existing message by messageId in the latest messages
|
|
@@ -1133,6 +1186,7 @@ export function AgentTerminal({
|
|
|
1133
1186
|
const toolCall = {
|
|
1134
1187
|
id: toolCallId,
|
|
1135
1188
|
messageId: toolCallMessageId,
|
|
1189
|
+
dbMessageId: message.data.messageId as string | undefined, // Database message ID for approval/rejection
|
|
1136
1190
|
toolCallId: toolCallId,
|
|
1137
1191
|
functionName: functionName,
|
|
1138
1192
|
functionArguments:
|
|
@@ -1197,6 +1251,24 @@ export function AgentTerminal({
|
|
|
1197
1251
|
}
|
|
1198
1252
|
}
|
|
1199
1253
|
|
|
1254
|
+
// Extract cost/token data from tool result if present
|
|
1255
|
+
const data = message.data as any;
|
|
1256
|
+
if (
|
|
1257
|
+
data &&
|
|
1258
|
+
(data.totalCost !== undefined || data.totalTokens !== undefined)
|
|
1259
|
+
) {
|
|
1260
|
+
setLiveTotals({
|
|
1261
|
+
input: Number(data.totalInputTokens) || 0,
|
|
1262
|
+
output: Number(data.totalOutputTokens) || 0,
|
|
1263
|
+
cached: Number(data.totalCachedTokens) || 0,
|
|
1264
|
+
inputCost: Number(data.totalInputTokenCost) || 0,
|
|
1265
|
+
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
1266
|
+
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
1267
|
+
totalCost: Number(data.totalCost) || 0,
|
|
1268
|
+
currency: data.currency || "USD",
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1200
1272
|
// Update tool result directly in the messages array
|
|
1201
1273
|
if (!resultMessageId) {
|
|
1202
1274
|
return;
|
|
@@ -1346,6 +1418,7 @@ export function AgentTerminal({
|
|
|
1346
1418
|
if (kind === "tokenUsage") {
|
|
1347
1419
|
const totals = (message as any)?.data?.totals;
|
|
1348
1420
|
if (totals) {
|
|
1421
|
+
const totalCost = Number(totals.totalCost) || 0;
|
|
1349
1422
|
setLiveTotals({
|
|
1350
1423
|
input: Number(totals.totalInputTokens) || 0,
|
|
1351
1424
|
output: Number(totals.totalOutputTokens) || 0,
|
|
@@ -1354,9 +1427,21 @@ export function AgentTerminal({
|
|
|
1354
1427
|
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
1355
1428
|
cachedCost:
|
|
1356
1429
|
Number(totals.totalCachedInputTokenCost) || 0,
|
|
1357
|
-
totalCost:
|
|
1430
|
+
totalCost: totalCost,
|
|
1358
1431
|
currency: totals.currency,
|
|
1359
1432
|
});
|
|
1433
|
+
|
|
1434
|
+
// Check if cost limit exceeded
|
|
1435
|
+
if (agent?.costLimit && totalCost > agent.costLimit) {
|
|
1436
|
+
setCostLimitExceeded({
|
|
1437
|
+
totalCost: totalCost,
|
|
1438
|
+
costLimit: agent.costLimit,
|
|
1439
|
+
initialCostLimit: agent.costLimit,
|
|
1440
|
+
});
|
|
1441
|
+
setIsWaitingForResponse(false);
|
|
1442
|
+
shouldCreateNewMessage.current = false;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1360
1445
|
// Force a re-render to update cost display immediately
|
|
1361
1446
|
setMessages((prev) => [...prev]);
|
|
1362
1447
|
}
|
|
@@ -1423,7 +1508,8 @@ export function AgentTerminal({
|
|
|
1423
1508
|
setAgentMetadata((prev) => {
|
|
1424
1509
|
const current = (prev || {}) as AgentMetadata;
|
|
1425
1510
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
1426
|
-
const
|
|
1511
|
+
const currentWithoutContext = { ...current };
|
|
1512
|
+
delete (currentWithoutContext as any).context;
|
|
1427
1513
|
const next: AgentMetadata = {
|
|
1428
1514
|
...currentWithoutContext,
|
|
1429
1515
|
additionalData: {
|
|
@@ -1439,9 +1525,9 @@ export function AgentTerminal({
|
|
|
1439
1525
|
try {
|
|
1440
1526
|
const currentMeta: AgentMetadata | null = (() => {
|
|
1441
1527
|
try {
|
|
1442
|
-
return prevAgent.
|
|
1528
|
+
return prevAgent.agentContext
|
|
1443
1529
|
? (JSON.parse(
|
|
1444
|
-
prevAgent.
|
|
1530
|
+
prevAgent.agentContext,
|
|
1445
1531
|
) as AgentMetadata)
|
|
1446
1532
|
: null;
|
|
1447
1533
|
} catch {
|
|
@@ -1450,15 +1536,11 @@ export function AgentTerminal({
|
|
|
1450
1536
|
})();
|
|
1451
1537
|
const nextMeta: AgentMetadata = {
|
|
1452
1538
|
...(currentMeta || ({} as AgentMetadata)),
|
|
1453
|
-
|
|
1454
|
-
...(((currentMeta as any)?.additionalData as any) ||
|
|
1455
|
-
{}),
|
|
1456
|
-
context: nextContext,
|
|
1457
|
-
},
|
|
1539
|
+
...nextContext,
|
|
1458
1540
|
} as AgentMetadata;
|
|
1459
1541
|
return {
|
|
1460
1542
|
...prevAgent,
|
|
1461
|
-
|
|
1543
|
+
agentContext: JSON.stringify(nextMeta),
|
|
1462
1544
|
};
|
|
1463
1545
|
} catch {
|
|
1464
1546
|
return prevAgent;
|
|
@@ -1639,25 +1721,37 @@ export function AgentTerminal({
|
|
|
1639
1721
|
resetDotsTimer();
|
|
1640
1722
|
break;
|
|
1641
1723
|
|
|
1642
|
-
case "
|
|
1643
|
-
//
|
|
1724
|
+
case "contextUpdate":
|
|
1725
|
+
// Update agent context when backend sends context update
|
|
1644
1726
|
try {
|
|
1645
|
-
const
|
|
1646
|
-
if (
|
|
1647
|
-
|
|
1648
|
-
(
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1727
|
+
const updatedContext = message.data as AgentMetadata;
|
|
1728
|
+
if (updatedContext) {
|
|
1729
|
+
// Update local metadata state
|
|
1730
|
+
setAgentMetadata((prev) => ({
|
|
1731
|
+
...prev,
|
|
1732
|
+
...updatedContext,
|
|
1733
|
+
}));
|
|
1734
|
+
|
|
1735
|
+
// Update agent state with new context
|
|
1736
|
+
setAgent((prevAgent) => {
|
|
1737
|
+
if (!prevAgent) return prevAgent;
|
|
1738
|
+
return {
|
|
1739
|
+
...prevAgent,
|
|
1740
|
+
agentContext: JSON.stringify(updatedContext),
|
|
1741
|
+
};
|
|
1654
1742
|
});
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1743
|
+
|
|
1744
|
+
console.log(
|
|
1745
|
+
"✅ Context updated from backend:",
|
|
1746
|
+
updatedContext,
|
|
1747
|
+
);
|
|
1659
1748
|
}
|
|
1660
|
-
} catch {
|
|
1749
|
+
} catch (err) {
|
|
1750
|
+
console.error("Error handling context update:", err);
|
|
1751
|
+
}
|
|
1752
|
+
break;
|
|
1753
|
+
|
|
1754
|
+
case "error":
|
|
1661
1755
|
console.error("❌ Stream error:", message.error);
|
|
1662
1756
|
setError(message.error || "Stream error occurred");
|
|
1663
1757
|
setIsWaitingForResponse(false);
|
|
@@ -1813,10 +1907,6 @@ export function AgentTerminal({
|
|
|
1813
1907
|
name: agentStub.name,
|
|
1814
1908
|
model: "",
|
|
1815
1909
|
currency: "USD",
|
|
1816
|
-
itemId: "",
|
|
1817
|
-
itemPath: "",
|
|
1818
|
-
language: "",
|
|
1819
|
-
version: 0,
|
|
1820
1910
|
profileId: initialProfileIdFromMeta || "",
|
|
1821
1911
|
profileName: initialProfileNameFromMeta || "",
|
|
1822
1912
|
totalTokensUsed: 0,
|
|
@@ -1866,95 +1956,77 @@ export function AgentTerminal({
|
|
|
1866
1956
|
return true;
|
|
1867
1957
|
}
|
|
1868
1958
|
})();
|
|
1959
|
+
// Create context with top-level properties (what ContextInfoBar expects)
|
|
1869
1960
|
const localCtx: AgentMetadata | null =
|
|
1870
1961
|
item && shouldSeedContext
|
|
1871
1962
|
? {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
version: item.version,
|
|
1879
|
-
name: editContext?.contentEditorItem?.name,
|
|
1880
|
-
},
|
|
1881
|
-
],
|
|
1882
|
-
componentIds: editContext?.selection?.length
|
|
1883
|
-
? (editContext.selection as any)
|
|
1884
|
-
: undefined,
|
|
1885
|
-
field:
|
|
1886
|
-
fieldsContext?.focusedField?.fieldId &&
|
|
1887
|
-
(fieldsContext.focusedField as any)?.item?.id
|
|
1888
|
-
? {
|
|
1889
|
-
fieldId: fieldsContext.focusedField.fieldId,
|
|
1890
|
-
itemId: (fieldsContext.focusedField as any).item.id,
|
|
1891
|
-
name: (fieldsContext.focusedField as any).fieldName,
|
|
1892
|
-
}
|
|
1893
|
-
: undefined,
|
|
1963
|
+
pages: [
|
|
1964
|
+
{
|
|
1965
|
+
id: item.id,
|
|
1966
|
+
language: item.language,
|
|
1967
|
+
version: item.version,
|
|
1968
|
+
name: editContext?.contentEditorItem?.name,
|
|
1894
1969
|
},
|
|
1895
|
-
|
|
1970
|
+
],
|
|
1971
|
+
componentIds: editContext?.selection?.length
|
|
1972
|
+
? (editContext.selection as any)
|
|
1973
|
+
: undefined,
|
|
1974
|
+
field:
|
|
1975
|
+
fieldsContext?.focusedField?.fieldId &&
|
|
1976
|
+
(fieldsContext.focusedField as any)?.item?.id
|
|
1977
|
+
? {
|
|
1978
|
+
fieldId: fieldsContext.focusedField.fieldId,
|
|
1979
|
+
itemId: (fieldsContext.focusedField as any).item.id,
|
|
1980
|
+
name: (fieldsContext.focusedField as any).fieldName,
|
|
1981
|
+
}
|
|
1982
|
+
: undefined,
|
|
1896
1983
|
}
|
|
1897
1984
|
: null;
|
|
1898
1985
|
|
|
1899
1986
|
let nextMetadata: AgentMetadata | null = null;
|
|
1900
1987
|
if (initialMetadata) {
|
|
1901
|
-
// Merge initial metadata
|
|
1902
|
-
const base: AgentMetadata = {
|
|
1903
|
-
...(initialMetadata as any),
|
|
1904
|
-
additionalData: {
|
|
1905
|
-
...((initialMetadata as any)?.additionalData || {}),
|
|
1906
|
-
context: {
|
|
1907
|
-
...(((initialMetadata as any)?.additionalData
|
|
1908
|
-
?.context as any) || {}),
|
|
1909
|
-
},
|
|
1910
|
-
},
|
|
1911
|
-
} as AgentMetadata;
|
|
1988
|
+
// Merge initial metadata with local context (using top-level structure)
|
|
1989
|
+
const base: AgentMetadata = { ...(initialMetadata as any) };
|
|
1912
1990
|
|
|
1913
|
-
if (localCtx
|
|
1914
|
-
const ctx = base.additionalData!.context as any;
|
|
1915
|
-
const local = localCtx.additionalData!.context as any;
|
|
1991
|
+
if (localCtx) {
|
|
1916
1992
|
// Normalize existing shape: migrate items -> pages if needed
|
|
1917
|
-
if (!
|
|
1918
|
-
|
|
1919
|
-
delete
|
|
1993
|
+
if (!base.pages && (base as any).items) {
|
|
1994
|
+
base.pages = (base as any).items;
|
|
1995
|
+
delete (base as any).items;
|
|
1920
1996
|
}
|
|
1921
1997
|
// Merge pages (avoid duplicates)
|
|
1922
|
-
if (
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
) {
|
|
1926
|
-
const targetPages: any[] = Array.isArray(ctx.pages)
|
|
1927
|
-
? [...ctx.pages]
|
|
1998
|
+
if (localCtx.pages && localCtx.pages.length) {
|
|
1999
|
+
const targetPages: any[] = Array.isArray(base.pages)
|
|
2000
|
+
? [...base.pages]
|
|
1928
2001
|
: [];
|
|
1929
2002
|
const existingKeys = new Set(
|
|
1930
2003
|
targetPages.map(
|
|
1931
2004
|
(p: any) => `${p.id}-${p.language || ""}-${p.version || ""}`,
|
|
1932
2005
|
),
|
|
1933
2006
|
);
|
|
1934
|
-
const
|
|
1935
|
-
const additions = incoming.filter(
|
|
2007
|
+
const additions = localCtx.pages.filter(
|
|
1936
2008
|
(p: any) =>
|
|
1937
2009
|
!existingKeys.has(
|
|
1938
2010
|
`${p.id}-${p.language || ""}-${p.version || ""}`,
|
|
1939
2011
|
),
|
|
1940
2012
|
);
|
|
1941
|
-
if (additions.length)
|
|
2013
|
+
if (additions.length) base.pages = [...targetPages, ...additions];
|
|
1942
2014
|
}
|
|
1943
2015
|
// Merge componentIds (avoid duplicates)
|
|
1944
|
-
if (
|
|
1945
|
-
const currentIds: string[] = Array.isArray(
|
|
1946
|
-
?
|
|
2016
|
+
if (localCtx.componentIds && localCtx.componentIds.length) {
|
|
2017
|
+
const currentIds: string[] = Array.isArray(base.componentIds)
|
|
2018
|
+
? base.componentIds
|
|
1947
2019
|
: [];
|
|
1948
2020
|
const existingIds = new Set(currentIds);
|
|
1949
|
-
const additions = (
|
|
2021
|
+
const additions = (localCtx.componentIds as string[]).filter(
|
|
1950
2022
|
(id) => !!id && !existingIds.has(id),
|
|
1951
2023
|
);
|
|
1952
2024
|
if (additions.length)
|
|
1953
|
-
|
|
2025
|
+
base.componentIds = [...currentIds, ...additions];
|
|
1954
2026
|
}
|
|
1955
2027
|
// Set field if missing
|
|
1956
|
-
if (!
|
|
1957
|
-
|
|
2028
|
+
if (!base.field && localCtx.field) {
|
|
2029
|
+
base.field = localCtx.field;
|
|
1958
2030
|
}
|
|
1959
2031
|
}
|
|
1960
2032
|
|
|
@@ -1988,17 +2060,61 @@ export function AgentTerminal({
|
|
|
1988
2060
|
// Set agent ID for existing agents too
|
|
1989
2061
|
(window as any).currentAgentId = agentData.id;
|
|
1990
2062
|
|
|
2063
|
+
// Check if cost limit was exceeded (detect from existing messages)
|
|
2064
|
+
try {
|
|
2065
|
+
const costLimitMessage = (agentData.messages || []).find(
|
|
2066
|
+
(msg: AgentChatMessage) =>
|
|
2067
|
+
msg.role === "assistant" &&
|
|
2068
|
+
msg.content &&
|
|
2069
|
+
msg.content.startsWith("⚠️") &&
|
|
2070
|
+
msg.content.includes("Cost limit"),
|
|
2071
|
+
);
|
|
2072
|
+
if (
|
|
2073
|
+
costLimitMessage &&
|
|
2074
|
+
costLimitMessage.content &&
|
|
2075
|
+
agentData.costLimit
|
|
2076
|
+
) {
|
|
2077
|
+
// Extract cost from the message if possible
|
|
2078
|
+
const match = costLimitMessage.content.match(
|
|
2079
|
+
/Current cost: \$([0-9.]+)/,
|
|
2080
|
+
);
|
|
2081
|
+
const totalCost =
|
|
2082
|
+
match && match[1] ? parseFloat(match[1]) : agentData.totalCost || 0;
|
|
2083
|
+
setCostLimitExceeded({
|
|
2084
|
+
totalCost: totalCost,
|
|
2085
|
+
costLimit: agentData.costLimit,
|
|
2086
|
+
initialCostLimit: agentData.costLimit,
|
|
2087
|
+
});
|
|
2088
|
+
} else if (
|
|
2089
|
+
agentData.costLimit &&
|
|
2090
|
+
agentData.totalCost &&
|
|
2091
|
+
agentData.totalCost > agentData.costLimit
|
|
2092
|
+
) {
|
|
2093
|
+
// Fallback: check if current cost exceeds limit
|
|
2094
|
+
setCostLimitExceeded({
|
|
2095
|
+
totalCost: agentData.totalCost,
|
|
2096
|
+
costLimit: agentData.costLimit,
|
|
2097
|
+
initialCostLimit: agentData.costLimit,
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
console.error("Failed to check cost limit on load:", e);
|
|
2102
|
+
}
|
|
2103
|
+
|
|
1991
2104
|
// Parse metadata from DB if present (do not seed for existing agents)
|
|
1992
2105
|
const parsedMeta: AgentMetadata | null = (() => {
|
|
1993
2106
|
try {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2107
|
+
const contextJson = agentData.agentContext;
|
|
2108
|
+
if (!contextJson) return null;
|
|
2109
|
+
|
|
2110
|
+
const parsedContext = JSON.parse(contextJson);
|
|
2111
|
+
|
|
2112
|
+
// Context is stored as flat structure with top-level properties
|
|
2113
|
+
if (parsedContext && typeof parsedContext === "object") {
|
|
2114
|
+
return parsedContext as AgentMetadata;
|
|
2000
2115
|
}
|
|
2001
|
-
|
|
2116
|
+
|
|
2117
|
+
return null;
|
|
2002
2118
|
} catch {
|
|
2003
2119
|
return null;
|
|
2004
2120
|
}
|
|
@@ -2007,37 +2123,48 @@ export function AgentTerminal({
|
|
|
2007
2123
|
// For existing agents, use database metadata or none
|
|
2008
2124
|
setAgentMetadata(parsedMeta);
|
|
2009
2125
|
|
|
2010
|
-
// Connect to stream if agent is running
|
|
2126
|
+
// Connect to stream if agent is running
|
|
2127
|
+
// For agents waiting for approval, DON'T auto-reconnect on load - let the user click approve first
|
|
2011
2128
|
const isRunning =
|
|
2012
2129
|
agentData.status === "running" || (agentData.status as any) === 1;
|
|
2130
|
+
const isWaitingForApproval =
|
|
2131
|
+
agentData.status === "waitingForApproval" ||
|
|
2132
|
+
(agentData.status as any) === 2;
|
|
2133
|
+
|
|
2013
2134
|
if (isRunning) {
|
|
2014
|
-
//
|
|
2135
|
+
// Only auto-reconnect for running agents, not waiting for approval
|
|
2015
2136
|
setTimeout(async () => {
|
|
2016
|
-
// Check if we're already connecting to avoid duplicate connections
|
|
2017
2137
|
if (abortControllerRef.current) {
|
|
2018
2138
|
return;
|
|
2019
2139
|
}
|
|
2020
2140
|
|
|
2021
|
-
// Reset streaming state for reconnection
|
|
2022
2141
|
shouldCreateNewMessage.current = false;
|
|
2023
2142
|
|
|
2024
|
-
// If there are pending approvals in current messages, skip reconnect for now
|
|
2025
2143
|
try {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
return;
|
|
2144
|
+
if (editContext?.sessionId) {
|
|
2145
|
+
await startAgent({
|
|
2146
|
+
agentId: agentData.id,
|
|
2147
|
+
message: "",
|
|
2148
|
+
sessionId: editContext.sessionId,
|
|
2149
|
+
profileId: agentData.profileId || "",
|
|
2150
|
+
model: agentData.model,
|
|
2151
|
+
});
|
|
2035
2152
|
}
|
|
2036
|
-
} catch {
|
|
2153
|
+
} catch (startError: any) {
|
|
2154
|
+
console.warn(
|
|
2155
|
+
"Failed to call startAgent during reconnection:",
|
|
2156
|
+
startError,
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2037
2159
|
|
|
2038
|
-
// Use the existing connectToStream function with the loaded agent data
|
|
2039
2160
|
await connectToStream(agentData);
|
|
2040
2161
|
}, 100);
|
|
2162
|
+
} else if (isWaitingForApproval) {
|
|
2163
|
+
// For agents waiting for approval, just show the UI
|
|
2164
|
+
// The approval button handler will restart the agent if needed
|
|
2165
|
+
console.log(
|
|
2166
|
+
"Agent is waiting for approval - showing approval UI without auto-reconnect",
|
|
2167
|
+
);
|
|
2041
2168
|
}
|
|
2042
2169
|
} catch (err: any) {
|
|
2043
2170
|
console.error("❌ Failed to load agent:", err);
|
|
@@ -2133,14 +2260,14 @@ export function AgentTerminal({
|
|
|
2133
2260
|
useEffect(() => {
|
|
2134
2261
|
try {
|
|
2135
2262
|
const metaMode = (agentMetadata as any)?.mode as
|
|
2136
|
-
| "
|
|
2137
|
-
| "
|
|
2138
|
-
| "
|
|
2263
|
+
| "autonomous"
|
|
2264
|
+
| "read-only"
|
|
2265
|
+
| "supervised"
|
|
2139
2266
|
| undefined;
|
|
2140
2267
|
if (
|
|
2141
|
-
metaMode === "
|
|
2142
|
-
metaMode === "
|
|
2143
|
-
metaMode === "
|
|
2268
|
+
metaMode === "autonomous" ||
|
|
2269
|
+
metaMode === "read-only" ||
|
|
2270
|
+
metaMode === "supervised"
|
|
2144
2271
|
) {
|
|
2145
2272
|
setMode(metaMode);
|
|
2146
2273
|
return;
|
|
@@ -2149,9 +2276,9 @@ export function AgentTerminal({
|
|
|
2149
2276
|
try {
|
|
2150
2277
|
const serverMode = (agent as any)?.mode as string | undefined;
|
|
2151
2278
|
if (
|
|
2152
|
-
serverMode === "
|
|
2153
|
-
serverMode === "
|
|
2154
|
-
serverMode === "
|
|
2279
|
+
serverMode === "autonomous" ||
|
|
2280
|
+
serverMode === "read-only" ||
|
|
2281
|
+
serverMode === "supervised"
|
|
2155
2282
|
) {
|
|
2156
2283
|
setMode(serverMode);
|
|
2157
2284
|
}
|
|
@@ -2175,7 +2302,7 @@ export function AgentTerminal({
|
|
|
2175
2302
|
setAgentMetadata(nextMeta);
|
|
2176
2303
|
return;
|
|
2177
2304
|
}
|
|
2178
|
-
await
|
|
2305
|
+
await updateAgentContext(agent.id, nextMeta);
|
|
2179
2306
|
setAgentMetadata(nextMeta);
|
|
2180
2307
|
setAgent((prev) =>
|
|
2181
2308
|
prev ? { ...prev, metadata: JSON.stringify(nextMeta) } : prev,
|
|
@@ -2282,29 +2409,14 @@ export function AgentTerminal({
|
|
|
2282
2409
|
agentId: agent.id,
|
|
2283
2410
|
message: prompt.trim(),
|
|
2284
2411
|
sessionId: editContext.sessionId,
|
|
2285
|
-
profileId: activeProfile?.id || "
|
|
2286
|
-
profile: activeProfile?.name || "
|
|
2412
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
2413
|
+
profile: activeProfile?.name || profiles[0]?.name || "",
|
|
2287
2414
|
model: selectedModelId,
|
|
2288
|
-
itemid: editContext.currentItemDescriptor?.id || "",
|
|
2289
|
-
language: editContext.currentItemDescriptor?.language || "en",
|
|
2290
|
-
version: editContext.currentItemDescriptor?.version || 1,
|
|
2291
|
-
selection: effectiveSelection,
|
|
2292
|
-
selectedText: selectedTextFromCtx,
|
|
2293
2415
|
mode: mode as any,
|
|
2294
|
-
|
|
2295
|
-
mode === "ask"
|
|
2296
|
-
? activeProfile?.askModeTools &&
|
|
2297
|
-
activeProfile.askModeTools.length > 0
|
|
2298
|
-
? activeProfile.askModeTools
|
|
2299
|
-
: []
|
|
2300
|
-
: undefined,
|
|
2301
|
-
addSelectedComponents: !!effectiveSelection?.length,
|
|
2302
|
-
addContextContent: false,
|
|
2303
|
-
addAllContent: false,
|
|
2304
|
-
context: (agentMetadata as any)?.additionalData?.context,
|
|
2416
|
+
context: agentMetadata, // Flat structure with top-level properties
|
|
2305
2417
|
deterministic: deterministicFlags.deterministic,
|
|
2306
2418
|
seed: deterministicFlags.seed,
|
|
2307
|
-
};
|
|
2419
|
+
} as any;
|
|
2308
2420
|
|
|
2309
2421
|
// Starting agent
|
|
2310
2422
|
|
|
@@ -2469,29 +2581,14 @@ export function AgentTerminal({
|
|
|
2469
2581
|
agentId: agent.id,
|
|
2470
2582
|
message: text.trim(),
|
|
2471
2583
|
sessionId: editContext.sessionId,
|
|
2472
|
-
profileId: activeProfile?.id || "
|
|
2473
|
-
profile: activeProfile?.name || "
|
|
2584
|
+
profileId: activeProfile?.id || profiles[0]?.id || "",
|
|
2585
|
+
profile: activeProfile?.name || profiles[0]?.name || "",
|
|
2474
2586
|
model: selectedModelId,
|
|
2475
|
-
itemid: editContext.currentItemDescriptor?.id || "",
|
|
2476
|
-
language: editContext.currentItemDescriptor?.language || "en",
|
|
2477
|
-
version: editContext.currentItemDescriptor?.version || 1,
|
|
2478
|
-
selection: effectiveSelection,
|
|
2479
|
-
selectedText: selectedTextFromCtx,
|
|
2480
2587
|
mode: mode as any,
|
|
2481
|
-
allowedFunctions:
|
|
2482
|
-
mode === "ask"
|
|
2483
|
-
? activeProfile?.askModeTools &&
|
|
2484
|
-
activeProfile.askModeTools.length > 0
|
|
2485
|
-
? activeProfile.askModeTools
|
|
2486
|
-
: []
|
|
2487
|
-
: undefined,
|
|
2488
|
-
addSelectedComponents: !!effectiveSelection?.length,
|
|
2489
|
-
addContextContent: false,
|
|
2490
|
-
addAllContent: false,
|
|
2491
2588
|
context: (agentMetadata as any)?.additionalData?.context,
|
|
2492
2589
|
deterministic: deterministicFlags.deterministic,
|
|
2493
2590
|
seed: deterministicFlags.seed,
|
|
2494
|
-
};
|
|
2591
|
+
} as any;
|
|
2495
2592
|
|
|
2496
2593
|
setIsWaitingForResponse(true);
|
|
2497
2594
|
resetDotsTimer();
|
|
@@ -2522,7 +2619,8 @@ export function AgentTerminal({
|
|
|
2522
2619
|
if (!agent?.id) return;
|
|
2523
2620
|
const current = agentMetadata || {};
|
|
2524
2621
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2525
|
-
const
|
|
2622
|
+
const currentWithoutContext = { ...current };
|
|
2623
|
+
delete (currentWithoutContext as any).context;
|
|
2526
2624
|
const next: AgentMetadata = {
|
|
2527
2625
|
...currentWithoutContext,
|
|
2528
2626
|
additionalData: {
|
|
@@ -2562,7 +2660,7 @@ export function AgentTerminal({
|
|
|
2562
2660
|
}
|
|
2563
2661
|
|
|
2564
2662
|
// Persisted agents: update server and local cache
|
|
2565
|
-
await
|
|
2663
|
+
await updateAgentContext(agent.id, next);
|
|
2566
2664
|
setAgentMetadata(next);
|
|
2567
2665
|
setAgent((prev) =>
|
|
2568
2666
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2586,7 +2684,7 @@ export function AgentTerminal({
|
|
|
2586
2684
|
language: item.language,
|
|
2587
2685
|
version: item.version,
|
|
2588
2686
|
name: editContext.contentEditorItem?.name,
|
|
2589
|
-
path:
|
|
2687
|
+
path: undefined,
|
|
2590
2688
|
};
|
|
2591
2689
|
|
|
2592
2690
|
const current = agentMetadata || {};
|
|
@@ -2607,7 +2705,8 @@ export function AgentTerminal({
|
|
|
2607
2705
|
}
|
|
2608
2706
|
|
|
2609
2707
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2610
|
-
const
|
|
2708
|
+
const currentWithoutContext = { ...current };
|
|
2709
|
+
delete (currentWithoutContext as any).context;
|
|
2611
2710
|
const next: AgentMetadata = {
|
|
2612
2711
|
...currentWithoutContext,
|
|
2613
2712
|
additionalData: {
|
|
@@ -2626,7 +2725,7 @@ export function AgentTerminal({
|
|
|
2626
2725
|
setAgentMetadata(next);
|
|
2627
2726
|
return;
|
|
2628
2727
|
}
|
|
2629
|
-
await
|
|
2728
|
+
await updateAgentContext(agent.id, next);
|
|
2630
2729
|
setAgentMetadata(next);
|
|
2631
2730
|
setAgent((prev) =>
|
|
2632
2731
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2653,7 +2752,8 @@ export function AgentTerminal({
|
|
|
2653
2752
|
if (newComponentIds.length === 0) return; // No new components to add
|
|
2654
2753
|
|
|
2655
2754
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2656
|
-
const
|
|
2755
|
+
const currentWithoutContext = { ...current };
|
|
2756
|
+
delete (currentWithoutContext as any).context;
|
|
2657
2757
|
const next: AgentMetadata = {
|
|
2658
2758
|
...currentWithoutContext,
|
|
2659
2759
|
additionalData: {
|
|
@@ -2672,7 +2772,7 @@ export function AgentTerminal({
|
|
|
2672
2772
|
setAgentMetadata(next);
|
|
2673
2773
|
return;
|
|
2674
2774
|
}
|
|
2675
|
-
await
|
|
2775
|
+
await updateAgentContext(agent.id, next);
|
|
2676
2776
|
setAgentMetadata(next);
|
|
2677
2777
|
setAgent((prev) =>
|
|
2678
2778
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2696,7 +2796,8 @@ export function AgentTerminal({
|
|
|
2696
2796
|
if (newComponentIds.length === 0) return;
|
|
2697
2797
|
|
|
2698
2798
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2699
|
-
const
|
|
2799
|
+
const currentWithoutContext = { ...current };
|
|
2800
|
+
delete (currentWithoutContext as any).context;
|
|
2700
2801
|
const next: AgentMetadata = {
|
|
2701
2802
|
...currentWithoutContext,
|
|
2702
2803
|
additionalData: {
|
|
@@ -2715,7 +2816,7 @@ export function AgentTerminal({
|
|
|
2715
2816
|
setAgentMetadata(next);
|
|
2716
2817
|
return;
|
|
2717
2818
|
}
|
|
2718
|
-
await
|
|
2819
|
+
await updateAgentContext(agent.id, next);
|
|
2719
2820
|
setAgentMetadata(next);
|
|
2720
2821
|
setAgent((prev) =>
|
|
2721
2822
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2750,7 +2851,8 @@ export function AgentTerminal({
|
|
|
2750
2851
|
if (pagesToAdd.length === 0) return;
|
|
2751
2852
|
|
|
2752
2853
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2753
|
-
const
|
|
2854
|
+
const currentWithoutContext = { ...current };
|
|
2855
|
+
delete (currentWithoutContext as any).context;
|
|
2754
2856
|
const next: AgentMetadata = {
|
|
2755
2857
|
...currentWithoutContext,
|
|
2756
2858
|
additionalData: {
|
|
@@ -2769,7 +2871,7 @@ export function AgentTerminal({
|
|
|
2769
2871
|
setAgentMetadata(next);
|
|
2770
2872
|
return;
|
|
2771
2873
|
}
|
|
2772
|
-
await
|
|
2874
|
+
await updateAgentContext(agent.id, next);
|
|
2773
2875
|
setAgentMetadata(next);
|
|
2774
2876
|
setAgent((prev) =>
|
|
2775
2877
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2863,7 +2965,8 @@ export function AgentTerminal({
|
|
|
2863
2965
|
|
|
2864
2966
|
const current = agentMetadata || {};
|
|
2865
2967
|
// Exclude top-level context to avoid duplicate keys when spreading
|
|
2866
|
-
const
|
|
2968
|
+
const currentWithoutContext = { ...current };
|
|
2969
|
+
delete (currentWithoutContext as any).context;
|
|
2867
2970
|
const next: AgentMetadata = {
|
|
2868
2971
|
...currentWithoutContext,
|
|
2869
2972
|
additionalData: {
|
|
@@ -2891,7 +2994,7 @@ export function AgentTerminal({
|
|
|
2891
2994
|
setAgentMetadata(next);
|
|
2892
2995
|
return;
|
|
2893
2996
|
}
|
|
2894
|
-
await
|
|
2997
|
+
await updateAgentContext(agent.id, next);
|
|
2895
2998
|
setAgentMetadata(next);
|
|
2896
2999
|
setAgent((prev) =>
|
|
2897
3000
|
prev ? { ...prev, metadata: JSON.stringify(next) } : prev,
|
|
@@ -2933,8 +3036,8 @@ export function AgentTerminal({
|
|
|
2933
3036
|
editContext?.contentEditorItem?.name
|
|
2934
3037
|
) {
|
|
2935
3038
|
name = editContext.contentEditorItem.name;
|
|
2936
|
-
} else if (!name
|
|
2937
|
-
name =
|
|
3039
|
+
} else if (!name) {
|
|
3040
|
+
name = undefined;
|
|
2938
3041
|
}
|
|
2939
3042
|
setResolvedPageName(name);
|
|
2940
3043
|
} else {
|
|
@@ -2979,15 +3082,28 @@ export function AgentTerminal({
|
|
|
2979
3082
|
agentMetadata,
|
|
2980
3083
|
editContext?.page,
|
|
2981
3084
|
editContext?.contentEditorItem,
|
|
2982
|
-
agent?.itemPath,
|
|
2983
3085
|
editContext?.currentItemDescriptor,
|
|
2984
3086
|
editContext?.itemsRepository,
|
|
2985
3087
|
]);
|
|
2986
3088
|
|
|
2987
3089
|
// Stop current execution/stream safely
|
|
2988
|
-
const handleStop = useCallback(() => {
|
|
3090
|
+
const handleStop = useCallback(async () => {
|
|
2989
3091
|
try {
|
|
2990
3092
|
setIsWaitingForResponse(false);
|
|
3093
|
+
|
|
3094
|
+
// **CRITICAL FIX**: Actually cancel the agent execution on the backend
|
|
3095
|
+
// Before we were only disconnecting from the stream, letting the agent run
|
|
3096
|
+
if (agentStub?.id) {
|
|
3097
|
+
try {
|
|
3098
|
+
await cancelAgent(agentStub.id);
|
|
3099
|
+
console.log(`Agent ${agentStub.id} cancellation requested`);
|
|
3100
|
+
} catch (err) {
|
|
3101
|
+
console.error("Failed to cancel agent on backend:", err);
|
|
3102
|
+
// Continue with UI cleanup even if backend call fails
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
// Disconnect from the stream
|
|
2991
3107
|
if (abortControllerRef.current) {
|
|
2992
3108
|
abortControllerRef.current.abort();
|
|
2993
3109
|
abortControllerRef.current = null;
|
|
@@ -3009,7 +3125,7 @@ export function AgentTerminal({
|
|
|
3009
3125
|
} catch (e) {
|
|
3010
3126
|
console.error("Failed to stop agent execution", e);
|
|
3011
3127
|
}
|
|
3012
|
-
}, [resetDotsTimer]);
|
|
3128
|
+
}, [agentStub?.id, resetDotsTimer]);
|
|
3013
3129
|
|
|
3014
3130
|
// Ensure waiting state resets when no active work remains
|
|
3015
3131
|
useEffect(() => {
|
|
@@ -3074,29 +3190,21 @@ export function AgentTerminal({
|
|
|
3074
3190
|
</span>
|
|
3075
3191
|
</div>
|
|
3076
3192
|
<div className="flex gap-2">
|
|
3077
|
-
<button
|
|
3078
|
-
className="rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100"
|
|
3079
|
-
onClick={async () => {
|
|
3080
|
-
if (!agent?.id) return;
|
|
3081
|
-
try {
|
|
3082
|
-
await updateAgentCostLimit(agent.id, "remove");
|
|
3083
|
-
setCostLimitExceeded(null);
|
|
3084
|
-
// Reconnect to stream for next actions
|
|
3085
|
-
await connectToStream(agent);
|
|
3086
|
-
} catch (e) {
|
|
3087
|
-
console.error("Failed to remove cost limit", e);
|
|
3088
|
-
}
|
|
3089
|
-
}}
|
|
3090
|
-
>
|
|
3091
|
-
Continue and remove limit
|
|
3092
|
-
</button>
|
|
3093
3193
|
<button
|
|
3094
3194
|
className="rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100"
|
|
3095
3195
|
onClick={async () => {
|
|
3096
3196
|
if (!agent?.id) return;
|
|
3097
3197
|
try {
|
|
3098
3198
|
// Extend by initial cost limit amount
|
|
3099
|
-
await updateAgentCostLimit(agent.id, "extend");
|
|
3199
|
+
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
3200
|
+
|
|
3201
|
+
// Update the agent's cost limit in local state
|
|
3202
|
+
if (result.success && result.costLimit !== undefined) {
|
|
3203
|
+
setAgent((prev) =>
|
|
3204
|
+
prev ? { ...prev, costLimit: result.costLimit } : prev,
|
|
3205
|
+
);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3100
3208
|
setCostLimitExceeded(null);
|
|
3101
3209
|
await connectToStream(agent);
|
|
3102
3210
|
} catch (e) {
|
|
@@ -3104,7 +3212,7 @@ export function AgentTerminal({
|
|
|
3104
3212
|
}
|
|
3105
3213
|
}}
|
|
3106
3214
|
>
|
|
3107
|
-
|
|
3215
|
+
Extend limit and continue
|
|
3108
3216
|
</button>
|
|
3109
3217
|
</div>
|
|
3110
3218
|
</div>
|
|
@@ -3119,9 +3227,8 @@ export function AgentTerminal({
|
|
|
3119
3227
|
className="flex-1 overflow-y-auto"
|
|
3120
3228
|
onScroll={handleScroll}
|
|
3121
3229
|
>
|
|
3122
|
-
{renderCostLimitBanner()}
|
|
3123
3230
|
{error && (
|
|
3124
|
-
<div className="m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3">
|
|
3231
|
+
<div className="m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3 select-text">
|
|
3125
3232
|
<div className="flex items-start">
|
|
3126
3233
|
<AlertCircle
|
|
3127
3234
|
className="mt-0.5 h-5 w-5 text-red-400"
|
|
@@ -3171,9 +3278,22 @@ export function AgentTerminal({
|
|
|
3171
3278
|
(msg) => !msg.isCompleted && msg.messageType === "streaming",
|
|
3172
3279
|
);
|
|
3173
3280
|
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3281
|
+
// Filter out cost limit error messages (they're shown in the banner instead)
|
|
3282
|
+
const filteredMessages = group.messages.filter((msg) => {
|
|
3283
|
+
const content = msg.content || "";
|
|
3284
|
+
// Skip messages that are cost limit errors (shown in banner instead)
|
|
3285
|
+
return (
|
|
3286
|
+
!content.startsWith("⚠️") || !content.includes("Cost limit")
|
|
3287
|
+
);
|
|
3288
|
+
});
|
|
3289
|
+
|
|
3290
|
+
// If all messages were filtered out, don't render this group
|
|
3291
|
+
if (filteredMessages.length === 0) {
|
|
3292
|
+
return null;
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
const convertedMessages =
|
|
3296
|
+
convertAgentMessagesToAiFormat(filteredMessages);
|
|
3177
3297
|
|
|
3178
3298
|
return (
|
|
3179
3299
|
<AiResponseMessage
|
|
@@ -3223,6 +3343,7 @@ export function AgentTerminal({
|
|
|
3223
3343
|
<div className={showDots ? "visible" : "invisible"}>
|
|
3224
3344
|
<DancingDots />
|
|
3225
3345
|
</div>
|
|
3346
|
+
{renderCostLimitBanner()}
|
|
3226
3347
|
<div ref={messagesEndRef} />
|
|
3227
3348
|
</div>
|
|
3228
3349
|
|
|
@@ -3259,16 +3380,16 @@ export function AgentTerminal({
|
|
|
3259
3380
|
<TooltipTrigger asChild>
|
|
3260
3381
|
<select
|
|
3261
3382
|
className={`h-5 rounded border px-1.5 text-[10px] ${
|
|
3262
|
-
mode === "
|
|
3383
|
+
mode === "read-only"
|
|
3263
3384
|
? "border-green-300 bg-green-50 text-green-700"
|
|
3264
|
-
: mode === "
|
|
3385
|
+
: mode === "supervised"
|
|
3265
3386
|
? "border-amber-300 bg-amber-50 text-amber-700"
|
|
3266
3387
|
: "border-red-300 bg-red-50 text-red-700"
|
|
3267
3388
|
}`}
|
|
3268
3389
|
value={mode}
|
|
3269
3390
|
onChange={async (e) => {
|
|
3270
3391
|
const nextMode =
|
|
3271
|
-
(e.target.value as
|
|
3392
|
+
(e.target.value as AgentMode) || "supervised";
|
|
3272
3393
|
// Optimistic UI update
|
|
3273
3394
|
setMode(nextMode);
|
|
3274
3395
|
const current = agentMetadata || ({} as AgentMetadata);
|
|
@@ -3301,21 +3422,23 @@ export function AgentTerminal({
|
|
|
3301
3422
|
aria-label="Mode"
|
|
3302
3423
|
data-testid="agent-mode-select"
|
|
3303
3424
|
>
|
|
3304
|
-
<option value="
|
|
3305
|
-
<option value="
|
|
3306
|
-
<option value="
|
|
3425
|
+
<option value="supervised">Supervised</option>
|
|
3426
|
+
<option value="autonomous">Autonomous</option>
|
|
3427
|
+
<option value="read-only">Read-Only</option>
|
|
3307
3428
|
</select>
|
|
3308
3429
|
</TooltipTrigger>
|
|
3309
3430
|
<TooltipContent side="top" sideOffset={6}>
|
|
3310
3431
|
<div className="max-w-[320px] space-y-1">
|
|
3311
3432
|
<div>
|
|
3312
|
-
<span className="font-semibold text-green-500">
|
|
3313
|
-
|
|
3433
|
+
<span className="font-semibold text-green-500">
|
|
3434
|
+
Read-Only
|
|
3435
|
+
</span>
|
|
3436
|
+
: Limited tool access as configured by the profile (Ask Mode
|
|
3314
3437
|
Tools).
|
|
3315
3438
|
</div>
|
|
3316
3439
|
<div>
|
|
3317
3440
|
<span className="font-semibold text-amber-500">
|
|
3318
|
-
|
|
3441
|
+
Supervised
|
|
3319
3442
|
</span>
|
|
3320
3443
|
: Full tool access, but writes are limited to pages/items in
|
|
3321
3444
|
the current context. Creating new items or updating existing
|
|
@@ -3323,8 +3446,10 @@ export function AgentTerminal({
|
|
|
3323
3446
|
approval.
|
|
3324
3447
|
</div>
|
|
3325
3448
|
<div>
|
|
3326
|
-
<span className="font-semibold text-red-500">
|
|
3327
|
-
|
|
3449
|
+
<span className="font-semibold text-red-500">
|
|
3450
|
+
Autonomous
|
|
3451
|
+
</span>
|
|
3452
|
+
: Full tool access; can write across the site/project only
|
|
3328
3453
|
limited by user permissions.
|
|
3329
3454
|
</div>
|
|
3330
3455
|
</div>
|
|
@@ -3472,6 +3597,7 @@ export function AgentTerminal({
|
|
|
3472
3597
|
}
|
|
3473
3598
|
: totalTokens
|
|
3474
3599
|
}
|
|
3600
|
+
costLimit={agent?.costLimit}
|
|
3475
3601
|
/>
|
|
3476
3602
|
{(() => {
|
|
3477
3603
|
try {
|