@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.
Files changed (200) hide show
  1. package/dist/agents-view/AgentsView.js +3 -3
  2. package/dist/agents-view/AgentsView.js.map +1 -1
  3. package/dist/agents-view/AgentsWorkspaceView.js +28 -23
  4. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  5. package/dist/components/ui/PlaceholderInput.js +3 -3
  6. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  7. package/dist/components/ui/PlaceholderInputTypes.js +2 -2
  8. package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
  9. package/dist/components/ui/PlaceholderItemSelector.js +2 -2
  10. package/dist/components/ui/PlaceholderItemSelector.js.map +1 -1
  11. package/dist/config/config.js +4 -1
  12. package/dist/config/config.js.map +1 -1
  13. package/dist/editor/LinkEditorDialog.js +2 -2
  14. package/dist/editor/LinkEditorDialog.js.map +1 -1
  15. package/dist/editor/MainLayout.d.ts +1 -0
  16. package/dist/editor/MainLayout.js +64 -1
  17. package/dist/editor/MainLayout.js.map +1 -1
  18. package/dist/editor/ai/AgentBanners.d.ts +11 -0
  19. package/dist/editor/ai/AgentBanners.js +16 -0
  20. package/dist/editor/ai/AgentBanners.js.map +1 -0
  21. package/dist/editor/ai/AgentCostDisplay.d.ts +12 -0
  22. package/dist/editor/ai/AgentCostDisplay.js +24 -1
  23. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  24. package/dist/editor/ai/AgentDocumentList.js +5 -4
  25. package/dist/editor/ai/AgentDocumentList.js.map +1 -1
  26. package/dist/editor/ai/AgentInlineDialogContent.d.ts +17 -0
  27. package/dist/editor/ai/AgentInlineDialogContent.js +32 -0
  28. package/dist/editor/ai/AgentInlineDialogContent.js.map +1 -0
  29. package/dist/editor/ai/AgentSharingSection.d.ts +6 -0
  30. package/dist/editor/ai/AgentSharingSection.js +149 -0
  31. package/dist/editor/ai/AgentSharingSection.js.map +1 -0
  32. package/dist/editor/ai/AgentTerminal.d.ts +2 -1
  33. package/dist/editor/ai/AgentTerminal.js +288 -984
  34. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  35. package/dist/editor/ai/AgentTerminalStatusBar.d.ts +18 -0
  36. package/dist/editor/ai/AgentTerminalStatusBar.js +388 -125
  37. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  38. package/dist/editor/ai/Agents.js +10 -5
  39. package/dist/editor/ai/Agents.js.map +1 -1
  40. package/dist/editor/ai/HeartbeatMessage.d.ts +4 -0
  41. package/dist/editor/ai/HeartbeatMessage.js +7 -0
  42. package/dist/editor/ai/HeartbeatMessage.js.map +1 -0
  43. package/dist/editor/ai/InitialThinkingSplash.d.ts +3 -0
  44. package/dist/editor/ai/InitialThinkingSplash.js +9 -0
  45. package/dist/editor/ai/InitialThinkingSplash.js.map +1 -0
  46. package/dist/editor/ai/InlineAiDialog.js +49 -1
  47. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  48. package/dist/editor/ai/QueuedPromptsPanel.d.ts +4 -0
  49. package/dist/editor/ai/QueuedPromptsPanel.js +36 -0
  50. package/dist/editor/ai/QueuedPromptsPanel.js.map +1 -0
  51. package/dist/editor/ai/TodoListPanel.d.ts +5 -0
  52. package/dist/editor/ai/TodoListPanel.js +113 -0
  53. package/dist/editor/ai/TodoListPanel.js.map +1 -0
  54. package/dist/editor/ai/ToolCallDisplay.js +54 -10
  55. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  56. package/dist/editor/ai/UserMessage.d.ts +4 -0
  57. package/dist/editor/ai/UserMessage.js +42 -0
  58. package/dist/editor/ai/UserMessage.js.map +1 -0
  59. package/dist/editor/ai/agentMessageConversion.d.ts +11 -0
  60. package/dist/editor/ai/agentMessageConversion.js +134 -0
  61. package/dist/editor/ai/agentMessageConversion.js.map +1 -0
  62. package/dist/editor/ai/agentMessageGrouping.d.ts +22 -0
  63. package/dist/editor/ai/agentMessageGrouping.js +103 -0
  64. package/dist/editor/ai/agentMessageGrouping.js.map +1 -0
  65. package/dist/editor/ai/agentMessageHelpers.d.ts +23 -0
  66. package/dist/editor/ai/agentMessageHelpers.js +124 -0
  67. package/dist/editor/ai/agentMessageHelpers.js.map +1 -0
  68. package/dist/editor/ai/agentMessageMarkdown.d.ts +2 -0
  69. package/dist/editor/ai/agentMessageMarkdown.js +14 -0
  70. package/dist/editor/ai/agentMessageMarkdown.js.map +1 -0
  71. package/dist/editor/ai/agentTodoExtraction.d.ts +12 -0
  72. package/dist/editor/ai/agentTodoExtraction.js +205 -0
  73. package/dist/editor/ai/agentTodoExtraction.js.map +1 -0
  74. package/dist/editor/ai/useAgentStatus.js +2 -2
  75. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  76. package/dist/editor/client/EditorShell.js +19 -1
  77. package/dist/editor/client/EditorShell.js.map +1 -1
  78. package/dist/editor/client/editContext.d.ts +5 -0
  79. package/dist/editor/client/editContext.js.map +1 -1
  80. package/dist/editor/client/operations.d.ts +4 -1
  81. package/dist/editor/client/operations.js +57 -5
  82. package/dist/editor/client/operations.js.map +1 -1
  83. package/dist/editor/client/ui/EditorChrome.js +3 -4
  84. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  85. package/dist/editor/commands/componentCommands.js +35 -2
  86. package/dist/editor/commands/componentCommands.js.map +1 -1
  87. package/dist/editor/menubar/ItemLanguageVersion.js +22 -3
  88. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  89. package/dist/editor/menubar/PreviewDatePicker.d.ts +1 -0
  90. package/dist/editor/menubar/PreviewDatePicker.js +55 -0
  91. package/dist/editor/menubar/PreviewDatePicker.js.map +1 -0
  92. package/dist/editor/menubar/VersionPreviewCard.js +34 -3
  93. package/dist/editor/menubar/VersionPreviewCard.js.map +1 -1
  94. package/dist/editor/menubar/VersionSelector.d.ts +2 -1
  95. package/dist/editor/menubar/VersionSelector.js +14 -15
  96. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  97. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +2 -2
  98. package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
  99. package/dist/editor/page-editor-chrome/FrameMenu.js +2 -2
  100. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  101. package/dist/editor/page-viewer/EditorForm.js +81 -1
  102. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  103. package/dist/editor/page-viewer/PageViewer.js +22 -90
  104. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  105. package/dist/editor/page-viewer/PageViewerFrame.js +12 -2
  106. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  107. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +4 -0
  108. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  109. package/dist/editor/page-viewer/pageViewContext.d.ts +2 -0
  110. package/dist/editor/page-viewer/pageViewContext.js +8 -1
  111. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  112. package/dist/editor/pageModel.d.ts +2 -0
  113. package/dist/editor/personalization/ActionList.d.ts +7 -0
  114. package/dist/editor/personalization/ActionList.js +16 -0
  115. package/dist/editor/personalization/ActionList.js.map +1 -0
  116. package/dist/editor/personalization/ConditionTreeEditor.d.ts +7 -0
  117. package/dist/editor/personalization/ConditionTreeEditor.js +117 -0
  118. package/dist/editor/personalization/ConditionTreeEditor.js.map +1 -0
  119. package/dist/editor/personalization/PersonalizationPanel.d.ts +12 -0
  120. package/dist/editor/personalization/PersonalizationPanel.js +308 -0
  121. package/dist/editor/personalization/PersonalizationPanel.js.map +1 -0
  122. package/dist/editor/personalization/RuleElementPicker.d.ts +11 -0
  123. package/dist/editor/personalization/RuleElementPicker.js +68 -0
  124. package/dist/editor/personalization/RuleElementPicker.js.map +1 -0
  125. package/dist/editor/personalization/RuleList.d.ts +19 -0
  126. package/dist/editor/personalization/RuleList.js +132 -0
  127. package/dist/editor/personalization/RuleList.js.map +1 -0
  128. package/dist/editor/personalization/RuleParameterInput.d.ts +10 -0
  129. package/dist/editor/personalization/RuleParameterInput.js +115 -0
  130. package/dist/editor/personalization/RuleParameterInput.js.map +1 -0
  131. package/dist/editor/personalization/TreeItemPicker.d.ts +9 -0
  132. package/dist/editor/personalization/TreeItemPicker.js +115 -0
  133. package/dist/editor/personalization/TreeItemPicker.js.map +1 -0
  134. package/dist/editor/personalization/utils.d.ts +6 -0
  135. package/dist/editor/personalization/utils.js +37 -0
  136. package/dist/editor/personalization/utils.js.map +1 -0
  137. package/dist/editor/reviews/SuggestedEdit.js +65 -48
  138. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  139. package/dist/editor/reviews/SuggestionDisplayPopover.js +5 -1
  140. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  141. package/dist/editor/services/agentService.d.ts +24 -0
  142. package/dist/editor/services/agentService.js +40 -0
  143. package/dist/editor/services/agentService.js.map +1 -1
  144. package/dist/editor/services/agentStatus.d.ts +1 -0
  145. package/dist/editor/services/agentStatus.js +55 -1
  146. package/dist/editor/services/agentStatus.js.map +1 -1
  147. package/dist/editor/services/aiService.d.ts +10 -0
  148. package/dist/editor/services/aiService.js.map +1 -1
  149. package/dist/editor/services/contentService.d.ts +5 -0
  150. package/dist/editor/services/contentService.js.map +1 -1
  151. package/dist/editor/services/personalizationService.d.ts +5 -0
  152. package/dist/editor/services/personalizationService.js +32 -0
  153. package/dist/editor/services/personalizationService.js.map +1 -0
  154. package/dist/editor/services/serviceHelper.d.ts +1 -1
  155. package/dist/editor/services/serviceHelper.js +2 -1
  156. package/dist/editor/services/serviceHelper.js.map +1 -1
  157. package/dist/editor/settings/panels/AgentProfileConfigPanel.js +1 -1
  158. package/dist/editor/settings/panels/AgentProfileConfigPanel.js.map +1 -1
  159. package/dist/editor/sidebar/OperationItem.js +1 -0
  160. package/dist/editor/sidebar/OperationItem.js.map +1 -1
  161. package/dist/editor/sidebar/SidebarPanel.js +3 -3
  162. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  163. package/dist/editor/ui/PublishRestrictionsDialog.js +1 -0
  164. package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
  165. package/dist/editor/ui/SimpleTabs.d.ts +1 -0
  166. package/dist/editor/ui/SimpleTabs.js +2 -2
  167. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  168. package/dist/editor/ui/Splitter.d.ts +4 -0
  169. package/dist/editor/ui/Splitter.js +42 -17
  170. package/dist/editor/ui/Splitter.js.map +1 -1
  171. package/dist/editor/utils/sitecoreDate.d.ts +2 -0
  172. package/dist/editor/utils/sitecoreDate.js +27 -0
  173. package/dist/editor/utils/sitecoreDate.js.map +1 -0
  174. package/dist/editor/utils/versionAtDate.d.ts +2 -0
  175. package/dist/editor/utils/versionAtDate.js +23 -0
  176. package/dist/editor/utils/versionAtDate.js.map +1 -0
  177. package/dist/lib/viewTransition.d.ts +1 -0
  178. package/dist/lib/viewTransition.js +17 -0
  179. package/dist/lib/viewTransition.js.map +1 -0
  180. package/dist/revision.d.ts +2 -2
  181. package/dist/revision.js +2 -2
  182. package/dist/task-board/TaskBoardWorkspace.js +126 -87
  183. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  184. package/dist/task-board/components/ProjectAgentsPanel.js +2 -2
  185. package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -1
  186. package/dist/task-board/components/ProjectDashboard.js +2 -3
  187. package/dist/task-board/components/ProjectDashboard.js.map +1 -1
  188. package/dist/task-board/components/ProjectPropertiesPanel.js +76 -24
  189. package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
  190. package/dist/task-board/components/TaskBoardTitlebar.js +2 -1
  191. package/dist/task-board/components/TaskBoardTitlebar.js.map +1 -1
  192. package/dist/task-board/components/TaskDetailPanel.d.ts +10 -0
  193. package/dist/task-board/components/TaskDetailPanel.js +10 -5
  194. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  195. package/dist/types.d.ts +55 -1
  196. package/package.json +1 -1
  197. package/styles.css +11 -10
  198. package/dist/task-board/components/AssignAgentDialog.d.ts +0 -7
  199. package/dist/task-board/components/AssignAgentDialog.js +0 -104
  200. 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, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, 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, 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, formatDateTime } from "../utils";
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
- const [isLoading, setIsLoading] = useState(false);
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: operationAllowances.sitecore,
769
+ rows: displayedOperationAllowances.sitecore,
1487
770
  },
