@parhelia/core 0.1.12585 → 0.1.12602

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 (584) hide show
  1. package/dist/agents-view/AgentCard.d.ts +4 -6
  2. package/dist/agents-view/AgentCard.js +24 -143
  3. package/dist/agents-view/AgentCard.js.map +1 -1
  4. package/dist/agents-view/AgentsInbox.d.ts +1 -1
  5. package/dist/agents-view/AgentsInbox.js +92 -7
  6. package/dist/agents-view/AgentsInbox.js.map +1 -1
  7. package/dist/agents-view/AgentsTitlebar.js +2 -3
  8. package/dist/agents-view/AgentsTitlebar.js.map +1 -1
  9. package/dist/agents-view/AgentsView.d.ts +7 -6
  10. package/dist/agents-view/AgentsView.js +99 -191
  11. package/dist/agents-view/AgentsView.js.map +1 -1
  12. package/dist/agents-view/AgentsWorkspaceView.d.ts +6 -2
  13. package/dist/agents-view/AgentsWorkspaceView.js +113 -266
  14. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  15. package/dist/agents-view/ProfileAgentsGroup.d.ts +1 -2
  16. package/dist/agents-view/ProfileAgentsGroup.js +3 -4
  17. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  18. package/dist/components/ActionButton.d.ts +1 -1
  19. package/dist/components/ActionButton.js.map +1 -1
  20. package/dist/components/FilterInput.d.ts +1 -1
  21. package/dist/components/FilterInput.js +1 -1
  22. package/dist/components/FilterInput.js.map +1 -1
  23. package/dist/components/ui/LanguageSelector.js +4 -2
  24. package/dist/components/ui/LanguageSelector.js.map +1 -1
  25. package/dist/components/ui/PlaceholderInput.js +3 -3
  26. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  27. package/dist/components/ui/PlaceholderInputTypes.js +1 -1
  28. package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
  29. package/dist/components/ui/alert-dialog.d.ts +1 -1
  30. package/dist/components/ui/alert-dialog.js +10 -6
  31. package/dist/components/ui/alert-dialog.js.map +1 -1
  32. package/dist/components/ui/button.d.ts +4 -4
  33. package/dist/components/ui/button.js +1 -4
  34. package/dist/components/ui/button.js.map +1 -1
  35. package/dist/components/ui/context-menu.d.ts +1 -1
  36. package/dist/components/ui/context-menu.js +4 -12
  37. package/dist/components/ui/context-menu.js.map +1 -1
  38. package/dist/components/ui/copy-button.d.ts +1 -2
  39. package/dist/components/ui/copy-button.js +2 -2
  40. package/dist/components/ui/copy-button.js.map +1 -1
  41. package/dist/components/ui/dialog.d.ts +1 -1
  42. package/dist/components/ui/dialog.js +126 -21
  43. package/dist/components/ui/dialog.js.map +1 -1
  44. package/dist/components/ui/input.d.ts +1 -1
  45. package/dist/components/ui/input.js +3 -5
  46. package/dist/components/ui/input.js.map +1 -1
  47. package/dist/components/ui/paste-button.d.ts +1 -2
  48. package/dist/components/ui/paste-button.js +2 -2
  49. package/dist/components/ui/paste-button.js.map +1 -1
  50. package/dist/components/ui/popover.js +9 -1
  51. package/dist/components/ui/popover.js.map +1 -1
  52. package/dist/components/ui/select.js +1 -1
  53. package/dist/components/ui/select.js.map +1 -1
  54. package/dist/components/ui/styled-dialog-title.js +1 -1
  55. package/dist/components/ui/styled-dialog-title.js.map +1 -1
  56. package/dist/components/ui/tabs.d.ts +1 -1
  57. package/dist/components/ui/tabs.js +11 -4
  58. package/dist/components/ui/tabs.js.map +1 -1
  59. package/dist/config/config.d.ts +2 -4
  60. package/dist/config/config.js +70 -250
  61. package/dist/config/config.js.map +1 -1
  62. package/dist/config/types/workspace.d.ts +0 -6
  63. package/dist/config/types.d.ts +12 -63
  64. package/dist/config/types.js.map +1 -1
  65. package/dist/editor/ComponentInfo.d.ts +4 -0
  66. package/dist/editor/ComponentInfo.js +41 -0
  67. package/dist/editor/ComponentInfo.js.map +1 -0
  68. package/dist/editor/ConfirmationDialog.js +4 -20
  69. package/dist/editor/ConfirmationDialog.js.map +1 -1
  70. package/dist/editor/ContentTree.d.ts +1 -2
  71. package/dist/editor/ContentTree.js +32 -93
  72. package/dist/editor/ContentTree.js.map +1 -1
  73. package/dist/editor/Editor.js +22 -87
  74. package/dist/editor/Editor.js.map +1 -1
  75. package/dist/editor/FieldHistory.js +36 -84
  76. package/dist/editor/FieldHistory.js.map +1 -1
  77. package/dist/editor/FieldListField.js +9 -21
  78. package/dist/editor/FieldListField.js.map +1 -1
  79. package/dist/editor/FieldListFieldWithFallbacks.js +2 -23
  80. package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
  81. package/dist/editor/GlobalMenuBar.js +2 -29
  82. package/dist/editor/GlobalMenuBar.js.map +1 -1
  83. package/dist/editor/ImageEditor.js +2 -5
  84. package/dist/editor/ImageEditor.js.map +1 -1
  85. package/dist/editor/ItemInfo.js +1 -36
  86. package/dist/editor/ItemInfo.js.map +1 -1
  87. package/dist/editor/LinkEditorDialog.js +0 -3
  88. package/dist/editor/LinkEditorDialog.js.map +1 -1
  89. package/dist/editor/MainLayout.d.ts +2 -0
  90. package/dist/editor/MainLayout.js +8 -65
  91. package/dist/editor/MainLayout.js.map +1 -1
  92. package/dist/editor/MigrationsView.js +5 -29
  93. package/dist/editor/MigrationsView.js.map +1 -1
  94. package/dist/editor/MobileLayout.js +12 -37
  95. package/dist/editor/MobileLayout.js.map +1 -1
  96. package/dist/editor/PictureCropper.js +45 -54
  97. package/dist/editor/PictureCropper.js.map +1 -1
  98. package/dist/editor/PictureEditor.js +15 -17
  99. package/dist/editor/PictureEditor.js.map +1 -1
  100. package/dist/editor/QuickItemSwitcher.js +63 -37
  101. package/dist/editor/QuickItemSwitcher.js.map +1 -1
  102. package/dist/editor/SetupWizard.js +12 -52
  103. package/dist/editor/SetupWizard.js.map +1 -1
  104. package/dist/editor/Titlebar.js +2 -7
  105. package/dist/editor/Titlebar.js.map +1 -1
  106. package/dist/editor/ai/AgentCostDisplay.d.ts +0 -1
  107. package/dist/editor/ai/AgentCostDisplay.js +1 -1
  108. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  109. package/dist/editor/ai/AgentDocumentList.js +14 -32
  110. package/dist/editor/ai/AgentDocumentList.js.map +1 -1
  111. package/dist/editor/ai/AgentGreeting.js +5 -6
  112. package/dist/editor/ai/AgentGreeting.js.map +1 -1
  113. package/dist/editor/ai/AgentProfileSelector.js +1 -2
  114. package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
  115. package/dist/editor/ai/AgentStatusBadge.d.ts +5 -0
  116. package/dist/editor/ai/AgentStatusBadge.js +65 -67
  117. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  118. package/dist/editor/ai/AgentTerminal.d.ts +2 -14
  119. package/dist/editor/ai/AgentTerminal.js +572 -2508
  120. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  121. package/dist/editor/ai/AgentTerminalStatusBar.d.ts +4 -9
  122. package/dist/editor/ai/AgentTerminalStatusBar.js +56 -481
  123. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  124. package/dist/editor/ai/Agents.js +113 -161
  125. package/dist/editor/ai/Agents.js.map +1 -1
  126. package/dist/editor/ai/AiResponseMessage.d.ts +1 -10
  127. package/dist/editor/ai/AiResponseMessage.js +26 -267
  128. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  129. package/dist/editor/ai/ContextInfoBar.d.ts +3 -2
  130. package/dist/editor/ai/ContextInfoBar.js +7 -64
  131. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  132. package/dist/editor/ai/EditOperationsPanel.d.ts +2 -3
  133. package/dist/editor/ai/EditOperationsPanel.js +78 -21
  134. package/dist/editor/ai/EditOperationsPanel.js.map +1 -1
  135. package/dist/editor/ai/GuidanceOverlay.js +11 -17
  136. package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
  137. package/dist/editor/ai/HelpTerminal.d.ts +5 -0
  138. package/dist/editor/ai/HelpTerminal.js +166 -0
  139. package/dist/editor/ai/HelpTerminal.js.map +1 -0
  140. package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
  141. package/dist/editor/ai/InlineAiDialog.js +192 -514
  142. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  143. package/dist/editor/ai/InlineAiTrigger.js +12 -115
  144. package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
  145. package/dist/editor/ai/MediaImage.js +8 -40
  146. package/dist/editor/ai/MediaImage.js.map +1 -1
  147. package/dist/editor/ai/SpawnedAgentsPanel.js +12 -10
  148. package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
  149. package/dist/editor/ai/ToolCallDisplay.d.ts +2 -22
  150. package/dist/editor/ai/ToolCallDisplay.js +202 -614
  151. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  152. package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +8 -1
  153. package/dist/editor/ai/dialogs/AgentDialogHandler.js +42 -379
  154. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  155. package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +1 -5
  156. package/dist/editor/ai/dialogs/QuestionnaireInline.js +60 -628
  157. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  158. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +0 -117
  159. package/dist/editor/ai/dialogs/agentDialogTypes.js +0 -2
  160. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  161. package/dist/editor/ai/types.d.ts +1 -3
  162. package/dist/editor/ai/useAgentStatus.d.ts +1 -2
  163. package/dist/editor/ai/useAgentStatus.js +100 -90
  164. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  165. package/dist/editor/ai/useInlineAiPosition.js +5 -45
  166. package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
  167. package/dist/editor/client/AboutDialog.js +2 -4
  168. package/dist/editor/client/AboutDialog.js.map +1 -1
  169. package/dist/editor/client/EditorShell.d.ts +1 -4
  170. package/dist/editor/client/EditorShell.js +258 -853
  171. package/dist/editor/client/EditorShell.js.map +1 -1
  172. package/dist/editor/client/editContext.d.ts +19 -33
  173. package/dist/editor/client/editContext.js.map +1 -1
  174. package/dist/editor/client/helpers.js +0 -6
  175. package/dist/editor/client/helpers.js.map +1 -1
  176. package/dist/editor/client/hooks/useEditorUrlSync.js +2 -1
  177. package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
  178. package/dist/editor/client/hooks/useEditorWebSocket.d.ts +0 -10
  179. package/dist/editor/client/hooks/useEditorWebSocket.js +14 -209
  180. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  181. package/dist/editor/client/hooks/useQuota.d.ts +0 -8
  182. package/dist/editor/client/hooks/useQuota.js.map +1 -1
  183. package/dist/editor/client/hooks/useSocketMessageHandler.js +15 -73
  184. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  185. package/dist/editor/client/itemsRepository.js +6 -10
  186. package/dist/editor/client/itemsRepository.js.map +1 -1
  187. package/dist/editor/client/operations.d.ts +3 -6
  188. package/dist/editor/client/operations.js +30 -208
  189. package/dist/editor/client/operations.js.map +1 -1
  190. package/dist/editor/client/pageModelBuilder.js +31 -4
  191. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  192. package/dist/editor/client/ui/DevModeIndicator.js +2 -2
  193. package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
  194. package/dist/editor/client/ui/EditorChrome.d.ts +6 -0
  195. package/dist/editor/client/ui/EditorChrome.js +72 -55
  196. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  197. package/dist/editor/client/ui/FullscreenControls.js +3 -5
  198. package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
  199. package/dist/editor/commands/commands.d.ts +1 -11
  200. package/dist/editor/commands/commands.js +1 -12
  201. package/dist/editor/commands/commands.js.map +1 -1
  202. package/dist/editor/commands/componentCommands.js +55 -109
  203. package/dist/editor/commands/componentCommands.js.map +1 -1
  204. package/dist/editor/commands/customCommandConverter.d.ts +1 -8
  205. package/dist/editor/commands/customCommandConverter.js +5 -35
  206. package/dist/editor/commands/customCommandConverter.js.map +1 -1
  207. package/dist/editor/commands/handlers/agentHandler.js +1 -2
  208. package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
  209. package/dist/editor/commands/itemCommands.d.ts +0 -3
  210. package/dist/editor/commands/itemCommands.js +10 -93
  211. package/dist/editor/commands/itemCommands.js.map +1 -1
  212. package/dist/editor/commands/undo.d.ts +15 -9
  213. package/dist/editor/commands/undo.js +0 -24
  214. package/dist/editor/commands/undo.js.map +1 -1
  215. package/dist/editor/context-menu/InsertMenu.js +39 -83
  216. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  217. package/dist/editor/field-types/MultiLineText.js +1 -1
  218. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  219. package/dist/editor/field-types/RawEditor.js +1 -1
  220. package/dist/editor/field-types/ReactQuill.d.ts +125 -0
  221. package/dist/editor/field-types/ReactQuill.js +385 -0
  222. package/dist/editor/field-types/ReactQuill.js.map +1 -0
  223. package/dist/editor/field-types/RichTextEditor.js +5 -13
  224. package/dist/editor/field-types/RichTextEditor.js.map +1 -1
  225. package/dist/editor/field-types/RichTextEditorComponent.js +3 -37
  226. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  227. package/dist/editor/field-types/SingleLineText.js +1 -1
  228. package/dist/editor/field-types/TreeListEditor.js +2 -3
  229. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  230. package/dist/editor/field-types/richtext/components/ReactSlate.css +5 -23
  231. package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +0 -2
  232. package/dist/editor/field-types/richtext/components/ReactSlate.js +4 -28
  233. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  234. package/dist/editor/field-types/richtext/components/ToolbarButton.js +2 -4
  235. package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
  236. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +0 -13
  237. package/dist/editor/field-types/richtext/contextMenuFactory.js +24 -181
  238. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
  239. package/dist/editor/field-types/richtext/types.d.ts +0 -2
  240. package/dist/editor/field-types/richtext/types.js.map +1 -1
  241. package/dist/editor/field-types/richtext/utils/plugins.js +0 -4
  242. package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
  243. package/dist/editor/field-types/textContextMenuFactory.js +2 -3
  244. package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
  245. package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -4
  246. package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
  247. package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
  248. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  249. package/dist/editor/media-selector/MediaSelector.js +1 -7
  250. package/dist/editor/media-selector/MediaSelector.js.map +1 -1
  251. package/dist/editor/media-selector/TreeSelector.js +35 -40
  252. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  253. package/dist/editor/menubar/ActiveUsers.js +1 -1
  254. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  255. package/dist/editor/menubar/GenericToolbar.js +2 -4
  256. package/dist/editor/menubar/GenericToolbar.js.map +1 -1
  257. package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
  258. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  259. package/dist/editor/menubar/PageSelector.js +147 -26
  260. package/dist/editor/menubar/PageSelector.js.map +1 -1
  261. package/dist/editor/menubar/Separator.js +1 -1
  262. package/dist/editor/menubar/VersionSelector.js +4 -2
  263. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  264. package/dist/editor/menubar/WorkflowButton.js +12 -39
  265. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  266. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +38 -16
  267. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
  268. package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
  269. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  270. package/dist/editor/menubar/toolbar-sections/HelpButton.js +0 -1
  271. package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
  272. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +10 -6
  273. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +220 -597
  274. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  275. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +2 -13
  276. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  277. package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -42
  278. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  279. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  280. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  281. package/dist/editor/page-editor-chrome/InlineEditor.js +48 -97
  282. package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
  283. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +17 -38
  284. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  285. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +11 -17
  286. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  287. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
  288. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  289. package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
  290. package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
  291. package/dist/editor/page-viewer/EditorForm.js +11 -69
  292. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  293. package/dist/editor/page-viewer/MiniMap.d.ts +4 -2
  294. package/dist/editor/page-viewer/MiniMap.js +28 -91
  295. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  296. package/dist/editor/page-viewer/PageViewer.d.ts +1 -3
  297. package/dist/editor/page-viewer/PageViewer.js +19 -92
  298. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  299. package/dist/editor/page-viewer/PageViewerFrame.d.ts +1 -2
  300. package/dist/editor/page-viewer/PageViewerFrame.js +115 -348
  301. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  302. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +49 -114
  303. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  304. package/dist/editor/page-viewer/pageViewContext.d.ts +0 -1
  305. package/dist/editor/page-viewer/pageViewContext.js +14 -51
  306. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  307. package/dist/editor/pageModel.d.ts +1 -14
  308. package/dist/editor/reviews/Comment.d.ts +1 -2
  309. package/dist/editor/reviews/Comment.js +15 -92
  310. package/dist/editor/reviews/Comment.js.map +1 -1
  311. package/dist/editor/reviews/CommentDisplayPopover.js +5 -70
  312. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  313. package/dist/editor/reviews/CommentView.d.ts +1 -3
  314. package/dist/editor/reviews/CommentView.js +6 -26
  315. package/dist/editor/reviews/CommentView.js.map +1 -1
  316. package/dist/editor/reviews/Comments.js +75 -140
  317. package/dist/editor/reviews/Comments.js.map +1 -1
  318. package/dist/editor/reviews/CreateReviewDialog.js +177 -281
  319. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  320. package/dist/editor/reviews/DecisionsMatrix.js +25 -96
  321. package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
  322. package/dist/editor/reviews/DiffView.js +14 -7
  323. package/dist/editor/reviews/DiffView.js.map +1 -1
  324. package/dist/editor/reviews/EditReviewSettingsDialog.js +4 -6
  325. package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
  326. package/dist/editor/reviews/MultiReviewManager.js +3 -25
  327. package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
  328. package/dist/editor/reviews/PagesPanel.js +15 -31
  329. package/dist/editor/reviews/PagesPanel.js.map +1 -1
  330. package/dist/editor/reviews/PreviewInfo.js +4 -1
  331. package/dist/editor/reviews/PreviewInfo.js.map +1 -1
  332. package/dist/editor/reviews/ReviewCard.js +7 -13
  333. package/dist/editor/reviews/ReviewCard.js.map +1 -1
  334. package/dist/editor/reviews/ReviewDetail.js +2 -3
  335. package/dist/editor/reviews/ReviewDetail.js.map +1 -1
  336. package/dist/editor/reviews/ReviewsList.js +3 -7
  337. package/dist/editor/reviews/ReviewsList.js.map +1 -1
  338. package/dist/editor/reviews/SuggestedEdit.js +3 -34
  339. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  340. package/dist/editor/reviews/SuggestionDisplayPopover.js +5 -31
  341. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  342. package/dist/editor/reviews/commentAi.js +6 -25
  343. package/dist/editor/reviews/commentAi.js.map +1 -1
  344. package/dist/editor/reviews/reviewCommands.js +1 -4
  345. package/dist/editor/reviews/reviewCommands.js.map +1 -1
  346. package/dist/editor/reviews/useMultiReview.js +2 -2
  347. package/dist/editor/reviews/useMultiReview.js.map +1 -1
  348. package/dist/editor/reviews/useReviews.d.ts +3 -4
  349. package/dist/editor/reviews/useReviews.js +32 -21
  350. package/dist/editor/reviews/useReviews.js.map +1 -1
  351. package/dist/editor/services/agentService.d.ts +5 -240
  352. package/dist/editor/services/agentService.js +39 -299
  353. package/dist/editor/services/agentService.js.map +1 -1
  354. package/dist/editor/services/aiService.d.ts +1 -57
  355. package/dist/editor/services/aiService.js +6 -79
  356. package/dist/editor/services/aiService.js.map +1 -1
  357. package/dist/editor/services/contentService.d.ts +3 -6
  358. package/dist/editor/services/contentService.js +12 -13
  359. package/dist/editor/services/contentService.js.map +1 -1
  360. package/dist/editor/services/editService.d.ts +1 -52
  361. package/dist/editor/services/editService.js +2 -94
  362. package/dist/editor/services/editService.js.map +1 -1
  363. package/dist/editor/services/indexService.js +1 -1
  364. package/dist/editor/services/indexService.js.map +1 -1
  365. package/dist/editor/services/reviewsService.d.ts +6 -3
  366. package/dist/editor/services/reviewsService.js +11 -2
  367. package/dist/editor/services/reviewsService.js.map +1 -1
  368. package/dist/editor/services/serviceHelper.d.ts +1 -2
  369. package/dist/editor/services/serviceHelper.js +20 -112
  370. package/dist/editor/services/serviceHelper.js.map +1 -1
  371. package/dist/editor/services/systemService.d.ts +1 -2
  372. package/dist/editor/services/systemService.js +0 -3
  373. package/dist/editor/services/systemService.js.map +1 -1
  374. package/dist/editor/services-server/api.d.ts +2 -1
  375. package/dist/editor/services-server/api.js +6 -11
  376. package/dist/editor/services-server/api.js.map +1 -1
  377. package/dist/editor/services-server/graphQL.d.ts +29 -0
  378. package/dist/editor/services-server/graphQL.js +53 -0
  379. package/dist/editor/services-server/graphQL.js.map +1 -0
  380. package/dist/editor/settings/About.js +3 -317
  381. package/dist/editor/settings/About.js.map +1 -1
  382. package/dist/editor/settings/AllAgentsPanel.d.ts +5 -0
  383. package/dist/editor/settings/AllAgentsPanel.js +139 -0
  384. package/dist/editor/settings/AllAgentsPanel.js.map +1 -0
  385. package/dist/editor/settings/IndexOverview.js +1 -3
  386. package/dist/editor/settings/IndexOverview.js.map +1 -1
  387. package/dist/editor/settings/LatestFeedback.d.ts +1 -0
  388. package/dist/editor/settings/LatestFeedback.js +136 -0
  389. package/dist/editor/settings/LatestFeedback.js.map +1 -0
  390. package/dist/editor/settings/QuotaInfo.js +4 -210
  391. package/dist/editor/settings/QuotaInfo.js.map +1 -1
  392. package/dist/editor/settings/SettingsView.js +23 -25
  393. package/dist/editor/settings/SettingsView.js.map +1 -1
  394. package/dist/editor/settings/Setup.d.ts +1 -0
  395. package/dist/editor/settings/Setup.js +211 -0
  396. package/dist/editor/settings/Setup.js.map +1 -0
  397. package/dist/editor/settings/Status.js +6 -7
  398. package/dist/editor/settings/Status.js.map +1 -1
  399. package/dist/editor/settings/index/useIndexStatus.js +22 -23
  400. package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
  401. package/dist/editor/settings/panels/AgentsPanel.d.ts +4 -0
  402. package/dist/editor/settings/panels/AgentsPanel.js +121 -95
  403. package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
  404. package/dist/editor/settings/panels/DatabasePanel.d.ts +6 -0
  405. package/dist/editor/settings/panels/DatabasePanel.js +50 -0
  406. package/dist/editor/settings/panels/DatabasePanel.js.map +1 -0
  407. package/dist/editor/settings/panels/ModelsPanel.js +108 -324
  408. package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
  409. package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
  410. package/dist/editor/settings/panels/ProvidersPanel.js +59 -86
  411. package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
  412. package/dist/editor/settings/panels/SearchConfigPanel.js +6 -67
  413. package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
  414. package/dist/editor/settings/panels/StatusPanel.js +2 -7
  415. package/dist/editor/settings/panels/StatusPanel.js.map +1 -1
  416. package/dist/editor/settings/panels/index.d.ts +2 -3
  417. package/dist/editor/settings/panels/index.js +2 -3
  418. package/dist/editor/settings/panels/index.js.map +1 -1
  419. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
  420. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
  421. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
  422. package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +2 -0
  423. package/dist/editor/settings/setup-steps/AiSetupStep/index.js +21 -0
  424. package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +1 -0
  425. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -0
  426. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +233 -0
  427. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
  428. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
  429. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
  430. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
  431. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
  432. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
  433. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
  434. package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +1 -0
  435. package/dist/editor/settings/setup-steps/AiSetupStep/types.js +2 -0
  436. package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +1 -0
  437. package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +5 -0
  438. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +44 -0
  439. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +1 -0
  440. package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +2 -0
  441. package/dist/editor/settings/setup-steps/IndexSetupStep.js +36 -0
  442. package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +1 -0
  443. package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +2 -0
  444. package/dist/editor/settings/setup-steps/SettingsSetupStep.js +111 -0
  445. package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +1 -0
  446. package/dist/editor/settings/setup-steps/SetupOverview.d.ts +14 -0
  447. package/dist/editor/settings/setup-steps/SetupOverview.js +38 -0
  448. package/dist/editor/settings/setup-steps/SetupOverview.js.map +1 -0
  449. package/dist/editor/settings/status/coreStatusChecks.js +19 -124
  450. package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
  451. package/dist/editor/settings/status/useStartupChecks.d.ts +1 -3
  452. package/dist/editor/settings/status/useStartupChecks.js +5 -9
  453. package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
  454. package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +1 -2
  455. package/dist/editor/setup-wizard/steps/CompleteStep.js +1 -2
  456. package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
  457. package/dist/editor/sidebar/ComponentPalette.js +1 -2
  458. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  459. package/dist/editor/sidebar/ComponentTree.d.ts +1 -8
  460. package/dist/editor/sidebar/ComponentTree.js +69 -216
  461. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  462. package/dist/editor/sidebar/Debug.d.ts +1 -0
  463. package/dist/editor/sidebar/Debug.js +70 -0
  464. package/dist/editor/sidebar/Debug.js.map +1 -0
  465. package/dist/editor/sidebar/EditHistory.js +46 -22
  466. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  467. package/dist/editor/sidebar/Favorites.js +8 -4
  468. package/dist/editor/sidebar/Favorites.js.map +1 -1
  469. package/dist/editor/sidebar/GraphQL.d.ts +2 -0
  470. package/dist/editor/sidebar/GraphQL.js +234 -0
  471. package/dist/editor/sidebar/GraphQL.js.map +1 -0
  472. package/dist/editor/sidebar/LeftToolbar.d.ts +1 -0
  473. package/dist/editor/sidebar/LeftToolbar.js +12 -0
  474. package/dist/editor/sidebar/LeftToolbar.js.map +1 -0
  475. package/dist/editor/sidebar/MainContentTree.js +3 -4
  476. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  477. package/dist/editor/sidebar/NavigationSidebar.d.ts +4 -0
  478. package/dist/editor/sidebar/NavigationSidebar.js +254 -0
  479. package/dist/editor/sidebar/NavigationSidebar.js.map +1 -0
  480. package/dist/editor/sidebar/OperationItem.js +7 -21
  481. package/dist/editor/sidebar/OperationItem.js.map +1 -1
  482. package/dist/editor/sidebar/SidebarPanel.d.ts +1 -3
  483. package/dist/editor/sidebar/SidebarPanel.js +12 -44
  484. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  485. package/dist/editor/sidebar/SidebarStack.d.ts +1 -2
  486. package/dist/editor/sidebar/SidebarStack.js +3 -4
  487. package/dist/editor/sidebar/SidebarStack.js.map +1 -1
  488. package/dist/editor/sidebar/Validation.js +12 -24
  489. package/dist/editor/sidebar/Validation.js.map +1 -1
  490. package/dist/editor/sidebar/Workbox.js +3 -53
  491. package/dist/editor/sidebar/Workbox.js.map +1 -1
  492. package/dist/editor/sidebar/WorkspaceRail.d.ts +1 -0
  493. package/dist/editor/sidebar/WorkspaceRail.js +167 -56
  494. package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
  495. package/dist/editor/tree-indicators/GutterColumns.d.ts +1 -3
  496. package/dist/editor/tree-indicators/GutterColumns.js +5 -26
  497. package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
  498. package/dist/editor/tree-indicators/GutterContext.d.ts +0 -4
  499. package/dist/editor/tree-indicators/GutterContext.js +0 -23
  500. package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
  501. package/dist/editor/tree-indicators/GutterSelector.d.ts +5 -0
  502. package/dist/editor/tree-indicators/GutterSelector.js +91 -0
  503. package/dist/editor/tree-indicators/GutterSelector.js.map +1 -0
  504. package/dist/editor/tree-indicators/index.d.ts +1 -0
  505. package/dist/editor/tree-indicators/index.js +1 -0
  506. package/dist/editor/tree-indicators/index.js.map +1 -1
  507. package/dist/editor/tree-indicators/types.d.ts +1 -12
  508. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
  509. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
  510. package/dist/editor/ui/Icons.js +1 -1
  511. package/dist/editor/ui/Icons.js.map +1 -1
  512. package/dist/editor/ui/ItemNameDialogNew.d.ts +0 -2
  513. package/dist/editor/ui/ItemNameDialogNew.js +17 -33
  514. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  515. package/dist/editor/ui/ItemSearch.js +11 -7
  516. package/dist/editor/ui/ItemSearch.js.map +1 -1
  517. package/dist/editor/ui/SimpleIconButton.js +1 -1
  518. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  519. package/dist/editor/ui/SimpleTabs.d.ts +0 -1
  520. package/dist/editor/ui/SimpleTabs.js +25 -45
  521. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  522. package/dist/editor/ui/Splitter.d.ts +0 -1
  523. package/dist/editor/ui/Splitter.js +86 -102
  524. package/dist/editor/ui/Splitter.js.map +1 -1
  525. package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
  526. package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
  527. package/dist/editor/ui/TreeListSelector.d.ts +1 -6
  528. package/dist/editor/ui/TreeListSelector.js +2 -2
  529. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  530. package/dist/editor/utils/keyboardNavigation.d.ts +20 -6
  531. package/dist/editor/utils/keyboardNavigation.js +140 -48
  532. package/dist/editor/utils/keyboardNavigation.js.map +1 -1
  533. package/dist/editor/utils.js +9 -19
  534. package/dist/editor/utils.js.map +1 -1
  535. package/dist/editor/views/CompareView.d.ts +1 -3
  536. package/dist/editor/views/CompareView.js +5 -7
  537. package/dist/editor/views/CompareView.js.map +1 -1
  538. package/dist/editor/views/EditView.js +1 -1
  539. package/dist/editor/views/EditView.js.map +1 -1
  540. package/dist/editor/views/EditorSlot.js +34 -27
  541. package/dist/editor/views/EditorSlot.js.map +1 -1
  542. package/dist/editor/views/ItemEditor.js +3 -7
  543. package/dist/editor/views/ItemEditor.js.map +1 -1
  544. package/dist/editor/views/MediaFolderEditView.js +1 -1
  545. package/dist/editor/views/MediaFolderEditView.js.map +1 -1
  546. package/dist/editor/views/ParheliaView.js +6 -5
  547. package/dist/editor/views/ParheliaView.js.map +1 -1
  548. package/dist/editor/views/SingleEditView.d.ts +1 -2
  549. package/dist/editor/views/SingleEditView.js +8 -10
  550. package/dist/editor/views/SingleEditView.js.map +1 -1
  551. package/dist/editor/views/editorSlotContext.js +6 -35
  552. package/dist/editor/views/editorSlotContext.js.map +1 -1
  553. package/dist/index.d.ts +2 -16
  554. package/dist/index.js +0 -11
  555. package/dist/index.js.map +1 -1
  556. package/dist/revision.d.ts +2 -2
  557. package/dist/revision.js +2 -2
  558. package/dist/setup/services/setupWizardService.d.ts +14 -48
  559. package/dist/setup/services/setupWizardService.js +17 -52
  560. package/dist/setup/services/setupWizardService.js.map +1 -1
  561. package/dist/setup/wizard/steps/AddModelDialog.js +3 -12
  562. package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
  563. package/dist/setup/wizard/steps/ImportModelDialog.js +22 -46
  564. package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
  565. package/dist/splash-screen/ModernSplashScreen.js +32 -112
  566. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  567. package/dist/splash-screen/NewPage.js +50 -33
  568. package/dist/splash-screen/NewPage.js.map +1 -1
  569. package/dist/splash-screen/OpenPage.js +6 -2
  570. package/dist/splash-screen/OpenPage.js.map +1 -1
  571. package/dist/splash-screen/ParheliaAssistantChat.js +29 -12
  572. package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
  573. package/dist/splash-screen/ParheliaLogo.js +37 -87
  574. package/dist/splash-screen/ParheliaLogo.js.map +1 -1
  575. package/dist/splash-screen/RecentPages.js +3 -3
  576. package/dist/splash-screen/RecentPages.js.map +1 -1
  577. package/dist/tour/Tour.d.ts +1 -2
  578. package/dist/tour/Tour.js +75 -256
  579. package/dist/tour/Tour.js.map +1 -1
  580. package/dist/tour/default-tour.js +96 -222
  581. package/dist/tour/default-tour.js.map +1 -1
  582. package/dist/types.d.ts +29 -70
  583. package/package.json +15 -19
  584. package/styles.css +10 -39
