@parhelia/core 0.1.12485 → 0.1.12515

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 (310) hide show
  1. package/dist/agents-view/AgentCard.js +22 -20
  2. package/dist/agents-view/AgentCard.js.map +1 -1
  3. package/dist/agents-view/AgentsInbox.js +2 -13
  4. package/dist/agents-view/AgentsInbox.js.map +1 -1
  5. package/dist/agents-view/AgentsView.js +9 -56
  6. package/dist/agents-view/AgentsView.js.map +1 -1
  7. package/dist/agents-view/AgentsWorkspaceView.js +14 -16
  8. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  9. package/dist/agents-view/ProfileAgentsGroup.js +2 -1
  10. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  11. package/dist/components/ui/copy-button.d.ts +2 -1
  12. package/dist/components/ui/copy-button.js +2 -2
  13. package/dist/components/ui/copy-button.js.map +1 -1
  14. package/dist/components/ui/input.js +1 -1
  15. package/dist/components/ui/input.js.map +1 -1
  16. package/dist/components/ui/paste-button.d.ts +2 -1
  17. package/dist/components/ui/paste-button.js +2 -2
  18. package/dist/components/ui/paste-button.js.map +1 -1
  19. package/dist/components/ui/tabs.d.ts +1 -1
  20. package/dist/components/ui/tabs.js +4 -11
  21. package/dist/components/ui/tabs.js.map +1 -1
  22. package/dist/config/config.js +73 -8
  23. package/dist/config/config.js.map +1 -1
  24. package/dist/config/types.d.ts +30 -5
  25. package/dist/config/types.js.map +1 -1
  26. package/dist/editor/ContentTree.js +36 -4
  27. package/dist/editor/ContentTree.js.map +1 -1
  28. package/dist/editor/FieldHistory.js +49 -31
  29. package/dist/editor/FieldHistory.js.map +1 -1
  30. package/dist/editor/FieldListField.js +4 -4
  31. package/dist/editor/FieldListField.js.map +1 -1
  32. package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
  33. package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
  34. package/dist/editor/GlobalMenuBar.js +1 -1
  35. package/dist/editor/GlobalMenuBar.js.map +1 -1
  36. package/dist/editor/ItemInfo.js +36 -1
  37. package/dist/editor/ItemInfo.js.map +1 -1
  38. package/dist/editor/MainLayout.d.ts +0 -2
  39. package/dist/editor/MainLayout.js +0 -1
  40. package/dist/editor/MainLayout.js.map +1 -1
  41. package/dist/editor/Titlebar.js +2 -2
  42. package/dist/editor/Titlebar.js.map +1 -1
  43. package/dist/editor/ai/AgentDocumentList.js +32 -14
  44. package/dist/editor/ai/AgentDocumentList.js.map +1 -1
  45. package/dist/editor/ai/AgentGreeting.js +3 -2
  46. package/dist/editor/ai/AgentGreeting.js.map +1 -1
  47. package/dist/editor/ai/AgentProfileSelector.js +2 -1
  48. package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
  49. package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
  50. package/dist/editor/ai/AgentStatusBadge.js +57 -71
  51. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  52. package/dist/editor/ai/AgentTerminal.js +585 -248
  53. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  54. package/dist/editor/ai/Agents.js +33 -88
  55. package/dist/editor/ai/Agents.js.map +1 -1
  56. package/dist/editor/ai/AiResponseMessage.js +3 -4
  57. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  58. package/dist/editor/ai/GuidanceOverlay.js +17 -11
  59. package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
  60. package/dist/editor/ai/InlineAiDialog.js +4 -8
  61. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  62. package/dist/editor/ai/MediaImage.js +40 -8
  63. package/dist/editor/ai/MediaImage.js.map +1 -1
  64. package/dist/editor/ai/SpawnedAgentsPanel.js +10 -12
  65. package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
  66. package/dist/editor/ai/ToolCallDisplay.js +156 -64
  67. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  68. package/dist/editor/ai/agentDiagnostics.js +1 -3
  69. package/dist/editor/ai/agentDiagnostics.js.map +1 -1
  70. package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +1 -8
  71. package/dist/editor/ai/dialogs/AgentDialogHandler.js +89 -12
  72. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  73. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +62 -0
  74. package/dist/editor/ai/dialogs/agentDialogTypes.js +2 -0
  75. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  76. package/dist/editor/ai/dialogs/browserBoundCapture.d.ts +29 -0
  77. package/dist/editor/ai/dialogs/browserBoundCapture.js +117 -0
  78. package/dist/editor/ai/dialogs/browserBoundCapture.js.map +1 -0
  79. package/dist/editor/ai/dialogs/capturePageDom.d.ts +3 -0
  80. package/dist/editor/ai/dialogs/capturePageDom.js +64 -0
  81. package/dist/editor/ai/dialogs/capturePageDom.js.map +1 -0
  82. package/dist/editor/ai/dialogs/capturePageScreenshot.d.ts +3 -0
  83. package/dist/editor/ai/dialogs/capturePageScreenshot.js +446 -0
  84. package/dist/editor/ai/dialogs/capturePageScreenshot.js.map +1 -0
  85. package/dist/editor/ai/useAgentStatus.js +23 -85
  86. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  87. package/dist/editor/client/EditorShell.js +88 -100
  88. package/dist/editor/client/EditorShell.js.map +1 -1
  89. package/dist/editor/client/editContext.d.ts +8 -16
  90. package/dist/editor/client/editContext.js.map +1 -1
  91. package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
  92. package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
  93. package/dist/editor/client/hooks/useEditorWebSocket.js +56 -44
  94. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  95. package/dist/editor/client/hooks/useSocketMessageHandler.js +19 -6
  96. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  97. package/dist/editor/client/itemsRepository.js +10 -6
  98. package/dist/editor/client/itemsRepository.js.map +1 -1
  99. package/dist/editor/client/operations.js +15 -3
  100. package/dist/editor/client/operations.js.map +1 -1
  101. package/dist/editor/client/pageModelBuilder.js +1 -1
  102. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  103. package/dist/editor/client/ui/EditorChrome.d.ts +0 -4
  104. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  105. package/dist/editor/client/waitForEditOperationTerminal.d.ts +8 -3
  106. package/dist/editor/client/waitForEditOperationTerminal.js +5 -1
  107. package/dist/editor/client/waitForEditOperationTerminal.js.map +1 -1
  108. package/dist/editor/commands/componentCommands.js +3 -49
  109. package/dist/editor/commands/componentCommands.js.map +1 -1
  110. package/dist/editor/commands/customCommandConverter.js +2 -1
  111. package/dist/editor/commands/customCommandConverter.js.map +1 -1
  112. package/dist/editor/commands/handlers/agentHandler.js +2 -1
  113. package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
  114. package/dist/editor/commands/itemCommands.js +5 -0
  115. package/dist/editor/commands/itemCommands.js.map +1 -1
  116. package/dist/editor/content-tree/IndicatorSettings.d.ts +11 -0
  117. package/dist/editor/content-tree/IndicatorSettings.js +60 -0
  118. package/dist/editor/content-tree/IndicatorSettings.js.map +1 -0
  119. package/dist/editor/content-tree/TreeOptions.d.ts +6 -0
  120. package/dist/editor/content-tree/TreeOptions.js +8 -0
  121. package/dist/editor/content-tree/TreeOptions.js.map +1 -0
  122. package/dist/editor/content-tree/TreeSettingsMenu.d.ts +2 -0
  123. package/dist/editor/content-tree/TreeSettingsMenu.js +30 -0
  124. package/dist/editor/content-tree/TreeSettingsMenu.js.map +1 -0
  125. package/dist/editor/hooks/useNavigationPanelLogic.js +2 -6
  126. package/dist/editor/hooks/useNavigationPanelLogic.js.map +1 -1
  127. package/dist/editor/manualActionEvents.d.ts +8 -0
  128. package/dist/editor/manualActionEvents.js +48 -0
  129. package/dist/editor/manualActionEvents.js.map +1 -0
  130. package/dist/editor/menubar/PageSelector.js +9 -12
  131. package/dist/editor/menubar/PageSelector.js.map +1 -1
  132. package/dist/editor/menubar/WorkflowButton.js +23 -23
  133. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  134. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  135. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  136. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +3 -9
  137. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +225 -71
  138. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  139. package/dist/editor/notifications/WatchButton.js +2 -2
  140. package/dist/editor/notifications/WatchButton.js.map +1 -1
  141. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  142. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  143. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +11 -11
  144. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  145. package/dist/editor/page-viewer/EditorForm.js +2 -0
  146. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  147. package/dist/editor/page-viewer/PageViewer.js +8 -2
  148. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  149. package/dist/editor/page-viewer/PageViewerFrame.js +21 -8
  150. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  151. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +107 -49
  152. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  153. package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
  154. package/dist/editor/page-viewer/pageViewContext.js +51 -14
  155. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  156. package/dist/editor/reviews/CreateReviewDialog.js +1 -3
  157. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  158. package/dist/editor/reviews/DiffView.js +7 -14
  159. package/dist/editor/reviews/DiffView.js.map +1 -1
  160. package/dist/editor/reviews/useMultiReview.js +2 -2
  161. package/dist/editor/reviews/useMultiReview.js.map +1 -1
  162. package/dist/editor/services/agentService.d.ts +34 -3
  163. package/dist/editor/services/agentService.js +107 -72
  164. package/dist/editor/services/agentService.js.map +1 -1
  165. package/dist/editor/services/agentStatus.d.ts +12 -0
  166. package/dist/editor/services/agentStatus.js +59 -0
  167. package/dist/editor/services/agentStatus.js.map +1 -0
  168. package/dist/editor/services/aiService.d.ts +3 -1
  169. package/dist/editor/services/aiService.js.map +1 -1
  170. package/dist/editor/services/indexService.js +1 -1
  171. package/dist/editor/services/indexService.js.map +1 -1
  172. package/dist/editor/settings/SettingsView.js +22 -20
  173. package/dist/editor/settings/SettingsView.js.map +1 -1
  174. package/dist/editor/settings/Status.js +5 -4
  175. package/dist/editor/settings/Status.js.map +1 -1
  176. package/dist/editor/settings/index/useIndexStatus.js +20 -22
  177. package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
  178. package/dist/editor/settings/panels/CreateJavaScriptToolDialog.d.ts +7 -0
  179. package/dist/editor/settings/panels/CreateJavaScriptToolDialog.js +48 -0
  180. package/dist/editor/settings/panels/CreateJavaScriptToolDialog.js.map +1 -0
  181. package/dist/editor/settings/panels/GroupedFieldConfigPanel.d.ts +2 -1
  182. package/dist/editor/settings/panels/GroupedFieldConfigPanel.js +2 -2
  183. package/dist/editor/settings/panels/GroupedFieldConfigPanel.js.map +1 -1
  184. package/dist/editor/settings/panels/JavaScriptToolAgentPanel.d.ts +12 -0
  185. package/dist/editor/settings/panels/JavaScriptToolAgentPanel.js +46 -0
  186. package/dist/editor/settings/panels/JavaScriptToolAgentPanel.js.map +1 -0
  187. package/dist/editor/settings/panels/JavaScriptToolConfigPanel.d.ts +9 -0
  188. package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js +34 -0
  189. package/dist/editor/settings/panels/JavaScriptToolConfigPanel.js.map +1 -0
  190. package/dist/editor/settings/panels/JavaScriptToolsPanel.d.ts +2 -0
  191. package/dist/editor/settings/panels/JavaScriptToolsPanel.js +285 -0
  192. package/dist/editor/settings/panels/JavaScriptToolsPanel.js.map +1 -0
  193. package/dist/editor/settings/panels/ModelConfigPanel.d.ts +2 -1
  194. package/dist/editor/settings/panels/ModelConfigPanel.js +88 -7
  195. package/dist/editor/settings/panels/ModelConfigPanel.js.map +1 -1
  196. package/dist/editor/settings/panels/ModelsPanel.js +129 -70
  197. package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
  198. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +1 -4
  199. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -3
  200. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
  201. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +78 -22
  202. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  203. package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
  204. package/dist/editor/settings/panels/ProvidersPanel.js +40 -55
  205. package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
  206. package/dist/editor/settings/panels/index.d.ts +1 -0
  207. package/dist/editor/settings/panels/index.js +1 -0
  208. package/dist/editor/settings/panels/index.js.map +1 -1
  209. package/dist/editor/settings/status/coreStatusChecks.js +28 -17
  210. package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
  211. package/dist/editor/sidebar/ComponentPalette.js +2 -1
  212. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  213. package/dist/editor/sidebar/ComponentTree.js +210 -49
  214. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  215. package/dist/editor/sidebar/EditHistory.js +3 -38
  216. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  217. package/dist/editor/sidebar/MainContentTree.js +4 -3
  218. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  219. package/dist/editor/sidebar/MorePanelsButton.js +1 -1
  220. package/dist/editor/sidebar/MorePanelsButton.js.map +1 -1
  221. package/dist/editor/sidebar/NavigationPanelItem.js +3 -6
  222. package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
  223. package/dist/editor/sidebar/SidebarPanel.js +20 -4
  224. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  225. package/dist/editor/sidebar/SidebarStack.js +1 -0
  226. package/dist/editor/sidebar/SidebarStack.js.map +1 -1
  227. package/dist/editor/sidebar/Workbox.js +53 -3
  228. package/dist/editor/sidebar/Workbox.js.map +1 -1
  229. package/dist/editor/tree-indicators/GutterColumns.d.ts +3 -1
  230. package/dist/editor/tree-indicators/GutterColumns.js +4 -3
  231. package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
  232. package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
  233. package/dist/editor/tree-indicators/GutterContext.js +23 -0
  234. package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
  235. package/dist/editor/tree-indicators/index.d.ts +0 -1
  236. package/dist/editor/tree-indicators/index.js +0 -1
  237. package/dist/editor/tree-indicators/index.js.map +1 -1
  238. package/dist/editor/tree-indicators/types.d.ts +2 -1
  239. package/dist/editor/ui/HomeButton.js +1 -1
  240. package/dist/editor/ui/HomeButton.js.map +1 -1
  241. package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
  242. package/dist/editor/ui/ItemNameDialogNew.js +17 -7
  243. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  244. package/dist/editor/ui/ItemSearch.js +7 -11
  245. package/dist/editor/ui/ItemSearch.js.map +1 -1
  246. package/dist/editor/ui/SimpleTabs.js +33 -16
  247. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  248. package/dist/editor/ui/Splitter.js +1 -1
  249. package/dist/editor/ui/Splitter.js.map +1 -1
  250. package/dist/editor/views/CompareView.js +3 -1
  251. package/dist/editor/views/CompareView.js.map +1 -1
  252. package/dist/editor/views/EditorSlot.js +2 -2
  253. package/dist/editor/views/EditorSlot.js.map +1 -1
  254. package/dist/editor/views/ParheliaView.js +5 -6
  255. package/dist/editor/views/ParheliaView.js.map +1 -1
  256. package/dist/editor/views/SingleEditView.js +2 -0
  257. package/dist/editor/views/SingleEditView.js.map +1 -1
  258. package/dist/editor/views/editorSlotContext.js +35 -6
  259. package/dist/editor/views/editorSlotContext.js.map +1 -1
  260. package/dist/index.d.ts +4 -1
  261. package/dist/index.js +1 -0
  262. package/dist/index.js.map +1 -1
  263. package/dist/lib/sanitize.d.ts +10 -0
  264. package/dist/lib/sanitize.js +40 -0
  265. package/dist/lib/sanitize.js.map +1 -0
  266. package/dist/revision.d.ts +2 -2
  267. package/dist/revision.js +2 -2
  268. package/dist/setup/services/setupWizardService.d.ts +28 -0
  269. package/dist/setup/services/setupWizardService.js +34 -0
  270. package/dist/setup/services/setupWizardService.js.map +1 -1
  271. package/dist/setup/wizard/steps/AddModelDialog.js +12 -3
  272. package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
  273. package/dist/setup/wizard/steps/ImportModelDialog.js +3 -1
  274. package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
  275. package/dist/splash-screen/NewPage.js +24 -24
  276. package/dist/splash-screen/NewPage.js.map +1 -1
  277. package/dist/task-board/TaskBoardWorkspace.js +29 -10
  278. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  279. package/dist/task-board/components/AssignAgentDialog.js +0 -8
  280. package/dist/task-board/components/AssignAgentDialog.js.map +1 -1
  281. package/dist/task-board/components/ItemCollectionEditorDialog.js +5 -12
  282. package/dist/task-board/components/ItemCollectionEditorDialog.js.map +1 -1
  283. package/dist/task-board/components/ProjectAgentsPanel.js +2 -27
  284. package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -1
  285. package/dist/task-board/components/ProjectOverviewContent.js +2 -2
  286. package/dist/task-board/components/ProjectOverviewContent.js.map +1 -1
  287. package/dist/task-board/components/ProjectPropertiesPanel.js +1 -1
  288. package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
  289. package/dist/task-board/components/TaskAgentPanel.js +2 -6
  290. package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
  291. package/dist/task-board/components/TaskDetailPanel.js +1 -1
  292. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  293. package/dist/task-board/components/TaskboardPersistentLogPanel.js +3 -1
  294. package/dist/task-board/components/TaskboardPersistentLogPanel.js.map +1 -1
  295. package/dist/task-board/taskAgentLink.js +1 -3
  296. package/dist/task-board/taskAgentLink.js.map +1 -1
  297. package/dist/task-board/views/DependencyGraphView.js +19 -1
  298. package/dist/task-board/views/DependencyGraphView.js.map +1 -1
  299. package/dist/tour/Tour.js +10 -4
  300. package/dist/tour/Tour.js.map +1 -1
  301. package/dist/tour/default-tour.js +51 -11
  302. package/dist/tour/default-tour.js.map +1 -1
  303. package/dist/types.d.ts +4 -13
  304. package/package.json +4 -1
  305. package/dist/editor/ComponentInfo.d.ts +0 -4
  306. package/dist/editor/ComponentInfo.js +0 -41
  307. package/dist/editor/ComponentInfo.js.map +0 -1
  308. package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
  309. package/dist/editor/tree-indicators/GutterSelector.js +0 -91
  310. package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo, } from "react";