1488
771
  {
1489
772
  key: "filesystem",
1490
773
  label: "Filesystem",
1491
- rows: operationAllowances.filesystem,
774
+ rows: displayedOperationAllowances.filesystem,
1492
775
  },
1493
- ], [operationAllowances]);
1494
- const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
1495
- operationAllowances.filesystem.length > 0, [operationAllowances]);
1496
- const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
1497
- operationAllowances.filesystem.length, [operationAllowances]);
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
- if (textareaRef.current) {
1911
- try {
1912
- textareaRef.current.focus({ preventScroll: true });
1913
- }
1914
- catch {
1915
- textareaRef.current.focus();
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 = parseAgentStatus(statusData?.state) ||
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
- const skillKey = (p) => JSON.stringify({
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
- // Block submission while there is a pending tool call approval decision
4477
- if (hasPendingApprovals()) {
4478
- console.warn("[AgentTerminal] handleSubmit blocked: pending tool call approval");
4479
- return;
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?.submit();
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 (prompt &&
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
- const loadingContent = isLoading && !activeInlineDialog ? (_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;
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, initialCostLimit } = costLimitExceeded;
5741
- return (_jsxs("div", { className: "m-3 rounded border border-amber-300 bg-amber-50 p-3 text-[11px] text-amber-900", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-amber-500", strokeWidth: 1 }), _jsxs("span", { children: ["Cost limit exceeded. Spent $", totalCost.toFixed(2), " / $", costLimit.toFixed(2), "."] })] }), _jsx("div", { className: "flex gap-2", children: _jsx("button", { className: "rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100", onClick: async () => {
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 (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", "data-testid": "agent-error-banner", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-red-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Agent Error" }), _jsx("div", { className: "text-red-800", children: errorMessage })] })] }) }));
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 (_jsx("div", { className: "m-3 rounded border border-amber-300 bg-amber-50 p-3 text-[11px] text-amber-900", "data-testid": "agent-capacity-banner", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-500", strokeWidth: 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "mb-1 font-semibold", children: "Waiting for capacity" }), _jsx("div", { className: "text-amber-800", children: message })] })] }) }));
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
- if (dialogRegistration) {
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, _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error &&
5955
- !isAgentErrorStatusValue((agent || agentStub)?.status) && (_jsx("div", { className: "m-4 rounded-lg border-l-4 border-red-500 bg-red-50 p-3 select-text", children: _jsxs("div", { className: "flex items-start", children: [_jsx(AlertCircle, { className: "mt-0.5 h-5 w-5 text-red-400", strokeWidth: 1 }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-[11px] font-medium text-red-800", children: "Error" }), _jsx("p", { className: "mt-1 text-[11px] text-red-700", children: error })] })] }) })), showInitialThinkingSplash && (_jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [activeProfile?.svgIcon ? (_jsx("div", { className: "flex h-16 w-16 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
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 (activePlaceholderInput.behavior === "compose" &&
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
- else {
6003
- if (isExecuting) {
6004
- try {
6005
- handleStop();
6006
- }
6007
- catch { }
5339
+ if (isExecuting) {
5340
+ try {
5341
+ handleStop();
6008
5342
  }
6009
- sendQuickMessage(filledText);
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
- setPrompt(filledText);
5351
+ shouldSubmitFilledPlaceholderInputRef.current = false;
6016
5352
  setAllPlaceholdersFilled(false);
6017
5353
  if (filledText.trim()) {
6018
- if (isExecuting) {
6019
- try {
6020
- handleStop();
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("div", { className: "flex h-full items-center justify-center p-8", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [activeProfile?.svgIcon ? (_jsx("div", { className: "flex h-16 w-16 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
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 }))] }))), queuedPrompts.length > 0 && !simpleMode && (_jsx("div", { className: "border-t border-gray-200 bg-amber-50/50", "data-testid": "queued-prompts-section", children: _jsxs("div", { className: "px-4 pt-2.5 pb-2", children: [_jsxs("div", { className: "mb-1.5 flex items-center gap-1.5", children: [_jsx("div", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-amber-400" }), _jsxs("span", { className: "text-[10px] font-medium tracking-wide text-amber-600 uppercase", "data-testid": "queued-prompts-count", children: ["Queued (", queuedPrompts.length, ")"] })] }), _jsx("div", { className: "max-h-64 space-y-0.5 overflow-y-auto", children: queuedPrompts.map((qp) => {
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
- // When hideBottomControls is true (e.g., splash screen), always auto-submit
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
- else {
6275
- // Submit behavior or default (or compose with hideBottomControls)
6276
- if (isExecuting) {
6277
- try {
6278
- handleStop();
6279
- }
6280
- catch { }
5582
+ if (isExecuting) {
5583
+ try {
5584
+ handleStop();
6281
5585
  }
6282
- sendQuickMessage(filledText);
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
- setPrompt(filledText);
5597
+ shouldSubmitFilledPlaceholderInputRef.current = false;
6292
5598
  setAllPlaceholdersFilled(false);
6293
5599
  // Auto-submit after filling placeholders
6294
5600
  if (filledText.trim()) {
6295
- if (isExecuting) {
6296
- try {
6297
- handleStop();
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: operationAllowancesLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading allowances..." })) : hasAnyAllowances ? (_jsx("div", { className: "space-y-1", children: allowanceGroups.map((group) => group.rows.length > 0 ? (_jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "px-1 text-[9px] font-medium tracking-wide text-gray-400 uppercase", children: group.label }), group.rows.map((allowance, index) => {
6512
- const sourceLabel = formatAllowanceSource(allowance.source);
6513
- const pathLabel = "itemPath" in allowance
6514
- ? allowance.itemPath
6515
- : allowance.normalizedPath;
6516
- 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 ||
6517
- "*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
6518
- allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
6519
- sourceLabel,
6520
- allowance.grantedBy,
6521
- ]
6522
- .filter(Boolean)
6523
- .join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
6524
- })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6525
- ? "Allowances are shown after the agent is created"
6526
- : "No active allowances" })) })), operationAllowancesError && (_jsx("div", { className: "mt-1 text-[10px] text-red-600", children: operationAllowancesError }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setSubscribedTriggersSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": subscribedTriggersSectionExpanded, "data-testid": "agent-subscribed-triggers-section-toggle", children: [_jsx("span", { children: `Subscribed triggers (${activeTriggerSubscriptions.length})` }), subscribedTriggersSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), subscribedTriggersSectionExpanded && (_jsx("div", { className: "max-h-28 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-subscribed-triggers-section", children: triggerSubscriptionsLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading subscribed triggers..." })) : activeTriggerSubscriptions.length > 0 ? (_jsx("div", { className: "space-y-0.5", children: activeTriggerSubscriptions.map((sub) => {
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)