@@ -1,11 +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
- import { flushSync } from "react-dom";
4
- import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
5
- import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
6
- import { parseAgentStatus } from "../services/agentStatus";
3
+ import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, } from "lucide-react";
4
+ import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, } from "../services/agentService";
7
5
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
8
- import { localStorageService } from "../services/localStorageService";
9
6
  import { Textarea } from "../../components/ui/textarea";
10
7
  import { Button } from "../../components/ui/button";
11
8
  import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
@@ -15,155 +12,21 @@ import { AgentDocumentList, } from "./AgentDocumentList";
15
12
  import { AgentEditOperationsPanel } from "./EditOperationsPanel";
16
13
  import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
17
14
  import { getComponentById } from "../componentTreeHelper";
18
- import { toUserFacingAgentErrorMessage } from "../services/agentErrorMessage";
19
15
  import { AgentGreeting } from "./AgentGreeting";
20
16
  import { getAgentHistory } from "../services/editService";
21
- import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
22
- import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
23
- import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
24
17
  import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
25
18
  import { SecretAgentIcon } from "../ui/Icons";
26
19
  import { formatTime, formatDateTime } from "../utils";
27
20
  import { cn } from "../../lib/utils";
28
- import { sanitizeSvg } from "../../lib/sanitize";
29
21
  import { Select } from "../../components/ui/select";
30
22
  import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
23
+ import { useMediaQuery } from "../client/hooks/useMediaQuery";
31
24
  import { SimpleTabs } from "../ui/SimpleTabs";
32
- import { Splitter } from "../ui/Splitter";
33
- import { ScrollingContentTree } from "../ScrollingContentTree";
34
- import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
35
- const AGENT_HISTORY_LIMIT = 1000;
36
- function mergeAgentOperationHistory(existing, incoming, limit = AGENT_HISTORY_LIMIT) {
37
- const merged = new Map(existing.map((operation) => [operation.id, operation]));
38
- for (const operation of incoming) {
39
- merged.set(operation.id, operation);
40
- }
41
- return Array.from(merged.values())
42
- .sort((left, right) => new Date(right.date).getTime() - new Date(left.date).getTime())
43
- .slice(0, limit);
44
- }
45
- const userMessageMarkdownComponents = {
46
- h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
47
- h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
48
- h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
49
- h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
50
- p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
51
- ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
52
- ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
53
- li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
54
- 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" })),
55
- 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 })),
56
- };
57
- function buildPlaceholderAgentDetails(agentStub) {
58
- const now = new Date().toISOString();
59
- const updated = agentStub.updatedDate || now;
60
- // AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
61
- // This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
62
- return {
63
- ...agentStub,
64
- name: agentStub.name || "Agent",
65
- userId: agentStub.userId || "",
66
- updatedDate: updated,
67
- profileName: agentStub.profileName || "",
68
- model: agentStub.model || "",
69
- createdDate: agentStub.createdDate || updated,
70
- totalTokensUsed: 0,
71
- totalInputTokens: 0,
72
- totalOutputTokens: 0,
73
- totalCachedInputTokens: 0,
74
- totalInputTokenCost: 0,
75
- totalOutputTokenCost: 0,
76
- totalCachedInputTokenCost: 0,
77
- totalImageCost: 0,
78
- totalCost: 0,
79
- currency: agentStub.currency || "USD",
80
- messageCount: agentStub.messageCount || 0,
81
- };
82
- }
83
- function normalizeDialogAgentId(value) {
84
- return value?.trim().toLowerCase() || "";
85
- }
86
- function formatAllowanceSource(source) {
87
- const normalized = source?.trim();
88
- if (!normalized)
89
- return null;
90
- if (normalized === "user")
91
- return "User granted";
92
- if (normalized.startsWith("preconfigured:profile:"))
93
- return "Profile";
94
- if (normalized.startsWith("preconfigured:skill:"))
95
- return "Skill";
96
- if (normalized.startsWith("system:")) {
97
- return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
98
- }
99
- return normalized;
100
- }
101
- function formatAllowanceLabel(allowance) {
102
- return `${allowance.operationType || "*"}${"itemPath" in allowance
103
- ? ` ${allowance.itemPath}`
104
- : ` ${allowance.normalizedPath}`}`;
105
- }
106
- function getAgentRunMessageAgentId(payload) {
107
- const agentId = payload?.agentId;
108
- return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
109
- }
110
- function getAgentRunMessageSeq(payload) {
111
- const seq = payload?.seq;
112
- return typeof seq === "number" ? seq : null;
113
- }
114
- function getAgentRunMessageDetail(type, payload) {
115
- if (type === "agent:run:delta") {
116
- return payload?.type || null;
117
- }
118
- if (type === "agent:run:status") {
119
- return payload?.data?.state || payload?.data?.status || null;
120
- }
121
- if (type === "agent:run:error") {
122
- return payload?.error || null;
123
- }
124
- if (type === "agent:run:complete") {
125
- return payload?.finalStatus || null;
126
- }
127
- if (type === "agent:run:start") {
128
- return payload?.agentName || null;
129
- }
130
- return null;
131
- }
132
- function isHeartbeatRunEventMessage(message) {
133
- if (!message)
134
- return false;
135
- if (message.type === "agent:run:delta") {
136
- const deltaType = message.payload?.type;
137
- return String(deltaType || "").toLowerCase() === "heartbeat";
138
- }
139
- if (message.type === "agent:run:status") {
140
- const state = message.payload?.data?.state || message.payload?.data?.status;
141
- return String(state || "").toLowerCase() === "heartbeat";
142
- }
143
- return false;
144
- }
145
- function getVisibleDialogRegistry() {
146
- const registry = globalThis.__agentDialogVisibleCallbacks;
147
- return registry && typeof registry === "object" ? registry : {};
148
- }
149
- function isAgentErrorStatusValue(status) {
150
- return status === "error";
151
- }
152
25
  // Simple user message component
153
26
  const UserMessage = ({ message }) => {
154
- const content = message.content || "";
155
- const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
156
- // Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
157
- const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
158
- const triggerMatch = content.match(triggerPattern);
159
- const triggerName = triggerMatch?.[1]?.trim() || "";
160
- const triggerContent = triggerMatch?.[2] || "";
161
- const isTriggerMessage = triggerName.length > 0;
162
- if (isTriggerMessage) {
163
- 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 }) }))] }) }));
164
- }
165
27
  // Parse source agent name from content if it starts with "[From ...]:"
166
28
  // Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
29
+ const content = message.content || "";
167
30
  const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
168
31
  const match = content.match(fromPattern);
169
32
  let sourceAgentName;
@@ -188,10 +51,7 @@ const UserMessage = ({ message }) => {
188
51
  message.sourceAgent?.name;
189
52
  }
190
53
  const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
191
- 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 }) })] })] }));
192
- };
193
- const HeartbeatMessage = ({ message }) => {
194
- 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)) }))] }) }));
54
+ 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 })] })] }));
195
55
  };
196
56
  // Helper to extract todos from potentially incomplete JSON during streaming
197
57
  const extractPartialTodos = (jsonText) => {
@@ -425,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
425
285
  todoMap.set(key, todo);
426
286
  }
427
287
  const result = Array.from(todoMap.values());
288
+ // Only log when duplicates were actually removed (to reduce noise)
289
+ if (todos.length > result.length) {
290
+ console.log("🟡 TODO_DEBUG deduplication:", {
291
+ removed: todos.length - result.length,
292
+ before: todos.length,
293
+ after: result.length,
294
+ });
295
+ }
428
296
  return result;
429
297
  };
430
298
  // TodoListPanel component
@@ -432,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
432
300
  const [isExpanded, setIsExpanded] = useState(true);
433
301
  // First try to get todos from agent metadata (real-time updates)
434
302
  // Server sends additionalData.todoList directly via contextChanged status
