@parhelia/core 0.1.12749 → 0.1.12755
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents-view/AgentsView.js +3 -3
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.js +28 -23
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/components/ui/PlaceholderInput.js +3 -3
- package/dist/components/ui/PlaceholderInput.js.map +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js +2 -2
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
- package/dist/components/ui/PlaceholderItemSelector.js +2 -2
- package/dist/components/ui/PlaceholderItemSelector.js.map +1 -1
- package/dist/config/config.js +4 -1
- package/dist/config/config.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +2 -2
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +1 -0
- package/dist/editor/MainLayout.js +64 -1
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/ai/AgentBanners.d.ts +11 -0
- package/dist/editor/ai/AgentBanners.js +16 -0
- package/dist/editor/ai/AgentBanners.js.map +1 -0
- package/dist/editor/ai/AgentCostDisplay.d.ts +12 -0
- package/dist/editor/ai/AgentCostDisplay.js +24 -1
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentDocumentList.js +5 -4
- package/dist/editor/ai/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/AgentInlineDialogContent.d.ts +17 -0
- package/dist/editor/ai/AgentInlineDialogContent.js +32 -0
- package/dist/editor/ai/AgentInlineDialogContent.js.map +1 -0
- package/dist/editor/ai/AgentSharingSection.d.ts +6 -0
- package/dist/editor/ai/AgentSharingSection.js +149 -0
- package/dist/editor/ai/AgentSharingSection.js.map +1 -0
- package/dist/editor/ai/AgentTerminal.d.ts +2 -1
- package/dist/editor/ai/AgentTerminal.js +288 -984
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +18 -0
- package/dist/editor/ai/AgentTerminalStatusBar.js +388 -125
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +10 -5
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/HeartbeatMessage.d.ts +4 -0
- package/dist/editor/ai/HeartbeatMessage.js +7 -0
- package/dist/editor/ai/HeartbeatMessage.js.map +1 -0
- package/dist/editor/ai/InitialThinkingSplash.d.ts +3 -0
- package/dist/editor/ai/InitialThinkingSplash.js +9 -0
- package/dist/editor/ai/InitialThinkingSplash.js.map +1 -0
- package/dist/editor/ai/InlineAiDialog.js +49 -1
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/QueuedPromptsPanel.d.ts +4 -0
- package/dist/editor/ai/QueuedPromptsPanel.js +36 -0
- package/dist/editor/ai/QueuedPromptsPanel.js.map +1 -0
- package/dist/editor/ai/TodoListPanel.d.ts +5 -0
- package/dist/editor/ai/TodoListPanel.js +113 -0
- package/dist/editor/ai/TodoListPanel.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.js +54 -10
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/UserMessage.d.ts +4 -0
- package/dist/editor/ai/UserMessage.js +42 -0
- package/dist/editor/ai/UserMessage.js.map +1 -0
- package/dist/editor/ai/agentMessageConversion.d.ts +11 -0
- package/dist/editor/ai/agentMessageConversion.js +134 -0
- package/dist/editor/ai/agentMessageConversion.js.map +1 -0
- package/dist/editor/ai/agentMessageGrouping.d.ts +22 -0
- package/dist/editor/ai/agentMessageGrouping.js +103 -0
- package/dist/editor/ai/agentMessageGrouping.js.map +1 -0
- package/dist/editor/ai/agentMessageHelpers.d.ts +23 -0
- package/dist/editor/ai/agentMessageHelpers.js +124 -0
- package/dist/editor/ai/agentMessageHelpers.js.map +1 -0
- package/dist/editor/ai/agentMessageMarkdown.d.ts +2 -0
- package/dist/editor/ai/agentMessageMarkdown.js +14 -0
- package/dist/editor/ai/agentMessageMarkdown.js.map +1 -0
- package/dist/editor/ai/agentTodoExtraction.d.ts +12 -0
- package/dist/editor/ai/agentTodoExtraction.js +205 -0
- package/dist/editor/ai/agentTodoExtraction.js.map +1 -0
- package/dist/editor/ai/useAgentStatus.js +2 -2
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/client/EditorShell.js +19 -1
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +5 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/operations.d.ts +4 -1
- package/dist/editor/client/operations.js +57 -5
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.js +3 -4
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +35 -2
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +22 -3
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PreviewDatePicker.d.ts +1 -0
- package/dist/editor/menubar/PreviewDatePicker.js +55 -0
- package/dist/editor/menubar/PreviewDatePicker.js.map +1 -0
- package/dist/editor/menubar/VersionPreviewCard.js +34 -3
- package/dist/editor/menubar/VersionPreviewCard.js.map +1 -1
- package/dist/editor/menubar/VersionSelector.d.ts +2 -1
- package/dist/editor/menubar/VersionSelector.js +14 -15
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +2 -2
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +2 -2
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +81 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +22 -90
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +12 -2
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +4 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +2 -0
- package/dist/editor/page-viewer/pageViewContext.js +8 -1
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/pageModel.d.ts +2 -0
- package/dist/editor/personalization/ActionList.d.ts +7 -0
- package/dist/editor/personalization/ActionList.js +16 -0
- package/dist/editor/personalization/ActionList.js.map +1 -0
- package/dist/editor/personalization/ConditionTreeEditor.d.ts +7 -0
- package/dist/editor/personalization/ConditionTreeEditor.js +117 -0
- package/dist/editor/personalization/ConditionTreeEditor.js.map +1 -0
- package/dist/editor/personalization/PersonalizationPanel.d.ts +12 -0
- package/dist/editor/personalization/PersonalizationPanel.js +308 -0
- package/dist/editor/personalization/PersonalizationPanel.js.map +1 -0
- package/dist/editor/personalization/RuleElementPicker.d.ts +11 -0
- package/dist/editor/personalization/RuleElementPicker.js +68 -0
- package/dist/editor/personalization/RuleElementPicker.js.map +1 -0
- package/dist/editor/personalization/RuleList.d.ts +19 -0
- package/dist/editor/personalization/RuleList.js +132 -0
- package/dist/editor/personalization/RuleList.js.map +1 -0
- package/dist/editor/personalization/RuleParameterInput.d.ts +10 -0
- package/dist/editor/personalization/RuleParameterInput.js +115 -0
- package/dist/editor/personalization/RuleParameterInput.js.map +1 -0
- package/dist/editor/personalization/TreeItemPicker.d.ts +9 -0
- package/dist/editor/personalization/TreeItemPicker.js +115 -0
- package/dist/editor/personalization/TreeItemPicker.js.map +1 -0
- package/dist/editor/personalization/utils.d.ts +6 -0
- package/dist/editor/personalization/utils.js +37 -0
- package/dist/editor/personalization/utils.js.map +1 -0
- package/dist/editor/reviews/SuggestedEdit.js +65 -48
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +5 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +24 -0
- package/dist/editor/services/agentService.js +40 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/agentStatus.d.ts +1 -0
- package/dist/editor/services/agentStatus.js +55 -1
- package/dist/editor/services/agentStatus.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +10 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +5 -0
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/personalizationService.d.ts +5 -0
- package/dist/editor/services/personalizationService.js +32 -0
- package/dist/editor/services/personalizationService.js.map +1 -0
- package/dist/editor/services/serviceHelper.d.ts +1 -1
- package/dist/editor/services/serviceHelper.js +2 -1
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/settings/panels/AgentProfileConfigPanel.js +1 -1
- package/dist/editor/settings/panels/AgentProfileConfigPanel.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 +3 -3
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/ui/PublishRestrictionsDialog.js +1 -0
- package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +1 -0
- package/dist/editor/ui/SimpleTabs.js +2 -2
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +4 -0
- package/dist/editor/ui/Splitter.js +42 -17
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/utils/sitecoreDate.d.ts +2 -0
- package/dist/editor/utils/sitecoreDate.js +27 -0
- package/dist/editor/utils/sitecoreDate.js.map +1 -0
- package/dist/editor/utils/versionAtDate.d.ts +2 -0
- package/dist/editor/utils/versionAtDate.js +23 -0
- package/dist/editor/utils/versionAtDate.js.map +1 -0
- package/dist/lib/viewTransition.d.ts +1 -0
- package/dist/lib/viewTransition.js +17 -0
- package/dist/lib/viewTransition.js.map +1 -0
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +126 -87
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/ProjectAgentsPanel.js +2 -2
- package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -1
- package/dist/task-board/components/ProjectDashboard.js +2 -3
- package/dist/task-board/components/ProjectDashboard.js.map +1 -1
- package/dist/task-board/components/ProjectPropertiesPanel.js +76 -24
- package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
- package/dist/task-board/components/TaskBoardTitlebar.js +2 -1
- package/dist/task-board/components/TaskBoardTitlebar.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.d.ts +10 -0
- package/dist/task-board/components/TaskDetailPanel.js +10 -5
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/types.d.ts +55 -1
- package/package.json +1 -1
- package/styles.css +11 -10
- package/dist/task-board/components/AssignAgentDialog.d.ts +0 -7
- package/dist/task-board/components/AssignAgentDialog.js +0 -104
- package/dist/task-board/components/AssignAgentDialog.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
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 { flushSync } from "react-dom";
|
|
4
|
-
import { Send, AlertCircle, Loader2,
|
|
5
|
-
import { getAgent, startAgent, claimAgentBrowser, cancelAgentDialog, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, getAgentDiagnostics, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
|
|
6
|
-
import { parseAgentStatus } from "../services/agentStatus";
|
|
4
|
+
import { Send, AlertCircle, Loader2, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
|
|
5
|
+
import { getAgent, startAgent, claimAgentBrowser, cancelAgentDialog, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, getAgentDiagnostics, releaseAgentBrowser, rejectToolCall, revokeAgentSkill, } from "../services/agentService";
|
|
6
|
+
import { parseAgentRunStatusData, parseAgentStatus, } from "../services/agentStatus";
|
|
7
7
|
import { useEditContext, useFieldsEditContext } from "../client/editContext";
|
|
8
8
|
import { localStorageService } from "../services/localStorageService";
|
|
9
9
|
import { Textarea } from "../../components/ui/textarea";
|
|
@@ -11,19 +11,31 @@ import { Button } from "../../components/ui/button";
|
|
|
11
11
|
import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
|
|
12
12
|
import { AiResponseMessage } from "./AiResponseMessage";
|
|
13
13
|
import { ContextInfoBar } from "./ContextInfoBar";
|
|
14
|
+
import { UserMessage } from "./UserMessage";
|
|
15
|
+
import { HeartbeatMessage } from "./HeartbeatMessage";
|
|
16
|
+
import { calculateTotalTokens, getOperationsForMessageGroup, groupConsecutiveMessages, mergeMessagesById, } from "./agentMessageGrouping";
|
|
17
|
+
import { convertAgentMessagesToAiFormat, extractToolCallFields, stringifyToolField, } from "./agentMessageConversion";
|
|
18
|
+
import { TodoListPanel } from "./TodoListPanel";
|
|
19
|
+
import { extractTodosFromMessages } from "./agentTodoExtraction";
|
|
20
|
+
import { QueuedPromptsPanel } from "./QueuedPromptsPanel";
|
|
21
|
+
import { AgentCapacityBanner, AgentCostLimitBanner, AgentErrorBanner, } from "./AgentBanners";
|
|
22
|
+
import { InitialThinkingSplash } from "./InitialThinkingSplash";
|
|
23
|
+
import { AgentInlineDialogContent } from "./AgentInlineDialogContent";
|
|
24
|
+
import { AGENT_HISTORY_LIMIT, MACHINE_CAPACITY_REASON, buildPlaceholderAgentDetails, formatAllowanceLabel, formatAllowanceSource, getAgentRunMessageAgentId, getAgentRunMessageDetail, getAgentRunMessageSeq, getVisibleDialogRegistry, isAgentErrorStatusValue, isFinishedServerExecutionStatus, isHeartbeatRunEventMessage, mergeAgentOperationHistory, normalizeDialogAgentId, normalizeProfileAllowanceOperations, normalizeServerExecutionStatus, } from "./agentMessageHelpers";
|
|
14
25
|
import { AgentDocumentList, } from "./AgentDocumentList";
|
|
15
26
|
import { AgentEditOperationsPanel } from "./EditOperationsPanel";
|
|
16
27
|
import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
|
|
28
|
+
import { AgentSharingSection } from "./AgentSharingSection";
|
|
17
29
|
import { getComponentById } from "../componentTreeHelper";
|
|
18
30
|
import { toUserFacingAgentErrorMessage } from "../services/agentErrorMessage";
|
|
19
31
|
import { AgentGreeting } from "./AgentGreeting";
|
|
20
32
|
import { getAgentHistory } from "../services/editService";
|
|
21
|
-
import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
|
|
22
33
|
import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
|
|
34
|
+
import { invalidateAiProfilesCache } from "../services/aiService";
|
|
23
35
|
import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
|
|
24
36
|
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
|
|
25
37
|
import { SecretAgentIcon } from "../ui/Icons";
|
|
26
|
-
import { formatTime
|
|
38
|
+
import { formatTime } from "../utils";
|
|
27
39
|
import { cn } from "../../lib/utils";
|
|
28
40
|
import { sanitizeSvg } from "../../lib/sanitize";
|
|
29
41
|
import { Select } from "../../components/ui/select";
|
|
@@ -31,802 +43,22 @@ import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
|
|
|
31
43
|
import { SimpleTabs } from "../ui/SimpleTabs";
|
|
32
44
|
import { Splitter } from "../ui/Splitter";
|
|
33
45
|
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
34
|
-
import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
|
|
35
|
-
const AGENT_HISTORY_LIMIT = 1000;
|
|
36
46
|
const RECENT_RUN_EVENTS_LIMIT = 50;
|
|
37
|
-
function mergeAgentOperationHistory(existing, incoming, limit = AGENT_HISTORY_LIMIT) {
|
|
38
|
-
const merged = new Map(existing.map((operation) => [operation.id, operation]));
|
|
39
|
-
for (const operation of incoming) {
|
|
40
|
-
merged.set(operation.id, operation);
|
|
41
|
-
}
|
|
42
|
-
return Array.from(merged.values())
|
|
43
|
-
.sort((left, right) => new Date(right.date).getTime() - new Date(left.date).getTime())
|
|
44
|
-
.slice(0, limit);
|
|
45
|
-
}
|
|
46
|
-
const userMessageMarkdownComponents = {
|
|
47
|
-
h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
|
|
48
|
-
h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
|
|
49
|
-
h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
|
|
50
|
-
h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
|
|
51
|
-
p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
|
|
52
|
-
ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
53
|
-
ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
|
|
54
|
-
li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
|
|
55
|
-
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" })),
|
|
56
|
-
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 })),
|
|
57
|
-
};
|
|
58
|
-
function buildPlaceholderAgentDetails(agentStub) {
|
|
59
|
-
const now = new Date().toISOString();
|
|
60
|
-
const updated = agentStub.updatedDate || now;
|
|
61
|
-
// AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
|
|
62
|
-
// This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
|
|
63
|
-
return {
|
|
64
|
-
...agentStub,
|
|
65
|
-
name: agentStub.name || "Agent",
|
|
66
|
-
userId: agentStub.userId || "",
|
|
67
|
-
updatedDate: updated,
|
|
68
|
-
profileName: agentStub.profileName || "",
|
|
69
|
-
model: agentStub.model || "",
|
|
70
|
-
createdDate: agentStub.createdDate || updated,
|
|
71
|
-
totalTokensUsed: 0,
|
|
72
|
-
totalInputTokens: 0,
|
|
73
|
-
totalOutputTokens: 0,
|
|
74
|
-
totalCachedInputTokens: 0,
|
|
75
|
-
totalInputTokenCost: 0,
|
|
76
|
-
totalOutputTokenCost: 0,
|
|
77
|
-
totalCachedInputTokenCost: 0,
|
|
78
|
-
totalImageCost: 0,
|
|
79
|
-
totalCost: 0,
|
|
80
|
-
currency: agentStub.currency || "USD",
|
|
81
|
-
messageCount: agentStub.messageCount || 0,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function normalizeDialogAgentId(value) {
|
|
85
|
-
return value?.trim().toLowerCase() || "";
|
|
86
|
-
}
|
|
87
|
-
function normalizeServerExecutionStatus(value) {
|
|
88
|
-
return (value || "").replace(/\s+/g, "").toLowerCase();
|
|
89
|
-
}
|
|
90
|
-
function isFinishedServerExecutionStatus(value) {
|
|
91
|
-
const normalized = normalizeServerExecutionStatus(value);
|
|
92
|
-
return normalized === "completed" || normalized === "cancelled";
|
|
93
|
-
}
|
|
94
|
-
const MACHINE_CAPACITY_REASON = "machineCapacity";
|
|
95
|
-
const MACHINE_CAPACITY_DETAIL = "waitingForCapacity";
|
|
96
|
-
function formatAllowanceSource(source) {
|
|
97
|
-
const normalized = source?.trim();
|
|
98
|
-
if (!normalized)
|
|
99
|
-
return null;
|
|
100
|
-
if (normalized === "user")
|
|
101
|
-
return "User granted";
|
|
102
|
-
if (normalized.startsWith("preconfigured:profile:"))
|
|
103
|
-
return "Profile";
|
|
104
|
-
if (normalized.startsWith("preconfigured:skill:"))
|
|
105
|
-
return "Skill";
|
|
106
|
-
if (normalized.startsWith("system:")) {
|
|
107
|
-
return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
|
|
108
|
-
}
|
|
109
|
-
return normalized;
|
|
110
|
-
}
|
|
111
|
-
function formatAllowanceLabel(allowance) {
|
|
112
|
-
return `${allowance.operationType || "*"}${"itemPath" in allowance
|
|
113
|
-
? ` ${allowance.itemPath}`
|
|
114
|
-
: ` ${allowance.normalizedPath}`}`;
|
|
115
|
-
}
|
|
116
|
-
function getAgentRunMessageAgentId(payload) {
|
|
117
|
-
const agentId = payload?.agentId;
|
|
118
|
-
return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
|
|
119
|
-
}
|
|
120
|
-
function getAgentRunMessageSeq(payload) {
|
|
121
|
-
const seq = payload?.seq;
|
|
122
|
-
return typeof seq === "number" ? seq : null;
|
|
123
|
-
}
|
|
124
|
-
function getAgentRunMessageDetail(type, payload) {
|
|
125
|
-
if (type === "agent:run:delta") {
|
|
126
|
-
return payload?.type || null;
|
|
127
|
-
}
|
|
128
|
-
if (type === "agent:run:status") {
|
|
129
|
-
if (payload?.data?.reason === MACHINE_CAPACITY_REASON) {
|
|
130
|
-
return MACHINE_CAPACITY_DETAIL;
|
|
131
|
-
}
|
|
132
|
-
return payload?.data?.state || payload?.data?.status || null;
|
|
133
|
-
}
|
|
134
|
-
if (type === "agent:run:error") {
|
|
135
|
-
return payload?.error || null;
|
|
136
|
-
}
|
|
137
|
-
if (type === "agent:run:complete") {
|
|
138
|
-
return payload?.finalStatus || null;
|
|
139
|
-
}
|
|
140
|
-
if (type === "agent:run:start") {
|
|
141
|
-
return payload?.agentName || null;
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
function isHeartbeatRunEventMessage(message) {
|
|
146
|
-
if (!message)
|
|
147
|
-
return false;
|
|
148
|
-
if (message.type === "agent:run:delta") {
|
|
149
|
-
const deltaType = message.payload?.type;
|
|
150
|
-
return String(deltaType || "").toLowerCase() === "heartbeat";
|
|
151
|
-
}
|
|
152
|
-
if (message.type === "agent:run:status") {
|
|
153
|
-
const state = message.payload?.data?.state || message.payload?.data?.status;
|
|
154
|
-
return String(state || "").toLowerCase() === "heartbeat";
|
|
155
|
-
}
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
function getVisibleDialogRegistry() {
|
|
159
|
-
const registry = globalThis.__agentDialogVisibleCallbacks;
|
|
160
|
-
return registry && typeof registry === "object" ? registry : {};
|
|
161
|
-
}
|
|
162
|
-
function isAgentErrorStatusValue(status) {
|
|
163
|
-
return status === "error";
|
|
164
|
-
}
|
|
165
|
-
// Simple user message component
|
|
166
|
-
const UserMessage = ({ message }) => {
|
|
167
|
-
const content = message.content || "";
|
|
168
|
-
const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
|
|
169
|
-
// Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
|
|
170
|
-
const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
|
|
171
|
-
const triggerMatch = content.match(triggerPattern);
|
|
172
|
-
const triggerName = triggerMatch?.[1]?.trim() || "";
|
|
173
|
-
const triggerContent = triggerMatch?.[2] || "";
|
|
174
|
-
const isTriggerMessage = triggerName.length > 0;
|
|
175
|
-
if (isTriggerMessage) {
|
|
176
|
-
return (_jsx("div", { className: "px-4 py-2", children: _jsxs("div", { className: "text-[11px]", children: [_jsxs("button", { type: "button", onClick: () => setIsTriggerExpanded((expanded) => !expanded), className: "text-theme-secondary hover:bg-theme-hover flex w-full items-center gap-2 rounded-md border-l-2 border-cyan-200 px-2 py-1 text-left transition-colors", "data-testid": "trigger-message-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.5 }), _jsxs("span", { className: "truncate font-medium", children: ["Trigger: ", triggerName] }), message.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-gray-400", children: formatTime(new Date(message.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-1 border-l-2 border-cyan-100 pl-[1.35rem] text-[11px] text-gray-600", children: _jsx(MarkdownDisplay, { source: triggerContent, components: userMessageMarkdownComponents }) }))] }) }));
|
|
177
|
-
}
|
|
178
|
-
// Parse source agent name from content if it starts with "[From ...]:"
|
|
179
|
-
// Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
|
|
180
|
-
const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
|
|
181
|
-
const match = content.match(fromPattern);
|
|
182
|
-
let sourceAgentName;
|
|
183
|
-
let displayContent = content;
|
|
184
|
-
if (match && match[1]) {
|
|
185
|
-
const extractedName = match[1];
|
|
186
|
-
// If it says "From User", treat it as a regular user message
|
|
187
|
-
if (extractedName.toLowerCase() === "user") {
|
|
188
|
-
sourceAgentName = undefined;
|
|
189
|
-
displayContent = content; // Keep original content
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
sourceAgentName = extractedName;
|
|
193
|
-
displayContent = match[2] || ""; // Remove the "[From ...]:" prefix from content
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// Fallback: check for source agent name in message metadata or properties
|
|
198
|
-
sourceAgentName =
|
|
199
|
-
message.sourceAgentName ||
|
|
200
|
-
message.metadata?.sourceAgentName ||
|
|
201
|
-
message.sourceAgent?.name;
|
|
202
|
-
}
|
|
203
|
-
const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
|
|
204
|
-
return (_jsxs("div", { className: "flex gap-3 p-4", children: [_jsx("div", { className: "shrink-0", children: _jsx(User, { className: "text-theme-secondary h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "min-w-0 flex-1 select-text", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-[12px] font-medium text-gray-900", children: displayName }), message.createdDate && (_jsx("span", { className: "text-[12px] text-gray-400", "data-testid": "user-message-timestamp", "data-timestamp": message.createdDate, children: formatTime(new Date(message.createdDate)) }))] }), _jsx("div", { className: "prose prose max-w-none text-[12px] text-gray-700 select-text", children: _jsx(MarkdownDisplay, { source: displayContent, components: userMessageMarkdownComponents }) })] })] }));
|
|
205
|
-
};
|
|
206
|
-
const HeartbeatMessage = ({ message }) => {
|
|
207
|
-
return (_jsx("div", { className: "px-4 py-2", "data-testid": "agent-heartbeat-message", children: _jsxs("div", { className: "flex items-center gap-2 rounded-md border border-sky-100 bg-sky-50/80 px-3 py-2 text-[11px] text-sky-700", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 flex-1", children: message.content }), message.createdDate && (_jsx("span", { className: "shrink-0 text-[10px] text-sky-500", children: formatTime(new Date(message.createdDate)) }))] }) }));
|
|
208
|
-
};
|
|
209
|
-
// Helper to extract todos from potentially incomplete JSON during streaming
|
|
210
|
-
const extractPartialTodos = (jsonText) => {
|
|
211
|
-
// First try to parse complete JSON
|
|
212
|
-
try {
|
|
213
|
-
const parsed = JSON.parse(jsonText);
|
|
214
|
-
const result = Array.isArray(parsed) ? parsed : parsed?.items || [];
|
|
215
|
-
return result;
|
|
216
|
-
}
|
|
217
|
-
catch (e) {
|
|
218
|
-
// If JSON is incomplete, try to extract whatever todo items we can find
|
|
219
|
-
const items = [];
|
|
220
|
-
// Look for individual todo objects in the partial JSON
|
|
221
|
-
// Match patterns like: { "text": "...", "done": false, "note": "..." }
|
|
222
|
-
// Handle various field orderings (text can be anywhere in the object)
|
|
223
|
-
const textPattern = /"text"\s*:\s*"([^"]+)"/g;
|
|
224
|
-
const textMatches = [];
|
|
225
|
-
let textMatch;
|
|
226
|
-
while ((textMatch = textPattern.exec(jsonText)) !== null) {
|
|
227
|
-
if (textMatch[1]) {
|
|
228
|
-
textMatches.push({
|
|
229
|
-
text: textMatch[1],
|
|
230
|
-
startIdx: textMatch.index,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// For each text field found, try to find the enclosing object
|
|
235
|
-
for (const { text, startIdx } of textMatches) {
|
|
236
|
-
// Find the opening brace before this text field
|
|
237
|
-
let openBrace = -1;
|
|
238
|
-
for (let i = startIdx - 1; i >= 0; i--) {
|
|
239
|
-
if (jsonText[i] === "{") {
|
|
240
|
-
openBrace = i;
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
if (jsonText[i] === "}")
|
|
244
|
-
break; // Hit another object's end
|
|
245
|
-
}
|
|
246
|
-
if (openBrace === -1)
|
|
247
|
-
continue;
|
|
248
|
-
// Find the closing brace after this text field
|
|
249
|
-
let closeBrace = -1;
|
|
250
|
-
let depth = 0;
|
|
251
|
-
for (let i = openBrace; i < jsonText.length; i++) {
|
|
252
|
-
if (jsonText[i] === "{")
|
|
253
|
-
depth++;
|
|
254
|
-
if (jsonText[i] === "}") {
|
|
255
|
-
depth--;
|
|
256
|
-
if (depth === 0) {
|
|
257
|
-
closeBrace = i;
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
// Extract the object and try to parse it
|
|
263
|
-
const objStr = closeBrace !== -1
|
|
264
|
-
? jsonText.substring(openBrace, closeBrace + 1)
|
|
265
|
-
: jsonText.substring(openBrace) + "}"; // Try to close incomplete object
|
|
266
|
-
try {
|
|
267
|
-
const obj = JSON.parse(objStr);
|
|
268
|
-
if (obj.title || obj.text) {
|
|
269
|
-
items.push({
|
|
270
|
-
title: obj.title || obj.text,
|
|
271
|
-
status: obj.status || "pending",
|
|
272
|
-
instructions: obj.instructions || obj.note || undefined,
|
|
273
|
-
agentProfileId: obj.agentProfileId || undefined,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch (e) {
|
|
278
|
-
// Skip malformed objects
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// Also try to extract from partial objects at the end
|
|
282
|
-
// Look for the last opening brace and try to parse up to where we have valid content
|
|
283
|
-
const lines = jsonText.split("\n");
|
|
284
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
285
|
-
const partialJson = lines.slice(0, i + 1).join("\n");
|
|
286
|
-
// Try to close any open braces/brackets
|
|
287
|
-
let testJson = partialJson;
|
|
288
|
-
const openBraces = (testJson.match(/\{/g) || []).length;
|
|
289
|
-
const closeBraces = (testJson.match(/\}/g) || []).length;
|
|
290
|
-
const openBrackets = (testJson.match(/\[/g) || []).length;
|
|
291
|
-
const closeBrackets = (testJson.match(/\]/g) || []).length;
|
|
292
|
-
// Add missing closing characters
|
|
293
|
-
testJson += "]".repeat(openBrackets - closeBrackets);
|
|
294
|
-
testJson += "}".repeat(openBraces - closeBraces);
|
|
295
|
-
try {
|
|
296
|
-
const parsed = JSON.parse(testJson);
|
|
297
|
-
const partialItems = Array.isArray(parsed)
|
|
298
|
-
? parsed
|
|
299
|
-
: parsed?.items || [];
|
|
300
|
-
if (partialItems.length > items.length) {
|
|
301
|
-
return partialItems;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
catch (e) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
return items;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
const extractTodosFromMessages = (messages) => {
|
|
312
|
-
const todos = [];
|
|
313
|
-
const fencedTodoToken = "```todo_list";
|
|
314
|
-
const plainTodoToken = "todo_list";
|
|
315
|
-
for (const message of messages) {
|
|
316
|
-
if (message.role !== "assistant" || !message.content)
|
|
317
|
-
continue;
|
|
318
|
-
const content = message.content;
|
|
319
|
-
let cursor = 0;
|
|
320
|
-
while (cursor < content.length) {
|
|
321
|
-
const nextFenced = content.indexOf(fencedTodoToken, cursor);
|
|
322
|
-
const nextPlain = content.indexOf(plainTodoToken, cursor);
|
|
323
|
-
let todoStart = -1;
|
|
324
|
-
let isFenced = false;
|
|
325
|
-
if (nextFenced !== -1 && (nextPlain === -1 || nextFenced < nextPlain)) {
|
|
326
|
-
todoStart = nextFenced;
|
|
327
|
-
isFenced = true;
|
|
328
|
-
}
|
|
329
|
-
else if (nextPlain !== -1) {
|
|
330
|
-
// Check if it's at line start
|
|
331
|
-
const before = nextPlain > 0 ? content[nextPlain - 1] : "\n";
|
|
332
|
-
if (before === "\n" || before === "\r" || nextPlain === 0) {
|
|
333
|
-
todoStart = nextPlain;
|
|
334
|
-
isFenced = false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (todoStart === -1)
|
|
338
|
-
break;
|
|
339
|
-
try {
|
|
340
|
-
let jsonText = "";
|
|
341
|
-
let isComplete = true;
|
|
342
|
-
if (isFenced) {
|
|
343
|
-
const afterToken = todoStart + fencedTodoToken.length;
|
|
344
|
-
const closePos = content.indexOf("```", afterToken);
|
|
345
|
-
if (closePos === -1) {
|
|
346
|
-
// Incomplete fenced block - extract what we have so far
|
|
347
|
-
jsonText = content.slice(afterToken).trim();
|
|
348
|
-
isComplete = false;
|
|
349
|
-
cursor = content.length; // Process till end
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
jsonText = content.slice(afterToken, closePos).trim();
|
|
353
|
-
cursor = closePos + 3;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
const afterToken = todoStart + plainTodoToken.length;
|
|
358
|
-
const braceStart = content.indexOf("{", afterToken);
|
|
359
|
-
if (braceStart === -1)
|
|
360
|
-
break;
|
|
361
|
-
let depth = 0;
|
|
362
|
-
let braceEnd = -1;
|
|
363
|
-
for (let i = braceStart; i < content.length; i++) {
|
|
364
|
-
if (content[i] === "{")
|
|
365
|
-
depth++;
|
|
366
|
-
if (content[i] === "}") {
|
|
367
|
-
depth--;
|
|
368
|
-
if (depth === 0) {
|
|
369
|
-
braceEnd = i;
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
if (braceEnd === -1) {
|
|
375
|
-
// Incomplete JSON - extract what we have
|
|
376
|
-
jsonText = content.slice(braceStart).trim();
|
|
377
|
-
isComplete = false;
|
|
378
|
-
cursor = content.length;
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
jsonText = content.slice(braceStart, braceEnd + 1).trim();
|
|
382
|
-
cursor = braceEnd + 1;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
// Use the partial extraction helper for incomplete JSON
|
|
386
|
-
const todoItems = isComplete
|
|
387
|
-
? (() => {
|
|
388
|
-
try {
|
|
389
|
-
const parsed = JSON.parse(jsonText);
|
|
390
|
-
return Array.isArray(parsed) ? parsed : parsed?.items || [];
|
|
391
|
-
}
|
|
392
|
-
catch (e) {
|
|
393
|
-
return [];
|
|
394
|
-
}
|
|
395
|
-
})()
|
|
396
|
-
: extractPartialTodos(jsonText);
|
|
397
|
-
const title = (() => {
|
|
398
|
-
try {
|
|
399
|
-
const parsed = JSON.parse(jsonText);
|
|
400
|
-
return Array.isArray(parsed) ? undefined : parsed?.title;
|
|
401
|
-
}
|
|
402
|
-
catch (e) {
|
|
403
|
-
return undefined;
|
|
404
|
-
}
|
|
405
|
-
})();
|
|
406
|
-
todoItems.forEach((item) => {
|
|
407
|
-
if (!item)
|
|
408
|
-
return;
|
|
409
|
-
const title = item.title ||
|
|
410
|
-
item.text ||
|
|
411
|
-
item.content ||
|
|
412
|
-
item.label ||
|
|
413
|
-
String(item.task || "");
|
|
414
|
-
if (!title)
|
|
415
|
-
return;
|
|
416
|
-
todos.push({
|
|
417
|
-
id: item.id,
|
|
418
|
-
title,
|
|
419
|
-
status: item.status || "pending",
|
|
420
|
-
instructions: item.instructions || item.note || item.description,
|
|
421
|
-
agentProfileId: item.agentProfileId,
|
|
422
|
-
messageId: message.id,
|
|
423
|
-
sourceTitle: item.sourceTitle,
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
catch (e) {
|
|
428
|
-
cursor++;
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
// Deduplicate todos by ID (or title if no ID), keeping the latest version
|
|
434
|
-
// Later entries (from more recent messages) overwrite earlier ones
|
|
435
|
-
const todoMap = new Map();
|
|
436
|
-
for (const todo of todos) {
|
|
437
|
-
const key = todo.id || todo.title;
|
|
438
|
-
todoMap.set(key, todo);
|
|
439
|
-
}
|
|
440
|
-
const result = Array.from(todoMap.values());
|
|
441
|
-
return result;
|
|
442
|
-
};
|
|
443
|
-
// TodoListPanel component
|
|
444
|
-
const TodoListPanel = ({ messages, agentMetadata, }) => {
|
|
445
|
-
const [isExpanded, setIsExpanded] = useState(true);
|
|
446
|
-
// First try to get todos from agent metadata (real-time updates)
|
|
447
|
-
// Server sends additionalData.todoList directly via contextChanged status
|
|
448
|
-
const metadataTodos = (() => {
|
|
449
|
-
try {
|
|
450
|
-
const todoList = agentMetadata?.additionalData?.todoList;
|
|
451
|
-
if (todoList?.items && Array.isArray(todoList.items)) {
|
|
452
|
-
const rawItems = todoList.items
|
|
453
|
-
.map((item, idx) => ({
|
|
454
|
-
id: item.id || `metadata-${idx}`,
|
|
455
|
-
title: item.title || item.text || item.label || String(item.task || ""),
|
|
456
|
-
status: item.status || "pending",
|
|
457
|
-
instructions: item.instructions || item.note || item.description,
|
|
458
|
-
agentProfileId: item.agentProfileId,
|
|
459
|
-
messageId: undefined,
|
|
460
|
-
sourceTitle: todoList.title,
|
|
461
|
-
}))
|
|
462
|
-
.filter((item) => item.title);
|
|
463
|
-
// Apply deduplication to metadata todos as well
|
|
464
|
-
const todoMap = new Map();
|
|
465
|
-
for (const todo of rawItems) {
|
|
466
|
-
const key = todo.id || todo.title;
|
|
467
|
-
todoMap.set(key, todo);
|
|
468
|
-
}
|
|
469
|
-
return Array.from(todoMap.values());
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
catch (e) {
|
|
473
|
-
console.error("📋 Error extracting todos from metadata:", e);
|
|
474
|
-
}
|
|
475
|
-
return null;
|
|
476
|
-
})();
|
|
477
|
-
// If we have metadata todos, use them; otherwise extract from messages
|
|
478
|
-
const usingMetadata = metadataTodos && metadataTodos.length > 0;
|
|
479
|
-
const todos = usingMetadata
|
|
480
|
-
? metadataTodos
|
|
481
|
-
: extractTodosFromMessages(messages);
|
|
482
|
-
// Check if there's an active streaming message with incomplete todo content
|
|
483
|
-
const isUpdating = messages.some((msg) => {
|
|
484
|
-
if (msg.role !== "assistant" || msg.isCompleted)
|
|
485
|
-
return false;
|
|
486
|
-
const content = msg.content || "";
|
|
487
|
-
// Check for incomplete fenced todo blocks
|
|
488
|
-
const fencedStart = content.indexOf("```todo_list");
|
|
489
|
-
if (fencedStart !== -1) {
|
|
490
|
-
const afterStart = fencedStart + "```todo_list".length;
|
|
491
|
-
const closePos = content.indexOf("```", afterStart);
|
|
492
|
-
if (closePos === -1) {
|
|
493
|
-
// Incomplete fenced block
|
|
494
|
-
return true;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// Check for incomplete plain todo blocks
|
|
498
|
-
const plainStart = content.indexOf("todo_list");
|
|
499
|
-
if (plainStart !== -1 && plainStart !== fencedStart) {
|
|
500
|
-
const before = plainStart > 0 ? content[plainStart - 1] : "\n";
|
|
501
|
-
if (before === "\n" || before === "\r" || plainStart === 0) {
|
|
502
|
-
const braceStart = content.indexOf("{", plainStart);
|
|
503
|
-
if (braceStart !== -1) {
|
|
504
|
-
let depth = 0;
|
|
505
|
-
let braceEnd = -1;
|
|
506
|
-
for (let i = braceStart; i < content.length; i++) {
|
|
507
|
-
if (content[i] === "{")
|
|
508
|
-
depth++;
|
|
509
|
-
if (content[i] === "}") {
|
|
510
|
-
depth--;
|
|
511
|
-
if (depth === 0) {
|
|
512
|
-
braceEnd = i;
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (braceEnd === -1) {
|
|
518
|
-
// Incomplete plain block
|
|
519
|
-
return true;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return false;
|
|
525
|
-
});
|
|
526
|
-
if (todos.length === 0 && !isUpdating)
|
|
527
|
-
return null;
|
|
528
|
-
const completedCount = todos.filter((t) => t.status === "completed").length;
|
|
529
|
-
const totalCount = todos.length;
|
|
530
|
-
const getStatusIcon = (status) => {
|
|
531
|
-
switch (status) {
|
|
532
|
-
case "completed":
|
|
533
|
-
return (_jsx("div", { className: "flex h-4 w-4 items-center justify-center rounded border-2 border-green-500 bg-green-500", children: _jsx("svg", { className: "h-3 w-3 text-white", fill: "none", strokeWidth: 2, stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }) }));
|
|
534
|
-
case "in_progress":
|
|
535
|
-
return (_jsx("div", { className: "flex h-4 w-4 items-center justify-center rounded border-2 border-blue-500 bg-blue-500", children: _jsx(Loader2, { className: "h-3 w-3 animate-spin text-white", strokeWidth: 2 }) }));
|
|
536
|
-
case "cancelled":
|
|
537
|
-
return (_jsx("div", { className: "flex h-4 w-4 items-center justify-center rounded border-2 border-gray-400 bg-gray-400", children: _jsx("svg", { className: "h-3 w-3 text-white", fill: "none", strokeWidth: 2, stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }));
|
|
538
|
-
default: // pending
|
|
539
|
-
return _jsx("div", { className: "h-4 w-4 rounded border-2 border-gray-300" });
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
const getTextClass = (status) => {
|
|
543
|
-
switch (status) {
|
|
544
|
-
case "completed":
|
|
545
|
-
return "text-gray-500 line-through";
|
|
546
|
-
case "cancelled":
|
|
547
|
-
return "text-gray-400 line-through";
|
|
548
|
-
case "in_progress":
|
|
549
|
-
return "text-blue-700 font-medium";
|
|
550
|
-
default:
|
|
551
|
-
return "text-gray-900";
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
return (_jsxs("div", { className: "border-t border-gray-200 bg-gray-50", children: [_jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: "flex w-full cursor-pointer items-center justify-between px-4 py-2 text-left transition-colors hover:bg-gray-100", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ListTodo, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 }), _jsx("span", { className: "text-[11px] font-medium text-gray-700", children: "Todo List" }), isUpdating ? (_jsxs("span", { className: "text-theme-secondary flex items-center gap-1 text-[11px]", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin", strokeWidth: 1 }), "Updating..."] })) : (_jsxs("span", { className: "text-[11px] text-gray-500", children: [completedCount, "/", totalCount, " completed"] }))] }), isExpanded ? (_jsx(ChevronUp, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 })) : (_jsx(ChevronDown, { className: "h-4 w-4 text-gray-500", strokeWidth: 1 }))] }), isExpanded && (_jsxs("div", { className: "max-h-64 overflow-y-auto px-4 pb-3", children: [todos.length > 0 && (_jsx("div", { className: "space-y-1.5", children: todos.map((todo, idx) => (_jsxs("div", { className: "flex items-start gap-2 rounded bg-white p-2 text-[11px]", children: [_jsx("div", { className: "shrink-0 pt-0.5", children: getStatusIcon(todo.status) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: getTextClass(todo.status), children: todo.title }), todo.instructions && (_jsx("div", { className: "mt-0.5 text-[11px] text-gray-500", children: todo.instructions }))] })] }, todo.id || `${todo.messageId}-${idx}`))) })), isUpdating && (_jsxs("div", { className: `flex items-center gap-2 rounded px-3 py-2 text-[11px] ${todos.length > 0
|
|
555
|
-
? "bg-theme-secondary-light text-theme-secondary mt-2"
|
|
556
|
-
: "justify-center bg-white text-gray-500"}`, children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin", strokeWidth: 1 }), _jsx("span", { children: todos.length > 0
|
|
557
|
-
? "Updating todo list..."
|
|
558
|
-
: "Loading todo list..." })] }))] }))] }));
|
|
559
|
-
};
|
|
560
|
-
const groupConsecutiveMessages = (agentMessages) => {
|
|
561
|
-
// Work directly with the messages array - streaming messages are identified by their properties
|
|
562
|
-
const allMessages = agentMessages;
|
|
563
|
-
const groups = [];
|
|
564
|
-
let currentAssistantGroup = [];
|
|
565
|
-
for (const message of allMessages) {
|
|
566
|
-
if (message.role === "user") {
|
|
567
|
-
// Finish any current assistant group
|
|
568
|
-
if (currentAssistantGroup.length > 0) {
|
|
569
|
-
groups.push({
|
|
570
|
-
type: "assistant-group",
|
|
571
|
-
messages: currentAssistantGroup,
|
|
572
|
-
});
|
|
573
|
-
currentAssistantGroup = [];
|
|
574
|
-
}
|
|
575
|
-
// Add user message
|
|
576
|
-
groups.push({ type: "user", messages: [message] });
|
|
577
|
-
}
|
|
578
|
-
else if (message.messageType === "heartbeat" ||
|
|
579
|
-
message.role === "system") {
|
|
580
|
-
if (currentAssistantGroup.length > 0) {
|
|
581
|
-
groups.push({
|
|
582
|
-
type: "assistant-group",
|
|
583
|
-
messages: currentAssistantGroup,
|
|
584
|
-
});
|
|
585
|
-
currentAssistantGroup = [];
|
|
586
|
-
}
|
|
587
|
-
groups.push({ type: "heartbeat", messages: [message] });
|
|
588
|
-
}
|
|
589
|
-
else if (message.role === "assistant") {
|
|
590
|
-
// Add to current assistant group
|
|
591
|
-
currentAssistantGroup.push(message);
|
|
592
|
-
}
|
|
593
|
-
// Skip tool messages as they're handled within assistant messages
|
|
594
|
-
}
|
|
595
|
-
// Add any remaining assistant group
|
|
596
|
-
if (currentAssistantGroup.length > 0) {
|
|
597
|
-
groups.push({ type: "assistant-group", messages: currentAssistantGroup });
|
|
598
|
-
}
|
|
599
|
-
return groups;
|
|
600
|
-
};
|
|
601
|
-
// Merge messages from DB and local state with ID-based deduplication
|
|
602
|
-
const mergeMessagesById = (dbMessages, localMessages) => {
|
|
603
|
-
const messageMap = new Map();
|
|
604
|
-
// Normalize ID key (lowercase) to avoid duplicates caused by casing differences
|
|
605
|
-
const keyOf = (id) => (id ? id.toLowerCase() : "");
|
|
606
|
-
// First, add all DB messages (source of truth for completed messages)
|
|
607
|
-
dbMessages.forEach((msg) => {
|
|
608
|
-
if (msg.id)
|
|
609
|
-
messageMap.set(keyOf(msg.id), msg);
|
|
610
|
-
});
|
|
611
|
-
// Then merge local messages (preserve streaming state, prefer longer content)
|
|
612
|
-
localMessages.forEach((localMsg) => {
|
|
613
|
-
if (!localMsg.id)
|
|
614
|
-
return;
|
|
615
|
-
const key = keyOf(localMsg.id);
|
|
616
|
-
const existingMsg = messageMap.get(key);
|
|
617
|
-
if (!existingMsg) {
|
|
618
|
-
// New message only in local state (e.g., streaming)
|
|
619
|
-
messageMap.set(key, localMsg);
|
|
620
|
-
}
|
|
621
|
-
else if (!localMsg.isCompleted && localMsg.messageType === "streaming") {
|
|
622
|
-
// Keep streaming version if more recent/longer (considering both content and reasoning)
|
|
623
|
-
const localLen = (localMsg.content?.length || 0) + (localMsg.reasoning?.length || 0);
|
|
624
|
-
const existingLen = (existingMsg.content?.length || 0) +
|
|
625
|
-
(existingMsg.reasoning?.length || 0);
|
|
626
|
-
if (localLen > existingLen) {
|
|
627
|
-
messageMap.set(key, localMsg);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
// Otherwise, keep the DB version (completed messages from DB are authoritative)
|
|
631
|
-
});
|
|
632
|
-
// Sort by messageIndex or createdDate to maintain order
|
|
633
|
-
// Ignore messageIndex if it's -1 (unassigned/streaming)
|
|
634
|
-
return Array.from(messageMap.values()).sort((a, b) => {
|
|
635
|
-
const aIndex = a.messageIndex === -1 ? Infinity : a.messageIndex;
|
|
636
|
-
const bIndex = b.messageIndex === -1 ? Infinity : b.messageIndex;
|
|
637
|
-
if (aIndex !== bIndex) {
|
|
638
|
-
return aIndex - bIndex;
|
|
639
|
-
}
|
|
640
|
-
return (new Date(a.createdDate || 0).getTime() -
|
|
641
|
-
new Date(b.createdDate || 0).getTime());
|
|
642
|
-
});
|
|
643
|
-
};
|
|
644
|
-
// Calculate total token usage and cost data from agent messages
|
|
645
|
-
const calculateTotalTokens = (messages) => {
|
|
646
|
-
const totals = messages.reduce((acc, message) => {
|
|
647
|
-
return {
|
|
648
|
-
input: acc.input + (message.inputTokens || 0),
|
|
649
|
-
output: acc.output + (message.outputTokens || 0),
|
|
650
|
-
cached: acc.cached + (message.cachedInputTokens || 0),
|
|
651
|
-
cacheWrite: acc.cacheWrite,
|
|
652
|
-
inputCost: acc.inputCost + (message.inputTokenCost || 0),
|
|
653
|
-
outputCost: acc.outputCost + (message.outputTokenCost || 0),
|
|
654
|
-
cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
|
|
655
|
-
cacheWriteCost: acc.cacheWriteCost,
|
|
656
|
-
imageCost: acc.imageCost,
|
|
657
|
-
totalCost: acc.totalCost + (message.totalCost || 0),
|
|
658
|
-
};
|
|
659
|
-
}, {
|
|
660
|
-
input: 0,
|
|
661
|
-
output: 0,
|
|
662
|
-
cached: 0,
|
|
663
|
-
cacheWrite: 0,
|
|
664
|
-
inputCost: 0,
|
|
665
|
-
outputCost: 0,
|
|
666
|
-
cachedCost: 0,
|
|
667
|
-
cacheWriteCost: 0,
|
|
668
|
-
imageCost: 0,
|
|
669
|
-
totalCost: 0,
|
|
670
|
-
});
|
|
671
|
-
return totals;
|
|
672
|
-
};
|
|
673
|
-
// Get edit operations for a message group by matching toolCallIds
|
|
674
|
-
const getOperationsForMessageGroup = (messages, agentOperations) => {
|
|
675
|
-
const toolCallIds = new Set(messages.flatMap((m) => m.tool_calls?.map((tc) => tc.id) ?? []));
|
|
676
|
-
const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
|
|
677
|
-
return matched;
|
|
678
|
-
};
|
|
679
|
-
const stringifyToolField = (value) => {
|
|
680
|
-
if (value === undefined || value === null)
|
|
681
|
-
return undefined;
|
|
682
|
-
if (typeof value === "string") {
|
|
683
|
-
return value.trim().length > 0 ? value : undefined;
|
|
684
|
-
}
|
|
685
|
-
try {
|
|
686
|
-
return JSON.stringify(value);
|
|
687
|
-
}
|
|
688
|
-
catch {
|
|
689
|
-
return String(value);
|
|
690
|
-
}
|
|
691
|
-
};
|
|
692
|
-
const parseToolResultValue = (value) => {
|
|
693
|
-
if (value === undefined || value === null) {
|
|
694
|
-
return undefined;
|
|
695
|
-
}
|
|
696
|
-
if (typeof value === "object") {
|
|
697
|
-
return value;
|
|
698
|
-
}
|
|
699
|
-
if (typeof value !== "string") {
|
|
700
|
-
return String(value);
|
|
701
|
-
}
|
|
702
|
-
const trimmed = value.trim();
|
|
703
|
-
if (!trimmed) {
|
|
704
|
-
return undefined;
|
|
705
|
-
}
|
|
706
|
-
try {
|
|
707
|
-
let parsed = JSON.parse(trimmed);
|
|
708
|
-
if (typeof parsed === "string" &&
|
|
709
|
-
(parsed.startsWith("{") || parsed.startsWith("["))) {
|
|
710
|
-
parsed = JSON.parse(parsed);
|
|
711
|
-
}
|
|
712
|
-
if (parsed && typeof parsed === "object") {
|
|
713
|
-
return parsed;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
catch {
|
|
717
|
-
// Fall back to the original string when the payload is plain text.
|
|
718
|
-
}
|
|
719
|
-
return value;
|
|
720
|
-
};
|
|
721
|
-
const getFirstToolCallEnvelope = (data) => {
|
|
722
|
-
if (!data || typeof data !== "object")
|
|
723
|
-
return undefined;
|
|
724
|
-
const direct = data.toolCall || data.tool_call;
|
|
725
|
-
if (direct)
|
|
726
|
-
return direct;
|
|
727
|
-
const arrayCandidates = [data.tool_calls, data.toolCalls];
|
|
728
|
-
for (const candidate of arrayCandidates) {
|
|
729
|
-
if (Array.isArray(candidate) && candidate.length > 0) {
|
|
730
|
-
return candidate[0];
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return undefined;
|
|
734
|
-
};
|
|
735
|
-
const extractToolCallFields = (data) => {
|
|
736
|
-
const envelope = getFirstToolCallEnvelope(data);
|
|
737
|
-
const functionPayload = data?.function || envelope?.function;
|
|
738
|
-
const functionName = data?.functionName ||
|
|
739
|
-
data?.name ||
|
|
740
|
-
functionPayload?.name ||
|
|
741
|
-
envelope?.functionName ||
|
|
742
|
-
envelope?.name ||
|
|
743
|
-
"unknown";
|
|
744
|
-
const toolCallId = data?.toolCallId || data?.id || envelope?.id;
|
|
745
|
-
const functionArguments = stringifyToolField(data?.functionArguments) ||
|
|
746
|
-
stringifyToolField(data?.arguments) ||
|
|
747
|
-
stringifyToolField(functionPayload?.arguments) ||
|
|
748
|
-
stringifyToolField(envelope?.functionArguments) ||
|
|
749
|
-
stringifyToolField(envelope?.arguments) ||
|
|
750
|
-
"{}";
|
|
751
|
-
return {
|
|
752
|
-
toolCallId,
|
|
753
|
-
functionName,
|
|
754
|
-
functionArguments,
|
|
755
|
-
};
|
|
756
|
-
};
|
|
757
|
-
// Convert agent messages to AI terminal format for a response group
|
|
758
|
-
const convertAgentMessagesToAiFormat = (agentMessages) => {
|
|
759
|
-
return agentMessages.map((agentMessage) => {
|
|
760
|
-
const message = {
|
|
761
|
-
id: agentMessage.id,
|
|
762
|
-
content: agentMessage.content,
|
|
763
|
-
reasoning: agentMessage.reasoning,
|
|
764
|
-
formattedContent: agentMessage.content
|
|
765
|
-
?.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>")
|
|
766
|
-
?.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>")
|
|
767
|
-
?.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>")
|
|
768
|
-
?.replace(/^###\s+(.+)$/gm, "<h3>$1</h3>")
|
|
769
|
-
?.replace(/^##\s+(.+)$/gm, "<h2>$1</h2>")
|
|
770
|
-
?.replace(/^#\s+(.+)$/gm, "<h1>$1</h1>")
|
|
771
|
-
?.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
772
|
-
?.replace(/\n/g, "<br/>"),
|
|
773
|
-
name: agentMessage.name,
|
|
774
|
-
role: agentMessage.role,
|
|
775
|
-
createdDate: agentMessage.createdDate,
|
|
776
|
-
tool_calls: agentMessage.toolCalls
|
|
777
|
-
? agentMessage.toolCalls.map((toolCall) => {
|
|
778
|
-
const isPruned = !!toolCall.isPruned ||
|
|
779
|
-
/^PRUNED$/i.test(toolCall.functionError || "");
|
|
780
|
-
const displayResult = parseToolResultValue(toolCall.functionResultRichContent) ?? toolCall.functionResult;
|
|
781
|
-
return {
|
|
782
|
-
id: toolCall.toolCallId,
|
|
783
|
-
displayName: toolCall.functionName,
|
|
784
|
-
function: {
|
|
785
|
-
name: toolCall.functionName,
|
|
786
|
-
arguments: toolCall.functionArguments,
|
|
787
|
-
result: displayResult,
|
|
788
|
-
error: toolCall.functionError,
|
|
789
|
-
},
|
|
790
|
-
// Pass through approval info if present on the tool call
|
|
791
|
-
requiresApproval: toolCall.requiresApproval,
|
|
792
|
-
// Pass through prune metadata so the terminal can render a neutral state
|
|
793
|
-
isPruned,
|
|
794
|
-
prunedAt: toolCall.prunedAt,
|
|
795
|
-
// Pass through isCompleted so ToolCallDisplay knows when to hide spinner
|
|
796
|
-
isCompleted: toolCall.isCompleted,
|
|
797
|
-
// Tool call is streaming if message is not completed and tool call has no result yet
|
|
798
|
-
isStreaming: !agentMessage.isCompleted &&
|
|
799
|
-
!toolCall.isCompleted &&
|
|
800
|
-
!displayResult &&
|
|
801
|
-
!toolCall.functionError &&
|
|
802
|
-
!isPruned,
|
|
803
|
-
// Pass through message IDs for approval/rejection events
|
|
804
|
-
messageId: toolCall.messageId,
|
|
805
|
-
dbMessageId: toolCall.dbMessageId,
|
|
806
|
-
responseTimeMs: toolCall.responseTimeMs,
|
|
807
|
-
createdDate: toolCall.createdDate,
|
|
808
|
-
};
|
|
809
|
-
})
|
|
810
|
-
: [],
|
|
811
|
-
};
|
|
812
|
-
if (agentMessage.toolCallId) {
|
|
813
|
-
message.tool_call_id = agentMessage.toolCallId;
|
|
814
|
-
}
|
|
815
|
-
return message;
|
|
816
|
-
});
|
|
817
|
-
};
|
|
818
47
|
// interface AgentTerminalProps {
|
|
819
48
|
// agentStub: Agent;
|
|
820
49
|
// }
|
|
821
|
-
export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false, displayMode = "full", showSummaryInput = false, hideContext = false, hideBottomControls = false, hideGreeting = false, defaultCollapseJson = false, simpleMode = false, className, initialPrompt, onAgentUpdate, onMessage, onInteractionSubmitted, onQuestionnaireOpenChange, questionnaireFooterActions, hideSummaryMessages = false, summaryPlaceholderActions, summaryPlaceholderMessage, hideSummaryWaitingPlaceholder = false, }) {
|
|
50
|
+
export function AgentTerminal({ agentStub, initialMetadata, profiles, onReloadProfiles, isActive = true, compact = false, displayMode = "full", showSummaryInput = false, hideContext = false, hideBottomControls = false, hideGreeting = false, defaultCollapseJson = false, simpleMode = false, className, initialPrompt, onAgentUpdate, onMessage, onInteractionSubmitted, onQuestionnaireOpenChange, questionnaireFooterActions, hideSummaryMessages = false, summaryPlaceholderActions, summaryPlaceholderMessage, hideSummaryWaitingPlaceholder = false, }) {
|
|
822
51
|
const editContext = useEditContext();
|
|
823
52
|
const fieldsContext = useFieldsEditContext();
|
|
824
53
|
const [agent, setAgent] = useState(() => buildPlaceholderAgentDetails(agentStub));
|
|
825
54
|
const [messages, setMessages] = useState([]);
|
|
826
55
|
const [agentOperations, setAgentOperations] = useState([]);
|
|
827
|
-
const [prompt, setPrompt] = useState("");
|
|
56
|
+
const [prompt, setPrompt] = useState(() => localStorageService.getItem(`editor.agent.draftPrompt.${agentStub.id}`) || "");
|
|
828
57
|
const [inputPlaceholder, setInputPlaceholder] = useState("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
829
|
-
|
|
58
|
+
// Existing (persisted) agents start in loading state so the loading
|
|
59
|
+
// screen renders on first paint — avoids an empty-terminal flash between
|
|
60
|
+
// mount and the loadAgent() effect.
|
|
61
|
+
const [isLoading, setIsLoading] = useState(() => agentStub.status !== "new");
|
|
830
62
|
const [isConnecting, setIsConnecting] = useState(false);
|
|
831
63
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
832
64
|
const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
|
|
@@ -1154,7 +386,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1154
386
|
};
|
|
1155
387
|
const [mode, setMode] = useState("supervised");
|
|
1156
388
|
const [queuedPrompts, setQueuedPrompts] = useState([]);
|
|
1157
|
-
const [expandedQueuedTriggerIds, setExpandedQueuedTriggerIds] = useState({});
|
|
1158
389
|
const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
|
|
1159
390
|
const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
|
|
1160
391
|
const [showCostAndAgent, setShowCostAndAgent] = useState(false);
|
|
@@ -1364,6 +595,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1364
595
|
active = false;
|
|
1365
596
|
};
|
|
1366
597
|
}
|
|
598
|
+
// Always refresh profiles (and therefore available models) when the
|
|
599
|
+
// Agent settings popover opens, so changes made in Sitecore are picked
|
|
600
|
+
// up even if the websocket invalidation was missed.
|
|
601
|
+
invalidateAiProfilesCache();
|
|
602
|
+
void onReloadProfiles?.();
|
|
1367
603
|
if (!agent?.id || isLocalOnlyDraftAgent) {
|
|
1368
604
|
setTriggerSubscriptions([]);
|
|
1369
605
|
setTriggerSubscriptionsLoading(false);
|
|
@@ -1477,24 +713,71 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1477
713
|
agent?.userId,
|
|
1478
714
|
agent?.profileId,
|
|
1479
715
|
mode,
|
|
716
|
+
onReloadProfiles,
|
|
1480
717
|
]);
|
|
1481
718
|
const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
|
|
719
|
+
const profileOperationAllowances = useMemo(() => {
|
|
720
|
+
const profileSource = activeProfile?.id
|
|
721
|
+
? `preconfigured:profile:${activeProfile.id}`
|
|
722
|
+
: "preconfigured:profile";
|
|
723
|
+
return {
|
|
724
|
+
sitecore: (activeProfile?.sitecoreAllowances ?? []).flatMap((allowance) => {
|
|
725
|
+
const itemIdentifier = String(allowance?.itemIdentifier || "").trim();
|
|
726
|
+
if (!itemIdentifier)
|
|
727
|
+
return [];
|
|
728
|
+
return normalizeProfileAllowanceOperations(allowance.operationTypes).map((operationType) => ({
|
|
729
|
+
itemId: itemIdentifier,
|
|
730
|
+
itemPath: itemIdentifier,
|
|
731
|
+
operationType,
|
|
732
|
+
scopeType: "itemSubtree",
|
|
733
|
+
source: profileSource,
|
|
734
|
+
grantedBy: null,
|
|
735
|
+
grantedAt: null,
|
|
736
|
+
}));
|
|
737
|
+
}),
|
|
738
|
+
filesystem: (activeProfile?.filesystemAllowances ?? []).flatMap((allowance) => {
|
|
739
|
+
const normalizedPath = String(allowance?.path || "").trim();
|
|
740
|
+
if (!normalizedPath)
|
|
741
|
+
return [];
|
|
742
|
+
return normalizeProfileAllowanceOperations(allowance.operationTypes).map((operationType) => ({
|
|
743
|
+
normalizedPath,
|
|
744
|
+
operationType,
|
|
745
|
+
source: profileSource,
|
|
746
|
+
grantedBy: null,
|
|
747
|
+
grantedAt: null,
|
|
748
|
+
}));
|
|
749
|
+
}),
|
|
750
|
+
};
|
|
751
|
+
}, [
|
|
752
|
+
activeProfile?.filesystemAllowances,
|
|
753
|
+
activeProfile?.id,
|
|
754
|
+
activeProfile?.sitecoreAllowances,
|
|
755
|
+
]);
|
|
756
|
+
const displayedOperationAllowances = isLocalOnlyDraftAgent
|
|
757
|
+
? profileOperationAllowances
|
|
758
|
+
: operationAllowances;
|
|
759
|
+
const displayedOperationAllowancesLoading = isLocalOnlyDraftAgent
|
|
760
|
+
? false
|
|
761
|
+
: operationAllowancesLoading;
|
|
762
|
+
const displayedOperationAllowancesError = isLocalOnlyDraftAgent
|
|
763
|
+
? null
|
|
764
|
+
: operationAllowancesError;
|
|
1482
765
|
const allowanceGroups = useMemo(() => [
|
|
1483
766
|
{
|
|
1484
767
|
key: "sitecore",
|
|
1485
768
|
label: "Sitecore",
|
|
1486
|
-
rows:
|
|
769
|
+
rows: displayedOperationAllowances.sitecore,
|
|
1487
770
|
},
|
|
1488
771
|
{
|
|
1489
772
|
key: "filesystem",
|
|
1490
773
|
label: "Filesystem",
|
|
1491
|
-
rows:
|
|
774
|
+
rows: displayedOperationAllowances.filesystem,
|
|
1492
775
|
},
|
|
1493
|
-
], [
|
|
1494
|
-
const hasAnyAllowances = useMemo(() =>
|
|
1495
|
-
|
|
1496
|
-
const allowancesTotalCount = useMemo(() =>
|
|
1497
|
-
|
|
776
|
+
], [displayedOperationAllowances]);
|
|
777
|
+
const hasAnyAllowances = useMemo(() => displayedOperationAllowances.sitecore.length > 0 ||
|
|
778
|
+
displayedOperationAllowances.filesystem.length > 0, [displayedOperationAllowances]);
|
|
779
|
+
const allowancesTotalCount = useMemo(() => displayedOperationAllowances.sitecore.length +
|
|
780
|
+
displayedOperationAllowances.filesystem.length, [displayedOperationAllowances]);
|
|
1498
781
|
const listedProfileSkillIdSet = useMemo(() => {
|
|
1499
782
|
const ids = [
|
|
1500
783
|
...(activeProfile?.availableSkills ?? []),
|
|
@@ -1813,6 +1096,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1813
1096
|
useEffect(() => {
|
|
1814
1097
|
localStorageService.setItem("editor.agent.promptHistory", promptHistory);
|
|
1815
1098
|
}, [promptHistory]);
|
|
1099
|
+
// Rehydrate draft when switching to a different agent so each agent keeps its own draft.
|
|
1100
|
+
useEffect(() => {
|
|
1101
|
+
setPrompt(localStorageService.getItem(`editor.agent.draftPrompt.${agentStub.id}`) || "");
|
|
1102
|
+
}, [agentStub.id]);
|
|
1103
|
+
// Persist the in-progress prompt per agent so it survives workspace switches/unmounts.
|
|
1104
|
+
useEffect(() => {
|
|
1105
|
+
const key = `editor.agent.draftPrompt.${agentStub.id}`;
|
|
1106
|
+
if (prompt) {
|
|
1107
|
+
localStorageService.setItem(key, prompt);
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
localStorageService.removeItem(key);
|
|
1111
|
+
}
|
|
1112
|
+
}, [prompt, agentStub.id]);
|
|
1816
1113
|
useEffect(() => {
|
|
1817
1114
|
// Keep messagesRef synchronized with messages state
|
|
1818
1115
|
messagesRef.current = messages;
|
|
@@ -1858,6 +1155,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1858
1155
|
const documentListRef = useRef(null);
|
|
1859
1156
|
const placeholderInputRef = useRef(null);
|
|
1860
1157
|
const promptPlaceholderInputRef = useRef(null);
|
|
1158
|
+
const shouldSubmitFilledPlaceholderInputRef = useRef(false);
|
|
1861
1159
|
const messagesContainerRef = useRef(null);
|
|
1862
1160
|
const inlineDialogContainerRef = useRef(null);
|
|
1863
1161
|
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
@@ -1905,16 +1203,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
1905
1203
|
setIsVoiceSupported(false);
|
|
1906
1204
|
}
|
|
1907
1205
|
}, []);
|
|
1908
|
-
// Auto-focus terminal input on mount
|
|
1206
|
+
// Auto-focus terminal input on mount, and place cursor at the end of any
|
|
1207
|
+
// restored draft prompt (e.g. when switching workspaces with the same agent).
|
|
1909
1208
|
useEffect(() => {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1209
|
+
const textarea = textareaRef.current;
|
|
1210
|
+
if (!textarea)
|
|
1211
|
+
return;
|
|
1212
|
+
try {
|
|
1213
|
+
textarea.focus({ preventScroll: true });
|
|
1214
|
+
}
|
|
1215
|
+
catch {
|
|
1216
|
+
textarea.focus();
|
|
1917
1217
|
}
|
|
1218
|
+
const end = textarea.value.length;
|
|
1219
|
+
textarea.selectionStart = end;
|
|
1220
|
+
textarea.selectionEnd = end;
|
|
1918
1221
|
}, []);
|
|
1919
1222
|
// Start voice recognition
|
|
1920
1223
|
const startVoice = () => {
|
|
@@ -2311,12 +1614,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2311
1614
|
output: Number(cost.tokens?.output) || 0,
|
|
2312
1615
|
cached: Number(cost.tokens?.cached) || 0,
|
|
2313
1616
|
cacheWrite: Number(cost.tokens?.cacheWrite) || 0,
|
|
1617
|
+
inputAudio: Number(cost.tokens?.inputAudio) || 0,
|
|
1618
|
+
outputAudio: Number(cost.tokens?.outputAudio) || 0,
|
|
1619
|
+
outputImage: Number(cost.tokens?.outputImage) || 0,
|
|
1620
|
+
reasoning: Number(cost.tokens?.reasoning) || 0,
|
|
2314
1621
|
inputCost: Number(cost.input) || 0,
|
|
2315
1622
|
outputCost: Number(cost.output) || 0,
|
|
2316
1623
|
cachedCost: Number(cost.cached) || 0,
|
|
2317
1624
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
1625
|
+
inputAudioCost: Number(cost.inputAudio) || 0,
|
|
1626
|
+
outputAudioCost: Number(cost.outputAudio) || 0,
|
|
1627
|
+
outputImageCost: Number(cost.outputImage) || 0,
|
|
2318
1628
|
imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
|
|
2319
1629
|
0,
|
|
1630
|
+
markupCost: Number(cost.markup) || 0,
|
|
1631
|
+
reportedTotalCost: Number(cost.reportedTotal) || 0,
|
|
2320
1632
|
totalCost: Number(cost.total) || 0,
|
|
2321
1633
|
currency: "USD",
|
|
2322
1634
|
};
|
|
@@ -2650,11 +1962,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2650
1962
|
output: Number(cost.tokens?.output) || 0,
|
|
2651
1963
|
cached: Number(cost.tokens?.cached) || 0,
|
|
2652
1964
|
cacheWrite: Number(cost.tokens?.cacheWrite) || 0,
|
|
1965
|
+
inputAudio: Number(cost.tokens?.inputAudio) || 0,
|
|
1966
|
+
outputAudio: Number(cost.tokens?.outputAudio) || 0,
|
|
1967
|
+
outputImage: Number(cost.tokens?.outputImage) || 0,
|
|
1968
|
+
reasoning: Number(cost.tokens?.reasoning) || 0,
|
|
2653
1969
|
inputCost: Number(cost.input) || 0,
|
|
2654
1970
|
outputCost: Number(cost.output) || 0,
|
|
2655
1971
|
cachedCost: Number(cost.cached) || 0,
|
|
2656
1972
|
cacheWriteCost: Number(cost.cacheWrite) || 0,
|
|
1973
|
+
inputAudioCost: Number(cost.inputAudio) || 0,
|
|
1974
|
+
outputAudioCost: Number(cost.outputAudio) || 0,
|
|
1975
|
+
outputImageCost: Number(cost.outputImage) || 0,
|
|
2657
1976
|
imageCost: Number(cost.imageCost) || 0,
|
|
1977
|
+
markupCost: Number(cost.markup) || 0,
|
|
1978
|
+
reportedTotalCost: Number(cost.reportedTotal) || 0,
|
|
2658
1979
|
totalCost: Number(cost.total) || 0,
|
|
2659
1980
|
currency: "USD",
|
|
2660
1981
|
};
|
|
@@ -2699,11 +2020,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
2699
2020
|
output: Number(data.totalOutputTokens) || 0,
|
|
2700
2021
|
cached: Number(data.totalCachedTokens) || 0,
|
|
2701
2022
|
cacheWrite: Number(data.totalCacheWriteTokens) || 0,
|
|
2023
|
+
inputAudio: Number(data.totalInputAudioTokens) || 0,
|
|
2024
|
+
outputAudio: Number(data.totalOutputAudioTokens) || 0,
|
|
2025
|
+
outputImage: Number(data.totalOutputImageTokens) || 0,
|
|
2026
|
+
reasoning: Number(data.totalReasoningTokens) || 0,
|
|
2702
2027
|
inputCost: Number(data.totalInputTokenCost) || 0,
|
|
2703
2028
|
outputCost: Number(data.totalOutputTokenCost) || 0,
|
|
2704
2029
|
cachedCost: Number(data.totalCachedTokenCost) || 0,
|
|
2705
2030
|
cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
|
|
2031
|
+
inputAudioCost: Number(data.totalInputAudioCost) || 0,
|
|
2032
|
+
outputAudioCost: Number(data.totalOutputAudioCost) || 0,
|
|
2033
|
+
outputImageCost: Number(data.totalOutputImageCost) || 0,
|
|
2706
2034
|
imageCost: Number(data.totalImageCost) || 0,
|
|
2035
|
+
markupCost: Number(data.totalMarkupCost) || 0,
|
|
2036
|
+
reportedTotalCost: Number(data.totalReportedCost) || 0,
|
|
2707
2037
|
totalCost: Number(data.totalCost) || 0,
|
|
2708
2038
|
currency: data.currency || "USD",
|
|
2709
2039
|
};
|
|
@@ -3637,8 +2967,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3637
2967
|
// Route based on statusData.state
|
|
3638
2968
|
try {
|
|
3639
2969
|
// Normalize various status shapes and handle Cancelled uniformly
|
|
3640
|
-
const normalizedStatus =
|
|
3641
|
-
parseAgentStatus(statusData?.status);
|
|
2970
|
+
const normalizedStatus = parseAgentRunStatusData(statusData);
|
|
3642
2971
|
const statusReason = typeof statusData?.reason === "string"
|
|
3643
2972
|
? statusData.reason.trim() || null
|
|
3644
2973
|
: null;
|
|
@@ -3697,11 +3026,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3697
3026
|
output: Number(totals.totalOutputTokens) || 0,
|
|
3698
3027
|
cached: Number(totals.totalCachedInputTokens) || 0,
|
|
3699
3028
|
cacheWrite: Number(totals.totalCacheWriteTokens) || 0,
|
|
3029
|
+
inputAudio: Number(totals.totalInputAudioTokens) || 0,
|
|
3030
|
+
outputAudio: Number(totals.totalOutputAudioTokens) || 0,
|
|
3031
|
+
outputImage: Number(totals.totalOutputImageTokens) || 0,
|
|
3032
|
+
reasoning: Number(totals.totalReasoningTokens) || 0,
|
|
3700
3033
|
inputCost: Number(totals.totalInputTokenCost) || 0,
|
|
3701
3034
|
outputCost: Number(totals.totalOutputTokenCost) || 0,
|
|
3702
3035
|
cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
|
|
3703
3036
|
cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
|
|
3037
|
+
inputAudioCost: Number(totals.totalInputAudioCost) || 0,
|
|
3038
|
+
outputAudioCost: Number(totals.totalOutputAudioCost) || 0,
|
|
3039
|
+
outputImageCost: Number(totals.totalOutputImageCost) || 0,
|
|
3704
3040
|
imageCost: Number(totals.totalImageCost) || 0,
|
|
3041
|
+
markupCost: Number(totals.totalMarkupCost) || 0,
|
|
3042
|
+
reportedTotalCost: Number(totals.totalReportedCost) || 0,
|
|
3705
3043
|
totalCost: totalCost,
|
|
3706
3044
|
currency: totals.currency,
|
|
3707
3045
|
};
|
|
@@ -3753,7 +3091,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
3753
3091
|
}
|
|
3754
3092
|
return;
|
|
3755
3093
|
}
|
|
3756
|
-
if (statusData?.state === "ToolApprovalsRequired"
|
|
3094
|
+
if (statusData?.state === "ToolApprovalsRequired" ||
|
|
3095
|
+
statusData?.state === "toolApprovalsRequired") {
|
|
3757
3096
|
setPendingBrowserCaptureDialogType(null);
|
|
3758
3097
|
setPendingBrowserCaptureCallbackId(null);
|
|
3759
3098
|
setLastRunStatusReason(null);
|
|
@@ -4270,17 +3609,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4270
3609
|
setActiveProfile((prev) => {
|
|
4271
3610
|
if (!prev || prev.id !== candidate.id)
|
|
4272
3611
|
return candidate;
|
|
4273
|
-
|
|
4274
|
-
allowed: (p.allowedSkills ?? []).map((s) => [
|
|
4275
|
-
String(s?.id ?? ""),
|
|
4276
|
-
String(s?.name ?? ""),
|
|
4277
|
-
]),
|
|
4278
|
-
available: (p.availableSkills ?? []).map((s) => [
|
|
4279
|
-
String(s?.id ?? ""),
|
|
4280
|
-
String(s?.name ?? ""),
|
|
4281
|
-
]),
|
|
4282
|
-
});
|
|
4283
|
-
if (skillKey(prev) === skillKey(candidate))
|
|
3612
|
+
if (prev === candidate)
|
|
4284
3613
|
return prev;
|
|
4285
3614
|
return candidate;
|
|
4286
3615
|
});
|
|
@@ -4468,15 +3797,44 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4468
3797
|
}
|
|
4469
3798
|
}, []);
|
|
4470
3799
|
const handleSubmit = async (promptOverride) => {
|
|
3800
|
+
const hasPromptOverride = promptOverride !== undefined;
|
|
4471
3801
|
// Guard against double-submit and missing context
|
|
4472
3802
|
if (isSubmitting) {
|
|
4473
3803
|
console.warn("[AgentTerminal] handleSubmit blocked: already submitting");
|
|
4474
3804
|
return;
|
|
4475
3805
|
}
|
|
4476
|
-
//
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
3806
|
+
// If there are pending tool call approvals, treat this submission as a
|
|
3807
|
+
// supersede: reject all pending tool calls and continue with the new prompt.
|
|
3808
|
+
const pendingToReject = allPendingApprovals;
|
|
3809
|
+
if (pendingToReject.length > 0) {
|
|
3810
|
+
const agentIdForReject = agent?.id || agentStub.id;
|
|
3811
|
+
if (!agentIdForReject) {
|
|
3812
|
+
console.error("[AgentTerminal] handleSubmit blocked: cannot supersede pending approvals — no agentId");
|
|
3813
|
+
setError("Agent not ready. Please try again.");
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
try {
|
|
3817
|
+
await Promise.all(pendingToReject.map(async (pending) => {
|
|
3818
|
+
await rejectToolCall({
|
|
3819
|
+
agentId: agentIdForReject,
|
|
3820
|
+
messageId: pending.dbMessageId || pending.messageId,
|
|
3821
|
+
toolCallId: pending.toolCallId,
|
|
3822
|
+
note: "Superseded by new user instructions",
|
|
3823
|
+
});
|
|
3824
|
+
window.dispatchEvent(new CustomEvent("agent:toolApprovalResolved", {
|
|
3825
|
+
detail: {
|
|
3826
|
+
messageId: pending.dbMessageId || pending.messageId,
|
|
3827
|
+
toolCallId: pending.toolCallId,
|
|
3828
|
+
approved: false,
|
|
3829
|
+
},
|
|
3830
|
+
}));
|
|
3831
|
+
}));
|
|
3832
|
+
}
|
|
3833
|
+
catch (rejectErr) {
|
|
3834
|
+
console.error("[AgentTerminal] Failed to supersede pending tool calls:", rejectErr);
|
|
3835
|
+
setError(`Failed to cancel pending tool call: ${rejectErr?.message || String(rejectErr)}`);
|
|
3836
|
+
return;
|
|
3837
|
+
}
|
|
4480
3838
|
}
|
|
4481
3839
|
if (!editContext) {
|
|
4482
3840
|
console.error("[AgentTerminal] handleSubmit blocked: editContext is undefined");
|
|
@@ -4484,12 +3842,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
4484
3842
|
return;
|
|
4485
3843
|
}
|
|
4486
3844
|
// If placeholder input is active, delegate to its submit method
|
|
4487
|
-
if (activePlaceholderInput && allPlaceholdersFilled) {
|
|
4488
|
-
placeholderInputRef.current
|
|
3845
|
+
if (!hasPromptOverride && activePlaceholderInput && allPlaceholdersFilled) {
|
|
3846
|
+
const placeholderInput = placeholderInputRef.current;
|
|
3847
|
+
if (!placeholderInput)
|
|
3848
|
+
return;
|
|
3849
|
+
shouldSubmitFilledPlaceholderInputRef.current = true;
|
|
3850
|
+
placeholderInput.submit();
|
|
4489
3851
|
return;
|
|
4490
3852
|
}
|
|
4491
3853
|
// If prompt contains placeholders, delegate to the prompt placeholder input's submit method
|
|
4492
|
-
if (
|
|
3854
|
+
if (!hasPromptOverride &&
|
|
3855
|
+
prompt &&
|
|
4493
3856
|
/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) &&
|
|
4494
3857
|
allPlaceholdersFilled) {
|
|
4495
3858
|
promptPlaceholderInputRef.current?.submit();
|
|
@@ -5732,56 +5095,52 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5732
5095
|
detail: { agentId: parentAgentId },
|
|
5733
5096
|
}));
|
|
5734
5097
|
}, [parentAgentId]);
|
|
5735
|
-
|
|
5098
|
+
// Gate the chat UI on the initial history load so streaming deltas arriving
|
|
5099
|
+
// via WebSocket don't render on a blank canvas before history is fetched.
|
|
5100
|
+
// The placeholder `agent` is always set, so the previous `!agent` check
|
|
5101
|
+
// never fired — drop it. Keep the dialog escape so blocking inline prompts
|
|
5102
|
+
// still surface during refreshes.
|
|
5103
|
+
const shouldShowLoadingContent = isLoading && !activeInlineDialog;
|
|
5104
|
+
const loadingContent = shouldShowLoadingContent ? (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-gray-500", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1 }), "Loading agent..."] }) })) : null;
|
|
5736
5105
|
const renderContextInfoBar = () => (_jsx(ContextInfoBar, { agent: agent, agentMetadata: agentMetadata, setAgentMetadata: setAgentMetadata, setAgent: setAgent, resolvedPageName: resolvedPageName, resolvedComponentName: resolvedComponentName, resolvedFieldName: resolvedFieldName, isLiveEditorContextMode: isLiveEditorContextMode, omitEditorContext: omitsEditorContext, onRefreshContext: handleRefreshContext }));
|
|
5106
|
+
const handleExtendCostLimit = useCallback(async () => {
|
|
5107
|
+
if (!agent?.id)
|
|
5108
|
+
return;
|
|
5109
|
+
try {
|
|
5110
|
+
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
5111
|
+
if (result.success && result.costLimit !== undefined) {
|
|
5112
|
+
setAgent((prev) => prev
|
|
5113
|
+
? {
|
|
5114
|
+
...prev,
|
|
5115
|
+
costLimit: result.costLimit,
|
|
5116
|
+
status: prev.status === "costLimitReached"
|
|
5117
|
+
? "running"
|
|
5118
|
+
: prev.status,
|
|
5119
|
+
}
|
|
5120
|
+
: prev);
|
|
5121
|
+
}
|
|
5122
|
+
setCostLimitExceeded(null);
|
|
5123
|
+
setIsWaitingForResponse(true);
|
|
5124
|
+
isWaitingRef.current = true;
|
|
5125
|
+
setIsAgentThinking(true);
|
|
5126
|
+
setShouldAutoScroll(true);
|
|
5127
|
+
}
|
|
5128
|
+
catch (e) {
|
|
5129
|
+
console.error("Failed to extend cost limit:", e);
|
|
5130
|
+
setError(e instanceof Error ? e.message : "Failed to extend cost limit");
|
|
5131
|
+
}
|
|
5132
|
+
}, [agent?.id]);
|
|
5737
5133
|
const renderCostLimitBanner = () => {
|
|
5738
5134
|
if (!costLimitExceeded)
|
|
5739
5135
|
return null;
|
|
5740
|
-
const { totalCost, costLimit
|
|
5741
|
-
return (
|
|
5742
|
-
if (!agent?.id)
|
|
5743
|
-
return;
|
|
5744
|
-
try {
|
|
5745
|
-
// Extend cost limit - backend will automatically resume the agent
|
|
5746
|
-
const result = await updateAgentCostLimit(agent.id, "extend");
|
|
5747
|
-
// Update the agent's cost limit and clear the costLimitReached
|
|
5748
|
-
// status in local state so the useEffect watcher doesn't
|
|
5749
|
-
// immediately re-show the banner before the backend status
|
|
5750
|
-
// update arrives.
|
|
5751
|
-
if (result.success && result.costLimit !== undefined) {
|
|
5752
|
-
setAgent((prev) => prev
|
|
5753
|
-
? {
|
|
5754
|
-
...prev,
|
|
5755
|
-
costLimit: result.costLimit,
|
|
5756
|
-
status: prev.status === "costLimitReached"
|
|
5757
|
-
? "running"
|
|
5758
|
-
: prev.status,
|
|
5759
|
-
}
|
|
5760
|
-
: prev);
|
|
5761
|
-
}
|
|
5762
|
-
// Clear the banner and set waiting state
|
|
5763
|
-
// Agent will resume automatically via backend's ResumeAgentAsync
|
|
5764
|
-
setCostLimitExceeded(null);
|
|
5765
|
-
setIsWaitingForResponse(true);
|
|
5766
|
-
isWaitingRef.current = true;
|
|
5767
|
-
setIsAgentThinking(true);
|
|
5768
|
-
setShouldAutoScroll(true);
|
|
5769
|
-
}
|
|
5770
|
-
catch (e) {
|
|
5771
|
-
console.error("Failed to extend cost limit:", e);
|
|
5772
|
-
setError(e instanceof Error
|
|
5773
|
-
? e.message
|
|
5774
|
-
: "Failed to extend cost limit");
|
|
5775
|
-
}
|
|
5776
|
-
}, children: "Extend limit and continue" }) })] }));
|
|
5136
|
+
const { totalCost, costLimit } = costLimitExceeded;
|
|
5137
|
+
return (_jsx(AgentCostLimitBanner, { totalCost: totalCost, costLimit: costLimit, onExtend: handleExtendCostLimit }));
|
|
5777
5138
|
};
|
|
5778
5139
|
const renderErrorBanner = () => {
|
|
5779
5140
|
const currentAgent = agent || agentStub;
|
|
5780
5141
|
const isErrorStatus = currentAgent?.status === "error";
|
|
5781
5142
|
const isWaitingForInputStatus = currentAgent?.status === "waitingForInput";
|
|
5782
5143
|
const isWaitingForApprovalStatus = currentAgent?.status === "waitingForApproval";
|
|
5783
|
-
// Show error banner for error status, or for any terminal status that still
|
|
5784
|
-
// carries a statusMessage (e.g. agent closed after an error).
|
|
5785
5144
|
const isTerminalWithError = !isErrorStatus &&
|
|
5786
5145
|
!!currentAgent?.statusMessage &&
|
|
5787
5146
|
currentAgent?.status !== "running" &&
|
|
@@ -5793,9 +5152,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5793
5152
|
: null) || error;
|
|
5794
5153
|
if (!rawErrorMessage)
|
|
5795
5154
|
return null;
|
|
5796
|
-
// Clean the error message (statusMessage from DB may contain raw JSON)
|
|
5797
5155
|
const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
|
|
5798
|
-
return
|
|
5156
|
+
return _jsx(AgentErrorBanner, { message: errorMessage });
|
|
5799
5157
|
};
|
|
5800
5158
|
const renderCapacityBanner = () => {
|
|
5801
5159
|
if (lastRunStatusReason !== MACHINE_CAPACITY_REASON) {
|
|
@@ -5804,7 +5162,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5804
5162
|
const currentAgent = agent || agentStub;
|
|
5805
5163
|
const message = currentAgent?.statusMessage ||
|
|
5806
5164
|
"Waiting for capacity. The agent will start automatically when a slot becomes available.";
|
|
5807
|
-
return
|
|
5165
|
+
return _jsx(AgentCapacityBanner, { message: message });
|
|
5808
5166
|
};
|
|
5809
5167
|
const renderBrowserClaimBanner = (variant = "inline") => {
|
|
5810
5168
|
if (!agent?.id || !editContext?.sessionId)
|
|
@@ -5895,31 +5253,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5895
5253
|
const renderInlineDialogContent = () => {
|
|
5896
5254
|
if (!activeInlineDialog)
|
|
5897
5255
|
return null;
|
|
5898
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5899
|
-
return (_jsx("div", { ref: inlineDialogContainerRef, className: cn("agent-inline-dialog min-h-0 overflow-hidden", displayMode === "full" && "h-full"), children: _jsx(QuestionnaireInline, { requestId: activeInlineDialog.request.callbackId, agentId: activeInlineDialog.request.agentId, title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, footerActions: questionnaireFooterActions, onClose: (result) => {
|
|
5900
|
-
activeInlineDialog.onComplete(result);
|
|
5901
|
-
setActiveInlineDialog(null);
|
|
5902
|
-
void onInteractionSubmitted?.();
|
|
5903
|
-
}, onCancel: () => {
|
|
5904
|
-
activeInlineDialog.onCancel();
|
|
5905
|
-
setActiveInlineDialog(null);
|
|
5906
|
-
} }) }));
|
|
5907
|
-
}
|
|
5908
5256
|
const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
|
|
5909
|
-
|
|
5910
|
-
const DialogComponent = dialogRegistration.component;
|
|
5911
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
|
|
5912
|
-
activeInlineDialog.onComplete(result);
|
|
5913
|
-
setActiveInlineDialog(null);
|
|
5914
|
-
if (activeInlineDialog.request.dialogType === "questionnaire") {
|
|
5915
|
-
void onInteractionSubmitted?.();
|
|
5916
|
-
}
|
|
5917
|
-
}, onCancel: () => {
|
|
5918
|
-
activeInlineDialog.onCancel();
|
|
5919
|
-
setActiveInlineDialog(null);
|
|
5920
|
-
} }) }));
|
|
5921
|
-
}
|
|
5922
|
-
return (_jsx("div", { className: "agent-inline-dialog", children: _jsxs("div", { className: "p-4 text-sm text-red-500", children: ["Unknown dialog type: ", activeInlineDialog.request.dialogType] }) }));
|
|
5257
|
+
return (_jsx(AgentInlineDialogContent, { activeInlineDialog: activeInlineDialog, setActiveInlineDialog: setActiveInlineDialog, containerRef: inlineDialogContainerRef, displayMode: displayMode, questionnaireFooterActions: questionnaireFooterActions, dialogRegistration: dialogRegistration, onInteractionSubmitted: onInteractionSubmitted }));
|
|
5923
5258
|
};
|
|
5924
5259
|
const latestSummaryAssistantGroup = useMemo(() => {
|
|
5925
5260
|
if (hideSummaryMessages)
|
|
@@ -5951,10 +5286,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5951
5286
|
const summaryOperations = latestSummaryAssistantGroup
|
|
5952
5287
|
? getOperationsForMessageGroup(summaryMessages, agentOperations)
|
|
5953
5288
|
: [];
|
|
5954
|
-
return (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [fixedBrowserClaimBanner,
|
|
5955
|
-
|
|
5956
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
5957
|
-
} })) : (_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, renderCapacityBanner(), 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 ||
|
|
5289
|
+
return (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [fixedBrowserClaimBanner, error &&
|
|
5290
|
+
!isAgentErrorStatusValue((agent || agentStub)?.status) && (_jsx("div", { className: "m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3 select-text", children: _jsxs("div", { className: "flex items-start", children: [_jsx(AlertCircle, { className: "mt-0.5 h-5 w-5 text-red-400", strokeWidth: 1 }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-[11px] font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-[11px] text-red-700", children: error })] })] }) })), renderCapacityBanner(), renderErrorBanner(), _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [showInitialThinkingSplash && (_jsx(InitialThinkingSplash, { svgIcon: activeProfile?.svgIcon })), inlineBrowserClaimBanner, 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 ||
|
|
5958
5291
|
activeProfile?.displayTitle ||
|
|
5959
5292
|
activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
|
|
5960
5293
|
const text = (action.prompt ||
|
|
@@ -5983,10 +5316,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5983
5316
|
activeProfile?.displayTitle ||
|
|
5984
5317
|
activeProfile?.name ||
|
|
5985
5318
|
"Agent" }), _jsx("span", { className: "text-xs text-gray-400", children: formatTime(new Date()) })] }), _jsxs("div", { className: "flex items-center gap-1 pt-2", children: [_jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400" })] })] })] })), _jsx("div", { ref: messagesEndRef })] }), showSummaryInput && !activeInlineDialog ? (_jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5319
|
+
const shouldSubmitFilledText = shouldSubmitFilledPlaceholderInputRef.current ||
|
|
5320
|
+
activePlaceholderInput.behavior !== "compose" ||
|
|
5321
|
+
hideBottomControls;
|
|
5322
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
5986
5323
|
setActivePlaceholderInput(null);
|
|
5987
5324
|
setAllPlaceholdersFilled(false);
|
|
5988
|
-
if (
|
|
5989
|
-
!hideBottomControls) {
|
|
5325
|
+
if (!shouldSubmitFilledText) {
|
|
5990
5326
|
setPrompt(filledText);
|
|
5991
5327
|
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
5992
5328
|
if (textareaRef.current) {
|
|
@@ -5998,32 +5334,30 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
5998
5334
|
}
|
|
5999
5335
|
catch { }
|
|
6000
5336
|
}
|
|
5337
|
+
return;
|
|
6001
5338
|
}
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
handleStop();
|
|
6006
|
-
}
|
|
6007
|
-
catch { }
|
|
5339
|
+
if (isExecuting) {
|
|
5340
|
+
try {
|
|
5341
|
+
handleStop();
|
|
6008
5342
|
}
|
|
6009
|
-
|
|
5343
|
+
catch { }
|
|
6010
5344
|
}
|
|
5345
|
+
void handleSubmit(filledText);
|
|
6011
5346
|
}, onCancel: () => {
|
|
5347
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6012
5348
|
setActivePlaceholderInput(null);
|
|
6013
5349
|
setAllPlaceholdersFilled(false);
|
|
6014
5350
|
} })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
6015
|
-
|
|
5351
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6016
5352
|
setAllPlaceholdersFilled(false);
|
|
6017
5353
|
if (filledText.trim()) {
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
catch { }
|
|
6023
|
-
}
|
|
6024
|
-
sendQuickMessage(filledText);
|
|
5354
|
+
void handleSubmit(filledText);
|
|
5355
|
+
}
|
|
5356
|
+
else {
|
|
5357
|
+
setPrompt("");
|
|
6025
5358
|
}
|
|
6026
5359
|
}, onCancel: () => {
|
|
5360
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6027
5361
|
setPrompt("");
|
|
6028
5362
|
setAllPlaceholdersFilled(false);
|
|
6029
5363
|
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
@@ -6065,7 +5399,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6065
5399
|
})()
|
|
6066
5400
|
: null;
|
|
6067
5401
|
const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
|
|
6068
|
-
const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [fixedBrowserClaimBanner, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [messages.length === 0 && !error && !hideGreeting && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
|
|
5402
|
+
const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [fixedBrowserClaimBanner, renderCapacityBanner(), renderErrorBanner(), _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [messages.length === 0 && !error && !hideGreeting && !isLoading && (_jsx("div", { className: "flex h-full items-center justify-center", children: !activeProfile ? (_jsx(Loader2, { className: "mx-auto h-8 w-8 animate-spin text-gray-400" })) : (_jsx(AgentGreeting, { profile: activeProfile, onPromptClick: (p) => {
|
|
6069
5403
|
setPrompt(p);
|
|
6070
5404
|
// Use setTimeout to ensure state is updated before submission
|
|
6071
5405
|
setTimeout(() => {
|
|
@@ -6078,9 +5412,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6078
5412
|
handleSubmit();
|
|
6079
5413
|
}
|
|
6080
5414
|
}, 0);
|
|
6081
|
-
} })) })), showInitialThinkingSplash && (_jsx(
|
|
6082
|
-
__html: sanitizeSvg(activeProfile.svgIcon),
|
|
6083
|
-
} })) : (_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, renderCapacityBanner(), renderErrorBanner(), _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
|
|
5415
|
+
} })) })), showInitialThinkingSplash && (_jsx(InitialThinkingSplash, { svgIcon: activeProfile?.svgIcon })), inlineBrowserClaimBanner, _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
|
|
6084
5416
|
const groups = groupConsecutiveMessages(messages);
|
|
6085
5417
|
return groups.map((group, groupIndex) => {
|
|
6086
5418
|
const isLastGroup = groupIndex === groups.length - 1;
|
|
@@ -6207,33 +5539,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6207
5539
|
hasTodoContent,
|
|
6208
5540
|
hasSpawnedAgents,
|
|
6209
5541
|
agent?.id && hasHistoryContent,
|
|
6210
|
-
].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && (_jsx(AgentEditOperationsPanel, { operations: agentOperations }))] }))),
|
|
6211
|
-
let triggerName = "";
|
|
6212
|
-
if (qp.data) {
|
|
6213
|
-
try {
|
|
6214
|
-
const parsed = JSON.parse(qp.data);
|
|
6215
|
-
triggerName = parsed?.triggerName?.trim() || "";
|
|
6216
|
-
}
|
|
6217
|
-
catch {
|
|
6218
|
-
// Ignore invalid JSON metadata and render as regular queued prompt.
|
|
6219
|
-
}
|
|
6220
|
-
}
|
|
6221
|
-
const isTriggerQueuedPrompt = !qp.sourceAgentName && triggerName.length > 0;
|
|
6222
|
-
const isTriggerExpanded = !!expandedQueuedTriggerIds[qp.id];
|
|
6223
|
-
if (isTriggerQueuedPrompt) {
|
|
6224
|
-
return (_jsxs("div", { className: "text-[11px]", "data-testid": "queued-prompt-item", children: [_jsxs("button", { type: "button", onClick: () => setExpandedQueuedTriggerIds((prev) => ({
|
|
6225
|
-
...prev,
|
|
6226
|
-
[qp.id]: !prev[qp.id],
|
|
6227
|
-
})), className: "flex w-full items-center gap-1.5 rounded px-1 py-0.5 text-left text-amber-800 transition-colors hover:bg-amber-100/60", "data-testid": "queued-trigger-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3 w-3 shrink-0 text-amber-500", strokeWidth: 2 }), _jsx("span", { className: "truncate font-medium", children: triggerName }), qp.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-amber-500", children: formatTime(new Date(qp.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0 text-amber-400", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3" })) : (_jsx(ChevronDown, { className: "h-3 w-3" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-0.5 border-l-2 border-amber-200 pl-5 text-[11px] text-amber-700/80", children: qp.prompt }))] }, qp.id));
|
|
6228
|
-
}
|
|
6229
|
-
return (_jsx("div", { className: "rounded-md border border-amber-200 bg-white p-2.5 text-[11px]", "data-testid": "queued-prompt-item", children: _jsx("div", { className: "flex items-start justify-between gap-2", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "mb-1 flex items-center gap-1.5", children: qp.sourceAgentName ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-medium text-gray-700", children: ["From ", qp.sourceAgentName] }), qp.priority > 5 && (_jsx("span", { className: "rounded bg-red-100 px-1.5 py-0.5 text-[11px] font-medium text-red-700", children: "High Priority" }))] })) : (_jsx("span", { className: "font-medium text-gray-700", children: "From User" })) }), _jsx("div", { className: "wrap-break-word whitespace-pre-wrap text-gray-600", "data-testid": "queued-prompt-text", children: qp.prompt }), _jsxs("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px] text-gray-400", children: [qp.createdDate && (_jsxs("span", { children: ["Queued ", formatTime(new Date(qp.createdDate))] })), qp.scheduledFor &&
|
|
6230
|
-
new Date(qp.scheduledFor).getTime() >
|
|
6231
|
-
new Date(qp.createdDate || 0).getTime() +
|
|
6232
|
-
1000 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-gray-300", children: "\u2022" }), _jsxs("span", { className: "font-medium text-amber-600", children: ["Scheduled for", " ", new Date(qp.scheduledFor).toDateString() ===
|
|
6233
|
-
new Date().toDateString()
|
|
6234
|
-
? formatTime(new Date(qp.scheduledFor))
|
|
6235
|
-
: formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
|
|
6236
|
-
}) })] }) }))] }));
|
|
5542
|
+
].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && (_jsx(AgentEditOperationsPanel, { operations: agentOperations }))] }))), !simpleMode && (_jsx(QueuedPromptsPanel, { queuedPrompts: queuedPrompts }))] }));
|
|
6237
5543
|
const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
|
|
6238
5544
|
const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
|
|
6239
5545
|
{
|
|
@@ -6253,12 +5559,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6253
5559
|
// Placeholder Input (from quick actions)
|
|
6254
5560
|
// Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
|
|
6255
5561
|
_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
5562
|
+
const shouldSubmitFilledText = shouldSubmitFilledPlaceholderInputRef.current ||
|
|
5563
|
+
activePlaceholderInput.behavior !== "compose" ||
|
|
5564
|
+
hideBottomControls;
|
|
5565
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6256
5566
|
setActivePlaceholderInput(null);
|
|
6257
5567
|
setAllPlaceholdersFilled(false);
|
|
6258
|
-
|
|
6259
|
-
// because the regular textarea is hidden and user can't manually send
|
|
6260
|
-
if (activePlaceholderInput.behavior === "compose" &&
|
|
6261
|
-
!hideBottomControls) {
|
|
5568
|
+
if (!shouldSubmitFilledText) {
|
|
6262
5569
|
setPrompt(filledText);
|
|
6263
5570
|
setInputPlaceholder("Review and edit, then press Enter to send");
|
|
6264
5571
|
if (textareaRef.current) {
|
|
@@ -6270,37 +5577,34 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6270
5577
|
}
|
|
6271
5578
|
catch { }
|
|
6272
5579
|
}
|
|
5580
|
+
return;
|
|
6273
5581
|
}
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
try {
|
|
6278
|
-
handleStop();
|
|
6279
|
-
}
|
|
6280
|
-
catch { }
|
|
5582
|
+
if (isExecuting) {
|
|
5583
|
+
try {
|
|
5584
|
+
handleStop();
|
|
6281
5585
|
}
|
|
6282
|
-
|
|
5586
|
+
catch { }
|
|
6283
5587
|
}
|
|
5588
|
+
void handleSubmit(filledText);
|
|
6284
5589
|
}, onCancel: () => {
|
|
5590
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6285
5591
|
setActivePlaceholderInput(null);
|
|
6286
5592
|
setAllPlaceholdersFilled(false);
|
|
6287
5593
|
} })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (
|
|
6288
5594
|
// Template mode: show PlaceholderInput when prompt contains placeholders
|
|
6289
5595
|
// Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
|
|
6290
5596
|
_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
|
|
6291
|
-
|
|
5597
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6292
5598
|
setAllPlaceholdersFilled(false);
|
|
6293
5599
|
// Auto-submit after filling placeholders
|
|
6294
5600
|
if (filledText.trim()) {
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
catch { }
|
|
6300
|
-
}
|
|
6301
|
-
sendQuickMessage(filledText);
|
|
5601
|
+
void handleSubmit(filledText);
|
|
5602
|
+
}
|
|
5603
|
+
else {
|
|
5604
|
+
setPrompt("");
|
|
6302
5605
|
}
|
|
6303
5606
|
}, onCancel: () => {
|
|
5607
|
+
shouldSubmitFilledPlaceholderInputRef.current = false;
|
|
6304
5608
|
setPrompt("");
|
|
6305
5609
|
setAllPlaceholdersFilled(false);
|
|
6306
5610
|
setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
|
|
@@ -6508,27 +5812,27 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
|
|
|
6508
5812
|
}, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
|
|
6509
5813
|
}) }))] }), _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
|
|
6510
5814
|
? "No available tools for this profile and mode"
|
|
6511
|
-
: "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:
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
? "
|
|
6526
|
-
: "No active allowances" })) })),
|
|
5815
|
+
: "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: displayedOperationAllowancesLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading allowances..." })) : hasAnyAllowances ? (_jsxs("div", { className: "space-y-1", children: [isLocalOnlyDraftAgent && (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Preview based on the current profile." })), 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) => {
|
|
5816
|
+
const sourceLabel = formatAllowanceSource(allowance.source);
|
|
5817
|
+
const pathLabel = "itemPath" in allowance
|
|
5818
|
+
? allowance.itemPath
|
|
5819
|
+
: allowance.normalizedPath;
|
|
5820
|
+
return (_jsxs("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": `agent-allowance-row-${group.key}`, children: [_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsx("div", { className: "shrink-0 text-[10px] font-medium text-gray-700", children: allowance.operationType ||
|
|
5821
|
+
"*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
|
|
5822
|
+
allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
|
|
5823
|
+
sourceLabel,
|
|
5824
|
+
allowance.grantedBy,
|
|
5825
|
+
]
|
|
5826
|
+
.filter(Boolean)
|
|
5827
|
+
.join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
|
|
5828
|
+
})] }, group.key)) : null)] })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
5829
|
+
? "No configured allowances for this profile"
|
|
5830
|
+
: "No active allowances" })) })), displayedOperationAllowancesError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: displayedOperationAllowancesError }))] }), _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) => {
|
|
6527
5831
|
const filterText = (sub.filter || "").trim();
|
|
6528
5832
|
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));
|
|
6529
5833
|
}) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
|
|
6530
5834
|
? "Subscribed triggers are shown after the agent is created"
|
|
6531
|
-
: "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: () => {
|
|
5835
|
+
: "No active trigger subscriptions" })) })), triggerSubscriptionsError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: triggerSubscriptionsError }))] }), agent?.id && !isLocalOnlyDraftAgent ? (_jsx("div", { className: "border-t border-gray-200 pt-2", children: _jsx(AgentSharingSection, { agentId: agent.id, canManage: agent?.canManage ?? true }) })) : null] }) })] }), 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: () => {
|
|
6532
5836
|
setPrompt(p.prompt);
|
|
6533
5837
|
setShowPredefined(false);
|
|
6534
5838
|
if (textareaRef.current)
|