3
3
  import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
4
- import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, } from "../services/agentService";
4
+ import { getAgent, startAgent, claimAgentBrowser, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, } from "../services/agentService";
5
+ import { parseAgentStatus } from "../services/agentStatus";
5
6
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
6
7
  import { localStorageService } from "../services/localStorageService";
7
8
  import { Textarea } from "../../components/ui/textarea";
@@ -16,15 +17,31 @@ import { getComponentById } from "../componentTreeHelper";
16
17
  import { AgentGreeting } from "./AgentGreeting";
17
18
  import { getAgentHistory } from "../services/editService";
18
19
  import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
20
+ import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
21
+ import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
19
22
  import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
20
23
  import { SecretAgentIcon } from "../ui/Icons";
21
24
  import { formatTime, formatDateTime } from "../utils";
22
25
  import { cn } from "../../lib/utils";
26
+ import { sanitizeSvg } from "../../lib/sanitize";
23
27
  import { Select } from "../../components/ui/select";
24
28
  import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
25
29
  import { SimpleTabs } from "../ui/SimpleTabs";
26
30
  import { Splitter } from "../ui/Splitter";
27
31
  import { ScrollingContentTree } from "../ScrollingContentTree";
32
+ import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
33
+ const userMessageMarkdownComponents = {
34
+ h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm font-semibold leading-5 text-gray-900" })),
35
+ h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] font-semibold leading-5 text-gray-900" })),
36
+ h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] font-semibold leading-5 text-gray-900" })),
37
+ h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] font-medium leading-5 text-gray-800" })),
38
+ p: (props) => _jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" }),
39
+ ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
40
+ ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
41
+ li: (props) => _jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" }),
42
+ pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
43
+ code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
44
+ };
28
45
  function buildPlaceholderAgentDetails(agentStub) {
29
46
  const now = new Date().toISOString();
30
47
  const updated = agentStub.updatedDate || now;
@@ -75,23 +92,19 @@ function formatAllowanceLabel(allowance) {
75
92
  : ` ${allowance.normalizedPath}`}`;
76
93
  }
77
94
  function getAgentRunMessageAgentId(payload) {
78
- const agentId = payload?.agentId || payload?.AgentId;
95
+ const agentId = payload?.agentId;
79
96
  return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
80
97
  }
81
98
  function getAgentRunMessageSeq(payload) {
82
- const seq = payload?.seq ?? payload?.Seq;
99
+ const seq = payload?.seq;
83
100
  return typeof seq === "number" ? seq : null;
84
101
  }
85
102
  function getAgentRunMessageDetail(type, payload) {
86
103
  if (type === "agent:run:delta") {
87
- return payload?.type || payload?.Type || null;
104
+ return payload?.type || null;
88
105
  }
89
106
  if (type === "agent:run:status") {
90
- return (payload?.data?.state ||
91
- payload?.data?.State ||
92
- payload?.data?.status ||
93
- payload?.data?.Status ||
94
- null);
107
+ return payload?.data?.state || payload?.data?.status || null;
95
108
  }
96
109
  if (type === "agent:run:error") {
97
110
  return payload?.error || null;
@@ -104,6 +117,19 @@ function getAgentRunMessageDetail(type, payload) {
104
117
  }
105
118
  return null;
106
119
  }
120
+ function isHeartbeatRunEventMessage(message) {
121
+ if (!message)
122
+ return false;
123
+ if (message.type === "agent:run:delta") {
124
+ const deltaType = message.payload?.type;
125
+ return String(deltaType || "").toLowerCase() === "heartbeat";
126
+ }
127
+ if (message.type === "agent:run:status") {
128
+ const state = message.payload?.data?.state || message.payload?.data?.status;
129
+ return String(state || "").toLowerCase() === "heartbeat";
130
+ }
131
+ return false;
132
+ }
107
133
  function getVisibleDialogRegistry() {
108
134
  const registry = globalThis.__agentDialogVisibleCallbacks;
109
135
  return registry && typeof registry === "object" ? registry : {};
@@ -119,6 +145,38 @@ function decodeHtmlEntities(value) {
119
145
  function toUserFacingAgentErrorMessage(value) {
120
146
  if (!value)
121
147
  return "";
148
+ const normalizedValue = decodeHtmlEntities(value.replace(/<br\s*\/?>/gi, "\n")).trim();
149
+ if (!normalizedValue)
150
+ return "";
151
+ const trimmed = normalizedValue.trim();
152
+ const maybeJson = trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith('"');
153
+ if (maybeJson) {
154
+ try {
155
+ const parsed = JSON.parse(trimmed);
156
+ const structuredMessage = typeof parsed === "string"
157
+ ? parsed
158
+ : typeof parsed?.error === "string"
159
+ ? parsed.error
160
+ : typeof parsed?.message === "string"
161
+ ? parsed.message
162
+ : typeof parsed?.detail === "string"
163
+ ? parsed.detail
164
+ : typeof parsed?.error_description === "string"
165
+ ? parsed.error_description
166
+ : typeof parsed?.error === "object" &&
167
+ parsed?.error &&
168
+ typeof parsed.error.message ===
169
+ "string"
170
+ ? String(parsed.error.message)
171
+ : "";
172
+ if (structuredMessage.trim()) {
173
+ value = structuredMessage;
174
+ }
175
+ }
176
+ catch {
177
+ // Fall through to plain-text cleanup when the provider error isn't valid JSON.
178
+ }
179
+ }
122
180
  const firstLine = value
123
181
  .replace(/<br\s*\/?>/gi, "\n")
124
182
  .split(/\r?\n/)
@@ -134,7 +192,7 @@ function toUserFacingAgentErrorMessage(value) {
134
192
  return cleaned;
135
193
  }
136
194
  function isAgentErrorStatusValue(status) {
137
- return status === "error" || status === 4;
195
+ return status === "error";
138
196
  }
139
197
  // Simple user message component
140
198
  const UserMessage = ({ message }) => {
@@ -147,7 +205,7 @@ const UserMessage = ({ message }) => {
147
205
  const triggerContent = triggerMatch?.[2] || "";
148
206
  const isTriggerMessage = triggerName.length > 0;
149
207
  if (isTriggerMessage) {
150
- 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: triggerContent }))] }) }));
208
+ 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 }) }))] }) }));
151
209
  }
152
210
  // Parse source agent name from content if it starts with "[From ...]:"
153
211
  // Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
@@ -175,7 +233,10 @@ const UserMessage = ({ message }) => {
175
233
  message.sourceAgent?.name;
176
234
  }
177
235
  const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
178
- 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: displayContent })] })] }));
236
+ 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 }) })] })] }));
237
+ };
238
+ const HeartbeatMessage = ({ message }) => {
239
+ 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)) }))] }) }));
179
240
  };
180
241
  // Helper to extract todos from potentially incomplete JSON during streaming
181
242
  const extractPartialTodos = (jsonText) => {
@@ -416,12 +477,9 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
416
477
  const [isExpanded, setIsExpanded] = useState(true);
417
478
  // First try to get todos from agent metadata (real-time updates)
418
479
  // Server sends additionalData.todoList directly via contextChanged status
419
- // Also check top-level todoList for backward compatibility with stored contexts
420
480
  const metadataTodos = (() => {
421
481
  try {
422
- // Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
423
- const todoList = agentMetadata?.additionalData?.todoList ||
424
- agentMetadata?.todoList;
482
+ const todoList = agentMetadata?.additionalData?.todoList;
425
483
  if (todoList?.items && Array.isArray(todoList.items)) {
426
484
  const rawItems = todoList.items
427
485
  .map((item, idx) => ({
@@ -549,6 +607,16 @@ const groupConsecutiveMessages = (agentMessages) => {
549
607
  // Add user message
550
608
  groups.push({ type: "user", messages: [message] });
551
609
  }
610
+ else if (message.messageType === "heartbeat" || message.role === "system") {
611
+ if (currentAssistantGroup.length > 0) {
612
+ groups.push({
613
+ type: "assistant-group",
614
+ messages: currentAssistantGroup,
615
+ });
616
+ currentAssistantGroup = [];
617
+ }
618
+ groups.push({ type: "heartbeat", messages: [message] });
619
+ }
552
620
  else if (message.role === "assistant") {
553
621
  // Add to current assistant group
554
622
  currentAssistantGroup.push(message);
@@ -655,10 +723,10 @@ const stringifyToolField = (value) => {
655
723
  const getFirstToolCallEnvelope = (data) => {
656
724
  if (!data || typeof data !== "object")
657
725
  return undefined;
658
- const direct = data.toolCall || data.tool_call || data.ToolCall;
726
+ const direct = data.toolCall || data.tool_call;
659
727
  if (direct)
660
728
  return direct;
661
- const arrayCandidates = [data.tool_calls, data.toolCalls, data.ToolCalls];
729
+ const arrayCandidates = [data.tool_calls, data.toolCalls];
662
730
  for (const candidate of arrayCandidates) {
663
731
  if (Array.isArray(candidate) && candidate.length > 0) {
664
732
  return candidate[0];
@@ -668,27 +736,18 @@ const getFirstToolCallEnvelope = (data) => {
668
736
  };
669
737
  const extractToolCallFields = (data) => {
670
738
  const envelope = getFirstToolCallEnvelope(data);
671
- const functionPayload = data?.function ||
672
- data?.Function ||
673
- envelope?.function ||
674
- envelope?.Function;
739
+ const functionPayload = data?.function || envelope?.function;
675
740
  const functionName = data?.functionName ||
676
741
  data?.name ||
677
742
  functionPayload?.name ||
678
- functionPayload?.Name ||
679
743
  envelope?.functionName ||
680
744
  envelope?.name ||
681
- envelope?.FunctionName ||
682
- envelope?.Name ||
683
745
  "unknown";
684
- const toolCallId = data?.toolCallId || data?.id || envelope?.id || envelope?.Id;
746
+ const toolCallId = data?.toolCallId || data?.id || envelope?.id;
685
747
  const functionArguments = stringifyToolField(data?.functionArguments) ||
686
- stringifyToolField(data?.Arguments) ||
687
748
  stringifyToolField(data?.arguments) ||
688
749
  stringifyToolField(functionPayload?.arguments) ||
689
- stringifyToolField(functionPayload?.Arguments) ||
690
750
  stringifyToolField(envelope?.functionArguments) ||
691
- stringifyToolField(envelope?.Arguments) ||
692
751
  stringifyToolField(envelope?.arguments) ||
693
752
  "{}";
694
753
  return {
@@ -774,6 +833,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
774
833
  const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
775
834
  const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
776
835
  const [agentMetadata, setAgentMetadata] = useState(null);
836
+ const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
837
+ const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
777
838
  // Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
778
839
  // This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
779
840
  useEffect(() => {
@@ -926,6 +987,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
926
987
  if (!message?.type?.startsWith("agent:run:")) {
927
988
  return false;
928
989
  }
990
+ if (isHeartbeatRunEventMessage(message)) {
991
+ return false;
992
+ }
929
993
  return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
930
994
  })
931
995
  .slice(-8)
@@ -1008,11 +1072,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1008
1072
  value === "supervised") {
1009
1073
  return value;
1010
1074
  }
1011
- // Backend task/spawned agents persist raw "agent", which behaves like
1012
- // autonomous in the frontend's current 3-mode model.
1013
- if (value === "agent") {
1014
- return "autonomous";
1015
- }
1016
1075
  return null;
1017
1076
  };
1018
1077
  const [mode, setMode] = useState("supervised");
@@ -1029,30 +1088,33 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1029
1088
  const [skillsLoading, setSkillsLoading] = useState(false);
1030
1089
  const [skillsError, setSkillsError] = useState(null);
1031
1090
  const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
1091
+ const [availableTools, setAvailableTools] = useState([]);
1032
1092
  const [operationAllowances, setOperationAllowances] = useState({
1033
1093
  sitecore: [],
1034
1094
  filesystem: [],
1035
1095
  });
1036
1096
  const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
1097
+ const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
1037
1098
  const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
1038
1099
  const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
1100
+ const [availableToolsError, setAvailableToolsError] = useState(null);
1039
1101
  const [operationAllowancesError, setOperationAllowancesError] = useState(null);
1102
+ const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
1040
1103
  const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
1041
1104
  const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
1105
+ const isNewAgent = agent?.status === "new";
1042
1106
  const hasSpawnedAgents = useMemo(() => {
1043
1107
  if (!agentMetadata)
1044
1108
  return false;
1045
- const childAgents = agentMetadata?.ChildAgents ||
1046
- agentMetadata?.childAgents;
1109
+ const childAgents = agentMetadata?.childAgents;
1047
1110
  if (!Array.isArray(childAgents) || childAgents.length === 0)
1048
1111
  return false;
1049
- return childAgents.some((a) => a != null && typeof a === "object" && (a.AgentId || a.agentId));
1112
+ return childAgents.some((a) => a != null && typeof a === "object" && a.agentId);
1050
1113
  }, [agentMetadata]);
1051
1114
  const hasTodoContent = useMemo(() => {
1052
1115
  const metadataTodos = (() => {
1053
1116
  try {
1054
- const todoList = agentMetadata?.additionalData?.todoList ||
1055
- agentMetadata?.todoList;
1117
+ const todoList = agentMetadata?.additionalData?.todoList;
1056
1118
  if (todoList?.items && Array.isArray(todoList.items)) {
1057
1119
  const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
1058
1120
  return raw.length > 0;
@@ -1165,6 +1227,95 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1165
1227
  active = false;
1166
1228
  };
1167
1229
  }, []);
1230
+ const modeOptions = useMemo(() => [
1231
+ {
1232
+ value: "supervised",
1233
+ label: "Supervised",
1234
+ className: "!bg-amber-50 text-amber-900 data-[selected=true]:!bg-amber-200 data-[selected=true]:text-amber-950",
1235
+ description: "Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside context requires approval.",
1236
+ },
1237
+ {
1238
+ value: "autonomous",
1239
+ label: "Autonomous",
1240
+ className: "!bg-red-50 text-red-900 data-[selected=true]:!bg-red-200 data-[selected=true]:text-red-950",
1241
+ description: "Full tool access; can write across the site/project only limited by user permissions.",
1242
+ },
1243
+ {
1244
+ value: "read-only",
1245
+ label: "Read-Only",
1246
+ className: "!bg-green-50 text-green-900 data-[selected=true]:!bg-green-200 data-[selected=true]:text-green-950",
1247
+ description: "Limited tool access as configured by the profile (Ask Mode Tools).",
1248
+ },
1249
+ ], []);
1250
+ const profileOptions = useMemo(() => {
1251
+ // Get the current agent's profile ID
1252
+ const currentProfileId = (agent?.profileId || agentStub.profileId)?.toLowerCase();
1253
+ // Filter profiles: show non-hidden ones + always include current profile
1254
+ const filteredProfiles = profiles?.filter((p) => !p.hiddenFromOverview || p.id.toLowerCase() === currentProfileId) || [];
1255
+ return filteredProfiles.map((p) => ({
1256
+ value: p.id,
1257
+ label: p.name,
1258
+ }));
1259
+ }, [profiles, agent?.profileId, agentStub.profileId]);
1260
+ const modelOptions = useMemo(() => (activeProfile?.models?.map((m) => ({
1261
+ value: m.id,
1262
+ label: m.name,
1263
+ })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1264
+ const metadataSelectedSkillIds = useMemo(() => {
1265
+ const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
1266
+ if (!Array.isArray(rawSkillIds)) {
1267
+ return [];
1268
+ }
1269
+ return rawSkillIds
1270
+ .map((x) => String(x || "").trim())
1271
+ .filter((x) => x.length > 0);
1272
+ }, [agentMetadata]);
1273
+ const backendAssignedSkillIds = useMemo(() => {
1274
+ const rawSkillIds = agent?.assignedSkillIds ?? [];
1275
+ if (!Array.isArray(rawSkillIds)) {
1276
+ return [];
1277
+ }
1278
+ return rawSkillIds
1279
+ .map((x) => String(x || "").trim())
1280
+ .filter((x) => x.length > 0);
1281
+ }, [agent]);
1282
+ const preloadedSkillIds = useMemo(() => {
1283
+ const rawSkillIds = activeProfile?.preloadSkills ?? [];
1284
+ if (!Array.isArray(rawSkillIds)) {
1285
+ return [];
1286
+ }
1287
+ return rawSkillIds
1288
+ .map((skill) => String(skill?.id || "").trim())
1289
+ .filter((id) => id.length > 0);
1290
+ }, [activeProfile?.preloadSkills]);
1291
+ const autoAssignedSkillIds = useMemo(() => {
1292
+ const all = isNewAgent
1293
+ ? [...preloadedSkillIds, ...backendAssignedSkillIds]
1294
+ : backendAssignedSkillIds;
1295
+ const seen = new Set();
1296
+ const unique = [];
1297
+ for (const id of all) {
1298
+ const key = id.toLowerCase();
1299
+ if (seen.has(key))
1300
+ continue;
1301
+ seen.add(key);
1302
+ unique.push(id);
1303
+ }
1304
+ return unique;
1305
+ }, [backendAssignedSkillIds, isNewAgent, preloadedSkillIds]);
1306
+ const selectedSkillIds = useMemo(() => {
1307
+ const all = [...autoAssignedSkillIds, ...metadataSelectedSkillIds];
1308
+ const seen = new Set();
1309
+ const unique = [];
1310
+ for (const id of all) {
1311
+ const key = id.toLowerCase();
1312
+ if (seen.has(key))
1313
+ continue;
1314
+ seen.add(key);
1315
+ unique.push(id);
1316
+ }
1317
+ return unique;
1318
+ }, [autoAssignedSkillIds, metadataSelectedSkillIds]);
1168
1319
  useEffect(() => {
1169
1320
  let active = true;
1170
1321
  if (!showAgentSettings) {
@@ -1176,6 +1327,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1176
1327
  setTriggerSubscriptions([]);
1177
1328
  setTriggerSubscriptionsLoading(false);
1178
1329
  setTriggerSubscriptionsError(null);
1330
+ setAvailableTools([]);
1331
+ setAvailableToolsLoading(false);
1332
+ setAvailableToolsError(null);
1179
1333
  setOperationAllowances({ sitecore: [], filesystem: [] });
1180
1334
  setOperationAllowancesLoading(false);
1181
1335
  setOperationAllowancesError(null);
@@ -1223,83 +1377,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1223
1377
  }
1224
1378
  }
1225
1379
  };
1380
+ const loadAvailableTools = async () => {
1381
+ try {
1382
+ setAvailableToolsLoading(true);
1383
+ setAvailableToolsError(null);
1384
+ const tools = await getAgentAvailableTools(agent.id);
1385
+ if (active) {
1386
+ setAvailableTools(tools);
1387
+ }
1388
+ }
1389
+ catch (e) {
1390
+ if (active) {
1391
+ setAvailableToolsError(e?.message || "Failed to load available tools");
1392
+ }
1393
+ }
1394
+ finally {
1395
+ if (active) {
1396
+ setAvailableToolsLoading(false);
1397
+ }
1398
+ }
1399
+ };
1226
1400
  void loadTriggerSubscriptions();
1401
+ void loadAvailableTools();
1227
1402
  void loadOperationAllowances();
1228
1403
  return () => {
1229
1404
  active = false;
1230
1405
  };
1231
- }, [showAgentSettings, agent?.id, agent?.status]);
1232
- const modeOptions = useMemo(() => [
1233
- {
1234
- value: "supervised",
1235
- label: "Supervised",
1236
- className: "!bg-amber-50 text-amber-900 data-[selected=true]:!bg-amber-200 data-[selected=true]:text-amber-950",
1237
- description: "Full tool access, but writes are limited to pages/items in the current context. Creating new items or updating existing items outside context requires approval.",
1238
- },
1239
- {
1240
- value: "autonomous",
1241
- label: "Autonomous",
1242
- className: "!bg-red-50 text-red-900 data-[selected=true]:!bg-red-200 data-[selected=true]:text-red-950",
1243
- description: "Full tool access; can write across the site/project only limited by user permissions.",
1244
- },
1245
- {
1246
- value: "read-only",
1247
- label: "Read-Only",
1248
- className: "!bg-green-50 text-green-900 data-[selected=true]:!bg-green-200 data-[selected=true]:text-green-950",
1249
- description: "Limited tool access as configured by the profile (Ask Mode Tools).",
1250
- },
1251
- ], []);
1252
- const profileOptions = useMemo(() => {
1253
- // Get the current agent's profile ID
1254
- const currentProfileId = (agent?.profileId || agentStub.profileId)?.toLowerCase();
1255
- // Filter profiles: show non-hidden ones + always include current profile
1256
- const filteredProfiles = profiles?.filter((p) => !p.hiddenFromOverview || p.id.toLowerCase() === currentProfileId) || [];
1257
- return filteredProfiles.map((p) => ({
1258
- value: p.id,
1259
- label: p.name,
1260
- }));
1261
- }, [profiles, agent?.profileId, agentStub.profileId]);
1262
- const modelOptions = useMemo(() => (activeProfile?.models?.map((m) => ({
1263
- value: m.id,
1264
- label: m.name,
1265
- })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1266
- const metadataSelectedSkillIds = useMemo(() => {
1267
- const rawSkillIds = agentMetadata?.additionalData?.skillIds ??
1268
- agentMetadata?.AdditionalData?.skillIds ??
1269
- agentMetadata?.skillIds ??
1270
- agentMetadata?.SkillIds ??
1271
- [];
1272
- if (!Array.isArray(rawSkillIds)) {
1273
- return [];
1274
- }
1275
- return rawSkillIds
1276
- .map((x) => String(x || "").trim())
1277
- .filter((x) => x.length > 0);
1278
- }, [agentMetadata]);
1279
- const backendAssignedSkillIds = useMemo(() => {
1280
- const rawSkillIds = agent?.assignedSkillIds ??
1281
- agent?.AssignedSkillIds ??
1282
- [];
1283
- if (!Array.isArray(rawSkillIds)) {
1284
- return [];
1285
- }
1286
- return rawSkillIds
1287
- .map((x) => String(x || "").trim())
1288
- .filter((x) => x.length > 0);
1289
- }, [agent]);
1290
- const selectedSkillIds = useMemo(() => {
1291
- const all = [...backendAssignedSkillIds, ...metadataSelectedSkillIds];
1292
- const seen = new Set();
1293
- const unique = [];
1294
- for (const id of all) {
1295
- const key = id.toLowerCase();
1296
- if (seen.has(key))
1297
- continue;
1298
- seen.add(key);
1299
- unique.push(id);
1300
- }
1301
- return unique;
1302
- }, [backendAssignedSkillIds, metadataSelectedSkillIds]);
1406
+ }, [
1407
+ showAgentSettings,
1408
+ agent?.id,
1409
+ agent?.status,
1410
+ agent?.profileId,
1411
+ mode,
1412
+ selectedSkillIds,
1413
+ ]);
1303
1414
  const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
1304
1415
  const allowanceGroups = useMemo(() => [
1305
1416
  {
@@ -1317,18 +1428,21 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1317
1428
  operationAllowances.filesystem.length > 0, [operationAllowances]);
1318
1429
  const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
1319
1430
  operationAllowances.filesystem.length, [operationAllowances]);
1320
- const allowedProfileSkillIdSet = useMemo(() => {
1321
- const ids = activeProfile?.allowedSkills
1322
- ?.map((skill) => String(skill?.id || "").toLowerCase())
1323
- .filter((id) => id.length > 0) || [];
1431
+ const listedProfileSkillIdSet = useMemo(() => {
1432
+ const ids = [
1433
+ ...(activeProfile?.availableSkills ?? []),
1434
+ ...(activeProfile?.allowedSkills ?? []),
1435
+ ]
1436
+ .map((skill) => String(skill?.id || "").toLowerCase())
1437
+ .filter((id) => id.length > 0);
1324
1438
  return new Set(ids);
1325
- }, [activeProfile?.allowedSkills]);
1439
+ }, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
1326
1440
  const profileFilteredSkills = useMemo(() => {
1327
- if (allowedProfileSkillIdSet.size === 0) {
1328
- return availableSkills;
1441
+ if (listedProfileSkillIdSet.size === 0) {
1442
+ return [];
1329
1443
  }
1330
- return availableSkills.filter((skill) => allowedProfileSkillIdSet.has(skill.id.toLowerCase()));
1331
- }, [availableSkills, allowedProfileSkillIdSet]);
1444
+ return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
1445
+ }, [availableSkills, listedProfileSkillIdSet]);
1332
1446
  const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
1333
1447
  const availableSkillIdSet = useMemo(() => new Set(availableSkills.map((skill) => skill.id.toLowerCase())), [availableSkills]);
1334
1448
  const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
@@ -1336,7 +1450,40 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1336
1450
  .map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
1337
1451
  .filter((s) => !!s), [availableSkills, selectedSkillIds]);
1338
1452
  const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
1339
- const backendAssignedSkillSet = useMemo(() => new Set(backendAssignedSkillIds.map((id) => id.toLowerCase())), [backendAssignedSkillIds]);
1453
+ const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
1454
+ const previewAvailableTools = useMemo(() => {
1455
+ const baseToolNames = mode === "read-only"
1456
+ ? activeProfile?.readOnlyToolNames ?? []
1457
+ : activeProfile?.allowedToolNames ?? [];
1458
+ const toolNames = new Set();
1459
+ for (const toolName of baseToolNames) {
1460
+ const normalized = String(toolName || "").trim();
1461
+ if (normalized) {
1462
+ toolNames.add(normalized);
1463
+ }
1464
+ }
1465
+ for (const skill of selectedSkills) {
1466
+ for (const toolName of skill.additionalAllowedTools ?? []) {
1467
+ const normalized = String(toolName || "").trim();
1468
+ if (normalized) {
1469
+ toolNames.add(normalized);
1470
+ }
1471
+ }
1472
+ }
1473
+ return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
1474
+ }, [
1475
+ activeProfile?.allowedToolNames,
1476
+ activeProfile?.readOnlyToolNames,
1477
+ mode,
1478
+ selectedSkills,
1479
+ ]);
1480
+ const displayedAvailableTools = isNewAgent
1481
+ ? previewAvailableTools
1482
+ : availableTools;
1483
+ const displayedAvailableToolsLoading = isNewAgent
1484
+ ? false
1485
+ : availableToolsLoading;
1486
+ const displayedAvailableToolsError = isNewAgent ? null : availableToolsError;
1340
1487
  // Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
1341
1488
  const sanitizeAgentMetadata = useCallback((meta) => {
1342
1489
  try {
@@ -1365,9 +1512,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1365
1512
  if (!agent?.id)
1366
1513
  return;
1367
1514
  const current = agentMetadata || {};
1368
- const currentAdditionalData = current.additionalData ||
1369
- current.AdditionalData ||
1370
- {};
1515
+ const currentAdditionalData = current.additionalData || {};
1371
1516
  const nextAdditionalData = {
1372
1517
  ...currentAdditionalData,
1373
1518
  };
@@ -1382,11 +1527,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1382
1527
  };
1383
1528
  if (Object.keys(nextAdditionalData).length > 0) {
1384
1529
  next.additionalData = nextAdditionalData;
1385
- delete next.AdditionalData;
1386
1530
  }
1387
1531
  else {
1388
1532
  delete next.additionalData;
1389
- delete next.AdditionalData;
1390
1533
  }
1391
1534
  try {
1392
1535
  if (agent.status === "new") {
@@ -1542,7 +1685,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1542
1685
  // Auto-focus terminal input on mount
1543
1686
  useEffect(() => {
1544
1687
  if (textareaRef.current) {
1545
- textareaRef.current.focus();
1688
+ try {
1689
+ textareaRef.current.focus({ preventScroll: true });
1690
+ }
1691
+ catch {
1692
+ textareaRef.current.focus();
1693
+ }
1546
1694
  }
1547
1695
  }, []);
1548
1696
  // Start voice recognition
@@ -1805,6 +1953,55 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1805
1953
  toolCalls: [],
1806
1954
  };
1807
1955
  }, [agent, agentStub.id]);
1956
+ const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
1957
+ const handleHeartbeatMessage = useCallback((message) => {
1958
+ const heartbeatText = (message.data?.message ||
1959
+ message.data?.Message ||
1960
+ "Still working on your request. This is taking a little longer than usual.").trim() ||
1961
+ "Still working on your request. This is taking a little longer than usual.";
1962
+ const createdDate = message.timestamp || new Date().toISOString();
1963
+ const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
1964
+ setMessages((prev) => {
1965
+ const withoutHeartbeats = stripHeartbeatMessages(prev);
1966
+ const updated = [
1967
+ ...withoutHeartbeats,
1968
+ {
1969
+ id: heartbeatId,
1970
+ agentId: agent?.id || agentStub.id,
1971
+ messageIndex: -1,
1972
+ role: "system",
1973
+ content: heartbeatText,
1974
+ name: "system",
1975
+ messageType: "heartbeat",
1976
+ isCompleted: true,
1977
+ model: "",
1978
+ tokensUsed: 0,
1979
+ inputTokens: 0,
1980
+ outputTokens: 0,
1981
+ cachedInputTokens: 0,
1982
+ inputTokenCost: 0,
1983
+ outputTokenCost: 0,
1984
+ cachedInputTokenCost: 0,
1985
+ totalCost: 0,
1986
+ currency: "USD",
1987
+ createdDate,
1988
+ toolCalls: [],
1989
+ },
1990
+ ];
1991
+ messagesRef.current = updated;
1992
+ return updated;
1993
+ });
1994
+ }, [agent?.id, agentStub.id, stripHeartbeatMessages]);
1995
+ const clearHeartbeatMessages = useCallback(() => {
1996
+ setMessages((prev) => {
1997
+ const updated = stripHeartbeatMessages(prev);
1998
+ if (updated.length === prev.length) {
1999
+ return prev;
2000
+ }
2001
+ messagesRef.current = updated;
2002
+ return updated;
2003
+ });
2004
+ }, [stripHeartbeatMessages]);
1808
2005
  const handleContentChunk = useCallback((message, agentData) => {
1809
2006
  // Get messageId from data, or generate one from agent ID (for backward compatibility)
1810
2007
  // If no messageId is provided, we'll use the last assistant message or create a new one
@@ -1823,7 +2020,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1823
2020
  // If the agent isn't currently running (e.g., we switched tabs after the run
1824
2021
  // completed), skip creating a new streaming message to avoid duplicates.
1825
2022
  const currentAgentStatus = (agentData || agent)?.status;
1826
- const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
2023
+ const isAgentRunning = currentAgentStatus === "running";
1827
2024
  if (!isAgentRunning) {
1828
2025
  return;
1829
2026
  }
@@ -2630,29 +2827,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2630
2827
  if (!contextJson)
2631
2828
  return null;
2632
2829
  const parsedContext = JSON.parse(contextJson);
2633
- // Context is stored as flat structure with top-level properties.
2634
- // Due to C# [JsonExtensionData], AdditionalData entries can be serialized at top level.
2635
- // Normalize well-known extension keys back under additionalData for consistent UI behavior.
2636
2830
  if (parsedContext && typeof parsedContext === "object") {
2637
- const normalized = { ...parsedContext };
2638
- const additionalData = {
2639
- ...(normalized.additionalData || {}),
2640
- };
2641
- let additionalDataUpdated = false;
2642
- // If todoList is at top level but not in additionalData, move it
2643
- if (normalized.todoList && !normalized.additionalData?.todoList) {
2644
- additionalData.todoList = normalized.todoList;
2645
- additionalDataUpdated = true;
2646
- }
2647
- // Planner/task skill selections may also be flattened from JsonExtensionData.
2648
- if (normalized.skillIds && !normalized.additionalData?.skillIds) {
2649
- additionalData.skillIds = normalized.skillIds;
2650
- additionalDataUpdated = true;
2651
- }
2652
- if (additionalDataUpdated) {
2653
- normalized.additionalData = additionalData;
2654
- }
2655
- return normalized;
2831
+ return parsedContext;
2656
2832
  }
2657
2833
  return null;
2658
2834
  }
@@ -2738,7 +2914,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2738
2914
  return;
2739
2915
  }
2740
2916
  // Check if cost limit exceeded based on status or cost values
2741
- const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
2917
+ const statusIndicatesLimit = agent.status === "costLimitReached";
2742
2918
  // Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
2743
2919
  const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
2744
2920
  const costExceedsLimit = agent.costLimit &&
@@ -2823,9 +2999,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2823
2999
  // Handle agent:profile:switched (profile changed via switch-profile function)
2824
3000
  if (messageType === "agent:profile:switched") {
2825
3001
  const payload = message.payload || {};
2826
- const switchedAgentId = payload.agentId || payload.AgentId;
2827
- const newProfileId = payload.newProfileId || payload.NewProfileId;
2828
- const newProfileName = payload.newProfileName || payload.NewProfileName;
3002
+ const switchedAgentId = payload.agentId;
3003
+ const newProfileId = payload.newProfileId;
3004
+ const newProfileName = payload.newProfileName;
2829
3005
  if (switchedAgentId === agent.id && newProfileId) {
2830
3006
  // Update the agent's profile
2831
3007
  setAgent((prev) => {
@@ -2866,7 +3042,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2866
3042
  return;
2867
3043
  }
2868
3044
  // For other agent messages, check if this is for our agent
2869
- const agentId = message.payload?.agentId || message.payload?.AgentId;
3045
+ const agentId = message.payload?.agentId;
2870
3046
  if (agentId !== agent.id) {
2871
3047
  return;
2872
3048
  }
@@ -3008,15 +3184,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3008
3184
  }
3009
3185
  // Always allow ContextUpdate messages (metadata updates) regardless of agent status
3010
3186
  const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
3187
+ const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
3188
+ const shouldClearHeartbeat = type === "ContentChunk" ||
3189
+ type === "contentChunk" ||
3190
+ type === "ToolCall" ||
3191
+ type === "toolCall" ||
3192
+ type === "ToolResult" ||
3193
+ type === "toolResult";
3011
3194
  // Allow deltas if the agent is running OR if we already have an active streaming message
3012
3195
  // OR if the server provided a messageId for targeted updates.
3013
3196
  // This avoids dropping early deltas that may arrive before the 'running' status update.
3014
3197
  // ContextUpdate messages are always allowed as they're metadata updates, not content.
3015
3198
  const isRunning = agent.status === "running" || agent.status === 1;
3016
3199
  const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
3017
- const hasMessageId = !!message?.payload?.data?.messageId ||
3018
- !!message?.payload?.data?.MessageId;
3019
- if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
3200
+ const hasMessageId = !!message?.payload?.data?.messageId;
3201
+ if (!isContextUpdate &&
3202
+ !isHeartbeat &&
3203
+ !isRunning &&
3204
+ !hasStreaming &&
3205
+ !hasMessageId) {
3020
3206
  return; // ignore only if we cannot safely target an existing streaming message
3021
3207
  }
3022
3208
  // Deduplicate by sequence
@@ -3033,6 +3219,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3033
3219
  cost,
3034
3220
  timestamp: new Date().toISOString(),
3035
3221
  };
3222
+ if (shouldClearHeartbeat) {
3223
+ clearHeartbeatMessages();
3224
+ }
3036
3225
  if (type === "ContentChunk" || type === "contentChunk") {
3037
3226
  handleContentChunk(agentStreamMessage, agent);
3038
3227
  }
@@ -3042,6 +3231,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3042
3231
  else if (type === "ToolResult" || type === "toolResult") {
3043
3232
  handleToolResult(agentStreamMessage, agent);
3044
3233
  }
3234
+ else if (type === "Heartbeat" || type === "heartbeat") {
3235
+ handleHeartbeatMessage(agentStreamMessage);
3236
+ }
3045
3237
  else if (type === "ContextUpdate" || type === "contextUpdate") {
3046
3238
  // Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
3047
3239
  const contextData = data;
@@ -3051,10 +3243,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3051
3243
  const current = (prev || {});
3052
3244
  const updated = { ...current };
3053
3245
  // Merge additionalData if present (deep merge to preserve existing values)
3054
- if (contextData.additionalData || contextData.AdditionalData) {
3055
- const sourceAdditionalData = contextData.additionalData ||
3056
- contextData.AdditionalData ||
3057
- {};
3246
+ if (contextData.additionalData) {
3247
+ const sourceAdditionalData = contextData.additionalData || {};
3058
3248
  updated.additionalData = {
3059
3249
  ...(current.additionalData || {}),
3060
3250
  ...sourceAdditionalData,
@@ -3062,10 +3252,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3062
3252
  }
3063
3253
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3064
3254
  // Backend sends the complete updated list, not just additions
3065
- if (contextData.ChildAgents || contextData.childAgents) {
3066
- const childAgents = contextData.ChildAgents || contextData.childAgents;
3255
+ if (contextData.childAgents) {
3256
+ const childAgents = contextData.childAgents;
3067
3257
  if (Array.isArray(childAgents)) {
3068
- updated.ChildAgents = childAgents;
3258
+ updated.childAgents = childAgents;
3069
3259
  }
3070
3260
  }
3071
3261
  return updated;
@@ -3091,14 +3281,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3091
3281
  // Route based on statusData.state
3092
3282
  try {
3093
3283
  // Normalize various status shapes and handle Cancelled uniformly
3094
- const normalizedStatus = statusData?.state ||
3095
- statusData?.Status ||
3096
- statusData?.status;
3097
- if (normalizedStatus === "Cancelled" ||
3098
- normalizedStatus === "canceled" ||
3099
- normalizedStatus === "stopped" ||
3100
- normalizedStatus === "Stopped") {
3284
+ const normalizedStatus = parseAgentStatus(statusData?.state) ||
3285
+ parseAgentStatus(statusData?.status);
3286
+ if (normalizedStatus === "idle") {
3101
3287
  // Stop indicators and mark any in-progress streaming messages as completed
3288
+ clearHeartbeatMessages();
3102
3289
  setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3103
3290
  setIsWaitingForResponse(false);
3104
3291
  isWaitingRef.current = false;
@@ -3201,6 +3388,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3201
3388
  return;
3202
3389
  }
3203
3390
  if (statusData?.state === "ToolApprovalsRequired") {
3391
+ setPendingBrowserCaptureDialogType(null);
3204
3392
  const msgId = statusData.messageId;
3205
3393
  const ids = statusData.toolCallIds || [];
3206
3394
  if (msgId && Array.isArray(ids) && ids.length > 0) {
@@ -3233,23 +3421,39 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3233
3421
  return;
3234
3422
  }
3235
3423
  // Handle waiting states explicitly
3236
- if (statusData?.state === "WaitingForApproval" ||
3237
- statusData?.state === "waitingForApproval") {
3424
+ if (normalizedStatus === "waitingForApproval") {
3425
+ setPendingBrowserCaptureDialogType(null);
3238
3426
  setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
3239
3427
  setIsConnecting(false);
3240
3428
  setIsWaitingForResponse(false);
3241
3429
  setIsAgentThinking(false);
3242
3430
  return;
3243
3431
  }
3244
- if (statusData?.state === "WaitingForInput" ||
3245
- statusData?.state === "waitingForInput") {
3246
- setAgent((prev) => prev ? { ...prev, status: "waitingForInput" } : prev);
3432
+ if (normalizedStatus === "waitingForInput") {
3433
+ const dialogType = typeof statusData?.dialogType === "string"
3434
+ ? statusData.dialogType
3435
+ : null;
3436
+ const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
3437
+ dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
3438
+ setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
3439
+ setAgent((prev) => prev
3440
+ ? {
3441
+ ...prev,
3442
+ status: "waitingForInput",
3443
+ statusMessage: isBrowserCaptureWait &&
3444
+ typeof statusData?.title === "string" &&
3445
+ statusData.title.trim()
3446
+ ? statusData.title.trim()
3447
+ : prev.statusMessage,
3448
+ }
3449
+ : prev);
3247
3450
  setIsConnecting(false);
3248
3451
  setIsWaitingForResponse(false);
3249
3452
  setIsAgentThinking(false);
3250
3453
  return;
3251
3454
  }
3252
- if (statusData?.state === "CostLimitReached") {
3455
+ if (normalizedStatus === "costLimitReached") {
3456
+ setPendingBrowserCaptureDialogType(null);
3253
3457
  const totalCost = Number(statusData.totalCost) || 0;
3254
3458
  const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
3255
3459
  setCostLimitExceeded({
@@ -3267,7 +3471,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3267
3471
  // Server sends additionalData directly (with todoList inside)
3268
3472
  // Also may include ChildAgents for spawned agents
3269
3473
  try {
3270
- const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
3474
+ const serverAdditionalData = statusData.additionalData || {};
3271
3475
  setAgentMetadata((prev) => {
3272
3476
  const current = (prev || {});
3273
3477
  const updated = { ...current };
@@ -3281,10 +3485,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3281
3485
  }
3282
3486
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3283
3487
  // Backend sends the complete updated list, not just additions
3284
- if (statusData.ChildAgents || statusData.childAgents) {
3285
- const childAgents = statusData.ChildAgents || statusData.childAgents;
3488
+ if (statusData.childAgents) {
3489
+ const childAgents = statusData.childAgents;
3286
3490
  if (Array.isArray(childAgents)) {
3287
- updated.ChildAgents = childAgents;
3491
+ updated.childAgents = childAgents;
3288
3492
  }
3289
3493
  }
3290
3494
  return updated;
@@ -3296,10 +3500,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3296
3500
  return;
3297
3501
  }
3298
3502
  // Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
3299
- if (normalizedStatus === "completed" ||
3300
- normalizedStatus === "Completed") {
3503
+ if (normalizedStatus === "completed") {
3301
3504
  // Reset deduplication for the next run
3302
3505
  lastSeqRef.current = 0;
3506
+ clearHeartbeatMessages();
3303
3507
  // Mark the last assistant message as completed
3304
3508
  setMessages((prev) => {
3305
3509
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3322,20 +3526,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3322
3526
  setIsAgentThinking(false);
3323
3527
  return;
3324
3528
  }
3325
- // Handle "Idle" state - agent has finished processing
3326
- if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
3327
- // Update agent status to idle
3328
- setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3329
- setIsWaitingForResponse(false);
3330
- isWaitingRef.current = false;
3331
- setIsConnecting(false);
3332
- shouldCreateNewMessage.current = false;
3333
- setIsAgentThinking(false);
3334
- return;
3335
- }
3336
3529
  // Handle "Running" state - agent is actively processing
3337
- if (normalizedStatus === "running" ||
3338
- normalizedStatus === "Running") {
3530
+ if (normalizedStatus === "running") {
3339
3531
  // Update agent status to running
3340
3532
  setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
3341
3533
  setIsWaitingForResponse(true);
@@ -3344,8 +3536,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3344
3536
  return;
3345
3537
  }
3346
3538
  // Handle "Error" state
3347
- if (normalizedStatus === "error" || normalizedStatus === "Error") {
3539
+ if (normalizedStatus === "error") {
3348
3540
  const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
3541
+ clearHeartbeatMessages();
3349
3542
  setAgent((prev) => prev
3350
3543
  ? { ...prev, status: "error", statusMessage: errorMsg }
3351
3544
  : prev);
@@ -3365,6 +3558,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3365
3558
  if (messageType === "agent:run:complete") {
3366
3559
  // Reset deduplication for the next run
3367
3560
  lastSeqRef.current = 0;
3561
+ clearHeartbeatMessages();
3368
3562
  // Mark the last assistant message as completed
3369
3563
  setMessages((prev) => {
3370
3564
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3391,6 +3585,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3391
3585
  if (messageType === "agent:run:error") {
3392
3586
  const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
3393
3587
  "AI could not complete this request.";
3588
+ clearHeartbeatMessages();
3394
3589
  // Reset deduplication for the next run after an error
3395
3590
  lastSeqRef.current = 0;
3396
3591
  setError(errorMsg);
@@ -3405,7 +3600,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3405
3600
  }
3406
3601
  }, [
3407
3602
  agent,
3603
+ clearHeartbeatMessages,
3408
3604
  handleContentChunk,
3605
+ handleHeartbeatMessage,
3409
3606
  handleToolCall,
3410
3607
  handleToolResult,
3411
3608
  onAgentUpdate,
@@ -3611,9 +3808,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3611
3808
  }
3612
3809
  setTimeout(() => {
3613
3810
  if (request.dialogType === "questionnaire") {
3614
- inlineDialogContainerRef.current?.scrollIntoView({
3615
- block: "start",
3616
- });
3811
+ scrollToBottomRefForDialogs.current?.();
3617
3812
  return;
3618
3813
  }
3619
3814
  scrollToBottomRefForDialogs.current?.();
@@ -3681,27 +3876,34 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3681
3876
  // Use case-insensitive comparison for GUID matching (backend may return different casing)
3682
3877
  const normalizedProfileId = profileIdToUse?.toLowerCase();
3683
3878
  const candidate = normalizedProfileId
3684
- ? (profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId) ??
3685
- profiles[0])
3686
- : profiles[0];
3687
- if (!candidate)
3879
+ ? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
3880
+ : undefined;
3881
+ if (!candidate) {
3882
+ setActiveProfile(undefined);
3688
3883
  return;
3884
+ }
3689
3885
  // Keep active profile in sync whenever the matching entry in `profiles` changes —
3690
- // not only when the profile id changes. Otherwise allowedSkills (and similar fields)
3886
+ // not only when the profile id changes. Otherwise availableSkills (and similar fields)
3691
3887
  // that are merged in the parent after async loads never update (e.g. Template Builder
3692
3888
  // settings skills merged into the profile).
3693
3889
  setActiveProfile((prev) => {
3694
3890
  if (!prev || prev.id !== candidate.id)
3695
3891
  return candidate;
3696
- const skillKey = (p) => JSON.stringify((p.allowedSkills ?? []).map((s) => [
3697
- String(s?.id ?? ""),
3698
- String(s?.name ?? ""),
3699
- ]));
3892
+ const skillKey = (p) => JSON.stringify({
3893
+ allowed: (p.allowedSkills ?? []).map((s) => [
3894
+ String(s?.id ?? ""),
3895
+ String(s?.name ?? ""),
3896
+ ]),
3897
+ available: (p.availableSkills ?? []).map((s) => [
3898
+ String(s?.id ?? ""),
3899
+ String(s?.name ?? ""),
3900
+ ]),
3901
+ });
3700
3902
  if (skillKey(prev) === skillKey(candidate))
3701
3903
  return prev;
3702
3904
  return candidate;
3703
3905
  });
3704
- }, [profiles, agent?.profileId, agentStub.profileId]);
3906
+ }, [profiles, agent?.id, agent?.profileId, agentStub.id, agentStub.profileId]);
3705
3907
  // Clear queued prompts when agent changes or is new;
3706
3908
  // initial fetch is handled by loadAgent() for better performance and reliability
3707
3909
  useEffect(() => {
@@ -3792,6 +3994,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3792
3994
  console.error("Failed to persist pending settings", e);
3793
3995
  }
3794
3996
  }, [agent?.id]);
3997
+ const getPendingRequestSettings = useCallback(() => {
3998
+ const pending = pendingSettingsRef.current;
3999
+ const requestProfile = pending?.profileId
4000
+ ? profiles.find((profile) => profile.id === pending.profileId) ||
4001
+ activeProfile ||
4002
+ profiles[0]
4003
+ : activeProfile || profiles[0];
4004
+ let requestModelId = selectedModelId;
4005
+ if (pending?.modelName) {
4006
+ const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
4007
+ pending.modelName?.trim().toLowerCase());
4008
+ requestModelId = requestModel?.id || requestModelId;
4009
+ }
4010
+ return {
4011
+ mode: (pending?.mode || mode),
4012
+ profileId: pending?.profileId || requestProfile?.id || "",
4013
+ profileName: pending?.profileName || requestProfile?.name || "",
4014
+ modelId: requestModelId,
4015
+ };
4016
+ }, [activeProfile, mode, profiles, selectedModelId]);
3795
4017
  const getSubmitErrorMessage = (error) => {
3796
4018
  const fallback = "Failed to submit prompt. Please try again.";
3797
4019
  if (!(error instanceof Error))
@@ -3993,24 +4215,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3993
4215
  console.warn("[AgentTerminal] Failed to compute live context:", e);
3994
4216
  }
3995
4217
  // Add visible test IDs to context (only for Help agent)
3996
- const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4218
+ const requestSettings = getPendingRequestSettings();
4219
+ const currentProfileName = requestSettings.profileName;
3997
4220
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
3998
4221
  const request = {
3999
4222
  agentId: agentId,
4000
4223
  message: savedPrompt,
4001
4224
  sessionId: editContext.sessionId,
4002
- profileId: activeProfile?.id || profiles[0]?.id || "",
4225
+ profileId: requestSettings.profileId,
4003
4226
  profile: currentProfileName,
4004
- model: selectedModelId,
4005
- mode: mode,
4227
+ model: requestSettings.modelId,
4228
+ mode: requestSettings.mode,
4006
4229
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
4007
4230
  };
4008
4231
  console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
4009
4232
  const response = await startAgent(request);
4010
4233
  console.log("[AgentTerminal] startAgent response:", response);
4011
4234
  // Check if prompt was queued (agent was already running)
4012
- const wasQueued = response.message?.toLowerCase().includes("queued") ||
4013
- response.status === "Queued";
4235
+ const wasQueued = response.message?.toLowerCase().includes("queued");
4014
4236
  if (wasQueued) {
4015
4237
  // Prompt was queued - show a brief notification but don't set waiting state
4016
4238
  // The prompt will be processed when the agent becomes idle
@@ -4251,16 +4473,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4251
4473
  console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
4252
4474
  }
4253
4475
  // Add visible test IDs to context (only for Help agent)
4254
- const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4476
+ const requestSettings = getPendingRequestSettings();
4477
+ const currentProfileName = requestSettings.profileName;
4255
4478
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
4256
4479
  const request = {
4257
4480
  agentId: agent.id,
4258
4481
  message: savedPrompt,
4259
4482
  sessionId: editContext.sessionId,
4260
- profileId: activeProfile?.id || profiles[0]?.id || "",
4483
+ profileId: requestSettings.profileId,
4261
4484
  profile: currentProfileName,
4262
- model: selectedModelId,
4263
- mode: mode,
4485
+ model: requestSettings.modelId,
4486
+ mode: requestSettings.mode,
4264
4487
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
4265
4488
  };
4266
4489
  console.log("[AgentTerminal] Calling startAgent API for quick message");
@@ -4685,6 +4908,70 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4685
4908
  activeProfile,
4686
4909
  profiles,
4687
4910
  ]);
4911
+ const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
4912
+ const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
4913
+ pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
4914
+ const currentSessionId = editContext?.sessionId?.trim() || "";
4915
+ const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
4916
+ const isClaimedByCurrentSession = !!currentSessionId &&
4917
+ !!claimedSessionId &&
4918
+ currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
4919
+ const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
4920
+ const handleClaimBrowser = useCallback(async (takeOver) => {
4921
+ if (!agent?.id || !editContext?.sessionId)
4922
+ return;
4923
+ setIsBrowserClaimMutationPending(true);
4924
+ try {
4925
+ const response = await claimAgentBrowser({
4926
+ agentId: agent.id,
4927
+ sessionId: editContext.sessionId,
4928
+ takeOver,
4929
+ terminalInstanceId: dialogTerminalInstanceIdRef.current,
4930
+ });
4931
+ setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
4932
+ }
4933
+ catch (err) {
4934
+ console.error("[AgentTerminal] Failed to claim browser:", err);
4935
+ editContext.showErrorToast(err);
4936
+ }
4937
+ finally {
4938
+ setIsBrowserClaimMutationPending(false);
4939
+ }
4940
+ }, [agent?.id, editContext, sanitizeAgentMetadata]);
4941
+ const handleReleaseBrowser = useCallback(async () => {
4942
+ if (!agent?.id || !editContext?.sessionId)
4943
+ return;
4944
+ setIsBrowserClaimMutationPending(true);
4945
+ try {
4946
+ const response = await releaseAgentBrowser({
4947
+ agentId: agent.id,
4948
+ sessionId: editContext.sessionId,
4949
+ terminalInstanceId: dialogTerminalInstanceIdRef.current,
4950
+ });
4951
+ setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
4952
+ }
4953
+ catch (err) {
4954
+ console.error("[AgentTerminal] Failed to release browser:", err);
4955
+ editContext.showErrorToast(err);
4956
+ }
4957
+ finally {
4958
+ setIsBrowserClaimMutationPending(false);
4959
+ }
4960
+ }, [agent?.id, editContext, sanitizeAgentMetadata]);
4961
+ useEffect(() => {
4962
+ return () => {
4963
+ if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
4964
+ return;
4965
+ }
4966
+ void releaseAgentBrowser({
4967
+ agentId: agent.id,
4968
+ sessionId: editContext.sessionId,
4969
+ terminalInstanceId: dialogTerminalInstanceIdRef.current,
4970
+ }).catch((error) => {
4971
+ console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
4972
+ });
4973
+ };
4974
+ }, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
4688
4975
  // Stop current execution/stream safely
4689
4976
  const handleStop = useCallback(async () => {
4690
4977
  try {
@@ -4817,8 +5104,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4817
5104
  // We only want these global thinking dots if the last message was from the user
4818
5105
  // or if no messages exist yet (waiting for initial response).
4819
5106
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
4820
- if (isExecuting && lastMessage?.role === "assistant")
5107
+ if (isExecuting &&
5108
+ lastMessage?.role === "assistant" &&
5109
+ !lastMessage.isCompleted) {
4821
5110
  return false;
5111
+ }
4822
5112
  // Existing check for uncompleted assistant messages
4823
5113
  const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
4824
5114
  if (hasActiveStreamingMessage)
@@ -4897,12 +5187,46 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4897
5187
  };
4898
5188
  const renderErrorBanner = () => {
4899
5189
  const currentAgent = agent || agentStub;
4900
- const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
4901
- const errorMessage = currentAgent?.statusMessage;
4902
- if (!isErrorStatus || !errorMessage)
5190
+ const isErrorStatus = currentAgent?.status === "error";
5191
+ const errorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
5192
+ if (!errorMessage)
4903
5193
  return null;
4904
- return (_jsx("div", { className: "m-3 rounded border border-red-300 bg-red-50 p-3 text-[11px] text-red-900", 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 })] })] }) }));
5194
+ 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 })] })] }) }));
4905
5195
  };
5196
+ const renderBrowserClaimBanner = (variant = "inline") => {
5197
+ if (!agent?.id || !editContext?.sessionId)
5198
+ return null;
5199
+ if (!isClaimedByCurrentSession &&
5200
+ !isClaimedByAnotherBrowser &&
5201
+ !isPendingBrowserCaptureWait) {
5202
+ return null;
5203
+ }
5204
+ const label = isClaimedByCurrentSession
5205
+ ? "Attached to this browser"
5206
+ : isClaimedByAnotherBrowser
5207
+ ? "Attached in another browser"
5208
+ : "No browser attached";
5209
+ const description = isClaimedByCurrentSession
5210
+ ? "This browser will handle page screenshot and DOM capture requests for the agent."
5211
+ : isClaimedByAnotherBrowser
5212
+ ? "Capture requests will stay with the other browser until you take over control here."
5213
+ : "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
5214
+ const bannerClassName = cn("rounded border border-blue-200 bg-blue-50 p-3 text-[11px] text-blue-900", variant === "fixed" ? "mx-3 mt-3 mb-2 shrink-0" : "m-3", isClaimedByCurrentSession && "border-blue-300");
5215
+ return (_jsx("div", { className: bannerClassName, children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold", children: label }), _jsx("div", { className: "mt-1 text-blue-800", children: description })] }), _jsx("div", { className: "flex shrink-0 items-center gap-2", children: isClaimedByCurrentSession ? (_jsx("button", { type: "button", className: "rounded border border-blue-300 bg-white px-2 py-1 text-[11px] font-medium text-blue-900 disabled:cursor-not-allowed disabled:opacity-60", disabled: isBrowserClaimMutationPending, onClick: () => {
5216
+ void handleReleaseBrowser();
5217
+ }, children: "Release" })) : (_jsx("button", { type: "button", className: "rounded border border-blue-300 bg-white px-2 py-1 text-[11px] font-medium text-blue-900 disabled:cursor-not-allowed disabled:opacity-60", disabled: isBrowserClaimMutationPending, onClick: () => {
5218
+ void handleClaimBrowser(isClaimedByAnotherBrowser);
5219
+ }, children: isClaimedByAnotherBrowser
5220
+ ? "Take over browser control"
5221
+ : "Attach to this browser" })) })] }) }));
5222
+ };
5223
+ const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5224
+ const inlineBrowserClaimBanner = null;
5225
+ useEffect(() => {
5226
+ if (agent?.status !== "waitingForInput") {
5227
+ setPendingBrowserCaptureDialogType(null);
5228
+ }
5229
+ }, [agent?.status]);
4906
5230
  const renderInlineDialogContent = () => {
4907
5231
  if (!activeInlineDialog)
4908
5232
  return null;
@@ -4962,10 +5286,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4962
5286
  const summaryOperations = latestSummaryAssistantGroup
4963
5287
  ? getOperationsForMessageGroup(summaryMessages, agentOperations)
4964
5288
  : [];
4965
- return (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error &&
5289
+ 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 &&
4966
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 })] })] }) })), 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: {
4967
- __html: activeProfile.svgIcon,
4968
- } })) : (_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" })] })] }) })), 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, error: error || undefined, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5291
+ __html: sanitizeSvg(activeProfile.svgIcon),
5292
+ } })) : (_jsx(SecretAgentIcon, { size: 64, strokeWidth: 1, className: "text-gray-400" })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400" })] })] }) })), inlineBrowserClaimBanner, renderErrorBanner(), inlineDialog ? (inlineDialog) : latestSummaryAssistantGroup ? (_jsx("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: _jsx(AiResponseMessage, { messages: summaryMessages, finished: !latestSummaryAssistantGroup.isLastGroup || !isExecuting, editOperations: summaryOperations, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
4969
5293
  activeProfile?.displayTitle ||
4970
5294
  activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
4971
5295
  const text = (action.prompt ||
@@ -4989,7 +5313,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4989
5313
  shouldShowThinkingDots &&
4990
5314
  !inlineDialog &&
4991
5315
  !latestSummaryAssistantGroup && (_jsxs("div", { className: "flex gap-3 px-4 py-3", "data-testid": "agent-thinking-dots", children: [_jsx("div", { className: "shrink-0", children: activeProfile?.svgIcon ? (_jsx("div", { className: "text-gray-2 flex h-6 w-6 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
4992
- __html: activeProfile.svgIcon,
5316
+ __html: sanitizeSvg(activeProfile.svgIcon),
4993
5317
  } })) : (_jsx(SecretAgentIcon, { size: 20, strokeWidth: 1, className: "text-gray-2" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-dark text-xs font-medium", children: activeProfile?.agentName ||
4994
5318
  activeProfile?.displayTitle ||
4995
5319
  activeProfile?.name ||
@@ -5076,7 +5400,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5076
5400
  })()
5077
5401
  : null;
5078
5402
  const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
5079
- const fullModeUpperContent = (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [_jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error && !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 })] })] }) })), 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) => {
5403
+ 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) => {
5080
5404
  setPrompt(p);
5081
5405
  // Use setTimeout to ensure state is updated before submission
5082
5406
  setTimeout(() => {
@@ -5090,8 +5414,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5090
5414
  }
5091
5415
  }, 0);
5092
5416
  } })) })), 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: {
5093
- __html: activeProfile.svgIcon,
5094
- } })) : (_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" })] })] }) })), renderErrorBanner(), _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
5417
+ __html: sanitizeSvg(activeProfile.svgIcon),
5418
+ } })) : (_jsx(SecretAgentIcon, { size: 64, strokeWidth: 1, className: "text-gray-400" })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("span", { className: "h-2 w-2 animate-bounce rounded-full bg-gray-400" })] })] }) })), inlineBrowserClaimBanner, renderErrorBanner(), _jsxs("div", { className: "space-y-0 divide-y divide-gray-100 select-text", children: [(() => {
5095
5419
  const groups = groupConsecutiveMessages(messages);
5096
5420
  return groups.map((group, groupIndex) => {
5097
5421
  const isLastGroup = groupIndex === groups.length - 1;
@@ -5099,6 +5423,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5099
5423
  // Render user message
5100
5424
  return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
5101
5425
  }
5426
+ else if (group.type === "heartbeat" && group.messages[0]) {
5427
+ return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
5428
+ }
5102
5429
  else {
5103
5430
  // Render bundled assistant messages
5104
5431
  // Check if this group contains any streaming message
@@ -5115,7 +5442,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5115
5442
  }
5116
5443
  const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
5117
5444
  const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
5118
- return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, error: error || undefined, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5445
+ return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5119
5446
  activeProfile?.displayTitle ||
5120
5447
  activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5121
5448
  const text = (action.prompt ||
@@ -5161,7 +5488,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5161
5488
  }
5162
5489
  });
5163
5490
  })(), shouldShowThinkingDots && (_jsxs("div", { className: "flex gap-3 px-4 py-3", "data-testid": "agent-thinking-dots", children: [_jsx("div", { className: "shrink-0", children: activeProfile?.svgIcon ? (_jsx("div", { className: "text-gray-2 flex h-6 w-6 items-center justify-center [&>svg]:h-full [&>svg]:w-full", dangerouslySetInnerHTML: {
5164
- __html: activeProfile.svgIcon,
5491
+ __html: sanitizeSvg(activeProfile.svgIcon),
5165
5492
  } })) : (_jsx(SecretAgentIcon, { size: 20, strokeWidth: 1, className: "text-gray-2" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx("span", { className: "text-dark text-xs font-medium", children: activeProfile?.agentName ||
5166
5493
  activeProfile?.displayTitle ||
5167
5494
  activeProfile?.name ||
@@ -5220,10 +5547,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5220
5547
  if (qp.data) {
5221
5548
  try {
5222
5549
  const parsed = JSON.parse(qp.data);
5223
- triggerName =
5224
- parsed?.TriggerName?.trim() ||
5225
- parsed?.triggerName?.trim() ||
5226
- "";
5550
+ triggerName = parsed?.triggerName?.trim() || "";
5227
5551
  }
5228
5552
  catch {
5229
5553
  // Ignore invalid JSON metadata and render as regular queued prompt.
@@ -5337,13 +5661,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5337
5661
  return null;
5338
5662
  return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
5339
5663
  ? "justify-end"
5340
- : "justify-between"), children: [!hideBottomControls && !simpleMode && !isInPlaceholderMode && (_jsxs("div", { className: "flex flex-wrap items-center justify-start gap-2", children: [_jsx(Select, { size: "xs", maxWidth: 240, className: cn("h-5 w-auto min-w-[95px] rounded border px-1.5 text-[11px] font-normal", mode === "read-only"
5664
+ : "justify-between"), children: [!hideBottomControls && !simpleMode && !isInPlaceholderMode && (_jsxs("div", { className: "flex flex-wrap items-center justify-start gap-2", children: [_jsx(Select, { "data-testid": "agent-mode-selector", size: "xs", maxWidth: 240, className: cn("h-5 w-auto min-w-[95px] rounded border px-1.5 text-[11px] font-normal", mode === "read-only"
5341
5665
  ? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
5342
5666
  : mode === "supervised"
5343
5667
  ? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
5344
5668
  : "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
5345
5669
  const nextMode = val || "supervised";
5346
- setMode(nextMode);
5347
5670
  const current = agentMetadata || {};
5348
5671
  const nextMeta = {
5349
5672
  ...current,
@@ -5351,6 +5674,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5351
5674
  };
5352
5675
  try {
5353
5676
  if (!agent?.id || agent.status === "new") {
5677
+ setMode(nextMode);
5354
5678
  setAgentMetadata(nextMeta);
5355
5679
  pendingSettingsRef.current = {
5356
5680
  ...(pendingSettingsRef.current || {}),
@@ -5358,13 +5682,18 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5358
5682
  };
5359
5683
  return;
5360
5684
  }
5361
- await updateAgentSettings(agent.id, {
5685
+ const result = await updateAgentSettings(agent.id, {
5362
5686
  mode: nextMode,
5363
5687
  });
5688
+ if (result.success === false || result.updates?.mode === false) {
5689
+ throw new Error("Mode change was not applied");
5690
+ }
5691
+ setMode(nextMode);
5364
5692
  setAgentMetadata(nextMeta);
5365
5693
  setAgent((prev) => prev
5366
5694
  ? {
5367
5695
  ...prev,
5696
+ mode: nextMode,
5368
5697
  metadata: JSON.stringify(nextMeta),
5369
5698
  }
5370
5699
  : prev);
@@ -5411,6 +5740,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5411
5740
  setAgent((prev) => prev
5412
5741
  ? {
5413
5742
  ...prev,
5743
+ profileId: nextProfile.id,
5744
+ profileName: nextProfile.name,
5414
5745
  metadata: JSON.stringify({
5415
5746
  ...(agentMetadata || {}),
5416
5747
  profile: nextProfile.name,
@@ -5475,15 +5806,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5475
5806
  skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
5476
5807
  !skillsError &&
5477
5808
  profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
5478
- ? "All allowed skills are selected"
5479
- : "No skills available for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
5809
+ ? "All available/allowed skills are selected"
5810
+ : "No available or allowed skills for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
5480
5811
  const skill = selectedSkills.find((s) => s.id === skillId);
5481
5812
  return (_jsxs("div", { className: "inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-100 px-1.5 py-0.5 text-[10px] text-gray-700", children: [_jsx("span", { children: skill?.name || skillId }), _jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", title: "Open skill item", "aria-label": `Open ${skill?.name || skillId}`, onClick: () => {
5482
5813
  void handleOpenSkillItem(skillId);
5483
- }, children: _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }), backendAssignedSkillSet.has(skillId.toLowerCase()) ? (_jsx("span", { className: "text-[9px] text-gray-500", children: "auto" })) : (_jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", onClick: () => {
5814
+ }, children: _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 }) }), autoAssignedSkillSet.has(skillId.toLowerCase()) ? (_jsx("span", { className: "text-[9px] text-gray-500", children: "auto" })) : (_jsx("button", { type: "button", className: "rounded p-0.5 text-gray-500 hover:bg-gray-200 hover:text-gray-700", onClick: () => {
5484
5815
  void handleRemoveSkill(skillId);
5485
5816
  }, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
5486
- }) }))] }), _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) => {
5817
+ }) }))] }), _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: [isNewAgent && (_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: isNewAgent
5818
+ ? "No available tools for this profile and mode"
5819
+ : "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) => {
5487
5820
  const sourceLabel = formatAllowanceSource(allowance.source);
5488
5821
  const pathLabel = "itemPath" in allowance
5489
5822
  ? allowance.itemPath
@@ -5496,10 +5829,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5496
5829
  ]
5497
5830
  .filter(Boolean)
5498
5831
  .join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
5499
- })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "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) => {
5832
+ })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
5833
+ ? "Allowances are shown after the agent is created"
5834
+ : "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) => {
5500
5835
  const filterText = (sub.filter || "").trim();
5501
5836
  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));
5502
- }) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "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: () => {
5837
+ }) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isNewAgent
5838
+ ? "Subscribed triggers are shown after the agent is created"
5839
+ : "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: () => {
5503
5840
  setPrompt(p.prompt);
5504
5841
  setShowPredefined(false);
5505
5842
  if (textareaRef.current)