303
+ // Also check top-level todoList for backward compatibility with stored contexts
435
304
  const metadataTodos = (() => {
436
305
  try {
437
- const todoList = agentMetadata?.additionalData?.todoList;
306
+ // Check both additionalData.todoList and top-level todoList (from [JsonExtensionData] serialization)
307
+ const todoList = agentMetadata?.additionalData?.todoList ||
308
+ agentMetadata?.todoList;
438
309
  if (todoList?.items && Array.isArray(todoList.items)) {
439
310
  const rawItems = todoList.items
440
311
  .map((item, idx) => ({
@@ -562,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
562
433
  // Add user message
563
434
  groups.push({ type: "user", messages: [message] });
564
435
  }
565
- else if (message.messageType === "heartbeat" ||
566
- message.role === "system") {
567
- if (currentAssistantGroup.length > 0) {
568
- groups.push({
569
- type: "assistant-group",
570
- messages: currentAssistantGroup,
571
- });
572
- currentAssistantGroup = [];
573
- }
574
- groups.push({ type: "heartbeat", messages: [message] });
575
- }
576
436
  else if (message.role === "assistant") {
577
437
  // Add to current assistant group
578
438
  currentAssistantGroup.push(message);
@@ -640,7 +500,6 @@ const calculateTotalTokens = (messages) => {
640
500
  outputCost: acc.outputCost + (message.outputTokenCost || 0),
641
501
  cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
642
502
  cacheWriteCost: acc.cacheWriteCost,
643
- imageCost: acc.imageCost,
644
503
  totalCost: acc.totalCost + (message.totalCost || 0),
645
504
  };
646
505
  }, {
@@ -652,7 +511,6 @@ const calculateTotalTokens = (messages) => {
652
511
  outputCost: 0,
653
512
  cachedCost: 0,
654
513
  cacheWriteCost: 0,
655
- imageCost: 0,
656
514
  totalCost: 0,
657
515
  });
658
516
  return totals;
@@ -663,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
663
521
  const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
664
522
  return matched;
665
523
  };
666
- const stringifyToolField = (value) => {
667
- if (value === undefined || value === null)
668
- return undefined;
669
- if (typeof value === "string") {
670
- return value.trim().length > 0 ? value : undefined;
671
- }
672
- try {
673
- return JSON.stringify(value);
674
- }
675
- catch {
676
- return String(value);
677
- }
678
- };
679
- const parseToolResultValue = (value) => {
680
- if (value === undefined || value === null) {
681
- return undefined;
682
- }
683
- if (typeof value === "object") {
684
- return value;
685
- }
686
- if (typeof value !== "string") {
687
- return String(value);
688
- }
689
- const trimmed = value.trim();
690
- if (!trimmed) {
691
- return undefined;
692
- }
693
- try {
694
- let parsed = JSON.parse(trimmed);
695
- if (typeof parsed === "string" &&
696
- (parsed.startsWith("{") || parsed.startsWith("["))) {
697
- parsed = JSON.parse(parsed);
698
- }
699
- if (parsed && typeof parsed === "object") {
700
- return parsed;
701
- }
702
- }
703
- catch {
704
- // Fall back to the original string when the payload is plain text.
705
- }
706
- return value;
707
- };
708
- const getFirstToolCallEnvelope = (data) => {
709
- if (!data || typeof data !== "object")
710
- return undefined;
711
- const direct = data.toolCall || data.tool_call;
712
- if (direct)
713
- return direct;
714
- const arrayCandidates = [data.tool_calls, data.toolCalls];
715
- for (const candidate of arrayCandidates) {
716
- if (Array.isArray(candidate) && candidate.length > 0) {
717
- return candidate[0];
718
- }
719
- }
720
- return undefined;
721
- };
722
- const extractToolCallFields = (data) => {
723
- const envelope = getFirstToolCallEnvelope(data);
724
- const functionPayload = data?.function || envelope?.function;
725
- const functionName = data?.functionName ||
726
- data?.name ||
727
- functionPayload?.name ||
728
- envelope?.functionName ||
729
- envelope?.name ||
730
- "unknown";
731
- const toolCallId = data?.toolCallId || data?.id || envelope?.id;
732
- const functionArguments = stringifyToolField(data?.functionArguments) ||
733
- stringifyToolField(data?.arguments) ||
734
- stringifyToolField(functionPayload?.arguments) ||
735
- stringifyToolField(envelope?.functionArguments) ||
736
- stringifyToolField(envelope?.arguments) ||
737
- "{}";
738
- return {
739
- toolCallId,
740
- functionName,
741
- functionArguments,
742
- };
743
- };
744
524
  // Convert agent messages to AI terminal format for a response group
745
525
  const convertAgentMessagesToAiFormat = (agentMessages) => {
746
526
  return agentMessages.map((agentMessage) => {
@@ -761,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
761
541
  role: agentMessage.role,
762
542
  createdDate: agentMessage.createdDate,
763
543
  tool_calls: agentMessage.toolCalls
764
- ? agentMessage.toolCalls.map((toolCall) => {
765
- const isPruned = !!toolCall.isPruned ||
766
- /^PRUNED$/i.test(toolCall.functionError || "");
767
- const displayResult = parseToolResultValue(toolCall.functionResultRichContent) ?? toolCall.functionResult;
768
- return {
769
- id: toolCall.toolCallId,
770
- displayName: toolCall.functionName,
771
- function: {
772
- name: toolCall.functionName,
773
- arguments: toolCall.functionArguments,
774
- result: displayResult,
775
- error: toolCall.functionError,
776
- },
777
- // Pass through approval info if present on the tool call
778
- requiresApproval: toolCall.requiresApproval,
779
- // Pass through prune metadata so the terminal can render a neutral state
780
- isPruned,
781
- prunedAt: toolCall.prunedAt,
782
- // Pass through isCompleted so ToolCallDisplay knows when to hide spinner
783
- isCompleted: toolCall.isCompleted,
784
- // Tool call is streaming if message is not completed and tool call has no result yet
785
- isStreaming: !agentMessage.isCompleted &&
786
- !toolCall.isCompleted &&
787
- !displayResult &&
788
- !toolCall.functionError &&
789
- !isPruned,
790
- // Pass through message IDs for approval/rejection events
791
- messageId: toolCall.messageId,
792
- dbMessageId: toolCall.dbMessageId,
793
- responseTimeMs: toolCall.responseTimeMs,
794
- createdDate: toolCall.createdDate,
795
- };
796
- })
544
+ ? agentMessage.toolCalls.map((toolCall) => ({
545
+ id: toolCall.toolCallId,
546
+ displayName: toolCall.functionName,
547
+ function: {
548
+ name: toolCall.functionName,
549
+ arguments: toolCall.functionArguments,
550
+ result: toolCall.functionResult,
551
+ error: toolCall.functionError,
552
+ },
553
+ // Pass through approval info if present on the tool call
554
+ requiresApproval: toolCall.requiresApproval,
555
+ // Pass through isCompleted so ToolCallDisplay knows when to hide spinner
556
+ isCompleted: toolCall.isCompleted,
557
+ // Tool call is streaming if message is not completed and tool call has no result yet
558
+ isStreaming: !agentMessage.isCompleted &&
559
+ !toolCall.isCompleted &&
560
+ !toolCall.functionResult &&
561
+ !toolCall.functionError,
562
+ // Pass through message IDs for approval/rejection events
563
+ messageId: toolCall.messageId,
564
+ dbMessageId: toolCall.dbMessageId,
565
+ }))
797
566
  : [],
798
567
  };
799
568
  if (agentMessage.toolCallId) {
@@ -805,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
805
574
  // interface AgentTerminalProps {
806
575
  // agentStub: Agent;
807
576
  // }
808
- export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false, displayMode = "full", showSummaryInput = false, hideContext = false, hideBottomControls = false, hideGreeting = false, defaultCollapseJson = false, simpleMode = false, className, initialPrompt, onAgentUpdate, onMessage, onInteractionSubmitted, onQuestionnaireOpenChange, questionnaireFooterActions, hideSummaryMessages = false, summaryPlaceholderActions, summaryPlaceholderMessage, hideSummaryWaitingPlaceholder = false, }) {
577
+ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive = true, compact = false, hideContext = false, hideBottomControls = false, hideGreeting = false, simpleMode = false, className, initialPrompt, onAgentUpdate, }) {
809
578
  const editContext = useEditContext();
810
579
  const fieldsContext = useFieldsEditContext();
811
- const [agent, setAgent] = useState(() => buildPlaceholderAgentDetails(agentStub));
580
+ const [agent, setAgent] = useState(undefined);
812
581
  const [messages, setMessages] = useState([]);
813
582
  const [agentOperations, setAgentOperations] = useState([]);
814
583
  const [prompt, setPrompt] = useState("");
@@ -819,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
819
588
  const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
820
589
  const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
821
590
  const [agentMetadata, setAgentMetadata] = useState(null);
822
- const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
823
- const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
824
- // Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
825
- // This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
826
- useEffect(() => {
827
- setAgent((prev) => {
828
- if (prev?.id === agentStub.id)
829
- return prev;
830
- return buildPlaceholderAgentDetails(agentStub);
831
- });
832
- }, [agentStub.id]);
833
- const observedMessageIdsRef = useRef(new Set());
834
- useEffect(() => {
835
- observedMessageIdsRef.current = new Set();
836
- }, [agentStub.id]);
837
- useEffect(() => {
838
- if (!onMessage)
839
- return;
840
- for (const message of messages) {
841
- if (!message?.id)
842
- continue;
843
- if (!message.isCompleted)
844
- continue;
845
- if (observedMessageIdsRef.current.has(message.id))
846
- continue;
847
- observedMessageIdsRef.current.add(message.id);
848
- onMessage(message);
849
- }
850
- }, [messages, onMessage]);
851
591
  // Generate a stable clientSessionId per component instance for stream deduplication
852
592
  const clientSessionIdRef = useRef(null);
853
593
  if (!clientSessionIdRef.current) {
@@ -859,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
859
599
  const [isListening, setIsListening] = useState(false);
860
600
  const recognitionRef = useRef(null);
861
601
  const prevPlaceholderRef = useRef(null);
862
- const promptBeforeVoiceRef = useRef("");
863
602
  // Voice button press-and-hold tracking
864
603
  const voicePressStartRef = useRef(null);
865
604
  const voiceHoldTimerRef = useRef(null);
@@ -869,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
869
608
  const [showCompressionPopover, setShowCompressionPopover] = useState(false);
870
609
  // Agent inline dialog state (for component type selector, etc.)
871
610
  const [activeInlineDialog, setActiveInlineDialog] = useState(null);
872
- const dialogTerminalInstanceIdRef = useRef("");
873
- if (!dialogTerminalInstanceIdRef.current) {
874
- dialogTerminalInstanceIdRef.current = crypto.randomUUID();
875
- }
876
- const activeInlineDialogRef = useRef(activeInlineDialog);
877
- const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
878
- const orphanTimeoutRef = useRef(null);
879
- useEffect(() => {
880
- activeInlineDialogRef.current = activeInlineDialog;
881
- const visibleRegistry = { ...getVisibleDialogRegistry() };
882
- const callbackId = activeInlineDialog?.request.callbackId || null;
883
- const terminalInstanceId = dialogTerminalInstanceIdRef.current;
884
- const agentKeys = [
885
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
886
- normalizeDialogAgentId(agentIdRefForDialogs.current),
887
- ].filter(Boolean);
888
- agentKeys.forEach((key) => {
889
- if (callbackId) {
890
- visibleRegistry[key] = {
891
- callbackId,
892
- terminalInstanceId,
893
- };
894
- }
895
- else {
896
- delete visibleRegistry[key];
897
- }
898
- });
899
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
900
- }, [activeInlineDialog]);
901
- useEffect(() => {
902
- onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
903
- }, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
904
- useLayoutEffect(() => {
905
- if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
906
- return;
907
- }
908
- const scrollSummaryTerminalToTop = () => {
909
- const container = messagesContainerRef.current;
910
- if (!container) {
911
- return;
912
- }
913
- container.scrollTop = 0;
914
- };
915
- scrollSummaryTerminalToTop();
916
- const frame1 = requestAnimationFrame(() => {
917
- scrollSummaryTerminalToTop();
918
- });
919
- let frame3 = 0;
920
- const frame2 = requestAnimationFrame(() => {
921
- frame3 = requestAnimationFrame(() => {
922
- scrollSummaryTerminalToTop();
923
- });
924
- });
925
- return () => {
926
- cancelAnimationFrame(frame1);
927
- cancelAnimationFrame(frame2);
928
- cancelAnimationFrame(frame3);
929
- };
930
- }, [displayMode, isQuestionnaireDialogOpen]);
931
- useEffect(() => {
932
- return () => {
933
- onQuestionnaireOpenChange?.(false);
934
- };
935
- }, [onQuestionnaireOpenChange]);
936
611
  const isWaitingRef = useRef(false);
937
612
  useEffect(() => {
938
613
  isWaitingRef.current = isWaitingForResponse;
@@ -941,96 +616,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
941
616
  // while a stop operation is in progress. This prevents race conditions where
942
617
  // messages arriving during the stop process could flip the UI back to "executing".
943
618
  const isStoppingRef = useRef(false);
944
- const [isStopGuardActive, setIsStopGuardActive] = useState(false);
945
- const stopGuardReleaseTimeoutRef = useRef(null);
946
- function clearStopGuard() {
947
- isStoppingRef.current = false;
948
- setIsStopGuardActive(false);
949
- if (stopGuardReleaseTimeoutRef.current) {
950
- clearTimeout(stopGuardReleaseTimeoutRef.current);
951
- stopGuardReleaseTimeoutRef.current = null;
952
- }
953
- }
954
- function armStopGuard() {
955
- isStoppingRef.current = true;
956
- setIsStopGuardActive(true);
957
- if (stopGuardReleaseTimeoutRef.current) {
958
- clearTimeout(stopGuardReleaseTimeoutRef.current);
959
- }
960
- stopGuardReleaseTimeoutRef.current = setTimeout(() => {
961
- stopGuardReleaseTimeoutRef.current = null;
962
- isStoppingRef.current = false;
963
- setIsStopGuardActive(false);
964
- }, 30000);
965
- }
966
619
  // Server-driven state: true when agent is actively processing (set by WebSocket messages)
967
620
  const [isAgentThinking, setIsAgentThinking] = useState(false);
968
- useEffect(() => {
969
- return () => {
970
- if (stopGuardReleaseTimeoutRef.current) {
971
- clearTimeout(stopGuardReleaseTimeoutRef.current);
972
- stopGuardReleaseTimeoutRef.current = null;
973
- }
974
- };
975
- }, []);
976
- useEffect(() => {
977
- if (!initialMetadata)
978
- return;
979
- setAgentMetadata((prev) => sanitizeAgentMetadata({
980
- ...(prev || {}),
981
- ...initialMetadata,
982
- additionalData: {
983
- ...(prev?.additionalData || {}),
984
- ...(initialMetadata?.additionalData || {}),
985
- },
986
- }));
987
- }, [initialMetadata]);
988
621
  const hasActiveStreaming = useCallback(() => {
989
622
  const current = messagesRef.current || [];
990
623
  return current.some((m) => !m.isCompleted && m.messageType === "streaming");
991
624
  }, []);
992
- const currentAgentId = agent?.id || agentStub.id;
993
- const recentAgentRunEvents = useMemo(() => {
994
- const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
995
- if (!normalizedAgentId) {
996
- return [];
997
- }
998
- if (!editContext) {
999
- return [];
1000
- }
1001
- return (editContext.webSocketMessages || [])
1002
- .filter((message) => {
1003
- if (!message?.type?.startsWith("agent:run:")) {
1004
- return false;
1005
- }
1006
- if (isHeartbeatRunEventMessage(message)) {
1007
- return false;
1008
- }
1009
- return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
1010
- })
1011
- .slice(-8)
1012
- .map((message) => ({
1013
- timestamp: message.timestamp,
1014
- type: message.type,
1015
- seq: getAgentRunMessageSeq(message.payload),
1016
- detail: getAgentRunMessageDetail(message.type, message.payload),
1017
- }));
1018
- }, [currentAgentId, editContext?.webSocketMessages]);
1019
- const appendToolUiEvent = useCallback((type, detail, seq) => {
1020
- const timestamp = new Date().toISOString();
1021
- setRecentToolUiEvents((prev) => {
1022
- const next = [
1023
- ...prev,
1024
- {
1025
- timestamp,
1026
- type,
1027
- detail: detail || null,
1028
- seq: seq ?? null,
1029
- },
1030
- ];
1031
- return next.slice(-40);
1032
- });
1033
- }, []);
1034
625
  // Collect all pending tool calls for batch approval functionality
1035
626
  const allPendingApprovals = useMemo(() => {
1036
627
  const pending = [];
@@ -1072,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1072
663
  }, [allPendingApprovals]);
1073
664
  // Handle mode switch to autonomous
1074
665
  const handleSwitchToAutonomous = useCallback(() => {
1075
- const nextMode = "autonomous";
1076
- setMode(nextMode);
1077
- setAgentMetadata((prev) => ({
1078
- ...(prev ?? {}),
1079
- mode: nextMode,
1080
- }));
1081
- setAgent((prev) => {
1082
- if (!prev)
1083
- return prev;
1084
- return {
1085
- ...prev,
1086
- mode: nextMode,
1087
- };
1088
- });
1089
- }, [setAgent, setAgentMetadata]);
666
+ setMode("autonomous");
667
+ }, []);
1090
668
  const [resolvedPageName, setResolvedPageName] = useState(undefined);
1091
669
  const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
1092
670
  const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
1093
671
  const [promptHistory, setPromptHistory] = useState(() => {
1094
- return (localStorageService.getItem("editor.agent.promptHistory") || []);
672
+ if (typeof window !== "undefined") {
673
+ return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
674
+ }
675
+ return [];
1095
676
  });
1096
677
  const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
1097
678
  const [showPredefined, setShowPredefined] = useState(false);
1098
679
  const [activeProfile, setActiveProfile] = useState(undefined);
1099
680
  const [selectedModelId, setSelectedModelId] = useState(undefined);
1100
- const normalizeAgentMode = (value) => {
1101
- if (value === "autonomous" ||
1102
- value === "read-only" ||
1103
- value === "supervised") {
1104
- return value;
1105
- }
1106
- return null;
1107
- };
1108
681
  const [mode, setMode] = useState("supervised");
1109
682
  const [queuedPrompts, setQueuedPrompts] = useState([]);
1110
- const [expandedQueuedTriggerIds, setExpandedQueuedTriggerIds] = useState({});
683
+ const isMobile = useMediaQuery("(max-width: 768px)");
1111
684
  const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
1112
685
  const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
1113
686
  const [showCostAndAgent, setShowCostAndAgent] = useState(false);
1114
- const [showAgentSettings, setShowAgentSettings] = useState(false);
1115
- const [showSkillPicker, setShowSkillPicker] = useState(false);
1116
- const [availableSkills, setAvailableSkills] = useState([]);
1117
- const [skillRootIds, setSkillRootIds] = useState([]);
1118
- const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
1119
- const [skillsLoading, setSkillsLoading] = useState(false);
1120
- const [skillsError, setSkillsError] = useState(null);
1121
- const [skillActionError, setSkillActionError] = useState(null);
1122
- const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
1123
- const [availableTools, setAvailableTools] = useState([]);
1124
- const [operationAllowances, setOperationAllowances] = useState({
1125
- sitecore: [],
1126
- filesystem: [],
1127
- });
1128
- const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
1129
- const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
1130
- const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
1131
- const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
1132
- const [availableToolsError, setAvailableToolsError] = useState(null);
1133
- const [operationAllowancesError, setOperationAllowancesError] = useState(null);
1134
- const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
1135
- const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
1136
- const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
1137
- const isPersistedAgent = !!agent?.userId;
1138
- const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
1139
687
  const hasSpawnedAgents = useMemo(() => {
1140
688
  if (!agentMetadata)
1141
689
  return false;
1142
- const childAgents = agentMetadata?.childAgents;
690
+ const childAgents = agentMetadata?.ChildAgents ||
691
+ agentMetadata?.childAgents;
1143
692
  if (!Array.isArray(childAgents) || childAgents.length === 0)
1144
693
  return false;
1145
- return childAgents.some((a) => a != null && typeof a === "object" && a.agentId);
694
+ return childAgents.some((a) => a != null && typeof a === "object" && (a.AgentId || a.agentId));
1146
695
  }, [agentMetadata]);
1147
696
  const hasTodoContent = useMemo(() => {
1148
697
  const metadataTodos = (() => {
1149
698
  try {
1150
- const todoList = agentMetadata?.additionalData?.todoList;
699
+ const todoList = agentMetadata?.additionalData?.todoList ||
700
+ agentMetadata?.todoList;
1151
701
  if (todoList?.items && Array.isArray(todoList.items)) {
1152
702
  const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
1153
703
  return raw.length > 0;
@@ -1169,7 +719,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1169
719
  return isUpdating;
1170
720
  }, [agentMetadata, messages]);
1171
721
  const prevAgentIdRef = useRef(undefined);
1172
- const hasHistoryContent = agentOperations.length > 0;
722
+ const [hasHistoryContent, setHasHistoryContent] = useState(false);
1173
723
  useEffect(() => {
1174
724
  const currentId = agent?.id;
1175
725
  if (prevAgentIdRef.current !== currentId) {
@@ -1185,37 +735,47 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1185
735
  }
1186
736
  }, [agent?.id]);
1187
737
  useEffect(() => {
1188
- let active = true;
1189
- const loadSkills = async () => {
1190
- try {
1191
- setSkillsLoading(true);
1192
- setSkillsError(null);
1193
- const catalog = await getAgentSkillCatalog(false);
1194
- if (active) {
1195
- setAvailableSkills(catalog.skills.filter((s) => !s.disabled));
1196
- setSkillRootIds(catalog.rootIds);
1197
- setSelectableTemplateIds(catalog.selectableTemplateIds);
1198
- }
1199
- }
1200
- catch (e) {
1201
- if (active) {
1202
- setSkillsError(e?.message || "Failed to load skills");
1203
- setAvailableSkills([]);
1204
- setSkillRootIds([]);
1205
- setSelectableTemplateIds([]);
1206
- }
738
+ if (!agent?.id) {
739
+ setHasHistoryContent(false);
740
+ return;
741
+ }
742
+ setHasHistoryContent(false);
743
+ let cancelled = false;
744
+ getAgentHistory(agent.id, 10)
745
+ .then((result) => {
746
+ if (cancelled)
747
+ return;
748
+ if (result.type === "success" && result.data && result.data.length > 0) {
749
+ setHasHistoryContent(true);
1207
750
  }
1208
- finally {
1209
- if (active) {
1210
- setSkillsLoading(false);
1211
- }
751
+ else {
752
+ setHasHistoryContent(false);
1212
753
  }
1213
- };
1214
- void loadSkills();
754
+ })
755
+ .catch(() => {
756
+ if (!cancelled)
757
+ setHasHistoryContent(false);
758
+ });
1215
759
  return () => {
1216
- active = false;
760
+ cancelled = true;
1217
761
  };
1218
- }, []);
762
+ }, [agent?.id]);
763
+ const agentIdForHistoryRef = useRef(agent?.id);
764
+ agentIdForHistoryRef.current = agent?.id;
765
+ useEffect(() => {
766
+ if (!agent?.id || !editContext?.addSocketMessageListener)
767
+ return;
768
+ const unsubscribe = editContext.addSocketMessageListener((message) => {
769
+ if (message.type !== "edit-operation")
770
+ return;
771
+ const op = message.payload;
772
+ const operationAgentId = op.agentId || op.user?.agentId;
773
+ if (operationAgentId === agentIdForHistoryRef.current) {
774
+ setHasHistoryContent(true);
775
+ }
776
+ });
777
+ return () => unsubscribe();
778
+ }, [agent?.id, editContext?.addSocketMessageListener]);
1219
779
  const modeOptions = useMemo(() => [
1220
780
  {
1221
781
  value: "supervised",
@@ -1250,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1250
810
  value: m.id,
1251
811
  label: m.name,
1252
812
  })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1253
- const metadataSelectedSkillIds = useMemo(() => {
1254
- const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
1255
- if (!Array.isArray(rawSkillIds)) {
1256
- return [];
1257
- }
1258
- return rawSkillIds
1259
- .map((x) => String(x || "").trim())
1260
- .filter((x) => x.length > 0);
1261
- }, [agentMetadata]);
1262
- const backendAssignedSkillIds = useMemo(() => {
1263
- const rawSkillIds = agent?.assignedSkillIds ?? [];
1264
- if (!Array.isArray(rawSkillIds)) {
1265
- return [];
1266
- }
1267
- return rawSkillIds
1268
- .map((x) => String(x || "").trim())
1269
- .filter((x) => x.length > 0);
1270
- }, [agent]);
1271
- const preloadedSkillIds = useMemo(() => {
1272
- const rawSkillIds = activeProfile?.preloadSkills ?? [];
1273
- if (!Array.isArray(rawSkillIds)) {
1274
- return [];
1275
- }
1276
- return rawSkillIds
1277
- .map((skill) => String(skill?.id || "").trim())
1278
- .filter((id) => id.length > 0);
1279
- }, [activeProfile?.preloadSkills]);
1280
- const autoAssignedSkillIds = useMemo(() => {
1281
- const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
1282
- const all = isLocalOnlyDraftAgent
1283
- ? preloadedSkillIds
1284
- : backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
1285
- const seen = new Set();
1286
- const unique = [];
1287
- for (const id of all) {
1288
- const key = id.toLowerCase();
1289
- if (seen.has(key))
1290
- continue;
1291
- seen.add(key);
1292
- unique.push(id);
1293
- }
1294
- return unique;
1295
- }, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
1296
- const selectedSkillIds = useMemo(() => {
1297
- const all = [
1298
- ...autoAssignedSkillIds,
1299
- ...backendAssignedSkillIds,
1300
- ...metadataSelectedSkillIds,
1301
- ];
1302
- const seen = new Set();
1303
- const unique = [];
1304
- for (const id of all) {
1305
- const key = id.toLowerCase();
1306
- if (seen.has(key))
1307
- continue;
1308
- seen.add(key);
1309
- unique.push(id);
1310
- }
1311
- return unique;
1312
- }, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
1313
- useEffect(() => {
1314
- let active = true;
1315
- if (!showAgentSettings) {
1316
- return () => {
1317
- active = false;
1318
- };
1319
- }
1320
- if (!agent?.id || isLocalOnlyDraftAgent) {
1321
- setTriggerSubscriptions([]);
1322
- setTriggerSubscriptionsLoading(false);
1323
- setTriggerSubscriptionsError(null);
1324
- setAvailableTools([]);
1325
- setAvailableToolsLoading(false);
1326
- setAvailableToolsError(null);
1327
- setOperationAllowances({ sitecore: [], filesystem: [] });
1328
- setOperationAllowancesLoading(false);
1329
- setOperationAllowancesError(null);
1330
- return () => {
1331
- active = false;
1332
- };
1333
- }
1334
- const loadTriggerSubscriptions = async () => {
1335
- try {
1336
- setTriggerSubscriptionsLoading(true);
1337
- setTriggerSubscriptionsError(null);
1338
- const subscriptions = await getAgentTriggerSubscriptions(agent.id);
1339
- if (active) {
1340
- setTriggerSubscriptions(subscriptions);
1341
- }
1342
- }
1343
- catch (e) {
1344
- if (active) {
1345
- setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
1346
- }
1347
- }
1348
- finally {
1349
- if (active) {
1350
- setTriggerSubscriptionsLoading(false);
1351
- }
1352
- }
1353
- };
1354
- const loadOperationAllowances = async () => {
1355
- try {
1356
- setOperationAllowancesLoading(true);
1357
- setOperationAllowancesError(null);
1358
- const allowances = await getAgentOperationAllowances(agent.id);
1359
- if (active) {
1360
- setOperationAllowances(allowances);
1361
- }
1362
- }
1363
- catch (e) {
1364
- if (active) {
1365
- setOperationAllowancesError(e?.message || "Failed to load allowances");
1366
- }
1367
- }
1368
- finally {
1369
- if (active) {
1370
- setOperationAllowancesLoading(false);
1371
- }
1372
- }
1373
- };
1374
- const loadAvailableTools = async () => {
1375
- try {
1376
- setAvailableToolsLoading(true);
1377
- setAvailableToolsError(null);
1378
- const tools = await getAgentAvailableTools(agent.id);
1379
- if (active) {
1380
- setAvailableTools(tools);
1381
- }
1382
- }
1383
- catch (e) {
1384
- if (active) {
1385
- setAvailableToolsError(e?.message || "Failed to load available tools");
1386
- }
1387
- }
1388
- finally {
1389
- if (active) {
1390
- setAvailableToolsLoading(false);
1391
- }
1392
- }
1393
- };
1394
- void loadTriggerSubscriptions();
1395
- void loadAvailableTools();
1396
- void loadOperationAllowances();
1397
- return () => {
1398
- active = false;
1399
- };
1400
- }, [
1401
- showAgentSettings,
1402
- agent?.id,
1403
- agent?.status,
1404
- agent?.userId,
1405
- agent?.profileId,
1406
- mode,
1407
- selectedSkillIds,
1408
- ]);
1409
- const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
1410
- const allowanceGroups = useMemo(() => [
1411
- {
1412
- key: "sitecore",
1413
- label: "Sitecore",
1414
- rows: operationAllowances.sitecore,
1415
- },
1416
- {
1417
- key: "filesystem",
1418
- label: "Filesystem",
1419
- rows: operationAllowances.filesystem,
1420
- },
1421
- ], [operationAllowances]);
1422
- const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
1423
- operationAllowances.filesystem.length > 0, [operationAllowances]);
1424
- const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
1425
- operationAllowances.filesystem.length, [operationAllowances]);
1426
- const listedProfileSkillIdSet = useMemo(() => {
1427
- const ids = [
1428
- ...(activeProfile?.availableSkills ?? []),
1429
- ...(activeProfile?.allowedSkills ?? []),
1430
- ]
1431
- .map((skill) => String(skill?.id || "").toLowerCase())
1432
- .filter((id) => id.length > 0);
1433
- return new Set(ids);
1434
- }, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
1435
- const profileFilteredSkills = useMemo(() => {
1436
- if (listedProfileSkillIdSet.size === 0) {
1437
- return [];
1438
- }
1439
- return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
1440
- }, [availableSkills, listedProfileSkillIdSet]);
1441
- const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
1442
- const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
1443
- .map((skill) => String(skill?.id || "").toLowerCase())
1444
- .filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
1445
- const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
1446
- const selectedSkills = useMemo(() => selectedSkillIds
1447
- .map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
1448
- .filter((s) => !!s), [availableSkills, selectedSkillIds]);
1449
- const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
1450
- const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
1451
- const previewAvailableTools = useMemo(() => {
1452
- const baseToolNames = mode === "read-only"
1453
- ? (activeProfile?.readOnlyToolNames ?? [])
1454
- : (activeProfile?.allowedToolNames ?? []);
1455
- const toolNames = new Set();
1456
- for (const toolName of baseToolNames) {
1457
- const normalized = String(toolName || "").trim();
1458
- if (normalized) {
1459
- toolNames.add(normalized);
1460
- }
1461
- }
1462
- for (const skill of selectedSkills) {
1463
- for (const toolName of skill.additionalAllowedTools ?? []) {
1464
- const normalized = String(toolName || "").trim();
1465
- if (normalized) {
1466
- toolNames.add(normalized);
1467
- }
1468
- }
1469
- }
1470
- return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
1471
- }, [
1472
- activeProfile?.allowedToolNames,
1473
- activeProfile?.readOnlyToolNames,
1474
- mode,
1475
- selectedSkills,
1476
- ]);
1477
- const displayedAvailableTools = isLocalOnlyDraftAgent
1478
- ? previewAvailableTools
1479
- : availableTools;
1480
- const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
1481
- ? false
1482
- : availableToolsLoading;
1483
- const displayedAvailableToolsError = isLocalOnlyDraftAgent
1484
- ? null
1485
- : availableToolsError;
1486
813
  // Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
1487
814
  const sanitizeAgentMetadata = useCallback((meta) => {
1488
815
  try {
@@ -1507,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1507
834
  return meta;
1508
835
  }
1509
836
  }, []);
1510
- const getSkillActionErrorMessage = useCallback((error) => {
1511
- const message = error instanceof Error && error.message.trim()
1512
- ? error.message.trim()
1513
- : "Failed to update skill";
1514
- if (message.includes("Skill is not available for this agent profile")) {
1515
- return "This skill cannot be added for the current agent profile.";
1516
- }
1517
- return message;
1518
- }, []);
1519
- const clearLegacySelectedSkillsFromMetadata = useCallback(async () => {
1520
- const legacySkillIds = metadataSelectedSkillIds;
1521
- if (!agent?.id || legacySkillIds.length === 0) {
1522
- return;
1523
- }
1524
- const current = agentMetadata || {};
1525
- const currentAdditionalData = current.additionalData ||
1526
- {};
1527
- const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
1528
- const next = {
1529
- ...current,
837
+ // Read deterministic flags from query string once
838
+ let deterministicFlags;
839
+ try {
840
+ const params = new URLSearchParams(window.location.search);
841
+ const detParam = params.get("deterministic");
842
+ const deterministic = detParam === "true" ||
843
+ (detParam === null ? params.has("deterministic") : false);
844
+ const seedStr = params.get("seed");
845
+ const seed = seedStr ? Number(seedStr) : undefined;
846
+ deterministicFlags = {
847
+ deterministic,
848
+ seed: Number.isFinite(seed) ? seed : undefined,
1530
849
  };
1531
- if (Object.keys(nextAdditionalData).length > 0) {
1532
- next.additionalData = nextAdditionalData;
1533
- }
1534
- else {
1535
- delete next.additionalData;
1536
- }
1537
- try {
1538
- await updateAgentContext(agent.id, next);
1539
- setAgentMetadata(next);
1540
- setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
1541
- }
1542
- catch (e) {
1543
- console.error("Failed to clear legacy selected skills", e);
1544
- }
1545
- }, [
1546
- agent?.id,
1547
- agentMetadata,
1548
- metadataSelectedSkillIds,
1549
- setAgent,
1550
- setAgentMetadata,
1551
- ]);
1552
- const parsePersistedAgentContext = (contextJson) => {
1553
- try {
1554
- if (!contextJson)
1555
- return null;
1556
- const parsedContext = JSON.parse(contextJson);
1557
- if (!parsedContext || typeof parsedContext !== "object") {
1558
- return null;
1559
- }
1560
- return sanitizeAgentMetadata(parsedContext);
1561
- }
1562
- catch {
1563
- return null;
1564
- }
1565
- };
1566
- const buildDraftPersistenceContext = () => {
1567
- let effectiveContext = agentMetadata;
1568
- try {
1569
- const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
1570
- const profile = activeProfile ||
1571
- profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
1572
- const isLiveMode = profile?.editorContextMode === "live";
1573
- if (isLiveMode && typeof buildCurrentContext === "function") {
1574
- const freshContext = buildCurrentContext();
1575
- if (freshContext) {
1576
- effectiveContext = {
1577
- ...(agentMetadata || {}),
1578
- items: freshContext.items,
1579
- currentItemId: freshContext.currentItemId,
1580
- components: freshContext.components,
1581
- field: freshContext.field,
1582
- activeWorkspace: freshContext.activeWorkspace,
1583
- hasPageLoaded: freshContext.hasPageLoaded,
1584
- openSidebars: freshContext.openSidebars,
1585
- };
1586
- }
1587
- }
1588
- }
1589
- catch (e) {
1590
- console.warn("[AgentTerminal] Failed to compute draft context:", e);
1591
- }
1592
- return sanitizeAgentMetadata(effectiveContext || null);
1593
- };
1594
- const ensureDraftAgentPersisted = async () => {
1595
- if (!agent?.id) {
1596
- throw new Error("Agent not ready. Please try again.");
1597
- }
1598
- if (!isLocalOnlyDraftAgent) {
1599
- return agent;
1600
- }
1601
- const requestSettings = getPendingRequestSettings();
1602
- if (!requestSettings.profileId) {
1603
- throw new Error("Select an agent profile before adding a skill.");
1604
- }
1605
- const effectiveContext = buildDraftPersistenceContext();
1606
- const persistedAgent = await persistDraftAgent({
1607
- agentId: agent.id,
1608
- sessionId: editContext?.sessionId || undefined,
1609
- profileId: requestSettings.profileId,
1610
- mode: requestSettings.mode,
1611
- model: requestSettings.modelId || undefined,
1612
- name: agent.name,
1613
- context: effectiveContext,
1614
- });
1615
- pendingSettingsRef.current = null;
1616
- const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
1617
- effectiveContext;
1618
- setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
1619
- setAgent((prev) => {
1620
- const next = {
1621
- ...(prev || {}),
1622
- ...persistedAgent,
1623
- };
1624
- if (prev?.messages?.length && !persistedAgent.messages?.length) {
1625
- next.messages = prev.messages;
1626
- }
1627
- return next;
1628
- });
1629
- onAgentUpdate?.(persistedAgent);
1630
- return persistedAgent;
1631
- };
1632
- const handleAddSkill = useCallback(async (skillId) => {
1633
- if (selectedSkillSet.has(skillId.toLowerCase()))
1634
- return;
1635
- if (!agent?.id)
1636
- return;
1637
- try {
1638
- setSkillActionError(null);
1639
- const persistedAgent = await ensureDraftAgentPersisted();
1640
- await assignAgentSkill({ agentId: persistedAgent.id, skillId });
1641
- setAgent((prev) => {
1642
- if (!prev)
1643
- return prev;
1644
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1645
- ? prev.assignedSkillIds
1646
- : [];
1647
- if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
1648
- return prev;
1649
- }
1650
- return {
1651
- ...prev,
1652
- assignedSkillIds: [...currentAssigned, skillId],
1653
- };
1654
- });
1655
- await clearLegacySelectedSkillsFromMetadata();
1656
- return true;
1657
- }
1658
- catch (e) {
1659
- setSkillActionError(getSkillActionErrorMessage(e));
1660
- return false;
1661
- }
1662
- }, [
1663
- agent?.id,
1664
- assignAgentSkill,
1665
- clearLegacySelectedSkillsFromMetadata,
1666
- ensureDraftAgentPersisted,
1667
- getSkillActionErrorMessage,
1668
- setAgent,
1669
- selectedSkillSet,
1670
- ]);
1671
- const handleRemoveSkill = useCallback(async (skillId) => {
1672
- if (!agent?.id)
1673
- return;
1674
- try {
1675
- setSkillActionError(null);
1676
- await revokeAgentSkill({ agentId: agent.id, skillId });
1677
- setAgent((prev) => {
1678
- if (!prev)
1679
- return prev;
1680
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1681
- ? prev.assignedSkillIds
1682
- : [];
1683
- return {
1684
- ...prev,
1685
- assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
1686
- };
1687
- });
1688
- await clearLegacySelectedSkillsFromMetadata();
1689
- }
1690
- catch (e) {
1691
- setSkillActionError(getSkillActionErrorMessage(e));
1692
- }
1693
- }, [
1694
- agent?.id,
1695
- clearLegacySelectedSkillsFromMetadata,
1696
- getSkillActionErrorMessage,
1697
- revokeAgentSkill,
1698
- setAgent,
1699
- ]);
1700
- const handleOpenProfileSettings = useCallback(async () => {
1701
- if (!editContext || !activeProfile?.id)
1702
- return;
1703
- const lang = editContext.currentItemDescriptor?.language || "en";
1704
- await editContext.loadItem({
1705
- id: activeProfile.id,
1706
- language: lang,
1707
- version: 0,
1708
- });
1709
- editContext.switchWorkspace("editor");
1710
- }, [editContext, activeProfile?.id]);
1711
- const handleEditProfileSideBySide = useCallback(async () => {
1712
- if (!editContext || !activeProfile?.id)
1713
- return;
1714
- const lang = editContext.currentItemDescriptor?.language || "en";
1715
- const profileItem = {
1716
- id: activeProfile.id,
1717
- language: lang,
1718
- version: 0,
850
+ }
851
+ catch {
852
+ deterministicFlags = {
853
+ deterministic: false,
854
+ seed: undefined,
1719
855
  };
1720
- if (editContext.workspaceId === "agents") {
1721
- editContext.setShowAgentsWorkspaceEditor(true);
1722
- await editContext.loadItem(profileItem);
1723
- return;
1724
- }
1725
- await editContext.loadItem(profileItem, {
1726
- openInNewSlot: true,
1727
- });
1728
- editContext.switchWorkspace("editor");
1729
- }, [editContext, activeProfile?.id]);
1730
- const handleOpenSkillItem = useCallback(async (skillId) => {
1731
- if (!editContext || !skillId)
1732
- return;
1733
- const lang = editContext.currentItemDescriptor?.language || "en";
1734
- await editContext.loadItem({
1735
- id: skillId,
1736
- language: lang,
1737
- version: 0,
1738
- });
1739
- editContext.switchWorkspace("editor");
1740
- }, [editContext]);
856
+ }
1741
857
  useEffect(() => {
1742
- localStorageService.setItem("editor.agent.promptHistory", promptHistory);
858
+ localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
1743
859
  }, [promptHistory]);
1744
860
  useEffect(() => {
1745
861
  // Keep messagesRef synchronized with messages state
@@ -1751,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1751
867
  const [liveTotals, setLiveTotals] = useState(null);
1752
868
  // Context window status from backend (extracted from delta cost object)
1753
869
  const [contextWindowStatus, setContextWindowStatus] = useState(null);
1754
- const effectiveModelName = useMemo(() => {
1755
- const fromContextWindow = contextWindowStatus?.model?.trim();
1756
- if (fromContextWindow)
1757
- return fromContextWindow;
1758
- const fromAgent = agent?.model?.trim();
1759
- if (fromAgent)
1760
- return fromAgent;
1761
- const models = activeProfile?.models || [];
1762
- const selectedModelName = selectedModelId
1763
- ? models.find((m) => m.id === selectedModelId)?.name?.trim()
1764
- : undefined;
1765
- if (selectedModelName)
1766
- return selectedModelName;
1767
- const defaultModelName = activeProfile?.defaultModelId
1768
- ? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
1769
- : undefined;
1770
- if (defaultModelName)
1771
- return defaultModelName;
1772
- return models[0]?.name?.trim() || undefined;
1773
- }, [
1774
- contextWindowStatus?.model,
1775
- agent?.model,
1776
- activeProfile?.models,
1777
- activeProfile?.defaultModelId,
1778
- selectedModelId,
1779
- ]);
1780
870
  // Flag to track when we should create a new message
1781
871
  const shouldCreateNewMessage = useRef(false);
1782
872
  // Keep a ref to the current messages for immediate access
@@ -1787,17 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1787
877
  const placeholderInputRef = useRef(null);
1788
878
  const promptPlaceholderInputRef = useRef(null);
1789
879
  const messagesContainerRef = useRef(null);
1790
- const inlineDialogContainerRef = useRef(null);
1791
880
  const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
1792
- const [recentToolUiEvents, setRecentToolUiEvents] = useState([]);
1793
881
  // WebSocket subscription state for agent streaming
1794
882
  const seenMessageIdsRef = useRef(new Set());
1795
883
  const lastSeqRef = useRef(0);
1796
884
  const subscribedAgentIdRef = useRef(null);
1797
- const reconcileServerStateInFlightRef = useRef(false);
1798
- const toolCallFirstSeenAtRef = useRef({});
1799
- const pendingToolCompletionTimersRef = useRef({});
1800
- // Cache mode/model/profile changes made while the agent is still "new" (not yet persisted)
885
+ // Cache mode/model changes made while the agent is still "new" (not yet persisted)
1801
886
  const pendingSettingsRef = useRef(null);
1802
887
  // Track whether textarea should maintain focus during re-renders
1803
888
  const shouldMaintainFocusRef = useRef(false);
@@ -1819,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1819
904
  setIsVoiceSupported(!!SR);
1820
905
  if (SR === undefined) {
1821
906
  setIsVoiceDisabled(true);
1822
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
907
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1823
908
  return;
1824
909
  }
1825
910
  else {
1826
- const wasDisabled = localStorageService.getString(VOICE_DISABLED_KEY) === "true";
911
+ const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
1827
912
  setIsVoiceDisabled(wasDisabled);
1828
913
  }
1829
914
  }
@@ -1834,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1834
919
  // Auto-focus terminal input on mount
1835
920
  useEffect(() => {
1836
921
  if (textareaRef.current) {
1837
- try {
1838
- textareaRef.current.focus({ preventScroll: true });
1839
- }
1840
- catch {
1841
- textareaRef.current.focus();
1842
- }
922
+ textareaRef.current.focus();
1843
923
  }
1844
924
  }, []);
1845
925
  // Start voice recognition
@@ -1858,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1858
938
  r.lang = editContext?.currentItemDescriptor?.language || "en-US";
1859
939
  r.continuous = true;
1860
940
  r.interimResults = true;
1861
- promptBeforeVoiceRef.current = prompt;
1862
941
  r.onstart = () => {
1863
942
  setIsListening(true);
1864
943
  prevPlaceholderRef.current = inputPlaceholder;
1865
944
  setInputPlaceholder("Listening...");
1866
945
  };
1867
- // Desktop Chrome: single result at index 0, transitions interim→final once.
1868
- // Mobile Chrome: new result index per event, ALL immediately isFinal,
1869
- // each containing the full cumulative transcript. We always take the last
1870
- // non-empty final result and REPLACE the prompt to handle both patterns.
1871
946
  r.onresult = (event) => {
1872
- let lastFinalTranscript = "";
947
+ let finalText = "";
1873
948
  let interimText = "";
1874
- for (let i = event.results.length - 1; i >= 0; i--) {
949
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1875
950
  const res = event.results[i];
1876
- if (res.isFinal &&
1877
- !lastFinalTranscript &&
1878
- (res[0]?.transcript || "").trim()) {
1879
- lastFinalTranscript = res[0].transcript;
1880
- }
1881
- if (!res.isFinal) {
1882
- interimText = (res[0]?.transcript || "") + interimText;
1883
- }
951
+ if (res.isFinal)
952
+ finalText += res[0]?.transcript || "";
953
+ else
954
+ interimText += res[0]?.transcript || "";
1884
955
  }
1885
956
  if (interimText) {
1886
957
  setInputPlaceholder(`Listening... ${interimText.trim()}`);
@@ -1888,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1888
959
  else {
1889
960
  setInputPlaceholder("Listening...");
1890
961
  }
1891
- const base = promptBeforeVoiceRef.current;
1892
- const separator = base && !base.endsWith(" ") ? " " : "";
1893
- const recognized = lastFinalTranscript.trim();
1894
- if (recognized) {
1895
- setPrompt(base + separator + recognized + " ");
962
+ if (finalText && finalText.trim()) {
963
+ setPrompt((prev) => {
964
+ const prefix = prev && !prev.endsWith(" ") ? prev + " " : prev;
965
+ return (prefix || "") + finalText.trim() + " ";
966
+ });
1896
967
  if (textareaRef.current) {
1897
968
  try {
1898
969
  const v = textareaRef.current.value || "";
@@ -1908,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1908
979
  // network error also comes from incompatible chromium client
1909
980
  if (e?.error === "network") {
1910
981
  try {
1911
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
982
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1912
983
  setIsVoiceDisabled(true);
1913
984
  }
1914
985
  catch { }
@@ -2077,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2077
1148
  // Shared stream message handlers with messageId support
2078
1149
  const createNewStreamMessage = useCallback((messageId, agentData) => {
2079
1150
  const currentAgent = agentData || agent;
2080
- const effectiveAgentId = currentAgent?.id || agentStub.id;
1151
+ if (!currentAgent) {
1152
+ console.error("❌ createNewStreamMessage: No agent available", {
1153
+ messageId,
1154
+ agentData: !!agentData,
1155
+ agent: !!agent,
1156
+ });
1157
+ throw new Error("No agent available");
1158
+ }
2081
1159
  // Reduced: avoid verbose logging during streaming
2082
1160
  return {
2083
1161
  id: messageId,
2084
- agentId: effectiveAgentId,
1162
+ agentId: currentAgent.id,
2085
1163
  messageIndex: -1,
2086
1164
  role: "assistant",
2087
1165
  content: "",
2088
1166
  name: "agent",
2089
1167
  messageType: "streaming",
2090
1168
  isCompleted: false,
2091
- model: currentAgent?.model || "",
1169
+ model: currentAgent.model || "",
2092
1170
  tokensUsed: 0,
2093
1171
  inputTokens: 0,
2094
1172
  outputTokens: 0,
@@ -2097,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2097
1175
  outputTokenCost: 0,
2098
1176
  cachedInputTokenCost: 0,
2099
1177
  totalCost: 0,
2100
- currency: currentAgent?.currency || "USD",
1178
+ currency: currentAgent.currency || "USD",
2101
1179
  createdDate: new Date().toISOString(),
2102
1180
  toolCalls: [],
2103
1181
  };
2104
- }, [agent, agentStub.id]);
2105
- const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
2106
- const handleHeartbeatMessage = useCallback((message) => {
2107
- const heartbeatText = (message.data?.message ||
2108
- message.data?.Message ||
2109
- "Still working on your request. This is taking a little longer than usual.").trim() ||
2110
- "Still working on your request. This is taking a little longer than usual.";
2111
- const createdDate = message.timestamp || new Date().toISOString();
2112
- const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
2113
- setMessages((prev) => {
2114
- const withoutHeartbeats = stripHeartbeatMessages(prev);
2115
- const updated = [
2116
- ...withoutHeartbeats,
2117
- {
2118
- id: heartbeatId,
2119
- agentId: agent?.id || agentStub.id,
2120
- messageIndex: -1,
2121
- role: "system",
2122
- content: heartbeatText,
2123
- name: "system",
2124
- messageType: "heartbeat",
2125
- isCompleted: true,
2126
- model: "",
2127
- tokensUsed: 0,
2128
- inputTokens: 0,
2129
- outputTokens: 0,
2130
- cachedInputTokens: 0,
2131
- inputTokenCost: 0,
2132
- outputTokenCost: 0,
2133
- cachedInputTokenCost: 0,
2134
- totalCost: 0,
2135
- currency: "USD",
2136
- createdDate,
2137
- toolCalls: [],
2138
- },
2139
- ];
2140
- messagesRef.current = updated;
2141
- return updated;
2142
- });
2143
- }, [agent?.id, agentStub.id, stripHeartbeatMessages]);
2144
- const clearHeartbeatMessages = useCallback(() => {
2145
- setMessages((prev) => {
2146
- const updated = stripHeartbeatMessages(prev);
2147
- if (updated.length === prev.length) {
2148
- return prev;
2149
- }
2150
- messagesRef.current = updated;
2151
- return updated;
2152
- });
2153
- }, [stripHeartbeatMessages]);
1182
+ }, [agent]);
2154
1183
  const handleContentChunk = useCallback((message, agentData) => {
2155
1184
  // Get messageId from data, or generate one from agent ID (for backward compatibility)
2156
1185
  // If no messageId is provided, we'll use the last assistant message or create a new one
2157
1186
  let messageId = message.data?.messageId;
2158
1187
  if (!messageId && agentData?.id) {
2159
- console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
2160
- agentId: agentData.id,
2161
- isIncremental: message.data?.isIncremental,
2162
- previousContentLength: message.data?.previousContentLength,
2163
- totalContentLength: message.data?.totalContentLength,
2164
- });
2165
1188
  // For backward compatibility: if no messageId, find or create the current streaming message
2166
1189
  // This handles cases where the backend doesn't send messageId
2167
1190
  const currentMessages = messagesRef.current;
@@ -2175,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2175
1198
  // If the agent isn't currently running (e.g., we switched tabs after the run
2176
1199
  // completed), skip creating a new streaming message to avoid duplicates.
2177
1200
  const currentAgentStatus = (agentData || agent)?.status;
2178
- const isAgentRunning = currentAgentStatus === "running";
1201
+ const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
2179
1202
  if (!isAgentRunning) {
2180
1203
  return;
2181
1204
  }
@@ -2208,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2208
1231
  outputCost: Number(cost.output) || 0,
2209
1232
  cachedCost: Number(cost.cached) || 0,
2210
1233
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2211
- imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
2212
- 0,
2213
1234
  totalCost: Number(cost.total) || 0,
2214
1235
  currency: "USD",
2215
1236
  };
@@ -2233,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2233
1254
  setIsWaitingForResponse(false);
2234
1255
  shouldCreateNewMessage.current = false;
2235
1256
  }
2236
- // Extract context window info from cost object.
2237
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2238
- // so we must check for null/undefined instead of truthiness.
2239
- const contextWindowRaw = cost.contextWindow;
2240
- const contextUsedRaw = cost.contextUsed;
2241
- if (contextWindowRaw !== undefined &&
2242
- contextWindowRaw !== null &&
2243
- contextUsedRaw !== undefined &&
2244
- contextUsedRaw !== null) {
2245
- const contextWindowValue = Number(contextWindowRaw);
2246
- const contextUsedValue = Number(contextUsedRaw);
2247
- if (contextWindowValue > 0 &&
2248
- Number.isFinite(contextUsedValue) &&
2249
- contextUsedValue >= 0) {
1257
+ // Extract context window info from cost object
1258
+ if (cost.contextWindow && cost.contextUsed) {
1259
+ const contextWindowValue = Number(cost.contextWindow);
1260
+ const contextUsedValue = Number(cost.contextUsed);
1261
+ if (contextWindowValue > 0 && contextUsedValue >= 0) {
2250
1262
  setContextWindowStatus({
2251
1263
  contextWindowTokens: contextWindowValue,
2252
1264
  estimatedInputTokens: contextUsedValue,
@@ -2262,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2262
1274
  const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
2263
1275
  if (existingMessageIndex === -1) {
2264
1276
  // Message doesn't exist - create new streaming message
2265
- const previousContentLength = message.data?.previousContentLength || 0;
2266
- if (message.data?.isIncremental && previousContentLength > 0) {
2267
- console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
2268
- messageId,
2269
- previousContentLength,
2270
- totalContentLength: message.data?.totalContentLength,
2271
- deltaLength: (message.data?.deltaContent || "").length,
2272
- });
2273
- }
2274
1277
  const newStreamMessage = createNewStreamMessage(messageId, agentData);
2275
1278
  // Set the content for the new message
2276
1279
  const updatedNewMessage = { ...newStreamMessage };
@@ -2293,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2293
1296
  return prev;
2294
1297
  // Check if existing content is already longer than what we're trying to stream
2295
1298
  const currentContentLength = existingMessage.content?.length || 0;
2296
- const previousContentLength = message.data?.previousContentLength || 0;
2297
1299
  const totalContentLength = message.data?.totalContentLength || 0;
2298
- if (message.data?.isIncremental &&
2299
- previousContentLength !== currentContentLength &&
2300
- (previousContentLength > 0 || currentContentLength > 0)) {
2301
- console.warn("[AgentTerminal] Content chunk length mismatch", {
2302
- messageId,
2303
- previousContentLength,
2304
- currentContentLength,
2305
- totalContentLength,
2306
- deltaLength: (message.data?.deltaContent || "").length,
2307
- });
2308
- }
2309
- if (message.data?.isIncremental &&
2310
- currentContentLength >= totalContentLength &&
1300
+ if (currentContentLength >= totalContentLength &&
2311
1301
  totalContentLength > 0) {
2312
1302
  return prev;
2313
1303
  }
@@ -2331,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2331
1321
  });
2332
1322
  }, [createNewStreamMessage, agent]);
2333
1323
  const handleToolCall = useCallback((message, agentData) => {
2334
- const extractedToolCall = extractToolCallFields(message.data);
2335
- const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1324
+ const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2336
1325
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2337
1326
  let toolCallMessageId = message.data?.messageId;
2338
1327
  if (!toolCallMessageId) {
2339
- console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
2340
- toolCallId,
2341
- toolName: message.data?.name || message.data?.displayName,
2342
- });
2343
1328
  const current = messagesRef.current;
2344
1329
  const lastStreaming = [...current]
2345
1330
  .reverse()
@@ -2347,13 +1332,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2347
1332
  if (lastStreaming?.id) {
2348
1333
  toolCallMessageId = lastStreaming.id;
2349
1334
  }
2350
- else {
2351
- // Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
2352
- // Create a synthetic streaming message so the UI can render the tool call immediately.
2353
- toolCallMessageId = crypto.randomUUID();
2354
- }
2355
1335
  }
2356
- appendToolUiEvent("ui:tool-call-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId || "none"} providedMessageId=${String(message.data?.messageId || "none")}`);
2357
1336
  // Find or create the target message for this tool call
2358
1337
  if (toolCallMessageId) {
2359
1338
  const currentMessages = messagesRef.current;
@@ -2380,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2380
1359
  }
2381
1360
  // Add tool call to the message in the array
2382
1361
  if (toolCallMessageId && message.data && toolCallId) {
2383
- const toolCallError = message.data.functionError || message.data.error || "";
2384
- const isPruned = !!message.data?.isPruned || /^PRUNED$/i.test(String(toolCallError));
2385
- const toolCallCreatedDate = message.data.createdDate ||
2386
- message.timestamp ||
2387
- new Date().toISOString();
1362
+ const functionName = message.data.functionName ||
1363
+ message.data.name ||
1364
+ message.data.function?.name ||
1365
+ "unknown";
2388
1366
  const toolCall = {
2389
1367
  id: toolCallId,
2390
1368
  messageId: toolCallMessageId,
2391
1369
  dbMessageId: message.data.messageId, // Database message ID for approval/rejection
2392
1370
  toolCallId: toolCallId,
2393
- functionName: extractedToolCall.functionName,
2394
- functionArguments: extractedToolCall.functionArguments,
1371
+ functionName: functionName,
1372
+ functionArguments: message.data.functionArguments ||
1373
+ message.data.arguments ||
1374
+ JSON.stringify(message.data.function?.arguments || {}),
2395
1375
  functionResult: message.data.functionResult || message.data.result || "",
2396
- functionResultRichContent: message.data.richContent || undefined,
2397
- functionError: toolCallError,
2398
- isPruned,
1376
+ functionError: message.data.functionError || message.data.error || "",
2399
1377
  isCompleted: false,
2400
1378
  responseTimeMs: message.data.responseTimeMs,
2401
- createdDate: toolCallCreatedDate,
1379
+ createdDate: new Date().toISOString(),
2402
1380
  requiresApproval: message.data?.requiresApproval,
2403
1381
  };
2404
1382
  // Check for existing tool call - search across ALL messages by toolCallId first
@@ -2437,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2437
1415
  // Check if the new data has more information than what we have
2438
1416
  const newArgs = toolCall.functionArguments;
2439
1417
  const existingArgs = existingToolCall.functionArguments;
2440
- const newArgsText = stringifyToolField(newArgs) || "";
2441
- const existingArgsText = stringifyToolField(existingArgs) || "";
2442
- const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
2443
- newArgsText !== existingArgsText) ||
2444
- (existingArgsText === "{}" && newArgsText !== "{}");
1418
+ const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
2445
1419
  const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
2446
- const hasNewRichContent = toolCall.functionResultRichContent &&
2447
- !existingToolCall.functionResultRichContent;
2448
1420
  const hasNewError = toolCall.functionError && !existingToolCall.functionError;
2449
1421
  const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
2450
1422
  const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
2451
1423
  // Only update if there's meaningful new data
2452
1424
  if (hasMoreCompleteArgs ||
2453
1425
  hasNewResult ||
2454
- hasNewRichContent ||
2455
1426
  hasNewError ||
2456
1427
  hasNewApprovalInfo ||
2457
1428
  hasNewDbMessageId) {
@@ -2468,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2468
1439
  updatedToolCalls[idx] = {
2469
1440
  ...existing,
2470
1441
  functionArguments: hasMoreCompleteArgs
2471
- ? newArgsText
2472
- : existingArgsText || existing.functionArguments,
1442
+ ? newArgs
1443
+ : existing.functionArguments,
2473
1444
  functionResult: toolCall.functionResult || existing.functionResult,
2474
- functionResultRichContent: toolCall.functionResultRichContent ||
2475
- existing.functionResultRichContent,
2476
1445
  functionError: toolCall.functionError || existing.functionError,
2477
1446
  requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
2478
1447
  };
@@ -2490,36 +1459,27 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2490
1459
  }
2491
1460
  return; // Done updating existing tool call
2492
1461
  }
2493
- flushSync(() => {
2494
- setMessages((prev) => {
2495
- const updated = prev.map((msg) => {
2496
- if (msg.id !== toolCallMessageId)
2497
- return msg;
2498
- const existingToolCalls = msg.toolCalls || [];
2499
- return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
2500
- });
2501
- messagesRef.current = updated;
2502
- return updated;
1462
+ setMessages((prev) => {
1463
+ const updated = prev.map((msg) => {
1464
+ if (msg.id !== toolCallMessageId)
1465
+ return msg;
1466
+ const existingToolCalls = msg.toolCalls || [];
1467
+ return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
2503
1468
  });
1469
+ messagesRef.current = updated;
1470
+ return updated;
2504
1471
  });
2505
- const messageWithToolCall = messagesRef.current.find((msg) => msg.id === toolCallMessageId);
2506
- appendToolUiEvent("ui:tool-call-attached", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId} messageToolCalls=${messageWithToolCall?.toolCalls?.length || 0} assistantMessages=${messagesRef.current.filter((msg) => msg.role === "assistant").length}`);
2507
1472
  // If tool requires approval, agent is now waiting for user action - stop thinking
2508
1473
  if (message.data?.requiresApproval) {
2509
1474
  setIsAgentThinking(false);
2510
1475
  }
2511
1476
  }
2512
- }, [appendToolUiEvent, createNewStreamMessage]);
1477
+ }, [createNewStreamMessage]);
2513
1478
  const handleToolResult = useCallback((message, agentData) => {
2514
- const extractedToolCall = extractToolCallFields(message.data);
2515
- const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1479
+ const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2516
1480
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2517
1481
  let resultMessageId = message.data?.messageId;
2518
1482
  if (!resultMessageId) {
2519
- console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
2520
- toolCallId: resultToolCallId,
2521
- toolName: message.data?.functionName || message.data?.displayName,
2522
- });
2523
1483
  const current = messagesRef.current;
2524
1484
  const lastStreaming = [...current]
2525
1485
  .reverse()
@@ -2541,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2541
1501
  outputCost: Number(cost.output) || 0,
2542
1502
  cachedCost: Number(cost.cached) || 0,
2543
1503
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2544
- imageCost: Number(cost.imageCost) || 0,
2545
1504
  totalCost: Number(cost.total) || 0,
2546
1505
  currency: "USD",
2547
1506
  };
@@ -2553,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2553
1512
  if (anyNonZero) {
2554
1513
  setLiveTotals(nextTotals);
2555
1514
  }
2556
- // Extract context window info from cost object.
2557
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2558
- // so we must check for null/undefined instead of truthiness.
2559
- const contextWindowRaw = cost.contextWindow;
2560
- const contextUsedRaw = cost.contextUsed;
2561
- if (contextWindowRaw !== undefined &&
2562
- contextWindowRaw !== null &&
2563
- contextUsedRaw !== undefined &&
2564
- contextUsedRaw !== null) {
2565
- const contextWindowValue = Number(contextWindowRaw);
2566
- const contextUsedValue = Number(contextUsedRaw);
2567
- if (contextWindowValue > 0 &&
2568
- Number.isFinite(contextUsedValue) &&
2569
- contextUsedValue >= 0) {
1515
+ // Extract context window info from cost object
1516
+ if (cost.contextWindow && cost.contextUsed) {
1517
+ const contextWindowValue = Number(cost.contextWindow);
1518
+ const contextUsedValue = Number(cost.contextUsed);
1519
+ if (contextWindowValue > 0 && contextUsedValue >= 0) {
2570
1520
  setContextWindowStatus({
2571
1521
  contextWindowTokens: contextWindowValue,
2572
1522
  estimatedInputTokens: contextUsedValue,
@@ -2590,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2590
1540
  outputCost: Number(data.totalOutputTokenCost) || 0,
2591
1541
  cachedCost: Number(data.totalCachedTokenCost) || 0,
2592
1542
  cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
2593
- imageCost: Number(data.totalImageCost) || 0,
2594
1543
  totalCost: Number(data.totalCost) || 0,
2595
1544
  currency: data.currency || "USD",
2596
1545
  };
@@ -2606,10 +1555,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2606
1555
  }
2607
1556
  // Update tool result directly in the messages array
2608
1557
  if (!resultMessageId) {
2609
- appendToolUiEvent("ui:tool-result-dropped", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} reason=no-result-message-id`);
2610
1558
  return;
2611
1559
  }
2612
- appendToolUiEvent("ui:tool-result-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId}`);
2613
1560
  // Update the message with tool result
2614
1561
  setMessages((prev) => {
2615
1562
  const updated = prev.map((msg) => {
@@ -2625,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2625
1572
  const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
2626
1573
  if (existingToolCall && message.data) {
2627
1574
  const updatedToolCalls = [...updatedMessage.toolCalls];
2628
- const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
2629
- const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
2630
- const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
2631
- nextArgsText !== existingArgsText) ||
2632
- (existingArgsText === "{}" && nextArgsText !== "{}");
2633
1575
  const toolCall = {
2634
1576
  id: existingToolCall.id,
2635
1577
  messageId: existingToolCall.messageId,
2636
1578
  toolCallId: existingToolCall.toolCallId,
2637
1579
  functionName: existingToolCall.functionName,
2638
- functionArguments: hasMoreCompleteArgs
2639
- ? nextArgsText
2640
- : existingToolCall.functionArguments,
1580
+ functionArguments: existingToolCall.functionArguments,
2641
1581
  functionResult: message.data.functionResult || message.data.result || "",
2642
- functionResultRichContent: message.data.richContent ||
2643
- existingToolCall.functionResultRichContent,
2644
1582
  functionError: message.data.functionError || message.data.error || "",
2645
1583
  isCompleted: true,
2646
1584
  responseTimeMs: message.data.responseTimeMs,
@@ -2657,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2657
1595
  }
2658
1596
  else if (message.data && resultToolCallId && resultMessageId) {
2659
1597
  // Create new tool call if it doesn't exist
2660
- const toolCallCreatedDate = message.data.createdDate ||
2661
- message.timestamp ||
2662
- new Date().toISOString();
1598
+ const functionName = message.data.functionName ||
1599
+ message.data.name ||
1600
+ message.data.function?.name ||
1601
+ "unknown";
2663
1602
  const toolCall = {
2664
1603
  id: resultToolCallId,
2665
1604
  messageId: resultMessageId,
2666
1605
  toolCallId: resultToolCallId,
2667
- functionName: extractedToolCall.functionName,
2668
- functionArguments: extractedToolCall.functionArguments,
1606
+ functionName: functionName,
1607
+ functionArguments: message.data.functionArguments ||
1608
+ message.data.arguments ||
1609
+ JSON.stringify(message.data.function?.arguments || {}),
2669
1610
  functionResult: message.data.functionResult || message.data.result || "",
2670
- functionResultRichContent: message.data.richContent || undefined,
2671
1611
  functionError: message.data.functionError || message.data.error || "",
2672
1612
  isCompleted: true,
2673
1613
  responseTimeMs: message.data.responseTimeMs,
2674
- createdDate: toolCallCreatedDate,
1614
+ createdDate: new Date().toISOString(),
2675
1615
  };
2676
1616
  updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
2677
1617
  }
@@ -2679,12 +1619,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2679
1619
  return updatedMessage;
2680
1620
  });
2681
1621
  messagesRef.current = updated;
2682
- const messageWithToolResult = updated.find((msg) => msg.id === resultMessageId);
2683
- const matchingToolCall = messageWithToolResult?.toolCalls?.find((tc) => tc.toolCallId === resultToolCallId);
2684
- appendToolUiEvent("ui:tool-result-applied", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId} completed=${matchingToolCall?.isCompleted ? "yes" : "no"} messageToolCalls=${messageWithToolResult?.toolCalls?.length || 0}`);
2685
1622
  return updated;
2686
1623
  });
2687
- }, [appendToolUiEvent]);
1624
+ }, []);
2688
1625
  // Listen for local approval resolution to update UI
2689
1626
  useEffect(() => {
2690
1627
  const onApprovalResolved = (ev) => {
@@ -2701,11 +1638,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2701
1638
  if (!messageId || !toolCallId)
2702
1639
  return;
2703
1640
  // Update local state to reflect approval status
2704
- const currentMessages = messagesRef.current || [];
2705
- const hasMatchingToolCall = currentMessages.some((m) => (m.toolCalls || []).some((tc) => tc.toolCallId === toolCallId));
2706
- if (!hasMatchingToolCall) {
2707
- return;
2708
- }
2709
1641
  setMessages((prev) => {
2710
1642
  const updated = prev.map((m) => {
2711
1643
  // Update tool calls in ANY message that contains this toolCallId
@@ -2770,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2770
1702
  // The agent might have been persisted after sending a prompt
2771
1703
  // Only treat as "new" if backend returns 404
2772
1704
  const hasExistingMessages = messagesRef.current.length > 0;
2773
- if (agentStub.status === "new" &&
2774
- !agentStub.userId &&
2775
- !hasExistingMessages) {
1705
+ if (agentStub.status === "new" && !hasExistingMessages) {
2776
1706
  // Only initialize as new if we have no messages yet (initial mount)
2777
1707
  // Derive initial profile from provided metadata if present
2778
1708
  const initialProfileIdFromMeta = (() => {
@@ -2812,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2812
1742
  totalInputTokenCost: 0,
2813
1743
  totalOutputTokenCost: 0,
2814
1744
  totalCachedInputTokenCost: 0,
2815
- totalImageCost: 0,
2816
1745
  totalCost: 0,
2817
1746
  messageCount: 0,
2818
1747
  });
@@ -2855,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2855
1784
  }
2856
1785
  })();
2857
1786
  // Create context with top-level properties (what ContextInfoBar expects)
2858
- const localCtx = item && shouldSeedContext ? buildEditorContextPayload(item) : null;
1787
+ const localCtx = item && shouldSeedContext
1788
+ ? {
1789
+ items: [
1790
+ {
1791
+ id: item.id,
1792
+ language: item.language,
1793
+ version: item.version,
1794
+ name: editContext?.item?.name,
1795
+ path: editContext?.item?.path,
1796
+ },
1797
+ ],
1798
+ components: editContext?.selection?.length && item
1799
+ ? editContext.selection.map((componentId) => ({
1800
+ componentId,
1801
+ pageItem: {
1802
+ id: item.id,
1803
+ language: item.language,
1804
+ version: item.version,
1805
+ name: editContext?.item?.name,
1806
+ },
1807
+ }))
1808
+ : undefined,
1809
+ field: fieldsContext?.focusedField?.fieldId &&
1810
+ fieldsContext.focusedField?.item?.id
1811
+ ? {
1812
+ fieldId: fieldsContext.focusedField.fieldId,
1813
+ fieldName: fieldsContext.focusedField
1814
+ .fieldName,
1815
+ item: {
1816
+ id: fieldsContext.focusedField.item.id,
1817
+ language: fieldsContext.focusedField.item.language ||
1818
+ editContext?.currentItemDescriptor?.language ||
1819
+ "en",
1820
+ version: fieldsContext.focusedField.item.version ??
1821
+ editContext?.currentItemDescriptor?.version ??
1822
+ 0,
1823
+ name: editContext?.item?.name,
1824
+ },
1825
+ }
1826
+ : undefined,
1827
+ }
1828
+ : null;
2859
1829
  let nextMetadata = null;
2860
1830
  if (initialMetadata) {
2861
1831
  // Merge initial metadata with local context (using top-level structure)
@@ -2931,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2931
1901
  seenMessageIdsRef.current.add(msg.id.toLowerCase());
2932
1902
  }
2933
1903
  });
2934
- // Keep local streaming if the agent is still active (running/waiting); otherwise discard locals.
2935
- // This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
2936
- // "waiting" but we still want to keep the in-flight tool call UI visible.
1904
+ // Keep local streaming only if the agent is still running; otherwise discard locals
2937
1905
  const isRunning = agentData.status === "running" || agentData.status === 1;
2938
- const isWaiting = agentData.status === "waitingForApproval" ||
2939
- agentData.status === 2 ||
2940
- agentData.status === "waitingForInput" ||
2941
- agentData.status === "costLimitReached" ||
2942
- agentData.status === 7;
2943
- const keepLocalStreaming = isRunning || isWaiting;
2944
1906
  // Filter local messages to only include streaming/incomplete messages that aren't in DB
2945
1907
  // This prevents duplicates when reloading - DB messages are source of truth for completed messages
2946
1908
  const localStreaming = isRunning
@@ -2958,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2958
1920
  // Don't keep completed messages from local state - DB is source of truth
2959
1921
  return false;
2960
1922
  })
2961
- : keepLocalStreaming
2962
- ? messagesRef.current.filter((localMsg) => {
2963
- if (!localMsg.id)
2964
- return false;
2965
- if (!localMsg.isCompleted &&
2966
- localMsg.messageType === "streaming") {
2967
- const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
2968
- dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
2969
- return !existsInDb;
2970
- }
2971
- return false;
2972
- })
2973
- : [];
1923
+ : [];
2974
1924
  const merged = mergeMessagesById(dbMessages, localStreaming);
2975
1925
  messagesRef.current = merged;
2976
1926
  setMessages(merged);
@@ -2981,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2981
1931
  const runningNow = agentData.status === "running" || agentData.status === 1;
2982
1932
  const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
2983
1933
  agentData.status === 2;
2984
- const waitingForInputNow = agentData.status === "waitingForInput";
2985
1934
  const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
2986
1935
  if (runningNow || hasStreamingNow) {
2987
1936
  setIsWaitingForResponse(true);
@@ -2989,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2989
1938
  // Agent is actively running, show thinking dots
2990
1939
  setIsAgentThinking(true);
2991
1940
  }
2992
- else if (waitingForApprovalNow || waitingForInputNow) {
1941
+ else if (waitingForApprovalNow) {
2993
1942
  setIsWaitingForResponse(false);
2994
1943
  isWaitingRef.current = false;
2995
1944
  setIsConnecting(false);
2996
- // Agent is waiting for user input/approval, hide thinking dots
1945
+ // Agent is waiting for user approval, hide thinking dots
2997
1946
  setIsAgentThinking(false);
2998
1947
  }
2999
1948
  else {
@@ -3042,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3042
1991
  if (!contextJson)
3043
1992
  return null;
3044
1993
  const parsedContext = JSON.parse(contextJson);
1994
+ // Context is stored as flat structure with top-level properties
1995
+ // Due to C# [JsonExtensionData], AdditionalData entries are serialized at top level
1996
+ // We need to normalize: move todoList from top level into additionalData if present
3045
1997
  if (parsedContext && typeof parsedContext === "object") {
3046
- return parsedContext;
1998
+ const normalized = { ...parsedContext };
1999
+ // If todoList is at top level but not in additionalData, move it
2000
+ if (normalized.todoList && !normalized.additionalData?.todoList) {
2001
+ normalized.additionalData = {
2002
+ ...(normalized.additionalData || {}),
2003
+ todoList: normalized.todoList,
2004
+ };
2005
+ }
2006
+ return normalized;
3047
2007
  }
3048
2008
  return null;
3049
2009
  }
@@ -3089,21 +2049,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3089
2049
  setIsLoading(false);
3090
2050
  }
3091
2051
  }, [agentStub.id]);
3092
- const reconcileServerStateRef = useRef(null);
3093
- useEffect(() => {
3094
- reconcileServerStateRef.current = async () => {
3095
- if (reconcileServerStateInFlightRef.current) {
3096
- return;
3097
- }
3098
- reconcileServerStateInFlightRef.current = true;
3099
- try {
3100
- await loadAgent();
3101
- }
3102
- finally {
3103
- reconcileServerStateInFlightRef.current = false;
3104
- }
3105
- };
3106
- }, [loadAgent]);
3107
2052
  // Initial load
3108
2053
  useEffect(() => {
3109
2054
  loadAgent();
@@ -3125,9 +2070,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3125
2070
  setAgentOperations([]);
3126
2071
  return;
3127
2072
  }
3128
- setAgentOperations([]);
3129
2073
  try {
3130
- const result = await getAgentHistory(agent.id, AGENT_HISTORY_LIMIT);
2074
+ const result = await getAgentHistory(agent.id, 1000);
3131
2075
  if (result.type === "success") {
3132
2076
  setAgentOperations(result.data ?? []);
3133
2077
  }
@@ -3145,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3145
2089
  return;
3146
2090
  }
3147
2091
  // Check if cost limit exceeded based on status or cost values
3148
- const statusIndicatesLimit = agent.status === "costLimitReached";
2092
+ const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
3149
2093
  // Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
3150
2094
  const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
3151
2095
  const costExceedsLimit = agent.costLimit &&
@@ -3230,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3230
2174
  // Handle agent:profile:switched (profile changed via switch-profile function)
3231
2175
  if (messageType === "agent:profile:switched") {
3232
2176
  const payload = message.payload || {};
3233
- const switchedAgentId = payload.agentId;
3234
- const newProfileId = payload.newProfileId;
3235
- const newProfileName = payload.newProfileName;
2177
+ const switchedAgentId = payload.agentId || payload.AgentId;
2178
+ const newProfileId = payload.newProfileId || payload.NewProfileId;
2179
+ const newProfileName = payload.newProfileName || payload.NewProfileName;
3236
2180
  if (switchedAgentId === agent.id && newProfileId) {
3237
2181
  // Update the agent's profile
3238
2182
  setAgent((prev) => {
@@ -3263,15 +2207,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3263
2207
  const op = message.payload;
3264
2208
  const operationAgentId = op.agentId || op.user?.agentId;
3265
2209
  if (operationAgentId === agent.id) {
3266
- setAgentOperations((previous) => mergeAgentOperationHistory(previous, [op]));
2210
+ // Reload operations when this agent performs an operation
2211
+ getAgentHistory(agent.id, 1000).then((result) => {
2212
+ if (result.type === "success") {
2213
+ setAgentOperations(result.data ?? []);
2214
+ }
2215
+ });
3267
2216
  }
3268
2217
  return;
3269
2218
  }
3270
2219
  // For other agent messages, check if this is for our agent
3271
- const agentId = message.payload?.agentId;
3272
- if (agentId !== agent.id) {
2220
+ const agentId = message.payload?.agentId || message.payload?.AgentId;
2221
+ if (agentId !== agent.id)
3273
2222
  return;
3274
- }
3275
2223
  // Handle agent:run:start
3276
2224
  if (messageType === "agent:run:start") {
3277
2225
  // If a stop operation is in progress, ignore this message to prevent
@@ -3280,15 +2228,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3280
2228
  console.log("[AgentTerminal] Ignoring agent:run:start during stop operation");
3281
2229
  return;
3282
2230
  }
3283
- const currentStatus = agentRef.current?.status;
3284
- if (currentStatus === "waitingForInput" ||
3285
- currentStatus === "waitingForApproval" ||
3286
- currentStatus === "costLimitReached") {
3287
- // Replayed start messages arrive before the buffered status payload when
3288
- // reopening an already-paused agent. Preserve the attention state instead
3289
- // of flashing "running" until the follow-up status update lands.
3290
- return;
3291
- }
3292
2231
  // Reset run-scoped deduplication so new run seq values (starting at 1)
3293
2232
  // are not discarded due to previous run's lastSeqRef
3294
2233
  lastSeqRef.current = 0;
@@ -3384,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3384
2323
  if (messageType === "agent:prompt:queued") {
3385
2324
  const { queueEntry } = message.payload;
3386
2325
  if (queueEntry) {
3387
- if (shouldSuppressQueuedPrompt(queueEntry)) {
3388
- return;
3389
- }
3390
2326
  // Add the new prompt to the queued prompts list
3391
2327
  setQueuedPrompts((prev) => {
3392
2328
  // Check if prompt already exists (deduplication)
@@ -3415,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3415
2351
  return;
3416
2352
  }
3417
2353
  const { seq, type, data, cost } = message.payload;
3418
- if (type === "ToolCall" || type === "toolCall") {
3419
- }
3420
2354
  // Always allow ContextUpdate messages (metadata updates) regardless of agent status
3421
2355
  const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
3422
- const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
3423
- const shouldClearHeartbeat = type === "ContentChunk" ||
3424
- type === "contentChunk" ||
3425
- type === "ToolCall" ||
3426
- type === "toolCall" ||
3427
- type === "ToolResult" ||
3428
- type === "toolResult";
3429
2356
  // Allow deltas if the agent is running OR if we already have an active streaming message
3430
2357
  // OR if the server provided a messageId for targeted updates.
3431
2358
  // This avoids dropping early deltas that may arrive before the 'running' status update.
3432
2359
  // ContextUpdate messages are always allowed as they're metadata updates, not content.
3433
2360
  const isRunning = agent.status === "running" || agent.status === 1;
3434
2361
  const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
3435
- const hasMessageId = !!message?.payload?.data?.messageId;
3436
- if (!isContextUpdate &&
3437
- !isHeartbeat &&
3438
- !isRunning &&
3439
- !hasStreaming &&
3440
- !hasMessageId) {
2362
+ const hasMessageId = !!message?.payload?.data?.messageId ||
2363
+ !!message?.payload?.data?.MessageId;
2364
+ if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
3441
2365
  return; // ignore only if we cannot safely target an existing streaming message
3442
2366
  }
3443
2367
  // Deduplicate by sequence
@@ -3454,23 +2378,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3454
2378
  cost,
3455
2379
  timestamp: new Date().toISOString(),
3456
2380
  };
3457
- if (shouldClearHeartbeat) {
3458
- clearHeartbeatMessages();
3459
- }
3460
2381
  if (type === "ContentChunk" || type === "contentChunk") {
3461
2382
  handleContentChunk(agentStreamMessage, agent);
3462
2383
  }
3463
2384
  else if (type === "ToolCall" || type === "toolCall") {
3464
- appendToolUiEvent("ui:tool-delta-received", `ToolCall:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
3465
2385
  handleToolCall(agentStreamMessage, agent);
3466
2386
  }
3467
2387
  else if (type === "ToolResult" || type === "toolResult") {
3468
- appendToolUiEvent("ui:tool-delta-received", `ToolResult:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
3469
2388
  handleToolResult(agentStreamMessage, agent);
3470
2389
  }
3471
- else if (type === "Heartbeat" || type === "heartbeat") {
3472
- handleHeartbeatMessage(agentStreamMessage);
3473
- }
3474
2390
  else if (type === "ContextUpdate" || type === "contextUpdate") {
3475
2391
  // Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
3476
2392
  const contextData = data;
@@ -3480,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3480
2396
  const current = (prev || {});
3481
2397
  const updated = { ...current };
3482
2398
  // Merge additionalData if present (deep merge to preserve existing values)
3483
- if (contextData.additionalData) {
3484
- const sourceAdditionalData = contextData.additionalData || {};
2399
+ if (contextData.additionalData || contextData.AdditionalData) {
2400
+ const sourceAdditionalData = contextData.additionalData ||
2401
+ contextData.AdditionalData ||
2402
+ {};
3485
2403
  updated.additionalData = {
3486
2404
  ...(current.additionalData || {}),
3487
2405
  ...sourceAdditionalData,
@@ -3489,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3489
2407
  }
3490
2408
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3491
2409
  // Backend sends the complete updated list, not just additions
3492
- if (contextData.childAgents) {
3493
- const childAgents = contextData.childAgents;
2410
+ if (contextData.ChildAgents || contextData.childAgents) {
2411
+ const childAgents = contextData.ChildAgents || contextData.childAgents;
3494
2412
  if (Array.isArray(childAgents)) {
3495
- updated.childAgents = childAgents;
2413
+ updated.ChildAgents = childAgents;
3496
2414
  }
3497
2415
  }
3498
2416
  return updated;
@@ -3518,19 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3518
2436
  // Route based on statusData.state
3519
2437
  try {
3520
2438
  // Normalize various status shapes and handle Cancelled uniformly
3521
- const normalizedStatus = parseAgentStatus(statusData?.state) ||
3522
- parseAgentStatus(statusData?.status);
3523
- if (normalizedStatus === "idle") {
3524
- clearStopGuard();
2439
+ const normalizedStatus = statusData?.state ||
2440
+ statusData?.Status ||
2441
+ statusData?.status;
2442
+ if (normalizedStatus === "Cancelled" ||
2443
+ normalizedStatus === "canceled") {
3525
2444
  // Stop indicators and mark any in-progress streaming messages as completed
3526
- clearHeartbeatMessages();
3527
- setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3528
2445
  setIsWaitingForResponse(false);
3529
2446
  isWaitingRef.current = false;
3530
2447
  setIsConnecting(false);
3531
- setIsSubmitting(false);
3532
- shouldCreateNewMessage.current = false;
3533
- setIsAgentThinking(false);
3534
2448
  setMessages((prev) => {
3535
2449
  const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
3536
2450
  ? {
@@ -3542,7 +2456,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3542
2456
  messagesRef.current = updated;
3543
2457
  return updated;
3544
2458
  });
3545
- void reconcileServerStateRef.current?.();
3546
2459
  return;
3547
2460
  }
3548
2461
  if (statusData?.state === "streamOpen") {
@@ -3574,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3574
2487
  outputCost: Number(totals.totalOutputTokenCost) || 0,
3575
2488
  cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
3576
2489
  cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
3577
- imageCost: Number(totals.totalImageCost) || 0,
3578
2490
  totalCost: totalCost,
3579
2491
  currency: totals.currency,
3580
2492
  };
@@ -3586,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3586
2498
  if (anyNonZero) {
3587
2499
  setLiveTotals(nextTotals);
3588
2500
  }
3589
- // Fallback context usage update for providers that do not include
3590
- // context usage in every stream delta (for example some OpenAI streams).
3591
- // Prefer explicit status values when present; otherwise derive from totals.
3592
- const contextWindowValue = Number(statusData?.contextWindow ??
3593
- agent?.contextWindowTokens ??
3594
- 0);
3595
- const contextUsedValue = Number(statusData?.contextUsed ??
3596
- (Number(totals.totalInputTokens) || 0) +
3597
- (Number(totals.totalCachedInputTokens) || 0) +
3598
- (Number(totals.totalCacheWriteTokens) || 0));
3599
- if (contextWindowValue > 0 &&
3600
- Number.isFinite(contextUsedValue) &&
3601
- contextUsedValue >= 0) {
3602
- setContextWindowStatus({
3603
- contextWindowTokens: contextWindowValue,
3604
- estimatedInputTokens: contextUsedValue,
3605
- contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
3606
- model: agent?.model || "",
3607
- });
3608
- }
3609
2501
  // If server provides costLimit along with totals, persist it locally
3610
2502
  if (statusCostLimit) {
3611
2503
  setAgent((prev) => prev &&
@@ -3627,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3627
2519
  return;
3628
2520
  }
3629
2521
  if (statusData?.state === "ToolApprovalsRequired") {
3630
- setPendingBrowserCaptureDialogType(null);
3631
2522
  const msgId = statusData.messageId;
3632
2523
  const ids = statusData.toolCallIds || [];
3633
2524
  if (msgId && Array.isArray(ids) && ids.length > 0) {
@@ -3659,44 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3659
2550
  setIsWaitingForResponse(false);
3660
2551
  return;
3661
2552
  }
3662
- // Handle waiting states explicitly
3663
- if (normalizedStatus === "waitingForApproval") {
3664
- clearStopGuard();
3665
- setPendingBrowserCaptureDialogType(null);
2553
+ // Handle "WaitingForApproval" state explicitly
2554
+ if (statusData?.state === "WaitingForApproval" ||
2555
+ statusData?.state === "waitingForApproval") {
3666
2556
  setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
3667
2557
  setIsConnecting(false);
3668
2558
  setIsWaitingForResponse(false);
3669
2559
  setIsAgentThinking(false);
3670
- void reconcileServerStateRef.current?.();
3671
- return;
3672
- }
3673
- if (normalizedStatus === "waitingForInput") {
3674
- clearStopGuard();
3675
- const dialogType = typeof statusData?.dialogType === "string"
3676
- ? statusData.dialogType
3677
- : null;
3678
- const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
3679
- dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
3680
- setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
3681
- setAgent((prev) => prev
3682
- ? {
3683
- ...prev,
3684
- status: "waitingForInput",
3685
- statusMessage: isBrowserCaptureWait &&
3686
- typeof statusData?.title === "string" &&
3687
- statusData.title.trim()
3688
- ? statusData.title.trim()
3689
- : prev.statusMessage,
3690
- }
3691
- : prev);
3692
- setIsConnecting(false);
3693
- setIsWaitingForResponse(false);
3694
- setIsAgentThinking(false);
3695
2560
  return;
3696
2561
  }
3697
- if (normalizedStatus === "costLimitReached") {
3698
- clearStopGuard();
3699
- setPendingBrowserCaptureDialogType(null);
2562
+ if (statusData?.state === "CostLimitReached") {
3700
2563
  const totalCost = Number(statusData.totalCost) || 0;
3701
2564
  const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
3702
2565
  setCostLimitExceeded({
@@ -3714,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3714
2577
  // Server sends additionalData directly (with todoList inside)
3715
2578
  // Also may include ChildAgents for spawned agents
3716
2579
  try {
3717
- const serverAdditionalData = statusData.additionalData || {};
2580
+ const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
3718
2581
  setAgentMetadata((prev) => {
3719
2582
  const current = (prev || {});
3720
2583
  const updated = { ...current };
@@ -3728,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3728
2591
  }
3729
2592
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3730
2593
  // Backend sends the complete updated list, not just additions
3731
- if (statusData.childAgents) {
3732
- const childAgents = statusData.childAgents;
2594
+ if (statusData.ChildAgents || statusData.childAgents) {
2595
+ const childAgents = statusData.ChildAgents || statusData.childAgents;
3733
2596
  if (Array.isArray(childAgents)) {
3734
- updated.childAgents = childAgents;
2597
+ updated.ChildAgents = childAgents;
3735
2598
  }
3736
2599
  }
3737
2600
  return updated;
@@ -3743,11 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3743
2606
  return;
3744
2607
  }
3745
2608
  // Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
3746
- if (normalizedStatus === "completed") {
3747
- clearStopGuard();
2609
+ if (normalizedStatus === "completed" ||
2610
+ normalizedStatus === "Completed") {
3748
2611
  // Reset deduplication for the next run
3749
2612
  lastSeqRef.current = 0;
3750
- clearHeartbeatMessages();
3751
2613
  // Mark the last assistant message as completed
3752
2614
  setMessages((prev) => {
3753
2615
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3766,29 +2628,34 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3766
2628
  isWaitingRef.current = false;
3767
2629
  setIsConnecting(false);
3768
2630
  shouldCreateNewMessage.current = false;
3769
- // Server says agent finished thinking
2631
+ // Server says agent finished thinking
2632
+ setIsAgentThinking(false);
2633
+ return;
2634
+ }
2635
+ // Handle "Idle" state - agent has finished processing
2636
+ if (normalizedStatus === "idle" || normalizedStatus === "Idle") {
2637
+ // Update agent status to idle
2638
+ setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
2639
+ setIsWaitingForResponse(false);
2640
+ isWaitingRef.current = false;
2641
+ setIsConnecting(false);
2642
+ shouldCreateNewMessage.current = false;
3770
2643
  setIsAgentThinking(false);
3771
2644
  return;
3772
2645
  }
3773
2646
  // Handle "Running" state - agent is actively processing
3774
- if (normalizedStatus === "running") {
3775
- if (isStoppingRef.current) {
3776
- return;
3777
- }
3778
- // Update agent status to running and clear any previous error statusMessage
3779
- setAgent((prev) => prev
3780
- ? { ...prev, status: "running", statusMessage: undefined }
3781
- : prev);
2647
+ if (normalizedStatus === "running" ||
2648
+ normalizedStatus === "Running") {
2649
+ // Update agent status to running
2650
+ setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
3782
2651
  setIsWaitingForResponse(true);
3783
2652
  isWaitingRef.current = true;
3784
2653
  setIsAgentThinking(true);
3785
2654
  return;
3786
2655
  }
3787
2656
  // Handle "Error" state
3788
- if (normalizedStatus === "error") {
3789
- clearStopGuard();
3790
- const errorMsg = toUserFacingAgentErrorMessage(statusData?.statusMessage ?? statusData?.error) || "Unknown error";
3791
- clearHeartbeatMessages();
2657
+ if (normalizedStatus === "error" || normalizedStatus === "Error") {
2658
+ const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
3792
2659
  setAgent((prev) => prev
3793
2660
  ? { ...prev, status: "error", statusMessage: errorMsg }
3794
2661
  : prev);
@@ -3808,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3808
2675
  if (messageType === "agent:run:complete") {
3809
2676
  // Reset deduplication for the next run
3810
2677
  lastSeqRef.current = 0;
3811
- clearHeartbeatMessages();
3812
2678
  // Mark the last assistant message as completed
3813
2679
  setMessages((prev) => {
3814
2680
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3833,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3833
2699
  }
3834
2700
  // Lifecycle: agent:run:error
3835
2701
  if (messageType === "agent:run:error") {
3836
- const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
3837
- "AI could not complete this request.";
3838
- clearHeartbeatMessages();
2702
+ const errorMsg = message.payload?.error || "Unknown error";
3839
2703
  // Reset deduplication for the next run after an error
3840
2704
  lastSeqRef.current = 0;
3841
2705
  setError(errorMsg);
@@ -3850,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3850
2714
  }
3851
2715
  }, [
3852
2716
  agent,
3853
- clearHeartbeatMessages,
3854
2717
  handleContentChunk,
3855
- handleHeartbeatMessage,
3856
2718
  handleToolCall,
3857
2719
  handleToolResult,
3858
2720
  onAgentUpdate,
@@ -3907,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3907
2769
  const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
3908
2770
  const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
3909
2771
  currentAgent.status === 2;
3910
- const isWaitingForInput = currentAgent.status === "waitingForInput";
3911
2772
  if (isRunning) {
3912
2773
  setIsWaitingForResponse(true);
3913
2774
  isWaitingRef.current = true;
@@ -3915,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3915
2776
  // Agent is currently running, show thinking dots
3916
2777
  setIsAgentThinking(true);
3917
2778
  }
3918
- else if (isWaitingForApproval || isWaitingForInput) {
2779
+ else if (isWaitingForApproval) {
3919
2780
  setIsWaitingForResponse(false);
3920
2781
  isWaitingRef.current = false;
3921
- // Agent is waiting for user input/approval, hide thinking dots
2782
+ // Agent is waiting for user approval, hide thinking dots
3922
2783
  setIsAgentThinking(false);
3923
2784
  }
3924
2785
  else {
@@ -3965,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3965
2826
  window.addEventListener("editor:focusAgentPrompt", focusHandler);
3966
2827
  return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
3967
2828
  }, []);
3968
- // Keep stable refs so we don't miss window events during effect re-runs.
3969
- const agentIdRefForDialogs = useRef(null);
3970
- const agentStubIdRefForDialogs = useRef(agentStub.id);
3971
- const isActiveRefForDialogs = useRef(isActive);
3972
- const scrollToBottomRefForDialogs = useRef(scrollToBottom);
3973
- useEffect(() => {
3974
- agentIdRefForDialogs.current = agent?.id ?? null;
3975
- }, [agent?.id]);
3976
- useEffect(() => {
3977
- const prevId = agentStubIdRefForDialogs.current;
3978
- agentStubIdRefForDialogs.current = agentStub.id;
3979
- const visibleRegistry = { ...getVisibleDialogRegistry() };
3980
- const normalizedPrevId = normalizeDialogAgentId(prevId);
3981
- if (normalizedPrevId) {
3982
- delete visibleRegistry[normalizedPrevId];
3983
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
3984
- }
3985
- if (prevId && prevId !== agentStub.id) {
3986
- const orphanedDialog = activeInlineDialogRef.current;
3987
- if (orphanedDialog) {
3988
- setActiveInlineDialog(null);
3989
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
3990
- detail: { callbackId: orphanedDialog.request.callbackId },
3991
- }));
3992
- }
3993
- }
3994
- }, [agentStub.id]);
3995
- useEffect(() => {
3996
- isActiveRefForDialogs.current = isActive;
3997
- }, [isActive]);
3998
- useEffect(() => {
3999
- scrollToBottomRefForDialogs.current = scrollToBottom;
4000
- }, [scrollToBottom]);
4001
2829
  // Listen for agent inline dialog requests from AgentDialogHandler
4002
2830
  useEffect(() => {
4003
- if (orphanTimeoutRef.current) {
4004
- clearTimeout(orphanTimeoutRef.current);
4005
- orphanTimeoutRef.current = null;
4006
- }
4007
- const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
4008
- const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
4009
- if (normalizedAgentStubId &&
4010
- !globalListeners.includes(normalizedAgentStubId)) {
4011
- globalListeners.push(normalizedAgentStubId);
4012
- }
4013
- globalThis.__agentDialogMountedAgents = globalListeners;
4014
2831
  const handleDialogShow = (event) => {
4015
- const detail = event?.detail;
4016
- const request = detail?.request;
4017
- const onComplete = detail?.onComplete;
4018
- const onCancel = detail?.onCancel;
4019
- const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
4020
- const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
4021
- const isActiveNow = isActiveRefForDialogs.current;
4022
- if (!request)
4023
- return;
4024
- const requestAgentId = normalizeDialogAgentId(request.agentId);
4025
- const agentMatch = !requestAgentId ||
4026
- !terminalAgentStubId ||
4027
- requestAgentId === terminalAgentStubId ||
4028
- requestAgentId === terminalAgentId;
4029
- if (!isActiveNow) {
4030
- return;
4031
- }
4032
- if (!request)
4033
- return;
4034
- // Only handle dialog requests for this terminal's agent stub
4035
- if (requestAgentId &&
4036
- terminalAgentStubId &&
4037
- requestAgentId !== terminalAgentStubId &&
4038
- requestAgentId !== terminalAgentId) {
2832
+ const { request, onComplete, onCancel } = event.detail;
2833
+ // Only handle dialog requests for this agent
2834
+ if (request.agentId && agent?.id && request.agentId !== agent.id) {
4039
2835
  return;
4040
2836
  }
4041
2837
  console.log("[AgentTerminal] Received inline dialog request:", request);
4042
- setActiveInlineDialog({
4043
- request,
4044
- onComplete: (result) => {
4045
- onComplete(result);
4046
- onInteractionSubmitted?.();
4047
- },
4048
- onCancel: () => {
4049
- onCancel();
4050
- onInteractionSubmitted?.();
4051
- },
4052
- });
2838
+ setActiveInlineDialog({ request, onComplete, onCancel });
4053
2839
  // Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
4054
- if (request.callbackId) {
4055
- window.dispatchEvent(new CustomEvent("agent:dialog:accepted", {
4056
- detail: { callbackId: request.callbackId },
4057
- }));
4058
- }
4059
- setTimeout(() => {
4060
- if (request.dialogType === "questionnaire") {
4061
- scrollToBottomRefForDialogs.current?.();
4062
- return;
4063
- }
4064
- scrollToBottomRefForDialogs.current?.();
4065
- }, 100);
2840
+ window.dispatchEvent(new CustomEvent("agent:dialog:accepted", {
2841
+ detail: { callbackId: request.callbackId },
2842
+ }));
2843
+ // Scroll to bottom to show the dialog
2844
+ setTimeout(scrollToBottom, 100);
4066
2845
  };
4067
2846
  window.addEventListener("agent:dialog:show", handleDialogShow);
4068
- return () => {
4069
- const mounted = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
4070
- globalThis.__agentDialogMountedAgents = mounted.filter((id) => id !== normalizeDialogAgentId(agentStubIdRefForDialogs.current));
4071
- const visibleRegistry = { ...getVisibleDialogRegistry() };
4072
- const idsToClear = [
4073
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
4074
- normalizeDialogAgentId(agentIdRefForDialogs.current),
4075
- ].filter(Boolean);
4076
- idsToClear.forEach((id) => delete visibleRegistry[id]);
4077
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
4078
- // If unmounting with an active dialog, defer the orphan event so that
4079
- // React strict mode remounts can cancel it. Only real unmounts fire.
4080
- const orphanedDialog = activeInlineDialogRef.current;
4081
- if (orphanedDialog) {
4082
- orphanTimeoutRef.current = setTimeout(() => {
4083
- orphanTimeoutRef.current = null;
4084
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
4085
- detail: { callbackId: orphanedDialog.request.callbackId },
4086
- }));
4087
- }, 300);
4088
- }
4089
- window.removeEventListener("agent:dialog:show", handleDialogShow);
4090
- };
4091
- }, []);
4092
- // Announce when this terminal is ready to accept dialogs.
4093
- // Fire immediately on mount using agentStub.id so the AgentDialogHandler
4094
- // can re-dispatch pending dialogs without waiting for the async agent load.
4095
- // Also fire when agent?.id becomes available in case it differs from the stub.
2847
+ return () => window.removeEventListener("agent:dialog:show", handleDialogShow);
2848
+ }, [agent?.id, scrollToBottom]);
2849
+ // Announce when this terminal is ready to accept dialogs
2850
+ // This allows AgentDialogHandler to re-dispatch pending dialogs when switching to an agent
4096
2851
  useEffect(() => {
4097
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4098
- if (normalizedStubId) {
2852
+ if (agent?.id) {
2853
+ console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
4099
2854
  window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4100
- detail: {
4101
- agentId: normalizedStubId,
4102
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4103
- },
2855
+ detail: { agentId: agent.id },
4104
2856
  }));
4105
2857
  }
4106
- }, [agentStub.id]);
4107
- useEffect(() => {
4108
- const normalizedAgentId = normalizeDialogAgentId(agent?.id);
4109
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4110
- if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
4111
- window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4112
- detail: {
4113
- agentId: normalizedAgentId,
4114
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4115
- },
4116
- }));
4117
- }
4118
- }, [agent?.id, agentStub.id]);
2858
+ }, [agent?.id]);
4119
2859
  // Profiles are provided by parent component (Agents). No local loading here.
4120
2860
  // Select active profile based on agent.profileId or agentStub.profileId
4121
2861
  useEffect(() => {
@@ -4126,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4126
2866
  // Use case-insensitive comparison for GUID matching (backend may return different casing)
4127
2867
  const normalizedProfileId = profileIdToUse?.toLowerCase();
4128
2868
  const candidate = normalizedProfileId
4129
- ? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
4130
- : undefined;
4131
- if (!candidate) {
4132
- setActiveProfile(undefined);
4133
- return;
2869
+ ? (profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId) ??
2870
+ profiles[0])
2871
+ : profiles[0];
2872
+ if (candidate && (!activeProfile || activeProfile.id !== candidate.id)) {
2873
+ setActiveProfile(candidate);
4134
2874
  }
4135
- // Keep active profile in sync whenever the matching entry in `profiles` changes
4136
- // not only when the profile id changes. Otherwise availableSkills (and similar fields)
4137
- // that are merged in the parent after async loads never update (e.g. Template Builder
4138
- // settings skills merged into the profile).
4139
- setActiveProfile((prev) => {
4140
- if (!prev || prev.id !== candidate.id)
4141
- return candidate;
4142
- const skillKey = (p) => JSON.stringify({
4143
- allowed: (p.allowedSkills ?? []).map((s) => [
4144
- String(s?.id ?? ""),
4145
- String(s?.name ?? ""),
4146
- ]),
4147
- available: (p.availableSkills ?? []).map((s) => [
4148
- String(s?.id ?? ""),
4149
- String(s?.name ?? ""),
4150
- ]),
4151
- });
4152
- if (skillKey(prev) === skillKey(candidate))
4153
- return prev;
4154
- return candidate;
4155
- });
4156
- }, [
4157
- profiles,
4158
- agent?.id,
4159
- agent?.profileId,
4160
- agentStub.id,
4161
- agentStub.profileId,
4162
- ]);
2875
+ }, [profiles, agent?.profileId, agentStub.profileId]);
4163
2876
  // Clear queued prompts when agent changes or is new;
4164
2877
  // initial fetch is handled by loadAgent() for better performance and reliability
4165
2878
  useEffect(() => {
4166
- if (!agent?.id || isLocalOnlyDraftAgent) {
2879
+ if (!agent?.id || agent.status === "new") {
4167
2880
  setQueuedPrompts([]);
4168
2881
  }
4169
- }, [agent?.id, isLocalOnlyDraftAgent]);
2882
+ }, [agent?.id, agent?.status]);
4170
2883
  // Update selected model when the active profile or agent model changes
4171
2884
  useEffect(() => {
4172
2885
  if (!activeProfile)
@@ -4195,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4195
2908
  // Initialize mode from metadata; fall back to agent.Mode from server
4196
2909
  useEffect(() => {
4197
2910
  try {
4198
- const metaMode = normalizeAgentMode(agentMetadata?.mode);
4199
- if (metaMode) {
2911
+ const metaMode = agentMetadata?.mode;
2912
+ if (metaMode === "autonomous" ||
2913
+ metaMode === "read-only" ||
2914
+ metaMode === "supervised") {
4200
2915
  setMode(metaMode);
4201
2916
  return;
4202
2917
  }
4203
2918
  }
4204
2919
  catch { }
4205
2920
  try {
4206
- const serverMode = normalizeAgentMode(agent?.mode);
4207
- if (serverMode) {
2921
+ const serverMode = agent?.mode;
2922
+ if (serverMode === "autonomous" ||
2923
+ serverMode === "read-only" ||
2924
+ serverMode === "supervised") {
4208
2925
  setMode(serverMode);
4209
2926
  }
4210
2927
  }
@@ -4224,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4224
2941
  textareaRef.current.focus();
4225
2942
  }
4226
2943
  }, [messages, activePlaceholderInput]);
4227
- // Persist any pending settings (mode/model/profile) once an agent exists server-side
2944
+ // Persist any pending settings (mode/model) once an agent exists server-side
4228
2945
  const persistPendingSettingsIfNeeded = useCallback(async () => {
4229
2946
  try {
4230
2947
  if (!agent?.id)
@@ -4237,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4237
2954
  payload.model = pending.modelName;
4238
2955
  if (pending.mode)
4239
2956
  payload.mode = pending.mode;
4240
- if (pending.profileId)
4241
- payload.profileId = pending.profileId;
4242
- if (pending.profileName != null)
4243
- payload.profileName = pending.profileName;
4244
2957
  if (Object.keys(payload).length === 0)
4245
2958
  return;
4246
2959
  await updateAgentSettings(agent.id, payload);
@@ -4250,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4250
2963
  console.error("Failed to persist pending settings", e);
4251
2964
  }
4252
2965
  }, [agent?.id]);
4253
- const getPendingRequestSettings = useCallback(() => {
4254
- const pending = pendingSettingsRef.current;
4255
- const requestProfile = pending?.profileId
4256
- ? profiles.find((profile) => profile.id === pending.profileId) ||
4257
- activeProfile ||
4258
- profiles[0]
4259
- : activeProfile || profiles[0];
4260
- let requestModelId = selectedModelId;
4261
- if (pending?.modelName) {
4262
- const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
4263
- pending.modelName?.trim().toLowerCase());
4264
- requestModelId = requestModel?.id || requestModelId;
4265
- }
4266
- return {
4267
- mode: (pending?.mode || mode),
4268
- profileId: pending?.profileId || requestProfile?.id || "",
4269
- profileName: pending?.profileName || requestProfile?.name || "",
4270
- modelId: requestModelId,
4271
- };
4272
- }, [activeProfile, mode, profiles, selectedModelId]);
4273
- const getSubmitErrorMessage = (error) => {
4274
- const fallback = "Failed to submit prompt. Please try again.";
4275
- if (!(error instanceof Error))
4276
- return fallback;
4277
- const cleaned = toUserFacingAgentErrorMessage(error.message);
4278
- return cleaned || fallback;
4279
- };
4280
- const suppressedQueuedPromptsRef = useRef([]);
4281
- const pruneSuppressedQueuedPrompts = useCallback(() => {
4282
- const cutoff = Date.now() - 15_000;
4283
- suppressedQueuedPromptsRef.current =
4284
- suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
4285
- }, []);
4286
- const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
4287
- const normalizedAgentId = agentId.trim().toLowerCase();
4288
- const normalizedPrompt = promptText.trim();
4289
- if (!normalizedAgentId || !normalizedPrompt) {
4290
- return null;
4291
- }
4292
- pruneSuppressedQueuedPrompts();
4293
- const token = crypto.randomUUID();
4294
- suppressedQueuedPromptsRef.current = [
4295
- ...suppressedQueuedPromptsRef.current,
4296
- {
4297
- token,
4298
- agentId: normalizedAgentId,
4299
- prompt: normalizedPrompt,
4300
- createdAt: Date.now(),
4301
- },
4302
- ];
4303
- return token;
4304
- }, [pruneSuppressedQueuedPrompts]);
4305
- const clearSuppressedQueuedPrompt = useCallback((token) => {
4306
- if (!token) {
4307
- return;
4308
- }
4309
- suppressedQueuedPromptsRef.current =
4310
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
4311
- }, []);
4312
- const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
4313
- const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
4314
- const normalizedPrompt = queueEntry?.prompt?.trim();
4315
- if (!normalizedAgentId || !normalizedPrompt) {
4316
- return false;
4317
- }
4318
- pruneSuppressedQueuedPrompts();
4319
- const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
4320
- entry.prompt === normalizedPrompt);
4321
- if (!matchedEntry) {
4322
- return false;
4323
- }
4324
- suppressedQueuedPromptsRef.current =
4325
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
4326
- return true;
4327
- }, [pruneSuppressedQueuedPrompts]);
4328
- const cancelActiveInlineDialog = useCallback(() => {
4329
- const activeDialog = activeInlineDialogRef.current;
4330
- if (!activeDialog)
4331
- return;
4332
- try {
4333
- activeDialog.onCancel();
4334
- }
4335
- finally {
4336
- setActiveInlineDialog(null);
4337
- }
4338
- }, []);
4339
2966
  const handleSubmit = async () => {
4340
2967
  // Guard against double-submit and missing context
4341
2968
  if (isSubmitting) {
@@ -4379,13 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4379
3006
  setError("Agent not ready. Please try again.");
4380
3007
  return;
4381
3008
  }
4382
- clearStopGuard();
4383
- const hadQuestionnaireDialogOpen = activeInlineDialogRef.current?.request.dialogType === "questionnaire";
4384
- const suppressedQueuedPromptToken = hadQuestionnaireDialogOpen && savedPrompt
4385
- ? registerSuppressedQueuedPrompt(agentId, savedPrompt)
4386
- : null;
4387
- // A new user prompt supersedes any active questionnaire/inline dialog.
4388
- cancelActiveInlineDialog();
4389
3009
  // Generate a temporary ID for optimistic UI - will be replaced by server ID
4390
3010
  const tempMessageId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
4391
3011
  try {
@@ -4472,24 +3092,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4472
3092
  console.warn("[AgentTerminal] Failed to compute live context:", e);
4473
3093
  }
4474
3094
  // Add visible test IDs to context (only for Help agent)
4475
- const requestSettings = getPendingRequestSettings();
4476
- const currentProfileName = requestSettings.profileName;
3095
+ const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4477
3096
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
4478
3097
  const request = {
4479
3098
  agentId: agentId,
4480
3099
  message: savedPrompt,
4481
3100
  sessionId: editContext.sessionId,
4482
- profileId: requestSettings.profileId,
3101
+ profileId: activeProfile?.id || profiles[0]?.id || "",
4483
3102
  profile: currentProfileName,
4484
- model: requestSettings.modelId,
4485
- mode: requestSettings.mode,
3103
+ model: selectedModelId,
3104
+ mode: mode,
4486
3105
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
3106
+ deterministic: deterministicFlags.deterministic,
3107
+ seed: deterministicFlags.seed,
4487
3108
  };
4488
3109
  console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
4489
3110
  const response = await startAgent(request);
4490
3111
  console.log("[AgentTerminal] startAgent response:", response);
4491
3112
  // Check if prompt was queued (agent was already running)
4492
- const wasQueued = response.message?.toLowerCase().includes("queued");
3113
+ const wasQueued = response.message?.toLowerCase().includes("queued") ||
3114
+ response.status === "Queued";
4493
3115
  if (wasQueued) {
4494
3116
  // Prompt was queued - show a brief notification but don't set waiting state
4495
3117
  // The prompt will be processed when the agent becomes idle
@@ -4499,7 +3121,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4499
3121
  isWaitingRef.current = false;
4500
3122
  }
4501
3123
  else {
4502
- clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
4503
3124
  // Normal submission - set waiting state to show dancing dots immediately
4504
3125
  setIsWaitingForResponse(true);
4505
3126
  isWaitingRef.current = true;
@@ -4514,13 +3135,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4514
3135
  ...prev.filter((p) => p !== savedPrompt).slice(0, 9),
4515
3136
  ]);
4516
3137
  setCurrentHistoryIndex(-1);
4517
- await onInteractionSubmitted?.();
4518
3138
  // WebSocket connection is already active via subscription - no need for SSE
4519
3139
  }
4520
3140
  catch (err) {
4521
3141
  console.error("[AgentTerminal] Failed to submit prompt:", err);
4522
- clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
4523
- setError(getSubmitErrorMessage(err));
3142
+ setError("Failed to submit prompt. Please try again.");
4524
3143
  setIsWaitingForResponse(false);
4525
3144
  isWaitingRef.current = false;
4526
3145
  // Remove the optimistic user message on API error
@@ -4730,29 +3349,29 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4730
3349
  console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
4731
3350
  }
4732
3351
  // Add visible test IDs to context (only for Help agent)
4733
- const requestSettings = getPendingRequestSettings();
4734
- const currentProfileName = requestSettings.profileName;
3352
+ const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4735
3353
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
4736
3354
  const request = {
4737
3355
  agentId: agent.id,
4738
3356
  message: savedPrompt,
4739
3357
  sessionId: editContext.sessionId,
4740
- profileId: requestSettings.profileId,
3358
+ profileId: activeProfile?.id || profiles[0]?.id || "",
4741
3359
  profile: currentProfileName,
4742
- model: requestSettings.modelId,
4743
- mode: requestSettings.mode,
3360
+ model: selectedModelId,
3361
+ mode: mode,
4744
3362
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
3363
+ deterministic: deterministicFlags.deterministic,
3364
+ seed: deterministicFlags.seed,
4745
3365
  };
4746
3366
  console.log("[AgentTerminal] Calling startAgent API for quick message");
4747
3367
  await startAgent(request);
4748
3368
  // If user changed mode/model while the agent was new, persist them now
4749
3369
  await persistPendingSettingsIfNeeded();
4750
- await onInteractionSubmitted?.();
4751
3370
  // WebSocket connection is already active via subscription - no need for SSE
4752
3371
  }
4753
3372
  catch (err) {
4754
3373
  console.error("[AgentTerminal] Failed to submit quick message:", err);
4755
- setError(getSubmitErrorMessage(err));
3374
+ setError("Failed to submit prompt. Please try again.");
4756
3375
  setIsWaitingForResponse(false);
4757
3376
  isWaitingRef.current = false;
4758
3377
  // Remove the optimistic user message on API error
@@ -4930,90 +3549,66 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4930
3549
  }
4931
3550
  return context;
4932
3551
  }, [collectVisibleTestIds]);
4933
- const buildPageContextItem = useCallback((item) => ({
4934
- id: item.id,
4935
- language: item.language,
4936
- version: item.version,
4937
- name: editContext?.item?.name,
4938
- path: editContext?.item?.path,
4939
- }), [editContext?.item?.name, editContext?.item?.path]);
4940
- const buildComponentContext = useCallback((item) => {
4941
- if (!editContext?.selection?.length)
4942
- return undefined;
4943
- return editContext.selection.map((componentId) => {
4944
- let componentName;
4945
- let componentType;
4946
- let renderingItemId;
4947
- if (editContext.page) {
4948
- try {
4949
- const component = getComponentById(componentId, editContext.page);
4950
- componentName =
4951
- component?.datasourceItem?.name || component?.name || undefined;
4952
- componentType = component?.type || undefined;
4953
- renderingItemId = component?.rendering?.id || undefined;
4954
- }
4955
- catch { }
4956
- }
4957
- return {
4958
- componentId,
4959
- componentName,
4960
- componentType,
4961
- renderingItemId,
4962
- pageItem: buildPageContextItem(item),
4963
- };
4964
- });
4965
- }, [buildPageContextItem, editContext?.page, editContext?.selection]);
4966
- const buildFieldContext = useCallback(() => {
4967
- const focusedField = fieldsContext?.focusedField;
4968
- if (!focusedField?.fieldId || !focusedField?.item?.id)
4969
- return undefined;
4970
- const fieldItem = focusedField.item;
4971
- const currentItem = editContext?.currentItemDescriptor;
4972
- let fieldItemName = fieldItem.id === currentItem?.id ? editContext?.item?.name : undefined;
4973
- if (!fieldItemName && editContext?.page) {
4974
- try {
4975
- const component = getComponentById(fieldItem.id, editContext.page);
4976
- fieldItemName =
4977
- component?.datasourceItem?.name || component?.name || undefined;
4978
- }
4979
- catch { }
4980
- }
3552
+ // Helper function to build current context from editor state
3553
+ const buildCurrentContext = useCallback(() => {
3554
+ // Return context even without item - we want view info regardless
3555
+ const item = editContext?.currentItemDescriptor;
4981
3556
  return {
4982
- fieldId: focusedField.fieldId,
4983
- fieldName: focusedField.fieldName,
4984
- item: {
4985
- id: fieldItem.id,
4986
- language: fieldItem.language || currentItem?.language || "en",
4987
- version: fieldItem.version ?? currentItem?.version ?? 0,
4988
- name: fieldItem.name || fieldItemName,
4989
- },
3557
+ items: item
3558
+ ? [
3559
+ {
3560
+ id: item.id,
3561
+ language: item.language,
3562
+ version: item.version,
3563
+ name: editContext?.item?.name,
3564
+ path: editContext?.item?.path,
3565
+ },
3566
+ ]
3567
+ : undefined,
3568
+ currentItemId: item?.id, // ID of the currently loaded item user is viewing
3569
+ components: editContext?.selection?.length && item
3570
+ ? editContext.selection.map((componentId) => ({
3571
+ componentId,
3572
+ pageItem: {
3573
+ id: item.id,
3574
+ language: item.language,
3575
+ version: item.version,
3576
+ name: editContext?.item?.name,
3577
+ },
3578
+ }))
3579
+ : undefined,
3580
+ field: fieldsContext?.focusedField?.fieldId &&
3581
+ fieldsContext.focusedField?.item?.id
3582
+ ? {
3583
+ fieldId: fieldsContext.focusedField.fieldId,
3584
+ fieldName: fieldsContext.focusedField.fieldName,
3585
+ item: {
3586
+ id: fieldsContext.focusedField.item.id,
3587
+ language: fieldsContext.focusedField.item.language ||
3588
+ editContext?.currentItemDescriptor?.language ||
3589
+ "en",
3590
+ version: fieldsContext.focusedField.item.version ??
3591
+ editContext?.currentItemDescriptor?.version ??
3592
+ 0,
3593
+ name: editContext?.item?.name,
3594
+ },
3595
+ }
3596
+ : undefined,
3597
+ // View context - always include so agent knows editor state
3598
+ activeWorkspace: editContext?.workspaceId,
3599
+ hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
3600
+ // Open sidebars - helps agent understand what panels are currently visible
3601
+ openSidebars: editContext?.openSidebars,
4990
3602
  };
4991
3603
  }, [
4992
3604
  editContext?.currentItemDescriptor,
3605
+ editContext?.selection,
3606
+ editContext?.workspaceId,
4993
3607
  editContext?.item?.name,
4994
- editContext?.page,
3608
+ editContext?.activeSlotId,
3609
+ editContext?.openSidebars,
4995
3610
  fieldsContext?.focusedField,
4996
3611
  ]);
4997
- const buildEditorContextPayload = useCallback((item) => ({
4998
- items: item ? [buildPageContextItem(item)] : undefined,
4999
- currentItemId: item?.id,
5000
- components: item ? buildComponentContext(item) : undefined,
5001
- field: buildFieldContext(),
5002
- activeWorkspace: editContext?.workspaceId,
5003
- hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
5004
- openSidebars: editContext?.openSidebars,
5005
- }), [
5006
- buildComponentContext,
5007
- buildFieldContext,
5008
- buildPageContextItem,
5009
- editContext,
5010
- ]);
5011
- // Helper function to build current context from editor state
5012
- const buildCurrentContext = useCallback(() => {
5013
- // Return context even without item - we want view info regardless
5014
- const item = editContext?.currentItemDescriptor;
5015
- return buildEditorContextPayload(item);
5016
- }, [buildEditorContextPayload, editContext?.currentItemDescriptor]);
5017
3612
  // Live context updates: watch for changes and update agent context when in "live" mode
5018
3613
  const previousContextRef = useRef("");
5019
3614
  useEffect(() => {
@@ -5113,15 +3708,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5113
3708
  const handleRefreshContext = useCallback(async () => {
5114
3709
  if (!agent?.id)
5115
3710
  return;
5116
- const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
5117
- const refreshProfile = activeProfile ||
5118
- profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
5119
- if (refreshProfile?.editorContextMode === "none") {
5120
- editContext?.showInfoToast({
5121
- summary: "This profile excludes editor context.",
5122
- });
5123
- return;
5124
- }
5125
3711
  try {
5126
3712
  const currentCtx = buildCurrentContext();
5127
3713
  if (!currentCtx) {
@@ -5162,90 +3748,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5162
3748
  buildCurrentContext,
5163
3749
  sanitizeAgentMetadata,
5164
3750
  editContext,
5165
- activeProfile,
5166
- profiles,
5167
3751
  ]);
5168
- const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
5169
- const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
5170
- pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
5171
- const currentSessionId = editContext?.sessionId?.trim() || "";
5172
- const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
5173
- const isClaimedByCurrentSession = !!currentSessionId &&
5174
- !!claimedSessionId &&
5175
- currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
5176
- const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
5177
- const handleClaimBrowser = useCallback(async (takeOver) => {
5178
- if (!agent?.id || !editContext?.sessionId)
5179
- return;
5180
- setIsBrowserClaimMutationPending(true);
5181
- try {
5182
- const response = await claimAgentBrowser({
5183
- agentId: agent.id,
5184
- sessionId: editContext.sessionId,
5185
- takeOver,
5186
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5187
- });
5188
- setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
5189
- }
5190
- catch (err) {
5191
- console.error("[AgentTerminal] Failed to claim browser:", err);
5192
- editContext.showErrorToast(err);
5193
- }
5194
- finally {
5195
- setIsBrowserClaimMutationPending(false);
5196
- }
5197
- }, [agent?.id, editContext, sanitizeAgentMetadata]);
5198
- const handleReleaseBrowser = useCallback(async () => {
5199
- if (!agent?.id || !editContext?.sessionId)
5200
- return;
5201
- setIsBrowserClaimMutationPending(true);
5202
- try {
5203
- const response = await releaseAgentBrowser({
5204
- agentId: agent.id,
5205
- sessionId: editContext.sessionId,
5206
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5207
- });
5208
- setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
5209
- }
5210
- catch (err) {
5211
- console.error("[AgentTerminal] Failed to release browser:", err);
5212
- editContext.showErrorToast(err);
5213
- }
5214
- finally {
5215
- setIsBrowserClaimMutationPending(false);
5216
- }
5217
- }, [agent?.id, editContext, sanitizeAgentMetadata]);
5218
- useEffect(() => {
5219
- return () => {
5220
- if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
5221
- return;
5222
- }
5223
- void releaseAgentBrowser({
5224
- agentId: agent.id,
5225
- sessionId: editContext.sessionId,
5226
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5227
- }).catch((error) => {
5228
- console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
5229
- });
5230
- };
5231
- }, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
5232
3752
  // Stop current execution/stream safely
5233
3753
  const handleStop = useCallback(async () => {
5234
3754
  try {
5235
3755
  // 1. Set the stopping guard to prevent WebSocket handlers from re-enabling states
5236
3756
  // This must happen FIRST, before any other state changes
5237
- armStopGuard();
3757
+ isStoppingRef.current = true;
5238
3758
  // 2. Update all UI state to reflect stopped status
5239
3759
  setIsWaitingForResponse(false);
5240
3760
  isWaitingRef.current = false;
5241
3761
  setIsConnecting(false);
5242
3762
  setIsSubmitting(false);
5243
- setAgent((prev) => prev ? { ...prev, status: "idle", statusMessage: undefined } : prev);
5244
- shouldCreateNewMessage.current = false;
5245
3763
  // User stopped the agent, hide thinking dots
5246
3764
  setIsAgentThinking(false);
5247
- // Stopping an agent discards queued follow-up prompts for that run.
5248
- setQueuedPrompts([]);
5249
3765
  // 3. Mark any in-progress streaming messages as completed in UI
5250
3766
  setMessages((prev) => {
5251
3767
  const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
@@ -5263,111 +3779,46 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5263
3779
  catch (err) {
5264
3780
  console.error("Failed to cancel agent on backend:", err);
5265
3781
  // Continue - UI is already in stopped state, but backend may still be running
5266
- // The stop guard has its own timeout so future runs can proceed.
3782
+ // The isStoppingRef guard will be cleared below so future runs can proceed
5267
3783
  }
5268
3784
  }
5269
3785
  }
5270
3786
  catch (e) {
5271
3787
  console.error("Failed to stop agent execution", e);
5272
3788
  }
5273
- }, []);
5274
- // Cost limit is owned by the persisted agent record. Profiles can provide defaults
5275
- // during creation, but once an agent exists the frontend should only display the
5276
- // backend-provided value for that specific agent.
5277
- const agentCostLimit = (() => {
5278
- try {
5279
- const value = agent?.costLimit;
5280
- const parsed = value != null ? Number(value) : 0;
5281
- return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
3789
+ finally {
3790
+ // Clear the stopping guard so future runs can proceed normally
3791
+ // This happens after backend confirmation (or error)
3792
+ isStoppingRef.current = false;
5282
3793
  }
5283
- catch {
5284
- return undefined;
3794
+ }, []);
3795
+ // Determine effective cost limit from agent, profile, or metadata so the cost display
3796
+ // is visible immediately even before any messages or server-side persistence.
3797
+ let effectiveCostLimit;
3798
+ try {
3799
+ const candidates = [
3800
+ agent?.costLimit,
3801
+ activeProfile?.costLimit,
3802
+ ];
3803
+ for (const c of candidates) {
3804
+ const n = c != null ? Number(c) : 0;
3805
+ if (Number.isFinite(n) && n > 0) {
3806
+ effectiveCostLimit = n;
3807
+ break;
3808
+ }
5285
3809
  }
5286
- })();
5287
- // Calculate total token usage for cost display.
5288
- // Message rows do not currently persist imageCost, so on refresh we fall back
5289
- // to the persisted agent aggregate for that single field.
5290
- const totalTokens = (() => {
5291
- const totals = calculateTotalTokens(messages);
5292
- return {
5293
- ...totals,
5294
- imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
5295
- };
5296
- })();
5297
- const normalizedAgentStatus = parseAgentStatus(agent?.status);
5298
- const hasSettledToNonExecutingStatus = normalizedAgentStatus === "idle" ||
5299
- normalizedAgentStatus === "completed" ||
5300
- normalizedAgentStatus === "error" ||
5301
- normalizedAgentStatus === "closed" ||
5302
- normalizedAgentStatus === "waitingForApproval" ||
5303
- normalizedAgentStatus === "waitingForInput" ||
5304
- normalizedAgentStatus === "costLimitReached";
3810
+ }
3811
+ catch { }
3812
+ if (effectiveCostLimit === undefined) {
3813
+ effectiveCostLimit = undefined;
3814
+ }
3815
+ // Calculate total token usage for cost display
3816
+ const totalTokens = calculateTotalTokens(messages);
5305
3817
  // Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
5306
- const isExecuting = !isStopGuardActive &&
5307
- (isSubmitting ||
5308
- isConnecting ||
5309
- (!hasSettledToNonExecutingStatus &&
5310
- (isWaitingForResponse || hasActiveStreaming())));
5311
- const assistantMessageCount = useMemo(() => messages.filter((message) => message.role === "assistant").length, [messages]);
5312
- const messagesWithToolCallsCount = useMemo(() => messages.filter((message) => (message.toolCalls || []).length > 0).length, [messages]);
5313
- const totalToolCallCount = useMemo(() => messages.reduce((sum, message) => sum + (message.toolCalls?.length || 0), 0), [messages]);
5314
- const incompleteToolCallCount = useMemo(() => messages.reduce((sum, message) => sum +
5315
- (message.toolCalls?.filter((toolCall) => !toolCall.isCompleted).length || 0), 0), [messages]);
5316
- const assistantGroupsWithRenderableToolCalls = useMemo(() => {
5317
- const groups = groupConsecutiveMessages(messages);
5318
- return groups.filter((group) => {
5319
- if (group.type !== "assistant-group")
5320
- return false;
5321
- const convertedMessages = convertAgentMessagesToAiFormat(group.messages);
5322
- return convertedMessages.some((message) => (message.tool_calls?.length || 0) > 0);
5323
- }).length;
5324
- }, [messages]);
5325
- const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
5326
- const runDiagnosticsSnapshot = useMemo(() => {
5327
- const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
5328
- return {
5329
- agentId: currentAgentId,
5330
- isSubmitting,
5331
- isConnecting,
5332
- isWaitingForResponse,
5333
- isAgentThinking,
5334
- isExecuting,
5335
- hasActiveStreaming: hasActiveStreaming(),
5336
- isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
5337
- normalizeDialogAgentId(currentAgentId),
5338
- lastSeq: lastSeqRef.current,
5339
- lastEventType: lastEvent?.type ?? null,
5340
- lastEventAt: lastEvent?.timestamp ?? null,
5341
- recentEvents: recentAgentRunEvents,
5342
- assistantMessageCount,
5343
- assistantGroupCount,
5344
- assistantGroupsWithRenderableToolCalls,
5345
- messagesWithToolCalls: messagesWithToolCallsCount,
5346
- totalToolCallCount,
5347
- incompleteToolCallCount,
5348
- recentToolUiEvents,
5349
- };
5350
- }, [
5351
- assistantGroupCount,
5352
- assistantGroupsWithRenderableToolCalls,
5353
- assistantMessageCount,
5354
- currentAgentId,
5355
- hasActiveStreaming,
5356
- incompleteToolCallCount,
5357
- isAgentThinking,
5358
- isConnecting,
5359
- isExecuting,
5360
- isSubmitting,
5361
- isWaitingForResponse,
5362
- messagesWithToolCallsCount,
5363
- recentAgentRunEvents,
5364
- recentToolUiEvents,
5365
- totalToolCallCount,
5366
- ]);
5367
- const showInitialThinkingSplash = messages.length === 0 &&
5368
- !error &&
5369
- hideGreeting &&
5370
- (isSubmitting || isConnecting);
3818
+ const isExecuting = isSubmitting ||
3819
+ isConnecting ||
3820
+ isWaitingForResponse ||
3821
+ hasActiveStreaming();
5371
3822
  // Compute dots visibility: only show BEFORE any assistant message exists
5372
3823
  // This prevents duplicate headers - the dots indicator has its own header,
5373
3824
  // and we don't want to show a second header below existing messages
@@ -5382,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5382
3833
  // The message with the pending approval will display its own UI for approval
5383
3834
  if (allPendingApprovals.length > 0)
5384
3835
  return false;
5385
- // The hidden-greeting startup splash already renders its own bouncing dots.
5386
- // Suppress the generic indicator so reopening/running terminals don't show two.
5387
- if (showInitialThinkingSplash)
5388
- return false;
5389
3836
  // IMPORTANT: If the last message is an assistant message and we're still executing,
5390
3837
  // the AiResponseMessage for that message will show its own activity indicator.
5391
3838
  // We only want these global thinking dots if the last message was from the user
5392
3839
  // or if no messages exist yet (waiting for initial response).
5393
3840
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
5394
- if (isExecuting &&
5395
- lastMessage?.role === "assistant" &&
5396
- !lastMessage.isCompleted) {
3841
+ if (isExecuting && lastMessage?.role === "assistant")
5397
3842
  return false;
5398
- }
5399
3843
  // Existing check for uncompleted assistant messages
5400
3844
  const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
5401
3845
  if (hasActiveStreamingMessage)
@@ -5411,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5411
3855
  messages,
5412
3856
  activeInlineDialog,
5413
3857
  allPendingApprovals,
5414
- showInitialThinkingSplash,
5415
3858
  ]);
5416
3859
  // Move useMemo hook before early return to comply with Rules of Hooks
5417
- const resolvedEditorContextMode = React.useMemo(() => {
3860
+ const isLiveEditorContextMode = React.useMemo(() => {
5418
3861
  try {
5419
3862
  const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
5420
3863
  const profile = activeProfile ||
5421
3864
  profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
5422
- return profile?.editorContextMode ?? null;
3865
+ const mode = profile?.editorContextMode;
3866
+ return mode === "live";
5423
3867
  }
5424
3868
  catch {
5425
- return null;
3869
+ return false;
5426
3870
  }
5427
3871
  }, [activeProfile, profiles, agent?.profileId]);
5428
- const isLiveEditorContextMode = resolvedEditorContextMode === "live";
5429
- const omitsEditorContext = resolvedEditorContextMode === "none";
5430
3872
  // Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
5431
3873
  const parentAgentId = agent?.parentAgentId ||
5432
3874
  agent?.ParentAgentId ||
@@ -5440,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5440
3882
  detail: { agentId: parentAgentId },
5441
3883
  }));
5442
3884
  }, [parentAgentId]);
5443
- const loadingContent = isLoading && !activeInlineDialog ? (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-gray-500", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1 }), "Loading agent..."] }) })) : null;
5444
- const renderContextInfoBar = () => (_jsx(ContextInfoBar, { agent: agent, agentMetadata: agentMetadata, setAgentMetadata: setAgentMetadata, setAgent: setAgent, resolvedPageName: resolvedPageName, resolvedComponentName: resolvedComponentName, resolvedFieldName: resolvedFieldName, isLiveEditorContextMode: isLiveEditorContextMode, omitEditorContext: omitsEditorContext, onRefreshContext: handleRefreshContext }));
3885
+ if (isLoading) {
3886
+ return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-gray-500", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1 }), "Loading agent..."] }) }));
3887
+ }
3888
+ const renderContextInfoBar = () => (_jsx(ContextInfoBar, { agent: agent, agentMetadata: agentMetadata, setAgentMetadata: setAgentMetadata, setAgent: setAgent, resolvedPageName: resolvedPageName, resolvedComponentName: resolvedComponentName, resolvedFieldName: resolvedFieldName, isLiveEditorContextMode: isLiveEditorContextMode, activeProfile: activeProfile, onRefreshContext: handleRefreshContext }));
5445
3889
  const renderCostLimitBanner = () => {
5446
3890
  if (!costLimitExceeded)
5447
3891
  return null;
@@ -5452,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5452
3896
  try {
5453
3897
  // Extend cost limit - backend will automatically resume the agent
5454
3898
  const result = await updateAgentCostLimit(agent.id, "extend");
5455
- // Update the agent's cost limit and clear the costLimitReached
5456
- // status in local state so the useEffect watcher doesn't
5457
- // immediately re-show the banner before the backend status
5458
- // update arrives.
3899
+ // Update the agent's cost limit in local state
5459
3900
  if (result.success && result.costLimit !== undefined) {
5460
- setAgent((prev) => prev
5461
- ? {
5462
- ...prev,
5463
- costLimit: result.costLimit,
5464
- status: prev.status === "costLimitReached"
5465
- ? "running"
5466
- : prev.status,
5467
- }
5468
- : prev);
3901
+ setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
5469
3902
  }
5470
3903
  // Clear the banner and set waiting state
5471
3904
  // Agent will resume automatically via backend's ResumeAgentAsync
@@ -5485,257 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5485
3918
  };
5486
3919
  const renderErrorBanner = () => {
5487
3920
  const currentAgent = agent || agentStub;
5488
- const isErrorStatus = currentAgent?.status === "error";
5489
- const isWaitingForInputStatus = currentAgent?.status === "waitingForInput";
5490
- const isWaitingForApprovalStatus = currentAgent?.status === "waitingForApproval";
5491
- // Show error banner for error status, or for any terminal status that still
5492
- // carries a statusMessage (e.g. agent closed after an error).
5493
- const isTerminalWithError = !isErrorStatus &&
5494
- !!currentAgent?.statusMessage &&
5495
- currentAgent?.status !== "running" &&
5496
- currentAgent?.status !== "new" &&
5497
- !isWaitingForInputStatus &&
5498
- !isWaitingForApprovalStatus;
5499
- const rawErrorMessage = (isErrorStatus || isTerminalWithError
5500
- ? currentAgent?.statusMessage
5501
- : null) || error;
5502
- if (!rawErrorMessage)
3921
+ const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
3922
+ const errorMessage = currentAgent?.statusMessage;
3923
+ if (!isErrorStatus || !errorMessage)
5503
3924
  return null;
5504
- // Clean the error message (statusMessage from DB may contain raw JSON)
5505
- const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
5506
- 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 })] })] }) }));
3925
+ 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 })] })] }) }));
5507
3926
  };
5508
- const renderBrowserClaimBanner = (variant = "inline") => {
5509
- if (!agent?.id || !editContext?.sessionId)
5510
- return null;
5511
- if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
5512
- return null;
5513
- }
5514
- if (isPendingBrowserCaptureWait) {
5515
- return null;
5516
- }
5517
- const label = isClaimedByCurrentSession
5518
- ? "Attached to this browser"
5519
- : isClaimedByAnotherBrowser
5520
- ? "Attached in another browser"
5521
- : "No browser attached";
5522
- const description = isClaimedByCurrentSession
5523
- ? "This browser will handle page screenshot and DOM capture requests for the agent."
5524
- : isClaimedByAnotherBrowser
5525
- ? "Capture requests will stay with the other browser until you take over control here."
5526
- : "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
5527
- 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");
5528
- 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: () => {
5529
- void handleReleaseBrowser();
5530
- }, 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: () => {
5531
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5532
- }, children: isClaimedByAnotherBrowser
5533
- ? "Take over browser control"
5534
- : "Attach to this browser" })) })] }) }));
5535
- };
5536
- const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5537
- const inlineBrowserClaimBanner = null;
5538
- const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
5539
- ? {
5540
- toolNames: [
5541
- "capture-page-screenshot",
5542
- "capture-parhelia-ui-screenshot",
5543
- "capture-page-dom",
5544
- ],
5545
- label: isClaimedByAnotherBrowser
5546
- ? "Attached in another browser"
5547
- : "No browser attached",
5548
- description: isClaimedByAnotherBrowser
5549
- ? "This capture request is waiting in another browser. Take over browser control here to continue."
5550
- : "This capture request is waiting for a browser attachment. Attach this browser to continue.",
5551
- actionLabel: isClaimedByAnotherBrowser
5552
- ? "Take over browser control"
5553
- : "Attach to this browser",
5554
- isPending: isBrowserClaimMutationPending,
5555
- onAction: () => {
5556
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5557
- },
5558
- }
5559
- : null;
5560
- useEffect(() => {
5561
- if (agent?.status !== "waitingForInput") {
5562
- setPendingBrowserCaptureDialogType(null);
5563
- }
5564
- }, [agent?.status]);
5565
- const renderInlineDialogContent = () => {
5566
- if (!activeInlineDialog)
5567
- return null;
5568
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5569
- return (_jsx("div", { ref: inlineDialogContainerRef, className: cn("agent-inline-dialog min-h-0 overflow-hidden", displayMode === "full" && "h-full"), children: _jsx(QuestionnaireInline, { requestId: activeInlineDialog.request.callbackId, agentId: activeInlineDialog.request.agentId, title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, footerActions: questionnaireFooterActions, onClose: (result) => {
5570
- activeInlineDialog.onComplete(result);
5571
- setActiveInlineDialog(null);
5572
- void onInteractionSubmitted?.();
5573
- }, onCancel: () => {
5574
- activeInlineDialog.onCancel();
5575
- setActiveInlineDialog(null);
5576
- } }) }));
5577
- }
5578
- const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
5579
- if (dialogRegistration) {
5580
- const DialogComponent = dialogRegistration.component;
5581
- return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
5582
- activeInlineDialog.onComplete(result);
5583
- setActiveInlineDialog(null);
5584
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5585
- void onInteractionSubmitted?.();
5586
- }
5587
- }, onCancel: () => {
5588
- activeInlineDialog.onCancel();
5589
- setActiveInlineDialog(null);
5590
- } }) }));
5591
- }
5592
- return (_jsx("div", { className: "agent-inline-dialog", children: _jsxs("div", { className: "p-4 text-sm text-red-500", children: ["Unknown dialog type: ", activeInlineDialog.request.dialogType] }) }));
5593
- };
5594
- const latestSummaryAssistantGroup = useMemo(() => {
5595
- if (hideSummaryMessages)
5596
- return null;
5597
- const groups = groupConsecutiveMessages(messages);
5598
- for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
5599
- const group = groups[groupIndex];
5600
- if (!group || group.type !== "assistant-group")
5601
- continue;
5602
- const filteredMessages = group.messages.filter((msg) => {
5603
- const content = msg.content || "";
5604
- return !content.startsWith("⚠️") || !content.includes("Cost limit");
5605
- });
5606
- if (filteredMessages.length === 0)
5607
- continue;
5608
- return {
5609
- messages: filteredMessages,
5610
- isLastGroup: groupIndex === groups.length - 1,
5611
- };
5612
- }
5613
- return null;
5614
- }, [messages, hideSummaryMessages]);
5615
- const summaryModeContent = displayMode === "summary"
5616
- ? (() => {
5617
- const inlineDialog = renderInlineDialogContent();
5618
- const summaryMessages = latestSummaryAssistantGroup
5619
- ? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
5620
- : [];
5621
- const summaryOperations = latestSummaryAssistantGroup
5622
- ? getOperationsForMessageGroup(summaryMessages, agentOperations)
5623
- : [];
5624
- 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 &&
5625
- !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: {
5626
- __html: sanitizeSvg(activeProfile.svgIcon),
5627
- } })) : (_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 ||
5628
- activeProfile?.displayTitle ||
5629
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
5630
- const text = (action.prompt ||
5631
- action.value ||
5632
- action.label ||
5633
- "").trim();
5634
- if (!text)
5635
- return;
5636
- if (isExecuting) {
5637
- try {
5638
- handleStop();
5639
- }
5640
- catch { }
5641
- }
5642
- sendQuickMessage(text);
5643
- } }) })) : hideSummaryWaitingPlaceholder ? (_jsx("div", { className: `flex h-full items-center justify-center ${compact ? "min-h-[100px] p-3" : "min-h-[220px] p-6"}`, children: summaryPlaceholderActions ? (_jsx("div", { className: "flex justify-center", children: summaryPlaceholderActions })) : null })) : (_jsx("div", { className: `flex h-full items-center justify-center ${compact ? "min-h-[100px] p-3" : "min-h-[220px] p-6"}`, children: _jsxs("div", { className: `max-w-md rounded-xl border border-slate-200 bg-slate-50 text-center text-slate-600 ${compact ? "px-3 py-2 text-xs" : "px-5 py-4 text-sm"}`, children: [_jsx("div", { children: shouldShowThinkingDots || isExecuting
5644
- ? "The agent is still working. The next update will appear here automatically."
5645
- : agent?.statusMessage ||
5646
- summaryPlaceholderMessage ||
5647
- "Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
5648
- shouldShowThinkingDots &&
5649
- !inlineDialog &&
5650
- !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: {
5651
- __html: sanitizeSvg(activeProfile.svgIcon),
5652
- } })) : (_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 ||
5653
- activeProfile?.displayTitle ||
5654
- activeProfile?.name ||
5655
- "Agent" }), _jsx("span", { className: "text-xs text-gray-400", children: formatTime(new Date()) })] }), _jsxs("div", { className: "flex items-center gap-1 pt-2", children: [_jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400" })] })] })] })), _jsx("div", { ref: messagesEndRef })] }), showSummaryInput && !activeInlineDialog ? (_jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (_jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
5656
- setActivePlaceholderInput(null);
5657
- setAllPlaceholdersFilled(false);
5658
- if (activePlaceholderInput.behavior === "compose" &&
5659
- !hideBottomControls) {
5660
- setPrompt(filledText);
5661
- setInputPlaceholder("Review and edit, then press Enter to send");
5662
- if (textareaRef.current) {
5663
- try {
5664
- textareaRef.current.focus();
5665
- const v = textareaRef.current.value || "";
5666
- textareaRef.current.selectionStart = v.length;
5667
- textareaRef.current.selectionEnd = v.length;
5668
- }
5669
- catch { }
5670
- }
5671
- }
5672
- else {
5673
- if (isExecuting) {
5674
- try {
5675
- handleStop();
5676
- }
5677
- catch { }
5678
- }
5679
- sendQuickMessage(filledText);
5680
- }
5681
- }, onCancel: () => {
5682
- setActivePlaceholderInput(null);
5683
- setAllPlaceholdersFilled(false);
5684
- } })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
5685
- setPrompt(filledText);
5686
- setAllPlaceholdersFilled(false);
5687
- if (filledText.trim()) {
5688
- if (isExecuting) {
5689
- try {
5690
- handleStop();
5691
- }
5692
- catch { }
5693
- }
5694
- sendQuickMessage(filledText);
5695
- }
5696
- }, onCancel: () => {
5697
- setPrompt("");
5698
- setAllPlaceholdersFilled(false);
5699
- setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
5700
- } })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
5701
- setPrompt(e.target.value);
5702
- if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
5703
- setAllPlaceholdersFilled(false);
5704
- }
5705
- if (currentHistoryIndex !== -1) {
5706
- setCurrentHistoryIndex(-1);
5707
- }
5708
- }, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
5709
- shouldMaintainFocusRef.current = true;
5710
- }, onBlur: () => {
5711
- shouldMaintainFocusRef.current = false;
5712
- }, placeholder: inputPlaceholder, className: "max-h-[250px] min-h-[80px] flex-1 resize-y overflow-y-auto text-[12px] lg:max-h-[450px]", "data-testid": "agent-terminal-prompt", disabled: isSubmitting }) })), (() => {
5713
- const isInPlaceholderMode = activePlaceholderInput ||
5714
- (prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
5715
- const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
5716
- if (placeholderShowsOwnButtons)
5717
- return null;
5718
- return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
5719
- simpleMode ||
5720
- isInPlaceholderMode
5721
- ? "justify-end"
5722
- : "justify-between"), children: [!hideBottomControls &&
5723
- !simpleMode &&
5724
- !isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
5725
- if (isExecuting) {
5726
- handleStop();
5727
- }
5728
- else {
5729
- handleSubmit();
5730
- }
5731
- }, disabled: !isExecuting &&
5732
- !activePlaceholderInput &&
5733
- (!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
5734
- })()] })) : null] }));
5735
- })()
5736
- : null;
5737
- const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
5738
- 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) => {
3927
+ return (_jsxs("div", { className: `flex h-full flex-col ${className || ""}`, children: [parentAgentId && !simpleMode && (_jsx("div", { className: "border-b border-gray-200 bg-gray-50 px-4 py-2", children: _jsxs("button", { onClick: handleBackToParent, className: "flex items-center gap-2 text-[11px] text-gray-600 transition-colors hover:text-gray-900", title: "Back to parent agent", children: [_jsx(ArrowLeft, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), _jsx("span", { children: "Back to parent agent" })] }) })), _jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [error && (_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) => {
5739
3928
  setPrompt(p);
5740
3929
  // Use setTimeout to ensure state is updated before submission
5741
3930
  setTimeout(() => {
@@ -5748,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5748
3937
  handleSubmit();
5749
3938
  }
5750
3939
  }, 0);
5751
- } })) })), 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: {
5752
- __html: sanitizeSvg(activeProfile.svgIcon),
5753
- } })) : (_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: [(() => {
3940
+ } })) })), messages.length === 0 &&
3941
+ !error &&
3942
+ hideGreeting &&
3943
+ (isSubmitting || isConnecting) && (_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: {
3944
+ __html: activeProfile.svgIcon,
3945
+ } })) : (_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: [(() => {
5754
3946
  const groups = groupConsecutiveMessages(messages);
5755
3947
  return groups.map((group, groupIndex) => {
5756
3948
  const isLastGroup = groupIndex === groups.length - 1;
@@ -5758,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5758
3950
  // Render user message
5759
3951
  return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
5760
3952
  }
5761
- else if (group.type === "heartbeat" && group.messages[0]) {
5762
- return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
5763
- }
5764
3953
  else {
5765
3954
  // Render bundled assistant messages
5766
3955
  // Check if this group contains any streaming message
@@ -5777,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5777
3966
  }
5778
3967
  const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
5779
3968
  const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
5780
- return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, defaultCollapseJson: defaultCollapseJson, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
3969
+ return (_jsx(AiResponseMessage, { messages: convertedMessages, finished: !isLastGroup || !isExecuting, editOperations: operationsForGroup, error: error || undefined, profileSvgIcon: activeProfile?.svgIcon, agentId: agent?.id || agentStub.id, agentName: activeProfile?.agentName ||
5781
3970
  activeProfile?.displayTitle ||
5782
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
3971
+ activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5783
3972
  const text = (action.prompt ||
5784
3973
  action.value ||
5785
3974
  action.label ||
@@ -5823,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5823
4012
  }
5824
4013
  });
5825
4014
  })(), 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: {
5826
- __html: sanitizeSvg(activeProfile.svgIcon),
4015
+ __html: activeProfile.svgIcon,
5827
4016
  } })) : (_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 ||
5828
4017
  activeProfile?.displayTitle ||
5829
4018
  activeProfile?.name ||
5830
4019
  "Agent" }), _jsx("span", { className: "text-xs text-gray-400", children: formatTime(new Date()) })] }), _jsxs("div", { className: "flex items-center gap-1 pt-2", children: [_jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" }), _jsx("div", { className: "h-1 w-1 animate-bounce rounded-full bg-gray-400" })] })] })] }))] }), !simpleMode && renderCostLimitBanner(), _jsx("div", { ref: messagesEndRef })] }), !hideContext &&
5831
4020
  !simpleMode &&
5832
- (editContext?.isMobile ? (_jsx("div", { className: "border-t border-gray-200 bg-gray-50", "data-testid": "agent-context-panel-tabs", children: _jsx(SimpleTabs, { tabs: [
4021
+ (isMobile ? (_jsx("div", { className: "border-t border-gray-200 bg-gray-50", "data-testid": "agent-context-panel-tabs", children: _jsx(SimpleTabs, { tabs: [
5833
4022
  {
5834
4023
  id: "context",
5835
4024
  label: "Context",
@@ -5867,7 +4056,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5867
4056
  {
5868
4057
  id: "history",
5869
4058
  label: "History",
5870
- content: (_jsx(AgentEditOperationsPanel, { operations: agentOperations })),
4059
+ content: (_jsx(AgentEditOperationsPanel, { agentId: agent.id })),
5871
4060
  },
5872
4061
  ]
5873
4062
  : []),
@@ -5877,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5877
4066
  hasTodoContent,
5878
4067
  hasSpawnedAgents,
5879
4068
  agent?.id && hasHistoryContent,
5880
- ].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && (_jsx(AgentEditOperationsPanel, { operations: agentOperations }))] }))), queuedPrompts.length > 0 && !simpleMode && (_jsx("div", { className: "border-t border-gray-200 bg-amber-50/50", "data-testid": "queued-prompts-section", children: _jsxs("div", { className: "px-4 pt-2.5 pb-2", children: [_jsxs("div", { className: "mb-1.5 flex items-center gap-1.5", children: [_jsx("div", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-amber-400" }), _jsxs("span", { className: "text-[10px] font-medium tracking-wide text-amber-600 uppercase", "data-testid": "queued-prompts-count", children: ["Queued (", queuedPrompts.length, ")"] })] }), _jsx("div", { className: "max-h-64 space-y-0.5 overflow-y-auto", children: queuedPrompts.map((qp) => {
5881
- let triggerName = "";
5882
- if (qp.data) {
5883
- try {
5884
- const parsed = JSON.parse(qp.data);
5885
- triggerName = parsed?.triggerName?.trim() || "";
5886
- }
5887
- catch {
5888
- // Ignore invalid JSON metadata and render as regular queued prompt.
5889
- }
5890
- }
5891
- const isTriggerQueuedPrompt = !qp.sourceAgentName && triggerName.length > 0;
5892
- const isTriggerExpanded = !!expandedQueuedTriggerIds[qp.id];
5893
- if (isTriggerQueuedPrompt) {
5894
- return (_jsxs("div", { className: "text-[11px]", "data-testid": "queued-prompt-item", children: [_jsxs("button", { type: "button", onClick: () => setExpandedQueuedTriggerIds((prev) => ({
5895
- ...prev,
5896
- [qp.id]: !prev[qp.id],
5897
- })), className: "flex w-full items-center gap-1.5 rounded px-1 py-0.5 text-left text-amber-800 transition-colors hover:bg-amber-100/60", "data-testid": "queued-trigger-toggle", "data-expanded": isTriggerExpanded ? "true" : "false", children: [_jsx(Target, { className: "h-3 w-3 shrink-0 text-amber-500", strokeWidth: 2 }), _jsx("span", { className: "truncate font-medium", children: triggerName }), qp.createdDate && (_jsx("span", { className: "ml-1 shrink-0 text-[10px] text-amber-500", children: formatTime(new Date(qp.createdDate)) })), _jsx("span", { className: "ml-auto shrink-0 text-amber-400", children: isTriggerExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3" })) : (_jsx(ChevronDown, { className: "h-3 w-3" })) })] }), isTriggerExpanded && (_jsx("div", { className: "mt-0.5 border-l-2 border-amber-200 pl-5 text-[11px] text-amber-700/80", children: qp.prompt }))] }, qp.id));
5898
- }
5899
- return (_jsx("div", { className: "rounded-md border border-amber-200 bg-white p-2.5 text-[11px]", "data-testid": "queued-prompt-item", children: _jsx("div", { className: "flex items-start justify-between gap-2", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "mb-1 flex items-center gap-1.5", children: qp.sourceAgentName ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-medium text-gray-700", children: ["From ", qp.sourceAgentName] }), qp.priority > 5 && (_jsx("span", { className: "rounded bg-red-100 px-1.5 py-0.5 text-[11px] font-medium text-red-700", children: "High Priority" }))] })) : (_jsx("span", { className: "font-medium text-gray-700", children: "From User" })) }), _jsx("div", { className: "wrap-break-word whitespace-pre-wrap text-gray-600", "data-testid": "queued-prompt-text", children: qp.prompt }), _jsxs("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px] text-gray-400", children: [qp.createdDate && (_jsxs("span", { children: ["Queued ", formatTime(new Date(qp.createdDate))] })), qp.scheduledFor &&
5900
- new Date(qp.scheduledFor).getTime() >
5901
- new Date(qp.createdDate || 0).getTime() +
5902
- 1000 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-gray-300", children: "\u2022" }), _jsxs("span", { className: "font-medium text-amber-600", children: ["Scheduled for", " ", new Date(qp.scheduledFor).toDateString() ===
5903
- new Date().toDateString()
5904
- ? formatTime(new Date(qp.scheduledFor))
5905
- : formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
5906
- }) })] }) }))] }));
5907
- const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
5908
- const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
5909
- {
5910
- name: "conversation",
5911
- defaultSize: 65,
5912
- content: fullModeUpperContent,
5913
- },
5914
- {
5915
- name: "questionnaire",
5916
- defaultSize: 35,
5917
- content: fullModeInlineDialog,
5918
- },
5919
- ], direction: "vertical", localStorageKey: compact
5920
- ? "agent-terminal-compact-questionnaire-splitter"
5921
- : "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
5922
- return loadingContent ? (loadingContent) : displayMode === "summary" && summaryModeContent ? (summaryModeContent) : (_jsxs("div", { className: `flex h-full min-h-0 flex-col ${className || ""}`, children: [parentAgentId && !simpleMode && (_jsx("div", { className: "border-b border-gray-200 bg-gray-50 px-4 py-2", children: _jsxs("button", { onClick: handleBackToParent, className: "flex items-center gap-2 text-[11px] text-gray-600 transition-colors hover:text-gray-900", title: "Back to parent agent", children: [_jsx(ArrowLeft, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), _jsx("span", { children: "Back to parent agent" })] }) })), fullModeContent, _jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (
4069
+ ].filter(Boolean).length - 1)), setActiveTab: setContextPanelsActiveTab, className: "justify-start px-4" }) })) : (_jsxs(_Fragment, { children: [renderContextInfoBar(), agent?.id && activeProfile && (_jsx(AgentDocumentList, { ref: documentListRef, agentId: agent.id, maxFileSizeMB: activeProfile.maxDocumentSizeMB ?? 10, enabled: activeProfile.enableDocumentUpload ?? false, profileId: activeProfile.id }, `${agent.id}-${agent.updatedDate || ""}-${activeProfile.id}`)), _jsx(TodoListPanel, { messages: messages, agentMetadata: agentMetadata }), _jsx(SpawnedAgentsPanel, { agentMetadata: agentMetadata }), agent?.id && _jsx(AgentEditOperationsPanel, { agentId: agent.id })] }))), queuedPrompts.length > 0 && !simpleMode && (_jsx("div", { className: "border-t border-gray-200 bg-amber-50/50", "data-testid": "queued-prompts-section", children: _jsxs("div", { className: "px-4 pt-3 pb-2", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx("div", { className: "h-2 w-2 animate-pulse rounded-full bg-amber-500" }), _jsxs("span", { className: "text-[11px] font-semibold text-amber-900", "data-testid": "queued-prompts-count", children: ["Queued Messages (", queuedPrompts.length, ")"] })] }), _jsx("div", { className: "max-h-64 space-y-2 overflow-y-auto", children: queuedPrompts.map((qp) => (_jsx("div", { className: "rounded-md border border-amber-200 bg-white p-2.5 text-[11px]", "data-testid": "queued-prompt-item", children: _jsx("div", { className: "flex items-start justify-between gap-2", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "mb-1 flex items-center gap-1.5", children: qp.sourceAgentName ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-medium text-gray-700", children: ["From ", qp.sourceAgentName] }), qp.priority > 5 && (_jsx("span", { className: "rounded bg-red-100 px-1.5 py-0.5 text-[11px] font-medium text-red-700", children: "High Priority" }))] })) : (_jsx("span", { className: "font-medium text-gray-700", children: "From User" })) }), _jsx("div", { className: "wrap-break-word whitespace-pre-wrap text-gray-600", "data-testid": "queued-prompt-text", children: qp.prompt }), _jsxs("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px] text-gray-400", children: [qp.createdDate && (_jsxs("span", { children: ["Queued ", formatTime(new Date(qp.createdDate))] })), qp.scheduledFor &&
4070
+ new Date(qp.scheduledFor).getTime() >
4071
+ new Date(qp.createdDate || 0).getTime() + 1000 && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-gray-300", children: "\u2022" }), _jsxs("span", { className: "font-medium text-amber-600", children: ["Scheduled for", " ", new Date(qp.scheduledFor).toDateString() ===
4072
+ new Date().toDateString()
4073
+ ? formatTime(new Date(qp.scheduledFor))
4074
+ : formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id))) })] }) })), activeInlineDialog &&
4075
+ (() => {
4076
+ // Look up dialog component from config
4077
+ const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
4078
+ if (dialogRegistration) {
4079
+ const DialogComponent = dialogRegistration.component;
4080
+ return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
4081
+ activeInlineDialog.onComplete(result);
4082
+ setActiveInlineDialog(null);
4083
+ }, onCancel: () => {
4084
+ activeInlineDialog.onCancel();
4085
+ setActiveInlineDialog(null);
4086
+ } }) }));
4087
+ }
4088
+ // Fallback for unknown dialog types
4089
+ return (_jsx("div", { className: "agent-inline-dialog", children: _jsxs("div", { className: "p-4 text-sm text-red-500", children: ["Unknown dialog type: ", activeInlineDialog.request.dialogType] }) }));
4090
+ })(), _jsxs("div", { className: cn("border-t border-gray-200 p-4", simpleMode && "pb-10"), children: [activePlaceholderInput ? (
5923
4091
  // Placeholder Input (from quick actions)
5924
4092
  // Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
5925
4093
  _jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
@@ -5996,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5996
4164
  return null;
5997
4165
  return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
5998
4166
  ? "justify-end"
5999
- : "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"
4167
+ : "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"
6000
4168
  ? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
6001
4169
  : mode === "supervised"
6002
4170
  ? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
6003
4171
  : "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
6004
4172
  const nextMode = val || "supervised";
4173
+ // Optimistic UI update
4174
+ setMode(nextMode);
6005
4175
  const current = agentMetadata || {};
6006
4176
  const nextMeta = {
6007
4177
  ...current,
6008
4178
  mode: nextMode,
6009
4179
  };
6010
4180
  try {
6011
- if (!agent?.id || isLocalOnlyDraftAgent) {
6012
- setMode(nextMode);
4181
+ if (!agent?.id || agent.status === "new") {
6013
4182
  setAgentMetadata(nextMeta);
4183
+ // Cache until first start when agent is persisted
6014
4184
  pendingSettingsRef.current = {
6015
4185
  ...(pendingSettingsRef.current || {}),
6016
4186
  mode: nextMode,
6017
4187
  };
6018
4188
  return;
6019
4189
  }
6020
- const result = await updateAgentSettings(agent.id, {
4190
+ await updateAgentSettings(agent.id, {
6021
4191
  mode: nextMode,
6022
4192
  });
6023
- if (result.success === false ||
6024
- result.updates?.mode === false) {
6025
- throw new Error("Mode change was not applied");
6026
- }
6027
- setMode(nextMode);
6028
4193
  setAgentMetadata(nextMeta);
4194
+ setAgent((prev) => prev
4195
+ ? { ...prev, metadata: JSON.stringify(nextMeta) }
4196
+ : prev);
4197
+ }
4198
+ catch (e2) {
4199
+ console.error("Failed to persist mode change", e2);
4200
+ }
4201
+ } }), profiles?.length > 0 && (_jsx(Select, { size: "xs", maxWidth: 200, searchable: profiles.length > 5, searchPlaceholder: "Filter profiles...", className: "h-5 w-auto min-w-[120px] rounded border px-1.5 text-[11px] text-gray-500", value: activeProfile?.id || "", options: profileOptions, "data-testid": "agent-profile-selector", onValueChange: async (val) => {
4202
+ const nextProfile = profiles.find((x) => x.id === val);
4203
+ if (!nextProfile)
4204
+ return;
4205
+ setActiveProfile(nextProfile);
4206
+ try {
4207
+ if (agent?.id && agent.status !== "new") {
4208
+ await updateAgentSettings(agent.id, {
4209
+ profileId: nextProfile.id,
4210
+ profileName: nextProfile.name,
4211
+ });
4212
+ }
4213
+ else {
4214
+ // cache until first start
4215
+ pendingSettingsRef.current = {
4216
+ ...(pendingSettingsRef.current || {}),
4217
+ // we cache profile by updating local metadata
4218
+ };
4219
+ setAgentMetadata((current) => {
4220
+ const next = { ...(current || {}) };
4221
+ next.profile = nextProfile.name;
4222
+ next.additionalData = {
4223
+ ...(next.additionalData || {}),
4224
+ profileId: nextProfile.id,
4225
+ profileName: nextProfile.name,
4226
+ };
4227
+ return next;
4228
+ });
4229
+ }
4230
+ // reflect in local agent stub so tabs and titles can use it if needed
6029
4231
  setAgent((prev) => prev
6030
4232
  ? {
6031
4233
  ...prev,
6032
- mode: nextMode,
6033
- metadata: JSON.stringify(nextMeta),
4234
+ metadata: JSON.stringify({
4235
+ ...(agentMetadata || {}),
4236
+ profile: nextProfile.name,
4237
+ additionalData: {
4238
+ ...(agentMetadata
4239
+ ?.additionalData || {}),
4240
+ profileId: nextProfile.id,
4241
+ profileName: nextProfile.name,
4242
+ },
4243
+ }),
6034
4244
  }
6035
4245
  : prev);
6036
4246
  }
6037
- catch (e2) {
6038
- console.error("Failed to persist mode change", e2);
4247
+ catch (err) {
4248
+ console.error("Failed to persist agent profile", err);
4249
+ }
4250
+ } })), activeProfile?.models?.length ? (_jsx(Select, { size: "xs", maxWidth: 300, searchable: activeProfile.models.length > 5, searchPlaceholder: "Filter models...", className: "h-5 w-auto min-w-[120px] rounded border px-1.5 text-[11px] text-gray-500", value: selectedModelId || "", options: modelOptions, onValueChange: async (val) => {
4251
+ const nextId = val;
4252
+ setSelectedModelId(nextId);
4253
+ const modelName = activeProfile?.models?.find((m) => m.id === nextId)
4254
+ ?.name || "";
4255
+ // Update local agent state immediately for UX and to reflect in streaming stub
4256
+ setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
4257
+ // Persist only for existing agents; otherwise cache until first start
4258
+ try {
4259
+ if (agent?.id && agent.status !== "new") {
4260
+ await updateAgentSettings(agent.id, {
4261
+ model: modelName,
4262
+ });
4263
+ }
4264
+ else {
4265
+ pendingSettingsRef.current = {
4266
+ ...(pendingSettingsRef.current || {}),
4267
+ modelName,
4268
+ };
4269
+ }
6039
4270
  }
6040
- } }), _jsxs(Popover, { open: showAgentSettings, onOpenChange: (open) => {
6041
- setShowAgentSettings(open);
6042
- if (!open) {
6043
- setShowSkillPicker(false);
4271
+ catch (err) {
4272
+ console.error("Failed to persist agent model", err);
6044
4273
  }
6045
- }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[11px] text-gray-600", "data-testid": "agent-settings-popover-trigger", children: [_jsx(Settings2, { className: "mr-1 h-3 w-3", strokeWidth: 1 }), "Agent settings"] }) }), _jsx(PopoverContent, { className: "w-80 p-3", align: "start", onInteractOutside: (e) => {
6046
- const target = e.target;
6047
- if (target?.closest('[data-help-panel="true"]')) {
6048
- e.preventDefault();
6049
- }
6050
- }, onPointerDownOutside: (e) => {
6051
- const target = e.target;
6052
- if (target?.closest('[data-help-panel="true"]')) {
6053
- e.preventDefault();
6054
- }
6055
- }, onFocusOutside: (e) => {
6056
- const target = e.target;
6057
- if (target?.closest('[data-help-panel="true"]')) {
6058
- e.preventDefault();
6059
- }
6060
- }, children: _jsxs("div", { className: "space-y-3", children: [profiles?.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-[11px] font-medium text-gray-700", children: "Agent profile" }), _jsx(Select, { size: "xs", maxWidth: 300, searchable: profiles.length > 5, searchPlaceholder: "Filter profiles...", className: "h-6 w-full rounded border px-1.5 text-[11px] text-gray-500", value: activeProfile?.id || "", options: profileOptions, "data-testid": "agent-profile-selector", onValueChange: async (val) => {
6061
- const nextProfile = profiles.find((x) => x.id === val);
6062
- if (!nextProfile)
6063
- return;
6064
- setActiveProfile(nextProfile);
6065
- try {
6066
- if (agent?.id && !isLocalOnlyDraftAgent) {
6067
- await updateAgentSettings(agent.id, {
6068
- profileId: nextProfile.id,
6069
- profileName: nextProfile.name,
6070
- });
6071
- }
6072
- else {
6073
- pendingSettingsRef.current = {
6074
- ...(pendingSettingsRef.current || {}),
6075
- profileId: nextProfile.id,
6076
- profileName: nextProfile.name,
6077
- };
6078
- setAgentMetadata((current) => {
6079
- const next = {
6080
- ...(current || {}),
6081
- };
6082
- next.profile = nextProfile.name;
6083
- next.additionalData = {
6084
- ...(next.additionalData || {}),
6085
- profileId: nextProfile.id,
6086
- profileName: nextProfile.name,
6087
- };
6088
- return next;
6089
- });
6090
- }
6091
- setAgent((prev) => prev
6092
- ? {
6093
- ...prev,
6094
- profileId: nextProfile.id,
6095
- profileName: nextProfile.name,
6096
- metadata: JSON.stringify({
6097
- ...(agentMetadata || {}),
6098
- profile: nextProfile.name,
6099
- additionalData: {
6100
- ...(agentMetadata
6101
- ?.additionalData || {}),
6102
- profileId: nextProfile.id,
6103
- profileName: nextProfile.name,
6104
- },
6105
- }),
6106
- }
6107
- : prev);
6108
- }
6109
- catch (err) {
6110
- console.error("Failed to persist agent profile", err);
6111
- }
6112
- } }), activeProfile && (_jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2", children: [_jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-6 px-2 text-[10px]", onClick: () => {
6113
- void handleEditProfileSideBySide();
6114
- setShowAgentSettings(false);
6115
- }, "data-testid": "agent-profile-edit-button", children: "Edit" }), _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 text-[10px] text-gray-500 hover:text-gray-700", onClick: () => {
6116
- void handleOpenProfileSettings();
6117
- setShowAgentSettings(false);
6118
- }, children: ["Open profile settings", _jsx(ExternalLink, { className: "h-2.5 w-2.5", strokeWidth: 1.5 })] })] }))] })), activeProfile?.models?.length ? (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-[11px] font-medium text-gray-700", children: "Model" }), _jsx(Select, { "data-testid": "agent-model-selector", size: "xs", maxWidth: 300, searchable: activeProfile.models.length > 5, searchPlaceholder: "Filter models...", className: "h-6 w-full rounded border px-1.5 text-[11px] text-gray-500", value: selectedModelId || "", options: modelOptions, onValueChange: async (val) => {
6119
- const nextId = val;
6120
- setSelectedModelId(nextId);
6121
- const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
6122
- setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
6123
- try {
6124
- if (agent?.id && !isLocalOnlyDraftAgent) {
6125
- await updateAgentSettings(agent.id, {
6126
- model: modelName,
6127
- });
6128
- }
6129
- else {
6130
- pendingSettingsRef.current = {
6131
- ...(pendingSettingsRef.current || {}),
6132
- modelName,
6133
- };
6134
- }
6135
- }
6136
- catch (err) {
6137
- console.error("Failed to persist agent model", err);
6138
- }
6139
- } })] })) : null, _jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-1 text-[11px] font-medium text-gray-700", children: [_jsx(Target, { className: "h-3 w-3", strokeWidth: 1 }), "Skills"] }), _jsxs(Popover, { open: showSkillPicker, onOpenChange: (open) => {
6140
- setShowSkillPicker(open);
6141
- if (open) {
6142
- setSkillActionError(null);
6143
- }
6144
- }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "xs", variant: "outline", className: "h-5 rounded border px-1.5 text-[10px] text-gray-600", "data-testid": "agent-skill-picker-trigger", children: [_jsx(Plus, { className: "mr-1 h-3 w-3", strokeWidth: 1.5 }), "Add"] }) }), _jsx(PopoverContent, { className: "w-88 p-2", align: "end", side: "bottom", children: _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-[11px] font-medium text-gray-700", children: "Select a skill" }), _jsxs("div", { className: "relative h-56 rounded border border-gray-200 bg-gray-50", children: [_jsx(ScrollingContentTree, { rootItemIds: skillRootIds, selectedItemId: selectedSkillIds[selectedSkillIds.length - 1] || undefined, expandedItemId: selectedSkillIds[selectedSkillIds.length - 1] || skillRootIds[0], scrollToSelected: true, hideRootNodes: false, onSelectionChange: (selection) => {
6145
- const selected = selection[0];
6146
- if (!selected?.id)
6147
- return;
6148
- setSkillActionError(null);
6149
- if (selectableTemplateIdSet.size > 0 &&
6150
- (!selected.templateId ||
6151
- !selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
6152
- return;
6153
- }
6154
- if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
6155
- setSkillActionError("This skill cannot be added for the current agent profile.");
6156
- return;
6157
- }
6158
- void (async () => {
6159
- const added = await handleAddSkill(selected.id);
6160
- if (added) {
6161
- setShowSkillPicker(false);
6162
- }
6163
- })();
6164
- } }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
6165
- !skillsError &&
6166
- profileFilteredSkills.length > 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "Click a skill item in the tree to add it." })), skillsError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillsError })), !skillsError && skillActionError && (_jsx("div", { className: "text-[10px] text-red-600", children: skillActionError })), !skillsLoading &&
6167
- !skillsError &&
6168
- skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
6169
- !skillsError &&
6170
- profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
6171
- ? "All addable skills are selected"
6172
- : "No skills can be added for this profile" }))] }) })] })] }), selectedSkillIds.length > 0 && (_jsx("div", { className: "mb-2 flex flex-wrap gap-1", children: selectedSkillIds.map((skillId) => {
6173
- const skill = selectedSkills.find((s) => s.id === skillId);
6174
- 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: () => {
6175
- void handleOpenSkillItem(skillId);
6176
- }, 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: () => {
6177
- void handleRemoveSkill(skillId);
6178
- }, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
6179
- }) }))] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setToolsSectionExpanded((open) => !open), className: "mb-0.5 flex w-full items-center justify-between rounded px-0.5 py-0.5 text-left text-[11px] font-medium text-gray-700 hover:bg-gray-100/80", "aria-expanded": toolsSectionExpanded, "data-testid": "agent-tools-section-toggle", children: [_jsxs("span", { children: ["Available tools (", displayedAvailableTools.length, ")"] }), toolsSectionExpanded ? (_jsx(ChevronUp, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 })) : (_jsx(ChevronDown, { className: "h-3 w-3 shrink-0 text-gray-400", strokeWidth: 1.5 }))] }), toolsSectionExpanded && (_jsx("div", { className: "max-h-36 overflow-y-auto rounded border border-gray-100 bg-gray-50/50 p-1", "data-testid": "agent-tools-section", children: displayedAvailableToolsLoading ? (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Loading available tools..." })) : displayedAvailableTools.length > 0 ? (_jsxs("div", { className: "space-y-0.5", children: [isLocalOnlyDraftAgent && (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: "Preview based on the current profile, mode, and selected skills." })), displayedAvailableTools.map((toolName) => (_jsx("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": "agent-tool-row", title: toolName, children: _jsx("div", { className: "truncate text-[10px] text-gray-700", children: toolName }) }, toolName)))] })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6180
- ? "No available tools for this profile and mode"
6181
- : "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) => {
6182
- const sourceLabel = formatAllowanceSource(allowance.source);
6183
- const pathLabel = "itemPath" in allowance
6184
- ? allowance.itemPath
6185
- : allowance.normalizedPath;
6186
- return (_jsxs("div", { className: "rounded px-1 py-0.5 transition-colors hover:bg-white/60", "data-testid": `agent-allowance-row-${group.key}`, children: [_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsx("div", { className: "shrink-0 text-[10px] font-medium text-gray-700", children: allowance.operationType ||
6187
- "*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
6188
- allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
6189
- sourceLabel,
6190
- allowance.grantedBy,
6191
- ]
6192
- .filter(Boolean)
6193
- .join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
6194
- })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6195
- ? "Allowances are shown after the agent is created"
6196
- : "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) => {
6197
- const filterText = (sub.filter || "").trim();
6198
- 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));
6199
- }) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6200
- ? "Subscribed triggers are shown after the agent is created"
6201
- : "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: () => {
4274
+ } })) : null, activeProfile?.prompts?.length ? (_jsxs(Popover, { open: showPredefined, onOpenChange: setShowPredefined, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { className: "rounded p-1 hover:bg-gray-100", onClick: () => { }, title: "Predefined prompts", "aria-label": "Predefined prompts", type: "button", children: _jsx(Wand2, { className: "h-3 w-3", strokeWidth: 1 }) }) }), _jsx(PopoverContent, { className: "w-64 p-0", align: "start", children: _jsx("div", { className: "max-h-56 overflow-y-auto p-2", children: activeProfile.prompts.map((p, index) => (_jsx("div", { className: "cursor-pointer rounded p-1.5 text-[10px] text-gray-700 hover:bg-gray-100", onClick: () => {
6202
4275
  setPrompt(p.prompt);
6203
4276
  setShowPredefined(false);
6204
4277
  if (textareaRef.current)
6205
4278
  textareaRef.current.focus();
6206
- }, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
6207
- !simpleMode &&
6208
- editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
6209
- if (editContext?.isMobile)
4279
+ }, children: p.title }, index))) }) })] })) : null, !hideBottomControls && !simpleMode && isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
4280
+ if (isMobile)
6210
4281
  setShowCostAndAgent((prev) => !prev);
6211
- }, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded": editContext?.isMobile
6212
- ? showCostAndAgent
6213
- : undefined, "aria-label": "Toggle cost and context info", children: _jsx(DollarSign, { className: "size-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: _jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, effectiveModelName: effectiveModelName, socketDiagnostics: editContext.socketDiagnostics, runDiagnosticsSnapshot: runDiagnosticsSnapshot, liveTotals: liveTotals, totalTokens: liveTotals
4282
+ }, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded": isMobile ? showCostAndAgent : undefined, "aria-label": "Toggle cost and context info", children: _jsx(DollarSign, { className: "size-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: _jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, liveTotals: liveTotals, totalTokens: liveTotals
6214
4283
  ? {
6215
4284
  input: liveTotals.input,
6216
4285
  output: liveTotals.output,
@@ -6220,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6220
4289
  outputCost: liveTotals.outputCost,
6221
4290
  cachedCost: liveTotals.cachedCost,
6222
4291
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6223
- imageCost: liveTotals.imageCost ?? 0,
6224
4292
  totalCost: liveTotals.totalCost,
6225
4293
  }
6226
- : totalTokens, costLimit: agentCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }) }) })] }))] })), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [_jsx("span", { title: isVoiceDisabled
4294
+ : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }) }) })] }))] })), _jsxs("div", { className: "flex items-center gap-1 self-end", children: [_jsx("span", { title: isVoiceDisabled
6227
4295
  ? "Your browser does not support Speech Recognition"
6228
4296
  : isListening
6229
4297
  ? "Stop voice input"
@@ -6244,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6244
4312
  : allPendingApprovals.length > 0
6245
4313
  ? "Approve or reject pending tool calls first"
6246
4314
  : "Send", "aria-label": isExecuting ? "Stop" : "Send", "data-testid": "agent-send-stop-button", "data-executing": isExecuting ? "true" : "false", children: isExecuting ? (_jsx(Square, { className: "size-3", strokeWidth: 1 })) : (_jsx(Send, { className: "size-3", strokeWidth: 1 })) })] })] }));
6247
- })(), !hideBottomControls &&
6248
- !simpleMode &&
6249
- editContext &&
6250
- !editContext.isMobile && (_jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, effectiveModelName: effectiveModelName, socketDiagnostics: editContext.socketDiagnostics, runDiagnosticsSnapshot: runDiagnosticsSnapshot, liveTotals: liveTotals, totalTokens: liveTotals
4315
+ })(), !hideBottomControls && !simpleMode && !isMobile && (_jsx(AgentTerminalStatusBar, { agent: agent, contextWindowStatus: contextWindowStatus, liveTotals: liveTotals, totalTokens: liveTotals
6251
4316
  ? {
6252
4317
  input: liveTotals.input,
6253
4318
  output: liveTotals.output,
@@ -6257,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6257
4322
  outputCost: liveTotals.outputCost,
6258
4323
  cachedCost: liveTotals.cachedCost,
6259
4324
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6260
- imageCost: liveTotals.imageCost ?? 0,
6261
4325
  totalCost: liveTotals.totalCost,
6262
4326
  }
6263
- : totalTokens, costLimit: agentCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
4327
+ : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
6264
4328
  }
6265
4329
  //# sourceMappingURL=AgentTerminal.js.map